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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * 32 * postprint - PostScript translator for ASCII files. 33 * 34 * A simple program that translates ASCII files into PostScript. All it really 35 * does is expand tabs and backspaces, handle character quoting, print text lines, 36 * and control when pages are started based on the requested number of lines per 37 * page. 38 * 39 * The PostScript prologue is copied from *prologue before any of the input files 40 * are translated. The program expects that the following procedures are defined 41 * in that file: 42 * 43 * setup 44 * 45 * mark ... setup - 46 * 47 * Handles special initialization stuff that depends on how the program 48 * was called. Expects to find a mark followed by key/value pairs on the 49 * stack. The def operator is applied to each pair up to the mark, then 50 * the default state is set up. 51 * 52 * pagesetup 53 * 54 * page pagesetup - 55 * 56 * Does whatever is needed to set things up for the next page. Expects 57 * to find the current page number on the stack. 58 * 59 * l 60 * 61 * string l - 62 * 63 * Prints string starting in the first column and then goes to the next 64 * line. 65 * 66 * L 67 * 68 * mark string column string column ... L mark 69 * 70 * Prints each string on the stack starting at the horizontal position 71 * selected by column. Used when tabs and spaces can be sufficiently well 72 * compressed to make the printer overhead worthwhile. Always used when 73 * we have to back up. 74 * 75 * done 76 * 77 * done 78 * 79 * Makes sure the last page is printed. Only needed when we're printing 80 * more than one page on each sheet of paper. 81 * 82 * Almost everything has been changed in this version of postprint. The program 83 * is more intelligent, especially about tabs, spaces, and backspacing, and as a 84 * result output files usually print faster. Output files also now conform to 85 * Adobe's file structuring conventions, which is undoubtedly something I should 86 * have done in the first version of the program. If the number of lines per page 87 * is set to 0, which can be done using the -l option, pointsize will be used to 88 * guess a reasonable value. The estimate is based on the values of LINESPP, 89 * POINTSIZE, and pointsize, and assumes LINESPP lines would fit on a page if 90 * we printed in size POINTSIZE. Selecting a point size using the -s option and 91 * adding -l0 to the command line forces the guess to be made. 92 * 93 * Many default values, like the magnification and orientation, are defined in 94 * the prologue, which is where they belong. If they're changed (by options), an 95 * appropriate definition is made after the prologue is added to the output file. 96 * The -P option passes arbitrary PostScript through to the output file. Among 97 * other things it can be used to set (or change) values that can't be accessed by 98 * other options. 99 * 100 */ 101 102 103 #include <stdio.h> 104 #include <signal.h> 105 #include <ctype.h> 106 #include <fcntl.h> 107 #include <unistd.h> 108 #include <string.h> 109 110 #include "comments.h" /* PostScript file structuring comments */ 111 #include "gen.h" /* general purpose definitions */ 112 #include "path.h" /* for the prologue */ 113 #include "ext.h" /* external variable declarations */ 114 #include "postprint.h" /* a few special definitions */ 115 116 117 char *optnames = "a:c:e:f:l:m:n:o:p:r:s:t:x:y:A:C:J:L:P:R:DI"; 118 119 char *prologue = POSTPRINT; /* default PostScript prologue */ 120 char *formfile = FORMFILE; /* stuff for multiple pages per sheet */ 121 char *locale = NULL; 122 123 int formsperpage = 1; /* page images on each piece of paper */ 124 int copies = 1; /* and this many copies of each sheet */ 125 126 int linespp = LINESPP; /* number of lines per page */ 127 int pointsize = POINTSIZE; /* in this point size */ 128 int tabstops = TABSTOPS; /* tabs set at these columns */ 129 int crmode = 0; /* carriage return mode - 0, 1, or 2 */ 130 131 int col = 1; /* next character goes in this column */ 132 int line = 1; /* on this line */ 133 134 int stringcount = 0; /* number of strings on the stack */ 135 int stringstart = 1; /* column where current one starts */ 136 137 Fontmap fontmap[] = FONTMAP; /* for translating font names */ 138 char *fontname = "Courier"; /* use this PostScript font */ 139 140 int page = 0; /* page we're working on */ 141 int printed = 0; /* printed this many pages */ 142 143 FILE *fp_in = stdin; /* read from this file */ 144 FILE *fp_out = stdout; /* and write stuff here */ 145 FILE *fp_acct = NULL; /* for accounting data */ 146 147 static void account(void); 148 static void arguments(void); 149 static void done(void); 150 static void endline(void); 151 static void formfeed(void); 152 static void header(void); 153 static void init_signals(void); 154 static void newline(void); 155 static void options(void); 156 static void oput(int); 157 static void redirect(int); 158 static void setup(void); 159 static void spaces(int); 160 static void startline(void); 161 static void text(void); 162 163 /*****************************************************************************/ 164 165 166 int 167 main(int agc, char *agv[]) 168 { 169 170 /* 171 * 172 * A simple program that translates ASCII files into PostScript. If there's more 173 * than one input file, each begins on a new page. 174 * 175 */ 176 177 178 argc = agc; /* other routines may want them */ 179 argv = agv; 180 181 prog_name = argv[0]; /* really just for error messages */ 182 183 init_signals(); /* sets up interrupt handling */ 184 header(); /* PostScript header and prologue */ 185 setup(); /* for PostScript */ 186 arguments(); /* followed by each input file */ 187 done(); /* print the last page etc. */ 188 account(); /* job accounting data */ 189 190 return (x_stat); /* not much could be wrong */ 191 192 } /* End of main */ 193 194 195 /*****************************************************************************/ 196 197 198 static void 199 init_signals(void) 200 { 201 void interrupt(); /* signal handler */ 202 203 /* 204 * 205 * Makes sure we handle interrupts. 206 * 207 */ 208 209 210 if ( signal(SIGINT, interrupt) == SIG_IGN ) { 211 signal(SIGINT, SIG_IGN); 212 signal(SIGQUIT, SIG_IGN); 213 signal(SIGHUP, SIG_IGN); 214 } else { 215 signal(SIGHUP, interrupt); 216 signal(SIGQUIT, interrupt); 217 } /* End else */ 218 219 signal(SIGTERM, interrupt); 220 221 } /* End of init_signals */ 222 223 224 /*****************************************************************************/ 225 226 227 static void 228 header(void) 229 { 230 int ch; /* return value from getopt() */ 231 int old_optind = optind; /* for restoring optind - should be 1 */ 232 233 /* 234 * 235 * Scans the option list looking for things, like the prologue file, that we need 236 * right away but could be changed from the default. Doing things this way is an 237 * attempt to conform to Adobe's latest file structuring conventions. In particular 238 * they now say there should be nothing executed in the prologue, and they have 239 * added two new comments that delimit global initialization calls. Once we know 240 * where things really are we write out the job header, follow it by the prologue, 241 * and then add the ENDPROLOG and BEGINSETUP comments. 242 * 243 */ 244 245 246 while ( (ch = getopt(argc, argv, optnames)) != EOF ) 247 if ( ch == 'L' ) 248 prologue = optarg; 249 else if ( ch == '?' ) 250 error(FATAL, ""); 251 252 optind = old_optind; /* get ready for option scanning */ 253 254 fprintf(stdout, "%s", CONFORMING); 255 fprintf(stdout, "%s %s\n", CREATOR, "%M%"); 256 fprintf(stdout, "%s %s\n", VERSION, "%I%"); 257 fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND); 258 fprintf(stdout, "%s %s\n", PAGES, ATEND); 259 fprintf(stdout, "%s", ENDCOMMENTS); 260 261 options(); /* handle the command line options */ 262 263 if ( cat(prologue) == FALSE ) 264 error(FATAL, "can't read %s", prologue); 265 266 fprintf(stdout, "%s", ENDPROLOG); 267 fprintf(stdout, "%s", BEGINSETUP); 268 fprintf(stdout, "mark\n"); 269 270 } /* End of header */ 271 272 273 /*****************************************************************************/ 274 275 276 static void 277 options(void) 278 { 279 int ch; /* return value from getopt() */ 280 int euro = 0; 281 extern char *getenv(char *); 282 283 284 /* 285 * 286 * Reads and processes the command line options. Added the -P option so arbitrary 287 * PostScript code can be passed through. Expect it could be useful for changing 288 * definitions in the prologue for which options have not been defined. 289 * 290 * Although any PostScript font can be used, things will only work well for 291 * constant width fonts. 292 * 293 */ 294 295 if (((locale = getenv("LC_MONETARY")) != NULL) || 296 ((locale = getenv("LANG")) != NULL)) { 297 char *tmp = NULL; 298 299 /* if there is a locale specific prologue, use it as the default */ 300 if ((tmp = calloc(1, strlen(POSTPRINT) + strlen(locale) + 2)) != NULL) { 301 sprintf(tmp, "%s-%s", POSTPRINT, locale); 302 if (access(tmp, R_OK) == 0) 303 prologue = tmp; 304 else 305 free(tmp); 306 } 307 308 /* if the locale has 8859-15 or euro in it, add the symbol to font */ 309 if ((strstr(locale, "8859-15") != NULL) || 310 (strstr(locale, "euro") != NULL)) 311 euro = 1; 312 } 313 314 while ( (ch = getopt(argc, argv, optnames)) != EOF ) { 315 #if defined(DEBUG) 316 fprintf(stderr, " Opt: %c, arg: %s\n", ch, optarg); 317 #endif 318 switch ( ch ) { 319 320 case 'a': /* aspect ratio */ 321 fprintf(stdout, "/aspectratio %s def\n", optarg); 322 break; 323 324 case 'c': /* copies */ 325 copies = atoi(optarg); 326 fprintf(stdout, "/#copies %s store\n", optarg); 327 break; 328 329 case 'e': /* should we add the euro ? */ 330 euro = (strcasecmp(optarg, "on") == 0); 331 break; 332 333 case 'f': /* use this PostScript font */ 334 fontname = get_font(optarg); 335 fprintf(stdout, "/font /%s def\n", fontname); 336 break; 337 338 case 'l': /* lines per page */ 339 linespp = atoi(optarg); 340 break; 341 342 case 'm': /* magnification */ 343 fprintf(stdout, "/magnification %s def\n", optarg); 344 break; 345 346 case 'n': /* forms per page */ 347 formsperpage = atoi(optarg); 348 349 if (formsperpage <= 0) { 350 /* set default value */ 351 formsperpage = 1; 352 } 353 354 fprintf(stdout, "/formsperpage %d def\n", formsperpage); 355 356 break; 357 358 case 'o': /* output page list */ 359 out_list(optarg); 360 break; 361 362 case 'p': /* landscape or portrait mode */ 363 if ( *optarg == 'l' ) 364 fprintf(stdout, "/landscape true def\n"); 365 else fprintf(stdout, "/landscape false def\n"); 366 break; 367 368 case 'r': /* carriage return mode */ 369 crmode = atoi(optarg); 370 break; 371 372 case 's': /* point size */ 373 pointsize = atoi(optarg); 374 fprintf(stdout, "/pointsize %s def\n", optarg); 375 break; 376 377 case 't': /* tabstops */ 378 tabstops = atoi(optarg); 379 380 if (tabstops <= 0) { 381 /* set default */ 382 tabstops = TABSTOPS; 383 } 384 385 break; 386 387 case 'x': /* shift things horizontally */ 388 fprintf(stdout, "/xoffset %s def\n", optarg); 389 break; 390 391 case 'y': /* and vertically on the page */ 392 fprintf(stdout, "/yoffset %s def\n", optarg); 393 break; 394 395 case 'A': /* force job accounting */ 396 case 'J': 397 if ( (fp_acct = fopen(optarg, "a")) == NULL ) 398 error(FATAL, "can't open accounting file %s", optarg); 399 break; 400 401 case 'C': /* copy file straight to output */ 402 if ( cat(optarg) == FALSE ) 403 error(FATAL, "can't read %s", optarg); 404 break; 405 406 case 'L': /* PostScript prologue file */ 407 prologue = optarg; 408 break; 409 410 case 'P': /* PostScript pass through */ 411 fprintf(stdout, "%s\n", optarg); 412 break; 413 414 case 'R': /* special global or page level request */ 415 saverequest(optarg); 416 break; 417 418 case 'D': /* debug flag */ 419 debug = ON; 420 break; 421 422 case 'I': /* ignore FATAL errors */ 423 ignore = ON; 424 break; 425 426 case '?': /* don't understand the option */ 427 error(FATAL, ""); 428 break; 429 430 default: /* don't know what to do for ch */ 431 error(FATAL, "missing case for option %c\n", ch); 432 break; 433 434 } /* End switch */ 435 436 } /* End while */ 437 438 if (euro != 0) 439 fprintf(stdout, "/must-add-euro-to-font true def\n"); 440 441 argc -= optind; /* get ready for non-option args */ 442 argv += optind; 443 444 } /* End of options */ 445 446 447 /*****************************************************************************/ 448 449 450 char *get_font(name) 451 452 453 char *name; /* name the user asked for */ 454 455 456 { 457 458 459 int i; /* for looking through fontmap[] */ 460 461 462 /* 463 * 464 * Called from options() to map a user's font name into a legal PostScript name. 465 * If the lookup fails *name is returned to the caller. That should let you choose 466 * any PostScript font, although things will only work well for constant width 467 * fonts. 468 * 469 */ 470 471 472 for ( i = 0; fontmap[i].name != NULL; i++ ) 473 if ( strcmp(name, fontmap[i].name) == 0 ) 474 return(fontmap[i].val); 475 476 return(name); 477 478 } /* End of get_font */ 479 480 481 /*****************************************************************************/ 482 483 484 static void 485 setup(void) 486 { 487 488 /* 489 * 490 * Handles things that must be done after the options are read but before the 491 * input files are processed. linespp (lines per page) can be set using the -l 492 * option. If it's not positive we calculate a reasonable value using the 493 * requested point size - assuming LINESPP lines fit on a page in point size 494 * POINTSIZE. 495 * 496 */ 497 498 writerequest(0, stdout); /* global requests eg. manual feed */ 499 fprintf(stdout, "setup\n"); 500 501 if ( formsperpage > 1 ) { 502 if ( cat(formfile) == FALSE ) 503 error(FATAL, "can't read %s", formfile); 504 fprintf(stdout, "%d setupforms\n", formsperpage); 505 } /* End if */ 506 507 fprintf(stdout, "%s", ENDSETUP); 508 509 if ( linespp <= 0 ) 510 linespp = LINESPP * POINTSIZE / pointsize; 511 512 } /* End of setup */ 513 514 515 /*****************************************************************************/ 516 517 518 static void 519 arguments(void) 520 { 521 522 /* 523 * 524 * Makes sure all the non-option command line arguments are processed. If we get 525 * here and there aren't any arguments left, or if '-' is one of the input files 526 * we'll translate stdin. 527 * 528 */ 529 530 if ( argc < 1 ) 531 text(); 532 else { /* at least one argument is left */ 533 while ( argc > 0 ) { 534 if ( strcmp(*argv, "-") == 0 ) 535 fp_in = stdin; 536 else if ( (fp_in = fopen(*argv, "r")) == NULL ) 537 error(FATAL, "can't open %s", *argv); 538 text(); 539 if ( fp_in != stdin ) 540 fclose(fp_in); 541 argc--; 542 argv++; 543 } /* End while */ 544 } /* End else */ 545 546 } /* End of arguments */ 547 548 549 /*****************************************************************************/ 550 551 552 static void 553 done(void) 554 { 555 556 /* 557 * 558 * Finished with all the input files, so mark the end of the pages with a TRAILER 559 * comment, make sure the last page prints, and add things like the PAGES comment 560 * that can only be determined after all the input files have been read. 561 * 562 */ 563 if (printed % formsperpage != 0) { /* pad to ENDPAGE */ 564 while (printed % formsperpage) { 565 printed++; 566 567 fprintf(stdout, "save\n"); 568 fprintf(stdout, "mark\n"); 569 writerequest(printed, stdout); 570 fprintf(stdout, "%d pagesetup\n", printed); 571 572 fprintf(stdout, "cleartomark\n"); 573 fprintf(stdout, "showpage\n"); 574 fprintf(stdout, "restore\n"); 575 } 576 fprintf(stdout, "%s %d %d\n", ENDPAGE, page, printed); 577 } 578 579 fprintf(stdout, "%s", TRAILER); 580 fprintf(stdout, "done\n"); 581 fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname); 582 fprintf(stdout, "%s %d\n", PAGES, printed); 583 584 } /* End of done */ 585 586 587 /*****************************************************************************/ 588 589 590 static void 591 account(void) 592 { 593 594 /* 595 * 596 * Writes an accounting record to *fp_acct provided it's not NULL. Accounting is 597 * requested using the -A or -J options. 598 * 599 */ 600 601 if ( fp_acct != NULL ) 602 fprintf(fp_acct, " print %d\n copies %d\n", printed, copies); 603 604 } /* End of account */ 605 606 607 /*****************************************************************************/ 608 609 610 static void 611 text(void) 612 { 613 int ch; /* next input character */ 614 615 /* 616 * 617 * Translates *fp_in into PostScript. All we do here is handle newlines, tabs, 618 * backspaces, and quoting of special characters. All other unprintable characters 619 * are totally ignored. The redirect(-1) call forces the initial output to go to 620 * /dev/null. It's done to force the stuff that formfeed() does at the end of 621 * each page to /dev/null rather than the real output file. 622 * 623 */ 624 625 626 redirect(-1); /* get ready for the first page */ 627 formfeed(); /* force PAGE comment etc. */ 628 629 while ( (ch = getc(fp_in)) != EOF ) 630 631 switch ( ch ) { 632 633 case '\n': 634 newline(); 635 break; 636 637 case '\t': 638 case '\b': 639 case ' ': 640 spaces(ch); 641 break; 642 643 case '\014': 644 formfeed(); 645 break; 646 647 case '\r': 648 if ( crmode == 1 ) 649 spaces(ch); 650 else if ( crmode == 2 ) 651 newline(); 652 break; 653 654 case '(': 655 case ')': 656 case '\\': 657 startline(); 658 putc('\\', fp_out); 659 660 /* 661 * 662 * Fall through to the default case. 663 * 664 */ 665 666 default: 667 if ( isascii(ch) && isprint(ch) ) 668 oput(ch); 669 else { 670 #define isintlprint(ch) ((ch)&0x80) 671 #define isss(ch) 0 672 if (isintlprint(ch) || isss(ch)) { 673 startline(); 674 fprintf(fp_out, "\\%03o", 0xFF&ch); 675 col++; 676 } 677 } 678 break; 679 680 } /* End switch */ 681 682 formfeed(); /* next file starts on a new page? */ 683 684 } /* End of text */ 685 686 687 /*****************************************************************************/ 688 689 690 static void 691 formfeed(void) 692 { 693 694 /* 695 * 696 * Called whenever we've finished with the last page and want to get ready for the 697 * next one. Also used at the beginning and end of each input file, so we have to 698 * be careful about what's done. The first time through (up to the redirect() call) 699 * output goes to /dev/null. 700 * 701 * Adobe now recommends that the showpage operator occur after the page level 702 * restore so it can be easily redefined to have side-effects in the printer's VM. 703 * Although it seems reasonable I haven't implemented it, because it makes other 704 * things, like selectively setting manual feed or choosing an alternate paper 705 * tray, clumsy - at least on a per page basis. 706 * 707 */ 708 709 710 if ( fp_out == stdout ) /* count the last page */ 711 printed++; 712 713 endline(); /* print the last line */ 714 715 fprintf(fp_out, "cleartomark\n"); 716 fprintf(fp_out, "showpage\n"); 717 fprintf(fp_out, "restore\n"); 718 if (printed % formsperpage == 0) 719 fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed); 720 721 if ( ungetc(getc(fp_in), fp_in) == EOF ) 722 redirect(-1); 723 else redirect(++page); 724 725 if (printed % formsperpage == 0) 726 fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1); 727 fprintf(fp_out, "save\n"); 728 fprintf(fp_out, "mark\n"); 729 writerequest(printed+1, fp_out); 730 fprintf(fp_out, "%d pagesetup\n", printed+1); 731 732 line = 1; 733 734 } /* End of formfeed */ 735 736 737 /*****************************************************************************/ 738 739 740 static void 741 newline(void) 742 { 743 744 /* 745 * 746 * Called when we've read a newline character. The call to startline() ensures 747 * that at least an empty string is on the stack. 748 * 749 */ 750 751 startline(); 752 endline(); /* print the current line */ 753 754 if ( ++line > linespp ) /* done with this page */ 755 formfeed(); 756 757 } /* End of newline */ 758 759 760 /*****************************************************************************/ 761 762 763 static void 764 spaces(int ch) 765 /* next input character */ 766 { 767 int endcol; /* ending column */ 768 int i; /* final distance - in spaces */ 769 770 /* 771 * 772 * Counts consecutive spaces, tabs, and backspaces and figures out where the next 773 * string should start. Once that's been done we try to choose an efficient way 774 * to output the required number of spaces. The choice is between using procedure 775 * l with a single string on the stack and L with several string and column pairs. 776 * We usually break even, in terms of the size of the output file, if we need four 777 * consecutive spaces. More means using L decreases the size of the file. For now 778 * if there are less than 6 consecutive spaces we just add them to the current 779 * string, otherwise we end that string, follow it by its starting position, and 780 * begin a new one that starts at endcol. Backspacing is always handled this way. 781 * 782 */ 783 784 785 startline(); /* so col makes sense */ 786 endcol = col; 787 788 do { 789 if ( ch == ' ' ) 790 endcol++; 791 else if ( ch == '\t' ) 792 endcol += tabstops - ((endcol - 1) % tabstops); 793 else if ( ch == '\b' ) 794 endcol--; 795 else if ( ch == '\r' ) 796 endcol = 1; 797 else break; 798 } while ( ch = getc(fp_in) ); /* if ch is 0 we'd quit anyway */ 799 800 ungetc(ch, fp_in); /* wasn't a space, tab, or backspace */ 801 802 if ( endcol < 1 ) /* can't move past left edge */ 803 endcol = 1; 804 805 if ( (i = endcol - col) >= 0 && i < 6 ) 806 for ( ; i > 0; i-- ) 807 oput((int)' '); 808 else { 809 fprintf(fp_out, ")%d(", stringstart-1); 810 stringcount++; 811 col = stringstart = endcol; 812 } /* End else */ 813 814 } /* End of spaces */ 815 816 817 /*****************************************************************************/ 818 819 820 static void 821 startline(void) 822 { 823 824 /* 825 * 826 * Called whenever we want to be certain we're ready to start pushing characters 827 * into an open string on the stack. If stringcount is positive we've already 828 * started, so there's nothing to do. The first string starts in column 1. 829 * 830 */ 831 832 833 if ( stringcount < 1 ) { 834 putc('(', fp_out); 835 stringstart = col = 1; 836 stringcount = 1; 837 } /* End if */ 838 839 } /* End of startline */ 840 841 842 /*****************************************************************************/ 843 844 845 static void 846 endline(void) 847 { 848 849 850 /* 851 * 852 * Generates a call to the PostScript procedure that processes all the text on 853 * the stack - provided stringcount is positive. If one string is on the stack 854 * the fast procedure (ie. l) is used to print the line, otherwise the slower 855 * one that processes string and column pairs is used. 856 * 857 */ 858 859 860 if ( stringcount == 1 ) 861 fprintf(fp_out, ")l\n"); 862 else if ( stringcount > 1 ) 863 fprintf(fp_out, ")%d L\n", stringstart-1); 864 865 stringcount = 0; 866 867 } /* End of endline */ 868 869 870 /*****************************************************************************/ 871 872 873 static void 874 oput(int ch) 875 /* next output character */ 876 { 877 878 /* 879 * 880 * Responsible for adding all printing characters from the input file to the 881 * open string on top of the stack. The only other characters that end up in 882 * that string are the quotes required for special characters. Some simple 883 * changes here and in spaces could make line wrapping possible. Doing a good 884 * job would probably force lots of printer dependent stuff into the program, 885 * so I haven't bothered with it. Could also change the prologue, or perhaps 886 * write a different one, that uses kshow instead of show to display strings. 887 * 888 */ 889 890 891 startline(); 892 putc(ch, fp_out); 893 col++; 894 895 } /* End of oput */ 896 897 898 /*****************************************************************************/ 899 900 901 static void 902 redirect(int pg) 903 /* next page we're printing */ 904 { 905 static FILE *fp_null = NULL; /* if output is turned off */ 906 907 /* 908 * 909 * If we're not supposed to print page pg, fp_out will be directed to /dev/null, 910 * otherwise output goes to stdout. 911 * 912 */ 913 914 915 if ( pg >= 0 && in_olist(pg) == ON ) 916 fp_out = stdout; 917 else if ( (fp_out = fp_null) == NULL ) 918 fp_out = fp_null = fopen("/dev/null", "w"); 919 920 } /* End of redirect */ 921 922 923 /*****************************************************************************/ 924 925 926