Home | History | Annotate | Download | only in codereview
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * lwlp - Convert ASCII text to PostScript
     28  *
     29  * Usage:
     30  *	lwlp [-{2|4|8}] [-p] [-L] [-r] [-n#] [-l#|-w#] [-c#] [-t#]
     31  *		[-hstring] [-Bstring] [-Istring] [-Xstring] [-Pfile] [file ...]
     32  *
     33  * Options:
     34  *	-{1|2|4|8}	print multiple logical pages per page
     35  *	-d		debug, don't remove temporary file
     36  *	-L		specify Landscape instead of Portrait
     37  *	-p		filter input through pr
     38  *	-r		toggle page reversal flag (default is off)
     39  *	-e		elide unchanged functions
     40  *	-n#		number with numberwidth digits
     41  *	-l#		specify number of lines/logical page, default 66
     42  *	-w#		specify number of columns
     43  *	-c#		specify number of copies
     44  *	-t#		specify tab spacing
     45  *	-htext		specify header text
     46  *	-Btext		specify bold font selector
     47  *	-Itext		specify italic font selector
     48  *	-Xtext		specify bold-italic font selector
     49  *	-Gtext		specify graying selector
     50  *	-Pfile		specify different Postscript prologue file
     51  *
     52  * If no files are specified, stdin is used.
     53  * Form feeds handled
     54  * Backspacing with underlining (or overprinting) works
     55  * The output conforms to Adobe 2.0
     56  *
     57  * Problems:
     58  *	- assumes fixed-width (non-proportional) font in some places
     59  *	- can't back up (using backspaces) over tabs
     60  *	- assumes 8.5 x 11.0 paper
     61  *	- uses logical page with aspect ratio of 3 * 4
     62  *
     63  */
     64 
     65 #define	USAGE1	"[-{1|2|4|8}] [-p] [-L] [-r] [-n<numberwidth]"
     66 #define	USAGE2	"[-l<lines>|-w<columns>] [-c<count>] [-t<tabs>]"
     67 #define	USAGE3	"[-hstring] [-Bstring] [-Istring] [-Xstring] [-Gstring]"
     68 #define	USAGE4	"[-Pfile] [file ...]"
     69 #define	USAGE6	"[-hstring] [-e] [-y comment] oldfile newfile"
     70 
     71 #include <stdio.h>
     72 #include <string.h>
     73 #include <stdlib.h>
     74 #include <sys/file.h>
     75 #include <ctype.h>
     76 #include <pwd.h>
     77 #include <sys/utsname.h>
     78 #include <sys/stat.h>
     79 #include <unistd.h>
     80 #include <sys/types.h>
     81 #include <time.h>
     82 #include <stdarg.h>
     83 
     84 /*
     85  * Configurable...
     86  * BUFOUT should be fairly large
     87  */
     88 #define	BUFIN			1024	/* maximum length of an input line */
     89 #define	BUFOUT			(BUFIN * 5)
     90 #define	MAXPAGES		10000
     91 #define	REVERSE_OFF		0
     92 
     93 #define	DEFAULT_PAPER_HEIGHT	11.0
     94 #define	DEFAULT_PAPER_WIDTH	8.50
     95 #define	DEFAULT_PAGE_HEIGHT	10.0
     96 #define	DEFAULT_PAGE_WIDTH	7.50
     97 #define	DEFAULT_LINES_PER_PAGE	66
     98 #define	DEFAULT_TAB_SIZE	8
     99 static char	*default_font = "Courier";
    100 static char	*default_font_bold = "Courier-Bold";
    101 static char	*default_font_italic = "Courier-Oblique";
    102 static char	*default_font_bold_italic = "Courier-BoldOblique";
    103 static char	*select_default_font = "FRN";
    104 static char	*select_default_font_bold = "FRB";
    105 static char	*select_default_font_italic = "FIN";
    106 static char	*select_default_font_bold_italic = "FIB";
    107 #define	DEFAULT_FONT			select_default_font
    108 #define	DEFAULT_FONT_BOLD		select_default_font_bold
    109 #define	DEFAULT_FONT_ITALIC		select_default_font_italic
    110 #define	DEFAULT_FONT_BOLD_ITALIC	select_default_font_bold_italic
    111 #define	DEFAULT_CHAR_WIDTH	(.6)
    112 #define	DEFAULT_SPACES_AFTER_NUMBER	1
    113 #define	DEFAULT_DESCENDER_FRACTION	0.3
    114 #define	LWLP			"lwlp"
    115 #define	CODEREVIEW		"codereview"
    116 #define	END_C_FUNCTION		'}'
    117 #define	END_ASM_FUNCTION	"SET_SIZE("
    118 static char	*banner =
    119 	"**********************************************************";
    120 
    121 /*
    122  * PostScript command strings
    123  */
    124 #define	LINETO			"lineto"
    125 #define	NEWPATH			"newpath"
    126 #define	SETLINEWIDTH		"setlinewidth"
    127 #define	STROKE			"stroke"
    128 /*
    129  * PostScript command strings defined in the prologue file
    130  */
    131 #define	BACKSPACE		"B"
    132 #define	MOVETO			"M"	/* x y */
    133 #define	SHOW			"S"	/* string */
    134 #define	TAB			"T"	/* spaces */
    135 #define	ZEROMOVETO		"Z"	/* y */
    136 #define	SELECT_FONT		"SFT"	/* size font */
    137 #define	SET_WIDTHS		"SWT"
    138 #define	START_PAGE		"SPG"	/* angle scale x y */
    139 #define	END_PAGE		"EPG"
    140 #define	FLUSH_PAGE		"FPG"	/* ncopies */
    141 #define	SHADE			"SHD"	/* x0 y0 x1 y1 */
    142 
    143 /*
    144  * Conformance requires that no PostScript line exceed 256 characters
    145  */
    146 #define	POINTS_PER_INCH		72
    147 #define	MAX_OUTPUT_LINE_LENGTH	256
    148 
    149 #define	START_X			0	/* position of start of each line */
    150 #define	THREE_HOLE_X		1.0	/* portrait x offset (inches) 3 hole */
    151 #define	THREE_HOLE_Y		0.5	/* landscape y offset (inches) 3 hole */
    152 #define	RULE_WIDTH		0.25	/* width in units of paging rules */
    153 
    154 static struct print_state {
    155 	int	page_count;
    156 	int	logical_page_count;
    157 	int	lineno;
    158 	long	offset;
    159 	float	row;
    160 	char	*font;
    161 }	current, saved;
    162 
    163 struct format_state {
    164 	int	numberwidth, linenumber, altlinenumber;
    165 	int	makegray;
    166 	char	*font;
    167 };
    168 
    169 static int	change_seen, dots_inserted, in_change, old_stuff, makegray;
    170 static int	lines_per_page;
    171 static int	columns;
    172 static float	point_size;
    173 static int	start_x, start_y, end_x;
    174 static int	landscape, rot_text;
    175 
    176 static int	ncopies;
    177 static int	tabstop;
    178 static int	reverse;
    179 static int	elide;
    180 static int	usetmp;
    181 static int	dflag, lflag, pflag, vflag, wflag;
    182 static int	numberwidth, linenumber, altlinenumber;
    183 static int	boldlength, itlclength, bitclength, graylength;
    184 static char	*boldstring, *itlcstring, *bitcstring, *graystring;
    185 #define	HEADER_EXPLICIT	1
    186 #define	HEADER_IMPLICIT	2
    187 static int	header = HEADER_IMPLICIT;
    188 static char	*headerstring;
    189 static char	*bannerfile;
    190 
    191 static char	bufin[BUFIN];		/* input buffer */
    192 static char	bufout[BUFOUT];		/* output buffer */
    193 static long	*page_map;		/* offset of first byte of each page */
    194 
    195 static char	*username, *hostname, *currentdate;
    196 static char	*comment;
    197 
    198 static void	preamble(void);
    199 static void	postamble(void);
    200 static void	setcurrentfont(char *, FILE *);
    201 static void	savestate(FILE *);
    202 static void	restorestate(FILE *);
    203 static void	save_format_state(struct format_state *);
    204 static void	printfile(FILE *);
    205 static int	printpage(FILE *, FILE *);
    206 static int	startpage(FILE *);
    207 static void	endpage(FILE *);
    208 static void	copypage(FILE *, long, long);
    209 static void	process_elide(FILE *);
    210 static void	setheaderfile(char *);
    211 static void	restore_format_state(struct format_state *, FILE *);
    212 static void	flushpage(FILE *);
    213 static void	setuppage(FILE *);
    214 static void	reversepages(FILE *);
    215 static void	proc(char *, FILE *);
    216 static void	setup(void);
    217 static int	printbanner(char *, FILE *);
    218 static char	*fgetline(char *, int, FILE *);
    219 static void	fatal(char *fmt, ...);
    220 
    221 static char	*prologue;
    222 static char	*progname;
    223 static int	iscodereview;
    224 
    225 static char	*default_prologue[] = {
    226 "%%EndComments\n",
    227 "%\n",
    228 "% PostScript Prologue for lwlp LaserWriter Line Printer\n",
    229 "%\n",
    230 "/SFT {findfont exch scalefont setfont}bind def\n",
    231 "/SWT {( ) stringwidth pop dup /W exch def neg /NW exch def}bind def\n",
    232 "/SPG {/SV save def translate dup scale rotate}bind def\n",
    233 "/EPG {SV restore}bind def\n",
    234 "/FPG {/#copies exch def showpage}bind def\n",
    235 "/B {NW 0 rmoveto}def\n",
    236 "/M /moveto load def\n",
    237 "/T {W mul 0 rmoveto}def\n",
    238 "/S /show load def\n",
    239 "/Z {0 exch moveto}bind def\n",
    240 "/SHD {save 5 1 roll			% S x1 y1 x0 y0\n",
    241 "	2 copy moveto			% S x1 y1 x0 y0\n",
    242 "	3 index exch lineto		% S x1 y1 x0\n",
    243 "	3 -1 roll 2 index lineto	% S y1 x0\n",
    244 "	exch lineto			% S\n",
    245 "	0.95 setgray fill		% S\n",
    246 "	restore}def\n",
    247 "%%EndProlog\n",
    248 	NULL
    249 };
    250 
    251 struct layout {
    252 	float	scale;
    253 	int	pages, page_rows, page_cols;
    254 	int	rotation;
    255 };
    256 static struct layout	*layoutp;
    257 static struct layout	layout1 = { 1.000000, 1, 1, 1, 0 };
    258 static struct layout	layout2 = { 0.666666, 2, 2, 1, 90 };
    259 static struct layout	layout4 = { 0.500000, 4, 2, 2, 0 };
    260 static struct layout	layout8 = { 0.333333, 8, 4, 2, 90 };
    261 
    262 static int	box_width, box_height;
    263 static int	gap_width, gap_height;
    264 static int	margin_x, margin_y;
    265 
    266 static struct position {
    267 	int	base_x;
    268 	int	base_y;
    269 }	positions[8];
    270 
    271 int
    272 main(int argc, char **argv)
    273 {
    274 	int	ch, i, j, first_file;
    275 	char	*pc;
    276 	FILE	*infile;
    277 
    278 	if ((pc = strrchr(argv[0], '/')) != NULL)
    279 		progname = pc + 1;
    280 	else
    281 		progname = argv[0];
    282 
    283 	lines_per_page = DEFAULT_LINES_PER_PAGE;
    284 	layoutp = &layout1;
    285 	tabstop = DEFAULT_TAB_SIZE;
    286 	current.page_count = 0;
    287 	ncopies = 1;
    288 	reverse = REVERSE_OFF;
    289 
    290 	/*LINTED*/
    291 	if (iscodereview = strncmp(progname, CODEREVIEW,
    292 	    sizeof (CODEREVIEW) - 1) == 0) {
    293 		layoutp = &layout2;
    294 		numberwidth = 4;
    295 		columns = 85;		/* extra space for numbering */
    296 		wflag = -1;
    297 	}
    298 
    299 	while ((ch = getopt(argc, argv,
    300 	    "1248B:c:deG:h:I:l:Ln:P:prt:vw:X:y:")) != -1) {
    301 		switch (ch) {
    302 		case '1':
    303 			layoutp = &layout1;
    304 			break;
    305 		case '2':
    306 			layoutp = &layout2;
    307 			break;
    308 		case '4':
    309 			layoutp = &layout4;
    310 			break;
    311 		case '8':
    312 			layoutp = &layout8;
    313 			break;
    314 		case 'B':
    315 			boldlength = strlen(optarg);
    316 			boldstring = malloc((size_t)(boldlength + 1));
    317 			(void) strcpy(boldstring, optarg);
    318 			break;
    319 		case 'c':
    320 			ncopies = atof(optarg);
    321 			if (ncopies <= 0) {
    322 				fatal("number of copies must be > 0");
    323 				/*NOTREACHED*/
    324 			}
    325 			break;
    326 		case 'd':
    327 			dflag = 1;
    328 			break;
    329 		case 'e':
    330 			elide = 1;
    331 			break;
    332 		case 'G':
    333 			graylength = strlen(optarg);
    334 			graystring = malloc((size_t)(graylength + 1));
    335 			(void) strcpy(graystring, optarg);
    336 			break;
    337 		case 'h':
    338 			header = HEADER_EXPLICIT;
    339 			i = strlen(optarg);
    340 			headerstring = malloc((size_t)(i + 1));
    341 			(void) strcpy(headerstring, optarg);
    342 			if (strcmp(headerstring, "-") == 0)
    343 				header = HEADER_IMPLICIT;
    344 			break;
    345 		case 'I':
    346 			itlclength = strlen(optarg);
    347 			itlcstring = malloc((size_t)(itlclength + 1));
    348 			(void) strcpy(itlcstring, optarg);
    349 			break;
    350 		case 'l':
    351 			lines_per_page = atoi(optarg);
    352 			if (lines_per_page < 1) {
    353 				fatal("invalid number of lines/page");
    354 				/*NOTREACHED*/
    355 			}
    356 			lflag = 1;
    357 			if (wflag > 0) {
    358 				fatal("can't have both -l and -w");
    359 				/*NOTREACHED*/
    360 			}
    361 			wflag = 0;
    362 			break;
    363 		case 'L':
    364 			landscape = 1;
    365 			break;
    366 		case 'm':
    367 			break;
    368 		case 'n':
    369 			numberwidth = atoi(optarg);
    370 			if (numberwidth < 2) {
    371 				fatal("invalid numbering width");
    372 				/*NOTREACHED*/
    373 			}
    374 			break;
    375 		case 'P':
    376 			prologue = optarg;
    377 			break;
    378 		case 'p':
    379 			pflag = 1;
    380 			break;
    381 		case 'r':
    382 			reverse = !reverse;
    383 			break;
    384 		case 't':
    385 			tabstop = atoi(optarg);
    386 			if (tabstop < 1) {
    387 				fatal("negative tabstop");
    388 				/*NOTREACHED*/
    389 			}
    390 			break;
    391 		case 'v':
    392 			vflag = 1;
    393 			break;
    394 		case 'w':
    395 			columns = atoi(optarg);
    396 			if (columns < 1) {
    397 				fatal("invalid number of columns");
    398 				/*NOTREACHED*/
    399 			}
    400 			wflag = 1;
    401 			if (lflag) {
    402 				fatal("can't have both -l and -w");
    403 				/*NOTREACHED*/
    404 			}
    405 			break;
    406 		case 'X':
    407 			bitclength = strlen(optarg);
    408 			bitcstring = malloc((size_t)(bitclength + 1));
    409 			(void) strcpy(bitcstring, optarg);
    410 			break;
    411 		case 'y':
    412 			comment = optarg;
    413 			break;
    414 		default:
    415 			(void) fprintf(stderr,
    416 			    "usage: %s %s\n\t%s\n\t%s\n\t%s\n",
    417 			    iscodereview ? LWLP : progname,
    418 			    USAGE1, USAGE2, USAGE3, USAGE4);
    419 			if (iscodereview)
    420 				(void) fprintf(stderr, "\t%s [%s flags] %s\n",
    421 				    CODEREVIEW, LWLP, USAGE6);
    422 			exit(1);
    423 		}
    424 	}
    425 
    426 	if (elide && !iscodereview) {
    427 		fatal("-e option valid only with codereview");
    428 		/*NOTREACHED*/
    429 	}
    430 	usetmp = reverse || elide;
    431 	/* allocate page_map if we need one */
    432 	if (reverse) {
    433 		page_map = malloc((size_t)(MAXPAGES * sizeof (long *)));
    434 		if (page_map == NULL) {
    435 			fatal("unable to allocate memory for page reversal");
    436 			/*NOTREACHED*/
    437 		}
    438 	}
    439 
    440 	/*
    441 	 * Check that all files are readable
    442 	 * This is so that no output at all is produced if any file is not
    443 	 * readable in case the output is being piped to a printer
    444 	 */
    445 	first_file = optind;
    446 	for (j = first_file; j < argc; j++) {
    447 		if (access(argv[j], R_OK) == -1 && !(iscodereview &&
    448 		    strcmp(argv[j], "-") == 0)) {
    449 			fatal("cannot access %s", argv[j]);
    450 			/*NOTREACHED*/
    451 		}
    452 	}
    453 	if (iscodereview && (first_file + 2) != argc) {
    454 		fatal("codereview: need old and new file");
    455 		/*NOTREACHED*/
    456 	}
    457 
    458 	/* compute logical point size, logical dimensions */
    459 	if (!landscape) {
    460 		rot_text = layoutp->rotation;
    461 		start_y = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH;
    462 		start_x = START_X;
    463 		end_x = DEFAULT_PAGE_WIDTH * POINTS_PER_INCH;
    464 		if (wflag) {
    465 			point_size = DEFAULT_PAGE_WIDTH * POINTS_PER_INCH /
    466 			    ((columns + 0.5) * DEFAULT_CHAR_WIDTH);
    467 			lines_per_page = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH /
    468 			    point_size;
    469 		} else {
    470 			point_size = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH /
    471 			    (lines_per_page + 0.5);
    472 			columns = DEFAULT_PAGE_WIDTH * POINTS_PER_INCH /
    473 			    (point_size * DEFAULT_CHAR_WIDTH);
    474 		}
    475 	} else {
    476 		rot_text = 90 - layoutp->rotation;
    477 		start_y = DEFAULT_PAGE_WIDTH * POINTS_PER_INCH;
    478 		start_x = START_X;
    479 		end_x = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH;
    480 		if (wflag) {
    481 			point_size = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH /
    482 			    ((columns + 0.5) * DEFAULT_CHAR_WIDTH);
    483 			lines_per_page = DEFAULT_PAGE_WIDTH * POINTS_PER_INCH /
    484 			    point_size;
    485 		} else {
    486 			point_size = DEFAULT_PAGE_WIDTH * POINTS_PER_INCH /
    487 			    (lines_per_page + 0.5);
    488 			columns = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH /
    489 			    (point_size * DEFAULT_CHAR_WIDTH);
    490 		}
    491 	}
    492 
    493 	box_height = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH / layoutp->page_rows;
    494 	if (layoutp->rotation == 0)
    495 		box_width = box_height /
    496 		    DEFAULT_PAGE_HEIGHT * DEFAULT_PAGE_WIDTH;
    497 	else
    498 		box_width = box_height *
    499 		    DEFAULT_PAGE_HEIGHT / DEFAULT_PAGE_WIDTH;
    500 	gap_width = DEFAULT_PAPER_WIDTH * POINTS_PER_INCH /
    501 	    layoutp->page_cols - box_width;
    502 	gap_height = DEFAULT_PAPER_HEIGHT * POINTS_PER_INCH /
    503 	    layoutp->page_rows - box_height;
    504 	margin_x = gap_width/2;
    505 	margin_y = gap_height/2;
    506 
    507 	columns -= numberwidth + DEFAULT_SPACES_AFTER_NUMBER;
    508 	if (columns <= 0) {
    509 		fatal("numbering width exceeds number of columns");
    510 		/* NOT REACHED */
    511 	}
    512 	/* compute physical "lower left corner" of each logical page */
    513 	for (j = 0; j < layoutp->pages; j++) {
    514 		int	phys_row;		/* 0 is bottom row */
    515 		int	phys_col;		/* 0 is left column */
    516 
    517 		if (landscape == (rot_text == 0)) {
    518 			/* logical pages run physically up and down */
    519 			phys_row = j % layoutp->page_rows;
    520 			phys_col = j / layoutp->page_rows;
    521 		} else {
    522 			/* logical pages run physically left to right */
    523 			phys_row = j / layoutp->page_cols;
    524 			phys_col = j % layoutp->page_cols;
    525 		}
    526 		if (rot_text == 0) {
    527 			/* top physical row is logically first */
    528 			phys_row = layoutp->page_rows - 1 - phys_row;
    529 		}
    530 
    531 		positions[j].base_x = margin_x +
    532 		    phys_col * (box_width + gap_width);
    533 		positions[j].base_y = margin_y +
    534 		    phys_row * (box_height + gap_height);
    535 		if (rot_text != 0) {
    536 			positions[j].base_x += box_width;
    537 		}
    538 	}
    539 
    540 	if (vflag) {
    541 		(void) fprintf(stderr, "%s:\n\n", progname);
    542 		(void) fprintf(stderr, "Lines/page = %d\n", lines_per_page);
    543 		(void) fprintf(stderr, "Columns = %d\n", columns);
    544 		for (j = 0; j < layoutp->pages; j++) {
    545 			(void) fprintf(stderr, "\tx=%3d, y=%3d\n",
    546 			    positions[j].base_x, positions[j].base_y);
    547 		}
    548 		(void) fprintf(stderr, "box_width=%3d, box_height=%3d\n",
    549 		    box_width, box_height);
    550 		(void) fprintf(stderr, "gap_width=%3d, gap_height=%3d\n",
    551 		    gap_width, gap_height);
    552 	}
    553 
    554 	setup();
    555 	preamble();
    556 
    557 	if (iscodereview) {
    558 		char	command[BUFSIZ];
    559 
    560 		(void) snprintf(command, BUFSIZ, "diff -b -D %s %s %s",
    561 		    CODEREVIEW, argv[first_file+1], argv[first_file]);
    562 		infile = popen(command, "r");
    563 		bannerfile = argv[first_file+1];
    564 		if (ungetc(getc(infile), infile) == EOF) {
    565 			(void) pclose(infile);
    566 			(void) sprintf(command,
    567 			    "echo No differences encountered");
    568 			infile = popen(command, "r");
    569 		}
    570 		setheaderfile(bannerfile);
    571 		printfile(infile);
    572 		(void) pclose(infile);
    573 	} else if (first_file == argc) {	/* no files on command line */
    574 		if (vflag)
    575 			(void) fprintf(stderr, "\tprinting stdin\n");
    576 		setheaderfile("stdin");
    577 		printfile(stdin);
    578 	} else {
    579 		for (i = first_file; i < argc; i++) {
    580 			if ((infile = fopen(argv[i], "r")) == (FILE *)NULL) {
    581 				fatal("can't open %s for reading", argv[i]);
    582 				/*NOTREACHED*/
    583 			}
    584 			if (pflag) {
    585 				char	cmdbuf[BUFSIZ];
    586 				(void) snprintf(cmdbuf, BUFSIZ, "pr %s",
    587 				    argv[i]);
    588 				(void) fclose(infile);
    589 				infile = popen(cmdbuf, "r");
    590 			}
    591 			if (vflag)
    592 				(void) fprintf(stderr, "\tprinting %s\n",
    593 				    argv[i]);
    594 			setheaderfile(argv[i]);
    595 			printfile(infile);
    596 			if (pflag)
    597 				(void) pclose(infile);
    598 			else
    599 				(void) fclose(infile);
    600 		}
    601 	}
    602 
    603 	postamble();
    604 
    605 	if (fflush(stdout) == EOF) {
    606 		fatal("write error on stdout");
    607 		/*NOTREACHED*/
    608 	}
    609 	exit(0);
    610 	/*NOTREACHED*/
    611 	/*LINTED*/
    612 }
    613 
    614 /*
    615  * Initial lines sent to the LaserWriter
    616  * Generates the PostScript header and includes the prologue file
    617  * There is limited checking for I/O errors here
    618  */
    619 void
    620 preamble(void)
    621 {
    622 	(void) printf("%%!PS-Adobe-2.0\n");
    623 	(void) printf("%%%%Creator: %s on %s\n", progname, hostname);
    624 	(void) printf("%%%%CreationDate: %s\n", currentdate);
    625 	(void) printf("%%%%For: %s\n", username);
    626 	(void) printf("%%%%DocumentFonts: %s %s %s %s\n",
    627 	    default_font, default_font_bold,
    628 	    default_font_italic, default_font_bold_italic);
    629 	(void) printf("%%%%Pages: (atend)\n");
    630 
    631 	if (prologue == NULL) {
    632 		char	**cpp;
    633 		for (cpp = default_prologue; *cpp; cpp++) {
    634 			(void) fputs(*cpp, stdout);
    635 		}
    636 	} else {
    637 		FILE	*fp;
    638 		if ((fp = fopen(prologue, "r")) == NULL) {
    639 			fatal("can't open prologue file %s", prologue);
    640 			/*NOTREACHED*/
    641 		}
    642 		while (fgets(bufin, sizeof (bufin), fp) != NULL)
    643 			(void) fputs(bufin, stdout);
    644 		(void) fclose(fp);
    645 	}
    646 	if (ferror(stdout) || fflush(stdout) == EOF) {
    647 		fatal("write error on stdout");
    648 		/*NOTREACHED*/
    649 	}
    650 
    651 	(void) printf("/%s {%f /%s %s}bind def\n", DEFAULT_FONT,
    652 	    point_size, default_font, SELECT_FONT);
    653 	(void) printf("/%s {%f /%s %s}bind def\n", DEFAULT_FONT_BOLD,
    654 	    point_size, default_font_bold, SELECT_FONT);
    655 	(void) printf("/%s {%f /%s %s}bind def\n", DEFAULT_FONT_ITALIC,
    656 	    point_size, default_font_italic, SELECT_FONT);
    657 	(void) printf("/%s {%f /%s %s}bind def\n", DEFAULT_FONT_BOLD_ITALIC,
    658 	    point_size, default_font_bold_italic, SELECT_FONT);
    659 }
    660 
    661 void
    662 postamble(void)
    663 {
    664 	(void) printf("%%%%Trailer\n");
    665 	(void) printf("%%%%Pages: %d\n", current.page_count);
    666 }
    667 
    668 int
    669 printbanner(char *filename, FILE *outfile)
    670 {
    671 	char		buffer[BUFSIZ];
    672 	struct stat	statbuf;
    673 	struct format_state	format_state;
    674 	int		nlines = 0;
    675 
    676 	/* we've already verified readability */
    677 	(void) stat(filename, &statbuf);
    678 
    679 	save_format_state(&format_state);
    680 	numberwidth = 0;
    681 
    682 	setcurrentfont(DEFAULT_FONT_BOLD_ITALIC, outfile);
    683 
    684 	current.row -= point_size;
    685 	(void) fprintf(outfile, "%d %.2f %s\n", start_x, current.row, MOVETO);
    686 	proc(banner, outfile);
    687 	nlines++;
    688 
    689 	current.row -= point_size;
    690 	(void) fprintf(outfile, "%d %.2f %s\n", start_x, current.row, MOVETO);
    691 	(void) snprintf(buffer, BUFSIZ, "%8ld %.24s", statbuf.st_size,
    692 	    ctime(&statbuf.st_mtime));
    693 	proc(buffer, outfile);
    694 	nlines++;
    695 
    696 	do {
    697 		current.row -= point_size;
    698 		(void) fprintf(outfile, "%d %.2f %s\n", start_x, current.row,
    699 		    MOVETO);
    700 		filename += sprintf(buffer, "%.*s", columns, filename);
    701 		proc(buffer, outfile);
    702 		nlines++;
    703 	} while (strlen(filename) != 0);
    704 
    705 	if (comment != NULL && comment[0] != 0) {
    706 		const char *cur = comment;
    707 		const char *endl;
    708 		int len;
    709 
    710 		while (*cur != 0) {
    711 			current.row -= point_size;
    712 			(void) fprintf(outfile, "%d %.2f %s\n", start_x,
    713 			    current.row, MOVETO);
    714 
    715 			endl = strchr(cur, '\n');
    716 			if (endl == NULL)
    717 				endl = cur + strlen(cur);
    718 
    719 			/* truncate to columns */
    720 			len = endl - cur;
    721 			if (len > columns)
    722 				len = columns;
    723 			(void) sprintf(buffer, "%.*s", len, cur);
    724 			proc(buffer, outfile);
    725 			nlines++;
    726 
    727 			if (*endl == 0)
    728 				break;
    729 			cur = endl + 1;
    730 		}
    731 	}
    732 
    733 	current.row -= point_size;
    734 	(void) fprintf(outfile, "%d %.2f %s\n", start_x, current.row, MOVETO);
    735 	proc(banner, outfile);
    736 	nlines++;
    737 
    738 	restore_format_state(&format_state, outfile);
    739 	return (nlines);
    740 }
    741 
    742 void
    743 setcurrentfont(char *newfont, FILE *outfile)
    744 {
    745 	if (current.font != newfont) {
    746 		if (newfont)
    747 			current.font = newfont;
    748 		(void) fprintf(outfile, "%s\n", current.font);
    749 	}
    750 }
    751 
    752 void
    753 savestate(FILE *f)
    754 {
    755 	current.offset = ftell(f);
    756 	saved = current;
    757 }
    758 
    759 void
    760 restorestate(FILE *f)
    761 {
    762 	char	*font;
    763 
    764 	font = current.font;
    765 	(void) fseek(f, saved.offset, 0);
    766 	current = saved;
    767 	setcurrentfont(font, f);
    768 }
    769 
    770 void
    771 save_format_state(struct format_state *fs)
    772 {
    773 	fs->numberwidth = numberwidth;
    774 	fs->linenumber = linenumber;
    775 	fs->altlinenumber = altlinenumber;
    776 	fs->makegray = makegray;
    777 	fs->font = current.font;
    778 }
    779 
    780 void
    781 restore_format_state(struct format_state *fs, FILE *outfile)
    782 {
    783 	numberwidth = fs->numberwidth;
    784 	linenumber = fs->linenumber;
    785 	altlinenumber = fs->altlinenumber;
    786 	makegray = fs->makegray;
    787 	setcurrentfont(fs->font, outfile);
    788 }
    789 
    790 /*
    791  * Print a file
    792  *
    793  * The input stream may be stdin, a file, or a pipe
    794  */
    795 void
    796 printfile(FILE *infile)
    797 {
    798 	int	eof;
    799 	char	*p;
    800 	FILE	*outfile;
    801 
    802 	if (reverse)
    803 		page_map[0] = 0L;
    804 	if (usetmp) {
    805 		(void) snprintf(bufin, BUFIN, "/tmp/%sXXXXXX", progname);
    806 		p = mktemp(bufin);
    807 		if ((outfile = fopen(p, "w+")) == NULL) {
    808 			fatal("can't open temporary file %s", p);
    809 			/* NOTREACHED */
    810 		}
    811 		if (!dflag)
    812 			(void) unlink(p);
    813 		else
    814 			(void) fprintf(stderr, "will not unlink %s\n", p);
    815 	}
    816 	else
    817 		outfile = stdout;
    818 
    819 	setcurrentfont(DEFAULT_FONT, outfile);
    820 	change_seen = 0;
    821 	dots_inserted = 0;
    822 	in_change = 0;
    823 	makegray = 0;
    824 	linenumber = 0;
    825 	altlinenumber = 0;
    826 	current.logical_page_count = 0;
    827 	do {
    828 		current.row = start_y;
    829 		eof = printpage(infile, outfile);
    830 	} while (!eof);
    831 
    832 	if (((int)current.row) != start_y)
    833 		endpage(outfile);
    834 	if ((current.logical_page_count % layoutp->pages) != 0)
    835 		flushpage(outfile);
    836 	if (vflag)
    837 		(void) fprintf(stderr, "\n");
    838 	if (fflush(outfile) == EOF) {
    839 		fatal("write error while flushing output");
    840 		/*NOTREACHED*/
    841 	}
    842 	if (usetmp) {
    843 		if (reverse)
    844 			reversepages(outfile);
    845 		else
    846 			copypage(outfile, 0L, current.offset);
    847 		(void) fclose(outfile);
    848 	}
    849 }
    850 
    851 void
    852 process_elide(FILE *outfile)
    853 {
    854 	if (!change_seen && !in_change) {
    855 		/* don't include function in output */
    856 		restorestate(outfile);
    857 		if (!dots_inserted) {
    858 			struct format_state	format_state;
    859 
    860 			save_format_state(&format_state);
    861 			numberwidth = 0;
    862 			current.lineno++;
    863 			current.row -= point_size;
    864 			setcurrentfont(DEFAULT_FONT_BOLD_ITALIC, outfile);
    865 			proc("______unchanged_portion_omitted_", outfile);
    866 			restore_format_state(&format_state, outfile);
    867 			savestate(outfile);
    868 			dots_inserted = 1;
    869 		}
    870 	} else {
    871 		savestate(outfile);
    872 		change_seen = in_change;
    873 		dots_inserted = 0;
    874 	}
    875 }
    876 
    877 /*
    878  * Process the next page
    879  * Return 1 on EOF, 0 otherwise
    880  */
    881 int
    882 printpage(FILE *infile, FILE *outfile)
    883 {
    884 	int	tmplinenumber;
    885 	char	command[BUFSIZ], flag[BUFSIZ];
    886 
    887 	if (ungetc(getc(infile), infile) == EOF)
    888 		return (1);
    889 
    890 	current.lineno = 0;
    891 	current.lineno += startpage(outfile);
    892 	if (bannerfile) {
    893 		current.lineno += printbanner(bannerfile, outfile);
    894 		bannerfile = NULL;
    895 		savestate(outfile);
    896 	}
    897 	for (; current.lineno < lines_per_page; ) {
    898 		if (fgetline(bufin, sizeof (bufin), infile) == (char *)NULL) {
    899 			if (elide)
    900 				process_elide(outfile);
    901 			return (1);
    902 		}
    903 		/*
    904 		 * Allow C comment delimiters around flag; only really applies
    905 		 * to #else and #endif, but we don't expect to see C comments
    906 		 * around flag for #if. Also accept flag with no C comment
    907 		 * delimiters.
    908 		 */
    909 		if (iscodereview &&
    910 		    (sscanf(bufin, "#%32s /* %80s */", command, flag) == 2 ||
    911 		    sscanf(bufin, "#%32s %80s", command, flag) == 2) &&
    912 		    strcmp(flag, CODEREVIEW) == 0) {
    913 			if (strcmp(command, "ifdef") == 0) {
    914 				change_seen = 1;
    915 				in_change = 1;
    916 				makegray = 1;
    917 				old_stuff = 1;
    918 				tmplinenumber = linenumber;
    919 				linenumber = altlinenumber;
    920 				altlinenumber = tmplinenumber;
    921 				setcurrentfont(DEFAULT_FONT_ITALIC, outfile);
    922 			} else if (strcmp(command, "ifndef") == 0) {
    923 				change_seen = 1;
    924 				in_change = 1;
    925 				makegray = 1;
    926 				old_stuff = 0;
    927 				setcurrentfont(DEFAULT_FONT_BOLD, outfile);
    928 			} else if (strcmp(command, "else") == 0) {
    929 				makegray = 1;
    930 				old_stuff = !old_stuff;
    931 				tmplinenumber = linenumber;
    932 				linenumber = altlinenumber;
    933 				altlinenumber = tmplinenumber;
    934 				if (!old_stuff)
    935 					setcurrentfont(DEFAULT_FONT_BOLD,
    936 					    outfile);
    937 				else
    938 					setcurrentfont(DEFAULT_FONT_ITALIC,
    939 					    outfile);
    940 			} else /* if (strcmp(command, "endif") == 0) */ {
    941 				in_change = 0;
    942 				makegray = 0;
    943 				savestate(outfile);
    944 				setcurrentfont(DEFAULT_FONT, outfile);
    945 				if (old_stuff) {
    946 					tmplinenumber = linenumber;
    947 					linenumber = altlinenumber;
    948 					altlinenumber = tmplinenumber;
    949 				}
    950 			}
    951 			continue;
    952 		}
    953 		current.lineno++;
    954 		current.row -= point_size;
    955 		if (bufin[0] == '\f')
    956 			break;
    957 		proc(bufin, outfile);
    958 		if (elide && (bufin[0] == END_C_FUNCTION ||
    959 		    (strstr(bufin, END_ASM_FUNCTION) != NULL)))
    960 			process_elide(outfile);
    961 	}
    962 	endpage(outfile);
    963 	return (0);
    964 }
    965 
    966 /*
    967  * Start a new page
    968  */
    969 int
    970 startpage(FILE *outfile)
    971 {
    972 	int	logical_page, lines, buflen;
    973 	struct format_state	format_state;
    974 	char	buf[8];
    975 
    976 	logical_page = current.logical_page_count % layoutp->pages;
    977 
    978 	if (logical_page == 0)
    979 		setuppage(outfile);
    980 	else
    981 		setcurrentfont((char *)NULL, outfile);
    982 	(void) fprintf(outfile, "%s ", SET_WIDTHS);
    983 	(void) fprintf(outfile, "%d %f %d %d %s\n",
    984 	    rot_text, layoutp->scale, positions[logical_page].base_x,
    985 	    positions[logical_page].base_y, START_PAGE);
    986 	lines = 0;
    987 	if (header) {
    988 		save_format_state(&format_state);
    989 		setcurrentfont(DEFAULT_FONT_BOLD, outfile);
    990 		numberwidth = 0;
    991 		makegray = 0;
    992 
    993 		current.row -= point_size;
    994 		(void) fprintf(outfile, "%d %.2f %s\n", start_x, current.row,
    995 		    MOVETO);
    996 		proc(headerstring, outfile);
    997 		(void) snprintf(buf, 8, "%d", current.logical_page_count + 1);
    998 		buflen = strlen(buf);
    999 		(void) fprintf(outfile, "%d %.2f %s (%s)%s\n",
   1000 		    (int)(end_x - (buflen + 0.5) *
   1001 		    DEFAULT_CHAR_WIDTH * point_size),
   1002 		    current.row, MOVETO, buf, SHOW);
   1003 		current.row -= point_size;
   1004 		restore_format_state(&format_state, outfile);
   1005 		lines = 2;
   1006 	}
   1007 	return (lines);
   1008 }
   1009 
   1010 void
   1011 setheaderfile(char *filename)
   1012 {
   1013 	if (header == HEADER_IMPLICIT)
   1014 		headerstring = filename;
   1015 }
   1016 
   1017 /*
   1018  * Setup page
   1019  */
   1020 void
   1021 setuppage(FILE *outfile)
   1022 {
   1023 	int	i, ilimit;
   1024 	int	begin, end, place;
   1025 
   1026 	(void) fprintf(outfile, "%%%%Page: ? %d\n", current.page_count + 1);
   1027 	setcurrentfont((char *)NULL, outfile);
   1028 	if (layoutp->pages == 1)
   1029 		return;
   1030 
   1031 	(void) fprintf(outfile, "%f %s %s\n", RULE_WIDTH, SETLINEWIDTH,
   1032 	    NEWPATH);
   1033 	begin = 0; end = DEFAULT_PAPER_WIDTH * POINTS_PER_INCH;
   1034 	for (i = 1, ilimit = layoutp->page_rows; i < ilimit; i++) {
   1035 		place = margin_y - gap_height/2 + i * (box_height+gap_height);
   1036 		(void) fprintf(outfile, "%d %d %s ", begin, place, MOVETO);
   1037 		(void) fprintf(outfile, "%d %d %s\n", end, place, LINETO);
   1038 	}
   1039 	begin = 0; end = DEFAULT_PAPER_HEIGHT * POINTS_PER_INCH;
   1040 	for (i = 1, ilimit = layoutp->page_cols; i < ilimit; i++) {
   1041 		place = margin_x - gap_width/2 + i * (box_width+gap_width);
   1042 		(void) fprintf(outfile, "%d %d %s ", place, begin, MOVETO);
   1043 		(void) fprintf(outfile, "%d %d %s\n", place, end, LINETO);
   1044 	}
   1045 	(void) fprintf(outfile, "%s\n", STROKE);
   1046 }
   1047 
   1048 /*
   1049  * Terminate the logical page and indicate the start of the next
   1050  */
   1051 void
   1052 endpage(FILE *outfile)
   1053 {
   1054 	(void) fprintf(outfile, "%s\n", END_PAGE);
   1055 	current.logical_page_count++;
   1056 	if (vflag)
   1057 		(void) fprintf(stderr, "x");
   1058 	if ((current.logical_page_count % layoutp->pages) == 0)
   1059 		flushpage(outfile);
   1060 }
   1061 
   1062 /*
   1063  * Flush the physical page
   1064  * Record the start of the next page
   1065  */
   1066 void
   1067 flushpage(FILE *outfile)
   1068 {
   1069 	(void) fprintf(outfile, "%d %s\n", ncopies, FLUSH_PAGE);
   1070 	current.page_count++;
   1071 	current.offset = ftell(outfile);
   1072 	if (reverse) {
   1073 		if (current.page_count >= MAXPAGES) {
   1074 			fatal("page reversal limit (%d) reached", MAXPAGES);
   1075 			/* NOTREACHED */
   1076 		}
   1077 		page_map[current.page_count] = current.offset;
   1078 	}
   1079 	if (vflag)
   1080 		(void) fprintf(stderr, "|");
   1081 }
   1082 
   1083 /*
   1084  * reverse the order of pages
   1085  */
   1086 void
   1087 reversepages(FILE *outfile)
   1088 {
   1089 	int	i;
   1090 
   1091 	if (vflag)
   1092 		(void) fprintf(stderr, "\nreversing %d page%s\n",
   1093 		    current.page_count, current.page_count > 1 ? "s" : "");
   1094 	for (i = current.page_count - 1; i >= 0; i--) {
   1095 		copypage(outfile, page_map[i], page_map[i+1]);
   1096 	}
   1097 }
   1098 
   1099 /*
   1100  * copy a page (or more) from tempfile to stdout
   1101  */
   1102 void
   1103 copypage(FILE *outfile, long off_beg, long off_end)
   1104 {
   1105 	int	bytecount, nbytes;
   1106 
   1107 	if (fseek(outfile, off_beg, 0) == -1L) {
   1108 		fatal("temporary file seek error");
   1109 		/* NOTREACHED */
   1110 	}
   1111 	nbytes = off_end - off_beg;
   1112 	while (nbytes > 0) {
   1113 		bytecount = nbytes;
   1114 		if (bytecount > sizeof (bufout))
   1115 			bytecount = sizeof (bufout);
   1116 		bytecount = fread(bufout, 1, bytecount, outfile);
   1117 		if (bytecount <= 0) {
   1118 			fatal("temporary file read error");
   1119 			/* NOTREACHED */
   1120 		}
   1121 		if (fwrite(bufout, 1, bytecount, stdout) != bytecount) {
   1122 			fatal("write error during page copy");
   1123 			/* NOTREACHED */
   1124 		}
   1125 		nbytes -= bytecount;
   1126 	}
   1127 }
   1128 
   1129 /*
   1130  * Process a line of input, escaping characters when necessary and handling
   1131  * tabs
   1132  *
   1133  * The output is improved somewhat by coalescing consecutive tabs and
   1134  * backspaces and eliminating tabs at the end of a line
   1135  *
   1136  * Overprinting (presumably most often used in underlining) can be far from
   1137  * optimal; in particular the way nroff underlines by sequences like
   1138  * "_\ba_\bb_\bc" creates a large volume of PostScript.  This isn't too
   1139  * serious since a lot of nroff underlining is unlikely.
   1140  *
   1141  * Since a newline is generated for each call there will be more
   1142  * newlines in the output than is necessary
   1143  */
   1144 void
   1145 proc(char *in, FILE *outfile)
   1146 {
   1147 	int	i;
   1148 	char	*last, *p, *q;
   1149 	int	currentp, instr, tabc, tabto, grayed;
   1150 	char	*altfont;
   1151 
   1152 	currentp = 0;
   1153 	instr = 0;
   1154 	tabto = 0;
   1155 	if (iscodereview) {
   1156 		grayed = makegray;
   1157 		altfont = current.font;
   1158 	} else {
   1159 		grayed = 0;
   1160 		altfont = DEFAULT_FONT;
   1161 	}
   1162 	/* subtract slop factor */
   1163 	last = bufout + MAX_OUTPUT_LINE_LENGTH - 20;
   1164 	for (;;) { /* check for any special line treatment */
   1165 		if (graylength && strncmp(in, graystring, graylength) == 0) {
   1166 			grayed++;
   1167 			in += graylength;
   1168 		} else if (boldlength &&
   1169 		    strncmp(in, boldstring, boldlength) == 0) {
   1170 			altfont = DEFAULT_FONT_BOLD;
   1171 			in += boldlength;
   1172 		} else if (itlclength &&
   1173 		    strncmp(in, itlcstring, itlclength) == 0) {
   1174 			altfont = DEFAULT_FONT_ITALIC;
   1175 			in += itlclength;
   1176 		} else if (bitclength &&
   1177 		    strncmp(in, bitcstring, bitclength) == 0) {
   1178 			altfont = DEFAULT_FONT_BOLD_ITALIC;
   1179 			in += bitclength;
   1180 		} else
   1181 			break;
   1182 	}
   1183 	if (grayed) {
   1184 		(void) fprintf(outfile, "%d %.2f %d %.2f %s\n",
   1185 		    start_x,
   1186 		    current.row - DEFAULT_DESCENDER_FRACTION * point_size,
   1187 		    end_x,
   1188 		    current.row +
   1189 		    (1.0 - DEFAULT_DESCENDER_FRACTION) * point_size,
   1190 		    SHADE);
   1191 	}
   1192 
   1193 	linenumber++;
   1194 	if (!in_change)
   1195 		altlinenumber++;
   1196 	if (*in == '\0')
   1197 		return;
   1198 
   1199 	if (start_x != 0) {
   1200 		(void) fprintf(outfile, "%d %.2f %s\n",
   1201 		    start_x, current.row, MOVETO);
   1202 	}
   1203 	else
   1204 		(void) fprintf(outfile, "%.2f %s\n",
   1205 		    current.row, ZEROMOVETO);
   1206 	if (numberwidth) {
   1207 		setcurrentfont(DEFAULT_FONT, outfile);
   1208 		(void) sprintf(bufout, "%*d", numberwidth, linenumber);
   1209 		for (q = bufout, i = 0; *q == ' '; q++, i++)
   1210 			;
   1211 		(void) fprintf(outfile, "%d %s (%s)%s %d %s ",
   1212 		    i, TAB, q, SHOW, DEFAULT_SPACES_AFTER_NUMBER, TAB);
   1213 	}
   1214 	setcurrentfont(altfont, outfile);
   1215 
   1216 	q = bufout;
   1217 	*q = '\0';
   1218 	for (p = in; *p != '\0'; p++) {
   1219 		switch (*p) {
   1220 		case '\t':
   1221 			/*
   1222 			 * Count the number of tabs that immediately follow
   1223 			 * the one we're looking at
   1224 			 */
   1225 			tabc = 0;
   1226 			while (*(p + 1) == '\t') {
   1227 				p++;
   1228 				tabc++;
   1229 			}
   1230 			if (currentp > 0) {	/* not beginning of line */
   1231 				i = tabstop - (currentp % tabstop) +
   1232 				    tabc * tabstop;
   1233 				if (instr) {
   1234 					(void) snprintf(q,
   1235 					    BUFOUT - (q - bufout), ")%s ",
   1236 					    SHOW);
   1237 					q += strlen(q);
   1238 					instr = 0;
   1239 				}
   1240 			}
   1241 			else
   1242 				i = (tabc + 1) * tabstop;
   1243 			tabto += i;
   1244 			currentp += i;
   1245 			break;
   1246 		case '\b':
   1247 			/* backspacing over tabs doesn't work... */
   1248 			if (tabto != 0) {
   1249 				fatal("attempt to backspace over a tab");
   1250 				/*NOTREACHED*/
   1251 			}
   1252 			p++;
   1253 			for (i = 1; *p == '\b'; p++)
   1254 				i++;
   1255 			p--;
   1256 			if (currentp - i < 0) {
   1257 				fatal("too many backspaces");
   1258 				/*NOTREACHED*/
   1259 			}
   1260 			if (instr) {
   1261 				*q = '\0';
   1262 				(void) fprintf(outfile, "%s)%s\n",
   1263 				    bufout, SHOW);
   1264 			}
   1265 			instr = 0;
   1266 			if (currentp >= columns)
   1267 				i -= currentp-columns;
   1268 			if (i <= 0) {
   1269 				/* backspace in truncated line */
   1270 				bufout[0] = '\0';
   1271 			} else if (i == 1) {
   1272 				/* frequent case gets special attention */
   1273 				(void) snprintf(bufout, BUFOUT, "%s ",
   1274 				    BACKSPACE);
   1275 			} else
   1276 				(void) snprintf(bufout, BUFOUT, "-%d %s ", i,
   1277 				    TAB);
   1278 			q = bufout + strlen(bufout);
   1279 			currentp -= i;
   1280 			break;
   1281 		case '\f':
   1282 			tabto = 0;		/* optimizes */
   1283 			*q = '\0';
   1284 			if (instr)
   1285 				(void) fprintf(outfile, "%s)%s\n",
   1286 				    bufout, SHOW);
   1287 			else
   1288 				(void) fprintf(outfile, "%s\n", bufout);
   1289 			endpage(outfile);
   1290 			(void) startpage(outfile);
   1291 			current.row = start_y;
   1292 			(void) fprintf(outfile, "%d %.2f %s\n",
   1293 			    start_x, current.row, MOVETO);
   1294 			if (numberwidth)
   1295 				(void) fprintf(outfile, "%d %s\n", numberwidth +
   1296 				    DEFAULT_SPACES_AFTER_NUMBER, TAB);
   1297 			q = bufout;
   1298 			currentp = 0;
   1299 			instr = 0;
   1300 			break;
   1301 		case '\r':
   1302 			tabto = 0;		/* optimizes */
   1303 			if (instr) {
   1304 				*q = '\0';
   1305 				(void) fprintf(outfile, "%s)%s\n",
   1306 				    bufout, SHOW);
   1307 				instr = 0;
   1308 				q = bufout;
   1309 			}
   1310 			(void) fprintf(outfile, "%d %.2f %s\n",
   1311 			    start_x, current.row, MOVETO);
   1312 			if (numberwidth)
   1313 				(void) fprintf(outfile, "%d %s\n", numberwidth +
   1314 				    DEFAULT_SPACES_AFTER_NUMBER, TAB);
   1315 			currentp = 0;
   1316 			break;
   1317 		case '\\':
   1318 		case '(':
   1319 		case ')':
   1320 			if (currentp < columns) {
   1321 				if (!instr) {
   1322 					if (tabto) {
   1323 						(void) snprintf(q,
   1324 						    BUFOUT - (q - bufout),
   1325 						    "%d %s ", tabto, TAB);
   1326 						q += strlen(q);
   1327 						tabto = 0;
   1328 					}
   1329 					*q++ = '(';
   1330 					instr = 1;
   1331 				}
   1332 				*q++ = '\\';
   1333 				*q++ = *p;
   1334 			}
   1335 			currentp++;
   1336 			break;
   1337 		default: {
   1338 			/*
   1339 			 * According to the PostScript Language Manual,
   1340 			 * PostScript files can contain only "the printable
   1341 			 * subset of the ASCII character set (plus the
   1342 			 * newline marker)".
   1343 			 */
   1344 			char	pchar;
   1345 
   1346 			pchar = *p;
   1347 			if (currentp < columns) {
   1348 				if (!instr) {
   1349 					if (tabto) {
   1350 						(void) snprintf(q,
   1351 						    BUFOUT - (q - bufout),
   1352 						    "%d %s ", tabto, TAB);
   1353 						q += strlen(q);
   1354 						tabto = 0;
   1355 					}
   1356 					*q++ = '(';
   1357 					instr = 1;
   1358 				}
   1359 				if (!isascii(pchar) || !isprint(pchar)) {
   1360 					if (iscntrl(pchar)) {
   1361 						if (pchar == '\177')
   1362 							pchar = '_';
   1363 						else
   1364 							pchar += '@';
   1365 						*q++ = '^';
   1366 					} else {
   1367 						*q++ = '\\';
   1368 						*q++ = '0' + ((pchar>>6) & 7);
   1369 						*q++ = '0' + ((pchar>>3) & 7);
   1370 						pchar = '0' + (pchar & 7);
   1371 					}
   1372 				}
   1373 				*q++ = pchar;
   1374 			}
   1375 			currentp++;
   1376 			break;
   1377 			}
   1378 		}
   1379 		if (q >= last) {
   1380 			*q = '\0';
   1381 			if (instr)
   1382 				(void) fprintf(outfile, "%s)%s\n", bufout,
   1383 				    SHOW);
   1384 			else
   1385 				(void) fprintf(outfile, "%s\n", bufout);
   1386 			q = bufout;
   1387 			instr = 0;
   1388 		}
   1389 	}
   1390 	if (instr) {
   1391 		(void) snprintf(q, BUFOUT - (q - bufout), ")%s", SHOW);
   1392 		q += strlen(q);
   1393 	}
   1394 	else
   1395 		*q = '\0';
   1396 	if (q >= last) {
   1397 		fatal("bufout overflow");
   1398 		/*NOTREACHED*/
   1399 	}
   1400 	if (bufout[0] != '\0')
   1401 		(void) fprintf(outfile, "%s\n", bufout);
   1402 }
   1403 
   1404 /*
   1405  * Initialize globals:
   1406  *	username - login name of user
   1407  *	hostname - name of machine on which lwlp is run
   1408  *	currentdate - what it says
   1409  * Possible system dependencies here...
   1410  */
   1411 void
   1412 setup(void)
   1413 {
   1414 	int	len;
   1415 	char	*p;
   1416 	long	t;
   1417 	struct utsname	utsname;
   1418 	struct passwd	*pw;
   1419 
   1420 	if ((p = getlogin()) == (char *)NULL) {
   1421 		if ((pw = getpwuid(getuid())) == (struct passwd *)NULL)
   1422 			p = "Whoknows";
   1423 		else
   1424 			p = pw->pw_name;
   1425 		endpwent();
   1426 	}
   1427 	username = strdup(p);
   1428 
   1429 	(void) uname(&utsname);
   1430 	hostname = strdup(utsname.nodename);
   1431 
   1432 	t = time((long *)0);
   1433 	p = ctime(&t);
   1434 	len = strlen(p);
   1435 	*(p + len - 1) = '\0';		/* zap the newline character */
   1436 	currentdate = strdup(p);
   1437 	current.font = DEFAULT_FONT;
   1438 }
   1439 
   1440 /*
   1441  * Special version of fgets
   1442  * Read until a formfeed, newline, or overflow
   1443  * If a formfeed is the first character, return it immediately
   1444  * If a formfeed is found after the first character, replace it by a newline
   1445  * and push the formfeed back onto the input stream
   1446  * A special case is a formfeed followed by a newline in which case the
   1447  * newline is ignored
   1448  * The input buffer will be null-terminated and will *not* end with a newline
   1449  * The buffer size n includes the null
   1450  */
   1451 char *
   1452 fgetline(char *s, int n, FILE *iop)
   1453 {
   1454 	int	ch;
   1455 	char	*cs;
   1456 
   1457 	if (n < 2) {
   1458 		fatal("fgetline called with bad buffer size!?");
   1459 		/*NOTREACHED*/
   1460 	}
   1461 
   1462 	cs = s;
   1463 	n--;				/* the null */
   1464 
   1465 	/*
   1466 	 * Check out the special cases
   1467 	 */
   1468 	if ((ch = getc(iop)) == EOF)
   1469 		return ((char *)NULL);
   1470 	if (ch == '\f') {
   1471 		if ((ch = getc(iop)) != '\n') {
   1472 			/*
   1473 			 * If EOF was just read it will be noticed
   1474 			 * next time through
   1475 			 */
   1476 			if (ungetc(ch, iop) == EOF && !feof(iop)) {
   1477 				/*
   1478 				 * Shouldn't happen since a getc()
   1479 				 * was just done
   1480 				 */
   1481 				fatal("fgetline - ungetc failed");
   1482 				/*NOTREACHED*/
   1483 			}
   1484 		}
   1485 		*cs++ = '\f';
   1486 		*cs = '\0';
   1487 		return (s);
   1488 	}
   1489 
   1490 	/*
   1491 	 * Check for "weird" input characters is made in proc()
   1492 	 */
   1493 	while (n-- > 0) {
   1494 		if (ch == '\f' || ch == '\n')
   1495 			break;
   1496 		*cs++ = ch;
   1497 		if ((ch = getc(iop)) == EOF)
   1498 			break;
   1499 	}
   1500 
   1501 	if (ch == EOF && cs == s)		/* Nothing was read */
   1502 		return ((char *)NULL);
   1503 	if (ch == '\f') {
   1504 		if (ungetc(ch, iop) == EOF)
   1505 			(void) fprintf(stderr, "fgetline - can't ungetc??\n");
   1506 	} else if (ch != '\n' && ch != EOF) {
   1507 		fatal("fgetline - input line too long");
   1508 		/*NOTREACHED*/
   1509 	}
   1510 	*cs = '\0';
   1511 	return (s);
   1512 }
   1513 
   1514 /*PRINTFLIKE1*/
   1515 void
   1516 fatal(char *fmt, ...)
   1517 {
   1518 	va_list ap;
   1519 
   1520 	(void) fprintf(stderr, "%s: ", progname);
   1521 	va_start(ap, fmt);
   1522 	(void) vfprintf(stderr, fmt, ap);
   1523 	va_end(ap);
   1524 	(void) fprintf(stderr, "\n");
   1525 	exit(1);
   1526 	/*NOTREACHED*/
   1527 }
   1528