8cb2cf0997
git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@4 ea778897-0a13-0410-b9d1-a72fbfd435f5
1484 lines
35 KiB
C
1484 lines
35 KiB
C
/*
|
|
*
|
|
* Copyright (c) 1996-2001, Darren Hiebert
|
|
*
|
|
* This source code is released for free distribution under the terms of the
|
|
* GNU General Public License.
|
|
*
|
|
* This module contains functions to process command line options.
|
|
*/
|
|
|
|
/*
|
|
* INCLUDE FILES
|
|
*/
|
|
#include "general.h" /* must always come first */
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h> /* to declare isspace () */
|
|
|
|
#include "ctags.h"
|
|
#include "main.h"
|
|
#define OPTION_WRITE
|
|
#include "options.h"
|
|
#include "parse.h"
|
|
|
|
/*
|
|
* MACROS
|
|
*/
|
|
#define INVOCATION "Usage: %s [options] [file(s)]\n"
|
|
|
|
#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 (MSDOS) || defined (WIN32) || defined (OS2) || defined (AMIGA) || defined (HAVE_OPENDIR)
|
|
# define RECURSE_SUPPORTED
|
|
#endif
|
|
|
|
#define isCompoundOption(c) (boolean) (strchr ("fohiILpDb", (c)) != NULL)
|
|
|
|
/*
|
|
* Data declarations
|
|
*/
|
|
|
|
enum eOptionLimits {
|
|
MaxHeaderExtensions = 100, /* maximum number of extensions in -h option */
|
|
MaxSupportedTagFormat = 2
|
|
};
|
|
|
|
typedef struct sOptionDescription {
|
|
int usedByEtags;
|
|
const char *const 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 */
|
|
} parametricOption;
|
|
|
|
typedef const struct {
|
|
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 */
|
|
} booleanOption;
|
|
|
|
/*
|
|
* DATA DEFINITIONS
|
|
*/
|
|
|
|
static boolean ParsedLeadingOptions = FALSE;
|
|
|
|
static const char *const HeaderExtensions [] = {
|
|
"h", "H", "hh", "hpp", "hxx", "h++", "inc", "def", NULL
|
|
};
|
|
|
|
optionValues Option = {
|
|
{
|
|
FALSE, /* --extra=f */
|
|
FALSE, /* --extra=q */
|
|
TRUE, /* --file-scope */
|
|
},
|
|
{
|
|
TRUE, /* -fields=a */
|
|
TRUE, /* -fields=f */
|
|
FALSE, /* -fields=m */
|
|
TRUE, /* -fields=i */
|
|
FALSE, /* -fields=k */
|
|
TRUE, /* -fields=z */
|
|
TRUE, /* -fields=K */
|
|
FALSE, /* -fields=l */
|
|
TRUE, /* -fields=n */
|
|
TRUE, /* -fields=s */
|
|
TRUE, /* -fields=P */
|
|
TRUE /* -fields=A */
|
|
},
|
|
NULL, /* -I */
|
|
FALSE, /* -a */
|
|
FALSE, /* -B */
|
|
FALSE, /* -e */
|
|
#ifdef MACROS_USE_PATTERNS
|
|
EX_PATTERN, /* -n, --excmd */
|
|
#else
|
|
EX_MIX, /* -n, --excmd */
|
|
#endif
|
|
FALSE, /* -R */
|
|
TRUE, /* -u, --sort */
|
|
FALSE, /* -V */
|
|
FALSE, /* -x */
|
|
NULL, /* -L */
|
|
NULL, /* -o */
|
|
NULL, /* -h */
|
|
NULL, /* --etags-include */
|
|
DEFAULT_FILE_FORMAT,/* --format */
|
|
FALSE, /* --if0 */
|
|
FALSE, /* --kind-long */
|
|
LANG_AUTO, /* --lang */
|
|
TRUE, /* --links */
|
|
FALSE, /* --filter */
|
|
NULL, /* --filter-terminator */
|
|
FALSE, /* --qualified-tags */
|
|
FALSE, /* --tag-relative */
|
|
FALSE, /* --totals */
|
|
FALSE, /* --line-directives */
|
|
FALSE,
|
|
#ifdef TM_DEBUG
|
|
0, 0 /* -D, -b */
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
- Locally used only
|
|
*/
|
|
|
|
static optionDescription LongOptionDescription [] = {
|
|
{1," --version"},
|
|
{1," Print version identifier to standard output."},
|
|
{1, NULL}
|
|
};
|
|
|
|
static const char* const License =
|
|
"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\n\
|
|
of the License, or (at your option) any later version.\n\
|
|
\n\
|
|
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
|
|
#ifdef DJGPP
|
|
"msdos_32",
|
|
#else
|
|
# ifdef MSDOS
|
|
"msdos_16",
|
|
# endif
|
|
#endif
|
|
#ifdef OS2
|
|
"os2",
|
|
#endif
|
|
#ifdef AMIGA
|
|
"amiga",
|
|
#endif
|
|
#ifdef VMS
|
|
"vms",
|
|
#endif
|
|
#ifdef HAVE_FNMATCH
|
|
"wildcards",
|
|
#endif
|
|
#ifdef HAVE_REGEX
|
|
"regex",
|
|
#endif
|
|
#ifndef EXTERNAL_SORT
|
|
"internal-sort",
|
|
#endif
|
|
#ifdef CUSTOM_CONFIGURATION_FILE
|
|
"custom-conf",
|
|
#endif
|
|
#if (defined (MSDOS) || defined (WIN32) || defined (OS2)) && defined (UNIX_PATH_SEPARATOR)
|
|
"unix-path-separator",
|
|
#endif
|
|
#ifdef TM_DEBUG
|
|
"debug",
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
/*
|
|
* FUNCTION PROTOTYPES
|
|
*/
|
|
static boolean parseFileOptions (const char *const fileName);
|
|
|
|
/*
|
|
* FUNCTION DEFINITIONS
|
|
*/
|
|
|
|
extern void verbose (const char *const format, ...)
|
|
{
|
|
if (Option.verbose)
|
|
{
|
|
va_list ap;
|
|
va_start (ap, format);
|
|
vprintf (format, ap);
|
|
va_end (ap);
|
|
}
|
|
}
|
|
|
|
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 void checkOptions (void)
|
|
{
|
|
const char* notice;
|
|
if (Option.xref)
|
|
{
|
|
notice = "xref output";
|
|
if (Option.include.fileNames)
|
|
{
|
|
error (WARNING, "%s disables file name tags", notice);
|
|
Option.include.fileNames = 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);
|
|
}
|
|
#ifdef UPDATE_ENABLED
|
|
if (Option.update)
|
|
{
|
|
notice = "update option is not compatible with";
|
|
if (Option.etags)
|
|
error (FATAL, "%s emacs-style tags", notice);
|
|
if (Option.filter)
|
|
error (FATAL, "%s filter option", notice);
|
|
if (isDestinationStdout ())
|
|
error (FATAL, "%s tags to stdout", notice);
|
|
Option.append = TRUE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void setEtagsMode (void)
|
|
{
|
|
Option.etags = TRUE;
|
|
Option.sorted = FALSE;
|
|
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 ();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Cooked argument parsing
|
|
*/
|
|
|
|
static void parseShortOption (cookedArgs *const args)
|
|
{
|
|
args->simple [0] = *args->shortOptions++;
|
|
args->simple [1] = '\0';
|
|
args->item = 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
|
|
{
|
|
const size_t length = equal - item;
|
|
args->item = xMalloc (length + 1, char);
|
|
strncpy (args->item, item, length);
|
|
args->item [length] = '\0';
|
|
args->parameter = equal + 1;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
else if (*item == '-')
|
|
{
|
|
current->isOption = TRUE;
|
|
current->longOption = FALSE;
|
|
current->shortOptions = item + 1;
|
|
parseShortOption (current);
|
|
}
|
|
else
|
|
{
|
|
current->isOption = FALSE;
|
|
current->longOption = FALSE;
|
|
current->item = item;
|
|
current->parameter = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
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 (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;
|
|
}
|
|
if (Option.verbose)
|
|
{
|
|
printf ("\n now: ");
|
|
stringListPrint (slist);
|
|
putchar ('\n');
|
|
}
|
|
eFree (extensionList);
|
|
}
|
|
|
|
extern const char *fileExtension (const char *const fileName)
|
|
{
|
|
const char *extension;
|
|
const char *pDelimiter = NULL;
|
|
#ifdef QDOS
|
|
pDelimiter = strrchr (fileName, '_');
|
|
#endif
|
|
if (pDelimiter == NULL)
|
|
pDelimiter = strrchr (fileName, '.');
|
|
|
|
if (pDelimiter == NULL)
|
|
extension = "";
|
|
else
|
|
extension = pDelimiter + 1; /* skip to first char of extension */
|
|
|
|
return extension;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
static void processLanguageForceOption (const char *const option,
|
|
const char *const parameter)
|
|
{
|
|
langType language;
|
|
if (stricmp (parameter, "auto") == 0)
|
|
language = LANG_AUTO;
|
|
else
|
|
language = getNamedLanguage (parameter);
|
|
|
|
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, "Uknown language specified in \"%s\" option", 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* addLanguageMap (const langType language, char* map)
|
|
{
|
|
char* p = NULL;
|
|
const char first = *map;
|
|
if (first == EXTENSION_SEPARATOR) /* extension map */
|
|
{
|
|
++map;
|
|
p = skipPastMap (map);
|
|
if (*p == '\0')
|
|
{
|
|
verbose (" .%s", map);
|
|
addLanguageExtensionMap (language, map);
|
|
p = map + strlen (map);
|
|
}
|
|
else
|
|
{
|
|
const char separator = *p;
|
|
*p = '\0';
|
|
verbose (" .%s", map);
|
|
addLanguageExtensionMap (language, map);
|
|
*p = separator;
|
|
}
|
|
}
|
|
else if (first == PATTERN_START) /* pattern map */
|
|
{
|
|
++map;
|
|
for (p = map ; *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
|
|
{
|
|
*p++ = '\0';
|
|
verbose (" (%s)", map);
|
|
addLanguagePatternMap (language, map);
|
|
}
|
|
}
|
|
else
|
|
error (FATAL, "Badly formed language map for %s language",
|
|
getLanguageName (language));
|
|
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);
|
|
if (language != LANG_IGNORE)
|
|
{
|
|
char* p;
|
|
if (*list == '+')
|
|
++list;
|
|
else
|
|
clear = TRUE;
|
|
for (p = list ; *p != ',' && *p != '\0' ; ++p) /*no-op*/ ;
|
|
if (strnicmp (list, "default", 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);
|
|
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 specified in \"%s\" option", option);
|
|
map = next;
|
|
}
|
|
eFree (maps);
|
|
}
|
|
|
|
static void processLanguageListOption (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);
|
|
if (language == LANG_IGNORE)
|
|
error (WARNING, "Unknown language specified in \"%s\" option", 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);
|
|
}
|
|
|
|
static void processOptionFile (const char *const option,
|
|
const char *const parameter)
|
|
{
|
|
if (parameter == NULL || parameter [0] == '\0')
|
|
error (WARNING, "no option file supplied for \"%s\"", option);
|
|
else if (! parseFileOptions (parameter))
|
|
error (FATAL | PERROR, "cannot open option file \"%s\"", parameter);
|
|
}
|
|
|
|
static void installHeaderListDefaults (void)
|
|
{
|
|
Option.headerExt = stringListNewFromArgv (HeaderExtensions);
|
|
if (Option.verbose)
|
|
{
|
|
printf (" Setting default header extensions: ");
|
|
stringListPrint (Option.headerExt);
|
|
putchar ('\n');
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void addIgnoreListFromFile (const char *const fileName)
|
|
{
|
|
stringList* tokens = stringListNewFromFile (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 (MSDOS) || defined (WIN32) || defined (OS2)
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Specific option processing
|
|
*/
|
|
|
|
static void printfFeatureList (void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0 ; Features [i] != NULL ; ++i)
|
|
{
|
|
if (i == 0)
|
|
printf (" Optional compiled features: ");
|
|
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 printProgramIdentification (void)
|
|
{
|
|
printf ("%s %s, Copyright (C) 1996-2001 %s\n",
|
|
PROGRAM_NAME, PROGRAM_VERSION, AUTHOR_NAME);
|
|
printf (" Addresses: <%s>, %s\n", AUTHOR_EMAIL, PROGRAM_URL);
|
|
printfFeatureList ();
|
|
}
|
|
|
|
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 printHelp (const optionDescription *const optDesc)
|
|
{
|
|
|
|
printProgramIdentification ();
|
|
putchar ('\n');
|
|
printInvocationDescription ();
|
|
putchar ('\n');
|
|
printOptionDescriptions (optDesc);
|
|
}
|
|
|
|
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 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);
|
|
}
|
|
|
|
static void processEtagsInclude (const char *const __unused__ option,
|
|
const char *const parameter)
|
|
{
|
|
vString *const file = vStringNewInit (parameter);
|
|
if (Option.etagsInclude == NULL)
|
|
Option.etagsInclude = stringListNew ();
|
|
stringListAdd (Option.etagsInclude, file);
|
|
}
|
|
|
|
static void processFilterTerminatorOption (const char *const __unused__ option,
|
|
const char *const parameter)
|
|
{
|
|
freeString (&Option.filterTerminator);
|
|
Option.filterTerminator = stringCopy (parameter);
|
|
}
|
|
|
|
static void processExtensionFieldsOption (const char *const option,
|
|
const char *const parameter)
|
|
{
|
|
struct sExtFields *const field = &Option.extensionFields;
|
|
const char *p = parameter;
|
|
boolean mode = TRUE;
|
|
int c;
|
|
|
|
if (*p != '+' && *p != '-')
|
|
{
|
|
field->access = FALSE;
|
|
field->fileScope = FALSE;
|
|
field->implementation = FALSE;
|
|
field->inheritance = FALSE;
|
|
field->kind = FALSE;
|
|
field->kindKey = FALSE;
|
|
field->kindLong = FALSE;
|
|
field->language = FALSE;
|
|
field->scope = FALSE;
|
|
field->argList = FALSE;
|
|
}
|
|
while ((c = *p++) != '\0') switch (c)
|
|
{
|
|
case '+': mode = TRUE; break;
|
|
case '-': mode = FALSE; break;
|
|
|
|
case 'a': field->access = mode; break;
|
|
case 'f': field->fileScope = mode; break;
|
|
case 'm': field->implementation = mode; break;
|
|
case 'i': field->inheritance = mode; break;
|
|
case 'k': field->kind = mode; break;
|
|
case 'K': field->kindLong = mode; break;
|
|
case 'l': field->language = mode; break;
|
|
case 'n': field->lineNumber = mode; break;
|
|
case 's': field->scope = mode; break;
|
|
case 'z': field->kindKey = mode; break;
|
|
case 'P': field->filePosition = mode; break;
|
|
case 'A': field->argList = mode; break;
|
|
|
|
default: error(WARNING, "Unsupported parameter '%c' for \"%s\" option",
|
|
c, option);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void processExtraTagsOption (const char *const option,
|
|
const char *const parameter)
|
|
{
|
|
struct sInclude *const inc = &Option.include;
|
|
const char *p = parameter;
|
|
boolean mode = TRUE;
|
|
int c;
|
|
|
|
if (*p != '+' && *p != '-')
|
|
{
|
|
inc->fileNames = FALSE;
|
|
inc->qualifiedTags = FALSE;
|
|
#if 0
|
|
inc->fileScope = FALSE;
|
|
#endif
|
|
}
|
|
while ((c = *p++) != '\0') switch (c)
|
|
{
|
|
case '+': mode = TRUE; break;
|
|
case '-': mode = FALSE; break;
|
|
|
|
case 'f': inc->fileNames = mode; break;
|
|
case 'q': inc->qualifiedTags = mode; break;
|
|
#if 0
|
|
case 'F': inc->fileScope = mode; break;
|
|
#endif
|
|
|
|
default: error(WARNING, "Unsupported parameter '%c' for \"%s\" option",
|
|
c, option);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Option tables
|
|
*/
|
|
|
|
static parametricOption ParametricOptions [] = {
|
|
{ "etags-include", processEtagsInclude, FALSE },
|
|
{ "exclude", processExcludeOption, FALSE },
|
|
{ "excmd", processExcmdOption, FALSE },
|
|
{ "extra", processExtraTagsOption, FALSE },
|
|
{ "fields", processExtensionFieldsOption, FALSE },
|
|
{ "filter-terminator", processFilterTerminatorOption, TRUE },
|
|
{ "format", processFormatOption, TRUE },
|
|
{ "lang", processLanguageForceOption, FALSE },
|
|
{ "language", processLanguageForceOption, FALSE },
|
|
{ "language-force", processLanguageForceOption, FALSE },
|
|
{ "languages", processLanguageListOption, FALSE },
|
|
{ "langdef", processLanguageDefineOption, FALSE },
|
|
{ "langmap", processLanguageMapOption, FALSE },
|
|
{ "options", processOptionFile, FALSE },
|
|
};
|
|
|
|
static booleanOption BooleanOptions [] = {
|
|
{ "append", &Option.append, TRUE },
|
|
{ "file-scope", &Option.include.fileScope, FALSE },
|
|
{ "file-tags", &Option.include.fileNames, FALSE },
|
|
{ "filter", &Option.filter, TRUE },
|
|
{ "if0", &Option.if0, FALSE },
|
|
{ "kind-long", &Option.kindLong, TRUE },
|
|
{ "line-directives",&Option.lineDirectives, FALSE },
|
|
{ "nest",&Option.nestFunction, FALSE },
|
|
{ "links", &Option.followLinks, FALSE },
|
|
#ifdef RECURSE_SUPPORTED
|
|
{ "recurse", &Option.recurse, FALSE },
|
|
#endif
|
|
{ "sort", &Option.sorted, TRUE },
|
|
{ "tag-relative", &Option.tagRelative, TRUE },
|
|
{ "totals", &Option.printTotals, TRUE },
|
|
{ "verbose", &Option.verbose, FALSE },
|
|
};
|
|
|
|
/*
|
|
* Generic option parsing
|
|
*/
|
|
|
|
static void checkOptionOrder (const char* const option)
|
|
{
|
|
if (ParsedLeadingOptions)
|
|
error (FATAL, "-%s option may not follow a file name", 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->initOnly)
|
|
checkOptionOrder (option);
|
|
(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 (strcmp (parameter, "0" ) == 0 ||
|
|
strcmp (parameter, "no" ) == 0 ||
|
|
strcmp (parameter, "off") == 0)
|
|
selection = FALSE;
|
|
else if (strcmp (parameter, "1" ) == 0 ||
|
|
strcmp (parameter, "yes") == 0 ||
|
|
strcmp (parameter, "on" ) == 0)
|
|
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];
|
|
if (strcmp (option, entry->name) == 0)
|
|
{
|
|
found = TRUE;
|
|
if (entry->initOnly)
|
|
checkOptionOrder (option);
|
|
*entry->pValue = getBooleanOption (option, parameter);
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
static void processLongOption (const char *const option,
|
|
const char *const parameter)
|
|
{
|
|
if (parameter == NULL || *parameter == '\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))
|
|
;
|
|
#ifdef HAVE_REGEX
|
|
else if (processRegexOption (option, parameter))
|
|
;
|
|
#endif
|
|
#define isOption(item) (boolean) (strcmp (item,option) == 0)
|
|
else if (isOption ("help"))
|
|
{
|
|
printHelp (LongOptionDescription);
|
|
printKindOptions ();
|
|
exit (0);
|
|
}
|
|
else if (isOption ("license"))
|
|
{
|
|
printProgramIdentification ();
|
|
puts ("");
|
|
puts (License);
|
|
exit (0);
|
|
}
|
|
else if (isOption ("version"))
|
|
{
|
|
printProgramIdentification ();
|
|
exit (0);
|
|
}
|
|
#ifndef RECURSE_SUPPORTED
|
|
else if (isOption ("recurse"))
|
|
error (WARNING, "%s option not supported on this host", option);
|
|
#endif
|
|
else
|
|
error (FATAL, "Unknown option: --%s", option);
|
|
#undef isOption
|
|
}
|
|
|
|
static void processShortOption (const char *const option,
|
|
const char *const parameter)
|
|
{
|
|
if (parameter == NULL || *parameter == '\0')
|
|
verbose (" Option: -%s\n", option);
|
|
else
|
|
verbose (" Option: -%s %s\n", option, parameter);
|
|
|
|
if (isCompoundOption (*option) && *parameter == '\0')
|
|
error (FATAL, "Missing parameter for \"%s\" option", option);
|
|
else switch (*option)
|
|
{
|
|
case '?':
|
|
printHelp (LongOptionDescription);
|
|
exit (0);
|
|
break;
|
|
case 'a':
|
|
checkOptionOrder (option);
|
|
Option.append = TRUE;
|
|
break;
|
|
case 'B':
|
|
Option.backward = TRUE;
|
|
break;
|
|
case 'e':
|
|
checkOptionOrder (option);
|
|
setEtagsMode ();
|
|
break;
|
|
case 'f':
|
|
case 'o':
|
|
checkOptionOrder (option);
|
|
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 'h':
|
|
processHeaderListOption (*option, parameter);
|
|
break;
|
|
case 'i':
|
|
processLegacyKindOption (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);
|
|
Option.sorted = FALSE;
|
|
break;
|
|
case 'V':
|
|
Option.verbose = TRUE;
|
|
break;
|
|
case 'w':
|
|
/* silently ignored */
|
|
break;
|
|
case 'x':
|
|
checkOptionOrder (option);
|
|
Option.xref = TRUE;
|
|
break;
|
|
default:
|
|
error (FATAL, "Unknown option: -%s", option);
|
|
break;
|
|
}
|
|
}
|
|
|
|
extern 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);
|
|
}
|
|
}
|
|
|
|
extern void parseOptions (cookedArgs* const args)
|
|
{
|
|
while (! cArgOff (args) && cArgIsOption (args))
|
|
parseOption (args);
|
|
if (! cArgOff (args) && ! cArgIsOption (args))
|
|
ParsedLeadingOptions = TRUE;
|
|
}
|
|
|
|
static boolean parseFileOptions (const char* const fileName)
|
|
{
|
|
boolean fileFound = FALSE;
|
|
FILE* const fp = fopen (fileName, "r");
|
|
if (fp != NULL)
|
|
{
|
|
cookedArgs* const args = cArgNewFromLineFile (fp);
|
|
verbose ("Reading options from %s\n", fileName);
|
|
parseOptions (args);
|
|
cArgDelete (args);
|
|
fclose (fp);
|
|
fileFound = TRUE;
|
|
}
|
|
return fileFound;
|
|
}
|
|
|
|
extern void previewFirstOption (cookedArgs* const args)
|
|
{
|
|
if (cArgIsOption (args) &&
|
|
(strcmp (args->item, "V") == 0 || strcmp (args->item, "verbose") == 0))
|
|
{
|
|
parseOption (args);
|
|
}
|
|
}
|
|
|
|
static void parseConfigurationFileOptions (void)
|
|
{
|
|
const char* const home = getenv ("HOME");
|
|
parseFileOptions ("/etc/ctags.conf");
|
|
parseFileOptions ("/usr/local/etc/ctags.conf");
|
|
#ifdef CUSTOM_CONFIGURATION_FILE
|
|
parseFileOptions (CUSTOM_CONFIGURATION_FILE);
|
|
#endif
|
|
if (home != NULL)
|
|
{
|
|
vString* const dotFile = combinePathAndFile (home, ".ctags");
|
|
parseFileOptions (vStringValue (dotFile));
|
|
vStringDelete (dotFile);
|
|
}
|
|
parseFileOptions (".ctags");
|
|
}
|
|
|
|
static void parseEnvironmentOptions (void)
|
|
{
|
|
const char *envOptions = NULL;
|
|
|
|
if (Option.etags)
|
|
envOptions = getenv (ETAGS_ENVIRONMENT);
|
|
if (envOptions == NULL)
|
|
envOptions = getenv (CTAGS_ENVIRONMENT);
|
|
if (envOptions != NULL && envOptions [0] != '\0')
|
|
{
|
|
cookedArgs* const args = cArgNewFromString (envOptions);
|
|
verbose ("Reading options from $CTAGS\n");
|
|
parseOptions (args);
|
|
cArgDelete (args);
|
|
}
|
|
}
|
|
|
|
extern void readOptionConfiguration (void)
|
|
{
|
|
parseConfigurationFileOptions ();
|
|
parseEnvironmentOptions ();
|
|
}
|
|
|
|
/*
|
|
* Option initialization
|
|
*/
|
|
|
|
extern void initOptions (void)
|
|
{
|
|
verbose ("Setting option defaults\n");
|
|
installHeaderListDefaults ();
|
|
verbose (" Installing default language mappings:\n");
|
|
installLanguageMapDefaults ();
|
|
}
|
|
|
|
extern void freeOptionResources (void)
|
|
{
|
|
freeString (&Option.tagFileName);
|
|
freeString (&Option.fileList);
|
|
freeString (&Option.filterTerminator);
|
|
|
|
freeList (&Option.ignore);
|
|
freeList (&Option.headerExt);
|
|
freeList (&Option.etagsInclude);
|
|
}
|
|
|
|
/* vi:set tabstop=8 shiftwidth=4: */
|