xref: /plan9/sys/src/cmd/postscript/postdaisy/Opostdaisy.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
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