4 * Copyright (c) 1996-2003, Darren Hiebert
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 * This module contains functions to process command line options.
15 #include "general.h" /* must always come first */
20 #include <ctype.h> /* to declare isspace () */
33 #define INVOCATION "Usage: %s [options] [file(s)]\n"
35 #define CTAGS_ENVIRONMENT "CTAGS"
36 #define ETAGS_ENVIRONMENT "ETAGS"
38 #define CTAGS_FILE "tags"
39 #define ETAGS_FILE "TAGS"
42 # define ETAGS "etags" /* name which causes default use of to -e */
45 /* The following separators are permitted for list options.
47 #define EXTENSION_SEPARATOR '.'
48 #define PATTERN_START '('
49 #define PATTERN_STOP ')'
50 #define IGNORE_SEPARATORS ", \t\n"
52 #ifndef DEFAULT_FILE_FORMAT
53 # define DEFAULT_FILE_FORMAT 2
56 #if defined (HAVE_OPENDIR) || defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST) || defined (AMIGA)
57 # define RECURSE_SUPPORTED
60 #define isCompoundOption(c) (boolean) (strchr ("fohiILpDb", (c)) != NULL)
67 MaxHeaderExtensions
= 100, /* maximum number of extensions in -h option */
68 MaxSupportedTagFormat
= 2
71 typedef struct sOptionDescription
{
73 const char *description
;
76 typedef void (*parametricOptionHandler
) (const char *const option
, const char *const parameter
);
78 typedef const struct {
79 const char* name
; /* name of option as specified by user */
80 parametricOptionHandler handler
; /* routine to handle option */
81 boolean initOnly
; /* option must be specified before any files */
84 typedef const struct {
85 const char* name
; /* name of option as specified by user */
86 boolean
* pValue
; /* pointer to option value */
87 boolean initOnly
; /* option must be specified before any files */
94 static boolean NonOptionEncountered
;
95 static stringList
*OptionFiles
;
96 static stringList
* Excluded
;
97 static boolean FilesRequired
= TRUE
;
98 static boolean SkipConfiguration
;
100 static const char *const HeaderExtensions
[] = {
101 "h", "H", "hh", "hpp", "hxx", "h++", "inc", "def", NULL
104 optionValues Option
= {
106 FALSE
, /* --extra=f */
107 FALSE
, /* --extra=q */
108 TRUE
, /* --file-scope */
111 FALSE
, /* -fields=a */
112 TRUE
, /* -fields=f */
113 FALSE
, /* -fields=m */
114 FALSE
, /* -fields=i */
115 TRUE
, /* -fields=k */
116 FALSE
, /* -fields=z */
117 FALSE
, /* -fields=K */
118 FALSE
, /* -fields=l */
119 FALSE
, /* -fields=n */
120 TRUE
, /* -fields=s */
121 FALSE
, /* -fields=S */
128 #ifdef MACROS_USE_PATTERNS
129 EX_PATTERN
, /* -n, --excmd */
131 EX_MIX
, /* -n, --excmd */
134 SO_SORTED
, /* -u, --sort */
140 NULL
, /* --etags-include */
141 DEFAULT_FILE_FORMAT
,/* --format */
143 FALSE
, /* --kind-long */
144 LANG_AUTO
, /* --lang */
146 FALSE
, /* --filter */
147 NULL
, /* --filter-terminator */
148 FALSE
, /* --tag-relative */
149 FALSE
, /* --totals */
150 FALSE
, /* --line-directives */
160 static optionDescription LongOptionDescription
[] = {
161 {1," -a Append the tags to an existing tag file."},
164 {1," Set break line."},
166 {0," -B Use backward searching patterns (?...?)."},
169 {1," Set debug level."},
171 {0," -e Output tag file for use with Emacs."},
173 {1," Write tags to specified file. Value of \"-\" writes tags to stdout"},
174 {1," [\"tags\"; or \"TAGS\" when -e supplied]."},
175 {0," -F Use forward searching patterns (/.../) (default)."},
177 {1," Specify list of file extensions to be treated as include files."},
178 {1," [\".h.H.hh.hpp.hxx.h++\"]."},
179 {1," -I <list|@file>"},
180 {1," A list of tokens to be specially handled is read from either the"},
181 {1," command line or the specified file."},
183 {1," A list of source file names are read from the specified file."},
184 {1," If specified as \"-\", then standard input is read."},
185 {0," -n Equivalent to --excmd=number."},
186 {0," -N Equivalent to --excmd=pattern."},
187 {1," -o Alternative for -f."},
188 #ifdef RECURSE_SUPPORTED
189 {1," -R Equivalent to --recurse."},
191 {1," -R Not supported on this platform."},
193 {0," -u Equivalent to --sort=no."},
194 {1," -V Equivalent to --verbose."},
195 {1," -x Print a tabular cross reference file to standard output."},
196 {1," --append=[yes|no]"},
197 {1," Should tags should be appended to existing tag file [no]?"},
198 {1," --etags-include=file"},
199 {1," Include reference to 'file' in Emacs-style tag file (requires -e)."},
200 {1," --exclude=pattern"},
201 {1," Exclude files and directories matching 'pattern'."},
202 {0," --excmd=number|pattern|mix"},
203 #ifdef MACROS_USE_PATTERNS
204 {0," Uses the specified type of EX command to locate tags [pattern]."},
206 {0," Uses the specified type of EX command to locate tags [mix]."},
208 {1," --extra=[+|-]flags"},
209 {1," Include extra tag entries for selected information (flags: \"fq\")."},
210 {1," --fields=[+|-]flags"},
211 {1," Include selected extension fields (flags: \"afmikKlnsStz\") [fks]."},
212 {1," --file-scope=[yes|no]"},
213 {1," Should tags scoped only for a single file (e.g. \"static\" tags"},
214 {1," be included in the output [yes]?"},
215 {1," --filter=[yes|no]"},
216 {1," Behave as a filter, reading file names from standard input and"},
217 {1," writing tags to standard output [no]."},
218 {1," --filter-terminator=string"},
219 {1," Specify string to print to stdout following the tags for each file"},
220 {1," parsed when --filter is enabled."},
221 {0," --format=level"},
222 #if DEFAULT_FILE_FORMAT == 1
223 {0," Force output of specified tag file format [1]."},
225 {0," Force output of specified tag file format [2]."},
228 {1," Print this option summary."},
229 {1," --if0=[yes|no]"},
230 {1," Should C code within #if 0 conditional branches be parsed [no]?"},
231 {1," --<LANG>-kinds=[+|-]kinds"},
232 {1," Enable/disable tag kinds for language <LANG>."},
233 {1," --langdef=name"},
234 {1," Define a new language to be parsed with regular expressions."},
235 {1," --langmap=map(s)"},
236 {1," Override default mapping of language to source file extension."},
237 {1," --language-force=language"},
238 {1," Force all files to be interpreted using specified language."},
239 {1," --languages=[+|-]list"},
240 {1," Restrict files scanned for tags to those mapped to langauges"},
241 {1," specified in the comma-separated 'list'. The list can contain any"},
242 {1," built-in or user-defined language [all]."},
244 {1," Print details of software license."},
245 {0," --line-directives=[yes|no]"},
246 {0," Should #line directives be processed [no]?"},
247 {1," --links=[yes|no]"},
248 {1," Indicate whether symbolic links should be followed [yes]."},
249 {1," --list-kinds=[language|all]"},
250 {1," Output a list of all tag kinds for specified language or all."},
251 {1," --list-languages"},
252 {1," Output list of supported languages."},
253 {1," --list-maps=[language|all]"},
254 {1," Output list of language mappings."},
255 {1," --options=file"},
256 {1," Specify file from which command line options should be read."},
257 {1," --recurse=[yes|no]"},
258 #ifdef RECURSE_SUPPORTED
259 {1," Recurse into directories supplied on command line [no]."},
261 {1," Not supported on this platform."},
264 {1," --regex-<LANG>=/line_pattern/name_pattern/[flags]"},
265 {1," Define regular expression for locating tags in specific language."},
267 {0," --sort=[yes|no|foldcase]"},
268 {0," Should tags be sorted (optionally ignoring case) [yes]?."},
269 {0," --tag-relative=[yes|no]"},
270 {0," Should paths be relative to location of tag file [no; yes when -e]?"},
271 {1," --totals=[yes|no]"},
272 {1," Print statistics about source and tag files [no]."},
273 {1," --verbose=[yes|no]"},
274 {1," Enable verbose messages describing actions on each source file."},
276 {1," Print version identifier to standard output."},
280 static const char* const License1
=
281 "This program is free software; you can redistribute it and/or\n"
282 "modify it under the terms of the GNU General Public License\n"
283 "as published by the Free Software Foundation; either version 2\n"
284 "of the License, or (at your option) any later version.\n"
286 static const char* const License2
=
287 "This program is distributed in the hope that it will be useful,\n"
288 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
289 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
290 "GNU General Public License for more details.\n"
292 "You should have received a copy of the GNU General Public License\n"
293 "along with this program; if not, write to the Free Software\n"
294 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n";
296 /* Contains a set of strings describing the set of "features" compiled into
299 static const char *const Features
[] = {
325 #ifndef EXTERNAL_SORT
328 #ifdef CUSTOM_CONFIGURATION_FILE
331 #if (defined (MSDOS) || defined (WIN32) || defined (OS2)) && defined (UNIX_PATH_SEPARATOR)
332 "unix-path-separator",
341 * FUNCTION PROTOTYPES
343 static boolean
parseFileOptions (const char *const fileName
);
346 * FUNCTION DEFINITIONS
349 extern void verbose (const char *const format
, ...)
351 if (1/*Option.verbose*/)
354 va_start (ap
, format
);
355 vprintf (format
, ap
);
360 static char *stringCopy (const char *const string
)
364 result
= eStrdup (string
);
368 static void freeString (char **const pString
)
370 if (*pString
!= NULL
)
377 extern void freeList (stringList
** const pList
)
381 stringListDelete (*pList
);
386 extern void setDefaultTagFileName (void)
388 if (Option
.tagFileName
!= NULL
)
389 ; /* accept given name */
390 else if (Option
.etags
)
391 Option
.tagFileName
= stringCopy (ETAGS_FILE
);
393 Option
.tagFileName
= stringCopy (CTAGS_FILE
);
396 extern boolean
filesRequired (void)
398 boolean result
= FilesRequired
;
404 extern void checkOptions (void)
409 notice
= "xref output";
410 if (Option
.include
.fileNames
)
412 error (WARNING
, "%s disables file name tags", notice
);
413 Option
.include
.fileNames
= FALSE
;
418 notice
= "append mode is not compatible with";
419 if (isDestinationStdout ())
420 error (FATAL
, "%s tags to stdout", notice
);
424 notice
= "filter mode";
425 if (Option
.printTotals
)
427 error (WARNING
, "%s disables totals", notice
);
428 Option
.printTotals
= FALSE
;
430 if (Option
.tagFileName
!= NULL
)
431 error (WARNING
, "%s ignores output tag file name", notice
);
435 static void setEtagsMode (void)
438 Option
.sorted
= SO_UNSORTED
;
439 Option
.lineDirectives
= FALSE
;
440 Option
.tagRelative
= TRUE
;
443 extern void testEtagsInvocation (void)
445 char* const execName
= eStrdup (getExecutableName ());
446 char* const etags
= eStrdup (ETAGS
);
447 #ifdef CASE_INSENSITIVE_FILENAMES
448 toLowerString (execName
);
449 toLowerString (etags
);
451 if (strstr (execName
, etags
) != NULL
)
453 verbose ("Running in etags mode\n");
459 * Cooked argument parsing
462 static void parseShortOption (cookedArgs
*const args
)
464 args
->simple
[0] = *args
->shortOptions
++;
465 args
->simple
[1] = '\0';
466 args
->item
= args
->simple
;
467 if (! isCompoundOption (*args
->simple
))
468 args
->parameter
= "";
469 else if (*args
->shortOptions
== '\0')
471 argForth (args
->args
);
472 if (argOff (args
->args
))
473 args
->parameter
= NULL
;
475 args
->parameter
= argItem (args
->args
);
476 args
->shortOptions
= NULL
;
480 args
->parameter
= args
->shortOptions
;
481 args
->shortOptions
= NULL
;
485 static void parseLongOption (cookedArgs
*const args
, const char *item
)
487 const char* const equal
= strchr (item
, '=');
490 args
->item
= eStrdup (item
);
491 args
->parameter
= "";
495 const size_t length
= equal
- item
;
496 args
->item
= xMalloc (length
+ 1, char);
497 strncpy (args
->item
, item
, length
);
498 args
->item
[length
] = '\0';
499 args
->parameter
= equal
+ 1;
501 Assert (args
->item
!= NULL
);
502 Assert (args
->parameter
!= NULL
);
505 static void cArgRead (cookedArgs
*const current
)
509 Assert (current
!= NULL
);
510 if (! argOff (current
->args
))
512 item
= argItem (current
->args
);
513 current
->shortOptions
= NULL
;
514 Assert (item
!= NULL
);
515 if (strncmp (item
, "--", (size_t) 2) == 0)
517 current
->isOption
= TRUE
;
518 current
->longOption
= TRUE
;
519 parseLongOption (current
, item
+ 2);
520 Assert (current
->item
!= NULL
);
521 Assert (current
->parameter
!= NULL
);
523 else if (*item
== '-')
525 current
->isOption
= TRUE
;
526 current
->longOption
= FALSE
;
527 current
->shortOptions
= item
+ 1;
528 parseShortOption (current
);
532 current
->isOption
= FALSE
;
533 current
->longOption
= FALSE
;
534 current
->item
= item
;
535 current
->parameter
= NULL
;
540 extern cookedArgs
* cArgNewFromString (const char* string
)
542 cookedArgs
* const result
= xMalloc (1, cookedArgs
);
543 memset (result
, 0, sizeof (cookedArgs
));
544 result
->args
= argNewFromString (string
);
549 extern cookedArgs
* cArgNewFromArgv (char* const* const argv
)
551 cookedArgs
* const result
= xMalloc (1, cookedArgs
);
552 memset (result
, 0, sizeof (cookedArgs
));
553 result
->args
= argNewFromArgv (argv
);
558 extern cookedArgs
* cArgNewFromFile (FILE* const fp
)
560 cookedArgs
* const result
= xMalloc (1, cookedArgs
);
561 memset (result
, 0, sizeof (cookedArgs
));
562 result
->args
= argNewFromFile (fp
);
567 extern cookedArgs
* cArgNewFromLineFile (FILE* const fp
)
569 cookedArgs
* const result
= xMalloc (1, cookedArgs
);
570 memset (result
, 0, sizeof (cookedArgs
));
571 result
->args
= argNewFromLineFile (fp
);
576 extern void cArgDelete (cookedArgs
* const current
)
578 Assert (current
!= NULL
);
579 argDelete (current
->args
);
580 memset (current
, 0, sizeof (cookedArgs
));
584 static boolean
cArgOptionPending (cookedArgs
* const current
)
586 boolean result
= FALSE
;
587 if (current
->shortOptions
!= NULL
)
588 if (*current
->shortOptions
!= '\0')
593 extern boolean
cArgOff (cookedArgs
* const current
)
595 Assert (current
!= NULL
);
596 return (boolean
) (argOff (current
->args
) && ! cArgOptionPending (current
));
599 extern boolean
cArgIsOption (cookedArgs
* const current
)
601 Assert (current
!= NULL
);
602 return current
->isOption
;
605 extern const char* cArgItem (cookedArgs
* const current
)
607 Assert (current
!= NULL
);
608 return current
->item
;
611 extern void cArgForth (cookedArgs
* const current
)
613 Assert (current
!= NULL
);
614 Assert (! cArgOff (current
));
615 if (cArgOptionPending (current
))
616 parseShortOption (current
);
619 Assert (! argOff (current
->args
));
620 argForth (current
->args
);
621 if (! argOff (current
->args
))
625 current
->isOption
= FALSE
;
626 current
->longOption
= FALSE
;
627 current
->shortOptions
= NULL
;
628 current
->item
= NULL
;
629 current
->parameter
= NULL
;
635 * File extension and language mapping
638 static void addExtensionList (
639 stringList
*const slist
, const char *const elist
, const boolean clear
)
641 char *const extensionList
= eStrdup (elist
);
642 const char *extension
= NULL
;
643 boolean first
= TRUE
;
647 verbose (" clearing\n");
648 stringListClear (slist
);
650 verbose (" adding: ");
651 if (elist
!= NULL
&& *elist
!= '\0')
653 extension
= extensionList
;
654 if (elist
[0] == EXTENSION_SEPARATOR
)
657 while (extension
!= NULL
)
659 char *separator
= strchr (extension
, EXTENSION_SEPARATOR
);
660 if (separator
!= NULL
)
662 verbose ("%s%s", first
? "" : ", ",
663 *extension
== '\0' ? "(NONE)" : extension
);
664 stringListAdd (slist
, vStringNewInit (extension
));
666 if (separator
== NULL
)
669 extension
= separator
+ 1;
674 stringListPrint (slist
);
677 eFree (extensionList
);
680 static boolean
isFalse (const char *parameter
)
683 strcasecmp (parameter
, "0" ) == 0 ||
684 strcasecmp (parameter
, "n" ) == 0 ||
685 strcasecmp (parameter
, "no" ) == 0 ||
686 strcasecmp (parameter
, "off") == 0);
689 static boolean
isTrue (const char *parameter
)
692 strcasecmp (parameter
, "1" ) == 0 ||
693 strcasecmp (parameter
, "y" ) == 0 ||
694 strcasecmp (parameter
, "yes") == 0 ||
695 strcasecmp (parameter
, "on" ) == 0);
698 /* Determines whether the specified file name is considered to be a header
699 * file for the purposes of determining whether enclosed tags are global or
702 extern boolean
isIncludeFile (const char *const fileName
)
704 boolean result
= FALSE
;
705 const char *const extension
= fileExtension (fileName
);
706 if (Option
.headerExt
!= NULL
)
707 result
= stringListExtensionMatched (Option
.headerExt
, extension
);
712 * Specific option processing
715 static void processEtagsInclude (
716 const char *const option
, const char *const parameter
)
719 error (FATAL
, "Etags must be enabled to use \"%s\" option", option
);
722 vString
*const file
= vStringNewInit (parameter
);
723 if (Option
.etagsInclude
== NULL
)
724 Option
.etagsInclude
= stringListNew ();
725 stringListAdd (Option
.etagsInclude
, file
);
726 FilesRequired
= FALSE
;
730 static void processExcludeOption (
731 const char *const option __unused__
, const char *const parameter
)
733 const char *const fileName
= parameter
+ 1;
734 if (parameter
[0] == '\0')
735 freeList (&Excluded
);
736 else if (parameter
[0] == '@')
738 stringList
* const sl
= stringListNewFromFile (fileName
);
740 error (FATAL
| PERROR
, "cannot open \"%s\"", fileName
);
741 if (Excluded
== NULL
)
744 stringListCombine (Excluded
, sl
);
745 verbose (" adding exclude patterns from %s\n", fileName
);
749 vString
*const item
= vStringNewInit (parameter
);
750 if (Excluded
== NULL
)
751 Excluded
= stringListNew ();
752 stringListAdd (Excluded
, item
);
753 verbose (" adding exclude pattern: %s\n", parameter
);
757 extern boolean
isExcludedFile (const char* const name
)
759 const char* base
= baseFilename (name
);
760 boolean result
= FALSE
;
761 if (Excluded
!= NULL
)
763 result
= stringListFileMatched (Excluded
, base
);
764 if (! result
&& name
!= base
)
765 result
= stringListFileMatched (Excluded
, name
);
768 /* not a good solution, but the only one which works often */
770 result
= (boolean
) (strcmp (name
, TagFile
.name
) == 0);
775 static void processExcmdOption (
776 const char *const option
, const char *const parameter
)
780 case 'm': Option
.locate
= EX_MIX
; break;
781 case 'n': Option
.locate
= EX_LINENUM
; break;
782 case 'p': Option
.locate
= EX_PATTERN
; break;
784 error (FATAL
, "Invalid value for \"%s\" option", option
);
789 static void processExtraTagsOption (
790 const char *const option
, const char *const parameter
)
792 struct sInclude
*const inc
= &Option
.include
;
793 const char *p
= parameter
;
797 if (*p
!= '+' && *p
!= '-')
799 inc
->fileNames
= FALSE
;
800 inc
->qualifiedTags
= FALSE
;
802 inc
->fileScope
= FALSE
;
805 while ((c
= *p
++) != '\0') switch (c
)
807 case '+': mode
= TRUE
; break;
808 case '-': mode
= FALSE
; break;
810 case 'f': inc
->fileNames
= mode
; break;
811 case 'q': inc
->qualifiedTags
= mode
; break;
813 case 'F': inc
->fileScope
= mode
; break;
816 default: error(WARNING
, "Unsupported parameter '%c' for \"%s\" option",
822 static void processFieldsOption (
823 const char *const option
, const char *const parameter
)
825 struct sExtFields
*field
= &Option
.extensionFields
;
826 const char *p
= parameter
;
830 if (*p
!= '+' && *p
!= '-')
832 field
->access
= FALSE
;
833 field
->fileScope
= FALSE
;
834 field
->implementation
= FALSE
;
835 field
->inheritance
= FALSE
;
837 field
->kindKey
= FALSE
;
838 field
->kindLong
= FALSE
;
839 field
->language
= FALSE
;
840 field
->scope
= FALSE
;
841 field
->typeRef
= FALSE
;
843 while ((c
= *p
++) != '\0') switch (c
)
845 case '+': mode
= TRUE
; break;
846 case '-': mode
= FALSE
; break;
848 case 'a': field
->access
= mode
; break;
849 case 'f': field
->fileScope
= mode
; break;
850 case 'm': field
->implementation
= mode
; break;
851 case 'i': field
->inheritance
= mode
; break;
852 case 'k': field
->kind
= mode
; break;
853 case 'K': field
->kindLong
= mode
; break;
854 case 'l': field
->language
= mode
; break;
855 case 'n': field
->lineNumber
= mode
; break;
856 case 's': field
->scope
= mode
; break;
857 case 'S': field
->signature
= mode
; break;
858 case 'z': field
->kindKey
= mode
; break;
859 case 't': field
->typeRef
= mode
; break;
861 default: error(WARNING
, "Unsupported parameter '%c' for \"%s\" option",
867 static void processFilterTerminatorOption (
868 const char *const option __unused__
, const char *const parameter
)
870 freeString (&Option
.filterTerminator
);
871 Option
.filterTerminator
= stringCopy (parameter
);
874 static void processFormatOption (
875 const char *const option
, const char *const parameter
)
879 if (sscanf (parameter
, "%u", &format
) < 1)
880 error (FATAL
, "Invalid value for \"%s\" option",option
);
881 else if (format
<= (unsigned int) MaxSupportedTagFormat
)
882 Option
.tagFileFormat
= format
;
884 error (FATAL
, "Unsupported value for \"%s\" option", option
);
887 static void printInvocationDescription (void)
889 printf (INVOCATION
, getExecutableName ());
892 static void printOptionDescriptions (const optionDescription
*const optDesc
)
895 for (i
= 0 ; optDesc
[i
].description
!= NULL
; ++i
)
897 if (! Option
.etags
|| optDesc
[i
].usedByEtags
)
898 puts (optDesc
[i
].description
);
902 static void printFeatureList (void)
906 for (i
= 0 ; Features
[i
] != NULL
; ++i
)
909 printf (" Optional compiled features: ");
910 printf ("%s+%s", (i
>0 ? ", " : ""), Features
[i
]);
911 #ifdef CUSTOM_CONFIGURATION_FILE
912 if (strcmp (Features
[i
], "custom-conf") == 0)
913 printf ("=%s", CUSTOM_CONFIGURATION_FILE
);
920 static void printProgramIdentification (void)
922 printf ("%s %s, Copyright (C) 1996-2004 %s\n",
923 PROGRAM_NAME
, PROGRAM_VERSION
, AUTHOR_NAME
);
924 printf (" Compiled: %s, %s\n", __DATE__
, __TIME__
);
925 printf (" Addresses: <%s>, %s\n", AUTHOR_EMAIL
, PROGRAM_URL
);
929 static void processHelpOption (
930 const char *const option __unused__
,
931 const char *const parameter __unused__
)
933 printProgramIdentification ();
935 printInvocationDescription ();
937 printOptionDescriptions (LongOptionDescription
);
941 static void processLanguageForceOption (
942 const char *const option
, const char *const parameter
)
945 if (strcasecmp (parameter
, "auto") == 0)
946 language
= LANG_AUTO
;
948 language
= getNamedLanguage (parameter
);
950 if (strcmp (option
, "lang") == 0 || strcmp (option
, "language") == 0)
952 "\"--%s\" option is obsolete; use \"--language-force\" instead",
954 if (language
== LANG_IGNORE
)
955 error (FATAL
, "Unknown language specified in \"%s\" option", option
);
957 Option
.language
= language
;
959 static char* skipPastMap (char* p
)
961 while (*p
!= EXTENSION_SEPARATOR
&&
962 *p
!= PATTERN_START
&& *p
!= ',' && *p
!= '\0')
967 /* Parses the mapping beginning at `map', adds it to the language map, and
968 * returns first character past the map.
970 static char* addLanguageMap (const langType language
, char* map
)
973 const char first
= *map
;
974 if (first
== EXTENSION_SEPARATOR
) /* extension map */
977 p
= skipPastMap (map
);
980 verbose (" .%s", map
);
981 addLanguageExtensionMap (language
, map
);
982 p
= map
+ strlen (map
);
986 const char separator
= *p
;
988 verbose (" .%s", map
);
989 addLanguageExtensionMap (language
, map
);
993 else if (first
== PATTERN_START
) /* pattern map */
996 for (p
= map
; *p
!= PATTERN_STOP
&& *p
!= '\0' ; ++p
)
998 if (*p
== '\\' && *(p
+ 1) == PATTERN_STOP
)
1002 error (FATAL
, "Unterminated file name pattern for %s language",
1003 getLanguageName (language
));
1007 verbose (" (%s)", map
);
1008 addLanguagePatternMap (language
, map
);
1012 error (FATAL
, "Badly formed language map for %s language",
1013 getLanguageName (language
));
1017 static char* processLanguageMap (char* map
)
1019 char* const separator
= strchr (map
, ':');
1020 char* result
= NULL
;
1021 if (separator
!= NULL
)
1024 char *list
= separator
+ 1;
1025 boolean clear
= FALSE
;
1027 language
= getNamedLanguage (map
);
1028 if (language
!= LANG_IGNORE
)
1030 const char *const deflt
= "default";
1036 for (p
= list
; *p
!= ',' && *p
!= '\0' ; ++p
) /*no-op*/ ;
1037 if ((size_t) (p
- list
) == strlen (deflt
) &&
1038 strncasecmp (list
, deflt
, p
- list
) == 0)
1040 verbose (" Restoring default %s language map: ", getLanguageName (language
));
1041 installLanguageMapDefault (language
);
1048 verbose (" Setting %s language map:", getLanguageName (language
));
1049 clearLanguageMap (language
);
1052 verbose (" Adding to %s language map:", getLanguageName (language
));
1053 while (list
!= NULL
&& *list
!= '\0' && *list
!= ',')
1054 list
= addLanguageMap (language
, list
);
1057 if (list
!= NULL
&& *list
== ',')
1065 static void processLanguageMapOption (
1066 const char *const option
, const char *const parameter
)
1068 char *const maps
= eStrdup (parameter
);
1071 if (strcmp (parameter
, "default") == 0)
1073 verbose (" Restoring default language maps:\n");
1074 installLanguageMapDefaults ();
1076 else while (map
!= NULL
&& *map
!= '\0')
1078 char* const next
= processLanguageMap (map
);
1080 error (WARNING
, "Unknown language specified in \"%s\" option", option
);
1086 static void processLanguagesOption (
1087 const char *const option
, const char *const parameter
)
1089 char *const langs
= eStrdup (parameter
);
1090 enum { Add
, Remove
, Replace
} mode
= Replace
;
1091 boolean first
= TRUE
;
1093 const char* prefix
= "";
1094 verbose (" Enabled languages: ");
1095 while (lang
!= NULL
)
1097 char *const end
= strchr (lang
, ',');
1098 if (lang
[0] == '+')
1104 else if (lang
[0] == '-')
1110 if (mode
== Replace
)
1111 enableLanguages (FALSE
);
1114 if (lang
[0] != '\0')
1116 if (strcmp (lang
, "all") == 0)
1117 enableLanguages ((boolean
) (mode
!= Remove
));
1120 const langType language
= getNamedLanguage (lang
);
1121 if (language
== LANG_IGNORE
)
1122 error (WARNING
, "Unknown language specified in \"%s\" option", option
);
1124 enableLanguage (language
, (boolean
) (mode
!= Remove
));
1126 verbose ("%s%s%s", (first
? "" : ", "), prefix
, lang
);
1129 if (mode
== Replace
)
1132 lang
= (end
!= NULL
? end
+ 1 : NULL
);
1138 static void processLicenseOption (
1139 const char *const option __unused__
,
1140 const char *const parameter __unused__
)
1142 printProgramIdentification ();
1149 static void processListKindsOption (
1150 const char *const option
, const char *const parameter
)
1152 if (parameter
[0] == '\0' || strcasecmp (parameter
, "all") == 0)
1153 printLanguageKinds (LANG_AUTO
);
1156 langType language
= getNamedLanguage (parameter
);
1157 if (language
== LANG_IGNORE
)
1158 error (FATAL
, "Unknown language specified in \"%s\" option",option
);
1160 printLanguageKinds (language
);
1165 static void processListMapsOption (
1166 const char *const __unused__ option
,
1167 const char *const __unused__ parameter
)
1169 if (parameter
[0] == '\0' || strcasecmp (parameter
, "all") == 0)
1170 printLanguageMaps (LANG_AUTO
);
1173 langType language
= getNamedLanguage (parameter
);
1174 if (language
== LANG_IGNORE
)
1175 error (FATAL
, "Unknown language specified in \"%s\" option",option
);
1177 printLanguageMaps (language
);
1182 static void processListLanguagesOption (
1183 const char *const option __unused__
,
1184 const char *const parameter __unused__
)
1186 printLanguageList ();
1190 static void processOptionFile (
1191 const char *const option
, const char *const parameter
)
1193 if (parameter
[0] == '\0')
1194 error (WARNING
, "no option file supplied for \"%s\"", option
);
1195 else if (! parseFileOptions (parameter
))
1196 error (FATAL
| PERROR
, "cannot open option file \"%s\"", parameter
);
1199 static void processSortOption (
1200 const char *const option
, const char *const parameter
)
1202 if (isFalse (parameter
))
1203 Option
.sorted
= SO_UNSORTED
;
1204 else if (isTrue (parameter
))
1205 Option
.sorted
= SO_SORTED
;
1206 else if (strcasecmp (parameter
, "f") == 0 ||
1207 strcasecmp (parameter
, "fold") == 0 ||
1208 strcasecmp (parameter
, "foldcase") == 0)
1209 Option
.sorted
= SO_FOLDSORTED
;
1211 error (FATAL
, "Invalid value for \"%s\" option", option
);
1214 static void installHeaderListDefaults (void)
1216 Option
.headerExt
= stringListNewFromArgv (HeaderExtensions
);
1219 printf (" Setting default header extensions: ");
1220 stringListPrint (Option
.headerExt
);
1225 static void processHeaderListOption (const int option
, const char *parameter
)
1227 /* Check to make sure that the user did not enter "ctags -h *.c"
1228 * by testing to see if the list is a filename that exists.
1230 if (doesFileExist (parameter
))
1231 error (FATAL
, "-%c: Invalid list", option
);
1232 if (strcmp (parameter
, "default") == 0)
1233 installHeaderListDefaults ();
1236 boolean clear
= TRUE
;
1238 if (parameter
[0] == '+')
1243 if (Option
.headerExt
== NULL
)
1244 Option
.headerExt
= stringListNew ();
1245 verbose (" Header Extensions:\n");
1246 addExtensionList (Option
.headerExt
, parameter
, clear
);
1251 * Token ignore processing
1254 /* Determines whether or not "name" should be ignored, per the ignore list.
1256 extern boolean
isIgnoreToken (
1257 const char *const name
, boolean
*const pIgnoreParens
,
1258 const char **const replacement
)
1260 boolean result
= FALSE
;
1262 if (Option
.ignore
!= NULL
)
1264 const size_t nameLen
= strlen (name
);
1267 if (pIgnoreParens
!= NULL
)
1268 *pIgnoreParens
= FALSE
;
1270 for (i
= 0 ; i
< stringListCount (Option
.ignore
) ; ++i
)
1272 vString
*token
= stringListItem (Option
.ignore
, i
);
1274 if (strncmp (vStringValue (token
), name
, nameLen
) == 0)
1276 const size_t tokenLen
= vStringLength (token
);
1278 if (nameLen
== tokenLen
)
1283 else if (tokenLen
== nameLen
+ 1 &&
1284 vStringChar (token
, tokenLen
- 1) == '+')
1287 if (pIgnoreParens
!= NULL
)
1288 *pIgnoreParens
= TRUE
;
1291 else if (vStringChar (token
, nameLen
) == '=')
1293 if (replacement
!= NULL
)
1294 *replacement
= vStringValue (token
) + nameLen
+ 1;
1303 static void saveIgnoreToken (vString
*const ignoreToken
)
1305 if (Option
.ignore
== NULL
)
1306 Option
.ignore
= stringListNew ();
1307 stringListAdd (Option
.ignore
, ignoreToken
);
1308 verbose (" ignore token: %s\n", vStringValue (ignoreToken
));
1311 static void readIgnoreList (const char *const list
)
1313 char* newList
= stringCopy (list
);
1314 const char *token
= strtok (newList
, IGNORE_SEPARATORS
);
1316 while (token
!= NULL
)
1318 vString
*const entry
= vStringNewInit (token
);
1320 saveIgnoreToken (entry
);
1321 token
= strtok (NULL
, IGNORE_SEPARATORS
);
1326 void addIgnoreListFromFile (const char *const fileName
)
1328 stringList
* tokens
= stringListNewFromFile (fileName
);
1330 error (FATAL
| PERROR
, "cannot open \"%s\"", fileName
);
1331 if (Option
.ignore
== NULL
)
1332 Option
.ignore
= tokens
;
1334 stringListCombine (Option
.ignore
, tokens
);
1337 static void processIgnoreOption (const char *const list
)
1339 if (strchr ("@./\\", list
[0]) != NULL
)
1341 const char* fileName
= (*list
== '@') ? list
+ 1 : list
;
1342 addIgnoreListFromFile (fileName
);
1344 #if defined (MSDOS) || defined (WIN32) || defined (OS2)
1345 else if (isalpha (list
[0]) && list
[1] == ':')
1346 addIgnoreListFromFile (list
);
1348 else if (strcmp (list
, "-") == 0)
1350 freeList (&Option
.ignore
);
1351 verbose (" clearing list\n");
1354 readIgnoreList (list
);
1357 static void processVersionOption (
1358 const char *const option __unused__
,
1359 const char *const parameter __unused__
)
1361 printProgramIdentification ();
1369 static parametricOption ParametricOptions
[] = {
1370 { "etags-include", processEtagsInclude
, FALSE
},
1371 { "exclude", processExcludeOption
, FALSE
},
1372 { "excmd", processExcmdOption
, FALSE
},
1373 { "extra", processExtraTagsOption
, FALSE
},
1374 { "fields", processFieldsOption
, FALSE
},
1375 { "filter-terminator", processFilterTerminatorOption
, TRUE
},
1376 { "format", processFormatOption
, TRUE
},
1377 { "help", processHelpOption
, TRUE
},
1378 { "lang", processLanguageForceOption
, FALSE
},
1379 { "language", processLanguageForceOption
, FALSE
},
1380 { "language-force", processLanguageForceOption
, FALSE
},
1381 { "languages", processLanguagesOption
, FALSE
},
1382 { "langdef", processLanguageDefineOption
, FALSE
},
1383 { "langmap", processLanguageMapOption
, FALSE
},
1384 { "license", processLicenseOption
, TRUE
},
1385 { "list-kinds", processListKindsOption
, TRUE
},
1386 { "list-maps", processListMapsOption
, TRUE
},
1387 { "list-languages", processListLanguagesOption
, TRUE
},
1388 { "options", processOptionFile
, FALSE
},
1389 { "sort", processSortOption
, TRUE
},
1390 { "version", processVersionOption
, TRUE
},
1393 static booleanOption BooleanOptions
[] = {
1394 { "append", &Option
.append
, TRUE
},
1395 { "file-scope", &Option
.include
.fileScope
, FALSE
},
1396 { "file-tags", &Option
.include
.fileNames
, FALSE
},
1397 { "filter", &Option
.filter
, TRUE
},
1398 { "if0", &Option
.if0
, FALSE
},
1399 { "kind-long", &Option
.kindLong
, TRUE
},
1400 { "line-directives",&Option
.lineDirectives
, FALSE
},
1401 { "links", &Option
.followLinks
, FALSE
},
1402 #ifdef RECURSE_SUPPORTED
1403 { "recurse", &Option
.recurse
, FALSE
},
1405 { "tag-relative", &Option
.tagRelative
, TRUE
},
1406 { "totals", &Option
.printTotals
, TRUE
},
1407 { "verbose", &Option
.verbose
, FALSE
},
1411 * Generic option parsing
1414 static void checkOptionOrder (const char* const option
)
1416 if (NonOptionEncountered
)
1417 error (FATAL
, "-%s option may not follow a file name", option
);
1420 static boolean
processParametricOption (
1421 const char *const option
, const char *const parameter
)
1423 const int count
= sizeof (ParametricOptions
) / sizeof (parametricOption
);
1424 boolean found
= FALSE
;
1427 for (i
= 0 ; i
< count
&& ! found
; ++i
)
1429 parametricOption
* const entry
= &ParametricOptions
[i
];
1430 if (strcmp (option
, entry
->name
) == 0)
1433 if (entry
->initOnly
)
1434 checkOptionOrder (option
);
1435 (entry
->handler
) (option
, parameter
);
1441 static boolean
getBooleanOption (
1442 const char *const option
, const char *const parameter
)
1444 boolean selection
= TRUE
;
1446 if (parameter
[0] == '\0')
1448 else if (isFalse (parameter
))
1450 else if (isTrue (parameter
))
1453 error (FATAL
, "Invalid value for \"%s\" option", option
);
1458 static boolean
processBooleanOption (
1459 const char *const option
, const char *const parameter
)
1461 const int count
= sizeof (BooleanOptions
) / sizeof (booleanOption
);
1462 boolean found
= FALSE
;
1465 for (i
= 0 ; i
< count
&& ! found
; ++i
)
1467 booleanOption
* const entry
= &BooleanOptions
[i
];
1468 if (strcmp (option
, entry
->name
) == 0)
1471 if (entry
->initOnly
)
1472 checkOptionOrder (option
);
1473 *entry
->pValue
= getBooleanOption (option
, parameter
);
1479 static void processLongOption (
1480 const char *const option
, const char *const parameter
)
1482 Assert (parameter
!= NULL
);
1483 if (parameter
== NULL
&& parameter
[0] == '\0')
1484 verbose (" Option: --%s\n", option
);
1486 verbose (" Option: --%s=%s\n", option
, parameter
);
1488 if (processBooleanOption (option
, parameter
))
1490 else if (processParametricOption (option
, parameter
))
1492 else if (processKindOption (option
, parameter
))
1494 else if (processRegexOption (option
, parameter
))
1496 #ifndef RECURSE_SUPPORTED
1497 else if (strcmp (option
, "recurse") == 0)
1498 error (WARNING
, "%s option not supported on this host", option
);
1501 error (FATAL
, "Unknown option: --%s", option
);
1504 static void processShortOption (
1505 const char *const option
, const char *const parameter
)
1507 if (parameter
== NULL
|| parameter
[0] == '\0')
1508 verbose (" Option: -%s\n", option
);
1510 verbose (" Option: -%s %s\n", option
, parameter
);
1512 if (isCompoundOption (*option
) && (parameter
== NULL
|| parameter
[0] == '\0'))
1513 error (FATAL
, "Missing parameter for \"%s\" option", option
);
1514 else switch (*option
)
1517 processHelpOption ("?", NULL
);
1521 checkOptionOrder (option
);
1522 Option
.append
= TRUE
;
1526 if (atol (parameter
) < 0)
1527 error (FATAL
, "-%s: Invalid line number", option
);
1528 Option
.breakLine
= atol (parameter
);
1531 Option
.debugLevel
= strtol (parameter
, NULL
, 0);
1532 if (debug (DEBUG_STATUS
))
1533 Option
.verbose
= TRUE
;
1537 Option
.backward
= TRUE
;
1540 checkOptionOrder (option
);
1545 checkOptionOrder (option
);
1546 if (Option
.tagFileName
!= NULL
)
1549 "-%s option specified more than once, last value used",
1551 freeString (&Option
.tagFileName
);
1553 else if (parameter
[0] == '-' && parameter
[1] != '\0')
1554 error (FATAL
, "output file name may not begin with a '-'");
1555 Option
.tagFileName
= stringCopy (parameter
);
1558 Option
.backward
= FALSE
;
1561 processHeaderListOption (*option
, parameter
);
1564 processIgnoreOption (parameter
);
1567 if (Option
.fileList
!= NULL
)
1570 "-%s option specified more than once, last value used",
1572 freeString (&Option
.fileList
);
1574 Option
.fileList
= stringCopy (parameter
);
1577 Option
.locate
= EX_LINENUM
;
1580 Option
.locate
= EX_PATTERN
;
1583 #ifdef RECURSE_SUPPORTED
1584 Option
.recurse
= TRUE
;
1586 error (WARNING
, "-%s option not supported on this host", option
);
1590 checkOptionOrder (option
);
1591 Option
.sorted
= SO_UNSORTED
;
1594 Option
.verbose
= TRUE
;
1597 /* silently ignored */
1600 checkOptionOrder (option
);
1604 error (FATAL
, "Unknown option: -%s", option
);
1609 extern void parseOption (cookedArgs
* const args
)
1611 Assert (! cArgOff (args
));
1614 if (args
->longOption
)
1615 processLongOption (args
->item
, args
->parameter
);
1618 const char *parameter
= args
->parameter
;
1619 while (*parameter
== ' ')
1621 processShortOption (args
->item
, parameter
);
1627 extern void parseOptions (cookedArgs
* const args
)
1629 NonOptionEncountered
= FALSE
;
1630 while (! cArgOff (args
) && cArgIsOption (args
))
1632 if (! cArgOff (args
) && ! cArgIsOption (args
))
1633 NonOptionEncountered
= TRUE
;
1636 static const char *CheckFile
;
1637 static boolean
checkSameFile (const char *const fileName
)
1639 return isSameFile (CheckFile
, fileName
);
1642 static boolean
parseFileOptions (const char* const fileName
)
1644 boolean fileFound
= FALSE
;
1645 const char* const format
= "Considering option file %s: %s\n";
1646 CheckFile
= fileName
;
1647 if (stringListHasTest (OptionFiles
, checkSameFile
))
1648 verbose (format
, fileName
, "already considered");
1651 FILE* const fp
= fopen (fileName
, "r");
1653 verbose (format
, fileName
, "not found");
1656 cookedArgs
* const args
= cArgNewFromLineFile (fp
);
1657 vString
* file
= vStringNewInit (fileName
);
1658 stringListAdd (OptionFiles
, file
);
1659 verbose (format
, fileName
, "reading...");
1660 parseOptions (args
);
1661 if (NonOptionEncountered
)
1662 error (WARNING
, "Ignoring non-option in %s\n", fileName
);
1671 /* Actions to be taken before reading any other options */
1672 extern void previewFirstOption (cookedArgs
* const args
)
1674 while (cArgIsOption (args
))
1676 if (strcmp (args
->item
, "V") == 0 || strcmp (args
->item
, "verbose") == 0)
1678 else if (strcmp (args
->item
, "options") == 0 &&
1679 strcmp (args
->parameter
, "NONE") == 0)
1681 fprintf (stderr
, "No options will be read from files or environment\n");
1682 SkipConfiguration
= TRUE
;
1690 static void parseConfigurationFileOptions (void)
1692 const char* const home
= getenv ("HOME");
1694 #ifdef CUSTOM_CONFIGURATION_FILE
1695 parseFileOptions (CUSTOM_CONFIGURATION_FILE
);
1697 #ifdef MSDOS_STYLE_PATH
1698 parseFileOptions ("/ctags.cnf");
1703 parseFileOptions ("/etc/ctags.conf");
1704 parseFileOptions ("/usr/local/etc/ctags.conf");
1707 vString
* const homeFile
= combinePathAndFile (home
, conf
);
1708 parseFileOptions (vStringValue (homeFile
));
1709 vStringDelete (homeFile
);
1711 parseFileOptions (conf
);
1714 static void parseEnvironmentOptions (void)
1716 const char *envOptions
= NULL
;
1717 const char* var
= NULL
;
1721 var
= ETAGS_ENVIRONMENT
;
1722 envOptions
= getenv (var
);
1724 if (envOptions
== NULL
)
1726 var
= CTAGS_ENVIRONMENT
;
1727 envOptions
= getenv (var
);
1729 if (envOptions
!= NULL
&& envOptions
[0] != '\0')
1731 cookedArgs
* const args
= cArgNewFromString (envOptions
);
1732 verbose ("Reading options from $CTAGS\n");
1733 parseOptions (args
);
1735 if (NonOptionEncountered
)
1736 error (WARNING
, "Ignoring non-option in %s variable", var
);
1740 extern void readOptionConfiguration (void)
1742 if (! SkipConfiguration
)
1744 parseConfigurationFileOptions ();
1745 parseEnvironmentOptions ();
1750 * Option initialization
1753 extern void initOptions (void)
1755 OptionFiles
= stringListNew ();
1756 verbose ("Setting option defaults\n");
1757 installHeaderListDefaults ();
1758 verbose (" Installing default language mappings:\n");
1759 installLanguageMapDefaults ();
1761 /* always excluded by default */
1762 verbose (" Installing default exclude patterns:\n");
1763 processExcludeOption (NULL
, "EIFGEN");
1764 processExcludeOption (NULL
, "SCCS");
1765 processExcludeOption (NULL
, "RCS");
1766 processExcludeOption (NULL
, "CVS");
1770 extern void freeOptionResources (void)
1772 freeString (&Option
.tagFileName
);
1773 freeString (&Option
.fileList
);
1774 freeString (&Option
.filterTerminator
);
1776 freeList (&Excluded
);
1777 freeList (&Option
.ignore
);
1778 freeList (&Option
.headerExt
);
1779 freeList (&Option
.etagsInclude
);
1780 freeList (&OptionFiles
);
1783 /* vi:set tabstop=4 shiftwidth=4: */