2 * Generate a file with test registrations from a list
3 * of files in a directory.
4 * Casper S. Hornstrup <chorns@users.sourceforge.net>
20 #include <sys/types.h>
28 #define DIR_SEPARATOR_CHAR '/'
29 #define DIR_SEPARATOR_STRING "/"
31 #define DIR_SEPARATOR_CHAR '\\'
32 #define DIR_SEPARATOR_STRING "\\"
38 static char *makefile
;
39 static char *umstubfile
;
40 static char *kmstubfile
;
41 static char *exestubfile
;
44 convert_path(char* origpath
)
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
);
55 while (newpath
[i
] != 0)
58 if (newpath
[i
] == '\\')
64 if (newpath
[i
] == '/')
76 write_line(char *line
)
81 memset(buf
, 0, sizeof(buf
));
83 /* Terminate the line */
84 buf
[strlen(buf
)] = '\r';
85 buf
[strlen(buf
)] = '\n';
87 n_out
= fwrite(&buf
[0], 1, strlen(buf
), out
);
91 change_extension(char *filenamebuffer
,
97 if (newextension
== NULL
)
99 strcpy(filenamebuffer
, filename
);
103 ptr
= strrchr(filename
, '.');
106 strncpy(filenamebuffer
, filename
, ptr
- filename
);
107 filenamebuffer
[ptr
- filename
] = 0;
108 strcat(filenamebuffer
, newextension
);
112 strcpy(filenamebuffer
, filename
);
113 strcat(filenamebuffer
, newextension
);
118 get_test_name(char *filename
,
123 strcpy(testname
, filename
);
125 i
= strlen(testname
);
126 while (i
> 0 && testname
[i
] != '.')
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')))
143 while (i
< strlen(testname
))
145 testname
[i
] = tolower(testname
[i
]);
146 if (!((testname
[i
] >= 'a' && testname
[i
] <= 'z') ||
147 (testname
[i
] >= '0' && testname
[i
] <= '9')))
156 * filename - name of file to make registrations for
157 * type - type of registration (0 = prototype, 1 = call, 2 = makefile)
160 register_test(char *filename
,
167 char filenamebuffer
[MAX_PATH
];
170 i
= strlen(filename
);
171 while (i
> 0 && filename
[i
] != '.')
177 memset(ext
, 0, sizeof(ext
));
178 strncpy(&ext
[0], &filename
[i
], strlen(&filename
[i
]));
180 if ((strncmp(ext
, ".c", 2) != 0) && (strncmp(ext
, ".C", 2) != 0))
190 memset(testname
, 0, sizeof(testname
));
191 get_test_name(filename
, testname
);
195 sprintf(regtest
, "extern int %sTest(int Command, char *Buffer);", testname
);
200 sprintf(call
, "%sTest", testname
);
201 sprintf(regtest
, " AddTest((TestRoutine)%s);", call
);
206 change_extension(filenamebuffer
, filename
, ".o");
207 sprintf(regtest
, "%s \\", filenamebuffer
);
217 make_file_list (int type
)
219 struct _finddata_t f
;
221 char searchbuf
[MAX_PATH
];
223 strcpy(searchbuf
, path
);
224 strcat(searchbuf
, "*.*");
225 findhandle
=_findfirst(searchbuf
, &f
);
226 if (findhandle
!= -1)
230 if (f
.attrib
& _A_SUBDIR
)
232 /* Skip subdirectories */
236 register_test(f
.name
, type
);
238 while (_findnext(findhandle
, &f
) == 0);
239 _findclose(findhandle
);
247 make_file_list (int type
)
250 struct dirent
*entry
;
255 dirp
= opendir(path
);
258 while ((entry
= readdir(dirp
)) != NULL
)
260 if (strcmp(entry
->d_name
, ".") == 0 || strcmp(entry
->d_name
, "..") == 0)
261 continue; // skip self and parent
263 if (entry
->d_type
== DT_REG
) // normal file
265 // Check for an absolute path
266 if (path
[0] == DIR_SEPARATOR_CHAR
)
269 strcat(buf
, DIR_SEPARATOR_STRING
);
270 strcat(buf
, entry
->d_name
);
274 getcwd(buf
, sizeof(buf
));
275 strcat(buf
, DIR_SEPARATOR_STRING
);
277 strcat(buf
, entry
->d_name
);
280 if (stat(buf
, &stbuf
) == -1)
282 printf("Can't access '%s' (%s)\n", buf
, strerror(errno
));
286 if (S_ISDIR(stbuf
.st_mode
))
288 /* Skip subdirectories */
292 register_test(entry
->d_name
, type
);
299 printf("Can't open %s\n", path
);
305 dirp
= opendir(path
);
308 while ((entry
= readdir(dirp
)) != NULL
)
310 if (strcmp(entry
->d_name
, ".") == 0 || strcmp(entry
->d_name
, "..") == 0)
311 continue; // skip self and parent
313 // Check for an absolute path
314 if (path
[0] == DIR_SEPARATOR_CHAR
)
317 strcat(buf
, DIR_SEPARATOR_STRING
);
318 strcat(buf
, entry
->d_name
);
322 getcwd(buf
, sizeof(buf
));
323 strcat(buf
, DIR_SEPARATOR_STRING
);
325 strcat(buf
, entry
->d_name
);
328 if (stat(buf
, &stbuf
) == -1)
330 printf("Can't access '%s' (%s)\n", buf
, strerror(errno
));
334 if (S_ISDIR(stbuf
.st_mode
))
336 /* Skip subdirectories */
340 register_test(entry
->d_name
, type
);
346 printf("Can't open %s\n", path
);
356 is_file_changed(char *filename
,
364 file
= fopen(filename
, "rb");
370 fseek(file
, 0, SEEK_END
);
372 fseek(file
, 0, SEEK_SET
);
378 filecontent
= malloc(size
);
379 if (filecontent
== NULL
)
385 n
= fread(filecontent
, 1, size
, file
);
387 if (n
!= strlen(content
))
394 if (strcmp(content
, filecontent
) != 0)
409 write_file_if_changed(char *filename
,
415 if (is_file_changed(filename
, content
) == 0)
420 file
= fopen(filename
, "wb");
426 n
= fwrite(content
, 1, strlen(content
), file
);
433 static char KMSTUB
[] =
434 "/* This file is autogenerated. */\n"
436 "#include <roskrnl.h>\n"
437 "#include <../kmregtests/kmregtests.h>\n"
439 "typedef int (*TestRoutine)(int Command, char *Buffer);\n"
441 "extern void RegisterTests();\n"
443 "static PDEVICE_OBJECT KMRegTestsDeviceObject = NULL;\n"
444 "static PFILE_OBJECT KMRegTestsFileObject = NULL;\n"
446 "void AddTest(TestRoutine Routine)\n"
448 " UNICODE_STRING DriverName;\n"
449 " IO_STATUS_BLOCK IoStatus;\n"
450 " NTSTATUS Status;\n"
454 " if (KMRegTestsDeviceObject == NULL)\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"
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"
467 "void PrepareTests()\n"
469 " RegisterTests();\n"
472 static char UMSTUB
[] =
473 "/* This file is autogenerated. */\n"
475 "#include <windows.h>\n"
476 "#define NTOS_MODE_USER\n"
477 "#include <ntos.h>\n"
478 "#include \"regtests.h\"\n"
481 "AllocateMemory(ULONG Size)\n"
483 " return (PVOID) RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);\n"
487 "FreeMemory(PVOID Base)\n"
489 " RtlFreeHeap(RtlGetProcessHeap(), 0, Base);\n"
492 "/* This function will be called several times */\n"
493 "void PrepareTests()\n"
495 " static int testsRegistered = 0;\n"
496 " if (testsRegistered == 0)\n"
499 " hEvent = OpenEventW(\n"
500 " EVENT_ALL_ACCESS,\n"
502 " L\"WinRegTests\");\n"
503 " if (hEvent != NULL)\n"
505 " SetEvent(hEvent);\n"
506 " CloseHandle(hEvent);\n"
507 " testsRegistered = 1;\n"
508 " InitializeTests();\n"
509 " RegisterTests();\n"
510 " PerformTests(NULL, NULL);\n"
515 static char EXESTUB
[] =
516 "/* This file is autogenerated. */\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"
525 "AllocateMemory(ULONG Size)\n"
527 " return (PVOID) RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);\n"
531 "FreeMemory(PVOID Base)\n"
533 " RtlFreeHeap(RtlGetProcessHeap(), 0, Base);\n"
537 "ConsoleWrite(char *Buffer)\n"
543 "DbgPrint(PCH Format, ...)\n"
548 "int __stdcall WinMain(HINSTANCE hInstance,\n"
549 " HINSTANCE hPrevInstance,\n"
550 " LPSTR lpCmdLine,\n"
553 " InitializeTests();\n"
554 " RegisterTests();\n"
555 " PerformTests(ConsoleWrite, NULL);\n"
559 static char STUBS_HEADER
[] =
560 "/* This file is autogenerated. */\n"
562 " call _FrameworkGetHook@4\n"
567 " /* This will most likely corrupt the stack */\n"
571 static char HOOKS_HEADER
[] =
572 "/* This file is autogenerated. */\n"
573 "#include <windows.h>\n"
574 "#include \"regtests.h\"\n"
576 "API_DESCRIPTION ExternalDependencies[] =\n"
579 static char HOOKS_FOOTER
[] =
582 "#define ExternalDependencyCount %d\n"
583 "ULONG MaxExternalDependency = ExternalDependencyCount - 1;\n"
586 "FrameworkGetHook(ULONG index)\n"
588 " return FrameworkGetHookInternal(index);\n"
592 "REGTESTS path file makefile [-u umstubfile] [-k kmstubfile] [-e exestubfile]\n"
593 "REGTESTS -s stublistfile stubsfile hooksfile\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";
605 #define INPUT_BUFFER_SIZE 255
608 write_stubs_header(FILE * out
)
610 fputs(STUBS_HEADER
, out
);
614 write_hooks_header(FILE * out
)
616 fputs(HOOKS_HEADER
, out
);
620 write_hooks_footer(FILE *hooks_out
, unsigned long nr_stubs
)
622 fprintf(hooks_out
, HOOKS_FOOTER
, nr_stubs
);
626 get_undecorate_name(char *buf
,
632 while (start
< strlen(decoratedname
) && decoratedname
[start
] == '@')
636 strcpy(buf
, &decoratedname
[start
]);
637 end
= strlen(buf
) - 1;
638 while (end
> 0 && isdigit(buf
[end
]))
650 write_stub(FILE *stubs_out
, FILE *hooks_out
, char *dllname
,
651 char *decoratedname
, unsigned int stub_index
)
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");
661 fprintf(hooks_out
, " {\"%s\", \"%s\", NULL, NULL},\n",
662 dllname
, get_undecorate_name(buf
, decoratedname
));
666 create_stubs_and_hooks(
671 char line
[INPUT_BUFFER_SIZE
];
677 write_stubs_header(stubs_out
);
679 write_hooks_header(hooks_out
);
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:
686 * DLLNAME (e.g. ntdll.dll)
687 * DECORATED NAME (e.g. NtCreateProcess@32, @InterlockedIncrement@4 or printf)
690 /* First stub has index zero */
692 /* Go on until EOF or read zero bytes */
693 ((!feof(in
)) && (fgets(line
, sizeof line
, in
) != NULL
));
694 /* Next stub index */
698 * Remove, if present, the trailing CR.
701 if ((s
= (char *) strchr(line
,'\r')) != NULL
)
707 * Skip comments (#) and empty lines.
710 if ((*s
) != '#' && (*s
) != '\0')
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
);
721 write_hooks_footer(hooks_out
, stub_index
+ 1);
724 int run_stubs(int argc
,
731 in
= fopen(argv
[2], "rb");
734 perror("Failed to open stub description input file");
738 stubs_out
= fopen(argv
[3], "wb");
739 if (stubs_out
== NULL
)
741 perror("Failed to open stubs output file");
745 hooks_out
= fopen(argv
[4], "wb");
746 if (hooks_out
== NULL
)
748 perror("Failed to open hooks output file");
752 create_stubs_and_hooks(in
, stubs_out
, hooks_out
);
760 int run_registrations(int argc
,
772 strcpy(buf
, convert_path(argv
[1]));
773 if (buf
[strlen(buf
)] != DIR_SEPARATOR_CHAR
)
776 buf
[strlen(buf
)] = DIR_SEPARATOR_CHAR
;
782 printf("Missing path\n");
786 file
= convert_path(argv
[2]);
789 printf("Missing file\n");
793 makefile
= convert_path(argv
[3]);
794 if (makefile
[0] == 0)
796 printf("Missing makefile\n");
803 for (i
= 4; i
< argc
; i
++)
805 if (argv
[i
][0] == '-')
807 if (argv
[i
][1] == 'u')
809 umstubfile
= convert_path(argv
[++i
]);
810 if (umstubfile
[0] == 0)
812 printf("Missing umstubfile\n");
816 else if (argv
[i
][1] == 'k')
818 kmstubfile
= convert_path(argv
[++i
]);
819 if (kmstubfile
[0] == 0)
821 printf("Missing kmstubfile\n");
825 else if (argv
[i
][1] == 'e')
827 exestubfile
= convert_path(argv
[++i
]);
828 if (exestubfile
[0] == 0)
830 printf("Missing exestubfile\n");
836 printf("Unknown switch\n");
843 /* Registration file */
844 out
= fopen(file
, "wb");
847 perror("Cannot create output file");
851 write_line("/* This file is autogenerated. */");
853 write_line("typedef int (*TestRoutine)(int Command, char *Buffer);");
859 write_line("extern void AddTest(TestRoutine Routine);");
861 write_line("void RegisterTests()");
872 out
= fopen(makefile
, "wb");
875 perror("Cannot create output makefile");
879 write_line("# This file is autogenerated.");
881 write_line("TESTS = \\");
889 /* User-mode stubfile */
890 if (umstubfile
!= NULL
)
892 if (write_file_if_changed(umstubfile
, UMSTUB
) != 0)
894 perror("Cannot create output user-mode stubfile");
899 /* Kernel-mode stubfile */
900 if (kmstubfile
!= NULL
)
902 if (write_file_if_changed(kmstubfile
, KMSTUB
) != 0)
904 perror("Cannot create output kernel-mode stubfile");
909 /* Executable stubfile */
910 if (exestubfile
!= NULL
)
912 if (write_file_if_changed(exestubfile
, EXESTUB
) != 0)
914 perror("Cannot create output executable stubfile");
931 if (strlen(argv
[1]) > 1 && argv
[1][0] == '-' && argv
[1][1] == 's')
933 return run_stubs(argc
, argv
);
937 return run_registrations(argc
, argv
);