/* ** cref - C cross referencer VERSION 2. ** This program reads its standard input or one or more files ** and prints the file plus an alphabetic list of word references ** by line number. ** ** To run: ** cref [-q] [-lnnn] [-wnnn] [-hheading] [-tnnn] [-?] [file ...] ** ** Options: ** q - don't print normal input file listing. ** lnnn - set page length to n instead of the default 66. ** wnnn - set page width to n instead of the default 132. ** hccc - set page heading to 'ccc' rather than file names. ** tnnn - set tab spaces to n instead if the default 8. ** ? - display this list. ** ** Mike Edmonds - 5/81 **-------------------------------------------------------------------- ** ** VERSION 2 with comment bug fixed. May 1988 ** ** Amiga port by Joel Swank 9/87 ** ** Compiled under Manx: ** cc cref.c ** ln cref.o -lc ** ** Changes besides porting: ** ** + Deleted temporary file option because the Amiga sort command ** cannot sort files larger than memory. ** + Added -t option ** + Added -? option ** + Added Usage message and error msgs. ** + Rewrote case statments that overflowed compiler table ** + Fixed BUG that caused end of comment to be missed. star-star-slash ** was missed. */ #include #include #include #include #include #define MAXWORD 15 /* maximum chars in word */ #define MAXPAGLINES 9999 /* maximum posible lines in page */ #define MINPAGLINES 4 /* minimum posible lines in page */ #define MAXLINWIDTH 132 /* maximum posible chars in line */ #define MINLINWIDTH MAXWORD+12 /* minimum posible chars in line */ #define MAXTABSIZE 132 /* maximum posible chars in tab */ #define MINTABSIZE 1 /* minimum posible chars in tab */ #define igncase(c) (isupper(c)? tolower(c) : (c)) #undef FALSE #undef TRUE #define FALSE 0 #define TRUE 1 #ifdef DEBUG #define debug(a,b) fprintf(stderr,(a),(b)) #else #define debug(a,b) #endif /* * global structures */ #define LINLIST struct _linlist #define WRDLIST struct _wrdlist struct _linlist { /* line number list node */ long ln_no ; /* line number */ LINLIST *ln_next ; /* next element pointer (or NULL) */ } ; struct _wrdlist { /* word list node */ char *wd_wd ; /* pointer to word */ char *wd_lwd ; /* pointer to word (lower case) */ LINLIST wd_ln ; /* first element of line list */ WRDLIST *wd_low ; /* lower child */ WRDLIST *wd_hi ; /* higher child */ } ; /* * options */ char *Progname ; int Quiet = FALSE ; /* don't print input file listing? (-q) */ int Maxpaglines = 66 ; /* maximum lines in page (-l) */ int Maxlinwidth = 132 ; /* maximum chars in print line (-w) */ int Tabsize = 8 ; /* default length of tab */ /* * global variables */ char Crefhdr[MAXLINWIDTH+1] ;/* report header */ char *Filename ; /* name of current input file */ char Date[30] ; /* current or last file modify date */ long Hiline = 1L ; /* current (and max.) input file line number */ int files=0 ; /* count of source files */ WRDLIST *Wdtree = NULL ; /* ptr to root node of binary word list */ /* * C language reserved keywords (in pseudo random order) */ char *Ckeywords[] = { "char", "static", "break", "#define", "#if", "default", "#ifdef", "#ifndef", "register", "void", "if", "while", "#line", "union", "switch", "#else", "asm", "do", "#include", "#undef", "#endif", "long", "continue", "float", "short", "typedef", "for", "struct", "case", "else", "unsigned", "int", "extern", "auto", "goto", "entry", "return", "double", "sizeof", 0 } ; /* * main - Store C keywords. * Get program options and format heading lines. * Get words from input file (or files) and store in tree. * Retrieve and print in word sequence. */ main(argc, argv) int argc ; char *argv[] ; { char wordfilebuf[BUFSIZ] ; register FILE *filep ; char *getword(), *word ; struct stat stbuf ; long time() ; register cnt ; Progname = *argv ; /* get options */ getcmd(argc, argv) ; /* store C keywords */ for (cnt=0 ; Ckeywords[cnt] ; cnt++) storword(Ckeywords[cnt], 0L) ; listchr(-2); /* clear output line */ /* read and store files */ for (cnt=1 ; cnt < argc ; cnt++) if (*argv[cnt] != '-') { files++ ; Filename = argv[cnt] ; if ((filep = fopen(Filename, "r")) == NULL) fatal("can't open %s", Filename) ; stat(Filename, &stbuf) ; mkdate((long)stbuf.st_mtime) ; while (word = getword(filep)) storword(word, Hiline); fclose(filep) ; } if (!files) /* no files - read stdin */ { if (*Crefhdr) Filename = Crefhdr ; else Filename = "stdin" ; mkdate(time( (long *)0)) ; while (word = getword(stdin)) storword(word, Hiline) ; } /* print cross reference report */ cref(Wdtree) ; exit(0) ; } /* * getcmd - get arguments from command line & build page headings */ getcmd(argc, argv) register argc ; register char *argv[] ; { register cnt ; debug("GETCMD(%d", argc) ; debug(", %s)\n", argv[0]) ; *Crefhdr = '\0' ; /* get command options */ for (cnt=1; cnt < argc; cnt++) { if (*argv[cnt] == '-') { switch(argv[cnt][1]) { case 'q': Quiet = TRUE ; break ; case 'l': Maxpaglines = atoi(&argv[cnt][2]) ; if (Maxpaglines < MINPAGLINES || Maxpaglines > MAXPAGLINES) fatal("Bad -l value: %s", argv[cnt]) ; break ; case 'w': Maxlinwidth = atoi(&argv[cnt][2]) ; if (Maxlinwidth < MINLINWIDTH || Maxlinwidth > MAXLINWIDTH) fatal("Bad -w value: %s", argv[cnt]) ; break ; case 't': Tabsize = atoi(&argv[cnt][2]) ; if (Tabsize < MINTABSIZE || Tabsize > MAXTABSIZE) fatal("Bad -t value: %s", argv[cnt]) ; break ; case 'h': strncpy(Crefhdr, &argv[cnt][2], MAXLINWIDTH) ; Crefhdr[MAXLINWIDTH] = '\0' ; break ; case '?': /* help option */ usage(); printf(" Options:\n"); printf(" q - don't print normal input file listing\n"); printf(" lnnn - set page length to n instead of the default 66.\n"); printf(" wnnn - set page width to n instead of the default 132.\n"); printf(" hccc - set page heading to 'ccc' rather than file names\n"); printf(" tnnn - set tab spacing to n instead of the default 8\n"); printf(" ? - display this list.\n"); exit(0); default: usage(); exit(0); } } } /* insert file names in hdr */ if (!*Crefhdr) for (cnt=1; cnt < argc; cnt++) if (*argv[cnt] != '-') strjoin(Crefhdr, ' ', argv[cnt], MAXLINWIDTH) ; } usage() { printf("usage:cref [-q] [-lnnn] [-wnnn] [-hheading] [-tnnn] [-?] [file ...]\n"); } /* * getword - read, print and return next word from file */ char * getword(filep) FILE *filep ; { static char wordbuf[MAXWORD+1] ; register char *wp = wordbuf ; register maxw = sizeof(wordbuf) ; register chr ; int inword=0, lastchr=0 ; long slineno ; #define _listchr(chr) if (!Quiet) listchr(chr) #define _rtrnwrd(wp) \ { ungetc(chr, filep) ; \ *(wp) = '\0' ; \ return wordbuf ; \ } while ((chr = getc(filep)) != EOF) { /* normal char - add to current word */ if ((chr <= 'z' && chr >= 'a') || (chr <= 'Z' && chr >= 'A') || chr == '_' ) { if (maxw-- <= 1) _rtrnwrd(wp) ; *wp++ = chr ; inword++ ; _listchr(chr) ; } else switch (chr) { /* digit - can't be 1st char in word */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (inword) { if (maxw-- <= 1) _rtrnwrd(wp) ; *wp++ = chr ; } _listchr(chr) ; break ; /* '#' - must be 1st char in word */ case '#': if (inword) _rtrnwrd(wp) ; *wp++ = chr ; inword++ ; _listchr(chr) ; break ; /* newline - end current word */ case '\n': if (inword) _rtrnwrd(wp) ; _listchr(chr) ; Hiline++ ; break ; /* comments - print & bypass */ case '/': if (inword) _rtrnwrd(wp) ; _listchr(chr) ; slineno = Hiline ; if ((chr = getc(filep)) == '*') { _listchr(chr) ; while (chr != EOF) { chr = getc(filep) ; _listchr(chr) ; if (chr == '\n') Hiline++ ; else if (chr == '*') { restar: /* Fix for missing end of comment bug */ chr = getc(filep) ; /* star-star-slash was missed */ _listchr(chr) ; if (chr == '\n') Hiline++ ; else if (chr == '/') break ; ; if (chr == '*') goto restar; /* JHS 5/24/88 */ } } if (chr == EOF) fatal("unterminated comment at %ld in %s", slineno, Filename) ; } else ungetc(chr, filep) ; break ; /* words in quotes - print & bypass */ case '"': if (inword) _rtrnwrd(wp) ; _listchr(chr) ; slineno = Hiline ; if (lastchr != '\\') { do { if (chr == '\\' && lastchr == '\\') lastchr = '\0' ; else lastchr = chr ; if ((chr = getc(filep)) == EOF) fatal("unterminated quote at %ld in %s", slineno, Filename) ; _listchr(chr) ; if (chr == '\n') Hiline++ ; } while (chr != '"' || lastchr == '\\') ; } break ; /* letters in quotes - print & bypass */ case '\'': if (inword) _rtrnwrd(wp) ; _listchr(chr) ; if (isprint(chr = getc(filep))) { _listchr(chr) ; if (chr == '\\') { if (!isprint(chr = getc(filep))) goto toofar ; _listchr(chr) ; } if ((chr = getc(filep)) != '\'') goto toofar ; _listchr(chr) ; } else toofar: ungetc(chr, filep) ; break ; default: if (inword) _rtrnwrd(wp) ; _listchr(chr) ; break ; } lastchr = chr ; } if (inword) _rtrnwrd(wp) ; _listchr(EOF) ; return NULL ; } /* * listchr - list the input files one character at a time */ static Listpage = 0 ; static Listpline = MAXPAGLINES ; listchr(chr) register chr ; { static char linebuf[MAXLINWIDTH*2], *lineptr=linebuf ; static lastchr=0, linecnt=0 ; int remain; if (chr == -2) /* clear line buffer */ { setmem(linebuf,Maxlinwidth,' '); return; } if (chr == EOF) /* EOF - print final line */ { *lineptr = '\0' ; listline(linebuf) ; Listpage = 0 ; Listpline = MAXPAGLINES ; lineptr = linebuf ; linecnt = 0 ; return ; } if (lineptr == linebuf) /* new line - format line number */ { ltoc(linebuf, Hiline, 6) ; lineptr = linebuf+6 ; *lineptr++ = ' ' ; *lineptr++ = ' ' ; linecnt = 8 ; } #define _lineoflo(ctr, newctr) \ if ((ctr) >= Maxlinwidth) \ { *lineptr = '\0' ; \ listline(linebuf) ; \ lineptr = &linebuf[8] ; \ linecnt = (newctr) ; \ } switch (chr) { /* newline - print last line */ case '\n': if (lastchr != '\f') { *lineptr = '\0' ; listline(linebuf) ; } lineptr = linebuf ; linecnt = 0 ; break ; /* formfeed - print line and end page */ case '\f': if (linecnt != 8) { *lineptr = '\0' ; listline(linebuf) ; } Listpline = MAXPAGLINES ; lineptr = linebuf ; linecnt = 0 ; break ; /* tab - skip to next tab stop */ case '\t': linecnt += Tabsize ; remain = linecnt % Tabsize ; linecnt -= remain; _lineoflo(linecnt, 8) ; lineptr += Tabsize ; lineptr -= remain; break ; /* backspace - print, but don't count */ case '\b': *lineptr++ = chr ; break ; /* ctl-char - print as "^x" */ case 001: case 002: case 003: case 004: case 005: case 006: case 007: case 013: case 015: case 016: case 017: case 020: case 021: case 022: case 023: case 024: case 025: case 026: case 027: case 030: case 031: case 032: case 033: case 034: case 035: case 036: case 037: _lineoflo(linecnt+=2, 10) ; *lineptr++ = '^' ; *lineptr++ = ('A'-1) + chr ; break ; default: if (isprint(chr)) { _lineoflo(++linecnt, 9) ; *lineptr++ = chr ; } else /* non-ascii chars - print as "\nnn" */ { _lineoflo(linecnt+=4, 12) ; *lineptr++ = '\\' ; *lineptr++ = '0' + ((chr & 0300) >> 6) ; *lineptr++ = '0' + ((chr & 070) >> 3) ; *lineptr++ = '0' + (chr & 07) ; } break ; } lastchr = chr ; } /* print a completed line from the input file */ listline(line) register char *line ; { if (*line) { if (++Listpline >= (Maxpaglines-8)) { if (files >1 || Listpage) putchar('\f') ; printf("\n%s %s Page %d\n\n", Date, Filename, ++Listpage) ; Listpline = 0 ; } puts(line) ; listchr(-2); /* clear line buffer */ } } /* * storword - store word and line # in binary word tree or word file */ storword(word, lineno) register char *word ; long lineno ; { char lword[MAXWORD+1] ; register char *cp1, *cp2 ; WRDLIST *addword() ; /* convert word to lower case */ for (cp1=word, cp2=lword ; *cp2++ = igncase(*cp1) ; cp1++) ; /* store words and lineno */ Wdtree = addword(Wdtree, word, lword, lineno) ; } /* * addword - add word and line# to in-core word list */ WRDLIST * addword(wdp, word, lword, lineno) register WRDLIST *wdp ; char *word, *lword ; long lineno ; { char *malloc() ; int comp ; /* insert new word into list */ if (wdp == NULL) { register wordlen = strlen(word) + 1 ; wdp = (WRDLIST *)malloc((wordlen * 2) + sizeof(WRDLIST)) ; if (wdp == NULL) goto nomemory ; wdp->wd_wd = (char *)wdp + sizeof(WRDLIST) ; wdp->wd_lwd = wdp->wd_wd + wordlen ; strcpy(wdp->wd_wd, word) ; strcpy(wdp->wd_lwd, lword) ; wdp->wd_hi = wdp->wd_low = NULL ; wdp->wd_ln.ln_no = lineno ; wdp->wd_ln.ln_next = NULL ; } /* word matched in list? */ else if (((comp = strcmp(lword, wdp->wd_lwd)) == 0) && ((comp = strcmp(word, wdp->wd_wd)) == 0)) { register LINLIST *lnp, **lnpp ; if (wdp->wd_ln.ln_no) { /* add line# to linked list */ lnp = &wdp->wd_ln ; do { if (lineno == lnp->ln_no) return wdp ; lnpp = &lnp->ln_next ; } while ((lnp = *lnpp) != NULL) ; *lnpp = (LINLIST *)malloc(sizeof(LINLIST)) ; if ((lnp = *lnpp) == NULL) goto nomemory ; lnp->ln_no = lineno ; lnp->ln_next = NULL ; } } else if (comp < 0) /* search for word in children */ wdp->wd_low = addword(wdp->wd_low, word, lword, lineno) ; else wdp->wd_hi = addword(wdp->wd_hi, word, lword, lineno) ; return wdp ; /* not enough memory - convert to -b */ nomemory: fatal("not enough memory for in-core word list") ; } /* * cref - print cross reference report from internal word list */ #define MAXLNOS 2000 /* maximum line nos. for a word */ long Linenos[MAXLNOS] ; /* list of line numbers for a word */ cref(wdtree) register WRDLIST *wdtree ; { creftree(wdtree) ; } creftree(wdp) /* recursively print word tree nodes */ register WRDLIST *wdp ; { register LINLIST *lnp ; register nos ; if (wdp != NULL) { creftree(wdp->wd_low) ; /* print lower children */ nos = 0 ; if (Linenos[0] = wdp->wd_ln.ln_no) { lnp = &wdp->wd_ln ; while ((lnp = lnp->ln_next) != NULL) if (nos < (MAXLNOS-2)) Linenos[++nos] = lnp->ln_no ; printword(wdp->wd_wd, nos) ; } creftree(wdp->wd_hi) ; /* print higher children */ } } /* * printword - print a word and all its line number references */ printword(word, nos) char *word ; register nos ; { static firstime=TRUE, linecnt, maxlnos, lnosize ; register cnt ; if (firstime) { firstime = FALSE ; linecnt = Maxpaglines ; for (lnosize=1 ; Hiline ; lnosize++) Hiline /= 10L ; maxlnos = (Maxlinwidth - (MAXWORD+7)) / lnosize ; } if (linecnt >= (Maxpaglines - 8)) { printheads() ; linecnt = 5 ; } printf("%-15s%5d ", word, ++nos) ; Linenos[nos] = 0 ; for (nos=0, cnt=0 ; Linenos[nos] ; nos++) { if (++cnt > maxlnos) { cnt = 1 ; if (linecnt++ >= (Maxpaglines - 2)) { printheads() ; linecnt = 5 ; printf("%-15s(cont) ", word); } else printf("\n%22s", " ") ; } printf("%*ld", lnosize, Linenos[nos]) ; } putchar('\n') ; linecnt++ ; } /* * printheads - print page headings */ printheads() { static page=0 ; long time() ; if (!page) mkdate(time( (long *)0)) ; putchar('\f') ; printf("\nCREF %s %.*s Page %d\n\n", Date, (Maxlinwidth-36), Crefhdr, ++page) ; printf("word refs line numbers\n\n") ; } /* * ltoc - store ASCII equivalent of long value in given field */ ltoc(fld, lval, len) register char *fld ; register long lval ; register len ; { fld += len ; while (len-->0) if (lval) { *--fld = '0' + (lval%10L) ; lval /= 10L ; } else *--fld = ' ' ; } /* * mkdate - build time/date for use in heading lines */ mkdate(atime) long atime ; { long mtime ; char *cp, *ctime() ; debug("MKDATE(%ld)\n", atime) ; mtime = atime ; cp = ctime(&mtime) ; *(cp+24) = ' ' ; /* clear newline */ strcpy(cp+16, cp+19) ; /* shift over seconds */ strcpy(Date, cp+4) ; } /* * strjoin - join "str1" to "str2" (separated by "sep") * Truncate if necessary to "max" chars. */ strjoin(str1, sep, str2, max) register char *str1, *str2; char sep ; register max ; { if (*str2) { if (*str1) { while (*str1++) if (--max <= 0) goto oflo ; max--, str1-- ; *str1++ = sep ; } while (*str1++ = *str2++) if (--max <= 0) goto oflo ; } return ; oflo: *--str1 = '\0' ; return ; } /* * error - print standard error msg */ error(ptrn, data1, data2) register char *ptrn, *data1, *data2 ; { fprintf(stderr, "%s: ", Progname) ; fprintf(stderr, ptrn, data1, data2) ; putc('\n', stderr) ; } /* * fatal - print standard error msg and halt process */ fatal(ptrn, data1, data2) register char *ptrn, *data1, *data2 ; { error(ptrn, data1, data2) ; exit(1); }