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