📄 options.c
字号:
/*
* $Id: options.c,v 1.25 2004/02/28 15:19:45 darren Exp $
*
* Copyright (c) 1996-2003, 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 "debug.h"
#include "main.h"
#define OPTION_WRITE
#include "options.h"
#include "parse.h"
#include "routines.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 (HAVE_OPENDIR) || defined (HAVE_FINDFIRST) || defined (HAVE__FINDFIRST) || defined (AMIGA)
# 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 *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 NonOptionEncountered;
static stringList *OptionFiles;
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 = {
{
FALSE, /* --extra=f */
FALSE, /* --extra=q */
TRUE, /* --file-scope */
},
{
FALSE, /* -fields=a */
TRUE, /* -fields=f */
FALSE, /* -fields=m */
FALSE, /* -fields=i */
TRUE, /* -fields=k */
FALSE, /* -fields=z */
FALSE, /* -fields=K */
FALSE, /* -fields=l */
FALSE, /* -fields=n */
TRUE, /* -fields=s */
FALSE /* -fields=S */
},
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 */
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, /* --tag-relative */
FALSE, /* --totals */
FALSE, /* --line-directives */
#ifdef DEBUG
0, 0 /* -D, -b */
#endif
};
/*
- 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," -h <list>"},
{1," Specify list of file extensions to be treated as include files."},
{1," [\".h.H.hh.hpp.hxx.h++\"]."},
{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 source file names are 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," --append=[yes|no]"},
{1," Should tags should be appended to existing tag file [no]?"},
{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: \"fq\")."},
{1," --fields=[+|-]flags"},
{1," Include selected extension fields (flags: \"afmikKlnsSz\") [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," --help"},
{1," Print this option summary."},
{1," --if0=[yes|no]"},
{1," Should C code within #if 0 conditional branches be parsed [no]?"},
{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 source 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 langauges"},
{1," specified in the comma-separated 'list'. The list can contain any"},
{1," built-in or user-defined language [all]."},
{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-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," --options=file"},
{1," Specify file from which command line options should be read."},
{1," --recurse=[yes|no]"},
#ifdef RECURSE_SUPPORTED
{1," Recurse into directories supplied on command line [no]."},
#else
{1," Not supported on this platform."},
#endif
#ifdef HAVE_REGEX
{1," --regex-<LANG>=/line_pattern/name_pattern/[flags]"},
{1," Define regular expression for locating tags in specific language."},
#endif
{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 source and tag files [no]."},
{1," --verbose=[yes|no]"},
{1," Enable verbose messages describing actions on each source file."},
{1," --version"},
{1," Print version identifier to standard output."},
{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\n"
"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
#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 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 boolean filesRequired (void)
{
boolean result = FilesRequired;
if (Option.recurse)
result = FALSE;
return result;
}
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);
}
}
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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -