Page MenuHomeSoftware Heritage

options.c
No OneTemporary

options.c

/*
* Copyright (c) 1996-2003, Darren Hiebert
*
* This source code is released for free distribution under the terms of the
* GNU General Public License version 2 or (at your option) any later version.
*
* This module contains functions to process command line options.
*/
/*
* INCLUDE FILES
*/
#include "general.h" /* must always come first */
#define _GNU_SOURCE /* for asprintf */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h> /* to declare isspace () */
#if defined(HAVE_SCANDIR)
#include <dirent.h>
#endif
#include "ctags.h"
#include "debug.h"
#include "field.h"
#include "main.h"
#define OPTION_WRITE
#include "options.h"
#include "parse.h"
#include "ptag.h"
#include "routines.h"
#include "xtag.h"
/*
* MACROS
*/
#define INVOCATION "Usage: %s [options] [file(s)]\n"
#define CTAGS_DATA_PATH_ENVIRONMENT "CTAGS_DATA_PATH"
#define CTAGS_LIBEXEC_PATH_ENVIRONMENT "CTAGS_LIBEXEC_PATH"
#define CTAGS_ENVIRONMENT "CTAGS"
#define ETAGS_ENVIRONMENT "ETAGS"
#define CTAGS_FILE "tags"
#define ETAGS_FILE "TAGS"
#ifndef ETAGS
# define ETAGS "etags" /* name which causes default use of to -e */
#endif
/* The following separators are permitted for list options.
*/
#define EXTENSION_SEPARATOR '.'
#define PATTERN_START '('
#define PATTERN_STOP ')'
#define IGNORE_SEPARATORS ", \t\n"
#ifndef DEFAULT_FILE_FORMAT
# define DEFAULT_FILE_FORMAT 2
#endif
#if defined (HAVE_OPENDIR) || defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST)
# define RECURSE_SUPPORTED
#endif
#define isCompoundOption(c) (boolean) (strchr ("fohiILpDb", (c)) != NULL)
#define SUBDIR_OPTLIB "optlib"
#define SUBDIR_PRELOAD "preload"
#define SUBDIR_DRIVERS "drivers"
#define ENTER(STAGE) do { \
Assert (Stage <= OptionLoadingStage##STAGE); \
Stage = OptionLoadingStage##STAGE; \
} while (0)
#define ACCEPT(STAGE) (1UL << OptionLoadingStage##STAGE)
/*
* Data declarations
*/
enum eOptionLimits {
MaxHeaderExtensions = 100, /* maximum number of extensions in -h option */
MaxSupportedTagFormat = 2
};
typedef struct sOptionDescription {
int usedByEtags;
const char *description;
} optionDescription;
typedef void (*parametricOptionHandler) (const char *const option, const char *const parameter);
typedef const struct {
const char* name; /* name of option as specified by user */
parametricOptionHandler handler; /* routine to handle option */
boolean initOnly; /* option must be specified before any files */
unsigned long acceptableStages;
} parametricOption;
typedef const struct sBooleanOption {
const char* name; /* name of option as specified by user */
boolean* pValue; /* pointer to option value */
boolean initOnly; /* option must be specified before any files */
unsigned long acceptableStages;
boolean* (* redirect) (const struct sBooleanOption *const option);
} booleanOption;
/*
* DATA DEFINITIONS
*/
static boolean NonOptionEncountered = FALSE;
static stringList *OptionFiles;
typedef stringList searchPathList;
static searchPathList *OptlibPathList;
static searchPathList *PreloadPathList;
static searchPathList *DriversPathList;
static stringList* Excluded;
static boolean FilesRequired = TRUE;
static boolean SkipConfiguration;
static const char *const HeaderExtensions [] = {
"h", "H", "hh", "hpp", "hxx", "h++", "inc", "def", NULL
};
optionValues Option = {
NULL, /* -I */
FALSE, /* -a */
FALSE, /* -B */
FALSE, /* -e */
#ifdef MACROS_USE_PATTERNS
EX_PATTERN, /* -n, --excmd */
#else
EX_MIX, /* -n, --excmd */
#endif
FALSE, /* -R */
SO_SORTED, /* -u, --sort */
FALSE, /* -V */
FALSE, /* -x */
.customXfmt = NULL,
NULL, /* -L */
NULL, /* -o */
NULL, /* -h */
NULL, /* --config-filename */
NULL, /* --etags-include */
DEFAULT_FILE_FORMAT,/* --format */
#ifdef HAVE_ICONV
NULL, /* --input-encoding */
NULL, /* --output-encoding */
#endif
FALSE, /* --if0 */
LANG_AUTO, /* --lang */
TRUE, /* --links */
FALSE, /* --filter */
NULL, /* --filter-terminator */
FALSE, /* --tag-relative */
FALSE, /* --totals */
FALSE, /* --line-directives */
FALSE, /* --print-language */
FALSE, /* --guess-language-eagerly(-G) */
FALSE, /* --quiet */
FALSE, /* --_allow-xcmd-in-homedir */
FALSE, /* --_fatal-warnings */
.patternLengthLimit = 96,
.putFieldPrefix = FALSE,
.maxRecursionDepth = 0xffffffff,
#ifdef DEBUG
0, 0 /* -D, -b */
#endif
};
static OptionLoadingStage Stage = OptionLoadingStageNone;
#define STAGE_ANY ~0UL
/*
- Locally used only
*/
static optionDescription LongOptionDescription [] = {
{1," -a Append the tags to an existing tag file."},
#ifdef DEBUG
{1," -b <line>"},
{1," Set break line."},
#endif
{0," -B Use backward searching patterns (?...?)."},
#ifdef DEBUG
{1," -D <level>"},
{1," Set debug level."},
#endif
{0," -e Output tag file for use with Emacs."},
{1," -f <name>"},
{1," Write tags to specified file. Value of \"-\" writes tags to stdout"},
{1," [\"tags\"; or \"TAGS\" when -e supplied]."},
{0," -F Use forward searching patterns (/.../; default)."},
{1," -G Equivalent to --guess-language-eagerly."},
{1," -h <list>"},
{1," Specify list of file extensions to be treated as include files"},
{1," [\".h.H.hh.hpp.hxx.h++.inc.def\"]."},
{1," -I <list|@file>"},
{1," A list of tokens to be specially handled is read from either the"},
{1," command line or the specified file."},
{1," -L <file>"},
{1," A list of input file names is read from the specified file."},
{1," If specified as \"-\", then standard input is read."},
{0," -n Equivalent to --excmd=number."},
{0," -N Equivalent to --excmd=pattern."},
{1," -o Alternative for -f."},
#ifdef RECURSE_SUPPORTED
{1," -R Equivalent to --recurse."},
#else
{1," -R Not supported on this platform."},
#endif
{0," -u Equivalent to --sort=no."},
{1," -V Equivalent to --verbose."},
{1," -x Print a tabular cross reference file to standard output."},
{1," --alias-<LANG>=[+|-]aliasPattern"},
{1," Add a pattern detecting a name, can be used as an alternative name"},
{1," for LANG."},
{1," --append=[yes|no]"},
{1," Should tags should be appended to existing tag file [no]?"},
{1," --config-filename=fileName"},
{1," Use 'fileName' instead of 'ctags' in option file names."},
{1," --data-dir=[+]DIR"},
{1," Add or set DIR to data directory search path."},
{1," --etags-include=file"},
{1," Include reference to 'file' in Emacs-style tag file (requires -e)."},
{1," --exclude=pattern"},
{1," Exclude files and directories matching 'pattern'."},
{0," --excmd=number|pattern|mix"},
#ifdef MACROS_USE_PATTERNS
{0," Uses the specified type of EX command to locate tags [pattern]."},
#else
{0," Uses the specified type of EX command to locate tags [mix]."},
#endif
{1," --extra=[+|-]flags"},
{1," Include extra tag entries for selected information (flags: \"Ffq.\") [F]."},
{1," --fields=[+|-]flags"},
{1," Include selected extension fields (flags: \"afmikKlnsStzZ\") [fks]."},
{1," --file-scope=[yes|no]"},
{1," Should tags scoped only for a single file (e.g. \"static\" tags)"},
{1," be included in the output [yes]?"},
{1," --filter=[yes|no]"},
{1," Behave as a filter, reading file names from standard input and"},
{1," writing tags to standard output [no]."},
{1," --filter-terminator=string"},
{1," Specify string to print to stdout following the tags for each file"},
{1," parsed when --filter is enabled."},
{0," --format=level"},
#if DEFAULT_FILE_FORMAT == 1
{0," Force output of specified tag file format [1]."},
#else
{0," Force output of specified tag file format [2]."},
#endif
{1," --guess-language-eagerly"},
{1," Guess the language of input file more eagerly"},
{1," (but taking longer time for guessing):"},
{1," o shebang, even if the input file is not executable,"},
{1," o emacs mode specification at the beginning and end of input file, and"},
{1," o vim syntax specification at the end of input file."},
{1," --help"},
{1," Print this option summary."},
{1," --if0=[yes|no]"},
{1," Should code within #if 0 conditional branches be parsed [no]?"},
#ifdef HAVE_ICONV
{1," --input-encoding=encoding"},
{1," Specify encoding of all input files."},
{1," --input-encoding-<LANG>=encoding"},
{1," Specify encoding of the LANG input files."},
#endif
{1," --kinds-<LANG>=[+|-]kinds, or"},
{1," --<LANG>-kinds=[+|-]kinds"},
{1," Enable/disable tag kinds for language <LANG>."},
{1," --langdef=name"},
{1," Define a new language to be parsed with regular expressions."},
{1," --langmap=map(s)"},
{1," Override default mapping of language to input file extension."},
{1," --language-force=language"},
{1," Force all files to be interpreted using specified language."},
{1," --languages=[+|-]list"},
{1," Restrict files scanned for tags to those mapped to languages"},
{1," specified in the comma-separated 'list'. The list can contain any"},
{1," built-in or user-defined language [all]."},
{1," --libexec-dir=[+]DIR"},
{1," Add or set DIR to libexec directory search path."},
{1," --license"},
{1," Print details of software license."},
{0," --line-directives=[yes|no]"},
{0," Should #line directives be processed [no]?"},
{1," --links=[yes|no]"},
{1," Indicate whether symbolic links should be followed [yes]."},
{1," --list-aliases=[language|all]"},
{1," Output list of alias patterns."},
{1," --list-extensions=[language|all]"},
{1," Output list of language extensions in mapping."},
{1," --list-extra"},
{1," Output list of extra tag flags."},
{1," --list-features"},
{1," Output list of features."},
{1," --list-fields"},
{1," Output list of fields."},
{1," --list-file-kind"},
{1," List kind letter for file."},
{1," --list-kinds=[language|all]"},
{1," Output a list of all tag kinds for specified language or all."},
{1," --list-languages"},
{1," Output list of supported languages."},
{1," --list-maps=[language|all]"},
{1," Output list of language mappings(both extensions and patterns)."},
{1," --list-patterns=[language|all]"},
{1," Output list of language patterns in mapping."},
{0," --list-pseudo-tags"},
{0," Output list of pseudo tags."},
{1," --list-regex-flags"},
{1," Output list of flags which can be used in a regex parser definition."},
{1," --map-<LANG>=[+]pattern|extension"},
{1," Set or add(+) a map for <LANG>."},
{1," Unlike --langmap, only one pattern or one extension can be specified"},
{1," at once. Unlike, --langmap adding one affects the map of LANG; it does't"},
{1," affect maps of the other languages."},
{1," --maxdepth=N"},
#ifdef RECURSE_SUPPORTED
{1," Specify maximum recursion depth."},
#else
{1," Not supported on this platform."},
#endif
{1," --options=file"},
{1," Specify file from which command line options should be read."},
#ifdef HAVE_ICONV
{1," --output-encoding=encoding"},
{1," The encoding to write the tag file in. Defaults to UTF-8 if --input-encoding"},
{1," is specified, otherwise no conversion is performed."},
#endif
{0," --print-language"},
{0," Don't make tags file but just print the guessed language name for"},
{0," input file."},
{0," --pseudo-tags=[+|-]ptag"},
{0," --pseudo-tags=*"},
{0," Enable/disable emitting pseudo tag named ptag."},
{0," if * is given, enable emitting all pseudo tags."},
{0," --put-field-prefix"},
{0," Put \"" CTAGS_FIELD_PREFIX "\" as prefix for the name of fields newly introduced in"},
{0," universal-ctags."},
{1," --quiet=[yes|no]"},
{0," Don't print NOTICE class messages [no]."},
{1," --recurse=[yes|no]"},
#ifdef RECURSE_SUPPORTED
{1," Recurse into directories supplied on command line [no]."},
#else
{1," Not supported on this platform."},
#endif
{1," --regex-<LANG>=/line_pattern/name_pattern/[flags]"},
{1," Define regular expression for locating tags in specific language."},
{0," --sort=[yes|no|foldcase]"},
{0," Should tags be sorted (optionally ignoring case) [yes]?"},
{0," --tag-relative=[yes|no]"},
{0," Should paths be relative to location of tag file [no; yes when -e]?"},
{1," --totals=[yes|no]"},
{1," Print statistics about input and tag files [no]."},
{1," --verbose=[yes|no]"},
{1," Enable verbose messages describing actions on each input file."},
{1," --version"},
{1," Print version identifier to standard output."},
#ifdef HAVE_COPROC
{1," --xcmd-<LANG>=parser_command_path|parser_command_name"},
{1," Define external parser command path or name for specific language."},
#endif
{1," --_allow-xcmd-in-homedir"},
{1," Allow specifying --xcmd-<LANG> option in ~/.ctags and/or ~/.ctags/*."},
{1," By default it is not allowed. This option itself can be specified only"},
{1," in /etc or /usr/local/etc."},
{1," --_echo=msg"},
{1," Echo MSG to standard error. Useful to debug the chain"},
{1," of loading option files."},
{1," --_fatal-warnings"},
{1," Make all warnings fatal."},
{1," --_force-quit=[num]"},
{1," Quit when the option is processed. Useful to debug the chain"},
{1," of loading option files."},
{1," --_list-kinds-full=[language|all]"},
{1," Output a list of quadruplet of all tag kinds for specified language or"},
{1," all. The elements of a quadruplet are \"letter\", \"name\", \"description\""},
{1," and \"disabled\". Elements are separated with tab characters."},
{1," If a kind is disabled, its \"disabled\" element is printed as \"off\"."},
{1," If it is enabled, \"on\" is printed."},
{1," For each line, associated language name is printed when \"all\" is"},
{1," specified as language."},
{1," --_list-roles=[[language|all]:[kindletters|*]]"},
{1," Output list of all roles of tag kind(s) specified for language(s)."},
{1," e.g. --_list-roles=Make:I"},
{1," --_xformat=field_format"},
{1," Specify custom format for tabular cross reference (-x)."},
{1," Fields can be specified with letter listed in --list-fields."},
{1," e.g. --_xformat=%10N %10l:%K @ %-20F:%-20n"},
{1, NULL}
};
static const char* const License1 =
"This program is free software; you can redistribute it and/or\n"
"modify it under the terms of the GNU General Public License\n"
"as published by the Free Software Foundation; either version 2"
"of the License, or (at your option) any later version.\n"
"\n";
static const char* const License2 =
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License for more details.\n"
"\n"
"You should have received a copy of the GNU General Public License\n"
"along with this program; if not, write to the Free Software\n"
"Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n";
/* Contains a set of strings describing the set of "features" compiled into
* the code.
*/
static const char *const Features [] = {
#ifdef WIN32
"win32",
#endif
"wildcards", /* Always available on universal ctags */
"regex", /* Always available on universal ctags */
#ifndef EXTERNAL_SORT
"internal-sort",
#endif
#ifdef CUSTOM_CONFIGURATION_FILE
"custom-conf",
#endif
#if defined (WIN32) && defined (UNIX_PATH_SEPARATOR)
"unix-path-separator",
#endif
#ifdef HAVE_ICONV
"multibyte",
#endif
#ifdef DEBUG
"debug",
#endif
#ifdef HAVE_SCANDIR
"option-directory",
#endif
#ifdef HAVE_COPROC
"coproc",
#endif
#ifdef HAVE_LIBXML
"xpath",
#endif
NULL
};
static const char *const StageDescription [] = {
[OptionLoadingStageNone] = "not initialized",
[OptionLoadingStageCustom] = "custom file",
[OptionLoadingStageDosCnf] = "DOS .cnf file",
[OptionLoadingStageEtc] = "file under /etc (e.g. ctags.conf)",
[OptionLoadingStageLocalEtc] = "file under /usr/local/etc (e.g. ctags.conf)",
[OptionLoadingStageHomeRecursive] = "file(s) under HOME",
[OptionLoadingStageCurrentRecursive] = "file(s) under the current directory",
[OptionLoadingStagePreload] = "optlib preload files",
[OptionLoadingStageEnvVar] = "environment variable",
[OptionLoadingStageCmdline] = "command line",
};
/*
* FUNCTION PROTOTYPES
*/
static boolean parseFileOptions (const char *const fileName);
static boolean parseAllConfigurationFilesOptionsInDirectory (const char *const fileName,
stringList* const already_loaded_files);
/*
* FUNCTION DEFINITIONS
*/
static vString* getHome (void)
{
const char* const home = getenv ("HOME");
if (home)
return vStringNewInit (home);
else
{
#ifdef MSDOS_STYLE_PATH
/*
* Windows users don't usually set HOME.
* The OS sets HOMEDRIVE and HOMEPATH for them.
*/
const char* homeDrive = getenv ("HOMEDRIVE");
const char* homePath = getenv ("HOMEPATH");
if (homeDrive != NULL && homePath != NULL)
{
vString* const windowsHome = vStringNew ();
vStringCatS (windowsHome, homeDrive);
vStringCatS (windowsHome, homePath);
return windowsHome;
}
#endif
return NULL;
}
}
#if defined(_WIN32) && !(defined(__USE_MINGW_ANSI_STDIO) && defined(__MINGW64_VERSION_MAJOR))
/* Some versions of MinGW are missing _vscprintf's declaration, although they
* still provide the symbol in the import library.
*/
#ifdef __MINGW32__
_CRTIMP int _vscprintf(const char *format, va_list argptr);
#endif
#ifndef va_copy
#define va_copy(dest, src) (dest = src)
#endif
int asprintf(char **strp, const char *fmt, ...)
{
va_list args;
va_list args_copy;
int length;
size_t size;
va_start(args, fmt);
va_copy(args_copy, args);
#ifdef _WIN32
/* We need to use _vscprintf to calculate the length as vsnprintf returns -1
* if the number of characters to write is greater than count.
*/
length = _vscprintf(fmt, args_copy);
#else
char dummy;
length = vsnprintf(&dummy, sizeof dummy, fmt, args_copy);
#endif
va_end(args_copy);
Assert(length >= 0);
size = length + 1;
*strp = malloc(size);
if (!*strp) {
return -1;
}
va_start(args, fmt);
vsnprintf(*strp, size, fmt, args);
va_end(args);
return length;
}
#endif
extern void verbose (const char *const format, ...)
{
if (Option.verbose)
{
va_list ap;
va_start (ap, format);
vfprintf (stderr, format, ap);
va_end (ap);
}
}
extern void notice (const char *const format, ...)
{
if (!Option.quiet)
{
va_list ap;
fprintf (stderr, "%s: Notice: ", getExecutableName ());
va_start (ap, format);
vfprintf (stderr, format, ap);
va_end (ap);
fputs ("\n", stderr);
}
}
static char *stringCopy (const char *const string)
{
char* result = NULL;
if (string != NULL)
result = eStrdup (string);
return result;
}
static void freeString (char **const pString)
{
if (*pString != NULL)
{
eFree (*pString);
*pString = NULL;
}
}
extern void freeList (stringList** const pList)
{
if (*pList != NULL)
{
stringListDelete (*pList);
*pList = NULL;
}
}
extern void setDefaultTagFileName (void)
{
if (Option.tagFileName != NULL)
; /* accept given name */
else if (Option.etags)
Option.tagFileName = stringCopy (ETAGS_FILE);
else
Option.tagFileName = stringCopy (CTAGS_FILE);
}
extern boolean filesRequired (void)
{
boolean result = FilesRequired;
if (Option.recurse)
result = FALSE;
return result;
}
extern void checkOptions (void)
{
const char* notice;
if (Option.xref && (Option.customXfmt == NULL))
{
notice = "xref output";
if (isXtagEnabled(XTAG_FILE_NAMES) || isXtagEnabled(XTAG_FILE_NAMES_WITH_TOTAL_LINES))
{
error (WARNING, "%s disables file name tags", notice);
enableXtag (XTAG_FILE_NAMES, FALSE);
enableXtag (XTAG_FILE_NAMES_WITH_TOTAL_LINES, FALSE);
}
}
if (Option.append)
{
notice = "append mode is not compatible with";
if (isDestinationStdout ())
error (FATAL, "%s tags to stdout", notice);
}
if (Option.filter)
{
notice = "filter mode";
if (Option.printTotals)
{
error (WARNING, "%s disables totals", notice);
Option.printTotals = FALSE;
}
if (Option.tagFileName != NULL)
error (WARNING, "%s ignores output tag file name", notice);
}
}
extern langType getLanguageComponentInOption (const char *const option,
const char *const prefix)
{
size_t len;
langType language;
const char *lang;
Assert (prefix && prefix[0]);
Assert (option);
len = strlen (prefix);
if (strncmp (option, prefix, len) != 0)
return LANG_IGNORE;
else
{
lang = option + len;
if (lang [0] == '\0')
return LANG_IGNORE;
}
language = getNamedLanguage (lang, 0);
if (language == LANG_IGNORE)
error (FATAL, "Unknown language \"%s\" in \"%s\" option", lang, option);
return language;
}
static void setEtagsMode (void)
{
Option.etags = TRUE;
Option.sorted = SO_UNSORTED;
Option.lineDirectives = FALSE;
Option.tagRelative = TRUE;
}
extern void testEtagsInvocation (void)
{
char* const execName = eStrdup (getExecutableName ());
char* const etags = eStrdup (ETAGS);
#ifdef CASE_INSENSITIVE_FILENAMES
toLowerString (execName);
toLowerString (etags);
#endif
if (strstr (execName, etags) != NULL)
{
verbose ("Running in etags mode\n");
setEtagsMode ();
}
eFree (execName);
eFree (etags);
}
/*
* Cooked argument parsing
*/
static void parseShortOption (cookedArgs *const args)
{
args->simple [0] = *args->shortOptions++;
args->simple [1] = '\0';
args->item = eStrdup (args->simple);
if (! isCompoundOption (*args->simple))
args->parameter = "";
else if (*args->shortOptions == '\0')
{
argForth (args->args);
if (argOff (args->args))
args->parameter = NULL;
else
args->parameter = argItem (args->args);
args->shortOptions = NULL;
}
else
{
args->parameter = args->shortOptions;
args->shortOptions = NULL;
}
}
static void parseLongOption (cookedArgs *const args, const char *item)
{
const char* const equal = strchr (item, '=');
if (equal == NULL)
{
args->item = eStrdup (item);
args->parameter = "";
}
else
{
args->item = eStrndup (item, equal - item);
args->parameter = equal + 1;
}
Assert (args->item != NULL);
Assert (args->parameter != NULL);
}
static void cArgRead (cookedArgs *const current)
{
char* item;
Assert (current != NULL);
if (! argOff (current->args))
{
item = argItem (current->args);
current->shortOptions = NULL;
Assert (item != NULL);
if (strncmp (item, "--", (size_t) 2) == 0)
{
current->isOption = TRUE;
current->longOption = TRUE;
parseLongOption (current, item + 2);
Assert (current->item != NULL);
Assert (current->parameter != NULL);
}
else if (*item == '-')
{
current->isOption = TRUE;
current->longOption = FALSE;
current->shortOptions = item + 1;
parseShortOption (current);
}
else
{
current->isOption = FALSE;
current->longOption = FALSE;
current->item = eStrdup (item);
current->parameter = NULL;
}
}
}
extern cookedArgs* cArgNewFromString (const char* string)
{
cookedArgs* const result = xMalloc (1, cookedArgs);
memset (result, 0, sizeof (cookedArgs));
result->args = argNewFromString (string);
cArgRead (result);
return result;
}
extern cookedArgs* cArgNewFromArgv (char* const* const argv)
{
cookedArgs* const result = xMalloc (1, cookedArgs);
memset (result, 0, sizeof (cookedArgs));
result->args = argNewFromArgv (argv);
cArgRead (result);
return result;
}
extern cookedArgs* cArgNewFromFile (FILE* const fp)
{
cookedArgs* const result = xMalloc (1, cookedArgs);
memset (result, 0, sizeof (cookedArgs));
result->args = argNewFromFile (fp);
cArgRead (result);
return result;
}
extern cookedArgs* cArgNewFromLineFile (FILE* const fp)
{
cookedArgs* const result = xMalloc (1, cookedArgs);
memset (result, 0, sizeof (cookedArgs));
result->args = argNewFromLineFile (fp);
cArgRead (result);
return result;
}
extern void cArgDelete (cookedArgs* const current)
{
Assert (current != NULL);
argDelete (current->args);
if (current->item != NULL)
eFree (current->item);
memset (current, 0, sizeof (cookedArgs));
eFree (current);
}
static boolean cArgOptionPending (cookedArgs* const current)
{
boolean result = FALSE;
if (current->shortOptions != NULL)
if (*current->shortOptions != '\0')
result = TRUE;
return result;
}
extern boolean cArgOff (cookedArgs* const current)
{
Assert (current != NULL);
return (boolean) (argOff (current->args) && ! cArgOptionPending (current));
}
extern boolean cArgIsOption (cookedArgs* const current)
{
Assert (current != NULL);
return current->isOption;
}
extern const char* cArgItem (cookedArgs* const current)
{
Assert (current != NULL);
return current->item;
}
extern void cArgForth (cookedArgs* const current)
{
Assert (current != NULL);
Assert (! cArgOff (current));
if (current->item != NULL)
eFree (current->item);
if (cArgOptionPending (current))
parseShortOption (current);
else
{
Assert (! argOff (current->args));
argForth (current->args);
if (! argOff (current->args))
cArgRead (current);
else
{
current->isOption = FALSE;
current->longOption = FALSE;
current->shortOptions = NULL;
current->item = NULL;
current->parameter = NULL;
}
}
}
/*
* File extension and language mapping
*/
static void addExtensionList (
stringList *const slist, const char *const elist, const boolean clear)
{
char *const extensionList = eStrdup (elist);
const char *extension = NULL;
boolean first = TRUE;
if (clear)
{
verbose (" clearing\n");
stringListClear (slist);
}
verbose (" adding: ");
if (elist != NULL && *elist != '\0')
{
extension = extensionList;
if (elist [0] == EXTENSION_SEPARATOR)
++extension;
}
while (extension != NULL)
{
char *separator = strchr (extension, EXTENSION_SEPARATOR);
if (separator != NULL)
*separator = '\0';
verbose ("%s%s", first ? "" : ", ",
*extension == '\0' ? "(NONE)" : extension);
stringListAdd (slist, vStringNewInit (extension));
first = FALSE;
if (separator == NULL)
extension = NULL;
else
extension = separator + 1;
}
BEGIN_VERBOSE(vfp);
{
fprintf (vfp, "\n now: ");
stringListPrint (slist, vfp);
putc ('\n', vfp);
}
END_VERBOSE();
}
static boolean isFalse (const char *parameter)
{
return (boolean) (
strcasecmp (parameter, "0" ) == 0 ||
strcasecmp (parameter, "n" ) == 0 ||
strcasecmp (parameter, "no" ) == 0 ||
strcasecmp (parameter, "off") == 0);
}
static boolean isTrue (const char *parameter)
{
return (boolean) (
strcasecmp (parameter, "1" ) == 0 ||
strcasecmp (parameter, "y" ) == 0 ||
strcasecmp (parameter, "yes") == 0 ||
strcasecmp (parameter, "on" ) == 0);
}
/* Determines whether the specified file name is considered to be a header
* file for the purposes of determining whether enclosed tags are global or
* static.
*/
extern boolean isIncludeFile (const char *const fileName)
{
boolean result = FALSE;
const char *const extension = fileExtension (fileName);
if (Option.headerExt != NULL)
result = stringListExtensionMatched (Option.headerExt, extension);
return result;
}
/*
* Specific option processing
*/
static void processConfigFilenameOption (
const char *const option __unused__, const char *const parameter)
{
freeString (&Option.configFilename);
Option.configFilename = stringCopy (parameter);
}
static void processEtagsInclude (
const char *const option, const char *const parameter)
{
if (! Option.etags)
error (FATAL, "Etags must be enabled to use \"%s\" option", option);
else
{
vString *const file = vStringNewInit (parameter);
if (Option.etagsInclude == NULL)
Option.etagsInclude = stringListNew ();
stringListAdd (Option.etagsInclude, file);
FilesRequired = FALSE;
}
}
static void processExcludeOption (
const char *const option __unused__, const char *const parameter)
{
const char *const fileName = parameter + 1;
if (parameter [0] == '\0')
freeList (&Excluded);
else if (parameter [0] == '@')
{
stringList* const sl = stringListNewFromFile (fileName);
if (sl == NULL)
error (FATAL | PERROR, "cannot open \"%s\"", fileName);
if (Excluded == NULL)
Excluded = sl;
else
stringListCombine (Excluded, sl);
verbose (" adding exclude patterns from %s\n", fileName);
}
else
{
vString *const item = vStringNewInit (parameter);
if (Excluded == NULL)
Excluded = stringListNew ();
stringListAdd (Excluded, item);
verbose (" adding exclude pattern: %s\n", parameter);
}
}
extern boolean isExcludedFile (const char* const name)
{
const char* base = baseFilename (name);
boolean result = FALSE;
if (Excluded != NULL)
{
result = stringListFileMatched (Excluded, base);
if (! result && name != base)
result = stringListFileMatched (Excluded, name);
}
return result;
}
static void processExcmdOption (
const char *const option, const char *const parameter)
{
switch (*parameter)
{
case 'm': Option.locate = EX_MIX; break;
case 'n': Option.locate = EX_LINENUM; break;
case 'p': Option.locate = EX_PATTERN; break;
default:
error (FATAL, "Invalid value for \"%s\" option", option);
break;
}
}
static void processExtraTagsOption (
const char *const option, const char *const parameter)
{
xtagType t;
const char *p = parameter;
boolean mode = TRUE;
int c;
int i;
if (*p != '+' && *p != '-')
{
int i;
for (i = 0; i < XTAG_COUNT; i++)
enableXtag (i, FALSE);
}
while ((c = *p++) != '\0') switch (c)
{
case '+': mode = TRUE; break;
case '-': mode = FALSE; break;
case '*':
for (i = 0; i < XTAG_COUNT; ++i)
enableXtag (i, TRUE);
break;
default:
t = getXtagTypeForOption (c);
if (t == XTAG_UNKNOWN)
error(WARNING, "Unsupported parameter '%c' for \"%s\" option",
c, option);
else
enableXtag (t, mode);
break;
}
}
static void resetFieldsOption (boolean mode)
{
int i;
for (i = 0; i < FIELD_COUNT; ++i)
if (!getFieldDesc (i)->basic)
getFieldDesc (i)->enabled = mode;
}
static void processFieldsOption (
const char *const option, const char *const parameter)
{
const char *p = parameter;
boolean mode = TRUE;
int c;
fieldType t;
if (*p == '*')
{
resetFieldsOption (TRUE);
p++;
}
else if (*p != '+' && *p != '-')
resetFieldsOption (FALSE);
while ((c = *p++) != '\0') switch (c)
{
case '+': mode = TRUE; break;
case '-': mode = FALSE; break;
default : t = getFieldTypeForOption (c);
if (t == FIELD_UNKNOWN)
error(WARNING, "Unsupported parameter '%c' for \"%s\" option",
c, option);
else if (getFieldDesc (t)->basic && (mode == FALSE))
error(WARNING, "Cannot disable basic field: '%c'(%s) for \"%s\" option",
c, getFieldDesc (t)->name, option);
else
getFieldDesc (t)->enabled = mode;
break;
}
}
static void processFilterTerminatorOption (
const char *const option __unused__, const char *const parameter)
{
freeString (&Option.filterTerminator);
Option.filterTerminator = stringCopy (parameter);
}
static void processFormatOption (
const char *const option, const char *const parameter)
{
unsigned int format;
if (sscanf (parameter, "%u", &format) < 1)
error (FATAL, "Invalid value for \"%s\" option",option);
else if (format <= (unsigned int) MaxSupportedTagFormat)
Option.tagFileFormat = format;
else
error (FATAL, "Unsupported value for \"%s\" option", option);
}
#ifdef HAVE_ICONV
static void processInputEncodingOption(const char *const option,
const char *const parameter)
{
if (Option.inputEncoding)
eFree (Option.inputEncoding);
else
{
if (!Option.outputEncoding)
Option.outputEncoding = eStrdup("UTF-8");
}
Option.inputEncoding = eStrdup(parameter);
}
static void processOutputEncodingOption(const char *const option,
const char *const parameter)
{
if (Option.outputEncoding)
eFree (Option.outputEncoding);
Option.outputEncoding = eStrdup(parameter);
}
#endif
static void printInvocationDescription (void)
{
printf (INVOCATION, getExecutableName ());
}
static void printOptionDescriptions (const optionDescription *const optDesc)
{
int i;
for (i = 0 ; optDesc [i].description != NULL ; ++i)
{
if (! Option.etags || optDesc [i].usedByEtags)
puts (optDesc [i].description);
}
}
static void printFeatureList (void)
{
int i;
for (i = 0 ; Features [i] != NULL ; ++i)
{
if (i == 0)
printf (" Optional compiled features: ");
if (strcmp (Features [i], "regex") != 0 || checkRegex ())
printf ("%s+%s", (i>0 ? ", " : ""), Features [i]);
#ifdef CUSTOM_CONFIGURATION_FILE
if (strcmp (Features [i], "custom-conf") == 0)
printf ("=%s", CUSTOM_CONFIGURATION_FILE);
#endif
}
if (i > 0)
putchar ('\n');
}
static void processListFeaturesOption(const char *const option __unused__,
const char *const parameter __unused__)
{
int i;
for (i = 0 ; Features [i] != NULL ; ++i)
if (strcmp (Features [i], "regex") != 0 || checkRegex ())
printf ("%s\n", Features [i]);
if (i == 0)
putchar ('\n');
exit (0);
}
static void processListFieldsOption(const char *const option __unused__,
const char *const parameter __unused__)
{
printFields ();
exit (0);
}
static void printProgramIdentification (void)
{
if ((ctags_repoinfo == NULL)
|| (strcmp (ctags_repoinfo, PROGRAM_VERSION) == 0))
printf ("%s %s, %s %s\n",
PROGRAM_NAME, PROGRAM_VERSION,
PROGRAM_COPYRIGHT, AUTHOR_NAME);
else
printf ("%s %s(%s), %s %s\n",
PROGRAM_NAME, PROGRAM_VERSION, ctags_repoinfo,
PROGRAM_COPYRIGHT, AUTHOR_NAME);
printf ("Universal Ctags is derived from Exuberant Ctags.\n");
printf ("Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert\n");
printf (" Compiled: %s, %s\n", __DATE__, __TIME__);
printf (" URL: %s\n", PROGRAM_URL);
printFeatureList ();
}
static void processHelpOption (
const char *const option __unused__,
const char *const parameter __unused__)
{
printProgramIdentification ();
putchar ('\n');
printInvocationDescription ();
putchar ('\n');
printOptionDescriptions (LongOptionDescription);
exit (0);
}
static void processLanguageForceOption (
const char *const option, const char *const parameter)
{
langType language;
if (strcasecmp (parameter, "auto") == 0)
language = LANG_AUTO;
else
language = getNamedLanguage (parameter, 0);
if (strcmp (option, "lang") == 0 || strcmp (option, "language") == 0)
error (WARNING,
"\"--%s\" option is obsolete; use \"--language-force\" instead",
option);
if (language == LANG_IGNORE)
error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
else
Option.language = language;
}
static char* skipPastMap (char* p)
{
while (*p != EXTENSION_SEPARATOR &&
*p != PATTERN_START && *p != ',' && *p != '\0')
++p;
return p;
}
/* Parses the mapping beginning at `map', adds it to the language map, and
* returns first character past the map.
*/
static char* extractMapFromParameter (const langType language,
char* parameter,
char** tail,
boolean* pattern_p,
char* (* skip) (char *))
{
char* p = NULL;
const char first = *parameter;
char tmp;
char* result;
if (first == EXTENSION_SEPARATOR) /* extension map */
{
*pattern_p = FALSE;
++parameter;
p = (* skip) (parameter);
if (*p == '\0')
{
result = eStrdup (parameter);
*tail = parameter + strlen (parameter);
return result;
}
else
{
tmp = *p;
*p = '\0';
result = eStrdup (parameter);
*p = tmp;
*tail = p;
return result;
}
}
else if (first == PATTERN_START) /* pattern map */
{
*pattern_p = TRUE;
++parameter;
for (p = parameter ; *p != PATTERN_STOP && *p != '\0' ; ++p)
{
if (*p == '\\' && *(p + 1) == PATTERN_STOP)
++p;
}
if (*p == '\0')
error (FATAL, "Unterminated file name pattern for %s language",
getLanguageName (language));
else
{
tmp = *p;
*p = '\0';
result = eStrdup (parameter);
*p = tmp;
*tail = p + 1;
return result;
}
}
return NULL;
}
static char* addLanguageMap (const langType language, char* map_parameter, boolean exclusive)
{
char* p = NULL;
boolean pattern_p;
char* map;
map = extractMapFromParameter (language, map_parameter, &p, &pattern_p, skipPastMap);
if (map && pattern_p == FALSE)
addLanguageExtensionMap (language, map, exclusive);
else if (map && pattern_p == TRUE)
addLanguagePatternMap (language, map, exclusive);
else
error (FATAL, "Badly formed language map for %s language",
getLanguageName (language));
if (map)
eFree (map);
return p;
}
static char* processLanguageMap (char* map)
{
char* const separator = strchr (map, ':');
char* result = NULL;
if (separator != NULL)
{
langType language;
char *list = separator + 1;
boolean clear = FALSE;
*separator = '\0';
language = getNamedLanguage (map, 0);
if (language != LANG_IGNORE)
{
const char *const deflt = "default";
char* p;
if (*list == '+')
++list;
else
clear = TRUE;
for (p = list ; *p != ',' && *p != '\0' ; ++p) /*no-op*/ ;
if ((size_t) (p - list) == strlen (deflt) &&
strncasecmp (list, deflt, p - list) == 0)
{
verbose (" Restoring default %s language map: ", getLanguageName (language));
installLanguageMapDefault (language);
list = p;
}
else
{
if (clear)
{
verbose (" Setting %s language map:", getLanguageName (language));
clearLanguageMap (language);
}
else
verbose (" Adding to %s language map:", getLanguageName (language));
while (list != NULL && *list != '\0' && *list != ',')
list = addLanguageMap (language, list, TRUE);
verbose ("\n");
}
if (list != NULL && *list == ',')
++list;
result = list;
}
}
return result;
}
static void processLanguageMapOption (
const char *const option, const char *const parameter)
{
char *const maps = eStrdup (parameter);
char *map = maps;
if (strcmp (parameter, "default") == 0)
{
verbose (" Restoring default language maps:\n");
installLanguageMapDefaults ();
}
else while (map != NULL && *map != '\0')
{
char* const next = processLanguageMap (map);
if (next == NULL)
error (WARNING, "Unknown language \"%s\" in \"%s\" option", parameter, option);
map = next;
}
eFree (maps);
}
static void processLanguagesOption (
const char *const option, const char *const parameter)
{
char *const langs = eStrdup (parameter);
enum { Add, Remove, Replace } mode = Replace;
boolean first = TRUE;
char *lang = langs;
const char* prefix = "";
verbose (" Enabled languages: ");
while (lang != NULL)
{
char *const end = strchr (lang, ',');
if (lang [0] == '+')
{
++lang;
mode = Add;
prefix = "+ ";
}
else if (lang [0] == '-')
{
++lang;
mode = Remove;
prefix = "- ";
}
if (mode == Replace)
enableLanguages (FALSE);
if (end != NULL)
*end = '\0';
if (lang [0] != '\0')
{
if (strcmp (lang, "all") == 0)
enableLanguages ((boolean) (mode != Remove));
else
{
const langType language = getNamedLanguage (lang, 0);
if (language == LANG_IGNORE)
error (WARNING, "Unknown language \"%s\" in \"%s\" option", lang, option);
else
enableLanguage (language, (boolean) (mode != Remove));
}
verbose ("%s%s%s", (first ? "" : ", "), prefix, lang);
prefix = "";
first = FALSE;
if (mode == Replace)
mode = Add;
}
lang = (end != NULL ? end + 1 : NULL);
}
verbose ("\n");
eFree (langs);
}
extern boolean processMapOption (
const char *const option, const char *const parameter)
{
langType language;
const char* spec;
char* map_parameter;
boolean clear = FALSE;
language = getLanguageComponentInOption (option, "map-");
if (language == LANG_IGNORE)
return FALSE;
if (parameter == NULL || parameter [0] == '\0')
error (FATAL, "no parameter is given for %s", option);
spec = parameter;
if (*spec == '+')
spec++;
else
clear = TRUE;
if (clear)
{
verbose (" Setting %s language map:", getLanguageName (language));
clearLanguageMap (language);
}
else
verbose (" Adding to %s language map:", getLanguageName (language));
map_parameter = eStrdup (spec);
addLanguageMap (language, map_parameter, FALSE);
eFree (map_parameter);
verbose ("\n");
return TRUE;
}
static void processLicenseOption (
const char *const option __unused__,
const char *const parameter __unused__)
{
printProgramIdentification ();
puts ("");
puts (License1);
puts (License2);
exit (0);
}
static void processListAliasesOption (
const char *const option, const char *const parameter)
{
if (parameter [0] == '\0' || strcasecmp (parameter, "all") == 0)
printLanguageAliases (LANG_AUTO);
else
{
langType language = getNamedLanguage (parameter, 0);
if (language == LANG_IGNORE)
error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
else
printLanguageAliases (language);
}
exit (0);
}
static void processListExtraOption (
const char *const option __unused__, const char *const parameter __unused__)
{
int i;
for (i = 0; i < XTAG_COUNT; i++)
printXtag (i);
exit (0);
}
static void processListFileKindOption (
const char *const option, const char *const parameter)
{
if (parameter [0] == '\0' || strcasecmp (parameter, "all") == 0)
printLanguageFileKind (LANG_AUTO);
else
{
langType language = getNamedLanguage (parameter, 0);
if (language == LANG_IGNORE)
error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
else
printLanguageFileKind (language);
}
exit (0);
}
static void processListKindsOption (
const char *const option, const char *const parameter)
{
boolean print_all = (strcmp (option, "_list-kinds-full") == 0)? TRUE: FALSE;
if (parameter [0] == '\0' || strcasecmp (parameter, "all") == 0)
printLanguageKinds (LANG_AUTO, print_all);
else
{
langType language = getNamedLanguage (parameter, 0);
if (language == LANG_IGNORE)
error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
else
printLanguageKinds (language, print_all);
}
exit (0);
}
static void processListMapsOptionForType (const char *const __unused__ option,
const char *const parameter,
langmapType type)
{
if (parameter [0] == '\0' || strcasecmp (parameter, "all") == 0)
printLanguageMaps (LANG_AUTO, type);
else
{
langType language = getNamedLanguage (parameter, 0);
if (language == LANG_IGNORE)
error (FATAL, "Unknown language \"%s\" in \"%s\" option", parameter, option);
else
printLanguageMaps (language, type);
}
exit (0);
}
static void processListExtensionsOption (const char *const option,
const char *const parameter)
{
processListMapsOptionForType (option, parameter, LMAP_EXTENSION);
}
static void processListPatternsOption (const char *const option,
const char *const parameter)
{
processListMapsOptionForType (option, parameter, LMAP_PATTERN);
}
static void processListMapsOption (
const char *const __unused__ option,
const char *const __unused__ parameter)
{
processListMapsOptionForType (option, parameter, LMAP_ALL);
}
static void processListLanguagesOption (
const char *const option __unused__,
const char *const parameter __unused__)
{
printLanguageList ();
exit (0);
}
static void processListPseudoTagsOptions (
const char *const option __unused__,
const char *const parameter __unused__)
{
int i;
for (i = 0; i < PTAG_COUNT; i++)
printPtag (i);
exit (0);
}
static void processListRegexFlagsOptions (
const char *const option __unused__,
const char *const parameter __unused__)
{
printRegexFlags ();
exit (0);
}
static void processListRolesOptions (const char *const option __unused__,
const char *const parameter)
{
const char* sep;
const char *kindletters;
langType lang;
if (parameter == NULL || parameter[0] == '\0')
{
printLanguageRoles (LANG_AUTO, "*");
exit (0);
}
sep = strchr (parameter, ':');
if (sep == NULL || sep [1] == '\0')
{
vString* vstr = vStringNewInit (parameter);
vStringCatS (vstr, (sep? "*": ":*"));
processListRolesOptions (option, vStringValue (vstr));
/* The control should never reache here. */
}
kindletters = sep + 1;
if (strncmp (parameter, "all:", 4) == 0
|| strncmp (parameter, "*:", 1) == 0
|| strncmp (parameter, ":", 1) == 0)
lang = LANG_AUTO;
else
{
lang = getNamedLanguage (parameter, sep - parameter);
if (lang == LANG_IGNORE)
error (FATAL, "Unknown language \"%s\" in \"%s\"", parameter, option);
}
printLanguageRoles (lang, kindletters);
exit (0);
}
static void freeSearchPathList (searchPathList** pathList)
{
stringListClear (*pathList);
stringListDelete (*pathList);
*pathList = NULL;
}
static void verboseSearchPathList (const searchPathList* pathList, const char *const varname)
{
unsigned int i;
verbose ("Install %s:\n", varname);
for (i = 0; i < stringListCount (pathList); ++i)
verbose (" %s\n", vStringValue (stringListItem (pathList, i)));
}
static vString* expandOnSearchPathList (searchPathList *pathList, const char* leaf,
boolean (* check) (const char *const))
{
unsigned int i;
for (i = 0; i < stringListCount (pathList); ++i)
{
const char* const body = vStringValue (stringListItem (pathList, i));
char* tmp = combinePathAndFile (body, leaf);
if ((* check) (tmp))
{
vString *r = vStringNewOwn (tmp);
return r;
}
else
eFree (tmp);
}
return NULL;
}
static boolean isDirectory (const char *const dirName)
{
fileStatus *status = eStat (dirName);
return status->exists && status->isDirectory;
}
static vString* expandOnOptlibPathList (const char* leaf)
{
vString* r;
vString* leaf_with_suffix;
leaf_with_suffix = vStringNewInit (leaf);
vStringCatS (leaf_with_suffix, ".d");
r = expandOnSearchPathList (OptlibPathList, vStringValue (leaf_with_suffix),
isDirectory);
if (!r)
{
vStringCopyS (leaf_with_suffix, leaf);
vStringCatS (leaf_with_suffix, ".conf");
r = expandOnSearchPathList (OptlibPathList, vStringValue (leaf_with_suffix),
doesFileExist);
}
if (!r)
{
vStringCopyS (leaf_with_suffix, leaf);
vStringCatS (leaf_with_suffix, ".ctags");
r = expandOnSearchPathList (OptlibPathList, vStringValue (leaf_with_suffix),
doesFileExist);
}
#ifdef MSDOS_STYLE_PATH
if (!r)
{
vStringCopyS (leaf_with_suffix, leaf);
vStringCatS (leaf_with_suffix, ".cnf");
r = expandOnSearchPathList (OptlibPathList, vStringValue (leaf_with_suffix),
doesFileExist);
}
#endif
vStringDelete (leaf_with_suffix);
return r;
}
extern vString* expandOnDriversPathList (const char* leaf)
{
return expandOnSearchPathList (DriversPathList, leaf, doesExecutableExist);
}
static void processOptionFile (
const char *const option, const char *const parameter)
{
const char* path;
vString* vpath = NULL;
fileStatus *status;
if (parameter [0] == '\0')
error (FATAL, "no option file supplied for \"%s\"", option);
if (parameter [0] != '/' && parameter [0] != '.')
{
vpath = expandOnOptlibPathList (parameter);
path = vpath? vStringValue (vpath): parameter;
}
else
path = parameter;
status = eStat (path);
if (!status->exists)
{
error (FATAL | PERROR, "cannot stat \"%s\"", path);
}
else if (status->isDirectory)
{
if (!parseAllConfigurationFilesOptionsInDirectory (path, NULL))
error (FATAL | PERROR, "cannot open option directory \"%s\"", path);
}
else
{
if (!parseFileOptions (path))
error (FATAL | PERROR, "cannot open option file \"%s\"", path);
}
eStatFree (status);
if (vpath)
vStringDelete (vpath);
}
static void processPseudoTags (const char *const option __unused__,
const char *const parameter)
{
const char *p = parameter;
boolean s;
if (*p == '*')
{
int i;
for (i = 0; i < PTAG_COUNT; i++)
enablePtag (i, TRUE);
return;
}
if (*p != '+' && *p != '-')
{
int i;
for (i = 0; i < PTAG_COUNT; i++)
enablePtag (i, FALSE);
}
else
{
ptagType t;
s = (*p == '+')? TRUE: FALSE;
p++;
t = getPtagTypeForName (p);
if (t == PTAG_UNKNOWN)
error (FATAL, "Unknown pseudo tag name: %s", p);
enablePtag (t, s);
}
}
static void processSortOption (
const char *const option, const char *const parameter)
{
if (isFalse (parameter))
Option.sorted = SO_UNSORTED;
else if (isTrue (parameter))
Option.sorted = SO_SORTED;
else if (strcasecmp (parameter, "f") == 0 ||
strcasecmp (parameter, "fold") == 0 ||
strcasecmp (parameter, "foldcase") == 0)
Option.sorted = SO_FOLDSORTED;
else
error (FATAL, "Invalid value for \"%s\" option", option);
}
static void installHeaderListDefaults (void)
{
Option.headerExt = stringListNewFromArgv (HeaderExtensions);
BEGIN_VERBOSE(vfp);
{
fprintf (vfp, " Setting default header extensions: ");
stringListPrint (Option.headerExt, vfp);
putc ('\n', vfp);
}
END_VERBOSE();
}
static void processHeaderListOption (const int option, const char *parameter)
{
/* Check to make sure that the user did not enter "ctags -h *.c"
* by testing to see if the list is a filename that exists.
*/
if (doesFileExist (parameter))
error (FATAL, "-%c: Invalid list", option);
if (strcmp (parameter, "default") == 0)
installHeaderListDefaults ();
else
{
boolean clear = TRUE;
if (parameter [0] == '+')
{
++parameter;
clear = FALSE;
}
if (Option.headerExt == NULL)
Option.headerExt = stringListNew ();
verbose (" Header Extensions:\n");
addExtensionList (Option.headerExt, parameter, clear);
}
}
/*
* Token ignore processing
*/
/* Determines whether or not "name" should be ignored, per the ignore list.
*/
extern boolean isIgnoreToken (
const char *const name, boolean *const pIgnoreParens,
const char **const replacement)
{
boolean result = FALSE;
if (Option.ignore != NULL)
{
const size_t nameLen = strlen (name);
unsigned int i;
if (pIgnoreParens != NULL)
*pIgnoreParens = FALSE;
for (i = 0 ; i < stringListCount (Option.ignore) ; ++i)
{
vString *token = stringListItem (Option.ignore, i);
if (strncmp (vStringValue (token), name, nameLen) == 0)
{
const size_t tokenLen = vStringLength (token);
if (nameLen == tokenLen)
{
result = TRUE;
break;
}
else if (tokenLen == nameLen + 1 &&
vStringChar (token, tokenLen - 1) == '+')
{
result = TRUE;
if (pIgnoreParens != NULL)
*pIgnoreParens = TRUE;
break;
}
else if (vStringChar (token, nameLen) == '=')
{
if (replacement != NULL)
*replacement = vStringValue (token) + nameLen + 1;
break;
}
}
}
}
return result;
}
static void saveIgnoreToken (vString *const ignoreToken)
{
if (Option.ignore == NULL)
Option.ignore = stringListNew ();
stringListAdd (Option.ignore, ignoreToken);
verbose (" ignore token: %s\n", vStringValue (ignoreToken));
}
static void readIgnoreList (const char *const list)
{
char* newList = stringCopy (list);
const char *token = strtok (newList, IGNORE_SEPARATORS);
while (token != NULL)
{
vString *const entry = vStringNewInit (token);
saveIgnoreToken (entry);
token = strtok (NULL, IGNORE_SEPARATORS);
}
eFree (newList);
}
static void addIgnoreListFromFile (const char *const fileName)
{
stringList* tokens = stringListNewFromFile (fileName);
if (tokens == NULL)
error (FATAL | PERROR, "cannot open \"%s\"", fileName);
if (Option.ignore == NULL)
Option.ignore = tokens;
else
stringListCombine (Option.ignore, tokens);
}
static void processIgnoreOption (const char *const list)
{
if (strchr ("@./\\", list [0]) != NULL)
{
const char* fileName = (*list == '@') ? list + 1 : list;
addIgnoreListFromFile (fileName);
}
#if defined (WIN32)
else if (isalpha (list [0]) && list [1] == ':')
addIgnoreListFromFile (list);
#endif
else if (strcmp (list, "-") == 0)
{
freeList (&Option.ignore);
verbose (" clearing list\n");
}
else
readIgnoreList (list);
}
static void processEchoOption (const char *const option, const char *const parameter)
{
if (parameter == NULL || parameter[0] == '\0')
error (FATAL, "Something message is needed for \"%s\" option", option);
notice ("%s", parameter);
}
static void processForceQuitOption (const char *const option __unused__,
const char *const parameter)
{
long s = 0;
if (parameter != NULL && parameter[0] != '\0')
s = strtol (parameter, NULL, 0);
exit (s);
}
static void processVersionOption (
const char *const option __unused__,
const char *const parameter __unused__)
{
printProgramIdentification ();
exit (0);
}
static void processXformatOption (const char *const option __unused__,
const char *const parameter)
{
if (Option.customXfmt)
fmtDelete (Option.customXfmt);
Option.customXfmt = fmtNew (parameter);
}
static void resetPathList (searchPathList** pathList, const char *const varname)
{
freeSearchPathList (pathList);
verbose ("Reset %s\n", varname);
*pathList = stringListNew ();
}
static void resetDataPathList (void)
{
resetPathList (&OptlibPathList, "OptlibPathList");
}
static void resetLibexecPathList (void)
{
resetPathList (&DriversPathList, "DriversPathList");
}
static void appendToPathList (const char *const dir, const char *const subdir, searchPathList* const pathList, const char *const varname,
boolean report_in_verboe, const char* const action)
{
char* path;
path = combinePathAndFile (dir, subdir);
if (report_in_verboe)
verbose ("%s %s to %s\n", action, path, varname);
stringListAdd (pathList, vStringNewOwn (path));
}
static void prependToPathList (const char *const dir, const char *const subdir, searchPathList* const pathList, const char *const varname,
boolean report_in_verboe, const char* const action)
{
stringListReverse (pathList);
appendToPathList(dir, subdir, pathList, varname, report_in_verboe, action);
stringListReverse (pathList);
}
static void appendToDataPathList (const char *const dir, boolean from_cmdline)
{
appendToPathList (dir, SUBDIR_OPTLIB, OptlibPathList, "OptlibPathList",
from_cmdline, from_cmdline? "Append": NULL);
if (!from_cmdline)
appendToPathList (dir, SUBDIR_PRELOAD, PreloadPathList, "PreloadPathList",
FALSE, NULL);
}
static void appendToLibexecPathList (const char *const dir, boolean from_cmdline)
{
appendToPathList (dir, SUBDIR_DRIVERS, DriversPathList, "DriversPathList",
from_cmdline, from_cmdline? "Append": NULL);
}
static void prependToDataPathList (const char *const dir, boolean from_cmdline)
{
prependToPathList (dir, SUBDIR_OPTLIB, OptlibPathList, "OptlibPathList",
from_cmdline, from_cmdline? "Prepend": NULL);
if (!from_cmdline)
prependToPathList (dir, SUBDIR_PRELOAD, PreloadPathList, "PreloadPathList",
FALSE, NULL);
}
static void prependToLibexecPathList (const char *const dir, boolean from_cmdline)
{
prependToPathList (dir, SUBDIR_DRIVERS, DriversPathList, "DriversPathList",
from_cmdline, from_cmdline? "Prepend": NULL);
}
static void processDataDir (
const char *const option, const char *const parameter)
{
const char* path;
if (parameter == NULL || parameter[0] == '\0')
error (FATAL, "Path for a directory is needed for \"%s\" option", option);
if (parameter[0] == '+')
{
path = parameter + 1;
prependToDataPathList (path, TRUE);
}
else if (!strcmp (parameter, "NONE"))
resetDataPathList ();
else
{
resetDataPathList ();
path = parameter;
appendToDataPathList (path, TRUE);
}
}
static void processLibexecDir (const char *const option,
const char *const parameter)
{
const char* path;
if (parameter == NULL || parameter[0] == '\0')
error (FATAL, "Path for a directory is needed for \"%s\" option", option);
if (parameter[0] == '+')
{
path = parameter + 1;
prependToLibexecPathList (path, TRUE);
}
else if (!strcmp (parameter, "NONE"))
resetLibexecPathList ();
else
{
resetLibexecPathList ();
path = parameter;
appendToLibexecPathList (path, TRUE);
}
}
static void processMaxRecursionDepthOption (const char *const option, const char *const parameter)
{
if (parameter == NULL || parameter[0] == '\0')
error (FATAL, "A parameter is needed after \"%s\" option", option);
if (atol (parameter) < 1)
error (FATAL, "-%s: Invalid maximum recursion depth", option);
Option.maxRecursionDepth = atol(parameter);
}
static boolean* redirectToXtag(const booleanOption *const option)
{
/* WARNING/TODO: This function breaks capsulization. */
xtagType t = (xtagType)option->pValue;
boolean default_value = isXtagEnabled (t);
enableXtag (t, default_value);
return &(getXtagDesc (t)->enabled);
}
/*
* Option tables
*/
static parametricOption ParametricOptions [] = {
{ "config-filename", processConfigFilenameOption, TRUE, STAGE_ANY },
{ "data-dir", processDataDir, FALSE, STAGE_ANY },
{ "etags-include", processEtagsInclude, FALSE, STAGE_ANY },
{ "exclude", processExcludeOption, FALSE, STAGE_ANY },
{ "excmd", processExcmdOption, FALSE, STAGE_ANY },
{ "extra", processExtraTagsOption, FALSE, STAGE_ANY },
{ "fields", processFieldsOption, FALSE, STAGE_ANY },
{ "filter-terminator", processFilterTerminatorOption, TRUE, STAGE_ANY },
{ "format", processFormatOption, TRUE, STAGE_ANY },
{ "help", processHelpOption, TRUE, STAGE_ANY },
#ifdef HAVE_ICONV
{ "input-encoding", processInputEncodingOption, FALSE, STAGE_ANY },
{ "output-encoding", processOutputEncodingOption, FALSE, STAGE_ANY },
#endif
{ "lang", processLanguageForceOption, FALSE, STAGE_ANY },
{ "language", processLanguageForceOption, FALSE, STAGE_ANY },
{ "language-force", processLanguageForceOption, FALSE, STAGE_ANY },
{ "languages", processLanguagesOption, FALSE, STAGE_ANY },
{ "langdef", processLanguageDefineOption, FALSE, STAGE_ANY },
{ "langmap", processLanguageMapOption, FALSE, STAGE_ANY },
{ "libexec-dir", processLibexecDir, FALSE, STAGE_ANY },
{ "license", processLicenseOption, TRUE, STAGE_ANY },
{ "list-aliases", processListAliasesOption, TRUE, STAGE_ANY },
{ "list-extensions", processListExtensionsOption, TRUE, STAGE_ANY },
{ "list-extra", processListExtraOption, TRUE, STAGE_ANY },
{ "list-features", processListFeaturesOption, TRUE, STAGE_ANY },
{ "list-fields", processListFieldsOption, TRUE, STAGE_ANY },
{ "list-file-kind", processListFileKindOption, TRUE, STAGE_ANY },
{ "list-kinds", processListKindsOption, TRUE, STAGE_ANY },
{ "_list-kinds-full", processListKindsOption, TRUE, STAGE_ANY },
{ "list-languages", processListLanguagesOption, TRUE, STAGE_ANY },
{ "list-maps", processListMapsOption, TRUE, STAGE_ANY },
{ "list-patterns", processListPatternsOption, TRUE, STAGE_ANY },
{ "list-pseudo-tags", processListPseudoTagsOptions, TRUE, STAGE_ANY },
{ "list-regex-flags", processListRegexFlagsOptions, TRUE, STAGE_ANY },
{ "_list-roles", processListRolesOptions, TRUE, STAGE_ANY },
{ "maxdepth", processMaxRecursionDepthOption, TRUE, STAGE_ANY },
{ "options", processOptionFile, FALSE, STAGE_ANY },
{ "pseudo-tags", processPseudoTags, FALSE, STAGE_ANY },
{ "sort", processSortOption, TRUE, STAGE_ANY },
{ "version", processVersionOption, TRUE, STAGE_ANY },
{ "_echo", processEchoOption, FALSE, STAGE_ANY },
{ "_force-quit", processForceQuitOption, FALSE, STAGE_ANY },
{ "_xformat", processXformatOption, FALSE, STAGE_ANY },
};
static booleanOption BooleanOptions [] = {
{ "append", &Option.append, TRUE, STAGE_ANY },
{ "file-scope", ((boolean *)XTAG_FILE_SCOPE), FALSE, STAGE_ANY, redirectToXtag },
{ "file-tags", ((boolean *)XTAG_FILE_NAMES), FALSE, STAGE_ANY, redirectToXtag },
{ "filter", &Option.filter, TRUE, STAGE_ANY },
{ "guess-language-eagerly", &Option.guessLanguageEagerly, FALSE, STAGE_ANY },
{ "if0", &Option.if0, FALSE, STAGE_ANY },
{ "line-directives",&Option.lineDirectives, FALSE, STAGE_ANY },
{ "links", &Option.followLinks, FALSE, STAGE_ANY },
{ "put-field-prefix", &Option.putFieldPrefix, FALSE, STAGE_ANY },
{ "print-language", &Option.printLanguage, TRUE, STAGE_ANY },
{ "quiet", &Option.quiet, FALSE, STAGE_ANY },
#ifdef RECURSE_SUPPORTED
{ "recurse", &Option.recurse, FALSE, STAGE_ANY },
#endif
{ "tag-relative", &Option.tagRelative, TRUE, STAGE_ANY },
{ "totals", &Option.printTotals, TRUE, STAGE_ANY },
{ "verbose", &Option.verbose, FALSE, STAGE_ANY },
{ "_allow-xcmd-in-homedir", &Option.allowXcmdInHomeDir, TRUE, ACCEPT(Etc)|ACCEPT(LocalEtc) },
{ "_fatal-warnings",&Option.fatalWarnings, FALSE, STAGE_ANY },
};
/*
* Generic option parsing
*/
static void checkOptionOrder (const char* const option, boolean longOption)
{
if (NonOptionEncountered)
error (FATAL, "-%s%s option may not follow a file name", longOption? "-": "", option);
}
static boolean processParametricOption (
const char *const option, const char *const parameter)
{
const int count = sizeof (ParametricOptions) / sizeof (parametricOption);
boolean found = FALSE;
int i;
for (i = 0 ; i < count && ! found ; ++i)
{
parametricOption* const entry = &ParametricOptions [i];
if (strcmp (option, entry->name) == 0)
{
found = TRUE;
if (!(entry->acceptableStages & (1UL << Stage)))
{
error (WARNING, "Cannot use --%s option in %s",
option, StageDescription[Stage]);
break;
}
if (entry->initOnly)
checkOptionOrder (option, TRUE);
(entry->handler) (option, parameter);
}
}
return found;
}
static boolean getBooleanOption (
const char *const option, const char *const parameter)
{
boolean selection = TRUE;
if (parameter [0] == '\0')
selection = TRUE;
else if (isFalse (parameter))
selection = FALSE;
else if (isTrue (parameter))
selection = TRUE;
else
error (FATAL, "Invalid value for \"%s\" option", option);
return selection;
}
static boolean processBooleanOption (
const char *const option, const char *const parameter)
{
const int count = sizeof (BooleanOptions) / sizeof (booleanOption);
boolean found = FALSE;
int i;
for (i = 0 ; i < count && ! found ; ++i)
{
booleanOption* const entry = &BooleanOptions [i];
boolean *slot;
if (strcmp (option, entry->name) == 0)
{
found = TRUE;
if (!(entry->acceptableStages & (1UL << Stage)))
{
error (WARNING, "Cannot use --%s option in %s",
option, StageDescription[Stage]);
break;
}
if (entry->initOnly)
checkOptionOrder (option, TRUE);
if (entry->redirect)
slot = entry->redirect (entry);
else
slot = entry->pValue;
*slot = getBooleanOption (option, parameter);
}
}
return found;
}
static void processLongOption (
const char *const option, const char *const parameter)
{
Assert (parameter != NULL);
Assert (option != NULL);
if (parameter [0] == '\0')
verbose (" Option: --%s\n", option);
else
verbose (" Option: --%s=%s\n", option, parameter);
if (processBooleanOption (option, parameter))
;
else if (processParametricOption (option, parameter))
;
else if (processKindOption (option, parameter))
;
else if (processAliasOption (option, parameter))
;
else if (processRegexOption (option, parameter))
;
else if (processXcmdOption (option, parameter, Stage))
;
else if (processMapOption (option, parameter))
;
#ifdef HAVE_ICONV
else if (processLanguageEncodingOption (option, parameter))
;
#endif
#ifndef RECURSE_SUPPORTED
else if (strcmp (option, "recurse") == 0)
error (WARNING, "%s option not supported on this host", option);
#endif
else
error (FATAL, "Unknown option: --%s", option);
}
static void processShortOption (
const char *const option, const char *const parameter)
{
if (parameter == NULL || parameter [0] == '\0')
verbose (" Option: -%s\n", option);
else
verbose (" Option: -%s %s\n", option, parameter);
if (isCompoundOption (*option) && (parameter == NULL || parameter [0] == '\0'))
error (FATAL, "Missing parameter for \"%s\" option", option);
else switch (*option)
{
case '?':
processHelpOption ("?", NULL);
exit (0);
break;
case 'a':
checkOptionOrder (option, FALSE);
Option.append = TRUE;
break;
#ifdef DEBUG
case 'b':
if (atol (parameter) < 0)
error (FATAL, "-%s: Invalid line number", option);
Option.breakLine = atol (parameter);
break;
case 'D':
Option.debugLevel = strtol (parameter, NULL, 0);
if (debug (DEBUG_STATUS))
Option.verbose = TRUE;
break;
#endif
case 'B':
Option.backward = TRUE;
break;
case 'e':
checkOptionOrder (option, FALSE);
setEtagsMode ();
break;
case 'f':
case 'o':
checkOptionOrder (option, FALSE);
if (Option.tagFileName != NULL)
{
error (WARNING,
"-%s option specified more than once, last value used",
option);
freeString (&Option.tagFileName);
}
else if (parameter [0] == '-' && parameter [1] != '\0')
error (FATAL, "output file name may not begin with a '-'");
Option.tagFileName = stringCopy (parameter);
break;
case 'F':
Option.backward = FALSE;
break;
case 'G':
Option.guessLanguageEagerly = TRUE;
break;
case 'h':
processHeaderListOption (*option, parameter);
break;
case 'I':
processIgnoreOption (parameter);
break;
case 'L':
if (Option.fileList != NULL)
{
error (WARNING,
"-%s option specified more than once, last value used",
option);
freeString (&Option.fileList);
}
Option.fileList = stringCopy (parameter);
break;
case 'n':
Option.locate = EX_LINENUM;
break;
case 'N':
Option.locate = EX_PATTERN;
break;
case 'R':
#ifdef RECURSE_SUPPORTED
Option.recurse = TRUE;
#else
error (WARNING, "-%s option not supported on this host", option);
#endif
break;
case 'u':
checkOptionOrder (option, FALSE);
Option.sorted = SO_UNSORTED;
break;
case 'V':
Option.verbose = TRUE;
break;
case 'w':
/* silently ignored */
break;
case 'x':
checkOptionOrder (option, FALSE);
Option.xref = TRUE;
break;
default:
error (FATAL, "Unknown option: -%s", option);
break;
}
}
static void parseOption (cookedArgs* const args)
{
Assert (! cArgOff (args));
if (args->isOption)
{
if (args->longOption)
processLongOption (args->item, args->parameter);
else
{
const char *parameter = args->parameter;
while (*parameter == ' ')
++parameter;
processShortOption (args->item, parameter);
}
cArgForth (args);
}
}
static void parseOptions (cookedArgs* const args)
{
while (! cArgOff (args) && cArgIsOption (args))
parseOption (args);
if (! cArgOff (args) && ! cArgIsOption (args))
NonOptionEncountered = TRUE;
}
extern void parseCmdlineOptions (cookedArgs* const args)
{
ENTER (Cmdline);
parseOptions (args);
}
static boolean checkSameFile (const char *const fileName, void * userData)
{
return isSameFile ((const char* const) userData, fileName);
}
static boolean parseFileOptions (const char* const fileName)
{
boolean fileFound = FALSE;
const char* const format = "Considering option file %s: %s\n";
if (stringListHasTest (OptionFiles, checkSameFile, (void *) fileName))
{
verbose (format, fileName, "already considered");
fileFound = TRUE;
}
else
{
FILE* const fp = fopen (fileName, "r");
if (fp == NULL)
verbose (format, fileName, "not found");
else
{
cookedArgs* const args = cArgNewFromLineFile (fp);
vString* file = vStringNewInit (fileName);
stringListAdd (OptionFiles, file);
verbose (format, fileName, "reading...");
parseOptions (args);
if (NonOptionEncountered)
error (WARNING, "Ignoring non-option in %s\n", fileName);
cArgDelete (args);
fclose (fp);
fileFound = TRUE;
}
}
return fileFound;
}
/* Actions to be taken before reading any other options */
extern void previewFirstOption (cookedArgs* const args)
{
while (cArgIsOption (args))
{
if (strcmp (args->item, "V") == 0
|| strcmp (args->item, "verbose") == 0
|| strcmp (args->item, "config-filename") == 0
|| strcmp (args->item, "quiet") == 0)
parseOption (args);
else if (strcmp (args->item, "options") == 0 &&
strcmp (args->parameter, "NONE") == 0)
{
notice ("No options will be read from files or environment");
SkipConfiguration = TRUE;
cArgForth (args);
}
else
break;
}
}
static void parseConfigurationFileOptionsInDirectoryWithLeafname (const char* directory, const char* leafname)
{
char* pathname = combinePathAndFile (directory, leafname);
parseFileOptions (pathname);
eFree (pathname);
}
static void parseConfigurationFileOptionsInDirectory (const char* directory)
{
char *leafname = NULL;
if (asprintf (&leafname,".%s",(Option.configFilename)?Option.configFilename:"ctags") == -1)
{
error (FATAL, "error in asprintf");
}
parseConfigurationFileOptionsInDirectoryWithLeafname (directory, leafname);
free (leafname);
#ifdef MSDOS_STYLE_PATH
if (asprintf (&leafname,"%s.cnf",(Option.configFilename)?Option.configFilename:"ctags") == -1)
{
error (FATAL, "error in asprintf");
}
parseConfigurationFileOptionsInDirectoryWithLeafname (directory, leafname);
free (leafname);
#endif
}
#if defined(HAVE_SCANDIR)
static int ignore_dot_file(const struct dirent* dent)
{
/* Ignore a file which name is started from dot. */
if (*dent->d_name == '.')
return 0;
else
return 1;
}
static int accept_only_dot_d(const struct dirent* dent)
{
size_t len;
/* accept only a directory ended with ".d" */
len = strlen(dent->d_name);
if (len < 3)
return 0;
return !strcmp(dent->d_name + (len - 2), ".d");
}
static int accept_only_dot_ctags(const struct dirent* dent)
{
size_t len;
/* accept only a file ended with ".conf" or ".ctags" */
len = strlen(dent->d_name);
if (len < 6)
return 0;
if (strcmp(dent->d_name + (len - 5), ".conf") == 0)
return 1;
if (len < 7)
return 0;
if (strcmp(dent->d_name + (len - 6), ".ctags") == 0)
return 1;
return 0;
}
static boolean parseAllConfigurationFilesOptionsInDirectory (const char* const dirName,
stringList* const already_loaded_files)
{
struct dirent **dents;
int i, n;
n = scandir (dirName, &dents, ignore_dot_file, alphasort);
if (n < 0)
return FALSE;
for (i = 0; i < n; i++)
{
char* path;
fileStatus *s;
if (already_loaded_files && stringListHas (already_loaded_files, dents[i]->d_name))
continue;
else if (already_loaded_files)
stringListAdd (already_loaded_files, vStringNewInit (dents[i]->d_name));
path = combinePathAndFile (dirName, dents[i]->d_name);
s = eStat (path);
if (s->exists && s->isDirectory && accept_only_dot_d(dents[i]))
parseAllConfigurationFilesOptionsInDirectory (path,
already_loaded_files);
else if (s->exists && accept_only_dot_ctags(dents[i]))
parseConfigurationFileOptionsInDirectoryWithLeafname (dirName,
dents[i]->d_name);
eStatFree (s);
free (dents[i]);
eFree (path);
}
free (dents);
return TRUE;
}
#else
static boolean parseAllConfigurationFilesOptionsInDirectory (const char* const dirName,
stringList* const already_loaded_files)
{
return FALSE;
}
#endif
static void preload (const searchPathList *const pathList)
{
unsigned int i;
stringList* loaded;
loaded = stringListNew ();
for (i = 0; i < stringListCount (pathList); ++i)
{
const char* const dir = vStringValue (stringListItem (pathList, i));
parseAllConfigurationFilesOptionsInDirectory (dir, loaded);
}
stringListClear (loaded);
stringListDelete (loaded);
}
static void parseConfigurationFileOptions (void)
{
vString *home;
/* We parse .ctags on all systems, and additionally ctags.cnf on DOS. */
char *filename = NULL;
const char *filename_body;
#ifdef CUSTOM_CONFIGURATION_FILE
ENTER(Custom);
parseFileOptions (CUSTOM_CONFIGURATION_FILE);
#endif
filename_body = (Option.configFilename)?Option.configFilename:"ctags";
#ifdef MSDOS_STYLE_PATH
if (asprintf (&filename,"/%s.cnf", filename_body) == -1)
{
error (FATAL, "error in asprintf");
}
ENTER(DosCnf);
parseFileOptions (filename);
free (filename);
#endif
if (asprintf (&filename,"/etc/%s.conf", filename_body) == -1)
{
error (FATAL, "error in asprintf");
}
ENTER(Etc);
parseFileOptions (filename);
free (filename);
if (asprintf (&filename,"/usr/local/etc/%s.conf", filename_body) == -1)
{
error (FATAL, "error in asprintf");
}
ENTER(LocalEtc);
parseFileOptions (filename);
free (filename);
home = getHome ();
if (home != NULL)
{
ENTER(HomeRecursive);
parseConfigurationFileOptionsInDirectory (vStringValue (home));
vStringDelete (home);
}
ENTER(CurrentRecursive);
parseConfigurationFileOptionsInDirectory (".");
ENTER(Preload);
preload (PreloadPathList);
}
static void parseEnvironmentOptions (void)
{
const char *envOptions = NULL;
const char* var = NULL;
ENTER(EnvVar);
if (Option.etags)
{
var = ETAGS_ENVIRONMENT;
envOptions = getenv (var);
}
if (envOptions == NULL)
{
var = CTAGS_ENVIRONMENT;
envOptions = getenv (var);
}
if (envOptions != NULL && envOptions [0] != '\0')
{
cookedArgs* const args = cArgNewFromString (envOptions);
verbose ("Reading options from $CTAGS\n");
parseOptions (args);
cArgDelete (args);
if (NonOptionEncountered)
error (WARNING, "Ignoring non-option in %s variable", var);
}
}
extern void readOptionConfiguration (void)
{
if (! SkipConfiguration)
{
parseConfigurationFileOptions ();
parseEnvironmentOptions ();
}
}
static void installDataPathList (void)
{
char* dataPath = getenv (CTAGS_DATA_PATH_ENVIRONMENT);
OptlibPathList = stringListNew ();
PreloadPathList = stringListNew ();
if (dataPath)
{
char* needle;
while (dataPath[0])
{
needle = strchr (dataPath, ':');
if (needle)
*needle = '\0';
appendToDataPathList (dataPath, FALSE);
if (needle)
{
*needle = ':';
dataPath = needle + 1;
}
else
break;
}
}
{
vString *home = getHome ();
if (home != NULL)
{
char *ctags_d;
ctags_d = combinePathAndFile (vStringValue (home), ".ctags.d");
appendToDataPathList (ctags_d, FALSE);
eFree (ctags_d);
vStringDelete (home);
}
}
#ifdef PKGSYSCONFDIR
appendToDataPathList (PKGSYSCONFDIR, FALSE);
#endif
}
static void installLibexecPathList (void)
{
char* libexecPath = getenv (CTAGS_LIBEXEC_PATH_ENVIRONMENT);
DriversPathList = stringListNew ();
if (libexecPath)
{
char* needle;
while (libexecPath[0])
{
needle = strchr (libexecPath, ':');
if (needle)
*needle = '\0';
appendToDataPathList (libexecPath, FALSE);
if (needle)
{
*needle = ':';
libexecPath = needle + 1;
}
else
break;
}
}
{
vString *home = getHome ();
if (home != NULL)
{
char *ctags_d;
ctags_d = combinePathAndFile (vStringValue (home), ".ctags.d");
appendToLibexecPathList (ctags_d, FALSE);
eFree (ctags_d);
vStringDelete (home);
}
}
#ifdef PKGLIBEXECDIR
appendToLibexecPathList (PKGLIBEXECDIR, FALSE);
#endif
}
/*
* Option initialization
*/
extern void initOptions (void)
{
OptionFiles = stringListNew ();
installDataPathList ();
installLibexecPathList ();
verboseSearchPathList (OptlibPathList, "OptlibPathList");
verboseSearchPathList (PreloadPathList, "PreloadPathList");
verboseSearchPathList (DriversPathList, "DriversPathList");
verbose ("Setting option defaults\n");
installHeaderListDefaults ();
verbose (" Installing default language mappings:\n");
installLanguageMapDefaults ();
verbose (" Installing default language aliases:\n");
installLanguageAliasesDefaults ();
/* always excluded by default */
verbose (" Installing default exclude patterns:\n");
processExcludeOption (NULL, "{arch}");
processExcludeOption (NULL, ".arch-ids");
processExcludeOption (NULL, ".arch-inventory");
processExcludeOption (NULL, "autom4te.cache");
processExcludeOption (NULL, "BitKeeper");
processExcludeOption (NULL, ".bzr");
processExcludeOption (NULL, ".bzrignore");
processExcludeOption (NULL, "CVS");
processExcludeOption (NULL, ".cvsignore");
processExcludeOption (NULL, "_darcs");
processExcludeOption (NULL, ".deps");
processExcludeOption (NULL, "EIFGEN");
processExcludeOption (NULL, ".git");
processExcludeOption (NULL, ".gitignore");
processExcludeOption (NULL, ".gitattributes");
processExcludeOption (NULL, ".hg");
processExcludeOption (NULL, ".hgignore");
processExcludeOption (NULL, "PENDING");
processExcludeOption (NULL, "RCS");
processExcludeOption (NULL, "RESYNC");
processExcludeOption (NULL, "SCCS");
processExcludeOption (NULL, ".svn");
processExcludeOption (NULL, "*~");
processExcludeOption (NULL, ".*.swp");
/* Exclude binary files
* -----------------------------------------------
*
* TODO
*
* It will be interesting if ctags can extract
* symbols from these binaries.
*
* --langdef=nm --regex-nm=...
* --langdef=elf --pre-processor-elf=/bin/nm ...
*
* vim/emacs users never wants the cursor to jump to
* a binary file but may wants to utilize the symbol
* information for completion.
*
* https://bitbucket.org/haypo/hachoir3 can be
* used the alternative for /bin/nm
*/
processExcludeOption (NULL, "*.o");
processExcludeOption (NULL, "*.a");
processExcludeOption (NULL, "*.so");
processExcludeOption (NULL, "*.obj");
processExcludeOption (NULL, "*.lib");
processExcludeOption (NULL, "*.dll");
processExcludeOption (NULL, "*.exe");
processExcludeOption (NULL, "*.gcno");
processExcludeOption (NULL, "*.gcda");
processExcludeOption (NULL, "*.class");
}
extern void freeOptionResources (void)
{
freeString (&Option.tagFileName);
freeString (&Option.fileList);
freeString (&Option.filterTerminator);
freeList (&Excluded);
freeList (&Option.ignore);
freeList (&Option.headerExt);
freeList (&Option.etagsInclude);
freeSearchPathList (&OptlibPathList);
freeSearchPathList (&PreloadPathList);
freeSearchPathList (&DriversPathList);
freeList (&OptionFiles);
}
/* vi:set tabstop=4 shiftwidth=4: */

File Metadata

Mime Type
text/x-c
Expires
Jun 4 2025, 6:41 PM (14 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3398700

Event Timeline