xref: /plan9/sys/src/cmd/postscript/postdaisy/postdaisy.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 #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