www.pudn.com > vim53src.zip > options.c


/***************************************************************************** 
*   $Id: options.c,v 6.11 1998/08/04 03:25:29 darren Exp $ 
* 
*   Copyright (c) 1996-1998, 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 
============================================================================*/ 
#ifdef HAVE_CONFIG_H 
# include  
#endif 
 
#ifdef DEBUG 
# include  
#endif 
 
#include "ctags.h" 
 
/*============================================================================ 
=   Defines 
============================================================================*/ 
 
/*---------------------------------------------------------------------------- 
 *  Miscellaneous defines 
 *--------------------------------------------------------------------------*/ 
 
#define CTAGS_INVOCATION  "\ 
  Usage: ctags [-aBeFnNRux] [-{f|o} name] [-h list] [-i [+-=]types]\n\ 
               [-I list] [-L file] [-p path] [--append] [--excmd=n|p|m]\n\ 
               [--format=level] [--help] [--if0] [--lang=lang] [--recurse]\n\ 
	       [--sort] [--totals] [--version] file(s)\n" 
 
#define ETAGS_INVOCATION  "\ 
  Usage: etags [-aRx] [-{f|o} name] [-h list] [-i [+-=]types] [-I list]\n\ 
               [-L file] [-p path] [--append] [--help] [--if0] [--lang=lang]\n\ 
	       [--recurse] [--totals] [--version] file(s)\n" 
 
#define CTAGS_ENVIRONMENT	"CTAGS" 
#define ETAGS_ENVIRONMENT	"ETAGS" 
 
/*  The following separators are permitted for list options. 
 */ 
#define EXTENSION_SEPARATORS   "." 
#define IGNORE_SEPARATORS   ", \t\n" 
 
#ifndef DEFAULT_FILE_FORMAT 
# define DEFAULT_FILE_FORMAT	2 
#endif 
 
/*============================================================================ 
=   Data declarations 
============================================================================*/ 
typedef struct { 
    int usedByEtags; 
    const char *const description; 
} optionDescription; 
 
/*============================================================================ 
=   Data definitions 
============================================================================*/ 
 
/*---------------------------------------------------------------------------- 
-   Globally shared 
----------------------------------------------------------------------------*/ 
 
static const char *const CExtensionList[] = { 
    "c", NULL 
}; 
static const char *const CppExtensionList[] = { 
#if !defined(MSDOS) && !defined(WIN32) && !defined(OS2) 
    "C", 
#endif 
    "c++", "cc", "cpp", "cxx", NULL 
}; 
static const char *const JavaExtensionList[] = { 
    "java", NULL 
}; 
static const char *const HeaderExtensionList[] = { 
    "h", "H", "hh", "hpp", "hxx", "h++", NULL 
}; 
 
optionValues Option = { 
    { 
	TRUE,		/* -ic */ 
	TRUE,		/* -id */ 
	TRUE,		/* -ie */ 
	TRUE,		/* -if */ 
	TRUE,		/* -ig */ 
	TRUE,		/* -ii */ 
	FALSE,		/* -im */ 
	TRUE,		/* -in */ 
	FALSE,		/* -ip */ 
	TRUE,		/* -is */ 
	TRUE,		/* -it */ 
	TRUE,		/* -iu */ 
	TRUE,		/* -iv */ 
	FALSE,		/* -ix */ 
	FALSE,		/* -iC */ 
	FALSE,		/* -iF */ 
	TRUE,		/* -iS */ 
    }, 
    { NULL, 0, 0 },	/* -I */ 
    FALSE,		/* -a */ 
    FALSE,		/* -B */ 
    FALSE,		/* -e */ 
#ifdef MACROS_USE_PATTERNS 
    EX_PATTERN,		/* -n, --excmd */ 
#else 
    EX_MIX,		/* -n, --excmd */ 
#endif 
    NULL,		/* -p */ 
    FALSE,		/* -R */ 
    TRUE,		/* -u, --sort */ 
    FALSE,		/* -x */ 
    NULL,		/* -L */ 
    NULL,		/* -o */ 
    HeaderExtensionList,/* -h */ 
#ifdef DEBUG 
    0, 0,		/* -D, -b */ 
#endif 
    FALSE,		/* started as etags */ 
    FALSE,		/* brace formatting */ 
    DEFAULT_FILE_FORMAT,/* --format */ 
    FALSE,		/* --if0 */ 
    LANG_AUTO,		/* --lang */ 
    {			/* --langmap */ 
	CExtensionList, 
	CppExtensionList, 
	JavaExtensionList, 
    }, 
    FALSE		/* --totals */ 
}; 
 
/*---------------------------------------------------------------------------- 
-   Locally used only 
----------------------------------------------------------------------------*/ 
 
static optionDescription LongOptionDescription[] = { 
 {1,"  -a   Append the tags to an existing tag file."}, 
#ifdef DEBUG 
 {1,"  -b "}, 
 {1,"       Set break line."}, 
#endif 
 {0,"  -B   Use backward searching patterns (?...?)."}, 
#ifdef DEBUG 
 {1,"  -D "}, 
 {1,"       Set debug level."}, 
#endif 
 {0,"  -e   Output tag file for use with Emacs."}, 
 {1,"  -f "}, 
 {1,"       Output tags to the specified file (default is \"tags\"; or \"TAGS\""}, 
 {1,"       if -e is specified). If specified as \"-\", tags are written to"}, 
 {1,"       standard output."}, 
 {0,"  -F   Use forward searching patterns (/.../) (default)."}, 
 {1,"  -h "}, 
 {1,"       Specifies a list of file extensions used for headers."}, 
 {1,"       The default list is \".h.H.hh.hpp.hxx.h++\"."}, 
 {1,"  -i "}, 
 {1,"       Specifies the list of tag types to include in the output file."}, 
 {1,"       \"Types\" is a group of letters designating the types of tags"}, 
 {1,"       affected. Each letter or group of letters may be preceded by"}, 
 {1,"       either a '+' sign (default, if omitted) to add it to those already"}, 
 {1,"       included, a '-' sign to exclude it from the list (e.g. to exclude"}, 
 {1,"       a default tag type), or an '=' sign to include its corresponding"}, 
 {1,"       tag type at the exclusion of those not listed. A space separating"}, 
 {1,"       the option letter from the list is optional. The following tag"}, 
 {1,"       types are supported (default settings are on except as noted):"}, 
 {1,"          c   class names"}, 
 {1,"          d   macro definitions"}, 
 {1,"          e   enumerators (values inside an enumeration)"}, 
 {1,"          f   function (or method) definitions"}, 
 {1,"          g   enumeration names"}, 
 {1,"          i   interface names (Java only)"}, 
 {1,"          m   data members [off]"}, 
 {1,"          n   namespace names (C++ only)"}, 
 {1,"          p   function prototypes [off]"}, 
 {1,"          s   structure names"}, 
 {1,"          t   typedefs"}, 
 {1,"          u   union names"}, 
 {1,"          v   variable definitions"}, 
 {1,"          x   external variable declarations [off]"}, 
 {1,"       In addition, the following modifiers are accepted:"}, 
 {1,"          C   include extra, class-qualified member tag entries [off]"}, 
 {1,"          F   include source filenames as tags [off]"}, 
 {1,"          S   include static tags"}, 
 {1,"  -I "}, 
 {1,"       A list of tokens to ignore is read from either the command line,"}, 
 {1,"       or the specified file (if leading character is '.', '/', or '\\')."}, 
 {1,"       Particularly useful when a function definition or declaration"}, 
 {1,"       contains some special macro before the parameter list."}, 
 {1,"  -L "}, 
 {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."}, 
 {1,"  -p "}, 
 {1,"       Default path to use for all (relative path) filenames."}, 
#ifdef RECURSE_SUPPORTED 
 {1,"  -R   Equivalent to --recurse=yes."}, 
#else 
 {1,"  -R   Not supported on this platform."}, 
#endif 
 {0,"  -u   Equivalent to --sort=no."}, 
 {1,"  -x   Print a tabular cross reference file to standard output."}, 
 {1,"  --append=[yes|no]"}, 
 {1,"       Indicates whether tags should be appended to existing tag file"}, 
 {1,"       (default=no)."}, 
 {0,"  --excmd=number|pattern|mix"}, 
#ifdef MACROS_USE_PATTERNS 
 {0,"       Uses the specified type of EX command to locate tags (default=pattern)."}, 
#else 
 {0,"       Uses the specified type of EX command to locate tags (default=mix)."}, 
#endif 
 {0,"  --format=level"}, 
#if DEFAULT_FILE_FORMAT==1 
 {0,"       Forces output of specified tag file format (default=1)."}, 
#else 
 {0,"       Forces output of specified tag file format (default=2)."}, 
#endif 
 {1,"  --help"}, 
 {1,"       Prints this option summary."}, 
 {1,"  --if0=[yes|no]"}, 
 {1,"       Indicates whether code within #if 0 conditional branches should"}, 
 {1,"       be examined for tags (default=no)."}, 
 {1,"  --lang=[c|c++|java]"}, 
 {1,"       Forces specified language, disabling automatic selection."}, 
 {1,"  --recurse=[yes|no]"}, 
#ifdef RECURSE_SUPPORTED 
 {1,"       Recurse into directories supplied on command line (default=no)."}, 
#else 
 {1,"       Not supported on this platform."}, 
#endif 
 {0,"  --sort=[yes|no]"}, 
 {0,"       Indicates whether tags should be sorted (default=yes)."}, 
 {1,"  --totals=[yes|no]"}, 
 {1,"       Prints statistics about source and tag files (default=no)."}, 
 {1,"  --version"}, 
 {1,"       Prints a version identifier to standard output."}, 
 {1, NULL} 
}; 
 
/*  Contains a set of strings describing the set of "features" compiled into 
 *  the code. 
 */ 
static const char *const Features[] = { 
#ifdef DEBUG 
    "debug", 
#endif 
#ifdef WIN32 
    "win32", 
#endif 
#ifdef DJGPP 
    "msdos_32", 
#else 
# ifdef MSDOS 
    "msdos_16", 
# endif 
#endif 
#ifdef OS2 
    "os2", 
#endif 
#ifdef AMIGA 
    "amiga", 
#endif 
#ifndef EXTERNAL_SORT 
    "internal_sort", 
#endif 
    NULL 
}; 
 
/*============================================================================ 
=   Function prototypes 
============================================================================*/ 
static void printfFeatureList __ARGS((FILE *const where)); 
static void printProgramIdentification __ARGS((FILE *const where)); 
static void printInvocationDescription __ARGS((FILE *const where)); 
static void printOptionDescriptions __ARGS((const optionDescription *const optDesc, FILE *const where)); 
static void printHelp __ARGS((const optionDescription *const optDesc, FILE *const where)); 
static char *readOptionArg __ARGS((const int option, char **const pArg, char *const *const argList, int *const pArgNum)); 
static unsigned int countExtensions __ARGS((const char *const list)); 
static const char *const *readExtensionList __ARGS((const char *const list)); 
static void clearTagList __ARGS((void)); 
static void applyTagInclusionList __ARGS((const char *const list)); 
static char *saveString __ARGS((const char *const string)); 
static void resizeIgnoreList __ARGS((void)); 
static void saveIgnoreToken __ARGS((const char *const ignoreToken)); 
static void readIgnoreList __ARGS((char *const list)); 
static void readIgnoreListFromFile __ARGS((const char *const fileName)); 
static void processHeaderListOption __ARGS((const int option, char **const argP, char *const *const argList, int *const argNumP)); 
static void processIgnoreOption __ARGS((const int option, char **const argP, char *const *const argList, int *const argNumP)); 
static boolean getBooleanOption __ARGS((const char *const optionName, const char *const parameter, const boolean defaultValue)); 
static void processExcmdOption __ARGS((const char *const optionName, const char *const parameter)); 
static void processFormatOption __ARGS((const char *const optionName, const char *const parameter)); 
static langType getLangType __ARGS((const char *const name)); 
static void processLangOption __ARGS((const char *const optionName, const char *const parameter)); 
static void installLangMap __ARGS((char *const map)); 
static void processLangMapOption __ARGS((const char *const optionName, const char *const parameter)); 
static void processLongOption __ARGS((const char *const optionString)); 
static void processCompoundOption __ARGS((const int option, char **const pArg, char *const *const argList, int *const pArgNum)); 
static boolean processSimpleOption __ARGS((const int option)); 
static void parseStringToArgs __ARGS((const char *const string, char *parsedBuffer, char **const argList, const unsigned int maxArgs)); 
static unsigned int countStringWords __ARGS((const char *const string)); 
static char **creatArgListForString __ARGS((const char *const string)); 
 
/*============================================================================ 
=   Function definitions 
============================================================================*/ 
 
static void printfFeatureList( where ) 
    FILE *const where; 
{ 
    int i; 
 
    for (i = 0 ; Features[i] != NULL ; ++i) 
    { 
	if (i == 0) 
	    fputs(" (", where); 
	fprintf(where, "%s+%s", (i>0 ? ", " : ""), Features[i]); 
    } 
    fputs(i>0 ? ")" : "", where); 
} 
 
static void printProgramIdentification( where ) 
    FILE *const where; 
{ 
    fprintf(where, "%s %s, by %s", PROGRAM_NAME, PROGRAM_VERSION, AUTHOR_NAME); 
    printfFeatureList(where); 
    fputs("\n", where); 
} 
 
static void printInvocationDescription( where ) 
    FILE *const where; 
{ 
    if (Option.startedAsEtags) 
	fprintf(where, ETAGS_INVOCATION); 
    else 
	fprintf(where, CTAGS_INVOCATION); 
} 
 
static void printOptionDescriptions( optDesc, where ) 
    const optionDescription *const optDesc; 
    FILE *const where; 
{ 
    int i; 
 
    for (i = 0 ; optDesc[i].description != NULL ; ++i) 
    { 
	if (! Option.startedAsEtags || optDesc[i].usedByEtags) 
	{ 
	    fputs(optDesc[i].description, where); 
	    fputc('\n', where); 
	} 
    } 
} 
 
static void printHelp( optDesc, where ) 
    const optionDescription *const optDesc; 
    FILE *const where; 
{ 
 
    printProgramIdentification(where); 
    fputs("\n", where); 
    printInvocationDescription(where); 
    fputs("\n", where); 
    printOptionDescriptions(optDesc, where); 
} 
 
static char *readOptionArg( option, pArg, argList, pArgNum ) 
    const int option; 
    char **const pArg; 
    char *const *const argList; 
    int *const pArgNum; 
{ 
    char *list; 
 
    if ((*pArg)[0] != '\0')	    /* does list immediately follow option? */ 
    { 
	list = *pArg; 
	*pArg += strlen(*pArg); 
    } 
    else if ((list = argList[++(*pArgNum)]) == NULL) /* at least 1 more arg? */ 
	error(FATAL, "-%c: Parameter missing", option); 
 
    DebugStatement( if (debug(DEBUG_OPTION)) fputs(list, errout); ) 
 
    return list; 
} 
 
/*  Reads a list of file extensions. 
 */ 
static unsigned int countExtensions( list ) 
    const char *const list; 
{ 
    unsigned int count = 0; 
    const char *p; 
 
    /*  Increase count by one if list does not begin with a separator. 
     */ 
    if (strchr(EXTENSION_SEPARATORS, list[0]) == NULL) 
	++count; 
 
    for (p = list  ;  *p != '\0'  ;  ++p) 
    { 
	if (strchr(EXTENSION_SEPARATORS, *p) != NULL) 
	    ++count; 
    } 
    return count + 1; 
} 
 
static const char *const *readExtensionList( list ) 
    const char *const list; 
{ 
    int extIndex = 0; 
    const char *extension; 
    const unsigned int numExtensions = countExtensions(list); 
    char *const extensionList  = (char *)malloc(strlen(list) + 1); 
    const char **const extensionArray = (const char **)malloc( 
					(numExtensions + 1) * sizeof(char *)); 
 
    if (extensionList == NULL  ||  extensionArray == NULL) 
	error(FATAL | PERROR, ""); 
    strcpy(extensionList, list); 
    extension = strtok(extensionList, EXTENSION_SEPARATORS); 
    while (extension != NULL) 
    { 
	DebugStatement( if (debug(DEBUG_STATUS)) 
			    printf("extension: %s\n", extension); ) 
	extensionArray[extIndex++] = extension; 
	extension = strtok(NULL, EXTENSION_SEPARATORS); 
    } 
    extensionArray[extIndex] = NULL; 
 
    return extensionArray; 
} 
 
static void clearTagList() 
{ 
    Option.include.classNames		= FALSE;	/* -ic */ 
    Option.include.defines		= FALSE;	/* -id */ 
    Option.include.enumerators		= FALSE;	/* -ie */ 
    Option.include.functions		= FALSE;	/* -if */ 
    Option.include.enumNames		= FALSE;	/* -ig */ 
    Option.include.interfaceNames	= FALSE;	/* -ii */ 
    Option.include.members		= FALSE;	/* -im */ 
    Option.include.namespaceNames	= FALSE;	/* -in */ 
    Option.include.prototypes		= FALSE;	/* -ip */ 
    Option.include.structNames		= FALSE;	/* -is */ 
    Option.include.typedefs		= FALSE;	/* -it */ 
    Option.include.unionNames		= FALSE;	/* -iu */ 
    Option.include.variables		= FALSE;	/* -iC */ 
    Option.include.sourceFiles		= FALSE;	/* -iF */ 
    Option.include.statics		= FALSE;	/* -iS */ 
} 
 
static void applyTagInclusionList( list ) 
    const char *const list; 
{ 
    boolean mode = TRUE;	/* default mode is to add following types */ 
    const char *p; 
 
    for (p = list  ;  *p != '\0'  ;  ++p) 
	switch (*p) 
	{ 
	    case '=':	/* exclusive mode; ONLY types following are included */ 
		clearTagList(); 
		mode = TRUE; 
		break; 
 
	    case '+':	mode = TRUE;	break;	/* include types following */ 
	    case '-':	mode = FALSE;	break;	/* exclude types following */ 
 
	    case 'c':	Option.include.classNames	= mode;		break; 
	    case 'd':	Option.include.defines		= mode;		break; 
	    case 'e':	Option.include.enumerators	= mode;		break; 
	    case 'f':	Option.include.functions	= mode;		break; 
	    case 'g':	Option.include.enumNames	= mode;		break; 
	    case 'i':	Option.include.interfaceNames	= mode;		break; 
	    case 'm':	Option.include.members		= mode;		break; 
	    case 'n':	Option.include.namespaceNames	= mode;		break; 
	    case 'p':	Option.include.prototypes	= mode;		break; 
	    case 's':	Option.include.structNames	= mode;		break; 
	    case 't':	Option.include.typedefs		= mode;		break; 
	    case 'u':	Option.include.unionNames	= mode;		break; 
	    case 'v':	Option.include.variables	= mode;		break; 
	    case 'x':	Option.include.externVars	= mode;		break; 
	    case 'C':	Option.include.classPrefix	= mode;		break; 
	    case 'F':	Option.include.sourceFiles	= mode;		break; 
	    case 'S':	Option.include.statics		= mode;		break; 
 
	    default: error(FATAL, "-i: Invalid tag option '%c'", *p);	break; 
	} 
} 
 
/*  Determines whether or not "name" should be ignored, per the ignore list. 
 */ 
extern boolean isIgnoreToken( name ) 
    const char *const name; 
{ 
    boolean ignore = FALSE; 
    unsigned int i; 
 
    for (i = 0  ;  i < Option.ignore.count ; ++i) 
    { 
	if (strcmp(Option.ignore.list[i], name) == 0) 
	{ 
	    ignore = TRUE; 
	    break; 
	} 
    } 
    return ignore; 
} 
 
static char *saveString( string ) 
    const char *const string; 
{ 
    char *const here = (char *)malloc(strlen(string) + 1); 
 
    if (here == NULL) 
	error(FATAL | PERROR, ""); 
    strcpy(here, string); 
 
    return here; 
} 
 
static void resizeIgnoreList() 
{ 
    size_t newSize; 
 
    Option.ignore.max = Option.ignore.count + 10; 
    newSize = Option.ignore.max * sizeof(char *); 
 
    if (Option.ignore.list == NULL) 
	Option.ignore.list = (char **)malloc(newSize); 
    else 
	Option.ignore.list = (char **)realloc(Option.ignore.list, newSize); 
    if (Option.ignore.list == NULL) 
	error(FATAL | PERROR, "cannot create ignore list"); 
} 
 
static void saveIgnoreToken( ignoreToken ) 
    const char *const ignoreToken; 
{ 
    const unsigned int i = Option.ignore.count++; 
 
    if (Option.ignore.count > Option.ignore.max) 
	resizeIgnoreList(); 
    Option.ignore.list[i] = saveString(ignoreToken); 
    DebugStatement( if (debug(DEBUG_STATUS)) 
			printf("ignore token: %s\n", ignoreToken); ) 
} 
 
static void readIgnoreList( list ) 
    char *const list; 
{ 
    const char *token = strtok(list, IGNORE_SEPARATORS); 
 
    while (token != NULL) 
    { 
	saveIgnoreToken(token); 
	token = strtok(NULL, IGNORE_SEPARATORS); 
    } 
} 
 
static void readIgnoreListFromFile( fileName ) 
    const char *const fileName; 
{ 
    FILE *const fp = fopen(fileName, "r"); 
 
    if (fp == NULL) 
	error(FATAL | PERROR, "cannot open \"%s\"", fileName); 
    else 
    { 
	char ignoreToken[MaxNameLength]; 
 
	while (fscanf(fp, "%255s", ignoreToken) == 1) 
	    saveIgnoreToken(ignoreToken); 
    } 
} 
 
extern void freeIgnoreList() 
{ 
    while (Option.ignore.count > 0) 
	free(Option.ignore.list[--Option.ignore.count]); 
 
    if (Option.ignore.list != NULL) 
	free(Option.ignore.list); 
 
    Option.ignore.list = NULL; 
    Option.ignore.max = 0; 
} 
 
static void processHeaderListOption( option, argP, argList, argNumP ) 
    const int option; 
    char **const argP; 
    char *const *const argList; 
    int *const argNumP; 
{ 
    char *const list = readOptionArg(option, argP, argList, argNumP); 
 
    /*  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(list) == 0) 
	error(FATAL, "-h: Invalid list"); 
    else 
    { 
	DebugStatement( if (debug(DEBUG_STATUS)) 
			    printf("Header Extensions:\n"); ) 
	Option.headerExt = readExtensionList(list); 
    } 
} 
 
static void processIgnoreOption( option, argP, argList, argNumP ) 
    const int option; 
    char **const argP; 
    char *const *const argList; 
    int *const argNumP; 
{ 
    char *const list = readOptionArg(option, argP, argList, argNumP); 
 
    if (strchr("./\\", list[0]) != NULL) 
	readIgnoreListFromFile(list); 
    else 
	readIgnoreList(list); 
} 
 
static boolean getBooleanOption( optionName, parameter, defaultValue ) 
    const char *const optionName; 
    const char *const parameter; 
    const boolean defaultValue; 
{ 
    boolean selection = defaultValue; 
 
    if (parameter[0] == '\0') 
	selection = defaultValue; 
    else if (strcmp(parameter, "0") == 0  ||  strcmp(parameter, "no") == 0) 
	selection = FALSE; 
    else if (strcmp(parameter, "1") == 0  ||  strcmp(parameter, "yes") == 0) 
	selection = TRUE; 
    else 
	error(FATAL, "Invalid value for option --%s", optionName); 
 
    return selection; 
} 
 
static void processExcmdOption( optionName, parameter ) 
    const char *const optionName; 
    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 option --%s", optionName); 
	    break; 
    } 
} 
 
static void processFormatOption( optionName, parameter ) 
    const char *const optionName; 
    const char *const parameter; 
{ 
    unsigned int format; 
 
    if (sscanf(parameter, "%u", &format) < 1) 
	error(FATAL, "Missing or invalid value for \"--%s\" option",optionName); 
    else if (format <= (unsigned int)MaxSupportedTagFormat) 
	Option.tagFileFormat = format; 
    else 
	error(FATAL, "Unsupported value for \"--%s\" option", optionName); 
} 
 
extern const char *getLanguageName( language ) 
    const langType language; 
{ 
    static const char *const names[] = { "c", "c++", "java" }; 
 
    DebugStatement( if (sizeof(names)/sizeof(names[0]) != LANG_COUNT) 
	error(FATAL, "LangNames array not consistent with LANG enumeration"); ) 
 
    return names[(int)language]; 
} 
 
extern boolean strequiv( s1, s2 ) 
    const char *s1; 
    const char *s2; 
{ 
    boolean equivalent; 
 
    if (strcmp(s1, s2) == 0) 
	equivalent = TRUE; 
    else 
    { 
	equivalent = TRUE; 
	do 
	{ 
	    if (toupper(*s1) != toupper(*s2)) 
	    { 
		equivalent = FALSE; 
		break; 
	    } 
	} while (*s1++ != '\0'  &&  *s2++ != '\0'); 
    } 
    return equivalent; 
} 
 
static langType getLangType( name ) 
    const char *const name; 
{ 
    unsigned int i; 
    langType language = LANG_IGNORE; 
 
    for (i = 0  ;  i < LANG_COUNT  ;  ++i) 
    { 
         if (strequiv(name, getLanguageName((langType)i))) 
	 { 
	    language = (langType)i; 
	    break; 
	 } 
    } 
    return language; 
} 
 
static void processLangOption( optionName, parameter ) 
    const char *const optionName; 
    const char *const parameter; 
{ 
    const langType language = getLangType(parameter); 
 
    if (language == LANG_IGNORE) 
	error(FATAL, "Invalid value for option --%s", optionName); 
    else 
	Option.language = language; 
} 
 
static void installLangMap( map ) 
    char *const map; 
{ 
    char *const separator = strchr(map, ':'); 
 
    if (separator != NULL) 
    { 
	langType language; 
 
	*separator = '\0'; 
	language = getLangType(map); 
	if (language == LANG_IGNORE) 
	    error(FATAL, "Invalid language specified for option --langmap"); 
	DebugStatement( if (debug(DEBUG_STATUS)) 
			    printf("%s map:\n", map); ) 
	Option.langMap[(int)language] = readExtensionList(separator + 1); 
    } 
} 
 
static void processLangMapOption( optionName, parameter ) 
    const char *const __unused__ optionName; 
    const char *const parameter; 
{ 
    char *const maps = (char *)malloc(strlen(parameter) + 1); 
    char *map = maps; 
 
    if (maps == NULL) 
	error(FATAL | PERROR, ""); 
    strcpy(maps, parameter); 
 
    DebugStatement( if (debug(DEBUG_STATUS)) 
			printf("Language-extension maps:\n"); ) 
    while (map != NULL) 
    { 
	char *end = strchr(parameter, ','); 
 
	if (end != NULL) 
	    *end = '\0'; 
	installLangMap(map); 
	if (end != NULL) 
	    map = end + 1; 
	else 
	    map = NULL; 
    } 
    free(maps); 
} 
 
static void processLongOption( optionString ) 
    const char *const optionString; 
{ 
    enum { MaxOptionName = 10 }; 
    char optionName[MaxOptionName + 1]; 
    const char *const equal = strchr(optionString, '='); 
    const char *parameter = (equal == NULL) ? "" : equal + 1; 
    const size_t optionLength = (equal == NULL) ? strlen(optionString) : 
	    					  (equal - optionString); 
 
    DebugStatement( if (debug(DEBUG_OPTION)) 
			fprintf(errout, "Option: --%s\n", optionString); ) 
 
    strncpy(optionName, optionString, optionLength); 
    if (optionLength < (size_t)MaxOptionName) 
	optionName[optionLength] = '\0'; 
    else 
	optionName[(size_t)MaxOptionName] = '\0'; 
 
#define isOption(string)	(strcmp(optionName, string) == 0) 
    if (isOption("append")) 
	Option.append = getBooleanOption(optionName, parameter, TRUE); 
    else if (isOption("excmd")) 
	processExcmdOption(optionName, parameter); 
    else if (isOption("format")) 
	processFormatOption(optionName, parameter); 
    else if (isOption("help")) 
	{ printHelp(LongOptionDescription, stdout); exit(0); } 
    else if (isOption("if0")) 
	Option.if0 = getBooleanOption(optionName, parameter, TRUE); 
    else if (isOption("lang")) 
	processLangOption(optionName, parameter); 
    else if (isOption("langmap")) 
	processLangMapOption(optionName, parameter); 
    else if (isOption("recurse")) 
#ifdef RECURSE_SUPPORTED 
	Option.recurse = getBooleanOption(optionName, parameter, TRUE); 
#else 
	error(FATAL, "--%s option not supported on this host", optionName); 
#endif 
    else if (isOption("sort")) 
	Option.sorted = getBooleanOption(optionName, parameter, TRUE); 
    else if (isOption("totals")) 
	Option.printTotals = getBooleanOption(optionName, parameter, TRUE); 
    else if (isOption("version")) 
    { 
	printProgramIdentification(stdout); 
	exit(0); 
    } 
    else 
	error(FATAL, "Unknown option: --%s", optionName); 
#undef isOption 
} 
 
static void processCompoundOption( option, pArg, argList, pArgNum ) 
    const int option; 
    char **const pArg; 
    char *const *const argList; 
    int *const pArgNum; 
{ 
    char *param; 
 
    DebugStatement( if (debug(DEBUG_OPTION) && option != '-') 
			fprintf(errout, "Option: -%c ", option); ) 
    switch (option) 
    { 
    /*	Options requiring parameters. 
     */ 
    case 'f': 
    case 'o':	Option.tagFileName=readOptionArg(option, pArg, argList,pArgNum); 
		break; 
    case 'h':	processHeaderListOption(option, pArg, argList, pArgNum); 
		break; 
    case 'i':	param = readOptionArg(option, pArg, argList, pArgNum); 
		applyTagInclusionList(param); 
		break; 
    case 'I':	processIgnoreOption(option, pArg, argList, pArgNum); 
		break; 
    case 'L':	Option.fileList = readOptionArg(option, pArg, argList, pArgNum); 
		break; 
    case 'p':	Option.path = readOptionArg(option, pArg, argList, pArgNum); 
		break; 
#ifdef DEBUG 
    case 'D':	param = readOptionArg(option, pArg, argList, pArgNum); 
		Option.debugLevel = atoi(param); 
		break; 
    case 'b':	param = readOptionArg(option, pArg, argList, pArgNum); 
		if (atol(param) < 0) 
		    error(FATAL, "-%c: Invalid line number", option); 
		Option.breakLine = atol(param); 
		break; 
#endif 
    default: error(FATAL, "Unknown option: -%c", option); break; 
    } 
    DebugStatement( if (debug(DEBUG_OPTION)) fputs("\n", errout); ) 
} 
 
static boolean processSimpleOption( option ) 
    const int option; 
{ 
    boolean handled = TRUE; 
 
    switch (option) 
    { 
	case 'a':	Option.append		= TRUE;		break; 
	case 'B':	Option.backward		= TRUE;		break; 
	case 'e':	Option.etags		= TRUE; 
			Option.sorted		= FALSE;	break; 
	case 'F':	Option.backward		= FALSE;	break; 
	case 'n':	Option.locate		= EX_LINENUM;	break; 
	case 'N':	Option.locate		= EX_PATTERN;	break; 
	case 'R': 
#ifdef RECURSE_SUPPORTED 
			Option.recurse		= TRUE;		break; 
#else 
			error(FATAL, "-R option not supported on this host"); 
#endif 
	case 'u':	Option.sorted		= FALSE;	break; 
	case 'w': 
	case 'W':	break; 
	case 'x':	Option.xref		= TRUE;		break; 
 
	case '?':	printHelp(LongOptionDescription, stdout); 
			exit(0); 
 
	default:	handled = FALSE;			break; 
    } 
 
    DebugStatement( if (handled && debug(DEBUG_OPTION)) 
			fprintf(errout, "Option: -%c\n", option); ) 
 
    return handled; 
} 
 
extern char *const *parseOptions( argList ) 
    char *const *const argList; 
{ 
    int	argNum; 
 
    for (argNum = 0  ;  argList[argNum] != NULL  ;  ++argNum) 
    { 
	char *arg = argList[argNum]; 
	int c; 
 
	if (*arg++ != '-')		/* stop at first non-option switch */ 
	    break; 
	else if (*arg == '-')		/* double dash: "--" */ 
	    processLongOption(arg + 1); 
	else while ((c = *arg++) != '\0') 
	{ 
	    if (! processSimpleOption(c)) 
		processCompoundOption(c, &arg, argList, &argNum); 
	} 
    } 
    return &argList[argNum]; 
} 
 
/*---------------------------------------------------------------------------- 
*-	Conversion of string into arg list 
----------------------------------------------------------------------------*/ 
 
static void parseStringToArgs( string, parsedBuffer, argList, maxArgs ) 
    const char *const string; 
    char *parsedBuffer; 
    char **const argList; 
    const unsigned int maxArgs; 
{ 
    boolean argInProgress = FALSE; 
    unsigned int count = 0; 
    const char *src; 
 
    for (src = string  ;  *src != '\0'  ;  ++src) 
    { 
	if (*src == ' ')			/* designates end of argument */ 
	{ 
	    if (argInProgress) 
	    { 
		*parsedBuffer++ = '\0';		/* terminate arg in progress */ 
		argInProgress = FALSE; 
		if (count >= maxArgs) 
		    break; 
	    } 
	} 
	else 
	{ 
	    if (! argInProgress) 
	    { 
		argInProgress = TRUE; 
		argList[count++] = parsedBuffer;	/* point to new arg */ 
	    } 
	    if (*src == '\\')			/* next character is literal */ 
		++src;				/* skip over '\\' */ 
	    *parsedBuffer++ = *src; 
	} 
    } 
    *parsedBuffer = '\0';		/* null terminate last argument */ 
    argList[count] = NULL;		/* terminate list */ 
} 
 
static unsigned int countStringWords( string ) 
    const char *const string; 
{ 
    const char *const whiteSpace = " \t\n"; 
    const char *p = string; 
    unsigned int numWords = 0; 
 
    p += strspn(p, whiteSpace);			/* skip over leading spaces */ 
    while (*p != '\0') 
    { 
	++numWords; 
	p += strcspn(p, whiteSpace);		/* skip to white space */ 
	p += strspn(p, whiteSpace);		/* skip to non-white space */ 
    } 
    return numWords; 
} 
 
static char **creatArgListForString( string ) 
    const char *const string; 
{ 
    const unsigned int numWords = countStringWords(string); 
    char **argList = NULL; 
 
    if (string != NULL  &&  string[0] != '\0') 
    { 
	/*  We place the parsed string at the end of the memory block, past 
	 *  the bottom of the argument table. 
	 */ 
	const size_t argListSize= (numWords + 1) * sizeof(char *); 
	const size_t blockSize	= argListSize + strlen(string) + 1; 
 
	argList	= (char **)malloc(blockSize); 
	if (argList != NULL) 
	    parseStringToArgs(string, (char *)argList + argListSize, 
			      argList, numWords); 
    } 
    return argList; 
} 
 
extern void *parseEnvironmentOptions() 
{ 
    const char *envOptions = NULL; 
    char **argList = NULL; 
 
    if (Option.startedAsEtags) 
	envOptions = getenv(ETAGS_ENVIRONMENT); 
    if (envOptions == NULL) 
	envOptions = getenv(CTAGS_ENVIRONMENT); 
    if (envOptions != NULL  &&  envOptions[0] != '\0') 
    { 
	argList = creatArgListForString(envOptions); 
	parseOptions(argList); 
    } 
    return argList; 
} 
 
/* vi:set tabstop=8 shiftwidth=4: */