xref: /Universal-ctags/main/options.c (revision c62e6612b9bde9529f58f8a1710a383ab2a224e4)
1 /*
2 *   Copyright (c) 1996-2003, Darren Hiebert
3 *
4 *   This source code is released for free distribution under the terms of the
5 *   GNU General Public License version 2 or (at your option) any later version.
6 *
7 *   This module contains functions to process command line options.
8 */
9 
10 /*
11 *   INCLUDE FILES
12 */
13 #include "general.h"  /* must always come first */
14 
15 #define OPTION_WRITE
16 #include "options_p.h"
17 
18 #ifndef _GNU_SOURCE
19 # define _GNU_SOURCE   /* for asprintf */
20 #endif
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <ctype.h>  /* to declare isspace () */
25 
26 #include "ctags.h"
27 #include "debug.h"
28 #include "entry_p.h"
29 #include "field_p.h"
30 #include "gvars.h"
31 #include "keyword_p.h"
32 #include "parse_p.h"
33 #include "ptag_p.h"
34 #include "routines_p.h"
35 #include "xtag_p.h"
36 #include "param_p.h"
37 #include "error_p.h"
38 #include "interactive_p.h"
39 #include "writer_p.h"
40 #include "trace.h"
41 
42 #ifdef HAVE_JANSSON
43 #include <jansson.h>
44 #endif
45 
46 /*
47 *   MACROS
48 */
49 #define INVOCATION  "Usage: %s [options] [file(s)]\n"
50 
51 #define CTAGS_ENVIRONMENT  "CTAGS"
52 #define ETAGS_ENVIRONMENT  "ETAGS"
53 
54 #ifndef ETAGS
55 # define ETAGS	"etags"  /* name which causes default use of to -e */
56 #endif
57 
58 /*  The following separators are permitted for list options.
59  */
60 #define EXTENSION_SEPARATOR '.'
61 #define PATTERN_START '('
62 #define PATTERN_STOP  ')'
63 #define IGNORE_SEPARATORS   ", \t\n"
64 
65 #ifndef DEFAULT_FILE_FORMAT
66 # define DEFAULT_FILE_FORMAT  2
67 #endif
68 
69 #if defined (HAVE_OPENDIR) || defined (HAVE__FINDFIRST)
70 # define RECURSE_SUPPORTED
71 #endif
72 
73 #define isCompoundOption(c)  (bool) (strchr ("fohiILpdDb", (c)) != NULL)
74 
75 #define ENTER(STAGE) do {												\
76 		Assert (Stage <= OptionLoadingStage##STAGE);					\
77 		if (Stage != OptionLoadingStage##STAGE)							\
78 		{																\
79 			Stage = OptionLoadingStage##STAGE;							\
80 			verbose ("Entering configuration stage: loading %s\n", StageDescription[Stage]); \
81 		}																\
82 	} while (0)
83 
84 /*
85 *   Data declarations
86 */
87 
88 enum eOptionLimits {
89 	MaxHeaderExtensions	= 100,  /* maximum number of extensions in -h option */
90 	MaxSupportedTagFormat = 2
91 };
92 
93 typedef struct sOptionDescription {
94 	int usedByEtags;
95 	int experimentalOption;
96 	const char *description;
97 } optionDescription;
98 
99 typedef void (*parametricOptionHandler) (const char *const option, const char *const parameter);
100 
101 typedef const struct {
102 	const char* name;   /* name of option as specified by user */
103 	parametricOptionHandler handler;  /* routine to handle option */
104 	bool initOnly;   /* option must be specified before any files */
105 	unsigned long acceptableStages;
106 } parametricOption;
107 
108 typedef const struct sBooleanOption {
109 	const char* name;   /* name of option as specified by user */
110 	bool* pValue;    /* pointer to option value */
111 	bool initOnly;   /* option must be specified before any files */
112 	unsigned long acceptableStages;
113 	void (* set) (const struct sBooleanOption *const option, bool value);
114 } booleanOption;
115 
116 /*
117 *   DATA DEFINITIONS
118 */
119 
120 static bool NonOptionEncountered = false;
121 static stringList *OptionFiles;
122 
123 typedef stringList searchPathList;
124 static searchPathList *OptlibPathList;
125 
126 static stringList *Excluded, *ExcludedException;
127 static bool FilesRequired = true;
128 static bool SkipConfiguration;
129 
130 static const char *const HeaderExtensions [] = {
131 	"h", "H", "hh", "hpp", "hxx", "h++", "inc", "def", NULL
132 };
133 
134 long ctags_debugLevel = 0L;
135 bool ctags_verbose = false;
136 
137 optionValues Option = {
138 	.append = false,
139 	.backward = false,
140 	.etags = false,
141 	.locate =
142 #ifdef MACROS_USE_PATTERNS
143 	EX_PATTERN
144 #else
145 	EX_MIX
146 #endif
147 	,
148 	.recurse = false,
149 	.sorted = SO_SORTED,
150 	.xref = false,
151 	.customXfmt = NULL,
152 	.fileList = NULL,
153 	.tagFileName = NULL,
154 	.headerExt = NULL,
155 	.etagsInclude = NULL,
156 	.tagFileFormat = DEFAULT_FILE_FORMAT,
157 #ifdef HAVE_ICONV
158 	.inputEncoding= NULL,
159 	.outputEncoding = NULL,
160 #endif
161 	.language = LANG_AUTO,
162 	.followLinks = true,
163 	.filter = false,
164 	.filterTerminator = NULL,
165 	.tagRelative = TREL_NO,
166 	.printTotals = 0,
167 	.lineDirectives = false,
168 	.printLanguage =false,
169 	.guessLanguageEagerly = false,
170 	.quiet = false,
171 	.fatalWarnings = false,
172 	.patternLengthLimit = 96,
173 	.putFieldPrefix = false,
174 	.maxRecursionDepth = 0xffffffff,
175 	.interactive = false,
176 	.fieldsReset = false,
177 #ifdef WIN32
178 	.useSlashAsFilenameSeparator = FILENAME_SEP_UNSET,
179 #endif
180 #ifdef DEBUG
181 	.breakLine = 0,
182 #endif
183 };
184 
185 struct localOptionValues {
186 	bool machinable;			/* --machinable */
187 	bool withListHeader;		/* --with-list-header */
188 } localOption = {
189 	.machinable = false,
190 	.withListHeader = true,
191 };
192 
193 typedef enum eOptionLoadingStage {
194 	OptionLoadingStageNone,
195 	OptionLoadingStageCustom,
196 	OptionLoadingStageXdg,
197 	OptionLoadingStageHomeRecursive,
198 	OptionLoadingStageCurrentRecursive,
199 	OptionLoadingStageEnvVar,
200 	OptionLoadingStageCmdline,
201 } OptionLoadingStage;
202 
203 static OptionLoadingStage Stage = OptionLoadingStageNone;
204 #define STAGE_ANY ~0UL
205 
206 /*
207 -   Locally used only
208 */
209 
210 static optionDescription LongOptionDescription [] = {
211  {1,0,"Input/Output Options"},
212  {1,0,"  --exclude=<pattern>"},
213  {1,0,"       Exclude files and directories matching <pattern>."},
214  {1,0,"       See also --exclude-exception option."},
215  {1,0,"  --exclude-exception=<pattern>"},
216  {1,0,"      Don't exclude files and directories matching <pattern> even if"},
217  {1,0,"      they match the pattern specified with --exclude option."},
218  {1,0,"  --filter[=(yes|no)]"},
219  {1,0,"       Behave as a filter, reading file names from standard input and"},
220  {1,0,"       writing tags to standard output [no]."},
221  {1,0,"  --filter-terminator=<string>"},
222  {1,0,"       Specify <string> to print to stdout following the tags for each file"},
223  {1,0,"       parsed when --filter is enabled."},
224  {1,0,"  --links[=(yes|no)]"},
225  {1,0,"       Indicate whether symbolic links should be followed [yes]."},
226  {1,0,"  --maxdepth=<N>"},
227 #ifdef RECURSE_SUPPORTED
228  {1,0,"       Specify maximum recursion depth."},
229 #else
230  {1,0,"       Not supported on this platform."},
231 #endif
232  {1,0,"  --recurse[=(yes|no)]"},
233 #ifdef RECURSE_SUPPORTED
234  {1,0,"       Recurse into directories supplied on command line [no]."},
235  {1,0,"  -R   Equivalent to --recurse."},
236 #else
237  {1,0,"       Not supported on this platform."},
238  {1,0,"  -R   Not supported on this platform."},
239 #endif
240  {1,0,"  -L <file>"},
241  {1,0,"       A list of input file names is read from the specified <file>."},
242  {1,0,"       If specified as \"-\", then standard input is read."},
243  {1,0,"  --append[=(yes|no)]"},
244  {1,0,"       Should tags should be appended to existing tag file [no]?"},
245  {1,0,"  -a   Append the tags to an existing tag file."},
246  {1,0,"  -f <tagfile>"},
247  {1,0,"       Write tags to specified <tagfile>. Value of \"-\" writes tags to stdout"},
248  {1,0,"       [\"tags\"; or \"TAGS\" when -e supplied]."},
249  {1,0,"  -o   Alternative for -f."},
250  {1,0,""},
251  {1,0,"Output Format Options"},
252  {0,0,"  --format=(1|2)"},
253 #if DEFAULT_FILE_FORMAT == 1
254  {0,0,"       Force output of specified tag file format [1]."},
255 #else
256  {0,0,"       Force output of specified tag file format [2]."},
257 #endif
258 #ifdef HAVE_JANSSON
259  {0,0,"  --output-format=(u-ctags|e-ctags|etags|xref|json)"},
260 #else
261  {0,0,"  --output-format=(u-ctags|e-ctags|etags|xref)"},
262 #endif
263  {0,0,"      Specify the output format. [u-ctags]"},
264  {0,0,"  -e   Output tag file for use with Emacs."},
265  {1,0,"  -x   Print a tabular cross reference file to standard output."},
266  {0,0,"  --sort=(yes|no|foldcase)"},
267  {0,0,"       Should tags be sorted (optionally ignoring case) [yes]?"},
268  {0,0,"  -u   Equivalent to --sort=no."},
269  {1,0,"  --etags-include=<file>"},
270  {1,0,"       Include reference to <file> in Emacs-style tag file (requires -e)."},
271 #ifdef HAVE_ICONV
272  {1,0,"  --input-encoding=<encoding>"},
273  {1,0,"       Specify <encoding> of all input files."},
274  {1,0,"  --input-encoding-<LANG>=<encoding>"},
275  {1,0,"       Specify <encoding> of the <LANG> input files."},
276  {1,0,"  --output-encoding=<encoding>"},
277  {1,0,"       The <encoding> to write the tag file in. Defaults to UTF-8 if --input-encoding"},
278  {1,0,"       is specified, otherwise no conversion is performed."},
279 #endif
280  {1,1,"  --_xformat=<field_format>"},
281  {1,1,"       Specify custom format for tabular cross reference (-x)."},
282  {1,1,"       Fields can be specified with letter listed in --list-fields."},
283  {1,1,"       e.g. --_xformat=%10N %10l:%K @ %-20F:%-20n"},
284  {1,0,""},
285  {1,0,"Language Selection and Mapping Options"},
286  {1,0,"  --language-force=(<language>|auto)"},
287  {1,0,"       Force all files to be interpreted using specified <language>."},
288  {1,0,"  --languages=[+|-](<list>|all)"},
289  {1,0,"       Restrict files scanned for tags to those mapped to languages"},
290  {1,0,"       specified in the comma-separated <list>. The list can contain any"},
291  {1,0,"       built-in or user-defined language [all]."},
292  {1,0,"  --alias-<LANG>=[+|-](<pattern>|default)"},
293  {1,0,"       Add a <pattern> detecting a name, can be used as an alternative name"},
294  {1,0,"       for <LANG>."},
295  {1,0,"  --guess-language-eagerly"},
296  {1,0,"       Guess the language of input file more eagerly"},
297  {1,0,"       (but taking longer time for guessing):"},
298  {1,0,"       o shebang, even if the input file is not executable,"},
299  {1,0,"       o emacs mode specification at the beginning and end of input file, and"},
300  {1,0,"       o vim syntax specification at the end of input file."},
301  {1,0,"  -G   Equivalent to --guess-language-eagerly."},
302  {1,0,"  --langmap=<map>[,<map>[...]]"},
303  {1,0,"       Override default mapping of language to input file extension."},
304  {1,0,"       e.g. --langmap=c:.c.x,java:+.j,make:([Mm]akefile).mak"},
305  {1,0,"  --map-<LANG>=[+|-]<extension>|<pattern>"},
306  {1,0,"       Set, add(+) or remove(-) the map for <LANG>."},
307  {1,0,"       Unlike --langmap, this doesn't take a list; only one file name <pattern>"},
308  {1,0,"       or one file <extension> can be specified at once."},
309  {1,0,"       Unlike --langmap the change with this option affects mapping of <LANG> only."},
310  {1,0,""},
311  {1,0,"Tags File Contents Options"},
312  {0,0,"  --excmd=(number|pattern|mix|combine)"},
313 #ifdef MACROS_USE_PATTERNS
314  {0,0,"       Uses the specified type of EX command to locate tags [pattern]."},
315 #else
316  {0,0,"       Uses the specified type of EX command to locate tags [mix]."},
317 #endif
318  {0,0,"  -n   Equivalent to --excmd=number."},
319  {0,0,"  -N   Equivalent to --excmd=pattern."},
320  {1,0,"  --extras=[+|-][<flags>|*]"},
321  {1,0,"       Include extra tag entries for selected information (<flags>: \"fFgpqrs\") [F]."},
322  {1,0,"  --extras-(<LANG>|all)=[+|-][<flags>|*]"},
323  {1,0,"       Include <LANG> own extra tag entries for selected information"},
324  {1,0,"       (<flags>: see the output of --list-extras=<LANG> option)."},
325  {1,0,"  --fields=[+|-][<flags>|*]"},
326  {1,0,"       Include selected extension fields (<flags>: \"aCeEfFikKlmnNpPrRsStxzZ\") [fks]."},
327  {1,0,"  --fields-(<LANG>|all)=[+|-][<flags>|*]"},
328  {1,0,"       Include selected <LANG> own extension fields"},
329  {1,0,"       (<flags>: see the output of --list-fields=<LANG> option)."},
330  {1,0,"  --kinds-(<LANG>|all)=[+|-](<kinds>|*)"},
331  {1,0,"       Enable/disable tag <kinds> for language <LANG>."},
332  {0,0,"  --pattern-length-limit=<N>"},
333  {0,0,"      Cutoff patterns of tag entries after <N> characters. Disable by setting to 0. [96]"},
334  {0,0,"  --pseudo-tags=[+|-](<pseudo-tag>|*)"},
335  {0,0,"       Enable/disable emitting pseudo tag named <pseudo-tag>."},
336  {0,0,"       if '*' is given, enable emitting all pseudo tags."},
337  {0,0,"  --put-field-prefix"},
338  {0,0,"       Put \"" CTAGS_FIELD_PREFIX "\" as prefix for the name of fields newly introduced in"},
339  {0,0,"       universal-ctags."},
340  {1,0,"  --roles-(<LANG>|all).(<kind>|*)=[+|-][<roles>|*]"},
341  {1,0,"       Enable/disable tag roles for kinds of language <LANG>."},
342  {0,0,"  --tag-relative=(yes|no|always|never)"},
343  {0,0,"       Should paths be relative to location of tag file [no; yes when -e]?"},
344  {0,0,"       always: be relative even if input files are passed in with absolute paths" },
345  {0,0,"       never:  be absolute even if input files are passed in with relative paths" },
346 #ifdef WIN32
347  {1,0,"  --use-slash-as-filename-separator[=(yes|no)]"},
348  {1,0,"       Use slash as filename separator [yes] for u-ctags output format."},
349 #endif
350  {0,0,"  -B   Use backward searching patterns (?...?)."},
351  {0,0,"  -F   Use forward searching patterns (/.../; default)."},
352  {1,0,""},
353  {1,0,"Option File Options"},
354  {1,0,"  --options=<pathname>"},
355  {1,0,"       Specify file (or directory) <pathname> from which command line options should be read."},
356  {1,0,"  --options-maybe=<pathname>"},
357  {1,0,"       Do the same as --options but this doesn't make an error for non-existing file."},
358  {1,0,"  --optlib-dir=[+]<directory>"},
359  {1,0,"       Add or set <directory> to optlib search path."},
360  {1,1,"  --_echo=<msg>"},
361  {1,1,"       Echo <msg> to standard error. Useful to debug the chain"},
362  {1,1,"       of loading option files."},
363  {1,1,"  --_force-quit[=<num>]"},
364  {1,1,"       Quit when loading the option files is processed."},
365  {1,1,"       Useful to debug the chain of loading option files."},
366  {1,0,""},
367  {1,0,"optlib Options"},
368  {1,0,"  --kinddef-<LANG>=<letter>,<name>,<description>"},
369  {1,0,"       Define new kind for <LANG>."},
370  {1,0,"  --langdef=<name>"},
371  {1,0,"       Define a new language to be parsed with regular expressions."},
372  {1,0,"  --mline-regex-<LANG>=/<line_pattern>/<name_pattern>/<kind-spec>/[<flags>]"},
373  {1,0,"       Define multiline regular expression for locating tags in specific language."},
374  {1,0,"  --regex-<LANG>=/<line_pattern>/<name_pattern>/<kind-spec>/[<flags>]"},
375  {1,0,"       Define single-line regular expression for locating tags in specific language."},
376  {1,1,"  --_extradef-<LANG>=<name>,<description>"},
377  {1,1,"       Define new extra for <LANG>. --extras-<LANG>=+{name} enables it."},
378  {1,1,"  --_fielddef-<LANG>=<name>,<description>"},
379  {1,1,"       Define new field for <LANG>."},
380  {1,1,"  --_mtable-extend-<LANG>=disttable+srctable."},
381  {1,1,"       Copy patterns of a regex table to another regex table."},
382  {1,1,"  --_mtable-regex-<LANG>=<table>/<line_pattern>/<name_pattern>/[<flags>]"},
383  {1,1,"       Define multitable regular expression for locating tags in specific language."},
384  {1,1,"  --_prelude-<LANG>={{ optscript-code }}"},
385  {1,1,"       Specify code run before parsing with <LANG> parser."},
386  {1,1,"  --_pretend-<NEWLANG>=<OLDLANG>"},
387  {1,1,"       Make NEWLANG parser pretend OLDLANG parser in lang: field."},
388  {1,1,"  --_roledef-<LANG>.<kind>=<name>,<description>"},
389  {1,1,"       Define new role for the kind in <LANG>."},
390  {1,1,"  --_scopesep-<LANG>=[<parent_kind_letter>|*]/(<child_kind_letter>|*):<separator>"},
391  {1,1,"       Specify scope separator between <PARENT_KIND> and <KIND>."},
392  {1,1,"  --_sequel-<LANG>={{ optscript-code }}"},
393  {1,1,"       Specify code run after parsing with <LANG> parser."},
394  {1,1,"  --_tabledef-<LANG>=<name>"},
395  {1,1,"       Define new regex table for <LANG>."},
396  {1,0,""},
397  {1,0,"Language Specific Options"},
398  {1,0,"  --if0[=(yes|no)]"},
399  {1,0,"       Should code within #if 0 conditional branches be parsed [no]?"},
400  {0,0,"  --line-directives[=(yes|no)]"},
401  {0,0,"       Should '#line' directives be processed [no]?"},
402  {1,0,"  -D <macro>=<definition>"},
403  {1,0,"       (CPreProcessor) Give <definition> for <macro>."},
404  {1,0,"  -h (<list>|default)"},
405  {1,0,"       Specify a <list> of file extensions to be treated as include files"},
406  {1,0,"       [\".h.H.hh.hpp.hxx.h++.inc.def\"]."},
407  {1,0,"  -I [+|-]<list>|@<file>"},
408  {1,0,"       A <list> of tokens to be specially handled is read from either the"},
409  {1,0,"       command line or the specified <file>."},
410  {1,0,"  --param-<LANG>.<name>=<argument>"},
411  {1,0,"       Set <LANG> specific parameter. Available parameters can be listed with --list-params."},
412  {1,0,""},
413  {1,0,"Listing Options"},
414  {1,0,"  --list-aliases[=(<language>|all)]"},
415  {1,0,"       Output list of alias patterns."},
416  {1,0,"  --list-excludes"},
417  {1,0,"       Output list of exclude patterns for excluding files/directories."},
418  {1,0,"  --list-extras[=(<language>|all)]"},
419  {1,0,"       Output list of extra tag flags."},
420  {1,0,"  --list-features"},
421  {1,0,"       Output list of compiled features."},
422  {1,0,"  --list-fields[=(<language>|all)]"},
423  {1,0,"       Output list of fields."},
424  {1,0,"  --list-kinds[=(<language>|all)]"},
425  {1,0,"       Output a list of all tag kinds for specified <language> or all."},
426  {1,0,"  --list-kinds-full[=(<language>|all)]"},
427  {1,0,"       List the details of all tag kinds for specified <language> or all"},
428  {1,0,"       For each line, associated language name is printed when \"all\" is"},
429  {1,0,"       specified as language."},
430  {1,0,"  --list-languages"},
431  {1,0,"       Output list of supported languages."},
432  {1,0,"  --list-map-extensions[=(<language>|all)]"},
433  {1,0,"       Output list of language extensions in mapping."},
434  {1,0,"  --list-map-patterns[=(<language>|all)]"},
435  {1,0,"       Output list of language patterns in mapping."},
436  {1,0,"  --list-maps[=(<language>|all)]"},
437  {1,0,"       Output list of language mappings (both extensions and patterns)."},
438  {1,0,"  --list-mline-regex-flags"},
439  {1,0,"       Output list of flags which can be used in a multiline regex parser definition."},
440  {1,0,"  --list-params[=(<language>|all)]"},
441  {1,0,"       Output list of language parameters. This works with --machinable."},
442  {0,0,"  --list-pseudo-tags"},
443  {0,0,"       Output list of pseudo tags."},
444  {1,0,"  --list-regex-flags"},
445  {1,0,"       Output list of flags which can be used in a regex parser definition."},
446  {1,0,"  --list-roles[=(<language>|all)[.(<kindspecs>|*)]]"},
447  {1,0,"       Output list of all roles of tag kind(s) specified for <language>."},
448  {1,0,"       Both letters and names can be used in <kindspecs>."},
449  {1,0,"       e.g. --list-roles=C.{header}d"},
450  {1,0,"  --list-subparsers[=(<baselang>|all)]"},
451  {1,0,"       Output list of subparsers for the base language."},
452  {1,0,"  --machinable[=(yes|no)]"},
453  {1,0,"       Use tab separated representation in --list-* option output. [no]"},
454  {1,0,"       --list-{aliases,extras,features,fields,kind-full,langdef-flags,params," },
455  {1,0,"       pseudo-tags,regex-flags,roles,subparsers} support this option."},
456  {1,0,"       Suitable for scripting. Specify before --list-* option."},
457  {1,0,"  --with-list-header[=(yes|no)]"},
458  {1,0,"       Prepend the column descriptions in --list- output. [yes]"},
459  {1,0,"       --list-{aliases,extras,features,fields,kind-full,langdef-flags,params," },
460  {1,0,"       pseudo-tags,regex-flags,roles,subparsers} support this option."},
461  {1,0,"       Specify before --list-* option."},
462  {1,1,"  --_list-kinddef-flags"},
463  {1,1,"       Output list of flags which can be used with --kinddef option."},
464  {1,1,"  --_list-langdef-flags"},
465  {1,1,"       Output list of flags which can be used with --langdef option."},
466  {1,1,"  --_list-mtable-regex-flags"},
467  {1,1,"       Output list of flags which can be used in a multitable regex parser definition."},
468  {1,1,"  --_list-operators"},
469  {1,1,"       Output list of optscript operators."},
470  {1,0,""},
471  {1,0,"Miscellaneous Options"},
472  {1,0,"  --help"},
473  {1,0,"       Print this option summary."},
474  {1,0,"  -?   Print this option summary."},
475  {1,0,"  --help-full"},
476  {1,0,"       Print this option summary including experimental features."},
477  {1,0,"  --license"},
478  {1,0,"       Print details of software license."},
479  {0,0,"  --print-language"},
480  {0,0,"       Don't make tags file but just print the guessed language name for"},
481  {0,0,"       input file."},
482  {1,0,"  --quiet[=(yes|no)]"},
483  {0,0,"       Don't print NOTICE class messages [no]."},
484  {1,0,"  --totals[=(yes|no|extra)]"},
485  {1,0,"       Print statistics about input and tag files [no]."},
486  {1,0,"  --verbose[=(yes|no)]"},
487  {1,0,"       Enable verbose messages describing actions on each input file."},
488  {1,0,"  --version"},
489  {1,0,"       Print version identifier to standard output."},
490  {1,0,"  -V   Equivalent to --verbose."},
491 #ifdef DEBUG
492  {1,0,"  -b <line>"},
493  {1,0,"       Set break line. (for DEBUG)"},
494  {1,0,"  -d <level>"},
495  {1,0,"       Set debug level. (for DEBUG)"},
496 #endif
497 
498  {1,1,"  --_anonhash=<fname>"},
499  {1,1,"       Used in u-ctags test harness"},
500  {1,1,"  --_dump-keywords"},
501  {1,1,"       Dump keywords of initialized parser(s)."},
502  {1,1,"  --_dump-options"},
503  {1,1,"       Dump options."},
504  {1,1,"  --_dump-prelude"},
505  {1,1,"       Dump contents of optscript prelude."},
506  {1,1,"  --_fatal-warnings"},
507  {1,1,"       Make all warnings fatal."},
508  {1,1,"  --_force-initializing"},
509  {1,1,"       Initialize all parsers in early stage"},
510 #ifdef HAVE_JANSSON
511  {0,1,"  --_interactive"
512 #ifdef HAVE_SECCOMP
513   "[=(default|sandbox)]"
514 #endif
515  },
516  {0,1,"       Enter interactive mode (JSON over stdio)."},
517 #ifdef HAVE_SECCOMP
518  {0,1,"       Enter file I/O limited interactive mode if sandbox is specified. [default]"},
519 #endif
520 #endif
521 #ifdef DO_TRACING
522  {1,1,"  --_trace=<list>"},
523  {1,1,"       Trace parsers for the languages."},
524 #endif
525  {1,1, NULL}
526 };
527 
528 static const char* const License1 =
529 "This program is free software; you can redistribute it and/or\n"
530 "modify it under the terms of the GNU General Public License\n"
531 "as published by the Free Software Foundation; either version 2"
532 "of the License, or (at your option) any later version.\n"
533 "\n";
534 static const char* const License2 =
535 "This program is distributed in the hope that it will be useful,\n"
536 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
537 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
538 "GNU General Public License for more details.\n"
539 "\n"
540 "You should have received a copy of the GNU General Public License\n"
541 "along with this program; if not, write to the Free Software\n"
542 "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n";
543 
544 /*  Contains a set of strings describing the set of "features" compiled into
545  *  the code.
546  */
547 static struct Feature {
548 	const char *name;
549 	const char *description;
550 } Features [] = {
551 #ifdef WIN32
552 	{"win32", "TO BE WRITTEN"},
553 #endif
554 	/* Following two features are always available on universal ctags */
555 	{"wildcards", "can use glob matching"},
556 	{"regex", "can use regular expression based pattern matching"},
557 #ifdef USE_GNULIB_FNMATCH
558 	{"gnulib_fnmatch", "linked with the Gnulib fnmatch library"},
559 #endif
560 /* https://lists.gnu.org/archive/html/bug-gnulib/2011-07/msg00435.html */
561 #ifdef _REGEX_INCLUDE_LIMITS_H
562 	{"gnulib_regex", "linked with the Gnulib regular expression library"},
563 #endif
564 #ifndef EXTERNAL_SORT
565 	{"internal-sort", "uses internal sort routine instead of invoking sort command"},
566 #endif
567 #ifdef CUSTOM_CONFIGURATION_FILE
568 	{"custom-conf", "read \"" CUSTOM_CONFIGURATION_FILE "\" as config file"},
569 #endif
570 #if defined (WIN32)
571 	{"unix-path-separator", "can use '/' as file name separator"},
572 #endif
573 #ifdef HAVE_ICONV
574 	{"iconv", "can convert input/output encodings"},
575 #endif
576 #ifdef DEBUG
577 	{"debug", "TO BE WRITTEN"},
578 #endif
579 #if defined (HAVE_DIRENT_H) || defined (_MSC_VER)
580 	{"option-directory", "TO BE WRITTEN"},
581 #endif
582 #ifdef HAVE_LIBXML
583 	{"xpath", "linked with library for parsing xml input"},
584 #endif
585 #ifdef HAVE_JANSSON
586 	{"json", "supports json format output"},
587 	{"interactive", "accepts source code from stdin"},
588 #endif
589 #ifdef HAVE_SECCOMP
590 	{"sandbox", "linked with code for system call level sandbox"},
591 #endif
592 #ifdef HAVE_LIBYAML
593 	{"yaml", "linked with library for parsing yaml input"},
594 #endif
595 #ifdef CASE_INSENSITIVE_FILENAMES
596 	{"case-insensitive-filenames", "TO BE WRITTEN"},
597 #endif
598 #ifdef ENABLE_GCOV
599 	{"gcov", "linked with code for coverage analysis"},
600 #endif
601 #ifdef HAVE_PACKCC
602 	/* The test harnesses use this as hints for skipping test cases */
603 	{"packcc", "has peg based parser(s)"},
604 #endif
605 	{"optscript", "can use the interpreter"},
606 #ifdef HAVE_PCRE2
607 	{"pcre2", "has pcre2 regex engine"},
608 #endif
609 	{NULL,}
610 };
611 
612 static const char *const StageDescription [] = {
613 	[OptionLoadingStageNone]   = "not initialized",
614 	[OptionLoadingStageCustom] = "custom file",
615 	[OptionLoadingStageXdg] = "file(s) under $XDG_CONFIG_HOME and $HOME/.config",
616 	[OptionLoadingStageHomeRecursive] = "file(s) under $HOME",
617 	[OptionLoadingStageCurrentRecursive] = "file(s) under the current directory",
618 	[OptionLoadingStageCmdline] = "command line",
619 };
620 
621 /*
622 *   FUNCTION PROTOTYPES
623 */
624 static bool parseFileOptions (const char *const fileName);
625 static bool parseAllConfigurationFilesOptionsInDirectory (const char *const fileName,
626 							     stringList* const already_loaded_files);
627 static bool getBooleanOption (const char *const option, const char *const parameter);
628 
629 /*
630 *   FUNCTION DEFINITIONS
631 */
632 
633 #ifndef HAVE_ASPRINTF
634 
635 /* Some versions of MinGW are missing _vscprintf's declaration, although they
636  * still provide the symbol in the import library.
637  */
638 #ifdef __MINGW32__
639 _CRTIMP int _vscprintf(const char *format, va_list argptr);
640 #endif
641 
642 #ifndef va_copy
643 #define va_copy(dest, src) (dest = src)
644 #endif
645 
asprintf(char ** strp,const char * fmt,...)646 int asprintf(char **strp, const char *fmt, ...)
647 {
648 	va_list args;
649 	va_list args_copy;
650 	int length;
651 	size_t size;
652 
653 	va_start(args, fmt);
654 
655 	va_copy(args_copy, args);
656 
657 #ifdef _WIN32
658 	/* We need to use _vscprintf to calculate the length as vsnprintf returns -1
659 	 * if the number of characters to write is greater than count.
660 	 */
661 	length = _vscprintf(fmt, args_copy);
662 #else
663 	char dummy;
664 	length = vsnprintf(&dummy, sizeof dummy, fmt, args_copy);
665 #endif
666 
667 	va_end(args_copy);
668 
669 	Assert(length >= 0);
670 	size = length + 1;
671 
672 	*strp = malloc(size);
673 	if (!*strp) {
674 		return -1;
675 	}
676 
677 	va_start(args, fmt);
678 	vsnprintf(*strp, size, fmt, args);
679 	va_end(args);
680 
681 	return length;
682 }
683 #endif
684 
verbose(const char * const format,...)685 extern void verbose (const char *const format, ...)
686 {
687 	if (ctags_verbose)
688 	{
689 		va_list ap;
690 		va_start (ap, format);
691 		vfprintf (stderr, format, ap);
692 		va_end (ap);
693 	}
694 }
695 
stringCopy(const char * const string)696 static char *stringCopy (const char *const string)
697 {
698 	char* result = NULL;
699 	if (string != NULL)
700 		result = eStrdup (string);
701 	return result;
702 }
703 
freeString(char ** const pString)704 static void freeString (char **const pString)
705 {
706 	if (*pString != NULL)
707 	{
708 		eFree (*pString);
709 		*pString = NULL;
710 	}
711 }
712 
freeList(stringList ** const pList)713 extern void freeList (stringList** const pList)
714 {
715 	if (*pList != NULL)
716 	{
717 		stringListDelete (*pList);
718 		*pList = NULL;
719 	}
720 }
721 
setDefaultTagFileName(void)722 extern void setDefaultTagFileName (void)
723 {
724 	if (Option.filter || Option.interactive)
725 		return;
726 
727 	if (Option.tagFileName == NULL)
728 	{
729 		const char *tmp = outputDefaultFileName ();
730 
731 		if (tmp == NULL)
732 			tmp = "-";
733 
734 		Option.tagFileName = stringCopy (tmp);
735 	}
736 }
737 
filesRequired(void)738 extern bool filesRequired (void)
739 {
740 	bool result = FilesRequired;
741 	if (Option.recurse)
742 		result = false;
743 	return result;
744 }
745 
checkOptions(void)746 extern void checkOptions (void)
747 {
748 	const char* notice;
749 	if (Option.xref && (Option.customXfmt == NULL))
750 	{
751 		notice = "xref output";
752 		if (isXtagEnabled(XTAG_FILE_NAMES))
753 		{
754 			error (WARNING, "%s disables file name tags", notice);
755 			enableXtag (XTAG_FILE_NAMES, false);
756 		}
757 	}
758 	if (Option.append)
759 	{
760 		notice = "append mode is not compatible with";
761 		if (isDestinationStdout ())
762 			error (FATAL, "%s tags to stdout", notice);
763 	}
764 	if (Option.filter)
765 	{
766 		notice = "filter mode";
767 		if (Option.printTotals)
768 		{
769 			error (WARNING, "%s disables totals", notice);
770 			Option.printTotals = 0;
771 		}
772 		if (Option.tagFileName != NULL)
773 			error (WARNING, "%s ignores output tag file name", notice);
774 	}
775 	writerCheckOptions (Option.fieldsReset);
776 }
777 
getLanguageComponentInOptionFull(const char * const option,const char * const prefix,bool noPretending)778 extern langType getLanguageComponentInOptionFull (const char *const option,
779 												  const char *const prefix,
780 												  bool noPretending)
781 {
782 	size_t prefix_len;
783 	langType language;
784 	const char *lang;
785 	char *sep = NULL;
786 	size_t lang_len = 0;
787 
788 	Assert (prefix && prefix[0]);
789 	Assert (option);
790 
791 	prefix_len = strlen (prefix);
792 	if (strncmp (option, prefix, prefix_len) != 0)
793 		return LANG_IGNORE;
794 	else
795 	{
796 		lang = option + prefix_len;
797 		if (lang [0] == '\0')
798 			return LANG_IGNORE;
799 	}
800 
801 	/* Extract <LANG> from
802 	 * --param-<LANG>.<PARAM>=..., and
803 	 * --_roledef-<LANG>.<KIND>=... */
804 
805 	/*  `:' is only for keeping self compatibility. */
806 	sep = strpbrk (lang, ":.");
807 	if (sep)
808 	{
809 		if (*sep == ':')
810 			error (WARNING, "using `:' as a separator is obsolete; use `.' instead: --%s", option);
811 		lang_len = sep - lang;
812 	}
813 	language = getNamedLanguageFull (lang, lang_len, noPretending, false);
814 	if (language == LANG_IGNORE)
815 	{
816 		const char *langName = (lang_len == 0)? lang: eStrndup (lang, lang_len);
817 		error (FATAL, "Unknown language \"%s\" in \"%s\" option", langName, option);
818 	}
819 
820 	return language;
821 }
822 
getLanguageComponentInOption(const char * const option,const char * const prefix)823 extern langType getLanguageComponentInOption (const char *const option,
824 											  const char *const prefix)
825 {
826 	return getLanguageComponentInOptionFull (option, prefix, false);
827 }
828 
setEtagsMode(void)829 static void setEtagsMode (void)
830 {
831 	Option.etags = true;
832 	Option.sorted = SO_UNSORTED;
833 	Option.lineDirectives = false;
834 	Option.tagRelative = TREL_YES;
835 	enableLanguage (LANG_FALLBACK, true);
836 	setTagWriter (WRITER_ETAGS, NULL);
837 }
838 
testEtagsInvocation(void)839 extern void testEtagsInvocation (void)
840 {
841 	char* const execName = eStrdup (getExecutableName ());
842 	char* const etags = eStrdup (ETAGS);
843 #ifdef CASE_INSENSITIVE_FILENAMES
844 	toLowerString (execName);
845 	toLowerString (etags);
846 #endif
847 	if (strstr (execName, etags) != NULL)
848 	{
849 		verbose ("Running in etags mode\n");
850 		setEtagsMode ();
851 	}
852 	eFree (execName);
853 	eFree (etags);
854 }
855 
setXrefMode(void)856 static void setXrefMode (void)
857 {
858 	Option.xref = true;
859 	setTagWriter (WRITER_XREF, NULL);
860 }
861 
862 #ifdef HAVE_JANSSON
setJsonMode(void)863 static void setJsonMode (void)
864 {
865 	enablePtag (PTAG_JSON_OUTPUT_VERSION, true);
866 	enablePtag (PTAG_OUTPUT_MODE, false);
867 	enablePtag (PTAG_FILE_FORMAT, false);
868 	setTagWriter (WRITER_JSON, NULL);
869 }
870 #endif
871 
872 /*
873  *  Cooked argument parsing
874  */
875 
parseShortOption(cookedArgs * const args)876 static void parseShortOption (cookedArgs *const args)
877 {
878 	args->simple [0] = *args->shortOptions++;
879 	args->simple [1] = '\0';
880 	args->item = eStrdup (args->simple);
881 	if (! isCompoundOption (*args->simple))
882 		args->parameter = "";
883 	else if (*args->shortOptions == '\0')
884 	{
885 		argForth (args->args);
886 		if (argOff (args->args))
887 			args->parameter = NULL;
888 		else
889 			args->parameter = argItem (args->args);
890 		args->shortOptions = NULL;
891 	}
892 	else
893 	{
894 		args->parameter = args->shortOptions;
895 		args->shortOptions = NULL;
896 	}
897 }
898 
parseLongOption(cookedArgs * const args,const char * item)899 static void parseLongOption (cookedArgs *const args, const char *item)
900 {
901 	const char* const equal = strchr (item, '=');
902 	if (equal == NULL)
903 	{
904 		args->item = eStrdup (item);
905 		args->parameter = "";
906 	}
907 	else
908 	{
909 		args->item = eStrndup (item, equal - item);
910 		args->parameter = equal + 1;
911 	}
912 	Assert (args->item != NULL);
913 	Assert (args->parameter != NULL);
914 }
915 
cArgRead(cookedArgs * const current)916 static void cArgRead (cookedArgs *const current)
917 {
918 	char* item;
919 
920 	Assert (current != NULL);
921 	if (! argOff (current->args))
922 	{
923 		item = argItem (current->args);
924 		current->shortOptions = NULL;
925 		Assert (item != NULL);
926 		if (strncmp (item, "--", (size_t) 2) == 0)
927 		{
928 			current->isOption = true;
929 			current->longOption = true;
930 			parseLongOption (current, item + 2);
931 			Assert (current->item != NULL);
932 			Assert (current->parameter != NULL);
933 		}
934 		else if (*item == '-')
935 		{
936 			current->isOption = true;
937 			current->longOption = false;
938 			current->shortOptions = item + 1;
939 			parseShortOption (current);
940 		}
941 		else
942 		{
943 			current->isOption = false;
944 			current->longOption = false;
945 			current->item = eStrdup (item);
946 			current->parameter = NULL;
947 		}
948 	}
949 }
950 
cArgNewFromString(const char * string)951 extern cookedArgs* cArgNewFromString (const char* string)
952 {
953 	cookedArgs* const result = xMalloc (1, cookedArgs);
954 	memset (result, 0, sizeof (cookedArgs));
955 	result->args = argNewFromString (string);
956 	cArgRead (result);
957 	return result;
958 }
959 
cArgNewFromArgv(char * const * const argv)960 extern cookedArgs* cArgNewFromArgv (char* const* const argv)
961 {
962 	cookedArgs* const result = xMalloc (1, cookedArgs);
963 	memset (result, 0, sizeof (cookedArgs));
964 	result->args = argNewFromArgv (argv);
965 	cArgRead (result);
966 	return result;
967 }
968 
cArgNewFromFile(FILE * const fp)969 extern cookedArgs* cArgNewFromFile (FILE* const fp)
970 {
971 	cookedArgs* const result = xMalloc (1, cookedArgs);
972 	memset (result, 0, sizeof (cookedArgs));
973 	result->args = argNewFromFile (fp);
974 	cArgRead (result);
975 	return result;
976 }
977 
cArgNewFromLineFile(FILE * const fp)978 extern cookedArgs* cArgNewFromLineFile (FILE* const fp)
979 {
980 	cookedArgs* const result = xMalloc (1, cookedArgs);
981 	memset (result, 0, sizeof (cookedArgs));
982 	result->args = argNewFromLineFile (fp);
983 	cArgRead (result);
984 	return result;
985 }
986 
cArgDelete(cookedArgs * const current)987 extern void cArgDelete (cookedArgs* const current)
988 {
989 	Assert (current != NULL);
990 	argDelete (current->args);
991 	if (current->item != NULL)
992 		eFree (current->item);
993 	memset (current, 0, sizeof (cookedArgs));
994 	eFree (current);
995 }
996 
cArgOptionPending(cookedArgs * const current)997 static bool cArgOptionPending (cookedArgs* const current)
998 {
999 	bool result = false;
1000 	if (current->shortOptions != NULL)
1001 		if (*current->shortOptions != '\0')
1002 			result = true;
1003 	return result;
1004 }
1005 
cArgOff(cookedArgs * const current)1006 extern bool cArgOff (cookedArgs* const current)
1007 {
1008 	Assert (current != NULL);
1009 	return (bool) (argOff (current->args) && ! cArgOptionPending (current));
1010 }
1011 
cArgIsOption(cookedArgs * const current)1012 extern bool cArgIsOption (cookedArgs* const current)
1013 {
1014 	Assert (current != NULL);
1015 	return current->isOption;
1016 }
1017 
cArgItem(cookedArgs * const current)1018 extern const char* cArgItem (cookedArgs* const current)
1019 {
1020 	Assert (current != NULL);
1021 	return current->item;
1022 }
1023 
cArgForth(cookedArgs * const current)1024 extern void cArgForth (cookedArgs* const current)
1025 {
1026 	Assert (current != NULL);
1027 	Assert (! cArgOff (current));
1028 	if (current->item != NULL)
1029 		eFree (current->item);
1030 	if (cArgOptionPending (current))
1031 		parseShortOption (current);
1032 	else
1033 	{
1034 		Assert (! argOff (current->args));
1035 		argForth (current->args);
1036 		if (! argOff (current->args))
1037 			cArgRead (current);
1038 		else
1039 		{
1040 			current->isOption = false;
1041 			current->longOption = false;
1042 			current->shortOptions = NULL;
1043 			current->item = NULL;
1044 			current->parameter = NULL;
1045 		}
1046 	}
1047 }
1048 
1049 /*
1050  *  File extension and language mapping
1051  */
1052 
addExtensionList(stringList * const slist,const char * const elist,const bool clear)1053 static void addExtensionList (
1054 		stringList *const slist, const char *const elist, const bool clear)
1055 {
1056 	char *const extensionList = eStrdup (elist);
1057 	const char *extension = NULL;
1058 	bool first = true;
1059 
1060 	if (clear)
1061 	{
1062 		verbose ("      clearing\n");
1063 		stringListClear (slist);
1064 	}
1065 	verbose ("      adding: ");
1066 	if (elist != NULL  &&  *elist != '\0')
1067 	{
1068 		extension = extensionList;
1069 		if (elist [0] == EXTENSION_SEPARATOR)
1070 			++extension;
1071 	}
1072 	while (extension != NULL)
1073 	{
1074 		char *separator = strchr (extension, EXTENSION_SEPARATOR);
1075 		if (separator != NULL)
1076 			*separator = '\0';
1077 		verbose ("%s%s", first ? "" : ", ",
1078 				*extension == '\0' ? "(NONE)" : extension);
1079 		stringListAdd (slist, vStringNewInit (extension));
1080 		first = false;
1081 		if (separator == NULL)
1082 			extension = NULL;
1083 		else
1084 			extension = separator + 1;
1085 	}
1086 	BEGIN_VERBOSE(vfp);
1087 	{
1088 		fprintf (vfp, "\n      now: ");
1089 		stringListPrint (slist, vfp);
1090 		putc ('\n', vfp);
1091 	}
1092 	END_VERBOSE();
1093 }
1094 
isFalse(const char * parameter)1095 static bool isFalse (const char *parameter)
1096 {
1097 	return (bool) (
1098 		strcasecmp (parameter, "0"  ) == 0  ||
1099 		strcasecmp (parameter, "n"  ) == 0  ||
1100 		strcasecmp (parameter, "no" ) == 0  ||
1101 		strcasecmp (parameter, "off") == 0  ||
1102 		strcasecmp (parameter, "false") == 0 );
1103 }
1104 
isTrue(const char * parameter)1105 static bool isTrue (const char *parameter)
1106 {
1107 	return (bool) (
1108 		strcasecmp (parameter, "1"  ) == 0  ||
1109 		strcasecmp (parameter, "y"  ) == 0  ||
1110 		strcasecmp (parameter, "yes") == 0  ||
1111 		strcasecmp (parameter, "on" ) == 0  ||
1112 		strcasecmp (parameter, "true" ) == 0);
1113 }
1114 
paramParserBool(const char * value,bool fallback,const char * errWhat,const char * errCategory)1115 extern bool paramParserBool (const char *value, bool fallback,
1116 							 const char *errWhat, const char *errCategory)
1117 {
1118 	bool r = fallback;
1119 
1120 	if (value [0] == '\0')
1121 		r = true;
1122 	else if (isFalse (value))
1123 		r = false;
1124 	else if (isTrue (value))
1125 		r = true;
1126 	else
1127 		error (FATAL, "Invalid value for \"%s\" %s", errWhat, errCategory);
1128 
1129 	return r;
1130 }
1131 
1132 /*  Determines whether the specified file name is considered to be a header
1133  *  file for the purposes of determining whether enclosed tags are global or
1134  *  static.
1135  */
isIncludeFile(const char * const fileName)1136 extern bool isIncludeFile (const char *const fileName)
1137 {
1138 	bool result = false;
1139 	const char *const extension = fileExtension (fileName);
1140 	if (Option.headerExt != NULL)
1141 		result = stringListExtensionMatched (Option.headerExt, extension);
1142 	return result;
1143 }
1144 
1145 /*
1146  *  Specific option processing
1147  */
1148 
processEtagsInclude(const char * const option,const char * const parameter)1149 static void processEtagsInclude (
1150 		const char *const option, const char *const parameter)
1151 {
1152 	if (! Option.etags)
1153 		error (FATAL, "Etags must be enabled to use \"%s\" option", option);
1154 	else
1155 	{
1156 		vString *const file = vStringNewInit (parameter);
1157 		if (Option.etagsInclude == NULL)
1158 			Option.etagsInclude = stringListNew ();
1159 		stringListAdd (Option.etagsInclude, file);
1160 		FilesRequired = false;
1161 	}
1162 }
1163 
processExcludeOptionCommon(stringList ** list,const char * const optname,const char * const parameter)1164 static void processExcludeOptionCommon (
1165 	stringList** list, const char *const optname, const char *const parameter)
1166 {
1167 	const char *const fileName = parameter + 1;
1168 	if (parameter [0] == '\0')
1169 		freeList (list);
1170 	else if (parameter [0] == '@')
1171 	{
1172 		stringList* const sl = stringListNewFromFile (fileName);
1173 		if (sl == NULL)
1174 			error (FATAL | PERROR, "cannot open \"%s\"", fileName);
1175 		if (*list == NULL)
1176 			*list = sl;
1177 		else
1178 			stringListCombine (*list, sl);
1179 		verbose ("    adding %s patterns from %s\n", optname, fileName);
1180 	}
1181 	else
1182 	{
1183 		vString *const item = vStringNewInit (parameter);
1184 #if defined (WIN32)
1185 		vStringTranslate(item, PATH_SEPARATOR, OUTPUT_PATH_SEPARATOR);
1186 #endif
1187 		if (*list == NULL)
1188 			*list = stringListNew ();
1189 		stringListAdd (*list, item);
1190 		verbose ("    adding %s pattern: %s\n", optname, parameter);
1191 	}
1192 }
1193 
processExcludeOption(const char * const option,const char * const parameter)1194 static void processExcludeOption (
1195 		const char *const option, const char *const parameter)
1196 {
1197 	processExcludeOptionCommon (&Excluded, option, parameter);
1198 }
1199 
processExcludeExceptionOption(const char * const option,const char * const parameter)1200 static void processExcludeExceptionOption (
1201 		const char *const option, const char *const parameter)
1202 {
1203 	processExcludeOptionCommon (&ExcludedException, option, parameter);
1204 }
1205 
isExcludedFile(const char * const name,bool falseIfExceptionsAreDefeind)1206 extern bool isExcludedFile (const char* const name,
1207 							bool falseIfExceptionsAreDefeind)
1208 {
1209 	const char* base = baseFilename (name);
1210 	bool result = false;
1211 
1212 	if (falseIfExceptionsAreDefeind
1213 		&& ExcludedException != NULL
1214 		&& stringListCount (ExcludedException) > 0)
1215 		return false;
1216 
1217 	if (Excluded != NULL)
1218 	{
1219 		result = stringListFileMatched (Excluded, base);
1220 		if (! result  &&  name != base)
1221 			result = stringListFileMatched (Excluded, name);
1222 	}
1223 
1224 	if (result && ExcludedException != NULL)
1225 	{
1226 		bool result_exception;
1227 
1228 		result_exception = stringListFileMatched (ExcludedException, base);
1229 		if (! result_exception && name != base)
1230 			result_exception = stringListFileMatched (ExcludedException, name);
1231 
1232 		if (result_exception)
1233 			result = false;
1234 	}
1235 	return result;
1236 }
1237 
processExcmdOption(const char * const option,const char * const parameter)1238 static void processExcmdOption (
1239 		const char *const option, const char *const parameter)
1240 {
1241 	switch (*parameter)
1242 	{
1243 		case 'm': Option.locate = EX_MIX;     break;
1244 		case 'n': Option.locate = EX_LINENUM; break;
1245 		case 'p': Option.locate = EX_PATTERN; break;
1246 		default:
1247 			if (strcmp(parameter, "combine") == 0)
1248 				Option.locate = EX_COMBINE;
1249 			else
1250 				error (FATAL, "Invalid value for \"%s\" option: %s", option, parameter);
1251 			break;
1252 	}
1253 }
1254 
resetXtags(langType lang,bool mode)1255 static void resetXtags (langType lang, bool mode)
1256 {
1257 	int i;
1258 	for (i = 0; i < countXtags (); i++)
1259 		if ((lang == LANG_AUTO) || (lang == getXtagOwner (i)))
1260 			enableXtag (i, mode);
1261 }
1262 
processExtraTagsOption(const char * const option,const char * const parameter)1263 static void processExtraTagsOption (
1264 		const char *const option, const char *const parameter)
1265 {
1266 	xtagType t;
1267 	const char *p = parameter;
1268 	bool mode = true;
1269 	int c;
1270 	static vString *longName;
1271 	bool inLongName = false;
1272 	const char *x;
1273 
1274 	if (strcmp (option, "extra") == 0)
1275 		error(WARNING, "--extra option is obsolete; use --extras instead");
1276 
1277 	if (*p == '*')
1278 	{
1279 		resetXtags (LANG_IGNORE, true);
1280 		p++;
1281 	}
1282 	else if (*p != '+'  &&  *p != '-')
1283 		resetXtags (LANG_IGNORE, false);
1284 
1285 	longName = vStringNewOrClearWithAutoRelease (longName);
1286 
1287 	while ((c = *p++) != '\0')
1288 	{
1289 		switch (c)
1290 		{
1291 		case '+':
1292 			if (inLongName)
1293 				vStringPut (longName, c);
1294 			else
1295 				mode = true;
1296 			break;
1297 		case '-':
1298 			if (inLongName)
1299 				vStringPut (longName, c);
1300 			else
1301 				mode = false;
1302 			break;
1303 		case '{':
1304 			if (inLongName)
1305 				error(FATAL,
1306 				      "unexpected character in extra specification: \'%c\'",
1307 				      c);
1308 			inLongName = true;
1309 			break;
1310 		case '}':
1311 			if (!inLongName)
1312 				error(FATAL,
1313 				      "unexpected character in extra specification: \'%c\'",
1314 				      c);
1315 			x = vStringValue (longName);
1316 			t = getXtagTypeForNameAndLanguage (x, LANG_IGNORE);
1317 
1318 			if (t == XTAG_UNKNOWN)
1319 				error(WARNING, "Unsupported parameter '{%s}' for \"%s\" option",
1320 				      x, option);
1321 			else
1322 				enableXtag (t, mode);
1323 
1324 			inLongName = false;
1325 			vStringClear (longName);
1326 			break;
1327 		default:
1328 			if (inLongName)
1329 				vStringPut (longName, c);
1330 			else
1331 			{
1332 				t = getXtagTypeForLetter (c);
1333 				if (t == XTAG_UNKNOWN)
1334 					error(WARNING, "Unsupported parameter '%c' for \"%s\" option",
1335 					      c, option);
1336 				else
1337 					enableXtag (t, mode);
1338 			}
1339 			break;
1340 		}
1341 	}
1342 }
1343 
resetFieldsOption(langType lang,bool mode)1344 static void resetFieldsOption (langType lang, bool mode)
1345 {
1346 	int i;
1347 
1348 	for (i = 0; i < countFields (); ++i)
1349 		if ((lang == LANG_AUTO) || (lang == getFieldOwner (i)))
1350 			enableField (i, mode);
1351 
1352 	if ((lang == LANG_AUTO || lang == LANG_IGNORE)&& !mode)
1353 		Option.fieldsReset = true;
1354 }
1355 
processFieldsOption(const char * const option,const char * const parameter)1356 static void processFieldsOption (
1357 		const char *const option, const char *const parameter)
1358 {
1359 	const char *p = parameter;
1360 	bool mode = true;
1361 	int c;
1362 	fieldType t;
1363 
1364 	static vString * longName;
1365 	bool inLongName = false;
1366 
1367 	longName = vStringNewOrClearWithAutoRelease (longName);
1368 
1369 	if (*p == '*')
1370 	{
1371 		resetFieldsOption (LANG_IGNORE, true);
1372 		p++;
1373 	}
1374 	else if (*p != '+'  &&  *p != '-')
1375 		resetFieldsOption (LANG_IGNORE, false);
1376 
1377 	while ((c = *p++) != '\0') switch (c)
1378 	{
1379 		case '+':
1380 			if (inLongName)
1381 				vStringPut (longName, c);
1382 			else
1383 				mode = true;
1384 			break;
1385 		case '-':
1386 			if (inLongName)
1387 				vStringPut (longName, c);
1388 			else
1389 				mode = false;
1390 			break;
1391 		case '{':
1392 			if (inLongName)
1393 				error(FATAL,
1394 				      "unexpected character in field specification: \'%c\'",
1395 				      c);
1396 			inLongName = true;
1397 			break;
1398 		case '}':
1399 			if (!inLongName)
1400 				error(FATAL,
1401 				      "unexpected character in field specification: \'%c\'",
1402 				      c);
1403 
1404 			{
1405 				const char *f = vStringValue (longName);
1406 				t = getFieldTypeForNameAndLanguage (f, LANG_IGNORE);
1407 			}
1408 
1409 			if (t == FIELD_UNKNOWN)
1410 				error(FATAL, "no such field: \'%s\'", vStringValue (longName));
1411 
1412 			enableField (t, mode);
1413 
1414 			inLongName = false;
1415 			vStringClear (longName);
1416 			break;
1417 		default :
1418 			if (inLongName)
1419 				vStringPut (longName, c);
1420 			else
1421 			{
1422 				t = getFieldTypeForOption (c);
1423 				if (t == FIELD_UNKNOWN)
1424 					error(WARNING, "Unsupported parameter '%c' for \"%s\" option",
1425 					      c, option);
1426 				else
1427 					enableField (t, mode);
1428 			}
1429 			break;
1430 	}
1431 }
1432 
processFilterTerminatorOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)1433 static void processFilterTerminatorOption (
1434 		const char *const option CTAGS_ATTR_UNUSED, const char *const parameter)
1435 {
1436 	freeString (&Option.filterTerminator);
1437 	Option.filterTerminator = stringCopy (parameter);
1438 }
1439 
processFormatOption(const char * const option,const char * const parameter)1440 static void processFormatOption (
1441 		const char *const option, const char *const parameter)
1442 {
1443 	unsigned int format;
1444 
1445 	if (sscanf (parameter, "%u", &format) < 1)
1446 		error (FATAL, "Invalid value for \"%s\" option",option);
1447 	else if (format <= (unsigned int) MaxSupportedTagFormat)
1448 		Option.tagFileFormat = format;
1449 	else
1450 		error (FATAL, "Unsupported value for \"%s\" option", option);
1451 }
1452 
1453 #ifdef HAVE_ICONV
processInputEncodingOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)1454 static void processInputEncodingOption(const char *const option CTAGS_ATTR_UNUSED,
1455 				const char *const parameter)
1456 {
1457 	if (Option.inputEncoding)
1458 		eFree (Option.inputEncoding);
1459 	else
1460 	{
1461 		if (!Option.outputEncoding)
1462 			Option.outputEncoding = eStrdup("UTF-8");
1463 	}
1464 	Option.inputEncoding = eStrdup(parameter);
1465 }
1466 
processOutputEncodingOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)1467 static void processOutputEncodingOption(const char *const option CTAGS_ATTR_UNUSED,
1468 				const char *const parameter)
1469 {
1470 	if (Option.outputEncoding)
1471 		eFree (Option.outputEncoding);
1472 	Option.outputEncoding = eStrdup(parameter);
1473 }
1474 #endif
1475 
printInvocationDescription(void)1476 static void printInvocationDescription (void)
1477 {
1478 	printf (INVOCATION, getExecutableName ());
1479 }
1480 
excludesCompare(struct colprintLine * a,struct colprintLine * b)1481 static int excludesCompare (struct colprintLine *a, struct colprintLine *b)
1482 {
1483 	return strcmp (colprintLineGetColumn (a, 0), colprintLineGetColumn (b, 0));
1484 }
1485 
processListExcludesOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)1486 static void processListExcludesOption(const char *const option CTAGS_ATTR_UNUSED,
1487 				      const char *const parameter CTAGS_ATTR_UNUSED)
1488 {
1489 	int i;
1490 	struct colprintTable *table = colprintTableNew ("L:NAME", NULL);
1491 
1492 	const int max = Excluded ? stringListCount (Excluded) : 0;
1493 
1494 	for (i = 0; i < max; ++i)
1495 	{
1496 		struct colprintLine * line = colprintTableGetNewLine (table);
1497 		colprintLineAppendColumnVString (line, stringListItem (Excluded, i));
1498 	}
1499 
1500 	colprintTableSort (table, excludesCompare);
1501 	colprintTablePrint (table, 0, localOption.withListHeader, localOption.machinable, stdout);
1502 	colprintTableDelete (table);
1503 
1504 	if (i == 0)
1505 		putchar ('\n');
1506 
1507 	exit (0);
1508 }
1509 
1510 
printFeatureList(void)1511 static void printFeatureList (void)
1512 {
1513 	int i;
1514 
1515 	for (i = 0 ; Features [i].name != NULL ; ++i)
1516 	{
1517 		if (i == 0)
1518 			printf ("  Optional compiled features: ");
1519 		if (strcmp (Features [i].name, "regex") != 0 || checkRegex ())
1520 			printf ("%s+%s", (i>0 ? ", " : ""), Features [i].name);
1521 #ifdef CUSTOM_CONFIGURATION_FILE
1522 		if (strcmp (Features [i].name, "custom-conf") == 0)
1523 			printf ("=%s", CUSTOM_CONFIGURATION_FILE);
1524 #endif
1525 	}
1526 	if (i > 0)
1527 		putchar ('\n');
1528 }
1529 
1530 
featureCompare(struct colprintLine * a,struct colprintLine * b)1531 static int featureCompare (struct colprintLine *a, struct colprintLine *b)
1532 {
1533 	return strcmp (colprintLineGetColumn (a, 0),
1534 				   colprintLineGetColumn (b, 0));
1535 }
1536 
processListFeaturesOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)1537 static void processListFeaturesOption(const char *const option CTAGS_ATTR_UNUSED,
1538 				      const char *const parameter CTAGS_ATTR_UNUSED)
1539 {
1540 	int i;
1541 
1542 	struct colprintTable *table = colprintTableNew ("L:NAME", "L:DESCRIPTION", NULL);
1543 
1544 	for (i = 0 ; Features [i].name != NULL ; ++i)
1545 	{
1546 		struct colprintLine * line = colprintTableGetNewLine (table);
1547 		if (strcmp (Features [i].name, "regex") != 0 || checkRegex ())
1548 		{
1549 			colprintLineAppendColumnCString (line, Features [i].name);
1550 			colprintLineAppendColumnCString (line, Features [i].description);
1551 		}
1552 
1553 	}
1554 
1555 	colprintTableSort (table, featureCompare);
1556 	colprintTablePrint (table, 0, localOption.withListHeader, localOption.machinable, stdout);
1557 	colprintTableDelete (table);
1558 
1559 	if (i == 0)
1560 		putchar ('\n');
1561 	exit (0);
1562 }
1563 
processListFieldsOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)1564 static void processListFieldsOption(const char *const option CTAGS_ATTR_UNUSED,
1565 				    const char *const parameter)
1566 {
1567 	/* Before listing, adjust states of enabled/disabled for fixed fields. */
1568 	writerCheckOptions (Option.fieldsReset);
1569 
1570 	struct colprintTable * table = fieldColprintTableNew ();
1571 
1572 	if (parameter [0] == '\0' || strcasecmp (parameter, RSV_LANG_ALL) == 0)
1573 	{
1574 		fieldColprintAddCommonLines (table);
1575 
1576 		initializeParser (LANG_AUTO);
1577 		for (unsigned int i = 0; i < countParsers (); i++)
1578 		{
1579 			if (isLanguageVisible(i))
1580 				fieldColprintAddLanguageLines (table, i);
1581 		}
1582 	}
1583 	else
1584 	{
1585 		langType language = getNamedLanguage (parameter, 0);
1586 		if (language == LANG_IGNORE)
1587 			error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
1588 
1589 		initializeParser (language);
1590 		fieldColprintAddLanguageLines (table, language);
1591 	}
1592 
1593 	fieldColprintTablePrint (table, localOption.withListHeader, localOption.machinable, stdout);
1594 	colprintTableDelete (table);
1595 	exit (0);
1596 }
1597 
printProgramIdentification(void)1598 static void printProgramIdentification (void)
1599 {
1600 	if ((ctags_repoinfo == NULL)
1601 	    || (strcmp (ctags_repoinfo, PROGRAM_VERSION) == 0))
1602 		printf ("%s %s, %s %s\n",
1603 			PROGRAM_NAME, PROGRAM_VERSION,
1604 			PROGRAM_COPYRIGHT, AUTHOR_NAME);
1605 	else
1606 		printf ("%s %s(%s), %s %s\n",
1607 			PROGRAM_NAME, PROGRAM_VERSION, ctags_repoinfo,
1608 			PROGRAM_COPYRIGHT, AUTHOR_NAME);
1609 	printf ("Universal Ctags is derived from Exuberant Ctags.\n");
1610 	printf ("Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert\n");
1611 
1612 	printf ("  Compiled: %s, %s\n", __DATE__, __TIME__);
1613 	printf ("  URL: %s\n", PROGRAM_URL);
1614 
1615 	printFeatureList ();
1616 }
1617 
processHelpOptionCommon(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED,bool includingExperimentalOptions)1618 static void processHelpOptionCommon (
1619 		const char *const option CTAGS_ATTR_UNUSED,
1620 		const char *const parameter CTAGS_ATTR_UNUSED,
1621 		bool includingExperimentalOptions)
1622 {
1623 	printProgramIdentification ();
1624 	putchar ('\n');
1625 	printInvocationDescription ();
1626 	putchar ('\n');
1627 
1628 	int i;
1629 	for (i = 0 ; LongOptionDescription [i].description != NULL ; ++i)
1630 	{
1631 		if ((! Option.etags || LongOptionDescription [i].usedByEtags)
1632 			&& (! LongOptionDescription [i].experimentalOption || includingExperimentalOptions))
1633 			puts (LongOptionDescription [i].description);
1634 	}
1635 }
1636 
processHelpOption(const char * const option,const char * const parameter)1637 static void processHelpOption (
1638 		const char *const option,
1639 		const char *const parameter)
1640 {
1641 	processHelpOptionCommon (option, parameter, false);
1642 	exit (0);
1643 }
1644 
processHelpFullOption(const char * const option,const char * const parameter)1645 static void processHelpFullOption (
1646 		const char *const option,
1647 		const char *const parameter)
1648 {
1649 	processHelpOptionCommon (option, parameter, true);
1650 	exit (0);
1651 }
1652 
1653 #ifdef HAVE_JANSSON
processInteractiveOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)1654 static void processInteractiveOption (
1655 		const char *const option CTAGS_ATTR_UNUSED,
1656 		const char *const parameter)
1657 {
1658 	static struct interactiveModeArgs args;
1659 
1660 
1661 	if (parameter && (strcmp (parameter, "sandbox") == 0))
1662 	{
1663 		Option.interactive = INTERACTIVE_SANDBOX;
1664 		args.sandbox = true;
1665 	}
1666 	else if (parameter && (strcmp (parameter, "default") == 0))
1667 	{
1668 		Option.interactive = INTERACTIVE_DEFAULT;
1669 		args.sandbox = false;
1670 	}
1671 	else if ((!parameter) || *parameter == '\0')
1672 	{
1673 		Option.interactive = INTERACTIVE_DEFAULT;
1674 		args.sandbox = false;
1675 	}
1676 	else
1677 		error (FATAL, "Unknown option argument \"%s\" for --%s option",
1678 			   parameter, option);
1679 
1680 #ifndef HAVE_SECCOMP
1681 	if (args.sandbox)
1682 		error (FATAL, "sandbox submode is not supported on this platform");
1683 #endif
1684 
1685 #ifdef ENABLE_GCOV
1686 	if (args.sandbox)
1687 		error (FATAL, "sandbox submode does not work if gcov is instrumented");
1688 #endif
1689 
1690 	Option.sorted = SO_UNSORTED;
1691 	setMainLoop (interactiveLoop, &args);
1692 	setErrorPrinter (jsonErrorPrinter, NULL);
1693 	setTagWriter (WRITER_JSON, NULL);
1694 	enablePtag (PTAG_JSON_OUTPUT_VERSION, true);
1695 
1696 	json_set_alloc_funcs (eMalloc, eFree);
1697 }
1698 #endif
1699 
processIf0Option(const char * const option,const char * const parameter)1700 static void processIf0Option (const char *const option,
1701 							  const char *const parameter)
1702 {
1703 	bool if0 = getBooleanOption (option, parameter);
1704 	langType lang = getNamedLanguage ("CPreProcessor", 0);
1705 	const char *arg = if0? "true": "false";
1706 
1707 	applyParameter (lang, "if0", arg);
1708 }
1709 
processLanguageForceOption(const char * const option,const char * const parameter)1710 static void processLanguageForceOption (
1711 		const char *const option, const char *const parameter)
1712 {
1713 	langType language;
1714 	if (strcasecmp (parameter, RSV_LANG_AUTO) == 0)
1715 		language = LANG_AUTO;
1716 	else
1717 		language = getNamedLanguage (parameter, 0);
1718 
1719 	if (strcmp (option, "lang") == 0  ||  strcmp (option, "language") == 0)
1720 		error (WARNING,
1721 			   "\"--%s\" option is obsolete; use \"--language-force\" instead",
1722 			   option);
1723 	if (language == LANG_IGNORE)
1724 		error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
1725 	else
1726 		Option.language = language;
1727 }
skipPastMap(char * p)1728 static char* skipPastMap (char* p)
1729 {
1730 	while (*p != EXTENSION_SEPARATOR  &&
1731 			*p != PATTERN_START  &&  *p != ','  &&  *p != '\0')
1732 		++p;
1733 	return p;
1734 }
1735 
1736 /* Parses the mapping beginning at `map', adds it to the language map, and
1737  * returns first character past the map.
1738  */
extractMapFromParameter(const langType language,char * parameter,char ** tail,bool * pattern_p,char * (* skip)(char *))1739 static char* extractMapFromParameter (const langType language,
1740 				      char* parameter,
1741 				      char** tail,
1742 				      bool* pattern_p,
1743 				      char* (* skip) (char *))
1744 {
1745 	char* p = NULL;
1746 	const char first = *parameter;
1747 	char  tmp;
1748 	char* result;
1749 
1750 	if (first == EXTENSION_SEPARATOR)  /* extension map */
1751 	{
1752 		*pattern_p = false;
1753 
1754 		++parameter;
1755 		p = (* skip) (parameter);
1756 		if (*p == '\0')
1757 		{
1758 			result = eStrdup (parameter);
1759 			*tail = parameter + strlen (parameter);
1760 			return result;
1761 		}
1762 		else
1763 		{
1764 			tmp = *p;
1765 			*p = '\0';
1766 			result = eStrdup (parameter);
1767 			*p = tmp;
1768 			*tail = p;
1769 			return result;
1770 		}
1771 	}
1772 	else if (first == PATTERN_START)  /* pattern map */
1773 	{
1774 		*pattern_p = true;
1775 
1776 		++parameter;
1777 		for (p = parameter  ;  *p != PATTERN_STOP  &&  *p != '\0'  ;  ++p)
1778 		{
1779 			if (*p == '\\'  &&  *(p + 1) == PATTERN_STOP)
1780 				++p;
1781 		}
1782 		if (*p == '\0')
1783 			error (FATAL, "Unterminated file name pattern for %s language",
1784 			   getLanguageName (language));
1785 		else
1786 		{
1787 			tmp = *p;
1788 			*p = '\0';
1789 			result = eStrdup (parameter);
1790 			*p = tmp;
1791 			*tail = p + 1;
1792 			return result;
1793 		}
1794 	}
1795 
1796 	return NULL;
1797 }
1798 
addLanguageMap(const langType language,char * map_parameter,bool exclusiveInAllLanguages)1799 static char* addLanguageMap (const langType language, char* map_parameter,
1800 			     bool exclusiveInAllLanguages)
1801 {
1802 	char* p = NULL;
1803 	bool pattern_p;
1804 	char* map;
1805 
1806 	map = extractMapFromParameter (language, map_parameter, &p, &pattern_p, skipPastMap);
1807 	if (map && pattern_p == false)
1808 		addLanguageExtensionMap (language, map, exclusiveInAllLanguages);
1809 	else if (map && pattern_p == true)
1810 		addLanguagePatternMap (language, map, exclusiveInAllLanguages);
1811 	else
1812 		error (FATAL, "Badly formed language map for %s language",
1813 				getLanguageName (language));
1814 
1815 	if (map)
1816 		eFree (map);
1817 	return p;
1818 }
1819 
removeLanguageMap(const langType language,char * map_parameter)1820 static char* removeLanguageMap (const langType language, char* map_parameter)
1821 {
1822 	char* p = NULL;
1823 	bool pattern_p;
1824 	char* map;
1825 
1826 	map = extractMapFromParameter (language, map_parameter, &p, &pattern_p, skipPastMap);
1827 	if (map && pattern_p == false)
1828 		removeLanguageExtensionMap (language, map);
1829 	else if (map && pattern_p == true)
1830 		removeLanguagePatternMap (language, map);
1831 	else
1832 		error (FATAL, "Badly formed language map for %s language",
1833 		       getLanguageName (language));
1834 
1835 	if (map)
1836 		eFree (map);
1837 	return p;
1838 }
1839 
processLanguageMap(char * map)1840 static char* processLanguageMap (char* map)
1841 {
1842 	char* const separator = strchr (map, ':');
1843 	char* result = NULL;
1844 	if (separator != NULL)
1845 	{
1846 		langType language;
1847 		char *list = separator + 1;
1848 		bool clear = false;
1849 		*separator = '\0';
1850 		language = getNamedLanguage (map, 0);
1851 		if (language != LANG_IGNORE)
1852 		{
1853 			const char *const deflt = RSV_LANGMAP_DEFAULT;
1854 			char* p;
1855 			if (*list == '+')
1856 				++list;
1857 			else
1858 				clear = true;
1859 			for (p = list  ;  *p != ','  &&  *p != '\0'  ;  ++p)  /*no-op*/ ;
1860 			if ((size_t) (p - list) == strlen (deflt) &&
1861 				strncasecmp (list, deflt, p - list) == 0)
1862 			{
1863 				verbose ("    Restoring default %s language map: ", getLanguageName (language));
1864 				installLanguageMapDefault (language);
1865 				list = p;
1866 			}
1867 			else
1868 			{
1869 				if (clear)
1870 				{
1871 					verbose ("    Setting %s language map:", getLanguageName (language));
1872 					clearLanguageMap (language);
1873 				}
1874 				else
1875 					verbose ("    Adding to %s language map:", getLanguageName (language));
1876 				while (list != NULL  &&  *list != '\0'  &&  *list != ',')
1877 					list = addLanguageMap (language, list, true);
1878 				verbose ("\n");
1879 			}
1880 			if (list != NULL  &&  *list == ',')
1881 				++list;
1882 			result = list;
1883 		}
1884 	}
1885 	return result;
1886 }
1887 
processLanguageMapOption(const char * const option,const char * const parameter)1888 static void processLanguageMapOption (
1889 		const char *const option, const char *const parameter)
1890 {
1891 	char *const maps = eStrdup (parameter);
1892 	char *map = maps;
1893 
1894 	if (strcmp (parameter, RSV_LANGMAP_DEFAULT) == 0)
1895 	{
1896 		verbose ("    Restoring default language maps:\n");
1897 		installLanguageMapDefaults ();
1898 	}
1899 	else while (map != NULL  &&  *map != '\0')
1900 	{
1901 		char* const next = processLanguageMap (map);
1902 		if (next == NULL)
1903 			error (WARNING, "Unknown language \"%s\" in \"%s\" option", parameter, option);
1904 		map = next;
1905 	}
1906 	eFree (maps);
1907 }
1908 
processLanguagesOption(const char * const option,const char * const parameter)1909 static void processLanguagesOption (
1910 		const char *const option, const char *const parameter)
1911 {
1912 	char *const langs = eStrdup (parameter);
1913 	enum { Add, Remove, Replace } mode = Replace;
1914 	bool first = true;
1915 	char *lang = langs;
1916 	const char* prefix = "";
1917 	verbose ("    Enabled languages: ");
1918 	while (lang != NULL)
1919 	{
1920 		char *const end = strchr (lang, ',');
1921 		if (lang [0] == '+')
1922 		{
1923 			++lang;
1924 			mode = Add;
1925 			prefix = "+ ";
1926 		}
1927 		else if (lang [0] == '-')
1928 		{
1929 			++lang;
1930 			mode = Remove;
1931 			prefix = "- ";
1932 		}
1933 		if (mode == Replace)
1934 			enableLanguages (false);
1935 		if (end != NULL)
1936 			*end = '\0';
1937 		if (lang [0] != '\0')
1938 		{
1939 			if (strcmp (lang, RSV_LANG_ALL) == 0)
1940 				enableLanguages ((bool) (mode != Remove));
1941 			else
1942 			{
1943 				const langType language = getNamedLanguage (lang, 0);
1944 				if (language == LANG_IGNORE)
1945 					error (WARNING, "Unknown language \"%s\" in \"%s\" option", lang, option);
1946 				else
1947 					enableLanguage (language, (bool) (mode != Remove));
1948 			}
1949 			verbose ("%s%s%s", (first ? "" : ", "), prefix, lang);
1950 			prefix = "";
1951 			first = false;
1952 			if (mode == Replace)
1953 				mode = Add;
1954 		}
1955 		lang = (end != NULL ? end + 1 : NULL);
1956 	}
1957 	verbose ("\n");
1958 	eFree (langs);
1959 }
1960 
processMapOption(const char * const option,const char * const parameter)1961 extern bool processMapOption (
1962 			const char *const option, const char *const parameter)
1963 {
1964 	langType language;
1965 	const char* spec;
1966 	char* map_parameter;
1967 	bool clear = false;
1968 	char op;
1969 
1970 	language = getLanguageComponentInOption (option, "map-");
1971 	if (language == LANG_IGNORE)
1972 		return false;
1973 
1974 	if (parameter == NULL || parameter [0] == '\0')
1975 		error (FATAL, "no parameter is given for %s", option);
1976 
1977 	spec = parameter;
1978 	if (*spec == '+' || *spec == '-')
1979 	{
1980 		op = *spec;
1981 		spec++;
1982 	}
1983 	else
1984 	{
1985 		op = '\0';
1986 		clear = true;
1987 	}
1988 
1989 	if (clear)
1990 	{
1991 		verbose ("    Setting %s language map:", getLanguageName (language));
1992 		clearLanguageMap (language);
1993 		op = '+';
1994 	}
1995 	else
1996 		verbose ("    %s %s %s %s language map:",
1997 			 op == '+'? "Adding": "Removing",
1998 			 spec,
1999 			 op == '+'? "to": "from",
2000 			 getLanguageName (language));
2001 	map_parameter = eStrdup (spec);
2002 
2003 	if (op == '+')
2004 		addLanguageMap (language, map_parameter, false);
2005 	else if (op == '-')
2006 		removeLanguageMap (language, map_parameter);
2007 	else
2008 		Assert ("Should not reach here" == NULL);
2009 
2010 	eFree (map_parameter);
2011 	verbose ("\n");
2012 
2013 	return true;
2014 }
2015 
processParamOption(const char * const option,const char * const value)2016 extern bool processParamOption (
2017 			const char *const option, const char *const value)
2018 {
2019 	langType language;
2020 	const char* name;
2021 	const char* sep;
2022 
2023 	language = getLanguageComponentInOption (option, "param-");
2024 	if (language == LANG_IGNORE)
2025 		return false;
2026 
2027 	sep = option + strlen ("param-") + strlen (getLanguageName (language));
2028 	/* `:' is only for keeping self compatibility */
2029 	if (! (*sep == '.' || *sep == ':' ))
2030 		error (FATAL, "no separator(.) is given for %s=%s", option, value);
2031 	name = sep + 1;
2032 
2033 	if (value == NULL || value [0] == '\0')
2034 		error (FATAL, "no value is given for %s", option);
2035 
2036 	applyParameter (language, name, value);
2037 
2038 	return true;
2039 }
2040 
processLicenseOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)2041 static void processLicenseOption (
2042 		const char *const option CTAGS_ATTR_UNUSED,
2043 		const char *const parameter CTAGS_ATTR_UNUSED)
2044 {
2045 	printProgramIdentification ();
2046 	puts ("");
2047 	puts (License1);
2048 	puts (License2);
2049 	exit (0);
2050 }
2051 
processListAliasesOption(const char * const option,const char * const parameter)2052 static void processListAliasesOption (
2053 		const char *const option, const char *const parameter)
2054 {
2055 	if (parameter [0] == '\0' || strcasecmp (parameter, RSV_LANG_ALL) == 0)
2056 		printLanguageAliases (LANG_AUTO,
2057 							  localOption.withListHeader, localOption.machinable, stdout);
2058 	else
2059 	{
2060 		langType language = getNamedLanguage (parameter, 0);
2061 		if (language == LANG_IGNORE)
2062 			error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
2063 		else
2064 			printLanguageAliases (language,
2065 								  localOption.withListHeader, localOption.machinable, stdout);
2066 	}
2067 	exit (0);
2068 }
2069 
processListExtrasOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)2070 static void processListExtrasOption (
2071 		const char *const option CTAGS_ATTR_UNUSED, const char *const parameter CTAGS_ATTR_UNUSED)
2072 {
2073 	struct colprintTable * table = xtagColprintTableNew ();
2074 
2075 	if (parameter [0] == '\0' || strcasecmp (parameter, RSV_LANG_ALL) == 0)
2076 	{
2077 		xtagColprintAddCommonLines (table);
2078 
2079 		initializeParser (LANG_AUTO);
2080 		for (unsigned int i = 0; i < countParsers (); i++)
2081 		{
2082 			if (isLanguageVisible(i))
2083 				xtagColprintAddLanguageLines (table, i);
2084 		}
2085 	}
2086 	else
2087 	{
2088 		langType language = getNamedLanguage (parameter, 0);
2089 		if (language == LANG_IGNORE)
2090 			error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
2091 
2092 		initializeParser (language);
2093 		xtagColprintAddLanguageLines (table, language);
2094 	}
2095 
2096 	xtagColprintTablePrint (table, localOption.withListHeader, localOption.machinable, stdout);
2097 	colprintTableDelete (table);
2098 	exit (0);
2099 }
2100 
processListKindsOption(const char * const option,const char * const parameter)2101 static void processListKindsOption (
2102 		const char *const option, const char *const parameter)
2103 {
2104 	bool print_all = (strcmp (option, "list-kinds-full") == 0)? true: false;
2105 
2106 	if (parameter [0] == '\0' || strcasecmp (parameter, RSV_LANG_ALL) == 0)
2107 		printLanguageKinds (LANG_AUTO, print_all,
2108 							localOption.withListHeader, localOption.machinable, stdout);
2109 	else
2110 	{
2111 		langType language = getNamedLanguage (parameter, 0);
2112 		if (language == LANG_IGNORE)
2113 			error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
2114 		else
2115 			printLanguageKinds (language, print_all,
2116 								localOption.withListHeader, localOption.machinable, stdout);
2117 	}
2118 	exit (0);
2119 }
2120 
processListParametersOption(const char * const option,const char * const parameter)2121 static void processListParametersOption (const char *const option,
2122 										 const char *const parameter)
2123 {
2124 	if (parameter [0] == '\0' || strcasecmp (parameter, RSV_LANG_ALL) == 0)
2125 		printLanguageParameters (LANG_AUTO,
2126 								 localOption.withListHeader, localOption.machinable,
2127 								 stdout);
2128 	else
2129 	{
2130 		langType language = getNamedLanguage (parameter, 0);
2131 		if (language == LANG_IGNORE)
2132 			error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
2133 		else
2134 			printLanguageParameters (language,
2135 									 localOption.withListHeader, localOption.machinable,
2136 									 stdout);
2137 	}
2138 	exit (0);
2139 }
2140 
2141 
processListMapsOptionForType(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter,langmapType type)2142 static void processListMapsOptionForType (const char *const option CTAGS_ATTR_UNUSED,
2143 					  const char *const  parameter,
2144 					  langmapType type)
2145 {
2146 	if (parameter [0] == '\0' || strcasecmp (parameter, RSV_LANG_ALL) == 0)
2147 		printLanguageMaps (LANG_AUTO, type,
2148 						   localOption.withListHeader, localOption.machinable,
2149 						   stdout);
2150 	else
2151 	{
2152 		langType language = getNamedLanguage (parameter, 0);
2153 		if (language == LANG_IGNORE)
2154 			error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
2155 		else
2156 			printLanguageMaps (language, type,
2157 							   localOption.withListHeader, localOption.machinable,
2158 							   stdout);
2159 	}
2160 	exit (0);
2161 }
2162 
processListMapExtensionsOption(const char * const option,const char * const parameter)2163 static void processListMapExtensionsOption (const char *const option,
2164 					 const char *const parameter)
2165 {
2166 	processListMapsOptionForType (option, parameter, LMAP_EXTENSION|LMAP_TABLE_OUTPUT);
2167 }
2168 
processListMapPatternsOption(const char * const option,const char * const parameter)2169 static void processListMapPatternsOption (const char *const option,
2170 				       const char *const parameter)
2171 {
2172 	processListMapsOptionForType (option, parameter, LMAP_PATTERN|LMAP_TABLE_OUTPUT);
2173 }
2174 
processListMapsOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)2175 static void processListMapsOption (
2176 		const char *const option CTAGS_ATTR_UNUSED,
2177 		const char *const parameter CTAGS_ATTR_UNUSED)
2178 {
2179 	processListMapsOptionForType (option, parameter, LMAP_ALL);
2180 }
2181 
processListLanguagesOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)2182 static void processListLanguagesOption (
2183 		const char *const option CTAGS_ATTR_UNUSED,
2184 		const char *const parameter CTAGS_ATTR_UNUSED)
2185 {
2186 	printLanguageList ();
2187 	exit (0);
2188 }
2189 
processListPseudoTagsOptions(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)2190 static void processListPseudoTagsOptions (
2191 		const char *const option CTAGS_ATTR_UNUSED,
2192 		const char *const parameter CTAGS_ATTR_UNUSED)
2193 {
2194 	printPtags (localOption.withListHeader, localOption.machinable, stdout);
2195 	exit (0);
2196 }
2197 
processListRegexFlagsOptions(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)2198 static void processListRegexFlagsOptions (
2199 		const char *const option CTAGS_ATTR_UNUSED,
2200 		const char *const parameter)
2201 {
2202 	printRegexFlags (localOption.withListHeader, localOption.machinable, parameter, stdout);
2203 	exit (0);
2204 }
2205 
processListMultilineRegexFlagsOptions(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)2206 static void processListMultilineRegexFlagsOptions (
2207 		const char *const option CTAGS_ATTR_UNUSED,
2208 		const char *const parameter)
2209 {
2210 	printMultilineRegexFlags (localOption.withListHeader, localOption.machinable, parameter, stdout);
2211 	exit (0);
2212 }
2213 
processListMultitableRegexFlagsOptions(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)2214 static void processListMultitableRegexFlagsOptions (
2215 		const char *const option CTAGS_ATTR_UNUSED,
2216 		const char *const parameter)
2217 {
2218 	printMultitableRegexFlags (localOption.withListHeader, localOption.machinable, parameter, stdout);
2219 	exit (0);
2220 }
2221 
processListLangdefFlagsOptions(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)2222 static void processListLangdefFlagsOptions (
2223 		const char *const option CTAGS_ATTR_UNUSED,
2224 		const char *const parameter CTAGS_ATTR_UNUSED)
2225 {
2226 	printLangdefFlags (localOption.withListHeader, localOption.machinable, stdout);
2227 	exit (0);
2228 }
2229 
processListKinddefFlagsOptions(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)2230 static void processListKinddefFlagsOptions (
2231 		const char *const option CTAGS_ATTR_UNUSED,
2232 		const char *const parameter CTAGS_ATTR_UNUSED)
2233 {
2234 	printKinddefFlags (localOption.withListHeader, localOption.machinable, stdout);
2235 	exit (0);
2236 }
2237 
processListRolesOptions(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)2238 static void processListRolesOptions (const char *const option CTAGS_ATTR_UNUSED,
2239 				     const char *const parameter)
2240 {
2241 	const char* sep;
2242 	const char *kindspecs;
2243 	langType lang;
2244 
2245 
2246 	if (parameter == NULL || parameter[0] == '\0')
2247 	{
2248 		printLanguageRoles (LANG_AUTO, "*",
2249 							localOption.withListHeader,
2250 							localOption.machinable,
2251 							stdout);
2252 		exit (0);
2253 	}
2254 
2255 	sep = strchr (parameter, '.');
2256 
2257 	if (sep == NULL || sep [1] == '\0')
2258 	{
2259 		vString* vstr = vStringNewInit (parameter);
2260 		vStringCatS (vstr, (sep? "*": ".*"));
2261 		processListRolesOptions (option, vStringValue (vstr));
2262 		/* The control should never reached here. */
2263 	}
2264 
2265 	kindspecs = sep + 1;
2266 	if (strncmp (parameter, "all.", 4) == 0
2267 		/* Handle the case if no language is specified.
2268 		 * This case is not documented. */
2269 	    || strncmp (parameter, ".", 1) == 0)
2270 		lang = LANG_AUTO;
2271 	else
2272 	{
2273 		lang = getNamedLanguage (parameter, sep - parameter);
2274 		if (lang == LANG_IGNORE)
2275 		{
2276 			const char *langName = eStrndup (parameter, sep - parameter);
2277 			error (FATAL, "Unknown language \"%s\" in \"%s\"", langName, option);
2278 		}
2279 	}
2280 	printLanguageRoles (lang, kindspecs,
2281 						localOption.withListHeader,
2282 						localOption.machinable,
2283 						stdout);
2284 	exit (0);
2285 }
2286 
processListSubparsersOptions(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)2287 static void processListSubparsersOptions (const char *const option CTAGS_ATTR_UNUSED,
2288 				     const char *const parameter)
2289 {
2290 	langType lang;
2291 
2292 
2293 	if (parameter == NULL || parameter[0] == '\0'
2294 		|| (strcmp(parameter, RSV_LANG_ALL) == 0))
2295 	{
2296 		printLanguageSubparsers(LANG_AUTO,
2297 								localOption.withListHeader, localOption.machinable,
2298 								stdout);
2299 		exit (0);
2300 	}
2301 
2302 	lang = getNamedLanguage (parameter, 0);
2303 	if (lang == LANG_IGNORE)
2304 		error (FATAL, "Unknown language \"%s\" in \"%s\"", parameter, option);
2305 
2306 	printLanguageSubparsers(lang,
2307 							localOption.withListHeader, localOption.machinable,
2308 							stdout);
2309 	exit (0);
2310 }
2311 
processListOperators(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)2312 static void processListOperators (const char *const option CTAGS_ATTR_UNUSED,
2313 								  const char *const parameter)
2314 {
2315 	listRegexOpscriptOperators (stdout);
2316 	exit (0);
2317 }
2318 
freeSearchPathList(searchPathList ** pathList)2319 static void freeSearchPathList (searchPathList** pathList)
2320 {
2321 	stringListClear (*pathList);
2322 	stringListDelete (*pathList);
2323 	*pathList = NULL;
2324 }
2325 
expandOnSearchPathList(searchPathList * pathList,const char * leaf,bool (* check)(const char * const))2326 static vString* expandOnSearchPathList (searchPathList *pathList, const char* leaf,
2327 					bool (* check) (const char *const))
2328 {
2329 	unsigned int i;
2330 
2331 	for (i = stringListCount (pathList); i > 0; --i)
2332 	{
2333 		const char* const body = vStringValue (stringListItem (pathList, i - 1));
2334 		char* tmp = combinePathAndFile (body, leaf);
2335 
2336 		if ((* check) (tmp))
2337 		{
2338 			vString *r = vStringNewOwn (tmp);
2339 			return r;
2340 		}
2341 		else
2342 			eFree (tmp);
2343 	}
2344 	return NULL;
2345 }
2346 
expandOnOptlibPathList(const char * leaf)2347 static vString* expandOnOptlibPathList (const char* leaf)
2348 {
2349 
2350 	return expandOnSearchPathList (OptlibPathList, leaf,
2351 								   doesFileExist);
2352 }
2353 
processOptionFileCommon(const char * const option,const char * const parameter,bool allowNonExistingFile)2354 static void processOptionFileCommon (
2355 	const char *const option, const char *const parameter,
2356 	bool allowNonExistingFile)
2357 {
2358 	const char* path;
2359 	vString* vpath = NULL;
2360 	fileStatus *status;
2361 
2362 	if (parameter [0] == '\0')
2363 		error (FATAL, "no option file supplied for \"%s\"", option);
2364 
2365 	if (parameter [0] != '/' && parameter [0] != '.')
2366 	{
2367 		vpath = expandOnOptlibPathList (parameter);
2368 		path = vpath? vStringValue (vpath): parameter;
2369 	}
2370 	else
2371 		path = parameter;
2372 
2373 	status = eStat (path);
2374 	if (!status->exists)
2375 	{
2376 		if (!allowNonExistingFile)
2377 			error (FATAL | PERROR, "cannot stat \"%s\"", path);
2378 	}
2379 	else if (status->isDirectory)
2380 	{
2381 		if (!parseAllConfigurationFilesOptionsInDirectory (path, NULL))
2382 			error (FATAL | PERROR, "cannot open option directory \"%s\"", path);
2383 	}
2384 	else
2385 	{
2386 		if (!parseFileOptions (path))
2387 			error (FATAL | PERROR, "cannot open option file \"%s\"", path);
2388 	}
2389 
2390 	eStatFree (status);
2391 	if (vpath)
2392 		vStringDelete (vpath);
2393 }
2394 
processOptionFile(const char * const option,const char * const parameter)2395 static void processOptionFile (
2396 	const char *const option, const char *const parameter)
2397 {
2398 	processOptionFileCommon(option, parameter, false);
2399 }
2400 
processOptionFileMaybe(const char * const option,const char * const parameter)2401 static void processOptionFileMaybe (
2402 	const char *const option, const char *const parameter)
2403 {
2404 	processOptionFileCommon(option, parameter, true);
2405 }
2406 
processOutputFormat(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)2407 static void processOutputFormat (const char *const option CTAGS_ATTR_UNUSED,
2408 				 const char *const parameter)
2409 {
2410 	if (parameter [0] == '\0')
2411 		error (FATAL, "no output format name supplied for \"%s\"", option);
2412 
2413 	if (strcmp (parameter, "u-ctags") == 0)
2414 		;
2415 	else if (strcmp (parameter, "e-ctags") == 0)
2416 		setTagWriter (WRITER_E_CTAGS, NULL);
2417 	else if (strcmp (parameter, "etags") == 0)
2418 		setEtagsMode ();
2419 	else if (strcmp (parameter, "xref") == 0)
2420 		setXrefMode ();
2421 #ifdef HAVE_JANSSON
2422 	else if (strcmp (parameter, "json") == 0)
2423 		setJsonMode ();
2424 #endif
2425 	else
2426 		error (FATAL, "unknown output format name supplied for \"%s=%s\"", option, parameter);
2427 }
2428 
processPseudoTags(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)2429 static void processPseudoTags (const char *const option CTAGS_ATTR_UNUSED,
2430 			       const char *const parameter)
2431 {
2432 	const char *p = parameter;
2433 	bool s = true;
2434 	ptagType t;
2435 	vString *str = vStringNew();
2436 
2437 	if (*p == '\0' || !strchr ("*+-", *p))
2438 	{
2439 		for (unsigned int i = 0; i < PTAG_COUNT; i++)
2440 			enablePtag (i, false);
2441 	}
2442 
2443 	while (1)
2444 	{
2445 		if (*p == '\0')
2446 			break;
2447 
2448 		if (*p == '*')
2449 		{
2450 			int i;
2451 			for (i = 0; i < PTAG_COUNT; i++)
2452 				enablePtag (i, true);
2453 			p++;
2454 			continue;
2455 		}
2456 		else if (*p == '-')
2457 		{
2458 			s= false;
2459 			p++;
2460 			continue;
2461 		}
2462 		else if (*p == '+')
2463 		{
2464 			s = true;
2465 			p++;
2466 			continue;
2467 		}
2468 		else if (*p == '{')
2469 		{
2470 			const char *origin = p;
2471 
2472 			p++;
2473 			while (*p != '\0' && *p != '}')
2474 			{
2475 				vStringPut (str, *p);
2476 				p++;
2477 			}
2478 			if (*p != '}')
2479 				error (FATAL, "curly bracket specifying a pseudo tags is unbalanced: %s",
2480 					   origin);
2481 			p++;
2482 		}
2483 		else
2484 		{
2485 			vStringCopyS (str, p);
2486 			p += vStringLength (str);
2487 		}
2488 
2489 		char *name = vStringValue (str);
2490 		t = getPtagTypeForName (name);
2491 		if (t == PTAG_UNKNOWN)
2492 			error (FATAL, "Unknown pseudo tag name: %s", name);
2493 		enablePtag (t, s);
2494 		vStringClear (str);
2495 	}
2496 	vStringDelete (str);
2497 }
2498 
processSortOption(const char * const option,const char * const parameter)2499 static void processSortOption (
2500 		const char *const option, const char *const parameter)
2501 {
2502 	if (isFalse (parameter))
2503 		Option.sorted = SO_UNSORTED;
2504 	else if (isTrue (parameter))
2505 		Option.sorted = SO_SORTED;
2506 	else if (strcasecmp (parameter, "f") == 0 ||
2507 			strcasecmp (parameter, "fold") == 0 ||
2508 			strcasecmp (parameter, "foldcase") == 0)
2509 		Option.sorted = SO_FOLDSORTED;
2510 	else
2511 		error (FATAL, "Invalid value for \"%s\" option", option);
2512 }
2513 
processTagRelative(const char * const option,const char * const parameter)2514 static void processTagRelative (
2515 		const char *const option, const char *const parameter)
2516 {
2517 	if (isFalse (parameter))
2518 		Option.tagRelative = TREL_NO;
2519 	else if (isTrue (parameter) || *parameter == '\0')
2520 		Option.tagRelative = TREL_YES;
2521 	else if (strcasecmp (parameter, "always") == 0)
2522 		Option.tagRelative = TREL_ALWAYS;
2523 	else if (strcasecmp (parameter, "never") == 0)
2524 		Option.tagRelative = TREL_NEVER;
2525 	else
2526 		error (FATAL, "Invalid value for \"%s\" option", option);
2527 }
2528 
processTotals(const char * const option,const char * const parameter)2529 static void processTotals (
2530 		const char *const option, const char *const parameter)
2531 {
2532 	if (isFalse (parameter))
2533 		Option.printTotals = 0;
2534 	else if (isTrue (parameter) || *parameter == '\0')
2535 		Option.printTotals = 1;
2536 	else if (strcasecmp (parameter, "extra") == 0)
2537 		Option.printTotals = 2;
2538 	else
2539 		error (FATAL, "Invalid value for \"%s\" option", option);
2540 }
2541 
installHeaderListDefaults(void)2542 static void installHeaderListDefaults (void)
2543 {
2544 	Option.headerExt = stringListNewFromArgv (HeaderExtensions);
2545 
2546 	BEGIN_VERBOSE(vfp);
2547 	{
2548 		fprintf (vfp, "    Setting default header extensions: ");
2549 		stringListPrint (Option.headerExt, vfp);
2550 		putc ('\n', vfp);
2551 	}
2552 	END_VERBOSE();
2553 }
2554 
processHeaderListOption(const int option,const char * parameter)2555 static void processHeaderListOption (const int option, const char *parameter)
2556 {
2557 	/*  Check to make sure that the user did not enter "ctags -h *.c"
2558 	 *  by testing to see if the list is a filename that exists.
2559 	 */
2560 	if (doesFileExist (parameter))
2561 		error (FATAL, "-%c: Invalid list", option);
2562 	if (strcmp (parameter, "default") == 0)
2563 		installHeaderListDefaults ();
2564 	else
2565 	{
2566 		bool clear = true;
2567 
2568 		if (parameter [0] == '+')
2569 		{
2570 			++parameter;
2571 			clear = false;
2572 		}
2573 		if (Option.headerExt == NULL)
2574 			Option.headerExt = stringListNew ();
2575 		verbose ("    Header Extensions:\n");
2576 		addExtensionList (Option.headerExt, parameter, clear);
2577 	}
2578 }
2579 
2580 /*
2581  *  Token ignore processing
2582  */
readIgnoreList(const char * const list)2583 static void readIgnoreList (const char *const list)
2584 {
2585 	langType lang = getNamedLanguage ("CPreProcessor", 0);
2586 	char* newList = stringCopy (list);
2587 	const char *token = strtok (newList, IGNORE_SEPARATORS);
2588 
2589 	while (token != NULL)
2590 	{
2591 		applyParameter (lang, "ignore", token);
2592 		token = strtok (NULL, IGNORE_SEPARATORS);
2593 	}
2594 	eFree (newList);
2595 }
2596 
addIgnoreListFromFile(const char * const fileName)2597 static void addIgnoreListFromFile (const char *const fileName)
2598 {
2599 	langType lang = getNamedLanguage ("CPreProcessor", 0);
2600 
2601 	stringList* tokens = stringListNewFromFile (fileName);
2602 	if (tokens == NULL)
2603 		error (FATAL | PERROR, "cannot open \"%s\"", fileName);
2604 
2605 	int c = stringListCount(tokens);
2606 	int i;
2607 
2608 	for(i=0;i<c;i++)
2609 	{
2610 		vString * s = stringListItem(tokens,i);
2611 		applyParameter (lang, "ignore", vStringValue(s));
2612 	}
2613 
2614 	stringListDelete(tokens);
2615 }
2616 
processIgnoreOption(const char * const list,int IgnoreOrDefine)2617 static void processIgnoreOption (const char *const list, int IgnoreOrDefine)
2618 {
2619 	langType lang = getNamedLanguage ("CPreProcessor", 0);
2620 
2621 	if (IgnoreOrDefine == 'D')
2622 		applyParameter (lang, "define", list);
2623 	else if (strchr ("@./\\", list [0]) != NULL)
2624 	{
2625 		const char* fileName = (*list == '@') ? list + 1 : list;
2626 		addIgnoreListFromFile (fileName);
2627 	}
2628 #if defined (WIN32)
2629 	else if (isalpha (list [0])  &&  list [1] == ':')
2630 		addIgnoreListFromFile (list);
2631 #endif
2632 	else if (strcmp (list, "-") == 0)
2633 		applyParameter (lang, "ignore", NULL);
2634 	else
2635 		readIgnoreList (list);
2636 }
2637 
processAnonHashOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)2638 static void processAnonHashOption (const char *const option CTAGS_ATTR_UNUSED, const char *const parameter CTAGS_ATTR_UNUSED)
2639 {
2640 	if (parameter == NULL || parameter[0] == '\0')
2641 		error (FATAL, "Something string is needed for \"%s\" option", option);
2642 	char buf [9];
2643 
2644 	anonHashString (parameter, buf);
2645 	printf("%s\n", buf);
2646 	exit (0);
2647 }
2648 
processDumpKeywordsOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)2649 static void processDumpKeywordsOption (const char *const option CTAGS_ATTR_UNUSED, const char *const parameter CTAGS_ATTR_UNUSED)
2650 {
2651 	dumpKeywordTable (stdout);
2652 }
2653 
processEchoOption(const char * const option,const char * const parameter)2654 static void processEchoOption (const char *const option, const char *const parameter)
2655 {
2656 	if (parameter == NULL || parameter[0] == '\0')
2657 		error (FATAL, "Something message is needed for \"%s\" option", option);
2658 	notice ("%s", parameter);
2659 }
2660 
processForceInitOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)2661 static void processForceInitOption (const char *const option CTAGS_ATTR_UNUSED,
2662 				    const char *const parameter CTAGS_ATTR_UNUSED)
2663 {
2664 	verbose ("force initializing all built-in parsers\n");
2665 	initializeParser (LANG_AUTO);
2666 }
2667 
processForceQuitOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)2668 static void processForceQuitOption (const char *const option CTAGS_ATTR_UNUSED,
2669 				    const char *const parameter)
2670 {
2671 	int s;
2672 	if (parameter == NULL || parameter[0] == '\0' || !strToInt(parameter, 0, &s))
2673 		s = 0;
2674 	exit (s);
2675 }
2676 
processVersionOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)2677 static void processVersionOption (
2678 		const char *const option CTAGS_ATTR_UNUSED,
2679 		const char *const parameter CTAGS_ATTR_UNUSED)
2680 {
2681 	printProgramIdentification ();
2682 	exit (0);
2683 }
2684 
2685 #ifdef DO_TRACING
processTraceOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)2686 static void processTraceOption(const char *const option CTAGS_ATTR_UNUSED,
2687 							   const char *const parameter)
2688 {
2689 	const char *langs = eStrdup (parameter);
2690 	const char *lang = langs;
2691 
2692 	traceMain();
2693 
2694 	while (lang != NULL)
2695 	{
2696 		if (*lang == '\0')
2697 			break;
2698 		if (*lang == ',')
2699 		{
2700 			lang++;
2701 			continue;
2702 		}
2703 
2704 		char *const end = strchr (lang, ',');
2705 
2706 		if (end != NULL)
2707 			*end = '\0';
2708 
2709 		const langType language = getNamedLanguage (lang, 0);
2710 		if (language == LANG_IGNORE)
2711 			error (WARNING, "Unknown language \"%s\" in \"%s\" option", lang, option);
2712 		else
2713 		{
2714 			traceLanguage (language);
2715 			verbose ("Enable tracing language: %s\n", lang);
2716 
2717 		}
2718 		lang = (end != NULL ? end + 1 : NULL);
2719 	}
2720 	eFree ((char *)langs);
2721 }
2722 #endif
2723 
processXformatOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter)2724 static void processXformatOption (const char *const option CTAGS_ATTR_UNUSED,
2725 				  const char *const parameter)
2726 {
2727 	if (Option.customXfmt)
2728 		fmtDelete (Option.customXfmt);
2729 
2730 	Option.customXfmt = fmtNew (parameter);
2731 }
2732 
prependToOptlibPathList(const char * const dir,bool report_in_verbose)2733 static void prependToOptlibPathList (const char *const dir, bool report_in_verbose)
2734 {
2735 	vString *elt = vStringNewInit (dir);
2736 
2737 	if (report_in_verbose)
2738 		verbose ("Prepend %s to %s\n",
2739 				 dir, "OptlibPathList");
2740 
2741 	stringListAdd (OptlibPathList, elt);
2742 }
2743 
resetOptlibPathList(bool report_in_verbose)2744 static void resetOptlibPathList (bool report_in_verbose)
2745 {
2746 	freeSearchPathList (&OptlibPathList);
2747 	if (report_in_verbose)
2748 		verbose ("Reset OptlibPathList\n");
2749 	OptlibPathList = stringListNew ();
2750 }
2751 
processOptlibDir(const char * const option,const char * const parameter)2752 static void processOptlibDir (
2753 		const char *const option, const char *const parameter)
2754 {
2755 	const char* path;
2756 
2757 	Assert (parameter);
2758 
2759 
2760 	if (parameter[0] == '\0')
2761 		resetOptlibPathList (true);
2762 	else if (parameter[0] == '+')
2763 	{
2764 		path = parameter + 1;
2765 		if (path[0] == '\0')
2766 			return;
2767 		prependToOptlibPathList (path, true);
2768 	}
2769 	else
2770 	{
2771 		resetOptlibPathList (true);
2772 		path = parameter;
2773 		prependToOptlibPathList (path, true);
2774 	}
2775 }
2776 
processMaxRecursionDepthOption(const char * const option,const char * const parameter)2777 static void processMaxRecursionDepthOption (const char *const option, const char *const parameter)
2778 {
2779 	if (parameter == NULL || parameter[0] == '\0')
2780 		error (FATAL, "A parameter is needed after \"%s\" option", option);
2781 
2782 	if (atol (parameter) < 1)
2783 		error (FATAL, "-%s: Invalid maximum recursion depth", option);
2784 
2785 	Option.maxRecursionDepth = atol(parameter);
2786 }
2787 
processPatternLengthLimit(const char * const option,const char * const parameter)2788 static void processPatternLengthLimit(const char *const option, const char *const parameter)
2789 {
2790 	if (parameter == NULL || parameter[0] == '\0')
2791 		error (FATAL, "A parameter is needed after \"%s\" option", option);
2792 
2793 	if (!strToUInt(parameter, 0, &Option.patternLengthLimit))
2794 		error (FATAL, "-%s: Invalid pattern length limit", option);
2795 }
2796 
ptagMakePatternLengthLimit(ptagDesc * pdesc,langType language CTAGS_ATTR_UNUSED,const void * data)2797 extern bool ptagMakePatternLengthLimit (ptagDesc *pdesc, langType language CTAGS_ATTR_UNUSED,
2798 										const void *data)
2799 {
2800 	const optionValues *opt = data;
2801 	char buf [21];
2802 
2803 	if (snprintf (buf, 21, "%u", opt->patternLengthLimit) >= 0)
2804 		return writePseudoTag (pdesc, buf, "0 for no limit", NULL);
2805 	return false;
2806 }
2807 
setBooleanToXtagWithWarning(booleanOption * const option,bool value)2808 static void setBooleanToXtagWithWarning(booleanOption *const option, bool value)
2809 {
2810 	/* WARNING/TODO: This function breaks capsulization. */
2811 
2812 	char x = 0;
2813 
2814 	if (strcmp (option->name, "file-tags") == 0)
2815 		x = 'f';
2816 	else if (strcmp (option->name, "file-scope") == 0)
2817 		x = 'F';
2818 
2819 	if (x)
2820 		error (WARNING, "\"--%s\" option is obsolete; use \"--extras=%c%c\" instead",
2821 			   option->name, value? '+': '-', x);
2822 
2823 	xtagType t = (xtagType)option->pValue;
2824 	enableXtag (t, value);
2825 }
2826 
2827 /*
2828  *  Option tables
2829  */
2830 
2831 static void processDumpOptionsOption (const char *const option, const char *const parameter);
2832 static void processDumpPreludeOption (const char *const option, const char *const parameter);
2833 
2834 static parametricOption ParametricOptions [] = {
2835 	{ "etags-include",          processEtagsInclude,            false,  STAGE_ANY },
2836 	{ "exclude",                processExcludeOption,           false,  STAGE_ANY },
2837 	{ "exclude-exception",      processExcludeExceptionOption,  false,  STAGE_ANY },
2838 	{ "excmd",                  processExcmdOption,             false,  STAGE_ANY },
2839 	{ "extra",                  processExtraTagsOption,         false,  STAGE_ANY },
2840 	{ "extras",                 processExtraTagsOption,         false,  STAGE_ANY },
2841 	{ "fields",                 processFieldsOption,            false,  STAGE_ANY },
2842 	{ "filter-terminator",      processFilterTerminatorOption,  true,   STAGE_ANY },
2843 	{ "format",                 processFormatOption,            true,   STAGE_ANY },
2844 	{ "help",                   processHelpOption,              true,   STAGE_ANY },
2845 	{ "help-full",              processHelpFullOption,          true,   STAGE_ANY },
2846 	{ "if0",                    processIf0Option,               false,  STAGE_ANY },
2847 #ifdef HAVE_ICONV
2848 	{ "input-encoding",         processInputEncodingOption,     false,  STAGE_ANY },
2849 	{ "output-encoding",        processOutputEncodingOption,    false,  STAGE_ANY },
2850 #endif
2851 	{ "lang",                   processLanguageForceOption,     false,  STAGE_ANY },
2852 	{ "language",               processLanguageForceOption,     false,  STAGE_ANY },
2853 	{ "language-force",         processLanguageForceOption,     false,  STAGE_ANY },
2854 	{ "languages",              processLanguagesOption,         false,  STAGE_ANY },
2855 	{ "langdef",                processLanguageDefineOption,    false,  STAGE_ANY },
2856 	{ "langmap",                processLanguageMapOption,       false,  STAGE_ANY },
2857 	{ "license",                processLicenseOption,           true,   STAGE_ANY },
2858 	{ "list-aliases",           processListAliasesOption,       true,   STAGE_ANY },
2859 	{ "list-excludes",          processListExcludesOption,      true,   STAGE_ANY },
2860 	{ "list-extras",            processListExtrasOption,        true,   STAGE_ANY },
2861 	{ "list-features",          processListFeaturesOption,      true,   STAGE_ANY },
2862 	{ "list-fields",            processListFieldsOption,        true,   STAGE_ANY },
2863 	{ "list-kinds",             processListKindsOption,         true,   STAGE_ANY },
2864 	{ "list-kinds-full",        processListKindsOption,         true,   STAGE_ANY },
2865 	{ "list-languages",         processListLanguagesOption,     true,   STAGE_ANY },
2866 	{ "list-maps",              processListMapsOption,          true,   STAGE_ANY },
2867 	{ "list-map-extensions",    processListMapExtensionsOption, true,   STAGE_ANY },
2868 	{ "list-map-patterns",      processListMapPatternsOption,   true,   STAGE_ANY },
2869 	{ "list-mline-regex-flags", processListMultilineRegexFlagsOptions, true, STAGE_ANY },
2870 	{ "list-params",            processListParametersOption,    true,   STAGE_ANY },
2871 	{ "list-pseudo-tags",       processListPseudoTagsOptions,   true,   STAGE_ANY },
2872 	{ "list-regex-flags",       processListRegexFlagsOptions,   true,   STAGE_ANY },
2873 	{ "list-roles",             processListRolesOptions,        true,   STAGE_ANY },
2874 	{ "list-subparsers",        processListSubparsersOptions,   true,   STAGE_ANY },
2875 	{ "maxdepth",               processMaxRecursionDepthOption, true,   STAGE_ANY },
2876 	{ "optlib-dir",             processOptlibDir,               false,  STAGE_ANY },
2877 	{ "options",                processOptionFile,              false,  STAGE_ANY },
2878 	{ "options-maybe",          processOptionFileMaybe,         false,  STAGE_ANY },
2879 	{ "output-format",          processOutputFormat,            true,   STAGE_ANY },
2880 	{ "pattern-length-limit",   processPatternLengthLimit,      true,   STAGE_ANY },
2881 	{ "pseudo-tags",            processPseudoTags,              false,  STAGE_ANY },
2882 	{ "sort",                   processSortOption,              true,   STAGE_ANY },
2883 	{ "tag-relative",           processTagRelative,             true,   STAGE_ANY },
2884 	{ "totals",                 processTotals,                  true,   STAGE_ANY },
2885 	{ "version",                processVersionOption,           true,   STAGE_ANY },
2886 	{ "_anonhash",              processAnonHashOption,          false,  STAGE_ANY },
2887 	{ "_dump-keywords",         processDumpKeywordsOption,      false,  STAGE_ANY },
2888 	{ "_dump-options",          processDumpOptionsOption,       false,  STAGE_ANY },
2889 	{ "_dump-prelude",          processDumpPreludeOption,       false,  STAGE_ANY },
2890 	{ "_echo",                  processEchoOption,              false,  STAGE_ANY },
2891 	{ "_force-initializing",    processForceInitOption,         false,  STAGE_ANY },
2892 	{ "_force-quit",            processForceQuitOption,         false,  STAGE_ANY },
2893 #ifdef HAVE_JANSSON
2894 	{ "_interactive",           processInteractiveOption,       true,   STAGE_ANY },
2895 #endif
2896 	{ "_list-kinddef-flags",    processListKinddefFlagsOptions, true,   STAGE_ANY },
2897 	{ "_list-langdef-flags",    processListLangdefFlagsOptions, true,   STAGE_ANY },
2898 	{ "_list-mtable-regex-flags", processListMultitableRegexFlagsOptions, true, STAGE_ANY },
2899 	{ "_list-operators",        processListOperators,           true,   STAGE_ANY },
2900 #ifdef DO_TRACING
2901 	{ "_trace",                 processTraceOption,             false,  STAGE_ANY },
2902 #endif
2903 	{ "_xformat",               processXformatOption,           false,  STAGE_ANY },
2904 };
2905 
2906 static booleanOption BooleanOptions [] = {
2907 	{ "append",         &Option.append,                 true,  STAGE_ANY },
2908 	{ "file-scope",     ((bool *)XTAG_FILE_SCOPE),      false, STAGE_ANY, setBooleanToXtagWithWarning },
2909 	{ "file-tags",      ((bool *)XTAG_FILE_NAMES),      false, STAGE_ANY, setBooleanToXtagWithWarning },
2910 	{ "filter",         &Option.filter,                 true,  STAGE_ANY },
2911 	{ "guess-language-eagerly", &Option.guessLanguageEagerly, false, STAGE_ANY },
2912 	{ "line-directives",&Option.lineDirectives,         false, STAGE_ANY },
2913 	{ "links",          &Option.followLinks,            false, STAGE_ANY },
2914 	{ "machinable",     &localOption.machinable,        true,  STAGE_ANY },
2915 	{ "put-field-prefix", &Option.putFieldPrefix,       false, STAGE_ANY },
2916 	{ "print-language", &Option.printLanguage,          true,  STAGE_ANY },
2917 	{ "quiet",          &Option.quiet,                  false, STAGE_ANY },
2918 #ifdef RECURSE_SUPPORTED
2919 	{ "recurse",        &Option.recurse,                false, STAGE_ANY },
2920 #endif
2921 	{ "verbose",        &ctags_verbose,                 false, STAGE_ANY },
2922 #ifdef WIN32
2923 	{ "use-slash-as-filename-separator", (bool *)&Option.useSlashAsFilenameSeparator, false, STAGE_ANY },
2924 #endif
2925 	{ "with-list-header", &localOption.withListHeader,  true,  STAGE_ANY },
2926 	{ "_fatal-warnings",&Option.fatalWarnings,          false, STAGE_ANY },
2927 };
2928 
2929 /*
2930  *  Generic option parsing
2931  */
2932 
checkOptionOrder(const char * const option,bool longOption)2933 static void checkOptionOrder (const char* const option, bool longOption)
2934 {
2935 	if (NonOptionEncountered)
2936 		error (FATAL, "-%s%s option may not follow a file name", longOption? "-": "", option);
2937 }
2938 
processParametricOption(const char * const option,const char * const parameter)2939 static bool processParametricOption (
2940 		const char *const option, const char *const parameter)
2941 {
2942 	const int count = sizeof (ParametricOptions) / sizeof (parametricOption);
2943 	bool found = false;
2944 	int i;
2945 
2946 	for (i = 0  ;  i < count  &&  ! found  ;  ++i)
2947 	{
2948 		parametricOption* const entry = &ParametricOptions [i];
2949 		if (strcmp (option, entry->name) == 0)
2950 		{
2951 			found = true;
2952 			if (!(entry->acceptableStages & (1UL << Stage)))
2953 			{
2954 				error (WARNING, "Cannot use --%s option in %s",
2955 				       option, StageDescription[Stage]);
2956 				break;
2957 			}
2958 			if (entry->initOnly)
2959 				checkOptionOrder (option, true);
2960 			(entry->handler) (option, parameter);
2961 		}
2962 	}
2963 	return found;
2964 }
2965 
getBooleanOption(const char * const option,const char * const parameter)2966 static bool getBooleanOption (
2967 		const char *const option, const char *const parameter)
2968 {
2969 	return paramParserBool (parameter, true, option, "option");
2970 }
2971 
processBooleanOption(const char * const option,const char * const parameter)2972 static bool processBooleanOption (
2973 		const char *const option, const char *const parameter)
2974 {
2975 	const int count = sizeof (BooleanOptions) / sizeof (booleanOption);
2976 	bool found = false;
2977 	int i;
2978 
2979 	for (i = 0  ;  i < count  &&  ! found  ;  ++i)
2980 	{
2981 		booleanOption* const entry = &BooleanOptions [i];
2982 		if (strcmp (option, entry->name) == 0)
2983 		{
2984 			found = true;
2985 			if (!(entry->acceptableStages & (1UL << Stage)))
2986 			{
2987 				error (WARNING, "Cannot use --%s option in %s",
2988 				       option, StageDescription[Stage]);
2989 				break;
2990 			}
2991 			if (entry->initOnly)
2992 				checkOptionOrder (option, true);
2993 
2994 			bool value = getBooleanOption (option, parameter);
2995 			if (entry->set)
2996 				entry->set (entry, value);
2997 			else
2998 				*entry->pValue = value;
2999 		}
3000 	}
3001 	return found;
3002 }
3003 
enableLanguageField(langType language,const char * field,bool mode)3004 static void enableLanguageField (langType language, const char *field, bool mode)
3005 {
3006 
3007 	fieldType t;
3008 
3009 	t = getFieldTypeForNameAndLanguage (field, language);
3010 	if (t == FIELD_UNKNOWN)
3011 		error(FATAL, "no such field: \'%s\'", field);
3012 	enableField (t, mode);
3013 	if (language == LANG_AUTO)
3014 	{
3015 		fieldType ftype_next = t;
3016 
3017 		while ((ftype_next = nextSiblingField (ftype_next)) != FIELD_UNKNOWN)
3018 			enableField (ftype_next, mode);
3019 	}
3020 }
3021 
enableLanguageXtag(langType language,const char * xtag,bool mode)3022 static void enableLanguageXtag (langType language, const char *xtag, bool mode)
3023 {
3024 
3025 	xtagType x;
3026 
3027 	x = getXtagTypeForNameAndLanguage (xtag, language);
3028 	if (x == XTAG_UNKNOWN)
3029 		error(FATAL, "no such extra: \'%s\'", xtag);
3030 	enableXtag (x, mode);
3031 	if (language == LANG_AUTO)
3032 	{
3033 		xtagType xtag_next = x;
3034 
3035 		while ((xtag_next = nextSiblingXtag (xtag_next)) != XTAG_UNKNOWN)
3036 			enableXtag (xtag_next, mode);
3037 	}
3038 }
3039 
processLangSpecificFieldsOption(const char * const option,const char * const parameter)3040 static bool processLangSpecificFieldsOption (const char *const option,
3041 						const char *const parameter)
3042 {
3043 #define PREFIX "fields-"
3044 #define PREFIX_LEN strlen(PREFIX)
3045 	const char* lang;
3046 	size_t len;
3047 	langType language = LANG_IGNORE;
3048 	const char *p = parameter;
3049 	int c;
3050 	static vString * longName;
3051 	bool mode = true;
3052 	const char *f;
3053 	bool inLongName = false;
3054 
3055 	if ( strncmp (option, PREFIX, PREFIX_LEN) != 0 )
3056 		return false;
3057 
3058 	lang = option + PREFIX_LEN;
3059 	len = strlen (lang);
3060 	if (len == 0)
3061 		error (FATAL, "No language given in \"%s\" option", option);
3062 	else if (len == strlen(RSV_LANG_ALL) && (strncmp(lang, RSV_LANG_ALL, len) == 0))
3063 		language = LANG_AUTO;
3064 	else
3065 		language = getNamedLanguage (lang, len);
3066 
3067 	if (language == LANG_IGNORE)
3068 	{
3069 		error (WARNING, "Unknown language: %s (ignoring \"--%s\")", lang, option);
3070 		/* The option is consumed in this function. */
3071 		return true;
3072 	}
3073 
3074 	initializeParser (language);
3075 
3076 	if (*p == '*')
3077 	{
3078 		resetFieldsOption (language, true);
3079 		p++;
3080 	}
3081 	else if (*p == '{' || *p == '\0')
3082 	{
3083 		resetFieldsOption (language, false);
3084 		if (*p == '\0')
3085 			return true;
3086 	}
3087 	else if (*p != '+' && *p != '-')
3088 		error (WARNING, "Wrong per language field specification: %s", p);
3089 
3090 	longName = vStringNewOrClearWithAutoRelease (longName);
3091 	while ((c = *p++) != '\0')
3092 	{
3093 		switch (c)
3094 		{
3095 		case '+':
3096 			if (inLongName)
3097 				vStringPut (longName, c);
3098 			else
3099 				mode = true;
3100 			break;
3101 		case '-':
3102 			if (inLongName)
3103 				vStringPut (longName, c);
3104 			else
3105 				mode = false;
3106 			break;
3107 		case '{':
3108 			if (inLongName)
3109 				error (FATAL,
3110 				       "unexpected character in field specification: \'%c\'",
3111 				       c);
3112 			inLongName = true;
3113 			break;
3114 		case '}':
3115 			if (!inLongName)
3116 				error (FATAL,
3117 				       "unexpected character in field specification: \'%c\'",
3118 				       c);
3119 
3120 			f = vStringValue (longName);
3121 			enableLanguageField (language, f, mode);
3122 			inLongName = false;
3123 			vStringClear (longName);
3124 			break;
3125 		default:
3126 			if (inLongName)
3127 				vStringPut (longName, c);
3128 			else
3129 				error (FATAL,
3130 				       "only long name can be used in per language field spec: \'%c\'",
3131 				       c);
3132 			break;
3133 		}
3134 	}
3135 #undef PREFIX_LEN
3136 #undef PREFIX
3137 	return true;
3138 }
3139 
processLangSpecificExtraOption(const char * const option,const char * const parameter)3140 static bool processLangSpecificExtraOption (const char *const option,
3141 						const char *const parameter)
3142 {
3143 #define PREFIX "extras-"
3144 #define PREFIX_LEN strlen(PREFIX)
3145 	const char* lang;
3146 	size_t len;
3147 	langType language = LANG_IGNORE;
3148 	const char *p = parameter;
3149 	int c;
3150 	static vString * longName;
3151 	bool mode = true;
3152 	const char *x;
3153 	bool inLongName = false;
3154 
3155 	if ( strncmp (option, PREFIX, PREFIX_LEN) != 0 )
3156 		return false;
3157 
3158 	lang = option + PREFIX_LEN;
3159 	len = strlen (lang);
3160 
3161 	if (len == 0)
3162 		error (FATAL, "No language given in \"%s\" option", option);
3163 	else if (len == strlen(RSV_LANG_ALL) && (strncmp(lang, RSV_LANG_ALL, len) == 0))
3164 		language = LANG_AUTO;
3165 	else
3166 		language = getNamedLanguage (lang, len);
3167 
3168 	if (language == LANG_IGNORE)
3169 	{
3170 		error (WARNING, "Unknown language: %s (ignoring \"--%s\")", lang, option);
3171 		/* The option is consumed in this function. */
3172 		return true;
3173 	}
3174 
3175 	initializeParser (language);
3176 
3177 	if (*p == '*')
3178 	{
3179 		resetXtags (language, true);
3180 		p++;
3181 	}
3182 	else if (*p == '{' || *p == '\0')
3183 	{
3184 		resetXtags (language, false);
3185 		if (*p == '\0')
3186 			return true;
3187 	}
3188 	else if (*p != '+' && *p != '-')
3189 		error (WARNING, "Wrong per language extra specification: %s", p);
3190 
3191 	longName = vStringNewOrClearWithAutoRelease (longName);
3192 	while ((c = *p++) != '\0')
3193 	{
3194 		switch (c)
3195 		{
3196 		case '+':
3197 			if (inLongName)
3198 				vStringPut (longName, c);
3199 			else
3200 				mode = true;
3201 			break;
3202 		case '-':
3203 			if (inLongName)
3204 				vStringPut (longName, c);
3205 			else
3206 				mode = false;
3207 			break;
3208 		case '{':
3209 			if (inLongName)
3210 				error (FATAL,
3211 				       "unexpected character in extra specification: \'%c\'",
3212 				       c);
3213 			inLongName = true;
3214 			break;
3215 		case '}':
3216 			if (!inLongName)
3217 				error (FATAL,
3218 				       "unexpected character in extra specification: \'%c\'",
3219 				       c);
3220 
3221 			x = vStringValue (longName);
3222 			enableLanguageXtag (language, x, mode);
3223 			inLongName = false;
3224 			vStringClear (longName);
3225 			break;
3226 		default:
3227 			if (inLongName)
3228 				vStringPut (longName, c);
3229 			else
3230 				error (FATAL,
3231 				       "only long name can be used in per language extra spec: \'%c\'",
3232 				       c);
3233 			break;
3234 		}
3235 	}
3236 	return true;
3237 }
3238 
processRegexOption(const char * const option,const char * const parameter)3239 static bool processRegexOption (const char *const option,
3240 								const char *const parameter)
3241 {
3242 	langType language;
3243 
3244 	language = getLanguageComponentInOption (option, "regex-");
3245 	if (language == LANG_IGNORE)
3246 		return false;
3247 
3248 	processLanguageRegexOption (language, REG_PARSER_SINGLE_LINE, parameter);
3249 
3250 	return true;
3251 }
3252 
processMultilineRegexOption(const char * const option,const char * const parameter)3253 static bool processMultilineRegexOption (const char *const option,
3254 										 const char *const parameter)
3255 {
3256 	langType language;
3257 
3258 	language = getLanguageComponentInOption (option, "mline-regex-");
3259 	if (language == LANG_IGNORE)
3260 		return false;
3261 
3262 	processLanguageRegexOption (language, REG_PARSER_MULTI_LINE, parameter);
3263 
3264 	return true;
3265 }
3266 
processMultitableRegexOption(const char * const option,const char * const parameter)3267 static bool processMultitableRegexOption (const char *const option,
3268 										  const char *const parameter)
3269 {
3270 	langType language;
3271 
3272 	language = getLanguageComponentInOption (option, "_mtable-regex-");
3273 	if (language == LANG_IGNORE)
3274 		return false;
3275 
3276 	processLanguageRegexOption (language, REG_PARSER_MULTI_TABLE, parameter);
3277 
3278 	return true;
3279 }
3280 
processMultitableExtendingOption(const char * const option,const char * const parameter)3281 static bool processMultitableExtendingOption (const char *const option,
3282 											  const char *const parameter)
3283 {
3284 	langType language;
3285 
3286 	language = getLanguageComponentInOption (option, "_mtable-extend-");
3287 	if (language == LANG_IGNORE)
3288 		return false;
3289 
3290 	processLanguageMultitableExtendingOption (language, parameter);
3291 
3292 	return true;
3293 }
3294 
processLongOption(const char * const option,const char * const parameter)3295 static void processLongOption (
3296 		const char *const option, const char *const parameter)
3297 {
3298 	Assert (parameter != NULL);
3299 	Assert (option != NULL);
3300 
3301 	if (parameter [0] == '\0')
3302 		verbose ("  Option: --%s\n", option);
3303 	else
3304 		verbose ("  Option: --%s=%s\n", option, parameter);
3305 
3306 	if (processBooleanOption (option, parameter))
3307 		;
3308 	else if (processLangSpecificFieldsOption(option, parameter))
3309 		 ;
3310 	else if (processExtradefOption(option, parameter))
3311 		;
3312 	else if (processFielddefOption(option, parameter))
3313 		;
3314 	else if (processLangSpecificExtraOption(option, parameter))
3315 		;
3316 	else if (processParametricOption (option, parameter))
3317 		;
3318 	else if (processKinddefOption (option, parameter))
3319 		;
3320 	else if (processKindsOption (option, parameter))
3321 		;
3322 	else if (processAliasOption (option, parameter))
3323 		;
3324 	else if (processRegexOption (option, parameter))
3325 		;
3326 	else if (processMultilineRegexOption (option, parameter))
3327 		;
3328 	else if (processMapOption (option, parameter))
3329 		;
3330 	else if (processParamOption (option, parameter))
3331 		;
3332 	else if (processTabledefOption (option, parameter))
3333 		;
3334 	else if (processMultitableRegexOption (option, parameter))
3335 		;
3336 	else if (processMultitableExtendingOption (option, parameter))
3337 		;
3338 #ifdef HAVE_ICONV
3339 	else if (processLanguageEncodingOption (option, parameter))
3340 		;
3341 #endif
3342 #ifndef RECURSE_SUPPORTED
3343 	else if (strcmp (option, "recurse") == 0)
3344 		error (WARNING, "%s option not supported on this host", option);
3345 #endif
3346 	else if (processRoledefOption (option, parameter))
3347 		;
3348 	else if (processScopesepOption (option, parameter))
3349 		;
3350 	else if (processPreludeOption (option, parameter))
3351 		;
3352 	else if (processSequelOption (option, parameter))
3353 		;
3354 	else if (processPretendOption (option, parameter))
3355 		;
3356 	else if (processRolesOption (option, parameter))
3357 		;
3358 	else if (option[0] == '_' && option[1] == '_')
3359 		/* ctags ignores an argument started from --__.
3360 		 * optlib2c may handle the argument. */
3361 		;
3362 	else
3363 		error (FATAL, "Unknown option: --%s", option);
3364 }
3365 
processShortOption(const char * const option,const char * const parameter)3366 static void processShortOption (
3367 		const char *const option, const char *const parameter)
3368 {
3369 	if (parameter == NULL  ||  parameter [0] == '\0')
3370 		verbose ("  Option: -%s\n", option);
3371 	else
3372 		verbose ("  Option: -%s %s\n", option, parameter);
3373 
3374 	if (isCompoundOption (*option) && (parameter == NULL  ||  parameter [0] == '\0'))
3375 		error (FATAL, "Missing parameter for \"%s\" option", option);
3376 	else switch (*option)
3377 	{
3378 		case '?':
3379 			processHelpOption ("?", NULL);
3380 			exit (0);
3381 			break;
3382 		case 'a':
3383 			checkOptionOrder (option, false);
3384 			Option.append = true;
3385 			break;
3386 #ifdef DEBUG
3387 		case 'b':
3388 			if (atol (parameter) < 0)
3389 				error (FATAL, "-%s: Invalid line number", option);
3390 			Option.breakLine = atol (parameter);
3391 			break;
3392 		case 'd':
3393 			if (!strToLong(parameter, 0, &ctags_debugLevel))
3394 				error (FATAL, "-%s: Invalid debug level", option);
3395 
3396 			if (debug (DEBUG_STATUS))
3397 				ctags_verbose = true;
3398 			break;
3399 #endif
3400 		case 'B':
3401 			Option.backward = true;
3402 			break;
3403 		case 'D':
3404 			processIgnoreOption (parameter, *option);
3405 			break;
3406 		case 'e':
3407 			checkOptionOrder (option, false);
3408 			setEtagsMode ();
3409 			break;
3410 		case 'f':
3411 		case 'o':
3412 			checkOptionOrder (option, false);
3413 			if (Option.tagFileName != NULL)
3414 			{
3415 				error (WARNING,
3416 					"-%s option specified more than once, last value used",
3417 					option);
3418 				freeString (&Option.tagFileName);
3419 			}
3420 			else if (parameter [0] == '-'  &&  parameter [1] != '\0')
3421 				error (FATAL, "output file name may not begin with a '-'");
3422 			Option.tagFileName = stringCopy (parameter);
3423 			break;
3424 		case 'F':
3425 			Option.backward = false;
3426 			break;
3427 		case 'G':
3428 			Option.guessLanguageEagerly = true;
3429 			break;
3430 		case 'h':
3431 			processHeaderListOption (*option, parameter);
3432 			break;
3433 		case 'I':
3434 			processIgnoreOption (parameter, *option);
3435 			break;
3436 		case 'L':
3437 			if (Option.fileList != NULL)
3438 			{
3439 				error (WARNING,
3440 					"-%s option specified more than once, last value used",
3441 					option);
3442 				freeString (&Option.fileList);
3443 			}
3444 			Option.fileList = stringCopy (parameter);
3445 			break;
3446 		case 'n':
3447 			Option.locate = EX_LINENUM;
3448 			break;
3449 		case 'N':
3450 			Option.locate = EX_PATTERN;
3451 			break;
3452 		case 'R':
3453 #ifdef RECURSE_SUPPORTED
3454 			Option.recurse = true;
3455 #else
3456 			error (WARNING, "-%s option not supported on this host", option);
3457 #endif
3458 			break;
3459 		case 'u':
3460 			checkOptionOrder (option, false);
3461 			Option.sorted = SO_UNSORTED;
3462 			break;
3463 		case 'V':
3464 			ctags_verbose = true;
3465 			break;
3466 		case 'w':
3467 			/* silently ignored */
3468 			break;
3469 		case 'x':
3470 			checkOptionOrder (option, false);
3471 			setXrefMode ();
3472 			break;
3473 		default:
3474 			error (FATAL, "Unknown option: -%s", option);
3475 			break;
3476 	}
3477 }
3478 
parseOption(cookedArgs * const args)3479 static void parseOption (cookedArgs* const args)
3480 {
3481 	Assert (! cArgOff (args));
3482 	if (args->isOption)
3483 	{
3484 		if (args->longOption)
3485 			processLongOption (args->item, args->parameter);
3486 		else
3487 		{
3488 			const char *parameter = args->parameter;
3489 			while (*parameter == ' ')
3490 				++parameter;
3491 			processShortOption (args->item, parameter);
3492 		}
3493 		cArgForth (args);
3494 	}
3495 }
3496 
parseOptions(cookedArgs * const args)3497 static void parseOptions (cookedArgs* const args)
3498 {
3499 	while (! cArgOff (args)  &&  cArgIsOption (args))
3500 		parseOption (args);
3501 	if (! cArgOff (args)  &&  ! cArgIsOption (args))
3502 		NonOptionEncountered = true;
3503 }
3504 
parseCmdlineOptions(cookedArgs * const args)3505 extern void parseCmdlineOptions (cookedArgs* const args)
3506 {
3507 	ENTER (Cmdline);
3508 	parseOptions (args);
3509 }
3510 
checkSameFile(const char * const fileName,void * userData)3511 static bool checkSameFile (const char *const fileName, void * userData)
3512 {
3513 	return isSameFile ((const char* const) userData, fileName);
3514 }
3515 
parseFileOptions(const char * const fileName)3516 static bool parseFileOptions (const char* const fileName)
3517 {
3518 	bool fileFound = false;
3519 	const char* const format = "Considering option file %s: %s\n";
3520 	if (stringListHasTest (OptionFiles, checkSameFile, (void *) fileName))
3521 	{
3522 		verbose (format, fileName, "already considered");
3523 		fileFound = true;
3524 	}
3525 	else
3526 	{
3527 		FILE* const fp = fopen (fileName, "r");
3528 		if (fp == NULL)
3529 			verbose (format, fileName, "not found");
3530 		else
3531 		{
3532 			cookedArgs* const args = cArgNewFromLineFile (fp);
3533 			vString* file = vStringNewInit (fileName);
3534 			stringListAdd (OptionFiles, file);
3535 			verbose (format, fileName, "reading...");
3536 			parseOptions (args);
3537 			if (NonOptionEncountered)
3538 				error (WARNING, "Ignoring non-option in %s\n", fileName);
3539 			cArgDelete (args);
3540 			fclose (fp);
3541 			fileFound = true;
3542 		}
3543 	}
3544 	return fileFound;
3545 }
3546 
3547 /* Actions to be taken before reading any other options */
previewFirstOption(cookedArgs * const args)3548 extern void previewFirstOption (cookedArgs* const args)
3549 {
3550 	while (cArgIsOption (args))
3551 	{
3552 		if (strcmp (args->item, "V") == 0
3553 		    || strcmp (args->item, "verbose") == 0
3554 		    || strcmp (args->item, "quiet") == 0)
3555 			parseOption (args);
3556 		else if (strcmp (args->item, "options") == 0  &&
3557 				strcmp (args->parameter, RSV_NONE) == 0)
3558 		{
3559 			notice ("No options will be read from files or environment");
3560 			SkipConfiguration = true;
3561 			cArgForth (args);
3562 		}
3563 		else
3564 			break;
3565 	}
3566 }
3567 
3568 #if defined (HAVE_DIRENT_H) || defined (_MSC_VER)
3569 extern int scanDirectory (const char *directory_name,
3570 						  struct dirent ***array_pointer, int (*select_function) (const struct dirent *),
3571 						  int (*compare_function) (const struct dirent **, const struct dirent **));
3572 
parseConfigurationFileOptionsInDirectoryWithLeafname(const char * directory,const char * leafname)3573 static void parseConfigurationFileOptionsInDirectoryWithLeafname (const char* directory, const char* leafname)
3574 {
3575 	char* pathname = combinePathAndFile (directory, leafname);
3576 	parseFileOptions (pathname);
3577 	eFree (pathname);
3578 }
3579 
ignore_dot_file(const struct dirent * dent)3580 static int ignore_dot_file(const struct dirent* dent)
3581 {
3582 	/* Ignore a file which name is started from dot. */
3583 	if (*dent->d_name == '.')
3584 		return 0;
3585 	else
3586 		return 1;
3587 }
3588 
accept_only_dot_ctags(const struct dirent * dent)3589 static int accept_only_dot_ctags(const struct dirent* dent)
3590 {
3591 	size_t len;
3592 
3593 	/* accept only a file ended with ".ctags" */
3594 	len = strlen(dent->d_name);
3595 
3596 	if (len < 7)
3597 		return 0;
3598 	if (strcmp(dent->d_name + (len - 6), ".ctags") == 0)
3599 		return 1;
3600 
3601 	return 0;
3602 }
3603 
alphaSort(const struct dirent ** a,const struct dirent ** b)3604 static int alphaSort(const struct dirent ** a,
3605 									  const struct dirent ** b)
3606 {
3607 	return strcmp ((*a)->d_name, (*b)->d_name);
3608 }
3609 
parseAllConfigurationFilesOptionsInDirectory(const char * const dirName,stringList * const already_loaded_files)3610 static bool parseAllConfigurationFilesOptionsInDirectory (const char* const dirName,
3611 							     stringList* const already_loaded_files)
3612 {
3613 	struct dirent **dents;
3614 	int i, n;
3615 
3616 	n = scanDirectory (dirName, &dents, ignore_dot_file, alphaSort);
3617 	if (n < 0)
3618 		return false;
3619 
3620 	for (i = 0; i < n; i++)
3621 	{
3622 		char* path;
3623 		fileStatus *s;
3624 
3625 		if (already_loaded_files && stringListHas (already_loaded_files, dents[i]->d_name))
3626 			continue;
3627 		else if (already_loaded_files)
3628 			stringListAdd (already_loaded_files, vStringNewInit (dents[i]->d_name));
3629 
3630 		path = combinePathAndFile (dirName, dents[i]->d_name);
3631 		s = eStat (path);
3632 
3633 		if (s->exists && accept_only_dot_ctags(dents[i]))
3634 			parseConfigurationFileOptionsInDirectoryWithLeafname (dirName,
3635 																  dents[i]->d_name);
3636 		eStatFree (s);
3637 		free (dents[i]);
3638 		eFree (path);
3639 	}
3640 	free (dents);
3641 	return true;
3642 }
3643 #else
parseAllConfigurationFilesOptionsInDirectory(const char * const dirName,stringList * const already_loaded_files)3644 static bool parseAllConfigurationFilesOptionsInDirectory (const char* const dirName,
3645 							     stringList* const already_loaded_files)
3646 {
3647 	return false;
3648 }
3649 #endif
3650 
3651 typedef char* (* preloadMakePathFunc) (const char*, const char*);
3652 
3653 struct preloadPathElt {
3654 	const char *path;			/* NULL means the end of list */
3655 	bool isDirectory;
3656 	preloadMakePathFunc makePath;
3657 	const char *extra;
3658 	OptionLoadingStage stage;
3659 };
3660 
prependEnvvar(const char * path,const char * envvar)3661 static char* prependEnvvar (const char *path, const char* envvar)
3662 {
3663 	char *full_path = NULL;
3664 
3665 	const char* const envval = getenv (envvar);
3666 	if (envval && strlen (envval))
3667 		full_path = combinePathAndFile(envval, path);
3668 
3669 	return full_path;
3670 }
3671 
3672 #ifndef WIN32
getConfigForXDG(const char * path CTAGS_ATTR_UNUSED,const char * extra CTAGS_ATTR_UNUSED)3673 static char *getConfigForXDG (const char *path CTAGS_ATTR_UNUSED,
3674 							  const char* extra CTAGS_ATTR_UNUSED)
3675 {
3676 	char *r = prependEnvvar ("ctags", "XDG_CONFIG_HOME");
3677 	if (r)
3678 		return r;
3679 
3680 	return prependEnvvar (".config/ctags", "HOME");
3681 }
3682 #endif
3683 
3684 #ifdef WIN32
getConfigAtHomeOnWindows(const char * path,const char * extra CTAGS_ATTR_UNUSED)3685 static char *getConfigAtHomeOnWindows (const char *path,
3686 									   const char* extra CTAGS_ATTR_UNUSED)
3687 {
3688 	/*
3689 	 * Windows users don't usually set HOME.
3690 	 * The OS sets HOMEDRIVE and HOMEPATH for them.
3691 	 */
3692 	const char* homeDrive = getenv ("HOMEDRIVE");
3693 	const char* homePath = getenv ("HOMEPATH");
3694 	if (homeDrive != NULL && homePath != NULL)
3695 	{
3696 		vString* const windowsHome = vStringNew ();
3697 		vStringCatS (windowsHome, homeDrive);
3698 		vStringCatS (windowsHome, homePath);
3699 
3700 		char *tmp = vStringIsEmpty (windowsHome)
3701 			? NULL
3702 			: combinePathAndFile (vStringValue(windowsHome), path);
3703 
3704 		vStringDelete (windowsHome);
3705 		return tmp;
3706 	}
3707 	return NULL;
3708 }
3709 #endif
3710 
preload(struct preloadPathElt * pathList)3711 static void preload (struct preloadPathElt *pathList)
3712 {
3713 	unsigned int i;
3714 	stringList* loaded;
3715 
3716 	loaded = stringListNew ();
3717 	for (i = 0; pathList[i].path != NULL || pathList[i].makePath != NULL; ++i)
3718 	{
3719 		struct preloadPathElt *elt = pathList + i;
3720 		preloadMakePathFunc maker = elt->makePath;
3721 		const char *path = elt->path;
3722 
3723 
3724 		if (maker)
3725 			path = maker(elt->path, elt->extra);
3726 
3727 		if (path == NULL)
3728 			continue;
3729 
3730 		Assert (Stage <= elt->stage);
3731 		if (Stage != elt->stage)
3732 		{
3733 			Stage = elt->stage;
3734 			verbose ("Entering configuration stage: loading %s\n", StageDescription[Stage]);
3735 		}
3736 
3737 		if (elt->isDirectory)
3738 			parseAllConfigurationFilesOptionsInDirectory (path, loaded);
3739 		else
3740 			parseFileOptions (path);
3741 
3742 		if (path != elt->path)
3743 			eFree ((void *)path);
3744 	}
3745 	stringListClear (loaded);
3746 	stringListDelete (loaded);
3747 }
3748 
3749 static struct preloadPathElt preload_path_list [] = {
3750 #ifdef CUSTOM_CONFIGURATION_FILE
3751 	{
3752 		.path = CUSTOM_CONFIGURATION_FILE,
3753 		.isDirectory = false,
3754 		.makePath = NULL,
3755 		.stage = OptionLoadingStageCustom,
3756 	},
3757 #endif
3758 #ifndef WIN32
3759 	{
3760 		.path = NULL,
3761 		.isDirectory = true,
3762 		.makePath = getConfigForXDG,
3763 		.stage = OptionLoadingStageXdg,
3764 	},
3765 #endif
3766 	{
3767 		.path = ".ctags.d",
3768 		.isDirectory = true,
3769 		.makePath = prependEnvvar,
3770 		.extra = "HOME",
3771 		.stage = OptionLoadingStageHomeRecursive,
3772 	},
3773 #ifdef WIN32
3774 	{
3775 		.path = "ctags.d",
3776 		.isDirectory = true,
3777 		.makePath = getConfigAtHomeOnWindows,
3778 		.extra = NULL,
3779 		.stage = OptionLoadingStageHomeRecursive,
3780 	},
3781 #endif
3782 	{
3783 		.path = ".ctags.d",
3784 		.isDirectory = true,
3785 		.makePath = NULL,
3786 		.stage = OptionLoadingStageCurrentRecursive,
3787 	},
3788 	{
3789 		.path = "ctags.d",
3790 		.isDirectory = true,
3791 		.makePath = NULL,
3792 		.stage = OptionLoadingStageCurrentRecursive,
3793 	},
3794 	{
3795 		.path = NULL,
3796 		.makePath = NULL,
3797 	},
3798 };
3799 
parseConfigurationFileOptions(void)3800 static void parseConfigurationFileOptions (void)
3801 {
3802 	preload (preload_path_list);
3803 }
3804 
readOptionConfiguration(void)3805 extern void readOptionConfiguration (void)
3806 {
3807 	if (! SkipConfiguration)
3808 		parseConfigurationFileOptions ();
3809 }
3810 
3811 /*
3812 *   Option initialization
3813 */
3814 
initOptions(void)3815 extern void initOptions (void)
3816 {
3817 	OptionFiles = stringListNew ();
3818 	OptlibPathList = stringListNew ();
3819 
3820 	verbose ("Setting option defaults\n");
3821 	installHeaderListDefaults ();
3822 	verbose ("  Installing default language mappings:\n");
3823 	installLanguageMapDefaults ();
3824 	verbose ("  Installing default language aliases:\n");
3825 	installLanguageAliasesDefaults ();
3826 
3827 	/* always excluded by default */
3828 	verbose ("  Installing default exclude patterns:\n");
3829 	processExcludeOption (NULL, "{arch}");
3830 	processExcludeOption (NULL, ".arch-ids");
3831 	processExcludeOption (NULL, ".arch-inventory");
3832 	processExcludeOption (NULL, "autom4te.cache");
3833 	processExcludeOption (NULL, "BitKeeper");
3834 	processExcludeOption (NULL, ".bzr");
3835 	processExcludeOption (NULL, ".bzrignore");
3836 	processExcludeOption (NULL, "CVS");
3837 	processExcludeOption (NULL, ".cvsignore");
3838 	processExcludeOption (NULL, "_darcs");
3839 	processExcludeOption (NULL, ".deps");
3840 	processExcludeOption (NULL, ".dvi");
3841 	processExcludeOption (NULL, ".DS_Store");
3842 	processExcludeOption (NULL, "EIFGEN");
3843 	processExcludeOption (NULL, ".git");
3844 	processExcludeOption (NULL, ".gitignore");
3845 	processExcludeOption (NULL, ".gitattributes");
3846 	processExcludeOption (NULL, ".hg");
3847 	processExcludeOption (NULL, ".hgignore");
3848 	processExcludeOption (NULL, "PENDING");
3849 	processExcludeOption (NULL, "RCS");
3850 	processExcludeOption (NULL, "RESYNC");
3851 	processExcludeOption (NULL, "SCCS");
3852 	processExcludeOption (NULL, ".svn");
3853 	processExcludeOption (NULL, "*~");
3854 	processExcludeOption (NULL, ".*.swp");
3855 
3856 	/* Exclude binary files
3857 	 * -----------------------------------------------
3858 	 *
3859 	 * TODO
3860 	 *
3861 	 * It will be interesting if ctags can extract
3862 	 * symbols from these binaries.
3863 	 *
3864 	 * --langdef=nm --regex-nm=...
3865 	 * --langdef=elf --pre-processor-elf=/bin/nm ...
3866 	 *
3867 	 * vim/emacs users never wants the cursor to jump to
3868 	 * a binary file but may wants to utilize the symbol
3869 	 * information for completion.
3870 	 *
3871 	 * https://bitbucket.org/haypo/hachoir3 can be
3872 	 * used the alternative for /bin/nm
3873 	 */
3874 	processExcludeOption (NULL, "*.o");
3875 	processExcludeOption (NULL, "*.a");
3876 	processExcludeOption (NULL, "*.so");
3877 
3878 	processExcludeOption (NULL, "*.obj");
3879 	processExcludeOption (NULL, "*.lib");
3880 	processExcludeOption (NULL, "*.dll");
3881 	processExcludeOption (NULL, "*.exe");
3882 
3883 	processExcludeOption (NULL, "*.gcno");
3884 	processExcludeOption (NULL, "*.gcda");
3885 
3886 	processExcludeOption (NULL, "*.class");
3887 
3888 	processExcludeOption (NULL, "*.pyc");
3889 	processExcludeOption (NULL, "*.pyo");
3890 }
3891 
freeOptionResources(void)3892 extern void freeOptionResources (void)
3893 {
3894 	freeString (&Option.tagFileName);
3895 	freeString (&Option.fileList);
3896 	freeString (&Option.filterTerminator);
3897 
3898 	freeList (&Excluded);
3899 	freeList (&ExcludedException);
3900 	freeList (&Option.headerExt);
3901 	freeList (&Option.etagsInclude);
3902 
3903 	freeSearchPathList (&OptlibPathList);
3904 
3905 	freeList (&OptionFiles);
3906 }
3907 
processDumpOptionsOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)3908 static void processDumpOptionsOption (const char *const option CTAGS_ATTR_UNUSED, const char *const parameter CTAGS_ATTR_UNUSED)
3909 {
3910 	fprintf(stdout, "# %s\n", "ParametricOptions");
3911 	for (unsigned int i = 0; i < ARRAY_SIZE(ParametricOptions); i++)
3912 		fprintf(stdout, "%s\n", ParametricOptions[i].name);
3913 
3914 	fprintf(stdout, "# %s\n", "BooleanOptions");
3915 	for (unsigned int i = 0; i < ARRAY_SIZE(BooleanOptions); i++)
3916 		fprintf(stdout, "%s\n", BooleanOptions[i].name);
3917 }
3918 
processDumpPreludeOption(const char * const option CTAGS_ATTR_UNUSED,const char * const parameter CTAGS_ATTR_UNUSED)3919 static void processDumpPreludeOption (const char *const option CTAGS_ATTR_UNUSED, const char *const parameter CTAGS_ATTR_UNUSED)
3920 {
3921 	extern const char ctagsCommonPrelude[];
3922 	fprintf(stdout, "%s\n", ctagsCommonPrelude);
3923 	exit (0);
3924 }
3925 
inSandbox(void)3926 extern bool inSandbox (void)
3927 {
3928 	return (Option.interactive == INTERACTIVE_SANDBOX);
3929 }
3930 
canUseLineNumberAsLocator(void)3931 extern bool canUseLineNumberAsLocator (void)
3932 {
3933 	return (Option.locate != EX_PATTERN);
3934 }
3935 
isDestinationStdout(void)3936 extern bool isDestinationStdout (void)
3937 {
3938 	bool toStdout = false;
3939 
3940 	if (Option.filter || Option.interactive ||
3941 		(Option.tagFileName != NULL  &&  (strcmp (Option.tagFileName, "-") == 0
3942 						  || strcmp (Option.tagFileName, "/dev/stdout") == 0
3943 		)))
3944 		toStdout = true;
3945 	else if (Option.tagFileName == NULL && NULL == outputDefaultFileName ())
3946 		toStdout = true;
3947 
3948 	return toStdout;
3949 }
3950