home *** CD-ROM | disk | FTP | other *** search
/ High Voltage Shareware / high1.zip / high1 / DIR36 / KEXX.ZIP / CTAGS.C < prev    next >
C/C++ Source or Header  |  1987-04-24  |  9KB  |  362 lines

  1. /* ctags.c -- Generate tags for subsequent use by Brief or VI editors.
  2.  
  3.    Usage: ctags source1.c source2.c ... >tags
  4.  
  5.    This program will perform a simple parsing of one or more C source
  6.    files and write a "tags" file to stdout. This file is then used in
  7.    conjunction with tagging commands build into VI and available (as 
  8.    macros) from the Solution Systems BBS. The tags file will contain
  9.    a line for each procedure in the source file. Each line has the form:
  10.  
  11.    <procedure name> <file name> <search criteria>
  12.  
  13.    The search criteria contains the entire source line containing the
  14.    procedure name to reduce the possibility of the search finding the
  15.    wrong line.
  16.  
  17.    This has been compiled under Microsoft C V4.0 and will also compile
  18.    under Unix V on an AT&T 3B1.    When using Microsoft C, link with
  19.    ssetargv.obj to enable wild card expansion of command line arguments.
  20.  
  21.    01/11/87    Initial release
  22.  
  23.    02/01/87    Misc. minor enhansements.
  24.  
  25.    04/22/87    Tag #defined names in addition to function names.
  26.  
  27.    Paul Verket */
  28.  
  29. #define    LINT_ARGS
  30.  
  31. #include <stdio.h>
  32. #include <ctype.h>
  33.  
  34. #ifndef SEEK_SET
  35. #    define SEEK_SET    0
  36. #endif
  37.  
  38. typedef enum {T_WORD, T_BRACEEXP, T_COMMA, T_SEMI, T_PREPROCESS, 
  39.     T_OPENPAREN, T_CLOSEPAREN, T_OPENBRACE, T_CLOSEBRACE, T_NL, 
  40.     T_EOF} TOKEN;
  41.  
  42. main(argc, argv)
  43. int    argc;
  44. char    *argv[];
  45. {
  46.     FILE    *in_file;
  47.  
  48.     if (argc < 2) {
  49.         fprintf(stderr, "Usage: %s file [file...] [>tags]\n", argv[0]);
  50.         exit(1);
  51.         }
  52.  
  53.     /* Cycle through each source-file argument on the command line. */
  54.     while (argc-- > 1) {
  55.         in_file = fopen((++argv)[0], "r");
  56.         if (in_file == NULL) {
  57.             perror(argv[0]);
  58.             exit(1);
  59.             }
  60.         find_functions(argv[0], in_file);
  61.         fclose(in_file);
  62.         }
  63.  
  64.     exit(0);
  65.     }
  66.  
  67. find_functions(filename, in_file)
  68. char    filename[];
  69. FILE    *in_file;
  70. {
  71.     TOKEN    gettoken(),
  72.         curr_token;
  73.     enum    {NEUTRAL, NAME, FN_NAME, INPAREN, INBRACE, CHECK_DEFINE, 
  74.         RECORD_DEFINE, PREPROCESSOR} 
  75.             state = NEUTRAL;
  76.     char    word[132],
  77.         function[132];
  78.     long    line_start,
  79.         defn_start;    /* ftell() of the procedure line */
  80.     int    paren_cnt,
  81.         brace_cnt;
  82.  
  83.     while ((curr_token = gettoken(word, &line_start, in_file)) != T_EOF) 
  84.         switch((int) state) {
  85.             /* The "home" state. If a "word" is found, assume that it is
  86.            a procedure name. If T_PREPROCESS, look for #define names
  87.            and toss the rest of the line since macro definitions look 
  88.            like procedures. If an open brace is found, start gobbling 
  89.            up the text contained within the braces. Keep a brace count 
  90.            to handle nested braces. */
  91.         case NEUTRAL:
  92.             switch ((int) curr_token) {
  93.                 case T_WORD:
  94.                     state = NAME;
  95.                     /* Note that the parens may start on
  96.                        the next line, so store the offset
  97.                        now. */
  98.                     defn_start = line_start;
  99.                     continue;
  100.                 case T_PREPROCESS:
  101.                     state = CHECK_DEFINE;
  102.                     defn_start = line_start;
  103.                     continue;
  104.                 case T_OPENBRACE:
  105.                     state = INBRACE;
  106.                     brace_cnt = 1;
  107.                     continue;
  108.                 default:
  109.                     continue;
  110.                 }
  111.         /* All subsequent "word"s will be assumed to be the real
  112.            function name until an open paren is found. If something
  113.            other than a word or paren is found, then this wasn't
  114.            a function name after all. */
  115.         case NAME:
  116.             switch ((int) curr_token) {
  117.                 case T_WORD:
  118.                 case T_NL:
  119.                     defn_start = line_start;
  120.                     continue;
  121.                 case T_OPENPAREN:
  122.                     state = INPAREN;
  123.                     strcpy(function, word);
  124.                     paren_cnt = 1;
  125.                     continue;
  126.                 default:
  127.                     state = NEUTRAL;
  128.                     continue;
  129.                 }
  130.         /* Eat up all the stuff within parens until the close paren
  131.            is found. Keep a counter to handle nested parens. */
  132.         case INPAREN:
  133.             switch ((int) curr_token) {
  134.                 case T_OPENPAREN:
  135.                     paren_cnt++;
  136.                     continue;
  137.                 case T_CLOSEPAREN:
  138.                     if (--paren_cnt == 0)
  139.                         state = FN_NAME;
  140.                     continue;
  141.                 default:
  142.                     continue;
  143.                 }
  144.         /* If a comma or a semicolon is found, then this was a false
  145.            alarm. If an opening brace or another word is found, then
  146.            we found a procedure definition. */
  147.         case FN_NAME:
  148.             switch ((int) curr_token) {
  149.                 case T_COMMA:
  150.                 case T_SEMI:
  151.                     state = NEUTRAL;
  152.                     continue;
  153.                 case T_NL:
  154.                     continue;
  155.                 case T_OPENBRACE:
  156.                     state = INBRACE;
  157.                     brace_cnt = 1;
  158.                     printf("%s %s ", function, filename);
  159.                     print_defn_line(in_file, defn_start);
  160.                     continue;
  161.                 default:
  162.                     state = NEUTRAL;
  163.                     printf("%s %s ", function, filename);
  164.                     print_defn_line(in_file, defn_start);
  165.                     continue;
  166.                 }
  167.         /* Loop until the closing brace is found. Keep a counter to
  168.            handle nested braces. */
  169.         case INBRACE:
  170.             switch((int) curr_token) {
  171.                 case T_OPENBRACE:
  172.                     brace_cnt++;
  173.                     continue;
  174.                 case T_CLOSEBRACE:
  175.                     if (--brace_cnt == 0) 
  176.                         state = NEUTRAL;
  177.                     continue;
  178.                 default:
  179.                     continue;
  180.                 }
  181.         /* Check preprocessor lines for #define statements */
  182.         case CHECK_DEFINE:
  183.             switch ((int) curr_token) {
  184.                 case T_WORD:
  185.                     if (0 == strcmp(word, "define"))
  186.                         state = RECORD_DEFINE;
  187.                     else state = PREPROCESSOR;
  188.                     continue;
  189.                 default:
  190.                     state = PREPROCESSOR;
  191.                     continue;
  192.                 }
  193.         /* Record the defined name in the same way as function names */
  194.         case RECORD_DEFINE:
  195.             state = PREPROCESSOR; /* toss the rest */
  196.             printf("%s %s ", word, filename);
  197.             print_defn_line(in_file, defn_start);
  198.             continue;
  199.         /* Handle the preprocessor line until a new-line is found.
  200.            The tokenizer tosses escaped new lines. */
  201.         case PREPROCESSOR:
  202.             switch ((int) curr_token) {
  203.                 case T_NL:
  204.                     state = NEUTRAL;
  205.                     continue;
  206.                 default:
  207.                     continue;
  208.                 }
  209.         }
  210.     }
  211.  
  212. /* Break up input file into tokens. Take care with characters inside quotes
  213.    and comments that might cause trouble. (like braces and parens!) */
  214. static TOKEN gettoken(word, line_start, in_file)
  215. char    *word;
  216. long    *line_start;
  217. FILE    *in_file;
  218. {
  219.     enum {NEUTRAL, INQUOTE, INSQUOTE, INWORD, INCOMMENT} 
  220.         state = NEUTRAL;
  221.     static int    col_count = 0;
  222.     int    c,
  223.         c2;
  224.     char    *w;
  225.  
  226.     w = word;
  227.     while ((c = getc(in_file)) != EOF) {
  228.         /* Keep a column count to aid in finding preprocessor lines.
  229.            Keep the ftell() of the start of the line for use when a
  230.            source line is to be printed. */
  231.         if (c == '\n') {
  232.             col_count = 0;
  233.             *line_start = ftell(in_file);
  234.             }
  235.         else col_count++;
  236.  
  237.         switch((int) state) {
  238.             /* The "home" state. Quoted strings and comments are
  239.                stripped. Words consisting of letters, digits and
  240.                the underscore are gathered. */
  241.             case NEUTRAL:
  242.                 switch(c) {
  243.                     case '(':
  244.                         return T_OPENPAREN;
  245.                     case ')':
  246.                         return T_CLOSEPAREN;
  247.                     case '#':
  248.                         if (col_count == 1)
  249.                             return T_PREPROCESS;
  250.                         continue;
  251.                     case '\n':
  252.                         return T_NL;
  253.                     case '"':
  254.                         state = INQUOTE;
  255.                         continue;
  256.                     case '\'':
  257.                         state = INSQUOTE;
  258.                         continue;
  259.                     case '{':
  260.                         return T_OPENBRACE;
  261.                     case '}':    /*}*/
  262.                         return T_CLOSEBRACE;
  263.                     case '/':    /* start of comment? */
  264.                         if ((c2 = getc(in_file)) == '*') {
  265.                             state = INCOMMENT;
  266.                             col_count++;
  267.                             continue;
  268.                             }
  269.                         else {
  270.                             ungetc(c2, in_file);
  271.                             continue;
  272.                             }
  273.                     case ';':
  274.                         return T_SEMI;
  275.                     case ',':
  276.                         return T_COMMA;
  277.                     case '\\': /* toss the escape */
  278.                         getc(in_file);
  279.                         continue;
  280.                     default:
  281.                         if (isalnum(c) || c == '_') {
  282.                             state = INWORD;
  283.                             *w++ = c;
  284.                             }
  285.                         continue;
  286.                     }
  287.             /* Stay in this state, tossing characters, until the
  288.                closing marker. */
  289.             case INCOMMENT:
  290.                 switch(c) {
  291.                     case '*':    /* end of comment? */
  292.                         if ((c2 = getc(in_file)) == '/') {
  293.                             state = NEUTRAL;
  294.                             col_count++;
  295.                             continue;
  296.                             }
  297.                         else {
  298.                             ungetc(c2, in_file);
  299.                             continue;
  300.                             }
  301.                     default:
  302.                         continue;
  303.                     }
  304.             case INQUOTE:
  305.                 switch(c) {
  306.                     case '"':
  307.                         state = NEUTRAL;
  308.                         continue;
  309.                     case '\\': /* toss the escape */
  310.                         getc(in_file);
  311.                         continue;
  312.                     default:
  313.                         continue;
  314.                     }
  315.             case INSQUOTE:
  316.                 switch(c) {
  317.                     case '\'':
  318.                         state = NEUTRAL;
  319.                         continue;
  320.                     case '\\': /* toss the escape */
  321.                         getc(in_file);
  322.                         continue;
  323.                     default:
  324.                         continue;
  325.                     }
  326.             /* Gather up the word. */
  327.             case INWORD:
  328.                 if (isalnum(c) || c == '_') {
  329.                     *w++ = c;
  330.                     continue;
  331.                     }
  332.                 else    {
  333.                     ungetc(c, in_file);
  334.                     *w = NULL;
  335.                     col_count--;
  336.                     return T_WORD;
  337.                     }
  338.             }
  339.         }
  340.     
  341.     return T_EOF;
  342.     }
  343.  
  344. /* Use the previously stored ftell() of the start of line to dump the source
  345.    line. */
  346. print_defn_line(in_file, line_start)
  347. FILE    *in_file;
  348. long    line_start;
  349. {
  350.     long    current_position;
  351.     int    c;
  352.  
  353.     current_position = ftell(in_file);
  354.     fseek(in_file, line_start, SEEK_SET);
  355.  
  356.     printf("?^");
  357.     while ((c = getc(in_file)) != EOF && c != '\n') putchar(c);
  358.     printf("$?\n");
  359.  
  360.     fseek(in_file, current_position, SEEK_SET);
  361.     }
  362.