home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / elvis184.zip / src / ctags.c < prev    next >
C/C++ Source or Header  |  1995-04-23  |  21KB  |  913 lines

  1. /* ctags.c */
  2.  
  3. /* This is a reimplementation of the ctags(1) program.  It supports ANSI C,
  4.  * and has heaps o' flags.  It is meant to be distributed with elvis.
  5.  */
  6.  
  7. /* OS/2, emx+gcc 0.9a */
  8. #ifdef OS2
  9. # include <sys/emx.h>  /* sigh...emx insists this be the first header file */
  10. #endif                 /* (used in wildcard.c, included at bottom of ctags.c) */
  11.  
  12. #include <stdio.h>
  13. #ifdef __STDC__
  14. # include <string.h>
  15. # include <stdlib.h>
  16. #endif
  17. #include "config.h"
  18. #ifndef FALSE
  19. # define FALSE    0
  20. # define TRUE    1
  21. #endif
  22. #ifndef TAGS
  23. # define TAGS    "tags"
  24. #endif
  25. #ifndef REFS
  26. # define REFS    "refs"
  27. #endif
  28. #ifndef BLKSIZE
  29. # define BLKSIZE 1024
  30. #endif
  31.  
  32. #include "ctype.c" /* yes, that really is the .c file, not the .h one. */
  33.  
  34. extern void    file_open P_((char *));
  35. extern int    file_getc P_((void));
  36. extern void    file_ungetc P_((int));
  37. extern void    file_copyline P_((long, FILE *));
  38. extern void    cpp_open P_((char *));
  39. extern void    cpp_echo P_((int));
  40. extern int    cpp_getc P_((void));
  41. extern void    cpp_ungetc P_((int));
  42. extern int    lex_gettoken P_((void));
  43. extern void    maketag P_((int, long));
  44. extern void    ctags P_((char *));
  45. extern void    usage P_((void));
  46. extern void    main P_((int, char **));
  47.  
  48.  
  49. /* -------------------------------------------------------------------------- */
  50. /* Some global variables */
  51.  
  52. /* The following boolean variables are set according to command line flags */
  53. int    backward;    /* -B  regexp patterns search backwards */
  54. int    no_colons;    /* -S  make static tags look like global tags */
  55. int    incl_static;    /* -s  include static tags */
  56. int    incl_types;    /* -t  include typedefs and structs */
  57. int    incl_vars;    /* -v  include variables */
  58. int    make_refs;    /* -r  generate a "refs" file */
  59. int    append_files;    /* -a  append to "tags" [and "refs"] files */
  60. #ifdef DEVNULL
  61. int    dont_make_tags;    /* -T  Don't make \"tags\" */
  62. #endif
  63.  
  64. /* The following are used for outputting to the "tags" and "refs" files */
  65. FILE    *tags;        /* used for writing to the "tags" file */
  66. FILE    *refs;        /* used for writing to the "refs" file */
  67.  
  68. /* -------------------------------------------------------------------------- */
  69. /* These are used for reading a source file.  It keeps track of line numbers */
  70. char    *file_name;    /* name of the current file */
  71. FILE    *file_fp;    /* stream used for reading the file */
  72. long    file_lnum;    /* line number in the current file */
  73. long    file_seek;    /* fseek() offset to the start of current line */
  74. int    file_afternl;    /* boolean: was previous character a newline? */
  75. int    file_prevch;    /* a single character that was ungotten */
  76. int    file_header;    /* boolean: is the current file a header file? */
  77.  
  78. /* This function opens a file, and resets the line counter.  If it fails, it
  79.  * it will display an error message and leave the file_fp set to NULL.
  80.  */
  81. void file_open(name)
  82.     char    *name;    /* name of file to be opened */
  83. {
  84.     /* if another file was already open, then close it */
  85.     if (file_fp)
  86.     {
  87.         fclose(file_fp);
  88.     }
  89.  
  90.     /* try to open the file for reading.  The file must be opened in
  91.      * "binary" mode because otherwise fseek() would misbehave under DOS.
  92.      */
  93. #if MSDOS || TOS || OS2
  94.     file_fp = fopen(name, "rb");
  95. #else
  96.     file_fp = fopen(name, "r");
  97. #endif
  98.     if (!file_fp)
  99.     {
  100.         perror(name);
  101.     }
  102.  
  103.     /* reset the name & line number */
  104.     file_name = name;
  105.     file_lnum = 0L;
  106.     file_seek = 0L;
  107.     file_afternl = TRUE;
  108.  
  109.     /* determine whether this is a header file */
  110.     file_header = FALSE;
  111.     name += strlen(name) - 2;
  112.     if (name >= file_name && name[0] == '.' && (name[1] == 'h' || name[1] == 'H'))
  113.     {
  114.         file_header = TRUE;
  115.     }
  116. }
  117.  
  118. /* This function reads a single character from the stream.  If the *previous*
  119.  * character was a newline, then it also increments file_lnum and sets
  120.  * file_offset.
  121.  */
  122. int file_getc()
  123. {
  124.     int    ch;
  125.  
  126.     /* if there is an ungotten character, then return it.  Don't do any
  127.      * other processing on it, though, because we already did that the
  128.      * first time it was read.
  129.      */
  130.     if (file_prevch)
  131.     {
  132.         ch = file_prevch;
  133.         file_prevch = 0;
  134.         return ch;
  135.     }
  136.  
  137.     /* if previous character was a newline, then we're starting a line */
  138.     if (file_afternl && file_fp)
  139.     {
  140.         file_afternl = FALSE;
  141.         file_seek = ftell(file_fp);
  142.         file_lnum++;
  143.     }
  144.  
  145.     /* Get a character.  If no file is open, then return EOF */
  146.     ch = (file_fp ? getc(file_fp) : EOF);
  147.  
  148.     /* if it is a newline, then remember that fact */
  149.     if (ch == '\n')
  150.     {
  151.         file_afternl = TRUE;
  152.     }
  153.  
  154.     /* return the character */
  155.     return ch;
  156. }
  157.  
  158. /* This function ungets a character from the current source file */
  159. void file_ungetc(ch)
  160.     int    ch;    /* character to be ungotten */
  161. {
  162.     file_prevch = ch;
  163. }
  164.  
  165. /* This function copies the current line out some other fp.  It has no effect
  166.  * on the file_getc() function.  During copying, any '\' characters are doubled
  167.  * and a leading '^' or trailing '$' is also quoted.  The '\n' character is not
  168.  * copied.  If the '\n' is preceded by a '\r', then the '\r' isn't copied.
  169.  *
  170.  * This is meant to be used when generating a tag line.
  171.  */
  172. void file_copyline(seek, fp)
  173.     long    seek;    /* where the lines starts in the source file */
  174.     FILE    *fp;    /* the output stream to copy it to */
  175. {
  176.     long    oldseek;/* where the file's pointer was before we messed it up */
  177.     char    ch;    /* a single character from the file */
  178.     char    next;    /* the next character from this file */
  179.  
  180.     /* go to the start of the line */
  181.     oldseek = ftell(file_fp);
  182.     fseek(file_fp, seek, 0);
  183.  
  184.     /* if first character is '^', then quote it */
  185.     ch = getc(file_fp);
  186. #if 0
  187.     if (ch == '^')
  188.     {
  189.         putc('\\', fp);
  190.     }
  191. #endif
  192.  
  193.     /* write everything up to, but not including, the newline */
  194.     while (ch != '\n')
  195.     {
  196.         /* preread the next character from this file */
  197.         next = getc(file_fp);
  198.  
  199.         /* if character is '\', or a terminal '$', then quote it */
  200.         if (ch == '\\'
  201.          || ch == (backward ? '?' : '/')
  202.          || (ch == '$' && next == '\n'))
  203.         {
  204.             putc('\\', fp);
  205.         }
  206.  
  207.         /* copy the character, unless it is a terminal '\r' */
  208.         if (ch != '\r' || next != '\n')
  209.             putc(ch, fp);
  210.  
  211.         /* next character... */
  212.         ch = next;
  213.     }
  214.  
  215.     /* seek back to the old position */
  216.     fseek(file_fp, oldseek, 0);
  217. }
  218.  
  219. /* -------------------------------------------------------------------------- */
  220. /* This section handles preprocessor directives.  It strips out all of the
  221.  * directives, and may emit a tag for #define directives.
  222.  */
  223.  
  224. int    cpp_afternl;    /* boolean: look for '#' character? */
  225. int    cpp_prevch;    /* an ungotten character, if any */
  226. int    cpp_refsok;    /* boolean: can we echo characters out to "refs"? */
  227. int    cpp_refsnl;    /* boolean: dup next \n in "refs"? */
  228.  
  229. /* This function opens the file & resets variables */
  230. void cpp_open(name)
  231.     char    *name;    /* name of source file to be opened */
  232. {
  233.     /* use the lower-level file_open function to open the file */
  234.     file_open(name);
  235.  
  236.     /* reset variables */
  237.     cpp_afternl = TRUE;
  238.     cpp_refsok = TRUE;
  239. }
  240.  
  241. /* This function copies a character from the source file to the "refs" file */
  242. void cpp_echo(ch)
  243.     int    ch; /* the character to copy */
  244. {
  245.     static    wasnl;
  246.  
  247.     /* echo non-EOF chars, unless not making "ref", or echo turned off */
  248.     if (ch != EOF && make_refs && cpp_refsok && !file_header)
  249.     {
  250.         /* try to avoid blank lines */
  251.         if (ch == '\n')
  252.         {
  253.             /* hack: double \n at end of declarations, to
  254.                help `ref' find the right starting point...
  255.             */
  256.             if (cpp_refsnl)
  257.             {
  258.                 putc('\n', refs);
  259.                 cpp_refsnl = FALSE;
  260.             }
  261.             if (wasnl)
  262.             {
  263.                 return;
  264.             }
  265.             wasnl = TRUE;
  266.         }
  267.         else
  268.         {
  269.             wasnl = FALSE;
  270.         }
  271.  
  272.         /* add the character */
  273.         putc(ch, refs);
  274.     }
  275. }
  276.  
  277. /* This function returns the next character which isn't part of a directive */
  278. int cpp_getc()
  279. {
  280.     static
  281.     int    ch;    /* the next input character */
  282.     char    *scan;
  283.  
  284.     /* if we have an ungotten character, then return it */
  285.     if (cpp_prevch)
  286.     {
  287.         ch = cpp_prevch;
  288.         cpp_prevch = 0;
  289.         return ch;
  290.     }
  291.  
  292.     /* Get a character from the file.  Return it if not special '#' */
  293.     ch = file_getc();
  294.     if (ch == '\n')
  295.     {
  296.         cpp_afternl = TRUE;
  297.         cpp_echo(ch);
  298.         return ch;
  299.     }
  300.     else if (ch != '#' || !cpp_afternl)
  301.     {
  302.         /* normal character.  Any non-whitespace should turn off afternl */
  303.         if (ch != ' ' && ch != '\t')
  304.         {
  305.             cpp_afternl = FALSE;
  306.         }
  307.         cpp_echo(ch);
  308.         return ch;
  309.     }
  310.  
  311.     /* Yikes!  We found a directive */
  312.  
  313.     /* see whether this is a #define line */
  314.     scan = " define ";
  315.     while (*scan)
  316.     {
  317.         if (*scan == ' ')
  318.         {
  319.             /* space character matches any whitespace */
  320.             do
  321.             {
  322.                 ch = file_getc();
  323.             } while (ch == ' ' || ch == '\t');
  324.             file_ungetc(ch);
  325.         }
  326.         else
  327.         {
  328.             /* other characters should match exactly */
  329.             ch = file_getc();
  330.             if (ch != *scan)
  331.             {
  332.                 file_ungetc(ch);
  333.                 break;
  334.             }
  335.         }
  336.         scan++;
  337.     }
  338.  
  339.     /* is this a #define line?  and should we generate a tag for it? */
  340.     if (!*scan && (file_header || incl_static))
  341.     {
  342.         /* if not a header, then this will be a static tag */
  343.         if (!file_header && !no_colons)
  344.         {
  345.             fputs(file_name, tags);
  346.             putc(':', tags);
  347.         }
  348.  
  349.         /* output the tag name */
  350.         for (ch = file_getc(); isalnum(ch) || ch == '_'; ch = file_getc())
  351.         {
  352.             putc(ch, tags);
  353.         }
  354.  
  355.         /* output a tab, the filename, another tab, and the line number */
  356.         fprintf(tags, "\t%s\t%ld\n", file_name, file_lnum);
  357.     }
  358.  
  359.     /* skip to the end of the directive -- a newline that isn't preceded
  360.      * by a '\' character.
  361.      */
  362.     while (ch != EOF && ch != '\n')
  363.     {
  364.         if (ch == '\\')
  365.         {
  366.             ch = file_getc();
  367.         }
  368.         ch = file_getc();
  369.     }
  370.  
  371.     /* return the newline that we found at the end of the directive */
  372.     cpp_echo(ch);
  373.     return ch;
  374. }
  375.  
  376. void
  377. /* This puts a character back into the input queue for the source file */
  378. cpp_ungetc(ch)
  379.     int    ch;    /* a character to be ungotten */
  380. {
  381.     cpp_prevch = ch;
  382. }
  383.  
  384.  
  385. /* -------------------------------------------------------------------------- */
  386. /* This is the lexical analyser.  It gets characters from the preprocessor,
  387.  * and gives tokens to the parser.  Some special codes are...
  388.  *   (deleted)  / *...* / (comments)
  389.  *   (deleted)    //...\n    (comments)
  390.  *   (deleted)    (*    (parens used in complex declaration)
  391.  *   (deleted)    [...]    (array subscript, when ... contains no ])
  392.  *   (deleted)    struct    (intro to structure declaration)
  393.  *   BODY    {...}    ('{' can occur anywhere, '}' only at BOW if ... has '{')
  394.  *   ARGS    (...{    (args of function, not extern or forward)
  395.  *   ARGS    (...);    (args of an extern/forward function declaration)
  396.  *   COMMA    ,    (separate declarations that have same scope)
  397.  *   SEMICOLON    ;    (separate declarations that have different scope)
  398.  *   SEMICOLON  =...;    (initializer)
  399.  *   TYPEDEF    typedef    (the "typedef" keyword)
  400.  *   KSTATIC    static    (the "static" keyword)
  401.  *   KSTATIC    private    (the "static" keyword)
  402.  *   KSTATIC    PRIVATE    (the "static" keyword)
  403.  *   NAME    [a-z]+    (really any valid name that isn't reserved word)
  404.  */
  405.  
  406. /* #define EOF -1 */
  407. #define DELETED      0
  408. #define BODY      1
  409. #define ARGS      2
  410. #define COMMA      3
  411. #define SEMICOLON 4
  412. #define TYPEDEF   5
  413. #define KSTATIC      6
  414. #define EXTERN      7
  415. #define NAME      8
  416.  
  417. char    lex_name[BLKSIZE];    /* the name of a "NAME" token */
  418. long    lex_seek;        /* start of line that contains lex_name */
  419.  
  420. int
  421. lex_gettoken()
  422. {
  423.     int    ch;        /* a character from the preprocessor */
  424.     int    next;        /* the next character */
  425.     int    token;        /* the token that we'll return */
  426.     int    i;
  427.  
  428.     /* loop until we get a token that isn't "DELETED" */
  429.     do
  430.     {
  431.         /* get the next character */
  432.         ch = cpp_getc();
  433.  
  434.         /* process the character */
  435.         switch (ch)
  436.         {
  437.           case ',':
  438.             token = COMMA;
  439.             break;
  440.  
  441.           case ';':
  442.             token = SEMICOLON;
  443.             break;
  444.  
  445.           case '/':
  446.             /* get the next character */
  447.             ch = cpp_getc();
  448.             switch (ch)
  449.             {
  450.               case '*':    /* start of C comment */
  451.                 ch = cpp_getc();
  452.                 next = cpp_getc();
  453.                 while (next != EOF && (ch != '*' || next != '/'))
  454.                 {
  455.                     ch = next;
  456.                     next = cpp_getc();
  457.                 }
  458.                 break;
  459.  
  460.               case '/':    /* start of a C++ comment */
  461.                 do
  462.                 {
  463.                     ch = cpp_getc();
  464.                 } while (ch != '\n' && ch != EOF);
  465.                 break;
  466.  
  467.               default:    /* some other slash */
  468.                 cpp_ungetc(ch);
  469.             }
  470.             token = DELETED;
  471.             break;
  472.  
  473.           case '(':
  474.             ch = cpp_getc();
  475.             if (ch == '*')
  476.             {
  477.                 token = DELETED;
  478.             }
  479.             else
  480.             {
  481.                 next = cpp_getc();
  482.                 while (ch != '{' && ch != EOF            /* }(  to keep vi's % command happy */
  483.                     && (ch != ')' && ch != ']'
  484.                     || next != ';' && next != ','))
  485.                 {
  486.                     ch = next;
  487.                     next = cpp_getc();
  488.                 }
  489.                 if (ch == '{')/*}*/
  490.                 {
  491.                     cpp_ungetc(ch);
  492.                 }
  493.                 else if (next == ';' || next == ',')
  494.                 {
  495.                     cpp_ungetc(next);
  496.                 }
  497.                 token = ARGS;
  498.             }
  499.             break;
  500.  
  501.           case '{':/*}*/
  502.             /* don't send the next characters to "refs" */
  503.             cpp_refsok = FALSE;
  504.  
  505.             /* skip ahead to closing '}', or to embedded '{' */
  506.             do
  507.             {
  508.                 ch = cpp_getc();
  509.             } while (ch != '{' && ch != '}' && ch != EOF);
  510.  
  511.             /* if has embedded '{', then skip to '}' in column 1 */
  512.             if (ch == '{') /*}*/
  513.             {
  514.                 ch = cpp_getc();
  515.                 next = cpp_getc();
  516.                 while (ch != EOF && (ch != '\n' || next != '}'))/*{*/
  517.                 {
  518.                     ch = next;
  519.                     next = cpp_getc();
  520.                 }
  521.             }
  522.  
  523.             /* resume "refs" processing */
  524.             cpp_refsok = TRUE;
  525.             cpp_echo('}');
  526.  
  527.             token = BODY;
  528.             cpp_refsnl = TRUE;
  529.             break;
  530.  
  531.           case '[':
  532.             /* skip to matching ']' */
  533.             do
  534.             {
  535.                 ch = cpp_getc();
  536.             } while (ch != ']' && ch != EOF);
  537.             token = DELETED;
  538.             break;
  539.  
  540.           case '=':
  541.               /* skip to next ';' */
  542.             do
  543.             {
  544.                 ch = cpp_getc();
  545.  
  546.                 /* leave array initializers out of "refs" */
  547.                 if (ch == '{')
  548.                 {
  549.                     cpp_refsok = FALSE;
  550.                 }
  551.             } while (ch != ';' && ch != EOF);
  552.  
  553.             /* resume echoing to "refs" */
  554.             if (!cpp_refsok)
  555.             {
  556.                 cpp_refsok = TRUE;
  557.                 cpp_echo('}');
  558.                 cpp_echo(';');
  559.             }
  560.             token = SEMICOLON;
  561.             break;
  562.  
  563.           case EOF:
  564.             token = EOF;
  565.             break;
  566.  
  567.           default:
  568.             /* is this the start of a name/keyword? */
  569.             if (isalpha(ch) || ch == '_')
  570.             {
  571.                 /* collect the whole word */
  572.                 lex_name[0] = ch;
  573.                 for (i = 1, ch = cpp_getc();
  574.                      i < BLKSIZE - 1 && (isalnum(ch) || ch == '_');
  575.                      i++, ch = cpp_getc())
  576.                 {
  577.                     lex_name[i] = ch;
  578.                 }
  579.                 lex_name[i] = '\0';
  580.                 cpp_ungetc(ch);
  581.  
  582.                 /* is it a reserved word? */
  583.                 if (!strcmp(lex_name, "typedef"))
  584.                 {
  585.                     token = TYPEDEF;
  586.                     lex_seek = -1L;
  587.                 }
  588.                 else if (!strcmp(lex_name, "static")
  589.                       || !strcmp(lex_name, "private")
  590.                       || !strcmp(lex_name, "PRIVATE"))
  591.                 {
  592.                     token = KSTATIC;
  593.                     lex_seek = -1L;
  594.                 }
  595.                 else if (!strcmp(lex_name, "extern")
  596.                       || !strcmp(lex_name, "EXTERN")
  597.                       || !strcmp(lex_name, "FORWARD"))
  598.                 {
  599.                     token = EXTERN;
  600.                     lex_seek = -1L;
  601.                 }
  602.                 else
  603.                 {
  604.                     token = NAME;
  605.                     lex_seek = file_seek;
  606.                 }
  607.             }
  608.             else /* not part of a name/keyword */
  609.             {
  610.                 token = DELETED;
  611.             }
  612.  
  613.         } /* end switch(ch) */
  614.  
  615.     } while (token == DELETED);
  616.  
  617.     return token;
  618. }
  619.  
  620. /* -------------------------------------------------------------------------- */
  621. /* This is the parser.  It locates tag candidates, and then decides whether to
  622.  * generate a tag for them.
  623.  */
  624.  
  625. /* This function generates a tag for the object in lex_name, whose tag line is
  626.  * located at a given seek offset.
  627.  */
  628. void maketag(scope, seek)
  629.     int    scope;    /* 0 if global, or KSTATIC if static */
  630.     long    seek;    /* the seek offset of the line */
  631. {
  632.     /* output the tagname and filename fields */
  633.     if (scope == EXTERN)
  634.     {
  635.         /* whoa!  we should *never* output a tag for "extern" decl */
  636.         return;
  637.     }
  638.     else if (scope == KSTATIC && !no_colons)
  639.     {
  640.         fprintf(tags, "%s:%s\t%s\t", file_name, lex_name, file_name);
  641.     }
  642.     else
  643.     {
  644.         fprintf(tags, "%s\t%s\t", lex_name, file_name);
  645.     }
  646.  
  647.     /* output the target line */
  648.     putc(backward ? '?' : '/', tags);
  649.     putc('^', tags);
  650.     file_copyline(seek, tags);
  651.     putc('$', tags);
  652.     putc(backward ? '?' : '/', tags);
  653.     putc('\n', tags);
  654. }
  655.  
  656.  
  657. /* This function parses a source file, adding any tags that it finds */
  658. void ctags(name)
  659.     char    *name;    /* the name of a source file to be checked */
  660. {
  661.     int    prev;    /* the previous token from the source file */
  662.     int    token;    /* the current token from the source file */
  663.     int    scope;    /* normally 0, but could be a TYPEDEF or KSTATIC token */
  664.     int    gotname;/* boolean: does lex_name contain a tag candidate? */
  665.     long    tagseek;/* start of line that contains lex_name */
  666.  
  667.     /* open the file */
  668.     cpp_open(name);
  669.  
  670.     /* reset */
  671.     scope = 0;
  672.     gotname = FALSE;
  673.     token = SEMICOLON;
  674.  
  675.     /* parse until the end of the file */
  676.     while (prev = token, (token = lex_gettoken()) != EOF)
  677.     {
  678.         /* scope keyword? */
  679.         if (token == TYPEDEF || token == KSTATIC || token == EXTERN)
  680.         {
  681.             scope = token;
  682.             gotname = FALSE;
  683.             continue;
  684.         }
  685.  
  686.         /* name of a possible tag candidate? */
  687.         if (token == NAME)
  688.         {
  689.             tagseek = file_seek;
  690.             gotname = TRUE;
  691.             continue;
  692.         }
  693.  
  694.         /* if NAME BODY, without ARGS, then NAME is a struct tag */
  695.         if (gotname && token == BODY && prev != ARGS)
  696.         {
  697.             gotname = FALSE;
  698.             
  699.             /* ignore if in typedef -- better name is coming soon */
  700.             if (scope == TYPEDEF)
  701.             {
  702.                 continue;
  703.             }
  704.  
  705.             /* generate a tag, if -t and maybe -s */
  706.             if (incl_types && (file_header || incl_static))
  707.             {
  708.                 maketag(file_header ? 0 : KSTATIC, tagseek);
  709.             }
  710.         }
  711.  
  712.         /* If NAME ARGS BODY, then NAME is a function */
  713.         if (gotname && prev == ARGS && token == BODY)
  714.         {
  715.             gotname = FALSE;
  716.             
  717.             /* generate a tag, maybe checking -s */
  718.             if (scope != KSTATIC || incl_static)
  719.             {
  720.                 maketag(scope, tagseek);
  721.             }
  722.         }
  723.  
  724.         /* If NAME SEMICOLON or NAME COMMA, then NAME is var/typedef.
  725.          * Note that NAME ARGS SEMICOLON is an extern function declaration,
  726.          * even if the word "extern" was left off, so we need to guard
  727.          * against that possibility.
  728.          */
  729.         if (gotname && (token == SEMICOLON || token == COMMA) && prev != ARGS)
  730.         {
  731.             gotname = FALSE;
  732.  
  733.             /* generate a tag, if -v/-t and maybe -s */
  734.             if (scope == TYPEDEF && incl_types && (file_header || incl_static)
  735.              || scope == KSTATIC && incl_vars && incl_static
  736.              || incl_vars)
  737.             {
  738.                 /* a TYPEDEF outside of a header is KSTATIC */
  739.                 if (scope == TYPEDEF && !file_header)
  740.                 {
  741.                     maketag(KSTATIC, tagseek);
  742.                 }
  743.                 else /* use whatever scope was declared */
  744.                 {
  745.                     maketag(scope, tagseek);
  746.                 }
  747.             }
  748.         }
  749.  
  750.         /* reset after a semicolon or ARGS BODY pair */
  751.         if (token == SEMICOLON || (prev == ARGS && token == BODY))
  752.         {
  753.             scope = 0;
  754.             gotname = FALSE;
  755.         }
  756.     }
  757.  
  758.     /* The source file will be automatically closed */
  759. }
  760.  
  761. /* -------------------------------------------------------------------------- */
  762.  
  763. void usage()
  764. {
  765.     fprintf(stderr, "usage: ctags [flags] filenames...\n");
  766.     fprintf(stderr, "\t-B  use ?regexp? instead of /regexp/\n");
  767.     fprintf(stderr, "\t-S  include static tags, but make them look global\n");
  768.     fprintf(stderr, "\t-s  include static tags, with colons\n");
  769.     fprintf(stderr, "\t-t  include typedefs\n");
  770.     fprintf(stderr, "\t-v  include variable declarations\n");
  771.     fprintf(stderr, "\t-r  generate a \"refs\" file, too\n");
  772.     fprintf(stderr, "\t-a  append to \"tags\", instead of overwriting\n");
  773. #ifdef DEVNULL
  774.     fprintf(stderr, "\t-T  *DON'T* generate a \"tags\" file\n");
  775. #endif
  776.     exit(2);
  777. }
  778.  
  779.  
  780.  
  781. #if AMIGA
  782. # include "amiwild.c"
  783. #endif
  784.  
  785. #if VMS
  786. # include "vmswild.c"
  787. #endif
  788.  
  789. void
  790. main(argc, argv)
  791.     int    argc;
  792.     char    **argv;
  793. {
  794.     int    i, j;
  795. #ifndef __EMX__
  796. # if MSDOS || TOS || OS2
  797.     char    **wildexpand();
  798. # endif
  799. #endif
  800.  
  801.     /* build the tables used by the ctype macros */
  802.     _ct_init((uchar *)"");
  803.  
  804. #ifdef __EMX__
  805.     _wildcard(&argc, &argv);
  806. #else
  807. # if MSDOS || TOS || OS2
  808.     argv = wildexpand(&argc, argv);
  809. # endif
  810. #endif
  811.  
  812.     /* parse the option flags */
  813.     for (i = 1; i < argc && argv[i][0] == '-'; i++)
  814.     {
  815.         for (j = 1; argv[i][j]; j++)
  816.         {
  817.             switch (argv[i][j])
  818.             {
  819.               case 'B':    backward = TRUE;        break;
  820.               case 'S':    no_colons = incl_static = TRUE;    break;
  821.               case 's':    incl_static = TRUE;        break;
  822.               case 't':    incl_types = TRUE;        break;
  823.               case 'v':    incl_vars = TRUE;        break;
  824.               case 'r':    make_refs = TRUE;        break;
  825.               case 'a':    append_files = TRUE;        break;
  826. #ifdef DEVNULL
  827.               case 'T':    dont_make_tags = TRUE;        break;
  828. #endif
  829.               default:    usage();
  830.             }
  831.         }
  832.     }
  833.  
  834.     /* There should always be at least one source file named in args */
  835.     if (i == argc)
  836.     {
  837.         usage();
  838.     }
  839.  
  840.     /* open the "tags" and maybe "refs" files */
  841. #ifdef DEVNULL
  842.     if (dont_make_tags)
  843.     {
  844.         tags = fopen(DEVNULL, "w");
  845.         if (!tags)
  846.         {
  847.             perror(DEVNULL);
  848.             exit(3);
  849.         }
  850.     }
  851.     else
  852. #endif
  853.     tags = fopen(TAGS, append_files ? "a" : "w");
  854.     if (!tags)
  855.     {
  856.         perror(TAGS);
  857.         exit(3);
  858.     }
  859.     if (make_refs)
  860.     {
  861.         refs = fopen(REFS, append_files ? "a" : "w");
  862.         if (!refs)
  863.         {
  864.             perror(REFS);
  865.             exit(4);
  866.         }
  867.     }
  868.  
  869.     /* parse each source file */
  870.     for (; i < argc; i++)
  871.     {
  872.         char *p = argv[i] - 1;
  873.  
  874.         /* reconvert any converted spaces */
  875.         while (*++p)
  876.         {
  877.             if ((unsigned char)(*p) == SPACEHOLDER)
  878.             {
  879.                 *p = ' ';
  880.             }
  881.         }
  882.         ctags(argv[i]);
  883.     }
  884.  
  885.     /* close "tags" and maybe "refs" */
  886.     fclose(tags);
  887.     if (make_refs)
  888.     {
  889.         fclose(refs);
  890.     }
  891.  
  892. #ifdef SORT
  893.         /* This is a hack which will sort the tags list.   It should
  894.          * on UNIX and OS-9.  You may have trouble with csh.   Note
  895.          * that the tags list only has to be sorted if you intend to
  896.          * use it with the real vi;  elvis permits unsorted tags.
  897.          */
  898. # if OSK
  899.         system("qsort tags >-_tags; -nx; del tags; rename _tags tags");
  900. # else    
  901.         system("sort tags >_tags$$; mv _tags$$ tags");
  902. # endif
  903. #endif
  904.  
  905.     exit(0);
  906.     /*NOTREACHED*/
  907. }
  908.  
  909. #if MSDOS || TOS || OS2
  910. # define WILDCARD_NO_MAIN
  911. # include "wildcard.c"
  912. #endif
  913.