[comp.sources.amiga] v02i058: lwf - ascii to postscript filter

page@swan.ulowell.edu (Bob Page) (11/10/88)

Submitted-by: paolucci@snll-arpagw.llnl.gov (Sam Paolucci)
Posting-number: Volume 2, Issue 58
Archive-name: printers/lwf.1

Lwf converts ASCII text files to PostScript.  Most of the processing
is done on the host rather than on the printer.  The name "lwf",
perhaps poorly chosen, stands for "LaserWriter Filter".

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	Makefile
#	README
#	README2
#	ehandler.ps
#	lwf.c
#	lwf.cfg
#	lwf.man
#	lwf.prologue
#	psclear.uu
#	range.c
# This archive created: Wed Nov  9 21:07:28 1988
cat << \SHAR_EOF > Makefile
# cc flags
CFLAGS = +fi +L

# Where to put the PostScript prologue file
PROLOGUE = S:lwf.prologue

# Environment options:
#
# -DAMIGA	Since we don't have the passwd information
# -DPROLOGUE="$(PROLOGUE)"
# -DPR=\"where pr is, e.g. /bin/pr\"
# -DSYSV	For System V (assumes SYSV-style pr)
# -DREVERSE=0	Set to non-zero if page reversal is to be the default
# -DHOSTNAME=\"yourhostname\"	If you don't have the hostname() system call
ENV = -DREVERSE=1 -DAMIGA

lwf: lwf.o range.o
	ln -o lwf lwf.o range.o -lsock32 -lma32 -lc32

lwf.o: lwf.c
	cc $(CFLAGS) $(ENV) lwf.c

range.o: range.c
	cc $(CFLAGS) range.c

clean:
	delete lwf.o range.o
SHAR_EOF
cat << \SHAR_EOF > README
				NOTES
				-----

Recently I ran into a posting of lwf in one of the UNIX groups on
USENET.  I ported it to the Amiga since I had a real need for
something like this.  It is relatively full featured, and I added a
couple of additional options myself and fixed a few things.  With
appropriate mods to the makefile the code should compile and run fine
on AmigaDOS as well as on UNIX machines (I used Manx).  To recompile
on the Amiga you will need Ameristar's socket library. 

On the Amiga it will look for environment variables USER and HOSTNAME
which will be printed on the header page along with the date and file
names.  The environment variables are as used by Manx and ARP and can
be set using the set command.  If they are not set, the fields will be
printed blank. 

The program generates a PostScript prologue file which is identical to
lwf.prologue.  If you want to use your own or a modified version of
lwf.prologue, you need to use the -P command line option. 

If you have difficulty in printing a large file, it may be because the
default is to print the pages in reverse order.  To accomplish this
feat, a temporary file is created in RAM: and deleted afterwards.  For
a large file this will require a corresponding amount of memory.  If
this is your problem, you can easily get around it by turning off the
page reversal with the -r flag. 

I'm hoping to tie this code to a print spooler running over an
Ethernet network.  When that happens I'll post it.  In the meantime
the -p flag that appears in the manual page is not currently
implemented on the Amiga. 

Defaults of all command line arguments can be specified in the
configuration file lwf.cfg which should be placed in S:.  An example
file is enclosed.  This file consists of lines in which the initial
character in each line specifies the command.  If the initial
character is a space, an asterisk, a pound sign, a semicolon, or a
line feed, the line is ignored.  This makes it useful for commenting
the file.  White space following the command is optional.  Comments
can also be placed on the individual lines after the complete command
is specified, as shown in the example. 

Also included in the directory are two useful files.  If you are lucky
enough to have a PostScript laser printer tied directly to your Amiga,
you should download ehandler.ps into the printer once and only once,
preferably from within your startup-sequence.  In the case you should
send a file to the printer which contains an error, this file, which
is an official Adobe error handler, will generate an error
description.  The second file, psclear, should be downloaded into the
laser printer when the printer seems to just be waiting for more
information AND you have not used ehandler.ps.  This file will send an
end-of-file to the laser printer. 

					Enjoy. 

Dr. Samuel Paolucci
1351 Roselli Dr.
Livermore, CA 94550
(415)294-2018

ARPA: paolucci@snll-arpagw.llnl.gov
SHAR_EOF
cat << \SHAR_EOF > README2

Lwf converts ASCII text files to PostScript.
It has been tested on the Apple LaserWriter/LaserWriter+ and the NEC
Silentwriter LC-890.  Most of the processing is done on the host
rather than on the printer.  The name "lwf", perhaps poorly chosen, stands
for "LaserWriter Filter".

There may be a couple of minor Berkeley-isms in the program (see the
routine setup()) and it is somewhat Unix dependent (eg., popen(), /bin/pr
are expected).  The output conforms to the Adobe 2.0 file structuring
conventions.  The program is set up to use the (fixed-width) Courier font.
If you use another font you'll have to modify the mapping table in lwf.c.
I can't recommend a proportional font for this purpose.

The distribution includes the following files:

README2		This file
Makefile
lwf.c		LaserWriter filter to convert ASCII text to PostScript
range.c		Routine to check if a number is in a given range
lwf.1.man	Manual page for lwf
lwf.prologue	Default PostScript support file

Please check the Makefile and the configurable #defines in lwf.c.
Check if the man page needs to be revised for your environment.
You can then proceed with 'make install'.

If you come across any bugs, including nonconformance to Adobe 2.0, or
make any changes you'd like to share please send mail to me rather than
posting to the net.

Enjoy.

-----
Barry Brachman           | {ihnp4!alberta,uw-beaver,uunet}!
Dept. of Computer Science|  ubc-vision!ubc-csgrads!brachman
Univ. of British Columbia| brachman@grads.cs.ubc.cdn
Vancouver, B.C. V6T 1W5  | brachman%ubc.csnet@csnet-relay.arpa
(604) 228-4327           | brachman@ubc.csnet

SHAR_EOF
cat << \SHAR_EOF > ehandler.ps
%!PS-Adobe-2.0
% ehandler.ps -- Downloaded Error Break-page handler
% Copyright (c) 1984, 1985, 1986  Adobe Systems Incorporated.
% All Rights Reserved.

0000 % exitserver password
/$brkpage where { %ifelse
	pop pop
	(Error Handler in place - not loaded again\n)
	print flush stop
}{ %else
	dup serverdict begin
	statusdict begin checkpassword { %ifelse
		(Error Handler downloaded.\n)print flush
		exitserver
	}{ %else
		pop
		(Bad Password on loading error handler!!!\n)
		print flush stop
	} ifelse
} ifelse
/$brkpage 64 dict def $brkpage begin
/prnt { %def
	dup type /stringtype ne { =string cvs } if
	dup length 6 mul
	/tx exch def /ty 10 def
	currentpoint /toy exch def /tox exch def
	1 setgray newpath
	tox toy 2 sub moveto
	0 ty rlineto tx 0 rlineto
	0 ty neg rlineto
	closepath fill
	tox toy moveto 0 setgray show
} bind def
/nl { %def
	currentpoint exch pop lmargin exch moveto
	0 -10 rmoveto
} def
/== { /cp 0 def typeprint nl } def
/typeprint {
	dup type dup currentdict exch known {exec}{
		unknowntype
	} ifelse
} readonly def
/lmargin 72 def /rmargin 72 def
/tprint { %def
	dup length cp add rmargin gt { nl /cp 0 def } if
	dup length cp add /cp exch def
	prnt
} readonly def
/cvsprint { =string cvs tprint ( ) tprint } readonly def
/unknowntype { %def
	exch pop cvlit (??) tprint cvsprint
} readonly def
/integertype { cvsprint } readonly def
/realtype { cvsprint } readonly def
/booleantype { cvsprint } readonly def
/operatortype { (//) tprint cvsprint } readonly def
/marktype { pop (-mark- ) tprint } readonly def
/dicttype { pop (-dictionary- ) tprint } readonly def
/nulltype { pop (-null- ) tprint } readonly def
/filetype { pop (-filestream- ) tprint } readonly def
/savetype { pop (-savelevel- ) tprint } readonly def
/fonttype { pop (-fontid- ) tprint } readonly def
/nametype { %def
	dup xcheck not { (/) tprint } if cvsprint
} readonly def
/stringtype { %def
	dup rcheck { %ifelse
		(\() tprint tprint (\)) tprint
	}{ %else
		pop (-string- ) tprint
	} ifelse
}readonly def
/arraytype { %def
	dup rcheck { %ifelse
		dup xcheck { %ifelse
			({) tprint { typeprint } forall (}) tprint
		}{ %else
			([) tprint { typeprint } forall (]) tprint
		} ifelse
	}{ %else
		pop (-array- ) tprint
	} ifelse
} readonly def
/packedarraytype { %def
	dup rcheck { %ifelse
		dup xcheck { %ifelse
			({) tprint { typeprint } forall (}) tprint
		}{ %else
			([) tprint { typeprint } forall (]) tprint
		} ifelse
	}{ %else
		pop (-packedarray- ) tprint
	} ifelse
} readonly def
/courier /Courier findfont 10 scalefont def
/OLDhandleerror errordict /handleerror get def
end %$brkpage

/handleerror { %put
	systemdict begin $error begin $brkpage begin
	newerror { %ifelse
		/newerror false store
		vmstatus pop pop 0 ne { grestoreall } if
		initgraphics courier setfont
		lmargin 720 moveto (ERROR: ) prnt
		errorname prnt
		nl (OFFENDING COMMAND: ) prnt
		/command load prnt
		$error /ostack known { %if
			nl nl (STACK:) prnt nl nl
			$error /ostack get aload length { == } repeat
		} if
		systemdict /showpage get exec
		/newerror true store
		/OLDhandleerror load end end end exec
	}{ %else
		end end end
	} ifelse
}
dup 0 systemdict put   % replace name by actual dict object
dup 4 $brkpage put     % replace name by dict object
bind readonly

errordict 3 1 roll put % put proc in errordict as /handleerror
SHAR_EOF
cat << \SHAR_EOF > lwf.c

/* vi: set tabstop=4 : */

/*
 * lwf - Convert ASCII text to PostScript
 *
 * Usage: lwf [-c#] [-d] [-i#] [-l] [-m] [-n] [-olist] [-p[str]] [-P filename]
 *			[-r] [-s#] [-t#] [-v] [-w] [-S] [file ...]
 *
 *
 * Options:
 *	-c#		Number of copies
 *	-d		Debug mode
 *	-i#		Indent each line # inches (so much for metric)
 *	-l		Landscape instead of Portrait
 *	-m		Use 3 hole punch margins
 *	-n		Leave top and bottom margin (66 lines/page at 10pt)
 *	-olist		Only print pages in the specified range
 *	-p[str]		Use pr to print, passing optional string
 *	-P filename	Copy prologue from filename instead of default
 *	-r		Toggle page reversal flag (see Makefile)
 *	-s#		Use point size #
 *	-t#		Spaces between tab stops is # characters
 *	-v		Verbose
 *	-w		Line wrap (not implemented yet)
 *	-S		Standalone mode (print header page, use EOF's)
 *
 * If no files are specified, stdin is used.
 * Form feeds handled.
 * Backspacing with underlining (or overprinting) works.
 * The output conforms to Adobe 2.0.
 *
 * Problems:
 *	- assumes fixed-width (non-proportional) font in some places
 *	- can't back up (using backspaces) over tabs
 *	- assumes 8.5 x 11.0 paper
 *
 * BJB - Jun/87
 * SP  - May/88
 * ========================================================================
 *
 * Permission is given to freely copy and distribute this software provided:
 *
 *	1) You do not sell it,
 *	2) You do not use it for commercial advantage, and
 *	3) This notice accompanies the distribution
 *
 * Copyright (c) 1988
 * Barry Brachman
 * Dept. of Computer Science
 * Univ. of British Columbia
 * Vancouver, B.C. V6T 1W5
 *
 * .. {ihnp4!alberta, uw-beaver, uunet}!ubc-vision!ubc-csgrads!brachman
 * brachman@grads.cs.ubc.cdn
 * brachman%ubc.csnet@csnet-relay.arpa
 * brachman@ubc.csnet
 * ========================================================================
 */

#ifndef AMIGA
#include <pwd.h>
#endif AMIGA
#include <ctype.h>
#include <stdio.h>

#define min(a, b)		((a) < (b) ? (a) : (b))

/*
 * Configurable...
 * BUFOUT should be fairly large
 */
#define BUFIN				1024	/* maximum length of an input line */
#define BUFOUT				(BUFIN * 5)
#define MAXPAGES			10000	/* maximum number of pages per job */
#define DEFAULT_TAB_SIZE	8
#define DEFAULT_POINT_SIZE	10
#ifndef REVERSE
#define REVERSE			0
#endif REVERSE

#ifdef SYSV
#define rindex			strrchr
#endif SYSV

/*
 * As mentioned in the man page, /bin/pr doesn't handle formfeeds correctly
 * when doing multicolumn formatting.
 * Instead of starting a new column or page it passes a formfeed through,
 * causing pr and lwf to get out-of-synch with regard to the current
 * location on the page.
 * If your pr behaves this way (4.[23] does, SYSV doesn't), define PRBUG so
 * that fgetline() will filter out these bogus formfeeds while preserving
 * the column structuring.
 */
#ifndef SYSV
#ifdef PR
#define PRBUG				1
#endif PR
#endif SYSV

/*
 * PostScript command strings defined in the prologue file
 */
#define BACKSPACE		"B"
#define ENDPAGE			"EP"
#define LINETO			"L"
#define MOVETO			"M"
#define NEWPATH			"NP"
#define SHOW			"S"
#define STARTPAGE		"SP"
#define STARTHPAGE		"SHP"
#define STARTLPAGE		"SLP"
#define STROKE			"ST"
#define TAB			"T"

/*
 * Conformance requires that no PostScript line exceed 256 characters
 */
#define MAX_OUTPUT_LINE_LENGTH	256

#define TEXTFONT		"Courier"
#define HEADERFONT		"Times-Roman"
#define HEADERPS		18	/* header page point size */

#define PORTRAIT_START_Y	767	/* first row (Y coord) on each page */
#define LANDSCAPE_START_Y	587	/* first row (Y coord) on each page */
#define PORTRAIT_START_X	25	/* position of start of each line */
#define LANDSCAPE_START_X	25	/* position of start of each line */
#define START_X_HEADER		100	/* x start of each header line */
#define START_Y_HEADER		700	/* first row (Y coord) of header */
#define THREE_HOLE_X		1.0	/* portrait x offset (inches) 3 hole */
#define THREE_HOLE_Y		0.5	/* landscape y offset (inches) 3 hole */
#define MARGIN			41	/* top and bottom margin (points) */

#define MAX_X			612
#define MAX_Y			792

#define SEP_CHAR		'\001'	/* pr column separator character */

#define PS_EOF			04

#define NPSIZES			6
struct psize {
	int size;			/* point size */
	double charsperinch;		/* approx. char width, for Courier */
	int portrait_page_length;	/* page length in lines */
	int portrait_cols;		/* maximum # of chars per line */
	int landscape_page_length;
	int landscape_cols;
} psize[NPSIZES] = {
	 7, 17.0, 106, 133, 80, 175,
	 8, 15.0,  93, 117, 70, 155,
	 9, 14.0,  82, 109, 62, 144,
	10, 12.0,  74,  94, 56, 124,
	11, 11.0,  67,  86, 51, 113,
	12, 10.0,  62,  78, 47, 103
};

#ifdef PR
#define USAGE	\
"[-c#] [-d] [-i#] [-l] [-m] [-n] [-olist] [-p[str]] [-P filename] [-r] [-s#] [-S] [-t#] [-v] [file ...]"
#else PR
#define USAGE	\
"[-c#] [-d] [-i#] [-l] [-m] [-n] [-olist] [-P filename] [-r] [-s#] [-S] [-t#] [-v] [file ...]"
#endif PR

struct psize *p, *get_psize();
long page_map[MAXPAGES];	/* offset of first byte of each page */
int page_count;
int page;

int lines_per_page;
int columns;
int point_size;
int start_x, start_y;
int ncopies;
int margin;
double offset;

char bufin[BUFIN];		/* input buffer */
char bufout[BUFOUT];		/* used for page reversal and output buffering */

#ifdef AMIGA
char username[32];
#else AMIGA
char *username;
#endif AMIGA
char hostname[32];
char tmpname[32];
char *currentdate;

int row;
char *range;
int tabstop;
char *propts;

int dflag, lflag, mflag, nflag, pflag, rflag, vflag, wflag, Sflag;

FILE *freopen();
char *fgets();
char *strcpy();
char *fgetline();

char *prologue;
char *progname;
char *configfile = "s:lwf.cfg";

char *version = "lwf V2.0a 30-May-88";

main(argc, argv)
int argc;
char **argv;
{
	register int i, j, first_file;
	char *pc;
	double offset, bsize, atof();
	int margin;
	char *rindex();
	FILE *infile, *popen();
	double ceil(), floor();

	if ((pc = rindex(argv[0], '/')) != (char *) NULL)
		progname = pc + 1;
	else
		progname = argv[0];

	init();

        getdefaults();

	for (i = 1; i < argc && argv[i][0] == '-'; i++) {
		switch (argv[i][1]) {
		case 'c':
			ncopies = atoi(&argv[i][2]);
			if (ncopies <= 0) {
				fatal("number of copies must be > 0");
				/*NOTREACHED*/
			}
			break;
		case 'd':
			dflag = 1;
			break;
		case 'i':
			offset = atof(&argv[i][2]);
			if (offset < 0.0 || offset >= 8.5) {
				fatal("bad indent");
				/*NOTREACHED*/
			}
			break;
		case 'l':
			lflag = 1;
			break;
		case 'm':
			mflag = 1;
			break;
		case 'n':
			nflag = 1;
			break;
		case 'o':
			range = &argv[i][2];
			if (checkrange(range)) {
				fatal("bad range specification");
				/*NOTREACHED*/
			}
			break;
#ifdef PR
		case 'p':
			pflag = 1;
			propts = &argv[i][2];
			break;
#endif PR
		case 'P':
			if (++i == argc) {
				fatal("missing filename after -P");
				/*NOTREACHED*/
			}
			prologue = argv[i];
			break;
		case 'r':
			rflag = !rflag;
			break;
		case 's':
			j = atoi(&argv[i][2]);
			if ((p = get_psize(j)) == (struct psize *) NULL) {
				fatal("bad point size");
				/*NOTREACHED*/
			}
			break;
		case 't':
			tabstop = atoi(&argv[i][2]);
			if (tabstop < 1) {
				fatal("bad tabstop");
				/*NOTREACHED*/
			}
			break;
		case 'v':
			vflag = 1;
			break;
		case 'w':
			wflag = 1;
			break;
		case 'S':
			Sflag = 1;
			break;
		default:
			(void) fprintf(stderr, "Usage: %s %s\n", progname, USAGE);
			exit(1);
		}
	}

	/*
	 * Check that all files are readable.
	 * This is so that no output at all is produced if any file is not
	 * readable in case the output is being piped to a printer.
	 */
	for (j = i; j < argc; j++) {
		if (access(argv[j], 4) == -1) {
			fatal("cannot access %s", argv[j]);
			/*NOTREACHED*/
		}
	}

	point_size = p->size;

	if (nflag) {
		margin = MARGIN;
		bsize = MAX_Y - 2 * (MAX_Y - PORTRAIT_START_Y + MARGIN);
		p->portrait_page_length = (int) floor(bsize / point_size);
		p->landscape_cols = (int) floor(bsize * p->charsperinch / 72.0);
	}

	if (lflag) {
		start_y = LANDSCAPE_START_Y;
		start_x = LANDSCAPE_START_X + margin + (int) (offset * 72.0);
		lines_per_page = p->landscape_page_length;
		columns = p->landscape_cols - (int) ceil(offset * p->charsperinch);
		if (mflag) {
			int nlines;

			nlines = (int) ceil((THREE_HOLE_Y * 72.0) / point_size);
			start_y -= (nlines * point_size);
			lines_per_page -= nlines;
			columns -= (int) ceil(THREE_HOLE_Y * p->charsperinch);
		}
	}
	else {
		start_y = PORTRAIT_START_Y - margin;
		lines_per_page = p->portrait_page_length;
		start_x = PORTRAIT_START_X;
		if (mflag)
				offset += THREE_HOLE_X;
		start_x += (int) (offset * 72.0);
		columns = p->portrait_cols - (int) ceil(offset * p->charsperinch);
	}
	if (vflag) {
		(void) fprintf(stderr, "%s\n\n", version);
		(void) fprintf(stderr, "Lines/page = %d\n", lines_per_page);
		(void) fprintf(stderr, "Columns = %d\n", columns);
		(void) fprintf(stderr, "X-offset = %5.2f inches\n", offset);
	}

	setup();
	preamble();

	first_file = i;

	if (!rflag && Sflag)
		header(argc - first_file, argv + first_file);

	if (i == argc) {	/* no files on command line */
		infile = stdin;
#ifdef PR
		if (pflag) {
			build_prcmd(bufin, "");
			if ((infile = popen(bufin, "r")) == (FILE *) NULL) {
				fatal("popen failed");
				/*NOTREACHED*/
			}
		}
#endif PR
		if (vflag)
			(void) fprintf(stderr, "printing stdin\n");
		print(infile);
#ifdef PR
		if (pflag)
			(void) pclose(infile);
#endif PR
	}

	/*
	 * If page reversal is performed, process the file arguments right to left,
	 * oth. left to right
	 * If the correct flag is used for the printer the first file argument
	 * will be on top in the printer's output tray when the paper is removed
	 */
	if (rflag)
		j = argc - 1;
	else
		j = i;
	while (i < argc) {
		infile = stdin;
#ifdef PR
		if (pflag) {
			build_prcmd(bufin, argv[j]);
			if ((infile = popen(bufin, "r")) == (FILE *) NULL) {
				fatal("popen failed");
				/*NOTREACHED*/
			}
		}
		else {
#endif PR
			if (freopen(argv[j], "r", stdin) == (FILE *) NULL) {
				fatal("can't open %s", argv[j]);
				/*NOTREACHED*/
			}
#ifdef PR
		}
#endif PR
		if (vflag)
			(void) fprintf(stderr, "printing %s\n", argv[j]);
		print(infile);
#ifdef PR
		if (pflag)
			(void) pclose(infile);
#endif PR
		if (rflag)
			j--;
		else
			j++;
		i++;
	}

	if (rflag && Sflag)
		header(argc - first_file, argv + first_file);

	(void) printf("%%%%Trailer\n");
	(void) printf("%%%%Pages: %d\n", page_count);
	(void) putc(PS_EOF, stdout);

	if (fflush(stdout) == EOF) {
		fatal("write error on stdout");
		/*NOTREACHED*/
	}
	exit(0);
}

/* initialize variables */
init()
{
    dflag = 0;
    lflag = 0;
    mflag = 0;
    nflag = 0;
    vflag = 0;
    wflag = 0;
    Sflag = 0;
    rflag = REVERSE;
#ifdef PR
    pflag = 0;
    propts = "";
#endif PR
#ifdef PROLOGUE
    prologue = PROLOGUE;
#else PROLOGUE
    prologue = "";
#endif PROLOGUE
    ncopies = 1;
    offset = 0.0;
    range = ":";
    tabstop = DEFAULT_TAB_SIZE;
    p = get_psize(DEFAULT_POINT_SIZE);
    page_count = 0;
    margin = 0;
}

/* read the configuration file for defaults */
getdefaults()
{
    FILE *deffile;
    char inline[100];
    char *pp;
    int j;

    if ((deffile = fopen(configfile, "r")) == NULL)
        return;

    while (fgets(inline, 100, deffile) != NULL) {
        switch (inline[0]) {
		case ' ': case '*': case '#': case ';': case '\n':
         		 break;
		case 'c':
         		 (void) sscanf(inline+1, "%d", &ncopies);
         		 break;
		case 'd':
         		 dflag = 1;
		         break ;
		case 'i':
         		 (void) sscanf(inline+1, "%lf", &offset);
         		 break;
		case 'l':
                         lflag = 1;
		         break ;
		case 'm':
                         mflag = 1;
		         break ;
		case 'n':
                         nflag = 1;
		         break ;
		case 'o':
                         (void) sscanf(inline+1, "%s", pp);
                         range = pp;
                         while (*pp++);
                         break;
#ifdef PR
		case 'p':
		         pflag = 1;
		         (void) sscanf(inline+1, "%s", pp);
                         propts = pp;
                         while (*pp++);
		         break ;
#endif PR
		case 'P':
		         (void) sscanf(inline+1, "%s", pp);
                         prologue = pp;
                         while (*pp++);
		         break ;
		case 'r':
		         rflag = !rflag;
		         break;
		case 's':
                         (void) sscanf(inline+1, "%d", &j);
                         if ((p = get_psize(j)) == (struct psize *) NULL) {
                             fatal("bad point size");
                             /*NOTREACHED*/
                         }
                         break;
		case 't':
		         (void) sscanf(inline+1, "%d", &tabstop);
		         break;
		case 'v':
                         vflag = 1;
		         break ;
		case 'w':
                         wflag = 1;
		         break ;
		case 'S':
                         Sflag = 1;
		         break ;
		default:
		         printf("Error in %s file: %s", configfile, inline) ;
        }
    }
}

/*
 * Return a pointer to the point size structure for the
 * specified point size
 */
struct psize *
get_psize(size)
int size;
{
	register int i;

	for (i = 0; i < NPSIZES; i++)
		if (psize[i].size == size)
			break;
	if (i == NPSIZES)
		return((struct psize *) NULL);
	return(&psize[i]);
}

/*
 * Initial lines sent to the LaserWriter.
 * This stuff is sent to stdout since we don't want it to be reversed.
 * Generates the PostScript header and includes the prologue file.
 * There is limited checking for I/O errors here.
 * When the standard prologue is being used we probably should verify
 * that it is the correct version (via %%BeginProcSet).
 */
preamble()
{
	FILE *fp;

	(void) printf("%%!PS-Adobe-2.0\n");
	(void) printf("%%%%Creator: %s on %s\n", progname, hostname);
	(void) printf("%%%%CreationDate: %s\n", currentdate);
	(void) printf("%%%%For: %s\n", username);
	(void) printf("%%%%DocumentFonts: %s", TEXTFONT);
	if (Sflag)
		(void) printf(" %s\n", HEADERFONT);
	else
		(void) printf("\n");
	(void) printf("%%%%Pages: (atend)\n");

	if (strcmp(prologue, "")) {
		if ((fp = fopen(prologue, "r")) == (FILE *) NULL) {
			fatal("can't open prologue file `%s'", prologue);
			/*NOTREACHED*/
		}

		while (fgets(bufin, sizeof(bufin), fp) != (char *) NULL)
			(void) fputs(bufin, stdout);
		(void) fclose(fp);
	} else {
		(void) printf("%%%%EndComments\n");
		(void) printf("%% PostScript Prologue for lwf V2.0a\n");
		(void) printf("/B {NW 0 rmoveto}bind def\n");
		(void) printf("/EP {SV restore /#copies exch def showpage}bind def\n");
		(void) printf("/L /lineto load def\n");
		(void) printf("/M /moveto load def\n");
		(void) printf("/NP /newpath load def\n");
		(void) printf("/S /show load def\n");
		(void) printf("/SHP {SP 2 setlinewidth}bind def\n");
		(void) printf("/SLP {SP 612 0 translate 90 rotate}bind def\n");
		(void) printf("/SP {/SV save def findfont exch scalefont setfont ( )\n");
		(void) printf("  stringwidth pop dup /W exch def neg /NW exch def}bind def\n");
		(void) printf("/ST /stroke load def\n");
		(void) printf("/T {W mul 0 rmoveto}bind def\n");
		(void) printf("%%%%EndProlog\n");
	}
	if (ferror(stdout) || fflush(stdout) == EOF) {
		fatal("write error on stdout");
		/*NOTREACHED*/
	}
}

#ifdef PR
/*
 * Generate a command, in the specified buffer, to print the given file
 * according to the options in effect.
 */
build_prcmd(buf, file)
char *buf, *file;
{

#ifdef SYSV
	(void) sprintf(buf, "%s -e%d -w%d -l%d -s%c %s %s",
				PR, tabstop, columns, lines_per_page, SEP_CHAR, propts, file);
#else SYSV
	(void) sprintf(buf, "%s -w%d -l%d -s%c %s %s",
						PR, columns, lines_per_page, SEP_CHAR, propts, file);
#endif SYSV
	if (vflag)
		(void) fprintf(stderr, "pr cmd: %s\n", buf);
}
#endif PR

/*
 * Print a file
 *
 * The input stream may be stdin, a file, or a pipe.
 * If page reversal is being performed, the output goes to a temporary file and
 * then reverse() is called to do the page reversal to stdout.
 */
print(infile)
FILE *infile;
{
	register int eof, pagenum, r;
	register char *p;
	FILE *outfile;
	char *mktemp();

	if (rflag) {
		static char bigbuf[BUFSIZ];

		page = 0;
		page_map[page] = 0L;
#ifdef AMIGA
		(void) sprintf(bufin, "ram:%sXXXXXX", progname);
#else AMIGA
		(void) sprintf(bufin, "/tmp/%sXXXXXX", progname);
#endif AMIGA
		if (vflag)
			(void) fprintf(stderr, "temp will be: %s ... ", bufin);
		p = mktemp(bufin);
		if (vflag)
			(void) fprintf(stderr, "%s\n", p);
		if ((outfile = fopen(p, "w+")) == (FILE *) NULL) {
			(void) fprintf(stderr, "%s: can't create %s\n", progname, p);
			cleanup();
			/*NOTREACHED*/
		}
		setbuf(outfile, bigbuf);
#ifdef AMIGA
		(void) strcpy(tmpname, p);
#else AMIGA
		if (!dflag)
			(void) unlink(p);
		else
			(void) fprintf(stderr, "will not unlink %s\n", p);
#endif AMIGA
	}
	else
		outfile = stdout;

	pagenum = 1;
	eof = 0;
	while (!eof) {
		row = start_y;
		if ((r = inrange(pagenum, range)) == -1) {
			cleanup();
			/*NOTREACHED*/
		}
		else if (r == 1)
			eof = printpage(infile, outfile);
		else if (r == 0)
			eof = flushpage(infile);
		else {
			fatal("bad inrange result");
			/*NOTREACHED*/
		}
		pagenum++;
	}
	if (row != start_y)
		endpage(outfile);
	if (vflag)
		(void) fprintf(stderr, "\n");
	if (fflush(outfile) == EOF) {
		fatal("write error while flushing output");
		/*NOTREACHED*/
	}
	if (rflag) {
		reverse(outfile);
		(void) fclose(outfile);
#ifdef AMIGA
		(void) unlink(tmpname);
#endif AMIGA
	}
}

/*
 * Process the next page.
 * Return 1 on EOF, 0 otherwise.
 */
printpage(infile, outfile)
FILE *infile, *outfile;
{
	register int lineno;

	if (ungetc(getc(infile), infile) == EOF)
		return(1);

	startpage(page_count + 1, outfile);
	for (lineno = 0; lineno < lines_per_page; lineno++) {
		if (fgetline(bufin, sizeof(bufin), infile) == (char *) NULL)
			return(1);
		if (bufin[0] == '\f')
			break;
		if (bufin[0] != '\0') {
			(void) fprintf(outfile, "%d %d %s\n", start_x, row, MOVETO);
			proc(bufin, outfile);
		}
		row -= point_size;
	}
	endpage(outfile);
	return(0);
}

/*
 * The next page will not be printed; just consume the input and discard.
 * Don't change xrow since we don't want an endpage().
 */
flushpage(infile)
FILE *infile;
{
	register int lineno, xrow;

	xrow = row;
	for (lineno = 0; lineno < lines_per_page; lineno++) {
		if (fgetline(bufin, sizeof(bufin), infile) == (char *) NULL)
			return(1);
		if (bufin[0] == '\f')
			break;
		xrow -= point_size;
	}
	return(0);
}

/*
 * Start a new page.
 */
startpage(n, outfile)
int n;
FILE *outfile;
{

	(void) fprintf(outfile, "%%%%Page: %d %d\n", n, n);
	(void) fprintf(outfile, "%d /%s %s\n",
			point_size, TEXTFONT, lflag ? STARTLPAGE : STARTPAGE);
}

/*
 * A page has been written to the temp file.
 * Record the start of the next page.
 * Terminate the page and indicate the start of the next.
 */
endpage(outfile)
FILE *outfile;
{
	long ftell();

	if (page_count == MAXPAGES) {
		fatal("pagelimit (%d) reached", MAXPAGES);
		/*NOTREACHED*/
	}
	(void) fprintf(outfile, "%d %s\n", ncopies, ENDPAGE);
	if (rflag) {
		if (fflush(outfile) == EOF) {
			fatal("write error while flushing page");
			/*NOTREACHED*/
		}
		page_map[++page] = ftell(outfile);
	}

	page_count++;

	if (vflag)
		(void) fprintf(stderr, "x");
}

/*
 * Print the pages to stdout in reverse order.
 * Assumes that the number of characters per page can be contained in an int.
 */
reverse(outfile)
FILE *outfile;
{
	register int i;
	int bytecount, nbytes;
	int fseek();

	if (vflag)
		(void) fprintf(stderr, "\nreversing %d page%s\n", page,
						page > 1 ? "s" : "");
	if (dflag) {
		for (i = 0; i <= page; i++)
			(void) fprintf(stderr, "[%ld]\n", page_map[i]);
	}
	for (i = page - 1; i >= 0; i--) {
		if (fseek(outfile, page_map[i], 0) == -1L) {
			fatal("seek error");
			/*NOTREACHED*/
		}
		nbytes = (int) (page_map[i + 1] - page_map[i]);
		while (nbytes > 0) {
			bytecount = min(nbytes, sizeof(bufout));
			if (fread(bufout, 1, bytecount, outfile) != bytecount) {
				fatal("read error while reversing pages");
				/*NOTREACHED*/
			}
			if (fwrite(bufout, 1, bytecount, stdout) != bytecount) {
				fatal("write error while reversing pages");
				/*NOTREACHED*/
			}
			nbytes -= bytecount;
		}
	}
}

/*
 * Process a line of input, escaping characters when necessary and handling
 * tabs.
 *
 * The output is improved somewhat by coalescing consecutive tabs and
 * backspaces and eliminating tabs at the end of a line.
 *
 * Overprinting (presumably most often used in underlining) can be far from
 * optimal; in particular the way nroff underlines by sequences like
 * "_\ba_\bb_\bc" creates a large volume of PostScript.  This isn't too
 * serious since a lot of nroff underlining is unlikely.
 *
 * Since a newline is generated for each call there will be more
 * newlines in the output than is necessary.
 */
proc(in, outfile)
char *in;
FILE *outfile;
{
	register int i;
	register char *last, *p, *q;
	int currentp, instr, tabc, tabto;
	char *savep;
	static int colskip, ncols;
	static int seen_sep = 0;

	currentp = 0;
	instr = 0;
	tabto = 0;
	last = bufout + MAX_OUTPUT_LINE_LENGTH - 20;	/* subtract slop factor */

	q = bufout;
	*q = '\0';
	for (p = in; *p != '\0'; p++) {
		switch (*p) {
		case SEP_CHAR:
			/*
			 * This assumes that the input buffer contains the entire line
			 * otherwise the column count will be off.
			 * Also, the input stream must be formatted into a constant number
			 * of columns oth. it would be necessary to scan each line to
			 * count SEP_CHARs (which is not hard but could be slow).
			 */
			if (!seen_sep) {	/* discern number of columns */
				seen_sep = 1;
				ncols = 2;	/* there are at least two columns... */
				savep = p++;
				while (*p != '\0') {
					if (*p++ == SEP_CHAR)
						ncols++;
				}
				p = savep;
				colskip = columns / ncols;
				if (vflag)
					(void) fprintf(stderr, "Using %d columns\n", ncols);
			}
			if (instr) {
				(void) sprintf(q, ")%s ", SHOW);
				q += strlen(q);
				instr = 0;
			}
			tabto += (colskip - currentp);
			currentp = 0;
			break;
		case '\t':
			/*
			 * Count the number of tabs that immediately follow the one we're
			 * looking at
			 */
			tabc = 0;
			while (*(p + 1) == '\t') {
				p++;
				tabc++;
			}
			if (currentp > 0) {		/* not beginning of line */
				i = tabstop - (currentp % tabstop) + tabc * tabstop;
				if (instr) {
					(void) sprintf(q, ")%s ", SHOW);
					q += strlen(q);
					instr = 0;
				}
			}
			else
				i = (tabc + 1) * tabstop;
			tabto += i;
			currentp += i;
			break;
		case '\b':
			*q = '\0';
			(void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
			/* backspacing over tabs doesn't work... */
			if (tabto != 0) {
				fatal("attempt to backspace over a tab");
				/*NOTREACHED*/
			}
			p++;
			for (i = 1; *p == '\b'; p++)
				i++;
			if (currentp - i < 0) {
				fatal("too many backspaces");
				/*NOTREACHED*/
			}
			if (!instr) {
				fatal("bad backspacing");
				/*NOTREACHED*/
			}
			if (i == 1)				/* frequent case gets special attention */
				(void) sprintf(bufout, "%s (", BACKSPACE);
			else
				(void) sprintf(bufout, "-%d %s (", i, TAB);
			currentp -= i;
			q = bufout + strlen(bufout);
			p--;
			break;
		case '\f':
			tabto = 0;							/* optimizes */
			*q = '\0';
			if (instr)
				(void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
			else
				(void) fprintf(outfile, "%s\n", bufout);
			endpage(outfile);
			startpage(page_count + 1, outfile);
			row = start_y;
			(void) fprintf(outfile, "%d %d %s\n", start_x, row, MOVETO);
			q = bufout;
			currentp = 0;
			instr = 0;
			break;
		case '\r':
			tabto = 0;							/* optimizes */
			if (instr) {
				*q = '\0';
				(void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
				instr = 0;
				q = bufout;
			}
			(void) fprintf(outfile, "%d %d %s\n", start_x, row, MOVETO);
			currentp = 0;
			break;
		case '\\':
		case '(':
		case ')':
			if (!instr) {
				if (tabto) {
					(void) sprintf(q, "%d %s ", tabto, TAB);
					q += strlen(q);
					tabto = 0;
				}
				*q++ = '(';
				instr = 1;
			}
			*q++ = '\\';
			*q++ = *p;
			currentp++;
			break;
		default:
			/*
			 * According to the PostScript Language Manual, PostScript files
			 * can contain only "the printable subset of the ASCII character
			 * set (plus the newline marker)".
			 */
			if (!isascii(*p) || !isprint(*p)) {
				fatal("bad character in input");
				/*NOTREACHED*/
			}
			if (!instr) {
				if (tabto) {
					(void) sprintf(q, "%d %s ", tabto, TAB);
					q += strlen(q);
					tabto = 0;
				}
				*q++ = '(';
				instr = 1;
			}
			*q++ = *p;
			currentp++;
			break;
		}
		if (q >= last) {
			*q = '\0';
			if (instr)
				(void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
			else
				(void) fprintf(outfile, "%s\n", bufout);
			q = bufout;
			instr = 0;
		}
	}
	if (instr) {
		(void) sprintf(q, ")%s", SHOW);
		q += strlen(q);
	}
	else
		*q = '\0';
	if (q >= last) {
		fatal("bufout overflow");
		/*NOTREACHED*/
	}
	if (bufout[0] != '\0')
		(void) fprintf(outfile, "%s\n", bufout);
}

/*
 * Find out who the user is, etc.
 * Possible system dependencies here...
 */
setup()
{
	int len;
	char *p;
	long t, time();
	int getusername(), gethostname();
	char *ctime(), *getlogin(), *malloc();
	struct passwd *pw, *getpwuid();

#ifdef AMIGA
	(void) getusername(username, sizeof(username));
#else AMIGA
	if ((p = getlogin()) == (char *) NULL) {
		if ((pw = getpwuid(getuid())) == (struct passwd *) NULL)
			p = "Whoknows";
		else
			p = pw->pw_name;
		endpwent();
	}
	username = (char *) malloc((unsigned) (strlen(p) + 1));
	(void) strcpy(username, p);
#endif AMIGA

#ifdef HOSTNAME
	(void) strncpy(hostname, HOSTNAME, sizeof(hostname));
	hostname[sizeof(hostname) - 1] = '\0';
#else HOSTNAME
	(void) gethostname(hostname, sizeof(hostname));
#endif HOSTNAME

	t = time((long *) 0);
	p = ctime(&t);
	len = strlen(p);
	*(p + len - 1) = '\0';		/* zap the newline character */
	currentdate = (char *) malloc((unsigned) len);
	(void) strcpy(currentdate, p);
}

/*
 * Print a header page.
 * Assumes setup() has already been called to fill in the user, host, etc.
 * Uses HEADERFONT in HEADERPS point.
 */
header(nfiles, files)
int nfiles;
char **files;
{
	register int i;
	register char *p;

	if (vflag) {
		(void) fprintf(stderr, "printing header\n");
		(void) fprintf(stderr, "%d file%s are:\n", nfiles,
							nfiles > 1 ? "s" : "");
		if (nfiles == 0)
			(void) fprintf(stderr, "\tstdin\n");
		for (i = 0; i < nfiles; i++)
			(void) fprintf(stderr, "\t%s\n", files[i]);
	}

	++page_count;

	(void) fprintf(stdout, "\n%%%%Page: %d %d\n", page_count, page_count);
	(void) fprintf(stdout, "%d /%s %s\n", HEADERPS, HEADERFONT, STARTHPAGE);

	/*
	 * The header sheet looks like:
	 *
	 * ----------------------------
	 * ----------------------------
	 *
	 * User:
	 * Host:
	 * Date:
	 * Files:
	 *
	 * ----------------------------
	 * ----------------------------
	 */
	row = START_Y_HEADER;
	(void) printf("%s %d %d %s\n", NEWPATH, START_X_HEADER, row, MOVETO);
	(void) printf("%d %d %s\n", START_X_HEADER + 400, row, LINETO);
	row -= 6;
	(void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
	(void) printf("%d %d %s\n", START_X_HEADER + 400, row, LINETO);
	row -= 24;
	(void) printf("%s\n", STROKE);

	(void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
	(void) sprintf(bufin, "User: %s", username);
	proc(bufin, stdout);
	row -= 24;
	(void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
	(void) sprintf(bufin, "Host: %s", hostname);
	proc(bufin, stdout);
	row -= 24;
	(void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
	(void) sprintf(bufin, "Date: %s", currentdate);
	proc(bufin, stdout);
	row -= 24;

	if (nfiles == 0) {
		(void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
		(void) sprintf(bufin, "File: <stdin>");
		proc(bufin, stdout);
	}
	else {
		register int len, max, sum;

		/*
		 * If the list of files is "too long" we'll only print as many as
		 * possible
		 * Arbitrary chop off point is 50 characters
		 * (assume bufin is bigger than this)
		 */
		(void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
		(void) sprintf(bufin, "File%s: ", nfiles > 1 ? "s" : "");
		p = bufin + (sum = strlen(bufin));
		max = 50;
		for (i = 0; i < nfiles - 1; i++) {
			sum += (len = strlen(files[i]) + 1);
			if (sum >= max)
				break;
			(void) sprintf(p, "%s,", files[i]);
			p += len;
		}
		sum += (len = strlen(files[i]) + 1);
		if (sum < max)
			(void) sprintf(p, "%s", files[i]);
		else
			(void) strcpy(p, "...");
		proc(bufin, stdout);
	}

	row -= 12;
	(void) printf("%s %d %d %s\n", NEWPATH, START_X_HEADER, row, MOVETO);
	(void) printf("%d %d %s\n", START_X_HEADER + 400, row, LINETO);
	row -= 6;
	(void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
	(void) printf("%d %d %s\n", START_X_HEADER + 400, row, LINETO);
	(void) printf("%s\n", STROKE);
	(void) printf("1 %s\n", ENDPAGE);
	if (fflush(stdout) == EOF) {
		fatal("write error on stdout");
		/*NOTREACHED*/
	}
}

/*
 * Special version of fgets.
 * Read until a formfeed, newline, or overflow.
 * If a formfeed is the first character, return it immediately.
 * If a formfeed is found after the first character, replace it by a newline
 * and push the formfeed back onto the input stream.
 * A special case is a formfeed followed by a newline in which case the
 * newline is ignored .
 * The input buffer will be null-terminated and will *not* end with a newline.
 * The buffer size n includes the null.
 */
char *
fgetline(s, n, iop)
char *s;
int n;
register FILE *iop;
{
	register int ch;
	register char *cs;

	if (n < 2) {
		fatal("fgetline called with bad buffer size!?");
		/*NOTREACHED*/
	}

	cs = s;
	n--;								/* the null */

	/*
	 * Check out the special cases
	 */
	if ((ch = getc(iop)) == EOF)
		return((char *) NULL);
	if (ch == '\f') {
#ifdef PR
#ifdef PRBUG
		if (pflag) {
			/*
			 * Filter out the formfeeds
			 */
			do {
				if (ch == '\f')
					continue;
				if (ch == '\n')
					break;
				*cs++ = ch;
				n--;
			} while (n > 0 && (ch = getc(iop)) != EOF);
			if (ch == EOF) {
				if (ungetc(ch, iop) == EOF && !feof(iop)) {
					/* Shouldn't happen since a getc() was just done */
					fatal("fgetline - ungetc failed");
					/*NOTREACHED*/
				}
			}
			else if (ch != '\n') {
				fatal("fgetline - input line too long");
				/*NOTREACHED*/
			}
			*cs = '\0';
			return(s);
		}
#endif PRBUG
#endif PR
		if ((ch = getc(iop)) != '\n') {
			/*
			 * If EOF was just read it will be noticed next time through
			 */
			if (ungetc(ch, iop) == EOF && !feof(iop)) {
				/* Shouldn't happen since a getc() was just done */
				fatal("fgetline - ungetc failed");
				/*NOTREACHED*/
			}
		}
		*cs++ = '\f';
		*cs = '\0';
		return(s);
	}

	/*
	 * Check for "weird" input characters is made in proc()
	 */
	while (n-- > 0) {
		if (ch == '\f' || ch == '\n')
			break;
		*cs++ = ch;
		if ((ch = getc(iop)) == EOF)
			break;
	}

	if (ch == EOF && cs == s)		/* Nothing was read */
		return((char *) NULL);
	if (ch == '\f') {
		if (ungetc(ch, iop) == EOF)
			(void) fprintf(stderr, "fgetline - can't ungetc??\n");
	}
	else if (ch != '\n' && ch != EOF) {
		fatal("fgetline - input line too long");
		/*NOTREACHED*/
	}
	*cs = '\0';
	return(s);
}

/*VARARGS*/
fatal(s, a, b, c, d, e, f, g, h, i, j)
char *s;
{

	(void) fprintf(stderr, "%s: ", progname);
	(void) fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j);
	(void) fprintf(stderr, "\n");
	cleanup();
	/*NOTREACHED*/
}

/*
 * Clean up and exit after an error
 */
cleanup()
{

	exit(1);
}
SHAR_EOF
cat << \SHAR_EOF > lwf.cfg
#
# lwf configuration file
#

m	; use 3 hole punch margins
n	; leave top and bottom margins
S	; print header page
SHAR_EOF
cat << \SHAR_EOF > lwf.man
.TH LWF 1-LOCAL "21 February 1988"
.UC
.SH NAME
lwf \- ASCII to PostScript filter
.SH SYNOPSIS
.B lwf
[-c#] [-d] [-i#] [-l] [-m] [-n] [-olist] [-p[str]]
.br
.ti +4
[-P filename] [-r] [-s#] [-S] [-t#] [-v] [file ...]
.SH DESCRIPTION
.I Lwf
takes one or more ASCII text files as input and produces PostScript
instructions that may be sent (see \fBlpr(1)\fR) to a PostScript printer
(e.g., an Apple LaserWriter) for printing.
If no files are given on the command line, the standard input is read.
The program correctly handles the form feed character and tabs and
understands backspacing; underscores followed by backspaces
may be used to underline.
Courier font is used.
The output conforms to the Adobe 2.0 file structuring conventions.
.PP
Note that flag arguments apply to all of the files in the argument list.
For example, using ``-s8'' prints each of the files in 8 point type.
.PP
Multiple copies of each page can be printed by immediately following the
.B -c
flag with the number of copies to make.
The pages are not collated.
.PP
Lines may be indented (shifted to the right) using the \fB-i\fR flag.
This flag is immediately followed by the distance in inches to shift
all text from the left edge of the paper instead of the default amount
(about 1/3 of an inch); the resolution is approximately 1/72 of an inch.
.PP
The
.B -l
flag indicates that landscape format is to be used instead of the default
portrait format.
.PP
Margins suitable for use with a three hole punch can be obtained using the
.B -m
flag.
This flag may be used with either portrait or landscape mode.
In portrait mode the
.B -m
flag and any indentation specified by a
.B -i
flag are additive.
.PP
To get top and bottom margins suitable for printing 66 lines per page
(with the default 10 point size), use the
.B -n
flag.  This
option works good when printing manual pages.
.PP
The
.B -o
flag is immediately followed by a range specification that indicates
which pages are to be printed.
A range specification is a comma-separated list of numbers and ranges.
A number N selects the Nth page;
a range N:M selects the Nth through Mth pages, inclusive;
an initial :N selects from the beginning up to and including the
Nth page; and a final N: selects from the Nth page to the end.
The default, ``:'', is to print all pages.
.PP
The
.B -p
flag indicates that
.B pr(1)
is to be used to perform pagination and print page headers.
An argument string to be passed on to \fBpr(1)\fR can immediately
follow the flag.
The usual way of producing multicolumn output is to pass a
.B -n
flag to
\fBpr(1)\fR, where
.B n
is the number of columns to generate.
Note that this string must be properly quoted if it contains whitespace,
metacharacters, backslashes, etc.
\fILwf\fR passes on the number of lines and columns to
\fBpr(1)\fR so that multicolumn output is handled correctly.
When specifying multicolumns you are responsible for selecting
an appropriate point size and/or landscape format.
The formfeed character is handled incorrectly by some versions of
\fBpr(1)\fR when multiple columns have been specified.
Instead of starting a new column or page it simply passes formfeeds
through.
.B Lwf
filters out these formfeeds.
.PP
.I Lwf
normally includes a standard internal PostScript prologue in its output.
Optionally, a compile time option is not to include it in 
.I lwf.
In this
case
.I lwf
will look for the file lwf.prologue in a standard place (usually 
/usr/local/lib/lwf.prologue).  The argument following a
.B -P
flag specifies a prologue file to be substituted for the standard
system file.
.PP
The default is to perform page reversal, which is correct for printers
like the Apple LaserWriter.
The
.B -r
flag disables page reversal so that the pages appear in
the correct sequence in the output tray of printers like the
NEC Silentwriter LC-890.
.PP
The
.B -s
flag, immediately followed by a 7, 8, 9, 10, 11, or 12 causes
the corresponding point size to be used.
The default point size is 10.
.PP
By default, the output of
.I lwf
is intended to go through a spooler that monitors the status of the
printer and separates jobs.
The
.B -S
flag indicates that such a monitor is not being used and that a
header page should be printed to separate jobs and end-of-file characters
should be inserted in the output.
.PP
The
.B -t
flag is immediately followed by a number indicating the distance between
tab stops.
The default value is 8 characters.
.PP
The
.B -d
and
.B -v
flags are used for debugging.
.SH EXAMPLES
The command
.sp 2
.ti +5
lwf -p-2 -i0.5 -s12 file1.c file2.c | lpr -Plw
.sp 2
would print the two files on printer 'lw' in portrait format with
page headings, indenting 0.5 inches from the left edge, using 12
point type.
Each file would be printed in two columns.
.sp 1
The command
.sp 2
.ti +5
lwf -l -s7 -p'-h foo' | lpr -Plw
.sp 2
would print the standard input with page headings in landscape format
using 7 point type.
The filename for the header line would be 'foo'.
.sp 1
The command
.sp 2
.ti +5
lwf -p'-h foo .login -h bar .cshrc' | lpr -Plw
.sp 2
would print the file ``.login'' with a header title ``foo'' and ``.cshrc''
with the header title ``bar''.
.sp 1
A useful csh alias is:
.sp 2
.ti +5
alias prlw 'lwf -s7 -t4 -l -p-2 \\!* | lpr -Plw'
.sp 2
which, when invoked as
.sp 2
.ti +5
prlw foo1 foo2
.sp 2
prints foo1 and foo2, two columns per page, on printer 'lw'.
.SH FILES
/tmp/lwfXXXXXX		\- temporary file used for page reversal
.SH SEE ALSO
pr(1), lpr(1)
.SH LIMITATIONS
The maximum input line length is 1024 characters.
This should not present a problem since the corresponding output line
length would be too long to be printed.
The program quits and prints a message if an input line is too long.
Output lines that are too long are normally silently truncated by the
printer.
.sp 2
.I Lwf
can be compiled such that there is a limit on the number of pages it can
produce.
.SH BUGS
It might be argued that flags should be allowed to be interspersed between
file arguments.
.sp 2
The character '\\001' (SOH) should not appear in the input as it is used
within the program to delimit columns.
The first time the program reads this character it determines the number
of columns being printed; all subsequent lines are expected to
have this number of columns or be ``single column''.
.sp 2
A \fB-s\fR flag should not be passed on to \fBpr(1)\fR since
.I lwf
uses it.
.sp 2
The program can only handle the 6 different point sizes and the single
font.
.sp 2
So much for metric.
.SH AUTHOR
Barry Brachman
.br
Dept. of Computer Science
.br
University of British Columbia

With additions/revisions and Amiga port by:

Dr. Samuel Paolucci
.br
1351 Roselli Dr.
.br
Livermore, CA 94550
.br
(415)294-2018
.br
ARPA: paolucci@snll-arpagw.llnl.gov

SHAR_EOF
cat << \SHAR_EOF > lwf.prologue
%%EndComments
% PostScript Prologue for lwf V2.0a
/B {NW 0 rmoveto}bind def
/EP {SV restore /#copies exch def showpage}bind def
/L /lineto load def
/M /moveto load def
/NP /newpath load def
/S /show load def
/SHP {SP 2 setlinewidth}bind def
/SLP {SP 612 0 translate 90 rotate}bind def
/SP {/SV save def findfont exch scalefont setfont ( )
  stringwidth pop dup /W exch def neg /NW exch def}bind def
/ST /stroke load def
/T {W mul 0 rmoveto}bind def
%%EndProlog
SHAR_EOF
cat << \SHAR_EOF > psclear.uu

begin 644 psclear
!!```$
``
end
size 1
SHAR_EOF
cat << \SHAR_EOF > range.c
/* vi: set tabstop=4 : */

/*
 * Return 1 if the given number is in the specified range,
 * -1 if there is an error in the range specification,
 * 0 otherwise
 *
 * Ranges have a similar (we use a colon instead of a dash) form to that
 * used by [nt]roff; i.e., a comma separated list of specifiers of the
 * form:
 *	1) n	means     x such that x = n
 *	2) :n	means all x such that x <= n
 *	3) n:	means all x such that x >= n
 *	4) n:m	means all x such that n <= x <= m
 *	5) :	means all x
 * n is an int
 *
 * Problems:
 * The routine prints an error message if the range is strange - this
 * might not always be desirable.
 *
 * Jul/86 BJB
 */

/*
 * ===================================================================
 *
 * Permission is given to freely copy and distribute this software
 * providing:
 *
 *	1) You do not sell it,
 *	2) You do not use it for commercial advantage, and
 *	3) This notice accompanies the distribution
 *
 * Copyright (c) 1988
 * Barry Brachman
 * Dept. of Computer Science
 * Univ. of British Columbia
 * Vancouver, B.C. V6T 1W5
 *
 * .. {ihnp4!alberta, uw-beaver, uunet}!ubc-vision!ubc-csgrads!brachman
 * brachman@grads.cs.ubc.cdn
 * brachman%ubc.csnet@csnet-relay.arpa
 * brachman@ubc.csnet
 * ====================================================================
 */

#include <ctype.h>
#include <stdio.h>

#define streq(a, b)			(!strcmp((a), (b)))
#define smalloc(a, t, s)	((a = (t) malloc((unsigned) (s))) == NULL)
#define srealloc(a, t, b, s) ((a = (t) realloc(b, (unsigned) (s))) == NULL)
#define max(a, b)			((a) >= (b) ? (a) : (b))

#define SEP_CHAR	':'
#define SEP_T		0		/* separator token */
#define NUM_T		1		/* number token */
#define BAD_T		2		/* token for bad character */

#define STR_ALLOC	80

struct range_header {
	char *range_str;		/* range character string */
	int range_str_alloc;	/* length in bytes */
	int nranges;			/* number of range entries */
	struct range *range;
} range_header = {
	NULL, 0, NULL
};

/*
 * If hflag (lflag) is non-zero then the high (low) value is present
 */
struct range {
	char hflag;			/* high value present */
	char lflag;			/* low value present */
	int high;			/* high part of range */
	int low;			/* low part of range */
};

#ifdef RANGE_DEBUG

/*
 * This is a program for demonstrating and debugging the range checking
 * code
 * Enter a range when prompted
 * (If there is a previous range shown you may enter <return> to
 * reselect it)
 * Enter a value
 * The program will indicate whether the value is in the given range
 */

char buf[BUFSIZ], range[BUFSIZ];

main(argc, argv)
int argc;
char **argv;
{
	register int i;
	char *p;
	struct range_header *rh;
	struct range *r;
	FILE *fp;
	char *gets(), *index(), *strcpy();

	buf[0] = range[0] = '\0';
	if (argc == 2) {
		if ((fp = fopen(argv[1], "r")) == NULL) {
			(void) fprintf(stderr, "Can't open %s\n", argv[1]);
			exit(1);
			/*NOTREACHED*/
		}
	}
	else
		fp = stdin;

	if (fp == stdin)
		(void) printf("Range? ");
	while (fgets(buf, sizeof(buf), fp) != NULL) {
		if ((p = index(buf, '\n')) != NULL)
			*p = '\0';
		if (buf[0] != '\0') {
			(void) strcpy(range, buf);
			if (checkrange(range)) {
				if (fp == stdin)
					(void) printf("Range? ");
				continue;
			}
			rh = &range_header;
			(void) printf("%s (%d alloc) (%d ranges):\n",
			      rh->range_str, rh->range_str_alloc, rh->nranges);
			for (r = rh->range, i = 0; i < rh->nranges; i++, r++)
				(void) printf("hflag=%d lflag=%d high=%d low=%d\n",
					r->hflag, r->lflag, r->high, r->low);
		}
		if (fp != stdin)
			continue;
		(void) printf("Value? ");
		if (gets(buf) == NULL)
			break;
		i = inrange(atoi(buf), range);
		if (i == 0)
			(void) printf("\tno\n");
		else if (i == 1)
			(void) printf("\tyes\n");
		else if (i == -1)
			(void) printf("\terror\n");
		else
			(void) printf("\tbad result\n");
		(void) printf("Range ['%s']? ", range);
	}
	(void) printf("\n");
}
#endif RANGE_DEBUG

/*
 * Check and compile the given range specification and then determine if
 * the number is in the range
 * Return -1 if there is a compilation error, 1 if the number is in the
 * range, or 0 if the number isn't in the range
 */
inrange(num, range_spec)
int num;
char *range_spec;
{
	register int i, rc;
	register struct range_header *rh;
	register struct range *r;

	if (checkrange(range_spec))
		return(-1);
	rh = &range_header;
	rc = 0;
	for (r = rh->range, i = 0; rc == 0 && i < rh->nranges; i++, r++) {
		if (r->hflag) {
			if (num > r->high)
				continue;
			if (r->lflag && num < r->low)
				continue;
			rc = 1;
		}
		else if (r->lflag) {
			if (num >= r->low)
				rc = 1;
		}
		else				/* both unset -> ":" */
			rc = 1;
	}
	return(rc);
}

/*
 * Check and compile a range specification
 * Print a message and return -1 on error; return 0 oth.
 *
 * Could be more efficient by allocating more structures at a time... SMOP
 */
checkrange(range_spec)
char *range_spec;
{
	register struct range_header *rh;
	register struct range *r;
	int len;
	int ltype, lval, rtype, rval;
	char *p;
	char *malloc(), *realloc(), *strcpy();

	rh = &range_header;
	/*
	 * Check if the previous range is being used
	 */
	if (rh->range_str != NULL && streq(range_spec, rh->range_str))
		return(0);

	/*
	 * New range spec
	 * If there is enough space, reuse it; oth. allocate enough
	 * (amount allocated never shrinks)
	 */
	len = max(strlen(range_spec) + 1, STR_ALLOC);
	if (rh->range_str != NULL  && len > rh->range_str_alloc) {
		free(rh->range_str);
		rh->range_str = (char *) malloc((unsigned) len);
		rh->range_str_alloc = len;
	}
	else if (rh->range_str == NULL) {
		rh->range_str = (char *) malloc((unsigned) len);
		rh->range_str_alloc = len;
	}
	(void) strcpy(rh->range_str, range_spec);
	if (rh->range != NULL)
		free((char *) rh->range);
	rh->range = NULL;

	p = range_spec;
	while (1) {
		lval = getnum(&p, &ltype);
		if (ltype == BAD_T) {
			(void) fprintf(stderr, "range: bad first number\n");
			*rh->range_str = '\0';		/* invalidate */
			return(-1);
		}

		if (rh->range == NULL) {
			smalloc(r, struct range *, sizeof(struct range));
			rh->nranges = 1;
		}
		else {
			len = sizeof(struct range) * ++(rh->nranges);
			srealloc(r, struct range *, (char *) rh->range, len);
		}
		rh->range = r;
		r += rh->nranges - 1;		/* point to new one */
		r->hflag = r->lflag = 0;
		r->high = r->low = 0;

		/*
		 * If ltype != NUM_T there is no lval
		 */
		if (ltype == NUM_T) {
			r->lflag = 1;
			r->low = lval;
		}

		switch (*p) {
		case ',':			/* single number */
			r->hflag = 1;
			r->high = lval;
			p++;
			continue;

		case '\0':			/* single number at end */
			r->hflag = 1;
			r->high = lval;
			return(0);

		case ':':
			p++;
			if (*p == '\0')		/* no rval */
				return(0);
			if (*p == ',') {	/* no rval */
				p++;
				break;
			}

			rval = getnum(&p, &rtype);
			if (rtype == BAD_T) {
				(void) fprintf(stderr, "range: bad second number\n");
				*rh->range_str = '\0';
				return(-1);
			}

			if (lval > rval) {
				(void) fprintf(stderr, "range: values reversed\n");
				*rh->range_str = '\0';	/* invalidate */
				return(-1);
			}
			r->hflag = 1;
			r->high = rval;
			if (*p == '\0')
				return(0);
			if (*p == ',')
				p++;
			break;

		default:
			(void) fprintf(stderr, "range: bad character\n");
			*rh->range_str = '\0';		/* invalidate */
			return(-1);
		}
	}
}

static
getnum(pp, type)
char **pp;
int *type;
{
	register int sign, val;
	register char *p;

	p = *pp;
	if (!isdigit(*p) && *p != '-') {
		if (*p == SEP_CHAR)
			*type = SEP_T;
		else
			*type = BAD_T;
		return(0);
	}
	sign = 1;
	if (*p == '-') {
		sign = -1;
		p++;
	}
	if (!isdigit(*p)) {
		*type = BAD_T;
		return(0);
	}
	for (val = 0; isdigit(*p) && *p != '\0'; p++)
		val = val * 10 + *p - '0';
	if (*p != '\0' && *p != ',' && *p != SEP_CHAR) {
		*type = BAD_T;
		return(0);
	}
	*pp = p;
	*type = NUM_T;
	return(sign * val);
}

SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.