// 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: */