Branch: refs/heads/master Author: Jiří Techet techet@gmail.com Committer: Jiří Techet techet@gmail.com Date: Mon, 14 Mar 2022 19:07:30 UTC Commit: d5e2b706899dfbbf95272e5276a2eefe98dba537 https://github.com/geany/geany/commit/d5e2b706899dfbbf95272e5276a2eefe98dba5...
Log Message: ----------- Use uctags parser for sh
The parser is also only line-based but seems to handle many more things than our sh parser.
Modified Paths: -------------- ctags/Makefile.am ctags/parsers/geany_sh.c ctags/parsers/sh.c
Modified: ctags/Makefile.am 2 lines changed, 1 insertions(+), 1 deletions(-) =================================================================== @@ -87,7 +87,7 @@ parsers = \ parsers/rst.c \ parsers/ruby.c \ parsers/rust.c \ - parsers/geany_sh.c \ + parsers/sh.c \ parsers/sql.c \ parsers/geany_tcl.c \ parsers/geany_tex.c \
Modified: ctags/parsers/geany_sh.c 110 lines changed, 0 insertions(+), 110 deletions(-) =================================================================== @@ -1,110 +0,0 @@ -/* -* Copyright (c) 2000-2002, Darren Hiebert -* -* This source code is released for free distribution under the terms of the -* GNU General Public License version 2 or (at your option) any later version. -* -* This module contains functions for generating tags for scripts for the -* Bourne shell (and its derivatives, the Korn and Z shells). -*/ - -/* -* INCLUDE FILES -*/ -#include "general.h" /* must always come first */ - -#include <string.h> - -#include "parse.h" -#include "read.h" -#include "routines.h" -#include "vstring.h" -#include "xtag.h" - -/* -* DATA DEFINITIONS -*/ -typedef enum { - K_FUNCTION -} shKind; - -static kindDefinition ShKinds [] = { - { true, 'f', "function", "functions"} -}; - -/* -* FUNCTION DEFINITIONS -*/ - -/* Reject any tag "main" from a file named "configure". These appear in - * here-documents in GNU autoconf scripts and will add a haystack to the - * needle. - */ -static bool hackReject (const vString* const tagName) -{ - const char *const scriptName = baseFilename (getInputFileName ()); - bool result = (bool) (strcmp (scriptName, "configure") == 0 && - strcmp (vStringValue (tagName), "main") == 0); - return result; -} - -static void findShTags (void) -{ - vString *name = vStringNew (); - const unsigned char *line; - - while ((line = readLineFromInputFile ()) != NULL) - { - const unsigned char* cp = line; - bool functionFound = false; - - if (line [0] == '#') - continue; - - while (isspace (*cp)) - cp++; - if (strncmp ((const char*) cp, "function", (size_t) 8) == 0 && - isspace ((int) cp [8])) - { - functionFound = true; - cp += 8; - if (! isspace ((int) *cp)) - continue; - while (isspace ((int) *cp)) - ++cp; - } - if (! (isalnum ((int) *cp) || *cp == '_')) - continue; - while (isalnum ((int) *cp) || *cp == '_') - { - vStringPut (name, (int) *cp); - ++cp; - } - while (isspace ((int) *cp)) - ++cp; - if (*cp++ == '(') - { - while (isspace ((int) *cp)) - ++cp; - if (*cp == ')' && ! hackReject (name)) - functionFound = true; - } - if (functionFound) - makeSimpleTag (name, K_FUNCTION); - vStringClear (name); - } - vStringDelete (name); -} - -extern parserDefinition* ShParser (void) -{ - static const char *const extensions [] = { - "sh", "SH", "bsh", "bash", "ksh", "zsh", "ash", NULL - }; - parserDefinition* def = parserNew ("Sh"); - def->kindTable = ShKinds; - def->kindCount = ARRAY_SIZE (ShKinds); - def->extensions = extensions; - def->parser = findShTags; - return def; -}
Modified: ctags/parsers/sh.c 490 lines changed, 490 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1,490 @@ +/* +* Copyright (c) 2000-2002, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* This module contains functions for generating tags for scripts for the +* Bourne shell (and its derivatives, the Korn and Z shells). +*/ + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#include <string.h> + +#include "entry.h" +#include "kind.h" +#include "parse.h" +#include "read.h" +#include "promise.h" +#include "routines.h" +#include "vstring.h" +#include "xtag.h" + +/* +* DATA DEFINITIONS +*/ +typedef enum { + K_NOTHING = -1, /* place holder. Never appears on tags file. */ + K_ALIAS, + K_FUNCTION, + K_SOURCE, + K_HEREDOCLABEL, +} shKind; + +typedef enum { + R_SCRIPT_LOADED, +} shScriptRole; + +static roleDefinition ShScriptRoles [] = { + { true, "loaded", "loaded" }, +}; + +typedef enum { + R_HEREDOC_ENDMARKER, +} shHeredocRole; + +static roleDefinition ShHeredocRoles [] = { + { true, "endmarker", "end marker" }, +}; + +static kindDefinition ShKinds [] = { + { true, 'a', "alias", "aliases"}, + { true, 'f', "function", "functions"}, + { true, 's', "script", "script files", + .referenceOnly = true, ATTACH_ROLES (ShScriptRoles) }, + { true, 'h', "heredoc", "label for here document", + .referenceOnly = false, ATTACH_ROLES (ShHeredocRoles) }, +}; + +/* +* FUNCTION DEFINITIONS +*/ + +static bool isFileChar (int c) +{ + return (isalnum (c) + || c == '_' || c == '-' + || c == '/' || c == '.' + || c == '+' || c == '^' + || c == '%' || c == '@' + || c == '~'); +} + +static bool isIdentChar (int c) +{ + return (isalnum (c) || c == '_' || c == '-'); +} + +/* bash allows all kinds of crazy stuff as the identifier after 'function' */ +static bool isBashFunctionChar (int c) +{ + return (c > 1 /* NUL and SOH are disallowed */ && c != 0x7f && + /* blanks are disallowed, but VT and FF (and CR to some extent, but + * let's not fall into the pit of craziness) */ + c != ' ' && c != '\t' && c != '\n' && c != '\r' && + c != '"' && c != ''' && c != '$' && c != '`' && c != '\' && + c != '&' && c != ';' && + c != '(' && c != ')' && + c != '<' && c != '>'); +} + +static const unsigned char *skipDoubleString (const unsigned char *cp) +{ + const unsigned char* prev = cp; + cp++; + while ((*cp != '"' || *prev == '\') && *cp != '\0') + { + prev = cp; + cp++; + } + return cp; +} + +static const unsigned char *skipSingleString (const unsigned char *cp) +{ + cp++; + while (*cp != ''' && *cp != '\0') + cp++; + return cp; +} + +static bool isEnvCommand (const vString *cmd) +{ + const char *lc = vStringValue(cmd); + const char * tmp = baseFilename (lc); + + return (strcmp(tmp, "env") == 0); +} + +static int readDestfileName (const unsigned char *cp, vString *destfile) +{ + const unsigned char *origin = cp; + + while (isspace ((int) *cp)) + ++cp; + + /* >... */ + if (*cp != '>') + return 0; + + /* >>... */ + if (*cp == '>') + ++cp; + + while (isspace ((int) *cp)) + ++cp; + + if (!isFileChar ((int) *cp)) + return 0; + + vStringClear(destfile); + do { + vStringPut (destfile, (int) *cp); + ++cp; + } while (isFileChar ((int) *cp)); + + if (vStringLength(destfile) > 0) + return cp - origin; + + return 0; +} + +struct hereDocParsingState { + vString *args[2]; + vString *destfile; + langType sublang; + unsigned long startLine; + + int corkIndex; +}; + +static void hdocStateInit (struct hereDocParsingState *hstate) +{ + hstate->args[0] = vStringNew (); + hstate->args[1] = vStringNew (); + hstate->destfile = vStringNew (); + + hstate->corkIndex = CORK_NIL; + hstate->sublang = LANG_IGNORE; +} + +static void hdocStateClear (struct hereDocParsingState *hstate) +{ + vStringClear (hstate->args[0]); + vStringClear (hstate->args[1]); + vStringClear (hstate->destfile); +} + +static void hdocStateFini (struct hereDocParsingState *hstate) +{ + vStringDelete (hstate->args[0]); + vStringDelete (hstate->args[1]); + vStringDelete (hstate->destfile); +} + +static void hdocStateUpdateArgs (struct hereDocParsingState *hstate, + vString *name) +{ + if (vStringIsEmpty(hstate->args[0])) + vStringCopy(hstate->args[0], name); + else if (vStringIsEmpty(hstate->args[1])) + vStringCopy(hstate->args[1], name); +} + +static void hdocStateMakePromiseMaybe (struct hereDocParsingState *hstate) +{ + if (hstate->sublang != LANG_IGNORE) + makePromise (getLanguageName(hstate->sublang), + hstate->startLine, 0, + getInputLineNumber(), 0, + 0); + hstate->sublang = LANG_IGNORE; +} + +static void hdocStateRecordStartlineFromDestfileMaybe (struct hereDocParsingState *hstate) +{ + const char *f = vStringValue(hstate->destfile); + + if (hstate->sublang != LANG_IGNORE) + return; + + hstate->sublang = getLanguageForFilename (f, 0); + if (hstate->sublang != LANG_IGNORE) + hstate->startLine = getInputLineNumber () + 1; + vStringClear (hstate->destfile); +} + +static void hdocStateRecordStatelineMaybe (struct hereDocParsingState *hstate) +{ + if (!vStringIsEmpty(hstate->args[0])) + { + const char *cmd; + + cmd = vStringValue(hstate->args[0]); + if (isEnvCommand (hstate->args[0])) + { + cmd = NULL; + if (!vStringIsEmpty(hstate->args[1])) + cmd = vStringValue(hstate->args[1]); + } + + if (cmd) + { + hstate->sublang = getLanguageForCommand (cmd, 0); + if (hstate->sublang != LANG_IGNORE) + hstate->startLine = getInputLineNumber () + 1; + } + } + + if (vStringLength(hstate->destfile) > 0) + hdocStateRecordStartlineFromDestfileMaybe (hstate); +} + +static int hdocStateReadDestfileName (struct hereDocParsingState *hstate, + const unsigned char* cp, + const vString *const hereDocDelimiter) +{ + int d = readDestfileName (cp, hstate->destfile); + + if (d > 0 && hereDocDelimiter) + hdocStateRecordStartlineFromDestfileMaybe (hstate); + + return d; +} + +static void hdocStateUpdateTag (struct hereDocParsingState *hstate, unsigned long endLine) +{ + tagEntryInfo *tag = getEntryInCorkQueue (hstate->corkIndex); + if (tag) + { + tag->extensionFields.endLine = endLine; + hstate->corkIndex = CORK_NIL; + } +} + +static void findShTags (void) +{ + vString *name = vStringNew (); + const unsigned char *line; + vString *hereDocDelimiter = NULL; + bool hereDocIndented = false; + bool (* check_char)(int); + + struct hereDocParsingState hstate; + hdocStateInit (&hstate); + + while ((line = readLineFromInputFile ()) != NULL) + { + const unsigned char* cp = line; + shKind found_kind = K_NOTHING; + + if (hereDocDelimiter) + { + if (hereDocIndented) + { + while (*cp == '\t') + cp++; + } + if ((strncmp ((const char *) cp, vStringValue (hereDocDelimiter), vStringLength (hereDocDelimiter)) == 0) + && ((*(cp + vStringLength (hereDocDelimiter)) == '\0') + || isspace (*(cp + vStringLength (hereDocDelimiter)) ))) + { + hdocStateUpdateTag (&hstate, getInputLineNumber ()); + hdocStateMakePromiseMaybe (&hstate); + + if (!vStringIsEmpty(hereDocDelimiter)) + makeSimpleRefTag(hereDocDelimiter, K_HEREDOCLABEL, R_HEREDOC_ENDMARKER); + vStringDelete (hereDocDelimiter); + hereDocDelimiter = NULL; + } + continue; + } + + hdocStateClear (&hstate); + while (*cp != '\0') + { + /* jump over whitespace */ + while (isspace ((int)*cp)) + cp++; + + /* jump over strings */ + if (*cp == '"') + cp = skipDoubleString (cp); + else if (*cp == ''') + cp = skipSingleString (cp); + /* jump over comments */ + else if (*cp == '#') + break; + /* jump over here-documents */ + else if (cp[0] == '<' && cp[1] == '<') + { + const unsigned char *start, *end; + bool trimEscapeSequences = false; + bool quoted = false; + cp += 2; + /* an optional "-" strips leading tabulations from the heredoc lines */ + if (*cp != '-') + hereDocIndented = false; + else + { + hereDocIndented = true; + cp++; + } + while (isspace (*cp)) + cp++; + start = end = cp; + /* the delimiter can be surrounded by quotes */ + if (*cp == '"') + { + start++; + end = cp = skipDoubleString (cp); + /* we need not to worry about variable substitution, they + * don't happen in heredoc delimiter definition */ + trimEscapeSequences = true; + quoted = true; + } + else if (*cp == ''') + { + start++; + end = cp = skipSingleString (cp); + quoted = true; + } + else + { + while (isIdentChar ((int) *cp)) + cp++; + end = cp; + } + if (end > start || quoted) + { + /* The input may be broken as a shell script but we need to avoid + memory leaking. */ + if (hereDocDelimiter) + vStringClear(hereDocDelimiter); + else + hereDocDelimiter = vStringNew (); + for (; end > start; start++) + { + if (trimEscapeSequences && *start == '\') + start++; + vStringPut (hereDocDelimiter, *start); + } + if (vStringLength(hereDocDelimiter) > 0) + hstate.corkIndex = makeSimpleTag(hereDocDelimiter, K_HEREDOCLABEL); + + hdocStateRecordStatelineMaybe(&hstate); + } + } + + check_char = isBashFunctionChar; + + if (strncmp ((const char*) cp, "function", (size_t) 8) == 0 && + isspace ((int) cp [8])) + { + found_kind = K_FUNCTION; + cp += 8; + } + else if (strncmp ((const char*) cp, "alias", (size_t) 5) == 0 && + isspace ((int) cp [5])) + { + check_char = isIdentChar; + found_kind = K_ALIAS; + cp += 5; + } + else if (cp [0] == '.' + && isspace((int) cp [1])) + { + found_kind = K_SOURCE; + ++cp; + check_char = isFileChar; + } + else if (strncmp ((const char*) cp, "source", (size_t) 6) == 0 + && isspace((int) cp [6])) + { + found_kind = K_SOURCE; + cp += 6; + check_char = isFileChar; + } + + if (found_kind != K_NOTHING) + while (isspace ((int) *cp)) + ++cp; + + // Get the name of the function, alias or file to be read by source + if (! check_char ((int) *cp)) + { + found_kind = K_NOTHING; + + int d = hdocStateReadDestfileName (&hstate, cp, + hereDocDelimiter); + if (d > 0) + cp += d; + else if (*cp != '\0') + ++cp; + continue; + } + while (check_char ((int) *cp)) + { + vStringPut (name, (int) *cp); + ++cp; + } + + while (isspace ((int) *cp)) + ++cp; + + if ((found_kind != K_SOURCE) + && *cp == '(') + { + ++cp; + while (isspace ((int) *cp)) + ++cp; + if (*cp == ')') + { + found_kind = K_FUNCTION; + ++cp; + } + } + + if (found_kind != K_NOTHING) + { + if (found_kind == K_SOURCE) + makeSimpleRefTag (name, K_SOURCE, R_SCRIPT_LOADED); + else + makeSimpleTag (name, found_kind); + found_kind = K_NOTHING; + } + else if (!hereDocDelimiter) + hdocStateUpdateArgs (&hstate, name); + vStringClear (name); + } + } + hdocStateFini (&hstate); + vStringDelete (name); + if (hereDocDelimiter) + vStringDelete (hereDocDelimiter); +} + +extern parserDefinition* ShParser (void) +{ + static const char *const extensions [] = { + "sh", "SH", "bsh", "bash", "ksh", "zsh", "ash", NULL + }; + static const char *const aliases [] = { + "sh", "bash", "ksh", "zsh", "ash", + /* major mode name in emacs */ + "shell-script", + NULL + }; + parserDefinition* def = parserNew ("Sh"); + def->kindTable = ShKinds; + def->kindCount = ARRAY_SIZE (ShKinds); + def->extensions = extensions; + def->aliases = aliases; + def->parser = findShTags; + def->useCork = CORK_QUEUE; + return def; +}
-------------- This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).