Added options to make Robodoc more customizable.
[robodoc.git] / Source / analyser.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
24
25 /****h* ROBODoc/Analyser
26  * NAME
27  *   Analyser -- Functions to scan source and collect headers
28  * FUNCTION
29  *   This module provides the functions to scan a sourcefile and
30  *   collect all the headers.
31  *
32  *****
33  * $Id: analyser.c,v 1.73 2007/07/10 19:13:51 gumpu Exp $
34  */
35
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <ctype.h>
39 #include <string.h>
40 #include <assert.h>
41 #include <stdlib.h>
42
43 #include "robodoc.h"
44 #include "globals.h"
45 #include "headers.h"
46 #include "headertypes.h"
47 #include "items.h"
48 #include "util.h"
49 #include "links.h"
50 #include "analyser.h"
51 #include "document.h"
52 #include "file.h"
53 #include "part.h"
54 #include "roboconfig.h"
55
56 #ifdef DMALLOC
57 #include <dmalloc.h>
58 #endif
59
60 static int          ToBeAdded(
61     struct RB_Document *document,
62     struct RB_header *header );
63 static int          Find_Header_Name(
64     FILE *,
65     struct RB_header * );
66 static struct RB_header *Grab_Header(
67     FILE *sourcehandle,
68     struct RB_Document *arg_document );
69 static char        *Function_Name(
70     char *header_name );
71 static char        *Module_Name(
72     char *header_name );
73 static int          Find_End_Marker(
74     FILE *document,
75     struct RB_header *new_header );
76 struct RB_HeaderType *AnalyseHeaderType(
77     char **cur_char,
78     int *is_internal );
79 static struct RB_HeaderType *RB_Find_Marker(
80     FILE *document,
81     int *is_internal,
82     int reuse_previous_line );
83 static int          Analyse_Items(
84     struct RB_header *arg_header );
85 static int          Is_ListItem_Start(
86     char *arg_line,
87     int arg_indent );
88
89 /****f* Analyser/Is_Pipe_Marker
90  * NAME
91  *   Is_Pipe_Marker
92  * FUNCTION
93  *   Check for "pipe" markers e.g. "|html ". 
94  * SYNOPSIS
95  */
96 static char        *Is_Pipe_Marker(
97     char *cur_char,
98     int *pipe_mode )
99 /*
100  * RESULT
101  *   Pointer to the data to be piped to document or in case no pointers
102  *   are found.
103  * SEE ALSO
104  *   RB_Check_Pipe
105  * SOURCE
106  */
107 {
108     char               *s = cur_char + 1;
109
110     *pipe_mode = -1;
111     if ( *cur_char == '|' && *s )
112     {
113         if ( strncmp( "html ", s, 5 ) == 0 )
114         {
115             *pipe_mode = HTML;
116             return ( s + 5 );
117         }
118         else if ( strncmp( "latex ", s, 6 ) == 0 )
119         {
120             *pipe_mode = LATEX;
121             return ( s + 6 );
122         }
123         else if ( strncmp( "rtf ", s, 4 ) == 0 )
124         {
125             *pipe_mode = RTF;
126             return ( s + 4 );
127         }
128         else if ( strncmp( "dbxml ", s, 6 ) == 0 )
129         {
130             *pipe_mode = XMLDOCBOOK;
131             return ( s + 6 );
132         }
133         else if ( strncmp( "ascii ", s, 6 ) == 0 )
134         {
135             *pipe_mode = ASCII;
136             return ( s + 6 );
137         }
138     }
139
140     return ( char * ) NULL;
141 }
142
143 /*****/
144
145
146 /****f* Analyser/Is_Tool
147  * SYNOPSIS
148  */
149 static char        *Is_Tool(
150     char *cur_char,
151     enum ItemLineKind *itemkind,
152     int *tool_active )
153 /*
154  * FUNCTION
155  *   Checks for tool start and end markers
156  * SOURCE
157  */
158 {
159     char               *s = cur_char + 1;
160
161     if ( *cur_char == '|' && *s )
162     {
163         // Check if tool starts or ends
164         if ( !strncmp( "tool ", s, 5 ) )
165         {
166             if ( *tool_active )
167             {
168                 *itemkind = ITEM_LINE_TOOL_END;
169                 *tool_active = 0;
170             }
171             else
172             {
173                 *itemkind = ITEM_LINE_TOOL_START;
174                 *tool_active = 1;
175             }
176
177             return ( s + 5 );
178         }
179         // Check if DOT starts or ends
180         if ( !strncmp( "dot ", s, 4 ) )
181         {
182             if ( *tool_active )
183             {
184                 *itemkind = ITEM_LINE_DOT_END;
185                 *tool_active = 0;
186             }
187             else
188             {
189                 *itemkind = ITEM_LINE_DOT_START;
190                 *tool_active = 1;
191             }
192
193             return ( s + 4 );
194         }
195         // Check for DOT file includes
196         else if ( !strncmp( "dotfile ", s, 8 ) && !*tool_active )
197         {
198             *itemkind = ITEM_LINE_DOT_FILE;
199             return ( s + 8 );
200         }
201         // Check for exec items
202         else if ( !strncmp( "exec ", s, 5 ) && !*tool_active )
203         {
204             *itemkind = ITEM_LINE_EXEC;
205             return ( s + 5 );
206         }
207     }
208
209     return NULL;
210 }
211
212 /*****/
213
214 /****f* Analyser/RB_Analyse_Document
215  *   foo
216  * FUNCTION
217  *   Scan all the sourcefiles of all parts of a document for
218  *   headers.  Store these headers in each part (RB_Part).
219  * SYNOPSIS
220  */
221 void RB_Analyse_Document(
222     struct RB_Document *arg_document )
223 /*
224  * INPUTS
225  *   o document -- document to be analysed.
226  * RESULT
227  *   Each part will contain the headers that were found in the
228  *   sourcefile of the part.
229  * SOURCE
230  */
231 {
232     struct RB_Part     *a_part;
233     struct RB_Filename *a_filename;
234     FILE               *filehandle;
235
236     for ( a_part = arg_document->parts; a_part; a_part = a_part->next )
237     {
238         struct RB_header   *new_header = NULL;
239
240         a_filename = a_part->filename;
241         RB_Say( "analysing %s\n", SAY_DEBUG, Get_Fullname( a_filename ) );
242         RB_SetCurrentFile( Get_Fullname( a_filename ) );
243
244         RB_Header_Lock_Reset(  );
245         filehandle = RB_Open_Source( a_part );
246         line_number = 0;
247
248         for ( new_header = Grab_Header( filehandle, arg_document );
249               new_header;
250               new_header = Grab_Header( filehandle, arg_document ) )
251         {
252             if ( ToBeAdded( arg_document, new_header ) )
253             {
254                 /* The Add is required before the 
255                  * Analyse because Add sets the owner of the header
256                  * which is needed for error messages.
257                  */
258                 RB_Part_Add_Header( a_part, new_header );
259                 Analyse_Items( new_header );
260             }
261             else
262             {
263                 RB_Free_Header( new_header );
264             }
265         }
266         fclose( filehandle );
267     }
268 }
269
270 /*****/
271
272 #if 0
273 /****f* Analyser/Check_Header_Start
274  * FUNCION
275  *   Sometimes a user makes a spelling mistake in the name of the first item.
276  *   ROBODoc then ignores all the lines leading up to the second item,
277  *   (which is the first item ROBODoc recognized).  This function
278  *   checks for this condition and gives the user a warning.
279  * SYNOPSIS
280  */
281 static void Check_Header_Start(
282     struct RB_header *arg_header,
283     int first_item )
284 /*
285  * INPUTS
286  *   * arg_header -- the header to be checked.
287  *   * first      -- the line on which the first item was found
288  * RESULT
289  *   A warning is given if the condition occured.
290  * SOURCE
291  */
292 {
293     if ( first_item )
294     {
295         int                 i = 0;
296
297         for ( i = 0; i < first_item; ++i )
298         {
299             char               *c = arg_header->lines[i];
300
301             c = RB_Skip_Whitespace( c );
302             if ( RB_Has_Remark_Marker( c ) )
303             {
304                 c = RB_Skip_Remark_Marker( c );
305                 for ( ; *c; ++c )
306                 {
307                     if ( !utf8_isspace( *c ) )
308                     {
309                         RB_Warning_Full( Get_Fullname
310                                          ( arg_header->owner->filename ),
311                                          arg_header->line_number,
312                                          "Header \"%s\" contains text before the fist item, "
313                                          "this might be caused by a misspelled item name.\n",
314                                          arg_header->name );
315                         return;
316                     }
317                 }
318             }
319         }
320     }
321 }
322
323 /*******/
324 #endif
325
326 /****f* Analyser/Is_Empty_Line
327  * FUNCTION
328  *   Check if line is empty. This assumes that 
329  *   Copy_Lines_To_Item has been run on the item.
330  * SYNOPSIS
331  */
332 static int Is_Empty_Line(
333     char *line )
334 /*
335  * INPUTS
336  *   * line -- the string to be analysed.
337  * SOURCE
338  */
339 {
340     line = RB_Skip_Whitespace( line );
341     return ( *line == '\0' );
342 }
343
344 /******/
345
346
347 /****f* Analyser/Get_Indent
348  * FUNCION
349  *   Determine the indentation of a line by counting
350  *   the number of spaces at the begining of the line.
351  * SYNOPSIS
352  */
353 static int Get_Indent(
354     char *line )
355 /*
356  * INPUTS
357  *   * line -- the line
358  * RESULT
359  *   The indentation.
360  * SOURCE
361  */
362 {
363     int                 i;
364
365     for ( i = 0; line[i] && utf8_isspace( line[i] ); ++i )
366     {
367         /* empty */
368     }
369     return i;
370 }
371
372 /*****/
373
374
375 /****f* Analyser/Is_ListItem_Start
376  * FUNCTION
377  *   Test if a line starts with something that indicates a list item.
378  *   List items start with '*', '-', or 'o'.
379  * SYNPOPSIS
380  */
381 static int Is_ListItem_Start(
382     char *arg_line,
383     int arg_indent )
384 /*
385  * INPUTS
386  *   * line -- the line
387  * RESULT
388  *   * TRUE  -- it did start with one of those characters
389  *   * FALSE -- it did not.
390  * SOURCE
391  */
392 {
393     char               *c = arg_line;
394     int                 cur_indent = Get_Indent( arg_line );
395
396     if ( cur_indent == arg_indent )
397     {
398         /* TODO  Think there is a function for this */
399         for ( ; *c && utf8_isspace( *c ); ++c )
400         {                       /* empty */
401         };
402
403         if ( *c && ( strlen( c ) >= 3 ) )
404         {
405             if ( strchr( "*-o", *c ) && utf8_isspace( *( c + 1 ) ) )
406             {
407                 return TRUE;
408             }
409         }
410     }
411     else
412     {
413         /* The line is indented so it must be
414          * the start of a pre block  */
415     }
416
417     return FALSE;
418 }
419
420 /*****/
421
422
423 /****f* Analyser/Is_List_Item_Continuation
424  * FUNCTION
425  *   Is it like the second line in something like:
426  *     * this is a list item
427  *       that continues 
428  * SYNPOPSIS
429  */
430 static int Is_List_Item_Continuation(
431     char *arg_line,
432     int indent )
433 /*
434  * INPUTS
435  *   * arg_line  -- the current line
436  *   * indent    -- the indent of the current item.
437  * RESULT
438  *   * TRUE  -- it is.
439  *   * FALSE -- it is not.
440  * SOURCE
441  */
442 {
443     int                 this_indent = Get_Indent( arg_line );
444
445     return ( this_indent > indent );
446 }
447
448 /*****/
449
450
451 #if 0
452 /****f* Analyser/Is_End_of_List
453  * FUNCTION
454  *   Check... (TODO) 
455  * INPUTS
456  *   * arg_line --
457  *   * indent  --
458  * SOURCE
459  */
460
461 static int Is_End_of_List(
462     char *arg_line,
463     int indent )
464 {
465     int                 this_indent = Get_Indent( arg_line );
466
467     return ( this_indent <= indent );
468 }
469
470 /******/
471 #endif
472
473
474 /****f* Analyser/Is_Start_List
475  * FUNCTION
476  *   Check... (TODO) 
477  * INPUTS
478  *   * arg_line --
479  *   * indent  --
480  * SOURCE
481  */
482
483 static int Is_Start_List(
484     char *arg_line,
485     int indent )
486 {
487     int                 this_indent = Get_Indent( arg_line );
488     char               *c = strrchr( arg_line, ':' );
489
490     if ( ( this_indent == indent ) && c )
491     {
492         for ( ++c; *c; ++c )
493         {
494             if ( !utf8_isspace( *c ) )
495             {
496                 return FALSE;
497             }
498         }
499         return TRUE;
500     }
501     return FALSE;
502 }
503
504 /*******/
505
506
507 /****f* Analyser/Remove_List_Char
508  * FUNCTION
509  *   Remove... (TODO) 
510  * INPUTS
511  *   * arg_item    -- the item to be analysed.
512  *   * start_index -- 
513  * SOURCE
514  */
515
516 static void Remove_List_Char(
517     struct RB_Item *arg_item,
518     int start_index )
519 {
520     char               *c = arg_item->lines[start_index]->line;
521
522     for ( ; *c && utf8_isspace( *c ); ++c )
523     {                           /* empty */
524     };
525     if ( *c && ( strlen( c ) >= 3 ) )
526     {
527         if ( strchr( "*-o", *c ) && utf8_isspace( *( c + 1 ) ) )
528         {
529             char               *temp = arg_item->lines[start_index]->line;
530
531             *c = ' ';
532             arg_item->lines[start_index]->line = RB_StrDup( temp + 2 );
533             free( temp );
534         }
535     }
536 }
537
538 /*******/
539
540 /****f* Analyser/Analyse_ListBody
541  * FUNCTION
542  *   Analyse... (TODO) 
543  * SYNPOPSIS
544  */
545 static int Analyse_ListBody(
546     struct RB_Item *arg_item,
547     int start_index,
548     int arg_indent )
549 /*
550  * INPUTS
551  *   * arg_item    -- the item to be analysed.
552  *   * start_index --
553  *   * arg_index   --
554  * SOURCE
555  */
556 {
557     int                 i = start_index;
558
559     for ( ; i < arg_item->no_lines; i++ )
560     {
561         char               *line = arg_item->lines[i]->line;
562
563         if ( ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN ) ||
564              ( arg_item->lines[i]->kind == ITEM_LINE_END ) )
565         {
566             if ( Is_ListItem_Start( line, arg_indent ) )
567             {
568                 arg_item->lines[i]->format |= RBILA_END_LIST_ITEM;
569                 arg_item->lines[i]->format |= RBILA_BEGIN_LIST_ITEM;
570                 Remove_List_Char( arg_item, i );
571             }
572             else if ( Is_List_Item_Continuation( line, arg_indent ) )
573             {
574                 /* Nothing */
575             }
576             else
577             {
578                 /* Must be the end of the list */
579                 arg_item->lines[i]->format |= RBILA_END_LIST_ITEM;
580                 arg_item->lines[i]->format |= RBILA_END_LIST;
581                 break;
582             }
583         }
584     }
585     return i;
586 }
587
588 /*******/
589
590 /****f* Analyser/Analyse_List
591  * FUNCTION 
592  *   Parse the item text to see if there are any lists.
593  *   A list is either case I:
594  *      ITEMNAME
595  *         o bla bla
596  *         o bla bla
597  *   or case II:
598  *      some text:     <-- begin of a list
599  *      o bla bla      <-- list item
600  *        bla bla bla  <-- continuation of list item.
601  *      o bla bla      <-- list item
602  *                     <-- end of a list 
603  *      bla bla        <-- this can also be the end of a list.
604  * SYNPOPSIS
605  */
606 static void Analyse_List(
607     struct RB_Item *arg_item,
608     int indent )
609 /*
610  * INPUTS
611  *   * arg_item  -- the item to be parsed.
612  *   * indent    -- the indent of this item.
613  * OUTPUT
614  *   * arg_item  -- the itemlines will contain formatting information 
615  *                  for all the lists that were found.
616  * SOURCE
617  */
618 {
619     if ( arg_item->no_lines >= 1 )
620     {
621         int                 i = 0;
622         char               *line = arg_item->lines[i]->line;
623
624         /* Case I */
625         if ( ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN ) &&
626              Is_ListItem_Start( line, indent ) )
627         {
628             /* Case I, the is a list item right after the item name */
629             arg_item->lines[i]->format |= RBILA_BEGIN_LIST;
630             arg_item->lines[i]->format |= RBILA_BEGIN_LIST_ITEM;
631             Remove_List_Char( arg_item, i );
632             /* Now try to find the end of the list */
633             i = Analyse_ListBody( arg_item, 1, indent );
634         }
635
636         /* Now search for case II cases */
637         for ( ; i < arg_item->no_lines; i++ )
638         {
639             line = arg_item->lines[i]->line;
640             if ( ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN ) &&
641                  Is_Start_List( line, indent ) )
642             {
643                 ++i;
644                 if ( i < arg_item->no_lines )
645                 {
646                     line = arg_item->lines[i]->line;
647                     if ( ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN ) &&
648                          Is_ListItem_Start( line, indent ) )
649                     {
650                         arg_item->lines[i]->format |= RBILA_BEGIN_LIST;
651                         arg_item->lines[i]->format |= RBILA_BEGIN_LIST_ITEM;
652                         Remove_List_Char( arg_item, i );
653                         ++i;
654                         i = Analyse_ListBody( arg_item, i, indent );
655
656
657                         /* One list might be immediately followed
658                          * by another. In this case we have to
659                          * analyse the last line again. */
660                         line = arg_item->lines[i]->line;
661                         if ( ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN )
662                              && Is_Start_List( line, indent ) )
663                         {
664                             --i;
665                         }
666
667                     }
668                 }
669             }
670         }
671     }
672 }
673
674 /******/
675
676 // If (unused(this_function)) { delete(this_function) }
677 #if 0
678 /****f* Analyser/Dump_Item
679  * FUNCTION
680  *   Print debug information.
681  */
682 static void Dump_Item(
683     struct RB_Item *arg_item )
684 /*
685  * INPUTS
686  *   * arg_item -- the item to be pretty-printed.
687  * SOURCE
688  */
689 {
690     int                 i;
691
692     /* preformatted blocks */
693     for ( i = 0; i < arg_item->no_lines; i++ )
694     {
695         char               *line = arg_item->lines[i]->line;
696         int                 format = arg_item->lines[i]->format;
697
698         printf( "%04d ", i );
699         printf( ( format & RBILA_END_PARAGRAPH ) ? "p" : " " );
700         printf( ( format & RBILA_BEGIN_PARAGRAPH ) ? "P" : " " );
701         printf( ( format & RBILA_END_PRE ) ? "e" : " " );
702         printf( ( format & RBILA_BEGIN_PRE ) ? "E" : " " );
703         printf( ( format & RBILA_END_LIST ) ? "l" : " " );
704         printf( ( format & RBILA_BEGIN_LIST ) ? "L" : " " );
705         printf( ( format & RBILA_END_LIST_ITEM ) ? "i" : " " );
706         printf( ( format & RBILA_BEGIN_LIST_ITEM ) ? "I" : " " );
707         printf( " (%s)\n", line );
708     }
709 }
710
711 /*******/
712 #endif
713
714 /****f* Analyser/Preformat_All
715  * FUNCTION
716  *   Process... (TODO) 
717  * SYNPOPSIS
718  */
719 static void Preformat_All(
720     struct RB_Item *arg_item,
721     int source )
722 /*
723  * INPUTS
724  *   * arg_item -- the item to be pre-formatted.
725  *   * source   -- is it a source item ?
726  * SOURCE
727  */
728 {
729     int                 i;
730     int                 preformatted = FALSE;
731     char               *line = NULL;
732
733     if ( arg_item->no_lines > 0 )
734     {
735         i = 0;
736         /* Skip any pipe stuff */
737         for ( ;
738               ( i < arg_item->no_lines )
739               && ( arg_item->lines[i]->kind == ITEM_LINE_PIPE ); ++i )
740         {
741             /* Empty */
742         }
743
744         line = arg_item->lines[i]->line;
745         if ( ( arg_item->lines[i]->kind == ITEM_LINE_RAW ) ||
746              ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN ) )
747         {
748             arg_item->lines[i]->format |=
749                 RBILA_BEGIN_PRE | ( source ? RBILA_BEGIN_SOURCE : 0 );
750             preformatted = TRUE;
751
752             for ( ++i; i < arg_item->no_lines; i++ )
753             {
754                 if ( arg_item->lines[i]->kind == ITEM_LINE_PIPE )
755                 {
756                     /* Temporarily end the preformatting to allow
757                      * the piping to happen
758                      */
759                     arg_item->lines[i]->format |=
760                         RBILA_END_PRE | ( source ? RBILA_END_SOURCE : 0 );
761                     /* Find the end of the pipe stuff */
762                     for ( ; ( i < arg_item->no_lines ) &&
763                           ( arg_item->lines[i]->kind == ITEM_LINE_PIPE );
764                           ++i )
765                     {           /* Empty */
766                     };
767                     /* Every item ends with an ITEM_LINE_END, so: */
768                     assert( i < arg_item->no_lines );
769                     /* And re-enable preformatting */
770                     arg_item->lines[i]->format |=
771                         RBILA_BEGIN_PRE | ( source ? RBILA_BEGIN_SOURCE : 0 );
772                 }
773
774                 if ( arg_item->lines[i]->kind == ITEM_LINE_END )
775                 {
776                     /* If the last line ends with a begin_pre remove
777                      * it, otherwise a begin and end pre will be
778                      * generated, in the wrong order, on the same line in the output.
779                      */
780                     if ( arg_item->lines[i]->format & RBILA_BEGIN_PRE ) {
781                         arg_item->lines[i]->format &= ~( RBILA_BEGIN_PRE );
782                     } else {
783                         arg_item->lines[i]->format |= RBILA_END_PRE;
784                     }
785                     arg_item->lines[i]->format |= ( source ? RBILA_END_SOURCE : 0 );
786                 }
787             }
788         }
789     }
790 }
791
792 /******/
793
794 /*
795  *     aaaaa
796  *       aa
797  *               </p>
798  *       aa      <p>
799  *     aaa
800  *     aaaa
801  *
802  *
803  */
804
805 /****f* Analyser/Analyse_Preformatted
806  * FUNCTION
807  *   Analyse preformatted text ... (TODO) 
808  * SYNPOPSIS
809  */
810 static void Analyse_Preformatted(
811     struct RB_Item *arg_item,
812     int indent )
813 /*
814  * INPUTS
815  *   * arg_item -- the item to be analysed.
816  *   * indent   -- 
817  * SOURCE
818  */
819 {
820     int                 i;
821     int                 in_list = FALSE;
822     int                 new_indent = -1;
823     int                 preformatted = FALSE;
824     char               *line = NULL;
825
826     /* preformatted blocks */
827     if ( arg_item->no_lines > 0 )
828     {
829         i = 0;
830         /* Skip any pipe stuff */
831         for ( ;
832               ( i < arg_item->no_lines )
833               && ( arg_item->lines[i]->kind == ITEM_LINE_PIPE ); ++i )
834         {
835             /* Empty */
836         }
837
838         line = arg_item->lines[i]->line;
839
840         if ( ( !in_list )
841              && ( arg_item->lines[i]->format & RBILA_BEGIN_LIST ) )
842         {
843             in_list = TRUE;
844         }
845         if ( ( in_list ) && ( arg_item->lines[i]->format & RBILA_END_LIST ) )
846         {
847             in_list = FALSE;
848         }
849
850         for ( ++i; i < arg_item->no_lines; i++ )
851         {
852             if ( arg_item->lines[i]->kind == ITEM_LINE_PIPE )
853             {
854                 if ( preformatted )
855                 {
856                     arg_item->lines[i]->format |= RBILA_END_PRE;
857                 }
858                 for ( ; ( i < arg_item->no_lines ) &&
859                       ( arg_item->lines[i]->kind == ITEM_LINE_PIPE ); ++i )
860                 {               /* Empty */
861                 };
862                 /* Every item ends with an ITEM_LINE_END, so: */
863                 assert( i < arg_item->no_lines );
864                 if ( preformatted )
865                 {
866                     arg_item->lines[i]->format |= RBILA_BEGIN_PRE;
867                 }
868             }
869
870             line = arg_item->lines[i]->line;
871             new_indent = Get_Indent( line );
872
873             if ( ( !in_list )
874                  && ( arg_item->lines[i]->format & RBILA_BEGIN_LIST ) )
875             {
876                 in_list = TRUE;
877             }
878             if ( ( in_list )
879                  && ( arg_item->lines[i]->format & RBILA_END_LIST ) )
880             {
881                 in_list = FALSE;
882             }
883
884             if ( !in_list )
885             {
886                 if ( ( new_indent > indent ) && !preformatted )
887                 {
888                     preformatted = TRUE;
889                     arg_item->lines[i]->format |= RBILA_BEGIN_PRE;
890                 }
891                 else if ( ( new_indent <= indent ) && preformatted )
892                 {
893                     preformatted = FALSE;
894                     arg_item->lines[i]->format |= RBILA_END_PRE;
895                 }
896                 else
897                 {
898                     /* An empty line */
899                 }
900             }
901             else
902             {
903                 /* We are in a list, do nothing */
904             }
905         }
906     }
907 }
908
909 /******/
910
911 /****f* Analyser/Analyse_Paragraphs
912  * FUNCTION
913  *   Analyse paragraphs... (TODO) 
914  * SYNPOPSIS
915  */
916 static void Analyse_Paragraphs(
917     struct RB_Item *arg_item )
918 /*
919  * INPUTS
920  *   * arg_item -- the item to be analysed.
921  * SOURCE
922  */
923 {
924     int                 i;
925     int                 in_par = FALSE;
926     int                 in_list = FALSE;
927     int                 in_pre = FALSE;
928     int                 is_empty = FALSE;
929     int                 prev_is_empty = FALSE;
930
931     for ( i = 0;
932           ( i < arg_item->no_lines )
933           && ( arg_item->lines[i]->kind == ITEM_LINE_PIPE ); ++i )
934     {
935         /* Empty */
936     }
937     assert( i < arg_item->no_lines );
938
939     if ( ( arg_item->lines[i]->format == 0 ) )
940     {
941         arg_item->lines[i]->format |= RBILA_BEGIN_PARAGRAPH;
942         in_par = TRUE;
943     }
944     for ( ; i < arg_item->no_lines; i++ )
945     {
946         char               *line = arg_item->lines[i]->line;
947
948         prev_is_empty = is_empty;
949         is_empty = Is_Empty_Line( line );
950         if ( arg_item->lines[i]->format & RBILA_BEGIN_LIST )
951         {
952             in_list = TRUE;
953         }
954         if ( arg_item->lines[i]->format & RBILA_BEGIN_PRE )
955         {
956             in_pre = TRUE;
957         }
958         if ( arg_item->lines[i]->format & RBILA_END_LIST )
959         {
960             in_list = FALSE;
961         }
962         if ( arg_item->lines[i]->format & RBILA_END_PRE )
963         {
964             in_pre = FALSE;
965         }
966         if ( in_par )
967         {
968             if ( ( arg_item->lines[i]->format & RBILA_BEGIN_LIST ) ||
969                  ( arg_item->lines[i]->format & RBILA_BEGIN_PRE ) ||
970                  is_empty )
971             {
972                 in_par = FALSE;
973                 arg_item->lines[i]->format |= RBILA_END_PARAGRAPH;
974             }
975         }
976         else
977         {
978             if ( ( arg_item->lines[i]->format & RBILA_END_LIST ) ||
979                  ( arg_item->lines[i]->format & RBILA_END_PRE ) ||
980                  ( !is_empty && prev_is_empty && !in_list && !in_pre ) )
981             {
982                 in_par = TRUE;
983                 arg_item->lines[i]->format |= RBILA_BEGIN_PARAGRAPH;
984             }
985         }
986     }
987     if ( in_par )
988     {
989         arg_item->lines[arg_item->no_lines - 1]->format |=
990             RBILA_END_PARAGRAPH;
991     }
992 }
993
994 /******/
995
996
997 /****f* Analyser/Analyse_Indentation
998  * FUNCTION
999  *  Figure out how text is indented. 
1000  * SYNPOPSIS
1001  */
1002 static int Analyse_Indentation(
1003     struct RB_Item *arg_item )
1004 /*
1005  * INPUTS
1006  *   * arg_item -- the item to be analysed.
1007  * SOURCE
1008  */
1009 {
1010     int                 i;
1011     int                 indent = -1;
1012
1013     assert( arg_item->no_lines > 0 );
1014
1015     for ( i = 0; i < arg_item->no_lines; ++i )
1016     {
1017         if ( arg_item->lines[i]->kind == ITEM_LINE_PLAIN )
1018         {
1019             char               *line = arg_item->lines[i]->line;
1020
1021             if ( Is_Empty_Line( line ) )
1022             {
1023                 /* Empty */
1024                 indent = 0;
1025             }
1026             else
1027             {
1028                 indent = Get_Indent( line );
1029                 break;
1030             }
1031         }
1032     }
1033     assert( indent >= 0 );
1034     return indent;
1035 }
1036
1037 /******/
1038
1039 /****f* Analyser/Analyse_Item_Format
1040  * FUNCTION
1041  *   Try to determine the formatting of an item.
1042  *   An empty line generates a new paragraph
1043  *   Things that are indented more that the rest of the text
1044  *   are preformatted.
1045  *   Things that start with a '*' or '-' create list items
1046  *   unless they are indented more that the rest of the text.
1047  * SYNPOPSIS
1048  */
1049 static void Analyse_Item_Format(
1050     struct RB_Item *arg_item )
1051 /*
1052  * INPUTS
1053  *   * arg_item -- the item to be analysed.
1054  * SOURCE
1055  */
1056 {
1057     // If item is not empty
1058     if ( arg_item->no_lines )
1059     {
1060         // If it is a SOURCE item
1061         if ( Works_Like_SourceItem( arg_item->type ) )
1062         {
1063             // Preformat it like a SOURCE item
1064             Preformat_All( arg_item, TRUE );
1065         }
1066         // Check if we have to analyse this item
1067         else if ( ( course_of_action.do_nopre
1068                     || Is_Format_Item( arg_item->type ) )
1069                   && !Is_Preformatted_Item( arg_item->type ) )
1070         {
1071             // analyse item
1072             int                 indent = Analyse_Indentation( arg_item );
1073
1074             Analyse_List( arg_item, indent );
1075             Analyse_Preformatted( arg_item, indent );
1076             Analyse_Paragraphs( arg_item );
1077         }
1078         // If none of above, preformat item
1079         else
1080         {
1081             // Preformat it
1082             Preformat_All( arg_item, FALSE );
1083         }
1084     }
1085     // Item is empty
1086     else
1087     {
1088         // Do nothing
1089     }
1090 }
1091
1092 /*****/
1093
1094
1095
1096 static int Trim_Empty_Item_Begin_Lines(
1097     struct RB_header *arg_header,
1098     struct RB_Item *arg_item,
1099     int current_index )
1100 {
1101
1102     char               *c;
1103
1104     if ( Works_Like_SourceItem( arg_item->type ) )
1105     {
1106         /* We skip the first line after the source item, if
1107          * it an remark end marker -- such as '*)'
1108          */
1109         c = arg_header->lines[current_index];
1110         if ( RB_Is_Remark_End_Marker( c ) )
1111         {
1112             c = RB_Skip_Remark_End_Marker( c );
1113             c = RB_Skip_Whitespace( c );
1114             if ( *c != '\0' )
1115             {
1116                 c = arg_header->lines[current_index];
1117                 RB_Warning( "text following a remark end marker:\n%s\n", c );
1118             }
1119             ++current_index;
1120         }
1121     }
1122
1123     if ( current_index > arg_item->end_index )
1124     {
1125         /* item is empty */
1126     }
1127     else
1128     {
1129         do
1130         {
1131             c = arg_header->lines[current_index];
1132             c = RB_Skip_Whitespace( c );
1133             if ( RB_Has_Remark_Marker( c ) )
1134             {
1135                 c = RB_Skip_Remark_Marker( c );
1136             }
1137             c = RB_Skip_Whitespace( c );
1138             if ( *c == '\0' )
1139             {
1140                 ++current_index;
1141             }
1142         }
1143         while ( ( *c == '\0' ) && ( current_index < arg_item->end_index ) );
1144     }
1145
1146     return current_index;
1147 }
1148
1149
1150
1151 static int Trim_Empty_Item_End_Lines(
1152     struct RB_header *arg_header,
1153     struct RB_Item *arg_item,
1154     int current_index )
1155 {
1156     char               *c;
1157
1158     if ( Works_Like_SourceItem( arg_item->type ) )
1159     {
1160         c = arg_header->lines[current_index];
1161         if ( RB_Is_Remark_Begin_Marker( c ) )
1162         {
1163             c = RB_Skip_Remark_Begin_Marker( c );
1164             c = RB_Skip_Whitespace( c );
1165             if ( *c != '\0' )
1166             {
1167                 c = arg_header->lines[current_index];
1168                 RB_Warning( "text following a remark begin marker:\n%s\n",
1169                             c );
1170             }
1171             --current_index;
1172         }
1173     }
1174
1175     do
1176     {
1177         c = arg_header->lines[current_index];
1178         c = RB_Skip_Whitespace( c );
1179         if ( RB_Has_Remark_Marker( c ) )
1180         {
1181             c = RB_Skip_Remark_Marker( c );
1182         }
1183         c = RB_Skip_Whitespace( c );
1184         if ( *c == '\0' )
1185         {
1186             --current_index;
1187         }
1188     }
1189     while ( ( *c == '\0' ) && ( current_index > arg_item->begin_index ) );
1190
1191     return current_index;
1192 }
1193
1194
1195
1196 static void Trim_Empty_Item_Lines(
1197     struct RB_header *arg_header,
1198     struct RB_Item *arg_item )
1199 {
1200     arg_item->no_lines = arg_item->end_index - arg_item->begin_index + 1;
1201     if ( arg_item->no_lines <= 1 )
1202     {
1203         /* item is empty */
1204         arg_item->no_lines = 0;
1205     }
1206     else
1207     {
1208         int                 current_index;
1209
1210         /* trim all empty lines at the begin of an item */
1211
1212         /* we skip the first line because that contains the item name.
1213          */
1214         current_index = arg_item->begin_index + 1;
1215         current_index =
1216             Trim_Empty_Item_Begin_Lines( arg_header, arg_item,
1217                                          current_index );
1218
1219         /* Is there anything left? */
1220         if ( current_index <= arg_item->end_index )
1221         {
1222             arg_item->begin_index = current_index;
1223
1224             /* trim all the empty lines at the end of an item */
1225             current_index = arg_item->end_index;
1226             current_index =
1227                 Trim_Empty_Item_End_Lines( arg_header, arg_item,
1228                                            current_index );
1229             if ( current_index >= arg_item->begin_index )
1230             {
1231                 arg_item->end_index = current_index;
1232                 arg_item->no_lines =
1233                     arg_item->end_index - arg_item->begin_index + 1;
1234             }
1235             else
1236             {
1237                 /* item is empty */
1238                 arg_item->no_lines = 0;
1239             }
1240         }
1241         else
1242         {
1243             /* item is empty */
1244             arg_item->no_lines = 0;
1245         }
1246     }
1247 }
1248
1249
1250
1251
1252 /* TODO This routine is way too long */
1253
1254 static void Copy_Lines_To_Item(
1255     struct RB_header *arg_header,
1256     struct RB_Item *arg_item )
1257 {
1258 #if 0
1259     printf( "%d\n%d\n%s\n%s\n",
1260             arg_item->begin_index,
1261             arg_item->end_index,
1262             arg_header->lines[arg_item->begin_index],
1263             arg_header->lines[arg_item->end_index] );
1264 #endif
1265
1266     Trim_Empty_Item_Lines( arg_header, arg_item );
1267
1268     if ( arg_item->no_lines > 0 )
1269     {
1270         int                 i = 0;
1271         int                 j = 0;
1272         struct RB_Item_Line *itemline = NULL;
1273         int                 tool_active = 0;    // Shows wether we are inside a tool body
1274
1275         /* Allocate enough memory for all the lines, plus one
1276          * extra line
1277          */
1278         ++arg_item->no_lines;
1279         arg_item->lines =
1280             calloc( arg_item->no_lines, sizeof( struct RB_Item_Line * ) );
1281         if ( !arg_item->lines )
1282         {
1283             RB_Panic( "Out of memory! %s\n", "Copy_Lines_To_Item" );
1284         }
1285
1286         /* And create an RB_Item_Line for each of them, and add
1287          * those to the RB_Item
1288          */
1289         for ( i = 0; i < arg_item->no_lines - 1; ++i )
1290         {
1291             char               *c =
1292                 arg_header->lines[arg_item->begin_index + i];
1293             /* TODO should be a Create_ItemLine() */
1294             itemline = malloc( sizeof( struct RB_Item_Line ) );
1295             if ( !itemline )
1296             {
1297                 RB_Panic( "Out of memory! %s (2)\n", "Copy_Lines_To_Item" );
1298             }
1299
1300             c = ExpandTab( c );
1301             c = RB_Skip_Whitespace( c );
1302             // Lines with remark marker
1303             if ( RB_Has_Remark_Marker( c )
1304                  && !Works_Like_SourceItem( arg_item->type ) )
1305             {
1306                 char               *c2, *c3;
1307                 int                 pipe_mode;
1308                 enum ItemLineKind   item_kind;
1309
1310                 c = RB_Skip_Remark_Marker( c );
1311                 c2 = RB_Skip_Whitespace( c );
1312                 if ( *c2 )
1313                 {
1314                     // Check wether a tool starts or ends
1315                     if ( ( c3 = Is_Tool( c2, &item_kind, &tool_active ) ) )
1316                     {
1317                         itemline->kind = item_kind;
1318                         c = c3;
1319                     }
1320                     // If we have an active tool, copy the body lines
1321                     else if ( tool_active )
1322                     {
1323                         itemline->kind = ITEM_LINE_TOOL_BODY;
1324                         c++;    // Skip space after the remark marker
1325                     }
1326                     // Check for pipes
1327                     else if ( ( c3 = Is_Pipe_Marker( c2, &pipe_mode ) ) )
1328                     {
1329                         itemline->kind = ITEM_LINE_PIPE;
1330                         itemline->pipe_mode = pipe_mode;
1331                         c = c3;
1332                     }
1333                     // Plain Items ...
1334                     else
1335                     {
1336                         itemline->kind = ITEM_LINE_PLAIN;
1337                     }
1338                 }
1339                 // Empty lines with remark markers and active tool
1340                 else if ( tool_active )
1341                 {
1342                     itemline->kind = ITEM_LINE_TOOL_BODY;
1343                 }
1344                 // Plain empty lines with remark markers...
1345                 else
1346                 {
1347                     itemline->kind = ITEM_LINE_PLAIN;
1348                 }
1349             }
1350             else
1351             {
1352                 itemline->kind = ITEM_LINE_RAW;
1353                 /* The is raw code, so we do not want to have the
1354                  * whitespace stripped of
1355                  */
1356                 c = arg_header->lines[arg_item->begin_index + i];
1357                 c = ExpandTab( c );
1358             }
1359
1360             if ( ( !Works_Like_SourceItem( arg_item->type ) &&
1361                    ( itemline->kind != ITEM_LINE_RAW ) ) ||
1362                  Works_Like_SourceItem( arg_item->type ) )
1363             {
1364                 itemline->line = RB_StrDup( c );
1365                 itemline->format = 0;
1366                 arg_item->lines[j] = itemline;
1367                 ++j;
1368             }
1369             else
1370             {
1371                 /* We dump the RAW item lines if we are not in a
1372                  * source item.
1373                  */
1374                 free( itemline );
1375             }
1376         }
1377
1378         if ( j > 0 )
1379         {
1380             /* And one empty line to mark the end of an item and
1381              * to be able to store some additional formatting actions
1382              */
1383             itemline = malloc( sizeof( struct RB_Item_Line ) );
1384             if ( !itemline )
1385             {
1386                 RB_Panic( "Out of memory! %s (3)\n", "Copy_Lines_To_Item" );
1387             }
1388
1389             itemline->kind = ITEM_LINE_END;
1390             itemline->line = RB_StrDup( "" );
1391             itemline->format = 0;
1392             arg_item->lines[j] = itemline;
1393
1394             /* Store the real number of lines we copied */
1395             assert( arg_item->no_lines >= ( j + 1 ) );
1396             arg_item->no_lines = j + 1;
1397         }
1398         else
1399         {
1400             arg_item->no_lines = 0;
1401             free( arg_item->lines );
1402             arg_item->lines = NULL;
1403         }
1404     }
1405     else
1406     {
1407         arg_item->no_lines = 0;
1408         arg_item->lines = NULL;
1409     }
1410 }
1411
1412
1413 /****f* Analyser/RB_Analyse_Items
1414  * FUNCTION
1415  *   Locate the items in the header and create RB_Item structures for
1416  *   them.
1417  * SYNPOPSIS
1418  */
1419 static int Analyse_Items(
1420     struct RB_header *arg_header )
1421 /*
1422  * SOURCE
1423  */
1424 {
1425     int                 line_nr;
1426     enum ItemType       item_type = NO_ITEM;
1427     struct RB_Item     *new_item;
1428     struct RB_Item     *cur_item;
1429
1430     RB_Item_Lock_Reset(  );
1431
1432     /* find the first item */
1433     for ( line_nr = 0; line_nr < arg_header->no_lines; ++line_nr )
1434     {
1435         item_type = RB_Is_ItemName( arg_header->lines[line_nr] );
1436         if ( item_type != NO_ITEM )
1437         {
1438             break;
1439         }
1440     }
1441
1442     /* and all the others */
1443     while ( ( item_type != NO_ITEM ) && ( line_nr < arg_header->no_lines ) )
1444     {
1445         new_item = RB_Create_Item( item_type );
1446         new_item->begin_index = line_nr;
1447
1448         /* Add the item to the end of the list of items. */
1449         if ( arg_header->items )
1450         {
1451             for ( cur_item = arg_header->items; cur_item->next;
1452                   cur_item = cur_item->next )
1453             {
1454                 /* Empty */
1455             }
1456             cur_item->next = new_item;
1457         }
1458         else
1459         {
1460             arg_header->items = new_item;
1461         }
1462         /* Find the next item */
1463         for ( ++line_nr; line_nr < arg_header->no_lines; ++line_nr )
1464         {
1465             item_type = RB_Is_ItemName( arg_header->lines[line_nr] );
1466             if ( item_type != NO_ITEM )
1467             {
1468                 break;
1469             }
1470         }
1471
1472         /* This points to the last line in the item */
1473         new_item->end_index = line_nr - 1;
1474
1475         assert( new_item->end_index >= new_item->begin_index );
1476
1477         /* Now analyse and copy the lines */
1478         Copy_Lines_To_Item( arg_header, new_item );
1479         Analyse_Item_Format( new_item );
1480         /* Handy for debugging wiki formatting 
1481          *   Dump_Item( new_item );
1482          */
1483     }
1484
1485     return 0;
1486 }
1487
1488 /******/
1489
1490
1491
1492 /****f* Analyser/ToBeAdded
1493  * FUNCTION
1494  *   Test whether or not a header needs to be added to the
1495  *   list of headers. This implements the options 
1496  *      --internal 
1497  *   and
1498  *      --internalonly
1499  * SYNPOPSIS
1500  */
1501 static int ToBeAdded(
1502     struct RB_Document *document,
1503     struct RB_header *header )
1504 /*
1505  * INPUTS
1506  *   o document  -- a document (to determine the options)
1507  *   o header    -- a header
1508  * RESULT
1509  *   TRUE  -- Add header
1510  *   FALSE -- Don't add header
1511  * SOURCE
1512  */
1513 {
1514     int                 add = FALSE;
1515
1516     if ( header->is_internal )
1517     {
1518         if ( ( document->actions.do_include_internal ) ||
1519              ( document->actions.do_internal_only ) )
1520         {
1521             add = TRUE;
1522         }
1523         else
1524         {
1525             add = FALSE;
1526         }
1527     }
1528     else
1529     {
1530         if ( document->actions.do_internal_only )
1531         {
1532             add = FALSE;
1533         }
1534         else
1535         {
1536             add = TRUE;
1537         }
1538     }
1539     return add;
1540 }
1541
1542 /******/
1543
1544
1545
1546 /****f* Analyser/Grab_Header
1547  * FUNCTION
1548  *   Grab a header from a source file, that is scan a source file
1549  *   until the start of a header is found.  Then search for the end
1550  *   of a header and store all the lines in between.
1551  * SYNPOPSIS
1552  */
1553 static struct RB_header *Grab_Header(
1554     FILE *sourcehandle,
1555     struct RB_Document *arg_document )
1556 /*
1557  * INPUTS
1558  *   o sourcehandle -- an opened source file.
1559  * OUTPUT
1560  *   o sourcehandle -- will point to the line following the end marker.
1561  * RESULT
1562  *   0 if no header was found, or a pointer to a new header otherwise.
1563  * SOURCE
1564  */
1565 {
1566     struct RB_header   *new_header = NULL;
1567     int                 is_internal = 0;
1568     struct RB_HeaderType *header_type = NULL;
1569     int                 good_header = FALSE;
1570     int                 reuse = FALSE;
1571
1572     do
1573     {
1574         good_header = FALSE;
1575         header_type = RB_Find_Marker( sourcehandle, &is_internal, reuse );
1576         reuse = FALSE;
1577         if ( header_type )
1578         {
1579             struct RB_header   *duplicate_header = NULL;
1580             long                previous_line = 0;
1581
1582             new_header = RB_Alloc_Header(  );
1583             new_header->htype = header_type;
1584             new_header->is_internal = is_internal;
1585
1586             if ( Find_Header_Name( sourcehandle, new_header ) )
1587             {
1588                 new_header->line_number = line_number;
1589                 RB_Say( "found header [line %5d]: \"%s\"\n", SAY_DEBUG,
1590                         line_number, new_header->name );
1591                 duplicate_header =
1592                     RB_Document_Check_For_Duplicate( arg_document,
1593                                                      new_header );
1594                 if ( duplicate_header )
1595                 {
1596                     /* Duplicate headers do not crash the program so
1597                      * we accept them.  But we do warn the user.
1598                      */
1599                     RB_Warning
1600                         ( "A header with the name \"%s\" already exists.\n  See %s(%d)\n",
1601                           new_header->name,
1602                           Get_Fullname( duplicate_header->owner->filename ),
1603                           duplicate_header->line_number );
1604                 }
1605
1606                 if ( ( new_header->function_name =
1607                        Function_Name( new_header->name ) ) == NULL )
1608                 {
1609                     RB_Warning( "Can't determine the \"function\" name.\n" );
1610                     RB_Free_Header( new_header );
1611                     new_header = NULL;
1612                 }
1613                 else
1614                 {
1615                     if ( ( new_header->module_name =
1616                            Module_Name( new_header->name ) ) == NULL )
1617                     {
1618                         RB_Warning
1619                             ( "Can't determine the \"module\" name.\n" );
1620                         RB_Free_Header( new_header );
1621                         new_header = NULL;
1622                     }
1623                     else
1624                     {
1625                         previous_line = line_number;
1626                         if ( Find_End_Marker( sourcehandle, new_header ) ==
1627                              0 )
1628                         {
1629                             RB_Warning
1630                                 ( "found header on line %d with name \"%s\"\n"
1631                                   "  but I can't find the end marker\n",
1632                                   previous_line, new_header->name );
1633                             /* Reuse the current line while finding the next
1634                              * Marking using RB_Find_Marker()
1635                              */
1636                             reuse = TRUE;
1637                             RB_Free_Header( new_header );
1638                             new_header = NULL;
1639                         }
1640                         else
1641                         {
1642                             RB_Say( "found end header [line %5d]:\n",
1643                                     SAY_DEBUG, line_number );
1644                             /* Good header found, we can stop */
1645                             good_header = TRUE;
1646                         }
1647                     }
1648                 }
1649             }
1650             else
1651             {
1652                 RB_Warning( "found header marker but no name\n" );
1653                 RB_Free_Header( new_header );
1654                 new_header = NULL;
1655             }
1656         }
1657         else
1658         {
1659             /* end of the file */
1660             good_header = TRUE;
1661         }
1662     }
1663     while ( !good_header );
1664     return new_header;
1665 }
1666
1667 /*******/
1668
1669
1670
1671 /****f* Analyser/Module_Name
1672  * FUNCTION
1673  *   Get the module name from the header name.  The header name will be
1674  *   something like
1675  *
1676  *     module/functionname.
1677  *
1678  * SYNPOPSIS
1679  */
1680 static char        *Module_Name(
1681     char *header_name )
1682 /*
1683  * INPUTS
1684  *   o header_name -- a pointer to a nul terminated string.
1685  * RESULT
1686  *   Pointer to the modulename.  You're responsible for freeing it.
1687  * SEE ALSO
1688  *   Function_Name()
1689  * SOURCE
1690  */
1691 {
1692     char               *cur_char;
1693     char                c;
1694     char               *name = NULL;
1695
1696     assert( header_name );
1697
1698     for ( cur_char = header_name; *cur_char && *cur_char != '/'; ++cur_char );
1699     if ( *cur_char )
1700     {
1701         c = *cur_char;
1702         *cur_char = '\0';
1703         name = RB_StrDup( header_name );
1704         *cur_char = c;
1705     }
1706     return name;
1707 }
1708
1709 /******/
1710
1711
1712
1713 /****f* Analyser/Function_Name
1714  * FUNCTION
1715  *   A header name is consists of two parts. The module name and
1716  *   the function name. This returns a pointer to the function name.
1717  *   The name "function name" is a bit obsolete. It is really the name
1718  *   of any of objects that can be documented; classes, methods,
1719  *   variables, functions, projects, etc.
1720  * SYNOPSIS
1721  */
1722 static char        *Function_Name(
1723     char *header_name )
1724 /*
1725  * SOURCE
1726  */
1727 {
1728     char               *cur_char;
1729     char               *name;
1730
1731     name = NULL;
1732     if ( ( cur_char = header_name ) != NULL )
1733     {
1734         for ( ; *cur_char != '\0'; ++cur_char )
1735         {
1736             if ( '/' == *cur_char )
1737             {
1738                 ++cur_char;
1739                 if ( *cur_char )
1740                 {
1741                     name = cur_char;
1742                     break;
1743                 }
1744             }
1745         }
1746     }
1747     if ( name )
1748     {
1749         return RB_StrDup( name );
1750     }
1751     else
1752     {
1753         return ( name );
1754     }
1755 }
1756
1757 /*** Function_Name ***/
1758
1759
1760 /****f* Analyser/RB_Find_Marker
1761  * NAME
1762  *   RB_Find_Marker -- Search for header marker in document.
1763  * FUNCTION
1764  *   Read document file line by line, and search each line for 
1765  *   any of the headers defined in the array  header_markers (OR
1766  *   if using the -rh switch, robo_head)
1767  * SYNOPSIS
1768  */
1769 static struct RB_HeaderType *RB_Find_Marker(
1770     FILE *document,
1771     int *is_internal,
1772     int reuse_previous_line )
1773 /*
1774  * INPUTS
1775  *   document - pointer to the file to be searched.
1776  *   the gobal buffer line_buffer.
1777  * OUTPUT
1778  *   o document will point to the line after the line with 
1779  *     the header marker.
1780  *   o is_internal will be TRUE if the header is an internal
1781  *     header.
1782  * RESULT
1783  *   o header type
1784  * BUGS
1785  *   Bad use of feof(), fgets().
1786  * SEE ALSO
1787  *   Find_End_Marker
1788  * SOURCE
1789  */
1790 {
1791     int                 found;
1792     char               *cur_char;
1793     struct RB_HeaderType *header_type = 0;
1794
1795     cur_char = NULL;
1796     found = FALSE;
1797     while ( !feof( document ) && !found )
1798     {
1799         if ( reuse_previous_line )
1800         {
1801             /* reuse line in the line_buffer */
1802             reuse_previous_line = FALSE;
1803         }
1804         else
1805         {
1806             RB_FreeLineBuffer(  );
1807             myLine = RB_ReadWholeLine( document, line_buffer, &readChars );
1808         }
1809         if ( !feof( document ) )
1810         {
1811             line_number++;
1812             found = RB_Is_Begin_Marker( myLine, &cur_char );
1813             if ( found )
1814             {
1815                 header_type = AnalyseHeaderType( &cur_char, is_internal );
1816                 RB_Say( "found header marker of type %s\n", SAY_DEBUG,
1817                         header_type->indexName );
1818             }
1819         }
1820     }
1821
1822     return header_type;
1823 }
1824
1825 /******** END RB_Find_Marker ******/
1826
1827
1828 /****f* Analyser/AnalyseHeaderType
1829  * FUNCTION
1830  *   Determine the type of the header.
1831  * SYNOPSIS
1832  */
1833 struct RB_HeaderType *AnalyseHeaderType(
1834     char **cur_char,
1835     int *is_internal )
1836 /*
1837  * INPUTS
1838  *   o cur_char -- pointer to the header type character
1839  * OUTPUT
1840  *   o is_internal -- indicates if it is an internal header or not.*
1841  *   o cur_char -- points to the header type character
1842  * RESULT
1843  *   o pointer to a RB_HeaderType
1844  * SOURCE
1845  */
1846 {
1847     struct RB_HeaderType *headertype = 0;
1848
1849     *is_internal = RB_IsInternalHeader( **cur_char );
1850
1851     if ( *is_internal )
1852     {
1853         /* Skip the character */
1854         ++( *cur_char );
1855     }
1856     headertype = RB_FindHeaderType( **cur_char );
1857     if ( !headertype )
1858     {
1859         RB_Panic( "Undefined headertype (%c)\n", **cur_char );
1860     }
1861
1862     return headertype;
1863 }
1864
1865 /*******/
1866
1867
1868
1869 /****f* Analyser/Find_End_Marker
1870  * FUNCTION
1871  *   Scan and store all lines from a source file until
1872  *   an end marker is found.
1873  * SYNOPSIS
1874  */
1875 static int Find_End_Marker(
1876     FILE *document,
1877     struct RB_header *new_header )
1878 /*
1879  * INPUTS
1880  *   o document -- a pointer to an opened source file.
1881  * OUTPUT
1882  *   o new_header -- the lines of source code will be added
1883  *                   here.
1884  * RESULT
1885  *   o TRUE  -- an end marker was found.
1886  *   o FALSE -- no end marker was found while scanning the
1887  *              source file.
1888  * SOURCE
1889  */
1890 {
1891     int                 found = FALSE;
1892     unsigned int        no_lines = 0;
1893     unsigned int        max_no_lines = 10;
1894     char              **lines = NULL;
1895     char              **new_lines = NULL;
1896     char               *dummy;
1897
1898     lines = ( char ** ) calloc( max_no_lines, sizeof( char * ) );
1899     if ( !lines )
1900     {
1901         RB_Panic( "Out of memory! %s()\n", "Find_End_Marker" );
1902     }
1903
1904     while ( !feof( document ) )
1905     {
1906         RB_FreeLineBuffer(  );
1907         myLine = RB_ReadWholeLine( document, line_buffer, &readChars );
1908         ++line_number;          /* global linecounter, koessi */
1909         if ( RB_Is_Begin_Marker( myLine, &dummy ) )
1910         {
1911             /* Bad... found a begin marker but was expecting to
1912                find an end marker.  Panic... */
1913             found = FALSE;
1914             return found;
1915         }
1916         else if ( RB_Is_End_Marker( myLine ) )
1917         {
1918             RB_Say( "Found end marker \"%s\"", SAY_DEBUG, myLine );
1919             found = TRUE;
1920             break;
1921         }
1922         else
1923         {
1924             unsigned int        n;
1925             char               *line;
1926
1927             line = RB_StrDup( myLine );
1928             n = strlen( line );
1929             assert( n > 0 );
1930             assert( line[n - 1] == '\n' );
1931             /* Strip CR */
1932             line[n - 1] = '\0';
1933             lines[no_lines] = line;
1934             ++no_lines;
1935             if ( no_lines == max_no_lines )
1936             {
1937                 max_no_lines *= 2;
1938                 new_lines = realloc( lines, max_no_lines * sizeof( char * ) );
1939
1940                 if ( !new_lines )
1941                 {
1942                     RB_Panic( "Out of memory! %s()\n", "Find_End_Marker" );
1943                 }
1944
1945                 lines = new_lines;
1946             }
1947         }
1948     }
1949
1950     new_header->no_lines = no_lines;
1951     new_header->lines = lines;
1952
1953     return found;
1954 }
1955
1956
1957 /******/
1958
1959
1960 /* TODO Documentation */
1961 static void Remove_Trailing_Asterics(
1962     char *line )
1963 {
1964     int                 i = strlen( line ) - 1;
1965
1966     for ( ; ( i > 0 ) && utf8_isspace( line[i] ); i-- )
1967     {
1968         /* Empty */
1969     }
1970     for ( ; ( i > 0 ) && ( line[i] == '*' ); i-- )
1971     {
1972         line[i] = ' ';
1973     }
1974 }
1975
1976
1977 /****if* Utilities/RB_WordWithSpacesLen
1978  * SYNOPSIS
1979  */
1980 static int RB_WordWithSpacesLen(
1981     char *str )
1982 /*
1983  * FUNCTION
1984  *   get the amount of bytes until next separator character or ignore character
1985  *   or end of line
1986  * INPUTS
1987  *   char *str      -- the line
1988  * RESULT
1989  *   int -- length of the next word or 0
1990  * SEE ALSO
1991  *   RB_Find_Header_Name()
1992  * SOURCE
1993  */
1994 {
1995     int                 len;
1996     char                c;
1997
1998     for ( len = 0; ( ( c = *str ) != '\0' ) && ( c != '\n' ); ++str, ++len )
1999     {
2000         // Look for header truncating characters
2001         if ( Find_Parameter_Char( &( configuration.header_separate_chars ),
2002                                   c ) != NULL
2003              ||
2004              Find_Parameter_Char( &( configuration.header_ignore_chars ),
2005                                   c ) != NULL )
2006         {
2007             // and exit loop if any found
2008             break;
2009         }
2010     }
2011     /* Don't count any of the trailing spaces. */
2012     if ( len )
2013     {
2014         --str;
2015         for ( ; utf8_isspace( *str ); --len, --str )
2016         {
2017             /* empty */
2018         }
2019     }
2020     return ( len );
2021 }
2022
2023 /*** RB_WordWithSpacesLen ***/
2024
2025
2026 /* TODO Documentation */
2027 static int Find_Header_Name(
2028     FILE *fh,
2029     struct RB_header *hdr )
2030 {
2031     char               *cur_char = myLine;
2032     char              **names = NULL;
2033     int                 num = 0;
2034
2035     Remove_Trailing_Asterics( cur_char );
2036     skip_while( *cur_char != '*' );
2037     skip_while( !utf8_isspace( *cur_char ) );
2038     skip_while( utf8_isspace( *cur_char ) );
2039     while ( *cur_char )
2040     {
2041         int                 length = RB_WordWithSpacesLen( cur_char );
2042
2043         if ( length == 0 )
2044             break;
2045         names = realloc( names, ( ++num ) * sizeof *names );
2046
2047         if ( !names )
2048         {
2049             RB_Panic( "Out of memory! %s()\n", "Find_Header_Name" );
2050         }
2051
2052         names[num - 1] = RB_StrDupLen( cur_char, length );
2053         /* printf("%c adding name = %s\n", num > 1 ? ' ' : '*', names[ num - 1 ] ); */
2054         cur_char += length;
2055         if ( Find_Parameter_Char( &( configuration.header_separate_chars ),
2056                                   *cur_char ) )
2057         {
2058             for ( cur_char++; utf8_isspace( *cur_char ); cur_char++ );
2059             /* End of line reach, but comma encountered, more headernames follow on next line */
2060             if ( *cur_char == 0 )
2061             {
2062                 /* Skip comment */
2063                 RB_FreeLineBuffer(  );
2064                 myLine = RB_ReadWholeLine( fh, line_buffer, &readChars );
2065                 line_number++;
2066                 for ( cur_char = myLine; *cur_char && !utf8_isalpha( *cur_char );
2067                       cur_char++ );
2068             }
2069         }
2070     }
2071     hdr->names = names;
2072     hdr->no_names = num;
2073     hdr->name = num ? names[0] : NULL;
2074     return num;
2075 }
2076
2077 /*****  Find_Header_Name  *****/