Added options to make Robodoc more customizable.
[robodoc.git] / Source / optioncheck.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 /* $Id: optioncheck.c,v 1.19 2007/07/10 19:13:52 gumpu Exp $*/
24
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include "robodoc.h"
30 #include "optioncheck.h"
31 #include "roboconfig.h"
32 #include "util.h"
33
34
35 char                short_use[] =
36     "Usage:\n"
37     "   robodoc --src <directory> --doc <directory> --multidoc   [type] [options]\n"
38     "   robodoc --src <directory> --doc <file>      --singledoc  [type] [options]\n"
39     "   robodoc --src <file>      --doc <file>      --singlefile [type] [options]\n"
40     "\n" "Use robodoc --help for more information\n";
41
42
43 void Print_Short_Use(
44     void )
45 {
46     fprintf( stdout, "%s\n", short_use );
47 }
48
49 /****t* UserInterface/Option_Test_Kind
50  * FUNCTION
51  *   Enumeration for the kind of tests that are carried out on the
52  *   options that the user specifies.
53  * SOURCE
54  */
55 typedef enum
56 {
57     TEST_INVALID = 0,
58     TEST_MUTUAL_EXCLUDE = 1,
59     TEST_SHOULD_EXISTS,         /* TODO Create this kind of test */
60     TEST_NO_EFFECT,             /* TODO Create this kind of test */
61     TEST_COUNT,                 /* TODO Create this kind of test */
62     TEST_SPELLING               /* TODO Create this kind of test */
63 } Option_Test_Kind;
64
65 /*****/
66
67 typedef enum
68 {
69     OPTION_FATAL = 1,
70     OPTION_WARNING
71 } Option_Error_Severity;
72
73 /****s* UserInterface/RB_Option_Name
74  * FUNCTION
75  *   Element in a list of option names.
76  *   Used in a RB_Option_Test to specify to what
77  *   options a test applies.
78  * SOURCE
79  */
80
81 struct RB_Option_Name
82 {
83     /* linked list administration */
84     struct RB_Option_Name *next;
85     /* name of the option */
86     char               *name;
87     /* use by the Count test */
88     int                 count;
89 };
90
91 /*****/
92
93 /****s* UserInterface/RB_Option_Test
94  * FUNCTION
95  *   A test specification for options. This
96  *   stores information about the kind of test and
97  *   the options it applies to, and the message that
98  *   is given to the user.
99  * SOURCE
100  */
101
102 struct RB_Option_Test
103 {
104     /* tests are stored in a linked list */
105     struct RB_Option_Test *next;
106     /* the group of options the test applies to. */
107     struct RB_Option_Name *option_group;
108     /* the kind of test */
109     Option_Test_Kind    kind;
110     /* More information for the user */
111     char               *more_information;       /* TODO Fill and use */
112
113     Option_Error_Severity severity;
114 };
115
116 /******/
117
118 /****v* UserInterface/ok_options
119  * FUNCTION
120  *   An list of all allowed command-line options.  If you add any
121  *   options add its name here too.  This list is used to verify
122  *   the options specified by the user.
123  * SOURCE
124  */
125
126 static char        *ok_options[] = {
127     "--rc",
128     "--src",
129     "--doc",
130     "--html",
131     "--latex",
132     "--ascii",
133     "--rtf",
134     "--troff",
135     "--dbxml",
136     "--doctype_name",
137     "--doctype_location",
138     "--singledoc",
139     "--singlefile",
140     "--multidoc",
141     "--one_file_per_header",
142     "--module_index_menu",
143     "--header_toc",
144     "--first_section_level",
145     "--sections",
146     "--internal",
147     "--internalonly",
148     "--ignore_case_when_linking",
149     "--toc",
150     "--index",
151     "--nosource",
152     "--tabsize",
153     "--tell",
154     "--debug",
155     "--test",                   /* Special output mode for testing */
156     "--nodesc",
157     "--nopre",
158     "--nogeneratedwith",
159     "--no_subdirectories",
160     "--cmode",
161     "--charset",
162     "--ext",
163     "--help",
164     "--css",
165     "--version",
166     "-c",
167     "--lock",
168     "--nosort",
169     "--headless",
170     "--footless",
171     "--sectionnameonly",
172     "--compress",
173     "--mansection",
174     "--verbal",                 /*FS TODO */
175     "--ms_errors",              /* TODO is this documented?? */
176     "--documenttitle",
177     "--altlatex",
178     "--latexparts",
179     "--tabstops",
180     "--syntaxcolors",
181     "--syntaxcolors_enable",
182     "--dotname",
183     "--masterindex",
184     "--sourceindex",
185     "--header_breaks",
186     "--document_header",
187     "--document_footer",
188     ( char * ) NULL
189 };
190
191 /*****/
192
193
194 /****v* UserInterface/option_tests
195  * FUNCTION
196  *   A linked lists of tests that check the options specified
197  *   by the user.
198  * SOURCE
199  */
200 static struct RB_Option_Test *option_tests = NULL;
201
202 /*****/
203
204
205 /****f* UserInterface/Add_Option_Test
206  * FUNCTION
207  *   Add a test to the linked list of options tests.
208  * SYNOPSIS
209  */
210 static void Add_Option_Test(
211     struct RB_Option_Test *option_test )
212 /*
213  * INPUTS
214  *   option_test -- the test to be added.
215  * SOURCE
216  */
217 {
218     option_test->next = option_tests;
219     option_tests = option_test;
220 }
221
222 /*****/
223
224 /****f* UserInterface/Add_Option_Name
225  * FUNCTION
226  *   Add the name of an option to the group of option names an option
227  *   test applies to.
228  * SYNOPSIS
229  */
230 static void Add_Option_Name(
231     struct RB_Option_Test *option_test,
232     char *name )
233 /*
234  * INPUTS
235  *   option_test -- the option test
236  *   name -- the name of the option
237  * SOURCE
238  */
239 {
240     struct RB_Option_Name *new_option_name =
241         malloc( sizeof( struct RB_Option_Name ) );
242     new_option_name->name = RB_StrDup( name );
243     new_option_name->next = option_test->option_group;
244     new_option_name->count = 0;
245     option_test->option_group = new_option_name;
246 }
247
248 /****/
249
250 /****f* UserInterface/Create_New_Option_Test
251  * FUNCTION
252  *   Allocate and initialize a new option test.
253  * SYNOPSIS
254  */
255 static struct RB_Option_Test *Create_New_Option_Test(
256     Option_Test_Kind kind,
257     Option_Error_Severity severity )
258 /*
259  * INPUTS
260  *   kind -- the kind of test that has to be created.
261  * SOURCE
262  */
263 {
264     struct RB_Option_Test *new_option_test =
265         malloc( sizeof( struct RB_Option_Test ) );
266     new_option_test->next = NULL;
267     new_option_test->option_group = NULL;
268     new_option_test->kind = kind;
269     new_option_test->severity = severity;
270     return new_option_test;
271 }
272
273 /*****/
274
275 /****f* UserInterface/Create_Test_Data
276  * FUNCTION
277  *   Create a linked list of tests.
278  * SYNOPSIS
279  */
280 static void Create_Test_Data(
281     void )
282 /*
283  * TODO
284  *   Generate this code automatically from a set
285  *   of high-level specifications.
286  * SOURCE
287  */
288 {
289     struct RB_Option_Test *cur_option_test = NULL;
290     int                 i;
291
292     cur_option_test =
293         Create_New_Option_Test( TEST_MUTUAL_EXCLUDE, OPTION_FATAL );
294     Add_Option_Name( cur_option_test, "--singledoc" );
295     Add_Option_Name( cur_option_test, "--multidoc" );
296     Add_Option_Name( cur_option_test, "--singlefile" );
297     Add_Option_Test( cur_option_test );
298
299     cur_option_test =
300         Create_New_Option_Test( TEST_MUTUAL_EXCLUDE, OPTION_FATAL );
301     Add_Option_Name( cur_option_test, "--html" );
302     Add_Option_Name( cur_option_test, "--rtf" );
303     Add_Option_Name( cur_option_test, "--ascii" );
304     Add_Option_Name( cur_option_test, "--dbxml" );
305     Add_Option_Name( cur_option_test, "--troff" );
306     Add_Option_Name( cur_option_test, "--latex" );
307     Add_Option_Name( cur_option_test, "--test" );
308     Add_Option_Test( cur_option_test );
309
310     /* Order is important here */
311     cur_option_test = Create_New_Option_Test( TEST_COUNT, OPTION_FATAL );
312     for ( i = 0; ok_options[i]; i++ )
313     {
314         Add_Option_Name( cur_option_test, ok_options[i] );
315     }
316     Add_Option_Test( cur_option_test );
317
318 }
319
320 /******/
321
322 static int Do_Count_Test(
323     struct RB_Option_Test *cur_option_test )
324 {
325     unsigned int        parameter_nr = 0;
326     int                 result = EXIT_SUCCESS;
327     struct RB_Option_Name *option_name;
328
329     assert( cur_option_test );
330
331     for ( parameter_nr = 0;
332           parameter_nr < configuration.options.number; parameter_nr++ )
333     {
334         for ( option_name = cur_option_test->option_group;
335               option_name; option_name = option_name->next )
336         {
337             if ( RB_Str_Case_Cmp
338                  ( configuration.options.names[parameter_nr],
339                    option_name->name ) == 0 )
340             {
341                 ( option_name->count )++;
342             }
343         }
344     }
345
346     for ( option_name = cur_option_test->option_group;
347           option_name; option_name = option_name->next )
348     {
349         if ( option_name->count > 1 )
350         {
351             fprintf( stderr, "The option %s is used more than once.\n",
352                      option_name->name );
353             result = EXIT_FAILURE;
354         }
355     }
356
357     return result;
358 }
359
360
361 /****f* UserInterface/Do_Mutual_Exlcude_Test
362  * FUNCTION
363  *   Check all the options to see if combinations of options
364  *   are used that mutually exclude each other, such as
365  *   --singledoc and --multidoc.
366  * SYNOPSIS
367  */
368 static int Do_Mutual_Exlcude_Test(
369     struct RB_Option_Test *cur_option_test )
370  /*
371   * INPUTS
372   *   * cur_option_test -- the test to be carried out.
373   * SOURCE
374   */
375 {
376     int                 n = 0;
377     unsigned int        parameter_nr = 0;
378     int                 result = EXIT_SUCCESS;
379
380     assert( cur_option_test );
381
382     for ( parameter_nr = 0;
383           parameter_nr < configuration.options.number; parameter_nr++ )
384     {
385         struct RB_Option_Name *option_name = cur_option_test->option_group;
386
387         for ( ; option_name; option_name = option_name->next )
388         {
389             if ( RB_Str_Case_Cmp
390                  ( configuration.options.names[parameter_nr],
391                    option_name->name ) == 0 )
392             {
393                 ++n;
394             }
395         }
396     }
397
398     /* Only one of the options in the group may be used */
399     if ( n > 1 )
400     {
401         fprintf( stderr, "The options: " );
402         for ( parameter_nr = 0;
403               parameter_nr < configuration.options.number; parameter_nr++ )
404         {
405             struct RB_Option_Name *option_name =
406                 cur_option_test->option_group;
407             for ( ; option_name; option_name = option_name->next )
408             {
409                 if ( RB_Str_Case_Cmp
410                      ( configuration.options.names[parameter_nr],
411                        option_name->name ) == 0 )
412                 {
413
414                     fprintf( stderr, "%s ",
415                              configuration.options.names[parameter_nr] );
416                 }
417             }
418         }
419         fprintf( stderr, "cannot be used together.\n" );
420         result = EXIT_FAILURE;
421     }
422
423     return result;
424 }
425
426 /*****/
427
428
429 /****f* UserInterface/Do_Option_Tests
430  * FUNCTION
431  *   Run a series of tests on the options that the user
432  *   specified.  These tests are specified in
433  *   option_tests.
434  * SYNOPSIS
435  */
436 static int Do_Option_Tests(
437     void )
438 /*
439  * RESULT
440  *   * EXIT_FAILURE -- one of the tests failed.
441  *   * EXIT_SUCCESS -- all test completed successfully
442  * SOURCE
443  */
444 {
445     struct RB_Option_Test *cur_option_test = NULL;
446     int                 result = EXIT_SUCCESS;
447     int                 final_result = EXIT_SUCCESS;
448
449     RB_Say( "Checking the option semantics.\n", SAY_INFO );
450     Create_Test_Data(  );
451     cur_option_test = option_tests;
452
453     assert( cur_option_test );
454
455     for ( ; cur_option_test; cur_option_test = cur_option_test->next )
456     {
457         switch ( cur_option_test->kind )
458         {
459         case TEST_MUTUAL_EXCLUDE:
460             RB_Say( "Checking for mutual excluding options.\n", SAY_INFO );
461             result = Do_Mutual_Exlcude_Test( cur_option_test );
462             break;
463         case TEST_COUNT:       /* TODO Create */
464             RB_Say( "Checking for duplicate options.\n", SAY_INFO );
465             result = Do_Count_Test( cur_option_test );
466             break;
467         case TEST_SHOULD_EXISTS:       /* TODO Create */
468         case TEST_NO_EFFECT:   /* TODO Create */
469         case TEST_SPELLING:    /* TODO Create */
470         default:
471             assert( 0 );
472         }
473         /* If one of the tests fails the final result is a fail. */
474         if ( result == EXIT_FAILURE )
475         {
476             final_result = EXIT_FAILURE;
477             if ( cur_option_test->severity == OPTION_FATAL )
478             {
479                 break;
480             }
481         }
482     }
483
484     return final_result;
485 }
486
487 /****/
488
489 /****f* UserInterface/Check_Option_Spelling
490  * FUNCTION
491  *   Check for misspelled options specified by the user.
492  * SYNOPSIS
493  */
494
495 int Check_Option_Spelling(
496     void )
497 /*
498  * RESULT
499  *   * EXIT_SUCCESS -- all options are correctly spelled.
500  *   * EXIT_FAILURE -- one of more options are misspelled.
501  * SOURCE
502  */
503 {
504     char                ok;
505     char               *arg;
506     char              **opts;
507     unsigned int        parameter_nr;
508
509     RB_Say( "Checking the option syntax.\n", SAY_INFO );
510     for ( parameter_nr = 0;
511           parameter_nr < configuration.options.number; parameter_nr++ )
512     {
513         arg = configuration.options.names[parameter_nr];
514         if ( ( arg[0] == '-' ) && ( arg[1] == '-' ) )
515         {
516             /* this arg is an option */
517             ok = 0;
518             opts = ok_options;
519             while ( *opts )
520             {
521                 if ( strcmp( arg, *opts ) == 0 )
522                 {
523                     ok = 1;
524                     break;
525                 }
526                 opts++;
527             }
528             if ( !ok )
529             {
530                 fprintf( stderr, "Invalid argument: %s\n", arg );
531                 fprintf( stderr,
532                          "This might also be in your robodoc.rc file\n" );
533                 return EXIT_FAILURE;
534             }
535         }
536     }
537     return EXIT_SUCCESS;
538 }
539
540 /******/
541
542
543 static int Check_Item_Names(
544     struct Parameters *arg_parameters,
545     char *block_name )
546 {
547     unsigned int        parameter_nr_1;
548     unsigned int        parameter_nr_2;
549     int                 name_is_ok = TRUE;
550
551     RB_Say( "Checking the item names for %s block.\n", SAY_INFO, block_name );
552     for ( parameter_nr_1 = 0;
553           parameter_nr_1 < arg_parameters->number; parameter_nr_1++ )
554     {
555         name_is_ok = FALSE;
556         for ( parameter_nr_2 = 0;
557               parameter_nr_2 < configuration.items.number; parameter_nr_2++ )
558         {
559             if ( strcmp( configuration.items.names[parameter_nr_2],
560                          arg_parameters->names[parameter_nr_1] ) == 0 )
561             {
562                 name_is_ok = TRUE;
563             }
564         }
565         if ( !name_is_ok )
566         {
567             RB_Say( "!! block.\n", SAY_INFO );
568             fprintf( stderr, "Unknown item %s found in the\n",
569                      arg_parameters->names[parameter_nr_1] );
570             fprintf( stderr, "   %s block\nof your configuration file.\n",
571                      block_name );
572             break;
573         }
574     }
575
576     RB_Say( "Is %d block.\n", SAY_INFO, name_is_ok );
577     return name_is_ok;
578 }
579
580 /****f* UserInterface/Check_Item_Options
581  * FUNCTION
582  *   Check the validity of the item options.  Users can specify their
583  *   own items, item order, and items that ar to be ignored.  This all
584  *   should be consistent.
585  * SYNOPSIS
586  */
587 static int Check_Item_Options(
588     void )
589 /*
590  * RESULT
591  *   * EXIT_SUCCESS -- all options are correct.
592  *   * EXIT_FAILURE -- one of more options incorrect.
593  * SOURCE
594  */
595 {
596     int                 item_OK = TRUE;
597     int                 result = TRUE;
598
599     RB_Say( "Checking the item names.\n", SAY_INFO );
600     result = item_OK
601         && Check_Item_Names( &( configuration.ignore_items ),
602                              "ignore items:" );
603     item_OK = item_OK && result;
604     result =
605         Check_Item_Names( &( configuration.source_items ), "source items:" );
606     item_OK = item_OK && result;
607     result =
608         Check_Item_Names( &( configuration.preformatted_items ),
609                           "preformatted items:" );
610     item_OK = item_OK && result;
611     result =
612         Check_Item_Names( &( configuration.format_items ), "format items:" );
613     item_OK = item_OK && result;
614     result = Check_Item_Names( &( configuration.item_order ), "item order:" );
615     item_OK = item_OK && result;
616     if ( item_OK )
617     {
618         return EXIT_SUCCESS;
619     }
620     else
621     {
622         return EXIT_FAILURE;
623     }
624 }
625
626 /*****/
627
628
629 /****f* UserInterface/Check_Options
630  * FUNCTION
631  *   Check the validity of all the options in configuration.options[].
632  *   This runs a number of checks.
633  * SYNOPSIS
634  */
635
636 int Check_Options(
637     void )
638 /*
639  * RESULT
640  *   * EXIT_SUCCESS -- all options are correct.
641  *   * EXIT_FAILURE -- one of more options incorrect.
642  * SOURCE
643  */
644 {
645     int                 status;
646
647     RB_Say( "Checking the options.\n", SAY_INFO );
648     status = Check_Option_Spelling(  );
649     if ( status == EXIT_SUCCESS )
650     {
651         status = Do_Option_Tests(  );
652         if ( status == EXIT_SUCCESS )
653         {
654             status = Check_Item_Options(  );
655         }
656         else
657         {
658             Print_Short_Use(  );
659         }
660     }
661     else
662     {
663         Print_Short_Use(  );
664     }
665
666     /* TODO free option_tests */
667
668     return status;
669 }
670
671 /*****/