From harvard!uucp Mon Nov 18 19:18:37 1985 Received: from harvard.HARVARD.EDU by seismo.CSS.GOV with SMTP; Mon, 18 Nov 85 19:13:03 EST Received: by harvard.HARVARD.EDU; Mon, 18 Nov 85 19:13:30 EST From: harvard!uucp (Black Hole) Return-Path: Received: by panda.LOCAL on Mon, 18 Nov 85 18:37:06 est Date: Mon, 18 Nov 85 18:37:06 est Message-Id: <8511182337.AA22113@panda.LOCAL> To: talcott!seismo!rick Subject: cxref Status: R From: linus!gatech!arnold Date: 17 Jan 85 16:30:36-EST (Thu) Original-From: Arnold Robbins Subject: Cxref -- C cross referencer Here is the latest version of my 'cxref' program, ready for posting. It has been posted to net.sources several times before; This version has one or two small enhancements, and should port across Unix versions fairly easily. Arnold Robbins CSNET: arnold@gatech ARPA: arnold%gatech.csnet@csnet-relay.arpa UUCP: { akgua, allegra, hplabs, ihnp4, seismo, ut-sally }!gatech!arnold ------------------ tear here -------------------- #! /bin/sh echo 'extracting --- README' 1>&2 cat > README << 'End of README' README: This directory contains the files for 'cxref', a C language cross referencing program. The makefile will build the program. Use 'make install' to put a copy in the right places. Cxref is a driver, and goes in DESTDIR, which will be your local bin of choice. LIB is where the programs go that do the work. The makefile makes the directory /usr/local/lib/cxref (i.e. LIB) to hold the programs. The cxref driver is dependant on where LIB is. I have them set to use my BIN. BIN is taken from the environment, since I use the UNIX 4.0 make. SORT is the full pathname for the Unix sort program, sometimes /bin/sort, other times /usr/bin/sort. Edit the makefile to set everything up properly. System III and V users should edit the file 'basename.c' to change the call to 'rindex' to 'strrchr'. The flow of information between programs is shown below. I suggest that you run the programs separately, successively adding the next program in a particular pipeline, to see how the information is transformed. The outline below of the flow of control does not necessarily show all the arguments that each program requires; see the source code to be sure, when you run the programs by hand. With some work in the cscan.l and cxref.c files, this program could be easily made to cross reference other languages. Here is a list of the files, and what they contain: README --- this file. cxref.1 --- man page. makefile --- the makefile. cxref.c --- driver, calls all the other programs, does arg handling. old.cxref.c --- old version, used the shell to do the dirty work. (this file can probably be deleted.) constdefs.h --- header file, used by cscan.l and cxrfilt.c. cscan.l --- does the work of finding indentifiers and contsants. docxref.c --- main program and some other stuff to drive lex. cxrfilt.c --- filters int and floats to their own files. also puts ints and floats back together after sorting. fmtxref.c --- formats output for printing. SORT[1-3] --- shell files to save typing during testing and development. For development, the flow of programs is: docxref files | SORT1 | cxrfilt | fmtxref SORT2 < tempfile1 | cxrfilt -i | fmtxref SORT3 < tempfile2 | cxrfilt -f | fmtxref Arnold Robbins, Information and Computer Science, Georgia Tech gatech!arnold January 1985. Copyright (c) 1985 by Arnold Robbins. All rights reserved. End of README echo 'extracting --- SORT1' 1>&2 cat > SORT1 << 'End of SORT1' # SORT1 -- do the first sorting job for cxref. # # the purpose of this file is to save typing during testing and development. # # Arnold Robbins, Information and Computer Science, Georgia Tech # gatech!arnold # Copyright (c) 1984 by Arnold Robbins. # All rights reserved. # This program may not be sold, but may be distributed # provided this header is included. exec sort -u +0b -2 +2n End of SORT1 echo 'extracting --- SORT2' 1>&2 cat > SORT2 << 'End of SORT2' # SORT2 -- do the second sorting job for cxref. # # the purpose of this file is to save typing during testing and development. # # Arnold Robbins, Information and Computer Science, Georgia Tech # gatech!arnold # Copyright (c) 1984 by Arnold Robbins. # All rights reserved. # This program may not be sold, but may be distributed # provided this header is included. exec sort -u +0n -1 +1b -2 +2n End of SORT2 echo 'extracting --- SORT3' 1>&2 cat > SORT3 << 'End of SORT3' # SORT3 -- do the third sorting job for cxref. # # the purpose of this file is to save typing during testing and development. # # Arnold Robbins, Information and Computer Science, Georgia Tech # gatech!arnold # Copyright (c) 1984 by Arnold Robbins. # All rights reserved. # This program may not be sold, but may be distributed # provided this header is included. exec sort -u +0n +1n -2 +2b -3 +3n End of SORT3 echo 'extracting --- basename.c' 1>&2 cat > basename.c << 'End of basename.c' /* ** basename.c ** ** return the last portion of a path name. ** included by all the cxref component programs. ** ** Arnold Robbins, Information and Computer Science, Georgia Tech ** gatech!arnold ** Copyright (c) 1984 by Arnold Robbins. ** All rights reserved. ** This program may not be sold, but may be distributed ** provided this header is included. */ char *basename(str) /* return last portion of a path name */ char *str; { char *cp, *rindex(); /* change to strrchr() for USG systems */ if((cp = rindex(str, '/')) == NULL) return(str); else return(++cp); } End of basename.c echo 'extracting --- constdefs.h' 1>&2 cat > constdefs.h << 'End of constdefs.h' /* ** constdefs.h ** ** definitions of keyletters for different constants. ** arbitrary, as long as char < string < int < float. ** ** used by several of the cxref component programs. ** ** Arnold Robbins, Information and Computer Science, Georgia Tech ** gatech!arnold ** Copyright (c) 1984 by Arnold Robbins. ** All rights reserved. ** This program may not be sold, but may be distributed ** provided this header is included. ** */ #define CHAR 'a' #define STRING 'b' #define INT 'c' #define FLOAT 'd' End of constdefs.h echo 'extracting --- cscan.l' 1>&2 cat > cscan.l << 'End of cscan.l' /* ** cscan.l ** ** Does the major work of removing identifiers and constants ** from the input stream, for Cxref. Its output is then extensively ** postprocessed. ** ** Arnold Robbins, Information and Computer Science, Georgia Tech ** gatech!arnold ** Copyright (c) 1984 by Arnold Robbins. ** All rights reserved. ** This program may not be sold, but may be distributed ** provided this header is included. */ %{ extern int line_no; extern char *fname, *basename(); %} letter [A-Za-z_] digit [0-9] %% int | char | float | double | struct | union | long | short | unsigned | auto | extern | register | typedef | static | goto | return | sizeof | break | continue | if | else | for | do | while | switch | case | default | entry | enum | void | define | undef | include | ifdef | ifndef | defined | endif ; /* ignore C and cpp keywords */ "<".+">" ; /* forget about include-file names */ "\n" line_no++; "/*" { /* get rid of comments */ register char c, c1; loop: while((c = input()) != '*' && c != 0) if(c == '\n') line_no++; if(c == 0) { fprintf(stderr, "unexpected EOF in comment at line %d, file %s\n", line_no, basename(fname)); exit(1); } if((c1 = input()) != '/') { unput(c1); /* could be '*' before '/' */ goto loop; } } {letter}({letter}|{digit})* outid(); /* what we actually want */ '[^\\']' | '\\{digit}{1,3}' | '\\[\\bfrnlt']' outchar(); \" { /* collect quoted strings */ register char c; register int i; for(i = 1, c = input(); ; i++, c = input()) switch (c) { case '"': yytext[i] = c; yytext[++i] = '\0'; yyleng = i - 1; goto fini; case '\\': yytext[i] = '\\'; yytext[++i] = input(); if (yytext[i] == '\n') { line_no++; yytext[i] = 'N'; /* make visible */ } break; case 0: fprintf(stderr, "unexpected EOF inside string at line %d, file %s\n", line_no, basename(fname)); exit(1); break; default: yytext[i] = c; break; } fini: outstring(); } [+-]?{digit}+[lL]? | [+-]?0[Xx]({digit}|[a-fA-F])+[lL]? outint(); [+-]?{digit}*"."{digit}+([Ee][+-]?{digit}+)? | [+-]?{digit}+"."{digit}*([Ee][+-]?{digit}+)? | [+-]?{digit}+[Ee][+-]?{digit}+ outfloat(); . ; /* delete everything else */ %% yywrap() /* wrap up for lex, return 1 */ { return(1); } #include "constdefs.h" extern char *fname; extern char *basename(); outchar() { outtext(CHAR); } outstring() { outtext(STRING); } outint() { int i = strlen(yytext); /* handle long integer constants */ if (yytext[i-1] == 'l' || yytext[i-1] == 'L') yytext[i-1] = '\0'; outtext(INT); } outfloat() { outtext(FLOAT); } outtext(type) char type; { printf("~%c%s\t%s\t%d\n", type, yytext, basename(fname), line_no); } End of cscan.l echo 'extracting --- cxref.1' 1>&2 cat > cxref.1 << 'End of cxref.1' .TH CXREF 1 "Georgia Tech" .SH NAME cxref \- cross reference C source files .SH SYNOPSIS .B cxref [ .B \-FSCcfis ] [ .B \-w .IR width " ]" [files] .SH DESCRIPTION .PP .I Cxref reads the named C source files and produces on the standard output a cross reference of all the identifiers and constants in the files. Constants are integer constants (12, 0421, 0x1A), floating point constants (123.45, 0.2e-4), string constants ("this is a string\en"), and character constants ('a', '\e033'). Identifiers, character constants, and string constants are sorted lexicographically, i.e. according to the machine collating sequence (7-bit ASCII on the Vax or the Pyramid). Integer and floating point constants are sorted numerically. The trailing 'l' or 'L' on long integer constants will not show up in the output listing. .PP If no files are named, .I cxref reads the standard input. For multiple files, the argument "\-" (a single dash) indicates that the standard input should be read at that point. .PP If arguments are given, they must come before any file names. .PP .I Cxref recognizes the following arguments: .RS .TP .B \-F Fold case in comparison. By default, case is distinct in comparison of identifiers and string and character constants. .RI ( cxref simply passes the "\-F" option on to .IR sort (1) as "\-f".) .TP .B \-S Cross reference all files separately. The default action is to cross reference all named files together. .TP .B \-c Leave character constants out of the cross reference listing. .TP .B \-f Leave floating point constants out of the cross reference listing. .TP .B \-i Leave integer constants out of the cross reference listing. .TP .B \-s Leave string constants out of the cross reference listing. .TP .B \-C Leave .I all constants, character, string, integer, and floating point, out of the cross reference listing. By default, all types of constants are included in the cross reference. .TP .BI "\-w " width Make the output be .I width columns wide. The output width will never be less than 51 or more than 132 columns. .I Cxref silently adjusts incorrect settings to the nearest allowable setting. If no width is specified, the output will default to 80 columns wide. .RE .PP .IR Cxref " does " not include #include files, or expand macro definitions. Files named in #include lines can be listed on the command line if they should also be cross referenced. .PP If a quoted string has an escaped newline in it (see ``The C Programming Language'', page 181, or Section 2.5 of the C Reference Manual), it will show up inside the string in the output listing as \eN. This is to make it visible to the programmer, and to keep the various filters which .I Cxref uses to actually do the work from getting terribly confused. .PP .I Cxref is best run in the background, with its output redirected into a file or the line printer spooler .IR lpr (1), since it reads all the named files, using .IR sort (1) as an intermediate pass. The sorting can take time which the user can probably put to more productive use. .SH DIAGNOSTICS .PP Self explanatory. .SH BUGS .PP Systems running UNIX 4.0 and later already have a program named .IR cxref . Therefore, on those systems, this program should be renamed. .PP .I Cxref does not do any formatting on its output (other than to insure that it writes the proper number of columns), so it should probably be run piping its output into .IR pr (1). .PP Floating point constants are converted to a common format for sorting, therefore they may appear in the output in a format different from (but numerically equivalent to) their form in the original source code. .SH "SEE ALSO" .IR lex (1), .IR lpr (1), .IR pr (1), .IR sort (1) .SH FILES .TP /tmp/cxr.$$.* temporary files for integer and floating point contstants. .I Cxref removes these files when it is through. .SH AUTHOR .PP .nf Arnold Robbins School of Information and Computer Science Georgia Institute of Technology Atlanta, Geogia 30332 UUCP: gatech!arnold CSNET: arnold@gatech ARPANET: arnold%gatech.csnet@csnet-relay.arpa .fi Copyright (c) 1984 by Arnold Robbins. All rights reserved. This program may not be sold, but may be distributed provided this notice is included. End of cxref.1 echo 'extracting --- cxref.c' 1>&2 cat > cxref.c << 'End of cxref.c' /* ** cxref.c ** ** C driver for Cxref program. ** does argument handling, then builds the right ** shell commands for passing to the system() routine. ** ** Set up the argument vectors ourselves, the i/o with a pipe() ** call, and do all the forking and execing ourselves. ** ** Arnold Robbins, Information and Computer Science, Georgia Tech ** gatech!arnold ** Copyright (c) 1984 by Arnold Robbins ** All rights reserved ** This program may not be sold, but may be distributed ** provided this header is included. */ #include #include #include #define TRUE 1 #define FALSE 0 char name[BUFSIZ]; /* save command name */ int xargc; /* make argc and argv available globally */ char **xargv; int width = 0; /* output width */ int sepflag = FALSE; /* do each one separately */ int iflag = TRUE; /* print out ints */ int fflag = TRUE; /* print out floats */ int cflag = TRUE; /* print out chars */ int sflag = TRUE; /* print out strings */ int Fflag = FALSE; /* fold case in indentifiers */ int ancestor; /* id of this process, used by children */ char *filename(); /* turns "-" into "stdin" */ #define do_pipe(x) if (pipe(x) < 0) { fprintf(stderr, "x: pipe failed\n");\ fflush(stderr); exit (1); } main(argc, argv) int argc; char **argv; { int i; int catchem(); signal (SIGQUIT, catchem); signal (SIGINT, catchem); strcpy (name, filename(argv[0])); ancestor = getpid(); for(argv++, argc--; argc > 0; argv++, argc--) if (argv[0][0] != '-') break; else if(argv[0][1] == '\0') /* filename of "-" */ break; else for(i = 1; argv[0][i] != '\0'; i++) { switch(argv[0][i]) { case 'F': Fflag = TRUE; break; case 'S': sepflag = TRUE; break; case 'C': /* leave out all constants */ cflag = iflag = fflag = sflag = FALSE; break; case 'c': cflag = FALSE; break; case 'i': iflag = FALSE; break; case 'f': fflag = FALSE; break; case 's': sflag = FALSE; break; case 'w': if (isdigit(argv[0][i+1])) { width = 0; for(i++; isdigit(argv[0][i]); i++) width = width * 10 + argv[0][i] - '0'; i--; } else { width = atoi(argv[1]); argv++; argc--; } break; default: usage(); break; } } if (width != 0) if (width < 51) width = 80; else if (width > 132) width = 132; xargc = argc; xargv = argv; setargs(); /* set up various argv buffers */ runprogs(); /* set up and run pipelines */ exit (0); } /* argv vectors for various commands */ char *docxref[BUFSIZ] = { "docxref" }; /* allows BUFSIZ - 2 files */ char *cxrfilt[] = { "cxrfilt", NULL, NULL, NULL }; char *fmtxref[] = { "fmtxref", NULL, NULL, NULL }; char *sort1[] = { "sort", "-u", "+0b", "-2", "+2n", NULL, NULL }; char *sort2[] = { "sort", "-u", "+0n", "-1", "+1b", "-2", "+2n", NULL }; char *sort3[] = { "sort", "-u", "+0n", "+1n", "-2", "+2b", "-3", "+3n", NULL }; /* pipes to connect programs */ typedef int PIPE[2]; PIPE pipe1, pipe2, pipe3; setargs() /* initialize argv vectors */ { static char widthbuf[100]; static char pidbuf[100]; if (width != 0) { fmtxref[1] = "-w"; sprintf(widthbuf, "%d", width); fmtxref[2] = widthbuf; fmtxref[3] = NULL; } sprintf(pidbuf, "%d", getpid()); if (Fflag) sort1[5] = "-f"; /* fold case in identifiers */ if (! cflag && sflag) { cxrfilt[1] = "-c"; cxrfilt[2] = pidbuf; cxrfilt[3] = NULL; } else if (cflag && ! sflag) { cxrfilt[1] = "-s"; cxrfilt[2] = pidbuf; cxrfilt[3] = NULL; } else if (! cflag && ! sflag) { cxrfilt[1] = "-cs"; cxrfilt[2] = pidbuf; cxrfilt[3] = NULL; } else { cxrfilt[1] = pidbuf; cxrfilt[2] = NULL; } } /* flow of control is: docxref pipe1 sort1 pipe2 cxrfilt -userargs pipe3 fmtxref -userargs sort2 pipe1 cxrfilt -i pipe2 fmtxref -userargs sort3 pipe1 cxrfilt -f pipe2 fmtxref -userargs */ runprogs() /* run the programs, obeying user's options */ { int i; if (sepflag) { for (i = 0; i < xargc; i++) { printf("\tC Cross Reference Listing of %s\n\n", filename(xargv[i])); fflush(stdout); docxref[1] = xargv[i]; docxref[2] = NULL; idens(); if (iflag) integers(); if (fflag) floats(); fflush(stdout); if (!isatty(fileno(stdout))) putchar('\f'); } } else { if (xargc == 1) printf("\tC Cross Reference Listing of %s\n\n", filename(xargv[0])); else printf("\tC Cross Reference Listing\n\n"); fflush(stdout); for (i = 0; xargv[i] != NULL; i++) docxref[i+1] = xargv[i]; docxref[i+1] = NULL; idens(); if (iflag) integers(); if (fflag) floats(); fflush(stdout); if (! isatty(fileno(stdout))) putchar('\f'); } deltemps(); } deltemps() /* delete temp files used for ints and floats */ { char buf[BUFSIZ]; int i; for (i = 1; i <= 2; i++) { sprintf(buf, "/tmp/cxr.%d.%d", getpid(), i); unlink(buf); } } /* * now begins the nitty gritty work of forking and setting up pipes. */ int level; /* how many children down are we */ idens() /* cross reference identifiers */ { int status; int pid; int ischild; char buf[BUFSIZ]; level = 0; /* starting off as grandparent */ ischild = ((pid = fork()) == 0); retest: switch (level) { case 0: /* first fork */ if (ischild) { level++; do_pipe(pipe3); if (ischild = ((pid = fork()) == 0)) goto retest; close(pipe3[1]); /* doesn't need this */ close (0); dup(pipe3[0]); close(pipe3[0]); sprintf (buf, "%s/fmtxref", SRCDIR); execv (buf, fmtxref); fprintf (stderr, "couldn't exec '%s'\n", buf); exit (1); } else while (wait(&status) != pid) ; break; case 1: /* second fork */ level++; close (pipe3[0]); close(1); dup(pipe3[1]); close(pipe3[1]); /* set up i/o for next child */ do_pipe(pipe2); if (ischild = ((pid = fork()) == 0)) goto retest; close (pipe2[1]); close (0); dup(pipe2[0]); close (pipe2[0]); sprintf (buf, "%s/cxrfilt", SRCDIR); execv (buf, cxrfilt); fprintf (stderr, "couldn't exec '%s'\n", buf); exit (1); break; case 2: level++; close (pipe2[0]); close(1); dup(pipe2[1]); close(pipe2[1]); /* now writes to parent */ /* set up to read from next child */ do_pipe(pipe1); if (ischild = ((pid = fork()) == 0)) goto retest; close (pipe1[1]); close (0); dup(pipe1[0]); close (pipe1[0]); execv (SORT, sort1); fprintf (stderr, "couldn't exec '%s'\n", SORT); exit (1); break; case 3: level++; close (pipe1[0]); close(1); dup(pipe1[1]); close(pipe1[1]); /* now writes to parent */ sprintf(buf, "%s/docxref", SRCDIR); execv (buf, docxref); fprintf (stderr, "couldn't exec '%s'\n", buf); exit (1); break; default: fprintf(stderr, "in cxref (idens): can't happen\n"); fflush(stderr); break; } } #include #include integers() { int status; int pid; int ischild; char buf[BUFSIZ]; struct stat fbuf; sprintf(buf, "/tmp/cxr.%d.1", ancestor); if (stat(buf, &fbuf) >= 0 && fbuf.st_size > 0) ; /* file is not empty */ else return; level = 0; /* starting off as grandparent */ ischild = ((pid = fork()) == 0); retest: switch (level) { case 0: /* first fork */ if (ischild) { level++; do_pipe(pipe2); if (ischild = ((pid = fork()) == 0)) goto retest; close(pipe2[1]); /* doesn't need this */ close (0); dup(pipe2[0]); close(pipe2[0]); sprintf (buf, "%s/fmtxref", SRCDIR); execv (buf, fmtxref); fprintf (stderr, "couldn't exec '%s'\n", buf); exit (1); } else while (wait(&status) != pid) ; break; case 1: /* second fork */ level++; close (pipe2[0]); close(1); dup(pipe2[1]); close(pipe2[1]); /* set up i/o for next child */ do_pipe(pipe1); if (ischild = ((pid = fork()) == 0)) goto retest; close (pipe1[1]); close (0); dup(pipe1[0]); close (pipe1[0]); cxrfilt[1] = "-i"; cxrfilt[2] = NULL; sprintf (buf, "%s/cxrfilt", SRCDIR); execv (buf, cxrfilt); fprintf (stderr, "couldn't exec '%s'\n", buf); exit (1); break; case 2: level++; close (pipe1[0]); close(1); dup(pipe1[1]); close(pipe1[1]); /* now writes to parent */ /* read from tempfile */ close (0); sprintf (buf, "/tmp/cxr.%d.1", ancestor); open (buf, 0); /* will be fd 0 */ execv (SORT, sort2); fprintf (stderr, "couldn't exec '%s'\n", SORT); exit (1); break; default: fprintf(stderr, "in cxref(integers): can't happen\n"); fflush(stderr); break; } } floats() { int status; int pid; int ischild; char buf[BUFSIZ]; struct stat fbuf; sprintf(buf, "/tmp/cxr.%d.2", ancestor); if (stat(buf, &fbuf) >= 0 && fbuf.st_size > 0) ; /* file is not empty */ else return; level = 0; /* starting off as grandparent */ ischild = ((pid = fork()) == 0); retest: switch (level) { case 0: /* first fork */ if (ischild) { level++; do_pipe(pipe2); if (ischild = ((pid = fork()) == 0)) goto retest; close(pipe2[1]); /* doesn't need this */ close (0); dup(pipe2[0]); close(pipe2[0]); sprintf (buf, "%s/fmtxref", SRCDIR); execv (buf, fmtxref); fprintf (stderr, "couldn't exec '%s'\n", buf); exit (1); } else while (wait(&status) != pid) ; break; case 1: /* second fork */ level++; close (pipe2[0]); close(1); dup(pipe2[1]); close(pipe2[1]); /* set up i/o for next child */ do_pipe(pipe1); if (ischild = ((pid = fork()) == 0)) goto retest; close (pipe1[1]); close (0); dup(pipe1[0]); close (pipe1[0]); cxrfilt[1] = "-f"; cxrfilt[2] = NULL; sprintf (buf, "%s/cxrfilt", SRCDIR); execv (buf, cxrfilt); fprintf (stderr, "couldn't exec '%s'\n", buf); exit (1); break; case 2: level++; close (pipe1[0]); close(1); dup(pipe1[1]); close(pipe1[1]); /* now writes to parent */ /* read from tempfile */ close (0); sprintf (buf, "/tmp/cxr.%d.2", ancestor); open (buf, 0); /* will be fd 0 */ execv (SORT, sort3); fprintf (stderr, "couldn't exec '%s'\n", SORT); exit (1); break; default: fprintf(stderr, "in cxref(floats): can't happen\n"); fflush(stderr); break; } } usage() { fprintf(stderr, "usage: %s [-SCcsif] [-w width] [files]\n", name); fflush(stderr); exit (1); } char *filename(fname) char *fname; { char *cp, *basename(); cp = basename(fname); return ( strcmp(cp, "-") == 0 ? "stdin" : cp); } catchem() /* simple signal catcher */ { signal(SIGQUIT, SIG_IGN); signal(SIGINT, SIG_IGN); deltemps(); exit (0); } #include "basename.c" End of cxref.c echo 'extracting --- cxrfilt.c' 1>&2 cat > cxrfilt.c << 'End of cxrfilt.c' /* ** cxrfilt.c ** ** if called with no flags, or with the -c or -s flags, it will ** separate out integer and floating point constants into ** their own files, pass char and string constants on through. ** input: sorted output of docxref, contains identifiers and constants. ** output: identifiers, char and string constants, depending on flags. ** output goes to fmtxref. floats and ints to separate files for sorting. ** ** if called with -i or -f option, behavior is to put sorted ints or floats ** back into their original formats, and then pass the output to fmtxref. ** ** originally, there was a separate program to do float and int, but these two ** have been merged to reduce the total number of programs needed for cxref. ** ** Arnold Robbins, Information and Computer Science, Georgia Tech ** gatech!arnold ** Copyright (c) 1984 by Arnold Robbins ** All rights reserved ** This program may not be sold, but may be distributed ** provided this header is included. */ #include #include "constdefs.h" #define MAXFILE 120 #define MAXLINE 120 FILE *fp1, *fp2; int cflag = 0; int sflag = 0; char *name; char *basename(); main(argc, argv) int argc; char **argv; { char buf[BUFSIZ]; char file1[MAXFILE], file2[MAXFILE]; int i; name = basename(argv[0]); if (argc <= 1) usage(); if(argv[1][0] == '-') { for (i = 1; argv[1][i] != '\0'; i++) switch (argv[1][i]) { case 'c': cflag = 1; break; case 's': sflag = 1; break; case 'i': intfilter(); exit(0); break; case 'f': floatfilter(); exit(0); break; default: /* bad option given */ usage(); break; } /* if it gets to here, we were called only w/-c or -s */ if (argc == 2) usage(); argv++; } /* code for splitting constants off into separate files */ sprintf(file1, "/tmp/cxr.%d.1", atoi(argv[1])); sprintf(file2, "/tmp/cxr.%d.2", atoi(argv[1])); if ((fp1 = fopen(file1, "w")) == NULL) { fprintf(stderr,"%s: couldn't create tempfile 1\n"); exit (2); } if ((fp2 = fopen(file2, "w")) == NULL) { fprintf(stderr,"%s: couldn't create tempfile 2\n"); exit (3); } while (gets(buf) != NULL) { if (buf[0] != '~') printf("%s\n", buf); else switch (buf[1]) { case CHAR: if (! cflag) printf("%s\n", &buf[2]); break; case STRING: if (! sflag) printf("%s\n", &buf[2]); break; case INT: outint(buf); break; case FLOAT: outfloat(buf); break; default: fprintf(stderr,"%s: bad input line '%s'\n", name, buf); exit (4); } } fclose(fp1); fclose(fp2); exit(0); } #define OCTAL 1 #define HEX 2 #define DEC 3 outint(buf) char *buf; { char file[MAXLINE], line[MAXLINE]; int val; int type = 0; buf += 2; /* skip leading ~INT */ file[0] = line[0] = '\0'; if (buf[0] == '0') /* octal or hex */ { if (buf[1] == 'x' || buf[1] == 'X') /* hex */ { type = HEX; buf += 2; /* skip leading 0x */ sscanf(buf, "%x %s %s", &val, file, line); } else { type = OCTAL; sscanf(buf, "%o %s %s", &val, file, line); } } else { type = DEC; sscanf(buf, "%d %s %s", &val, file, line); /* decimal */ } /* * strategy is to convert to decimal for numeric sorting, * then have output filter convert back to right base. * * type is used to tell intfilter() what to turn it back into. */ fprintf(fp1, "%d\t%s\t%s\t%d\n", val, file, line, type); } outfloat(buf) char *buf; { char file[MAXLINE], line[MAXLINE]; char mantissa[MAXLINE], exponent[MAXLINE]; char strval[MAXLINE]; /* character representation of float */ char controlstr[MAXLINE]; double val; int i, j; buf += 2; /* skip ~FLOAT */ mantissa[0] = exponent[0] = file[0] = line[0] = '\0'; sscanf(buf, "%lf %s %s", &val, file, line); for (i = 0; buf[i] != '\t'; i++) if (buf[i] == '.') break; for (j = i + 1; buf[j] != 'E' && buf[j] != 'e' && buf[j] != '\t'; j++) ; j -= i - 1; /* j is now num digits to right decimal point. */ if (j < 6) j = 6; /* default */ sprintf(controlstr, "%%1.%dg", j); /* make control string */ sprintf(strval, controlstr, val); /* make character string */ /* * strategy is a follows: * 1) convert all floats to a common printed format (%g) * 2) split up mantissa and exponent into separate parts for sorting * 3) put them back together later when called w/-f option. */ for(i = j = 0; strval[j] != 'e' && strval[j] != 'E' && strval[j] != '\0'; i++, j++) mantissa[i] = strval[j]; mantissa[i] = '\0'; if (strval[j] == 'e' || strval[j] == 'E') { j++; for(i = 0; strval[j] != '\0'; i++, j++) exponent[i] = strval[j]; exponent[i] = '\0'; } else exponent[0] = '\0'; fprintf(fp2, "%s\t%s\t%s\t%s\n", mantissa, exponent[0] != '\0' ? exponent : "0", file, line); } usage() { fprintf(stderr, "usage: %s [-csfi] pid\n", name); exit (1); } intfilter() /* put sorted ints back into their original bases */ { char buf[BUFSIZ]; char file[MAXLINE], number[MAXLINE]; int val; int type; while (gets(buf) != NULL) { sscanf(buf, "%d %s %s %d", &val, file, number, &type); switch (type) { case OCTAL: if (val == 0) /* don't print 00 */ printf("0\t%s\t%s\n", file, number); else printf("0%o\t%s\t%s\n", val, file, number); /* supply leading 0 */ break; case DEC: printf("%d\t%s\t%s\n", val, file, number); break; case HEX: printf("0x%x\t%s\t%s\n", val, file, number); break; default: fprintf(stderr,"%s: bad input line '%s'\n", name, buf); exit (4); } } } floatfilter() /* put sorted floats back together */ { char buf[BUFSIZ]; char file[MAXLINE], number[MAXLINE]; char mantissa[MAXLINE], exponent[MAXLINE]; while (gets(buf) != NULL) { sscanf(buf, "%s %s %s %s", mantissa, exponent, file, number); if (strcmp(exponent, "0") == 0) printf("%s", mantissa); else printf("%sE%s", mantissa, exponent); printf("\t%s\t%s\n", file, number); } } #include "basename.c" End of cxrfilt.c echo 'extracting --- docxref.c' 1>&2 cat > docxref.c << 'End of docxref.c' /* ** docxref.c ** ** Driver for lex based scanner. Arranges for stdin to be each named ** file in turn, so that yylex() never has to know about files. ** Some of this code is not pretty, but it's not too bad. ** ** Arnold Robbins, Information and Computer Science, Georgia Tech ** gatech!arnold ** Copyright (c) 1984 by Arnold Robbins. ** All rights reserved. ** This program may not be sold, but may be distributed ** provided this header is included. */ #include #include #define TRUE 1 #define FALSE 0 extern char yytext[]; extern int yyleng; int line_no = 1; /* current line number */ char *fname = NULL; /* current file name */ char *basename(); /* strips leading path of a file name */ main(argc,argv) int argc; char **argv; { FILE saved_in, *fp; char *name; int more_input = FALSE; /* more files to process */ int read_stdin = FALSE; name = basename(argv[0]); /* save command name */ fname = "stdin"; /* assume stdin */ if(argc == 1) { yylex(); exit(0); } if(argv[1][0] == '-' && argv[1][1] != '\0') usage(argv[0]); /* will exit */ saved_in = *stdin; /* save stdin in case "-" is found in middle of command line */ for(--argc, argv++; argc > 0; --argc, argv++) { if(fileno(stdin) != fileno((&saved_in)) || read_stdin) fclose(stdin); /* free unix file descriptors */ if(strcmp(*argv, "-") == 0) { *stdin = saved_in; fname = "stdin"; read_stdin = TRUE; more_input = (argc - 1 > 0); } else if((fp = fopen(*argv,"r")) == NULL) { fprintf(stderr,"%s: can't open %s\n", name, *argv); continue; } else { *stdin = *fp; /* do it this way so that yylex() */ /* never knows about files etc. */ more_input = (argc - 1 > 0); fname = *argv; } yylex(); /* do the work */ if(more_input) line_no = 1; } } outid() { char *basename(); printf("%s\t%s\t%d\n", yytext, basename(fname), line_no); } usage(name) char *name; { fprintf(stderr,"usage: %s [files]\n", name); exit(1); } #include "basename.c" End of docxref.c echo 'extracting --- fmtxref.c' 1>&2 cat > fmtxref.c << 'End of fmtxref.c' /* ** fmtxref.c ** ** format the output of the C cross referencer. ** this program relies on the fact that its input ** is sorted and uniq-ed. ** ** Arnold Robbins, Information and Computer Science, Georgia Tech ** gatech!arnold ** Copyright (c) 1984 by Arnold Robbins. ** All rights reserved. ** This program may not be sold, but may be distributed ** provided this header is included. */ #include #include #define TRUE 1 #define FALSE 0 #define MAXID 121 /* maximum lengths of identifiers, file names, etc. */ #define MAXFILE 15 #define MAXLINE 121 #define MAXNUM 7 #define ID 1 /* return codes to indicate what is new on input line */ #define NEWFILE 2 #define LINE 3 #define ERROR 4 #define WIDTH 80 /* default line output width */ int width = WIDTH; char prev_id[MAXID] = ""; char prev_file[MAXFILE] = ""; char id[MAXID] = ""; char file[MAXFILE] = ""; char line[MAXNUM] = ""; char *name; char *basename(); /* strips leading path name */ main(argc, argv) int argc; char **argv; { char inline[BUFSIZ]; char *gets(); int val; name = basename(argv[0]); /* * since this program is ONLY called by the cxref driver, * we know that it will be called "fmtxref -w width" * so we don't have to do complicated argument parsing. * we also know that cxref makes sure we get a valid width. */ if (argc > 1) if (argc == 3) if (strcmp(argv[1], "-w") == 0) width = atoi(argv[2]); else usage(); else usage(); /* else use default width */ if(gets(inline) == NULL) { fprintf(stderr, "%s: standard input is empty.\n", name); exit(1); } if((val = breakup(inline)) == ERROR) { fprintf(stderr, "%s: malformed input '%s'\n", name, inline); exit(2); } output(val); /* does proper formatting */ while(gets(inline) != NULL && val != ERROR) { val = breakup(inline); output(val); } if(val == ERROR) { fprintf(stderr, "%s: malformed input '%s'\n", name, inline); exit(2); } putchar('\n'); } breakup(text) char *text; { int retval; int i, j; if(text[0] == '"' || text[0] == '\'') { /* quoted stuff, break the line up by hand */ i = 0; id[i++] = text[0]; for(j = 1; text[j] != text[0]; i++, j++) { id[i] = text[j]; if(id[i] == '\\') id[++i] = text[++j]; /* e.g. \" */ } id[i++] = text[0]; id[i] = '\0'; j++; /* skip close quote */ while(isspace(text[j])) j++; for(i = 0; !isspace(text[j]); i++, j++) file[i] = text[j]; file[i] = '\0'; while(isspace(text[j])) j++; for(i = 0; !isspace(text[j]) && text[j] != '\0'; i++, j++) line[i] = text[j]; line[i] = '\0'; } else { if(sscanf(text, "%s %s %s", id, file, line) != 3) return(ERROR); } /* now decide about return code for formatting */ if(strcmp(prev_id, id) != 0) /* different identifiers */ { strcpy(prev_id, id); strcpy(prev_file, file); retval = ID; } else if(strcmp(prev_file, file) != 0) /* different files */ { strcpy(prev_file, file); retval = NEWFILE; } else retval = LINE; return(retval); } output(val) int val; { static int curpos = 1; static int first = TRUE; int line_len = strlen(line); switch(val) { case ID: if(! first) putchar('\n'); /* finish off last line of prev id */ else first = FALSE; printf("%-20.20s\t%-14.14s\t%s", id, file, line); curpos = 40 + line_len; break; case NEWFILE: printf("\n\t\t\t%-14.14s\t%s", file, line); curpos = 40 + line_len; break; case LINE: if(curpos + line_len + 2 < width) { printf(", %s", line); /* same output line */ curpos += 2 + line_len; } else { printf(",\n\t\t\t\t\t%s", line); /* new line */ curpos = 40 + line_len; } break; case ERROR: /* shouldn't come to here */ fprintf(stderr, "%s: internal error: output() called with %s\n", name, "a value of ERROR"); fprintf(stderr, "%s: id == '%s'\tfile == '%s'\tline == '%s'\n", name, id, file, line); break; default: /* shouldn't come to here either */ fprintf(stderr, "%s: internal error: output() called with %s %d\n", name, "the unknown value", val); fprintf(stderr, "%s: id == '%s'\tfile == '%s'\tline == '%s'\n", name, id, file, line); break; } } usage() { char *basename(); fprintf(stderr, "usage: %s [-w width]\n", basename(name)); exit (1); } #include "basename.c" End of fmtxref.c echo 'extracting --- makefile' 1>&2 cat > makefile << 'End of makefile' # makefile for cxref -- C cross referencing program # # Arnold Robbins, Information and Computer Science, Georgia Tech # gatech!arnold # Copyright (c) 1985 by Arnold Robbins. # All rights reserved. # This program may not be sold, but may be distributed # provided this header is included. # some files are system dependant, e.g. where sort is. # change the appropriate macro definitions and recompile. ### definitions of files to compile and load, and other targets for make SCANOBJS= docxref.o cscan.o SCANSRCS= docxref.c cscan.l CXREF = cxref INCLS= constdefs.h basename.c PROGS= docxref fmtxref cxrfilt $(CXREF) SRCS= $(SCANSRCS) fmtxref.c cxrfilt.c $(CXREF).c DOCS= README makefile cxref.1 PRINTS= $(INCLS) $(SRCS) $(DOCS) CFLAGS= -O ### system dependant definitions, change when you install cxref # for my use during development, put in my bin, but see next few lines. DESTDIR= $(BIN) LIB= $(BIN) # when installing, use the lines below; change DESTDIR to local bin of choice. # DESTDIR=/ics/bin # LIB=/usr/local/lib/cxref # where to put the man page, use 1 instead of l if you don't have a manl. MANSEC=l # lex library, may be -lln on some systems LEXLIB= -ll # may be /bin/sort on some systems SORT=/usr/bin/sort # printer program, prt is for me, use pr on other systems P=prt # the owner and group of the installed program. Both are 'admin' on our # system, but they may different on yours. OWNER= admin GROUP= admin all: $(PROGS) @echo " all" done docxref: $(SCANOBJS) $(CC) $(SCANOBJS) $(LEXLIB) -o $@ cscan.o docxref.o cxrfilt.o: $(INCLS) fmtxref: fmtxref.c $(CC) $(CFLAGS) $@.c $(LDFLAGS) -o $@ cxrfilt: cxrfilt.c $(CC) $(CFLAGS) $@.c $(LDFLAGS) -o $@ $(CXREF): $(CXREF).c $(CC) $(CFLAGS) -DSRCDIR='"$(LIB)"' -DSORT='"$(SORT)"' $@.c $(LDFLAGS) -o $@ print: $(P) $(PRINTS) | lpr -b 'Cxref Source' touch print2 print2: $(PRINTS) $(P) $? | lpr -b 'Cxref New Source' touch print2 ### edit this before installing!! install: $(PROGS) # don't remove my bin! # rm -fr $(LIB) rm -f $(DESTDIR)/cxref # mkdir $(LIB) cp $(CXREF) $(DESTDIR)/$(CXREF) cp docxref $(LIB)/docxref cp fmtxref $(LIB)/fmtxref cp cxrfilt $(LIB)/cxrfilt # cp cxref.1 /usr/man/man$(MANSEC)/cxref.$(MANSEC) # cd $(DESTDIR); chmod 711 cxref; chown $(OWNER) cxref; chgrp $(GROUP) cxref # cd $(LIB); chmod 711 docxref fmtxref cxrfilt # cd $(LIB); chown $(OWNER) docxref fmtxref cxrfilt # cd $(LIB); chgrp $(GROUP) docxref fmtxref cxrfilt clean: rm -f $(SCANOBJS) clobber: clean rm -f $(PROGS) print2 End of makefile echo 'extracting --- old.cxref.c' 1>&2 cat > old.cxref.c << 'End of old.cxref.c' /* ** cxref.c ** ** C driver for Cxref program. ** does argument handling, then builds the right ** shell commands for passing to the system() routine. ** ** A possible but difficult speed improvement would be to ** set up the argument vectors ourselves, the i/o with a pipe() ** call, and do all the forking and execing ourselves. ** But, in keeping w/the philosophy of "Let someone else do ** the hard part", we leave well enough alone and let the shell do it. ** ** Arnold Robbins, Information and Computer Science, Georgia Tech ** gatech!arnold ** Copyright (c) 1984 by Arnold Robbins ** All rights reserved ** This program may not be sold, but may be distributed ** provided this header is included. */ #include #include #ifdef DEBUG #define system(str) printf("%s\n", str) #endif #ifdef TESTOUT dosystem(str) char *str; { int pid; fprintf(stderr, "%s\n", str); system(str); } #define system(str) dosystem(str) /* takes effect after above routine */ #endif #define TRUE 1 #define FALSE 0 char *name; /* save command name */ int xargc; /* make argc and argv available globally */ char **xargv; int width = 0; /* output width */ int sepflag = FALSE; /* do each one separately */ int iflag = TRUE; /* print out ints */ int fflag = TRUE; /* print out floats */ int cflag = TRUE; /* print out chars */ int sflag = TRUE; /* print out strings */ char *filename(); /* turns "-" into "stdin" */ main(argc, argv) int argc; char **argv; { int i; int extra_arg = FALSE; name = filename(argv[0]); for(argv++, argc--; argc > 0; argv++, argc--) if (argv[0][0] != '-') break; else if(argv[0][1] == '\0') /* filename of "-" */ break; else for(i = 1; argv[0][i] != '\0'; i++) { switch(argv[0][i]) { case 'S': sepflag = TRUE; break; case 'C': /* leave out all constants */ cflag = iflag = fflag = sflag = FALSE; break; case 'c': cflag = FALSE; break; case 'i': iflag = FALSE; break; case 'f': fflag = FALSE; break; case 's': sflag = FALSE; break; case 'w': if (isdigit(argv[0][i+1])) { width = 0; for(i++; isdigit(argv[0][i]); i++) width = width * 10 + argv[0][i] - '0'; i--; } else { width = atoi(argv[1]); extra_arg = TRUE; } break; default: usage(); break; } if (extra_arg) /* skip column width */ { extra_arg = FALSE; /* do this only once */ /* inside the for loop */ argv++; argc--; } } if (width != 0) if (width < 51) width = 80; else if (width > 132) width = 132; xargc = argc; xargv = argv; runprogs(); } char command[BUFSIZ * 10]; /* may need LOTS of room */ char com_buf[BUFSIZ * 10]; /* use short name for portability */ char *docxref(); /* functions to generate commands with args */ char *filter(); char *fmtxref(); #define ONLYONE 1 #define ALLOFTHEM 2 runprogs() /* execute the programs */ { int i; if (sepflag) /* do each file separately */ { for (i = 0; i < xargc; i++) { printf("\tC Cross Refence Listing of %s\n\n", filename(xargv[i])); fflush(stdout); /* send to ouput before commands start */ sprintf(command, "%s | sort -u +0b -2 +2n | %s | %s", docxref(i, ONLYONE), filter(), fmtxref()); system(command); if (iflag) { sprintf(com_buf, "sort -u +0n -1 +1b -2 +2n < /tmp/cxr.%d.1 | %s/cxrfilt -i | %s", getpid(), SRCDIR, fmtxref()); sprintf(command, "if test -s /tmp/cxr.%d.1 ; then %s ; fi", getpid(), com_buf); system(command); } if (fflag) { sprintf(com_buf, "sort -u +0n +1n -2 +2b -3 +3n < /tmp/cxr.%d.2 | %s/cxrfilt -f | %s", getpid(), SRCDIR, fmtxref()); sprintf(command, "if test -s /tmp/cxr.%d.2 ; then %s ; fi", getpid(), com_buf); system(command); } fflush(stdout); if (! isatty(fileno(stdout))) putchar('\f'); } } else { if (xargc == 1) printf("\tC Cross Refence Listing of %s\n\n", filename(xargv[0])); else printf("\tC Cross Reference Listing\n\n"); fflush(stdout); sprintf(command, "%s | sort -u +0b -2 +2n | %s | %s", docxref(0, ALLOFTHEM), filter(), fmtxref()); system (command); if (iflag) { sprintf(com_buf, "sort -u +0n -1 +1b -2 +2n < /tmp/cxr.%d.1 | %s/cxrfilt -i | %s", getpid(), SRCDIR, fmtxref()); sprintf(command, "if test -s /tmp/cxr.%d.1 ; then %s ; fi", getpid(), com_buf); system(command); } if (fflag) { sprintf(com_buf, "sort -u +0n +1n -2 +2b -3 +3n < /tmp/cxr.%d.2 | %s/cxrfilt -f | %s", getpid(), SRCDIR, fmtxref()); sprintf(command, "if test -s /tmp/cxr.%d.2 ; then %s ; fi", getpid(), com_buf); system(command); } fflush(stdout); if (! isatty(fileno(stdout))) putchar('\f'); } sprintf(command, "rm -f /tmp/cxr.%d.[12]", getpid()); system(command); } char *docxref(index, howmany) int index, howmany; { static char buf[BUFSIZ * 10]; int i, j; if (howmany == ONLYONE) sprintf(buf, "%s/docxref %s", SRCDIR, xargv[index]); else { /* put all the args on one command line */ sprintf(buf, "%s/docxref ", SRCDIR); i = strlen(buf); for(; xargc > 0; xargc--, xargv++) { for(j = 0; xargv[0][j] != '\0'; j++) buf[i++] = xargv[0][j]; buf[i++] = ' '; } buf[i] = '\0'; } return (buf); } char *filter() /* command line for splitting off ints and floats */ { static char buf[40]; if (! cflag && sflag) sprintf(buf, "%s/cxrfilt -c %d", SRCDIR, getpid()); else if (cflag && ! sflag) sprintf(buf, "%s/cxrfilt -s %d", SRCDIR, getpid()); else if (! cflag && ! sflag) sprintf(buf, "%s/cxrfilt -cs %d", SRCDIR, getpid()); else sprintf(buf, "%s/cxrfilt %d", SRCDIR, getpid()); return (buf); } char *fmtxref() { static char buf[40]; if (width != 0) sprintf(buf, "%s/fmtxref -w %d", SRCDIR, width); else sprintf(buf, "%s/fmtxref", SRCDIR); return(buf); } usage() { fprintf(stderr, "usage: %s [-SCcsif] [-w width] [files]\n", name); exit (1); } char *filename(fname) char *fname; { char *cp, *basename(); cp = basename(fname); return ( strcmp(cp, "-") == 0 ? "stdin" : cp); } #include "basename.c" End of old.cxref.c