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