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
main(agc,agv)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
init_signals()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
header()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
options()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
get_font(name)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
setup()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
arguments()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
done()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
account()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
text()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
inittabs()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
cleartabs()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
formfeed()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
linefeed()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
carriage()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
htab()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
vtab()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
backspace()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
escape()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
vmot(n)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
vgoto(n)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
hmot(n)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
hgoto(n)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
changefont(name)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
startline()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
endline()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
endstring()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
oput(ch)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
redirect(pg)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