Added options to make Robodoc more customizable.
[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     "   --document_header filename\n"
295     "                    Read document header from specified file\n"
296     "   --document_footer filename\n"
297     "                    Read document footer from specified file\n"
298     "   --one_file_per_header\n"
299     "                    Create a separate documentation file for each header\n"
300     "   --module_index_menu\n"
301     "                    Create a module menu when used with --one_file_per_header\n"
302     "   --header_toc\n"
303     "                    Create a header TOC when used with --one_file_per_header\n"
304     "   --header_breaks NUMBER\n"
305     "                    Insert a linebreak after every NUMBER header names\n"
306     "                    (default value: 2, set to zero to disable)\n" "\n";
307 char                use_authors[] =
308     "Authors/Contributors:\n"
309     "   Frans Slothouber, Jacco van Weert, Petteri Kettunen, Bernd Koesling,\n"
310     "   Thomas Aglassinger, Anthon Pang, Stefan Kost, David Druffner, Sasha Vasko,\n"
311     "   Kai Hofmann, Thierry Pierron, Friedrich Haase, Gergely Budai.\n";
312 /********/
313
314
315 /****v* UserInterface/copying
316  * FUNCTION
317  *   Information about the ROBODoc licence.
318  * AUTHOR
319  *   Frans
320  * HISTORY
321  *   2003-02-25/petterik: corrected link to GNU copyleft.
322  *******
323  */
324
325 char                copying[] =
326     "\n"
327     " Distributed under the GNU GENERAL PUBLIC LICENSE\n"
328     "   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n"
329     " See the source archive for a copy of the complete licence\n"
330     " If you do not have it you can get it from URL\n"
331     " http://www.gnu.org/copyleft/gpl.html\n";
332
333
334 static void dump_version(
335     void )
336 {
337     printf( "%s\n", VERSION );
338     /* TODO create an option to show robodoc configuration,
339      * including compiler setting etc..
340      printf( "Configuration:\n" );
341      printf( "  locale: %s", setlocale( LC_ALL, "" ) );
342      */
343 }
344
345 /* TODO  This function is too long. */
346
347 /****f* UserInterface/main
348  * FUNCTION
349  *   Get and parse the arguments.  Analyse document and generate the
350  *   documentation. Everything starts from here.
351  * SYNOPSIS
352  */
353 int main(
354     int argc,
355     char **argv )
356 /*
357  * SOURCE
358  */
359 {
360     struct RB_Document *document = NULL;
361     struct RB_Directory *srctree = NULL;
362     char               *optstr = NULL;
363     char               *used_rc_file = NULL;
364     char               *doc_h = NULL;
365     char               *doc_f = NULL;
366     char               buffer[8192];
367     int                r_len;
368     long                debug = 0;
369
370 /*
371    TODO, make setlocale work.
372     char * loc;
373
374     if ( (loc = getenv("LC_CTYPE") ) != NULL )
375     {
376         printf( ".... %s\n", loc );
377         setlocale( LC_ALL, loc);
378     }
379     else
380     {
381         setlocale(LC_ALL, "");
382     }
383 */
384
385     whoami = argv[0];           /* global me,myself&i */
386
387     // Init Actions global
388     course_of_action = No_Actions(  );
389
390     /* Read the configuration file. This might contain
391        addition options. */
392     RB_SetCurrentFile( NULL );
393     used_rc_file = ReadConfiguration( argc, argv,
394                                       RB_Find_In_Argv_Parameterized_Option
395                                       ( argc, argv, "--rc" ) );
396
397     /* Force debug mode early if the user wants the debug mode */
398     debugmode = Find_DebugMode(  );
399     course_of_action.do_tell = TRUE;
400
401     if ( Check_Options(  ) == EXIT_FAILURE )
402     {
403
404         return EXIT_FAILURE;
405     }
406
407     if ( Find_Option( "-c" ) )
408     {
409         printf( "%s", copying );
410         return EXIT_SUCCESS;
411     }
412
413     if ( Find_Option( "--version" ) )
414     {
415         dump_version(  );
416         return EXIT_SUCCESS;
417     }
418
419     if ( Find_Option( "--help" ) )
420     {
421         printf( "%s%s%s%s%s%s", use, use_usage, use_options1, use_options2,
422                 use_options3, use_authors );
423         return EXIT_SUCCESS;
424     }
425
426     output_mode = Find_DocType(  );     /* one of the globals that are still left */
427     if ( output_mode == UNKNOWN )
428     {
429         Print_Short_Use(  );
430         return EXIT_SUCCESS;
431     }
432
433     /* First the basics. */
434     document = RB_Get_RB_Document(  );
435     document->doctype = output_mode;
436     document->actions = Find_Actions(  );
437     debug = document->debugmode = Find_DebugMode(  );
438     document->charset = Find_Parameterized_Option( "--charset" );
439     document->extension = Find_Parameterized_Option( "--ext" );
440     document->css = Find_Parameterized_Option( "--css" );
441     document->compress = Find_Parameterized_Option( "--compress" );
442     document->section = Find_Parameterized_Option( "--mansection" );
443     document_title = Find_Parameterized_Option( "--documenttitle" );
444     doc_h = Find_Parameterized_Option( "--document_header" );
445     doc_f = Find_Parameterized_Option( "--document_footer" );
446     optstr = Find_Parameterized_Option( "--first_section_level" );
447     if ( optstr )
448     {
449         document->first_section_level = atoi( optstr );
450     }
451
452     course_of_action = document->actions;       /* a global */
453     debugmode = document->debugmode;    /* a global */
454
455     RB_Say( "Using %s for defaults\n", SAY_INFO, used_rc_file );
456     free( used_rc_file );       /* No longer necessary */
457     used_rc_file = NULL;
458
459     if ( document->css )
460     {
461         document->css = Path_Convert_Win32_to_Unix( document->css );
462     }
463
464     if ( ( document->actions.do_index ) && output_mode == TROFF )
465     {
466         RB_Warning( "Index generation not supported for TROFF format.\n" );
467         document->actions.do_index = FALSE;
468     }
469
470     if ( Find_Parameterized_Option( "--doctype_name" ) &&
471          Find_Parameterized_Option( "--doctype_location" ) )
472     {
473         document->doctype_name =
474             Find_Parameterized_Option( "--doctype_name" );
475         document->doctype_location =
476             Find_Parameterized_Option( "--doctype_location" );
477     }
478
479     // Find tab sizes and tab stops
480     Find_Tabstops(  );
481
482     // Find master index file name
483     Find_And_Install_Document_Name( "--masterindex", HT_MASTERINDEXTYPE );
484
485     // Find source index file name
486     Find_And_Install_Document_Name( "--sourceindex", HT_SOURCEHEADERTYPE );
487
488     // Find DOT tool name
489     optstr = Find_Parameterized_Option( "--dotname" );
490     if ( optstr )
491     {
492         dot_name = optstr;
493     }
494
495     // Find number of headers before linebreak
496     optstr = Find_Parameterized_Option( "--header_breaks" );
497     if ( optstr )
498     {
499         int                 breaks = atoi( optstr );
500
501         if ( breaks == 0 )
502         {
503             breaks = MAX_HEADER_BREAKS;
504         }
505         header_breaks = breaks;
506     }
507
508     // Get extension
509     if ( !document->extension )
510     {
511         document->extension = RB_Get_Default_Extension( document->doctype );
512     }
513
514
515     /* Test if there is a --src and --doc */
516     if ( !Find_Parameterized_Option( "--src" ) )
517     {
518         printf( "Error: you need to specify a source"
519                 " file or directory with --src.\n" );
520         Print_Short_Use(  );
521         return EXIT_FAILURE;
522     }
523     if ( !Find_Parameterized_Option( "--doc" ) )
524     {
525         printf( "Error: you need to specify a documentation file"
526                 " or directory with --doc.\n" );
527         Print_Short_Use(  );
528         return EXIT_FAILURE;
529     }
530
531     /* Open header and footer files */
532     if ( doc_h )
533     {
534         FILE *dh = fopen( doc_h, "r" );
535         if ( !dh )
536         {
537             printf( "Error: '%s' no such file or directory\n", doc_h );
538             Print_Short_Use(  );
539             return EXIT_FAILURE;
540         }
541
542         memset( buffer, 0, sizeof( buffer ) );
543         r_len = fread ( buffer, 1, sizeof( buffer ), dh );
544         if ( r_len > 0 )
545         {
546           document_header = calloc( r_len, sizeof( *document_header ) );
547           memcpy( document_header, buffer, r_len );
548         }
549         fclose( dh );
550     }
551     if ( doc_f )
552     {
553         FILE *df = fopen( doc_f, "r" );
554         if ( !df )
555         {
556             printf( "Error: '%s' no such file or directory\n", doc_f );
557             Print_Short_Use(  );
558             return EXIT_FAILURE;
559         }
560
561         memset( buffer, 0, sizeof( buffer ) );
562         r_len = fread ( buffer, 1, sizeof( buffer ), df );
563         if ( r_len > 0 )
564         {
565           document_footer = calloc( r_len, sizeof( *document_footer ) );
566           memcpy( document_footer, buffer, r_len );
567         }
568         fclose( df );
569     }
570
571     /* What mode are we using? */
572     if ( Find_Option( "--multidoc" ) )
573     {
574         char               *srcrootname;        /* directory */
575         char               *docrootname;
576
577         srcrootname = Find_And_Fix_Path( "--src" );
578         if ( !Stat_Path( 'e', srcrootname ) )
579         {
580             printf( "Error: %s does not exists\n", srcrootname );
581             Print_Short_Use(  );
582             return EXIT_FAILURE;
583         }
584
585         if ( !Stat_Path( 'd', srcrootname ) )
586         {
587             printf( "Error: %s is not a directory\n", srcrootname );
588             Print_Short_Use(  );
589             return EXIT_FAILURE;
590         }
591         document->srcroot = RB_Get_RB_Path( srcrootname );
592
593         docrootname = Find_And_Fix_Path( "--doc" );
594         Path_Check( srcrootname, docrootname );
595
596         document->docroot = RB_Get_RB_Path( docrootname );
597
598         srctree = RB_Get_RB_Directory( srcrootname, docrootname );
599         document->srctree = srctree;
600
601         RB_Document_Create_Parts( document );
602         RB_Analyse_Document( document );
603         RB_Generate_Documentation( document );
604
605         RB_Free_RB_Path( document->srcroot );
606         document->srcroot = 0;
607         RB_Free_RB_Path( document->docroot );
608         document->docroot = 0;
609         RB_Free_RB_Directory( srctree );
610         document->srctree = 0;
611     }
612     else if ( output_mode == TROFF )
613     {
614         RB_Panic( "Only --multidoc is supported for TROFF output.\n" );
615     }
616     else if ( Find_Option( "--singledoc" ) )
617     {
618         char               *srcrootname;        /* directory */
619
620         srcrootname = Find_And_Fix_Path( "--src" );
621         if ( !Stat_Path( 'e', srcrootname ) )
622         {
623             printf( "Error: %s does not exists\n", srcrootname );
624             Print_Short_Use(  );
625             return EXIT_FAILURE;
626         }
627
628         if ( !Stat_Path( 'd', srcrootname ) )
629         {
630             printf( "Error: %s is not a directory\n", srcrootname );
631             Print_Short_Use(  );
632             return EXIT_FAILURE;
633         };
634         document->srcroot = RB_Get_RB_Path( srcrootname );
635
636         document->docroot = 0;
637         document->singledoc_name = Find_And_Fix_Path( "--doc" );
638
639         srctree = RB_Get_RB_Directory( srcrootname, NULL );
640         document->srctree = srctree;
641
642         RB_Document_Create_Parts( document );
643         RB_Analyse_Document( document );
644         RB_Generate_Documentation( document );
645
646         RB_Free_RB_Directory( srctree );
647     }
648     else if ( Find_Option( "--singlefile" ) )
649     {
650         char               *srcfile;    /* file */
651         char               *docfile;    /* file */
652
653         document->docroot = 0;
654         docfile = Find_And_Fix_Path( "--doc" );
655         document->singledoc_name = docfile;
656         srcfile = Find_And_Fix_Path( "--src" );
657         if ( !Stat_Path( 'e', srcfile ) )
658         {
659             printf( "Error: %s does not exists\n", srcfile );
660             Print_Short_Use(  );
661             return EXIT_FAILURE;
662         };
663
664         if ( !Stat_Path( 'f', srcfile ) )
665         {
666             printf( "Error: %s is not a file\n", srcfile );
667             Print_Short_Use(  );
668             return EXIT_FAILURE;
669         };
670
671         document->srctree = RB_Get_RB_SingleFileDirectory( srcfile );
672         document->srcroot =
673             RB_Get_RB_Path( document->srctree->first_path->name );
674
675         RB_Document_Create_Parts( document );
676         RB_Analyse_Document( document );
677         RB_Generate_Documentation( document );
678
679         RB_Free_RB_Directory( document->srctree );
680     }
681     else
682     {
683         Print_Short_Use(  );
684         printf
685             ( "\n\nError: Use either --multidoc, --singledoc, or --singlefile\n" );
686         return EXIT_FAILURE;
687     }
688
689     RB_Summary( document );
690     RB_Free_RB_Document( document );
691     Free_Configuration(  );
692
693 #ifdef __APPLE__
694     /* Mac OS X specific: print memory leaks */
695     if ( debug & SAY_DEBUG )
696     {
697         char                cmd[32];
698
699         sprintf( cmd, "/usr/bin/leaks %d", getpid(  ) );
700         system( cmd );
701     }
702 #endif /* __APPLE__ */
703
704     return EXIT_SUCCESS;
705 }
706
707 /*******/
708
709
710 /****if* UserInterface/Find_Tabstops
711  * FUNCTION
712  *   This function looks for the tab stop configuration and fills out the
713  *   tab_stops table.
714  * SYNOPSIS
715  */
716 static void Find_Tabstops(
717     void )
718 /*
719  * SOURCE
720  */
721 {
722     int                 i;
723     int                 tabsize = DEFAULT_TABSIZE;
724     char               *optstr, *str;
725
726     // Find tab sizes
727     optstr = Find_Parameterized_Option( "--tabsize" );
728     if ( optstr )
729     {
730         tabsize = atoi( optstr );
731     }
732
733     // Fill tabstop table with default values
734     for ( i = 0; i < MAX_TABS; i++ )
735     {
736         tab_stops[i] = tabsize * ( i + 1 );
737     }
738
739     // Find tab stops
740     optstr = Find_Parameterized_Option( "--tabstops" );
741     if ( optstr )
742     {
743         // Get TAB sizes and fill table
744         for ( str = strtok( optstr, TABSIZE_SEPARATOR ), i = 0;
745               str != NULL && i < MAX_TABS;
746               str = strtok( NULL, TABSIZE_SEPARATOR ), i++ )
747         {
748             tab_stops[i] = atoi( str ) - 1;
749         }
750     }
751 }
752
753 /*******/
754
755
756 /****if* UserInterface/Find_And_Install_Document_Name
757  * FUNCTION
758  *   Looks for specific option strings and installs special document titles
759  *   and filenames.
760  *
761  *   Should be used to overwrite the HT_MASTERINDEXTYPE or HT_SOURCEHEADERTYPE
762  *   entries in the header_type_lookup_table.
763  * SYNOPSIS
764  */
765 static void Find_And_Install_Document_Name(
766     char *option,
767     unsigned char type )
768 /*
769  * INPUTS
770  *   o option -- the option string to look for
771  *   o type   -- the type entry in header_type_lookup_table to overwrite
772  *               (Should be either HT_MASTERINDEXTYPE or HT_SOURCEHEADERTYPE)
773  *
774  * SOURCE
775  */
776 {
777     char               *title = NULL, *filename = NULL, *optstr = NULL;
778
779     // First find the option
780     optstr = Find_Parameterized_Option( option );
781     if ( optstr )
782     {
783
784         // Break the option into substrings
785         title = strtok( optstr, "," );
786         filename = strtok( NULL, "," );
787
788         // If something missing
789         if ( title == NULL || filename == NULL )
790         {
791             RB_Panic( "Invalid %s option\n", option );
792         }
793
794         // Install document title and filename
795         RB_AddHeaderType( type, title, filename, 0 );
796     }
797 }
798
799 /*******/
800
801
802
803 /****f* UserInterface/Find_And_Fix_Path
804  * FUNCTION
805  *   Searches through the options to find a path.
806  *   This path is converted to a propper path
807  *   if it contains errors such as the use of
808  *   '\' or when it does not start with ./ or a
809  *   drive name.
810  *   The option must exist.
811  * SYNOPSIS
812  */
813 static char        *Find_And_Fix_Path(
814     char *option_name )
815 /*
816  * INPUTS
817  *   o option_name -- the option name for the path
818  * RESULT
819  *   o path -- the path.
820  *
821  * SOURCE
822  */
823 {
824     char               *temp;
825     char               *temp2;
826
827     temp = Find_Parameterized_Option( option_name );
828     assert( temp );
829     temp = Path_Convert_Win32_to_Unix( temp );
830     temp2 = Fix_Path( temp );
831     free( temp );
832     return temp2;
833 }
834
835 /*******/
836
837
838 /****f* UserInterface/Path_Convert_Win32_to_Unix
839  * FUNCTION
840  *   Although people are supposed to specify all paths
841  *   with a '/' as seperator, sometimes people on Win32
842  *   use '\', this causes problems later on in some
843  *   other function of robodoc that expect a '/'.
844  *   So to prevent this we replace all the '\' in a path
845  *   with '/'
846  *
847  *   In addition people sometimes add a '/' at the
848  *   end of the path. We remove it.
849  *
850  * SYNOPSIS
851  */
852 static char        *Path_Convert_Win32_to_Unix(
853     char *path )
854 /*
855  * INPUTS
856  *   o path -- the path.
857  * RESULT
858  *   o path -- the converted path (in a newly allocated
859  *             block of memory).
860  * SOURCE
861  */
862 {
863     size_t              i;
864
865     /* First make a copy */
866     path = RB_StrDup( path );
867     for ( i = 0; i < strlen( path ); ++i )
868     {
869         if ( path[i] == '\\' )
870         {
871             path[i] = '/';
872         }
873     }
874
875     /* Remove trailing '/' if there is one. */
876     if ( path[strlen( path ) - 1] == '/' )
877     {
878         path[strlen( path ) - 1] = '\0';
879     }
880
881     return path;
882 }
883
884 /*****/
885
886 /****f* UserInterface/Path_Check
887  * FUNCTION
888  *   Test the validity of the doc and source path. The doc path should
889  *   not be a sub directory of the source path otherwise the generated
890  *   documentation will be part of the generated documentation if
891  *   robodoc is run more than once.
892  * SYNOPSIS
893  */
894
895 static void Path_Check(
896     char *sourcepath,
897     char *docpath )
898 /*
899  * INPUTS
900  *   o sourcepath -- the path to the source files.
901  *   o docpath    -- the path to the documentation files.
902  * OUTPUT
903  *   o error messages
904  * SOURCE
905  */
906 {
907     if ( docpath )
908     {
909         int                 dl;
910         int                 sl;
911
912         dl = strlen( docpath );
913         sl = strlen( sourcepath );
914         if ( dl >= sl )
915         {
916             int                 i;
917             int                 equal = TRUE;
918
919             for ( i = 0; i < sl; ++i )
920             {
921                 if ( docpath[i] != sourcepath[i] )
922                 {
923                     equal = FALSE;
924                     break;
925                 }
926             }
927             if ( equal && ( dl == sl ) )
928             {
929                 RB_Panic
930                     ( "The source path and document path can not be equal\n" );
931             }
932             else
933             {
934                 /* OK  */
935             }
936         }
937     }
938 }
939
940 /*****/
941
942
943 /****f* UserInterface/PathBegin_Check
944  * FUNCTION
945  *   Checks the validity of a path.
946  *   A path should start with
947  *     ./
948  *   or
949  *     /
950  *   or
951  *     have a ':' some where
952  * SYNOPSIS
953  */
954 static int PathBegin_Check(
955     char *path )
956 /*
957  * INPUTS
958  *   o path -- the path to be cheked.
959  * RESULT
960  *   o FALSE -- path is not OK.
961  *   o TRUE  -- path is OK.
962  * SOURCE
963  */
964 {
965     int                 result = FALSE;
966     int                 l = strlen( path );
967
968     if ( l == 1 )
969     {
970         result = ( path[0] == '.' );
971     }
972     else if ( l >= 2 )
973     {
974         result = ( ( path[0] == '.' ) && ( path[1] == '/' ) ) ||
975             ( path[0] == '/' ) || ( strchr( path, ':' ) != NULL );
976     }
977     else
978     {
979         /* Empty */
980     }
981     return result;
982 }
983
984 /******/
985
986
987
988 /****f* UserInterface/Find_Option
989  * FUNCTION
990  *   Search configuration.options for a specific option.
991  * SYNOPSIS
992  */
993 int Find_Option(
994     char *option )
995 /* INPUTS
996  *   o option -- the option to be found.
997  * RESULT
998  *   o TRUE  -- option does exist
999  *   o FALSE -- option does not exist
1000  * SOURCE
1001  */
1002 {
1003     unsigned int        parameter_nr;
1004     int                 found = FALSE;
1005
1006     for ( parameter_nr = 0;
1007           parameter_nr < configuration.options.number; parameter_nr++ )
1008     {
1009         if ( !RB_Str_Case_Cmp
1010              ( configuration.options.names[parameter_nr], option ) )
1011         {
1012             found = TRUE;
1013             break;
1014         }
1015     }
1016     return found;
1017 }
1018
1019 /******/
1020
1021 /****f* UserInterface/Fix_Path
1022  * FUNCTION
1023  *   Add a "./" to a path if it does not start with a "./" or does not
1024  *   contain a ":".  If the path was "." just add a "/".  Adding a
1025  *   "./" simplifies the creating of relative links during the
1026  *   generation process.
1027  * SYNOPSIS
1028  */
1029
1030 static char        *Fix_Path(
1031     char *path )
1032 /*
1033  * INPUTS
1034  *   o path -- the path to be fixed.
1035  * RESULT
1036  *   A pointer to a newly allocated string containing the path.
1037  * SOURCE
1038  */
1039 {
1040     char               *result = 0;
1041
1042     if ( !PathBegin_Check( path ) )
1043     {
1044         char               *prefix = "./";
1045
1046         if ( strcmp( path, "." ) == 0 )
1047         {
1048             result = RB_StrDup( prefix );
1049         }
1050         else
1051         {
1052             int                 l = strlen( path );
1053
1054             l += strlen( prefix ) + 1;
1055             result = malloc( l );
1056             assert( result );
1057             result[0] = '\0';
1058             strcat( result, prefix );
1059             strcat( result, path );
1060         }
1061     }
1062     else
1063     {
1064         result = RB_StrDup( path );
1065     }
1066     return result;
1067 }
1068
1069 /*****/
1070
1071
1072 /* TODO: FS Documentation */
1073
1074 T_RB_DocType Find_DocType(
1075     void )
1076 {
1077     T_RB_DocType        doctype = UNKNOWN;
1078     unsigned int        parameter_nr;
1079
1080     for ( parameter_nr = 0;
1081           parameter_nr < configuration.options.number; parameter_nr++ )
1082     {
1083
1084         if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1085                                "--html" ) )
1086         {
1087             doctype = HTML;
1088             break;
1089         }
1090         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1091                                     "--latex" ) )
1092         {
1093             doctype = LATEX;
1094             break;
1095         }
1096         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1097                                     "--ascii" ) )
1098         {
1099             doctype = ASCII;
1100             break;
1101         }
1102         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1103                                     "--rtf" ) )
1104         {
1105             doctype = RTF;
1106             break;
1107         }
1108         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1109                                     "--test" ) )
1110         {
1111             doctype = TEST;
1112             break;
1113         }
1114         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1115                                     "--troff" ) )
1116         {
1117             doctype = TROFF;
1118             break;
1119         }
1120         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1121                                     "--dbxml" ) )
1122         {
1123             doctype = XMLDOCBOOK;
1124             break;
1125         }
1126         else
1127         {
1128             /* Ignore */
1129         }
1130     }
1131     return doctype;
1132 }
1133
1134
1135 actions_t No_Actions(
1136     void )
1137 {
1138     actions_t           actions;
1139     unsigned int        i;
1140     unsigned char      *actptr;
1141
1142     for ( i = 0, actptr = ( unsigned char * ) &actions;
1143           i < sizeof( actions ); i++, actptr++ )
1144     {
1145         *actptr = 0;
1146     }
1147
1148     return actions;
1149 }
1150
1151 /* TODO: FS Documentation */
1152 actions_t Find_Actions(
1153     void )
1154 {
1155     actions_t           actions;
1156     unsigned int        parameter_nr;
1157     char               *optstr;
1158
1159     actions = No_Actions(  );
1160
1161     for ( parameter_nr = 0;
1162           parameter_nr < configuration.options.number; parameter_nr++ )
1163     {
1164         if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1165                                "--singledoc" ) )
1166         {
1167             actions.do_singledoc = TRUE;
1168         }
1169         if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1170                                "--singlefile" ) )
1171         {
1172             actions.do_singlefile = TRUE;
1173         }
1174         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1175                                     "--multidoc" ) )
1176         {
1177             actions.do_multidoc = TRUE;
1178         }
1179         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1180                                     "--no_subdirectories" ) )
1181         {
1182             actions.do_no_subdirectories = TRUE;
1183         }
1184         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1185                                     "--one_file_per_header" ) )
1186         {
1187             actions.do_one_file_per_header = TRUE;
1188         }
1189         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1190                                     "--module_index_menu" ) )
1191         {
1192             actions.do_module_index_menu = TRUE;
1193         }
1194         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1195                                     "--header_toc" ) )
1196         {
1197             actions.do_header_toc = TRUE;
1198         }
1199         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1200                                     "--sections" ) )
1201         {
1202             actions.do_sections = TRUE;
1203         }
1204         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1205                                     "--internal" ) )
1206         {
1207             actions.do_include_internal = TRUE;
1208         }
1209         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1210                                     "--ignore_case_when_linking" ) )
1211         {
1212             actions.do_ignore_case_when_linking = TRUE;
1213         }
1214         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1215                                     "--internalonly" ) )
1216         {
1217             actions.do_internal_only = TRUE;
1218         }
1219         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1220                                     "--toc" ) )
1221         {
1222             actions.do_toc = TRUE;
1223         }
1224         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1225                                     "--index" ) )
1226         {
1227             actions.do_index = TRUE;
1228         }
1229         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1230                                     "--nosource" ) )
1231         {
1232             actions.do_nosource = TRUE;
1233         }
1234         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1235                                     "--tell" ) )
1236         {
1237             actions.do_tell = TRUE;
1238         }
1239         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1240                                     "--debug" ) )
1241         {
1242             actions.do_tell = TRUE;
1243         }
1244         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1245                                     "--nodesc" ) )
1246         {
1247             actions.do_nodesc = TRUE;
1248         }
1249         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1250                                     "--nogeneratedwith" ) )
1251         {
1252             actions.do_nogenwith = TRUE;
1253         }
1254         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1255                                     "--cmode" ) )
1256         {
1257             actions.do_quotes = TRUE;
1258             actions.do_squotes = TRUE;
1259             actions.do_line_comments = TRUE;
1260             actions.do_block_comments = TRUE;
1261             actions.do_keywords = TRUE;
1262             actions.do_non_alpha = TRUE;
1263
1264             Install_C_Syntax(  );
1265         }
1266         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1267                                     "--lock" ) )
1268         {
1269             actions.do_lockheader = TRUE;
1270         }
1271         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1272                                     "--footless" ) )
1273         {
1274             actions.do_footless = TRUE;
1275         }
1276         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1277                                     "--verbal" ) )
1278         {
1279             actions.do_verbal = TRUE;
1280         }
1281         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1282                                     "--ms_errors" ) )
1283         {
1284             actions.do_ms_errors = TRUE;
1285         }
1286         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1287                                     "--headless" ) )
1288         {
1289             actions.do_headless = TRUE;
1290         }
1291         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1292                                     "--nosort" ) )
1293         {
1294             actions.do_nosort = TRUE;
1295         }
1296         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1297                                     "--nopre" ) )
1298         {
1299             actions.do_nopre = TRUE;
1300         }
1301         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1302                                     "--sectionnameonly" ) )
1303         {
1304             actions.do_sectionnameonly = TRUE;
1305         }
1306         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1307                                     "--altlatex" ) )
1308         {
1309             actions.do_altlatex = TRUE;
1310         }
1311         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1312                                     "--latexparts" ) )
1313         {
1314             actions.do_latexparts = TRUE;
1315         }
1316         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1317                                     "--syntaxcolors" ) )
1318         {
1319             actions.do_quotes = TRUE;
1320             actions.do_squotes = TRUE;
1321             actions.do_line_comments = TRUE;
1322             actions.do_block_comments = TRUE;
1323             actions.do_keywords = TRUE;
1324             actions.do_non_alpha = TRUE;
1325         }
1326         else
1327         {
1328             /* Not an action */
1329         }
1330     }
1331
1332     // Find specific syntax colors enable options
1333     optstr = Find_Parameterized_Option( "--syntaxcolors_enable" );
1334     if ( optstr )
1335     {
1336         char               *str;
1337
1338         // Parse options
1339         for ( str = strtok( optstr, "," ); str; str = strtok( NULL, "," ) )
1340         {
1341             if ( !RB_Str_Case_Cmp( str, "quotes" ) )
1342             {
1343                 actions.do_quotes = TRUE;
1344             }
1345             else if ( !RB_Str_Case_Cmp( str, "squotes" ) )
1346             {
1347                 actions.do_squotes = TRUE;
1348             }
1349             else if ( !RB_Str_Case_Cmp( str, "line_comments" ) )
1350             {
1351                 actions.do_line_comments = TRUE;
1352             }
1353             else if ( !RB_Str_Case_Cmp( str, "block_comments" ) )
1354             {
1355                 actions.do_block_comments = TRUE;
1356             }
1357             else if ( !RB_Str_Case_Cmp( str, "keywords" ) )
1358             {
1359                 actions.do_keywords = TRUE;
1360             }
1361             else if ( !RB_Str_Case_Cmp( str, "non_alpha" ) )
1362             {
1363                 actions.do_non_alpha = TRUE;
1364             }
1365             else
1366             {
1367                 // Bad option specified
1368                 RB_Panic( "Invalid --syntaxcolors_enable option: %s\n", str );
1369             }
1370         }
1371     }
1372
1373     return actions;
1374 }
1375
1376 /* TODO: FS Documentation */
1377 long Find_DebugMode(
1378     void )
1379 {
1380     long                modes = 0;
1381     unsigned int        parameter_nr;
1382
1383     for ( parameter_nr = 0;
1384           parameter_nr < configuration.options.number; parameter_nr++ )
1385     {
1386         if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1387                                "--debug" ) )
1388         {
1389             modes |= SAY_INFO;
1390             modes |= SAY_DEBUG;
1391         }
1392         else if ( !RB_Str_Case_Cmp( configuration.options.names[parameter_nr],
1393                                     "--tell" ) )
1394         {
1395             modes |= SAY_INFO;
1396         }
1397     }
1398     return modes;
1399 }
1400
1401 /****f* UserInterface/Find_Parameterized_Option
1402  * FUNCTION
1403  *   Search for an option of the form
1404  *     --a_option_name a_value
1405  *   in configuration.options.
1406  * SYNOPSIS
1407  */
1408 char               *Find_Parameterized_Option(
1409     char *optionname )
1410 /*
1411  * INPUTS
1412  *   optionname -- the name of the option to search for.
1413  * RESULT
1414  *   NULL if the option is not found, a pointer to the value
1415  *   otherwise.
1416  * NOTES
1417  *   Results in a Panic if the option is found but
1418  *   no value is specified.
1419  * SOURCE
1420  */
1421 {
1422     return General_Find_Parameterized_Option( configuration.options.number,
1423                                               &( configuration.options.
1424                                                  names[0] ), optionname );
1425 }
1426
1427 /******/
1428
1429
1430 /****f* UserInterface/RB_Find_In_Argv_Parameterized_Option
1431  * FUNCTION
1432  *   Search for an option of the form
1433  *     --a_option_name a_value
1434  *   in argv.   The function is used to look for the
1435  *     --rc
1436  *   option that can be used to specify an
1437  *   alternate robodoc configuration file.
1438  * SYNOPSIS
1439  */
1440 char               *RB_Find_In_Argv_Parameterized_Option(
1441     int argc,
1442     char **argv,
1443     char *optionname )
1444 /*
1445  * INPUTS
1446  *   * argc -- the argument count as received by main().
1447  *   * argv -- the array of argument values as received by main()
1448  *   * optionname -- the name of the option to search for.
1449  * RESULT
1450  *   NULL if the option is not found, a pointer to the value
1451  *   otherwise.
1452  * NOTES
1453  *   Results in a Panic if the option is found but
1454  *   no value is specified.
1455  * SOURCE
1456  */
1457 {
1458     return General_Find_Parameterized_Option( argc, argv, optionname );
1459 }
1460
1461 /*****/
1462
1463
1464 /****f* UserInterface/General_Find_Parameterized_Option
1465  * FUNCTION
1466  *   Search for an option of the form
1467  *     --a_option_name a_value
1468  * SYNOPSIS
1469  */
1470 static char        *General_Find_Parameterized_Option(
1471     int n,
1472     char **options,
1473     char *optionname )
1474 /*
1475  * INPUTS
1476  *   o n -- the number of options in the options array.
1477  *   o options -- the options array
1478  *   o optionname -- the name of the option to search for.
1479  * RESULT
1480  *   NULL if the option is not found, a pointer to the value
1481  *   otherwise.
1482  * NOTES
1483  *   Results in a Panic if the option is found but
1484  *   no value is specified.
1485  * SOURCE
1486  */
1487 {
1488     int                 parameter_nr;
1489     char               *value = NULL;
1490
1491     for ( parameter_nr = 0; parameter_nr < n; parameter_nr++ )
1492     {
1493         if ( !RB_Str_Case_Cmp( options[parameter_nr], optionname ) )
1494         {
1495             if ( parameter_nr < n - 1 )
1496             {
1497                 value = options[parameter_nr + 1];
1498                 if ( ( value[0] == '-' ) && ( value[1] == '-' ) )
1499                 {
1500                     value = NULL;
1501                 }
1502             }
1503             else
1504             {
1505                 /* to few parameters. */
1506             }
1507             if ( !value )
1508             {
1509                 RB_Panic( "you must be more specific"
1510                           " with the %s option\n", optionname );
1511             }
1512         }
1513     }
1514     return value;
1515 }
1516
1517 /******/
1518
1519
1520 /* TODO Documentation */
1521
1522 static void RB_Summary(
1523     struct RB_Document *document )
1524 {
1525     USE( document );
1526 #if 0
1527     printf( "Ready\n" );
1528     if ( document )
1529     {
1530         printf( "Found %d headers\n", document->no_headers );
1531     }
1532     if ( number_of_warnings )
1533     {
1534         printf( "Found %d warnings\n", number_of_warnings );
1535     }
1536 #endif
1537 }