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 }