Imported Robodoc.
[robodoc.git] / Source / roboconfig.c
1 // vi: spell ff=unix
2 /*
3 Copyright (C) 1994-2007  Frans Slothouber, Jacco van Weert, Petteri Kettunen,
4 Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
5 Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
6
7 This file is part of ROBODoc
8
9 ROBODoc is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22 */
23
24
25 /****h* ROBODoc/Configuration
26  * FUNCTION
27  *   Functions to access the ROBODoc configuration and configuration
28  *   file (robodoc.rc) or the file specified with the --rc option.
29  *
30  *   The robodoc.rc file consists of a number of blocks.  Each
31  *   block starts with a line of the form 
32  *
33  *     <block name>:
34  *
35  *   This is followed by a number of lines of data.  Each line starts
36  *   with at least one space followed by the actual data.
37  *
38  *   This module parses this data and stores it in the global
39  *   configuration.
40  *
41  * NOTES
42  *   Is missing a lot of documentation.
43  *
44  *   You can not use RB_Say() in this module since the --tell flag
45  *   won't be parsed until after this module has finished.
46  *
47  ******
48  * $Id: roboconfig.c,v 1.55 2007/07/10 19:13:52 gumpu Exp $
49  */
50
51 #include <stdio.h>
52 #include <assert.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <ctype.h>
56 #include "headertypes.h"
57 #include "util.h"
58 #include "roboconfig.h"
59 #include "globals.h"
60 #include "optioncheck.h"
61
62 #ifdef DMALLOC
63 #include <dmalloc.h>
64 #endif
65
66
67 /****v* Configuration/default_item_names
68  * FUNCTION
69  *   Defines the names of items that ROBODoc recognized as
70  *   items by default if none are specified in the
71  *   robodoc.rc file.
72  * SOURCE
73  */
74
75 static char        *default_item_names[] = {
76     "SOURCE",                   /* source code inclusion */
77     "NAME",                     /* Item name + short description */
78     "COPYRIGHT",                /* who own the copyright : "(c) <year>-<year> by <company/person>" */
79     "SYNOPSIS", "USAGE",        /* how to use it */
80     "FUNCTION", "DESCRIPTION", "PURPOSE",       /* what does it */
81     "AUTHOR",                   /* who wrote it */
82     "CREATION DATE",            /* when did the work start */
83     "MODIFICATION HISTORY", "HISTORY",  /* who done what changes when */
84     "INPUTS", "ARGUMENTS", "OPTIONS", "PARAMETERS", "SWITCHES", /* what can we feed into it */
85     "OUTPUT", "SIDE EFFECTS",   /* what output will be made */
86     "RESULT", "RETURN VALUE",   /* what do we get returned */
87     "EXAMPLE",                  /* a clear example of the items use */
88     "NOTES",                    /* any annotations */
89     "DIAGNOSTICS",              /* diagnostical output */
90     "WARNINGS", "ERRORS",       /* warning & error-messages */
91     "BUGS",                     /* known bugs */
92     "TODO", "IDEAS",            /* what to implement next & ideas */
93     "PORTABILITY",              /* where does it come from, where will it work */
94     "SEE ALSO",                 /* references */
95     "METHODS", "NEW METHODS",   /* oop methods */
96     "ATTRIBUTES", "NEW ATTRIBUTES",     /* oop attributes */
97     "TAGS",                     /* tagitem description */
98     "COMMANDS",                 /* command description */
99     "DERIVED FROM",             /* oop super class */
100     "DERIVED BY",               /* oop sub class */
101     "USES", "CHILDREN",         /* what modules are used by this one */
102     "USED BY", "PARENTS",       /* which modules do use this */
103     NULL,                       /* don't delete, so we can count how many there are... */
104 };
105
106 /***********/
107
108 static char        *default_remark_begin_markers[] = {
109     "/*",
110     "(*",
111     "<!--",
112     "{*",
113     NULL
114 };
115
116 static char        *default_remark_end_markers[] = {
117     "*/",
118     "*)",
119     "-->",
120     "*}",
121     NULL
122 };
123
124 /****v* Configuration/c_keywords
125  * FUNCTION
126  *   The default C keywords.
127  * SOURCE
128  */
129
130 static char        *c_keywords[] = {
131
132     // ANSI C Keywords
133     "auto",
134     "break",
135     "case",
136     "char",
137     "const",
138     "continue",
139     "default",
140     "do",
141     "double",
142     "else",
143     "enum",
144     "extern",
145     "float",
146     "for",
147     "goto",
148     "if",
149     "int",
150     "long",
151     "register",
152     "return",
153     "short",
154     "signed",
155     "sizeof",
156     "static",
157     "struct",
158     "switch",
159     "typedef",
160     "union",
161     "unsigned",
162     "void",
163     "volatile",
164     "while",
165
166     // Some preprocessor directives
167     "#include",
168     "#define",
169     "#undef",
170     "#if",
171     "#else",
172     "#elif",
173     "#endif",
174     "#ifdef",
175     "#ifndef",
176     "#pragma",
177
178     NULL,                       /* don't delete, so we can count how many there are... */
179 };
180
181 // C comments
182 #define C_LINE_COMMENT        "//"
183 #define C_BLOCK_COMMENT_START "/*"
184 #define C_BLOCK_COMMENT_END   "*/"
185
186 /***********/
187
188
189 // Default header separation character
190 static char        *default_header_separate_chars[] = {
191     ",",
192     NULL
193 };
194
195
196 // Default header ignore character (to include remarks, version data, etc...)
197 static char        *default_header_ignore_chars[] = {
198     "[",
199     NULL
200 };
201
202
203 /* Maximum length of a line in the configuration file */
204 #define BUFFER_LENGTH 2048
205
206
207 /****v* Configuration/configuration
208  * FUNCTION
209  *   This global stores all the configuration parameters specified on
210  *   the command line and in the robodoc.rc file.
211  * SOURCE
212  */
213
214 struct RB_Configuration configuration;
215
216 /*****/
217
218
219 static T_Block_Kind BlockKind(
220     char *line );
221 static T_Line_Kind  ConfigLineKind(
222     char *line );
223 static void         SecondScan(
224     FILE *f );
225 static void         Alloc_Parameters(
226     struct Parameters *parameters,
227     unsigned int size );
228 static void         AddParameter(
229     char *name,
230     struct Parameters *parameters );
231 static void         GetParameters(
232     char *line,
233     struct Parameters *parameters );
234 static void         Install_Custom_HeaderTypes(
235     void );
236 static void         Complement_Remark_Markers(
237     void );
238 static void         ComplementItemNames(
239     void );
240 static void         ComplementHeaderMarkers(
241     void );
242 static char        *Get_rc(
243     char *rcname );
244
245
246 /****v* Configuration/keywords_hash_mask
247  * FUNCTION
248  *   Mask for keyword hash function.
249  *   This mask reduces the hash value for the actual hash table size.
250  *   Also the size of the hash table can be derived from this mask:
251  *     hash table size = keywords_hash_mask + 1
252  * SOURCE
253  */
254 static unsigned int keywords_hash_mask;
255
256 /*****/
257
258 /****v* Configuration/keywords_hash
259  * FUNCTION
260  *   This is the hash table for the keywords.
261  *   See keywords_hash_s.
262  * SOURCE
263  */
264 static struct keywords_hash_s **keywords_hash;
265
266 /*****/
267
268 /****f* Configuration/allocate_keywords_hash_table
269  * FUNCTION
270  *   Allocates space for the keyword hash table.
271  *
272  *   The size of the table depends on the number of keywords rounded up to the
273  *   next power of two.
274  * SYNOPSIS
275  */
276 void allocate_keywords_hash_table(
277     void )
278 /*
279  * SOURCE
280  */
281 {
282     unsigned int        i;
283
284     // Calculate hash table size (powers of two)
285     for ( keywords_hash_mask = 2;
286           keywords_hash_mask < configuration.keywords.number;
287           keywords_hash_mask <<= 1 );
288     keywords_hash_mask -= 1;
289
290     // Allocate space for hash table
291     keywords_hash = RB_malloc( ( keywords_hash_mask + 1 ) *
292                                sizeof( struct keywords_hash_s * ) );
293
294     // Zero out all rows
295     for ( i = 0; i <= keywords_hash_mask; i++ )
296     {
297         keywords_hash[i] = NULL;
298     }
299 }
300
301 /*****/
302
303 /****f* Configuration/Hash_Keyword
304  * FUNCTION
305  *   Calculate the hash value for a string
306  *
307  *   The hash value is based on the CRC32 hash function. It is then reduced by
308  *   an AND operation to the actual size of the hash table.
309  * SYNOPSIS
310  */
311 unsigned long Hash_Keyword(
312     char *keyword,
313     unsigned int len )
314 /*
315  * INPUTS
316  *   - keyword -- The keyword string
317  *   - len     -- The length of the keyword string
318  * RETURN VALUE
319  *   The hash value for the keyword.
320  * SOURCE
321  */
322 {
323     // Generate hash value
324     return RB_crc32( ( unsigned char * ) keyword, len,
325                      len ) & keywords_hash_mask;
326 }
327
328 /*****/
329
330
331 /****f* Configuration/add_to_keywords_hash_table
332  * FUNCTION
333  *   Add a keyword to the hash table
334  * SYNOPSIS
335  */
336 void add_to_keywords_hash_table(
337     char *keyword )
338 /*
339  * INPUTS
340  *   keyword -- The keyword string
341  * SOURCE
342  */
343 {
344     struct keywords_hash_s *tmp, **curr;
345     unsigned long       hash;
346
347     // Allocate space for new entry in hash table
348     tmp = RB_malloc( sizeof( struct keywords_hash_s ) );
349     // and initialise it
350     tmp->keyword = keyword;
351     tmp->next = NULL;
352
353     // Calculate hash value
354     hash = Hash_Keyword( keyword, strlen( keyword ) );
355
356     // Seek to last element in hash table row
357     for ( curr = &( keywords_hash[hash] ); *curr;
358           curr = &( ( *curr )->next ) );
359
360     // Insert entry into hash table
361     *curr = tmp;
362 }
363
364 /*****/
365
366
367 /****f* Configuration/Find_Keyword
368  * FUNCTION
369  *   Find a keyword in the hash table
370  * SYNOPSIS
371  */
372 char               *Find_Keyword(
373     char *keyword,
374     int len )
375 /*
376  * INPUTS
377  *   - keyword -- The keyword string
378  *   - len     -- The length of the keyword string
379  * RETURN VALUE
380  *   - pointer to the found keyword string in hash table or
381  *   - NULL if the keyword is not found
382  * SOURCE
383  */
384 {
385     unsigned long       hash;
386     struct keywords_hash_s *curr;
387
388     // Calculate hash value
389     hash = Hash_Keyword( keyword, len );
390
391     // Seek through hash table row
392     for ( curr = keywords_hash[hash]; curr; curr = curr->next )
393     {
394         // Check for keyword in row element
395         if ( !strncmp( keyword, curr->keyword, len ) )
396         {
397             // Found it!
398             return curr->keyword;
399         }
400     }
401
402     // Keyword not found
403     return NULL;
404 }
405
406 /*****/
407
408
409 /****f* Configuration/add_keywords_to_hash_table
410  * FUNCTION
411  *   Initalize hash table and add all keywords from configuration.keywords
412  *   to the hash table
413  * SOURCE
414  */
415 void add_keywords_to_hash_table(
416     void )
417 {
418     unsigned int        i;
419
420     // If nothing to add, exit
421     if ( !configuration.keywords.number )
422         return;
423
424     // Make some allocations
425     Make_crc32_table(  );
426     allocate_keywords_hash_table(  );
427
428     // Add keywords to hash table
429     for ( i = 0; i < configuration.keywords.number; i++ )
430     {
431         add_to_keywords_hash_table( configuration.keywords.names[i] );
432     }
433 }
434
435 /*****/
436
437
438 /* TODO Documentation */
439 static void AllocOptions(
440     unsigned int argc,
441     char **argv )
442 {
443     unsigned int        i;
444
445     Alloc_Parameters( &( configuration.options ), argc );
446     for ( i = 0; i < argc; ++i )
447     {
448         AddParameter( argv[i], &( configuration.options ) );
449     }
450 }
451
452
453 /* TODO Documentation */
454
455 char               *Get_rc(
456     char *rcname )
457 {
458     char               *s = NULL;
459     char               *s2 = NULL;
460     char               *path = NULL;
461
462     if ( Stat_Path( 'e', rcname ) && Stat_Path( 'f', rcname ) )
463     {
464         return RB_StrDup( rcname );
465     }
466     else
467     {
468         if ( strchr( rcname, ':' ) || strchr( rcname, '/' ) )
469         {
470             /* The rc names is a proper path, and not just a filename,
471              * we stop searching */
472         }
473         else
474         {
475             s = getenv( "HOME" );
476             if ( !s )
477             {
478                 if ( ( s = getenv( "HOMEDRIVE" ) )
479                      && ( s2 = getenv( "HOMEPATH" ) ) )
480                 {
481                     /* HOMEDRIVE includes backslash */
482                     path =
483                         ( char * ) malloc( sizeof( char ) *
484                                            ( strlen( s ) + strlen( s2 ) + 1 +
485                                              1 + strlen( rcname ) ) );
486                     sprintf( path, "%s%s%c%s", s, s2, '\\', rcname );
487                 }
488                 else
489                 {
490                     return NULL;
491                 }
492             }
493             else
494             {
495                 path =
496                     ( char * ) malloc( sizeof( char ) *
497                                        ( strlen( s ) + 1 + 1 +
498                                          strlen( rcname ) ) );
499                 sprintf( path, "%s%c%s", s, '/', rcname );
500             }
501
502             if ( path && Stat_Path( 'e', path ) && Stat_Path( 'f', path ) )
503             {
504                 return path;
505             }
506             else
507             {
508                 char* sitespecific =
509 #ifdef ROBO_PREFIX
510                 ROBO_PREFIX
511                 "/share/doc/robodoc/";
512 #else
513                 "/usr/local/robodoc/";
514 #endif
515
516                 if ( path )
517                 {
518                     free( path );
519                 }
520
521                 path =
522                     ( char * ) malloc( sizeof( char ) *
523                                        ( strlen( sitespecific ) + 1 +
524                                          strlen( rcname ) ) );
525                 sprintf( path, "%s%s", sitespecific, rcname );
526
527                 /* default failed -- try site-specific config file */
528                 if ( Stat_Path( 'e', path ) && Stat_Path( 'f', path ) )
529                 {
530                     /* site-specific file can be stat'ed */
531                     return path;
532                 }
533                 else
534                 {
535                     free( path );
536                     return NULL;
537                 }
538             }
539         }
540     }
541     return NULL;
542 }
543
544
545 /****f* Configuration/ReadConfiguration
546  * FUNCTION
547  *   Read the robodoc configuration file, and create
548  *   a RB_Configuration structure.
549  * SYNOPSIS
550  */
551
552 char               *ReadConfiguration(
553     unsigned int argc,
554     char **argv,
555     char *filename )
556 /*
557  * INPUTS
558  *   o argc -- the arg count as received by main()
559  *   o argv -- the arg valules as received by main()
560  *   o filename -- an optional filename.  If none is given,
561  *               "robodoc.rc" is used.
562  * RESULT
563  *   An initialized configuration (a global).
564  * SOURCE
565  */
566 {
567     FILE               *f = NULL;
568     char               *path = NULL;
569
570     if ( filename )
571     {
572         path = Get_rc( filename );
573         if ( path )
574         {
575             f = fopen( path, "r" );
576         }
577         if ( !f )
578         {
579             /* It should open as the user claimed it exists somewhere */
580             RB_Panic( "Can't open %s\n", filename );
581         }
582     }
583     else
584     {
585         /* Try the default rc file */
586         path = Get_rc( "robodoc.rc" );
587         if ( path )
588         {
589             f = fopen( path, "r" );
590         }
591     }
592
593     AllocOptions( argc, argv );
594     Alloc_Parameters( &( configuration.items ), 10 );
595     Alloc_Parameters( &( configuration.ignore_items ), 10 );
596     Alloc_Parameters( &( configuration.source_items ), 10 );
597     Alloc_Parameters( &( configuration.preformatted_items ), 10 );
598     Alloc_Parameters( &( configuration.format_items ), 10 );
599     Alloc_Parameters( &( configuration.item_order ), 10 );
600
601     Alloc_Parameters( &( configuration.custom_headertypes ), 10 );
602     Alloc_Parameters( &( configuration.ignore_files ), 10 );
603     Alloc_Parameters( &( configuration.accept_files ), 10 );
604     Alloc_Parameters( &( configuration.header_markers ), 10 );
605     Alloc_Parameters( &( configuration.remark_markers ), 10 );
606     Alloc_Parameters( &( configuration.end_markers ), 10 );
607     Alloc_Parameters( &( configuration.remark_begin_markers ), 10 );
608     Alloc_Parameters( &( configuration.remark_end_markers ), 10 );
609     Alloc_Parameters( &( configuration.keywords ), 10 );
610     Alloc_Parameters( &( configuration.source_line_comments ), 10 );
611     Alloc_Parameters( &( configuration.header_ignore_chars ), 10 );
612     Alloc_Parameters( &( configuration.header_separate_chars ), 10 );
613
614     if ( f )
615     {
616         SecondScan( f );
617         fclose( f );
618     }
619     else
620     {
621         /* No .rc file found.  That's OK */
622     }
623     ComplementItemNames(  );
624     ComplementHeaderMarkers(  );
625     Complement_Remark_Markers(  );
626     Install_Custom_HeaderTypes(  );
627
628     // Make keywords hash table (if necessarry)
629     add_keywords_to_hash_table(  );
630
631     assert( configuration.items.number );
632
633     return path;
634 }
635
636 /******/
637
638
639 /* TODO Documentation */
640 static void Complement_Remark_Markers(
641     void )
642 {
643     unsigned int        i;
644
645     if ( configuration.remark_begin_markers.number )
646     {
647         /* The user specified his own remark_begin_markers */
648     }
649     else
650     {
651         for ( i = 0; default_remark_begin_markers[i]; ++i )
652         {
653             AddParameter( default_remark_begin_markers[i],
654                           &( configuration.remark_begin_markers ) );
655         }
656     }
657
658     if ( configuration.remark_end_markers.number )
659     {
660         /* The user specified his own remark_end_markers */
661     }
662     else
663     {
664         for ( i = 0; default_remark_end_markers[i]; ++i )
665         {
666             AddParameter( default_remark_end_markers[i],
667                           &( configuration.remark_end_markers ) );
668         }
669     }
670 }
671
672
673 /****f* Configuration/Find_Parameter_Exact
674  * FUNCTION
675  *   Checks for the existence of a given configuration parameter
676  *   (exact string match)
677  * SOURCE
678  */
679 char               *Find_Parameter_Exact(
680     struct Parameters *params,
681     char *paramname )
682 {
683     unsigned int        i;
684
685     // we are looking for an exact match
686     for ( i = 0; i < params->number; i++ )
687     {
688         if ( !strcmp( params->names[i], paramname ) )
689         {
690             // found it
691             return params->names[i];
692         }
693     }
694
695     // parameter not found
696     return NULL;
697 }
698
699 /******/
700
701 /****f* Configuration/Find_Parameter_Partial
702  * FUNCTION
703  *   Checks for the existence of a given configuration parameter
704  *   (partial string match)
705  * SOURCE
706  */
707 char               *Find_Parameter_Partial(
708     struct Parameters *params,
709     char *paramname )
710 {
711     unsigned int        i;
712
713     // we are looking for a not exact match
714     for ( i = 0; i < params->number; i++ )
715     {
716         if ( !strncmp
717              ( params->names[i], paramname, strlen( params->names[i] ) ) )
718         {
719             // found it
720             return params->names[i];
721         }
722     }
723
724     // parameter not found
725     return NULL;
726 }
727
728 /******/
729
730
731 /****f* Configuration/Find_Parameter_Char
732  * FUNCTION
733  *   Checks for the existence of a given configuration parameter
734  *   (Character match)
735  * SOURCE
736  */
737 char               *Find_Parameter_Char(
738     struct Parameters *params,
739     char param )
740 {
741     unsigned int        i;
742
743     for ( i = 0; i < params->number; i++ )
744     {
745         if ( params->names[i][0] == param )
746         {
747             // found it
748             return params->names[i];
749         }
750     }
751
752     // parameter not found
753     return NULL;
754 }
755
756 /******/
757
758
759 /****f* Configuration/Install_C_Syntax
760  * FUNCTION
761  *   Install default C keywords and comments
762  * SOURCE
763  */
764 void Install_C_Syntax(
765     void )
766 {
767     unsigned int        i;
768
769     // Check if we can install our default C keywords
770     if ( !configuration.keywords.number )
771     {
772         for ( i = 0; c_keywords[i]; i++ )
773         {
774             AddParameter( c_keywords[i], &( configuration.keywords ) );
775         }
776
777         // Make keywords hash table (if necessarry)
778         add_keywords_to_hash_table(  );
779     }
780
781     // Make sure that C line comment is present
782     if ( Find_Parameter_Exact
783          ( &( configuration.source_line_comments ), C_LINE_COMMENT ) == NULL )
784     {
785         AddParameter( C_LINE_COMMENT,
786                       &( configuration.source_line_comments ) );
787     }
788
789
790     // Make sure that C block comment start is present
791     if ( Find_Parameter_Exact
792          ( &( configuration.remark_begin_markers ),
793            C_BLOCK_COMMENT_START ) == NULL )
794     {
795         AddParameter( C_BLOCK_COMMENT_START,
796                       &( configuration.remark_begin_markers ) );
797     }
798
799     // Make sure that C block comment end is present
800     if ( Find_Parameter_Exact
801          ( &( configuration.remark_end_markers ),
802            C_BLOCK_COMMENT_END ) == NULL )
803     {
804         AddParameter( C_BLOCK_COMMENT_END,
805                       &( configuration.remark_end_markers ) );
806     }
807 }
808
809 /******/
810
811
812
813
814 /* TODO Documentation */
815 static void ComplementHeaderMarkers(
816     void )
817 {
818     unsigned int        i;
819
820     if ( configuration.header_markers.number )
821     {
822         /* The user specified his own header_markers */
823     }
824     else
825     {
826         for ( i = 0; header_markers[i]; ++i )
827         {
828             AddParameter( header_markers[i],
829                           &( configuration.header_markers ) );
830         }
831     }
832
833     if ( configuration.remark_markers.number )
834     {
835         /* The user specified his own remark_markers */
836     }
837     else
838     {
839         for ( i = 0; remark_markers[i]; ++i )
840         {
841             AddParameter( remark_markers[i],
842                           &( configuration.remark_markers ) );
843         }
844     }
845
846     if ( configuration.end_markers.number )
847     {
848         /* The user specified his own end_markers */
849     }
850     else
851     {
852         for ( i = 0; end_markers[i]; ++i )
853         {
854             AddParameter( end_markers[i], &( configuration.end_markers ) );
855         }
856     }
857
858     if ( configuration.header_separate_chars.number )
859     {
860         /* The user specified his own header_separate_chars */
861     }
862     else
863     {
864         for ( i = 0; default_header_separate_chars[i]; ++i )
865         {
866             AddParameter( default_header_separate_chars[i],
867                           &( configuration.header_separate_chars ) );
868         }
869     }
870
871     if ( configuration.header_ignore_chars.number )
872     {
873         /* The user specified his own header_ignore_chars */
874     }
875     else
876     {
877         for ( i = 0; default_header_ignore_chars[i]; ++i )
878         {
879             AddParameter( default_header_ignore_chars[i],
880                           &( configuration.header_ignore_chars ) );
881         }
882     }
883 }
884
885
886
887 /****if* Config/ConfigLineKind
888  * FUNCTION
889  *   Deterimine the kind of line we a currently processing.
890  * SYNOPSIS
891  */
892
893 static T_Line_Kind ConfigLineKind(
894     char *line )
895 /*
896  * INPUTS
897  *   line -- the current line.
898  * RETURN
899  *   The kind of line.
900  * SOURCE
901  */
902 {
903     T_Line_Kind         kind = CFL_UNKNOWN;
904
905     if ( *line == '\0' )
906     {
907         kind = CFL_EMPTYLINE;
908     }
909     else if ( *line == '#' )
910     {
911         kind = CFL_REMARK;
912     }
913     else if ( utf8_isspace( *line ) )
914     {
915         char               *cur_char = line;
916
917         for ( ; *cur_char && utf8_isspace( *cur_char ); ++cur_char )
918         {
919             /* Empty */
920         }
921         if ( *cur_char == '\0' )
922         {
923             kind = CFL_EMPTYLINE;
924         }
925         else
926         {
927             /* There is atleast one non-space character */
928             kind = CFL_PARAMETER;
929         }
930     }
931     else
932     {
933         kind = CFL_SECTION;
934     }
935     return kind;
936 }
937
938 /********/
939
940
941 static T_Block_Kind BlockKind(
942     char *line )
943 {
944     T_Block_Kind        section_kind = SK_UNKNOWN;
945
946     if ( strcmp( line, "items:" ) == 0 )
947     {
948         section_kind = SK_ITEMS;
949     }
950     else if ( strcmp( line, "options:" ) == 0 )
951     {
952         section_kind = SK_OPTIONS;
953     }
954     else if ( strcmp( line, "extensions:" ) == 0 )
955     {
956         printf
957             ( "Warning:  the 'extensions:' block is obsolete, use 'ignore files:' instead\n" );
958     }
959     else if ( strcmp( line, "ignore items:" ) == 0 )
960     {
961         section_kind = SK_IGNOREITEMS;
962     }
963     else if ( strcmp( line, "source items:" ) == 0 )
964     {
965         section_kind = SK_SOURCE_ITEMS;
966     }
967     else if ( strcmp( line, "headertypes:" ) == 0 )
968     {
969         section_kind = SK_HEADERTYPES;
970     }
971     else if ( strcmp( line, "ignore files:" ) == 0 )
972     {
973         section_kind = SK_IGNORE_FILES;
974     }
975     else if ( strcmp( line, "accept files:" ) == 0 )
976     {
977         section_kind = SK_ACCEPT_FILES;
978     }
979     else if ( strcmp( line, "header markers:" ) == 0 )
980     {
981         section_kind = SK_HEADER_MARKERS;
982     }
983     else if ( strcmp( line, "remark markers:" ) == 0 )
984     {
985         section_kind = SK_REMARK_MARKERS;
986     }
987     else if ( strcmp( line, "end markers:" ) == 0 )
988     {
989         section_kind = SK_END_MARKERS;
990     }
991     else if ( strcmp( line, "remark begin markers:" ) == 0 )
992     {
993         section_kind = SK_REMARK_BEGIN_MARKERS;
994     }
995     else if ( strcmp( line, "remark end markers:" ) == 0 )
996     {
997         section_kind = SK_REMARK_END_MARKERS;
998     }
999     else if ( strcmp( line, "keywords:" ) == 0 )
1000     {
1001         section_kind = SK_KEYWORDS;
1002     }
1003     else if ( strcmp( line, "source line comments:" ) == 0 )
1004     {
1005         section_kind = SK_SOURCE_LINE_COMMENTS;
1006     }
1007     else if ( strcmp( line, "header ignore characters:" ) == 0 )
1008     {
1009         section_kind = SK_HEADER_IGNORE_CHARS;
1010     }
1011     else if ( strcmp( line, "header separate characters:" ) == 0 )
1012     {
1013         section_kind = SK_HEADER_SEPARATE_CHARS;
1014     }
1015     else if ( strcmp( line, "preformatted items:" ) == 0 )
1016     {
1017         section_kind = SK_PREFORMATTED_ITEMS;
1018     }
1019     else if ( strcmp( line, "format items:" ) == 0 )
1020     {
1021         section_kind = SK_FORMAT_ITEMS;
1022     }
1023     else if ( strcmp( line, "item order:" ) == 0 )
1024     {
1025         section_kind = SK_ITEM_ORDER;
1026     }
1027     else
1028     {
1029         RB_Panic( "unknown block kind \"%s\"\n", line );
1030     }
1031     return section_kind;
1032 }
1033
1034
1035 static void Install_Custom_HeaderTypes(
1036     void )
1037 {
1038     unsigned int        i;
1039     struct Parameters   headertype;
1040     unsigned int        priority = 0;
1041
1042     // Install custom header types
1043     for ( i = 0; i < configuration.custom_headertypes.number; i++ )
1044     {
1045         // Allocate some default space for parameters
1046         Alloc_Parameters( &headertype, 10 );
1047         // Break current line into parameters
1048         GetParameters( configuration.custom_headertypes.names[i],
1049                        &headertype );
1050
1051         // Check how many parameters do we have
1052         switch ( headertype.number )
1053         {
1054             // 3 parameters -> no priority specified, assign default
1055         case 3:
1056             priority = 0;
1057             break;
1058
1059             // 4 parameters -> priority specified, convert it
1060         case 4:
1061             priority = atoi( headertype.names[3] );
1062             break;
1063
1064             // Any more or less parameters are illegal
1065         default:
1066             RB_Panic( "Error near header type: '%s'\n"
1067                       "You must have either 3 or 4 parameters there !\n",
1068                       headertype.names[0] );
1069         }
1070
1071         // Check if type character is legal
1072         if ( strlen( headertype.names[0] ) > 1 )
1073         {
1074             RB_Panic( "Error near header type: '%s'\n"
1075                       "Type character can only be one character long !\n",
1076                       headertype.names[0] );
1077         }
1078
1079         // Add custom header type
1080         RB_AddHeaderType( headertype.names[0][0], headertype.names[1],
1081                           headertype.names[2], priority );
1082
1083         // Free temporary space
1084         free( headertype.names );
1085     }
1086 }
1087
1088
1089
1090 static void Alloc_Parameters(
1091     struct Parameters *parameters,
1092     unsigned int size )
1093 {
1094     parameters->size = size;
1095     parameters->number = 0;
1096     parameters->names = calloc( size, sizeof( char * ) );
1097 }
1098
1099
1100 /* TODO Documentation */
1101 static void AddParameter(
1102     char *name,
1103     struct Parameters *parameters )
1104 {
1105     parameters->names[parameters->number] = RB_StrDup( name );
1106     parameters->number++;
1107     if ( parameters->number >= parameters->size )
1108     {
1109         parameters->size *= 2;
1110         parameters->names =
1111             realloc( parameters->names, parameters->size * sizeof( char * ) );
1112     }
1113 }
1114
1115
1116 /****f* Configuration/GetParameters
1117  * FUNCTION
1118  *   Parse a line of text and store the individual words in 
1119  *   a Parameters structure.  Words are seperated by spaces,
1120  *   the exception are words surrounded by quotes. So:
1121  *      aap noot mies "back to the future"
1122  *   contains four words.
1123  * INPUTS
1124  *   o line -- the line of text.
1125  *   o parameters  -- the set of parameters
1126  * SOURCE
1127  */
1128
1129 static void GetParameters(
1130     char *line,
1131     struct Parameters *parameters )
1132 {
1133     int                 i;
1134     int                 n = strlen( line );
1135
1136     /* Remove any spaces at the end of the line */
1137     for ( i = n - 1; i >= 0 && utf8_isspace( line[i] ); --i )
1138     {
1139         line[i] = '\0';
1140     }
1141
1142     assert( i > 0 );            /* If i <= 0 then the line was empty
1143                                    and that cannot be, because this 
1144                                    is supposed to be a parameter */
1145
1146     /* Skip any white space at the begin of the line. */
1147     n = strlen( line );
1148     for ( i = 0; i < n && utf8_isspace( line[i] ); ++i )
1149     {
1150         /* Empty */
1151     }
1152     line += i;
1153
1154     n = strlen( line );
1155     for ( i = 0; i < n; /* empty */  )
1156     {
1157         char               *name = line;
1158
1159         if ( line[i] == '"' )
1160         {
1161             /* It is quoted string, fetch everything until
1162              * the next quote */
1163             ++name;             /* skip the double quote */
1164             for ( ++i; ( i < n ) && ( line[i] != '"' ); ++i )
1165             {
1166                 /* empty */
1167             }
1168             if ( i == n )
1169             {
1170                 RB_Panic( "Missing quote in your .rc file in line:\n  %s\n",
1171                           line );
1172             }
1173             else
1174             {
1175 #if defined(__APPLE__)
1176                 /* hacked because of error when compiling on Mac OS X */
1177                 assert( line[i] == 34 );
1178 #else
1179                 assert( line[i] == '"' );
1180 #endif
1181                 line[i] = '\0';
1182                 AddParameter( name, parameters );
1183             }
1184         }
1185         else
1186         {
1187             /* a single word, find the next space */
1188             for ( ; ( i < n ) && !utf8_isspace( line[i] ); ++i )
1189             {
1190                 /* empty */
1191             }
1192             if ( i < n )
1193             {
1194                 line[i] = '\0';
1195             }
1196             AddParameter( name, parameters );
1197         }
1198         /* Is there anything left? */
1199         if ( i < n )
1200         {
1201             /* skip any spaces until the next parameter */
1202             ++i;                /* first skip the nul character */
1203             line += i;
1204             n = strlen( line );
1205             for ( i = 0; ( i < n ) && utf8_isspace( line[i] ); ++i )
1206             {
1207                 /* empty */
1208             }
1209             line += i;
1210             n = strlen( line );
1211             i = 0;
1212         }
1213     }
1214 }
1215
1216 /*******/
1217
1218 void GetParameter(
1219     char *line,
1220     struct Parameters *parameters )
1221 {
1222     int                 i;
1223     int                 n = strlen( line );
1224
1225     /* Remove any spaces at the end of the line */
1226     for ( i = n - 1; i >= 0 && utf8_isspace( line[i] ); --i )
1227     {
1228         line[i] = '\0';
1229     }
1230     assert( i > 0 );            /* If i <= 0 then the line was empty
1231                                    and that cannot be, because this 
1232                                    is supposed to be a parameter */
1233     /* Skip any white space at the begin of the line. */
1234     n = strlen( line );
1235     for ( i = 0; i < n && utf8_isspace( line[i] ); ++i )
1236     {
1237         /* Empty */
1238     }
1239     line += i;
1240
1241     AddParameter( line, parameters );
1242 }
1243
1244
1245
1246
1247 void Free_Configuration(
1248     void )
1249 {
1250     /* TODO  Deallocate custom_headertypes */
1251 }
1252
1253
1254
1255
1256 static void ComplementItemNames(
1257     void )
1258 {
1259     if ( configuration.items.number )
1260     {
1261         char               *first_item = configuration.items.names[0];
1262
1263         /* The SOURCE item is always included, and should be the
1264          * first one! */
1265         configuration.items.names[0] = RB_StrDup( "SOURCE" );
1266         AddParameter( first_item, &( configuration.items ) );
1267         free( first_item );
1268     }
1269     else
1270     {
1271         /* No item names were defined, so we use the default ones */
1272         unsigned int        i = 0;
1273
1274         for ( ; default_item_names[i]; ++i )
1275         {
1276             AddParameter( default_item_names[i], &( configuration.items ) );
1277         }
1278     }
1279 }
1280
1281
1282 /* TODO Documentation */
1283
1284
1285 static void SecondScan(
1286     FILE *f )
1287 {
1288     char               *myConfLine = NULL;
1289     int                 readConfChars = 0;
1290     T_Block_Kind        section_kind = SK_UNKNOWN;
1291     T_Line_Kind         line_kind = CFL_UNKNOWN;
1292
1293     while ( !feof( f ) )
1294     {
1295         free( myConfLine );
1296         readConfChars = 0;
1297         myConfLine = RB_ReadWholeLine( f, line_buffer, &readConfChars );
1298         RB_StripCR( myConfLine );
1299         line_kind = ConfigLineKind( myConfLine );
1300         switch ( line_kind )
1301         {
1302         case CFL_REMARK:
1303         case CFL_EMPTYLINE:    /* fall through */
1304             /* Do nothing */
1305             break;
1306         case CFL_SECTION:
1307             section_kind = BlockKind( myConfLine );
1308             break;
1309         case CFL_PARAMETER:
1310             {
1311                 switch ( section_kind )
1312                 {
1313                 case SK_ITEMS:
1314                     GetParameter( myConfLine, &( configuration.items ) );
1315                     break;
1316                 case SK_OPTIONS:
1317                     GetParameters( myConfLine, &( configuration.options ) );
1318                     break;
1319                 case SK_IGNOREITEMS:
1320                     GetParameter( myConfLine,
1321                                   &( configuration.ignore_items ) );
1322                     break;
1323                 case SK_SOURCE_ITEMS:
1324                     GetParameter( myConfLine,
1325                                   &( configuration.source_items ) );
1326                     break;
1327                 case SK_HEADERTYPES:
1328                     // Store all complete lines, they will be broken down later
1329                     // in Install_Custom_HeaderTypes()
1330                     GetParameter( myConfLine,
1331                                   &( configuration.custom_headertypes ) );
1332                     break;
1333                 case SK_IGNORE_FILES:
1334                     GetParameters( myConfLine,
1335                                    &( configuration.ignore_files ) );
1336                     break;
1337                 case SK_ACCEPT_FILES:
1338                     GetParameters( myConfLine,
1339                                    &( configuration.accept_files ) );
1340                     break;
1341                 case SK_HEADER_MARKERS:
1342                     GetParameter( myConfLine,
1343                                   &( configuration.header_markers ) );
1344                     break;
1345                 case SK_REMARK_MARKERS:
1346                     GetParameter( myConfLine,
1347                                   &( configuration.remark_markers ) );
1348                     break;
1349                 case SK_END_MARKERS:
1350                     GetParameter( myConfLine,
1351                                   &( configuration.end_markers ) );
1352                     break;
1353                 case SK_REMARK_END_MARKERS:
1354                     GetParameter( myConfLine,
1355                                   &( configuration.remark_end_markers ) );
1356                     break;
1357                 case SK_REMARK_BEGIN_MARKERS:
1358                     GetParameter( myConfLine,
1359                                   &( configuration.remark_begin_markers ) );
1360                     break;
1361                 case SK_KEYWORDS:
1362                     GetParameter( myConfLine, &( configuration.keywords ) );
1363                     break;
1364                 case SK_SOURCE_LINE_COMMENTS:
1365                     GetParameter( myConfLine,
1366                                   &( configuration.source_line_comments ) );
1367                     break;
1368                 case SK_HEADER_IGNORE_CHARS:
1369                     GetParameter( myConfLine,
1370                                   &( configuration.header_ignore_chars ) );
1371                     break;
1372                 case SK_HEADER_SEPARATE_CHARS:
1373                     GetParameter( myConfLine,
1374                                   &( configuration.header_separate_chars ) );
1375                     break;
1376                 case SK_PREFORMATTED_ITEMS:
1377                     GetParameter( myConfLine,
1378                                   &( configuration.preformatted_items ) );
1379                     break;
1380                 case SK_FORMAT_ITEMS:
1381                     GetParameter( myConfLine,
1382                                   &( configuration.format_items ) );
1383                     break;
1384                 case SK_ITEM_ORDER:
1385                     GetParameter( myConfLine,
1386                                   &( configuration.item_order ) );
1387                     break;
1388                 case SK_UNKNOWN:
1389                     break;
1390                 default:
1391                     assert( 0 );
1392                 }
1393             }
1394             break;
1395         case CFL_UNKNOWN:
1396         default:
1397             assert( 0 );
1398         }
1399     }
1400     free( myConfLine );
1401 }