[net.sources] New and Improved Cxref

arnold@gatech.UUCP (Arnold Robbins) (05/01/84)

Here is a new and improved version of the cxref program I posted some
months ago.  This version fixes all known bugs, and adds two new options,
as well as a faster driver (which fixed a bug).

There is some discussion of this in net.lang.c.  See there for details.

It is in 'shar' format, to be decoded with /bin/sh, not /bin/csh.

Enjoy,

Arnold Robbins		(full addresses below in .signature)

(Tear here, don't forget .signature below)
--------------------------------------------------------------------
echo 'extracting --- README' 1>&2
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
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.

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.

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
May 1984.
Copyright (c) 1984 by Arnold Robbins.
All rights reserved.
!!FUNKY STUFF!!
echo 'extracting --- SORT1' 1>&2
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.

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

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

exec sort -u +0n +1n -2 +2b -3 +3n
!!FUNKY STUFF!!
echo 'extracting --- basename.c' 1>&2
cat >basename.c <<'!!FUNKY STUFF!!'
/*
** 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);
}
!!FUNKY STUFF!!
echo 'extracting --- constdefs.h' 1>&2
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' 1>&2
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;
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);
}
!!FUNKY STUFF!!
echo 'extracting --- cxref.1' 1>&2
cat >cxref.1 <<'!!FUNKY STUFF!!'
.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).
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.
!!FUNKY STUFF!!
echo 'extracting --- cxref.c' 1>&2
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.
**
** 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" };	/* allow 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;
	}
	if (cflag && ! sflag)
	{
		cxrfilt[1] = "-s";
		cxrfilt[2] = pidbuf;
		cxrfilt[3] = NULL;
	}
	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()
{
	char buf[BUFSIZ];
	int i;

	for (i = 1; i <= 2; i++)
	{
		sprintf(buf, "/tmp/cxr.%d.%d", getpid(), i);
		unlink(buf);
	}
}

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);
		}
		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);
		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]);
		sprintf (buf, "/usr/bin/sort");
		execv (buf, sort1);
		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);

	default:
		fprintf(stderr, "in docxref: 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);
		}
		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);
		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 ("/usr/bin/sort", sort2);
		break;

	default:
		fprintf(stderr, "in docxref: 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);
		}
		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);
		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 ("/usr/bin/sort", sort3);
		break;

	default:
		fprintf(stderr, "in docxref: 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"
!!FUNKY STUFF!!
echo 'extracting --- cxrfilt.c' 1>&2
cat >cxrfilt.c <<'!!FUNKY STUFF!!'
/*
** 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\t", mantissa);
		else
			printf("%sE%s\t", mantissa, exponent);
		
		printf("%s\t%s\n", file, number);
	}
}

#include "basename.c"
!!FUNKY STUFF!!
echo 'extracting --- docxref.c' 1>&2
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 */

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"
!!FUNKY STUFF!!
echo 'extracting --- fmtxref.c' 1>&2
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

#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"
!!FUNKY STUFF!!
echo 'extracting --- makefile' 1>&2
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

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

# for my use during development, put in my bin
DESTDIR= $(BIN)
LIB= $(BIN)
# when installing, use the lines below; change DESTDIR to local bin of choice.
# DESTDIR=/ics/bin
# LIB=/usr/lib/cxref

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

docxref: $(SCANOBJS)
	cc $(SCANOBJS) -ll -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)"' $(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)
# 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/manl/cxref.l
#	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
!!FUNKY STUFF!!
echo 'extracting --- old.cxref.c' 1>&2
cat >old.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>

#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"
!!FUNKY STUFF!!
-- 
Arnold Robbins
CSNET: arnold@gatech
ARPA:  arnold%gatech.csnet@csnet-relay.arpa
UUCP:  ...!{akgua, allegra, rlgvax, sb1, ut-sally}!gatech!arnold

"All this digital stuff is just a fad.  Analog is the way to go."
	-- William M. Robbins, 1984