Added SILC Thread Queue API
[silc.git] / util / robodoc / Source / analyser.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <ctype.h>
4 #include <string.h>
5
6 #include "robodoc.h"
7 #include "headers.h"
8 #include "items.h"
9 #include "util.h"
10 #include "folds.h"
11 #include "links.h"
12 #include "analyser.h"
13
14
15 /****** ROBODoc/RB_Analyse_Document [3.0i]
16  * NAME
17  *   RB_Analyse_Document -- scan document for headers and store them
18  * SYNOPSIS
19  *   RB_Analyse_Document (document)
20  *   RB_Analyse_Document (FILE *)
21  * FUNCTION
22  *   Searches the document for headers. Stores information about
23  *   any headers that are found in a linked list. Information
24  *   that is stored includes, the name of the header, its version
25  *   number, and its contents.
26  * INPUTS
27  *   document - a pointer to a file with the document to be
28  *              analysed
29  *   the gobal buffer line_buffer.
30  * RESULT
31  *   1)   A linked list pointed to by the global variable
32  *        first_header that contains information about each
33  *        header.
34  * NOTES
35  *   Using fseek and ftell because gcc doesn't know fgetpos and fsetpos,
36  *   on the sun unix system that I use.
37  * SEE ALSO
38  *   RB_Find_Marker
39  * SOURCE
40  */
41
42 void
43 RB_Analyse_Document (FILE * document)
44 {
45   int header_type;
46   int real_size;
47   char *name;
48
49   for (;
50        (header_type = RB_Find_Marker (document)) != NO_HEADER;
51        )
52     {
53       struct RB_header *new_header;
54       
55       if (!
56           (
57            ((header_type == INTERNAL_HEADER) &&
58             !(course_of_action & (DO_INCLUDE_INTERNAL | DO_INTERNAL_ONLY)))
59            ||
60            ((header_type != INTERNAL_HEADER) &&
61             (course_of_action & DO_INTERNAL_ONLY))
62            ||
63            (header_type == BLANK_HEADER)
64            )
65           )
66         {
67           long cur_file_pos;
68           
69           new_header = RB_Alloc_Header ();
70           RB_Insert_In_List (&first_header, new_header);
71           new_header->type = header_type;
72           if ((new_header->name = RB_Find_Header_Name ()) != NULL)
73             {
74               RB_Say ("found header [line %5d]: \"%s\"\n",
75                       line_number, new_header->name);
76 #if 0
77               if ((new_header->function_name
78                    = RB_Function_Name (new_header->name)) == NULL)
79 #endif
80               if ((new_header->function_name
81                    = RB_Function_Name (line_buffer)) == NULL)
82                 {
83                   RB_Panic ("Can't determine the \"function\" name.\n");
84                 }
85               cur_file_pos = (long) ftell (document);
86               if ((real_size = RB_Find_End_Marker (document,
87                                                    &new_header->size))
88                   != 0)
89                 {
90                   char *contents;
91                   
92                   fseek (document, cur_file_pos, 0);
93                   if ((contents = malloc ((new_header->size +
94                                            2) * sizeof (char)))
95                       != NULL)
96                     {
97                       fread (contents, new_header->size, sizeof (char), document);
98                       
99                       contents[real_size] = '\0';
100                       new_header->contents = contents;
101                       new_header->size = real_size;
102                     }
103                   else
104                     RB_Panic ("out of memory! [Alloc Header Contents]\n");
105                 }
106               else
107                 {
108                   RB_Panic ("found header with no end marker \"%s\"\n",
109                             new_header->name);
110                 }
111             }
112           else
113             {
114               RB_Panic ("found header marker but no name [line %d]\n",
115                         line_number);
116             }
117         }
118       else
119         {
120           if (header_type != BLANK_HEADER)
121             {
122               if ((name = RB_Find_Header_Name ()) != NULL)
123                 {
124                   new_header = RB_Alloc_Header ();
125                   if ((real_size =
126                        RB_Find_End_Marker (document, &new_header->size))
127                       == 0)
128                     {
129                       RB_Free_Header (new_header);
130                       RB_Panic ("found header with no end marker \"%s\"\n", name);
131                     }
132                   else
133                     {
134                       RB_Free_Header (new_header);
135                     }
136                 }
137               else
138                 {
139                   RB_Panic ("found header marker but no name [line %d]\n",
140                             line_number);
141                 }
142             }
143         }
144     }
145 }
146
147 /****** END RB_Analyse_Document *******/
148
149
150
151
152 /****f* ROBODoc/RB_Function_Name [2.0x]
153  * NAME
154  *   RB_Function_Name -- get pointer to the function name.
155  * SYNOPSIS
156  *   char *RB_NamePart(char *header_name)
157  * FUNCTION
158  *   A header name is consists of two parts. The module name and
159  *   the function name. This returns a pointer to the function name.
160  *   The name "function name" is a bit obsolete. It is really the name
161  *   of any of objects that can be documented; classes, methods,
162  *   variables, functions, projects, etc.
163  * SOURCE
164  */
165
166 char *
167 RB_Function_Name (char *header_name)
168 {
169   char *cur_char;
170   char c;
171   char *name;
172
173   name = NULL;
174   if ((cur_char = header_name) != NULL)
175     {
176       for (; (c = *cur_char) != '\0'; ++cur_char)
177         {
178           if ('/' == *cur_char)
179             {
180               ++cur_char;
181               if (*cur_char)
182                 name = cur_char;
183             }
184         }
185     }
186   if (name) {
187      char *temp;
188      temp = malloc((strlen(name) + 1) * sizeof(char));
189      strcpy(temp, name);
190      return temp; 
191   } else {
192      return (name);
193   }
194 }
195
196 /*** RB_Name_Part ***/
197
198
199
200 /****** ROBODoc/RB_Find_Marker [3.0h]
201  * NAME
202  *   RB_Find_Marker -- Search for header marker in document.
203  * SYNOPSIS
204  *   header_type = RB_Find_Marker (document)
205  *             int RB_Find_Marker (FILE *)
206  * FUNCTION
207  *   Read document file line by line, and search each line for the
208  *   any of the headers defined in the array  header_markers
209  * INPUTS
210  *   document - pointer to the file to be searched.
211  *   the gobal buffer line_buffer.
212  * RESULT
213  *   header type
214  *   can be:
215  *    (1) NO_HEADER - no header found, end of file reached
216  *    (2) MAIN_HEADER
217  *    (3) GENERIC_HEADER
218  *    (4) INTERNAL_HEADER
219  * BUGS
220  *   Bad use of feof(), fgets().
221  * SEE ALSO
222  *   RB_Find_End_Marker
223  * SOURCE
224  */
225
226 int
227 RB_Find_Marker (FILE * document)
228 {
229   int found;
230   int marker, marker_type;
231   char *cur_char, *cur_mchar;
232
233   marker_type = NO_HEADER;
234   cur_char = NULL;
235   found = FALSE;
236   while (!feof (document) && !found)
237     {
238       *line_buffer = '\0';
239       fgets (line_buffer, MAX_LINE_LEN, document);
240       if (!feof (document))
241         {
242           line_number++;
243           for (marker = 0;
244                ((cur_mchar = header_markers[marker]) != NULL) && !found;
245                marker++)
246             {
247               for (found = TRUE, cur_char = line_buffer;
248                    *cur_mchar && *cur_char && found;
249                    cur_mchar++, cur_char++)
250                 {
251                   if (tolower(*cur_mchar) != tolower(*cur_char))
252                     found = FALSE;
253                 }
254             }
255           if (found)
256             {
257               switch (*cur_char)
258                 {
259                 case 'h':
260                   marker_type = MAIN_HEADER;
261                   break;
262                 case '*':
263                   marker_type = GENERIC_HEADER;
264                   break;
265                 case 'i':
266                   marker_type = INTERNAL_HEADER;
267                   break;
268                 case 'f':
269                   marker_type = FUNCTION_HEADER;
270                   break;
271                 case 's':
272                   marker_type = STRUCT_HEADER;
273                   break;
274                 case 'c':
275                   marker_type = CLASS_HEADER;
276                   break;
277                 case 'm':
278                   marker_type = METHOD_HEADER;
279                   break;
280                 case 'd':
281                   marker_type = CONSTANT_HEADER;
282                   break;
283                 case 'v':
284                   marker_type = VARIABLE_HEADER;
285                   break;
286                 default:
287                   RB_Say ("%s: WARNING, [line %d] undefined headertype,"
288                           " using GENERIC\n", whoami, line_number);
289                   marker_type = GENERIC_HEADER;
290                 }
291             }
292         }
293     }
294   if (!found || feof (document))
295     {
296       marker_type = NO_HEADER;
297     }
298   else if (marker_type == GENERIC_HEADER)
299     {
300       skip_while (*cur_char == '*');
301       if (*cur_char == '/')
302         {
303           marker_type = BLANK_HEADER;
304         }
305     }
306   return marker_type;
307 }
308
309 /******** END RB_Find_Marker ******/
310
311
312 /****** ROBODoc/RB_Find_End_Marker [3.0h]
313  * NAME
314  *   RB_Find_End_Marker -- Search for end marker in document.
315  * SYNOPSIS
316  *   result = RB_Find_End_Marker (document)
317  *        int RB_Find_End_Marker (FILE *)
318  * FUNCTION
319  *   Searches line by line till any of the markers in the
320  *   array: end_markers is found.
321  * INPUTS
322  *   document   - pointer to the file to be searched.
323  *   int *total_size - external size
324  *   the gobal buffer line_buffer.
325  * RESULT
326  *                real_size if end marker was found
327  *   0          - no end marker was found
328  * SEE ALSO
329  *   RB_Find_Marker
330  * SOURCE
331  */
332
333 int
334 RB_Find_End_Marker (FILE * document, int *total_size)
335 {
336   int real_size = 0;
337   int found = FALSE;
338   int marker;
339   int line_len = 0;
340   char *cur_char, *cur_mchar;
341
342   while (!feof (document) && !found)
343     {
344       cur_char = line_buffer;
345       *cur_char = '\0';
346       fgets (cur_char, MAX_LINE_LEN, document);
347       ++line_number;            /* global linecounter *koessi */
348
349       line_len = strlen (cur_char);
350       real_size += line_len;
351
352       if (!feof (document))
353         {
354           for (marker = 0;
355                ((cur_mchar = end_markers[marker]) != NULL) && !found;
356                marker++)
357             {
358               for (found = TRUE, cur_char = line_buffer;
359                    *cur_mchar && *cur_char && found;
360                    cur_mchar++, cur_char++)
361                 {
362                   if (tolower(*cur_mchar) != tolower(*cur_char))
363                     found = FALSE;
364                 }
365             }
366         }
367     }
368   if (total_size)
369     *total_size = real_size;
370   if (found)
371     return real_size - line_len;
372   else
373     return 0;
374 }
375
376 /*****  RB_Find_End_Marker   *****/
377
378
379 /****** ROBODoc/RB_Find_Header_Name   [3.0b]
380  * NAME
381  *   RB_Find_Header_Name -- search for header name
382  * SYNOPSIS
383  *   result = RB_Find_Header_Name ()
384  *      char *RB_Find_Header_Name ()
385  * FUNCTION
386  *   Searches the line buffer for the header name.
387  *   It assumes that the header name follows after the
388  *   header marker, seperated by one or more spaces, and terminated
389  *   by one or more spaces or a '\n'.
390  *   It allocates an array of chars and copies the name to this array.
391  * INPUTS
392  *   the gobal buffer line_buffer.
393  * RESULT
394  *   pointer to the allocated array of chars that contains the name,
395  *   terminated with a '\0'.
396  *   NULL if no header name was found.
397  * MODIFICATION HISTORY
398  *   8. August 1995      --  optimized by koessi
399  * SEE ALSO
400  *   RB_Find_Function_Name(), RB_WordLen(), RB_StrDup()
401  * SOURCE
402  */
403
404 char *
405 RB_Find_Header_Name (void)
406 {
407   char *cur_char;
408
409   cur_char = line_buffer;
410   skip_while (*cur_char != '*');
411   skip_while (!isspace (*cur_char));
412   skip_while (isspace (*cur_char));
413   if (*cur_char)
414     {
415       char *n;
416       int len;
417
418       if (strchr(cur_char, '\n'))
419         *strchr(cur_char, '\n') = '\0';
420
421       len = RB_WordLen(cur_char);
422
423       n = calloc(len + 1, sizeof(*cur_char));
424       strncpy(n, cur_char, len);
425       return n;
426     }
427   return (NULL);
428 }
429
430 /*****  RB_Find_Header_Name  *****/
431
432
433 /****** ROBODoc/RB_Find_Item [3.0b]
434  * NAME
435  *   RB_Find_Item -- find item in header contents.
436  * SYNOPSIS
437  *   item_type = RB_Find_Item (next_line,item_line)
438  *
439  *           int RB_Find_Item (char **,  char **)
440  * FUNCTION
441  *   Searches the header contents line by line, looking
442  *   for an item Indicator.
443  * INPUTS
444  *   next_line  - pointer to a pointer that points to line
445  *                at which the search will start.
446  * SIDE-EFFECTS
447  *   next_line  - pointer to a pointer that points to begin of the line
448  *                after the line the item was found on.
449  *   item_line  - pointer to a pointer that points to the line the item
450  *                was found on.
451  * RESULT
452  *   item_type  - one of possible items indicators.
453  * SOURCE
454  */
455
456 int
457 RB_Find_Item (char **next_line, char **item_line)
458 {
459   char *cur_char = *next_line;
460   int item_type;
461
462   for (item_type = NO_ITEM;
463        *cur_char && (item_type == NO_ITEM);
464     )
465     {
466       *item_line = cur_char;
467       cur_char = RB_Skip_Remark_Marker (cur_char);
468
469       skip_while (isspace (*cur_char) && *cur_char != '\n');
470       if (isupper (*cur_char))
471         {
472           char *item_begin = cur_char;
473           char *item_end;
474
475           skip_while (isupper (*cur_char));
476           item_end = cur_char;
477           if (isspace (*cur_char) && *cur_char)
478             {
479               skip_while (isspace (*cur_char) && *cur_char != '\n');
480
481               /* Item consists of two words ? */
482               if (isupper (*cur_char) && *cur_char)
483                 {
484                   skip_while (isupper (*cur_char));
485                   item_end = cur_char;
486                   skip_while (isspace (*cur_char) && *cur_char != '\n');
487                 }
488               if (*cur_char == '\n')
489                 {
490                   char old_char = *item_end;
491
492                   *item_end = '\0';
493                   item_type = RB_Get_Item_Type (item_begin);
494                   *item_end = old_char;
495                   cur_char++;
496                 }
497             }
498         }
499       if (item_type == NO_ITEM)
500         {
501           find_eol;
502           if (*cur_char)
503             cur_char++;
504         }
505     }
506
507   /* advance item_line to end of comment block when we have no more items */
508   if (item_type == NO_ITEM)
509     {
510       *item_line = cur_char;
511     }
512   *next_line = cur_char;
513   return item_type;
514 }
515
516 /*****  RB_Find_Item    *****/
517
518
519 /****** ROBODoc/RRB_Number_Duplicate_Headers 
520  * NAME
521  *    RB_Number_Duplicate_Headers -- number duplicate headers
522  * SYNOPSIS
523  *    RB_Number_Duplicate_Headers (void)
524  * FUNCTION
525  *    Extends the function name with an additional number if there 
526  *    are several components with the same name.
527  *    Otherwise there will be labels with the same name in HTML
528  *    which confuses the browser.
529  * SOURCE
530  */
531
532 void 
533 RB_Number_Duplicate_Headers (void)
534 {
535   struct RB_header *cur_header;
536   struct RB_header *dup_header;
537   for (cur_header = first_header; 
538        cur_header;
539        cur_header = cur_header->next_header)
540   {  
541     char number[20];
542     int  nr = 0;
543     for (dup_header = cur_header->next_header; 
544          dup_header;
545          dup_header = dup_header->next_header)
546     {
547        if (strcmp(cur_header->function_name,
548                   dup_header->function_name) == 0) {
549           char *new_name;
550           nr++;
551           sprintf(number, "(%d)", nr);
552           new_name = malloc ((strlen(number) + 1 + 
553               strlen(dup_header->function_name) + 1 ) * sizeof(char));
554           if (new_name == NULL) 
555              RB_Panic ("out of memory! [Number Duplicates]\n");
556           sprintf(new_name, "%s%s", dup_header->function_name,
557                   number);
558           free(dup_header->function_name);
559           dup_header->function_name = new_name;
560        }
561     }
562   }
563 }
564
565 /******/
566
567
568 /****** ROBODoc/RB_Make_Index_Tables [3.0b]
569  * NAME
570  *    RB_Make_Index_Tables
571  * SYNOPSIS
572  *    void RB_Make_Index_Tables (void)
573  * FUNCTION
574  *    Creates sorted index tables of headers and links to speed up
575  *    matching links later on.
576  * INPUTS
577  *    none
578  * SIDE EFFECTS
579  *    Modifies header_index & link_index
580  * RESULT
581  *    none
582  * SOURCE
583  */
584
585 void
586 RB_Make_Index_Tables ()
587 {
588   int nr_of_headers, header;
589   int nr_of_links, link;
590   struct RB_link *cur_link;
591   struct RB_header *cur_header;
592
593   for (cur_header = first_header, nr_of_headers = 0;
594        cur_header;
595        cur_header = cur_header->next_header)
596     nr_of_headers++;
597
598   for (cur_link = first_link, nr_of_links = 0;
599        cur_link;
600        cur_link = cur_link->next_link)
601     nr_of_links++;
602
603   if (nr_of_headers)
604     {
605       int sort1, sort2;
606
607       RB_Say ("Allocating Header Index Table\n");
608       header_index = malloc (nr_of_headers * sizeof (struct RB_header **));
609
610       header_index_size = nr_of_headers;
611       if (!header_index)
612         RB_Panic ("out of memory! [Make Index Tables]\n");
613
614       /* Fill Index Table */
615       for (cur_header = first_header, header = 0;
616            cur_header;
617            cur_header = cur_header->next_header, header++)
618         header_index[header] = cur_header;
619
620       /* Sort Index Table */
621       RB_Say ("Sorting Header Index Table\n");
622       for (sort1 = 0; sort1 < nr_of_headers; sort1++)
623         {
624           struct RB_header *temp;
625
626           for (sort2 = sort1; sort2 < nr_of_headers; sort2++)
627             {
628               if (strcmp (header_index[sort1]->function_name,
629                           header_index[sort2]->function_name) > 0)
630                 {
631                   temp = header_index[sort1];
632                   header_index[sort1] = header_index[sort2];
633                   header_index[sort2] = temp;
634                 }
635             }
636         }
637     }
638   if (nr_of_links)
639     {
640       int sort1, sort2;
641
642       RB_Say ("Allocating Link Index Table\n");
643       link_index = malloc (nr_of_links * sizeof (struct RB_link **));
644
645       link_index_size = nr_of_links;
646       if (!link_index)
647         RB_Panic ("out of memory! [Make Index Tables]\n");
648
649       /* Fill Index Table */
650       for (cur_link = first_link, link = 0;
651            cur_link;
652            cur_link = cur_link->next_link, link++)
653         {
654           link_index[link] = cur_link;
655         }
656
657       /* Sort Index Table */
658       RB_Say ("Sorting Link Index Table\n");
659       for (sort1 = 0; sort1 < nr_of_links; sort1++)
660         {
661           struct RB_link *temp;
662
663           for (sort2 = sort1; sort2 < nr_of_links; sort2++)
664             {
665               if (strcmp (link_index[sort1]->label_name,
666                           link_index[sort2]->label_name) > 0)
667                 {
668                   temp = link_index[sort1];
669                   link_index[sort1] = link_index[sort2];
670                   link_index[sort2] = temp;
671                 }
672             }
673         }
674     }
675 }
676
677 /****** RB_Make_Index_Tables  *****/