1 /* 2 * 3 * postdaisy - PostScript translator for Diablo 1640 files. 4 * 5 * A program that translates Diablo 1640 files into PostScript. Absolutely nothing 6 * is guaranteed. Quite a few things haven't been implemented, and what's been 7 * done isn't well tested. Most of the documentation used to write this program 8 * was taken from the 'Diablo Emulator' section of a recent Imagen manual. 9 * 10 * Some of document comments that are generated may not be right. Most of the test 11 * files I used produced a trailing blank page. I've put a check in formfeed() that 12 * won't print the last page if it doesn't contain any text, but PAGES comments may 13 * not be right. The DOCUMENTFONTS comment will also be wrong if auto underline or 14 * bold printing have been turned on by escape commands. 15 * 16 * The brute force approach used to implement horizontal and vertical tabs leaves 17 * much to be desired, and may not work for very small initial hmi and vmi values. 18 * At the very least I should have used malloc() to get space for the two tabstop 19 * arrays after hmi and vmi are known! 20 * 21 * Reverse printing mode hasn't been tested at all, but what's here should be 22 * close even though it's not efficient. 23 * 24 * The PostScript prologue is copied from *prologue before any of the input files 25 * are translated. The program expects that the following PostScript procedures 26 * are defined in that file: 27 * 28 * setup 29 * 30 * mark ... setup - 31 * 32 * Handles special initialization stuff that depends on how this program 33 * was called. Expects to find a mark followed by key/value pairs on the 34 * stack. The def operator is applied to each pair up to the mark, then 35 * the default state is set up. 36 * 37 * pagesetup 38 * 39 * page pagesetup - 40 * 41 * Does whatever is needed to set things up for the next page. Expects to 42 * find the current page number on the stack. 43 * 44 * t 45 * 46 * mark str1 x1 str2 x2 ... strn xn y hmi t mark 47 * 48 * Handles all the text on the stack. Characters in the strings are 49 * printed using hmi as the character advance, and all strings are at 50 * vertical position y. Each string is begins at the horizontal position 51 * that preceeds it. 52 * 53 * f 54 * 55 * font f - 56 * 57 * Use font f, where f is the full PostScript font name. Only used when 58 * we switch to auto underline (Courier-Italic) or bold (Courier-Bold) 59 * printing. 60 * 61 * done 62 * 63 * done 64 * 65 * Makes sure the last page is printed. Only needed when we're printing 66 * more than one page on each sheet of paper. 67 * 68 * Many default values, like the magnification and orientation, are defined in 69 * the prologue, which is where they belong. If they're changed (by options), an 70 * appropriate definition is made after the prologue is added to the output file. 71 * The -P option passes arbitrary PostScript through to the output file. Among 72 * other things it can be used to set (or change) values that can't be accessed by 73 * other options. 74 * 75 */ 76 77 #include <stdio.h> 78 #include <signal.h> 79 #include <ctype.h> 80 #ifdef plan9 81 #define isascii(c) ((unsigned char)(c)<=0177) 82 #endif 83 #include <sys/types.h> 84 #include <fcntl.h> 85 86 #include "comments.h" /* PostScript file structuring comments */ 87 #include "gen.h" /* general purpose definitions */ 88 #include "path.h" /* for the prologue */ 89 #include "ext.h" /* external variable declarations */ 90 #include "postdaisy.h" /* a few special definitions */ 91 92 char *optnames = "a:c:f:h:l:m:n:o:p:r:s:v:x:y:A:C:E:J:L:P:DI"; 93 94 char *prologue = POSTDAISY; /* default PostScript prologue */ 95 char *formfile = FORMFILE; /* stuff for multiple pages per sheet */ 96 97 int formsperpage = 1; /* page images on each piece of paper */ 98 int copies = 1; /* and this many copies of each sheet */ 99 100 char htabstops[COLUMNS]; /* horizontal */ 101 char vtabstops[ROWS]; /* and vertical tabs */ 102 103 int res = RES; /* input file resolution - sort of */ 104 105 int hmi = HMI; /* horizontal motion index - 1/120 inch */ 106 int vmi = VMI; /* vertical motion index - 1/48 inch */ 107 int ohmi = HMI; /* original hmi */ 108 int ovmi = VMI; /* and vmi - for tabs and char size */ 109 110 int hpos = 0; /* current horizontal */ 111 int vpos = 0; /* and vertical position */ 112 113 int lastx = -1; /* printer's last horizontal */ 114 int lasty = -1; /* and vertical position */ 115 int lasthmi = -1; /* hmi for current text strings */ 116 117 int lastc = -1; /* last printed character */ 118 int prevx = -1; /* at this position */ 119 120 int leftmargin = LEFTMARGIN; /* page margins */ 121 int rightmargin = RIGHTMARGIN; 122 int topmargin = TOPMARGIN; 123 int bottommargin = BOTTOMMARGIN; 124 125 int stringcount = 0; /* number of strings on the stack */ 126 int stringstart = 1; /* column where current one starts */ 127 int advance = 1; /* -1 if in backward print mode */ 128 129 int lfiscr = OFF; /* line feed implies carriage return */ 130 int crislf = OFF; /* carriage return implies line feed */ 131 132 int linespp = 0; /* lines per page if it's positive */ 133 int markedpage = FALSE; /* helps prevent trailing blank page */ 134 int page = 0; /* page we're working on */ 135 int printed = 0; /* printed this many pages */ 136 137 Fontmap fontmap[] = FONTMAP; /* for translating font names */ 138 char *fontname = "Courier"; /* use this PostScript font */ 139 int shadowprint = OFF; /* automatic bold printing if ON */ 140 141 FILE *fp_in; /* read from this file */ 142 FILE *fp_out = stdout; /* and write stuff here */ 143 FILE *fp_acct = NULL; /* for accounting data */ 144 145 /*****************************************************************************/ 146 147 main(agc, agv) 148 149 int agc; 150 char *agv[]; 151 152 { 153 154 /* 155 * 156 * A simple program that translates Diablo 1640 files into PostScript. Nothing 157 * is guaranteed - the program not well tested and doesn't implement everything. 158 * 159 */ 160 161 argc = agc; /* other routines may want them */ 162 argv = agv; 163 164 prog_name = argv[0]; /* really just for error messages */ 165 166 init_signals(); /* sets up interrupt handling */ 167 header(); /* PostScript header comments */ 168 options(); /* handle the command line options */ 169 setup(); /* for PostScript */ 170 arguments(); /* followed by each input file */ 171 done(); /* print the last page etc. */ 172 account(); /* job accounting data */ 173 174 exit(x_stat); /* not much could be wrong */ 175 176 } /* End of main */ 177 178 /*****************************************************************************/ 179 180 init_signals() 181 182 { 183 184 /* 185 * 186 * Makes sure we handle interrupts. 187 * 188 */ 189 190 if ( signal(SIGINT, interrupt) == SIG_IGN ) { 191 signal(SIGINT, SIG_IGN); 192 signal(SIGQUIT, SIG_IGN); 193 signal(SIGHUP, SIG_IGN); 194 } else { 195 signal(SIGHUP, interrupt); 196 signal(SIGQUIT, interrupt); 197 } /* End else */ 198 199 signal(SIGTERM, interrupt); 200 201 } /* End of init_signals */ 202 203 /*****************************************************************************/ 204 205 header() 206 207 { 208 209 int ch; /* return value from getopt() */ 210 int old_optind = optind; /* for restoring optind - should be 1 */ 211 212 /* 213 * 214 * Scans the option list looking for things, like the prologue file, that we need 215 * right away but could be changed from the default. Doing things this way is an 216 * attempt to conform to Adobe's latest file structuring conventions. In particular 217 * they now say there should be nothing executed in the prologue, and they have 218 * added two new comments that delimit global initialization calls. Once we know 219 * where things really are we write out the job header, follow it by the prologue, 220 * and then add the ENDPROLOG and BEGINSETUP comments. 221 * 222 */ 223 224 while ( (ch = getopt(argc, argv, optnames)) != EOF ) 225 if ( ch == 'L' ) 226 prologue = optarg; 227 else if ( ch == '?' ) 228 error(FATAL, ""); 229 230 optind = old_optind; /* get ready for option scanning */ 231 232 fprintf(stdout, "%s", CONFORMING); 233 fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION); 234 fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND); 235 fprintf(stdout, "%s %s\n", PAGES, ATEND); 236 fprintf(stdout, "%s", ENDCOMMENTS); 237 238 if ( cat(prologue) == FALSE ) 239 error(FATAL, "can't read %s", prologue); 240 241 if ( DOROUND ) 242 cat(ROUNDPAGE); 243 244 fprintf(stdout, "%s", ENDPROLOG); 245 fprintf(stdout, "%s", BEGINSETUP); 246 fprintf(stdout, "mark\n"); 247 248 } /* End of header */ 249 250 /*****************************************************************************/ 251 252 options() 253 254 { 255 256 int ch; /* return value from getopt() */ 257 int n; /* for CR and LF modes */ 258 259 /* 260 * 261 * Reads and processes the command line options. Added the -P option so arbitrary 262 * PostScript code can be passed through. Expect it could be useful for changing 263 * definitions in the prologue for which options have not been defined. 264 * 265 * Although any PostScript font can be used, things will only work for constant 266 * width fonts. 267 * 268 */ 269 270 while ( (ch = getopt(argc, argv, optnames)) != EOF ) { 271 switch ( ch ) { 272 case 'a': /* aspect ratio */ 273 fprintf(stdout, "/aspectratio %s def\n", optarg); 274 break; 275 276 case 'c': /* copies */ 277 copies = atoi(optarg); 278 fprintf(stdout, "/#copies %s store\n", optarg); 279 break; 280 281 case 'f': /* use this PostScript font */ 282 fontname = get_font(optarg); 283 fprintf(stdout, "/font /%s def\n", fontname); 284 break; 285 286 case 'h': /* default character spacing */ 287 ohmi = hmi = atoi(optarg) * HSCALE; 288 fprintf(stdout, "/hmi %s def\n", optarg); 289 break; 290 291 case 'l': /* lines per page */ 292 linespp = atoi(optarg); 293 break; 294 295 case 'm': /* magnification */ 296 fprintf(stdout, "/magnification %s def\n", optarg); 297 break; 298 299 case 'n': /* forms per page */ 300 formsperpage = atoi(optarg); 301 fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg); 302 fprintf(stdout, "/formsperpage %s def\n", optarg); 303 break; 304 305 case 'o': /* output page list */ 306 out_list(optarg); 307 break; 308 309 case 'p': /* landscape or portrait mode */ 310 if ( *optarg == 'l' ) 311 fprintf(stdout, "/landscape true def\n"); 312 else fprintf(stdout, "/landscape false def\n"); 313 break; 314 315 case 'r': /* set CR and LF modes */ 316 n = atoi(optarg); 317 if ( n & 01 ) 318 lfiscr = ON; 319 else lfiscr = OFF; 320 if ( n & 02 ) 321 crislf = ON; 322 else crislf = OFF; 323 break; 324 325 case 's': /* point size */ 326 fprintf(stdout, "/pointsize %s def\n", optarg); 327 break; 328 329 case 'v': /* default line spacing */ 330 ovmi = vmi = atoi(optarg) * VSCALE; 331 break; 332 333 case 'x': /* shift things horizontally */ 334 fprintf(stdout, "/xoffset %s def\n", optarg); 335 break; 336 337 case 'y': /* and vertically on the page */ 338 fprintf(stdout, "/yoffset %s def\n", optarg); 339 break; 340 341 case 'A': /* force job accounting */ 342 case 'J': 343 if ( (fp_acct = fopen(optarg, "a")) == NULL ) 344 error(FATAL, "can't open accounting file %s", optarg); 345 break; 346 347 case 'C': /* copy file straight to output */ 348 if ( cat(optarg) == FALSE ) 349 error(FATAL, "can't read %s", optarg); 350 break; 351 352 case 'E': /* text font encoding */ 353 fontencoding = optarg; 354 break; 355 356 case 'L': /* PostScript prologue file */ 357 prologue = optarg; 358 break; 359 360 case 'P': /* PostScript pass through */ 361 fprintf(stdout, "%s\n", optarg); 362 break; 363 364 case 'R': /* special global or page level request */ 365 saverequest(optarg); 366 break; 367 368 case 'D': /* debug flag */ 369 debug = ON; 370 break; 371 372 case 'I': /* ignore FATAL errors */ 373 ignore = ON; 374 break; 375 376 case '?': /* don't understand the option */ 377 error(FATAL, ""); 378 break; 379 380 default: /* don't know what to do for ch */ 381 error(FATAL, "missing case for option %c\n", ch); 382 break; 383 } /* End switch */ 384 } /* End while */ 385 386 argc -= optind; /* get ready for non-option args */ 387 argv += optind; 388 389 } /* End of options */ 390 391 /*****************************************************************************/ 392 393 char *get_font(name) 394 395 char *name; /* name the user asked for */ 396 397 { 398 399 int i; /* for looking through fontmap[] */ 400 401 /* 402 * 403 * Called from options() to map a user's font name into a legal PostScript name. 404 * If the lookup fails *name is returned to the caller. That should let you choose 405 * any PostScript font, although things will only work well for constant width 406 * fonts. 407 * 408 */ 409 410 for ( i = 0; fontmap[i].name != NULL; i++ ) 411 if ( strcmp(name, fontmap[i].name) == 0 ) 412 return(fontmap[i].val); 413 414 return(name); 415 416 } /* End of get_font */ 417 418 /*****************************************************************************/ 419 420 setup() 421 422 { 423 424 /* 425 * 426 * Handles things that must be done after the options are read but before the 427 * input files are processed. 428 * 429 */ 430 431 writerequest(0, stdout); /* global requests eg. manual feed */ 432 setencoding(fontencoding); 433 fprintf(stdout, "setup\n"); 434 435 if ( formsperpage > 1 ) { 436 if ( cat(formfile) == FALSE ) 437 error(FATAL, "can't read %s", formfile); 438 fprintf(stdout, "%d setupforms\n", formsperpage); 439 } /* End if */ 440 441 fprintf(stdout, "%s", ENDSETUP); 442 443 } /* End of setup */ 444 445 /*****************************************************************************/ 446 447 arguments() 448 449 { 450 451 /* 452 * 453 * Makes sure all the non-option command line arguments are processed. If we get 454 * here and there aren't any arguments left, or if '-' is one of the input files 455 * we'll process stdin. 456 * 457 */ 458 459 fp_in = stdin; 460 461 if ( argc < 1 ) 462 text(); 463 else { /* at least one argument is left */ 464 while ( argc > 0 ) { 465 if ( strcmp(*argv, "-") == 0 ) 466 fp_in = stdin; 467 else if ( (fp_in = fopen(*argv, "r")) == NULL ) 468 error(FATAL, "can't open %s", *argv); 469 text(); 470 if ( fp_in != stdin ) 471 fclose(fp_in); 472 argc--; 473 argv++; 474 } /* End while */ 475 } /* End else */ 476 477 } /* End of arguments */ 478 479 /*****************************************************************************/ 480 481 done() 482 483 { 484 485 /* 486 * 487 * Finished with all the input files, so mark the end of the pages, make sure the 488 * last page is printed, and restore the initial environment. 489 * 490 */ 491 492 fprintf(stdout, "%s", TRAILER); 493 fprintf(stdout, "done\n"); 494 fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname); 495 fprintf(stdout, "%s %d\n", PAGES, printed); 496 497 } /* End of done */ 498 499 /*****************************************************************************/ 500 501 account() 502 503 { 504 505 /* 506 * 507 * Writes an accounting record to *fp_acct provided it's not NULL. Accounting 508 * is requested using the -A or -J options. 509 * 510 */ 511 512 if ( fp_acct != NULL ) 513 fprintf(fp_acct, " print %d\n copies %d\n", printed, copies); 514 515 } /* End of account */ 516 517 /*****************************************************************************/ 518 519 text() 520 521 { 522 523 int ch; /* next input character */ 524 525 /* 526 * 527 * Translates the next input file into PostScript. The redirect(-1) call forces 528 * the initial output to go to /dev/null - so the stuff formfeed() does at the 529 * end of each page doesn't go to stdout. 530 * 531 */ 532 533 redirect(-1); /* get ready for the first page */ 534 formfeed(); /* force PAGE comment etc. */ 535 inittabs(); 536 537 while ( (ch = getc(fp_in)) != EOF ) 538 switch ( ch ) { 539 case '\010': /* backspace */ 540 backspace(); 541 break; 542 543 case '\011': /* horizontal tab */ 544 htab(); 545 break; 546 547 case '\012': /* new line */ 548 linefeed(); 549 break; 550 551 case '\013': /* vertical tab */ 552 vtab(); 553 break; 554 555 case '\014': /* form feed */ 556 formfeed(); 557 break; 558 559 case '\015': /* carriage return */ 560 carriage(); 561 break; 562 563 case '\016': /* extended character set - SO */ 564 break; 565 566 case '\017': /* extended character set - SI */ 567 break; 568 569 case '\031': /* next char from supplementary set */ 570 break; 571 572 case '\033': /* 2 or 3 byte escape sequence */ 573 escape(); 574 break; 575 576 default: 577 oput(ch); 578 break; 579 } /* End switch */ 580 581 formfeed(); /* next file starts on a new page? */ 582 583 } /* End of text */ 584 585 /*****************************************************************************/ 586 587 inittabs() 588 589 { 590 591 int i; /* loop index */ 592 593 /* 594 * 595 * Initializes the horizontal and vertical tab arrays. The way tabs are handled is 596 * quite inefficient and may not work for all initial hmi or vmi values. 597 * 598 */ 599 600 for ( i = 0; i < COLUMNS; i++ ) 601 htabstops[i] = ((i % 8) == 0) ? ON : OFF; 602 603 for ( i = 0; i < ROWS; i++ ) 604 vtabstops[i] = ((i * ovmi) > BOTTOMMARGIN) ? ON : OFF; 605 606 } /* End of inittabs */ 607 608 /*****************************************************************************/ 609 610 cleartabs() 611 612 { 613 614 int i; /* loop index */ 615 616 /* 617 * 618 * Clears all horizontal and vertical tab stops. 619 * 620 */ 621 622 for ( i = 0; i < ROWS; i++ ) 623 htabstops[i] = OFF; 624 625 for ( i = 0; i < COLUMNS; i++ ) 626 vtabstops[i] = OFF; 627 628 } /* End of cleartabs */ 629 630 /*****************************************************************************/ 631 632 formfeed() 633 634 { 635 636 /* 637 * 638 * Called whenever we've finished with the last page and want to get ready for the 639 * next one. Also used at the beginning and end of each input file, so we have to 640 * be careful about what's done. I've added a simple test before the showpage that 641 * should eliminate the extra blank page that was put out at the end of many jobs, 642 * but the PAGES comments may be wrong. 643 * 644 */ 645 646 if ( fp_out == stdout ) /* count the last page */ 647 printed++; 648 649 endline(); /* print the last line */ 650 651 fprintf(fp_out, "cleartomark\n"); 652 if ( feof(fp_in) == 0 || markedpage == TRUE ) 653 fprintf(fp_out, "showpage\n"); 654 fprintf(fp_out, "saveobj restore\n"); 655 fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed); 656 657 if ( ungetc(getc(fp_in), fp_in) == EOF ) 658 redirect(-1); 659 else redirect(++page); 660 661 fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1); 662 fprintf(fp_out, "/saveobj save def\n"); 663 fprintf(fp_out, "mark\n"); 664 writerequest(printed+1, fp_out); 665 fprintf(fp_out, "%d pagesetup\n", printed+1); 666 667 vgoto(topmargin); 668 hgoto(leftmargin); 669 670 markedpage = FALSE; 671 672 } /* End of formfeed */ 673 674 /*****************************************************************************/ 675 676 linefeed() 677 678 { 679 680 int line = 0; /* current line - based on ovmi */ 681 682 /* 683 * 684 * Adjust our current vertical position. If we've passed the bottom of the page 685 * or exceeded the number of lines per page, print it and go to the upper left 686 * corner of the next page. This routine is also called from carriage() if crislf 687 * is ON. 688 * 689 */ 690 691 vmot(vmi); 692 693 if ( lfiscr == ON ) 694 hgoto(leftmargin); 695 696 if ( linespp > 0 ) /* means something so see where we are */ 697 line = vpos / ovmi + 1; 698 699 if ( vpos > bottommargin || line > linespp ) 700 formfeed(); 701 702 } /* End of linefeed */ 703 704 /*****************************************************************************/ 705 706 carriage() 707 708 { 709 710 /* 711 * 712 * Handles carriage return character. If crislf is ON we'll generate a line feed 713 * every time we get a carriage return character. 714 * 715 */ 716 717 if ( shadowprint == ON ) /* back to normal mode */ 718 changefont(fontname); 719 720 advance = 1; 721 shadowprint = OFF; 722 723 hgoto(leftmargin); 724 725 if ( crislf == ON ) 726 linefeed(); 727 728 } /* End of carriage */ 729 730 /*****************************************************************************/ 731 732 htab() 733 734 { 735 736 int col; /* 'column' we'll be at next */ 737 int i; /* loop index */ 738 739 /* 740 * 741 * Tries to figure out where the next tab stop is. Wasn't positive about this 742 * one, since hmi can change. I'll assume columns are determined by the original 743 * value of hmi. That fixes them on the page, which seems to make more sense than 744 * letting them float all over the place. 745 * 746 */ 747 748 endline(); 749 750 col = hpos/ohmi + 1; 751 for ( i = col; i < ROWS; i++ ) 752 if ( htabstops[i] == ON ) { 753 col = i; 754 break; 755 } /* End if */ 756 757 hgoto(col * ohmi); 758 lastx = hpos; 759 760 } /* End of htab */ 761 762 /*****************************************************************************/ 763 764 vtab() 765 766 { 767 768 int line; /* line we'll be at next */ 769 int i; /* loop index */ 770 771 /* 772 * 773 * Looks for the next vertical tab stop in the vtabstops[] array and moves to that 774 * line. If we don't find a tab we'll just move down one line - shouldn't happen. 775 * 776 */ 777 778 endline(); 779 780 line = vpos/ovmi + 1; 781 for ( i = line; i < COLUMNS; i++ ) 782 if ( vtabstops[i] == ON ) { 783 line = i; 784 break; 785 } /* End if */ 786 787 vgoto(line * ovmi); 788 789 } /* End of vtab */ 790 791 /*****************************************************************************/ 792 793 backspace() 794 795 { 796 797 /* 798 * 799 * Moves backwards a distance equal to the current value of hmi, but don't go 800 * past the left margin. 801 * 802 */ 803 804 endline(); 805 806 if ( hpos - leftmargin >= hmi ) 807 hmot(-hmi); 808 else hgoto(leftmargin); /* maybe just ignore the backspace?? */ 809 810 lastx = hpos; 811 812 } /* End of backspace */ 813 814 /*****************************************************************************/ 815 816 escape() 817 818 { 819 820 int ch; /* control character */ 821 822 /* 823 * 824 * Handles special codes that are expected to follow an escape character. The 825 * initial escape character is followed by one or two bytes. 826 * 827 */ 828 829 switch ( ch = getc(fp_in) ) { 830 case 'T': /* top margin */ 831 topmargin = vpos; 832 break; 833 834 case 'L': /* bottom margin */ 835 bottommargin = vpos; 836 break; 837 838 case 'C': /* clear top and bottom margins */ 839 bottommargin = BOTTOMMARGIN; 840 topmargin = TOPMARGIN; 841 break; 842 843 case '9': /* left margin */ 844 leftmargin = hpos; 845 break; 846 847 case '0': /* right margin */ 848 rightmargin = hpos; 849 break; 850 851 case '1': /* set horizontal tab */ 852 htabstops[hpos/ohmi] = ON; 853 break; 854 855 case '8': /* clear horizontal tab at hpos */ 856 htabstops[hpos/ohmi] = OFF; 857 break; 858 859 case '-': /* set vertical tab */ 860 vtabstops[vpos/ovmi] = ON; 861 break; 862 863 case '2': /* clear all tabs */ 864 cleartabs(); 865 break; 866 867 case '\014': /* set lines per page */ 868 linespp = getc(fp_in); 869 break; 870 871 case '\037': /* set hmi to next byte minus 1 */ 872 hmi = HSCALE * (getc(fp_in) - 1); 873 break; 874 875 case 'S': /* reset hmi to default */ 876 hmi = ohmi; 877 break; 878 879 case '\011': /* move to column given by next byte */ 880 hgoto((getc(fp_in)-1) * ohmi); 881 break; 882 883 case '?': /* do carriage return after line feed */ 884 lfiscr = ON; 885 break; 886 887 case '!': /* don't generate carriage return */ 888 lfiscr = OFF; 889 break; 890 891 case '5': /* forward print mode */ 892 advance = 1; 893 break; 894 895 case '6': /* backward print mode */ 896 advance = -1; 897 break; 898 899 case '\036': /* set vmi to next byte minus 1 */ 900 vmi = VSCALE * (getc(fp_in) - 1); 901 break; 902 903 case '\013': /* move to line given by next byte */ 904 vgoto((getc(fp_in)-1) * ovmi); 905 break; 906 907 case 'U': /* positive half line feed */ 908 vmot(vmi/2); 909 break; 910 911 case 'D': /* negative half line feed */ 912 vmot(-vmi/2); 913 break; 914 915 case '\012': /* negative line feed */ 916 vmot(-vmi); 917 break; 918 919 case '\015': /* clear all margins */ 920 bottommargin = BOTTOMMARGIN; 921 topmargin = TOPMARGIN; 922 leftmargin = BOTTOMMARGIN; 923 rightmargin = RIGHTMARGIN; 924 break; 925 926 case 'E': /* auto underscore - use italic font */ 927 changefont("/Courier-Oblique"); 928 break; 929 930 case 'R': /* disable auto underscore */ 931 changefont(fontname); 932 break; 933 934 case 'O': /* bold/shadow printing */ 935 case 'W': 936 changefont("/Courier-Bold"); 937 shadowprint = ON; 938 break; 939 940 case '&': /* disable bold printing */ 941 changefont(fontname); 942 shadowprint = OFF; 943 break; 944 945 case '/': /* ignored 2 byte escapes */ 946 case '\\': 947 case '<': 948 case '>': 949 case '%': 950 case '=': 951 case '.': 952 case '4': 953 case 'A': 954 case 'B': 955 case 'M': 956 case 'N': 957 case 'P': 958 case 'Q': 959 case 'X': 960 case '\010': 961 break; 962 963 case ',': /* ignored 3 byte escapes */ 964 case '\016': 965 case '\021': 966 getc(fp_in); 967 break; 968 969 case '3': /* graphics mode - should quit! */ 970 case '7': 971 case 'G': 972 case 'V': 973 case 'Y': 974 case 'Z': 975 error(FATAL, "graphics mode is not implemented"); 976 break; 977 978 default: 979 error(FATAL, "missing case for escape o%o\n", ch); 980 break; 981 } /* End switch */ 982 983 } /* End of escape */ 984 985 /*****************************************************************************/ 986 987 vmot(n) 988 989 int n; /* move this far vertically */ 990 991 { 992 993 /* 994 * 995 * Move vertically n units from where we are. 996 * 997 */ 998 999 vpos += n; 1000 1001 } /* End of vmot */ 1002 1003 /*****************************************************************************/ 1004 1005 vgoto(n) 1006 1007 int n; /* new vertical position */ 1008 1009 { 1010 1011 /* 1012 * 1013 * Moves to absolute vertical position n. 1014 * 1015 */ 1016 1017 vpos = n; 1018 1019 } /* End of vgoto */ 1020 1021 /*****************************************************************************/ 1022 1023 hmot(n) 1024 1025 int n; /* move this horizontally */ 1026 1027 { 1028 1029 /* 1030 * 1031 * Moves horizontally n units from our current position. 1032 * 1033 */ 1034 1035 hpos += n * advance; 1036 1037 if ( hpos < leftmargin ) 1038 hpos = leftmargin; 1039 1040 } /* End of hmot */ 1041 1042 /*****************************************************************************/ 1043 1044 hgoto(n) 1045 1046 int n; /* go to this horizontal position */ 1047 1048 { 1049 1050 /* 1051 * 1052 * Moves to absolute horizontal position n. 1053 * 1054 */ 1055 1056 hpos = n; 1057 1058 } /* End of hgoto */ 1059 1060 /*****************************************************************************/ 1061 1062 changefont(name) 1063 1064 char *name; 1065 1066 { 1067 1068 /* 1069 * 1070 * Changes the current font. Used to get in and out of auto underscore and bold 1071 * printing. 1072 * 1073 */ 1074 1075 endline(); 1076 fprintf(fp_out, "%s f\n", name); 1077 1078 } /* End of changefont */ 1079 1080 /*****************************************************************************/ 1081 1082 startline() 1083 1084 { 1085 1086 /* 1087 * 1088 * Called whenever we want to be certain we're ready to start pushing characters 1089 * into an open string on the stack. If stringcount is positive we've already 1090 * started, so there's nothing to do. The first string starts in column 1. 1091 * 1092 */ 1093 1094 if ( stringcount < 1 ) { 1095 putc('(', fp_out); 1096 stringstart = lastx = hpos; 1097 lasty = vpos; 1098 lasthmi = hmi; 1099 lastc = -1; 1100 prevx = -1; 1101 stringcount = 1; 1102 } /* End if */ 1103 1104 } /* End of startline */ 1105 1106 /*****************************************************************************/ 1107 1108 endline() 1109 1110 { 1111 1112 /* 1113 * 1114 * Generates a call to the PostScript procedure that processes the text on the 1115 * the stack - provided stringcount is positive. 1116 * 1117 */ 1118 1119 if ( stringcount > 0 ) 1120 fprintf(fp_out, ")%d %d %d t\n", stringstart, lasty, lasthmi); 1121 1122 stringcount = 0; 1123 1124 } /* End of endline */ 1125 1126 /*****************************************************************************/ 1127 1128 endstring() 1129 1130 { 1131 1132 /* 1133 * 1134 * Takes the string we've been working on and adds it to the output file. Called 1135 * when we need to adjust our horizontal position before starting a new string. 1136 * Also called from endline() when we're done with the current line. 1137 * 1138 */ 1139 1140 if ( stringcount > 0 ) { 1141 fprintf(fp_out, ")%d(", stringstart); 1142 lastx = stringstart = hpos; 1143 stringcount++; 1144 } /* End if */ 1145 1146 } /* End of endstring */ 1147 1148 /*****************************************************************************/ 1149 1150 oput(ch) 1151 1152 int ch; /* next output character */ 1153 1154 { 1155 1156 /* 1157 * 1158 * Responsible for adding all printing characters from the input file to the 1159 * open string on top of the stack. The only other characters that end up in 1160 * that string are the quotes required for special characters. Reverse printing 1161 * mode hasn't been tested but it should be close. hpos and lastx should disagree 1162 * each time (except after startline() does something), and that should force a 1163 * call to endstring() for every character. 1164 * 1165 */ 1166 1167 if ( stringcount > 100 ) /* don't put too much on the stack */ 1168 endline(); 1169 1170 if ( vpos != lasty ) 1171 endline(); 1172 1173 if ( advance == -1 ) /* for reverse printing - move first */ 1174 hmot(hmi); 1175 1176 startline(); 1177 1178 if ( lastc != ch || hpos != prevx ) { 1179 if ( lastx != hpos ) 1180 endstring(); 1181 1182 if ( isascii(ch) && isprint(ch) ) { 1183 if ( ch == '\\' || ch == '(' || ch == ')' ) 1184 putc('\\', fp_out); 1185 putc(ch, fp_out); 1186 } else fprintf(fp_out, "\\%.3o", ch & 0377); 1187 1188 lastc = ch; 1189 prevx = hpos; 1190 lastx += lasthmi; 1191 } /* End if */ 1192 1193 if ( advance != -1 ) 1194 hmot(hmi); 1195 1196 markedpage = TRUE; 1197 1198 } /* End of oput */ 1199 1200 /*****************************************************************************/ 1201 1202 redirect(pg) 1203 1204 int pg; /* next page we're printing */ 1205 1206 { 1207 1208 static FILE *fp_null = NULL; /* if output is turned off */ 1209 1210 /* 1211 * 1212 * If we're not supposed to print page pg, fp_out will be directed to /dev/null, 1213 * otherwise output goes to stdout. 1214 * 1215 */ 1216 1217 if ( pg >= 0 && in_olist(pg) == ON ) 1218 fp_out = stdout; 1219 else if ( (fp_out = fp_null) == NULL ) 1220 fp_out = fp_null = fopen("/dev/null", "w"); 1221 1222 } /* End of redirect */ 1223 1224 /*****************************************************************************/ 1225 1226