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