update for HEAD-2003091401
[reactos.git] / 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, newcvspath);
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 */