Unleashed v1.4
[unleashed.git] / usr / src / tools / codereview / lwlp.c
blob84d4fcf5a581a57417a1e4862d38c2881c40dfb0
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * lwlp - Convert ASCII text to PostScript
29 * Usage:
30 * lwlp [-{2|4|8}] [-p] [-L] [-r] [-n#] [-l#|-w#] [-c#] [-t#]
31 * [-hstring] [-Bstring] [-Istring] [-Xstring] [-Pfile] [file ...]
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
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
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
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"
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>
85 * Configurable...
86 * BUFOUT should be fairly large
88 #define BUFIN 1024 /* maximum length of an input line */
89 #define BUFOUT (BUFIN * 5)
90 #define MAXPAGES 10000
91 #define REVERSE_OFF 0
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 "**********************************************************";
122 * PostScript command strings
124 #define LINETO "lineto"
125 #define NEWPATH "newpath"
126 #define SETLINEWIDTH "setlinewidth"
127 #define STROKE "stroke"
129 * PostScript command strings defined in the prologue file
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 */
144 * Conformance requires that no PostScript line exceed 256 characters
146 #define POINTS_PER_INCH 72
147 #define MAX_OUTPUT_LINE_LENGTH 256
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 */
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;
163 struct format_state {
164 int numberwidth, linenumber, altlinenumber;
165 int makegray;
166 char *font;
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;
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;
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 */
195 static char *username, *hostname, *currentdate;
196 static char *comment;
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, ...);
221 static char *prologue;
222 static char *progname;
223 static int iscodereview;
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
251 struct layout {
252 float scale;
253 int pages, page_rows, page_cols;
254 int rotation;
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 };
262 static int box_width, box_height;
263 static int gap_width, gap_height;
264 static int margin_x, margin_y;
266 static struct position {
267 int base_x;
268 int base_y;
269 } positions[8];
272 main(int argc, char **argv)
274 int ch, i, j, first_file;
275 char *pc;
276 FILE *infile;
278 if ((pc = strrchr(argv[0], '/')) != NULL)
279 progname = pc + 1;
280 else
281 progname = argv[0];
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;
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;
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*/
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*/
356 lflag = 1;
357 if (wflag > 0) {
358 fatal("can't have both -l and -w");
359 /*NOTREACHED*/
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*/
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*/
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*/
400 wflag = 1;
401 if (lflag) {
402 fatal("can't have both -l and -w");
403 /*NOTREACHED*/
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);
426 if (elide && !iscodereview) {
427 fatal("-e option valid only with codereview");
428 /*NOTREACHED*/
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*/
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
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*/
453 if (iscodereview && (first_file + 2) != argc) {
454 fatal("codereview: need old and new file");
455 /*NOTREACHED*/
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);
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);
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;
507 columns -= numberwidth + DEFAULT_SPACES_AFTER_NUMBER;
508 if (columns <= 0) {
509 fatal("numbering width exceeds number of columns");
510 /* NOT REACHED */
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 */
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;
526 if (rot_text == 0) {
527 /* top physical row is logically first */
528 phys_row = layoutp->page_rows - 1 - phys_row;
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;
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);
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);
554 setup();
555 preamble();
557 if (iscodereview) {
558 char command[BUFSIZ];
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");
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")) == NULL) {
581 fatal("can't open %s for reading", argv[i]);
582 /*NOTREACHED*/
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");
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);
603 postamble();
605 if (fflush(stdout) == EOF) {
606 fatal("write error on stdout");
607 /*NOTREACHED*/
609 exit(0);
610 /*NOTREACHED*/
611 /*LINTED*/
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
619 void
620 preamble(void)
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");
631 if (prologue == NULL) {
632 char **cpp;
633 for (cpp = default_prologue; *cpp; cpp++) {
634 (void) fputs(*cpp, stdout);
636 } else {
637 FILE *fp;
638 if ((fp = fopen(prologue, "r")) == NULL) {
639 fatal("can't open prologue file %s", prologue);
640 /*NOTREACHED*/
642 while (fgets(bufin, sizeof (bufin), fp) != NULL)
643 (void) fputs(bufin, stdout);
644 (void) fclose(fp);
646 if (ferror(stdout) || fflush(stdout) == EOF) {
647 fatal("write error on stdout");
648 /*NOTREACHED*/
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);
661 void
662 postamble(void)
664 (void) printf("%%%%Trailer\n");
665 (void) printf("%%%%Pages: %d\n", current.page_count);
669 printbanner(char *filename, FILE *outfile)
671 char buffer[BUFSIZ];
672 struct stat statbuf;
673 struct format_state format_state;
674 int nlines = 0;
676 /* we've already verified readability */
677 (void) stat(filename, &statbuf);
679 save_format_state(&format_state);
680 numberwidth = 0;
682 setcurrentfont(DEFAULT_FONT_BOLD_ITALIC, outfile);
684 current.row -= point_size;
685 (void) fprintf(outfile, "%d %.2f %s\n", start_x, current.row, MOVETO);
686 proc(banner, outfile);
687 nlines++;
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++;
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);
705 if (comment != NULL && comment[0] != 0) {
706 const char *cur = comment;
707 const char *endl;
708 int len;
710 while (*cur != 0) {
711 current.row -= point_size;
712 (void) fprintf(outfile, "%d %.2f %s\n", start_x,
713 current.row, MOVETO);
715 endl = strchr(cur, '\n');
716 if (endl == NULL)
717 endl = cur + strlen(cur);
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++;
727 if (*endl == 0)
728 break;
729 cur = endl + 1;
733 current.row -= point_size;
734 (void) fprintf(outfile, "%d %.2f %s\n", start_x, current.row, MOVETO);
735 proc(banner, outfile);
736 nlines++;
738 restore_format_state(&format_state, outfile);
739 return (nlines);
742 void
743 setcurrentfont(char *newfont, FILE *outfile)
745 if (current.font != newfont) {
746 if (newfont)
747 current.font = newfont;
748 (void) fprintf(outfile, "%s\n", current.font);
752 void
753 savestate(FILE *f)
755 current.offset = ftell(f);
756 saved = current;
759 void
760 restorestate(FILE *f)
762 char *font;
764 font = current.font;
765 (void) fseek(f, saved.offset, 0);
766 current = saved;
767 setcurrentfont(font, f);
770 void
771 save_format_state(struct format_state *fs)
773 fs->numberwidth = numberwidth;
774 fs->linenumber = linenumber;
775 fs->altlinenumber = altlinenumber;
776 fs->makegray = makegray;
777 fs->font = current.font;
780 void
781 restore_format_state(struct format_state *fs, FILE *outfile)
783 numberwidth = fs->numberwidth;
784 linenumber = fs->linenumber;
785 altlinenumber = fs->altlinenumber;
786 makegray = fs->makegray;
787 setcurrentfont(fs->font, outfile);
791 * Print a file
793 * The input stream may be stdin, a file, or a pipe
795 void
796 printfile(FILE *infile)
798 int eof;
799 char *p;
800 FILE *outfile;
802 if (reverse)
803 page_map[0] = 0L;
804 if (usetmp) {
805 (void) snprintf(bufin, BUFIN, "/tmp/%sXXXXXX", progname);
806 p = mktemp(bufin);
807 if ((outfile = fopen(p, "w+")) == NULL) {
808 fatal("can't open temporary file %s", p);
809 /* NOTREACHED */
811 if (!dflag)
812 (void) unlink(p);
813 else
814 (void) fprintf(stderr, "will not unlink %s\n", p);
816 else
817 outfile = stdout;
819 setcurrentfont(DEFAULT_FONT, outfile);
820 change_seen = 0;
821 dots_inserted = 0;
822 in_change = 0;
823 makegray = 0;
824 linenumber = 0;
825 altlinenumber = 0;
826 current.logical_page_count = 0;
827 do {
828 current.row = start_y;
829 eof = printpage(infile, outfile);
830 } while (!eof);
832 if (((int)current.row) != start_y)
833 endpage(outfile);
834 if ((current.logical_page_count % layoutp->pages) != 0)
835 flushpage(outfile);
836 if (vflag)
837 (void) fprintf(stderr, "\n");
838 if (fflush(outfile) == EOF) {
839 fatal("write error while flushing output");
840 /*NOTREACHED*/
842 if (usetmp) {
843 if (reverse)
844 reversepages(outfile);
845 else
846 copypage(outfile, 0L, current.offset);
847 (void) fclose(outfile);
851 void
852 process_elide(FILE *outfile)
854 if (!change_seen && !in_change) {
855 /* don't include function in output */
856 restorestate(outfile);
857 if (!dots_inserted) {
858 struct format_state format_state;
860 save_format_state(&format_state);
861 numberwidth = 0;
862 current.lineno++;
863 current.row -= point_size;
864 setcurrentfont(DEFAULT_FONT_BOLD_ITALIC, outfile);
865 proc("______unchanged_portion_omitted_", outfile);
866 restore_format_state(&format_state, outfile);
867 savestate(outfile);
868 dots_inserted = 1;
870 } else {
871 savestate(outfile);
872 change_seen = in_change;
873 dots_inserted = 0;
878 * Process the next page
879 * Return 1 on EOF, 0 otherwise
882 printpage(FILE *infile, FILE *outfile)
884 int tmplinenumber;
885 char command[BUFSIZ], flag[BUFSIZ];
887 if (ungetc(getc(infile), infile) == EOF)
888 return (1);
890 current.lineno = 0;
891 current.lineno += startpage(outfile);
892 if (bannerfile) {
893 current.lineno += printbanner(bannerfile, outfile);
894 bannerfile = NULL;
895 savestate(outfile);
897 for (; current.lineno < lines_per_page; ) {
898 if (fgetline(bufin, sizeof (bufin), infile) == NULL) {
899 if (elide)
900 process_elide(outfile);
901 return (1);
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.
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;
951 continue;
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);
962 endpage(outfile);
963 return (0);
967 * Start a new page
970 startpage(FILE *outfile)
972 int logical_page, lines, buflen;
973 struct format_state format_state;
974 char buf[8];
976 logical_page = current.logical_page_count % layoutp->pages;
978 if (logical_page == 0)
979 setuppage(outfile);
980 else
981 setcurrentfont(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;
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;
1007 return (lines);
1010 void
1011 setheaderfile(char *filename)
1013 if (header == HEADER_IMPLICIT)
1014 headerstring = filename;
1018 * Setup page
1020 void
1021 setuppage(FILE *outfile)
1023 int i, ilimit;
1024 int begin, end, place;
1026 (void) fprintf(outfile, "%%%%Page: ? %d\n", current.page_count + 1);
1027 setcurrentfont(NULL, outfile);
1028 if (layoutp->pages == 1)
1029 return;
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);
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);
1045 (void) fprintf(outfile, "%s\n", STROKE);
1049 * Terminate the logical page and indicate the start of the next
1051 void
1052 endpage(FILE *outfile)
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);
1063 * Flush the physical page
1064 * Record the start of the next page
1066 void
1067 flushpage(FILE *outfile)
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 */
1077 page_map[current.page_count] = current.offset;
1079 if (vflag)
1080 (void) fprintf(stderr, "|");
1084 * reverse the order of pages
1086 void
1087 reversepages(FILE *outfile)
1089 int i;
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]);
1100 * copy a page (or more) from tempfile to stdout
1102 void
1103 copypage(FILE *outfile, long off_beg, long off_end)
1105 int bytecount, nbytes;
1107 if (fseek(outfile, off_beg, 0) == -1L) {
1108 fatal("temporary file seek error");
1109 /* NOTREACHED */
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 */
1121 if (fwrite(bufout, 1, bytecount, stdout) != bytecount) {
1122 fatal("write error during page copy");
1123 /* NOTREACHED */
1125 nbytes -= bytecount;
1130 * Process a line of input, escaping characters when necessary and handling
1131 * tabs
1133 * The output is improved somewhat by coalescing consecutive tabs and
1134 * backspaces and eliminating tabs at the end of a line
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.
1141 * Since a newline is generated for each call there will be more
1142 * newlines in the output than is necessary
1144 void
1145 proc(char *in, FILE *outfile)
1147 int i;
1148 char *last, *p, *q;
1149 int currentp, instr, tabc, tabto, grayed;
1150 char *altfont;
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;
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;
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);
1193 linenumber++;
1194 if (!in_change)
1195 altlinenumber++;
1196 if (*in == '\0')
1197 return;
1199 if (start_x != 0) {
1200 (void) fprintf(outfile, "%d %.2f %s\n",
1201 start_x, current.row, MOVETO);
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++)
1211 (void) fprintf(outfile, "%d %s (%s)%s %d %s ",
1212 i, TAB, q, SHOW, DEFAULT_SPACES_AFTER_NUMBER, TAB);
1214 setcurrentfont(altfont, outfile);
1216 q = bufout;
1217 *q = '\0';
1218 for (p = in; *p != '\0'; p++) {
1219 switch (*p) {
1220 case '\t':
1222 * Count the number of tabs that immediately follow
1223 * the one we're looking at
1225 tabc = 0;
1226 while (*(p + 1) == '\t') {
1227 p++;
1228 tabc++;
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;
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*/
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*/
1260 if (instr) {
1261 *q = '\0';
1262 (void) fprintf(outfile, "%s)%s\n",
1263 bufout, SHOW);
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;
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;
1329 *q++ = '(';
1330 instr = 1;
1332 *q++ = '\\';
1333 *q++ = *p;
1335 currentp++;
1336 break;
1337 default: {
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)".
1344 char pchar;
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;
1356 *q++ = '(';
1357 instr = 1;
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);
1373 *q++ = pchar;
1375 currentp++;
1376 break;
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;
1390 if (instr) {
1391 (void) snprintf(q, BUFOUT - (q - bufout), ")%s", SHOW);
1392 q += strlen(q);
1394 else
1395 *q = '\0';
1396 if (q >= last) {
1397 fatal("bufout overflow");
1398 /*NOTREACHED*/
1400 if (bufout[0] != '\0')
1401 (void) fprintf(outfile, "%s\n", bufout);
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...
1411 void
1412 setup(void)
1414 int len;
1415 char *p;
1416 long t;
1417 struct utsname utsname;
1418 struct passwd *pw;
1420 if ((p = getlogin()) == NULL) {
1421 if ((pw = getpwuid(getuid())) == NULL)
1422 p = "Whoknows";
1423 else
1424 p = pw->pw_name;
1425 endpwent();
1427 username = strdup(p);
1429 (void) uname(&utsname);
1430 hostname = strdup(utsname.nodename);
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;
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
1451 char *
1452 fgetline(char *s, int n, FILE *iop)
1454 int ch;
1455 char *cs;
1457 if (n < 2) {
1458 fatal("fgetline called with bad buffer size!?");
1459 /*NOTREACHED*/
1462 cs = s;
1463 n--; /* the null */
1466 * Check out the special cases
1468 if ((ch = getc(iop)) == EOF)
1469 return (NULL);
1470 if (ch == '\f') {
1471 if ((ch = getc(iop)) != '\n') {
1473 * If EOF was just read it will be noticed
1474 * next time through
1476 if (ungetc(ch, iop) == EOF && !feof(iop)) {
1478 * Shouldn't happen since a getc()
1479 * was just done
1481 fatal("fgetline - ungetc failed");
1482 /*NOTREACHED*/
1485 *cs++ = '\f';
1486 *cs = '\0';
1487 return (s);
1491 * Check for "weird" input characters is made in proc()
1493 while (n-- > 0) {
1494 if (ch == '\f' || ch == '\n')
1495 break;
1496 *cs++ = ch;
1497 if ((ch = getc(iop)) == EOF)
1498 break;
1501 if (ch == EOF && cs == s) /* Nothing was read */
1502 return (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*/
1510 *cs = '\0';
1511 return (s);
1514 /*PRINTFLIKE1*/
1515 void
1516 fatal(char *fmt, ...)
1518 va_list ap;
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*/