get.c
上传用户:gddssl
上传日期:2007-01-06
资源大小:1003k
文件大小:13k
源码类别:

编辑器/阅读器

开发平台:

DOS

  1. /*****************************************************************************
  2. *   $Id: get.c,v 6.2 1998/07/15 04:15:51 darren Exp $
  3. *
  4. *   Copyright (c) 1996-1998, Darren Hiebert
  5. *
  6. *   This source code is released for free distribution under the terms of the
  7. *   GNU General Public License.
  8. *
  9. *   This module contains the high level source read functions (preprocessor
  10. *   directives are handled within this level).
  11. *****************************************************************************/
  12. /*============================================================================
  13. =   Include files
  14. ============================================================================*/
  15. #ifdef HAVE_CONFIG_H
  16. # include <config.h>
  17. #endif
  18. #include "ctags.h"
  19. /*============================================================================
  20. =   Data declarations
  21. ============================================================================*/
  22. typedef enum { COMMENT_NONE, COMMENT_C, COMMENT_CPLUS } Comment;
  23. /*============================================================================
  24. =   Data definitions
  25. ============================================================================*/
  26. cppState Cpp = {
  27.     0, /* ungetch */
  28.     FALSE, /* resolveRequired */
  29.     {
  30. DRCTV_NONE, /* state */
  31. FALSE, /* accept */
  32. { 0, 0, "" }, /* tag info */
  33. 0, /* nestLevel */
  34. { {FALSE,FALSE,FALSE,FALSE} } /* ifdef array */
  35.     } /* directive */
  36. };
  37. /*============================================================================
  38. =   Function prototypes
  39. ============================================================================*/
  40. static boolean readDirective __ARGS((int c, char *const name, unsigned int maxLength));
  41. static boolean readDefineTag __ARGS((int c, tagInfo *const tag, boolean *const parameterized));
  42. static conditionalInfo *currentConditional __ARGS((void));
  43. static boolean isIgnore __ARGS((void));
  44. static boolean setIgnore __ARGS((const boolean ignore));
  45. static boolean isIgnoreBranch __ARGS((void));
  46. static void chooseBranch __ARGS((void));
  47. static boolean pushConditional __ARGS((const boolean firstBranchChosen));
  48. static boolean popConditional __ARGS((void));
  49. static boolean handleDirective __ARGS((const int c));
  50. static Comment isComment __ARGS((void));
  51. static int skipOverCComment __ARGS((void));
  52. static int skipOverCplusComment __ARGS((void));
  53. static int skipToEndOfString __ARGS((void));
  54. static int skipToEndOfChar __ARGS((void));
  55. /*============================================================================
  56. =   Function definitions
  57. ============================================================================*/
  58. /*----------------------------------------------------------------------------
  59. *   Scanning functions
  60. *
  61. *   This section handles preprocessor directives.  It strips out all
  62. *   directives and may emit a tag for #define directives.
  63. *--------------------------------------------------------------------------*/
  64. extern boolean cppOpen( name, language, isHeader )
  65.     const char *const name;
  66.     const langType language;
  67.     const boolean isHeader;
  68. {
  69.     boolean opened;
  70.     opened = fileOpen(name, language, isHeader);
  71.     if (opened)
  72.     {
  73. Cpp.ungetch = '';
  74. Cpp.resolveRequired = FALSE;
  75. Cpp.directive.state = DRCTV_NONE;
  76. Cpp.directive.accept = TRUE;
  77. Cpp.directive.nestLevel = 0;
  78.     }
  79.     return opened;
  80. }
  81. extern void cppClose()
  82. {
  83.     fileClose();
  84. }
  85. /*  This puts a character back into the input queue for the source File.
  86.  */
  87. extern void cppUngetc( c )
  88.     const int c;
  89. {
  90.     Cpp.ungetch = c;
  91. }
  92. /*  Reads a directive, whose first character is given by "c", into "name".
  93.  */
  94. static boolean readDirective( c, name, maxLength )
  95.     int c;
  96.     char *const name;
  97.     unsigned int maxLength;
  98. {
  99.     unsigned int i;
  100.     for (i = 0  ;  i < maxLength - 1  ;  ++i)
  101.     {
  102. if (i > 0)
  103. {
  104.     c = fileGetc();
  105.     if (c == EOF  ||  ! isalpha(c))
  106.     {
  107. fileUngetc(c);
  108. break;
  109.     }
  110. }
  111. name[i] = c;
  112.     }
  113.     name[i] = ''; /* null terminate */
  114.     return (boolean)isspacetab(c);
  115. }
  116. /*  Reads an identifier, whose first character is given by "c", into "tag",
  117.  *  together with the file location and corresponding line number.
  118.  */
  119. static boolean readDefineTag( c, tag, parameterized )
  120.     int c;
  121.     tagInfo *const tag;
  122.     boolean *const parameterized;
  123. {
  124.     char *name = tag->name;
  125.     do
  126.     {
  127. *name++ = c;
  128.     } while (c = fileGetc(), (c != EOF  &&  isident(c)));
  129.     fileUngetc(c);
  130.     *name = ''; /* null terminate */
  131.     tag->location   = File.seek;
  132.     tag->lineNumber = File.lineNumber;
  133.     *parameterized = (boolean)(c == '(');
  134.     return (boolean)(isspace(c)  ||  c == '(');
  135. }
  136. static conditionalInfo *currentConditional()
  137. {
  138.     return &Cpp.directive.ifdef[Cpp.directive.nestLevel];
  139. }
  140. static boolean isIgnore()
  141. {
  142.     return Cpp.directive.ifdef[Cpp.directive.nestLevel].ignoring;
  143. }
  144. static boolean setIgnore( ignore )
  145.     const boolean ignore;
  146. {
  147.     return Cpp.directive.ifdef[Cpp.directive.nestLevel].ignoring = ignore;
  148. }
  149. static boolean isIgnoreBranch()
  150. {
  151.     conditionalInfo *const ifdef = currentConditional();
  152.     /*  Force a single branch if an incomplete statement is discovered
  153.      *  en route. This may have allowed earlier branches containing complete
  154.      *  statements to be followed, but we must follow no further branches.
  155.      */
  156.     if (Cpp.resolveRequired  &&  ! Option.braceFormat)
  157. ifdef->singleBranch = TRUE;
  158.     /*  We will ignore this branch in the following cases:
  159.      *
  160.      *  1.  We are ignoring all branches (conditional was within an ignored
  161.      *        branch of the parent conditional)
  162.      *  2.  A branch has already been chosen and either of:
  163.      *      a.  A statement was incomplete upon entering the conditional
  164.      *      b.  A statement is incomplete upon encountering a branch
  165.      */
  166.     return (boolean)(ifdef->ignoreAllBranches ||
  167.      (ifdef->branchChosen  &&  ifdef->singleBranch));
  168. }
  169. static void chooseBranch()
  170. {
  171.     if (! Option.braceFormat)
  172.     {
  173. conditionalInfo *const ifdef = currentConditional();
  174. ifdef->branchChosen = (boolean)(ifdef->singleBranch ||
  175. Cpp.resolveRequired);
  176.     }
  177. }
  178. /*  Pushes one nesting level for an #if directive, indicating whether or not
  179.  *  the branch should be ignored and whether a branch has already been chosen.
  180.  */
  181. static boolean pushConditional( firstBranchChosen )
  182.     const boolean firstBranchChosen;
  183. {
  184.     const boolean ignoreAllBranches = isIgnore(); /* current ignore */
  185.     boolean ignoreBranch = FALSE;
  186.     if (Cpp.directive.nestLevel < (unsigned int)MaxCppNestingLevel - 1)
  187.     {
  188. conditionalInfo *ifdef;
  189. ++Cpp.directive.nestLevel;
  190. ifdef = currentConditional();
  191. /*  We take a snapshot of whether there is an incomplete statement in
  192.  *  progress upon encountering the preprocessor conditional. If so,
  193.  *  then we will flag that only a single branch of the conditional
  194.  *  should be followed.
  195.  */
  196. ifdef->ignoreAllBranches= ignoreAllBranches;
  197. ifdef->singleBranch = Cpp.resolveRequired;
  198. ifdef->branchChosen = firstBranchChosen;
  199. ifdef->ignoring = (boolean)(ignoreAllBranches || (
  200.     !firstBranchChosen && !Option.braceFormat &&
  201.     (ifdef->singleBranch || !Option.if0)));
  202. ignoreBranch = ifdef->ignoring;
  203.     }
  204.     return ignoreBranch;
  205. }
  206. /*  Pops one nesting level for an #endif directive.
  207.  */
  208. static boolean popConditional()
  209. {
  210.     if (Cpp.directive.nestLevel > 0)
  211. --Cpp.directive.nestLevel;
  212.     return isIgnore();
  213. }
  214. /*  Handles a pre-processor directive whose first character is given by "c".
  215.  */
  216. static boolean handleDirective( c )
  217.     const int c;
  218. {
  219.     enum { maxDirectiveName = 10 };
  220.     char directive[maxDirectiveName];
  221.     const tagScope scope = File.isHeader ? SCOPE_GLOBAL : SCOPE_STATIC;
  222.     boolean ignore = FALSE;
  223.     DebugStatement( const boolean ignore0 = isIgnore(); )
  224.     switch (Cpp.directive.state)
  225.     {
  226.     case DRCTV_NONE: /* used to ignore rest of line past interesting part*/
  227. ignore = isIgnore();
  228. break;
  229.     case DRCTV_HASH: /* '#' read; determine directive */
  230. readDirective(c, directive, maxDirectiveName);
  231. if (stringMatch(directive, "define"))
  232.     Cpp.directive.state = DRCTV_DEFINE;
  233. else if (strncmp(directive, "if", (size_t)2) == 0)
  234.     Cpp.directive.state = DRCTV_IF;
  235. else if (stringMatch(directive, "elif")  ||
  236.  stringMatch(directive, "else"))
  237. {
  238.     ignore = setIgnore(isIgnoreBranch());
  239.     if (! ignore  &&  stringMatch(directive, "else"))
  240. chooseBranch();
  241.     Cpp.directive.state = DRCTV_NONE;
  242.     DebugStatement( if (ignore != ignore0) debugCppIgnore(ignore); )
  243. }
  244. else if (stringMatch(directive, "endif"))
  245. {
  246.     DebugStatement( debugCppNest(FALSE, Cpp.directive.nestLevel); )
  247.     ignore = popConditional();
  248.     Cpp.directive.state = DRCTV_NONE;
  249.     DebugStatement( if (ignore != ignore0) debugCppIgnore(ignore); )
  250. }
  251. else /* "pragma", etc. */
  252.     Cpp.directive.state = DRCTV_NONE;
  253. break;
  254.     case DRCTV_IF: /* "if" or "ifdef" detected */
  255. ignore = pushConditional((boolean)(c != '0'));
  256. Cpp.directive.state = DRCTV_NONE;
  257. DebugStatement( debugCppNest(TRUE, Cpp.directive.nestLevel);
  258. if (ignore != ignore0) debugCppIgnore(ignore); )
  259. break;
  260.     case DRCTV_DEFINE: /* "define" detected: generate tag */
  261. {
  262.     boolean parameterized;
  263.     readDefineTag(c, &Cpp.directive.tag, &parameterized);
  264.     if (! isIgnoreToken(Cpp.directive.tag.name))
  265. makeDefineTag(&Cpp.directive.tag, scope, parameterized);
  266. }
  267. Cpp.directive.state = DRCTV_NONE;
  268. break;
  269.     }
  270.     return ignore;
  271. }
  272. /*  Called upon reading of a slash ('/') characters, determines whether a
  273.  *  comment is encountered, and its type.
  274.  */
  275. static Comment isComment()
  276. {
  277.     Comment comment;
  278.     const int next = fileGetc();
  279.     if (next == '*')
  280. comment = COMMENT_C;
  281.     else if (next == '/')
  282. comment = COMMENT_CPLUS;
  283.     else
  284.     {
  285. fileUngetc(next);
  286. comment = COMMENT_NONE;
  287.     }
  288.     return comment;
  289. }
  290. /*  Skips over a C style comment. According to ANSI specification a comment
  291.  *  is treated as white space, so we perform this subsitution.
  292.  */
  293. static int skipOverCComment()
  294. {
  295.     int c = fileGetc();
  296.     while (c != EOF)
  297.     {
  298. if (c != '*')
  299.     c = fileGetc();
  300. else
  301. {
  302.     const int next = fileGetc();
  303.     if (next != '/')
  304. c = next;
  305.     else
  306.     {
  307. c = ' '; /* replace comment with space */
  308. break;
  309.     }
  310. }
  311.     }
  312.     return c;
  313. }
  314. /*  Skips over a C++ style comment.
  315.  */
  316. static int skipOverCplusComment()
  317. {
  318.     int c;
  319.     while ((c = fileGetc()) != EOF)
  320.     {
  321. if (c == BACKSLASH)
  322.     fileGetc(); /* throw away next character, too */
  323. else if (c == NEWLINE)
  324.     break;
  325.     }
  326.     return c;
  327. }
  328. /*  Skips to the end of a string, returning a special character to
  329.  *  symbolically represent a generic string.
  330.  */
  331. static int skipToEndOfString()
  332. {
  333.     int c;
  334.     while ((c = fileGetc()) != EOF)
  335.     {
  336. if (c == BACKSLASH)
  337.     fileGetc(); /* throw away next character, too */
  338. else if (c == DOUBLE_QUOTE)
  339.     break;
  340. else if (c == NEWLINE)
  341. {
  342.     fileUngetc(c);
  343.     break;
  344. }
  345.     }
  346.     return STRING_SYMBOL; /* symbolic representation of string */
  347. }
  348. /*  Skips to the end of the three (possibly four) 'c' sequence, returning a
  349.  *  special character to symbolically represent a generic character.
  350.  */
  351. static int skipToEndOfChar()
  352. {
  353.     int c;
  354.     while ((c = fileGetc()) != EOF)
  355.     {
  356. if (c == BACKSLASH)
  357.     fileGetc(); /* throw away next character, too */
  358. else if (c == SINGLE_QUOTE)
  359.     break;
  360. else if (c == NEWLINE)
  361. {
  362.     fileUngetc(c);
  363.     break;
  364. }
  365.     }
  366.     return CHAR_SYMBOL;     /* symbolic representation of character */
  367. }
  368. /*  This function returns the next character, stripping out comments,
  369.  *  C pre-processor directives, and the contents of single and double
  370.  *  quoted strings. In short, strip anything which places a burden upon
  371.  *  the tokenizer.
  372.  */
  373. extern int cppGetc()
  374. {
  375.     boolean directive = FALSE;
  376.     boolean ignore = FALSE;
  377.     int c;
  378.     if (Cpp.ungetch != '')
  379.     {
  380. c = Cpp.ungetch;
  381. Cpp.ungetch = '';
  382. return c;     /* return here to avoid re-calling debugPutc() */
  383.     }
  384.     else do
  385.     {
  386. c = fileGetc();
  387. switch (c)
  388. {
  389. case EOF:
  390.     ignore = FALSE;
  391.     directive = FALSE;
  392.     break;
  393. case TAB:
  394. case SPACE:
  395.     break; /* ignore most white space */
  396. case NEWLINE:
  397.     if (directive  &&  ! ignore)
  398. directive = FALSE;
  399.     Cpp.directive.accept = TRUE;
  400.     break;
  401. case DOUBLE_QUOTE:
  402.     Cpp.directive.accept = FALSE;
  403.     c = skipToEndOfString();
  404.     break;
  405. case '#':
  406.     if (Cpp.directive.accept)
  407.     {
  408. directive = TRUE;
  409. Cpp.directive.state = DRCTV_HASH;
  410. Cpp.directive.accept = FALSE;
  411.     }
  412.     break;
  413. case SINGLE_QUOTE:
  414.     Cpp.directive.accept = FALSE;
  415.     c = skipToEndOfChar();
  416.     break;
  417. case '/':
  418.   {
  419.     const Comment comment = isComment();
  420.     if (comment == COMMENT_C)
  421. c = skipOverCComment();
  422.     else if (comment == COMMENT_CPLUS)
  423.     {
  424. c = skipOverCplusComment();
  425. if (c == NEWLINE)
  426.     fileUngetc(c);
  427.     }
  428.     else
  429. Cpp.directive.accept = FALSE;
  430.     break;
  431.   }
  432. default:
  433.     Cpp.directive.accept = FALSE;
  434.     if (directive)
  435. ignore = handleDirective(c);
  436.     break;
  437. }
  438.     } while (directive || ignore);
  439.     DebugStatement( debugPutc(c, DEBUG_CPP); )
  440.     return c;
  441. }
  442. /* vi:set tabstop=8 shiftwidth=4: */