[net.sources] lwf - filter to convert text to PostScript

brachman@ubc-cs.UUCP (Barry Brachman) (04/30/86)

Here's a program that converts ASCII text files to PostScript for
printing on an Apple LaserWriter.

See the README file for further info.

Enjoy.

Barry Brachman - University of British Columbia

----- CUT HERE ----- CUT HERE ----- CUT HERE ----- CUT HERE ----- CUT HERE
: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
echo 'Extracting README'
sed 's/^X//' > README << '+ END-OF-FILE README'
X
XHere's a program that reads ASCII text files and produces PostScript output
Xsuitable for printing on an Apple LaserWriter.
XThe program has been in use here for several months on both Vaxen and SUNs
Xrunning 4.2.
X
XDisclaimer section {
X
XThere are some 4.2 dependencies in the code, but (hopefully) not too many.
XThe code is not bulletproof and I've taken some shortcuts such as emitting
XPostScript from various places.
XAlso, since I didn't want to fool around with PostScript anymore than was
Xnecessary, lwf produces more output than is necessary.
XIf more of the processing were done on the LaserWriter then the volume
Xcould be reduced significantly.
XStill, I wanted to keep it simple and I've found lwf to run without problem.
X
X}
X
XThere are five files:
X
X	README		This file
X	lwf.c		LaserWriter filter to convert ASCII text to PostScript
X	range.c		Routine to check if a number is in a given range
X	ranget.c	A program to test out range.c
X	lwf.1		Manual page for lwf
X
XTo compile:
X	cc -o lwf lwf.c range.c
X	cc -o ranget ranget.c range.c
X
XI used the ranget program for debugging the inrange and checkrange
Xfunctions; you may want to use it to see how ranges work.
X
XI hope you find lwf to be useful.
XIf you come across any bugs please let me know.
X
XP.S.
XI've modified dvi2ps to use range.c so that more general page ranges may
Xbe specified.  The changes to dvi2ps are rather small and I don't think
Xyou'll have any trouble making them.
X
X-----
XBarry Brachman
XDept. of Computer Science
XUniv. of British Columbia
XVancouver, B.C. V6T 1W5
X
X.. {ihnp4!alberta, uw-beaver}!ubc-vision!ubc-cs!brachman
Xbrachman@cs.ubc.cdn
Xbrachman%ubc.csnet@csnet-relay.arpa
Xbrachman@ubc.csnet
+ END-OF-FILE README
chmod 'u=rw,g=r,o=r' 'README'
echo '	-rw-r--r--  1 brachman     1659 Apr 30 12:24 README        (as sent)'
echo -n '	'
/bin/ls -l README
echo 'Extracting lwf.1'
sed 's/^X//' > lwf.1 << '+ END-OF-FILE lwf.1'
X.TH LWF 1-LOCAL "16 January 1986"
X.UC
X.SH NAME
Xlwf \- LaserWriter filter
X.SH SYNOPSIS
X.B lwf
X[-d] [-h] [-i#] [-l] [-olist] [-p] [-s#] [-v] [file ...]
X.SH DESCRIPTION
X.I Lwf
Xtakes one or more ASCII text files as input and produces PostScript
Xinstructions that may be sent to a LaserWriter (see
X.B lpr(1))
Xto print the files.
XIf no files are given on the command line, the standard input is used.
XThe page order is automatically reversed so that the pages appear in
Xthe correct sequence in the output tray of the LaserWriter.
X.sp 2
XNormally a header page is printed to separate jobs; the
X.B -h
Xflag prevents this.
XThe
X.B -l
Xflag indicates that landscape format is to be used instead of the default
Xportrait format.
XLines may be shifted to the right using the
X.B -i
Xflag.
XThis flag is immediately followed by the distance in inches to shift
Xall text from the left margin instead of the default amount (about 1/3 inch);
Xthe resolution is approximately 1/72 of an inch.
XThe
X.B -p
Xflag indicates that
X.B pr(1)
Xis to be used to perform pagination and print page headers.
XThe
X.B -d
Xand
X.B -v
Xflags are used for debugging.
X.sp 2
XThe default point size is 10.
XThe
X.B -s
Xflag, immediately followed by a 7, 8, 9, 10, 11, or 12 causes the corresponding
Xpoint size to be used.
XCourier font is used.
XThe program correctly handles the form feed character and tabs.
XThe program also understands backspacing; backspaces followed by underscores
Xmay be used to underline.
X.PP
XThe
X.B -o
Xflag is immediately followed by a range specification;
Xonly pages whose page numbers appear in the comma-separated list of numbers
Xand ranges will be printed.
XA range N:M means pages N through M inclusive; an initial :N means from
Xthe beginning to page N; and a final N: means from N to the end.
XThe default, ``:'', is to print all pages.
X.SH NOTE
XWhen using
X.B lwf
Xwith
X.B nroff(1) or pr(1)
Xit may be necessary to know the number of lines per page:
X.br
X.in +5
X.sp 2
X.nf
XPoint Size	Portrait			Landscape
X			Lines/Columns		Lines/Columns
X
X    7		 108/135		   	80/181
X    8		  94/118		   	70/159
X    9		  84/105		   	62/141
X   10		  75/94		   	56/127
X   11		  68/86		   	51/115
X   12		  62/79		   	46/106
X.in -5
X.fi
X.SH EXAMPLE
XThe command
X.sp 2
X.ti +5
Xlwf -p -l -s12 -i1.5 file1.c file2.c | lpr -Plw
X.sp 2
Xwould print the two files on the LaserWriter in landscape format with
Xpage headings, indenting 1.5 inches, and in 12 point type.
X.sp 1
XThe command
X.sp 2
X.ti +5
Xpr -w162 -2 -l81 foo.c | lwf -i0.2 -h -l -s7 | lpr -Plw
X.sp 2
Xwould print foo.c in two column form with page headings, indenting 0.2 inches
Xin landscape format using 7 point type.
XNo header page would be printed.
X.SH SEE ALSO
Xpr(1), lpr(1), nroff(1)
X.SH BUGS
XIf the specified indent is too small for the font size being used
Xthe first few characters on a line may be lost.
XLines that are too long are silently truncated.
XThe program can only handle the 6 different point sizes and the single font.
XThe volume of output is much greater than the volume of input.
X.SH AUTHOR
XBarry Brachman
X.br
XThe 'only' page idea is borrowed from nroff.
+ END-OF-FILE lwf.1
chmod 'u=rw,g=r,o=r' 'lwf.1'
echo '	-rw-r--r--  1 brachman     3065 Apr 30 10:55 lwf.1        (as sent)'
echo -n '	'
/bin/ls -l lwf.1
echo 'Extracting lwf.c'
sed 's/^X//' > lwf.c << '+ END-OF-FILE lwf.c'
X
X/* 456789a123456789b123456789c123456789d123456789e123456789f123456789g1234567*/
X
X/*
X * lwf - print a text file on the LaserWriter
X *
X * Usage: lwf [-d] [-h] [-i#] [-l] [-olist] [-p] [-s#] [-v] [file ...]
X *
X * Options:
X *	-d		Debug mode
X *	-h		Don't print header page
X *	-i#		Indent each line
X *	-l		Landscape instead of PORTRAIT
X *	-olist		Only print pages in the specified range
X *	-p		Use pr to print
X *	-s#		Use point size #
X *	-v		Verbose
X *
X * If no files are specified, stdin is used.
X * Page reversal is performed.
X * Form feeds should work
X * Backspacing with underlining (or overprinting) should work as expected
X *
X * Problems:
X *	- Should have:
X *		[-#n] option, where n=number of copies of each file or stdin
X *		[-T filename] option, where filename is used for title instead
X *		of a blank name
X *	- User can't give pr header if stdin is used
X *	- The volumne of PostScript output produced by this program could be
X *	  reduced somewhat (there may also be do-nothing sequences produced)
X *
X * BJB - Nov/85
X * ========================================================================
X *
X * Copyright (c) Barry Brachman
X *
X * Permission is given to freely copy or distribute this program
X * with the following restrictions:
X *
X *	1) The author is not responsible for the consequences of use of
X *		this software and is not responsible for correcting any defects
X *	2) The origin of this software must not be misrepresented, either by
X *		explicit claim or by omission
X *	3) You may not sell this program
X *	4) Altered versions must be plainly marked as such, and must not
X *		be misrepresented as being the original software
X *
X *
X * Barry Brachman
X * Dept. of Computer Science
X * Univ. of British Columbia
X * Vancouver, B.C. V6T 1W5
X *
X * .. {ihnp4!alberta, uw-beaver}!ubc-vision!ubc-cs!brachman
X * brachman@cs.ubc.cdn
X * brachman%ubc.csnet@csnet-relay.arpa
X * brachman@ubc.csnet
X * ========================================================================
X */
X
X#include <sys/file.h>
X#include <pwd.h>
X#include <stdio.h>
X
X#define PRCMD			"/bin/pr"	/* where pr is */
X
X#define MAXPAGES		10000	/* maximum number of pages */
X#define PORTRAIT_START_Y	768	/* first row (Y coord) on each page */
X#define LANDSCAPE_START_Y	576
X#define START_X			25	/* position of start of each line */
X#define START_Y_HEADER		600	/* first row (Y coord) of header */
X
X#define MAX_X			612
X#define MAX_Y			792
X
X#define PS_INTR			03
X#define PS_EOF			04
X
X#define NPSIZES			6
Xstruct psize {
X	int size;
X	int portrait_page_length;
X	int landscape_page_length;
X} psize[NPSIZES] = {
X	 7, 108, 80,
X	 8,  94, 70,
X	 9,  84, 62,
X	10,  75, 56,
X	11,  68, 51,
X	12,  62, 46
X};
X
X#define USAGE		"[-d] [-h] [-i#] [-l] [-olist] [-p] [-s#] [-v] [file ...]"
X
Xlong page_map[MAXPAGES];		/* offset of first byte of each page */
Xint page_count;
X
Xint lines_per_page;
Xint point_size;
Xint start_x, start_y;
X
Xchar bufin[BUFSIZ];
Xchar bufout[10240];			/* should be plenty big enough */
Xchar bigbuf[32768];			/* max page size */
X
Xint currentp, row;
Xchar *range;
X
Xint dflag, hflag, lflag, pflag, vflag;
X
Xchar *fgetline();
X
Xchar *progname;
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	register int i, j, first_file;
X	char *pc;
X	struct psize *p, *get_psize();
X	double offset, atof();
X	char *rindex();
X	FILE *infile, *popen();
X
X	if ((pc = rindex(argv[0], '/')) != NULL)
X		progname = pc + 1;
X	else
X		progname = argv[0];
X	range = ":";
X	hflag = 1;
X	start_x = START_X;
X	p = get_psize(10);
X	for (i = 1; i < argc && argv[i][0] == '-'; i++) {
X		switch (argv[i][1]) {
X		case 'd':
X			dflag = 1;
X			break;
X		case 'h':
X			hflag = 0;
X			break;
X		case 'i':
X			offset = atof(&argv[i][2]);
X			if (offset < 0.0 || offset >= 8.5) {
X				fprintf(stderr, "%s: bad indent\n", progname);
X				exit(1);
X			}
X			start_x = (int) (offset * 72);
X			break;
X		case 'l':
X			lflag = 1;
X			break;
X		case 'o':
X			range = &argv[i][2];
X			if (checkrange(range)) {
X				fprintf(stderr, "%s: bad range specification\n",
X								progname);
X				exit(1);
X			}
X			break;
X		case 'p':
X			pflag = 1;
X			break;
X		case 's':
X			j = atoi(&argv[i][2]);
X			if ((p = get_psize(j)) == (struct psize *) NULL) {
X				fprintf(stderr, "%s: bad point size\n", progname);
X				exit(1);
X			}
X			break;
X		case 'v':
X			vflag = 1;
X			break;
X		default:
X			fprintf(stderr, "Usage: %s %s\n", progname, USAGE);
X			exit(1);
X		}
X	}
X
X	for (j = i; j < argc; j++) {
X		if (access(argv[j], R_OK) == -1) {
X			fprintf(stderr, "%s: cannot access %s\n", progname,
X								argv[j]);
X			exit(1);
X		}
X	}
X
X	if (lflag) {
X		start_y = LANDSCAPE_START_Y;
X		lines_per_page = p->landscape_page_length;
X	}
X	else {
X		start_y = PORTRAIT_START_Y;
X		lines_per_page = p->portrait_page_length;
X	}
X	point_size = p->size;
X
X	preamble();
X
X	first_file = i;
X	if (i == argc) {	/* no files on command line */
X		infile = stdin;
X		if (pflag) {
X			build_prcmd(bufin, "");
X			if ((infile = popen(bufin, "r")) == (FILE *) NULL) {
X				fprintf(stderr, "%s: can't popen\n", progname);
X				exit(1);
X			}
X		}
X		if (vflag)
X			fprintf(stderr, "printing stdin\n");
X		print(infile);
X		if (pflag)
X			pclose(infile);
X	}
X
X	for (j = argc - 1; i < argc; j--) {
X		infile = stdin;
X		if (pflag) {
X			build_prcmd(bufin, argv[j]);
X			if ((infile = popen(bufin, "r")) == (FILE *) NULL) {
X				fprintf(stderr, "%s: can't popen\n", progname);
X				exit(1);
X			}
X		}
X		else {
X			if (freopen(argv[j], "r", stdin) == NULL) {
X				fprintf(stderr, "%s: can't open %s\n",
X							progname, argv[j]);
X				exit(1);
X			}
X		}
X		if (vflag)
X			fprintf(stderr, "printing %s\n", argv[j]);
X		print(infile);
X		if (pflag)
X			pclose(infile);
X		i++;
X	}
X
X	if (hflag)
X		header(argc - first_file, argv + first_file);
X	printf("grestore initgraphics %c\n", PS_EOF);
X}
X
X/*
X * Return a pointer to the point size structure for the
X * specified point size
X */
Xstruct psize *
Xget_psize(size)
Xint size;
X{
X	register int i;
X
X	for (i = 0; i < NPSIZES; i++)
X		if (psize[i].size == size)
X			break;
X	if (i == NPSIZES)
X		return((struct psize *) NULL);
X	return(&psize[i]);
X}
X
X/*
X * Initial lines sent to the LaserWriter
X */
Xpreamble()
X{
X
X	printf("%c initgraphics gsave\n", PS_EOF);
X	printf("/Courier findfont %d scalefont\nsetfont\n", point_size);
X	printf("/bkup { stringwidth pop neg 0 rmoveto } def\n");
X	printf("/tab1 {( ) show} def\n");
X	printf("/tab2 {(  ) show} def\n");
X	printf("/tab3 {(   ) show} def\n");
X	printf("/tab4 {(    ) show} def\n");
X	printf("/tab5 {(     ) show} def\n");
X	printf("/tab6 {(      ) show} def\n");
X	printf("/tab7 {(       ) show} def\n");
X	printf("/tab8 {(        ) show} def\n");
X	if (lflag)
X		printf("612 0 translate\n90 rotate\n");
X	fflush(stdout);
X}
X
X/*
X * Generate a command, in the specified buffer, to print the given file
X * according to the options in effect
X */
Xbuild_prcmd(buf, file)
Xchar *buf, *file;
X{
X	sprintf(buf, "%s -l%d %s", PRCMD, lines_per_page, file);
X	if (vflag)
X		fprintf(stderr, "pr cmd: %s\n", buf);
X}
X
X/*
X * Print a file
X *
X * The input stream may be stdin, a file, or a pipe
X * The output goes to a temporary file, and then reverse is called
X * to do the page reversal to stdout
X */
Xprint(infile)
XFILE *infile;
X{
X	register int eof, pagenum, r;
X	register char *p;
X	FILE *outfile;
X	char *gets(), *mktemp();
X
X	page_count = 0;
X	page_map[0] = 0L;
X	sprintf(bufin, "/tmp/%sXXXXXX", progname);
X	if (vflag)
X		fprintf(stderr, "temp will be: %s ... ", bufin);
X	p = mktemp(bufin);
X	if (vflag)
X		fprintf(stderr, "%s\n", p);
X	if ((outfile = fopen(p, "w+")) == (FILE *) NULL) {
X		fprintf(stderr, "%s: can't create %s\n", progname, p);
X		cleanup();
X		/*NOTREACHED*/
X	}
X	if (!dflag)
X		unlink(p);
X	else
X		fprintf(stderr, "will not unlink %s\n", p);
X	pagenum = 1;
X	eof = 0;
X	while (!eof) {
X		row = start_y;
X		if ((r = inrange(pagenum, range)) == -1) {
X			cleanup();
X			/*NOTREACHED*/
X		}
X		else if (r == 1)
X			eof = printpage(infile, outfile);
X		else if (r == 0)
X			eof = flushpage(infile);
X		else {
X			fprintf(stderr, "%s: bad inrange result\n", progname);
X			cleanup();
X			/*NOTREACHED*/
X		}
X		pagenum++;
X	}
X	if (row != start_y)
X		endpage(outfile);
X	fflush(outfile);
X	reverse(outfile);
X	fclose(outfile);
X}
X
X/*
X * Process the next page
X */
Xprintpage(infile, outfile)
XFILE *infile, *outfile;
X{
X	register int lineno;
X
X	for (lineno = 0; lineno < lines_per_page; lineno++) {
X		if (fgetline(bufin, sizeof(bufin), infile) == (char *) NULL)
X			return(1);
X		if (bufin[0] == '\f')
X			break;
X		if (bufin[0] != '\n') {
X			fprintf(outfile, "%d %d moveto\n", start_x, row);
X			proc(bufin, outfile);
X			fprintf(outfile, "(%s) show\n", bufout);
X		}
X		row -= point_size;
X	}
X	endpage(outfile);
X	return(0);
X}
X
X/*
X * The next page will not be printed; just consume the input and discard
X * Don't change xrow since we don't want an endpage()
X */
Xflushpage(infile)
XFILE *infile;
X{
X	register int lineno, xrow;
X
X	xrow = row;
X	for (lineno = 0; lineno < lines_per_page; lineno++) {
X		if (fgetline(bufin, sizeof(bufin), infile) == (char *) NULL)
X			return(1);
X		if (bufin[0] == '\f')
X			break;
X		xrow -= point_size;
X	}
X	return(0);
X}
X
X/*
X * A page has been written to the temp file
X * Record the start of the next page
X */
Xendpage(outfile)
XFILE *outfile;
X{
X	long ftell();
X
X	if (page_count == MAXPAGES) {
X		fprintf(stderr, "%s: pagelimit (%d) reached\n",
X						progname, MAXPAGES);
X		cleanup();
X		/*NOTREACHED*/
X	}
X	fprintf(outfile, "copypage erasepage\n");
X	fflush(outfile);
X	page_map[++page_count] = ftell(outfile);
X	if (vflag)
X		fprintf(stderr, "x");
X}
X
X/*
X * Print the pages to stdout in reverse order
X */
Xreverse(outfile)
XFILE *outfile;
X{
X	register int fd, i, nbytes;
X
X	if (vflag)
X		fprintf(stderr, "\nreversing %d page%s\n", page_count,
X						page_count > 1 ? "s" : "");
X	fd = fileno(outfile);
X	if (dflag) {
X		for (i = 0; i <= page_count; i++)
X			fprintf(stderr, "[%ld]\n", page_map[i]);
X	}
X	for (i = page_count - 1; i >= 0; i--) {
X		if (lseek(fd, page_map[i], 0) == -1) {
X			fprintf(stderr, "%s: seek error\n", progname);
X			cleanup();
X			/*NOTREACHED*/
X		}
X		nbytes = page_map[i + 1] - page_map[i];
X		if (nbytes > sizeof(bigbuf)) {
X			fprintf(stderr, "%s: page too big\n", progname);
X			fprintf(stderr, "nbytes = %d\n", nbytes);
X			cleanup();
X			/*NOTREACHED*/
X		}
X		nbytes = read(fd, bigbuf, nbytes);
X		write(1, bigbuf, nbytes);
X	}
X}
X
X/*
X * Process a line, escaping characters when necessary and handling tabs
X * We are assuming that a line ends with a newline (fgets/fgetline)
X *
X * NOTE: lazy attempt at checking for buffer overflow on bufout
X */
Xproc(in, outfile)
Xchar *in;
XFILE *outfile;
X{
X	register int i;
X	register char *last, *p, *q, *savep;
X
X	currentp = 0;
X	last = bufout + sizeof(bufout);
X	for (p = in, q = bufout; *p != '\n' && *p != '\0' && q < last; p++) {
X		switch (*p) {
X		case '\r':
X			*q++ = '\\';
X			*q++ = 'r';
X			currentp = 0;
X			break;
X		case '\t':
X/*
X			*q++ = '\\';
X			*q++ = 't';
X*/
X			i = 8 - (currentp % 8);
X			sprintf(q, ") show tab%d (", i);
X/*			sprintf(q, ") show (%*s) show (", i, ""); */
X			q += strlen(q);
X			currentp += i;
X			break;
X		case '\b':
X/*
X			*q++ = '\\';
X			*q++ = 'b';
X*/
X			savep = p++;
X			for (i = 1; *p == '\b'; p++)
X				i++;
X			*q = '\0';
X			fprintf(outfile, "(%s) show\n", bufout);
X			fprintf(outfile, "(%.*s) bkup ", i, savep - i);
X/*
X			fprintf(outfile, "(%.*s) stringwidth ", i, savep - i);
X			fprintf(outfile, "pop neg 0 rmoveto\n");
X*/
X			currentp -= i;
X			q = bufout;
X			p--;
X			break;
X		case '\f':
X/*
X			*q++ = '\\';
X			*q++ = 'f';
X*/
X			*q = '\0';
X			fprintf(outfile, "(%s) show\n", bufout);
X			endpage(outfile);
X			row = start_y;
X			fprintf(outfile, "%d %d moveto\n", start_x, row);
X			q = bufout;
X			currentp = 0;
X			break;
X		case '\\':
X			*q++ = '\\';
X			*q++ = '\\';
X			currentp++;
X			break;
X		case '(':
X			*q++ = '\\';
X			*q++ = '(';
X			currentp++;
X			break;
X		case ')':
X			*q++ = '\\';
X			*q++ = ')';
X			currentp++;
X			break;
X		default:
X			*q++ = *p;
X			currentp++;
X			break;
X		}
X	}
X	*q = '\0';
X	if (q >= last) {
X		fprintf(stderr, "%s: bufout overflow\n", progname);
X		cleanup();
X		/*NOTREACHED*/
X	}
X}
X
X/*
X * Print a header page
X */
Xheader(nfiles, files)
Xint nfiles;
Xchar **files;
X{
X	register int i;
X	register char *p;
X	long t, time();
X	char *ctime(), *gethostname(), *getlogin();
X	struct passwd *pw, *getpwuid();
X
X	if (vflag) {
X		fprintf(stderr, "printing header\n");
X		fprintf(stderr, "%d file%s are:\n", nfiles,
X							nfiles > 1 ? "s" : "");
X		if (nfiles == 0)
X			fprintf(stderr, "\tstdin\n");
X		for (i = 0; i < nfiles; i++)
X			fprintf(stderr, "\t%s\n", files[i]);
X	}
X	printf("initgraphics\n");
X	printf("/Times-Roman findfont 18 scalefont\nsetfont\n");
X	row = START_Y_HEADER;
X	printf("newpath %d %d moveto\n", START_X, row);
X	printf("%d %d lineto\n", START_X + 400, row);
X	row -= 6;
X	printf("%d %d moveto\n", START_X, row);
X	printf("%d %d lineto\n", START_X + 400, row);
X	row -= 24;
X	printf("2 setlinewidth\n");
X	printf("stroke\n");
X
X	if ((p = getlogin()) == (char *) NULL) {
X		if ((pw = getpwuid(getuid())) == NULL)
X			p = "Whoknows";
X		else
X			p = pw->pw_name;
X		endpwent();
X	}
X	printf("%d %d moveto\n", START_X, row);
X	printf("(User: %s) show\n", p);
X	row -= 24;
X	gethostname(bufin, sizeof(bufin));
X	printf("%d %d moveto\n", START_X, row);
X	printf("(Host: %s) show\n", bufin);
X	row -= 24;
X	t = time(0);
X	p = ctime(&t);
X	p[strlen(p) - 1] = '\0';
X	printf("%d %d moveto\n", START_X, row);
X	printf("(Date: %s) show\n", p);
X	row -= 24;
X
X	if (nfiles == 0) {
X		printf("%d %d moveto\n", START_X, row);
X		printf("(File: stdin) show\n", bufout);
X	}
X	else {
X		printf("%d %d moveto\n", START_X, row);
X		for (i = 0, p = bufin; i < nfiles; i++) {
X			sprintf(p, "%s%s", files[i],
X					   i != nfiles - 1 ? ", " : "");
X			p += strlen(p);
X		}
X		proc(bufin, stdout);
X		printf("(File%s: %s) show\n", nfiles > 1 ? "s" : "", bufout);
X	}
X
X	row -= 12;
X	printf("newpath %d %d moveto\n", START_X, row);
X	printf("%d %d lineto\n", START_X + 400, row);
X	row -= 6;
X	printf("%d %d moveto\n", START_X, row);
X	printf("%d %d lineto\n", START_X + 400, row);
X	printf("stroke\n");
X	printf("showpage\n");
X}
X
X/*
X * Special version of fgets
X * Read until a formfeed or a newline
X * If a formfeed is the first character, return it immediately
X * If a formfeed is found after the first character, replace it by a newline
X * and push the formfeed back onto the input stream
X * A special case is a formfeed followed by a newline in which case the
X * newline is ignored 
X */
Xchar *
Xfgetline(s, n, iop)
Xchar *s;
Xint n;
Xregister FILE *iop;
X{
X	register int ch;
X	register char *cs;
X
X	cs = s;
X	if (--n > 0) {
X		if ((ch = getc(iop)) == EOF)
X			return(NULL);
X		if (ch == '\f') {
X			if ((ch = getc(iop)) != '\n')
X				if (ungetc(ch, iop) == EOF)
X					fprintf(stderr, "fgetline - ungetc\n");
X			strncpy(cs, "\f\n", n);
X			return(s);
X		}
X	}
X	while (n-- > 0) {
X		if (ch == '\f')
X			break;
X		*cs++ = ch;
X		if (ch == '\n')
X			break;
X		if ((ch = getc(iop)) == EOF)
X			break;
X	}
X
X	if (ch == EOF && cs == s)
X		return(NULL);
X	if (ch == '\f') {
X		*cs++ = '\n';
X		if (ungetc(ch, iop) == EOF)
X			fprintf(stderr, "fgetline can't ungetc\n");
X	}
X	*cs++ = '\0';
X	return(s);
X}
X
X/*
X * Clean up and exit after an error
X */
Xcleanup()
X{
X
X	exit(1);
X}
+ END-OF-FILE lwf.c
chmod 'u=rw,g=r,o=r' 'lwf.c'
echo '	-rw-r--r--  1 brachman    14940 Apr 30 11:05 lwf.c        (as sent)'
echo -n '	'
/bin/ls -l lwf.c
echo 'Extracting range.c'
sed 's/^X//' > range.c << '+ END-OF-FILE range.c'
X
X#include <ctype.h>
X#include <stdio.h>
X
X/*
X * Return 1 if the given number is in the specified range,
X * -1 if there is an error in the range specification,
X * 0 otherwise
X *
X * Ranges have a format *similar* to that used by [nt]roff; i.e.,
X * a comma separated list of specifiers of the form:
X *	1) n	means     x such that x = n
X *	2) :n	means all x such that x <= n
X *	3) n:	means all x such that x >= n
X *	4) n:m	means all x such that n <= x <= m
X *	5) :	means all x
X * n is an integer
X *
X * Problems:
X * The routine prints an error message if the range is strange - this might
X * not always be desirable.
X *
X * I've left the error checking in inrange; it is redundant (I hope!)
X * Nov/85 BJB
X * ========================================================================
X *
X * Copyright (c) Barry Brachman
X *
X * Permission is given to freely copy or distribute this program
X * with the following restrictions:
X *
X *	1) The author is not responsible for the consequences of use of
X *		this software and is not responsible for correcting any defects
X *	2) The origin of this software must not be misrepresented, either by
X *		explicit claim or by omission
X *	3) You may not sell this program
X *	4) Altered versions must be plainly marked as such, and must not
X *		be misrepresented as being the original software
X *
X *
X * Barry Brachman
X * Dept. of Computer Science
X * Univ. of British Columbia
X * Vancouver, B.C. V6T 1W5
X *
X * .. {ihnp4!alberta, uw-beaver}!ubc-vision!ubc-cs!brachman
X * brachman@cs.ubc.cdn
X * brachman%ubc.csnet@csnet-relay.arpa
X * brachman@ubc.csnet
X * ========================================================================
X */
X
X#define SEP_CHAR	':'
X#define BAD_CHAR	'?'
X
Xinrange(num, range)
Xint num;
Xchar *range;
X{
X	char *p;
X	int type1, type2, val1, val2;
X
X	if (checkrange(range))
X		return(-1);
X	type1 = type2 = 0;
X	p = range;
X	while (1) {
X		val1 = getnum(&p, &type1);
X		if (type1 == BAD_CHAR) {
X			fprintf(stderr, "Bad first number in range\n");
X			return(-1);
X		}
X
X		switch (*p) {
X		case ',':
X			if (type1 == SEP_CHAR)	/* just a colon */
X				return(1);
X			if (num == val1)	/* plain number */
X				return(1);
X			p++;
X			continue;
X		case ':':
X			p++;
X			if (*p == ',' || *p == '\0') {		/* no rhs */
X				if (type1 == SEP_CHAR)
X					return(1);
X				if (num >= val1)
X					return(1);
X				if (*p == '\0') 
X					return(0);
X				p++;
X				break;
X			}
X
X			val2 = getnum(&p, &type2);
X			if (type2 == BAD_CHAR) {
X				fprintf(stderr, "Bad second number in range\n");
X				return(-1);
X			}
X
X			if (val1 > val2) {
X				fprintf(stderr, "Range values reversed\n");
X				return(-1);
X			}
X			if (type1 == SEP_CHAR && num <= val2)
X				return(1);
X			if (num >= val1 && num <= val2)
X				return(1);
X			if (*p == '\0')
X				return(0);
X			if (*p == ',')
X				p++;
X			break;
X		case '\0':
X			if (val1 == num)
X				return(1);
X			return(0);
X		default:
X			fprintf(stderr, "Bad character in range\n");
X			return(-1);
X		}
X	}
X}
X
Xcheckrange(range)
Xchar *range;
X{
X	char *p;
X	int type1, type2, val1, val2;
X
X	type1 = type2 = 0;
X	p = range;
X	while (1) {
X		val1 = getnum(&p, &type1);
X		if (type1 == BAD_CHAR) {
X			fprintf(stderr, "Bad first number in range\n");
X			return(-1);
X		}
X
X		switch (*p) {
X		case ',':
X			p++;
X			continue;
X		case ':':
X			p++;
X			if (*p == ',' || *p == '\0') {
X				if (*p == '\0') 
X					return(0);
X				p++;
X				break;
X			}
X
X			val2 = getnum(&p, &type2);
X			if (type2 == BAD_CHAR) {
X				fprintf(stderr, "Bad second number in range\n");
X				return(-1);
X			}
X
X			if (val1 > val2) {
X				fprintf(stderr, "Range values reversed\n");
X				return(-1);
X			}
X			if (*p == '\0')
X				return(0);
X			if (*p == ',')
X				p++;
X			break;
X		case '\0':
X			return(0);
X		default:
X			fprintf(stderr, "Bad character in range\n");
X			return(-1);
X		}
X	}
X}
X
Xstatic
Xgetnum(pp, type)
Xchar **pp;
Xint *type;
X{
X	register int sign, val;
X	register char *p;
X
X	p = *pp;
X	if (!isdigit(*p) && *p != '-') {
X		if (*p == SEP_CHAR)
X			*type = SEP_CHAR;
X		else
X			*type = BAD_CHAR;
X		return(0);
X	}
X	sign = 1;
X	if (*p == '-') {
X		sign = -1;
X		p++;
X	}
X	if (!isdigit(*p)) {
X		*type = BAD_CHAR;
X		return(0);
X	}
X	for (val = 0; isdigit(*p) && *p != '\0'; p++)
X		val = val * 10 + *p - '0';
X	if (*p != '\0' && *p != ',' && *p != SEP_CHAR) {
X		*type = BAD_CHAR;
X		return(0);
X	}
X	*pp = p;
X	return(sign * val);
X}
+ END-OF-FILE range.c
chmod 'u=rw,g=r,o=r' 'range.c'
echo '	-rw-r--r--  1 brachman     4256 Apr 30 11:24 range.c        (as sent)'
echo -n '	'
/bin/ls -l range.c
echo 'Extracting ranget.c'
sed 's/^X//' > ranget.c << '+ END-OF-FILE ranget.c'
X
X/*
X * Program to test the range routine
X *
X * To reuse the previous range specification just type <return> to the prompt
X *
X * BJB
X */
X
X#include <stdio.h>
X
Xchar buf[BUFSIZ], range[BUFSIZ];
X
Xmain()
X{
X	register int i;
X	char *gets();
X
X	buf[0] = range[0] = '\0';
X	while (1) {
X		printf("range? ");
X		if (gets(buf) == (char *) NULL)
X			break;
X		if (buf[0] != '\0')
X			strcpy(range, buf);
X		if (checkrange(range))
X			continue;
X		printf("value? ");
X		if (gets(buf) == (char *) NULL) {
X			printf("\n");
X			exit(0);
X		}
X		i = inrange(atoi(buf), range);
X		if (i == 0)
X			printf("\tno\n");
X		else if (i == 1)
X			printf("\tyes\n");
X		else if (i == -1)
X			printf("\terror\n");
X		else
X			printf("\tbad result\n");
X	}
X	printf("\n");
X}
+ END-OF-FILE ranget.c
chmod 'u=rw,g=r,o=r' 'ranget.c'
echo '	-rw-r--r--  1 brachman      720 Apr 30 11:21 ranget.c        (as sent)'
echo -n '	'
/bin/ls -l ranget.c
exit 0