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