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 	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