2004-10-02 Casper S. Hornstrup <chorns@users.sourceforge.net>
[reactos.git] / reactos / tools / regtests.c
1 /*
2 * Generate a file with test registrations from a list
3 * of files in a directory.
4 * Casper S. Hornstrup <chorns@users.sourceforge.net>
5 */
6
7 #include <stdio.h>
8 #include <fcntl.h>
9 #include <sys/stat.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13
14 #ifdef WIN32
15 #include <io.h>
16 #include <dos.h>
17 #else
18 #include <sys/io.h>
19 #include <errno.h>
20 #include <sys/types.h>
21 #include <dirent.h>
22 #include <unistd.h>
23 #endif
24 #ifndef MAX_PATH
25 #define MAX_PATH 260
26 #endif
27 #ifndef WIN32
28 #define DIR_SEPARATOR_CHAR '/'
29 #define DIR_SEPARATOR_STRING "/"
30 #else
31 #define DIR_SEPARATOR_CHAR '\\'
32 #define DIR_SEPARATOR_STRING "\\"
33 #endif
34
35 static FILE *out;
36 static char *path;
37 static char *file;
38 static char *makefile;
39 static char *umstubfile;
40 static char *kmstubfile;
41 static char *exestubfile;
42
43 static char*
44 convert_path(char* origpath)
45 {
46 char* newpath;
47 int i;
48
49 /* for no good reason, i'm having trouble getting gcc to link strdup */
50 //newpath = strdup(origpath);
51 newpath = malloc(strlen(origpath)+1);
52 strcpy(newpath, origpath);
53
54 i = 0;
55 while (newpath[i] != 0)
56 {
57 #ifndef WIN32
58 if (newpath[i] == '\\')
59 {
60 newpath[i] = '/';
61 }
62 #else
63 #ifdef WIN32
64 if (newpath[i] == '/')
65 {
66 newpath[i] = '\\';
67 }
68 #endif
69 #endif
70 i++;
71 }
72 return(newpath);
73 }
74
75 static void
76 write_line(char *line)
77 {
78 int n_out;
79 char buf[200];
80
81 memset(buf, 0, sizeof(buf));
82 strcpy(buf, line);
83 /* Terminate the line */
84 buf[strlen(buf)] = '\r';
85 buf[strlen(buf)] = '\n';
86
87 n_out = fwrite(&buf[0], 1, strlen(buf), out);
88 }
89
90 static void
91 change_extension(char *filenamebuffer,
92 char *filename,
93 char *newextension)
94 {
95 char *ptr;
96
97 if (newextension == NULL)
98 {
99 strcpy(filenamebuffer, filename);
100 return;
101 }
102
103 ptr = strrchr(filename, '.');
104 if (ptr != NULL)
105 {
106 strncpy(filenamebuffer, filename, ptr - filename);
107 filenamebuffer[ptr - filename] = 0;
108 strcat(filenamebuffer, newextension);
109 }
110 else
111 {
112 strcpy(filenamebuffer, filename);
113 strcat(filenamebuffer, newextension);
114 }
115 }
116
117 static void
118 get_test_name(char *filename,
119 char *testname)
120 {
121 int i;
122
123 strcpy(testname, filename);
124
125 i = strlen(testname);
126 while (i > 0 && testname[i] != '.')
127 {
128 i--;
129 }
130 if (i > 0)
131 {
132 testname[i] = 0;
133 }
134
135 /* Make a capital first letter and make all other letters lower case */
136 testname[0] = toupper(testname[0]);
137 if (!((testname[0] >= 'A' && testname[0] <= 'Z') ||
138 (testname[0] >= '0' && testname[0] <= '9')))
139 {
140 testname[0] = '_';
141 }
142 i = 1;
143 while (i < strlen(testname))
144 {
145 testname[i] = tolower(testname[i]);
146 if (!((testname[i] >= 'a' && testname[i] <= 'z') ||
147 (testname[i] >= '0' && testname[i] <= '9')))
148 {
149 testname[i] = '_';
150 }
151 i++;
152 }
153 }
154
155 /*
156 * filename - name of file to make registrations for
157 * type - type of registration (0 = prototype, 1 = call, 2 = makefile)
158 */
159 static void
160 register_test(char *filename,
161 int type)
162 {
163 char ext[100];
164 char testname[100];
165 char call[100];
166 char regtest[100];
167 char filenamebuffer[MAX_PATH];
168 int i;
169
170 i = strlen(filename);
171 while (i > 0 && filename[i] != '.')
172 {
173 i--;
174 }
175 if (i > 0)
176 {
177 memset(ext, 0, sizeof(ext));
178 strncpy(&ext[0], &filename[i], strlen(&filename[i]));
179
180 if ((strncmp(ext, ".c", 2) != 0) && (strncmp(ext, ".C", 2) != 0))
181 {
182 return;
183 }
184 }
185 else
186 {
187 return;
188 }
189
190 memset(testname, 0, sizeof(testname));
191 get_test_name(filename, testname);
192
193 if (type == 0)
194 {
195 sprintf(regtest, "extern int %sTest(int Command, char *Buffer);", testname);
196 write_line(regtest);
197 }
198 else if (type == 1)
199 {
200 sprintf(call, "%sTest", testname);
201 sprintf(regtest, " AddTest((TestRoutine)%s);", call);
202 write_line(regtest);
203 }
204 else if (type == 2)
205 {
206 change_extension(filenamebuffer, filename, ".o");
207 sprintf(regtest, "%s \\", filenamebuffer);
208 write_line(regtest);
209 }
210 }
211
212 #ifdef WIN32
213
214 /* Win32 version */
215
216 static void
217 make_file_list (int type)
218 {
219 struct _finddata_t f;
220 int findhandle;
221 char searchbuf[MAX_PATH];
222
223 strcpy(searchbuf, path);
224 strcat(searchbuf, "*.*");
225 findhandle =_findfirst(searchbuf, &f);
226 if (findhandle != -1)
227 {
228 do
229 {
230 if (f.attrib & _A_SUBDIR)
231 {
232 /* Skip subdirectories */
233 continue;
234 }
235
236 register_test(f.name, type);
237 }
238 while (_findnext(findhandle, &f) == 0);
239 _findclose(findhandle);
240 }
241 }
242
243 #else
244
245 /* Linux version */
246 static void
247 make_file_list (int type)
248 {
249 DIR *dirp;
250 struct dirent *entry;
251 struct stat stbuf;
252 char buf[MAX_PATH];
253
254 #ifdef HAVE_D_TYPE
255 dirp = opendir(path);
256 if (dirp != NULL)
257 {
258 while ((entry = readdir(dirp)) != NULL)
259 {
260 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
261 continue; // skip self and parent
262
263 if (entry->d_type == DT_REG) // normal file
264 {
265 // Check for an absolute path
266 if (path[0] == DIR_SEPARATOR_CHAR)
267 {
268 strcpy(buf, path);
269 strcat(buf, DIR_SEPARATOR_STRING);
270 strcat(buf, entry->d_name);
271 }
272 else
273 {
274 getcwd(buf, sizeof(buf));
275 strcat(buf, DIR_SEPARATOR_STRING);
276 strcat(buf, path);
277 strcat(buf, entry->d_name);
278 }
279
280 if (stat(buf, &stbuf) == -1)
281 {
282 printf("Can't access '%s' (%s)\n", buf, strerror(errno));
283 return;
284 }
285
286 if (S_ISDIR(stbuf.st_mode))
287 {
288 /* Skip subdirectories */
289 continue;
290 }
291
292 register_test(entry->d_name, type);
293 }
294 }
295 closedir(dirp);
296 }
297 else
298 {
299 printf("Can't open %s\n", path);
300 return;
301 }
302
303 #else
304
305 dirp = opendir(path);
306 if (dirp != NULL)
307 {
308 while ((entry = readdir(dirp)) != NULL)
309 {
310 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
311 continue; // skip self and parent
312
313 // Check for an absolute path
314 if (path[0] == DIR_SEPARATOR_CHAR)
315 {
316 strcpy(buf, path);
317 strcat(buf, DIR_SEPARATOR_STRING);
318 strcat(buf, entry->d_name);
319 }
320 else
321 {
322 getcwd(buf, sizeof(buf));
323 strcat(buf, DIR_SEPARATOR_STRING);
324 strcat(buf, path);
325 strcat(buf, entry->d_name);
326 }
327
328 if (stat(buf, &stbuf) == -1)
329 {
330 printf("Can't access '%s' (%s)\n", buf, strerror(errno));
331 return;
332 }
333
334 if (S_ISDIR(stbuf.st_mode))
335 {
336 /* Skip subdirectories */
337 continue;
338 }
339
340 register_test(entry->d_name, type);
341 }
342 closedir(dirp);
343 }
344 else
345 {
346 printf("Can't open %s\n", path);
347 return;
348 }
349
350 #endif
351 }
352
353 #endif
354
355 static int
356 is_file_changed(char *filename,
357 char *content)
358 {
359 FILE *file;
360 int size;
361 int n;
362 char *filecontent;
363
364 file = fopen(filename, "rb");
365 if (file == NULL)
366 {
367 return 1;
368 }
369
370 fseek(file, 0, SEEK_END);
371 size = ftell(file);
372 fseek(file, 0, SEEK_SET);
373 if (size <= 0)
374 {
375 fclose(file);
376 return 1;
377 }
378 filecontent = malloc(size);
379 if (filecontent == NULL)
380 {
381 fclose(file);
382 return 1;
383 }
384
385 n = fread(filecontent, 1, size, file);
386
387 if (n != strlen(content))
388 {
389 free(filecontent);
390 fclose(file);
391 return 1;
392 }
393
394 if (strcmp(content, filecontent) != 0)
395 {
396 free(filecontent);
397 fclose(file);
398 return 1;
399 }
400
401 free(filecontent);
402
403 fclose(file);
404
405 return 0;
406 }
407
408 static int
409 write_file_if_changed(char *filename,
410 char *content)
411 {
412 FILE *file;
413 int n;
414
415 if (is_file_changed(filename, content) == 0)
416 {
417 return 0;
418 }
419
420 file = fopen(filename, "wb");
421 if (file == NULL)
422 {
423 return 1;
424 }
425
426 n = fwrite(content, 1, strlen(content), file);
427
428 fclose(file);
429
430 return 0;
431 }
432
433 static char KMSTUB[] =
434 "/* This file is autogenerated. */\n"
435 "\n"
436 "#include <roskrnl.h>\n"
437 "#include <../kmregtests/kmregtests.h>\n"
438 "\n"
439 "typedef int (*TestRoutine)(int Command, char *Buffer);\n"
440 "\n"
441 "extern void RegisterTests();\n"
442 "\n"
443 "static PDEVICE_OBJECT KMRegTestsDeviceObject = NULL;\n"
444 "static PFILE_OBJECT KMRegTestsFileObject = NULL;\n"
445 "\n"
446 "void AddTest(TestRoutine Routine)\n"
447 "{\n"
448 " UNICODE_STRING DriverName;\n"
449 " IO_STATUS_BLOCK IoStatus;\n"
450 " NTSTATUS Status;\n"
451 " KEVENT Event;\n"
452 " PIRP Irp;\n"
453 "\n"
454 " if (KMRegTestsDeviceObject == NULL)\n"
455 " {\n"
456 " RtlInitUnicodeString(&DriverName, L\"\\\\Device\\\\KMRegTests\");\n"
457 " Status = IoGetDeviceObjectPointer(&DriverName, FILE_WRITE_ATTRIBUTES,\n"
458 " &KMRegTestsFileObject, &KMRegTestsDeviceObject);\n"
459 " if (!NT_SUCCESS(Status)) return;\n"
460 " }\n"
461 " KeInitializeEvent(&Event, NotificationEvent, FALSE);\n"
462 " Irp = IoBuildDeviceIoControlRequest(IOCTL_KMREGTESTS_REGISTER,\n"
463 " KMRegTestsDeviceObject, &Routine, sizeof(TestRoutine), NULL, 0, FALSE, &Event, &IoStatus);\n"
464 " Status = IoCallDriver(KMRegTestsDeviceObject, Irp);\n"
465 "}\n"
466 "\n"
467 "void PrepareTests()\n"
468 "{\n"
469 " RegisterTests();\n"
470 "}\n";
471
472 static char UMSTUB[] =
473 "/* This file is autogenerated. */\n"
474 "\n"
475 "#include <windows.h>\n"
476 "#define NTOS_MODE_USER\n"
477 "#include <ntos.h>\n"
478 "#include \"regtests.h\"\n"
479 "\n"
480 "PVOID\n"
481 "AllocateMemory(ULONG Size)\n"
482 "{\n"
483 " return (PVOID) RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);\n"
484 "}\n"
485 "\n"
486 "VOID\n"
487 "FreeMemory(PVOID Base)\n"
488 "{\n"
489 " RtlFreeHeap(RtlGetProcessHeap(), 0, Base);\n"
490 "}\n"
491 "\n"
492 "/* This function will be called several times */\n"
493 "void PrepareTests()\n"
494 "{\n"
495 " static int testsRegistered = 0;\n"
496 " if (testsRegistered == 0)\n"
497 " {\n"
498 " HANDLE hEvent;\n"
499 " hEvent = OpenEventW(\n"
500 " EVENT_ALL_ACCESS,\n"
501 " FALSE,\n"
502 " L\"WinRegTests\");\n"
503 " if (hEvent != NULL)\n"
504 " {\n"
505 " SetEvent(hEvent);\n"
506 " CloseHandle(hEvent);\n"
507 " testsRegistered = 1;\n"
508 " InitializeTests();\n"
509 " RegisterTests();\n"
510 " PerformTests(NULL, NULL);\n"
511 " }\n"
512 " }\n"
513 "}\n";
514
515 static char EXESTUB[] =
516 "/* This file is autogenerated. */\n"
517 "\n"
518 "#include <stdio.h>\n"
519 "#include <windows.h>\n"
520 "#define NTOS_MODE_USER\n"
521 "#include <ntos.h>\n"
522 "#include \"regtests.h\"\n"
523 "\n"
524 "PVOID\n"
525 "AllocateMemory(ULONG Size)\n"
526 "{\n"
527 " return (PVOID) RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);\n"
528 "}\n"
529 "\n"
530 "VOID\n"
531 "FreeMemory(PVOID Base)\n"
532 "{\n"
533 " RtlFreeHeap(RtlGetProcessHeap(), 0, Base);\n"
534 "}\n"
535 "\n"
536 "void\n"
537 "ConsoleWrite(char *Buffer)\n"
538 "{\n"
539 " printf(Buffer);\n"
540 "}\n"
541 "\n"
542 "ULONG\n"
543 "DbgPrint(PCH Format, ...)\n"
544 "{\n"
545 " return 0;\n"
546 "}\n"
547 "\n"
548 "int __stdcall WinMain(HINSTANCE hInstance,\n"
549 " HINSTANCE hPrevInstance,\n"
550 " LPSTR lpCmdLine,\n"
551 " int nCmdShow)\n"
552 "{\n"
553 " InitializeTests();\n"
554 " RegisterTests();\n"
555 " PerformTests(ConsoleWrite, NULL);\n"
556 " return 0;\n"
557 "}\n";
558
559 static char STUBS_HEADER[] =
560 "/* This file is autogenerated. */\n"
561 "passthrough:\n"
562 " call _FrameworkGetHook@4\n"
563 " test %eax, %eax\n"
564 " je .return\n"
565 " jmp *%eax\n"
566 ".return:\n"
567 " /* This will most likely corrupt the stack */\n"
568 " ret\n"
569 "\n";
570
571 static char HOOKS_HEADER[] =
572 "/* This file is autogenerated. */\n"
573 "#include <windows.h>\n"
574 "#include \"regtests.h\"\n"
575 "\n"
576 "API_DESCRIPTION ExternalDependencies[] =\n"
577 "{\n";
578
579 static char HOOKS_FOOTER[] =
580 "};\n"
581 "\n"
582 "#define ExternalDependencyCount %d\n"
583 "ULONG MaxExternalDependency = ExternalDependencyCount - 1;\n"
584 "\n"
585 "PVOID STDCALL\n"
586 "FrameworkGetHook(ULONG index)\n"
587 "{\n"
588 " return FrameworkGetHookInternal(index);\n"
589 "}\n";
590
591 static char HELP[] =
592 "REGTESTS path file makefile [-u umstubfile] [-k kmstubfile] [-e exestubfile]\n"
593 "REGTESTS -s stublistfile stubsfile hooksfile\n"
594 "\n"
595 " path Path to files\n"
596 " file Registration file to create\n"
597 " makefile Makefile to create\n"
598 " umstubfile Optional stub for running tests internal to a user-mode module\n"
599 " kmstubfile Optional stub for running tests internal to a kernel-mode module\n"
600 " exestubfile Optional stub for running tests internal to a module in the build environment\n"
601 " stublistfile File with descriptions of stubs\n"
602 " stubsfile File with stubs to create\n"
603 " hooksfile File with hooks to create\n";
604
605 #define INPUT_BUFFER_SIZE 255
606
607 void
608 write_stubs_header(FILE * out)
609 {
610 fputs(STUBS_HEADER, out);
611 }
612
613 void
614 write_hooks_header(FILE * out)
615 {
616 fputs(HOOKS_HEADER, out);
617 }
618
619 void
620 write_hooks_footer(FILE *hooks_out, unsigned long nr_stubs)
621 {
622 fprintf(hooks_out, HOOKS_FOOTER, nr_stubs);
623 }
624
625 char *
626 get_undecorate_name(char *buf,
627 char *decoratedname)
628 {
629 int start = 0;
630 int end = 0;
631
632 while (start < strlen(decoratedname) && decoratedname[start] == '@')
633 {
634 start++;
635 }
636 strcpy(buf, &decoratedname[start]);
637 end = strlen(buf) - 1;
638 while (end > 0 && isdigit(buf[end]))
639 {
640 end--;
641 }
642 if (buf[end] == '@')
643 {
644 buf[end] = 0;
645 }
646 return buf;
647 }
648
649 void
650 write_stub(FILE *stubs_out, FILE *hooks_out, char *dllname,
651 char *decoratedname, unsigned int stub_index)
652 {
653 char buf[300];
654
655 fprintf(stubs_out, ".globl _%s\n", decoratedname);
656 fprintf(stubs_out, "_%s:\n", decoratedname);
657 fprintf(stubs_out, " pushl $%d\n", stub_index);
658 fprintf(stubs_out, " jmp passthrough\n");
659 fprintf(stubs_out, "\n");
660
661 fprintf(hooks_out, " {\"%s\", \"%s\", NULL, NULL},\n",
662 dllname, get_undecorate_name(buf, decoratedname));
663 }
664
665 void
666 create_stubs_and_hooks(
667 FILE *in,
668 FILE *stubs_out,
669 FILE *hooks_out)
670 {
671 char line[INPUT_BUFFER_SIZE];
672 char *s;
673 char *dllname;
674 char *decoratedname;
675 int stub_index;
676
677 write_stubs_header(stubs_out);
678
679 write_hooks_header(hooks_out);
680
681 /*
682 * Scan the database. The database is a text file; each
683 * line is a record, which contains data for one stub.
684 * Each record has two columns:
685 *
686 * DLLNAME (e.g. ntdll.dll)
687 * DECORATED NAME (e.g. NtCreateProcess@32, @InterlockedIncrement@4 or printf)
688 */
689 for (
690 /* First stub has index zero */
691 stub_index = 0;
692 /* Go on until EOF or read zero bytes */
693 ((!feof(in)) && (fgets(line, sizeof line, in) != NULL));
694 /* Next stub index */
695 stub_index++)
696 {
697 /*
698 * Remove, if present, the trailing CR.
699 * (os specific?)
700 */
701 if ((s = (char *) strchr(line,'\r')) != NULL)
702 {
703 *s = '\0';
704 }
705
706 /*
707 * Skip comments (#) and empty lines.
708 */
709 s = & line[0];
710 if ((*s) != '#' && (*s) != '\0')
711 {
712 /* Extract the DLL name */
713 dllname = (char *) strtok(s," \t");
714 /* Extract the decorated function name */
715 decoratedname = (char *) strtok(NULL," \t");
716 /* Extract the argument count */
717 write_stub(stubs_out, hooks_out, dllname, decoratedname, stub_index);
718 }
719 }
720
721 write_hooks_footer(hooks_out, stub_index + 1);
722 }
723
724 int run_stubs(int argc,
725 char **argv)
726 {
727 FILE *in;
728 FILE *stubs_out;
729 FILE *hooks_out;
730
731 in = fopen(argv[2], "rb");
732 if (in == NULL)
733 {
734 perror("Failed to open stub description input file");
735 return 1;
736 }
737
738 stubs_out = fopen(argv[3], "wb");
739 if (stubs_out == NULL)
740 {
741 perror("Failed to open stubs output file");
742 return 1;
743 }
744
745 hooks_out = fopen(argv[4], "wb");
746 if (hooks_out == NULL)
747 {
748 perror("Failed to open hooks output file");
749 return 1;
750 }
751
752 create_stubs_and_hooks(in, stubs_out, hooks_out);
753
754 fclose(stubs_out);
755 fclose(hooks_out);
756
757 return 0;
758 }
759
760 int run_registrations(int argc,
761 char **argv)
762 {
763 char buf[MAX_PATH];
764 int i;
765
766 if (argc < 4)
767 {
768 puts(HELP);
769 return 1;
770 }
771
772 strcpy(buf, convert_path(argv[1]));
773 if (buf[strlen(buf)] != DIR_SEPARATOR_CHAR)
774 {
775 int i = strlen(buf);
776 buf[strlen(buf)] = DIR_SEPARATOR_CHAR;
777 buf[i + 1] = 0;
778 }
779 path = buf;
780 if (path[0] == 0)
781 {
782 printf("Missing path\n");
783 return 1;
784 }
785
786 file = convert_path(argv[2]);
787 if (file[0] == 0)
788 {
789 printf("Missing file\n");
790 return 1;
791 }
792
793 makefile = convert_path(argv[3]);
794 if (makefile[0] == 0)
795 {
796 printf("Missing makefile\n");
797 return 1;
798 }
799
800 umstubfile = NULL;
801 kmstubfile = NULL;
802 exestubfile = NULL;
803 for (i = 4; i < argc; i++)
804 {
805 if (argv[i][0] == '-')
806 {
807 if (argv[i][1] == 'u')
808 {
809 umstubfile = convert_path(argv[++i]);
810 if (umstubfile[0] == 0)
811 {
812 printf("Missing umstubfile\n");
813 return 1;
814 }
815 }
816 else if (argv[i][1] == 'k')
817 {
818 kmstubfile = convert_path(argv[++i]);
819 if (kmstubfile[0] == 0)
820 {
821 printf("Missing kmstubfile\n");
822 return 1;
823 }
824 }
825 else if (argv[i][1] == 'e')
826 {
827 exestubfile = convert_path(argv[++i]);
828 if (exestubfile[0] == 0)
829 {
830 printf("Missing exestubfile\n");
831 return 1;
832 }
833 }
834 else
835 {
836 printf("Unknown switch\n");
837 return 1;
838 }
839 }
840 }
841
842
843 /* Registration file */
844 out = fopen(file, "wb");
845 if (out == NULL)
846 {
847 perror("Cannot create output file");
848 return 1;
849 }
850
851 write_line("/* This file is autogenerated. */");
852 write_line("");
853 write_line("typedef int (*TestRoutine)(int Command, char *Buffer);");
854 write_line("");
855
856 make_file_list(0);
857
858 write_line("");
859 write_line("extern void AddTest(TestRoutine Routine);");
860 write_line("");
861 write_line("void RegisterTests()");
862 write_line("{");
863
864 make_file_list(1);
865
866 write_line("}");
867
868 fclose(out);
869
870
871 /* Makefile */
872 out = fopen(makefile, "wb");
873 if (out == NULL)
874 {
875 perror("Cannot create output makefile");
876 return 1;
877 }
878
879 write_line("# This file is autogenerated.");
880 write_line("");
881 write_line("TESTS = \\");
882
883 make_file_list(2);
884
885 write_line("");
886
887 fclose(out);
888
889 /* User-mode stubfile */
890 if (umstubfile != NULL)
891 {
892 if (write_file_if_changed(umstubfile, UMSTUB) != 0)
893 {
894 perror("Cannot create output user-mode stubfile");
895 return 1;
896 }
897 }
898
899 /* Kernel-mode stubfile */
900 if (kmstubfile != NULL)
901 {
902 if (write_file_if_changed(kmstubfile, KMSTUB) != 0)
903 {
904 perror("Cannot create output kernel-mode stubfile");
905 return 1;
906 }
907 }
908
909 /* Executable stubfile */
910 if (exestubfile != NULL)
911 {
912 if (write_file_if_changed(exestubfile, EXESTUB) != 0)
913 {
914 perror("Cannot create output executable stubfile");
915 return 1;
916 }
917 }
918
919 return 0;
920 }
921
922 int main(int argc,
923 char **argv)
924 {
925 if (argc < 2)
926 {
927 puts(HELP);
928 return 1;
929 }
930
931 if (strlen(argv[1]) > 1 && argv[1][0] == '-' && argv[1][1] == 's')
932 {
933 return run_stubs(argc, argv);
934 }
935 else
936 {
937 return run_registrations(argc, argv);
938 }
939 }