[net.sources] C Cross Referencing Program

arnold@gatech.UUCP (Arnold Robbins) (02/23/84)

This is a suite of programs (in shell archive format) which produces a
cross reference listing of identifiers, and character, string, integer, and
floating point constants in C programs.  The man page explains how to use
it.  The makefile should need only a little bit of editing to tailor it
to your local system.

please tear at the dotted line (don't forget to remove my .signature too!)
-------------------------------------------------------------------------
echo 'extracting --- README'
cat >README <<'!!FUNKY STUFF!!'
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
in the destination directory.  You should set the destination inside
the makefile.  I have it set to use my BIN.  BIN is taken from the environment,
since I use the UNIX 4.0 make.  The cxref driver depends on the destination.

The cxref driver will you show the flow of information between programs.
I suggest that you run the programs separately, successively adding the next
program in a particular pipeline, to see how the information is transformed.

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.

constdefs.h	--- header file, used by cscan.l and filter.c.
cscan.l		--- does the work of finding indentifiers and contsants.
docxref.c	--- main program and some other stuff to drive lex.

filter.c	--- filters int and floats to their own files.

fmtxref.c	--- formats output for printing.

numfilter.c	--- formats integers and floats to pass to fmtxref.

SORT[1-3]	--- shell files to save typing during testing and development.


Arnold Robbins, Information and Computer Science, Georgia Tech
	gatech!arnold
February 1984.
Copyright (c) 1984 by Arnold Robbins
All rights reserved
!!FUNKY STUFF!!
echo 'extracting --- SORT1'
cat >SORT1 <<'!!FUNKY STUFF!!'
# 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.
#

sort -u +0b -2 +2n
!!FUNKY STUFF!!
echo 'extracting --- SORT2'
cat >SORT2 <<'!!FUNKY STUFF!!'
# 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.
#

sort -u +0n -1 +1b -2 +2n
!!FUNKY STUFF!!
echo 'extracting --- SORT3'
cat >SORT3 <<'!!FUNKY STUFF!!'
# 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.
#

sort -u +0n +1n -2 +2b -3 +3n
!!FUNKY STUFF!!
echo 'extracting --- basename.c'
cat >basename.c <<'!!FUNKY STUFF!!'
/*
** basename.c
**
** return the last portion of a path name.
** included 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.
**
*/

char *basename(str)	/* return last portion of a path name */
char *str;
{
	char *cp, *rindex();

	if((cp = rindex(str, '/')) == NULL)
		return(str);
	else
		return(++cp);
}
!!FUNKY STUFF!!
echo 'extracting --- constdefs.h'
cat >constdefs.h <<'!!FUNKY STUFF!!'
/*
** 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'
!!FUNKY STUFF!!
echo 'extracting --- cscan.l'
cat >cscan.l <<'!!FUNKY STUFF!!'
/*
** 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;
%}

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		|
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\n",
					line_no);
				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 then delete 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\n",
							line_no);
						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();

.			;	/* 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);

	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);
}
!!FUNKY STUFF!!
echo 'extracting --- cxref.1'
cat >cxref.1 <<'!!FUNKY STUFF!!'
.TH CXREF 1 local
.SH NAME
cxref \- cross reference C source files
.SH SYNOPSIS
.I cxref
[-Scfis] [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).
Integer and floating point constants are sorted numerically.
.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
S
Cross reference all files separately.
The default action is to cross reference all named files together.
.TP
c
Leave character constants out of the cross reference listing.
.TP
f
Leave floating point constants out of the cross reference listing.
.TP
i
Leave integer constants out of the cross reference listing.
.TP
s
Leave string constants out of the cross reference listing.
.RE

By default, all types of constants are included in the cross reference.
.PP
.I Cxref
does
.I 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
.I Cxref
is best run in the background, with its output redirected into
a file or the line printer spooler lpr(1), since it reads all
the named files, using 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 no more than 80 columns),
so it should probably be run piping its output into 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"
lex(1),
lpr(1),
pr(1),
sort(1)
.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-Relay
.br
.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.
!!FUNKY STUFF!!
echo 'extracting --- cxref.c'
cat >cxref.c <<'!!FUNKY STUFF!!'
/*
** 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 <stdio.h>
#include <ctype.h>

#ifdef DEBUG
#define system(str)	printf("%s\n", str)
#endif

#define TRUE	1
#define FALSE	0

char *name;
char **xargv;
int xargc;

int sflag = FALSE;	/* do each one separately */
int iflag = TRUE;	/* print out ints */
int fflag = TRUE;	/* print out floats */
int cflag = TRUE;	/* print out chars */
int strflag = TRUE;	/* print out strings */

main(argc, argv)
int argc;
char **argv;
{
	int i, j;

	name = 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':
					sflag = TRUE;
					break;
				
				case 'c':
					cflag = FALSE;
					break;
				
				case 'i':
					iflag = FALSE;
					break;
				
				case 'f':
					fflag = FALSE;
					break;
				
				case 's':
					strflag = FALSE;
					break;

				default:
					usage();
					break;
				}
	
	xargc = argc;
	xargv = argv;

	runprogs();
}

char command[BUFSIZ * 10];	/* may need LOTS of room */
char commandbuf[BUFSIZ * 10];
char *docxref();
char *filter();
char *intout();
char *floatout();

#define ONLYONE		1
#define ALLOFTHEM	2

runprogs()		/* execute the programs */
{
	int i;

	if (sflag)
	{
		for (i = 0; i < xargc; i++)
		{
			printf("\tC Cross Refence Listing of %s\n", xargv[i]);
			fflush(stdout);
			/* send to ouput before commands start */
			sprintf(command,
				"%s | sort -u +0b -2 +2n | %s | %s/fmtxref",
				docxref(i, ONLYONE), filter(), SRCDIR);
			system(command);
			if (iflag)
			{
				sprintf(commandbuf,
	"sort -u +0n -1 +1b -2 +2n < /tmp/cxr.%d.1 | %s/intfilter | %s/fmtxref",
					getpid(), SRCDIR, SRCDIR);
				sprintf(command,
			"if [ -s /tmp/cxr.%d.1 ] ; then %s ; fi",
					getpid(), commandbuf);
				system(command);
			}
			if (fflag)
			{
				sprintf(commandbuf,
"sort -u +0n +1n -2 +2b -3 +3n < /tmp/cxr.%d.2 | %s/floatfilter | %s/fmtxref",
					getpid(), SRCDIR, SRCDIR);
				sprintf(command,
			"if [ -s /tmp/cxr.%d.2 ] ; then %s ; fi",
					getpid(), commandbuf);
				system(command);
			}
			fflush(stdout);
		}
	}
	else
	{
		if (xargc == 1)
			printf("\tC Cross Reference Listing of %s\n\n", xargv[0]);
		else
			printf("\tC Cross Reference Listing\n\n");
		fflush(stdout);
		sprintf(command, "%s | sort -u +0b -2 +2n | %s | %s/fmtxref",
			docxref(0, ALLOFTHEM), filter(),SRCDIR);
		system (command);
		if (iflag)
		{
			sprintf(commandbuf,
	"sort -u +0n -1 +1b -2 +2n < /tmp/cxr.%d.1 | %s/intfilter | %s/fmtxref",
				getpid(), SRCDIR, SRCDIR);

			sprintf(command,
				"if [ -s /tmp/cxr.%d.1 ] ; then %s ; fi",
				getpid(), commandbuf);
			system(command);
		}
		if (fflag)
		{
			sprintf(commandbuf,
"sort -u +0n +1n -2 +2b -3 +3n < /tmp/cxr.%d.2 | %s/floatfilter | %s/fmtxref",
				getpid(), SRCDIR, SRCDIR);
			sprintf(command,
				"if [ -s /tmp/cxr.%d.2 ] ; then %s ; fi",
				getpid(), commandbuf);
			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];
	int i, j, k;

	if (howmany == ONLYONE)
		sprintf(buf, "%s/docxref %s", SRCDIR, xargv[index]);
	else
	{
		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()
{
	static char buf[40];

	if (! cflag && strflag)
		sprintf(buf, "%s/filter -c %d", SRCDIR, getpid());
	else if (cflag && ! strflag)
		sprintf(buf, "%s/filter -s %d", SRCDIR, getpid());
	else if (! cflag && ! strflag)
		sprintf(buf, "%s/filter -cs %d", SRCDIR, getpid());
	else
		sprintf(buf, "%s/filter %d", SRCDIR, getpid());

	return (buf);
}

usage()
{
	fprintf(stderr, "usage: %s [-Scsif] [files]\n", name);
	exit (1);
}
!!FUNKY STUFF!!
echo 'extracting --- docxref.c'
cat >docxref.c <<'!!FUNKY STUFF!!'
/*
** 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 <stdio.h>
#include <ctype.h>

#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 */

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 = argv[0];		/* save command name */
	fname = "stdin";
	if(argc == 1)
	{
		yylex();
		exit(0);
	}

	if(argv[1][0] == '-')
			usage(argv[0]);	/* will exit */

	saved_in = *stdin;
	/* save stdin in case "-" is found on 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"
!!FUNKY STUFF!!
echo 'extracting --- filter.c'
cat >filter.c <<'!!FUNKY STUFF!!'
/*
** filter.c
**
** 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.
**
**	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 <stdio.h>
#include "constdefs.h"

#define MAXFILE		120
#define MAXLINE		120

FILE *fp1, *fp2;
int cflag = 0;
int sflag = 0;
char *name;

main(argc, argv)
int argc;
char **argv;
{
	char buf[BUFSIZ];
	char file1[MAXFILE], file2[MAXFILE];

	if (argc <= 1)
		usage();

	if(argv[1][0] == '-')
	{
		if (argc == 2)
			usage();

		if (argv[1][1] == 'c' || argv[1][2] == 'c')
			cflag = 1;

		if (argv[1][1] == 's' || argv[1][2] == 's')
			sflag = 1;

		if (!cflag && !sflag)	/* bad option given */
			usage();

		argv++;
	}

	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);
}

#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.
	 */

	switch (type) {
	case OCTAL:
		fprintf(fp1, "%d\t%s\t%s\t1\n", val, file, line);
		break;

	case DEC:
		fprintf(fp1, "%d\t%s\t%s\t2\n", val, file, line);
		break;
	
	case HEX:
		fprintf(fp1, "%d\t%s\t%s\t3\n", val, file, line);
		break;
	}
}

outfloat(buf)
char *buf;
{
	char file[MAXLINE], line[MAXLINE];
	char mantissa[MAXLINE], exponent[MAXLINE];
	char strval[MAXLINE];
	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);
	sprintf(strval, controlstr, val);

	/*
	 * 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) a float filter will put them back together.
	 */

	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 [-cs] pid\n", name);
	exit (1);
}
!!FUNKY STUFF!!
echo 'extracting --- fmtxref.c'
cat >fmtxref.c <<'!!FUNKY STUFF!!'
/*
** 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 <stdio.h>
#include <ctype.h>

#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

char prev_id[MAXID] = "";
char prev_file[MAXFILE] = "";

char id[MAXID] = "";
char file[MAXFILE] = "";
char line[MAXNUM] = "";

main(argc, argv, envp)
int argc;
char **argv, **envp;
{
	char inline[BUFSIZ];
	char *gets();
	int val;

	if(gets(inline) == NULL)
	{
		fprintf(stderr, "%s: standard input is empty.\n", argv[0]);
		exit(1);
	}

	if((val = breakup(inline)) == ERROR)
	{
		fprintf(stderr, "malformed input '%s'\n", inline);
		exit(2);
	}

	output(val);		/* does proper formatting */

	while(gets(inline) != NULL && val != ERROR)
	{
		val = breakup(inline);
		output(val);
	}

	if(val == ERROR)
	{
		fprintf(stderr, "malformed input '%s'\n", inline);
		exit(2);
	}

	putchar('\n');
}

breakup(text)
char *text;
{
	int retval;
	int i, j;

	if(text[0] == '"' || text[0] == '\'')
	{
		/* 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');
		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 < 80)
		{
			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, "internal error: output() called with %s\n",
			"a value of ERROR");
		fprintf(stderr, "id == '%s'\tfile == '%s'\tline == '%s'\n",
			id, file, line);
		break;

	default:
		/* shouldn't come to here either */
		fprintf(stderr, "internal error: output() called with %s %d\n",
			"the unknown value", val);
		break;
	}
}
!!FUNKY STUFF!!
echo 'extracting --- makefile'
cat >makefile <<'!!FUNKY STUFF!!'
# makefile for cxref -- C cross referencing program
#
#	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.

SCANOBJS= docxref.o cscan.o
SCANSRCS= docxref.c cscan.l

INCLS= constdefs.h basename.c
PROGS= docxref fmtxref filter numfilter cxref
SRCS=  $(SCANSRCS) fmtxref.c filter.c numfilter.c cxref.c
DOCS=  README makefile cxref.1

PRINTS= $(INCLS) $(SRCS) $(DOCS)

CFLAGS= -O

# for my use during development, put in my bin
DESTDIR=$(BIN)
# when installing, use the line below
# DESTDIR=/ics/bin

all: $(PROGS)
	@echo "	all" done

docxref: $(SCANOBJS)
	cc $(SCANOBJS) -ll -o $@

cscan.o docxref.o filter.o: $(INCLS)

fmtxref: fmtxref.c
	$(CC) $(CFLAGS) fmtxref.c $(LDFLAGS) -o fmtxref

filter: filter.c
	$(CC) $(CFLAGS) filter.c $(LDFLAGS) -o filter

numfilter: numfilter.c $(INCLS)
	$(CC) $(CFLAGS) numfilter.c $(LDFLAGS) -o numfilter

cxref: cxref.c
	$(CC) $(CFLAGS) -DSRCDIR='"$(DESTDIR)"' cxref.c $(LDFLAGS) -o cxref

print:
	prt $(PRINTS) | lpr -b 'Cxref New Source'
	touch print2

print2: $(PRINTS)
	prt $? | lpr -b 'Cxref New Source'
	touch print2

install: $(PROGS)
	rm -f $(DESTDIR)/cxref $(DESTDIR)/docxref $(DESTDIR)/fmtxref
	rm -f $(DESTDIR)/filter $(DESTDIR)/intfilter $(DESTDIR)/floatfilter
	cp cxref $(DESTDIR)/cxref
	cp docxref  $(DESTDIR)/docxref
	cp fmtxref  $(DESTDIR)/fmtxref
	cp filter $(DESTDIR)/filter
	cp numfilter $(DESTDIR)/intfilter
	ln $(DESTDIR)/intfilter $(DESTDIR)/floatfilter
#	cp cxref.1 /usr/man/man.l
#	cd $(DESTDIR); chmod 711 $(PROGS)

clean:
	rm -f $(SCANOBJS)

clobber: clean
	rm -f $(PROGS) print2
!!FUNKY STUFF!!
echo 'extracting --- numfilter.c'
cat >numfilter.c <<'!!FUNKY STUFF!!'
/*
** numfilter.c
**
** Convert input back to proper form, depending on how called.
** Output is passed to the fmtxref program.
**
**	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 <stdio.h>

#define MAXLINE		120

char *name;

main(argc, argv)
int argc;
char **argv;
{
	char *basename();

	name = basename(argv[0]);	/* remove leading path */

	if (strcmp(name, "intfilter") == 0)	/* turn ints back to normal */
		intfilter();
	else if (strcmp(name, "floatfilter") == 0)
		floatfilter();			/* turn floats back to normal */
	else
	{
		fprintf(stderr, "%s: must be named floatfilter or intfilter\n",
			name);
		exit(1);
	}

	exit(0);
}

intfilter()
{
	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 1:		/* 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 2:		/* decimal */
			printf("%d\t%s\t%s\n", val, file, number);
			break;
		
		case 3:		/* 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()
{
	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\t", mantissa);
		else
			printf("%sE%s\t", mantissa, exponent);
		
		printf("%s\t%s\n", file, number);
	}
}

#include "basename.c"
!!FUNKY STUFF!!
-- 
Arnold Robbins
CSNET: arnold@gatech		ARPA: arnold.gatech@CSNet-relay
UUCP:	...!{akgua,allegra,rlgvax,sb1,ut-sally}!gatech!arnold

Did'ja ever have one of those re-incarnations?