xref: /plan9/sys/src/cmd/postscript/posttek/posttek.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 /*
2  *
3  * posttek - PostScript translator for tektronix 4014 files
4  *
5  * A program that can be used to translate tektronix 4014 files into PostScript.
6  * Most of the code was borrowed from the tektronix 4014 emulator that was written
7  * for DMDs. Things have been cleaned up some, but there's still plently that
8  * could be done.
9  *
10  * The PostScript prologue is copied from *prologue before any of the input files
11  * are translated. The program expects that the following PostScript procedures
12  * are defined in that file:
13  *
14  *	setup
15  *
16  *	  mark ... setup -
17  *
18  *	    Handles special initialization stuff that depends on how the program
19  *	    was called. Expects to find a mark followed by key/value pairs on the
20  *	    stack. The def operator is applied to each pair up to the mark, then
21  *	    the default state is set up.
22  *
23  *	pagesetup
24  *
25  *	  page pagesetup -
26  *
27  *	    Does whatever is needed to set things up for the next page. Expects
28  *	    to find the current page number on the stack.
29  *
30  *	v
31  *
32  *	  mark dx1 dy1 ... dxn dyn x y v mark
33  *
34  *	    Draws the vector described by the numbers on the stack. The top two
35  *	    numbers are the starting point. The rest are relative displacements
36  *	    from the preceeding point. Must make sure we don't put too much on
37  *	    the stack!
38  *
39  *	t
40  *
41  *	  x y string t -
42  *
43  *	    Prints the string that's on the top of the stack starting at point
44  *	    (x, y).
45  *
46  *	p
47  *
48  *	  x y p -
49  *
50  *	    Marks the point (x, y) with a circle whose radius varies with the
51  *	    current intensity setting.
52  *
53  *	i
54  *
55  *	  percent focus i -
56  *
57  *	    Changes the size of the circle used to mark individual points to
58  *	    percent of maximum for focused mode (focus=1) or defocused mode
59  *	    (focus=0). The implementation leaves much to be desired!
60  *
61  *	l
62  *
63  *	  mark array l mark
64  *
65  *	    Set the line drawing mode according to the description given in array.
66  *	    The arrays that describe the different line styles are declared in
67  *	    STYLES (file posttek.h). The array really belongs in the prologue!
68  *
69  *	w
70  *
71  *	  n w -
72  *
73  *	    Adjusts the line width for vector drawing. Used to select normal (n=0)
74  *	    or defocused (n=1) mode.
75  *
76  *	f
77  *
78  *	  size f -
79  *
80  *	    Changes the size of the font that's used to print characters in alpha
81  *	    mode. size is the tektronix character width and is used to choose an
82  *	    appropriate point size in the current font.
83  *
84  *	done
85  *
86  *	  done
87  *
88  *	    Makes sure the last page is printed. Only needed when we're printing
89  *	    more than one page on each sheet of paper.
90  *
91  * The default line width is zero, which forces lines to be one pixel wide. That
92  * works well on 'write to black' engines but won't be right for 'write to white'
93  * engines. The line width can be changed using the -w option, or you can change
94  * the initialization of linewidth in the prologue.
95  *
96  * Many default values, like the magnification and orientation, are defined in
97  * the prologue, which is where they belong. If they're changed (by options), an
98  * appropriate definition is made after the prologue is added to the output file.
99  * The -P option passes arbitrary PostScript through to the output file. Among
100  * other things it can be used to set (or change) values that can't be accessed by
101  * other options.
102  *
103  */
104 
105 #include <stdio.h>
106 #include <signal.h>
107 #include <sys/types.h>
108 #include <fcntl.h>
109 
110 #include "comments.h"			/* PostScript file structuring comments */
111 #include "gen.h"			/* general purpose definitions */
112 #include "path.h"			/* for the prologue */
113 #include "ext.h"			/* external variable definitions */
114 #include "posttek.h"			/* control codes and other definitions */
115 
116 char	*optnames = "a:c:f:m:n:o:p:w:x:y:A:C:E:J:L:P:R:DI";
117 
118 char	*prologue = POSTTEK;		/* default PostScript prologue */
119 char	*formfile = FORMFILE;		/* stuff for multiple pages per sheet */
120 
121 int	formsperpage = 1;		/* page images on each piece of paper */
122 int	copies = 1;			/* and this many copies of each sheet */
123 
124 int	charheight[] = CHARHEIGHT;	/* height */
125 int	charwidth[] = CHARWIDTH;	/* and width arrays for tek characters */
126 int	tekfont = TEKFONT;		/* index into charheight[] and charwidth[] */
127 
128 char	intensity[] = INTENSITY;	/* special point intensity array */
129 char	*styles[] = STYLES;		/* description of line styles */
130 int	linestyle = 0;			/* index into styles[] */
131 int	linetype = 0;			/* 0 for normal, 1 for defocused */
132 
133 int	dispmode = ALPHA;		/* current tektronix state */
134 int	points = 0;			/* points making up the current vector */
135 int	characters = 0;			/* characters waiting to be printed */
136 int	pen = UP;			/* just for point plotting */
137 int	margin = 0;			/* left edge - ALPHA state */
138 
139 Point	cursor;				/* should be current cursor position */
140 
141 Fontmap	fontmap[] = FONTMAP;		/* for translating font names */
142 char	*fontname = "Courier";		/* use this PostScript font */
143 
144 int	page = 0;			/* page we're working on */
145 int	printed = 0;			/* printed this many pages */
146 
147 FILE	*fp_in;				/* read from this file */
148 FILE	*fp_out = stdout;		/* and write stuff here */
149 FILE	*fp_acct = NULL;		/* for accounting data */
150 
151 /*****************************************************************************/
152 
main(agc,agv)153 main(agc, agv)
154 
155     int		agc;
156     char	*agv[];
157 
158 {
159 
160 /*
161  *
162  * A simple program that can be used to translate tektronix 4014 files into
163  * PostScript. Most of the code was taken from the DMD tektronix 4014 emulator,
164  * although things have been cleaned up some.
165  *
166  */
167 
168     argv = agv;				/* so everyone can use them */
169     argc = agc;
170 
171     prog_name = argv[0];		/* just for error messages */
172 
173     init_signals();			/* sets up interrupt handling */
174     header();				/* PostScript header comments */
175     options();				/* handle the command line options */
176     setup();				/* for PostScript */
177     arguments();			/* followed by each input file */
178     done();				/* print the last page etc. */
179     account();				/* job accounting data */
180 
181     exit(x_stat);			/* nothing could be wrong */
182 
183 }   /* End of main */
184 
185 /*****************************************************************************/
186 
init_signals()187 init_signals()
188 
189 {
190 
191 /*
192  *
193  * Make sure we handle interrupts.
194  *
195  */
196 
197     if ( signal(SIGINT, interrupt) == SIG_IGN )  {
198 	signal(SIGINT, SIG_IGN);
199 	signal(SIGQUIT, SIG_IGN);
200 	signal(SIGHUP, SIG_IGN);
201     } else {
202 	signal(SIGHUP, interrupt);
203 	signal(SIGQUIT, interrupt);
204     }   /* End else */
205 
206     signal(SIGTERM, interrupt);
207 
208 }   /* End of init_signals */
209 
210 /*****************************************************************************/
211 
header()212 header()
213 
214 {
215 
216     int		ch;			/* return value from getopt() */
217     int		old_optind = optind;	/* for restoring optind - should be 1 */
218 
219 /*
220  *
221  * Scans the option list looking for things, like the prologue file, that we need
222  * right away but could be changed from the default. Doing things this way is an
223  * attempt to conform to Adobe's latest file structuring conventions. In particular
224  * they now say there should be nothing executed in the prologue, and they have
225  * added two new comments that delimit global initialization calls. Once we know
226  * where things really are we write out the job header, follow it by the prologue,
227  * and then add the ENDPROLOG and BEGINSETUP comments.
228  *
229  */
230 
231     while ( (ch = getopt(argc, argv, optnames)) != EOF )
232 	if ( ch == 'L' )
233 	    prologue = optarg;
234 	else if ( ch == '?' )
235 	    error(FATAL, "");
236 
237     optind = old_optind;		/* get ready for option scanning */
238 
239     fprintf(stdout, "%s", CONFORMING);
240     fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
241     fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
242     fprintf(stdout, "%s %s\n", PAGES, ATEND);
243     fprintf(stdout, "%s", ENDCOMMENTS);
244 
245     if ( cat(prologue) == FALSE )
246 	error(FATAL, "can't read %s", prologue);
247 
248     fprintf(stdout, "%s", ENDPROLOG);
249     fprintf(stdout, "%s", BEGINSETUP);
250     fprintf(stdout, "mark\n");
251 
252 }   /* End of header */
253 
254 /*****************************************************************************/
255 
options()256 options()
257 
258 {
259 
260     int		ch;			/* value returned by getopt() */
261 
262 /*
263  *
264  * Reads and processes the command line options. Added the -P option so arbitrary
265  * PostScript code can be passed through. Expect it could be useful for changing
266  * definitions in the prologue for which options have not been defined.
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 'm':			/* magnification */
287 		    fprintf(stdout, "/magnification %s def\n", optarg);
288 		    break;
289 
290 	    case 'n':			/* forms per page */
291 		    formsperpage = atoi(optarg);
292 		    fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
293 		    fprintf(stdout, "/formsperpage %s def\n", optarg);
294 		    break;
295 
296 	    case 'o':			/* output page list */
297 		    out_list(optarg);
298 		    break;
299 
300 	    case 'p':			/* landscape or portrait mode */
301 		    if ( *optarg == 'l' )
302 			fprintf(stdout, "/landscape true def\n");
303 		    else fprintf(stdout, "/landscape false def\n");
304 		    break;
305 
306 	    case 'w':			/* line width */
307 		    fprintf(stdout, "/linewidth %s def\n", optarg);
308 		    break;
309 
310 	    case 'x':			/* shift horizontally */
311 		    fprintf(stdout, "/xoffset %s def\n", optarg);
312 		    break;
313 
314 	    case 'y':			/* and vertically on the page */
315 		    fprintf(stdout, "/yoffset %s def\n", optarg);
316 		    break;
317 
318 	    case 'A':			/* force job accounting */
319 	    case 'J':
320 		    if ( (fp_acct = fopen(optarg, "a")) == NULL )
321 			error(FATAL, "can't open accounting file %s", optarg);
322 		    break;
323 
324 	    case 'C':			/* copy file straight to output */
325 		    if ( cat(optarg) == FALSE )
326 			error(FATAL, "can't read %s", optarg);
327 		    break;
328 
329 	    case 'E':			/* text font encoding */
330 		    fontencoding = optarg;
331 		    break;
332 
333 	    case 'L':			/* PostScript prologue file */
334 		    prologue = optarg;
335 		    break;
336 
337 	    case 'P':			/* PostScript pass through */
338 		    fprintf(stdout, "%s\n", optarg);
339 		    break;
340 
341 	    case 'R':			/* special global or page level request */
342 		    saverequest(optarg);
343 		    break;
344 
345 	    case 'D':			/* debug flag */
346 		    debug = ON;
347 		    break;
348 
349 	    case 'I':			/* ignore FATAL errors */
350 		    ignore = ON;
351 		    break;
352 
353 	    case '?':			/* don't know the option */
354 		    error(FATAL, "");
355 		    break;
356 
357 	    default:			/* don't know what to do for ch */
358 		    error(FATAL, "missing case for option %c", ch);
359 		    break;
360 	}   /* End switch */
361     }	/* End while */
362 
363     argc -= optind;
364     argv += optind;
365 
366 }   /* End of options */
367 
368 /*****************************************************************************/
369 
get_font(name)370 char *get_font(name)
371 
372     char	*name;			/* name the user asked for */
373 
374 {
375 
376     int		i;			/* for looking through fontmap[] */
377 
378 /*
379  *
380  * Called from options() to map a user's font name into a legal PostScript name.
381  * If the lookup fails *name is returned to the caller. That should let you choose
382  * any PostScript font.
383  *
384  */
385 
386     for ( i = 0; fontmap[i].name != NULL; i++ )
387 	if ( strcmp(name, fontmap[i].name) == 0 )
388 	    return(fontmap[i].val);
389 
390     return(name);
391 
392 }   /* End of get_font */
393 
394 /*****************************************************************************/
395 
setup()396 setup()
397 
398 {
399 
400 /*
401  *
402  * Handles things that must be done after the options are read but before the
403  * input files are processed.
404  *
405  */
406 
407     writerequest(0, stdout);		/* global requests eg. manual feed */
408     setencoding(fontencoding);
409     fprintf(stdout, "setup\n");
410 
411     if ( formsperpage > 1 )  {
412 	if ( cat(formfile) == FALSE )
413 	    error(FATAL, "can't read %s", formfile);
414 	fprintf(stdout, "%d setupforms\n", formsperpage);
415     }	/* End if */
416 
417     fprintf(stdout, "%s", ENDSETUP);
418 
419 }   /* End of setup */
420 
421 /*****************************************************************************/
422 
arguments()423 arguments()
424 
425 {
426 
427 /*
428  *
429  * Makes sure all the non-option command line arguments are processed. If we get
430  * here and there aren't any arguments left, or if '-' is one of the input files
431  * we'll process stdin.
432  *
433  */
434 
435     if ( argc < 1 )
436 	statemachine(fp_in = stdin);
437     else  {				/* at least one argument is left */
438 	while ( argc > 0 )  {
439 	    if ( strcmp(*argv, "-") == 0 )
440 		fp_in = stdin;
441 	    else if ( (fp_in = fopen(*argv, "r")) == NULL )
442 		error(FATAL, "can't open %s", *argv);
443 	    statemachine(fp_in);
444 	    if ( fp_in != stdin )
445 		fclose(fp_in);
446 	    argc--;
447 	    argv++;
448 	}   /* End while */
449     }   /* End else */
450 
451 }   /* End of arguments */
452 
453 /*****************************************************************************/
454 
done()455 done()
456 
457 {
458 
459 /*
460  *
461  * Finished with all the input files, so mark the end of the pages with a TRAILER
462  * comment, make sure the last page prints, and add things like the PAGES comment
463  * that can only be determined after all the input files have been read.
464  *
465  */
466 
467     fprintf(stdout, "%s", TRAILER);
468     fprintf(stdout, "done\n");
469     fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname);
470     fprintf(stdout, "%s %d\n", PAGES, printed);
471 
472 }   /* End of done */
473 
474 /*****************************************************************************/
475 
account()476 account()
477 
478 {
479 
480 /*
481  *
482  * Writes an accounting record to *fp_acct provided it's not NULL. Accounting
483  * is requested using the -A or -J options.
484  *
485  */
486 
487     if ( fp_acct != NULL )
488 	fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
489 
490 }   /* End of account */
491 
492 /*****************************************************************************/
493 
statemachine(fp)494 statemachine(fp)
495 
496     FILE	*fp;			/* used to set fp_in */
497 
498 {
499 
500 /*
501  *
502  * Controls the translation of the next input file. Tektronix states (dispmode)
503  * are typically changed in control() and esc().
504  *
505  */
506 
507     redirect(-1);			/* get ready for the first page */
508     formfeed();
509     dispmode = RESET;
510 
511     while ( 1 )
512 	switch ( dispmode )  {
513 	    case RESET:
514 		    reset();
515 		    break;
516 
517 	    case ALPHA:
518 		    alpha();
519 		    break;
520 
521 	    case GIN:
522 		    gin();
523 		    break;
524 
525 	    case GRAPH:
526 		    graph();
527 		    break;
528 
529 	    case POINT:
530 	    case SPECIALPOINT:
531 		    point();
532 		    break;
533 
534 	    case INCREMENTAL:
535 		    incremental();
536 		    break;
537 
538 	    case EXIT:
539 		    formfeed();
540 		    return;
541 	}   /* End switch */
542 
543 }   /* End of statemachine */
544 
545 /*****************************************************************************/
546 
reset()547 reset()
548 
549 {
550 
551 /*
552  *
553  * Called to reset things, typically only at the beginning of each input file.
554  *
555  */
556 
557     tekfont = -1;
558     home();
559     setfont(TEKFONT);
560     setmode(ALPHA);
561 
562 }   /* End of reset */
563 
564 /*****************************************************************************/
565 
alpha()566 alpha()
567 
568 {
569 
570     int		c;			/* next character */
571     int		x, y;			/* cursor will be here when we're done */
572 
573 /*
574  *
575  * Takes care of printing characters in the current font.
576  *
577  */
578 
579     if ( (c = nextchar()) == OUTMODED )
580 	return;
581 
582     if ( (c < 040) && ((c = control(c)) <= 0) )
583 	return;
584 
585     x = cursor.x;			/* where the cursor is right now */
586     y = cursor.y;
587 
588     switch ( c )  {
589 	case DEL:
590 		return;
591 
592 	case BS:
593 		if ((x -= charwidth[tekfont]) < margin)
594 		    x = TEKXMAX - charwidth[tekfont];
595 		break;
596 
597 	case NL:
598 		y -= charheight[tekfont];
599 		break;
600 
601 	case CR:
602 		x = margin;
603 		break;
604 
605 	case VT:
606 		if ((y += charheight[tekfont]) >= TEKYMAX)
607 		    y = 0;
608 		break;
609 
610 	case HT:
611 	case ' ':
612 	default:
613 		if ( characters++ == 0 )
614 		    fprintf(fp_out, "%d %d (", cursor.x, cursor.y);
615 		switch ( c )  {
616 		    case '(':
617 		    case ')':
618 		    case '\\':
619 			putc('\\', fp_out);
620 
621 		    default:
622 			putc(c, fp_out);
623 		}   /* End switch */
624 		x += charwidth[tekfont];
625 		move(x, y);
626 		break;
627     }	/* End switch */
628 
629     if (x >= TEKXMAX) {
630 	x = margin;
631 	y -= charheight[tekfont];
632     }	/* End if */
633 
634     if (y < 0) {
635 	y = TEKYMAX - charheight[tekfont];
636 	x -= margin;
637 	margin = (TEKXMAX/2) - margin;
638 	if ((x += margin) > TEKXMAX)
639 	    x -= margin;
640     }	/* End if */
641 
642     if ( y != cursor.y || x != cursor.x )
643 	text();
644 
645     move(x, y);
646 
647 }   /* End of alpha */
648 
649 /*****************************************************************************/
650 
graph()651 graph()
652 
653 {
654 
655     int			c;		/* next character */
656     int			b;		/* for figuring out loy */
657     int			x, y;		/* next point in the vector */
658     static int		hix, hiy;	/* upper */
659     static int		lox, loy;	/* and lower part of the address */
660     static int		extra;		/* for extended addressing */
661 
662 /*
663  *
664  * Handles things when we're in GRAPH, POINT, or SPECIALPOINT mode.
665  *
666  */
667 
668     if ((c = nextchar()) < 040) {
669 	control(c);
670 	return;
671     }	/* End if */
672 
673     if ((c & 0140) == 040) {		/* new hiy */
674 	hiy = c & 037;
675 	do
676 	    if (((c = nextchar()) < 040) && ((c = control(c)) == OUTMODED))
677 		return;
678 	while (c == 0);
679     }	/* End if */
680 
681     if ((c & 0140) == 0140) {		/* new loy */
682 	b = c & 037;
683 	do
684 	    if (((c = nextchar()) < 040) && ((c = control(c)) == OUTMODED))
685 		return;
686 	while (c == 0);
687 	if ((c & 0140) == 0140) {	/* no, it was extra */
688 	    extra = b;
689 	    loy = c & 037;
690 	    do
691 		if (((c = nextchar()) < 040) && ((c = control(c)) == OUTMODED))
692 		    return;
693 	    while (c == 0);
694 	} else loy = b;
695     }	/* End if */
696 
697     if ((c & 0140) == 040) {		/* new hix */
698 	hix = c & 037;
699 	do
700 	    if (((c = nextchar()) < 040) && ((c = control(c)) == OUTMODED))
701 		return;
702 	while (c == 0);
703     }	/* End if */
704 
705     lox = c & 037;			/* this should be lox */
706     if (extra & 020)
707 	margin = TEKXMAX/2;
708 
709     x = (hix<<7) | (lox<<2) | (extra & 03);
710     y = (hiy<<7) | (loy<<2) | ((extra & 014)>>2);
711 
712     if ( points > 100 )  {		/* don't put too much on the stack */
713 	draw();
714 	points = 1;
715     }	/* End if */
716 
717     if ( points++ )
718 	fprintf(fp_out, "%d %d\n", cursor.x - x, cursor.y - y);
719 
720     move(x, y);				/* adjust the cursor */
721 
722 }   /* End of graph */
723 
724 /*****************************************************************************/
725 
point()726 point()
727 
728 {
729 
730     int		c;			/* next input character */
731 
732 /*
733  *
734  * Special point mode permits gray scaling by varying the size of the stored
735  * point, which is controlled by an intensity character that preceeds each point
736  * address.
737  *
738  */
739 
740     if ( dispmode == SPECIALPOINT )  {
741 	if ( (c = nextchar()) < 040 || c > 0175 )
742 	    return(control(c));
743 
744 	fprintf(fp_out, "%d %d i\n", intensity[c - ' '], c & 0100);
745     }	/* End if */
746 
747     graph();
748     draw();
749 
750 }   /* End of point */
751 
752 /*****************************************************************************/
753 
incremental()754 incremental()
755 
756 {
757 
758     int		c;			/* for the next few characters */
759     int		x, y;			/* cursor position when we're done */
760 
761 /*
762  *
763  * Handles incremental plot mode. It's entered after the RS control code and is
764  * used to mark points relative to our current position. It's typically followed
765  * by one or two bytes that set the pen state and are used to increment the
766  * current position.
767  *
768  */
769 
770     if ( (c = nextchar()) == OUTMODED )
771 	return;
772 
773     if ( (c < 040) && ((c = control(c)) <= 0) )
774 	return;
775 
776     x = cursor.x;			/* where we are right now */
777     y = cursor.y;
778 
779     if ( c & 060 )
780 	pen = ( c & 040 ) ? UP : DOWN;
781 
782     if ( c & 04 ) y++;
783     if ( c & 010 ) y--;
784     if ( c & 01 ) x++;
785     if ( c & 02 ) x--;
786 
787     move(x, y);
788 
789     if ( pen == DOWN )  {
790 	points = 1;
791 	draw();
792     }	/* End if */
793 
794 }   /* End of incremental */
795 
796 /*****************************************************************************/
797 
gin()798 gin()
799 
800 {
801 
802 /*
803  *
804  * All we really have to do for GIN mode is make sure it's properly ended.
805  *
806  */
807 
808     control(nextchar());
809 
810 }   /* End of gin */
811 
812 /*****************************************************************************/
813 
control(c)814 control(c)
815 
816     int		c;			/* check this control character */
817 
818 {
819 
820 /*
821  *
822  * Checks character c and does special things, like mode changes, that depend
823  * not only on the character, but also on the current state. If the mode changed
824  * becuase of c, OUTMODED is returned to the caller. In all other cases the
825  * return value is c or 0, if c doesn't make sense in the current mode.
826  *
827  */
828 
829     switch ( c )  {
830 	case BEL:
831 		return(0);
832 
833 	case BS:
834 	case HT:
835 	case VT:
836 		return(dispmode == ALPHA ? c : 0);
837 
838 	case CR:
839 		if ( dispmode != ALPHA )  {
840 		    setmode(ALPHA);
841 		    ungetc(c, fp_in);
842 		    return(OUTMODED);
843 		} else return(c);
844 
845 	case FS:
846 		if ( (dispmode == ALPHA) || (dispmode == GRAPH) )  {
847 		    setmode(POINT);
848 		    return(OUTMODED);
849 		}   /* End if */
850 		return(0);
851 
852 	case GS:
853 		if ( (dispmode == ALPHA) || (dispmode == GRAPH) )  {
854 		    setmode(GRAPH);
855 		    return(OUTMODED);
856 		}   /* End if */
857 		return(0);
858 
859 	case NL:
860 		ungetc(CR, fp_in);
861 		return(dispmode == ALPHA ? c : 0);
862 
863 	case RS:
864 		if ( dispmode != GIN )  {
865 		    setmode(INCREMENTAL);
866 		    return(OUTMODED);
867 		}   /* End if */
868 		return(0);
869 
870 	case US:
871 		if ( dispmode == ALPHA )
872 		    return(0);
873 		setmode(ALPHA);
874 		return(OUTMODED);
875 
876 	case ESC:
877 		return(esc());
878 
879 	case OUTMODED:
880 		return(c);
881 
882 	default:
883 		return(c < 040 ? 0 : c);
884     }	/* End switch */
885 
886 }   /* End of control */
887 
888 /*****************************************************************************/
889 
esc()890 esc()
891 
892 {
893 
894     int		c;			/* next input character */
895     int		ignore;			/* skip it if nonzero */
896 
897 /*
898  *
899  * Handles tektronix escape code. Called from control() whenever an ESC character
900  * is found in the input file.
901  *
902  */
903 
904     do  {
905 	c = nextchar();
906 	ignore = 0;
907 	switch ( c )  {
908 	    case CAN:
909 		    return(0);
910 
911 	    case CR:
912 		    ignore = 1;
913 		    break;
914 
915 	    case ENQ:
916 		    setmode(ALPHA);
917 		    return(OUTMODED);
918 
919 	    case ETB:
920 		    return(0);
921 
922 	    case FF:
923 		    formfeed();
924 		    setmode(ALPHA);
925 		    return(OUTMODED);
926 
927 	    case FS:
928 		    if ( (dispmode == INCREMENTAL) || ( dispmode == GIN) )
929 			return(0);
930 		    setmode(SPECIALPOINT);
931 		    return(OUTMODED);
932 
933 	    case SI:
934 	    case SO:
935 		    return(0);
936 
937 	    case SUB:
938 		    setmode(GIN);
939 		    return(OUTMODED);
940 
941 	    case OUTMODED:
942 		    return(OUTMODED);
943 
944 	    case '8':
945 	    case '9':
946 	    case ':':
947 	    case ';':
948 		    setfont(c - '8');
949 		    return(0);
950 
951 	    default:
952 		    if ( c == '?' && dispmode == GRAPH )
953 			return(DEL);
954 		    if ( (c<'`') || (c>'w') )
955 			break;
956 		    c -= '`';
957 		    if ( (c & 010) != linetype )
958 			fprintf(fp_out, "%d w\n", (linetype = (c & 010))/010);
959 		    if ( ((c + 1) & 7) >= 6 )
960 			break;
961 		    if ( (c + 1) & 7 )
962 			if ( (c & 7) != linestyle )  {
963 			    linestyle = c & 7;
964 			    setmode(dispmode);
965 			    fprintf(fp_out, "%s l\n", styles[linestyle]);
966 			}   /* End if */
967 		    return(0);
968 	}   /* End switch */
969 
970     } while (ignore);
971 
972     return(0);
973 
974 }   /* End of esc */
975 
976 /*****************************************************************************/
977 
move(x,y)978 move(x, y)
979 
980     int		x, y;			/* move the cursor here */
981 
982 {
983 
984 /*
985  *
986  * Moves the cursor to the point (x, y).
987  *
988  */
989 
990     cursor.x = x;
991     cursor.y = y;
992 
993 }   /* End of move */
994 
995 /*****************************************************************************/
996 
setmode(mode)997 setmode(mode)
998 
999     int		mode;			/* this should be the new mode */
1000 
1001 {
1002 
1003 /*
1004  *
1005  * Makes sure the current mode is properly ended and then sets dispmode to mode.
1006  *
1007  */
1008 
1009     switch ( dispmode )  {
1010 	case ALPHA:
1011 		text();
1012 		break;
1013 
1014 	case GRAPH:
1015 		draw();
1016 		break;
1017 
1018 	case INCREMENTAL:
1019 		pen = UP;
1020 		break;
1021     }	/* End switch */
1022 
1023     dispmode = mode;
1024 
1025 }   /* End of setmode */
1026 
1027 /*****************************************************************************/
1028 
home()1029 home()
1030 
1031 {
1032 
1033 /*
1034  *
1035  * Makes sure the cursor is positioned at the upper left corner of the page.
1036  *
1037  */
1038 
1039     margin = 0;
1040     move(0, TEKYMAX);
1041 
1042 }   /* End of home */
1043 
1044 /*****************************************************************************/
1045 
setfont(newfont)1046 setfont(newfont)
1047 
1048     int		newfont;		/* use this font next */
1049 
1050 {
1051 
1052 /*
1053  *
1054  * Generates the call to the procedure that's responsible for changing the
1055  * tektronix font (really just the size).
1056  *
1057  */
1058 
1059     if ( newfont != tekfont )  {
1060 	setmode(dispmode);
1061 	fprintf(fp_out, "%d f\n", charwidth[newfont]);
1062     }	/* End if */
1063 
1064     tekfont = newfont;
1065 
1066 }   /* End of setfont */
1067 
1068 /*****************************************************************************/
1069 
text()1070 text()
1071 
1072 {
1073 
1074 /*
1075  *
1076  * Makes sure any text we've put on the stack is printed.
1077  *
1078  */
1079 
1080     if ( dispmode == ALPHA && characters > 0 )
1081 	fprintf(fp_out, ") t\n");
1082 
1083     characters = 0;
1084 
1085 }   /* End of text */
1086 
1087 /*****************************************************************************/
1088 
draw()1089 draw()
1090 
1091 {
1092 
1093 /*
1094  *
1095  * Called whenever we need to draw a vector or plot a point. Nothing will be
1096  * done if points is 0 or if it's 1 and we're in GRAPH mode.
1097  *
1098  */
1099 
1100     if ( points > 1 )			/* it's a vector */
1101 	fprintf(fp_out, "%d %d v\n", cursor.x, cursor.y);
1102     else if ( points == 1 && dispmode != GRAPH )
1103 	fprintf(fp_out, "%d %d p\n", cursor.x, cursor.y);
1104 
1105     points = 0;
1106 
1107 }   /* End of draw */
1108 
1109 /*****************************************************************************/
1110 
formfeed()1111 formfeed()
1112 
1113 {
1114 
1115 /*
1116  *
1117  * Usually called when we've finished the last page and want to get ready for the
1118  * next one. Also used at the beginning and end of each input file, so we have to
1119  * be careful about exactly what's done.
1120  *
1121  */
1122 
1123     setmode(dispmode);			/* end any outstanding text or graphics */
1124 
1125     if ( fp_out == stdout )		/* count the last page */
1126 	printed++;
1127 
1128     fprintf(fp_out, "cleartomark\n");
1129     fprintf(fp_out, "showpage\n");
1130     fprintf(fp_out, "saveobj restore\n");
1131     fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
1132 
1133     if ( ungetc(getc(fp_in), fp_in) == EOF )
1134 	redirect(-1);
1135     else redirect(++page);
1136 
1137     fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
1138     fprintf(fp_out, "/saveobj save def\n");
1139     fprintf(fp_out, "mark\n");
1140     writerequest(printed+1, fp_out);
1141     fprintf(fp_out, "%d pagesetup\n", printed+1);
1142     fprintf(fp_out, "%d f\n", charwidth[tekfont]);
1143     fprintf(fp_out, "%s l\n", styles[linestyle]);
1144 
1145     home();
1146 
1147 }   /* End of formfeed */
1148 
1149 /*****************************************************************************/
1150 
nextchar()1151 nextchar()
1152 
1153 {
1154 
1155     int		ch;			/* next input character */
1156 
1157 /*
1158  *
1159  * Reads the next character from the current input file and returns it to the
1160  * caller. When we're finished with the file dispmode is set to EXIT and OUTMODED
1161  * is returned to the caller.
1162  *
1163  */
1164 
1165     if ( (ch = getc(fp_in)) == EOF )  {
1166 	setmode(EXIT);
1167 	ch = OUTMODED;
1168     }	/* End if */
1169 
1170     return(ch);
1171 
1172 }   /* End of nextchar */
1173 
1174 /*****************************************************************************/
1175 
redirect(pg)1176 redirect(pg)
1177 
1178     int		pg;			/* next page we're printing */
1179 
1180 {
1181 
1182     static FILE	*fp_null = NULL;	/* if output is turned off */
1183 
1184 /*
1185  *
1186  * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
1187  * otherwise output goes to stdout.
1188  *
1189  */
1190 
1191     if ( pg >= 0 && in_olist(pg) == ON )
1192 	fp_out = stdout;
1193     else if ( (fp_out = fp_null) == NULL )
1194 	fp_out = fp_null = fopen("/dev/null", "w");
1195 
1196 }   /* End of redirect */
1197 
1198 /*****************************************************************************/
1199 
1200