/* * x r f * * Cross reference */ /*)BUILD $(PROGRAM) = xrf $(INCLUDE) = xrf.h $(FILES) = { xrf0 xrf1 xrf2 xrf3 xrfi xrfd } $(TKBOPTIONS) = { TASK = ...XRF STACK = 1500 } $(ODL) = { ; ; OVERLAY DESCRIPTION FOR XRF ; BOB DENNY 29-MAY-81 ; .ROOT XRF0-XRF2-XRFD-$(RXLIB)-*(X1,X2,X3) ; X1: .FCTR XRFI-X1L1-X1L2-X1L3 X1L1: .FCTR $(RXLIB):CONCAT:FWILD ; Note: CTIME requires several other library modules: X1L2: .FCTR $(RXLIB):TIME:CTIME:LOCALT:$$$$RTIME:$$$$UTIME X1L3: .FCTR $(RXLIB):CALLOC:MUL$$L:$$$$DIV2:EIS$$I ; X2: .FCTR XRF1-X2L X2L: .FCTR $(RXLIB):EIS$$I ; X3: .FCTR XRF3-X3L X3L: .FCTR $(RXLIB):CPYSTR:SPRINT .END } $(OVR) = { XRF0,XRF2,XRFD,$(SUPORT),$(RTLIB) XRFI,$(RTLIB)/O:1 XRF1,$(RTLIB)/O:1 XRF3,$(RTLIB)/O:1 } */ /* * *************** * * X R F 0 . C * * *************** * * This is the mainline program for the C cross referencer and lister. * It handles the following tasks: * * - Scans the command line * - Opens the appropriate files * - Calls some initialization functions * - Invokes line by line processing * - Prints the cross-reference index * - Finishes up processing as appropriate * * * Usage: * * xrf [-s] [-n] [-o out_file] in_files * * Flags: * -s Spool out_file to printer (RSX11M only (??)) * * -n Narrow (80 column) output, normal is 132 column. * * -o file Write output to the indicated file. If -o is not * specified, output will be to the first in_file * with the filetype set to ".x" * * Written By: * Bob Denny * Creative System Design Co. * 3452 E. Foothill Blvd. << Suite 601 >> * Pasadena, Ca. 91107 * (213) 355-6836 * * Experimental Version X1.0 5-May-80 * Split off initialization X1.1 7-May-80 * Prototype. Y1.2 9-May-80 * Release. V1.3 9-May-80 * Chg. for DECUS OTS. & Cmplr. V1.4 1-Jul-80 * Add support for RT-11 V1.5 3-Jul-80 * Conditionalized spool call V1.6 MM 9-Jul-80 * Added narrow, outfile, etc. V1.7 MM 10-Jul-80 * Added multiple infiles V1.8 MM 22-Jul-80 * Fspool now in the library V1.9 MM 2-Aug-80 * ***************************************************************************** */ #ifdef DOCUMENTATION title xrf Cross-Reference Lister for C Programs index Cross-Reference Lister for C Programs synopsis xrf [-options] [-o outfile] [file ...] description xrf prepares a cross-reference listing for C source programs. The following options are defined: .lm +8 .s.i -8;-n Narrow output (80 columns). The default is 132 columns. .s.i -8;-s Spool output to the default line printer. .s.i -8;-o file Write output to the named file. The default is the first input file encountered with a filetype (extension) of ".x". .s.lm -8 xrf accepts wild-card file input. diagnostics .lm +8 .s.i -8;Trying to get too much. .s Internal error, get help. .s.i -8;Not enough memory space. .s xrf ran out of main memory space. .s.i -8;Cannot open "file" .s.i -8;Cannot open listing file "file" .s.i -8;Usage ... .lm -8 author Robert B. Denny bugs #endif #include /* RSX-11M Std. I/O Def's */ #include "xrf.h" /* Global definitions */ int pageno; /* Current listing page no. */ main ( argc, argv ) char *argv[]; { /* Start the main prog. */ register int i; /* Arg count */ register int c; /* Switch character code */ register char *p; /* Fast arg pointer */ char *in_file; /* Source file name string */ char *out_file; /* Cref file name string */ int splflg; /* Spool file flag */ int nofiles; /* Flag "got a file" */ out_file = NULL; /* No output file yet */ splflg = 0; /* Default no spool */ for( i=1; i i'th arg token */ if( *p++ == '-' ) /* if it's a switch token */ { while( c = *p++ ) /* Each char is a switch */ switch(tolower(c)) /* Process 'em */ { case 'o': /* Output file */ argv[i] = 0; if (++i >= argc) useage(); out_file = argv[i]; argv[i] = 0; goto nextarg; case 'n': /* Narrow output */ rperline = (80 - 16)/RSIZE; /* Set references */ break; case 's': /* Spool listing */ splflg++; break; default : /* Bad switch, give help */ useage(); } argv[i] = 0; /* Drop this one */ } nextarg:; } /* Done with cmd string */ nofiles = 1; for( i = 1; i < argc; i++) { if ((in_file = argv[i]) == 0) continue; initinfile(in_file); while (nextinfile()) { if (nofiles) { initoutfile(out_file); nofiles = 0; } pageno = 0; /* Init. page number */ lineno = 0; /* Init. line number */ myfree(); /* Clear out storage */ root = NULL; /* No tree yet */ newpage(); /* Start first page */ while(fgets(scanbf, LWIDTH, src) != NULL) /* Get a line of source */ { ++lineno; /* Increment line number */ scanp = scanbf; /* Reset scan pointer */ while(getid()) /* scanp --> id or NULL */ root = search(idbuf, root); /* Process identifier */ /* Till done with this line */ lstline(); /* List this line */ } /* Till end of source file */ newpage(); /* Start index on new page */ prtree(root); /* Print the index */ } } if (nofiles) useage(); #ifdef rsx if(splflg) /* If we're to spool, */ fspool(lst); /* Cross your fingers. */ #endif exit(); /* Exit closing files */ } /***** END MAIN *****/ /* * * Listing control routines. Newpage also used by prtree. * */ newpage() /* Start new page */ { ++pageno; /* Increment page number */ linpg = 0; /* Reset line-in-page count */ fprintf(lst,"%s%d\n\n", pghead, pageno); /* Output page heading */ } lstline() /* Write listing line out */ { if(++linpg > MAXLIN) newpage(); fprintf(lst, "%4d:\t%s", lineno, scanbf); } /* * *************** * * X R F 2 . C * * *************** * * This file contains the identifier tree and reference list management * things. It uses a simple binary tree to store the identifiers for * lookup and later sorted printing. Each node in the id tree has a * pointer to the id string, left and right links and pointers to the * beginning and end of the linked list of reference nodes for that * id. Only the root of the tree is global, it is used by the sorted * index printing function 'prtree'. * * Version V1.3 9-May-80 * Version V1.4 10-Jul-80 MM Bummed code * Version V1.5 23-Jul-80 MM Redid storage code * Version V1.6 23-Dec-80 RBD Fixed bug in myalloc() */ #include #include "xrf.h" /* * Tree search and insertion. This function returns a pointer to * the new node if created. This may require some head scratching * (I had to!). Look particularly at the significance of the pointer * that is returned and how it is used by the recursive caller. If * you want more, read "Algorithms + Data Structures = Programs" * by Niklaus Wirth (Prentice Hall ISBN 0-13-022418-9) Chapter 4. * * It searches through the tree for a match to the supplied id (*kp). * If it is found, a new reference node is linked into the list for * that id. If no match is found, a new is node is inserted into the * tree and appropriately initialized, including a first reference * node. * */ struct idt *search(kp, link) /* Function "search" */ struct idt *link; /* Pointer to id node in tree */ char *kp; /* Pointer to key string */ { register struct idt *l; /* Fast tree pointer */ register struct ref *r; /* Fast list pointer */ register int cond; /* Condition from strcmp */ struct ref *newref(); /* Make reference function */ l = link; /* Copy link into register */ if(l == NULL) /* Not found, insert new id node */ { l = myalloc(sizeof(struct idt)); /* Make a new ident node */ l->keyp = myalloc(strlen(kp)+1); /* Get string space */ /* Fill in pointer to ... */ strcpy(l->keyp, kp); /* ... stashed key string */ l->left = l->right = NULL; /* No children */ l->first = l->last = newref(); /* Attach it to the id node */ } else if((cond = strcmp(kp, l->keyp)) == 0) /* Match if true */ { r = newref(); /* Get a new ref. node */ l->last->next = r; /* Hook new node into the list */ l->last = r; } else if(cond < 0) /* Look left */ l->left = search(kp, l->left); else /* Look right */ l->right = search(kp, l->right); return(l); } /* * Initialize a line number referece */ static struct ref * newref() { register struct ref *r; r = myalloc(sizeof(struct ref)); /* Make a reference node */ r->lno = lineno; /* Fill in line no. of ref. */ r->next = NULL; /* This is the end for now */ return(r); /* Return pointer to the node */ } /* * Storage allocation: * * my_free Free byte pointer in local buffer * my_left Number of bytes left in local buffer * my_link Link of free space buffers (from malloc()) * * If space can be gotten locally, get it. If not, request a "large" * chunk of memory and update my_free, my_left. * * My_link links chunks from monitor, myfree() returns them (called * at a new input file). */ struct my_alloc { struct my_alloc *my_next; }; static struct my_alloc *my_link = NULL; static char *my_free = NULL; static int my_left = 0; myalloc(amount) int amount; /* * Allocate amount bytes. */ { register char *new; register int needed; char *malloc(); needed = (amount + 1) & 0177776; /* Round up to a word bound */ if (needed > my_left) { /* * Gotta get storage */ if ((my_free = malloc(512)) == NULL) quit(); my_left = 512 - (sizeof (struct my_alloc)); ((struct my_alloc *)my_free)->my_next = my_link; my_link = (struct my_alloc *)my_free; my_free += sizeof (struct my_alloc); } my_left -= needed; if (my_left < 0) error("Trying to get too much: %d\n", needed); new = my_free; my_free += needed; return(new); } myfree() /* * Return all storage to the pool */ { register struct my_alloc *link; register struct my_alloc *next; next = my_link; while ((link = next) != NULL) { next = link->my_next; free(link); } my_link = NULL; my_free = NULL; my_left = 0; } /* * 'quit' handles dynamic memory deficit. (Sounds like U.S. Government!). * It issues a polite message to the user, marks the list file for delete * and exits to RSX. * */ quit() { char workbuf[40]; puts("Not enough memory space, sorry.\n"); fgetname(lst, workbuf); fclose(lst); delete(workbuf); exit(1); } /* * *************** * * X R F D . C * * *************** * * This file contains global externals and tables used by the C xrf'er. * * Version 1.5 3-Jul-80 Added RT-11 compatibility. * Version 1.6 10-Jul-80 MM Added new stuff * Version 1.7 22-Jul-80 MM For fwild() stuff */ #include #include "xrf.h" FILE *src; /* Source file pointer */ FILE *lst; /* List file pointer */ int lineno = 0; /* Current source line number */ int linpg = 0; /* Line-in-page count */ int rperline = (LWIDTH - 16) / RSIZE; char pghead[LWIDTH+1]; char scanbf[LWIDTH+1]; /* Source line scan buffer */ char *scanp; /* Scan pointer */ char idbuf[NCPS+1]; /* Ident. string buffer */ struct idt *root; /* ID tree root */ int time_of_day[2]; /* For unix flavored systems */ /* * *************** * * X R F I . C * * *************** * * Do initialization things. * NOTE: This is once-only code. It should be run in an overlay, * along with FOPEN. The root need only contain XRF0 and XRFD. * * Version V1.4 1-Jul-80 * Version V1.5 3-Jul-80 Conditionaled date/time stuff. * Version V1.6 MM 9-Jul-80 Changed date/time stuff * Version V1.7 MM 10-Jul-80 File name changes * Version V1.8 MM 21-Jul-80 Ctime() changed * Version V1.9 MM 22-Jul-80 Redid initialization * Version V1.11 RBD 26-Dec-80 Cleanup rt11 usage message * Version V1.12 MM 22-Dec-81 Rtime isn't in vax library * Version V1.13 MM 16-Feb-82 ctime fixup */ #include #include "xrf.h" extern int time_of_day[]; /* * Set up RSX file names, open them, and initialize the page header strings */ initinfile(in) char *in; { char wrkbuf[40]; /* Working string buffer */ name(wrkbuf, in, "c", 0); /* Make input file name */ if((src = fwild(wrkbuf, "r")) == NULL) error("Cannot open %s\n", wrkbuf); if (time_of_day[0] == 0) { time(&time_of_day); } } initoutfile(out) char *out; { char wrkbuf[40]; char firstin[40]; if (out != NULL) { name(wrkbuf, out, "x", 0); } else { fgetname(src, firstin); name(wrkbuf, firstin, "x", 1); /* Make list file name */ } if((lst = fopen(wrkbuf, "w")) == NULL) /* Try to open it */ error("Cannot open listing file %s\n", wrkbuf); } nextinfile() { register char *wp; register int c; char wrkbuf[40]; char *timetemp; if (fnext(src) == NULL) return(0); fgetname(src, wrkbuf); for (wp = wrkbuf; (c = *wp++) && c != ':';); if (c == 0) wp = wrkbuf; timetemp = ctime(&time_of_day); timetemp[24] = '\0'; /* Trash newline */ concat(pghead, "\fCross Reference of: ", wp, "\t", timetemp, "\tPage ", 0); return(1); } /* * Make a file name. * The mode argument is either 0 which means use type as default extension, * or 1, which means force the extension to be type. Argument file is the * 'raw' file name, sysnam is the final filename string. All arg's except * mode are pointers, as usual. * */ name(sysnam, file, type, mode) char *sysnam, *file, *type; int mode; { register char *p1, *p2; /* Fast pointers */ register int c; /* Fast char. buffer */ p1 = sysnam; /* p1 --> output string */ p2 = file; /* p2 --> input string */ while((c = *p2++) && c != '.') /* Copy up to '.' */ *p1++ = c; if( mode == 0 ) /* Default extension */ { if( c == '.' ) /* Extension explicitly given */ { do /* Copy '.' + any ext. */ { *p1++ = c; } while( c = *p2++ ); } else /* No ext. given, default */ { *p1++ = '.'; p2 = type; while( c = *p2++ ) *p1++ = c; } } else /* Force extension */ { *p1++ = '.'; p2 = type; while( c = *p2++ ) *p1++ = c; } *p1 = '\0'; /* Terminate with eos */ } /* ***************************************************************************** * * Give user help on program useage. */ useage() { extern int $$rsts; #ifdef rt11 if ($$rsts) { fprintf(stderr, "Usage: MCR XRF [-n] [-s] [-o outfile] infiles\n\n"); fprintf(stderr, " -s Spool output file\n"); } else { fprintf(stderr, "Usage (short): .RUN XR infile\n\n"); fprintf(stderr, "Usage (long): .RUN XR\n"); fprintf(stderr, " Argv: [-n] [-o outfile] infiles\n"); } #else fprintf(stderr, "Usage: xrf [-s] [-n] [-o outfile] infiles\n\n"); fprintf(stderr, " -s Spool output file\n"); #endif fprintf(stderr, " -n Narrow (80 column) output\n"); fprintf(stderr, " -o Output to \"outfile\"\n\n"); fprintf(stderr, "Input files may have wild-card names\n"); fprintf(stderr, "Default input filetype is \".c\"\n"); fprintf(stderr, "Default output filename is \".x\"\n"); error("?XRF-E-parameter error"); } /* * *************** * * X R F 1 . C * * *************** * * Lexical processing for C xref'er. Sifts through C source code * and pulls out the identifiers. Recognizes reserved words and * disregards them. Returns non-zero integer (true) if an id was * successfully moved into 'idbuf', zero (false) if end-of-line * was reached. I took care to test the C compiler's reaction to * Formfeeds with respect to line numbers, and made sure the line * numbers assigned by xrf act the same. * * Version V1.3 9-May-80 * Version V1.4 10-Jul-80 MM Allow $ in identifiers, bummed code * Version V1.5 21-Jul-80 MM Dropped newline from ctime() * Version V1.6 22-Jul-80 MM '.' is not an alpha char. */ #include #include "xrf.h" #define A 1 /* Alpha character */ #define C 2 /* Start of comment possibly */ #define L 3 /* Literal delimiter */ #define N 4 /* Numeric character */ #define Z 5 /* End of string */ #define NRW 28 /* # reserved words */ static int cmtflg = 0; /* Comment flag */ static char ctype[] = { /* Character action lookup */ Z,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,L,0,A,0,0,L,0,0,0,0,0,0,0,C, N,N,N,N,N,N,N,N,N,N,0,0,0,0,0,0, 0,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A, A,A,A,A,A,A,A,A,A,A,A,0,0,0,0,A, 0,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A, A,A,A,A,A,A,A,A,A,A,A,0,0,0,0,0 }; static char *reswrd[NRW] = { "auto", "break", "case", "char", "continue", "default", "do", "double", "else", "entry", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "while" }; /* * Scan off a non reserved identifier. Put it into 'idbuf'. * Returns +1 if successful, 0 if end-of-line was reached. * */ getid() /* Get an identifier into idbuf */ { register char *p ; /* Fast scan pointer */ register char *i ; /* Fast id buf pointer */ register int c; /* Dispatch code */ p = scanp; /* Init fast pointers */ i = idbuf; while ((c = *p) != 0) /* Scan till end of string */ { if(c == '\014') /* If formfeed, */ { c = *p = ' '; /* Convert to harmless blank */ newpage(); /* Force new page */ } if (cmtflg) /* If comment flag is on */ { while(*p && *p++ != '*'); /* Scan to '*' or end of string */ if(*p == '/') /* If we found end of comment */ cmtflg = 0; /* Turn off comment flag */ continue; /* and recycle */ } switch(ctype[c]) /* Dispatch on the type */ { case A: /* Start of an identifier */ i = idbuf; /* Reset the id buffer pointer */ do if( i < &idbuf[NCPS] ) /* Copy into idbuf */ *i++ = *p++; else /* Unless it gets full */ p++; while( (c=ctype[*p]) == A || c == N ); /* While alphanumeric */ while( i < &idbuf[NCPS] ) /* Pad idbuf with nulls */ *i++ = '\0'; if(nonres(idbuf)) /* If it's not a reserved word */ { scanp = p; /* Update the scan pointer */ return (1); /* Return with success */ } break; /* End of identifier processing */ case N: /* Scan by a number string */ do p++; while( (c=ctype[*p]) == N || c == A ); break; case L: /* Scan by a literal */ while (*++p != c && *p) { /* Scan to the matching trailing */ if (*p == '\\') p++; /* Quote, ignoring backslash quoted */ } /* Characters. If not at the end */ if (*p) p++; /* Of the line, skip to the char. */ break; /* Following the trailing quote */ case C: if(*++p == '*') /* Start a comment */ cmtflg = 1; /* by setting comment flag */ break; default: /* Otherwise just scan it off */ p++; break; } /* End of switch statement */ } /* If we exit here, end-of line. */ return(0); /* Return with failure indication */ } /* * Search for reserved word. Return true (1) if NOT reserved word. * Uses binary search. */ nonres( bufp ) /* Test if not reserved word */ char *bufp; { register int low ; /* Low pointer index */ register int mid ; /* Mid ... */ register int hi ; /* hi ... */ int cond; /* Condition from strcmp */ low = 0; hi = NRW-1; while (low <= hi) { mid = (low + hi) / 2; if((cond = strcmp(bufp, reswrd[mid])) < 0) hi = mid - 1; else if (cond > 0) low = mid + 1; else return(0); /* False, it IS reserved */ } return(1); /* True, it's NOT reserved */ } /* * *************** * * X R F 3 . C * * *************** * * Sorted cross reference listing routines. 'prtree' performs an * inorder traversal of the id tree, printing the references to' * each id while visiting that node. * * Version V1.3 9-May-80 * Version V1.4 10-Jul-80 MM Bummed code, added 80 col. support */ #include #include "xrf.h" /* * Inorder tree traversal. */ prtree(link) struct idt *link; { if (link != NULL) { prtree(link->left); /* Visit the left */ prtrefs(link); /* Print refs for this one */ prtree(link->right); /* Visit the right */ } } /* * List out a line of references. * Start new page if it gets full. */ lstrefs() { if(++linpg > MAXLIN) /* New page if necessary */ newpage(); fputss(scanbf, lst); /* Write out the string */ } /* * Print id and references. * Share scan buffer for printout. * Use newpag. * * The ref line number field width is hard-wired into the format statement. * Trouble is, #define's don't (and shouldn't!) substitute into strings, * like formats. * * Current values are 5 char field (RSIZE) and rperline ref's per line. */ prtrefs(link) struct idt *link; { register struct ref *r; /* Ref chain pointer */ register char *p; /* Fast scan buffer pointer */ register int j; /* Counts refs printed on a line */ char *cpystr(); /* cpystr returns char pointer */ r = link->first; /* r --> head of ref chain */ p = scanbf; /* p --> start of scan buffer */ j = 0; /*Init refs-per-line */ p = cpystr(p, link->keyp); /* Start with the id string */ while(p < &scanbf[NCPS]) /* Pad with blanks */ *p++ = ' '; *p++ = '\t'; /* Followed by a tab */ do { /* List Reference line numbers */ if(j >= rperline) /* If this line is full */ { *p = '\0'; /* Terminate with null */ lstrefs(); /* Write it out */ p = scanbf; /* Reset buffer pointer */ j = 0; /* Reset refs-on-line count */ *p++ = '\t'; /* Start cont. line w/ 2 tabs */ *p++ = '\t'; } j++; sprintf(p,"%5d", r->lno); /* Insert reference into buffer */ p += RSIZE; /* Update buffer pointer */ r = r->next; /* On down the chain */ } while(r != NULL); /* Until the end */ *p = '\0'; /* Terminate with null */ lstrefs(); /* Write the line out */ }