Imported Robodoc.
[robodoc.git] / Source / headers.c
1 /*
2 Copyright (C) 1994-2007  Frans Slothouber, Jacco van Weert, Petteri Kettunen,
3 Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
4 Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
5
6 This file is part of ROBODoc
7
8 ROBODoc is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 */
22
23 /****h* ROBODoc/Headers
24  * FUNCTION
25  *   This module contains a set of variables that define how headers
26  *   start and how they end in various programming languages.
27  * NOTES
28  *   Added C++/ACM header option (David White)
29  *   Enables documentation only comments (//!) to be extracted from C++ 
30  *   and ACM files, rather than all comments.
31  ******
32  */
33
34 #include <assert.h>
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <string.h>
38 #include "robodoc.h"
39 #include "headers.h"
40 #include "globals.h"
41 #include "roboconfig.h"
42 #include "util.h"
43
44 /* This limits the total number of possible markers. */
45 /* TODO should be an enum */
46 #define NO_MARKER_LOCKED 100000
47 #define NO_MARKER        100002
48
49 static int          locked_header_marker = NO_MARKER_LOCKED;
50 static int          locked_end_marker = NO_MARKER_LOCKED;
51 static int          locked_remark_marker = NO_MARKER_LOCKED;
52
53 /****v* Headers/header_markers
54  * NAME
55  *   header_markers -- strings that mark the begin of a header.
56  * FUNCTION
57  *   These specify what robodoc recognizes as the beginning
58  * NOTE
59  *   The numbers at the beginning of the lines make it easier
60  *   to keep them in sync with the src_constants
61  *   of a header.
62  * SOURCE
63  */
64
65 char               *header_markers[] = {
66     "/****",                    /* 0   C, C++ */
67     "//!****",                  /* 1   C++, ACM */
68     "//****",                   /* 2   C++ */
69     "(****",                    /* 3   Pascal, Modula-2, B52 */
70     "{****",                    /* 4   Pascal */
71     ";;!****",                  /* 5   Aspen Plus */
72     ";****",                    /* 6   M68K assembler */
73     "****",                     /* 7   M68K assembler */
74     "C     ****",               /* 8   Fortran */
75     "REM ****",                 /* 9   BASIC */
76     "%****",                    /* 10  LaTeX, TeX, Postscript */
77     "#****",                    /* 11  Tcl/Tk */
78     "      ****",               /* 12  COBOL */
79     "--****",                   /* 13  Occam */
80     "<!--****",                 /* 14  HTML Code */
81     "<!---****",                /* 15  HTML Code,  the three-dashed comment
82                                  * tells the [server] pre-processor not
83                                  * to send that comment with the HTML 
84                                  */
85     "|****",                    /* 16  GNU Assembler */
86     "!****",                    /* 17  FORTRAN 90 */
87     "!!****",                   /* 18  FORTRAN 90 */
88     "$!****",                   /* 19  DCL */
89     "'****",                    /* 20  VB, LotusScript */
90     ".****",                    /* 21  DB/C */
91     "\\ ****",                  /* 22  Forth */
92     "<!-- ****",                /* 23  XML */
93     NULL
94 };
95
96 /****/
97
98 /****v* Headers/robo_header
99  * NAME
100  *   robo_header -- the distinct robodoc header - 
101  *                  alternative to using header_markers
102  * FUNCTION
103  *   This is an alternative to using header_markers - sometimes
104  *   ROBODOC confuses asterisks commonly used in comments as a header.
105  *   To use this header instead of header_markers use the -rh switch.
106  * NOTE
107  *   Added by David Druffner.   OBSOLETE
108  * SOURCE
109  */
110
111 char               *robo_header = "/*ROBODOC*"; /* TODO Remove */
112
113 /****/
114
115
116 /****v* Headers/remark_markers [3.0h]
117  * NAME
118  *   remark_markers
119  * FUNCTION
120  *   These specify what robodoc recognizes as a comment marker.
121  * TODO
122  *   (1) All the markers that start with one or more spaces are
123  *   never recognized, and should be removed.
124  *   (2) The numbers at the beginning of the lines make it easier
125  *   to keep them in sync with the src_remark_constants
126  * SOURCE
127  */
128
129 char               *remark_markers[] = {
130
131     " *",                       /* 0  C, C++, Pascal, Modula-2 */
132     "//!",                      /* 1  C++, ACM *//* MUST CHECK BEFORE C++ */
133     "//",                       /* 2  C++ */
134     "*",                        /* 3  C, C++, M68K assembler, Pascal,  Modula-2 */
135     ";;!",                      /* 4  Aspen Plus *//* MUST CHECK BEFORE M68K */
136     ";*",                       /* 5  M68K assembler */
137     ";",                        /* 6  M68K assembler */
138     "C",                        /* 7  Fortran */
139     "REM",                      /* 8  BASIC */
140     "%",                        /* 9  LaTeX, TeX, Postscript */
141     "#",                        /* 10 Tcl/Tk */
142     "      *",                  /* 11 COBOL */
143     "--",                       /* 12 Occam */
144     "|",                        /* 13 GNU Assembler */
145     "!!",                       /* 14 FORTRAN 90 */
146     "!",                        /* 15 FORTRAN 90 */
147     "$!",                       /* 16 DCL */
148     "'*",                       /* 17 VB */
149     ".*",                       /* 18 DB/C */
150     "\\",                       /* 19 Forth */
151     NULL
152 };
153
154 /****/
155
156
157 /****v* Headers/end_markers [3.0h]
158  * NAME
159  *   end_markers -- strings that mark the end of a header.
160  * FUNCTION
161  *   These specify what robodoc recognizes as the end of a 
162  *   documentation header. In most cases this will be
163  *   "***" or " ***". If the header contains a SOURCE item
164  *   then the end of the source has to be marked, which
165  *   is when the other strings in this array are used.
166  * NOTE
167  *   The numbers at the beginning of the lines make it easier
168  *   to find a special index-number.
169  * SOURCE
170  */
171
172 char               *end_markers[] = {
173     "/***",                     /* 0  C, C++ */
174     "//!***",                   /* 1  C++, ACM *//* Must check before C++ */
175     "//***",                    /* 2  C++ */
176     " ***",                     /* 3  C, C++, Pascal, Modula-2 */
177     "{***",                     /* 4  Pascal */
178     "(***",                     /* 5  Pascal, Modula-2, B52 */
179     ";;!***",                   /* 6  Aspen Plus *//* Must check before M68K */
180     ";***",                     /* 7  M68K assembler */
181     "***",                      /* 8  M68K assembler */
182     "C     ***",                /* 9  Fortran */
183     "REM ***",                  /* 10 BASIC */
184     "%***",                     /* 11 LaTeX, TeX, Postscript */
185     "#***",                     /* 12 Tcl/Tk */
186     "      ***",                /* 13 COBOL */
187     "--***",                    /* 14 Occam */
188     "<!--***",                  /* 15 HTML */
189     "<!---***",                 /* 16 HTML */
190     "|***",                     /* 17 GNU Assembler */
191     "!!***",                    /* 18 FORTRAN 90 */
192     "!***",                     /* 19 FORTRAN 90 */
193     "$!***",                    /* 20 DCL */
194     "'***",                     /* 21 VB, LotusScript */
195     ".***",                     /* 22 DB/C */
196     "\\ ***",                   /* 23 Forth */
197     "<!-- ***",                 /* 24 XML */
198     NULL
199 };
200
201 /****/
202
203 /****v* Headers/end_remark_markers
204  * NAME
205  *   end_remark_markers -- strings that mark the end of a comment.
206  * NOTE
207  *   The numbers at the beginning of the lines make it easier
208  *   to keep them in sync with the end_remark_constants
209  * SOURCE
210  */
211
212 char               *end_remark_markers[] = {
213     "*/",                       /* 0   C, C++ */
214     ( char * ) NULL,            /* 1   C++, ACM */
215     ( char * ) NULL,            /* 2   C++ */
216     ( char * ) NULL,            /* 3   C, C++, Pascal, Modula-2 */
217     "***}",                     /* 5   Pascal */
218     "***)",                     /* 6   Pascal, Modula-2, B52 */
219     ( char * ) NULL,            /* 7   M68K assembler */
220     ( char * ) NULL,            /* 8   M68K assembler */
221     ( char * ) NULL,            /* 9   Fortran */
222     ( char * ) NULL,            /* 10  BASIC */
223     ( char * ) NULL,            /* 11  LaTeX, TeX, Postscript */
224     ( char * ) NULL,            /* 12  Tcl/Tk */
225     ( char * ) NULL,            /* 13  COBOL */
226     ( char * ) NULL,            /* 14  Occam */
227     "-->",                      /* 15  HTML  & XML */
228     "--->",                     /* 16  HTML */
229     ( char * ) NULL,            /* 17  GNU Assembler */
230     ( char * ) NULL,            /* 18  FORTRAN 90 !! */
231     ( char * ) NULL,            /* 19  FORTRAN 90 ! */
232     ( char * ) NULL,            /* 20  VB */
233     ( char * ) NULL,            /* 21  Aspen Plus */
234     ( char * ) NULL             /* 22  Forth */
235 };
236
237 /****/
238
239
240 /****v* Headers/robo_end [3.0h]
241  * NAME
242  *   robo_end[] -- the distinct robodoc end marker - 
243  *                 alternative to using end_markers
244  * FUNCTION
245  *   This is an alternative to using end_markers - sometimes ROBODOC
246  *   confuses asterisks commonly used in comments as an end marker. To
247  *   use this footer instead of end_markers use the -rh switch.
248  * NOTE
249  *   Added by David Druffner.
250  * SOURCE
251  */
252
253 char               *robo_end[] = { "/*ROBODOC_END*", "*ROBODOC_END*", NULL };
254
255 /****/
256
257
258 /***f* Headers/RB_Is_Begin_Marker
259  * FUNCTION
260  *   Scan a line and see if any of the begin-of-a-header-markers 
261  *   defined in header_markers can be found.
262  * SYNOPSIS
263  */
264 int RB_Is_Begin_Marker(
265     char *cur_line,
266     char **type )
267 /*
268  * INPUTS
269  *   cur_line -- line to be searched.
270  * OUTPUT
271  *   type     -- the kind of header
272  * RESULT
273  *   TRUE  -- a begin header was found
274  *   FALSE -- no begin header was found.
275  * SOURCE
276  */
277 {
278     int                 found = FALSE;
279     unsigned int        marker = NO_MARKER;     /* for the assert */
280     char               *cur_mchar = NULL;
281     char               *cur_char = NULL;
282
283     if ( !( course_of_action.do_robo_head )
284          &&
285          ( ( ( course_of_action.do_lockheader ) &&
286              ( locked_header_marker == NO_MARKER_LOCKED ) )
287            || !( course_of_action.do_lockheader ) ) )
288     {
289         for ( marker = 0;
290               ( marker < configuration.header_markers.number ) && !found;
291               marker++ )
292         {
293             cur_mchar = configuration.header_markers.names[marker];
294             for ( found = TRUE, cur_char = RB_Skip_Whitespace( cur_line );
295                   *cur_mchar && *cur_char && found; cur_mchar++, cur_char++ )
296             {
297                 if ( tolower( *cur_mchar ) != tolower( *cur_char ) )
298                     found = FALSE;
299             }
300             if ( *cur_mchar != '\0' )
301             {
302                 /* It is not a complete match */
303                 found = FALSE;
304             }
305         }
306     }
307     else if ( ( course_of_action.do_lockheader ) &&
308               ( locked_header_marker != NO_MARKER_LOCKED ) )
309     {
310         cur_mchar = configuration.header_markers.names[locked_header_marker];
311         for ( found = TRUE, cur_char = RB_Skip_Whitespace( cur_line );
312               *cur_mchar && *cur_char && found; cur_mchar++, cur_char++ )
313         {
314             if ( tolower( *cur_mchar ) != tolower( *cur_char ) )
315             {
316                 found = FALSE;
317             }
318             if ( *cur_mchar != '\0' )
319             {
320                 /* It is not a complete match */
321                 found = FALSE;
322             }
323         }
324     }
325     else
326     {
327         assert( 0 );
328     }
329
330
331     if ( found && cur_char )
332     {
333         *type = cur_char;
334         ++cur_char;
335         if ( *cur_char == '*' )
336         {
337             ++cur_char;
338             found = utf8_isspace( *cur_char );
339         }
340         else if ( *( cur_char + 1 ) == '*' )
341         {
342             ++cur_char;
343             ++cur_char;
344             found = utf8_isspace( *cur_char );
345         }
346         else
347         {
348             found = FALSE;
349         }
350         if ( found )
351         {
352             found = FALSE;
353             /* It should contain some non * characters. */
354             for ( ; *cur_char; ++cur_char )
355             {
356                 if ( utf8_isalnum( *cur_char ) )
357                 {
358                     found = TRUE;
359                 }
360             }
361         }
362     }
363
364     if ( found &&
365          ( course_of_action.do_lockheader ) &&
366          ( locked_header_marker == NO_MARKER_LOCKED ) )
367     {
368         assert( marker != NO_MARKER );
369         locked_header_marker = marker - 1;
370         RB_Say( "header marker locked on %s\n", SAY_INFO,
371                 configuration.header_markers.names[locked_header_marker] );
372     }
373     return found;
374 }
375
376 /******/
377
378
379 /* Generic function to skip a remark begin or end marker */
380
381 static char        *RB_Skip_Remark_XXX_Marker(
382     char *cur_line,
383     struct Parameters *parameters )
384 {
385     char               *cur_char;
386     char               *space_pos;
387     unsigned int        marker;
388     int                 found = FALSE;
389
390     cur_char = RB_Skip_Whitespace( cur_line );
391     space_pos = strchr( cur_char, ' ' );
392     /* Replace the first space on the line with a '\0'
393      * this makes the comparison with the remark markers
394      * much easier.
395      */
396     if ( space_pos )
397     {
398         *space_pos = '\0';
399     }
400
401     for ( marker = 0; ( marker < parameters->number ) && !found; marker++ )
402     {
403         found =
404             ( RB_Str_Case_Cmp( cur_char, parameters->names[marker] ) == 0 );
405     }
406
407     assert( found );
408
409     if ( space_pos )
410     {
411         *space_pos = ' ';
412         return space_pos;
413     }
414     else
415     {
416         return cur_char + strlen( parameters->names[marker - 1] );
417     }
418 }
419
420
421 /* Generic function to see if there is a remark begin or end marker */
422
423 static int RB_Is_Remark_XXX_Marker(
424     char *cur_line,
425     struct Parameters *parameters )
426 {
427     char               *cur_char;
428     char               *space_pos;
429     unsigned int        marker;
430     int                 found = FALSE;
431
432     cur_char = RB_Skip_Whitespace( cur_line );
433     space_pos = strchr( cur_char, ' ' );
434     /* Replace the first space on the line with a '\0'
435      * this makes the comparison with the remark markers
436      * much easier.
437      */
438     if ( space_pos )
439     {
440         *space_pos = '\0';
441     }
442
443     for ( marker = 0; ( marker < parameters->number ) && !found; marker++ )
444     {
445         found =
446             ( RB_Str_Case_Cmp( cur_char, parameters->names[marker] ) == 0 );
447     }
448
449     if ( space_pos )
450     {
451         *space_pos = ' ';
452     }
453
454     return found;
455 }
456
457
458 /* TODO Documentation */
459 int RB_Is_Remark_End_Marker(
460     char *cur_line )
461 {
462     return RB_Is_Remark_XXX_Marker( cur_line,
463                                     &( configuration.remark_end_markers ) );
464 }
465
466 /* TODO Documentation */
467 int RB_Is_Remark_Begin_Marker(
468     char *cur_line )
469 {
470     return RB_Is_Remark_XXX_Marker( cur_line,
471                                     &( configuration.remark_begin_markers ) );
472 }
473
474 char               *RB_Skip_Remark_Begin_Marker(
475     char *cur_line )
476 {
477     return RB_Skip_Remark_XXX_Marker( cur_line,
478                                       &( configuration.
479                                          remark_begin_markers ) );
480 }
481
482 char               *RB_Skip_Remark_End_Marker(
483     char *cur_line )
484 {
485     return RB_Skip_Remark_XXX_Marker( cur_line,
486                                       &( configuration.remark_end_markers ) );
487 }
488
489
490 /****f* Headers/RB_Is_End_Marker
491  * FUNCTION
492  *   Scan a line and see if any of the end of a header markers 
493  *   defined in header_markers can be found.
494  * SYNOPSIS
495  */
496 int RB_Is_End_Marker(
497     char *cur_line )
498 /*
499  * INPUTS
500  *   cur_line -- line to be searched.
501  * OUTPUT
502  *   none
503  * RESULT
504  *   TRUE  -- an end header was found
505  *   FALSE -- none was found.
506  * SOURCE
507  */
508 {
509     int                 found = FALSE;
510     unsigned int        marker = NO_MARKER;     /* For the assert */
511     char               *cur_mchar;
512     char               *cur_char;
513
514     if ( !( course_of_action.do_robo_head )
515          &&
516          ( ( ( course_of_action.do_lockheader ) &&
517              ( locked_end_marker == NO_MARKER_LOCKED ) )
518            || !( course_of_action.do_lockheader ) ) )
519     {
520         for ( marker = 0;
521               ( marker < configuration.end_markers.number ) && !found;
522               marker++ )
523         {
524             cur_mchar = configuration.end_markers.names[marker];
525             cur_char = RB_Skip_Whitespace( cur_line );
526             if ( *cur_char )
527             {
528                 for ( found = TRUE;
529                       *cur_mchar && *cur_char && found;
530                       cur_mchar++, cur_char++ )
531                 {
532                     if ( tolower( *cur_mchar ) != tolower( *cur_char ) )
533                     {
534                         found = FALSE;
535                     }
536                 }
537             }
538         }
539     }
540     else if ( ( course_of_action.do_lockheader ) &&
541               ( locked_end_marker != NO_MARKER_LOCKED ) )
542     {
543         cur_mchar = configuration.end_markers.names[locked_end_marker];
544         cur_char = RB_Skip_Whitespace( cur_line );
545         if ( *cur_char )
546         {
547             for ( found = TRUE;
548                   *cur_mchar && *cur_char && found; cur_mchar++, cur_char++ )
549             {
550                 if ( tolower( *cur_mchar ) != tolower( *cur_char ) )
551                 {
552                     found = FALSE;
553                 }
554             }
555         }
556     }
557     else
558     {
559         assert( 0 );
560     }
561
562     /* Locking on end markers does not work at the moment,
563      * because there can be more than one end marker for
564      * a given language. TODO 
565      */
566 #if 0
567     if ( found &&
568          ( course_of_action.do_LOCKHEADER ) &&
569          ( locked_end_marker == NO_MARKER_LOCKED ) )
570     {
571         assert( marker != NO_MARKER );
572         locked_end_marker = marker - 1;
573         RB_Say( "end marker locked on %s\n", SAY_INFO,
574                 end_markers[locked_end_marker] );
575     }
576 #endif
577     return found;
578 }
579
580 /*****/
581
582
583 /****f* Headers/RB_Has_Remark_Marker
584  * FUNCTION
585  *   Check if a line starts with a remark marker.  This function
586  *   assumes that the remark marker starts on the first character of
587  *   the line.
588  * SYNOPSIS
589  */
590 int RB_Has_Remark_Marker(
591     char *lline_buffer )
592 /*
593  * INPUTS
594  *   o lline_buffer -- the line of text.
595  * RESULT
596  *   o TRUE -- it starts with a remark marker
597  *   o FALSE -- it does not.
598  * SOURCE
599  */
600 {
601     unsigned int        marker = 0;
602     unsigned int        marker_found = configuration.remark_markers.number;
603     int                 found = FALSE;
604     char               *space_pos = NULL;
605
606     space_pos = strchr( lline_buffer, ' ' );
607
608     /* Replace the first space on the line with a '\0'
609      * this makes the comparison with the remark markers
610      * much easier.
611      */
612     if ( space_pos )
613     {
614         *space_pos = '\0';
615     }
616
617     if ( ( ( course_of_action.do_lockheader ) &&
618            ( locked_remark_marker == NO_MARKER_LOCKED ) )
619          || !( course_of_action.do_lockheader ) )
620     {
621         for ( marker = 0; marker < configuration.remark_markers.number;
622               marker++ )
623         {
624             if ( RB_Str_Case_Cmp
625                  ( lline_buffer,
626                    configuration.remark_markers.names[marker] ) == 0 )
627             {
628                 marker_found = marker;
629                 found = TRUE;
630             }
631         }
632     }
633     else
634     {
635         if ( RB_Str_Case_Cmp
636              ( lline_buffer,
637                configuration.remark_markers.names[locked_remark_marker] ) ==
638              0 )
639         {
640             marker_found = marker;
641             found = TRUE;
642         }
643     }
644
645     if ( found &&
646          ( locked_remark_marker == NO_MARKER_LOCKED )
647          && ( course_of_action.do_lockheader ) )
648     {
649         assert( marker_found < configuration.remark_markers.number );
650         locked_remark_marker = marker_found;
651         RB_Say( "remark marker locked on %s\n", SAY_INFO,
652                 configuration.remark_markers.names[locked_remark_marker] );
653     }
654
655     /* Restore the space we replaced with a '\0' */
656     if ( space_pos )
657     {
658         *space_pos = ' ';
659     }
660
661     return found;
662 }
663
664 /******/
665
666
667 /****f* Headers/RB_Skip_Remark_Marker [2.0e]
668  * NAME
669  *    RB_Skip_Remark_Marker
670  * SYNOPSIS
671  */
672 char               *RB_Skip_Remark_Marker(
673     char *lline_buffer )
674 /*
675  * FUNCTION
676  *    Scan and search for a recognized remark marker; skip past the
677  *    marker to the body of the text
678  * SOURCE
679  */
680 {
681     unsigned int        marker, found;
682     char               *cur_char, *cur_mchar;
683
684     found = FALSE;
685     cur_char = NULL;
686     for ( marker = 0;
687           ( marker < configuration.remark_markers.number ) && !found;
688           marker++ )
689     {
690         cur_mchar = configuration.remark_markers.names[marker];
691         for ( found = TRUE, cur_char = lline_buffer;
692               *cur_mchar && *cur_char && found; cur_mchar++, cur_char++ )
693         {
694             if ( tolower( *cur_mchar ) != tolower( *cur_char ) )
695             {
696                 found = FALSE;
697             }
698         }
699     }
700     return ( cur_char );
701 }
702
703 /**************/
704
705
706
707
708 /* TODO Documentation */
709 void RB_Header_Lock_Reset(
710     void )
711 {
712     locked_header_marker = NO_MARKER_LOCKED;
713     locked_end_marker = NO_MARKER_LOCKED;
714 }
715
716 void RB_Item_Lock_Reset(
717     void )
718 {
719     locked_remark_marker = NO_MARKER_LOCKED;
720 }