home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / elvis_1.4.tar.Z / elvis_1.4.tar / ctags.c < prev    next >
C/C++ Source or Header  |  1990-12-06  |  8KB  |  351 lines

  1. /* ctags.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains the complete source to the ctags program. */
  12.  
  13. /* Special abilities:
  14.  * Can also make a "refs" file for use by the "ref" program.
  15.  */
  16.  
  17. /* Limitations:
  18.  * This version of ctags always writes its output to the file "tags".
  19.  * It assumes that every command-line argument (but "-r") is a C source file.
  20.  * It does not sort the list of tags, unless CFLAGS=-DSORT.
  21.  * It does not recognize duplicate definitions.
  22.  * It does not try to handle "static" functions in a clever way.
  23.  * It probably won't scan ANSI-C source code very well.
  24.  */
  25.  
  26. /* Implementation:
  27.  * Lines are scanned one-at-a-time.
  28.  * The context of lines is tracked via a finite state machine.
  29.  * Contexts are:
  30.  *    EXPECTFN - we're looking for a function name.
  31.  *    ARGS     - between function name and its opening {
  32.  *    BODY     - we found a function name, skip to end of body.
  33.  *
  34.  * Function tags are referenced by a search string, so that lines may be
  35.  * inserted or deleted without mucking up the tag search.
  36.  *
  37.  * Macro tags are referenced by their line number, because 1) they usually
  38.  * occur near the top of a file, so their line# won't change much; 2)  They
  39.  * often contain characters that are hard to search for; and 3)  Their #define
  40.  * line is likely to be altered.
  41.  *
  42.  * Each line of the resulting "tags" file describes one tag.  Lines begin with
  43.  * the tag name, then a tab, then the file name, then a tab, and then either
  44.  * a line number or a slash-delimited search string.
  45.  */
  46.  
  47. #include <ctype.h>
  48. #include <stdio.h>
  49. #include "config.h"
  50.  
  51. #define REFS    "refs"
  52.  
  53. #if OSK
  54. #define NUMFMT    "%%.%ds\t%%s\t%%ld\n"
  55. #define SRCHFMT    "%%.%ds\t%%s\t/^%%s$/\n"
  56. #define MAINFMT    "M%%.%ds\t%%s\t/^%%s$/\n"
  57. static char fmt[256];
  58. #else
  59. #define NUMFMT    "%.*s\t%s\t%ld\n"
  60. #define SRCHFMT    "%.*s\t%s\t/^%s$/\n"
  61. #define MAINFMT    "M%.*s\t%s\t/^%s$/\n"
  62. #endif
  63.  
  64. #ifdef VERBOSE
  65. # define SAY(x)    fprintf(stderr, "%s\n", x);
  66. #else
  67. # define SAY(x)
  68. #endif
  69.  
  70. #define EXPECTFN 1
  71. #define    ARGS     2
  72. #define BODY     3
  73.  
  74. extern char    *fgets();
  75.  
  76. char        *progname;    /* argv[0], used for diagnostic output */
  77.  
  78. main(argc, argv)
  79.     int    argc;
  80.     char    **argv;
  81. {
  82.     FILE    *fp;
  83.     int    i;
  84.     FILE    *refs;    /* used to write to the refs file */
  85.  
  86. #if MSDOS || TOS
  87.     char **wildexpand();
  88.     argv=wildexpand(&argc, argv);
  89. #endif
  90.     /* notice the program name */
  91.     progname = argv[0];
  92.  
  93.     /* create the "refs" file if first arg is "-r" */
  94.     if (argc > 1 && !strcmp(argv[1], "-r"))
  95.     {
  96.         /* delete the "-r" flag from the args list */
  97.         argc--;
  98.         argv++;
  99.  
  100.         /* open the "refs" file for writing */
  101.         refs = fopen(REFS, "w");
  102.         if (!refs)
  103.         {
  104.             fprintf(stderr, "%s: could not create \"%s\"\n", progname, REFS);
  105.             exit(2);
  106.         }
  107.     }
  108.     else
  109.     {
  110.         refs = (FILE *)0;
  111.     }
  112.  
  113.     /* process each file named on the command line, or complain if none */
  114.     if (argc > 1)
  115.     {
  116.         /* redirect stdout to go to the "tags" file */
  117.         if (!freopen("tags", "w", stdout))
  118.         {
  119.             fprintf(stderr, "%s: could not create \"%s\"\n", progname, TAGS);
  120.             exit(2);
  121.         }
  122.  
  123.         for (i = 1; i < argc; i++)
  124.         {
  125.             /* process this named file */
  126.             fp = fopen(argv[i], "r");
  127.             if (!fp)
  128.             {
  129.                 fprintf(stderr, "%s: could not read \"%s\"\n", progname, argv[i]);
  130.                 continue;
  131.             }
  132.             ctags(fp, argv[i], refs);
  133.             fclose(fp);
  134.         }
  135. #ifdef SORT
  136.         /* This is a hack which will sort the tags list.   It should
  137.          * on UNIX and Minix.  You may have trouble with csh.   Note
  138.          * that the tags list only has to be sorted if you intend to
  139.          * use it with the real vi;  elvis permits unsorted tags.
  140.          */
  141.         fflush(stdout);
  142. #if OSK
  143.         fclose(stdout);
  144.         system("qsort tags >-_tags; -nx; del tags; rename _tags tags");
  145. #else    
  146.         system("sort tags >_tags$$; mv _tags$$ tags");
  147. #endif
  148. #endif
  149.         exit(0);
  150.     }
  151.     else
  152.     {
  153.         fprintf(stderr, "usage: %s *.[ch]\n", progname);
  154.         exit(2);
  155.     }
  156. }
  157.  
  158.  
  159. /* this function finds all tags in a given file */
  160. ctags(fp, name, refs)
  161.     FILE    *fp;        /* stream of the file to scan */
  162.     char    *name;        /* name of the file being scanned */
  163.     FILE    *refs;        /* NULL, or where to write refs lines */
  164. {
  165.     int    context;    /* context - either EXPECTFN, ARGS, or BODY */
  166.     long    lnum;        /* line number */
  167.     char    text[1000];    /* a line of text from the file */
  168.     char    *scan;        /* used for searching through text */
  169.     int    len;        /* length of the line */
  170.  
  171.     /* for each line of the file... */
  172.     for (context = EXPECTFN, lnum = 1; fgets(text, sizeof text, fp); lnum++)
  173.     {
  174. #ifdef VERBOSE
  175.         switch(context)
  176.         {
  177.           case EXPECTFN:    scan = "EXPECTFN";    break;
  178.           case ARGS:        scan = "ARGS    ";    break;
  179.           case BODY:        scan = "BODY    ";    break;
  180.           default:        scan = "context?";
  181.         }
  182.         fprintf(stderr, "%s:%s", scan, text);
  183. #endif
  184.  
  185.         /* start of body? */
  186.         if (text[0] == '{')
  187.         {
  188.             context = BODY;
  189.             SAY("Start of BODY");
  190.             continue;
  191.         }
  192.  
  193.         /* argument line, to be written to "refs" ? */
  194.         if (refs && context == ARGS)
  195.         {
  196.             if (text[0] != '\t')
  197.             {
  198.                 putc('\t', refs);
  199.             }
  200.             fputs(text, refs);
  201.             SAY("Argument line");
  202.             continue;
  203.         }
  204.  
  205.         /* ignore empty or indented lines */
  206.         if (text[0] <= ' ')
  207.         {
  208.             SAY("Empty or indented");
  209.             continue;
  210.         }
  211.  
  212.         /* end of body? */
  213.         if (text[0] == '}')
  214.         {
  215.             context = EXPECTFN;
  216.             SAY("End of BODY");
  217.             continue;
  218.         }
  219.  
  220.         /* ignore lines in the body of a function */
  221.         if (context != EXPECTFN)
  222.         {
  223.             SAY("BODY or ARGS");
  224.             continue;
  225.         }
  226.  
  227.         /* strip the newline */
  228.         len = strlen(text);
  229.         text[--len] = '\0';
  230.  
  231.         /* a preprocessor line? */
  232.         if (text[0] == '#')
  233.         {
  234.             /* find the preprocessor directive */
  235.             for (scan = &text[1]; isspace(*scan); scan++)
  236.             {
  237.             }
  238.  
  239.             /* if it's a #define, make a tag out of it */
  240.             if (!strncmp(scan, "define", 6))
  241.             {
  242.                 /* find the start of the symbol name */
  243.                 for (scan += 6; isspace(*scan); scan++)
  244.                 {
  245.                 }
  246.  
  247.                 /* find the length of the symbol name */
  248.                 for (len = 1;
  249.                      isalnum(scan[len]) || scan[len] == '_';
  250.                      len++)
  251.                 {
  252.                 }
  253. #if OSK
  254.                 sprintf(fmt, NUMFMT, len);
  255.                 printf(fmt, scan, name, lnum);
  256. #else            
  257.                 printf(NUMFMT, len, scan, name, lnum);
  258. #endif
  259.             }
  260.             SAY("Preprocessor line");
  261.             continue;
  262.         }
  263.  
  264.         /* an extern or static declaration? */
  265.         if (text[len - 1] == ';'
  266.          || !strncmp(text, "extern", 6)
  267.          || !strncmp(text, "EXTERN", 6)
  268.          || !strncmp(text, "static", 6)
  269.          || !strncmp(text, "PRIVATE", 7))
  270.         {
  271.             SAY("Extern or static");
  272.             continue;
  273.         }
  274.  
  275.         /* if we get here & the first punctuation other than "*" is
  276.          * a "(" which is immediately preceded by a name, then
  277.          * assume the name is that of a function.
  278.          */
  279.         for (scan = text; *scan; scan++)
  280.         {
  281.             if (ispunct(*scan)
  282.              && !isspace(*scan) /* in BSD, spaces are punctuation?*/
  283.              && *scan != '*' && *scan != '_' && *scan != '(')
  284.             {
  285.                 SAY("Funny punctuation");
  286.                 goto ContinueContinue;
  287.             }
  288.  
  289.             if (*scan == '(')
  290.             {
  291.                 /* permit 0 or 1 spaces between name & '(' */
  292.                 if (scan > text && scan[-1] == ' ')
  293.                 {
  294.                     scan--;
  295.                 }
  296.  
  297.                 /* find the start & length of the name */
  298.                 for (len = 0, scan--;
  299.                      scan >= text && (isalnum(*scan) || *scan == '_');
  300.                      scan--, len++)
  301.                 {
  302.                 }
  303.                 scan++;
  304.  
  305.                 /* did we find a function? */
  306.                 if (len > 0)
  307.                 {
  308.                     /* found a function! */
  309.                     if (len == 4 && !strncmp(scan, "main", 4))
  310.                     {
  311. #if OSK
  312.                         sprintf(fmt, MAINFMT, strlen(name) - 2);
  313.                         printf(fmt, name, name, text);
  314. #else            
  315.                         printf(MAINFMT, strlen(name) - 2, name, name, text);
  316. #endif
  317.                     }
  318. #if OSK
  319.                     sprintf(fmt, SRCHFMT, len);
  320.                     printf(fmt, scan, name, text);
  321. #else                
  322.                     printf(SRCHFMT, len, scan, name, text);
  323. #endif
  324.                     context = ARGS;
  325.  
  326.                     /* add a line to refs, if needed */
  327.                     if (refs)
  328.                     {
  329.                         fputs(text, refs);
  330.                         putc('\n', refs);
  331.                     }
  332.  
  333.                     goto ContinueContinue;
  334.                 }
  335.             }
  336.             else
  337.             {
  338.                 SAY("No parenthesis");
  339.             }
  340.         }
  341.         SAY("No punctuation");
  342.  
  343. ContinueContinue:;
  344.     }
  345. }
  346.  
  347. #if MSDOS || TOS
  348. #define        WILDCARD_NO_MAIN
  349. #include    "wildcard.c"
  350. #endif
  351.