home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / elvis184.zip / src / ref.c < prev    next >
C/C++ Source or Header  |  1994-01-17  |  13KB  |  579 lines

  1. /* ref2.c */
  2.  
  3. /* This is a totally rewritten version of ref.  This version looks for the
  4.  * desired function name in the "tags" file, and then reads the header out
  5.  * from the source file.  There is no longer any need for a "refs" file.
  6.  *
  7.  * Usage:    ref [-t] [-x] [-f file] [-c class] tag
  8.  * Options:    -t       output tag info, not the description
  9.  *        -x         require exact match
  10.  *        -f file       default filename for static functions
  11.  *        -c class   default class names for class functions
  12.  */
  13. #ifdef __STDC__
  14. # include <string.h>
  15. # include <stdlib.h>
  16. #endif
  17.  
  18. #include <stdio.h>
  19. #include "config.h"
  20.  
  21. extern char    *cktagdir P_((char *, char *));
  22. extern int    getline P_((char *, int, FILE *));
  23. extern int    lookup P_((char *, char *));
  24. extern int    find P_((char *));
  25. extern void    usage P_((void));
  26. extern int    countcolons P_((char *));
  27. extern void    main P_((int, char **));
  28.  
  29.  
  30. /* This is the default path that is searched for tags */
  31. #if OSK
  32. # define DEFTAGPATH ".:/dd/defs:/dd/defs/sys:/dd/usr/src/lib:../lib:/dd/usr/lib"
  33. #else
  34. # if ANY_UNIX
  35. #  define DEFTAGPATH ".:/usr/include:/usr/include/sys:/usr/src/lib:../lib:/usr/local/lib"
  36. # else
  37. #  if MSDOS || TOS || OS2
  38. #   define DEFTAGPATH ".;C:\\include;C:\\include\\sys;C:\\lib;..\\lib"
  39. #   define SEP ';'
  40. #  else
  41. #   if AMIGA
  42. #    define DEFTAGPATH ".;Include:;Include:sys"
  43. #    define SEP ';'
  44. #   else /* any other OS */
  45. #    define DEFTAGPATH "."
  46. #   endif
  47. #  endif
  48. # endif
  49. #endif
  50.  
  51. #ifndef SEP
  52. # define SEP ':'
  53. #endif
  54.  
  55.  
  56. /* These variables reflect the command-line options given by the user. */
  57. int    taginfo;    /* boolean: give only the tag info? (not header?) */
  58. int    exact;        /* boolean: require exact match? */
  59. char    *def_file;    /* default filename for static functions */
  60. char    *def_class;    /* default classname for class members */
  61. int    colons;        /* #colons in tag: 0=normal, 1=static, 2=member */
  62.  
  63. /* This function checks for a tag in the "tags" file of given directory.
  64.  * If the tag is found, then it returns a pointer to a static buffer which
  65.  * contains the filename, a tab character, and a linespec for finding the
  66.  * the tag.  If the tag is not found in the "tags" file, or if the "tags"
  67.  * file cannot be opened or doesn't exist, then this function returns NULL.
  68.  */
  69. char *cktagdir(tag, dir)
  70.     char    *tag;    /* name of the tag to look for */
  71.     char    *dir;    /* name of the directory to check */
  72. {
  73.     char    buf[BLKSIZE];
  74.     static char found[BLKSIZE];
  75.     FILE    *tfile;
  76.     int    len;
  77.     char    *scan;
  78.  
  79. #if AMIGA
  80.     if (dir[strlen(dir) - 1] == COLON)
  81.         sprintf(buf, "%s%s", dir, TAGS);   /* no slash after colon. */
  82.     else
  83. #endif
  84.     /* construct the name of the "tags" file in this directory */
  85.     sprintf(buf, "%s%c%s", dir, SLASH, TAGS);
  86.  
  87.     /* Try to open the tags file.  Return NULL if can't open */
  88. #if AMIGA
  89.     if (buf[0] == '.' && buf[1] == SLASH)
  90.         tfile = fopen(&buf[2], "r");
  91.     else
  92. #endif
  93.     tfile = fopen(buf, "r");
  94.     if (!tfile)
  95.     {
  96.         return (char *)0;
  97.     }
  98.  
  99.     /* compute the length of the tagname once */
  100.     len = strlen(tag);
  101.  
  102.     /* read lines until we get the one for this tag */
  103.     found[0] = '\0';
  104.     while (fgets(buf, sizeof buf, tfile))
  105.     {
  106.         /* is this the one we want? */
  107.         if (!strncmp(buf, tag, len) && buf[len] == '\t')
  108.         {
  109.             /* we've found a match -- remember it */
  110.             strcpy(found, buf);
  111.  
  112.             /* if there is no default file, or this match is in
  113.              * the default file, then we've definitely found the
  114.              * one we want.  Break out of the loop now.
  115.              */
  116.             if (!def_file || !strncmp(&buf[len + 1], def_file, strlen(def_file)))
  117.             {
  118.                 break;
  119.             }
  120.         }
  121.  
  122.         /* does it maybe match the stuff after the colons? */
  123.         if (!exact)
  124.         {
  125.             scan = strchr(buf, '\t');
  126.             if (scan
  127.              && (int)(scan - buf) > len
  128.              && scan[-len - 1] == ':'
  129.              && !strncmp(tag, scan - len, len))
  130.             {
  131.                 strcpy(found, buf);
  132.                 len = (int)(scan - buf);
  133.                 break;
  134.             }
  135.         }
  136.     }
  137.  
  138.     /* we're through reading */
  139.     fclose(tfile);
  140.  
  141.     /* if there's anything in found[], use it */
  142.     if (found[0])
  143.     {
  144.         return &found[len + 1];
  145.     }
  146.  
  147.     /* else we didn't find it */
  148.     return (char *)0;
  149. }
  150.  
  151. /* This function reads a single textline from a binary file.  It returns
  152.  * the number of bytes read, or 0 at EOF.
  153.  */
  154. int getline(buf, limit, fp)
  155.     char    *buf;    /* buffer to read into */
  156.     int    limit;    /* maximum characters to read */
  157.     FILE    *fp;    /* binary stream to read from */
  158. {
  159.     int    bytes;    /* number of bytes read so far */
  160.     int    ch;    /* single character from file */
  161.  
  162.     for (bytes = 0, ch = 0; ch != '\n' && --limit > 0 && (ch = getc(fp)) != EOF; bytes++)
  163.     {
  164. #if MSDOS || TOS || OS2
  165.         /* since this is a binary file, we'll need to manually strip CR's */
  166.         if (ch == '\r')
  167.         {
  168.             continue;
  169.         }
  170. #endif
  171.         *buf++ = ch;
  172.     }
  173.     *buf = '\0';
  174.  
  175.     return bytes;
  176. }
  177.  
  178.  
  179. /* This function reads a source file, looking for a given tag.  If it finds
  180.  * the tag, then it displays it and returns TRUE.  Otherwise it returns FALSE.
  181.  * To display the tag, it attempts to output any introductory comment, the
  182.  * tag line itself, and any arguments.  Arguments are assumed to immediately
  183.  * follow the tag line, and start with whitespace.  Comments are assumed to
  184.  * start with lines that begin with "/ *", "//", "(*", or "--", and end at the
  185.  * tag line or at a blank line.
  186.  */
  187. int lookup(dir, entry)
  188.     char    *dir;    /* name of the directory that contains the source */
  189.     char    *entry;    /* source filename, <Tab>, linespec */
  190. {
  191.     char    *name;        /* basename of source file */
  192.     char    buf[BLKSIZE];    /* pathname of source file */
  193.     long    lnum;        /* desired line number */
  194.     long    thislnum;    /* current line number */
  195.     long    here;        /* seek position where current line began */
  196.     long    comment;    /* seek position of introductory comment, or -1L */
  197.     FILE    *sfile;        /* used for reading the source file */
  198.     int    len;        /* length of string */
  199.     int    noargs = 0;    /* boolean: don't show lines after tag line? */
  200.     char    *ptr;
  201.  
  202.  
  203.     /* construct the pathname of the source file */
  204.     name = entry;
  205.     strcpy(buf, dir);
  206.     ptr = buf + strlen(buf);
  207. #if AMIGA
  208.     if (ptr[-1] != COLON)
  209. #endif
  210.     *ptr++ = SLASH;
  211.     while (*entry != '\t')
  212.     {
  213.         *ptr++ = *entry++;
  214.     }
  215.     *entry++ = *ptr = '\0';
  216.  
  217.     /* searching for string or number? */
  218.     if (*entry >= '0' && *entry <= '9')
  219.     {
  220.         /* given a specific line number */
  221.         lnum = atol(entry);
  222.         entry = (char *)0;
  223.         noargs = 1;
  224.     }
  225.     else
  226.     {
  227.         /* given a string -- strip off "/^" and "$/\n" */
  228.         entry += 2;
  229.         len = strlen(entry) - 2;
  230.         if (entry[len - 1] == '$')
  231.         {
  232.             entry[len - 1] = '\n';
  233.         }
  234.         if (!strchr(entry, '('))
  235.         {
  236.             noargs = 1;
  237.         }
  238.         lnum = 0L;
  239.     }
  240.  
  241.     /* Open the file.  Note that we open the file in binary mode even
  242.      * though we know it is a text file, because ftell() and fseek()
  243.      * don't work on text files.
  244.      */
  245. #if MSDOS || TOS || OS2
  246.     sfile = fopen(buf, "rb");
  247. #else
  248. # if AMIGA
  249.     if (buf[0] == '.' && buf[1] == SLASH)
  250.         sfile = fopen(&buf[2], "r");
  251.     else
  252. # endif
  253.     sfile = fopen(buf, "r");
  254. #endif
  255.     if (!sfile)
  256.     {
  257.         /* can't open the real source file.  Try "refs" instead */
  258. #if AMIGA
  259.         if (dir[strlen(dir) - 1] == COLON)
  260.             sprintf(buf, "%srefs", dir);
  261.         else
  262. #endif
  263.         sprintf(buf, "%s%crefs", dir, SLASH);
  264. #if MSDOS || TOS || OS2
  265.         sfile = fopen(buf, "rb");
  266. #else
  267. # if AMIGA
  268.         if (buf[0] == '.' && buf[1] == SLASH)
  269.             sfile = fopen(&buf[2], "r");
  270.         else
  271. # endif
  272.         sfile = fopen(buf, "r");
  273. #endif
  274.         if (!sfile)
  275.         {
  276.             /* failed! */
  277.             return 0;
  278.         }
  279.         name = "refs";
  280.     }
  281.  
  282.     /* search the file */
  283.     for (comment = -1L, thislnum = 0; here = ftell(sfile), thislnum++, getline(buf, BLKSIZE, sfile) > 0; )
  284.     {
  285.         /* Is this the start/end of a comment? */
  286.         if (comment == -1L)
  287.         {
  288.             /* starting a comment? */
  289.             if (buf[0] == '/' && buf[1] == '*'
  290.              || buf[0] == '/' && buf[1] == '/'
  291.              || buf[0] == '(' && buf[1] == '*'
  292.              || buf[0] == '-' && buf[1] == '-')
  293.             {
  294.                 comment = here;
  295.             }
  296.         }
  297.         else
  298.         {
  299.             /* ending a comment? */
  300.             if (buf[0] == '\n' || buf[0] == '#')
  301.             {
  302.                 comment = -1L;
  303.             }
  304.         }
  305.  
  306.         /* is this the tag line? */
  307.         if (lnum == thislnum || (entry && !strncmp(buf, entry, len)))
  308.         {
  309.             /* display the filename & line number where found */
  310.             if (strcmp(dir, "."))
  311.                 printf("%s%c%s, line %ld:\n", dir, SLASH, name, thislnum);
  312.             else
  313.                 printf("%s, line %ld:\n", name, thislnum);
  314.  
  315.             /* if there were introductory comments, show them */
  316.             if (comment != -1L)
  317.             {
  318.                 fseek(sfile, comment, 0);
  319.                 while (comment != here)
  320.                 {
  321.                     getline(buf, BLKSIZE, sfile);
  322.                     fputs(buf, stdout);
  323.                     comment = ftell(sfile);
  324.                 }
  325.  
  326.                 /* re-fetch the tag line */
  327.                 fgets(buf, BLKSIZE, sfile);
  328.             }
  329.  
  330.             /* show the tag line */
  331.             fputs(buf, stdout);
  332.  
  333.             /* are we expected to show argument lines? */
  334.             if (!noargs)
  335.             {
  336.                 /* show any argument lines */
  337.                 while (getline(buf, BLKSIZE, sfile) > 0
  338.                     && buf[0] != '#'
  339.                     && strchr(buf, '{') == (char *)0)
  340.                 {
  341.                     fputs(buf, stdout);
  342.                 }
  343.             }
  344.  
  345.             /* Done!  Close the file, and return TRUE */
  346.             fclose(sfile);
  347.             return 1;
  348.         }
  349.     }
  350.  
  351.     /* not found -- return FALSE */
  352.     return 0;
  353. }
  354.  
  355. /* This function searches through the entire search path for a given tag.
  356.  * If it finds the tag, then it displays the info and returns TRUE;
  357.  * otherwise it returns FALSE.
  358.  */
  359. int find(tag)
  360.     char    *tag;    /* the tag to look up */
  361. {
  362.     char    *tagpath;
  363.     char    dir[80];
  364.     char    *ptr;
  365.     int    len;
  366.  
  367.     if (colons == 1)
  368.     {
  369.         /* looking for static function -- only look in current dir */
  370.         tagpath = ".";
  371.     }
  372.     else
  373.     {
  374.         /* get the tagpath from the environment.  Default to DEFTAGPATH */
  375.         tagpath = getenv("TAGPATH");
  376.         if (!tagpath)
  377.         {
  378.             tagpath = DEFTAGPATH;
  379.         }
  380.     }
  381.  
  382.     /* for each entry in the path... */
  383.     while (*tagpath)
  384.     {
  385.         /* Copy the entry into the dir[] buffer */
  386.         for (ptr = dir; *tagpath && *tagpath != SEP; tagpath++)
  387.         {
  388.             *ptr++ = *tagpath;
  389.         }
  390.         if (*tagpath == SEP)
  391.         {
  392.             tagpath++;
  393.         }
  394.  
  395.         /* if the entry ended with "/tags", then strip that off */
  396.         len = strlen(TAGS);
  397.         if (&dir[len] < ptr && ptr[-len - 1] == SLASH && !strncmp(&ptr[-len], TAGS, len))
  398.         {
  399.             ptr -= len + 1;
  400.         }
  401.  
  402.         /* if the entry is now an empty string, then assume "." */
  403.         if (ptr == dir)
  404.         {
  405.             *ptr++ = '.';
  406.         }
  407.         *ptr = '\0';
  408.  
  409.         /* look for the tag in this path.  If found, then display it
  410.          * and exit.
  411.          */
  412.         ptr = cktagdir(tag, dir);
  413.         if (ptr)
  414.         {
  415.             /* just supposed to display tag info? */
  416.             if (taginfo)
  417.             {
  418.                 /* then do only that! */
  419.                 if (strcmp(dir, "."))
  420.                 {
  421.                     printf("%s%c%s", dir, SLASH, ptr);
  422.                 }
  423.                 else
  424.                 {
  425.                     /* avoid leading "./" if possible */
  426.                     fputs(ptr, stdout);
  427.                 }
  428.                 return 1;
  429.             }
  430.             else
  431.             {
  432.                 /* else look up the declaration of the thing */
  433.                 return lookup(dir, ptr);
  434.             }
  435.         }
  436.     }
  437.  
  438.     /* if we get here, then the tag wasn't found anywhere */
  439.     return 0;
  440. }
  441.  
  442. void usage()
  443. {
  444.     fputs("usage: ref [-t] [-x] [-c class] [-f file] tag\n", stderr);
  445.     fputs("   -t        output tag info, instead of the function header\n", stderr);
  446.     fputs("   -x        require exact match, including colons\n", stderr);
  447.     fputs("   -f File   tag might be a static function in File\n", stderr);
  448.     fputs("   -c Class  tag might be a member of class Class\n", stderr);
  449.     exit(2);
  450. }
  451.  
  452.  
  453. int countcolons(str)
  454.     char    *str;
  455. {
  456.     while (*str != ':' && *str)
  457.     {
  458.         str++;
  459.     }
  460.     if (str[0] != ':')
  461.     {
  462.         return 0;
  463.     }
  464.     else if (str[1] != ':')
  465.     {
  466.         return 1;
  467.     }
  468.     return 2;
  469. }
  470.  
  471. void main(argc, argv)
  472.     int    argc;
  473.     char    **argv;
  474. {
  475.     char    def_tag[100];    /* used to build tag name with default file/class */
  476.     int    i;
  477.  
  478.     /* parse flags */
  479.     for (i = 1; i < argc && argv[i][0] == '-'; i++)
  480.     {
  481.         switch (argv[i][1])
  482.         {
  483.           case 't':
  484.             taginfo = 1;
  485.             break;
  486.  
  487.           case 'x':
  488.             exact = 1;
  489.             break;
  490.  
  491.           case 'f':
  492.             if (argv[i][2])
  493.             {
  494.                 def_file = &argv[i][2];
  495.             }
  496.             else if (++i < argc)
  497.             {
  498.                 def_file = argv[i];
  499.             }
  500.             else
  501.             {
  502.                 usage();
  503.             }
  504.             break;
  505.  
  506.           case 'c':
  507.             if (argv[i][2])
  508.             {
  509.                 def_class = &argv[i][2];
  510.             }
  511.             else if (++i < argc)
  512.             {
  513.                 def_class = argv[i];
  514.             }
  515.             else
  516.             {
  517.                 usage();
  518.             }
  519.             break;
  520.  
  521.           default:
  522.             usage();
  523.         }
  524.     }
  525.  
  526.     /* if no tag was given, complain */
  527.     if (i + 1 != argc)
  528.     {
  529.         usage();
  530.     }
  531.  
  532.     /* does the tag have an explicit class or file? */
  533.     colons = countcolons(argv[i]);
  534.  
  535.     /* if not, then maybe try some defaults */
  536.     if (colons == 0)
  537.     {
  538.         /* try a static function in the file first */
  539.         if (def_file)
  540.         {
  541.             sprintf(def_tag, "%s:%s", def_file, argv[i]);
  542.             colons = 1;
  543.             if (find(def_tag))
  544.             {
  545.                 exit(0);
  546.             }
  547.         }
  548.  
  549.         /* try a member function for a class */
  550.         if (def_class)
  551.         {
  552.             sprintf(def_tag, "%s::%s", def_class, argv[i]);
  553.             colons = 2;
  554.             if (find(def_tag))
  555.             {
  556.                 exit(0);
  557.             }
  558.         }
  559.  
  560.         /* oh, well */
  561.         colons = 0;
  562.     }
  563.     else /* at least one colon was given */
  564.     {
  565.         /* this implies -x */
  566.         exact = 1;
  567.     }
  568.  
  569.     /* find the tag */
  570.     if (find(argv[i]))
  571.     {
  572.         exit(0);
  573.     }
  574.  
  575.     /* Give up.  If doing tag lookup then exit(0), else exit(1) */
  576.     exit(!taginfo);
  577.     /*NOTREACHED*/
  578. }
  579.