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