[comp.sources.unix] v11i072: Including PostScript figures in ditroff, Part01/05

rsalz@uunet.UU.NET (Rich Salz) (09/24/87)

Submitted-by: trevor@linc.cis.upenn.edu (Trevor Darrell)
Posting-number: Volume 11, Issue 72
Archive-name: psfig/Part01

This is "psfig," a package to include PostScript figures in ditroff. It
was presented at the Phoenix Usenix and in Unix Review.


system requirements:
	PostScript printer
	ditroff DWB version 2.0 or later
	transcript, with source for psdit
		**This may not be needed if you have transcript 3.0

Please read the "system requirements" so I am not deluged with "but it
doesn't work with my ditroff!" complaints.

--tjd

Trevor Darrell

trevor@linc.cis.upenn.edu  |  trevor@grasp.cis.upenn.edu  |  (215) 898-5617
Computer and Information Science 
University of Pennsylvania CIS dept. 200 S. 33rd st. Philadelphia, PA 19104

-----------------------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Contents:  trf/ trf/src/ trf/README trf/src/Makefile trf/src/cmds.c
#	trf/src/item.c trf/src/item.h trf/src/keywords.c trf/src/lineio.c
#	trf/src/macros.c trf/src/main.c trf/src/psfig.h trf/src/troff.c
 
echo mkdir - trf
mkdir trf
chmod u=rwx,g=rx,o=rx trf
 
echo x - trf/README
sed 's/^@//' > "trf/README" <<'@//E*O*F trf/README//'
This is the source and documentation to the psfig preprocessor for ditroff.

All source code, documentation and related files are Copyright (C) 1987
by Trevor Darrell. All rights reserved. This software may not be distributed
on a for-profit basis in any way without express written permission of the
author(s).

===========================================================================

What you need to use psfig:

	+ a ditroff (device-independent troff) that accepts the \X
		command for text pass-thru. ditroff from DWB 2.0 or 
		greater will do.

	+ source to psdit from the adobe transcript package

	+ (optionally) source for ditroff to make some minor fixes.

	+ of course, a postscript printer for the final result. Draft
		copies do not need a postscript device.

========================================================================
What to do:

1. Compile the preprocessor:
	cd src; make psfig
	cp psfig /usr/local (or /usr/bin, &c...)

2. Next, install the patches to psdit and psdit.pro.
** Read patches/README for instructions on the psdit.c and psdit.pro patches.

3. Copy lib/psfig.tmac to the troff macro directory (/usr/lib/tmac).

4. Finally, if you are up to it and have ditroff source, apply
	the ditroff patch. (less critical) If you don't make the
	patch, figures may not center properly and figures won't
	work inside eqn. The centering problem can be alleviated
	by using "-f" with psfig.

Give the test program a spin...

% cd tst
% make test.ps

make a copy of the manaul...

% cd doc 
% dopaper

Note that the slew of warnings from psfig about `psfig.ps.?' on the first pass
is no problem and should be ignored. (due to recursive figure processing...)

@//E*O*F trf/README//
chmod u=rw,g=r,o=r trf/README
 
echo mkdir - trf/src
mkdir trf/src
chmod u=rwx,g=rx,o=rx trf/src
 
echo x - trf/src/Makefile
sed 's/^@//' > "trf/src/Makefile" <<'@//E*O*F trf/src/Makefile//'
CFLAGS = -g
OBJS = main.o lineio.o item.o troff.o cmds.o keywords.o macros.o
SRCS = main.c lineio.c item.c troff.c cmds.c keywords.c macros.c
HDRS = psfig.h item.h

@.c.o:
	-rm -f $@
	cc -c $(CFLAGS) $<

psfig : $(OBJS) 
	-rm -f psfig
	cc ${CFLAGS} -o psfig $(OBJS)

$(OBJS) : psfig.h
item.o cmds.o keywords.o: item.h

lint: $(SRCS) $(HDRS)
	lint $(SRCS)

tidy:
	-rm -f *.BAK *.CKP a.out
clean: tidy
	-rm -f *.o core '#'*
@//E*O*F trf/src/Makefile//
chmod u=rw,g=r,o=r trf/src/Makefile
 
echo x - trf/src/cmds.c
sed 's/^@//' > "trf/src/cmds.c" <<'@//E*O*F trf/src/cmds.c//'
/***	
 ***	cmds.c --
 ***	
 ***	Handle parsing of commands for psfig.
 ***	
 ***	T.Darrell, 3/86.
 ***/

# include "psfig.h"
# include "item.h"

int	Draft = DraftDefault;

int	inLineFlag = 0;		/* are we generating an in-line figure? */

/* 
 * dofigcmd()
 */

dofigcmd()
{
	int		end = 0;
	int		didsome = 0;
	int		global = 0;
	static char	cmdbuf[SMBUFSZ];
	static char	bigbuf[BUFSIZ];

	while (!end) {
		switch (getItem(cmdbuf)) {

		case iEndOfInput:
			++end;
		case iSeparator:			
			break;

		case iDefine:
			switch (getItem(cmdbuf)) {
			case iEndOfInput:
			case iSeparator:
				error("empty define");

			case iWord:
				if (getString(bigbuf, sizeof(bigbuf))) {
					addMacro(cmdbuf, bigbuf);
				} else {
					deleteMacro(cmdbuf);
				}
				break;

			default:
				error("can't define a keyword");
			}
			break;

		case iDelim:
			switch (getItem(cmdbuf)) {
			case iEndOfInput:
				++end;
			case iSeparator:
				delimSt = delimEnd = 0;
				break;

			case iWord:
				delimSt = cmdbuf[0];
				if (cmdbuf[1])
					delimEnd = cmdbuf[1];
				else
					delimEnd = delimSt;
				break;
			}
			break;

		case iSpace:
			switch(getItem(cmdbuf)) {
			case iWord:
				saveSpaceH(cmdbuf);
				didsome++;
				break;
			default:
				error("bad space command");
			}
			break;

		case iLiteral:

			if (!getString(bigbuf, sizeof(bigbuf))) 
				break;

			for (;;) {
				switch (getItem(cmdbuf)) {
				case iLevel:
					if (getItem(cmdbuf) != iWord)
						error("bad level in literal");
					if (atoi(cmdbuf) < Draft) {
						++didsome;
						emitLiteral(bigbuf);
					}
					continue;

				case iGlobal:
					++global;
					emitLiteral("globalstart");
					continue;

				default:
					if (didsome) {
						unGetItem(cmdbuf);
						break;
					}
					if (inLineFlag) {
						if (InLineLevel < Draft) {
							emitLiteral(bigbuf);
							++didsome;
						}
					} else {
						if (BrokenOutLevel < Draft) {
							emitLiteral(bigbuf);
							++didsome;
						}
					}
	
					unGetItem(cmdbuf);
					break;
				}
				break;
			}
			if (global) {
				emitLiteral("globalend");
				global = 0;
			}
			break;

		case iFile:
			*bigbuf = 0;
			if (getItem(bigbuf) != iWord)
				error("bad file command");

			for (;;) {
				switch (getItem(cmdbuf)) {
				case iLevel:
					if (getItem(cmdbuf) != iWord)
						error("bad level in literal");
					if (atoi(cmdbuf) < Draft) {
						includeFile(abspath(bigbuf));
						++didsome;
					}
					continue;

				case iGlobal:
					++global;
 					emitLiteral("globalstart");
					continue;
	
				default:
					if (inLineFlag) {
						if (InLineLevel < Draft) {
						  includeFile(abspath(bigbuf));
						  ++didsome;
						}
					} else {
						if (BrokenOutLevel < Draft) {
						  includeFile(abspath(bigbuf));
						  ++didsome;
						}
					}
					unGetItem(cmdbuf);
					break;
				}
				break;
			}
			if (global) {
				emitLiteral("globalend");
				global = 0;
			}

			break;
			
		case iFigure:
			includeFig();
			didsome++;
			break;

		case iWord:
			unGetItem(cmdbuf);
			includeFig();
			didsome++;
			break;

		default:
			error("unknown command");
		}
	}

	return didsome;
}


/* 
 * includeFig:
 * 
 * Appends the ditroff code needed to save space for figure specifed by the
 * 'file' command in cmd to dStr, and returns the new string.
 * 
 * The ditroff commands leave the "pen" in the upper right hand corner.
 */

static char path[SMBUFSZ], filenm[SMBUFSZ];
static char x[SMBUFSZ], y[SMBUFSZ];	/* postscript size */
static char rx[SMBUFSZ], ry[SMBUFSZ];	/* ditroff reserve size */
int doClip;
int nbox, draftlvl;
int bflag;				/* have bounds been set? */
float bb_llx, bb_lly, bb_urx, bb_ury;
char head[BUFSZ], foot[BUFSZ];		/* things to do before and
					 * after the figure is inlcluded
					 * (arguments, etc...)
					 */
char *
includeFig()
{
	float		fx, fy;

	bflag = 0;

	parse();

	(void) strcpy(filenm, abspath(path));

	/* 
	 * If no size was specified, use size from file (in points).
	 * If only a width was specified, calculate
	 * 	height = fileheight * (width / filewidth).
	 * If only a height, calculate
	 * 	width = filewidth * (height / fileheight).
	 */

	if (!bflag) {
		getFileBB(filenm, &bb_llx, &bb_lly, &bb_urx, &bb_ury);
	}

	fx = bb_urx - bb_llx;
	fy = bb_ury - bb_lly;
	
	if (!*x && !*y) {
		(void) sprintf(x, "%.2fp", fx);
		(void) sprintf(y, "%.2fp", fy);
	} else if (!*x) {
		(void) sprintf(x,"(%.2fp*%s/%.2fp)", fx, y, fy);
	} else if (!*y) {
		(void) sprintf(y,"(%.2fp*%s/%.2fp)", fy, x, fx);
	}

	/* 
	 * If reserve size was ommited, = figure size
	 */
	
	if (*rx == 0) 
		(void) strcpy(rx, x);
	if (*ry == 0)
		(void) strcpy(ry, y);


	if (draftlvl >= Draft) {

		if (!inLineFlag && strcmp(ry, "0")) {
			draftNote(path);
			makeBox(rx, ry);
		} else {
			if (strcmp(ry,"0"))
				saveSpaceV(ry, (inLineFlag ? UP : DOWN));

			saveSpaceH(rx);
		}
				
	} else {

		if (inLineFlag) 
			moveUp(y);

		startfig(x, y, bb_llx, bb_lly, bb_urx, bb_ury);
		if (doClip) emitDoClip();
		fputs(head, stdout);
		includeFile(filenm);
		fputs(foot, stdout);
		endfig();		

		/* Dont save space if == "0" */
		if (strcmp(ry, "0"))
			saveSpaceV(ry, (inLineFlag ? UP : DOWN));
		saveSpaceH(rx);

		if (inLineFlag)
			moveDown(y);
	}
}

/* 
 * parse:
 * 
 * parse file command.
 */

parse()
{
	int		end = 0;
	int		foundPath = 0;
	static char	tmpbuf[SMBUFSZ];
	static char	word[SMBUFSZ];

	/* 
	 *  initialize all values to their defaults,
	 *  then fill them in as specifed on the cmd line.
	 */

	strcpy(head, strcpy(foot, ""));

	if (inLineFlag)
		draftlvl = InLineLevel;
	else
		draftlvl = BrokenOutLevel;
	nbox = 0;
	doClip = 0;

	(void) strcpy(rx, strcpy(x, strcpy(y, "")));

	/* 
	 * If we are in-line, set default vert. reserve to nothing ("0")
	 * otherwise, set default reserve to NULL, which signals
	 * it should later be filled in with x,y.
	 */
	if (inLineFlag)
		(void) strcpy(ry, "0");
	else
		(void) strcpy(ry, "");

	while (!end) {
		switch(getItem(word)) {
			case iHeight:
				if (getItem(y) != iWord)
					error("bad height");
				break;
			case iWidth:
				if (getItem(x) != iWord)
					error("bad width");
				break;
			case iReserve:
				if (getItem(rx) != iWord) {
					unGetItem(rx);
					(void) strcpy(rx, strcpy(ry, ""));
					break;
				}
				if (getItem(ry) != iWord)
					error("bad reserve");
				break;
			case iClip:
				++doClip;
				break;
			case iLevel:
				if (getItem(tmpbuf) != iWord)
					error("bad level");
				draftlvl = atoi(tmpbuf);
				break;
			case iBounds:
				if (getItem(tmpbuf) != iWord)
					error("bad bounds");
				sscanf(tmpbuf, "%f", &bb_llx);
					
				if (getItem(tmpbuf) != iWord)
					error("bad bounds");
				sscanf(tmpbuf, "%f", &bb_lly);
	
				if (getItem(tmpbuf) != iWord)
					error("bad bounds");
				sscanf(tmpbuf, "%f", &bb_urx);
	
				if (getItem(tmpbuf) != iWord)
					error("bad bounds");
				sscanf(tmpbuf, "%f", &bb_ury);
				++bflag;
				break;
			case iLeftBrace:
				parse_format();
				break;
			case iEndOfInput:
			case iSeparator:
				++end;
				break;
			case iWord:
				if (!foundPath) {
					strcpy(path, word);
					++foundPath;
					break;
				}
				/* flow through */
			default:
				unGetItem(word);
				++end;
				break;
		}
	}

	/* 
	 * Make sure we have a file name.
	 */

	if (!foundPath) {
		error("missing file name in figure command");
	}
}

parse_format() {
	static char word[SMBUFSZ];
	int pre = 1; /* true until `figure' is encountered */

	for (;;) switch (getItem(word)) {

		case iFile :
			if (getItem(word) != iWord)
				error("bad file command in format");

			sIncludeFile((pre ? head : foot), abspath(word),BUFSZ);
			break;

		case iLiteral :
			if (!getString(word, sizeof(word)))
				error("bad literal in format");

			sEmitLiteral((pre ? head : foot), word, BUFSZ);
			break;

		case iFigure :
			pre = 0;
			break;

		case iRightBrace:
			return;

		default :
			error("bad command in format");
	}
}
/* end of cmds.c */
@//E*O*F trf/src/cmds.c//
chmod u=r,g=r,o=r trf/src/cmds.c
 
echo x - trf/src/item.c
sed 's/^@//' > "trf/src/item.c" <<'@//E*O*F trf/src/item.c//'
/***	
 ***	item.c --
 ***	
 ***	The item reading abstraction for psfig
 ***	
 ***	N.Batchelder, 3/8/86.
 ***/

# include "item.h"
# include "psfig.h"

# define elif		else if
# define isWhite(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')

static int	endofinput = 0;		/* Have we hit the end? */
static char	cbuf[1000];		/* Where we do all processing */
static char	*cp;			/* Our position in cbuf */
static char	*(*getmore)();		/* Our gimme gimme function */

# define StartOffset	700	/* Where in cbuf to start filling. */

/* 
 * getCh:
 * 
 * Read a character from our input. Returns 0 when no more.
 */

static
char
getCh()
{
	while (!endofinput) {
		if (*cp) {
			return *cp++;
		}

		if (!getmore) {
			endofinput++;
		} elif ((*getmore)(cp = cbuf + StartOffset) == NULL) {
			endofinput++;
		}
	}

	return 0;
}

/* 
 * pushBackCh:
 * 
 * Takes a character and pushes it back. Just like ungetc.
 */

static
pushBackCh(ch)
char	ch;
{
	*--cp = ch;
	endofinput = 0;
}

/* 
 * pushBackStr:
 * 
 * Takes a string and pushes it back into our input so that it will be read
 * next time.
 */

static
pushBackStr(str)
char	*str;
{
	register char	*sp;

	/* 
	 * Find the end of the string.
	 */
	
	for (sp = str; *sp; sp++)
		;

	/* 
	 * Now write it into the buffer backwards from the current point.
	 */
	
	while (--sp >= str) {
		*--cp = *sp;
	}

	if (cp < cbuf) {
		error("pushback overflow");
	}

	if (strlen(str)) {
		endofinput = 0;
	}
}

/* 
 * setInput:
 * 
 * Takes a string and a function, and installs them as the current working
 * text and the function to call to get more when that's used up.
 */

void
setInput(str, morefn)
char	*str;
char	*(*morefn)();
{
	endofinput = 0;
	(void) strcpy(cbuf+StartOffset, str);
	cp = cbuf+StartOffset;
	getmore = morefn;
}

/* 
 * getItem:
 * 
 * Reads the next item (token) from the input and copies it to the buffer
 * provided to it. The return value is the type of the item (enum item).
 */

static char		lastitem[SMBUFSZ];
static enum item	lasttype;
static int		pusheditem = 0;

enum item
getItem(ibuf)
char	*ibuf;
{
	char		ch;		/* Our latest character from getCh */
	char		quote = 0;	/* Our current quote char, if any */
	char		*bp = ibuf;	/* Fills up ibuf */
	enum item	itype;		/* What we eventually return */
	char		*value;		/* The value of a possible macro */

	if (pusheditem) {
		(void) strcpy(ibuf, lastitem);
		pusheditem = 0;
		return lasttype;
	}
		
	/* 
	 * First skip whitespace.
	 */

	while ((ch = getCh()) && isWhite(ch))
		;

	if (ch == 0) {
		return lasttype = iEndOfInput;
	}

	/* 
	 * Do we have a quote character in hand?
	 */

	if (index(Quotes, ch)) {
		quote = ch;
		if (!(ch = getCh())) {
			return lasttype = iEndOfInput;
		}
	}

	/* 
	 * Loop around building up the item.
	 */

	for (;;) {
		if (quote) {
			if (ch == quote) {
				break;
			}
		} elif (isWhite(ch)) {
			pushBackCh(ch);
			break;
		} elif (index(SelfDelim, ch)) {
			break;
		}

		*bp++ = ch;

		if (!(ch = getCh())) {
			break;
		}

		if (bp - ibuf > SMBUFSZ) {
			error("word overflow");
		}
	}
	*bp = '\0';

	if (index(SelfDelim, ch)) {
		if (bp == ibuf) {
			/* It was the first character read */
			switch (ch) {
			case Sep:
				return lasttype = iSeparator;
			case LBrace:
				return lasttype = iLeftBrace;
			case RBrace:
				return lasttype = iRightBrace;
			}
		} else {
			/* It terminated the item */
			pushBackCh(ch);
		}
	}

	/* 
	 * If the thing was quoted, then it is simply a generic word.
	 */
	
	if (quote) {
		return iWord;
	}
	
	/* 
	 * Now we have a word in ibuf. We must check to see if it is a
	 * keyword.
	 */

	if (isKeyword(ibuf, &itype)) {
		return lasttype = itype;
	}

	/* 
	 * Is it a macro?
	 */

	if (isMacro(ibuf, &value)) {
		pushBackStr(value);
		return getItem(ibuf);
	}

	/* 
	 * I guess it was just a generic word after all.
	 */

	return lasttype = iWord;
}

/* 
 * unGetItem:
 * 
 * Takes the text of the last item and causes it to be returned next time.
 */

unGetItem(ibuf)
char	*ibuf;
{
	pusheditem++;
	(void) strcpy(lastitem, ibuf);
}

/* 
 * getString:
 * 
 * Reads a self-delimited string from the input, and returns it in the buffer
 * provided. Self-delimited means that the first non-white character is taken
 * to be the delimiter. No macro expansion is done, and quotes are like
 * anything else.
 * Returns 1 or 0 depending on whether there was a string or not. (A separator
 * or end of input can mean no string).
 */

int
getString(sbuf, len)
char	*sbuf;
int	len;
{
	char	ch;		/* Our latest character from getCh */
	char	delim;		/* This string's delimiter */
	char	*bp = sbuf;	/* Fills up `sbuf' */

	/* 
	 * Skip whitespace.
	 */

	while ((ch = getCh()) && isWhite(ch))
		;

	if (ch == 0 || ch == Sep) {
		return 0;
	}

	delim = ch;

	do {
		ch = getCh();
	} while ((ch != delim) && (bp < sbuf + len) && (*bp++ = ch));

	if (bp == sbuf + len) error("buffer overflow");

	*bp = '\0';
	return 1;
}
	
/* end of item.c */
@//E*O*F trf/src/item.c//
chmod u=r,g=r,o=r trf/src/item.c
 
echo x - trf/src/item.h
sed 's/^@//' > "trf/src/item.h" <<'@//E*O*F trf/src/item.h//'
/***	
 ***	item.h --
 ***	
 ***	Definitions for the item stuff for psfig.
 ***	
 ***	N.Batchelder, 3/8/86.
 ***/

/* 
 * These are the different types of items we can return.
 */

enum item {
	iEndOfInput,
	iSeparator,
	iLeftBrace,
	iRightBrace,
	iFile,
	iFigure,
	iSpace,
	iDelim,
	iDefine,
	iLiteral,
	iWidth,
	iHeight,
	iReserve,
	iClip,
	iBox,
	iLevel,
	iBounds,
	iGlobal,
	iSafe,
	iWord,
};

/* 
 * These are the characters that we recognize as special.
 */

# define Quotes		"\"'"
# define SelfDelim	"{};"
# define Sep		';'
# define LBrace		'{'
# define RBrace		'}'

enum item	getItem();

/* end of item.h */
@//E*O*F trf/src/item.h//
chmod u=r,g=r,o=r trf/src/item.h
 
echo x - trf/src/keywords.c
sed 's/^@//' > "trf/src/keywords.c" <<'@//E*O*F trf/src/keywords.c//'
/***	
 ***	keywords.c --
 ***	
 ***	Keyword recognition function for psfig.
 ***	
 ***	N.Batchelder, 3/8/86.
 ***/

# include "psfig.h"
# include "item.h"

static struct keypair {
	char		*name;
	enum item	itype;
} keytable[] = {
	"figure",	iFigure,
	"space",	iSpace,
	"delim",	iDelim,
	"define",	iDefine,
	"literal",	iLiteral,
	"file",		iFile,
	"width",	iWidth,
	"height",	iHeight,
	"reserve", 	iReserve,
	"clip",		iClip,
	"box",		iBox,
	"level",	iLevel,
	"bounds",	iBounds,
	"global",	iGlobal,
	"safe",		iSafe,
	NULL,		iEndOfInput,
};

/* 
 * isKeyword:
 * 
 * Takes the text of an item and returns whether or not it is a keyword.
 * If it is, then it fills in the exact item type in the pointed to enum item.
 */

int
isKeyword(buf, iptr)
char		*buf;
enum item	*iptr;
{
	struct keypair	*keyp;

	for (keyp = keytable; keyp->name; keyp++) {
		if (!strcmp(buf, keyp->name)) {
			/* 
			 * A match!
			 */
			
			*iptr = keyp->itype;
			return 1;
		}
	}

	return 0;
}

/* end of keywords.c */

@//E*O*F trf/src/keywords.c//
chmod u=r,g=r,o=r trf/src/keywords.c
 
echo x - trf/src/lineio.c
sed 's/^@//' > "trf/src/lineio.c" <<'@//E*O*F trf/src/lineio.c//'
/***	
 ***	lineio.c --
 ***	
 ***	Low level i/o for psfig.
 ***	Provides the capability to get and unget lines.
 ***	
 ***	N.Batchelder, 3/8/86.
 ***/

# include <stdio.h>
# include "psfig.h"

/* 
 * This is non-NULL if a line was pushed back.
 */

static char	*ungotten = NULL;

/* 
 * getline:
 * 
 * A lot like fgets, but will return the last line ungotten by ungetline if
 * there is one.
 */

char *
getline(buf, len, file)
char	*buf;
int	len;
FILE	*file;
{
	if (ungotten) {
		(void) strcpy(buf, ungotten);
		ungotten = NULL;
		return buf;
	}

	linenumber++;
	return fgets(buf, len, file);
}

/* 
 * ungetline:
 * 
 * Takes the pointer passed to it and saves it to be returned by getline next
 * time.
 */

ungetline(buf)
char	*buf;
{
	if (ungotten) {
		error("Double ungetline!");
	}
	ungotten = buf;
}

/* end of lineio.c */
@//E*O*F trf/src/lineio.c//
chmod u=r,g=r,o=r trf/src/lineio.c
 
echo x - trf/src/macros.c
sed 's/^@//' > "trf/src/macros.c" <<'@//E*O*F trf/src/macros.c//'
/***	
 ***	macros.c --
 ***	
 ***	The macro handling stuff for psfig.
 ***	
 ***	N.Batchelder, 3/8/86.
 ***/

# include "psfig.h"

# define MaxMacros	100

/* 
 * Our table of macros. The end is marked by a pointer (macp). Entries may
 * have NULL for their name (if they have been deleted).
 */

struct mac {
	char	*name;
	char	*value;
};

static struct mac	macros[MaxMacros];
static struct mac	*macp = macros;

/* 
 * isMacro:
 * 
 * Look up a name. If it is a macro, point to the value through an argument,
 * and return 1. If it isn't, return 0.
 */

int
isMacro(nm, valp)
char	*nm;
char	**valp;
{
	struct mac	*mp;

	for (mp = macros; mp < macp; mp++) {
		if (!strcmp(nm, mp->name)) {
			*valp = mp->value;
			return 1;
		}
	}

	return 0;
}

/* 
 * addMacro:
 * 
 * Takes a name and a value and enters into the table.
 */

addMacro(n, v)
char	*n;
char	*v;
{
	char		*strsave();
	struct mac	*mp;

	if (macp >= macros+MaxMacros) {
		/* 
		 * Table is full. Attempt to compact it.
		 */
		
		for (mp = macros; mp < macp; mp++) {
			if (mp->name == NULL) {
				mp->name = --macp->name;
				mp->value = macp->value;
			}
		}

		if (macp >= macros+MaxMacros) {
			error("macro table overflow");
		}
	}

	macp->name = strsave(n);
	macp->value = strsave(v);
	macp++;
}

/* 
 * deleteMacro:
 * 
 * Removes the given name from the table.
 */

deleteMacro(nm)
char	*nm;
{
	struct mac	*mp;

	for (mp = macros; mp < macp; mp++) {
		if (!strcmp(nm, mp->name)) {
			mp->name = NULL;
		}
	}
}

/* 
 * strsave:
 * 
 * Stashes its argument away somewhere and returns a new pointer to it.
 */

# define NSAVETAB	4096

static char	*savetab;
static int	saveleft;

char *
strsave(cp)
register char *cp;
{
	register int len;

	len = strlen(cp) + 1;
	if (len > saveleft) {
		saveleft = NSAVETAB;
		if (len > saveleft)
			saveleft = len;
		savetab = malloc((unsigned) saveleft);
		if (savetab == 0) {
			error("no more string memory");
		}
	}
	(void) strncpy(savetab, cp, len);
	cp = savetab;
	savetab += len;
	saveleft -= len;

	return cp;
}

/* end of macros.c */
@//E*O*F trf/src/macros.c//
chmod u=r,g=r,o=r trf/src/macros.c
 
echo x - trf/src/main.c
sed 's/^@//' > "trf/src/main.c" <<'@//E*O*F trf/src/main.c//'
/***	
 ***	main.c --
 ***	
 ***	Central stuff for the psfig preprocessor.
 ***	
 ***	T.Darrell, 3/86.
 ***/

# include "psfig.h"

int	linenumber = 0;		/* line counter */

char	delimSt = 0,
	delimEnd = 0;		/* delimeters */

char	*XFlush = NULL;		/* The character to flush \X with */

char	*searchDirTable[MAXDIRS] = { "." };
int	dircnt = 0;

char	errbuf[SMBUFSZ];	/* temp buffer for error msg formatting */

int 	psditfix = TRUE;	/* use fix that compensates for the weird
					way psdit V2 draws lines (a point
					off here and there) 
				   "-t" option turns off this flag */

int 	newbb = TRUE;		/* search for bounding box comments the
					new way.
				   "-b" option turns off this flag */

/* 
 * error:
 * 
 * Print an error message, and leave.
 */

error(s)
char	*s;
{
	fprintf(stderr,"psfig: line %d: %s\n", linenumber, s);
	exit(1);
}
warn(s)
char	*s;
{
	fprintf(stderr,"psfig: line %d: warning -- %s\n", linenumber, s);
}

/* 
 * getaline:
 * 
 * A function that we can pass to the lineio level to get more lines when
 * processing .F+ .F- commands. Claims that there is no more input when it
 * hits a .F-.
 */

char *
getaline(buf)
char	*buf;
{
	if (getline(buf, BUFSZ, stdin) == NULL)
		return NULL;

	if (!strncmp(buf, FGEND, strlen(FGEND))) {
		ungetline(buf);
		return NULL;
	}

	return buf;
}

/* 
 * main:
 * 
 * The basic loop of the program is simply to read lines from the input, and
 * if thereare psfig commands to process, to process them. There are three
 * different ways to mark psfig commands:
 * 
 * 	1. Between .F+ and .F-
 * 	2. Between delimiter characters
 * 	3. The rest of a line that starts with .F=
 */

main(argc, argv)
int	argc;
char	*argv[];
{
	char		**argp;

	/* 
	 * Process arguments.
	 */
	
	argc--;
	argp = argv+1;

	while ((*argp)[0] == '-' && argc--) {
		switch ((*argp)[1]) {

		case 'd':	/* Specify draft level to run at */
			if ((*argp)[2] == '\0') {
				Draft = 0;
				break;
			} 
			*argp += 2;
			Draft = atoi(*argp);
			break;

		case 'f':	/* Ditroff bug: must flush before \X */
			XFlush = *argp + 2;
			if (strlen(XFlush) != 2) {
				XFlush = "ts";
			}
			break;

		case 't':	/* disable compensation for psdit's line drawing */
			psditfix = FALSE;
			break;

		case 'b':	/* use old bounding box comment search method */
			newbb = FALSE;
			break;
			
		case 'D':	/* add directory to search list */
			if (++dircnt > MAXDIRS)
				error("too many search directories");

			searchDirTable[dircnt] = strsave((*argp)+2);
			break;
		default:
			error("bad flag");
		}

		argp++;
	}
	
	searchDirTable[dircnt + 1] = NULL;

	/* 
	 * If we are flushing \X with a character, make sure it doesn't get
	 * printed.
	 */

	if (XFlush != NULL) {
		printf(".tr \\(%s \n", XFlush);
	}

	/* 
	 * Now we process files. No arguments means standard input. Otherwise,
	 * we just use the named files.
	 */
	
	if (argc == 0) {
		doit();
	} else {
		while (argc-- > 0) {
			if (!freopen(*argp++, "r", stdin)) {
				perror("error opening input file");
				exit(1);
			} else {
				clearerr(stdin);
				doit();
			}
		}
	}

	exit(0);
} /* main */

doit() {
	char		*ptr, *ptr1, *rest;
	char		*getaline();
	static char	buf[BUFSZ];

	while (getline(buf, sizeof(buf), stdin) != NULL) {
		
		if (!strncmp(buf, FGST, strlen(FGST))) {
			fputs(buf, stdout);			
			*buf = 0;
			setInput(buf, getaline);
			if (dofigcmd()) {
				(void) fputc('\n', stdout);
			}
			(void) getline(buf, sizeof(buf), stdin);
			fputs(buf, stdout);
			continue;
		}

		ptr = rest = buf;
		while (delimSt && (ptr1 = index(ptr, delimSt))) {
			ptr = ptr1;

			*(ptr++) = '\0';

			fputs(rest, stdout);

			if (rest = index(ptr, delimEnd)) {
				*(rest++) = '\0';
			} else {
				error("Unbalanced delimiters");
			}

			++inLineFlag;
			setInput(ptr, (char *(*)()) NULL);
			(void) dofigcmd();
			--inLineFlag; 
			ptr = rest;

		} 
		fputs(ptr, stdout);
	}
}

/* 
 * getFileBB:
 * 
 * Takes a file name and tries to find out what its bounding box is. If it
 * finds the info, it writes it into the four floats pointed to.
 */

getFileBB(filenm, llx, lly, urx, ury)
char	*filenm;
float	*llx, *lly, *urx, *ury;
{
	char	 	*bbcomment = "%%BoundingBox:";
	char 		*atend = "(atend)";
	char	 	*trailer = "%%Trailer";
	char 		*endcom = "%%EndComments";
	FILE		*psfile;
	static char	buf[BUFSZ];
	float		tllx, tlly, turx, tury;
	int		foundbb = FALSE;
	int		intrailer = FALSE;

	if (!(psfile = fopen(filenm, "r"))) {
		(void) sprintf(errbuf, "error opening file `%s'", filenm);
		error(errbuf);
	}

	(void) fgets(buf, sizeof(buf), psfile);
	if (strncmp(buf, "%!", 2)) {
		(void) sprintf(errbuf, "`%s' not a postscript file", filenm);
		error(errbuf);
	}



	if (newbb)
	     /* 
	      * Use full search for bb comment. Pays attention to
	      * "%%EndComments", "(atend)", and "%%Trailer"
	      */
	     
	     while (fgets(buf, sizeof(buf), psfile)) {

		/* If the first n chars of buf == %%BoundingBox: */
		if (!strncmp(buf, bbcomment, strlen(bbcomment))) {
			char *args = buf;
			args += strlen(bbcomment);
			while (*args == ' ') args++;
			if (!strncmp(args, atend, strlen(atend))) {
				/* 
				 * Scan for trailer section
				 */
				intrailer = FALSE;
				while (fgets(buf, sizeof(buf), psfile)) 
				    if (!strncmp(buf, trailer, strlen(trailer))) {
					intrailer = TRUE;
					break;
				    }
				if (!intrailer) {
					sprintf(errbuf, 
					 "%%%%Trailer not found in %s", filenm);
					error(errbuf);
					break;
				}
				/* 
				 * Now that we've fouind the trailer
				 * section, we can continue seaching from 
				 * the main while.
				 */
				continue;
			} 

			if (sscanf(args,"%f %f %f %f", &tllx, &tlly, &turx, &tury)
			       == 4) {
				/* 
				 * We found a match, now if we are in the
				 * header section we can leave straight away,
				 * since the *first* bb comment is the
				 * relevant one, but if we are in the trailer
				 * we have to keep going, since the *last*
				 * instance of a bb is the one we want to use.
				 * 
				 * At least so says the Adobe book.
				 */
				if (!intrailer) {
					*llx = tllx; *lly = tlly;
					*urx = turx; *ury = tury;
					foundbb = TRUE;
					fclose(psfile);
					return;
				} else {
					*llx = tllx; *lly = tlly;
					*urx = turx; *ury = tury;
					foundbb = TRUE;
					continue;
				}

			} 
			(void) sprintf(errbuf,
				"malformed bounding box comment in %s", filenm);
			error(errbuf);

		} /* endif the first n chars match %%BoundingBox */

		/* 
		 * If the first two characters aren't %% or %!, or
		 * if we reach %%EndComments, and we're not in the
		 * trailer, then we're done.
		 */
		if (intrailer) continue;

		if ((strncmp(buf, "%%", 2) && strncmp(buf, "%!", 2))
			|| !strncmp(buf, endcom, strlen(endcom)))
			break;
	    }
	else
	    /* 
	     * Use old (simple) search for bb comment.
	     */
	    
	    while (fgets(buf, sizeof(buf), psfile)) {
		if (sscanf(buf,"%%%%BoundingBox: %f %f %f %f",
						llx, lly, urx, ury) == 4) {
			fclose(psfile);
			return;
		}
	    }

	if (foundbb) {
		fclose(psfile);
		return;
	}

	(void) sprintf(errbuf,"no bounding box found in %s",filenm);
	error(errbuf);
}

/* 
 * abspath:
 * 
 * Return the absolute expansion of a path, searching all the relevant
 * directories.
 */

char *
abspath(path)
char	*path;
{
	static char	pathbuf[SMBUFSZ];
	static char	absbuf[SMBUFSZ];
	char		**searchDir = searchDirTable;

	if (!*path) return path;

	if (*path != '/') {
		/* 
		 * path is relative, so attempt to locate file
		 * in one of the search directories
		 */
		
		do {
			strcpy(pathbuf, *searchDir);
			strcat(pathbuf, "/");
			strcat(pathbuf, path);

			if (access(pathbuf, F_OK) >= 0) {
				/* 
				 * Now that we know where the file is, we make
				 * it absolute if necessary, and return it.
				 */

				if (*pathbuf != '/') {
					getwd(absbuf);
					strcat(absbuf, "/");
					strcat(absbuf, pathbuf);
					return absbuf;
				} else {
					return pathbuf;
				}
			}
		} while (*(++searchDir));

		/* 
		 * the file was not found in any of the 
		 * search directories, so notify the user
		 * and return the bare path
		 */

		sprintf(errbuf, "`%s' not found in search directories", path);
		warn(errbuf);
	}

	return path;
}

/* end of main.c */
@//E*O*F trf/src/main.c//
chmod u=r,g=r,o=r trf/src/main.c
 
echo x - trf/src/psfig.h
sed 's/^@//' > "trf/src/psfig.h" <<'@//E*O*F trf/src/psfig.h//'
/***	
 ***	psfig.h --
 ***	
 ***	Standard defines for psfig.
 ***	
 ***	T.Darrell, 3/86.
 ***/

# include <stdio.h>
# include <sys/file.h>

# define TRUE	1
# define FALSE	0

# define UP	1
# define DOWN	2

# define MAXDIRS	12	/* max no. of search dirs */

# define BUFSZ		1024
# define SMBUFSZ	128

# define FGST	".F+"
# define FGEND	".F-"

# define DraftDefault	100
# define BrokenOutLevel	10
# define InLineLevel	1

# define endof(s) (s + strlen(s))

extern		Draft, linenumber, inLineFlag;
extern		psditfix;
extern char	delimSt, delimEnd;
extern char	*XFlush;

extern char	*Strcat(),
		*Strcpy(),
		*getline(),
		*includeFig(),
		*getLab(),
		*abspath(),
		*strsave();

extern char	*malloc(),
		*free(),
		*strcat(),
		*strcpy(),
		*strncpy(),
		*index();

extern void	pushBackItem(),
		setInput();

/* end of psfig.h */
@//E*O*F trf/src/psfig.h//
chmod u=r,g=r,o=r trf/src/psfig.h
 
echo x - trf/src/troff.c
sed 's/^@//' > "trf/src/troff.c" <<'@//E*O*F trf/src/troff.c//'
/***	
 ***	troff.c --
 ***	
 ***	Functions to output perverted Troff command strings for psfig.
 ***	
 ***	T.Darrell, 3/86.
 ***/

# include "psfig.h"

/* 
 * Ditroff output format strings.
 */

char saveSpaceDown_s[] = "\\x'%s'";
char saveSpaceUp_s[] = "\\x'-(%s-\\n(.vu)'";

saveSpaceV(y, sign)
char	*y;
int	sign;
{
	if (sign == UP)
		printf(saveSpaceUp_s, y);
	else
		printf(saveSpaceDown_s, y);
}


char incl_file_s[] = "\\X'f%s'";
includeFile(filenm)
char *filenm; {
	printf(incl_file_s, filenm);
}

sIncludeFile(outbuf, filenm, len)
int len;
char *filenm; {
	sprintf(endof(outbuf), incl_file_s, filenm);
	if (strlen(outbuf) > len)
		error("buffer overflow");
}

char endfig_s[] = "\\X'pendFig'";
endfig() {
	printf(endfig_s);
}

char startfig_s[] =
"\\X'p\\w@\\h@%s@@'\\X'p\\w@\\h@%s@@'\\X'p%.2f'\\X'p%.2f'\\X'p%.2f'\\X'p%.2f'\\X'pstartFig'";

startfig(x, y, llx, lly, urx, ury)
char	*x, *y;
float	llx, lly, urx, ury;
{
	flushX();
	printf(startfig_s, x, y, llx, lly, urx, ury);
}

emitDoClip() {
	printf("\\X'pdoclip'");
}

flushX()
{
	if (XFlush != NULL) {
		printf("\\z\\(%s", XFlush);
	}
}

char makeBoxDn_s_psditfix[] = 
"\\L'%s\\(br'\\v'+1p'\\l'%s\\(ul'\\v'-1p'\\L'-%s\\(br'\\v'+1p'\\l'-%s\\(ul'\\x'%s'\\v'-1p'";
char makeBoxDn_s[] = 
"\\L'%s\\(br'\\l'%s\\(ul'\\L'-%s\\(br'\\l'-%s\\(ul'\\x'%s'";

makeBox(x, y)
char	*x, *y; 
{
	if (psditfix)
		printf(makeBoxDn_s_psditfix, y, x, y, x, y);
	else
		printf(makeBoxDn_s, y, x, y, x, y);

}

char moveDown_s[] = "\\v'%s'";

moveDown(y)
char	*y;
{
	printf(moveDown_s, y);
}

char moveUp_s[] = "\\v'-%s'";

moveUp(y)
char	*y;
{
	printf(moveUp_s, y);
}

char draftNote_s[] = "\\v'1m'\\h'1n'%s\\h'-\\w'%s'u'\\h'-1n'\\v'-1m'";

draftNote(filenm)
char	*filenm;
{
	printf(draftNote_s, filenm, filenm);
}

char saveSpaceH_s[] = "\\h'%s'";

saveSpaceH(x)
char	*x;
{
	printf(saveSpaceH_s, x);
}

#define isWhite(ch) ((ch) == ' ' || (ch) == '\t' || (ch) == '\n')

char literal_s[] = "\\X'p%s'";
emitLiteral(text)
char *text; {
	static char litbuf[BUFSZ];

	*litbuf = 0;
	sEmitLiteral(litbuf, text, sizeof(litbuf));
	fputs(litbuf, stdout);
}

sEmitLiteral(outbuf, text, len)
int len;
char *outbuf, *text; {
	char *ptr = text;

	/* 
	 * print each word of the literal (stuff separated by whitespace)
	 * in a separate \X'p'
	 */
	
	while (isWhite(*ptr)) ++ptr;

	/* 
	 * find delimiting character
	 */
	
	while (*ptr) {

		/* 
		 * skip to next delimiting char.
		 */
		
		for (text = ptr; *ptr && !isWhite(*ptr); ++ptr) ;

		/* 
		 * termainate string here, print this piece, 
		 * then repeat on the rest of the string.
		 */

		if (!*ptr) {
			sprintf(endof(outbuf), literal_s, text);
			break;
		}
	
		*(ptr++) = 0;
		sprintf(endof(outbuf), literal_s, text);
	
		while (isWhite(*ptr)) ++ptr;
	}

	if (strlen(outbuf) > len) 
		error("buffer overflow");
}
	

/* end of troff.c */
@//E*O*F trf/src/troff.c//
chmod u=r,g=r,o=r trf/src/troff.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      53     256    1687 README
      23      69     421 Makefile
     442     985    7906 cmds.c
     299     876    4917 item.c
      48      95     615 item.h
      63     150     992 keywords.c
      58     144     832 lineio.c
     138     352    2043 macros.c
     409    1239    8192 main.c
      55     121     822 psfig.h
     174     352    2825 troff.c
    1762    4639   31252 total
!!!
wc  trf/README trf/src/Makefile trf/src/cmds.c trf/src/item.c trf/src/item.h trf/src/keywords.c trf/src/lineio.c trf/src/macros.c trf/src/main.c trf/src/psfig.h trf/src/troff.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0