/* bref - AmigaBASIC cross referencer */ char Version[6] = "V 1.01"; /* November 3, 1989 ** This program reads standard input OR one or more files ** and prints the file(s) with line numbers added plus an ** alphabetic list of word references by line number. ** ** To run: ** bref [-Q] [-Lnnn] [-Wnnn] [-Hccc] [-?] [-S] [-K] [-E] [file...] ** ** Where 'file' must be ASCII, not binary -- AmigaBasic default. ** To save a Basic program in ASCII, enter in Basic output window: ** SAVE "prog.name",A ** ** Options--first letter can be upper or lower case: ** -Q - don't print normal input file listing (Quiet). ** -Lnnn - set page Length to n instead of the default 66. ** -Wnnn - set page Width to n instead of the default 80. ** Maximum permitted page width is 132. ** -Hccc - set page Heading to 'ccc' rather than file names. ** -S - Suppress form feeds; use for screen output ** -K - show BASIC Keywords in crossref table ** -E - print the input file 12 chars/in (Elite). Default 10 cpi. ** -? - display this list. ** ** Compiled with Manx Aztec C: ** cc bref.c ** ln bref.o -lc ** **-------------------------------------------------------------------- ** Program bref is a modification of cref, the C cross referencer ** ** History -- cref and bref ** May 1981 Mike Edmonds wrote cref, a cross ref for C language ** Sept 1987 Joel Swank ported cref to the Amiga ** 1989 Dick Taylor modified cref to produce bref for BASIC ** ** Dick Taylor Found a bug? ** 99 Valley View Rd Want a program change? ** Glastonbury CT 06033 USA Editorial remarks? ** Tel (203) 633-0100 Your comments are welcome!!! */ #include #include #include #include #include #define MAXWORD 15 /* maximum chars in word */ #define MAXPAGLINES 999 /* 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 = 80 ; /* default chars in print line (-w) */ int Tabsize = 8 ; /* default length of tab */ int FormFeed = TRUE; /* Suppress form feeds? (-S) */ int ShowKeyWords = FALSE; /* Show BASIC keywords in table? (-K)*/ int Elite = FALSE; /* Print source code in elite? (-E) */ /* global variables*/ char Brefhdr[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 */ int SkipToEOL = FALSE; /* Skip to EOL for REM, DATA */ int SaveMLW; /* Save Max line width */ /* AmigaBASIC language reserved keywords */ /* Entered in binary search order. This minimizes recursion */ /* during store of the keywords in the tree, thereby conserving on */ /* Stack usage. If the keywords are entered in alphabetic order, it */ /* causes a system crash (exceed Stack) during keyword store in the*/ /* tree. There are 202 keywords in this list. */ char *Bkeywords[] = { /* Level 1 -- #101 */ "MOUSE", /* Level 2 -- #50 & #151 */ "EOF", "RESUME", /* Level 3 -- #25 75 126 177 */ "COLOR", "INT", "OPTION", "SUB", /* Level 4 */ "BREAK", "DECLARE", "FRE","LOF","OBJECT.PLANES","POKEW","SIN","UCASE$", /* Level 5 */ "AREAFILL", "CINT", "CVD", "DEFSTR", "EXIT", "IF", "LIBRARY", "MERGE", "OBJECT.AY","OBJECT.VY", "PEEK", "PTAB","SADD","STATIC","TIME$","WEND", /* Level 6 */ "AND", "ATN", "CHAIN", "CLNG", "COS", "CVS", "DEFINT", "EDIT", "ERL", "FILES", "GOSUB", "INPUT", "LEFT$", "LLIST", "LPRINT", "MKI$", "NEXT", "OBJECT.HIT", "OBJECT.START", "OCT$", "PAINT", "POINT","PRINT", "READ", "RND", "SCREEN", "SPACE$", "STOP", "TAB", "TRANSLATE$", "VARPTR", "WINDOW", /* Level 7 */ "ABS", "APPEND", "AS", "BASE", "CALL", "CHDIR","CIRCLE","CLS","COMMON", "CSNG", "CVI", "DATA", "DEF", "DEFLNG", "DELETE", "ELSEIF", "EQV", "ERR", "EXP", "FIX", "FUNCTION", "GOTO", "IMP", "INPUT$", "KILL", "LEN", "LINE", "LOC", "LOG", "LSET", "MID$", "MKS$", "NAME", "NOT", "OBJECT.CLIP", "OBJECT.OFF", "OBJECT.PRIORITY", "OBJECT.STOP", "OBJECT.X", "ON", "OR", "PALETTE", "PEEKL", "POKE", "POS", "PRINT#", "PUT", "RESET", "RETURN", "RSET", "SAVE", "SGN", "SLEEP", "SPC", "STEP", "STRIG", "SWAP", "TAN", "TIMER", "TRON", "USING", "WAIT", "WHILE", "WRITE#", /* Level 8 -- everything else */ "ALL", "AREA", "ASC", "BEEP", "CDBL", "CHR$", "CLEAR", "CLOSE", "COLLISION", "CONT", "CSRLIN", "CVL", "DATE$", "DEFDBL", "DEFSNG", "DIM","ELSE", "END", "ERASE", "ERROR", "FIELD", "FOR", "GET", "HEX$", "INKEY$", "INPUT$", "INSTR", "LBOUND", "LET", "LIST", "LOAD", "LOCATE", "LPOS", "MENU", "MKD$", "MKL$", "MOD", "NEW","OBJECT.AX", "OBJECT.CLOSE", "OBJECT.ON", "OBJECT.SHAPE", "OBJECT.VX", "OBJECT.Y", "OFF", "OPEN", "OUTPUT", "PATTERN", "PEEKW", "POKEL", "PRESET", "PSET", "RANDOMIZE", "REM", "RESTORE", "RIGHT$", "RUN","SAY", "SCROLL", "SHARED", "SOUND", "SQR", "STICK", "STR$", "STRING$", "SYSTEM", "THEN","TO", "TROFF", "UBOUND", "VAL", "WAVE", "WIDTH", "WRITE", "XOR", 0 } ; /* main - Store Basic keywords in tree. * Get program options and format heading lines. * Get words/line numbers from input file(s), store in tree. * Retrieve and print words in alphabetic order. For each * word, print line numbers where word appears. */ 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) ; /* If the BASIC keywords are bypassed for the output */ /* table, then store keywords in the tree with */ /* line count = 0. */ if (!ShowKeyWords) for (cnt=0 ; Bkeywords[cnt] ; cnt++) storword(Bkeywords[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); if (strcmp(word,"REM") == 0 || strcmp(word,"DATA") == 0) SkipToEOL = TRUE; } fclose(filep) ; } if (!files) /* no files - read stdin */ { if (*Brefhdr) Filename = Brefhdr ; else Filename = "stdin" ; mkdate(time( (long *)0)) ; while (word = getword(stdin)) storword(word, Hiline) ; } /* print cross reference report */ bref(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]) ; *Brefhdr = '\0' ; /* get command options */ for (cnt=1; cnt < argc; cnt++) { if (*argv[cnt] == '-') { switch(igncase(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 's': FormFeed = FALSE; /* Suppress form feeds */ break; case 'k': ShowKeyWords = TRUE; /* Show BASIC keywords */ break; case 'e': Elite = TRUE; printf ("%s", ""); /* Send elite code to printer*/ break; case 'h': strncpy(Brefhdr, &argv[cnt][2], MAXLINWIDTH) ; Brefhdr[MAXLINWIDTH] = '\0' ; break ; case '?': /* help option */ usage(); printf(" Options--1st letter either upper or lower case:\n\n"); printf(" -Q - don't print input file listing (Quiet)\n"); printf(" -Lnnn - set page Length to n, not default 66.\n"); printf(" -Wnnn - set page Width to n, not default 80.\n"); printf(" -Hccc - set page Heading 'ccc', not file names\n"); printf(" -S - Suppress form feeds; use--screen output\n"); printf(" -K - show BASIC Keywords in CrossRef table\n"); printf(" -E - print input file 12 chars/inch (Elite)\n"); printf(" -? - display this list.\n"); exit(0); default: usage(); exit(0); } } } if (Elite) { SaveMLW = Maxlinwidth; /* Save Max Line Width */ Maxlinwidth = (Maxlinwidth * 6) / 5; /* New Max Line Width*/ } /* insert file names in hdr */ if (!*Brefhdr) for (cnt=1; cnt < argc; cnt++) if (*argv[cnt] != '-') strjoin(Brefhdr, ' ', argv[cnt], MAXLINWIDTH) ; } usage() { printf("usage:\n\n"); printf("bref [-Q] [-Lnnn] [-Wnnn] [-Hheading] [-S] [-K] [-E] \ [-?] [file ...]\n\n"); } /* getword - read, print and return next word from file*/ char * getword(filep) FILE *filep ; { static int first_read = TRUE; 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) { if (first_read) {first_read = FALSE; if (chr == 0xf5) fatal ("File %s is binary, can't process--ASCII req.",Filename); } if (SkipToEOL) goto REM_DATA; /* REM, DATA--skip to end of line*/ /* normal char - add to current word */ if ((chr <= 'z' && chr >= 'a') || (chr <= 'Z' && chr >= 'A') ) { if (maxw-- <= 1) _rtrnwrd(wp) ; *wp++ = chr ; inword++ ; _listchr(chr) ; } else switch (chr) { /* These can't be 1st char in word -- */ /* digit, period, suffixes for variable type */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': case '%': case '&': case '!': case '#': case '$': if (inword) { if (maxw-- <= 1) _rtrnwrd(wp) ; *wp++ = chr ; } _listchr(chr) ; break ; /* newline - end current word */ case '\n': if (inword) _rtrnwrd(wp) ; _listchr(chr) ; Hiline++ ; break ; /* Apostrophe (') comment - print & bypass */ /* to end of the line. */ case '\'': if (inword) _rtrnwrd(wp) ; REM_DATA: /* Come here for REM & DATA keywords */ _listchr(chr) ; while ((chr = getc(filep)) != EOF) { _listchr(chr); if (chr == '\n') { Hiline++; SkipToEOL = FALSE; break; } } if (chr == EOF) ungetc(chr, filep); break ; /* words in quotes - print & bypass */ case '"': if (inword) _rtrnwrd(wp) ; _listchr(chr) ; while ((chr = getc(filep)) != EOF) { _listchr(chr); if (chr == '"') break; if (chr == '\n') /* Making assumption here that */ { Hiline++; /* end of line is implied end of*/ break; /* quote string. Apparently this*/ } /* is what AmigaBasic does. */ } if (chr == EOF) ungetc(chr,filep); break ; default: if (inword) _rtrnwrd(wp) ; _listchr(chr) ; break ; } /* End of switch -- process char's */ lastchr = chr ; } /* End of while -- read char's */ 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 ; static int remain; static int LNWid = 4; /* Line number width on listing */ /* Changed from 6 in CREF to 4 in BREF */ 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, LNWid) ; lineptr = linebuf + LNWid ; *lineptr++ = ' ' ; *lineptr++ = ' ' ; linecnt = LNWid + 2; } #define _lineoflo(ctr, newctr) \ if ((ctr) >= Maxlinwidth) \ { *lineptr = '\0' ; \ listline(linebuf) ; \ lineptr = &linebuf[LNWid + 2] ; \ 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 != LNWid + 2) { *lineptr = '\0' ; listline(linebuf) ; } Listpline = MAXPAGLINES ; /* This triggers form feed*/ /* on next entry--listline*/ lineptr = linebuf ; linecnt = 0 ; break ; /* tab - skip to next tab stop */ case '\t': linecnt += Tabsize ; remain = linecnt % Tabsize ; linecnt -= remain; _lineoflo(linecnt, LNWid + 2) ; 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, LNWid + 4) ; *lineptr++ = '^' ; *lineptr++ = ('A'-1) + chr ; break ; default: if (isprint(chr)) { _lineoflo(++linecnt, LNWid + 3) ; *lineptr++ = chr ; } else /* non-ascii chars - print as "\nnn" */ { _lineoflo(linecnt+=4, LNWid + 6) ; *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 (FormFeed) if (files >1 || Listpage) putchar('\f') ; printf("\nBREF %s %s %s Page %d\n\n", Version, 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") ; } /* bref - 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 */ bref(wdtree) register WRDLIST *wdtree ; { if (Elite) {printf ("%s",""); /* Turn off elite for printer */ Maxlinwidth = SaveMLW; /* Restore original Max line width*/ } breftree(wdtree) ; if (FormFeed) putchar ('\f'); /*Final form feed after print x-ref table*/ } breftree(wdp) /* recursively print word tree nodes */ register WRDLIST *wdp ; { register LINLIST *lnp ; register nos ; if (wdp != NULL) { breftree(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) ; } breftree(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 - 5)) { 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 - 4)) { 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)) ; if (FormFeed) putchar('\f') ; /* Form feed */ printf("\nBREF %s %s %.*s Page %d\n\n", Version, Date, (Maxlinwidth-36), Brefhdr, ++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 ; } /* fatal - print standard error msg and halt process*/ fatal(ptrn, data1, data2) register char *ptrn, *data1, *data2 ; { fprintf(stderr, "%s: ", Progname) ; fprintf(stderr, ptrn, data1, data2) ; putc('\n', stderr) ; exit(1); }