cosmopolitan/third_party/ctags/parse.c
2022-11-13 13:26:28 -08:00

673 lines
17 KiB
C

// clang-format off
/*
* $Id: parse.c 597 2007-07-31 05:35:30Z dhiebert $
*
* 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 for managing source languages and
* dispatching files to the appropriate language parser.
*/
/*
* INCLUDE FILES
*/
#include "third_party/ctags/general.h" /* must always come first */
#include "libc/mem/alg.h"
#include "libc/str/str.h"
#include "third_party/ctags/debug.h"
#include "third_party/ctags/entry.h"
#include "third_party/ctags/main.h"
#define OPTION_WRITE
#include "third_party/ctags/options.h"
#include "third_party/ctags/parsers.h"
#include "third_party/ctags/read.h"
#include "third_party/ctags/routines.h"
#include "third_party/ctags/vstring.h"
/*
* DATA DEFINITIONS
*/
static parserDefinitionFunc* BuiltInParsers[] = { PARSER_LIST };
static parserDefinition** LanguageTable = NULL;
static unsigned int LanguageCount = 0;
/*
* FUNCTION DEFINITIONS
*/
extern void makeSimpleTag (
const vString* const name, kindOption* const kinds, const int kind)
{
if (kinds [kind].enabled && name != NULL && vStringLength (name) > 0)
{
tagEntryInfo e;
initTagEntry (&e, vStringValue (name));
e.kindName = kinds [kind].name;
e.kind = kinds [kind].letter;
makeTagEntry (&e);
}
}
/*
* parserDescription mapping management
*/
extern parserDefinition* parserNew (const char* name)
{
parserDefinition* result = xCalloc (1, parserDefinition);
result->name = eStrdup (name);
return result;
}
extern const char *getLanguageName (const langType language)
{
const char* result;
if (language == LANG_IGNORE)
result = "unknown";
else
{
Assert (0 <= language && language < (int) LanguageCount);
result = LanguageTable [language]->name;
}
return result;
}
extern langType getNamedLanguage (const char *const name)
{
langType result = LANG_IGNORE;
unsigned int i;
Assert (name != NULL);
for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i)
{
const parserDefinition* const lang = LanguageTable [i];
if (lang->name != NULL)
if (strcasecmp (name, lang->name) == 0)
result = i;
}
return result;
}
static langType getExtensionLanguage (const char *const extension)
{
langType result = LANG_IGNORE;
unsigned int i;
for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i)
{
stringList* const exts = LanguageTable [i]->currentExtensions;
if (exts != NULL && stringListExtensionMatched (exts, extension))
result = i;
}
return result;
}
static langType getPatternLanguage (const char *const fileName)
{
langType result = LANG_IGNORE;
const char* base = baseFilename (fileName);
unsigned int i;
for (i = 0 ; i < LanguageCount && result == LANG_IGNORE ; ++i)
{
stringList* const ptrns = LanguageTable [i]->currentPatterns;
if (ptrns != NULL && stringListFileMatched (ptrns, base))
result = i;
}
return result;
}
#ifdef SYS_INTERPRETER
/* The name of the language interpreter, either directly or as the argument
* to "env".
*/
static vString* determineInterpreter (const char* const cmd)
{
vString* const interpreter = vStringNew ();
const char* p = cmd;
do
{
vStringClear (interpreter);
for ( ; isspace ((int) *p) ; ++p)
; /* no-op */
for ( ; *p != '\0' && ! isspace ((int) *p) ; ++p)
vStringPut (interpreter, (int) *p);
vStringTerminate (interpreter);
} while (strcmp (vStringValue (interpreter), "env") == 0);
return interpreter;
}
static langType getInterpreterLanguage (const char *const fileName)
{
langType result = LANG_IGNORE;
FILE* const fp = fopen (fileName, "r");
if (fp != NULL)
{
vString* const vLine = vStringNew ();
const char* const line = readLine (vLine, fp);
if (line != NULL && line [0] == '#' && line [1] == '!')
{
const char* const lastSlash = strrchr (line, '/');
const char *const cmd = lastSlash != NULL ? lastSlash+1 : line+2;
vString* const interpreter = determineInterpreter (cmd);
result = getExtensionLanguage (vStringValue (interpreter));
if (result == LANG_IGNORE)
result = getNamedLanguage (vStringValue (interpreter));
vStringDelete (interpreter);
}
vStringDelete (vLine);
fclose (fp);
}
return result;
}
#endif
extern langType getFileLanguage (const char *const fileName)
{
langType language = Option.language;
if (language == LANG_AUTO)
{
language = getExtensionLanguage (fileExtension (fileName));
if (language == LANG_IGNORE)
language = getPatternLanguage (fileName);
#ifdef SYS_INTERPRETER
if (language == LANG_IGNORE)
{
fileStatus *status = eStat (fileName);
if (status->isExecutable)
language = getInterpreterLanguage (fileName);
}
#endif
}
return language;
}
extern void printLanguageMap (const langType language)
{
boolean first = TRUE;
unsigned int i;
stringList* map = LanguageTable [language]->currentPatterns;
Assert (0 <= language && language < (int) LanguageCount);
for (i = 0 ; map != NULL && i < stringListCount (map) ; ++i)
{
printf ("%s(%s)", (first ? "" : " "),
vStringValue (stringListItem (map, i)));
first = FALSE;
}
map = LanguageTable [language]->currentExtensions;
for (i = 0 ; map != NULL && i < stringListCount (map) ; ++i)
{
printf ("%s.%s", (first ? "" : " "),
vStringValue (stringListItem (map, i)));
first = FALSE;
}
}
extern void installLanguageMapDefault (const langType language)
{
parserDefinition* lang;
Assert (0 <= language && language < (int) LanguageCount);
lang = LanguageTable [language];
if (lang->currentPatterns != NULL)
stringListDelete (lang->currentPatterns);
if (lang->currentExtensions != NULL)
stringListDelete (lang->currentExtensions);
if (lang->patterns == NULL)
lang->currentPatterns = stringListNew ();
else
{
lang->currentPatterns =
stringListNewFromArgv (lang->patterns);
}
if (lang->extensions == NULL)
lang->currentExtensions = stringListNew ();
else
{
lang->currentExtensions =
stringListNewFromArgv (lang->extensions);
}
if (Option.verbose)
printLanguageMap (language);
verbose ("\n");
}
extern void installLanguageMapDefaults (void)
{
unsigned int i;
for (i = 0 ; i < LanguageCount ; ++i)
{
verbose (" %s: ", getLanguageName (i));
installLanguageMapDefault (i);
}
}
extern void clearLanguageMap (const langType language)
{
Assert (0 <= language && language < (int) LanguageCount);
stringListClear (LanguageTable [language]->currentPatterns);
stringListClear (LanguageTable [language]->currentExtensions);
}
extern void addLanguagePatternMap (const langType language, const char* ptrn)
{
vString* const str = vStringNewInit (ptrn);
parserDefinition* lang;
Assert (0 <= language && language < (int) LanguageCount);
lang = LanguageTable [language];
if (lang->currentPatterns == NULL)
lang->currentPatterns = stringListNew ();
stringListAdd (lang->currentPatterns, str);
}
extern boolean removeLanguageExtensionMap (const char *const extension)
{
boolean result = FALSE;
unsigned int i;
for (i = 0 ; i < LanguageCount && ! result ; ++i)
{
stringList* const exts = LanguageTable [i]->currentExtensions;
if (exts != NULL && stringListRemoveExtension (exts, extension))
{
verbose (" (removed from %s)", getLanguageName (i));
result = TRUE;
}
}
return result;
}
extern void addLanguageExtensionMap (
const langType language, const char* extension)
{
vString* const str = vStringNewInit (extension);
Assert (0 <= language && language < (int) LanguageCount);
removeLanguageExtensionMap (extension);
stringListAdd (LanguageTable [language]->currentExtensions, str);
}
extern void enableLanguage (const langType language, const boolean state)
{
Assert (0 <= language && language < (int) LanguageCount);
LanguageTable [language]->enabled = state;
}
extern void enableLanguages (const boolean state)
{
unsigned int i;
for (i = 0 ; i < LanguageCount ; ++i)
enableLanguage (i, state);
}
static void initializeParsers (void)
{
unsigned int i;
for (i = 0 ; i < LanguageCount ; ++i)
if (LanguageTable [i]->initialize != NULL)
(LanguageTable [i]->initialize) ((langType) i);
}
extern void initializeParsing (void)
{
unsigned int builtInCount;
unsigned int i;
builtInCount = sizeof (BuiltInParsers) / sizeof (BuiltInParsers [0]);
LanguageTable = xMalloc (builtInCount, parserDefinition*);
verbose ("Installing parsers: ");
for (i = 0 ; i < builtInCount ; ++i)
{
parserDefinition* const def = (*BuiltInParsers [i]) ();
if (def != NULL)
{
boolean accepted = FALSE;
if (def->name == NULL || def->name[0] == '\0')
error (FATAL, "parser definition must contain name\n");
else if (def->regex)
{
def->parser = findRegexTags;
accepted = TRUE;
}
else if ((def->parser == NULL) == (def->parser2 == NULL))
error (FATAL,
"%s parser definition must define one and only one parsing routine\n",
def->name);
else
accepted = TRUE;
if (accepted)
{
verbose ("%s%s", i > 0 ? ", " : "", def->name);
def->id = LanguageCount++;
LanguageTable [def->id] = def;
}
}
}
verbose ("\n");
enableLanguages (TRUE);
initializeParsers ();
}
extern void freeParserResources (void)
{
unsigned int i;
for (i = 0 ; i < LanguageCount ; ++i)
{
parserDefinition* const lang = LanguageTable [i];
freeList (&lang->currentPatterns);
freeList (&lang->currentExtensions);
eFree (lang->name);
lang->name = NULL;
eFree (lang);
}
if (LanguageTable != NULL)
eFree (LanguageTable);
LanguageTable = NULL;
LanguageCount = 0;
}
/*
* Option parsing
*/
extern void processLanguageDefineOption (
const char *const option, const char *const parameter __unused)
{
if (parameter [0] == '\0')
error (WARNING, "No language specified for \"%s\" option", option);
else if (getNamedLanguage (parameter) != LANG_IGNORE)
error (WARNING, "Language \"%s\" already defined", parameter);
else
{
unsigned int i = LanguageCount++;
parserDefinition* const def = parserNew (parameter);
def->parser = findRegexTags;
def->currentPatterns = stringListNew ();
def->currentExtensions = stringListNew ();
def->regex = TRUE;
def->enabled = TRUE;
def->id = i;
LanguageTable = xRealloc (LanguageTable, i + 1, parserDefinition*);
LanguageTable [i] = def;
}
}
static kindOption *langKindOption (const langType language, const int flag)
{
unsigned int i;
kindOption* result = NULL;
const parserDefinition* lang;
Assert (0 <= language && language < (int) LanguageCount);
lang = LanguageTable [language];
for (i=0 ; i < lang->kindCount && result == NULL ; ++i)
if (lang->kinds [i].letter == flag)
result = &lang->kinds [i];
return result;
}
static void disableLanguageKinds (const langType language)
{
const parserDefinition* lang;
Assert (0 <= language && language < (int) LanguageCount);
lang = LanguageTable [language];
if (lang->regex)
disableRegexKinds (language);
else
{
unsigned int i;
for (i = 0 ; i < lang->kindCount ; ++i)
lang->kinds [i].enabled = FALSE;
}
}
static boolean enableLanguageKind (
const langType language, const int kind, const boolean mode)
{
boolean result = FALSE;
if (LanguageTable [language]->regex)
result = enableRegexKind (language, kind, mode);
else
{
kindOption* const opt = langKindOption (language, kind);
if (opt != NULL)
{
opt->enabled = mode;
result = TRUE;
}
}
return result;
}
static void processLangKindOption (
const langType language, const char *const option,
const char *const parameter)
{
const char *p = parameter;
boolean mode = TRUE;
int c;
Assert (0 <= language && language < (int) LanguageCount);
if (*p != '+' && *p != '-')
disableLanguageKinds (language);
while ((c = *p++) != '\0') switch (c)
{
case '+': mode = TRUE; break;
case '-': mode = FALSE; break;
default:
if (! enableLanguageKind (language, c, mode))
error (WARNING, "Unsupported parameter '%c' for --%s option",
c, option);
break;
}
}
extern boolean processKindOption (
const char *const option, const char *const parameter)
{
boolean handled = FALSE;
const char* const dash = strchr (option, '-');
if (dash != NULL &&
(strcmp (dash + 1, "kinds") == 0 || strcmp (dash + 1, "types") == 0))
{
langType language;
vString* langName = vStringNew ();
vStringNCopyS (langName, option, dash - option);
language = getNamedLanguage (vStringValue (langName));
if (language == LANG_IGNORE)
error (WARNING, "Unknown language \"%s\" in \"%s\" option", vStringValue (langName), option);
else
processLangKindOption (language, option, parameter);
vStringDelete (langName);
handled = TRUE;
}
return handled;
}
static void printLanguageKind (const kindOption* const kind, boolean indent)
{
const char *const indentation = indent ? " " : "";
printf ("%s%c %s%s\n", indentation, kind->letter,
kind->description != NULL ? kind->description :
(kind->name != NULL ? kind->name : ""),
kind->enabled ? "" : " [off]");
}
static void printKinds (langType language, boolean indent)
{
const parserDefinition* lang;
Assert (0 <= language && language < (int) LanguageCount);
lang = LanguageTable [language];
if (lang->kinds != NULL || lang->regex)
{
unsigned int i;
for (i = 0 ; i < lang->kindCount ; ++i)
printLanguageKind (lang->kinds + i, indent);
printRegexKinds (language, indent);
}
}
extern void printLanguageKinds (const langType language)
{
if (language == LANG_AUTO)
{
unsigned int i;
for (i = 0 ; i < LanguageCount ; ++i)
{
const parserDefinition* const lang = LanguageTable [i];
printf ("%s%s\n", lang->name, lang->enabled ? "" : " [disabled]");
printKinds (i, TRUE);
}
}
else
printKinds (language, FALSE);
}
static void printMaps (const langType language)
{
const parserDefinition* lang;
unsigned int i;
Assert (0 <= language && language < (int) LanguageCount);
lang = LanguageTable [language];
printf ("%-8s", lang->name);
if (lang->currentExtensions != NULL)
for (i = 0 ; i < stringListCount (lang->currentExtensions) ; ++i)
printf (" *.%s", vStringValue (
stringListItem (lang->currentExtensions, i)));
if (lang->currentPatterns != NULL)
for (i = 0 ; i < stringListCount (lang->currentPatterns) ; ++i)
printf (" %s", vStringValue (
stringListItem (lang->currentPatterns, i)));
putchar ('\n');
}
extern void printLanguageMaps (const langType language)
{
if (language == LANG_AUTO)
{
unsigned int i;
for (i = 0 ; i < LanguageCount ; ++i)
printMaps (i);
}
else
printMaps (language);
}
static void printLanguage (const langType language)
{
const parserDefinition* lang;
Assert (0 <= language && language < (int) LanguageCount);
lang = LanguageTable [language];
if (lang->kinds != NULL || lang->regex)
printf ("%s%s\n", lang->name, lang->enabled ? "" : " [disabled]");
}
extern void printLanguageList (void)
{
unsigned int i;
for (i = 0 ; i < LanguageCount ; ++i)
printLanguage (i);
}
/*
* File parsing
*/
static void makeFileTag (const char *const fileName)
{
if (Option.include.fileNames)
{
tagEntryInfo tag;
initTagEntry (&tag, baseFilename (fileName));
tag.isFileEntry = TRUE;
tag.lineNumberEntry = TRUE;
tag.lineNumber = 1;
tag.kindName = "file";
tag.kind = 'F';
makeTagEntry (&tag);
}
}
static boolean createTagsForFile (
const char *const fileName, const langType language,
const unsigned int passCount)
{
boolean retried = FALSE;
Assert (0 <= language && language < (int) LanguageCount);
if (fileOpen (fileName, language))
{
const parserDefinition* const lang = LanguageTable [language];
if (Option.etags)
beginEtagsFile ();
makeFileTag (fileName);
if (lang->parser != NULL)
lang->parser ();
else if (lang->parser2 != NULL)
retried = lang->parser2 (passCount);
if (Option.etags)
endEtagsFile (getSourceFileTagPath ());
fileClose ();
}
return retried;
}
static boolean createTagsWithFallback (
const char *const fileName, const langType language)
{
const unsigned long numTags = TagFile.numTags.added;
fpos_t tagFilePosition;
unsigned int passCount = 0;
boolean tagFileResized = FALSE;
fgetpos (TagFile.fp, &tagFilePosition);
while (createTagsForFile (fileName, language, ++passCount))
{
/* Restore prior state of tag file.
*/
fsetpos (TagFile.fp, &tagFilePosition);
TagFile.numTags.added = numTags;
tagFileResized = TRUE;
}
return tagFileResized;
}
extern boolean parseFile (const char *const fileName)
{
boolean tagFileResized = FALSE;
langType language = Option.language;
if (Option.language == LANG_AUTO)
language = getFileLanguage (fileName);
Assert (language != LANG_AUTO);
if (language == LANG_IGNORE)
verbose ("ignoring %s (unknown language)\n", fileName);
else if (! LanguageTable [language]->enabled)
verbose ("ignoring %s (language disabled)\n", fileName);
else
{
if (Option.filter)
openTagFile ();
tagFileResized = createTagsWithFallback (fileName, language);
if (Option.filter)
closeTagFile (tagFileResized);
addTotals (1, 0L, 0L);
return tagFileResized;
}
return tagFileResized;
}
/* vi:set tabstop=4 shiftwidth=4 nowrap: */