825908d2934375d6b1608653322fa095fe9560a8
[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
13 #ifdef WIN32
14 #include <io.h>
15 #include <dos.h>
16 #else
17 #include <sys/io.h>
18 #include <errno.h>
19 #include <sys/types.h>
20 #include <dirent.h>
21 #include <unistd.h>
22 #endif
23 #include <ctype.h>
24 #ifndef WIN32
25 #ifndef MAX_PATH
26 #define MAX_PATH 260
27 #endif
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
42 char* convert_path(char* origpath)
43 {
44 char* newpath;
45 int i;
46
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);
51
52 i = 0;
53 while (newpath[i] != 0)
54 {
55 #ifndef WIN32
56 if (newpath[i] == '\\')
57 {
58 newpath[i] = '/';
59 }
60 #else
61 #ifdef WIN32
62 if (newpath[i] == '/')
63 {
64 newpath[i] = '\\';
65 }
66 #endif
67 #endif
68 i++;
69 }
70 return(newpath);
71 }
72
73 static void write_line(char *line)
74 {
75 int n_out;
76 char buf[200];
77
78 memset(buf, 0, sizeof(buf));
79 strcpy(buf, line);
80 /* Terminate the line */
81 buf[strlen(buf)] = '\r';
82 buf[strlen(buf)] = '\n';
83
84 n_out = fwrite(&buf[0], 1, strlen(buf), out);
85 }
86
87 static void change_extension(char *filenamebuffer, char *filename, char *newextension)
88 {
89 char *ptr;
90
91 if (newextension == NULL)
92 {
93 strcpy(filenamebuffer, filename);
94 return;
95 }
96
97 ptr = strrchr(filename, '.');
98 if (ptr != NULL)
99 {
100 strncpy(filenamebuffer, filename, ptr - filename);
101 filenamebuffer[ptr - filename] = 0;
102 strcat(filenamebuffer, newextension);
103 }
104 else
105 {
106 strcpy(filenamebuffer, filename);
107 strcat(filenamebuffer, newextension);
108 }
109 }
110
111 /*
112 * filename - name of file to make registrations for
113 * regtype - type of registration (0 = prototype, 1 = call, 2 = makefile)
114 */
115 void register_test(char *filename, int type)
116 {
117 char ext[100];
118 char testname[100];
119 char call[100];
120 char regtest[100];
121 char filenamebuffer[MAX_PATH];
122 int i, j;
123
124 strcpy(testname, filename);
125
126 i = strlen(testname);
127 while (i > 0 && testname[i] != '.')
128 {
129 i--;
130 }
131 if (i > 0)
132 {
133 memset(ext, 0, sizeof(ext));
134 strncpy(&ext[0], &testname[i], strlen(&testname[i]));
135
136 if ((strncmp(ext, ".c", 2) != 0) && (strncmp(ext, ".C", 2) != 0))
137 {
138 return;
139 }
140
141 testname[i] = 0;
142 }
143 else
144 {
145 return;
146 }
147
148 // Make a capital first letter and make all other letters lower case
149 testname[0] = toupper(testname[0]);
150 if (!((testname[0] >= 'A' && testname[0] <= 'Z') ||
151 (testname[0] >= '0' && testname[0] <= '9')))
152 {
153 testname[0] = '_';
154 }
155 j = 1;
156 while (j < strlen(testname))
157 {
158 testname[j] = tolower(testname[j]);
159 if (!((testname[j] >= 'a' && testname[j] <= 'z') ||
160 (testname[j] >= '0' && testname[j] <= '9')))
161 {
162 testname[j] = '_';
163 }
164 j++;
165 }
166
167 if (type == 0)
168 {
169 sprintf(regtest, "extern int %sTest(int Command, char *Buffer);", testname);
170 write_line(regtest);
171 }
172 else if (type == 1)
173 {
174 sprintf(call, "%sTest", testname);
175 sprintf(regtest, " AddTest((TestRoutine)%s);", call);
176 write_line(regtest);
177 }
178 else if (type == 2)
179 {
180 change_extension(filenamebuffer, filename, ".o");
181 sprintf(regtest, "%s \\", filenamebuffer);
182 write_line(regtest);
183 }
184 }
185
186 #ifdef WIN32
187
188 /* Win32 version */
189
190 static void
191 make_file_list (int type)
192 {
193 struct _finddata_t f;
194 int findhandle;
195 char searchbuf[MAX_PATH];
196
197 strcpy(searchbuf, path);
198 strcat(searchbuf, "*.*");
199 findhandle =_findfirst(searchbuf, &f);
200 if (findhandle != -1)
201 {
202 do
203 {
204 if (f.attrib & _A_SUBDIR)
205 {
206 /* Skip subdirectories */
207 continue;
208 }
209
210 register_test(f.name, type);
211 }
212 while (_findnext(findhandle, &f) == 0);
213 _findclose(findhandle);
214 }
215 }
216
217 #else
218
219 /* Linux version */
220 static void
221 make_file_list (int type)
222 {
223 DIR *dirp;
224 struct dirent *entry;
225 struct stat stbuf;
226 char buf[MAX_PATH];
227
228 #ifdef HAVE_D_TYPE
229 dirp = opendir(path);
230 if (dirp != NULL)
231 {
232 while ((entry = readdir(dirp)) != NULL)
233 {
234 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
235 continue; // skip self and parent
236
237 if (entry->d_type == DT_REG) // normal file
238 {
239 // Check for an absolute path
240 if (path[0] == DIR_SEPARATOR_CHAR)
241 {
242 strcpy(buf, path);
243 strcat(buf, DIR_SEPARATOR_STRING);
244 strcat(buf, entry->d_name);
245 }
246 else
247 {
248 getcwd(buf, sizeof(buf));
249 strcat(buf, DIR_SEPARATOR_STRING);
250 strcat(buf, path);
251 strcat(buf, entry->d_name);
252 }
253
254 if (stat(buf, &stbuf) == -1)
255 {
256 printf("Can't access '%s' (%s)\n", buf, strerror(errno));
257 return;
258 }
259
260 if (S_ISDIR(stbuf.st_mode))
261 {
262 /* Skip subdirectories */
263 continue;
264 }
265
266 register_test(entry->d_name, type);
267 }
268 }
269 closedir(dirp);
270 }
271 else
272 {
273 printf("Can't open %s\n", path);
274 return;
275 }
276
277 #else
278
279 dirp = opendir(path);
280 if (dirp != NULL)
281 {
282 while ((entry = readdir(dirp)) != NULL)
283 {
284 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
285 continue; // skip self and parent
286
287 // Check for an absolute path
288 if (path[0] == DIR_SEPARATOR_CHAR)
289 {
290 strcpy(buf, path);
291 strcat(buf, DIR_SEPARATOR_STRING);
292 strcat(buf, entry->d_name);
293 }
294 else
295 {
296 getcwd(buf, sizeof(buf));
297 strcat(buf, DIR_SEPARATOR_STRING);
298 strcat(buf, path);
299 strcat(buf, entry->d_name);
300 }
301
302 if (stat(buf, &stbuf) == -1)
303 {
304 printf("Can't access '%s' (%s)\n", buf, strerror(errno));
305 return;
306 }
307
308 if (S_ISDIR(stbuf.st_mode))
309 {
310 /* Skip subdirectories */
311 continue;
312 }
313
314 register_test(entry->d_name, type);
315 }
316 closedir(dirp);
317 }
318 else
319 {
320 printf("Can't open %s\n", path);
321 return;
322 }
323
324 #endif
325 }
326
327 #endif
328
329 static int
330 is_file_changed(char *filename, char *content)
331 {
332 FILE *file;
333 int size;
334 int n;
335 char *filecontent;
336
337 file = fopen(filename, "rb");
338 if (file == NULL)
339 {
340 return 1;
341 }
342
343 fseek(file, 0, SEEK_END);
344 size = ftell(file);
345 fseek(file, 0, SEEK_SET);
346 if (size <= 0)
347 {
348 fclose(file);
349 return 1;
350 }
351 filecontent = malloc(size);
352 if (filecontent == NULL)
353 {
354 fclose(file);
355 return 1;
356 }
357
358 n = fread(filecontent, 1, size, file);
359
360 if (n != strlen(content))
361 {
362 free(filecontent);
363 fclose(file);
364 return 1;
365 }
366
367 if (strcmp(content, filecontent) != 0)
368 {
369 free(filecontent);
370 fclose(file);
371 return 1;
372 }
373
374 free(filecontent);
375
376 fclose(file);
377
378 return 0;
379 }
380
381 static int
382 write_file_if_changed(char *filename, char *content)
383 {
384 FILE *file;
385 int n;
386
387 if (is_file_changed(filename, content) == 0)
388 {
389 return 0;
390 }
391
392 file = fopen(filename, "wb");
393 if (file == NULL)
394 {
395 return 1;
396 }
397
398 n = fwrite(content, 1, strlen(content), file);
399
400 fclose(file);
401
402 return 0;
403 }
404
405 static char KMSTUB[] =
406 "/* This file is autogenerated. */\n"
407 "\n"
408 "#include <roskrnl.h>\n"
409 "#include <../kmregtests/kmregtests.h>\n"
410 "\n"
411 "typedef int (*TestRoutine)(int Command, char *Buffer);\n"
412 "\n"
413 "extern void RegisterTests();\n"
414 "\n"
415 "static PDEVICE_OBJECT KMRegTestsDeviceObject = NULL;\n"
416 "static PFILE_OBJECT KMRegTestsFileObject = NULL;\n"
417 "\n"
418 "void AddTest(TestRoutine Routine)\n"
419 "{\n"
420 " UNICODE_STRING DriverName;\n"
421 " IO_STATUS_BLOCK IoStatus;\n"
422 " NTSTATUS Status;\n"
423 " KEVENT Event;\n"
424 " PIRP Irp;\n"
425 "\n"
426 " if (KMRegTestsDeviceObject == NULL)\n"
427 " {\n"
428 " RtlInitUnicodeString(&DriverName, L\"\\\\Device\\\\KMRegTests\");\n"
429 " Status = IoGetDeviceObjectPointer(&DriverName, FILE_WRITE_ATTRIBUTES,\n"
430 " &KMRegTestsFileObject, &KMRegTestsDeviceObject);\n"
431 " if (!NT_SUCCESS(Status)) return;\n"
432 " }\n"
433 " KeInitializeEvent(&Event, NotificationEvent, FALSE);\n"
434 " Irp = IoBuildDeviceIoControlRequest(IOCTL_KMREGTESTS_REGISTER,\n"
435 " KMRegTestsDeviceObject, &Routine, sizeof(TestRoutine), NULL, 0, FALSE, &Event, &IoStatus);\n"
436 " Status = IoCallDriver(KMRegTestsDeviceObject, Irp);\n"
437 "}\n"
438 "\n"
439 "void PrepareTests()\n"
440 "{\n"
441 " RegisterTests();\n"
442 "}\n";
443
444 static char UMSTUB[] =
445 "/* This file is autogenerated. */\n"
446 "\n"
447 "#include <windows.h>\n"
448 "#define NTOS_MODE_USER\n"
449 "#include <ntos.h>\n"
450 "#include \"regtests.h\"\n"
451 "\n"
452 "PVOID\n"
453 "AllocateMemory(ULONG Size)\n"
454 "{\n"
455 " return (PVOID) RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);\n"
456 "}\n"
457 "\n"
458 "VOID\n"
459 "FreeMemory(PVOID Base)\n"
460 "{\n"
461 " RtlFreeHeap(RtlGetProcessHeap(), 0, Base);\n"
462 "}\n"
463 "\n"
464 "/* This function will be called several times */\n"
465 "void PrepareTests()\n"
466 "{\n"
467 " static int testsRegistered = 0;\n"
468 " if (testsRegistered == 0)\n"
469 " {\n"
470 " HANDLE hEvent;\n"
471 " hEvent = OpenEventW(\n"
472 " EVENT_ALL_ACCESS,\n"
473 " FALSE,\n"
474 " L\"WinRegTests\");\n"
475 " if (hEvent != NULL)\n"
476 " {\n"
477 " SetEvent(hEvent);\n"
478 " CloseHandle(hEvent);\n"
479 " testsRegistered = 1;\n"
480 " InitializeTests();\n"
481 " RegisterTests();\n"
482 " PerformTests(NULL, NULL);\n"
483 " }\n"
484 " }\n"
485 "}\n";
486
487 static char HELP[] =
488 "REGTESTS path file makefile [-u umstubfile] [-k kmstubfile]\n"
489 "\n"
490 " path Path to files\n"
491 " file Registration file to create\n"
492 " makefile Makefile to create\n"
493 " umstubfile Optional stub for running tests internal to a user-mode module\n"
494 " kmstubfile Optional stub for running tests internal to a kernel-mode module\n";
495
496 int main(int argc, char **argv)
497 {
498 char buf[MAX_PATH];
499 int i;
500
501 if (argc < 4)
502 {
503 puts(HELP);
504 return 1;
505 }
506
507
508 strcpy(buf, convert_path(argv[1]));
509 if (buf[strlen(buf)] != DIR_SEPARATOR_CHAR)
510 {
511 int i = strlen(buf);
512 buf[strlen(buf)] = DIR_SEPARATOR_CHAR;
513 buf[i + 1] = 0;
514 }
515 path = buf;
516 if (path[0] == 0)
517 {
518 printf("Missing path\n");
519 return 1;
520 }
521
522 file = convert_path(argv[2]);
523 if (file[0] == 0)
524 {
525 printf("Missing file\n");
526 return 1;
527 }
528
529 makefile = convert_path(argv[3]);
530 if (makefile[0] == 0)
531 {
532 printf("Missing makefile\n");
533 return 1;
534 }
535
536 umstubfile = NULL;
537 kmstubfile = NULL;
538 for (i = 4; i < argc; i++)
539 {
540 if (argv[i][0] == '-')
541 {
542 if (argv[i][1] == 'u')
543 {
544 umstubfile = convert_path(argv[++i]);
545 if (umstubfile[0] == 0)
546 {
547 printf("Missing umstubfile\n");
548 return 1;
549 }
550 }
551 else if (argv[i][1] == 'k')
552 {
553 kmstubfile = convert_path(argv[++i]);
554 if (kmstubfile[0] == 0)
555 {
556 printf("Missing kmstubfile\n");
557 return 1;
558 }
559 }
560 else
561 {
562 printf("Unknown switch\n");
563 return 1;
564 }
565 }
566 }
567
568
569 /* Registration file */
570 out = fopen(file, "wb");
571 if (out == NULL)
572 {
573 perror("Cannot create output file");
574 return 1;
575 }
576
577 write_line("/* This file is autogenerated. */");
578 write_line("");
579 write_line("typedef int (*TestRoutine)(int Command, char *Buffer);");
580 write_line("");
581
582 make_file_list(0);
583
584 write_line("");
585 write_line("extern void AddTest(TestRoutine Routine);");
586 write_line("");
587 write_line("void RegisterTests()");
588 write_line("{");
589
590 make_file_list(1);
591
592 write_line("}");
593
594 fclose(out);
595
596
597 /* Makefile */
598 out = fopen(makefile, "wb");
599 if (out == NULL)
600 {
601 perror("Cannot create output makefile");
602 return 1;
603 }
604
605 write_line("# This file is autogenerated.");
606 write_line("");
607 write_line("TESTS = \\");
608
609 make_file_list(2);
610
611 write_line("");
612
613 fclose(out);
614
615 /* User-mode stubfile */
616 if (umstubfile != NULL)
617 {
618 if (write_file_if_changed(umstubfile, UMSTUB) != 0)
619 {
620 perror("Cannot create output user-mode stubfile");
621 return 1;
622 }
623 }
624
625 /* Kernel-mode stubfile */
626 if (kmstubfile != NULL)
627 {
628 if (write_file_if_changed(kmstubfile, KMSTUB) != 0)
629 {
630 perror("Cannot create output kernel-mode stubfile");
631 return 1;
632 }
633 }
634
635 return 0;
636 }