xref: /titanic_52/usr/src/tools/codereview/lwlp.c (revision 71269a2275bf5a143dad6461eee2710a344e7261)
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 	savestate(outfile);
740 	return (nlines);
741 }
742 
743 void
744 setcurrentfont(char *newfont, FILE *outfile)
745 {
746 	if (current.font != newfont) {
747 		if (newfont)
748 			current.font = newfont;
749 		(void) fprintf(outfile, "%s\n", current.font);
750 	}
751 }
752 
753 void
754 savestate(FILE *f)
755 {
756 	current.offset = ftell(f);
757 	saved = current;
758 }
759 
760 void
761 restorestate(FILE *f)
762 {
763 	char	*font;
764 
765 	font = current.font;
766 	(void) fseek(f, saved.offset, 0);
767 	current = saved;
768 	setcurrentfont(font, f);
769 }
770 
771 void
772 save_format_state(struct format_state *fs)
773 {
774 	fs->numberwidth = numberwidth;
775 	fs->linenumber = linenumber;
776 	fs->altlinenumber = altlinenumber;
777 	fs->makegray = makegray;
778 	fs->font = current.font;
779 }
780 
781 void
782 restore_format_state(struct format_state *fs, FILE *outfile)
783 {
784 	numberwidth = fs->numberwidth;
785 	linenumber = fs->linenumber;
786 	altlinenumber = fs->altlinenumber;
787 	makegray = fs->makegray;
788 	setcurrentfont(fs->font, outfile);
789 }
790 
791 /*
792  * Print a file
793  *
794  * The input stream may be stdin, a file, or a pipe
795  */
796 void
797 printfile(FILE *infile)
798 {
799 	int	eof;
800 	char	*p;
801 	FILE	*outfile;
802 
803 	if (reverse)
804 		page_map[0] = 0L;
805 	if (usetmp) {
806 		(void) snprintf(bufin, BUFIN, "/tmp/%sXXXXXX", progname);
807 		p = mktemp(bufin);
808 		if ((outfile = fopen(p, "w+")) == NULL) {
809 			fatal("can't open temporary file %s", p);
810 			/* NOTREACHED */
811 		}
812 		if (!dflag)
813 			(void) unlink(p);
814 		else
815 			(void) fprintf(stderr, "will not unlink %s\n", p);
816 	}
817 	else
818 		outfile = stdout;
819 
820 	setcurrentfont(DEFAULT_FONT, outfile);
821 	change_seen = 0;
822 	dots_inserted = 0;
823 	in_change = 0;
824 	makegray = 0;
825 	linenumber = 0;
826 	altlinenumber = 0;
827 	current.logical_page_count = 0;
828 	do {
829 		current.row = start_y;
830 		eof = printpage(infile, outfile);
831 	} while (!eof);
832 
833 	if (((int)current.row) != start_y)
834 		endpage(outfile);
835 	if ((current.logical_page_count % layoutp->pages) != 0)
836 		flushpage(outfile);
837 	if (vflag)
838 		(void) fprintf(stderr, "\n");
839 	if (fflush(outfile) == EOF) {
840 		fatal("write error while flushing output");
841 		/*NOTREACHED*/
842 	}
843 	if (usetmp) {
844 		if (reverse)
845 			reversepages(outfile);
846 		else
847 			copypage(outfile, 0L, current.offset);
848 		(void) fclose(outfile);
849 	}
850 }
851 
852 void
853 process_elide(FILE *outfile)
854 {
855 	if (!change_seen && !in_change) {
856 		/* don't include function in output */
857 		restorestate(outfile);
858 		if (!dots_inserted) {
859 			struct format_state	format_state;
860 
861 			save_format_state(&format_state);
862 			numberwidth = 0;
863 			current.lineno++;
864 			current.row -= point_size;
865 			setcurrentfont(DEFAULT_FONT_BOLD_ITALIC, outfile);
866 			proc("______unchanged_portion_omitted_", outfile);
867 			restore_format_state(&format_state, outfile);
868 			savestate(outfile);
869 			dots_inserted = 1;
870 		}
871 	} else {
872 		savestate(outfile);
873 		change_seen = in_change;
874 		dots_inserted = 0;
875 	}
876 }
877 
878 /*
879  * Process the next page
880  * Return 1 on EOF, 0 otherwise
881  */
882 int
883 printpage(FILE *infile, FILE *outfile)
884 {
885 	int	tmplinenumber;
886 	char	command[BUFSIZ], flag[BUFSIZ];
887 
888 	if (ungetc(getc(infile), infile) == EOF)
889 		return (1);
890 
891 	current.lineno = 0;
892 	current.lineno += startpage(outfile);
893 	if (bannerfile) {
894 		current.lineno += printbanner(bannerfile, outfile);
895 		bannerfile = NULL;
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