[net.sources] Third

arnold@gatech.UUCP (09/04/84)

cat > /dev/null << 'End of Informative Introduction'

Ok, folks, as announced in net.lang.c, net.unix-wizards, and net.unix,
here is the third and what I hope to be final version of 'cxref', my
C cross reference generator program.

The first version  that I posted went over real well, was fairly simple, and
worked.  The second version had more new features, some bug fixes, and also
some *new* bugs, principally that it was confused over who was the parent, and
who was the child.  (I never had trouble with it on our Vax running 4.1BSD;
I take it as an interesting commentary that apparently all the bugs in the
Berkeley kernel work to cancel out the bugs in the any given user program!)
[and that was only half ":-)"]  The second version didn't go over so well
with the USENET community, since it didn't always work.

Anyway, this version now knows who is the parent and who is the child.
It should work on almost any UNIX system from V7 on.  Make sure System V
sites change 'rindex' to 'strrchr' in basename.c, but that is about it.

This shar file has an 'exit' at the end, so all you have to do is delete
the news header lines; the shell will exit before the signature.  This
blurb will automatically go into /dev/null.  As always, use /bin/sh, and
not /bin/csh.

Enjoy,
	Arnold Robbins

End of Informative Introduction
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/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.

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.

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
September 1984.
Copyright (c) 1984 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		|
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 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, 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 \-SCcfis
] [
.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 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 \-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 <stdio.h>
#include <ctype.h>
#include <signal.h>

#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 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 '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 };
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 (! 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 <sys/types.h>
#include <sys/stat.h>

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

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 <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

#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) 1984 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

# who will own the thing.  admin on our system, may be "bin" or something
# else on your system
ADMIN= 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 New 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 $(ADMIN) cxref; chgrp $(ADMIN) cxref
#	cd $(LIB); chmod 711 docxref fmtxref cxrfilt
#	cd $(LIB); chown $(ADMIN) docxref fmtxref cxrfilt
#	cd $(LIB); chgrp $(ADMIN) 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 <stdio.h>
#include <ctype.h>

#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
exit
-- 
Arnold Robbins
CSNET: arnold@gatech	ARPA:	arnold%gatech.csnet@csnet-relay.arpa
UUCP: { akgua, allegra, hplabs, ihnp4 }!gatech!arnold

Can you tell me how to get, how to get to Sesame Street?