2cf53322059b0cc15016a34bd87d861c9ea7de2a
[robodoc.git] / Source / robodoc.c
1 /*
2 Copyright (C) 1994-2007  Frans Slothouber, Jacco van Weert, Petteri Kettunen,
3 Bernd Koesling, Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner,
4 Sasha Vasko, Kai Hofmann, Thierry Pierron, Friedrich Haase, and Gergely Budai.
5
6 This file is part of ROBODoc
7
8 ROBODoc is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 */
22
23 /****h* Docuwala/ROBODoc
24  * FUNCTION
25  *   ROBODoc is intended to be a replacement for the original AutoDocs
26  *   program.  ROBODoc will extract the comment headers from a source
27  *   file, and put them into a separate documentation file.
28  *   General Flow
29  *
30  *      Sourcecode  ---> [ROBODoc] ---> Document.
31  *
32  *   The whole ROBODoc process consists of three steps: scanning,
33  *   analysing, generating.
34  *
35  *   Scanning 
36  *
37  *   ROBODoc scans the source directory tree. This collects the names of
38  *   all the source files.
39  *
40  *   Analysing
41  *
42  *   ROBODOc analyses all the sourcefiles. This reads the content of all
43  *   source files and collects all the headers.
44  *
45  *   Generating
46  *
47  *   In this step the headers are written to one or more documentation files.
48  *   In addition 
49  *
50  *
51  *   The data collected during scanning and analysing is stored in a
52  *   number of structures.
53  *
54  *   RB_Directory, it stores the names of the sourcefiles and directories in
55  *   the source direcory tree.  File names are stored in a RB_Filename
56  *   structure, directory names in a RB_Path structure.  Each RB_Filename
57  *   contains a pointer (path) to a RB_Path structure that tells in which
58  *   directory a file is located.  Each RB_Path has a pointer (parent) to
59  *   another RB_Path structure that tells in which directory is a directory
60  *   located (of which directory it is a subdirectory).  The only exception
61  *   is the root directory.
62  *   
63  *   Besides the name of the sourcefile, the RB_Filename also stores the
64  *   name of the documentation file.
65  *   
66  *   For each sourcefile there is an RB_Part structure.  It contains a
67  *   pointer (filename) to the RB_Filename and a list (headers) of
68  *   RB_Header structure containing the headers found in the sourcefile.
69  *   
70  *   Every RB_Header structure contains a pointer (owner) to the RB_Part
71  *   structure to which it belongs.  Headers can form a hierarchy that is
72  *   used to create sections and subsections in the documentation.  To
73  *   store this hierarchy every RB_header structure contains a pointer
74  *   (parent) to its parent header.  For instance, given the following two
75  *   headers, SubModule is the parent of SubSubModule.
76  *     ****h* TopModule/SubModule
77  *     *
78  *     **** 
79  *   
80  *     ****h* SubModule/SubSubModule
81  *     *
82  *     **** 
83  *   
84  *   In the documentation this creates the sections
85  *      1.TopModule
86  *      1.1 SubModule
87  *      1.1.1 SubSubModule
88  *   
89  *   The RB_Directory and the linked list of RB_Part structures are
90  *   stored in a RB_Document structure.
91  *   
92  *   During the generation of the documentation ROBODoc tries to create
93  *   cross links between the mention of a header's name (an object) and the
94  *   documentation generated from that header (the documentation for the
95  *   object).
96  *   
97  *   To aid this proces there is an array of RB_link structures.  This
98  *   array is sorted for quick searching.  RB_link structures the name
99  *   of a header and the name of the label under which the documentation
100  *   can be found in the documentation file.
101  *
102  * AUTHOR
103  *   See AUTHORS in the archive.
104  * CREATION DATE
105  *   20-Dec-94  Jacco van Weert.
106  * MODIFICATION HISTORY
107  *   See ChangeLog in the archive. Latest version can be found on:
108  *   o http://www.xs4all.nl/~rfsber/Robo/
109  * BUGS
110  *   Other bugs? Catch them in a jar and send them to rfsber -(at)- xs4all.nl
111  *
112  ****
113  * $Id: robodoc.c,v 1.106 2007/07/10 19:13:52 gumpu Exp $
114  */
115
116 #include <assert.h>
117 #include <stdlib.h>
118 #include <stdio.h>
119 #include <string.h>
120 #include <sys/stat.h>
121 #include <stdlib.h>
122 #include <locale.h>
123 #ifdef __APPLE__
124 #include <unistd.h>
125 #endif
126
127 #include "robodoc.h"
128 #include "globals.h"
129 #include "headers.h"
130 #include "util.h"
131 #include "links.h"
132 #include "part.h"
133 #include "analyser.h"
134 #include "generator.h"
135 #include "document.h"
136 #include "directory.h"
137 #include "roboconfig.h"
138 #include "optioncheck.h"
139
140 #ifdef DMALLOC
141 #include <dmalloc.h>
142 #endif
143
144 /* Functions local to this file. */
145
146 T_RB_DocType        Find_DocType(
147     void );
148
149 actions_t           Find_Actions(
150     void );
151
152 long                Find_DebugMode(
153     void );
154
155 int                 Find_Option(
156     char *option );
157
158 static int          PathBegin_Check(
159     char *path );
160
161 static void         Path_Check(
162     char *sourcepath,
163     char *docpath );
164
165 char               *Find_Parameterized_Option(
166     char *actionname );
167
168 char               *RB_Find_In_Argv_Parameterized_Option(
169     int argc,
170     char **argv,
171     char *optionname );
172
173 static char        *General_Find_Parameterized_Option(
174     int n,
175     char **options,
176     char *optionname );
177
178 static char        *Fix_Path(
179     char *path );
180
181 static char        *Path_Convert_Win32_to_Unix(
182     char *path );
183
184 static char        *Find_And_Fix_Path(
185     char *option_name );
186
187 static void         RB_Summary(
188     struct RB_Document *document );
189
190 static void         Find_Tabstops(
191     void );
192
193 static void         Find_And_Install_Document_Name(
194     char *option,
195     unsigned char type );
196
197 char                RB_VER[] =
198     "$VER: robodoc " VERSION " (" __DATE__
199     ") (c) by Maverick Software Development 1994-2006";
200
201
202 /****h* ROBODoc/UserInterface
203  * FUNCTION
204  *   This module contains functions to parse the
205  *   command line and inform the user about any errors.
206  *****
207  */
208
209
210 /****v* UserInterface/use
211  * NAME
212  *   use -- usage string
213  * FUNCTION
214  *   Inform the user how to use ROBODoc.
215  * AUTHOR
216  *   Koessi
217  * SOURCE
218  */
219
220 char                use[] =
221 //           1         2         3         4         5         6         7         8
222 //  12345678901234567890123456789012345678901234567890123456789012345678901234567890
223     "ROBODoc Version " VERSION "    autodocs formatter ($Revision: 1.106 $)\n"
224     "(c) 1994-2007 Frans Slothouber, Petteri Kettunen, \n"
225     "              Gergely Budai and Jacco van Weert\n"
226     "ROBODoc comes with ABSOLUTELY NO WARRANTY.\n"
227     "This is free software, and you are welcome to redistribute it\n"
228     "under certain conditions; type `robodoc -c' for details.\n" "\n";
229 char                use_usage[] =
230     "Usage:\n"
231     "   robodoc --src <directory> --doc <directory> --multidoc   [type] [options]\n"
232     "   robodoc --src <directory> --doc <file>      --singledoc  [type] [options]\n"
233     "   robodoc --src <file>      --doc <file>      --singlefile [type] [options]\n"
234     "\n"
235     "Type:\n" "   --html, --rtf, --latex, --ascii, --dbxml, --troff\n" "\n";
236 char                use_options1[] =
237     "Options:\n"
238     "   --charset NAME   Add character encoding information (html only).\n"
239     "   --cmode          Use ANSI C grammar in source items (html only).\n"
240     "   --css            Specify the stylesheet to use.\n"
241     "   --ext EXTENSION  Set extension for generated files.\n"
242     "   --footless       Do not create the foot of a document.\n"
243     "   --headless       Do not create the head of a document.\n"
244     "   --index          Add an index.\n";
245 char                use_options2[] =
246     "   --internal       Also include internal headers.\n"
247     "   --internalonly   Only include internal headers.\n"
248     "   --lock           Recognize only one header marker per file.\n"
249     "   --nodesc         Do not descent into subdirectories.\n"
250     "   --no_subdirectories\n"
251     "                    Do no create documentation subdirectories.\n"
252     "   --nopre          Do not use <PRE> </PRE> in the HTML output.\n"
253     "   --nosort         Do not sort the headers.\n"
254     "   --nosource       Do not include SOURCE items.\n"
255     "   --nogeneratedwith\n"
256     "                    Do not add the 'generated by robodoc' message\n"
257     "                    at the top of each documentation file.\n"
258     "   --ignore_case_when_linking\n"
259     "                    Ignore the case of the symbols when trying\n"
260     "                    to create crosslinks.\n";
261 char                use_options3[] =
262     "   --rc             Specify an alternate configuration file.\n"
263     "   --sections       Add sections and subsections.\n"
264     "   --first_section_level NUMBER\n"
265     "                    Start the first section not at 1 but at \n"
266     "                    level NUMBER.\n"
267     "   --tabsize NUMBER Set the tab size.\n"
268     "   --tabstops a,b,..,n\n"
269     "                    Set TAB stops \n"
270     "   --tell           ROBODoc will tell you what it is doing.\n"
271     "   --debug          same as --tell, but with lots more details.\n"
272     "   --toc            Add a table of contents.\n"
273     "   --version        Print version info and exit.\n"
274     "   --sectionnameonly\n"
275     "                    Generate section header with name only.\n"
276     "   --compress       Only supported by TROFF output format. Defines by which\n"
277     "                    program manpages will be compressed. Either bzip2 or gzip.\n"
278     "   --mansection     Manual section where pages will be inserted (default: 3).\n"
279     "   --documenttitle TITLE\n"
280     "                    Set the document title\n"
281     "   --altlatex       Alternate LaTeX file format (bigger / clearer than normal)\n"
282     "   --latexparts     Make the first module level as PART in LaTeX output\n"
283     "   --syntaxcolors   Turn on all syntax highlighting features in SOURCE items\n"
284     "                    (html only)\n"
285     "   --syntaxcolors_enable quotes,squotes,line_comments,block_comments,\n"
286     "                         keywords,non_alpha\n"
287     "                    Enable only specific syntax highlighting features in\n"
288     "                    SOURCE items (html only)\n"
289     "   --dotname NAME   Specify the name (and path / options) of DOT tool\n"
290     "   --masterindex title,filename\n"
291     "                    Specify the tile and filename for master index page\n"
292     "   --sourceindex title,filename\n"
293     "                    Specify the tile and filename for source files index page\n"
294     "   --one_file_per_header\n"
295     "                    Create a separate documentation file for each header\n"
296     "   --header_breaks NUMBER\n"
297     "                    Insert a linebreak after every NUMBER header names\n"
298     "                    (default value: 2, set to zero to disable)\n" "\n";
299 char                use_authors[] =
300     "Authors/Contributors:\n"
301     "   Frans Slothouber, Jacco van Weert, Petteri Kettunen, Bernd Koesling,\n"
302     "   Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner, Sasha Vasko,\n"
303     "   Kai Hofmann, Thierry Pierron, Friedrich Haase, Gergely Budai.\n";
304 /********/
305
306
307 /****v* UserInterface/copying
308  * FUNCTION
309  *   Information about the ROBODoc licence.
310  * AUTHOR
311  *   Frans
312  * HISTORY
313  *   2003-02-25/petterik: corrected link to GNU copyleft.
314  *******
315  */
316
317 char                copying[] =
318     "\n"
319     " Distributed under the GNU GENERAL PUBLIC LICENSE\n"
320     "   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n"
321     " See the source archive for a copy of the complete licence\n"
322     " If you do not have it you can get it from URL\n"
323     " http://www.gnu.org/copyleft/gpl.html\n";
324
325
326 static void dump_version(
327     void )
328 {
329     printf( "%s\n", VERSION );
330     /* TODO create an option to show robodoc configuration,
331      * including compiler setting etc..
332      printf( "Configuration:\n" );
333      printf( "  locale: %s", setlocale( LC_ALL, "" ) );
334      */
335 }
336
337 /* TODO  This function is too long. */
338
339 /****f* UserInterface/main
340  * FUNCTION
341  *   Get and parse the arguments.  Analyse document and generate the
342  *   documentation. Everything starts from here.
343  * SYNOPSIS
344  */
345 int main(
346     int argc,
347     char **argv )
348 /*
349  * SOURCE
350  */
351 {
352     struct RB_Document *document = NULL;
353     struct RB_Directory *srctree = NULL;
354     char               *optstr = NULL;
355     char               *used_rc_file = NULL;
356     long                debug = 0;
357
358 /*
359    TODO, make setlocale work.
360     char * loc;
361
362     if ( (loc = getenv("LC_CTYPE") ) != NULL ) 
363     {
364         printf( ".... %s\n", loc );
365         setlocale( LC_ALL, loc);
366     }
367     else
368     {
369         setlocale(LC_ALL, "");
370     }
371 */
372
373     whoami = argv[0];           /* global me,myself&i */
374
375     // Init Actions global
376     course_of_action = No_Actions(  );
377
378     /* Read the configuration file. This might contain
379        addition options. */
380     RB_SetCurrentFile( NULL );
381     used_rc_file = ReadConfiguration( argc, argv,
382                                       RB_Find_In_Argv_Parameterized_Option
383                                       ( argc, argv, "--rc" ) );
384
385     /* Force debug mode early if the user wants the debug mode */
386     debugmode = Find_DebugMode(  );
387     course_of_action.do_tell = TRUE;
388
389     if ( Check_Options(  ) == EXIT_FAILURE )
390     {
391
392         return EXIT_FAILURE;
393     }
394
395     if ( Find_Option( "-c" ) )
396     {
397         printf( "%s", copying );
398         return EXIT_SUCCESS;
399     }
400
401     if ( Find_Option( "--version" ) )
402     {
403         dump_version(  );
404         return EXIT_SUCCESS;
405     }
406
407     if ( Find_Option( "--help" ) )
408     {
409         printf( "%s%s%s%s%s%s", use, use_usage, use_options1, use_options2,
410                 use_options3, use_authors );
411         return EXIT_SUCCESS;
412     }
413
414     output_mode = Find_DocType(  );     /* one of the globals that are still left */
415     if ( output_mode == UNKNOWN )
416     {
417         Print_Short_Use(  );
418         return EXIT_SUCCESS;
419     }
420
421     /* First the basics. */
422     document = RB_Get_RB_Document(  );
423     document->doctype = output_mode;
424     document->actions = Find_Actions(  );
425     debug = document->debugmode = Find_DebugMode(  );
426     document->charset = Find_Parameterized_Option( "--charset" );
427     document->extension = Find_Parameterized_Option( "--ext" );
428     document->css = Find_Parameterized_Option( "--css" );
429     document->compress = Find_Parameterized_Option( "--compress" );
430     document->section = Find_Parameterized_Option( "--mansection" );
431     document_title = Find_Parameterized_Option( "--documenttitle" );
432     optstr = Find_Parameterized_Option( "--first_section_level" );
433     if ( optstr )
434     {
435         document->first_section_level = atoi( optstr );
436     }
437
438     course_of_action = document->actions;       /* a global */
439     debugmode = document->debugmode;    /* a global */
440
441     RB_Say( "Using %s for defaults\n", SAY_INFO, used_rc_file );
442     free( used_rc_file );       /* No longer necessary */
443     used_rc_file = NULL;
444
445     if ( document->css )
446     {
447         document->css = Path_Convert_Win32_to_Unix( document->css );
448     }
449
450     if ( ( document->actions.do_index ) && output_mode == TROFF )
451     {
452         RB_Warning( "Index generation not supported for TROFF format.\n" );
453         document->actions.do_index = FALSE;
454     }
455
456     if ( Find_Parameterized_Option( "--doctype_name" ) &&
457          Find_Parameterized_Option( "--doctype_location" ) )
458     {
459         document->doctype_name =
460             Find_Parameterized_Option( "--doctype_name" );
461         document->doctype_location =
462             Find_Parameterized_Option( "--doctype_location" );
463     }
464
465     // Find tab sizes and tab stops
466     Find_Tabstops(  );
467
468     // Find master index file name
469     Find_And_Install_Document_Name( "--masterindex", HT_MASTERINDEXTYPE );
470
471     // Find source index file name
472     Find_And_Install_Document_Name( "--sourceindex", HT_SOURCEHEADERTYPE );
473
474     // Find DOT tool name
475     optstr = Find_Parameterized_Option( "--dotname" );
476     if ( optstr )
477     {
478         dot_name = optstr;
479     }
480
481     // Find number of headers before linebreak
482     optstr = Find_Parameterized_Option( "--header_breaks" );
483     if ( optstr )
484     {
485         int                 breaks = atoi( optstr );
486
487         if ( breaks == 0 )
488         {
489             breaks = MAX_HEADER_BREAKS;
490         }
491         header_breaks = breaks;
492     }
493
494     // Get extension
495     if ( !document->extension )
496     {
497         document->extension = RB_Get_Default_Extension( document->doctype );
498     }
499
500
501     /* Test if there is a --src and --doc */
502     if ( !Find_Parameterized_Option( "--src" ) )
503     {
504         printf( "Error: you need to specify a source"
505                 " file or directory with --src.\n" );
506         Print_Short_Use(  );
507         return EXIT_FAILURE;
508     }
509     if ( !Find_Parameterized_Option( "--doc" ) )
510     {
511         printf( "Error: you need to specify a documentation file"
512                 " or directory with --doc.\n" );
513         Print_Short_Use(  );
514         return EXIT_FAILURE;
515     }
516
517     /* What mode are we using? */
518     if ( Find_Option( "--multidoc" ) )
519     {
520         char               *srcrootname;        /* directory */
521         char               *docrootname;
522
523         srcrootname = Find_And_Fix_Path( "--src" );
524         if ( !Stat_Path( 'e', srcrootname ) )
525         {
526             printf( "Error: %s does not exists\n", srcrootname );
527             Print_Short_Use(  );
528             return EXIT_FAILURE;
529         }
530
531         if ( !Stat_Path( 'd', srcrootname ) )
532         {
533             printf( "Error: %s is not a directory\n", srcrootname );
534             Print_Short_Use(  );
535             return EXIT_FAILURE;
536         }
537         document->srcroot = RB_Get_RB_Path( srcrootname );
538
539         docrootname = Find_And_Fix_Path( "--doc" );
540         Path_Check( srcrootname, docrootname );
541
542         document->docroot = RB_Get_RB_Path( docrootname );
543
544         srctree = RB_Get_RB_Directory( srcrootname, docrootname );
545         document->srctree = srctree;
546
547         RB_Document_Create_Parts( document );
548         RB_Analyse_Document( document );
549         RB_Generate_Documentation( document );
550
551         RB_Free_RB_Path( document->srcroot );
552         document->srcroot = 0;
553         RB_Free_RB_Path( document->docroot );
554         document->docroot = 0;
555         RB_Free_RB_Directory( srctree );
556         document->srctree = 0;
557     }
558     else if ( output_mode == TROFF )
559     {
560         RB_Panic( "Only --multidoc is supported for TROFF output.\n" );
561     }
562     else if ( Find_Option( "--singledoc" ) )
563     {
564         char               *srcrootname;        /* directory */
565
566         srcrootname = Find_And_Fix_Path( "--src" );
567         if ( !Stat_Path( 'e', srcrootname ) )
568         {
569             printf( "Error: %s does not exists\n", srcrootname );
570             Print_Short_Use(  );
571             return EXIT_FAILURE;
572         }
573
574         if ( !Stat_Path( 'd', srcrootname ) )
575         {
576             printf( "Error: %s is not a directory\n", srcrootname );
577             Print_Short_Use(  );
578             return EXIT_FAILURE;
579         };
580         document->srcroot = RB_Get_RB_Path( srcrootname );
581
582         document->docroot = 0;
583         document->singledoc_name = Find_And_Fix_Path( "--doc" );
584
585         srctree = RB_Get_RB_Directory( srcrootname, NULL );
586         document->srctree = srctree;
587
588         RB_Document_Create_Parts( document );
589         RB_Analyse_Document( document );
590         RB_Generate_Documentation( document );
591
592         RB_Free_RB_Directory( srctree );
593     }
594     else if ( Find_Option( "--singlefile" ) )
595     {
596         char               *srcfile;    /* file */
597         char               *docfile;    /* file */
598
599         document->docroot = 0;
600         docfile = Find_And_Fix_Path( "--doc" );
601         document->singledoc_name = docfile;
602         srcfile = Find_And_Fix_Path( "--src" );
603         if ( !Stat_Path( 'e', srcfile ) )
604         {
605             printf( "Error: %s does not exists\n", srcfile );
606             Print_Short_Use(  );
607             return EXIT_FAILURE;
608         };
609
610         if ( !Stat_Path( 'f', srcfile ) )
611         {
612             printf( "Error: %s is not a file\n", srcfile );
613             Print_Short_Use(  );
614             return EXIT_FAILURE;
615         };
616
617         document->srctree = RB_Get_RB_SingleFileDirectory( srcfile );
618         document->srcroot =
619             RB_Get_RB_Path( document->srctree->first_path->name );
620
621         RB_Document_Create_Parts( document );
622         RB_Analyse_Document( document );
623         RB_Generate_Documentation( document );
624
625         RB_Free_RB_Directory( document->srctree );
626     }
627     else
628     {
629         Print_Short_Use(  );
630         printf
631             ( "\n\nError: Use either --multidoc, --singledoc, or --singlefile\n" );
632         return EXIT_FAILURE;
633     }
634
635     RB_Summary( document );
636     RB_Free_RB_Document( document );
637     Free_Configuration(  );
638
639 #ifdef __APPLE__
640     /* Mac OS X specific: print memory leaks */
641     if ( debug & SAY_DEBUG )
642     {
643         char                cmd[32];
644
645         sprintf( cmd, "/usr/bin/leaks %d", getpid(  ) );
646         system( cmd );
647     }
648 #endif /* __APPLE__ */
649
650     return EXIT_SUCCESS;
651 }
652
653 /*******/
654
655
656 /****if* UserInterface/Find_Tabstops
657  * FUNCTION
658  *   This function looks for the tab stop configuration and fills out the
659  *   tab_stops table.
660  * SYNOPSIS
661  */
662 static void Find_Tabstops(
663     void )
664 /*
665  * SOURCE
666  */
667 {
668     int                 i;
669     int                 tabsize = DEFAULT_TABSIZE;
670     char               *optstr, *str;
671
672     // Find tab sizes
673     optstr = Find_Parameterized_Option( "--tabsize" );
674     if ( optstr )
675     {
676         tabsize = atoi( optstr );
677     }
678
679     // Fill tabstop table with default values
680     for ( i = 0; i < MAX_TABS; i++ )
681     {
682         tab_stops[i] = tabsize * ( i + 1 );
683     }
684
685     // Find tab stops
686     optstr = Find_Parameterized_Option( "--tabstops" );
687     if ( optstr )
688     {
689         // Get TAB sizes and fill table
690         for ( str = strtok( optstr, TABSIZE_SEPARATOR ), i = 0;
691               str != NULL && i < MAX_TABS;
692               str = strtok( NULL, TABSIZE_SEPARATOR ), i++ )
693         {
694             tab_stops[i] = atoi( str ) - 1;
695         }
696     }
697 }
698
699 /*******/
700
701
702 /****if* UserInterface/Find_And_Install_Document_Name
703  * FUNCTION
704  *   Looks for specific option strings and installs special document titles
705  *   and filenames.
706  *
707  *   Should be used to overwrite the HT_MASTERINDEXTYPE or HT_SOURCEHEADERTYPE
708  *   entries in the header_type_lookup_table.
709  * SYNOPSIS
710  */
711 static void Find_And_Install_Document_Name(
712     char *option,
713     unsigned char type )
714 /*
715  * INPUTS
716  *   o option -- the option string to look for
717  *   o type   -- the type entry in header_type_lookup_table to overwrite
718  *               (Should be either HT_MASTERINDEXTYPE or HT_SOURCEHEADERTYPE)
719  *
720  * SOURCE
721  */
722 {
723     char               *title = NULL, *filename = NULL, *optstr = NULL;
724
725     // First find the option
726     optstr = Find_Parameterized_Option( option );
727     if ( optstr )
728     {
729
730         // Break the option into substrings
731         title = strtok( optstr, "," );
732         filename = strtok( NULL, "," );
733
734         // If something missing
735         if ( title == NULL || filename == NULL )
736         {
737             RB_Panic( "Invalid %s option\n", option );
738         }
739
740         // Install document title and filename
741         RB_AddHeaderType( type, title, filename, 0 );
742     }
743 }
744
745 /*******/
746
747
748
749 /****f* UserInterface/Find_And_Fix_Path
750  * FUNCTION
751  *   Searches through the options to find a path.
752  *   This path is converted to a propper path
753  *   if it contains errors such as the use of
754  *   '\' or when it does not start with ./ or a
755  *   drive name.
756  *   The option must exist.
757  * SYNOPSIS
758  */
759 static char        *Find_And_Fix_Path(
760     char *option_name )
761 /*
762  * INPUTS
763  *   o option_name -- the option name for the path
764  * RESULT
765  *   o path -- the path.
766  *
767  * SOURCE
768  */
769 {
770     char               *temp;
771     char               *temp2;
772
773     temp = Find_Parameterized_Option( option_name );
774     assert( temp );
775     temp = Path_Convert_Win32_to_Unix( temp );
776     temp2 = Fix_Path( temp );
777     free( temp );
778     return temp2;
779 }
780
781 /*******/
782
783
784 /****f* UserInterface/Path_Convert_Win32_to_Unix
785  * FUNCTION
786  *   Although people are supposed to specify all paths
787  *   with a '/' as seperator, sometimes people on Win32
788  *   use '\', this causes problems later on in some
789  *   other function of robodoc that expect a '/'.
790  *   So to prevent this we replace all the '\' in a path
791  *   with '/'
792  *    
793  *   In addition people sometimes add a '/' at the
794  *   end of the path. We remove it.
795  *
796  * SYNOPSIS
797  */
798 static char        *Path_Convert_Win32_to_Unix(
799     char *path )
800 /*
801  * INPUTS
802  *   o path -- the path.
803  * RESULT
804  *   o path -- the converted path (in a newly allocated
805  *             block of memory).
806  * SOURCE
807  */
808 {
809     size_t              i;
810
811     /* First make a copy */
812     path = RB_StrDup( path );
813     for ( i = 0; i < strlen( path ); ++i )
814     {
815         if ( path[i] == '\\' )
816         {
817             path[i] = '/';
818         }
819     }
820
821     /* Remove trailing '/' if there is one. */
822     if ( path[strlen( path ) - 1] == '/' )
823     {
824         path[strlen( path ) - 1] = '\0';
825     }
826
827     return path;
828 }
829
830 /*****/
831
832 /****f* UserInterface/Path_Check
833  * FUNCTION
834  *   Test the validity of the doc and source path. The doc path should
835  *   not be a sub directory of the source path otherwise the generated
836  *   documentation will be part of the generated documentation if
837  *   robodoc is run more than once.
838  * SYNOPSIS
839  */
840
841 static void Path_Check(
842     char *sourcepath,
843     char *docpath )
844 /*
845  * INPUTS
846  *   o sourcepath -- the path to the source files.
847  *   o docpath    -- the path to the documentation files.
848  * OUTPUT
849  *   o error messages
850  * SOURCE
851  */
852 {
853     if ( docpath )
854     {
855         int                 dl;
856         int                 sl;
857
858         dl = strlen( docpath );
859         sl = strlen( sourcepath );
860         if ( dl >= sl )
861         {
862             int                 i;
863             int                 equal = TRUE;
864
865             for ( i = 0; i < sl; ++i )
866             {
867                 if ( docpath[i] != sourcepath[i] )
868                 {
869                     equal = FALSE;
870                     break;
871                 }
872             }
873             if ( equal && ( dl == sl ) )
874             {
875                 RB_Panic
876                     ( "The source path and document path can not be equal\n" );
877             }
878             else
879             {
880                 /* OK  */
881             }
882         }
883     }
884 }
885
886 /*****/
887
888
889 /****f* UserInterface/PathBegin_Check
890  * FUNCTION
891  *   Checks the validity of a path.
892  *   A path should start with
893  *     ./
894  *   or
895  *     /
896  *   or
897  *     have a ':' some where
898  * SYNOPSIS
899  */
900 static int PathBegin_Check(
901     char *path )
902 /*
903  * INPUTS
904  *   o path -- the path to be cheked.
905  * RESULT
906  *   o FALSE -- path is not OK.
907  *   o TRUE  -- path is OK.
908  * SOURCE
909  */
910 {
911     int                 result = FALSE;
912     int                 l = strlen( path );
913
914     if ( l == 1 )
915     {
916         result = ( path[0] == '.' );
917     }
918     else if ( l >= 2 )
919     {
920         result = ( ( path[0] == '.' ) && ( path[1] == '/' ) ) ||
921             ( path[0] == '/' ) || ( strchr( path, ':' ) != NULL );
922     }
923     else
924     {
925         /* Empty */
926     }
927     return result;
928 }
929
930 /******/
931
932
933
934 /****f* UserInterface/Find_Option
935  * FUNCTION
936  *   Search configuration.options for a specific option.
937  * SYNOPSIS
938  */
939 int Find_Option(
940     char *option )
941 /* INPUTS
942  *   o option -- the option to be found.
943  * RESULT
944  *   o TRUE  -- option does exist
945  *   o FALSE -- option does not exist
946  * SOURCE
947  */
948 {
949     unsigned int        parameter_nr;
950     int                 found = FALSE;
951
952     for ( parameter_nr = 0;
953           parameter_nr < configuration.options.number; parameter_nr++ )
954     {
955         if ( !RB_Str_Case_Cmp
956              ( configuration.options.names[parameter_nr], option ) )
957         {
958             found = TRUE;
959             break;
960         }
961     }
962     return found;
963 }
964
965 /******/
966
967 /****f* UserInterface/Fix_Path
968  * FUNCTION
969  *   Add a "./" to a path if it does not start with a "./" or does not
970  *   contain a ":".  If the path was "." just add a "/".  Adding a
971  *   "./" simplifies the creating of relative links during the
972  *   generation process.
973  * SYNOPSIS
974  */
975
976 static char        *Fix_Path(
977     char *path )
978 /*
979  * INPUTS
980  *   o path -- the path to be fixed.
981  * RESULT
982  *   A pointer to a newly allocated string containing the path.
983  * SOURCE
984  */
985 {
986     char               *result = 0;
987
988     if ( !PathBegin_Check( path ) )
989     {
990         char               *prefix = "./";
991
992         if ( strcmp( path, "." ) == 0 )
993         {
994             result = RB_StrDup( prefix );
995         }
996         else
997         {
998             int                 l = strlen( path );
999
1000             l += strlen( prefix ) + 1;
1001             result = malloc( l );
1002             assert( result );
1003             result[0] = '\0';
1004             strcat( result, prefix );
1005             strcat( result, path );
1006         }
1007     }
1008     else
1009     {
1010         result = RB_StrDup( path );
1011     }
1012     return result;
1013 }
1014
1015 /*****/
1016
1017
1018 /* TODO: FS Documentation */
1019
1020 T_RB_DocType Find_DocType(
1021     void )
1022 {
1023     T_RB_DocType        doctype = UNKNOWN;
1024     unsigned int        parameter_nr;
1025
1026     for ( parameter_nr = 0;
1027           parameter_nr < configuration.options.number; parameter_nr++ )
1028     {
1029
1030         if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1031                                "--html" ) )
1032         {
1033             doctype = HTML;
1034             break;
1035         }
1036         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1037                                     "--latex" ) )
1038         {
1039             doctype = LATEX;
1040             break;
1041         }
1042         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1043                                     "--ascii" ) )
1044         {
1045             doctype = ASCII;
1046             break;
1047         }
1048         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1049                                     "--rtf" ) )
1050         {
1051             doctype = RTF;
1052             break;
1053         }
1054         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1055                                     "--test" ) )
1056         {
1057             doctype = TEST;
1058             break;
1059         }
1060         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1061                                     "--troff" ) )
1062         {
1063             doctype = TROFF;
1064             break;
1065         }
1066         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1067                                     "--dbxml" ) )
1068         {
1069             doctype = XMLDOCBOOK;
1070             break;
1071         }
1072         else
1073         {
1074             /* Ignore */
1075         }
1076     }
1077     return doctype;
1078 }
1079
1080
1081 actions_t No_Actions(
1082     void )
1083 {
1084     actions_t           actions;
1085     unsigned int        i;
1086     unsigned char      *actptr;
1087
1088     for ( i = 0, actptr = ( unsigned char * ) &actions;
1089           i < sizeof( actions ); i++, actptr++ )
1090     {
1091         *actptr = 0;
1092     }
1093
1094     return actions;
1095 }
1096
1097 /* TODO: FS Documentation */
1098 actions_t Find_Actions(
1099     void )
1100 {
1101     actions_t           actions;
1102     unsigned int        parameter_nr;
1103     char               *optstr;
1104
1105     actions = No_Actions(  );
1106
1107     for ( parameter_nr = 0;
1108           parameter_nr < configuration.options.number; parameter_nr++ )
1109     {
1110         if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1111                                "--singledoc" ) )
1112         {
1113             actions.do_singledoc = TRUE;
1114         }
1115         if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1116                                "--singlefile" ) )
1117         {
1118             actions.do_singlefile = TRUE;
1119         }
1120         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1121                                     "--multidoc" ) )
1122         {
1123             actions.do_multidoc = TRUE;
1124         }
1125         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1126                                     "--no_subdirectories" ) )
1127         {
1128             actions.do_no_subdirectories = TRUE;
1129         }
1130         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1131                                     "--one_file_per_header" ) )
1132         {
1133             actions.do_one_file_per_header = TRUE;
1134         }
1135         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1136                                     "--sections" ) )
1137         {
1138             actions.do_sections = TRUE;
1139         }
1140         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1141                                     "--internal" ) )
1142         {
1143             actions.do_include_internal = TRUE;
1144         }
1145         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1146                                     "--ignore_case_when_linking" ) )
1147         {
1148             actions.do_ignore_case_when_linking = TRUE;
1149         }
1150         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1151                                     "--internalonly" ) )
1152         {
1153             actions.do_internal_only = TRUE;
1154         }
1155         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1156                                     "--toc" ) )
1157         {
1158             actions.do_toc = TRUE;
1159         }
1160         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1161                                     "--index" ) )
1162         {
1163             actions.do_index = TRUE;
1164         }
1165         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1166                                     "--nosource" ) )
1167         {
1168             actions.do_nosource = TRUE;
1169         }
1170         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1171                                     "--tell" ) )
1172         {
1173             actions.do_tell = TRUE;
1174         }
1175         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1176                                     "--debug" ) )
1177         {
1178             actions.do_tell = TRUE;
1179         }
1180         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1181                                     "--nodesc" ) )
1182         {
1183             actions.do_nodesc = TRUE;
1184         }
1185         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1186                                     "--nogeneratedwith" ) )
1187         {
1188             actions.do_nogenwith = TRUE;
1189         }
1190         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1191                                     "--cmode" ) )
1192         {
1193             actions.do_quotes = TRUE;
1194             actions.do_squotes = TRUE;
1195             actions.do_line_comments = TRUE;
1196             actions.do_block_comments = TRUE;
1197             actions.do_keywords = TRUE;
1198             actions.do_non_alpha = TRUE;
1199
1200             Install_C_Syntax(  );
1201         }
1202         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1203                                     "--lock" ) )
1204         {
1205             actions.do_lockheader = TRUE;
1206         }
1207         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1208                                     "--footless" ) )
1209         {
1210             actions.do_footless = TRUE;
1211         }
1212         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1213                                     "--verbal" ) )
1214         {
1215             actions.do_verbal = TRUE;
1216         }
1217         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1218                                     "--ms_errors" ) )
1219         {
1220             actions.do_ms_errors = TRUE;
1221         }
1222         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1223                                     "--headless" ) )
1224         {
1225             actions.do_headless = TRUE;
1226         }
1227         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1228                                     "--nosort" ) )
1229         {
1230             actions.do_nosort = TRUE;
1231         }
1232         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1233                                     "--nopre" ) )
1234         {
1235             actions.do_nopre = TRUE;
1236         }
1237         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1238                                     "--sectionnameonly" ) )
1239         {
1240             actions.do_sectionnameonly = TRUE;
1241         }
1242         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1243                                     "--altlatex" ) )
1244         {
1245             actions.do_altlatex = TRUE;
1246         }
1247         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1248                                     "--latexparts" ) )
1249         {
1250             actions.do_latexparts = TRUE;
1251         }
1252         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1253                                     "--syntaxcolors" ) )
1254         {
1255             actions.do_quotes = TRUE;
1256             actions.do_squotes = TRUE;
1257             actions.do_line_comments = TRUE;
1258             actions.do_block_comments = TRUE;
1259             actions.do_keywords = TRUE;
1260             actions.do_non_alpha = TRUE;
1261         }
1262         else
1263         {
1264             /* Not an action */
1265         }
1266     }
1267
1268     // Find specific syntax colors enable options
1269     optstr = Find_Parameterized_Option( "--syntaxcolors_enable" );
1270     if ( optstr )
1271     {
1272         char               *str;
1273
1274         // Parse options
1275         for ( str = strtok( optstr, "," ); str; str = strtok( NULL, "," ) )
1276         {
1277             if ( !RB_Str_Case_Cmp( str, "quotes" ) )
1278             {
1279                 actions.do_quotes = TRUE;
1280             }
1281             else if ( !RB_Str_Case_Cmp( str, "squotes" ) )
1282             {
1283                 actions.do_squotes = TRUE;
1284             }
1285             else if ( !RB_Str_Case_Cmp( str, "line_comments" ) )
1286             {
1287                 actions.do_line_comments = TRUE;
1288             }
1289             else if ( !RB_Str_Case_Cmp( str, "block_comments" ) )
1290             {
1291                 actions.do_block_comments = TRUE;
1292             }
1293             else if ( !RB_Str_Case_Cmp( str, "keywords" ) )
1294             {
1295                 actions.do_keywords = TRUE;
1296             }
1297             else if ( !RB_Str_Case_Cmp( str, "non_alpha" ) )
1298             {
1299                 actions.do_non_alpha = TRUE;
1300             }
1301             else
1302             {
1303                 // Bad option specified
1304                 RB_Panic( "Invalid --syntaxcolors_enable option: %s\n", str );
1305             }
1306         }
1307     }
1308
1309     return actions;
1310 }
1311
1312 /* TODO: FS Documentation */
1313 long Find_DebugMode(
1314     void )
1315 {
1316     long                modes = 0;
1317     unsigned int        parameter_nr;
1318
1319     for ( parameter_nr = 0;
1320           parameter_nr < configuration.options.number; parameter_nr++ )
1321     {
1322         if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1323                                "--debug" ) )
1324         {
1325             modes |= SAY_INFO;
1326             modes |= SAY_DEBUG;
1327         }
1328         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1329                                     "--tell" ) )
1330         {
1331             modes |= SAY_INFO;
1332         }
1333     }
1334     return modes;
1335 }
1336
1337 /****f* UserInterface/Find_Parameterized_Option
1338  * FUNCTION
1339  *   Search for an option of the form
1340  *     --a_option_name a_value
1341  *   in configuration.options.
1342  * SYNOPSIS
1343  */
1344 char               *Find_Parameterized_Option(
1345     char *optionname )
1346 /*
1347  * INPUTS
1348  *   optionname -- the name of the option to search for.
1349  * RESULT
1350  *   NULL if the option is not found, a pointer to the value
1351  *   otherwise.
1352  * NOTES
1353  *   Results in a Panic if the option is found but
1354  *   no value is specified.
1355  * SOURCE
1356  */
1357 {
1358     return General_Find_Parameterized_Option( configuration.options.number,
1359                                               &( configuration.options.
1360                                                  names[0] ), optionname );
1361 }
1362
1363 /******/
1364
1365
1366 /****f* UserInterface/RB_Find_In_Argv_Parameterized_Option
1367  * FUNCTION
1368  *   Search for an option of the form
1369  *     --a_option_name a_value
1370  *   in argv.   The function is used to look for the
1371  *     --rc  
1372  *   option that can be used to specify an 
1373  *   alternate robodoc configuration file.
1374  * SYNOPSIS
1375  */
1376 char               *RB_Find_In_Argv_Parameterized_Option(
1377     int argc,
1378     char **argv,
1379     char *optionname )
1380 /*
1381  * INPUTS
1382  *   * argc -- the argument count as received by main().
1383  *   * argv -- the array of argument values as received by main()
1384  *   * optionname -- the name of the option to search for.
1385  * RESULT
1386  *   NULL if the option is not found, a pointer to the value
1387  *   otherwise.
1388  * NOTES
1389  *   Results in a Panic if the option is found but
1390  *   no value is specified.
1391  * SOURCE
1392  */
1393 {
1394     return General_Find_Parameterized_Option( argc, argv, optionname );
1395 }
1396
1397 /*****/
1398
1399
1400 /****f* UserInterface/General_Find_Parameterized_Option
1401  * FUNCTION
1402  *   Search for an option of the form
1403  *     --a_option_name a_value
1404  * SYNOPSIS
1405  */
1406 static char        *General_Find_Parameterized_Option(
1407     int n,
1408     char **options,
1409     char *optionname )
1410 /*
1411  * INPUTS
1412  *   o n -- the number of options in the options array.
1413  *   o options -- the options array
1414  *   o optionname -- the name of the option to search for.
1415  * RESULT
1416  *   NULL if the option is not found, a pointer to the value
1417  *   otherwise.
1418  * NOTES
1419  *   Results in a Panic if the option is found but
1420  *   no value is specified.
1421  * SOURCE
1422  */
1423 {
1424     int                 parameter_nr;
1425     char               *value = NULL;
1426
1427     for ( parameter_nr = 0; parameter_nr < n; parameter_nr++ )
1428     {
1429         if ( !RB_Str_Case_Cmp( options[parameter_nr], optionname ) )
1430         {
1431             if ( parameter_nr < n - 1 )
1432             {
1433                 value = options[parameter_nr + 1];
1434                 if ( ( value[0] == '-' ) && ( value[1] == '-' ) )
1435                 {
1436                     value = NULL;
1437                 }
1438             }
1439             else
1440             {
1441                 /* to few parameters. */
1442             }
1443             if ( !value )
1444             {
1445                 RB_Panic( "you must be more specific"
1446                           " with the %s option\n", optionname );
1447             }
1448         }
1449     }
1450     return value;
1451 }
1452
1453 /******/
1454
1455
1456 /* TODO Documentation */
1457
1458 static void RB_Summary(
1459     struct RB_Document *document )
1460 {
1461     USE( document );
1462 #if 0
1463     printf( "Ready\n" );
1464     if ( document )
1465     {
1466         printf( "Found %d headers\n", document->no_headers );
1467     }
1468     if ( number_of_warnings )
1469     {
1470         printf( "Found %d warnings\n", number_of_warnings );
1471     }
1472 #endif
1473 }