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