static void findVimTags (void) { vString *name = vStringNew (); const unsigned char *line; boolean inFunction = FALSE; int scope; while ((line = fileReadLine ()) != NULL) { while (isspace ((int) *line)) ++line; if ((int) *line == '"') continue; /* skip comment */ if (strncmp ((const char*) line, "fu", (size_t) 2) == 0) { const unsigned char *cp = line + 1; inFunction = TRUE; if ((int) *++cp == 'n' && (int) *++cp == 'c' && (int) *++cp == 't' && (int) *++cp == 'i' && (int) *++cp == 'o' && (int) *++cp == 'n') ++cp; if ((int) *cp == '!') ++cp; if (isspace ((int) *cp)) { while (isspace ((int) *cp)) ++cp; cp = skipPrefix (cp, &scope); if (isupper ((int) *cp) || scope == 's' || scope == '<') { do { vStringPut (name, (int) *cp); ++cp; } while (isalnum ((int) *cp) || *cp == '_'); vStringTerminate (name); makeSimpleTag (name, VimKinds, K_FUNCTION); vStringClear (name); } } } if (strncmp ((const char*) line, "aug", (size_t) 3) == 0) { /* Found Autocommand Group (augroup) */ const unsigned char *cp = line + 2; if ((int) *++cp == 'r' && (int) *++cp == 'o' && (int) *++cp == 'u' && (int) *++cp == 'p') ++cp; if (isspace ((int) *cp)) { while (isspace ((int) *cp)) ++cp; if (strncasecmp ((const char*) cp, "end", (size_t) 3) != 0) { do { vStringPut (name, (int) *cp); ++cp; } while (isalnum ((int) *cp) || *cp == '_'); vStringTerminate (name); makeSimpleTag (name, VimKinds, K_AUGROUP); vStringClear (name); } } } if (strncmp ((const char*) line, "endf", (size_t) 4) == 0) inFunction = FALSE; if (!inFunction && strncmp ((const char*) line, "let", (size_t) 3) == 0) { /* we've found a variable declared outside of a function!! */ const unsigned char *cp = line + 3; /* get the name */ if (isspace ((int) *cp)) { /* deal with spaces, $, @ and & */ while (!isalnum ((int) *cp)) ++cp; cp = skipPrefix (cp, &scope); do { vStringPut (name, (int) *cp); ++cp; } while (isalnum ((int) *cp) || *cp == '_'); vStringTerminate (name); makeSimpleTag (name, VimKinds, K_VARIABLE); vStringClear (name); } } } vStringDelete (name); }
/* Algorithm adapted from from GNU etags. * Perl support by Bart Robinson <*****@*****.**> * Perl sub names: look for /^ [ \t\n]sub [ \t\n]+ [^ \t\n{ (]+/ */ static void findPerlTags (void) { vString *name = vStringNew (); vString *package = NULL; boolean skipPodDoc = FALSE; const unsigned char *line; /* Core modules AutoLoader and SelfLoader support delayed compilation * by allowing Perl code that follows __END__ and __DATA__ tokens, * respectively. When we detect that one of these modules is used * in the file, we continue processing even after we see the * corresponding token that would usually terminate parsing of the * file. */ enum { RESPECT_END = (1 << 0), RESPECT_DATA = (1 << 1), } respect_token = RESPECT_END | RESPECT_DATA; while ((line = fileReadLine ()) != NULL) { boolean spaceRequired = FALSE; boolean qualified = FALSE; const unsigned char *cp = line; perlKind kind = K_NONE; tagEntryInfo e; if (skipPodDoc) { if (strncmp ((const char*) line, "=cut", (size_t) 4) == 0) skipPodDoc = FALSE; continue; } else if (line [0] == '=') { skipPodDoc = isPodWord ((const char*)line + 1); continue; } else if (strcmp ((const char*) line, "__DATA__") == 0) { if (respect_token & RESPECT_DATA) break; else continue; } else if (strcmp ((const char*) line, "__END__") == 0) { if (respect_token & RESPECT_END) break; else continue; } else if (line [0] == '#') continue; while (isspace (*cp)) cp++; if (strncmp((const char*) cp, "sub", (size_t) 3) == 0) { TRACE("this looks like a sub\n"); cp += 3; kind = K_SUBROUTINE; spaceRequired = TRUE; qualified = TRUE; } else if (strncmp((const char*) cp, "use", (size_t) 3) == 0) { cp += 3; if (!isspace(*cp)) continue; while (*cp && isspace (*cp)) ++cp; if (strncmp((const char*) cp, "AutoLoader", (size_t) 10) == 0) { respect_token &= ~RESPECT_END; continue; } if (strncmp((const char*) cp, "SelfLoader", (size_t) 10) == 0) { respect_token &= ~RESPECT_DATA; continue; } if (strncmp((const char*) cp, "constant", (size_t) 8) != 0) continue; cp += 8; /* Skip up to the first non-space character, skipping empty * and comment lines. */ while (isspace(*cp)) cp++; while (!*cp || '#' == *cp) { cp = fileReadLine (); if (!cp) goto END_MAIN_WHILE; while (isspace (*cp)) cp++; } if ('{' == *cp) { ++cp; if (0 == parseConstantsFromHashRef(cp, name, package)) { vStringClear(name); continue; } else goto END_MAIN_WHILE; } kind = K_CONSTANT; spaceRequired = FALSE; qualified = TRUE; } else if (strncmp((const char*) cp, "package", (size_t) 7) == 0 && ('\0' == cp[7] || isspace(cp[7]))) { cp += 7; while (isspace (*cp)) cp++; while (!*cp || '#' == *cp) { cp = fileReadLine (); if (!cp) goto END_MAIN_WHILE; while (isspace (*cp)) cp++; } if (package == NULL) package = vStringNew (); else vStringClear (package); const unsigned char *const first = cp; while (*cp && (int) *cp != ';' && !isspace ((int) *cp)) { vStringPut (package, (int) *cp); cp++; } vStringCatS (package, "::"); cp = first; /* Rewind */ kind = K_PACKAGE; spaceRequired = FALSE; qualified = TRUE; } else if (strncmp((const char*) cp, "format", (size_t) 6) == 0) { cp += 6; kind = K_FORMAT; spaceRequired = TRUE; qualified = TRUE; } else { if (isIdentifier1 (*cp)) { const unsigned char *p = cp; while (isIdentifier (*p)) ++p; while (isspace (*p)) ++p; if ((int) *p == ':' && (int) *(p + 1) != ':') kind = K_LABEL; } } if (kind != K_NONE) { TRACE("cp0: %s\n", (const char *) cp); if (spaceRequired && *cp && !isspace (*cp)) continue; TRACE("cp1: %s\n", (const char *) cp); while (isspace (*cp)) cp++; while (!*cp || '#' == *cp) { /* Gobble up empty lines and comments */ cp = fileReadLine (); if (!cp) goto END_MAIN_WHILE; while (isspace (*cp)) cp++; } while (isIdentifier (*cp) || (K_PACKAGE == kind && ':' == *cp)) { vStringPut (name, (int) *cp); cp++; } if (K_FORMAT == kind && vStringLength (name) == 0 && /* cp did not advance */ '=' == *cp) { /* format's name is optional. If it's omitted, 'STDOUT' is assumed. */ vStringCatS (name, "STDOUT"); } vStringTerminate (name); TRACE("name: %s\n", name->buffer); if (0 == vStringLength(name)) { vStringClear(name); continue; } if (K_SUBROUTINE == kind) { /* * isSubroutineDeclaration() may consume several lines. So * we record line positions. */ initTagEntry(&e, vStringValue(name), NULL); if (TRUE == isSubroutineDeclaration(cp)) { if (TRUE == PerlKinds[K_SUBROUTINE_DECLARATION].enabled) { kind = K_SUBROUTINE_DECLARATION; } else { vStringClear (name); continue; } } else if (! PerlKinds[kind].enabled) { continue; } e.kind = &(PerlKinds[kind]); makeTagEntry(&e); if (Option.include.qualifiedTags && qualified && package != NULL && vStringLength (package) > 0) { vString *const qualifiedName = vStringNew (); vStringCopy (qualifiedName, package); vStringCat (qualifiedName, name); e.name = vStringValue(qualifiedName); makeTagEntry(&e); vStringDelete (qualifiedName); } } else if (vStringLength (name) > 0) { makeSimpleTag (name, PerlKinds, kind); if (Option.include.qualifiedTags && qualified && K_PACKAGE != kind && package != NULL && vStringLength (package) > 0) { vString *const qualifiedName = vStringNew (); vStringCopy (qualifiedName, package); vStringCat (qualifiedName, name); makeSimpleTag (qualifiedName, PerlKinds, kind); vStringDelete (qualifiedName); } } vStringClear (name); } } END_MAIN_WHILE: vStringDelete (name); if (package != NULL) vStringDelete (package); }
static void findAspTags (void) { vString *name = vStringNew (); const unsigned char *line; while ((line = fileReadLine ()) != NULL) { const unsigned char *cp = line; while (*cp != '\0') { /* jump over whitespace */ while (isspace ((int)*cp)) cp++; /* jump over strings */ if (*cp == '"') { cp++; while (*cp!='"' && *cp!='\0') cp++; } /* jump over comments */ else if (*cp == '\'') break; /* jump over end function/sub lines */ else if (strncasecmp ((const char*) cp, "end", (size_t) 3)== 0) { cp += 3; if (isspace ((int)*cp)) { while (isspace ((int)*cp)) ++cp; if (strncasecmp ((const char*) cp, "function", (size_t) 8) == 0) { cp+=8; break; } else if (strncasecmp ((const char*) cp, "sub", (size_t) 3) == 0) { cp+=3; break; } } } /* jump over exit function/sub lines */ else if (strncasecmp ((const char*) cp, "exit", (size_t) 4)==0) { cp += 4; if (isspace ((int) *cp)) { while (isspace ((int) *cp)) ++cp; if (strncasecmp ((const char*) cp, "function", (size_t) 8) == 0) { cp+=8; break; } else if (strncasecmp ((const char*) cp, "sub", (size_t) 3) == 0) { cp+=3; break; } } } /* function? */ else if (strncasecmp ((const char*) cp, "function", (size_t) 8) == 0) { cp += 8; if (isspace ((int) *cp)) { while (isspace ((int) *cp)) ++cp; while (isalnum ((int) *cp) || *cp == '_') { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); makeSimpleTag (name, AspKinds, K_FUNCTION); vStringClear (name); } } /* sub? */ else if (strncasecmp ((const char*) cp, "sub", (size_t) 3) == 0) { cp += 3; if (isspace ((int) *cp)) { while (isspace ((int) *cp)) ++cp; while (isalnum ((int) *cp) || *cp == '_') { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); makeSimpleTag (name, AspKinds, K_SUB); vStringClear (name); } } /* dim variable? */ else if (strncasecmp ((const char*) cp, "dim", (size_t) 3) == 0) { cp += 3; if (isspace ((int) *cp)) { while (isspace ((int) *cp)) ++cp; while (isalnum ((int) *cp) || *cp == '_') { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); makeSimpleTag (name, AspKinds, K_DIM); vStringClear (name); } } /* const declaration? */ else if (strncasecmp ((const char*) cp, "const", (size_t) 5) == 0) { cp += 5; if (isspace ((int) *cp)) { while (isspace ((int) *cp)) ++cp; while (isalnum ((int) *cp) || *cp == '_') { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); makeSimpleTag (name, AspKinds, K_CONST); vStringClear (name); } } /* nothing relevant */ else if (*cp != '\0') cp++; } } vStringDelete (name); }
static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent) { boolean is_class = FALSE; boolean read_next_token = TRUE; vString * saveScope = vStringNew (); token->nestLevel++; /* * Make this routine a bit more forgiving. * If called on an open_curly advance it */ if ( isType (token, TOKEN_OPEN_CURLY) && isKeyword(token, KEYWORD_NONE) ) readToken(token); if (! isType (token, TOKEN_CLOSE_CURLY)) { /* * Read until we find the closing brace, * any nested braces will be handled within */ do { read_next_token = TRUE; if (isKeyword (token, KEYWORD_this)) { /* * Means we are inside a class and have found * a class, not a function */ is_class = TRUE; vStringCopy(saveScope, token->scope); addToScope (token, parent->string); /* * Ignore the remainder of the line * findCmdTerm(token); */ parseLine (token, is_class); vStringCopy(token->scope, saveScope); } else if (isKeyword (token, KEYWORD_var)) { /* * Potentially we have found an inner function. * Set something to indicate the scope */ vStringCopy(saveScope, token->scope); addToScope (token, parent->string); parseLine (token, is_class); vStringCopy(token->scope, saveScope); } else if (isKeyword (token, KEYWORD_function)) { vStringCopy(saveScope, token->scope); addToScope (token, parent->string); parseFunction (token); vStringCopy(token->scope, saveScope); } else if (isType (token, TOKEN_OPEN_CURLY)) { /* Handle nested blocks */ parseBlock (token, parent); } else { /* * It is possible for a line to have no terminator * if the following line is a closing brace. * parseLine will detect this case and indicate * whether we should read an additional token. */ read_next_token = parseLine (token, is_class); } /* * Always read a new token unless we find a statement without * a ending terminator */ if( read_next_token ) readToken(token); /* * If we find a statement without a terminator consider the * block finished, otherwise the stack will be off by one. */ } while (! isType (token, TOKEN_CLOSE_CURLY) && read_next_token ); } vStringDelete(saveScope); token->nestLevel--; return is_class; }
extern void createRTags(void) { vString *vLine = vStringNew(); vString *name = vStringNew(); int ikind; const unsigned char *line; while ((line = fileReadLine()) != NULL) { const unsigned char *cp = (const unsigned char*)line; vStringClear(name); while ((*cp != '\0') && (*cp != '#')) { /* iterate to the end of line or to a comment */ ikind = -1; switch (*cp) { case 'l': case 's': if (strncasecmp((const char*)cp, "library", (size_t)7) == 0) { /* load a library: library(tools) */ cp += 7; SKIPSPACE(cp); if (*cp == '(') ikind = 1; else cp -= 7; } else if (strncasecmp((const char*)cp, "source", (size_t)6) == 0) { /* load a source file: source("myfile.r") */ cp += 6; SKIPSPACE(cp); if (*cp == '(') ikind = 2; else cp -= 6; } if (ikind != -1) { cp++; vStringClear(name); while ((!isspace((int)*cp)) && *cp != '\0' && *cp != ')') { vStringPut(name, (int)*cp); cp++; } vStringTerminate(name); /* if the string really exists, make a tag of it */ if (vStringLength(name) > 0) makeRTag(name, ikind); /* prepare for the next iteration */ vStringClear(name); } else { vStringPut(name, (int)*cp); cp++; } break; case '<': cp++; if (*cp == '-') { /* assignment: ident <- someval */ cp++; SKIPSPACE(cp); if (*cp == '\0') { /* not in this line, read next */ /* sometimes functions are declared this way: ident <- function(...) { ... } I don't know if there is a reason to write the function keyword in a new line */ if ((line = fileReadLine()) != NULL) { cp = (const unsigned char*)line; SKIPSPACE(cp); } } if (strncasecmp((const char*)cp, "function", (size_t)8) == 0) { /* it's a function: ident <- function(args) */ cp += 8; vStringTerminate(name); /* if the string really exists, make a tag of it */ if (vStringLength(name) > 0) makeRTag(name, 0); /* prepare for the next iteration */ vStringClear(name); break; } } case ' ': case '\x009': /* skip whitespace */ cp++; break; default: /* collect all characters that could be a part of an identifier */ vStringPut(name, (int)*cp); cp++; break; } } } vStringDelete(name); vStringDelete(vLine); }
/* Structs and enums are very similar syntax-wise. * It is possible to parse variants a bit more cleverly (e.g. make tuple variants functions and * struct variants structs) but it'd be too clever and the signature wouldn't make too much sense without * the enum's definition (e.g. for the type bounds) * * Struct/Enum format: * "struct/enum" <ident>[<type_bounds>] "{" [<ident>,]+ "}" * "struct/enum" <ident>[<type_bounds>] ";" * */ static void parseStructOrEnum (lexerState *lexer, vString *scope, int parent_kind, boolean is_struct) { int kind = is_struct ? K_STRUCT : K_ENUM; int field_kind = is_struct ? K_FIELD : K_VARIANT; int goal_tokens1[] = {';', '{'}; advanceToken(lexer, TRUE); if (lexer->cur_token != TOKEN_IDENT) return; addTag(lexer->token_str, NULL, kind, lexer->line, lexer->pos, scope, parent_kind); addToScope(scope, lexer->token_str); skipUntil(lexer, goal_tokens1, 2); if (lexer->cur_token == '{') { vString *field_name = vStringNew(); while (lexer->cur_token != TOKEN_EOF) { int goal_tokens2[] = {'}', ','}; /* Skip attributes. Format: * #[..] or #![..] * */ if (lexer->cur_token == '#') { advanceToken(lexer, TRUE); if (lexer->cur_token == '!') advanceToken(lexer, TRUE); if (lexer->cur_token == '[') { /* It's an attribute, skip it. */ skipUntil(lexer, NULL, 0); } else { /* Something's up with this field, skip to the next one */ skipUntil(lexer, goal_tokens2, 2); continue; } } if (lexer->cur_token == TOKEN_IDENT) { if (strcmp(lexer->token_str->buffer, "priv") == 0 || strcmp(lexer->token_str->buffer, "pub") == 0) { advanceToken(lexer, TRUE); if (lexer->cur_token != TOKEN_IDENT) { /* Something's up with this field, skip to the next one */ skipUntil(lexer, goal_tokens2, 2); continue; } } vStringClear(field_name); vStringCat(field_name, lexer->token_str); addTag(field_name, NULL, field_kind, lexer->line, lexer->pos, scope, kind); skipUntil(lexer, goal_tokens2, 2); } if (lexer->cur_token == '}') { advanceToken(lexer, TRUE); break; } advanceToken(lexer, TRUE); } vStringDelete(field_name); } }
static void findShTags (void) { vString *name = vStringNew (); const unsigned char *line; vString *hereDocDelimiter = NULL; boolean hereDocIndented = FALSE; boolean (* check_char)(int); while ((line = fileReadLine ()) != NULL) { const unsigned char* cp = line; shKind found_kind = K_NOTHING; if (hereDocDelimiter) { if (hereDocIndented) { while (*cp == '\t') cp++; } if (strcmp ((const char *) cp, vStringValue (hereDocDelimiter)) == 0) { vStringDelete (hereDocDelimiter); hereDocDelimiter = NULL; } continue; } 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; boolean trimEscapeSequences = FALSE; boolean 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 (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])) { found_kind = K_ALIAS; cp += 5; } else if (cp [0] == '.' && isspace((int) cp [1])) { found_kind = K_SOURCE; ++cp; } else if (strncmp ((const char*) cp, "source", (size_t) 6) == 0 && isspace((int) cp [6])) { found_kind = K_SOURCE; cp += 6; } if (found_kind != K_NOTHING) while (isspace ((int) *cp)) ++cp; // Get the name of the function, alias or file to be read by source check_char = isIdentChar; if (found_kind == K_SOURCE) check_char = isFileChar; if (! check_char ((int) *cp)) { found_kind = K_NOTHING; if (*cp != '\0') ++cp; continue; } while (check_char ((int) *cp)) { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); 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) { makeSimpleTag (name, ShKinds, found_kind); found_kind = K_NOTHING; } vStringClear (name); } } vStringDelete (name); if (hereDocDelimiter) vStringDelete (hereDocDelimiter); }
/* parse a function * * if @name is NULL, parses a normal function * function myfunc($foo, $bar) {} * function &myfunc($foo, $bar) {} * function myfunc($foo, $bar) : type {} * * if @name is not NULL, parses an anonymous function with name @name * $foo = function($foo, $bar) {} * $foo = function&($foo, $bar) {} * $foo = function($foo, $bar) use ($x, &$y) {} * $foo = function($foo, $bar) use ($x, &$y) : type {} */ static boolean parseFunction (tokenInfo *const token, const tokenInfo *name) { boolean readNext = TRUE; accessType access = CurrentStatement.access; implType impl = CurrentStatement.impl; tokenInfo *nameFree = NULL; readToken (token); /* skip a possible leading ampersand (return by reference) */ if (token->type == TOKEN_AMPERSAND) readToken (token); if (! name) { if (token->type != TOKEN_IDENTIFIER && token->type != TOKEN_KEYWORD) return FALSE; name = nameFree = newToken (); copyToken (nameFree, token, TRUE); readToken (token); } if (token->type == TOKEN_OPEN_PAREN) { vString *arglist = vStringNew (); int depth = 1; vStringPut (arglist, '('); do { readToken (token); switch (token->type) { case TOKEN_OPEN_PAREN: depth++; break; case TOKEN_CLOSE_PAREN: depth--; break; default: break; } /* display part */ switch (token->type) { case TOKEN_AMPERSAND: vStringPut (arglist, '&'); break; case TOKEN_CLOSE_CURLY: vStringPut (arglist, '}'); break; case TOKEN_CLOSE_PAREN: vStringPut (arglist, ')'); break; case TOKEN_CLOSE_SQUARE: vStringPut (arglist, ']'); break; case TOKEN_COLON: vStringPut (arglist, ':'); break; case TOKEN_COMMA: vStringCatS (arglist, ", "); break; case TOKEN_EQUAL_SIGN: vStringCatS (arglist, " = "); break; case TOKEN_OPEN_CURLY: vStringPut (arglist, '{'); break; case TOKEN_OPEN_PAREN: vStringPut (arglist, '('); break; case TOKEN_OPEN_SQUARE: vStringPut (arglist, '['); break; case TOKEN_PERIOD: vStringPut (arglist, '.'); break; case TOKEN_SEMICOLON: vStringPut (arglist, ';'); break; case TOKEN_BACKSLASH: vStringPut (arglist, '\\'); break; case TOKEN_STRING: { vStringCatS (arglist, "'"); vStringCat (arglist, token->string); vStringCatS (arglist, "'"); break; } case TOKEN_IDENTIFIER: case TOKEN_KEYWORD: case TOKEN_VARIABLE: { switch (vStringLast (arglist)) { case 0: case ' ': case '{': case '(': case '[': case '.': case '\\': /* no need for a space between those and the identifier */ break; default: vStringPut (arglist, ' '); break; } if (token->type == TOKEN_VARIABLE) vStringPut (arglist, '$'); vStringCat (arglist, token->string); break; } default: break; } } while (token->type != TOKEN_EOF && depth > 0); vStringTerminate (arglist); makeFunctionTag (name, arglist, access, impl); vStringDelete (arglist); readToken (token); /* normally it's an open brace or "use" keyword */ } /* skip use(...) */ if (token->type == TOKEN_KEYWORD && token->keyword == KEYWORD_use) { readToken (token); skipOverParens (token); } /* PHP7 return type declaration or if parsing Zephir, skip function return * type hint */ if ((getInputLanguage () == Lang_php && token->type == TOKEN_COLON) || (getInputLanguage () == Lang_zephir && token->type == TOKEN_OPERATOR)) { do readToken (token); while (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_BACKSLASH); } if (token->type == TOKEN_OPEN_CURLY) enterScope (token, name->string, K_FUNCTION); else readNext = FALSE; if (nameFree) deleteToken (nameFree); return readNext; }
static void findBetaTags (void) { vString *line = vStringNew (); boolean incomment = FALSE; boolean inquote = FALSE; boolean dovirtuals = BetaKinds [K_VIRTUAL].enabled; boolean dopatterns = BetaKinds [K_PATTERN].enabled; do { boolean foundfragmenthere = FALSE; /* find fragment definition (line that starts and ends with --) */ int last; int first; int c; vStringClear (line); while ((c = fileGetc ()) != EOF && c != '\n' && c != '\r') vStringPut (line, c); vStringTerminate (line); last = vStringLength (line) - 1; first = 0; /* skip white space at start and end of line */ while (last && isspace ((int) vStringChar (line, last))) last--; while (first < last && isspace ((int) vStringChar (line, first))) first++; /* if line still has a reasonable length and ... */ if (last - first > 4 && (vStringChar (line, first) == '-' && vStringChar (line, first + 1) == '-' && vStringChar (line, last) == '-' && vStringChar (line, last - 1) == '-')) { if (!incomment && !inquote) { foundfragmenthere = TRUE; /* skip past -- and whitespace. Also skip back past 'dopart' or 'attributes' to the :. We have to do this because there is no sensible way to include whitespace in a ctags token so the conventional space after the ':' would mess us up */ last -= 2; first += 2; while (last && vStringChar (line, last) != ':') last--; while (last && (isspace ((int) vStringChar (line, last-1)))) last--; while (first < last && (isspace ((int) vStringChar (line, first)) || vStringChar (line, first) == '-')) first++; /* If there's anything left it is a fragment title */ if (first < last - 1) { vStringChar (line, last) = 0; if (strcasecmp ("LIB", vStringValue (line) + first) && strcasecmp ("PROGRAM", vStringValue (line) + first)) { makeBetaTag (vStringValue (line) + first, K_FRAGMENT); } } } } else { int pos = 0; int len = vStringLength (line); if (inquote) goto stringtext; if (incomment) goto commenttext; programtext: for ( ; pos < len; pos++) { if (vStringChar (line, pos) == '\'') { pos++; inquote = TRUE; goto stringtext; } if (vStringChar (line, pos) == '{') { pos++; incomment = TRUE; goto commenttext; } if (vStringChar (line, pos) == '(' && pos < len - 1 && vStringChar (line, pos+1) == '*') { pos +=2; incomment = TRUE; goto commenttext; } /* * SLOT definition looks like this: * <<SLOT nameofslot: dopart>> * or * <<SLOT nameofslot: descriptor>> */ if (!foundfragmenthere && vStringChar (line, pos) == '<' && pos+1 < len && vStringChar (line, pos+1) == '<' && strstr (vStringValue (line) + pos, ">>")) { /* Found slot name, get start and end */ int eoname; char c2; pos += 2; /* skip past << */ /* skip past space before SLOT */ while (pos < len && isspace ((int) vStringChar (line, pos))) pos++; /* skip past SLOT */ if (pos+4 <= len && !strncasecmp (vStringValue(line) + pos, "SLOT", (size_t)4)) pos += 4; /* skip past space after SLOT */ while (pos < len && isspace ((int) vStringChar (line, pos))) pos++; eoname = pos; /* skip to end of name */ while (eoname < len && (c2 = vStringChar (line, eoname)) != '>' && c2 != ':' && !isspace ((int) c2)) eoname++; if (eoname < len) { vStringChar (line, eoname) = 0; if (strcasecmp ("LIB", vStringValue (line) + pos) && strcasecmp ("PROGRAM", vStringValue (line) + pos) && strcasecmp ("SLOT", vStringValue (line) + pos)) { makeBetaTag (vStringValue (line) + pos, K_SLOT); } } if (eoname+1 < len) { pos = eoname + 1; } else { pos = len; continue; } } /* Only patterns that are virtual, extensions of virtuals or * final bindings are normally included so as not to overload * totally. * That means one of the forms name:: name:< or name::< */ if (!foundfragmenthere && vStringChar (line, pos) == ':' && (dopatterns || (dovirtuals && (vStringChar (line, pos+1) == ':' || vStringChar (line, pos+1) == '<') ) ) ) { /* Found pattern name, get start and end */ int eoname = pos; int soname; while (eoname && isspace ((int) vStringChar (line, eoname-1))) eoname--; foundanothername: /* terminate right after name */ vStringChar (line, eoname) = 0; soname = eoname; while (soname && isbident (vStringChar (line, soname-1))) { soname--; } if (soname != eoname) { makeBetaTag (vStringValue (line) + soname, K_PATTERN); /* scan back past white space */ while (soname && isspace ((int) vStringChar (line, soname-1))) soname--; if (soname && vStringChar (line, soname-1) == ',') { /* we found a new pattern name before comma */ eoname = soname; goto foundanothername; } } } } goto endofline; commenttext: for ( ; pos < len; pos++) { if (vStringChar (line, pos) == '*' && pos < len - 1 && vStringChar (line, pos+1) == ')') { pos += 2; incomment = FALSE; goto programtext; } if (vStringChar (line, pos) == '}') { pos++; incomment = FALSE; goto programtext; } } goto endofline; stringtext: for ( ; pos < len; pos++) { if (vStringChar (line, pos) == '\\') { if (pos < len - 1) pos++; } else if (vStringChar (line, pos) == '\'') { pos++; /* support obsolete '' syntax */ if (pos < len && vStringChar (line, pos) == '\'') { continue; } inquote = FALSE; goto programtext; } } } endofline: inquote = FALSE; /* This shouldn't really make a difference */ } while (!feof (File.fp)); }
static void findNsisTags (void) { vString *name = vStringNew (); const unsigned char *line; while ((line = fileReadLine ()) != NULL) { const unsigned char* cp = line; while (isspace (*cp)) cp++; if (*cp == '#' || *cp == ';') continue; /* functions */ if (strncasecmp ((const char*) cp, "function", (size_t) 8) == 0 && isspace ((int) cp [8])) { cp += 8; /* skip all whitespace */ while (isspace ((int) *cp)) ++cp; while (isalnum ((int) *cp) || *cp == '_' || *cp == '-' || *cp == '.' || *cp == '!') { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); makeSimpleTag (name, NsisKinds, K_FUNCTION); vStringClear (name); } /* variables */ else if (strncasecmp ((const char*) cp, "var", (size_t) 3) == 0 && isspace ((int) cp [3])) { cp += 3; /* skip all whitespace */ while (isspace ((int) *cp)) ++cp; /* skip any flags */ while (*cp == '/') { ++cp; while (! isspace ((int) *cp)) ++cp; while (isspace ((int) *cp)) ++cp; } while (isalnum ((int) *cp) || *cp == '_') { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); makeSimpleTag (name, NsisKinds, K_VARIABLE); vStringClear (name); } /* sections */ else if (strncasecmp ((const char*) cp, "section", (size_t) 7) == 0 && isspace ((int) cp [7])) { boolean in_quotes = FALSE; cp += 7; /* skip all whitespace */ while (isspace ((int) *cp)) ++cp; while (isalnum ((int) *cp) || isspace ((int) *cp) || *cp == '_' || *cp == '-' || *cp == '.' || *cp == '!' || *cp == '"') { if (*cp == '"') { if (in_quotes) break; else { in_quotes = TRUE; ++cp; continue; } } vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); makeSimpleTag (name, NsisKinds, K_SECTION); vStringClear (name); } } vStringDelete (name); }
static void findMakeTagsCommon (void *data) { stringList *identifiers = stringListNew (); boolean newline = TRUE; boolean in_define = FALSE; boolean in_value = FALSE; boolean in_rule = FALSE; boolean variable_possible = TRUE; boolean appending = FALSE; int c; while ((c = nextChar ()) != EOF) { if (newline) { if (in_rule) { if (c == '\t' || (c = skipToNonWhite (c)) == '#') { skipLine (); /* skip rule or comment */ c = nextChar (); } else if (c != '\n') in_rule = FALSE; } else if (in_value) in_value = FALSE; stringListClear (identifiers); variable_possible = (boolean)(!in_rule); newline = FALSE; } if (c == '\n') newline = TRUE; else if (isspace (c)) continue; else if (c == '#') skipLine (); else if (variable_possible && c == '?') { c = nextChar (); ungetcToInputFile (c); variable_possible = (c == '='); } else if (variable_possible && c == '+') { c = nextChar (); ungetcToInputFile (c); variable_possible = (c == '='); appending = TRUE; } else if (variable_possible && c == ':' && stringListCount (identifiers) > 0) { c = nextChar (); ungetcToInputFile (c); if (c != '=') { unsigned int i; for (i = 0; i < stringListCount (identifiers); i++) newTarget (stringListItem (identifiers, i)); stringListClear (identifiers); in_rule = TRUE; } } else if (variable_possible && c == '=' && stringListCount (identifiers) == 1) { newMacro (stringListItem (identifiers, 0), FALSE, appending, data); in_value = TRUE; in_rule = FALSE; appending = FALSE; } else if (variable_possible && isIdentifier (c)) { vString *name = vStringNew (); readIdentifier (c, name); stringListAdd (identifiers, name); if (in_value && valuesFoundCB) valuesFoundCB (name, data); if (stringListCount (identifiers) == 1) { if (in_define && ! strcmp (vStringValue (name), "endef")) in_define = FALSE; else if (in_define) skipLine (); else if (! strcmp (vStringValue (name), "define")) { in_define = TRUE; c = skipToNonWhite (nextChar ()); vStringClear (name); /* all remaining characters on the line are the name -- even spaces */ while (c != EOF && c != '\n') { vStringPut (name, c); c = nextChar (); } if (c == '\n') ungetcToInputFile (c); vStringTerminate (name); vStringStripTrailing (name); newMacro (name, TRUE, FALSE, data); } else if (! strcmp (vStringValue (name), "export")) stringListClear (identifiers); else if (! strcmp (vStringValue (name), "include") || ! strcmp (vStringValue (name), "sinclude") || ! strcmp (vStringValue (name), "-include")) { boolean optional = (vStringValue (name)[0] == 'i')? FALSE: TRUE; while (1) { c = skipToNonWhite (nextChar ()); readIdentifier (c, name); vStringStripTrailing (name); if (isAcceptableAsInclude(name)) newInclude (name, optional); /* non-space characters after readIdentifier() may * be rejected by the function: * e.g. * include $* * * Here, remove such characters from input stream. */ do c = nextChar (); while (c != EOF && c != '\n' && (!isspace (c))); if (c == '\n') ungetcToInputFile (c); if (c == EOF || c == '\n') break; } } } } else variable_possible = FALSE; } stringListDelete (identifiers); }
static void parseString (vString *const string) { boolean verbatim = FALSE; boolean align = FALSE; boolean end = FALSE; vString *verbatimCloser = vStringNew (); vString *lastLine = vStringNew (); int prev = '\0'; int c; while (! end) { c = getcFromInputFile (); if (c == EOF) end = TRUE; else if (c == '"') { if (! verbatim) end = TRUE; else end = (boolean) (strcmp (vStringValue (lastLine), vStringValue (verbatimCloser)) == 0); } else if (c == '\n') { if (verbatim) vStringClear (lastLine); if (prev == '[' /* || prev == '{' */) { verbatim = TRUE; vStringClear (verbatimCloser); vStringClear (lastLine); if (prev == '{') vStringPut (verbatimCloser, '}'); else { vStringPut (verbatimCloser, ']'); align = TRUE; } vStringNCat (verbatimCloser, string, vStringLength (string) - 1); vStringClear (string); } if (verbatim && align) { do c = getcFromInputFile (); while (isspace (c)); } } else if (c == '%') c = parseEscapedCharacter (); if (! end) { vStringPut (string, c); if (verbatim) { vStringPut (lastLine, c); vStringTerminate (lastLine); } prev = c; } } vStringTerminate (string); vStringDelete (lastLine); vStringDelete (verbatimCloser); }
static boolean parseTag (tokenInfo *const token, texKind kind) { tokenInfo *const name = newToken (); vString * fullname; boolean useLongName = TRUE; fullname = vStringNew (); vStringClear (fullname); /* * Tex tags are of these formats: * \keyword{any number of words} * \keyword[short desc]{any number of words} * \keyword*[short desc]{any number of words} * * When a keyword is found, loop through all words within * the curly braces for the tag name. */ if (isType (token, TOKEN_KEYWORD)) { copyToken (name, token); readToken (token); } if (isType (token, TOKEN_OPEN_SQUARE)) { useLongName = FALSE; readToken (token); while (! isType (token, TOKEN_CLOSE_SQUARE) ) { if (isType (token, TOKEN_IDENTIFIER)) { if (fullname->length > 0) vStringCatS (fullname, " "); vStringCatS (fullname, vStringValue (token->string)); } readToken (token); } vStringTerminate (fullname); vStringCopy (name->string, fullname); makeTexTag (name, kind); } if (isType (token, TOKEN_STAR)) { readToken (token); } if (isType (token, TOKEN_OPEN_CURLY)) { readToken (token); while (! isType (token, TOKEN_CLOSE_CURLY) ) { if (isType (token, TOKEN_IDENTIFIER) && useLongName) { if (fullname->length > 0) vStringCatS (fullname, " "); vStringCatS (fullname, vStringValue (token->string)); } readToken (token); } if (useLongName) { vStringTerminate (fullname); vStringCopy (name->string, fullname); makeTexTag (name, kind); } } deleteToken (name); vStringDelete (fullname); return TRUE; }
static boolean loadPathKinds (xcmdPath *const path, const langType language) { enum pcoprocError r; FILE* pp = NULL; char * argv[3]; int status; vString * opt; char file_kind = getLanguageFileKind (language)->letter; opt = vStringNewInit(XCMD_LIST_KIND_OPTION); vStringCatS (opt, "="); vStringCatS (opt, getLanguageName(language)); argv[2] = NULL; argv[1] = vStringValue (opt); argv[0] = vStringValue (path->path); errno = 0; if (getuid() == 0 || geteuid() == 0) { verbose ("all xcmd feature is disabled when running ctags in root privilege\n"); vStringDelete (opt); return FALSE; } if (! isSafeExecutable (argv [0])) { vStringDelete (opt); return FALSE; } verbose ("loading path kinds of %s from [%s %s]\n", getLanguageName(language), argv[0], argv[1]); r = pcoprocOpen (vStringValue (path->path), argv, &pp, NULL); switch (r) { case PCOPROC_ERROR_WPIPE: error (WARNING | PERROR, "failed to make pipe to write to command: [%s %s]", argv[0], argv[1]); break; case PCOPROC_ERROR_RPIPE: error (WARNING | PERROR, "failed to make pipe to read from command: [%s %s]", argv[0], argv[1]); break; case PCOPROC_ERROR_FORK: error (WARNING | PERROR, "failed to do fork: [%s %s]", argv[0], argv[1]); break; case PCOPROC_SUCCESSFUL: break; } if (pp) { vString* vline = vStringNew(); while (readLineWithNoSeek (vline, pp)) { char* line; char kind_letter; vStringStripNewline (vline); line = vStringValue (vline); if (!loadPathKind (path, line, argv)) break; kind_letter = path->kinds [path->n_kinds - 1].letter; if (kind_letter == file_kind) error (FATAL, "Kind letter \'%c\' returned from xcmd %s of %s language is reserved in ctags main", kind_letter, vStringValue (path->path), getLanguageName (language)); } vStringDelete (vline); status = pcoprocClose (pp); /* TODO: Decode status */ verbose(" status: %d\n", status); if (status != 0) { if (status > 0 && WIFEXITED (status) && (WEXITSTATUS (status) == path->not_available_status)) verbose ("xcmd: the %s backend is not available\n", argv[0]); else error (WARNING, "xcmd exits abnormally status(%d): [%s %s]", status, argv[0], argv[1]); vStringDelete (opt); return FALSE; } } else { error (WARNING | PERROR, "cannot make pipe to xcmd: [%s %s]", argv[0], argv[1]); } vStringDelete (opt); return path->kinds == NULL? FALSE: TRUE; }
initPhpEntry (&e, token, K_FUNCTION, access); if (impl != IMPL_UNDEFINED) e.extensionFields.implementation = implToString (impl); if (arglist) e.extensionFields.signature = vStringValue (arglist); makePhpTagEntry (&e); } } static void *newPoolToken (void *createArg CTAGS_ATTR_UNUSED) { tokenInfo *token = xMalloc (1, tokenInfo); token->string = vStringNew (); token->scope = vStringNew (); return token; } static void clearPoolToken (void *data) { tokenInfo *token = data; token->type = TOKEN_UNDEFINED; token->keyword = KEYWORD_NONE; token->lineNumber = getInputLineNumber (); token->filePosition = getInputFilePosition (); token->parentKind = -1; vStringClear (token->string); vStringClear (token->scope);
static boolean parseCommand (const unsigned char *line) { vString *name = vStringNew (); boolean cmdProcessed = TRUE; /* * Found a user-defined command * * They can have many options preceeded by a dash * command! -nargs=+ -complete Select :call s:DB_execSql("select " . <q-args>) * The name of the command should be the first word not preceeded by a dash * */ const unsigned char *cp = line; if ( (int) *cp == '\\' ) { /* * We are recursively calling this function is the command * has been continued on to the next line * * Vim statements can be continued onto a newline using a \ * to indicate the previous line is continuing. * * com -nargs=1 -bang -complete=customlist,EditFileComplete * \ EditFile edit<bang> <args> * * If the following lines do not have a line continuation * the command must not be spanning multiple lines and should * be synatically incorrect. */ if ((int) *cp == '\\') ++cp; while (*cp && isspace ((int) *cp)) ++cp; } else if ( (!strncmp ((const char*) line, "comp", (size_t) 4) == 0) && (!strncmp ((const char*) line, "comc", (size_t) 4) == 0) && (strncmp ((const char*) line, "com", (size_t) 3) == 0) ) { cp += 2; if ((int) *++cp == 'm' && (int) *++cp == 'a' && (int) *++cp == 'n' && (int) *++cp == 'd') ++cp; if ((int) *cp == '!') ++cp; if ((int) *cp != ' ') { /* * :command must be followed by a space. If it is not, it is * not a valid command. * Treat the line as processed and continue. */ cmdProcessed = TRUE; goto cleanUp; } while (*cp && isspace ((int) *cp)) ++cp; } else { /* * We are recursively calling this function. If it does not start * with "com" or a line continuation character, we have moved off * the command line and should let the other routines parse this file. */ cmdProcessed = FALSE; goto cleanUp; } /* * Strip off any spaces and options which are part of the command. * These should preceed the command name. */ do { if (isspace ((int) *cp)) { ++cp; } else if (*cp == '-') { /* * Read until the next space which separates options or the name */ while (*cp && !isspace ((int) *cp)) ++cp; } } while ( *cp && !isalnum ((int) *cp) ); if ( ! *cp ) { /* * We have reached the end of the line without finding the command name. * Read the next line and continue processing it as a command. */ line = readVimLine(); parseCommand(line); goto cleanUp; } do { vStringPut (name, (int) *cp); ++cp; } while (isalnum ((int) *cp) || *cp == '_'); vStringTerminate (name); makeSimpleTag (name, VimKinds, K_COMMAND); vStringClear (name); cleanUp: vStringDelete (name); return cmdProcessed; }
/* Function format: * "fn" <ident>[<type_bounds>] "(" [<args>] ")" ["->" <ret_type>] "{" [<body>] "}"*/ static void parseFn (lexerState *lexer, vString *scope, int parent_kind) { int kind = (parent_kind == K_TRAIT || parent_kind == K_IMPL) ? K_METHOD : K_FN; vString *name; vString *arg_list; unsigned long line; fpos_t pos; int paren_level = 0; boolean found_paren = FALSE; boolean valid_signature = TRUE; advanceToken(lexer, TRUE); if (lexer->cur_token != TOKEN_IDENT) return; name = vStringNewCopy(lexer->token_str); arg_list = vStringNew(); line = lexer->line; pos = lexer->pos; advanceToken(lexer, TRUE); /* HACK: This is a bit coarse as far as what tag entry means by * 'arglist'... */ while (lexer->cur_token != '{' && lexer->cur_token != ';') { if (lexer->cur_token == '}') { valid_signature = FALSE; break; } else if (lexer->cur_token == '(') { found_paren = TRUE; paren_level++; } else if (lexer->cur_token == ')') { paren_level--; if (paren_level < 0) { valid_signature = FALSE; break; } } else if (lexer->cur_token == TOKEN_EOF) { valid_signature = FALSE; break; } writeCurTokenToStr(lexer, arg_list); advanceToken(lexer, FALSE); } if (!found_paren || paren_level != 0) valid_signature = FALSE; if (valid_signature) { vStringStripTrailing(arg_list); addTag(name, arg_list->buffer, kind, line, pos, scope, parent_kind); addToScope(scope, name); parseBlock(lexer, TRUE, kind, scope); } vStringDelete(name); vStringDelete(arg_list); }
static boolean parseMap (const unsigned char *line) { vString *name = vStringNew (); const unsigned char *cp = line; /* Remove map */ while (*cp && isalnum ((int) *cp)) ++cp; if ((int) *cp == '!') ++cp; /* * Maps follow this basic format * map * nnoremap <silent> <F8> :Tlist<CR> * map <unique> <Leader>scdt <Plug>GetColumnDataType * inoremap ,,, <esc>diwi<<esc>pa><cr></<esc>pa><esc>kA * inoremap <buffer> ( <C-R>=PreviewFunctionSignature()<LF> * * The Vim help shows the various special arguments available to a map: * 1.2 SPECIAL ARGUMENTS *:map-arguments* * <buffer> * <silent> * <script> * <unique> * <special> * <expr> * * Strip the special arguments from the map command, this should leave * the map name which we will use as the "name". */ do { while (*cp && isspace ((int) *cp)) ++cp; if (strncmp ((const char*) cp, "<Leader>", (size_t) 8) == 0) break; if ( strncmp ((const char*) cp, "<buffer>", (size_t) 8) == 0 || strncmp ((const char*) cp, "<silent>", (size_t) 8) == 0 || strncmp ((const char*) cp, "<script>", (size_t) 8) == 0 || strncmp ((const char*) cp, "<unique>", (size_t) 8) == 0 ) { cp += 8; continue; } if (strncmp ((const char*) cp, "<expr>", (size_t) 6) == 0) { cp += 6; continue; } if (strncmp ((const char*) cp, "<special>", (size_t) 9) == 0) { cp += 9; continue; } break; } while (*cp); do { vStringPut (name, (int) *cp); ++cp; } while (*cp && *cp != ' '); vStringTerminate (name); makeSimpleTag (name, VimKinds, K_MAP); vStringClear (name); vStringDelete (name); return TRUE; }
static void findMakeTags (void) { stringList *identifiers = stringListNew (); boolean newline = TRUE; boolean in_define = FALSE; boolean in_rule = FALSE; boolean variable_possible = TRUE; int c; while ((c = nextChar ()) != EOF) { if (newline) { if (in_rule) { if (c == '\t' || (c = skipToNonWhite (c)) == '#') { skipLine (); /* skip rule or comment */ c = nextChar (); } else if (c != '\n') in_rule = FALSE; } stringListClear (identifiers); variable_possible = (boolean)(!in_rule); newline = FALSE; } if (c == '\n') newline = TRUE; else if (isspace (c)) continue; else if (c == '#') skipLine (); else if (variable_possible && c == '?') { c = nextChar (); fileUngetc (c); variable_possible = (c == '='); } else if (variable_possible && c == ':' && stringListCount (identifiers) > 0) { c = nextChar (); fileUngetc (c); if (c != '=') { unsigned int i; for (i = 0; i < stringListCount (identifiers); i++) newTarget (stringListItem (identifiers, i)); stringListClear (identifiers); in_rule = TRUE; } } else if (variable_possible && c == '=' && stringListCount (identifiers) == 1) { newMacro (stringListItem (identifiers, 0)); skipLine (); in_rule = FALSE; } else if (variable_possible && isIdentifier (c)) { vString *name = vStringNew (); readIdentifier (c, name); stringListAdd (identifiers, name); if (stringListCount (identifiers) == 1) { if (in_define && ! strcmp (vStringValue (name), "endef")) in_define = FALSE; else if (in_define) skipLine (); else if (! strcmp (vStringValue (name), "define")) { in_define = TRUE; c = skipToNonWhite (nextChar ()); vStringClear (name); /* all remaining characters on the line are the name -- even spaces */ while (c != EOF && c != '\n') { vStringPut (name, c); c = nextChar (); } if (c == '\n') fileUngetc (c); vStringTerminate (name); vStringStripTrailing (name); newMacro (name); } else if (! strcmp (vStringValue (name), "export")) stringListClear (identifiers); } } else variable_possible = FALSE; } stringListDelete (identifiers); }
static void parseVimBallFile (const unsigned char *line) { vString *fname = vStringNew (); const unsigned char *cp; int file_line_count; int i; /* * Vimball Archives follow this format * " Vimball Archiver comment * UseVimball * finish * filename * line count (n) for filename * (n) lines * filename * line count (n) for filename * (n) lines * ... */ /* Next line should be "finish" */ line = readVimLine(); if (line == NULL) { return; } while (line != NULL) { /* Next line should be a filename */ line = readVimLine(); if (line == NULL) { return; } else { cp = line; do { vStringPut (fname, (int) *cp); ++cp; } while (isalnum ((int) *cp) || *cp == '.' || *cp == '/' || *cp == '\\'); vStringTerminate (fname); makeSimpleTag (fname, VimKinds, K_FILENAME); vStringClear (fname); } file_line_count = 0; /* Next line should be the line count of the file */ line = readVimLine(); if (line == NULL) { return; } else { file_line_count = atoi( (const char *) line ); } /* Read all lines of the file */ for ( i=0; i<file_line_count; i++ ) { line = readVimballLine(); if (line == NULL) { return; } } } vStringDelete (fname); }
extern void openTagFile (void) { setDefaultTagFileName (); TagsToStdout = isDestinationStdout (); if (TagFile.vLine == NULL) TagFile.vLine = vStringNew (); /* Open the tags file. */ if (TagsToStdout) TagFile.fp = tempFile ("w", &TagFile.name); else { boolean fileExists; setDefaultTagFileName (); TagFile.name = eStrdup (Option.tagFileName); fileExists = doesFileExist (TagFile.name); if (fileExists && ! isTagFile (TagFile.name)) error (FATAL, "\"%s\" doesn't look like a tag file; I refuse to overwrite it.", TagFile.name); if (Option.etags) { if (Option.append && fileExists) TagFile.fp = fopen (TagFile.name, "a+b"); else TagFile.fp = fopen (TagFile.name, "w+b"); } else { if (Option.append && fileExists) { TagFile.fp = fopen (TagFile.name, "r+"); if (TagFile.fp != NULL) { TagFile.numTags.prev = updatePseudoTags (TagFile.fp); fclose (TagFile.fp); TagFile.fp = fopen (TagFile.name, "a+"); } } else { TagFile.fp = fopen (TagFile.name, "w"); if (TagFile.fp != NULL) addPseudoTags (); } } if (TagFile.fp == NULL) { error (FATAL | PERROR, "cannot open tag file"); exit (1); } } if (TagsToStdout) TagFile.directory = eStrdup (CurrentDirectory); else TagFile.directory = absoluteDirname (TagFile.name); }
static void findHxTags (void) { vString *name = vStringNew (); vString *clsName = vStringNew(); vString *scope2 = vStringNew(); vString *laccess = vStringNew(); const char *const priv = "private"; const char *const pub = "public"; const unsigned char *line; while ((line = readLineFromInputFile ()) != NULL) { const unsigned char *cp = line; another: while (isspace (*cp)) cp++; vStringCopyS(laccess,priv); if (strncmp ((const char*) cp, "var", (size_t) 3) == 0 && isspace ((int) cp [3])) { cp += 3; while (isspace ((int) *cp)) ++cp; vStringClear (name); while (isalnum ((int) *cp) || *cp == '_') { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); makeSimpleTag (name, HxKinds, HXTAG_VARIABLE); vStringClear (name); } else if (strncmp ((const char*) cp, "function", (size_t) 8) == 0 && isspace ((int) cp [8])) { cp += 8; while (isspace ((int) *cp)) ++cp; vStringClear (name); while (isalnum ((int) *cp) || *cp == '_') { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); makeSimpleTag (name, HxKinds, HXTAG_METHODS); vStringClear (name); } else if (strncmp ((const char*) cp, "class", (size_t) 5) == 0 && isspace ((int) cp [5])) { cp += 5; while (isspace ((int) *cp)) ++cp; vStringClear (name); while (isalnum ((int) *cp) || *cp == '_') { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); makeSimpleTag (name, HxKinds, HXTAG_CLASS); vStringCopy(clsName,name); vStringClear (name); } else if (strncmp ((const char*) cp, "enum", (size_t) 4) == 0 && isspace ((int) cp [4])) { cp += 4; while (isspace ((int) *cp)) ++cp; vStringClear (name); while (isalnum ((int) *cp) || *cp == '_') { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); makeSimpleTag (name, HxKinds, HXTAG_ENUM); vStringClear (name); } else if (strncmp ((const char*) cp, "public", (size_t) 6) == 0 && isspace((int) cp [6])) { cp += 6; while (isspace ((int) *cp)) ++cp; vStringCopyS(laccess,pub); goto another; } else if (strncmp ((const char*) cp, "static", (size_t) 6) == 0 && isspace((int) cp [6])) { cp += 6; while (isspace ((int) *cp)) ++cp; goto another; } else if (strncmp ((const char*) cp, "interface", (size_t) 9) == 0 && isspace((int) cp [9])) { cp += 9; while (isspace ((int) *cp)) ++cp; vStringClear (name); while (isalnum ((int) *cp) || *cp == '_') { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); makeSimpleTag (name, HxKinds, HXTAG_INTERFACE); vStringClear (name); } else if (strncmp ((const char *) cp,"typedef",(size_t) 7) == 0 && isspace(((int) cp[7]))) { cp += 7; while (isspace ((int) *cp)) ++cp; vStringClear (name); while (isalnum ((int) *cp) || *cp == '_') { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); makeSimpleTag (name, HxKinds, HXTAG_TYPEDEF); vStringClear (name); } } vStringDelete(name); vStringDelete(clsName); vStringDelete(scope2); vStringDelete(laccess); }
e.lineNumber = token->lineNumber; e.filePosition = token->filePosition; if (xtag != XTAG_UNKNOWN) markTagExtraBit (&e, xtag); return makeTagEntry (&e); } return CORK_NIL; } static void *newPoolToken (void *createArg CTAGS_ATTR_UNUSED) { tokenInfo *token = xMalloc (1, tokenInfo); token->string = vStringNew (); return token; } static void deletePoolToken (void *data) { tokenInfo *token = data; vStringDelete (token->string); eFree (token); } static void clearPoolToken (void *data) { tokenInfo *token = data; token->type = TOKEN_UNDEFINED;
static void findSdlTags (void) { vString *name = vStringNew() ; const unsigned char *line; sdlKind kindNow = K_UNDEFINED ; while ((line = fileReadLine ()) != NULL) { const unsigned char *cp = line; skipWhitespace (&cp); if (canMatch (&cp, "TOKEN")) { kindNow = K_TOKEN ; do{ cp = fileReadLine() ; while (isspace ((int) *cp)) ++cp; }while(!canMatch(&cp,"Name")) ; cp+=4 ; while (isspace ((int) *cp)) ++cp; while (*cp != '\0') { vStringPut (name, (int) *cp); ++cp; } } else if (canMatch (&cp, "ELINK")) { kindNow = K_ELINK ; do{ cp = fileReadLine() ; while (isspace ((int) *cp)) ++cp; }while(!canMatch(&cp,"Name")) ; cp+=4 ; while (*cp != '\0') { vStringPut (name, (int) *cp); ++cp; } } else if (canMatch (&cp, "PATH")) { kindNow = K_PATH ; do{ cp = fileReadLine() ; while (isspace ((int) *cp)) ++cp; }while(!canMatch(&cp,"Name")) ; cp+=4 ; while (*cp != '\0') { vStringPut (name, (int) *cp); ++cp; } } else if (canMatch (&cp, "MODULE")) { kindNow = K_MODULE ; do{ cp = fileReadLine() ; while (isspace ((int) *cp)) ++cp; }while(!canMatch(&cp,"Name")) ; cp+=4 ; while (*cp != '\0') { vStringPut (name, (int) *cp); ++cp; } } else if (canMatch (&cp, "IRQLINK")) { kindNow = K_IRQLINK ; do{ cp = fileReadLine() ; while (isspace ((int) *cp)) ++cp; }while(!canMatch(&cp,"Name")) ; cp+=4 ; while (*cp != '\0') { vStringPut (name, (int) *cp); ++cp; } } else if (canMatch (&cp, "IODEVICE")) { kindNow = K_IODEVICE ; do{ cp = fileReadLine() ; while (isspace ((int) *cp)) ++cp; }while(!canMatch(&cp,"Name")) ; cp+=4 ; while (*cp != '\0') { vStringPut (name, (int) *cp); ++cp; } } else if (canMatch (&cp, "PCIDEVICE")) { kindNow = K_PCIDEVICE ; do{ cp = fileReadLine() ; while (isspace ((int) *cp)) ++cp; }while(!canMatch(&cp,"Title")) ; cp+=4 ; while (*cp != '\0') { vStringPut (name, (int) *cp); ++cp; } } vStringTerminate (name); makeSimpleTag (name, sdlKinds, kindNow); vStringClear (name); while (*cp != '\0') { /* FIXME: we don't cope with here documents, * or regular expression literals, or ... you get the idea. * Hopefully, the restriction above that insists on seeing * definitions at the starts of lines should keep us out of * mischief. */ if (isspace (*cp)) { ++cp; } //else if (*cp == '#') //{ /* FIXME: this is wrong, but there *probably* won't be a * definition after an interpolated string (where # doesn't * mean 'comment'). */ // break; //} else if (*cp == '"') { /* Skip string literals. * FIXME: should cope with escapes and interpolation. */ do { ++cp; } while (*cp != 0 && *cp != '"'); } else if (*cp != '\0') { do ++cp; while (isalnum (*cp) || *cp == '_'); } } } }
/* Algorithm adapted from from GNU etags. * Locates tags for procedures & functions. Doesn't do any type- or * var-definitions. It does look for the keyword "extern" or "forward" * immediately following the procedure statement; if found, the tag is * skipped. */ static void findPascalTags (void) { vString *name = vStringNew (); tagEntryInfo tag; char *arglist = NULL; char *vartype = NULL; pascalKind kind = K_FUNCTION; /* each of these flags is TRUE iff: */ boolean incomment = FALSE; /* point is inside a comment */ int comment_char = '\0'; /* type of current comment */ boolean inquote = FALSE; /* point is inside '..' string */ boolean get_tagname = FALSE;/* point is after PROCEDURE/FUNCTION keyword, so next item = potential tag */ boolean found_tag = FALSE; /* point is after a potential tag */ boolean inparms = FALSE; /* point is within parameter-list */ boolean verify_tag = FALSE; /* point has passed the parm-list, so the next token will determine whether this is a FORWARD/EXTERN to be ignored, or whether it is a real tag */ dbp = fileReadLine (); while (dbp != NULL) { int c = *dbp++; if (c == '\0') /* if end of line */ { dbp = fileReadLine (); if (dbp == NULL || *dbp == '\0') continue; if (!((found_tag && verify_tag) || get_tagname)) c = *dbp++; /* only if don't need *dbp pointing to the beginning of the name of the procedure or function */ } if (incomment) { if (comment_char == '{' && c == '}') incomment = FALSE; else if (comment_char == '(' && c == '*' && *dbp == ')') { dbp++; incomment = FALSE; } continue; } else if (inquote) { if (c == '\'') inquote = FALSE; continue; } else switch (c) { case '\'': inquote = TRUE; /* found first quote */ continue; case '{': /* found open { comment */ incomment = TRUE; comment_char = c; continue; case '(': if (*dbp == '*') /* found open (* comment */ { incomment = TRUE; comment_char = c; dbp++; } else if (found_tag) /* found '(' after tag, i.e., parm-list */ inparms = TRUE; continue; case ')': /* end of parms list */ if (inparms) inparms = FALSE; continue; case ';': if (found_tag && !inparms) /* end of proc or fn stmt */ { verify_tag = TRUE; break; } continue; } if (found_tag && verify_tag && *dbp != ' ') { /* check if this is an "extern" declaration */ if (*dbp == '\0') continue; if (tolower ((int) *dbp == 'e')) { if (tail ("extern")) /* superfluous, really! */ { found_tag = FALSE; verify_tag = FALSE; } } else if (tolower ((int) *dbp) == 'f') { if (tail ("forward")) /* check for forward reference */ { found_tag = FALSE; verify_tag = FALSE; } } else if (tolower ((int) *dbp) == 't') { if (tail ("type")) /* check for forward reference */ { found_tag = FALSE; verify_tag = FALSE; } } if (found_tag && verify_tag) /* not external proc, so make tag */ { found_tag = FALSE; verify_tag = FALSE; makePascalTag (&tag); continue; } } if (get_tagname) /* grab name of proc or fn */ { const unsigned char *cp; if (*dbp == '\0') continue; /* grab block name */ while (isspace ((int) *dbp)) ++dbp; for (cp = dbp ; *cp != '\0' && !endtoken (*cp) ; cp++) continue; vStringNCopyS (name, (const char*) dbp, cp - dbp); if (arglist != NULL) eFree(arglist); if (kind == K_FUNCTION && vartype != NULL) eFree(vartype); parseArglist((const char*) cp, &arglist, (kind == K_FUNCTION) ? &vartype : NULL); createPascalTag (&tag, name, kind, arglist, (kind == K_FUNCTION) ? vartype : NULL); dbp = cp; /* set dbp to e-o-token */ get_tagname = FALSE; found_tag = TRUE; /* and proceed to check for "extern" */ } else if (!incomment && !inquote && !found_tag) { switch (tolower ((int) c)) { case 'c': if (tail ("onstructor")) { get_tagname = TRUE; kind = K_PROCEDURE; } break; case 'd': if (tail ("estructor")) { get_tagname = TRUE; kind = K_PROCEDURE; } break; case 'p': if (tail ("rocedure")) { get_tagname = TRUE; kind = K_PROCEDURE; } break; case 'f': if (tail ("unction")) { get_tagname = TRUE; kind = K_FUNCTION; } break; case 't': if (tail ("ype")) { get_tagname = TRUE; kind = K_FUNCTION; } break; } } /* while not eof */ } if (arglist != NULL) eFree(arglist); if (vartype != NULL) eFree(vartype); vStringDelete(name); }
static vString* extractVimFileType(FILE* input) { /* http://vimdoc.sourceforge.net/htmldoc/options.html#modeline [text]{white}{vi:|vim:|ex:}[white]se[t] {options}:[text] options=> filetype=TYPE or ft=TYPE 'modelines' 'mls' number (default 5) global {not in Vi} If 'modeline' is on 'modelines' gives the number of lines that is checked for set commands. */ vString* filetype = NULL; #define RING_SIZE 5 vString* ring[RING_SIZE]; int i, j; unsigned int k; const char* const prefix[] = { "vim:", "vi:", "ex:" }; for (i = 0; i < RING_SIZE; i++) ring[i] = vStringNew (); i = 0; while ((readLine (ring[i++], input)) != NULL) if (i == RING_SIZE) i = 0; j = i; do { const char* p; j--; if (j < 0) j = RING_SIZE - 1; for (k = 0; k < ARRAY_SIZE(prefix); k++) if ((p = strstr (vStringValue (ring[j]), prefix[k])) != NULL) { p += strlen(prefix[k]); for ( ; isspace ((int) *p) ; ++p) ; /* no-op */ filetype = determineVimFileType(p); break; } } while (((i == RING_SIZE)? (j != RING_SIZE - 1): (j != i)) && (!filetype)); for (i = RING_SIZE - 1; i >= 0; i--) vStringDelete (ring[i]); #undef RING_SIZE if (filetype && (vStringLength (filetype) == 0)) { vStringDelete (filetype); filetype = NULL; } return filetype; /* TODO: [text]{white}{vi:|vim:|ex:}[white]{options} */ }
extern void openTagFile (void) { setDefaultTagFileName (); TagsToStdout = isDestinationStdout (); if (TagFile.vLine == NULL) TagFile.vLine = vStringNew (); /* Open the tags file. */ if (TagsToStdout) { /* Open a tempfile with read and write mode. Read mode is used when * write the result to stdout. */ TagFile.fp = tempFile ("w+", &TagFile.name); if (isXtagEnabled (XTAG_PSEUDO_TAGS)) addPseudoTags (); } else { boolean fileExists; TagFile.name = eStrdup (Option.tagFileName); fileExists = doesFileExist (TagFile.name); if (fileExists && ! isTagFile (TagFile.name)) error (FATAL, "\"%s\" doesn't look like a tag file; I refuse to overwrite it.", TagFile.name); if (Option.etags) { if (Option.append && fileExists) TagFile.fp = fopen (TagFile.name, "a+b"); else TagFile.fp = fopen (TagFile.name, "w+b"); } else { if (Option.append && fileExists) { TagFile.fp = fopen (TagFile.name, "r+"); if (TagFile.fp != NULL) { TagFile.numTags.prev = updatePseudoTags (TagFile.fp); fclose (TagFile.fp); TagFile.fp = fopen (TagFile.name, "a+"); } } else { TagFile.fp = fopen (TagFile.name, "w"); if (TagFile.fp != NULL && isXtagEnabled (XTAG_PSEUDO_TAGS)) addPseudoTags (); } } if (TagFile.fp == NULL) error (FATAL | PERROR, "cannot open tag file"); } if (TagsToStdout) TagFile.directory = eStrdup (CurrentDirectory); else TagFile.directory = absoluteDirname (TagFile.name); }
static void findHaskellTags (int is_literate) { vString *name = vStringNew (); char token[1001], arg[1001]; int c; int in_tex_lit_code = 0; c = get_next_char(); while (c != EOF) { if (c == '\n') { c = get_next_char(); continue; } if (isspace(c)) { skip_rest_of_line(); c = get_next_char(); continue; } if (is_literate && !in_tex_lit_code) { if (c == '>') { c = fileGetc(); if (c == ' ') { c = get_next_char(); if (!isident(c)) { skip_rest_of_line(); c = get_next_char(); continue; } } else { skip_rest_of_line(); c = get_next_char(); continue; } } else if (c == '\\') { int n = get_line(token); if (strncmp(token, "begin{code}", 11) == 0) { in_tex_lit_code = 1; c = get_next_char(); continue; } else { if (n > 0 && token[n-1] != '\n') skip_rest_of_line(); else c = get_next_char(); } continue; } else { skip_rest_of_line(); c = get_next_char(); continue; } } if (is_literate && in_tex_lit_code && c == '\\') { if (strncmp(token, "end{code}", 9) == 0) { in_tex_lit_code = 0; c = get_next_char(); continue; } } token[0] = c; token[1] = '\0'; if (!isident(c)) { skip_rest_of_line(); c = get_next_char(); continue; } if (!get_token(token, 1)) { c = get_next_char(); continue; } do { if ((c = fileGetc()) == EOF) return; } while (c == ' ' || c == '\t'); arg[0] = c; get_token(arg, 1); if (strcmp(token, "data") == 0 || strcmp(token, "newtype") == 0) { add_tag(arg, K_TYPE, name); c = inside_datatype(name); continue; } if (strcmp(token, "type") == 0) add_tag(arg, K_TYPE, name); else if (strcmp(token, "module") == 0) add_tag(arg, K_MODULE, name); else if (strcmp(token, "instance") == 0 || strcmp(token, "foreign") == 0 || strcmp(token, "import") == 0) ; else { if (arg[0] != ':') add_tag(token, K_FUNCTION, name); } skip_rest_of_line(); c = get_next_char(); } vStringDelete(name); }
static void findDiffTags (void) { vString *filename = vStringNew (); vString *hunk = vStringNew (); const unsigned char *line, *tmp; int delim = DIFF_DELIM_MINUS; diffKind kind; int scope_index = SCOPE_NIL; while ((line = fileReadLine ()) != NULL) { const unsigned char* cp = line; if (strncmp ((const char*) cp, DiffDelims[delim], 4u) == 0) { scope_index = SCOPE_NIL; cp += 4; if (isspace ((int) *cp)) continue; /* when original filename is /dev/null use the new one instead */ if (delim == DIFF_DELIM_MINUS && strncmp ((const char*) cp, "/dev/null", 9u) == 0 && (cp[9] == 0 || isspace (cp[9]))) { delim = DIFF_DELIM_PLUS; continue; } tmp = stripAbsolute (cp); if (tmp != NULL) { while (! isspace(*tmp) && *tmp != '\0') { vStringPut(filename, *tmp); tmp++; } vStringTerminate(filename); if (delim == DIFF_DELIM_PLUS) kind = K_NEW_FILE; else kind = K_MODIFIED_FILE; scope_index = makeSimpleTag (filename, DiffKinds, kind); vStringClear (filename); } /* restore default delim */ delim = DIFF_DELIM_MINUS; } else if ((scope_index > SCOPE_NIL) && (strncmp ((const char*) cp, DiffDelims[1], 4u) == 0)) { cp += 4; if (isspace ((int) *cp)) continue; /* when modified filename is /dev/null, the original name is deleted. */ if (strncmp ((const char*) cp, "/dev/null", 9u) == 0 && (cp[9] == 0 || isspace (cp[9]))) markTheLastTagAsDeletedFile (scope_index); } else if (strncmp ((const char*) cp, HunkDelim[0], 3u) == 0) { if (parseHunk (cp, hunk, scope_index) != SCOPE_NIL) vStringClear (hunk); } } vStringDelete (hunk); vStringDelete (filename); }
/* Algorithm adapted from from GNU etags. * Perl support by Bart Robinson <*****@*****.**> * Perl sub names: look for /^ [ \t\n]sub [ \t\n]+ [^ \t\n{ (]+/ */ static void findPerlTags (void) { vString *name = vStringNew (); vString *package = NULL; bool skipPodDoc = false; const unsigned char *line; while ((line = readLineFromInputFile ()) != NULL) { bool spaceRequired = false; bool qualified = false; const unsigned char *cp = line; perlKind kind = K_NONE; tagEntryInfo e; if (skipPodDoc) { if (strncmp ((const char*) line, "=cut", (size_t) 4) == 0) skipPodDoc = false; continue; } else if (line [0] == '=') { skipPodDoc = isPodWord ((const char*)line + 1); continue; } else if (strcmp ((const char*) line, "__DATA__") == 0) break; else if (strcmp ((const char*) line, "__END__") == 0) break; else if (line [0] == '#') continue; while (isspace (*cp)) cp++; if (strncmp((const char*) cp, "sub", (size_t) 3) == 0) { TRACE("this looks like a sub\n"); cp += 3; kind = K_SUBROUTINE; spaceRequired = true; qualified = true; } else if (strncmp((const char*) cp, "use", (size_t) 3) == 0) { cp += 3; if (!isspace(*cp)) continue; while (*cp && isspace (*cp)) ++cp; if (strncmp((const char*) cp, "constant", (size_t) 8) != 0) continue; cp += 8; kind = K_CONSTANT; spaceRequired = true; qualified = true; } else if (strncmp((const char*) cp, "package", (size_t) 7) == 0) { /* This will point to space after 'package' so that a tag can be made */ const unsigned char *space = cp += 7; if (package == NULL) package = vStringNew (); else vStringClear (package); while (isspace (*cp)) cp++; while ((int) *cp != ';' && !isspace ((int) *cp)) { vStringPut (package, (int) *cp); cp++; } vStringCatS (package, "::"); cp = space; /* Rewind */ kind = K_PACKAGE; spaceRequired = true; qualified = true; } else if (strncmp((const char*) cp, "format", (size_t) 6) == 0) { cp += 6; kind = K_FORMAT; spaceRequired = true; qualified = true; } else { if (isIdentifier1 (*cp)) { const unsigned char *p = cp; while (isIdentifier (*p)) ++p; while (isspace (*p)) ++p; if ((int) *p == ':' && (int) *(p + 1) != ':') kind = K_LABEL; } } if (kind != K_NONE) { TRACE("cp0: %s\n", (const char *) cp); if (spaceRequired && *cp && !isspace (*cp)) continue; TRACE("cp1: %s\n", (const char *) cp); while (isspace (*cp)) cp++; while (!*cp || '#' == *cp) { /* Gobble up empty lines and comments */ cp = readLineFromInputFile (); if (!cp) goto END_MAIN_WHILE; while (isspace (*cp)) cp++; } while (isIdentifier (*cp) || (K_PACKAGE == kind && ':' == *cp)) { vStringPut (name, (int) *cp); cp++; } if (K_FORMAT == kind && vStringLength (name) == 0 && /* cp did not advance */ '=' == *cp) { /* format's name is optional. If it's omitted, 'STDOUT' is assumed. */ vStringCatS (name, "STDOUT"); } TRACE("name: %s\n", name->buffer); if (0 == vStringLength(name)) { vStringClear(name); continue; } if (K_SUBROUTINE == kind) { /* * isSubroutineDeclaration() may consume several lines. So * we record line positions. */ initTagEntry(&e, vStringValue(name), &(PerlKinds[kind])); if (true == isSubroutineDeclaration(cp)) { if (true == PerlKinds[K_SUBROUTINE_DECLARATION].enabled) { kind = K_SUBROUTINE_DECLARATION; } else { vStringClear (name); continue; } } makeTagEntry(&e); if (isXtagEnabled(XTAG_QUALIFIED_TAGS) && qualified && package != NULL && vStringLength (package) > 0) { vString *const qualifiedName = vStringNew (); vStringCopy (qualifiedName, package); vStringCat (qualifiedName, name); e.name = vStringValue(qualifiedName); makeTagEntry(&e); vStringDelete (qualifiedName); } } else if (vStringLength (name) > 0) { makeSimpleTag (name, PerlKinds, kind); if (isXtagEnabled(XTAG_QUALIFIED_TAGS) && qualified && K_PACKAGE != kind && package != NULL && vStringLength (package) > 0) { vString *const qualifiedName = vStringNew (); vStringCopy (qualifiedName, package); vStringCat (qualifiedName, name); makeSimpleTag (qualifiedName, PerlKinds, kind); vStringDelete (qualifiedName); } } vStringClear (name); } } END_MAIN_WHILE: vStringDelete (name); if (package != NULL) vStringDelete (package); }