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 *exestubfile
;
42 convert_path(char* origpath
)
47 /* for no good reason, i'm having trouble getting gcc to link strdup */
48 //newpath = strdup(origpath);
49 newpath
= malloc(strlen(origpath
)+1);
50 strcpy(newpath
, origpath
);
53 while (newpath
[i
] != 0)
56 if (newpath
[i
] == '\\')
62 if (newpath
[i
] == '/')
74 write_line(char *line
)
79 memset(buf
, 0, sizeof(buf
));
81 /* Terminate the line */
82 buf
[strlen(buf
)] = '\r';
83 buf
[strlen(buf
)] = '\n';
85 n_out
= fwrite(&buf
[0], 1, strlen(buf
), out
);
89 change_extension(char *filenamebuffer
,
95 if (newextension
== NULL
)
97 strcpy(filenamebuffer
, filename
);
101 ptr
= strrchr(filename
, '.');
104 strncpy(filenamebuffer
, filename
, ptr
- filename
);
105 filenamebuffer
[ptr
- filename
] = 0;
106 strcat(filenamebuffer
, newextension
);
110 strcpy(filenamebuffer
, filename
);
111 strcat(filenamebuffer
, newextension
);
116 get_test_name(char *filename
,
121 strcpy(testname
, filename
);
123 i
= strlen(testname
);
124 while (i
> 0 && testname
[i
] != '.')
133 /* Make a capital first letter and make all other letters lower case */
134 testname
[0] = toupper(testname
[0]);
135 if (!((testname
[0] >= 'A' && testname
[0] <= 'Z') ||
136 (testname
[0] >= '0' && testname
[0] <= '9')))
141 while (i
< strlen(testname
))
143 testname
[i
] = tolower(testname
[i
]);
144 if (!((testname
[i
] >= 'a' && testname
[i
] <= 'z') ||
145 (testname
[i
] >= '0' && testname
[i
] <= '9')))
154 * filename - name of file to make registrations for
155 * type - type of registration (0 = prototype, 1 = call, 2 = makefile)
158 register_test(char *filename
,
165 char filenamebuffer
[MAX_PATH
];
168 i
= strlen(filename
);
169 while (i
> 0 && filename
[i
] != '.')
175 memset(ext
, 0, sizeof(ext
));
176 strncpy(&ext
[0], &filename
[i
], strlen(&filename
[i
]));
178 if ((strncmp(ext
, ".c", 2) != 0) && (strncmp(ext
, ".C", 2) != 0))
188 memset(testname
, 0, sizeof(testname
));
189 get_test_name(filename
, testname
);
193 sprintf(regtest
, "extern int %sTest(int Command, char *Buffer);", testname
);
198 sprintf(call
, "%sTest", testname
);
199 sprintf(regtest
, " AddTest((TestRoutine)%s);", call
);
204 change_extension(filenamebuffer
, filename
, ".o");
205 sprintf(regtest
, "%s \\", filenamebuffer
);
215 make_file_list (int type
)
217 struct _finddata_t f
;
219 char searchbuf
[MAX_PATH
];
221 strcpy(searchbuf
, path
);
222 strcat(searchbuf
, "*.*");
223 findhandle
=_findfirst(searchbuf
, &f
);
224 if (findhandle
!= -1)
228 if (f
.attrib
& _A_SUBDIR
)
230 /* Skip subdirectories */
234 register_test(f
.name
, type
);
236 while (_findnext(findhandle
, &f
) == 0);
237 _findclose(findhandle
);
245 make_file_list (int type
)
248 struct dirent
*entry
;
253 dirp
= opendir(path
);
256 while ((entry
= readdir(dirp
)) != NULL
)
258 if (strcmp(entry
->d_name
, ".") == 0 || strcmp(entry
->d_name
, "..") == 0)
259 continue; // skip self and parent
261 if (entry
->d_type
== DT_REG
) // normal file
263 // Check for an absolute path
264 if (path
[0] == DIR_SEPARATOR_CHAR
)
267 strcat(buf
, DIR_SEPARATOR_STRING
);
268 strcat(buf
, entry
->d_name
);
272 getcwd(buf
, sizeof(buf
));
273 strcat(buf
, DIR_SEPARATOR_STRING
);
275 strcat(buf
, entry
->d_name
);
278 if (stat(buf
, &stbuf
) == -1)
280 printf("Can't access '%s' (%s)\n", buf
, strerror(errno
));
284 if (S_ISDIR(stbuf
.st_mode
))
286 /* Skip subdirectories */
290 register_test(entry
->d_name
, type
);
297 printf("Can't open %s\n", path
);
303 dirp
= opendir(path
);
306 while ((entry
= readdir(dirp
)) != NULL
)
308 if (strcmp(entry
->d_name
, ".") == 0 || strcmp(entry
->d_name
, "..") == 0)
309 continue; // skip self and parent
311 // Check for an absolute path
312 if (path
[0] == DIR_SEPARATOR_CHAR
)
315 strcat(buf
, DIR_SEPARATOR_STRING
);
316 strcat(buf
, entry
->d_name
);
320 getcwd(buf
, sizeof(buf
));
321 strcat(buf
, DIR_SEPARATOR_STRING
);
323 strcat(buf
, entry
->d_name
);
326 if (stat(buf
, &stbuf
) == -1)
328 printf("Can't access '%s' (%s)\n", buf
, strerror(errno
));
332 if (S_ISDIR(stbuf
.st_mode
))
334 /* Skip subdirectories */
338 register_test(entry
->d_name
, type
);
344 printf("Can't open %s\n", path
);
354 is_file_changed(char *filename
,
362 file
= fopen(filename
, "rb");
368 fseek(file
, 0, SEEK_END
);
370 fseek(file
, 0, SEEK_SET
);
376 filecontent
= malloc(size
);
377 if (filecontent
== NULL
)
383 n
= fread(filecontent
, 1, size
, file
);
385 if (n
!= strlen(content
))
392 if (strcmp(content
, filecontent
) != 0)
407 write_file_if_changed(char *filename
,
413 if (is_file_changed(filename
, content
) == 0)
418 file
= fopen(filename
, "wb");
424 n
= fwrite(content
, 1, strlen(content
), file
);
431 static char EXESTUB
[] =
432 "/* This file is autogenerated. */\n"
434 "#include \"regtests.h\"\n"
436 "#if defined(__USE_W32API)\n"
437 " #define HANDLE PVOID\n"
438 " #define NTSTATUS ULONG\n"
440 " NTSTATUS STDCALL\n"
441 " NtTerminateProcess(HANDLE ProcessHandle,\n"
442 " NTSTATUS ExitStatus);\n"
444 " #define NtCurrentProcess() ((HANDLE) 0xFFFFFFFF)\n"
448 "ConsoleWrite(char *Buffer)\n"
454 "mainCRTStartup(HANDLE hInstance,\n"
455 " HANDLE hPrevInstance,\n"
456 " LPSTR lpszCmdParam,\n"
459 " InitializeTests();\n"
460 " RegisterTests();\n"
462 " PerformTests(ConsoleWrite, NULL);\n"
463 " NtTerminateProcess (NtCurrentProcess(), 0);\n"
467 static char STUBS_HEADER
[] =
468 "/* This file is autogenerated. */\n"
470 " call _FrameworkGetHook@4\n"
475 " /* This will most likely corrupt the stack */\n"
479 static char HOOKS_HEADER
[] =
480 "/* This file is autogenerated. */\n"
481 "#include <windows.h>\n"
482 "#include \"regtests.h\"\n"
484 "API_DESCRIPTION ExternalDependencies[] =\n"
487 static char HOOKS_FOOTER
[] =
490 "#define ExternalDependencyCount %d\n"
491 "ULONG MaxExternalDependency = ExternalDependencyCount - 1;\n";
494 "REGTESTS path file makefile [-e exestubfile]\n"
495 "REGTESTS -s stublistfile stubsfile hooksfile\n"
497 " path Path to files\n"
498 " file Registration file to create\n"
499 " makefile Makefile to create\n"
500 " exestubfile Optional stub for running tests in the build environment\n"
501 " stublistfile File with descriptions of stubs\n"
502 " stubsfile File with stubs to create\n"
503 " hooksfile File with hooks to create\n";
505 #define INPUT_BUFFER_SIZE 255
508 write_stubs_header(FILE * out
)
510 fputs(STUBS_HEADER
, out
);
514 write_hooks_header(FILE * out
)
516 fputs(HOOKS_HEADER
, out
);
520 write_hooks_footer(FILE *hooks_out
, unsigned long nr_stubs
)
522 fprintf(hooks_out
, HOOKS_FOOTER
, nr_stubs
);
526 get_undecorated_name(char *buf
,
532 while (start
< strlen(decoratedname
) && decoratedname
[start
] == '@')
536 strcpy(buf
, &decoratedname
[start
]);
537 end
= strlen(buf
) - 1;
538 while (end
> 0 && isdigit(buf
[end
]))
550 get_forwarded_export(char *forwardedexport
)
554 if (forwardedexport
== NULL
)
560 sprintf(buf
, "\"%s\"", forwardedexport
);
566 write_stub(FILE *stubs_out
, FILE *hooks_out
, char *dllname
,
567 char *decoratedname_and_forward
, unsigned int stub_index
)
571 char *decoratedname
= NULL
;
572 char *forwardedexport
= NULL
;
574 p
= strtok(decoratedname_and_forward
, "=");
579 p
= strtok(NULL
, "=");
584 decoratedname
= decoratedname_and_forward
;
585 forwardedexport
= decoratedname_and_forward
;
588 fprintf(stubs_out
, ".globl _%s\n", decoratedname
);
589 fprintf(stubs_out
, "_%s:\n", decoratedname
);
590 fprintf(stubs_out
, " pushl $%d\n", stub_index
);
591 fprintf(stubs_out
, " jmp passthrough\n");
592 fprintf(stubs_out
, "\n");
593 forwardedexport
= get_forwarded_export(forwardedexport
);
594 fprintf(hooks_out
, " {\"%s\", \"%s\", %s, NULL, NULL},\n",
596 get_undecorated_name(buf
, decoratedname
),
598 free(forwardedexport
);
602 create_stubs_and_hooks(
607 char line
[INPUT_BUFFER_SIZE
];
610 char *decoratedname_and_forward
;
613 write_stubs_header(stubs_out
);
615 write_hooks_header(hooks_out
);
618 * Scan the database. The database is a text file; each
619 * line is a record, which contains data for one stub.
620 * Each record has two columns:
622 * DLLNAME (e.g. ntdll.dll)
623 * DECORATED NAME (e.g. NtCreateProcess@32, @InterlockedIncrement@4 or printf)
625 stub_index
= 0; /* First stub has index zero */
629 /* Go on until EOF or read zero bytes */
630 ((!feof(in
)) && (fgets(line
, sizeof line
, in
) != NULL
));
631 /* Next stub index */
635 * Remove, if present, the trailing LF.
637 if ((s
= (char *) strchr(line
,'\n')) != NULL
)
643 * Remove, if present, the trailing CR.
645 if ((s
= (char *) strchr(line
,'\r')) != NULL
)
651 * Skip comments (#) and empty lines.
654 if ((*s
) != '#' && (*s
) != '\0')
656 /* Extract the DLL name */
657 dllname
= (char *) strtok(s
, " \t");
658 if (dllname
!= NULL
&& strlen(dllname
) > 0)
661 * Extract the decorated function name and possibly forwarded export.
663 * decoratedname=forwardedexport (no DLL name)
665 decoratedname_and_forward
= (char *) strtok(NULL
, " \t");
666 /* Extract the argument count */
667 write_stub(stubs_out
, hooks_out
, dllname
, decoratedname_and_forward
, stub_index
);
673 write_hooks_footer(hooks_out
, stub_index
);
676 int run_stubs(int argc
,
683 in
= fopen(argv
[2], "rb");
686 perror("Failed to open stub description input file");
690 stubs_out
= fopen(argv
[3], "wb");
691 if (stubs_out
== NULL
)
693 perror("Failed to open stubs output file");
697 hooks_out
= fopen(argv
[4], "wb");
698 if (hooks_out
== NULL
)
700 perror("Failed to open hooks output file");
704 create_stubs_and_hooks(in
, stubs_out
, hooks_out
);
712 int run_registrations(int argc
,
724 strcpy(buf
, convert_path(argv
[1]));
725 if (buf
[strlen(buf
)] != DIR_SEPARATOR_CHAR
)
728 buf
[strlen(buf
)] = DIR_SEPARATOR_CHAR
;
734 printf("Missing path\n");
738 file
= convert_path(argv
[2]);
741 printf("Missing file\n");
745 makefile
= convert_path(argv
[3]);
746 if (makefile
[0] == 0)
748 printf("Missing makefile\n");
753 for (i
= 4; i
< argc
; i
++)
755 if (argv
[i
][0] == '-')
757 if (argv
[i
][1] == 'e')
759 exestubfile
= convert_path(argv
[++i
]);
760 if (exestubfile
[0] == 0)
762 printf("Missing exestubfile\n");
768 printf("Unknown switch -%c\n", argv
[i
][1]);
775 /* Registration file */
776 out
= fopen(file
, "wb");
779 perror("Cannot create output file");
783 write_line("/* This file is autogenerated. */");
785 write_line("typedef int (*TestRoutine)(int Command, char *Buffer);");
791 write_line("extern void AddTest(TestRoutine Routine);");
793 write_line("void RegisterTests()");
804 out
= fopen(makefile
, "wb");
807 perror("Cannot create output makefile");
811 write_line("# This file is autogenerated.");
813 write_line("TESTS = \\");
821 /* Executable stubfile */
822 if (exestubfile
!= NULL
)
824 if (write_file_if_changed(exestubfile
, EXESTUB
) != 0)
826 perror("Cannot create output executable stubfile");
843 if (strlen(argv
[1]) > 1 && argv
[1][0] == '-' && argv
[1][1] == 's')
845 return run_stubs(argc
, argv
);
849 return run_registrations(argc
, argv
);