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