/* 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 [-a] [-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 */ #include #include "config.h" extern char *getenv(); extern char *fgets(); /* 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, , linespec */ { char buf[BLKSIZE]; /* pathname of sourcefile */ long lnum; /* 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 */ char *ptr; /* construct the pathname of the source file */ strcpy(buf, dir); ptr = buf + strlen(buf); #if AMIGA if (ptr[-1] != COLON) #endif *ptr++ = SLASH; while (*entry != '\t') { *ptr++ = *entry++; } *ptr = '\0'; entry++; /* searching for string or number? */ if (*entry >= '0' && *entry <= '9') { /* given a specific line number */ lnum = atol(entry); entry = (char *)0; } else { /* given a string -- strip off "/^" and "$/\n" */ entry += 2; len = strlen(entry) - 2; if (entry[len - 1] == '$') { entry[len - 1] = '\n'; } 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; } } /* search the file */ for (comment = -1L; here = ftell(sfile), 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 == 0L || (entry && !strncmp(buf, entry, len))) { /* 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); /* 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 [-a] [-t] [-c class] [-f file] tag\n", stderr); fputs(" -a function's args may be flush against left margin\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; } int 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); } exit(1); /*NOTREACHED*/ }