www.pudn.com > vim53src.zip > get.c
/***************************************************************************** * $Id: get.c,v 6.2 1998/07/15 04:15:51 darren Exp $ * * Copyright (c) 1996-1998, Darren Hiebert * * This source code is released for free distribution under the terms of the * GNU General Public License. * * This module contains the high level source read functions (preprocessor * directives are handled within this level). *****************************************************************************/ /*============================================================================ = Include files ============================================================================*/ #ifdef HAVE_CONFIG_H # include#endif #include "ctags.h" /*============================================================================ = Data declarations ============================================================================*/ typedef enum { COMMENT_NONE, COMMENT_C, COMMENT_CPLUS } Comment; /*============================================================================ = Data definitions ============================================================================*/ cppState Cpp = { 0, /* ungetch */ FALSE, /* resolveRequired */ { DRCTV_NONE, /* state */ FALSE, /* accept */ { 0, 0, "" }, /* tag info */ 0, /* nestLevel */ { {FALSE,FALSE,FALSE,FALSE} } /* ifdef array */ } /* directive */ }; /*============================================================================ = Function prototypes ============================================================================*/ static boolean readDirective __ARGS((int c, char *const name, unsigned int maxLength)); static boolean readDefineTag __ARGS((int c, tagInfo *const tag, boolean *const parameterized)); static conditionalInfo *currentConditional __ARGS((void)); static boolean isIgnore __ARGS((void)); static boolean setIgnore __ARGS((const boolean ignore)); static boolean isIgnoreBranch __ARGS((void)); static void chooseBranch __ARGS((void)); static boolean pushConditional __ARGS((const boolean firstBranchChosen)); static boolean popConditional __ARGS((void)); static boolean handleDirective __ARGS((const int c)); static Comment isComment __ARGS((void)); static int skipOverCComment __ARGS((void)); static int skipOverCplusComment __ARGS((void)); static int skipToEndOfString __ARGS((void)); static int skipToEndOfChar __ARGS((void)); /*============================================================================ = Function definitions ============================================================================*/ /*---------------------------------------------------------------------------- * Scanning functions * * This section handles preprocessor directives. It strips out all * directives and may emit a tag for #define directives. *--------------------------------------------------------------------------*/ extern boolean cppOpen( name, language, isHeader ) const char *const name; const langType language; const boolean isHeader; { boolean opened; opened = fileOpen(name, language, isHeader); if (opened) { Cpp.ungetch = '\0'; Cpp.resolveRequired = FALSE; Cpp.directive.state = DRCTV_NONE; Cpp.directive.accept = TRUE; Cpp.directive.nestLevel = 0; } return opened; } extern void cppClose() { fileClose(); } /* This puts a character back into the input queue for the source File. */ extern void cppUngetc( c ) const int c; { Cpp.ungetch = c; } /* Reads a directive, whose first character is given by "c", into "name". */ static boolean readDirective( c, name, maxLength ) int c; char *const name; unsigned int maxLength; { unsigned int i; for (i = 0 ; i < maxLength - 1 ; ++i) { if (i > 0) { c = fileGetc(); if (c == EOF || ! isalpha(c)) { fileUngetc(c); break; } } name[i] = c; } name[i] = '\0'; /* null terminate */ return (boolean)isspacetab(c); } /* Reads an identifier, whose first character is given by "c", into "tag", * together with the file location and corresponding line number. */ static boolean readDefineTag( c, tag, parameterized ) int c; tagInfo *const tag; boolean *const parameterized; { char *name = tag->name; do { *name++ = c; } while (c = fileGetc(), (c != EOF && isident(c))); fileUngetc(c); *name = '\0'; /* null terminate */ tag->location = File.seek; tag->lineNumber = File.lineNumber; *parameterized = (boolean)(c == '('); return (boolean)(isspace(c) || c == '('); } static conditionalInfo *currentConditional() { return &Cpp.directive.ifdef[Cpp.directive.nestLevel]; } static boolean isIgnore() { return Cpp.directive.ifdef[Cpp.directive.nestLevel].ignoring; } static boolean setIgnore( ignore ) const boolean ignore; { return Cpp.directive.ifdef[Cpp.directive.nestLevel].ignoring = ignore; } static boolean isIgnoreBranch() { conditionalInfo *const ifdef = currentConditional(); /* Force a single branch if an incomplete statement is discovered * en route. This may have allowed earlier branches containing complete * statements to be followed, but we must follow no further branches. */ if (Cpp.resolveRequired && ! Option.braceFormat) ifdef->singleBranch = TRUE; /* We will ignore this branch in the following cases: * * 1. We are ignoring all branches (conditional was within an ignored * branch of the parent conditional) * 2. A branch has already been chosen and either of: * a. A statement was incomplete upon entering the conditional * b. A statement is incomplete upon encountering a branch */ return (boolean)(ifdef->ignoreAllBranches || (ifdef->branchChosen && ifdef->singleBranch)); } static void chooseBranch() { if (! Option.braceFormat) { conditionalInfo *const ifdef = currentConditional(); ifdef->branchChosen = (boolean)(ifdef->singleBranch || Cpp.resolveRequired); } } /* Pushes one nesting level for an #if directive, indicating whether or not * the branch should be ignored and whether a branch has already been chosen. */ static boolean pushConditional( firstBranchChosen ) const boolean firstBranchChosen; { const boolean ignoreAllBranches = isIgnore(); /* current ignore */ boolean ignoreBranch = FALSE; if (Cpp.directive.nestLevel < (unsigned int)MaxCppNestingLevel - 1) { conditionalInfo *ifdef; ++Cpp.directive.nestLevel; ifdef = currentConditional(); /* We take a snapshot of whether there is an incomplete statement in * progress upon encountering the preprocessor conditional. If so, * then we will flag that only a single branch of the conditional * should be followed. */ ifdef->ignoreAllBranches= ignoreAllBranches; ifdef->singleBranch = Cpp.resolveRequired; ifdef->branchChosen = firstBranchChosen; ifdef->ignoring = (boolean)(ignoreAllBranches || ( !firstBranchChosen && !Option.braceFormat && (ifdef->singleBranch || !Option.if0))); ignoreBranch = ifdef->ignoring; } return ignoreBranch; } /* Pops one nesting level for an #endif directive. */ static boolean popConditional() { if (Cpp.directive.nestLevel > 0) --Cpp.directive.nestLevel; return isIgnore(); } /* Handles a pre-processor directive whose first character is given by "c". */ static boolean handleDirective( c ) const int c; { enum { maxDirectiveName = 10 }; char directive[maxDirectiveName]; const tagScope scope = File.isHeader ? SCOPE_GLOBAL : SCOPE_STATIC; boolean ignore = FALSE; DebugStatement( const boolean ignore0 = isIgnore(); ) switch (Cpp.directive.state) { case DRCTV_NONE: /* used to ignore rest of line past interesting part*/ ignore = isIgnore(); break; case DRCTV_HASH: /* '#' read; determine directive */ readDirective(c, directive, maxDirectiveName); if (stringMatch(directive, "define")) Cpp.directive.state = DRCTV_DEFINE; else if (strncmp(directive, "if", (size_t)2) == 0) Cpp.directive.state = DRCTV_IF; else if (stringMatch(directive, "elif") || stringMatch(directive, "else")) { ignore = setIgnore(isIgnoreBranch()); if (! ignore && stringMatch(directive, "else")) chooseBranch(); Cpp.directive.state = DRCTV_NONE; DebugStatement( if (ignore != ignore0) debugCppIgnore(ignore); ) } else if (stringMatch(directive, "endif")) { DebugStatement( debugCppNest(FALSE, Cpp.directive.nestLevel); ) ignore = popConditional(); Cpp.directive.state = DRCTV_NONE; DebugStatement( if (ignore != ignore0) debugCppIgnore(ignore); ) } else /* "pragma", etc. */ Cpp.directive.state = DRCTV_NONE; break; case DRCTV_IF: /* "if" or "ifdef" detected */ ignore = pushConditional((boolean)(c != '0')); Cpp.directive.state = DRCTV_NONE; DebugStatement( debugCppNest(TRUE, Cpp.directive.nestLevel); if (ignore != ignore0) debugCppIgnore(ignore); ) break; case DRCTV_DEFINE: /* "define" detected: generate tag */ { boolean parameterized; readDefineTag(c, &Cpp.directive.tag, ¶meterized); if (! isIgnoreToken(Cpp.directive.tag.name)) makeDefineTag(&Cpp.directive.tag, scope, parameterized); } Cpp.directive.state = DRCTV_NONE; break; } return ignore; } /* Called upon reading of a slash ('/') characters, determines whether a * comment is encountered, and its type. */ static Comment isComment() { Comment comment; const int next = fileGetc(); if (next == '*') comment = COMMENT_C; else if (next == '/') comment = COMMENT_CPLUS; else { fileUngetc(next); comment = COMMENT_NONE; } return comment; } /* Skips over a C style comment. According to ANSI specification a comment * is treated as white space, so we perform this subsitution. */ static int skipOverCComment() { int c = fileGetc(); while (c != EOF) { if (c != '*') c = fileGetc(); else { const int next = fileGetc(); if (next != '/') c = next; else { c = ' '; /* replace comment with space */ break; } } } return c; } /* Skips over a C++ style comment. */ static int skipOverCplusComment() { int c; while ((c = fileGetc()) != EOF) { if (c == BACKSLASH) fileGetc(); /* throw away next character, too */ else if (c == NEWLINE) break; } return c; } /* Skips to the end of a string, returning a special character to * symbolically represent a generic string. */ static int skipToEndOfString() { int c; while ((c = fileGetc()) != EOF) { if (c == BACKSLASH) fileGetc(); /* throw away next character, too */ else if (c == DOUBLE_QUOTE) break; else if (c == NEWLINE) { fileUngetc(c); break; } } return STRING_SYMBOL; /* symbolic representation of string */ } /* Skips to the end of the three (possibly four) 'c' sequence, returning a * special character to symbolically represent a generic character. */ static int skipToEndOfChar() { int c; while ((c = fileGetc()) != EOF) { if (c == BACKSLASH) fileGetc(); /* throw away next character, too */ else if (c == SINGLE_QUOTE) break; else if (c == NEWLINE) { fileUngetc(c); break; } } return CHAR_SYMBOL; /* symbolic representation of character */ } /* This function returns the next character, stripping out comments, * C pre-processor directives, and the contents of single and double * quoted strings. In short, strip anything which places a burden upon * the tokenizer. */ extern int cppGetc() { boolean directive = FALSE; boolean ignore = FALSE; int c; if (Cpp.ungetch != '\0') { c = Cpp.ungetch; Cpp.ungetch = '\0'; return c; /* return here to avoid re-calling debugPutc() */ } else do { c = fileGetc(); switch (c) { case EOF: ignore = FALSE; directive = FALSE; break; case TAB: case SPACE: break; /* ignore most white space */ case NEWLINE: if (directive && ! ignore) directive = FALSE; Cpp.directive.accept = TRUE; break; case DOUBLE_QUOTE: Cpp.directive.accept = FALSE; c = skipToEndOfString(); break; case '#': if (Cpp.directive.accept) { directive = TRUE; Cpp.directive.state = DRCTV_HASH; Cpp.directive.accept = FALSE; } break; case SINGLE_QUOTE: Cpp.directive.accept = FALSE; c = skipToEndOfChar(); break; case '/': { const Comment comment = isComment(); if (comment == COMMENT_C) c = skipOverCComment(); else if (comment == COMMENT_CPLUS) { c = skipOverCplusComment(); if (c == NEWLINE) fileUngetc(c); } else Cpp.directive.accept = FALSE; break; } default: Cpp.directive.accept = FALSE; if (directive) ignore = handleDirective(c); break; } } while (directive || ignore); DebugStatement( debugPutc(c, DEBUG_CPP); ) return c; } /* vi:set tabstop=8 shiftwidth=4: */