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 (strcasecmp(ext
, ".c") != 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 <windows.h>\n"
435 "#include \"regtests.h\"\n"
438 "ConsoleWrite(char *Buffer)\n"
444 "mainCRTStartup(HANDLE hInstance,\n"
445 " HANDLE hPrevInstance,\n"
446 " LPSTR lpszCmdParam,\n"
449 " InitializeTests();\n"
450 " RegisterTests();\n"
452 " PerformTests(ConsoleWrite, NULL);\n"
453 " _ExitProcess(0);\n"
457 static char STUBS_HEADER
[] =
458 "/* This file is autogenerated. */\n"
460 " call _FrameworkGetHook@4\n"
465 " /* This will most likely corrupt the stack */\n"
469 static char HOOKS_HEADER
[] =
470 "/* This file is autogenerated. */\n"
471 "#include <windows.h>\n"
472 "#include \"regtests.h\"\n"
474 "API_DESCRIPTION ExternalDependencies[] =\n"
477 static char HOOKS_FOOTER
[] =
480 "#define ExternalDependencyCount %d\n"
481 "ULONG MaxExternalDependency = ExternalDependencyCount - 1;\n";
484 "REGTESTS path file makefile [-e exestubfile]\n"
485 "REGTESTS -s stublistfile stubsfile hooksfile\n"
487 " path Path to files\n"
488 " file Registration file to create\n"
489 " makefile Makefile to create\n"
490 " exestubfile Optional stub for running tests in the build environment\n"
491 " stublistfile File with descriptions of stubs\n"
492 " stubsfile File with stubs to create\n"
493 " hooksfile File with hooks to create\n";
495 #define INPUT_BUFFER_SIZE 255
498 write_stubs_header(FILE * out
)
500 fputs(STUBS_HEADER
, out
);
504 write_hooks_header(FILE * out
)
506 fputs(HOOKS_HEADER
, out
);
510 write_hooks_footer(FILE *hooks_out
, unsigned long nr_stubs
)
512 fprintf(hooks_out
, HOOKS_FOOTER
, nr_stubs
);
516 get_symbolname(char *decoratedname
)
520 if (decoratedname
[0] == '@')
521 return strdup(decoratedname
);
523 strcat(buf
, decoratedname
);
528 get_undecorated_name(char *buf
,
534 while (start
< strlen(decoratedname
) && decoratedname
[start
] == '@')
538 strcpy(buf
, &decoratedname
[start
]);
539 end
= strlen(buf
) - 1;
540 while (end
> 0 && isdigit(buf
[end
]))
552 get_forwarded_export(char *forwardedexport
)
556 if (forwardedexport
== NULL
)
562 sprintf(buf
, "\"%s\"", forwardedexport
);
568 write_stub(FILE *stubs_out
, FILE *hooks_out
, char *dllname
,
569 char *decoratedname_and_forward
, unsigned int stub_index
)
573 char *decoratedname
= NULL
;
574 char *forwardedexport
= NULL
;
575 char *symbolname
= NULL
;
577 p
= strtok(decoratedname_and_forward
, "=");
582 p
= strtok(NULL
, "=");
587 decoratedname
= decoratedname_and_forward
;
588 forwardedexport
= decoratedname_and_forward
;
591 symbolname
= get_symbolname(decoratedname
);
592 fprintf(stubs_out
, ".globl %s\n", symbolname
);
593 fprintf(stubs_out
, "%s:\n", symbolname
);
595 fprintf(stubs_out
, " pushl $%d\n", stub_index
);
596 fprintf(stubs_out
, " jmp passthrough\n");
597 fprintf(stubs_out
, "\n");
598 forwardedexport
= get_forwarded_export(forwardedexport
);
599 fprintf(hooks_out
, " {\"%s\", \"%s\", %s, NULL, NULL},\n",
601 get_undecorated_name(buf
, decoratedname
),
603 free(forwardedexport
);
607 create_stubs_and_hooks(
612 char line
[INPUT_BUFFER_SIZE
];
615 char *decoratedname_and_forward
;
618 write_stubs_header(stubs_out
);
620 write_hooks_header(hooks_out
);
623 * Scan the database. The database is a text file; each
624 * line is a record, which contains data for one stub.
625 * Each record has two columns:
627 * DLLNAME (e.g. ntdll.dll)
628 * DECORATED NAME (e.g. NtCreateProcess@32, @InterlockedIncrement@4 or printf)
630 stub_index
= 0; /* First stub has index zero */
634 /* Go on until EOF or read zero bytes */
635 ((!feof(in
)) && (fgets(line
, sizeof line
, in
) != NULL
));
636 /* Next stub index */
640 * Ignore leading blanks
642 for( start
= line
; *start
&& isspace(*start
); start
++ );
645 * Strip comments, eols
647 for( s
= start
; *s
&& !strchr("#\n\r", *s
); s
++ );
652 * Remove trailing blanks. Backup off the char that ended our
655 for( s
--; s
> start
&& isspace(*s
); s
-- ) *s
= '\0';
662 /* Extract the DLL name */
663 dllname
= (char *) strtok(start
, " \t");
664 if (dllname
!= NULL
&& strlen(dllname
) > 0)
667 * Extract the decorated function name and possibly forwarded export.
669 * decoratedname=forwardedexport (no DLL name)
671 decoratedname_and_forward
= (char *) strtok(NULL
, " \t");
672 /* Extract the argument count */
674 /* Something went wrong finding the separator ...
675 * print an error and bail. */
676 if( !decoratedname_and_forward
) {
679 "Could not find separator between decorated "
680 "function name and dll name.\n"
681 "Format entries as <dllname> <import>\n"
682 "Legal comments start with #\n");
686 write_stub(stubs_out
, hooks_out
, dllname
, decoratedname_and_forward
, stub_index
);
692 write_hooks_footer(hooks_out
, stub_index
);
695 int run_stubs(int argc
,
702 in
= fopen(argv
[2], "rb");
705 perror("Failed to open stub description input file");
709 stubs_out
= fopen(argv
[3], "wb");
710 if (stubs_out
== NULL
)
712 perror("Failed to open stubs output file");
716 hooks_out
= fopen(argv
[4], "wb");
717 if (hooks_out
== NULL
)
719 perror("Failed to open hooks output file");
723 create_stubs_and_hooks(in
, stubs_out
, hooks_out
);
731 int run_registrations(int argc
,
743 strcpy(buf
, convert_path(argv
[1]));
744 if (buf
[strlen(buf
)] != DIR_SEPARATOR_CHAR
)
747 buf
[strlen(buf
)] = DIR_SEPARATOR_CHAR
;
753 printf("Missing path\n");
757 file
= convert_path(argv
[2]);
760 printf("Missing file\n");
764 makefile
= convert_path(argv
[3]);
765 if (makefile
[0] == 0)
767 printf("Missing makefile\n");
772 for (i
= 4; i
< argc
; i
++)
774 if (argv
[i
][0] == '-')
776 if (argv
[i
][1] == 'e')
778 exestubfile
= convert_path(argv
[++i
]);
779 if (exestubfile
[0] == 0)
781 printf("Missing exestubfile\n");
787 printf("Unknown switch -%c\n", argv
[i
][1]);
794 /* Registration file */
795 out
= fopen(file
, "wb");
798 perror("Cannot create output file");
802 write_line("/* This file is autogenerated. */");
804 write_line("typedef int (*TestRoutine)(int Command, char *Buffer);");
810 write_line("extern void AddTest(TestRoutine Routine);");
812 write_line("void RegisterTests()");
823 out
= fopen(makefile
, "wb");
826 perror("Cannot create output makefile");
830 write_line("# This file is autogenerated.");
832 write_line("TESTS = \\");
840 /* Executable stubfile */
841 if (exestubfile
!= NULL
)
843 if (write_file_if_changed(exestubfile
, EXESTUB
) != 0)
845 perror("Cannot create output executable stubfile");
862 if (strlen(argv
[1]) > 1 && argv
[1][0] == '-' && argv
[1][1] == 's')
864 return run_stubs(argc
, argv
);
868 return run_registrations(argc
, argv
);