/* `end' points to the equal sign. Parse from right to left to get the * identifier. Assume we're dealing with something of form \s*\w+\s*=> */ static void makeTagFromLeftSide (const char *begin, const char *end, vString *name, vString *package) { tagEntryInfo entry; const char *b, *e; if (! PerlKinds[K_CONSTANT].enabled) return; for (e = end - 1; e > begin && isspace(*e); --e) ; if (e < begin) return; for (b = e; b >= begin && isIdentifier(*b); --b) ; /* Identifier must be either beginning of line of have some whitespace * on its left: */ if (b < begin || isspace(*b) || ',' == *b) ++b; else if (b != begin) return; if (e - b + 1 <= 0) return; /* Left side of => has an invalid identifier. */ vStringClear(name); vStringNCatS(name, b, e - b + 1); initTagEntry(&entry, vStringValue(name), &(PerlKinds[K_CONSTANT])); makeTagEntry(&entry); if (Option.include.qualifiedTags && package && vStringLength(package)) { vStringClear(name); vStringCopy(name, package); vStringNCatS(name, b, e - b + 1); initTagEntry(&entry, vStringValue(name), &(PerlKinds[K_CONSTANT])); makeTagEntry(&entry); } }
static void parseAutogroup (const unsigned char *line) { vString *name = vStringNew (); /* Found Autocommand Group (augroup) */ const unsigned char *cp = line; if (isspace ((int) *cp)) { while (*cp && isspace ((int) *cp)) ++cp; if (*cp) { const unsigned char *end = skipWord (cp); /* "end" (caseless) has a special meaning and should not generate a tag */ if (end > cp && strncasecmp ((const char *) cp, "end", end - cp) != 0) { vStringNCatS (name, (const char *) cp, end - cp); vStringTerminate (name); makeSimpleTag (name, VimKinds, K_AUGROUP); vStringClear (name); } } } vStringDelete (name); }
static void makeJsTag (tokenInfo *const token, const jsKind kind, vString *const signature) { if (JsKinds [kind].enabled && ! token->ignoreTag ) { const char *name = vStringValue (token->string); vString *fullscope = vStringNewCopy (token->scope); const char *p; tagEntryInfo e; if ( (p = strrchr (name, '.')) != NULL ) { if (vStringLength (fullscope) > 0) vStringPut (fullscope, '.'); vStringNCatS (fullscope, name, (size_t) (p - name)); name = p + 1; } initTagEntry (&e, name); e.lineNumber = token->lineNumber; e.filePosition = token->filePosition; e.kindName = JsKinds [kind].name; e.kind = JsKinds [kind].letter; if ( vStringLength(fullscope) > 0 ) { jsKind parent_kind = JSTAG_CLASS; /* * If we're creating a function (and not a method), * guess we're inside another function */ if (kind == JSTAG_FUNCTION) parent_kind = JSTAG_FUNCTION; e.extensionFields.scope[0] = JsKinds [parent_kind].name; e.extensionFields.scope[1] = vStringValue (fullscope); } if (signature && vStringLength(signature)) { size_t i; /* sanitize signature by replacing all control characters with a * space (because it's simple). * there should never be any junk in a valid signature, but who * knows what the user wrote and CTags doesn't cope well with weird * characters. */ for (i = 0; i < signature->length; i++) { unsigned char c = (unsigned char) signature->buffer[i]; if (c < 0x20 /* below space */ || c == 0x7F /* DEL */) signature->buffer[i] = ' '; } e.extensionFields.signature = vStringValue(signature); } makeTagEntry (&e); vStringDelete (fullscope); } }
/* * Emits a tag for the given 'name' of kind 'kind' at the current nesting. */ static void emitRubyTag (vString* name, rubyKind kind) { tagEntryInfo tag; vString* scope; tagEntryInfo *parent; rubyKind parent_kind = K_UNDEFINED; NestingLevel *lvl; const char *unqualified_name; const char *qualified_name; int r; if (!RubyKinds[kind].enabled) { return; } vStringTerminate (name); scope = nestingLevelsToScope (nesting); lvl = nestingLevelsGetCurrent (nesting); parent = getEntryOfNestingLevel (lvl); if (parent) parent_kind = parent->kind - RubyKinds; qualified_name = vStringValue (name); unqualified_name = strrchr (qualified_name, SCOPE_SEPARATOR); if (unqualified_name && unqualified_name[1]) { if (unqualified_name > qualified_name) { if (vStringLength (scope) > 0) vStringPut (scope, SCOPE_SEPARATOR); vStringNCatS (scope, qualified_name, unqualified_name - qualified_name); /* assume module parent type for a lack of a better option */ parent_kind = K_MODULE; } unqualified_name++; } else unqualified_name = qualified_name; initTagEntry (&tag, unqualified_name, &(RubyKinds [kind])); if (vStringLength (scope) > 0) { Assert (0 <= parent_kind && (size_t) parent_kind < (ARRAY_SIZE (RubyKinds))); tag.extensionFields.scopeKind = &(RubyKinds [parent_kind]); tag.extensionFields.scopeName = vStringValue (scope); } r = makeTagEntry (&tag); nestingLevelsPush (nesting, r); vStringClear (name); vStringDelete (scope); }
/* Match a "label:" style label. */ static void match_colon_label (char const *p) { char const *end = p + strlen (p) - 1; while (isspace (*end)) end--; if (*end == ':') { vString *name = vStringNew (); vStringNCatS (name, p, end - p); makeSimpleTag (name, BasicKinds, K_LABEL); vStringDelete (name); } }
static void makeJsTag (tokenInfo *const token, const jsKind kind) { if (JsKinds [kind].enabled && ! token->ignoreTag ) { const char *name = vStringValue (token->string); vString *fullscope = vStringNewCopy (token->scope); const char *p; tagEntryInfo e; if ( (p = strrchr (name, '.')) != NULL ) { if (vStringLength (fullscope) > 0) vStringPut (fullscope, '.'); vStringNCatS (fullscope, name, (size_t) (p - name)); name = p + 1; } initTagEntry (&e, name); e.lineNumber = token->lineNumber; e.filePosition = token->filePosition; e.kindName = JsKinds [kind].name; e.kind = JsKinds [kind].letter; if ( vStringLength(fullscope) > 0 ) { jsKind parent_kind = JSTAG_CLASS; /* * If we're creating a function (and not a method), * guess we're inside another function */ if (kind == JSTAG_FUNCTION) parent_kind = JSTAG_FUNCTION; e.extensionFields.scope[0] = JsKinds [parent_kind].name; e.extensionFields.scope[1] = vStringValue (fullscope); } makeTagEntry (&e); vStringDelete (fullscope); } }
static vString* substitute ( const char* const in, const char* out, const int nmatch, const regmatch_t* const pmatch) { vString* result = vStringNew (); const char* p; for (p = out ; *p != '\0' ; p++) { if (*p == '\\' && isdigit ((int) *++p)) { const int dig = *p - '0'; if (0 < dig && dig < nmatch && pmatch [dig].rm_so != -1) { const int diglen = pmatch [dig].rm_eo - pmatch [dig].rm_so; vStringNCatS (result, in + pmatch [dig].rm_so, diglen); } } else if (*p != '\n' && *p != '\r') vStringPut (result, *p); } vStringTerminate (result); return result; }
/* reads an HereDoc or a NowDoc (the part after the <<<). * <<<[ \t]*(ID|'ID'|"ID") * ... * ID;? * * note that: * 1) starting ID must be immediately followed by a newline; * 2) closing ID is the same as opening one; * 3) closing ID must be immediately followed by a newline or a semicolon * then a newline. * * Example of a *single* valid heredoc: * <<< FOO * something * something else * FOO this is not an end * FOO; this isn't either * FOO; # neither this is * FOO; * # previous line was the end, but the semicolon wasn't required */ static void parseHeredoc (vString *const string) { int c; unsigned int len; char delimiter[64]; /* arbitrary limit, but more is crazy anyway */ int quote = 0; do { c = getcFromInputFile (); } while (c == ' ' || c == '\t'); if (c == '\'' || c == '"') { quote = c; c = getcFromInputFile (); } for (len = 0; len < ARRAY_SIZE (delimiter) - 1; len++) { if (! isIdentChar (c)) break; delimiter[len] = (char) c; c = getcFromInputFile (); } delimiter[len] = 0; if (len == 0) /* no delimiter, give up */ goto error; if (quote) { if (c != quote) /* no closing quote for quoted identifier, give up */ goto error; c = getcFromInputFile (); } if (c != '\r' && c != '\n') /* missing newline, give up */ goto error; do { c = getcFromInputFile (); if (c != '\r' && c != '\n') vStringPut (string, (char) c); else { /* new line, check for a delimiter right after */ int nl = c; int extra = EOF; c = getcFromInputFile (); for (len = 0; c != 0 && (c - delimiter[len]) == 0; len++) c = getcFromInputFile (); if (delimiter[len] != 0) ungetcToInputFile (c); else { /* line start matched the delimiter, now check whether there * is anything after it */ if (c == '\r' || c == '\n') { ungetcToInputFile (c); break; } else if (c == ';') { int d = getcFromInputFile (); if (d == '\r' || d == '\n') { /* put back the semicolon since it's not part of the * string. we can't put back the newline, but it's a * whitespace character nobody cares about it anyway */ ungetcToInputFile (';'); break; } else { /* put semicolon in the string and continue */ extra = ';'; ungetcToInputFile (d); } } } /* if we are here it wasn't a delimiter, so put everything in the * string */ vStringPut (string, (char) nl); vStringNCatS (string, delimiter, len); if (extra != EOF) vStringPut (string, (char) extra); } } while (c != EOF); vStringTerminate (string); return; error: ungetcToInputFile (c); }
static void findAsciidocTags(void) { vString *name = vStringNew(); const unsigned char *line; unsigned char in_block = '\0'; /* holds the block marking char or \0 if not in block */ nestingLevels = nestingLevelsNew(); while ((line = readLineFromInputFile()) != NULL) { int line_len = strlen((const char*) line); int name_len_bytes = vStringLength(name); int name_len = utf8_strlen(vStringValue(name), name_len_bytes); /* if the name doesn't look like UTF-8, assume one-byte charset */ if (name_len < 0) name_len = name_len_bytes; /* if its a title underline, or a delimited block marking character */ if (line[0] == '=' || line[0] == '-' || line[0] == '~' || line[0] == '^' || line[0] == '+' || line[0] == '.' || line[0] == '*' || line[0] == '_' || line[0] == '/') { int n_same; for (n_same = 1; line[n_same] == line[0]; ++n_same); /* is it a two line title or a delimited block */ if (n_same == line_len) { /* if in a block, can't be block start or title, look for block end */ if (in_block) { if (line[0] == in_block) in_block = '\0'; } /* if its a =_~^+ and the same length +-2 as the line before then its a title */ /* (except in the special case its a -- open block start line) */ else if ((line[0] == '=' || line[0] == '-' || line[0] == '~' || line[0] == '^' || line[0] == '+') && line_len <= name_len + 2 && line_len >= name_len - 2 && !(line_len == 2 && line[0] == '-')) { int kind = get_kind((char)(line[0])); if (kind >= 0) { makeAsciidocTag(name, kind); continue; } } /* else if its 4 or more /+-.*_= (plus the -- special case) its a block start */ else if (((line[0] == '/' || line[0] == '+' || line[0] == '-' || line[0] == '.' || line[0] == '*' || line[0] == '_' || line[0] == '=') && line_len >= 4 ) || (line[0] == '-' && line_len == 2)) { in_block = line[0]; } } /* otherwise is it a one line title */ else if (line[0] == '=' && n_same <= 5 && isspace(line[n_same]) && !in_block) { int kind = n_same - 1; int start = n_same; int end = line_len - 1; while (line[end] == line[0])--end; while (isspace(line[start]))++start; while (isspace(line[end]))--end; vStringClear(name); vStringNCatS(name, (const char*)(&(line[start])), end - start + 1); vStringTerminate(name); makeAsciidocTag(name, kind); continue; } } vStringClear(name); if (! isspace(*line)) vStringCatS(name, (const char*) line); vStringTerminate(name); } vStringDelete(name); nestingLevelsFree(nestingLevels); }