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