2003-07-11 Casper S. Hornstrup <chorns@users.sourceforge.net>
[reactos.git] / reactos / tools / rgenstat / rgenstat.c
1 /*
2 * Generate a file with API status information 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 #endif
22 #include <ctype.h>
23 #ifndef WIN32
24 #ifndef MAX_PATH
25 #define MAX_PATH 260
26 #endif
27 #define DIR_SEPARATOR_CHAR '/'
28 #define DIR_SEPARATOR_STRING "/"
29 #else
30 #define DIR_SEPARATOR_CHAR '\\'
31 #define DIR_SEPARATOR_STRING "\\"
32 #endif
33
34 #define TAG_UNKNOWN -1
35 #define TAG_IMPLEMENTED 0
36 #define TAG_UNIMPLEMENTED 1
37
38 typedef struct _API_INFO
39 {
40 struct _API_INFO *next;
41 int tag_id;
42 char name[100];
43 } API_INFO, *PAPI_INFO;
44
45
46 PAPI_INFO sort_linked_list(PAPI_INFO,
47 unsigned, int (*)(PAPI_INFO, PAPI_INFO));
48
49
50 static FILE *in;
51 static FILE *out;
52 static char *file;
53 static FILE *file_handle = NULL;
54 static char *file_buffer = NULL;
55 static unsigned int file_size = 0;
56 static int file_pointer = 0;
57 static char tagname[200];
58 static PAPI_INFO api_info_list = NULL;
59
60
61 static char*
62 convert_path(char* origpath)
63 {
64 char* newpath;
65 int i;
66
67 newpath = strdup(origpath);
68
69 i = 0;
70 while (newpath[i] != 0)
71 {
72 #ifndef WIN32
73 if (newpath[i] == '\\')
74 {
75 newpath[i] = '/';
76 }
77 #else
78 #ifdef WIN32
79 if (newpath[i] == '/')
80 {
81 newpath[i] = '\\';
82 }
83 #endif
84 #endif
85 i++;
86 }
87 return(newpath);
88 }
89
90 static void
91 write_line(char *line)
92 {
93 int n_out;
94 char buf[200];
95
96 memset(buf, 0, sizeof(buf));
97 strcpy(buf, line);
98 /* Terminate the line */
99 buf[strlen(buf)] = '\r';
100 buf[strlen(buf)] = '\n';
101
102 n_out = fwrite(&buf[0], 1, strlen(buf), out);
103 }
104
105
106 static void
107 read_file(char *filename)
108 {
109 file_handle = fopen(filename, "rb");
110 if (file_handle == NULL)
111 {
112 printf("Can't open %s\n", filename);
113 exit(1);
114 }
115
116 // Get the size of the file
117 fseek(file_handle, 0, SEEK_END);
118 file_size = ftell(file_handle);
119
120 // Load it all into memory
121 file_buffer = malloc(file_size);
122 if (file_buffer == NULL)
123 {
124 fclose(file_handle);
125 printf("Out of memory\n");
126 exit(1);
127 }
128 fseek(file_handle, 0, SEEK_SET);
129 if (file_size > 0)
130 {
131 if (fread (file_buffer, 1, file_size, file_handle) < 1)
132 {
133 fclose(file_handle);
134 printf("Read error in file %s\n", filename);
135 exit(1);
136 }
137 }
138
139 file_pointer = 0;
140 }
141
142 static void
143 close_file()
144 {
145 free(file_buffer);
146 file_buffer = NULL;
147 fclose(file_handle);
148 file_handle = NULL;
149 file_pointer = 0;
150 }
151
152 static int
153 is_whitespace(char ch)
154 {
155 if (ch == ' ')
156 {
157 return 1;
158 }
159 if (ch == '\t')
160 {
161 return 1;
162 }
163 return 0;
164 }
165
166 static int
167 is_eol_char(char ch)
168 {
169 if (ch == '\r')
170 {
171 return 1;
172 }
173 if (ch == '\n')
174 {
175 return 1;
176 }
177 return 0;
178 }
179
180 static int
181 is_end_of_tag(char ch)
182 {
183 if ((ch >= 'a') && (ch <= 'z'))
184 {
185 return 0;
186 }
187 if ((ch >= 'A') && (ch <= 'Z'))
188 {
189 return 0;
190 }
191 if ((ch >= '0') && (ch <= '9'))
192 {
193 return 0;
194 }
195 if (ch == '_')
196 {
197 return 0;
198 }
199 return 1;
200 }
201
202 static int
203 is_end_of_name(char ch)
204 {
205 /* Currently the same as is_end_of_tag() */
206 return is_end_of_tag(ch);
207 }
208
209 static int
210 is_valid_file(char *filename)
211 {
212 char ext[MAX_PATH];
213 int i;
214
215 i = strlen(filename);
216 while (i > 0 && filename[i] != '.')
217 {
218 i--;
219 }
220 if (i > 0)
221 {
222 memset(ext, 0, sizeof(ext));
223 strncpy(&ext[0], &filename[i], strlen(&filename[i]));
224
225 if ((strncmp(ext, ".c", 2) == 0) || (strncmp(ext, ".C", 2) == 0))
226 {
227 return 1;
228 }
229 }
230 return 0;
231 }
232
233 static int
234 get_tag_id(char *tag)
235 {
236 if (strcasecmp(tag, "implemented") == 0)
237 {
238 return TAG_IMPLEMENTED;
239 }
240 if (strcasecmp(tag, "unimplemented") == 0)
241 {
242 return TAG_UNIMPLEMENTED;
243 }
244 return TAG_UNKNOWN;
245 }
246
247 static int
248 skip_to_next_tag()
249 {
250 unsigned int start;
251 int end_of_tag;
252 int found_tag = 0;
253 int tag_id;
254 int len;
255
256 tagname[0] = 0;
257 while ((file_pointer < file_size) && (!found_tag))
258 {
259 if (file_buffer[file_pointer] == '@')
260 {
261 file_pointer++;
262 start = file_pointer;
263 end_of_tag = 0;
264 while ((file_pointer < file_size) && (!end_of_tag))
265 {
266 end_of_tag = is_end_of_tag(file_buffer[file_pointer]);
267 file_pointer++;
268 }
269 len = file_pointer > start ? file_pointer - start - 1 : 0;
270 strncpy(tagname, &file_buffer[start], len);
271 tagname[len] = 0;
272
273 tag_id = get_tag_id(tagname);
274 if (tag_id != TAG_UNKNOWN)
275 {
276 return tag_id;
277 }
278 }
279 file_pointer++;
280 }
281
282 return TAG_UNKNOWN;
283 }
284
285 static void
286 skip_line()
287 {
288 while ((file_pointer < file_size) && (!is_eol_char(file_buffer[file_pointer])))
289 {
290 file_pointer++;
291 }
292 if ((file_pointer < file_size) && (file_buffer[file_pointer] == '\n'))
293 {
294 file_pointer++;
295 }
296 }
297
298 static void
299 skip_comments()
300 {
301 while ((file_pointer < file_size))
302 {
303 if (file_buffer[file_pointer] == '*')
304 {
305 if ((file_pointer + 1 < file_size))
306 {
307 if (file_buffer[file_pointer + 1] == '/')
308 {
309 skip_line();
310 return;
311 }
312 }
313 }
314 file_pointer++;
315 }
316 }
317
318 static int
319 get_previous_identifier(unsigned int end, char *name)
320 {
321 unsigned int my_file_pointer = end;
322 int len;
323
324 name[0] = 0;
325
326 while ((my_file_pointer > 0) && (is_whitespace(file_buffer[my_file_pointer])
327 || is_eol_char(file_buffer[my_file_pointer])))
328 {
329 my_file_pointer--;
330 }
331
332 /* Skip any comments between function name and it's parameters */
333 if ((my_file_pointer > 0) && (file_buffer[my_file_pointer] == '/'))
334 {
335 if ((my_file_pointer > 0) && (file_buffer[my_file_pointer - 1] == '*'))
336 {
337 my_file_pointer--;
338 while ((my_file_pointer > 0) && !((file_buffer[my_file_pointer] == '*')
339 && (file_buffer[my_file_pointer - 1] == '/')))
340 {
341 my_file_pointer--;
342 }
343 my_file_pointer -= 2;
344 }
345 }
346
347 /* Skip any remaining whitespace */
348 while ((my_file_pointer > 0) && (is_whitespace(file_buffer[my_file_pointer])))
349 {
350 my_file_pointer--;
351 }
352
353 end = my_file_pointer;
354 while ((my_file_pointer > 0))
355 {
356 if (is_end_of_name(file_buffer[my_file_pointer]))
357 {
358 len = end - my_file_pointer;
359 strncpy(name, &file_buffer[my_file_pointer + 1], len);
360 name[len] = 0;
361 return 1;
362 }
363 my_file_pointer--;
364 }
365
366 return 0;
367 }
368
369 static int
370 skip_to_next_name(char *name)
371 {
372 while ((file_pointer < file_size))
373 {
374 if (file_buffer[file_pointer] == '(')
375 {
376 return get_previous_identifier(file_pointer - 1, name);
377 }
378 file_pointer++;
379 }
380 return 0;
381 }
382
383 static void
384 parse_file(char *filename)
385 {
386 PAPI_INFO api_info;
387 char prev[200];
388 char name[200];
389 int tag_id;
390
391 read_file(filename);
392
393 prev[0] = 0;
394 do
395 {
396 tag_id = skip_to_next_tag();
397 if (tag_id == TAG_UNKNOWN)
398 {
399 break;
400 }
401
402 /* Skip rest of the comments between the tag and the function name */
403 skip_comments();
404
405 if (skip_to_next_name(name))
406 {
407 if (strlen(name) == 0)
408 {
409 printf("Warning: empty function name in file %s. Previous function name was %s.\n",
410 filename, prev);
411 }
412 api_info = malloc(sizeof(API_INFO));
413 if (api_info == NULL)
414 {
415 printf("Out of memory\n");
416 exit(1);
417 }
418
419 api_info->tag_id = tag_id;
420 strcpy(api_info->name, name);
421
422 api_info->next = api_info_list;
423 api_info_list = api_info;
424 strcpy(prev, name);
425 }
426 } while (1);
427
428 close_file();
429 }
430
431 #ifdef WIN32
432
433 /* Win32 version */
434 static void
435 process_directory (char *path)
436 {
437 struct _finddata_t f;
438 int findhandle;
439 char searchbuf[MAX_PATH];
440 char buf[MAX_PATH];
441
442 printf("Processing '%s'\n", path);
443
444 strcpy(searchbuf, path);
445 strcat(searchbuf, "*.*");
446
447 findhandle =_findfirst(searchbuf, &f);
448 if (findhandle != -1)
449 {
450 do
451 {
452 if (f.attrib & _A_SUBDIR)
453 {
454 if (f.name[0] != '.')
455 {
456 strcpy(buf, path);
457 strcat(buf, f.name);
458 strcat(buf, DIR_SEPARATOR_STRING);
459 process_directory(buf);
460 }
461 continue;
462 }
463
464 strcpy(buf, path);
465 strcat(buf, f.name);
466
467 /* Must be a .c file */
468 if (!is_valid_file(buf))
469 {
470 continue;
471 }
472
473 parse_file(buf);
474 }
475 while (_findnext(findhandle, &f) == 0);
476 _findclose(findhandle);
477 }
478 else
479 {
480 printf("Cannot open directory '%s'", path);
481 exit(1);
482 }
483 }
484
485 #else
486
487 /* Linux version */
488 static void
489 process_directory (char *path)
490 {
491 DIR *dirp;
492 struct dirent *entry;
493 struct stat stbuf;
494 char buf[MAX_PATH];
495
496 #ifdef HAVE_D_TYPE
497 dirp = opendir(path);
498 if (dirp != NULL)
499 {
500 while ((entry = readdir(dirp)) != NULL)
501 {
502 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
503 continue; // skip self and parent
504
505 if (entry->d_type == DT_REG) // normal file
506 {
507 // Check for an absolute path
508 if (path[0] == DIR_SEPARATOR_CHAR)
509 {
510 strcpy(buf, path);
511 strcat(buf, DIR_SEPARATOR_STRING);
512 strcat(buf, entry->d_name);
513 }
514 else
515 {
516 getcwd(buf, sizeof(buf));
517 strcat(buf, DIR_SEPARATOR_STRING);
518 strcat(buf, path);
519 strcat(buf, entry->d_name);
520 }
521
522 if (stat(buf, &stbuf) == -1)
523 {
524 printf("Can't access '%s' (%s)\n", buf, strerror(errno));
525 return;
526 }
527
528 if (S_ISDIR(stbuf.st_mode))
529 {
530 process_directory(buf);
531 continue;
532 }
533
534 /* Must be a .c file */
535 if (!is_valid_file(buf))
536 {
537 continue;
538 }
539
540 parse_file(buf);
541 }
542 }
543 closedir(dirp);
544 }
545 else
546 {
547 printf("Can't open %s\n", path);
548 exit(1);
549 }
550
551 #else
552
553 dirp = opendir(path);
554 if (dirp != NULL)
555 {
556 while ((entry = readdir(dirp)) != NULL)
557 {
558 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
559 continue; // skip self and parent
560
561 // Check for an absolute path
562 if (path[0] == DIR_SEPARATOR_CHAR)
563 {
564 strcpy(buf, path);
565 strcat(buf, DIR_SEPARATOR_STRING);
566 strcat(buf, entry->d_name);
567 }
568 else
569 {
570 getcwd(buf, sizeof(buf));
571 strcat(buf, DIR_SEPARATOR_STRING);
572 strcat(buf, path);
573 strcat(buf, entry->d_name);
574 }
575
576 if (stat(buf, &stbuf) == -1)
577 {
578 printf("Can't access '%s' (%s)\n", buf, strerror(errno));
579 return;
580 }
581
582 if (S_ISDIR(stbuf.st_mode))
583 {
584 process_directory(buf);
585 continue;
586 }
587
588 /* Must be a .c file */
589 if (!is_valid_file(buf))
590 {
591 continue;
592 }
593
594 parse_file(buf);
595 }
596 closedir(dirp);
597 }
598 else
599 {
600 printf("Can't open %s\n", path);
601 exit(1);
602 }
603
604 #endif
605 }
606
607 #endif
608
609 /*
610 * This function compares two API entries. It returns a negative value if p is
611 * before q, or a positive value if p is after q.
612 */
613 static int
614 compare_api_order(PAPI_INFO p, PAPI_INFO q)
615 {
616 return strcmp(p->name, q->name);
617 }
618
619 static void
620 generate_xml_for_component(char *component_name)
621 {
622 PAPI_INFO api_info;
623 char buf[200];
624 int complete;
625 int total;
626 int implemented_total;
627 int unimplemented_total;
628
629 // Sort list
630 api_info_list = sort_linked_list(api_info_list, 0, compare_api_order);
631
632 implemented_total = 0;
633 unimplemented_total = 0;
634
635 api_info = api_info_list;
636 while (api_info != NULL)
637 {
638 if (api_info->tag_id == TAG_IMPLEMENTED)
639 {
640 implemented_total ++;
641 }
642 else if (api_info->tag_id == TAG_UNIMPLEMENTED)
643 {
644 unimplemented_total ++;
645 }
646
647 api_info = api_info->next;
648 }
649
650 if (implemented_total + unimplemented_total > 0)
651 {
652 complete = ((implemented_total) * 100) / (implemented_total + unimplemented_total);
653 }
654 else
655 {
656 complete = 100;
657 }
658
659 sprintf(buf, "<component name=\"%s\" complete=\"%d\" implemented_total=\"%d\" unimplemented_total=\"%d\">",
660 component_name, complete, implemented_total, unimplemented_total);
661 write_line(buf);
662
663 if (api_info_list != NULL)
664 {
665 write_line("<functions>");
666
667 api_info = api_info_list;
668 while (api_info != NULL)
669 {
670 sprintf(buf, "<function name=\"%s\" implemented=\"%s\">",
671 api_info->name, api_info->tag_id == TAG_IMPLEMENTED ? "true" : "false");
672 write_line(buf);
673 write_line("</function>");
674 api_info = api_info->next;
675 }
676
677 write_line("</functions>");
678 }
679
680 write_line("</component>");
681 }
682
683 static void
684 read_input_file(char *input_file)
685 {
686 char component_name[MAX_PATH];
687 char component_path[MAX_PATH];
688 char *canonical_path;
689 unsigned int index;
690 unsigned int start;
691 PAPI_INFO api_info;
692 PAPI_INFO next_api_info;
693 char *buffer;
694 int size;
695 int len;
696
697 in = fopen(input_file, "rb");
698 if (in == NULL)
699 {
700 printf("Cannot open input file");
701 exit(1);
702 }
703
704 // Get the size of the file
705 fseek(in, 0, SEEK_END);
706 size = ftell(in);
707
708 // Load it all into memory
709 buffer = malloc(size);
710 if (buffer == NULL)
711 {
712 fclose(in);
713 printf("Out of memory\n");
714 exit(1);
715 }
716 fseek(in, 0, SEEK_SET);
717 if (fread (buffer, 1, size, in) < 1)
718 {
719 fclose(in);
720 printf("Read error in file %s\n", input_file);
721 exit(1);
722 }
723
724 index = 0;
725
726 write_line("<?xml version=\"1.0\" encoding=\"iso-8859-1\" ?>");
727 write_line("");
728 write_line("<components>");
729
730 while (1)
731 {
732 /* Free previous list */
733 for (api_info = api_info_list; api_info != NULL; api_info = next_api_info)
734 {
735 next_api_info = api_info->next;
736 free(api_info);
737 }
738 api_info_list = NULL;
739
740 /* Skip whitespace and eol characters */
741 while ((index < size) && (is_whitespace(buffer[index]) || (is_eol_char(buffer[index]))))
742 {
743 index++;
744 }
745 if ((file_pointer < size) && (buffer[index] == '\n'))
746 {
747 index++;
748 }
749
750 if (buffer[index] == ';')
751 {
752 /* Skip comments */
753 while ((index < size) && (!is_eol_char(buffer[index])))
754 {
755 index++;
756 }
757 if ((index < size) && (buffer[index] == '\n'))
758 {
759 index++;
760 }
761 continue;
762 }
763
764 /* Get component name */
765 start = index;
766 while ((index < size) && (!is_whitespace(buffer[index])))
767 {
768 index++;
769 }
770 if (index >= size)
771 {
772 break;
773 }
774
775 len = index - start;
776 strncpy(component_name, &buffer[start], len);
777 component_name[len] = 0;
778
779 /* Skip whitespace */
780 while ((index < size) && (is_whitespace(buffer[index])))
781 {
782 index++;
783 }
784 if (index >= size)
785 {
786 break;
787 }
788
789 /* Get component path */
790 start = index;
791 while ((index < size) && (!is_whitespace(buffer[index]) && !is_eol_char(buffer[index])))
792 {
793 index++;
794 }
795
796 len = index - start;
797 strncpy(component_path, &buffer[start], len);
798 component_path[len] = 0;
799
800 /* Append directory separator if needed */
801 if (component_path[strlen(component_path)] != DIR_SEPARATOR_CHAR)
802 {
803 int i = strlen(component_path);
804 component_path[strlen(component_path)] = DIR_SEPARATOR_CHAR;
805 component_path[i + 1] = 0;
806 }
807
808 /* Skip to end of line */
809 while ((index < size) && (!is_eol_char(buffer[index])))
810 {
811 index++;
812 }
813 if ((index < size) && (buffer[index] == '\n'))
814 {
815 index++;
816 }
817
818 canonical_path = convert_path(component_path);
819 if (canonical_path != NULL)
820 {
821 process_directory(canonical_path);
822 free(canonical_path);
823 generate_xml_for_component(component_name);
824 }
825 }
826
827 write_line("</components>");
828 }
829
830 static char HELP[] =
831 "RGENSTAT input-filename output-filename\n"
832 "\n"
833 " input-filename File containing list of components to process\n"
834 " output-filename File to create\n";
835
836 int main(int argc, char **argv)
837 {
838 char *input_file;
839 char *output_file;
840 int i;
841
842 if (argc < 2)
843 {
844 puts(HELP);
845 return 1;
846 }
847
848 input_file = convert_path(argv[1]);
849 if (input_file[0] == 0)
850 {
851 printf("Missing input-filename\n");
852 return 1;
853 }
854
855 output_file = convert_path(argv[2]);
856 if (output_file[0] == 0)
857 {
858 printf("Missing output-filename\n");
859 return 1;
860 }
861
862 out = fopen(output_file, "wb");
863 if (out == NULL)
864 {
865 printf("Cannot open output file");
866 return 1;
867 }
868
869 read_input_file(input_file);
870
871 fclose(out);
872
873 return 0;
874 }
875
876 /* EOF */