New ref program from elvis 1.7 that implements ctags.
authorAndrew Moore <alm@FreeBSD.org>
Fri, 18 Jun 1993 13:08:09 +0000 (13:08 +0000)
committerAndrew Moore <alm@FreeBSD.org>
Fri, 18 Jun 1993 13:08:09 +0000 (13:08 +0000)
usr.bin/ref/Makefile [new file with mode: 0644]
usr.bin/ref/ref.1 [new file with mode: 0644]
usr.bin/ref/ref.c [new file with mode: 0644]

diff --git a/usr.bin/ref/Makefile b/usr.bin/ref/Makefile
new file mode 100644 (file)
index 0000000..32de9d5
--- /dev/null
@@ -0,0 +1,7 @@
+
+PROG=  ref
+CFLAGS+=-I${.CURDIR}/../elvis
+SRCS=  ref.c
+
+.include <bsd.prog.mk>
+.PATH: ${.CURDIR}/../elvis
diff --git a/usr.bin/ref/ref.1 b/usr.bin/ref/ref.1
new file mode 100644 (file)
index 0000000..e7d2e17
--- /dev/null
@@ -0,0 +1,88 @@
+.TH REF 1
+.SH NAME
+ref - Display a C function header
+.SH SYNOPSIS
+\fBref\fR [-t] [-c \fIclass\fR]... [-f \fIfile\fR]... \fItag\fR
+.SH DESCRIPTION
+\fIref\fP quickly locates and displays the header of a function.
+To do this, \fIref\fR
+looks in the "tags" file for the line that describes the function, and then 
+scans the source file for the function.
+When it locates the function, it displays an introductory comment
+(if there is one), the function's declaration, and the declarations of all
+arguments.
+.SH "SEARCH METHOD"
+.PP
+\fIref\fR uses a fairly sophisticated tag look-up algorithm.
+If you supply a filename via \fB-f\fR \fIfile\fR, then elvis first scans
+the tags file for a static tag from that file.
+This search is limited to the tags file in the current directory.
+.PP
+If you supply a classname via \fB-c\fR \fIclass\fR, then elvis searches
+for a tag from that class.
+This search is not limited to the current directory;
+You can supply a list of directories in the environment variable \fITAGPATH\fR,
+and \fIref\fR will search through the "tags" file in each directory until it finds
+a tag in the desired class.
+.PP
+If that fails, \fIref\fR will then try to look up an ordinary global tag.
+This search checks all of the directories listed in \fITAGPATH\fR, too.
+.PP
+If you've given the \fB-t\fR flag, then \fIref\fR will simply output the tag line that
+it found, and then exit.
+Without \fB-t\fR, though, \fIref\fR will search for the tag line.
+It will try to open the source file, which should be in the same directory
+as the tags file where the tag was discovered.
+If the source file doesn't exist, or is unreadable, then \fIref\fR will try to open
+a file called "\fIrefs\fR" in that directory.
+Either way, \fIref\fR will try to locate the tag, and display whatever it finds.
+.SH "INTERACTION WITH ELVIS"
+.PP
+\fIref\fP is used by \fIelvis\fR' shift-K command.
+If the cursor is located on a word such as "splat", in the file "foo.c",
+then \fIelvis\fR will invoke \fIref\fR with the command "ref -f foo.c splat".
+.PP
+If \fIelvis\fR has been compiled with the -DEXTERNAL_TAGS flag, then \fIelvis\fR will
+use \fIref\fR \fB\fRto scan the tags files.
+This is slower than the built-in tag searching, but it allows \fIelvis\fR to access
+the more sophisticated tag lookup provided by \fIref\fR.
+Other than that, external tags should act exactly like internal tags.
+.SH OPTIONS
+.IP \fB-t\fR
+Output tag info, instead of the function header.
+.IP "\fB-f\fR \fIfile\fR"
+The tag might be a static function in \fIfile\fR.
+You can use several -f flags to have \fIref\fR consider static tags from more than one file.
+.IP "\fB-c\fR \fIclass\fR"
+The tag might be a member of class \fIclass\fR.
+You can use several -c flags to have \fIref\fR consider tags from more than one class.
+.SH FILES
+.IP \fBtags\fR
+List of function names and their locations, generated by \fIctags\fR.
+.IP \fBrefs\fR
+Function headers extracted from source files (optional).
+.SH ENVIRONMENT
+.IP \fBTAGPATH\fR
+List of directories to be searched.
+The elements in the list are separated by either
+semicolons (for MS-DOS, Atari TOS, and AmigaDos), or
+by colons (every other operating system).
+For each operating system, \fIref\fR has a built-in default which is probably
+adequate.
+.SH NOTES
+.PP
+You might want to generate a "tags" file the directory that contains the
+source code for standard C library on your system.
+If licensing restrictions prevent you from making the library source readable
+by everybody, then you can have \fIctags\fR generate a "refs" file,
+and make "refs" readable by everybody.
+.PP
+If your system doesn't come with the library source code, then perhaps you
+can produce something workable from the \fIlint\fR libraries.
+.SH "SEE ALSO"
+elvis(1), ctags(1)
+.SH AUTHOR
+.nf
+Steve Kirkendall
+kirkenda@cs.pdx.edu
+.fi
diff --git a/usr.bin/ref/ref.c b/usr.bin/ref/ref.c
new file mode 100644 (file)
index 0000000..0cc105f
--- /dev/null
@@ -0,0 +1,550 @@
+/* ref2.c */
+
+/* This is a totally rewritten version of ref.  This version looks for the
+ * desired function name in the "tags" file, and then reads the header out
+ * from the source file.  There is no longer any need for a "refs" file.
+ *
+ * Usage:      ref [-t] [-f file] [-c class] tag
+ * Options:    -t         output tag info, not the description
+ *             -f file    default filename for static functions
+ *             -c class   default class names for class functions
+ */
+#ifdef __STDC__
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+#include <stdio.h>
+#include "config.h"
+
+extern char    *cktagdir P_((char *, char *));
+extern int     getline P_((char *, int, FILE *));
+extern int     lookup P_((char *, char *));
+extern int     find P_((char *));
+extern void    usage P_((void));
+extern int     countcolons P_((char *));
+extern void    main P_((int, char **));
+
+
+/* This is the default path that is searched for tags */
+#if OSK
+# define DEFTAGPATH ".:/dd/defs:/dd/defs/sys:/dd/usr/src/lib:../lib:/dd/usr/lib"
+#else
+# if ANY_UNIX
+#  define DEFTAGPATH ".:/usr/include:/usr/include/sys:/usr/src/lib:../lib:/usr/local/lib"
+# else
+#  if MSDOS || TOS
+#   define DEFTAGPATH ".;C:\\include;C:\\include\\sys;C:\\lib;..\\lib"
+#   define SEP ';'
+#  else
+#   if AMIGA
+#    define DEFTAGPATH ".;Include:;Include:sys"
+#    define SEP ';'
+#   else /* any other OS */
+#    define DEFTAGPATH "."
+#   endif
+#  endif
+# endif
+#endif
+
+#ifndef SEP
+# define SEP ':'
+#endif
+
+
+/* These variables reflect the command-line options given by the user. */
+int    taginfo;        /* boolean: give only the tag info? (not header?) */
+char   *def_file;      /* default filename for static functions */
+char   *def_class;     /* default classname for class members */
+int    colons;         /* #colons in tag: 0=normal, 1=static, 2=member */
+
+/* This function checks for a tag in the "tags" file of given directory.
+ * If the tag is found, then it returns a pointer to a static buffer which
+ * contains the filename, a tab character, and a linespec for finding the
+ * the tag.  If the tag is not found in the "tags" file, or if the "tags"
+ * file cannot be opened or doesn't exist, then this function returns NULL.
+ */
+char *cktagdir(tag, dir)
+       char    *tag;   /* name of the tag to look for */
+       char    *dir;   /* name of the directory to check */
+{
+       char    buf[BLKSIZE];
+       static char found[BLKSIZE];
+       FILE    *tfile;
+       int     len;
+
+#if AMIGA
+       if (dir[strlen(dir) - 1] == COLON)
+           sprintf(buf, "%s%s", dir, TAGS);   /* no slash after colon. */
+       else
+#endif
+       /* construct the name of the "tags" file in this directory */
+       sprintf(buf, "%s%c%s", dir, SLASH, TAGS);
+
+       /* Try to open the tags file.  Return NULL if can't open */
+#if AMIGA
+       if (buf[0] == '.' && buf[1] == SLASH)
+           tfile = fopen(&buf[2], "r");
+       else
+#endif
+       tfile = fopen(buf, "r");
+       if (!tfile)
+       {
+               return (char *)0;
+       }
+
+       /* compute the length of the tagname once */
+       len = strlen(tag);
+
+       /* read lines until we get the one for this tag */
+       found[0] = '\0';
+       while (fgets(buf, sizeof buf, tfile))
+       {
+               /* is this the one we want? */
+               if (!strncmp(buf, tag, len) && buf[len] == '\t')
+               {
+                       /* we've found a match -- remember it */
+                       strcpy(found, buf);
+
+                       /* if there is no default file, or this match is in
+                        * the default file, then we've definitely found the
+                        * one we want.  Break out of the loop now.
+                        */
+                       if (!def_file || !strncmp(&buf[len + 1], def_file, strlen(def_file)))
+                       {
+                               break;
+                       }
+               }
+       }
+
+       /* we're through reading */
+       fclose(tfile);
+
+       /* if there's anything in found[], use it */
+       if (found[0])
+       {
+               return &found[len + 1];
+       }
+
+       /* else we didn't find it */
+       return (char *)0;
+}
+
+/* This function reads a single textline from a binary file.  It returns
+ * the number of bytes read, or 0 at EOF.
+ */
+int getline(buf, limit, fp)
+       char    *buf;   /* buffer to read into */
+       int     limit;  /* maximum characters to read */
+       FILE    *fp;    /* binary stream to read from */
+{
+       int     bytes;  /* number of bytes read so far */
+       int     ch;     /* single character from file */
+
+       for (bytes = 0, ch = 0; ch != '\n' && --limit > 0 && (ch = getc(fp)) != EOF; bytes++)
+       {
+#if MSDOS || TOS
+               /* since this is a binary file, we'll need to manually strip CR's */
+               if (ch == '\r')
+               {
+                       continue;
+               }
+#endif
+               *buf++ = ch;
+       }
+       *buf = '\0';
+
+       return bytes;
+}
+
+
+/* This function reads a source file, looking for a given tag.  If it finds
+ * the tag, then it displays it and returns TRUE.  Otherwise it returns FALSE.
+ * To display the tag, it attempts to output any introductory comment, the
+ * tag line itself, and any arguments.  Arguments are assumed to immediately
+ * follow the tag line, and start with whitespace.  Comments are assumed to
+ * start with lines that begin with "/*", "//", "(*", or "--", and end at the
+ * tag line or at a blank line.
+ */
+int lookup(dir, entry)
+       char    *dir;   /* name of the directory that contains the source */
+       char    *entry; /* source filename, <Tab>, linespec */
+{
+       char    *name;          /* basename of source file */
+       char    buf[BLKSIZE];   /* pathname of source file */
+       long    lnum;           /* desired line number */
+       long    thislnum;       /* current line number */
+       long    here;           /* seek position where current line began */
+       long    comment;        /* seek position of introductory comment, or -1L */
+       FILE    *sfile;         /* used for reading the source file */
+       int     len;            /* length of string */
+       int     noargs = 0;     /* boolean: don't show lines after tag line? */
+       char    *ptr;
+
+
+       /* construct the pathname of the source file */
+       name = entry;
+       strcpy(buf, dir);
+       ptr = buf + strlen(buf);
+#if AMIGA
+       if (ptr[-1] != COLON)
+#endif
+       *ptr++ = SLASH;
+       while (*entry != '\t')
+       {
+               *ptr++ = *entry++;
+       }
+       *entry++ = *ptr = '\0';
+
+       /* searching for string or number? */
+       if (*entry >= '0' && *entry <= '9')
+       {
+               /* given a specific line number */
+               lnum = atol(entry);
+               entry = (char *)0;
+               noargs = 1;
+       }
+       else
+       {
+               /* given a string -- strip off "/^" and "$/\n" */
+               entry += 2;
+               len = strlen(entry) - 2;
+               if (entry[len - 1] == '$')
+               {
+                       entry[len - 1] = '\n';
+               }
+               if (!strchr(entry, '('))
+               {
+                       noargs = 1;
+               }
+               lnum = 0L;
+       }
+
+       /* Open the file.  Note that we open the file in binary mode even
+        * though we know it is a text file, because ftell() and fseek()
+        * don't work on text files.
+        */
+#if MSDOS || TOS
+       sfile = fopen(buf, "rb");
+#else
+# if AMIGA
+       if (buf[0] == '.' && buf[1] == SLASH)
+           sfile = fopen(&buf[2], "r");
+       else
+# endif
+       sfile = fopen(buf, "r");
+#endif
+       if (!sfile)
+       {
+               /* can't open the real source file.  Try "refs" instead */
+#if AMIGA
+               if (dir[strlen(dir) - 1] == COLON)
+                       sprintf(buf, "%srefs", dir);
+               else
+#endif
+               sprintf(buf, "%s%crefs", dir, SLASH);
+#if MSDOS || TOS
+               sfile = fopen(buf, "rb");
+#else
+# if AMIGA
+               if (buf[0] == '.' && buf[1] == SLASH)
+                   sfile = fopen(&buf[2], "r");
+               else
+# endif
+               sfile = fopen(buf, "r");
+#endif
+               if (!sfile)
+               {
+                       /* failed! */
+                       return 0;
+               }
+               name = "refs";
+       }
+
+       /* search the file */
+       for (comment = -1L, thislnum = 0; here = ftell(sfile), thislnum++, getline(buf, BLKSIZE, sfile) > 0; )
+       {
+               /* Is this the start/end of a comment? */
+               if (comment == -1L)
+               {
+                       /* starting a comment? */
+                       if (buf[0] == '/' && buf[1] == '*'
+                        || buf[0] == '/' && buf[1] == '/'
+                        || buf[0] == '(' && buf[1] == '*'
+                        || buf[0] == '-' && buf[1] == '-')
+                       {
+                               comment = here;
+                       }
+               }
+               else
+               {
+                       /* ending a comment? */
+                       if (buf[0] == '\n' || buf[0] == '#')
+                       {
+                               comment = -1L;
+                       }
+               }
+
+               /* is this the tag line? */
+               if (lnum == thislnum || (entry && !strncmp(buf, entry, len)))
+               {
+                       /* display the filename & line number where found */
+                       if (strcmp(dir, "."))
+                               printf("%s%c%s, line %ld:\n", dir, SLASH, name, thislnum);
+                       else
+                               printf("%s, line %ld:\n", name, thislnum);
+
+                       /* if there were introductory comments, show them */
+                       if (comment != -1L)
+                       {
+                               fseek(sfile, comment, 0);
+                               while (comment != here)
+                               {
+                                       getline(buf, BLKSIZE, sfile);
+                                       fputs(buf, stdout);
+                                       comment = ftell(sfile);
+                               }
+
+                               /* re-fetch the tag line */
+                               fgets(buf, BLKSIZE, sfile);
+                       }
+
+                       /* show the tag line */
+                       fputs(buf, stdout);
+
+                       /* are we expected to show argument lines? */
+                       if (!noargs)
+                       {
+                               /* show any argument lines */
+                               while (getline(buf, BLKSIZE, sfile) > 0
+                                   && buf[0] != '#'
+                                   && strchr(buf, '{') == (char *)0)
+                               {
+                                       fputs(buf, stdout);
+                               }
+                       }
+
+                       /* Done!  Close the file, and return TRUE */
+                       fclose(sfile);
+                       return 1;
+               }
+       }
+
+       /* not found -- return FALSE */
+       return 0;
+}
+
+/* This function searches through the entire search path for a given tag.
+ * If it finds the tag, then it displays the info and returns TRUE;
+ * otherwise it returns FALSE.
+ */
+int find(tag)
+       char    *tag;   /* the tag to look up */
+{
+       char    *tagpath;
+       char    dir[80];
+       char    *ptr;
+       int     len;
+
+       if (colons == 1)
+       {
+               /* looking for static function -- only look in current dir */
+               tagpath = ".";
+       }
+       else
+       {
+               /* get the tagpath from the environment.  Default to DEFTAGPATH */
+               tagpath = getenv("TAGPATH");
+               if (!tagpath)
+               {
+                       tagpath = DEFTAGPATH;
+               }
+       }
+
+       /* for each entry in the path... */
+       while (*tagpath)
+       {
+               /* Copy the entry into the dir[] buffer */
+               for (ptr = dir; *tagpath && *tagpath != SEP; tagpath++)
+               {
+                       *ptr++ = *tagpath;
+               }
+               if (*tagpath == SEP)
+               {
+                       tagpath++;
+               }
+
+               /* if the entry ended with "/tags", then strip that off */
+               len = strlen(TAGS);
+               if (&dir[len] < ptr && ptr[-len - 1] == SLASH && !strncmp(&ptr[-len], TAGS, len))
+               {
+                       ptr -= len + 1;
+               }
+
+               /* if the entry is now an empty string, then assume "." */
+               if (ptr == dir)
+               {
+                       *ptr++ = '.';
+               }
+               *ptr = '\0';
+
+               /* look for the tag in this path.  If found, then display it
+                * and exit.
+                */
+               ptr = cktagdir(tag, dir);
+               if (ptr)
+               {
+                       /* just supposed to display tag info? */
+                       if (taginfo)
+                       {
+                               /* then do only that! */
+                               if (strcmp(dir, "."))
+                               {
+                                       printf("%s%c%s", dir, SLASH, ptr);
+                               }
+                               else
+                               {
+                                       /* avoid leading "./" if possible */
+                                       fputs(ptr, stdout);
+                               }
+                               return 1;
+                       }
+                       else
+                       {
+                               /* else look up the declaration of the thing */
+                               return lookup(dir, ptr);
+                       }
+               }
+       }
+
+       /* if we get here, then the tag wasn't found anywhere */
+       return 0;
+}
+
+void usage()
+{
+       fputs("usage: ref [-t] [-c class] [-f file] tag\n", stderr);
+       fputs("   -t        output tag info, instead of the function header\n", stderr);
+       fputs("   -f File   tag might be a static function in File\n", stderr);
+       fputs("   -c Class  tag might be a member of class Class\n", stderr);
+       exit(2);
+}
+
+
+int countcolons(str)
+       char    *str;
+{
+       while (*str != ':' && *str)
+       {
+               str++;
+       }
+       if (str[0] != ':')
+       {
+               return 0;
+       }
+       else if (str[1] != ':')
+       {
+               return 1;
+       }
+       return 2;
+}
+
+void main(argc, argv)
+       int     argc;
+       char    **argv;
+{
+       char    def_tag[100];   /* used to build tag name with default file/class */
+       int     i;
+
+       /* parse flags */
+       for (i = 1; i < argc && argv[i][0] == '-'; i++)
+       {
+               switch (argv[i][1])
+               {
+                 case 't':
+                       taginfo = 1;
+                       break;
+
+                 case 'f':
+                       if (argv[i][2])
+                       {
+                               def_file = &argv[i][2];
+                       }
+                       else if (++i < argc)
+                       {
+                               def_file = argv[i];
+                       }
+                       else
+                       {
+                               usage();
+                       }
+                       break;
+
+                 case 'c':
+                       if (argv[i][2])
+                       {
+                               def_class = &argv[i][2];
+                       }
+                       else if (++i < argc)
+                       {
+                               def_class = argv[i];
+                       }
+                       else
+                       {
+                               usage();
+                       }
+                       break;
+
+                 default:
+                       usage();
+               }
+       }
+
+       /* if no tag was given, complain */
+       if (i + 1 != argc)
+       {
+               usage();
+       }
+
+       /* does the tag have an explicit class or file? */
+       colons = countcolons(argv[i]);
+
+       /* if not, then maybe try some defaults */
+       if (colons == 0)
+       {
+               /* try a static function in the file first */
+               if (def_file)
+               {
+                       sprintf(def_tag, "%s:%s", def_file, argv[i]);
+                       colons = 1;
+                       if (find(def_tag))
+                       {
+                               exit(0);
+                       }
+               }
+
+               /* try a member function for a class */
+               if (def_class)
+               {
+                       sprintf(def_tag, "%s::%s", def_class, argv[i]);
+                       colons = 2;
+                       if (find(def_tag))
+                       {
+                               exit(0);
+                       }
+               }
+
+               /* oh, well */
+               colons = 0;
+       }
+
+       /* find the tag */
+       if (find(argv[i]))
+       {
+               exit(0);
+       }
+
+       /* Give up.  If doing tag lookup then exit(0), else exit(1) */
+       exit(!taginfo);
+       /*NOTREACHED*/
+}