summaryrefslogtreecommitdiff
path: root/sys/src/cmd/postscript/posttek
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/postscript/posttek
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/postscript/posttek')
-rwxr-xr-xsys/src/cmd/postscript/posttek/README4
-rwxr-xr-xsys/src/cmd/postscript/posttek/mkfile33
-rwxr-xr-xsys/src/cmd/postscript/posttek/posttek.c1199
-rwxr-xr-xsys/src/cmd/postscript/posttek/posttek.h183
-rwxr-xr-xsys/src/cmd/postscript/posttek/posttek.ps106
5 files changed, 1525 insertions, 0 deletions
diff --git a/sys/src/cmd/postscript/posttek/README b/sys/src/cmd/postscript/posttek/README
new file mode 100755
index 000000000..f46380669
--- /dev/null
+++ b/sys/src/cmd/postscript/posttek/README
@@ -0,0 +1,4 @@
+
+Tektronix 4014 to PostScript translator. Much of the code was
+borrowed from the 5620 Tektronix emulator.
+
diff --git a/sys/src/cmd/postscript/posttek/mkfile b/sys/src/cmd/postscript/posttek/mkfile
new file mode 100755
index 000000000..d5549b2d5
--- /dev/null
+++ b/sys/src/cmd/postscript/posttek/mkfile
@@ -0,0 +1,33 @@
+</$objtype/mkfile
+
+<../config
+
+TARG=posttek
+OFILES=posttek.$O\
+
+COMMONDIR=../common
+
+HFILES=posttek.h\
+ $COMMONDIR/comments.h\
+ $COMMONDIR/ext.h\
+ $COMMONDIR/gen.h\
+ $COMMONDIR/path.h\
+
+BIN=$POSTBIN
+LIB=$COMMONDIR/com.a$O
+
+</sys/src/cmd/mkone
+
+CC=pcc
+LD=pcc
+CFLAGS=-c -D$SYSTEM -D_POSIX_SOURCE -I$COMMONDIR -B
+
+install:V: $POSTLIB/posttek.ps
+
+$POSTLIB/posttek.ps : posttek.ps
+ cp $prereq $target
+
+$LIB:
+ cd $COMMONDIR
+ mk install
+ mk clean
diff --git a/sys/src/cmd/postscript/posttek/posttek.c b/sys/src/cmd/postscript/posttek/posttek.c
new file mode 100755
index 000000000..a2b1b1fae
--- /dev/null
+++ b/sys/src/cmd/postscript/posttek/posttek.c
@@ -0,0 +1,1199 @@
+/*
+ *
+ * posttek - PostScript translator for tektronix 4014 files
+ *
+ * A program that can be used to translate tektronix 4014 files into PostScript.
+ * Most of the code was borrowed from the tektronix 4014 emulator that was written
+ * for DMDs. Things have been cleaned up some, but there's still plently that
+ * could be done.
+ *
+ * The PostScript prologue is copied from *prologue before any of the input files
+ * are translated. The program expects that the following PostScript procedures
+ * are defined in that file:
+ *
+ * setup
+ *
+ * mark ... setup -
+ *
+ * Handles special initialization stuff that depends on how the program
+ * was called. Expects to find a mark followed by key/value pairs on the
+ * stack. The def operator is applied to each pair up to the mark, then
+ * the default state is set up.
+ *
+ * pagesetup
+ *
+ * page pagesetup -
+ *
+ * Does whatever is needed to set things up for the next page. Expects
+ * to find the current page number on the stack.
+ *
+ * v
+ *
+ * mark dx1 dy1 ... dxn dyn x y v mark
+ *
+ * Draws the vector described by the numbers on the stack. The top two
+ * numbers are the starting point. The rest are relative displacements
+ * from the preceeding point. Must make sure we don't put too much on
+ * the stack!
+ *
+ * t
+ *
+ * x y string t -
+ *
+ * Prints the string that's on the top of the stack starting at point
+ * (x, y).
+ *
+ * p
+ *
+ * x y p -
+ *
+ * Marks the point (x, y) with a circle whose radius varies with the
+ * current intensity setting.
+ *
+ * i
+ *
+ * percent focus i -
+ *
+ * Changes the size of the circle used to mark individual points to
+ * percent of maximum for focused mode (focus=1) or defocused mode
+ * (focus=0). The implementation leaves much to be desired!
+ *
+ * l
+ *
+ * mark array l mark
+ *
+ * Set the line drawing mode according to the description given in array.
+ * The arrays that describe the different line styles are declared in
+ * STYLES (file posttek.h). The array really belongs in the prologue!
+ *
+ * w
+ *
+ * n w -
+ *
+ * Adjusts the line width for vector drawing. Used to select normal (n=0)
+ * or defocused (n=1) mode.
+ *
+ * f
+ *
+ * size f -
+ *
+ * Changes the size of the font that's used to print characters in alpha
+ * mode. size is the tektronix character width and is used to choose an
+ * appropriate point size in the current font.
+ *
+ * done
+ *
+ * done
+ *
+ * Makes sure the last page is printed. Only needed when we're printing
+ * more than one page on each sheet of paper.
+ *
+ * The default line width is zero, which forces lines to be one pixel wide. That
+ * works well on 'write to black' engines but won't be right for 'write to white'
+ * engines. The line width can be changed using the -w option, or you can change
+ * the initialization of linewidth in the prologue.
+ *
+ * Many default values, like the magnification and orientation, are defined in
+ * the prologue, which is where they belong. If they're changed (by options), an
+ * appropriate definition is made after the prologue is added to the output file.
+ * The -P option passes arbitrary PostScript through to the output file. Among
+ * other things it can be used to set (or change) values that can't be accessed by
+ * other options.
+ *
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "comments.h" /* PostScript file structuring comments */
+#include "gen.h" /* general purpose definitions */
+#include "path.h" /* for the prologue */
+#include "ext.h" /* external variable definitions */
+#include "posttek.h" /* control codes and other definitions */
+
+char *optnames = "a:c:f:m:n:o:p:w:x:y:A:C:E:J:L:P:R:DI";
+
+char *prologue = POSTTEK; /* default PostScript prologue */
+char *formfile = FORMFILE; /* stuff for multiple pages per sheet */
+
+int formsperpage = 1; /* page images on each piece of paper */
+int copies = 1; /* and this many copies of each sheet */
+
+int charheight[] = CHARHEIGHT; /* height */
+int charwidth[] = CHARWIDTH; /* and width arrays for tek characters */
+int tekfont = TEKFONT; /* index into charheight[] and charwidth[] */
+
+char intensity[] = INTENSITY; /* special point intensity array */
+char *styles[] = STYLES; /* description of line styles */
+int linestyle = 0; /* index into styles[] */
+int linetype = 0; /* 0 for normal, 1 for defocused */
+
+int dispmode = ALPHA; /* current tektronix state */
+int points = 0; /* points making up the current vector */
+int characters = 0; /* characters waiting to be printed */
+int pen = UP; /* just for point plotting */
+int margin = 0; /* left edge - ALPHA state */
+
+Point cursor; /* should be current cursor position */
+
+Fontmap fontmap[] = FONTMAP; /* for translating font names */
+char *fontname = "Courier"; /* use this PostScript font */
+
+int page = 0; /* page we're working on */
+int printed = 0; /* printed this many pages */
+
+FILE *fp_in; /* read from this file */
+FILE *fp_out = stdout; /* and write stuff here */
+FILE *fp_acct = NULL; /* for accounting data */
+
+/*****************************************************************************/
+
+main(agc, agv)
+
+ int agc;
+ char *agv[];
+
+{
+
+/*
+ *
+ * A simple program that can be used to translate tektronix 4014 files into
+ * PostScript. Most of the code was taken from the DMD tektronix 4014 emulator,
+ * although things have been cleaned up some.
+ *
+ */
+
+ argv = agv; /* so everyone can use them */
+ argc = agc;
+
+ prog_name = argv[0]; /* just for error messages */
+
+ init_signals(); /* sets up interrupt handling */
+ header(); /* PostScript header comments */
+ options(); /* handle the command line options */
+ setup(); /* for PostScript */
+ arguments(); /* followed by each input file */
+ done(); /* print the last page etc. */
+ account(); /* job accounting data */
+
+ exit(x_stat); /* nothing could be wrong */
+
+} /* End of main */
+
+/*****************************************************************************/
+
+init_signals()
+
+{
+
+/*
+ *
+ * Make sure we handle interrupts.
+ *
+ */
+
+ if ( signal(SIGINT, interrupt) == SIG_IGN ) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ } else {
+ signal(SIGHUP, interrupt);
+ signal(SIGQUIT, interrupt);
+ } /* End else */
+
+ signal(SIGTERM, interrupt);
+
+} /* End of init_signals */
+
+/*****************************************************************************/
+
+header()
+
+{
+
+ int ch; /* return value from getopt() */
+ int old_optind = optind; /* for restoring optind - should be 1 */
+
+/*
+ *
+ * Scans the option list looking for things, like the prologue file, that we need
+ * right away but could be changed from the default. Doing things this way is an
+ * attempt to conform to Adobe's latest file structuring conventions. In particular
+ * they now say there should be nothing executed in the prologue, and they have
+ * added two new comments that delimit global initialization calls. Once we know
+ * where things really are we write out the job header, follow it by the prologue,
+ * and then add the ENDPROLOG and BEGINSETUP comments.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF )
+ if ( ch == 'L' )
+ prologue = optarg;
+ else if ( ch == '?' )
+ error(FATAL, "");
+
+ optind = old_optind; /* get ready for option scanning */
+
+ fprintf(stdout, "%s", CONFORMING);
+ fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
+ fprintf(stdout, "%s %s\n", PAGES, ATEND);
+ fprintf(stdout, "%s", ENDCOMMENTS);
+
+ if ( cat(prologue) == FALSE )
+ error(FATAL, "can't read %s", prologue);
+
+ fprintf(stdout, "%s", ENDPROLOG);
+ fprintf(stdout, "%s", BEGINSETUP);
+ fprintf(stdout, "mark\n");
+
+} /* End of header */
+
+/*****************************************************************************/
+
+options()
+
+{
+
+ int ch; /* value returned by getopt() */
+
+/*
+ *
+ * Reads and processes the command line options. Added the -P option so arbitrary
+ * PostScript code can be passed through. Expect it could be useful for changing
+ * definitions in the prologue for which options have not been defined.
+ *
+ */
+
+ while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
+ switch ( ch ) {
+ case 'a': /* aspect ratio */
+ fprintf(stdout, "/aspectratio %s def\n", optarg);
+ break;
+
+ case 'c': /* copies */
+ copies = atoi(optarg);
+ fprintf(stdout, "/#copies %s store\n", optarg);
+ break;
+
+ case 'f': /* use this PostScript font */
+ fontname = get_font(optarg);
+ fprintf(stdout, "/font /%s def\n", fontname);
+ break;
+
+ case 'm': /* magnification */
+ fprintf(stdout, "/magnification %s def\n", optarg);
+ break;
+
+ case 'n': /* forms per page */
+ formsperpage = atoi(optarg);
+ fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
+ fprintf(stdout, "/formsperpage %s def\n", optarg);
+ break;
+
+ case 'o': /* output page list */
+ out_list(optarg);
+ break;
+
+ case 'p': /* landscape or portrait mode */
+ if ( *optarg == 'l' )
+ fprintf(stdout, "/landscape true def\n");
+ else fprintf(stdout, "/landscape false def\n");
+ break;
+
+ case 'w': /* line width */
+ fprintf(stdout, "/linewidth %s def\n", optarg);
+ break;
+
+ case 'x': /* shift horizontally */
+ fprintf(stdout, "/xoffset %s def\n", optarg);
+ break;
+
+ case 'y': /* and vertically on the page */
+ fprintf(stdout, "/yoffset %s def\n", optarg);
+ break;
+
+ case 'A': /* force job accounting */
+ case 'J':
+ if ( (fp_acct = fopen(optarg, "a")) == NULL )
+ error(FATAL, "can't open accounting file %s", optarg);
+ break;
+
+ case 'C': /* copy file straight to output */
+ if ( cat(optarg) == FALSE )
+ error(FATAL, "can't read %s", optarg);
+ break;
+
+ case 'E': /* text font encoding */
+ fontencoding = optarg;
+ break;
+
+ case 'L': /* PostScript prologue file */
+ prologue = optarg;
+ break;
+
+ case 'P': /* PostScript pass through */
+ fprintf(stdout, "%s\n", optarg);
+ break;
+
+ case 'R': /* special global or page level request */
+ saverequest(optarg);
+ break;
+
+ case 'D': /* debug flag */
+ debug = ON;
+ break;
+
+ case 'I': /* ignore FATAL errors */
+ ignore = ON;
+ break;
+
+ case '?': /* don't know the option */
+ error(FATAL, "");
+ break;
+
+ default: /* don't know what to do for ch */
+ error(FATAL, "missing case for option %c", ch);
+ break;
+ } /* End switch */
+ } /* End while */
+
+ argc -= optind;
+ argv += optind;
+
+} /* End of options */
+
+/*****************************************************************************/
+
+char *get_font(name)
+
+ char *name; /* name the user asked for */
+
+{
+
+ int i; /* for looking through fontmap[] */
+
+/*
+ *
+ * Called from options() to map a user's font name into a legal PostScript name.
+ * If the lookup fails *name is returned to the caller. That should let you choose
+ * any PostScript font.
+ *
+ */
+
+ for ( i = 0; fontmap[i].name != NULL; i++ )
+ if ( strcmp(name, fontmap[i].name) == 0 )
+ return(fontmap[i].val);
+
+ return(name);
+
+} /* End of get_font */
+
+/*****************************************************************************/
+
+setup()
+
+{
+
+/*
+ *
+ * Handles things that must be done after the options are read but before the
+ * input files are processed.
+ *
+ */
+
+ writerequest(0, stdout); /* global requests eg. manual feed */
+ setencoding(fontencoding);
+ fprintf(stdout, "setup\n");
+
+ if ( formsperpage > 1 ) {
+ if ( cat(formfile) == FALSE )
+ error(FATAL, "can't read %s", formfile);
+ fprintf(stdout, "%d setupforms\n", formsperpage);
+ } /* End if */
+
+ fprintf(stdout, "%s", ENDSETUP);
+
+} /* End of setup */
+
+/*****************************************************************************/
+
+arguments()
+
+{
+
+/*
+ *
+ * Makes sure all the non-option command line arguments are processed. If we get
+ * here and there aren't any arguments left, or if '-' is one of the input files
+ * we'll process stdin.
+ *
+ */
+
+ if ( argc < 1 )
+ statemachine(fp_in = stdin);
+ else { /* at least one argument is left */
+ while ( argc > 0 ) {
+ if ( strcmp(*argv, "-") == 0 )
+ fp_in = stdin;
+ else if ( (fp_in = fopen(*argv, "r")) == NULL )
+ error(FATAL, "can't open %s", *argv);
+ statemachine(fp_in);
+ if ( fp_in != stdin )
+ fclose(fp_in);
+ argc--;
+ argv++;
+ } /* End while */
+ } /* End else */
+
+} /* End of arguments */
+
+/*****************************************************************************/
+
+done()
+
+{
+
+/*
+ *
+ * Finished with all the input files, so mark the end of the pages with a TRAILER
+ * comment, make sure the last page prints, and add things like the PAGES comment
+ * that can only be determined after all the input files have been read.
+ *
+ */
+
+ fprintf(stdout, "%s", TRAILER);
+ fprintf(stdout, "done\n");
+ fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname);
+ fprintf(stdout, "%s %d\n", PAGES, printed);
+
+} /* End of done */
+
+/*****************************************************************************/
+
+account()
+
+{
+
+/*
+ *
+ * Writes an accounting record to *fp_acct provided it's not NULL. Accounting
+ * is requested using the -A or -J options.
+ *
+ */
+
+ if ( fp_acct != NULL )
+ fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
+
+} /* End of account */
+
+/*****************************************************************************/
+
+statemachine(fp)
+
+ FILE *fp; /* used to set fp_in */
+
+{
+
+/*
+ *
+ * Controls the translation of the next input file. Tektronix states (dispmode)
+ * are typically changed in control() and esc().
+ *
+ */
+
+ redirect(-1); /* get ready for the first page */
+ formfeed();
+ dispmode = RESET;
+
+ while ( 1 )
+ switch ( dispmode ) {
+ case RESET:
+ reset();
+ break;
+
+ case ALPHA:
+ alpha();
+ break;
+
+ case GIN:
+ gin();
+ break;
+
+ case GRAPH:
+ graph();
+ break;
+
+ case POINT:
+ case SPECIALPOINT:
+ point();
+ break;
+
+ case INCREMENTAL:
+ incremental();
+ break;
+
+ case EXIT:
+ formfeed();
+ return;
+ } /* End switch */
+
+} /* End of statemachine */
+
+/*****************************************************************************/
+
+reset()
+
+{
+
+/*
+ *
+ * Called to reset things, typically only at the beginning of each input file.
+ *
+ */
+
+ tekfont = -1;
+ home();
+ setfont(TEKFONT);
+ setmode(ALPHA);
+
+} /* End of reset */
+
+/*****************************************************************************/
+
+alpha()
+
+{
+
+ int c; /* next character */
+ int x, y; /* cursor will be here when we're done */
+
+/*
+ *
+ * Takes care of printing characters in the current font.
+ *
+ */
+
+ if ( (c = nextchar()) == OUTMODED )
+ return;
+
+ if ( (c < 040) && ((c = control(c)) <= 0) )
+ return;
+
+ x = cursor.x; /* where the cursor is right now */
+ y = cursor.y;
+
+ switch ( c ) {
+ case DEL:
+ return;
+
+ case BS:
+ if ((x -= charwidth[tekfont]) < margin)
+ x = TEKXMAX - charwidth[tekfont];
+ break;
+
+ case NL:
+ y -= charheight[tekfont];
+ break;
+
+ case CR:
+ x = margin;
+ break;
+
+ case VT:
+ if ((y += charheight[tekfont]) >= TEKYMAX)
+ y = 0;
+ break;
+
+ case HT:
+ case ' ':
+ default:
+ if ( characters++ == 0 )
+ fprintf(fp_out, "%d %d (", cursor.x, cursor.y);
+ switch ( c ) {
+ case '(':
+ case ')':
+ case '\\':
+ putc('\\', fp_out);
+
+ default:
+ putc(c, fp_out);
+ } /* End switch */
+ x += charwidth[tekfont];
+ move(x, y);
+ break;
+ } /* End switch */
+
+ if (x >= TEKXMAX) {
+ x = margin;
+ y -= charheight[tekfont];
+ } /* End if */
+
+ if (y < 0) {
+ y = TEKYMAX - charheight[tekfont];
+ x -= margin;
+ margin = (TEKXMAX/2) - margin;
+ if ((x += margin) > TEKXMAX)
+ x -= margin;
+ } /* End if */
+
+ if ( y != cursor.y || x != cursor.x )
+ text();
+
+ move(x, y);
+
+} /* End of alpha */
+
+/*****************************************************************************/
+
+graph()
+
+{
+
+ int c; /* next character */
+ int b; /* for figuring out loy */
+ int x, y; /* next point in the vector */
+ static int hix, hiy; /* upper */
+ static int lox, loy; /* and lower part of the address */
+ static int extra; /* for extended addressing */
+
+/*
+ *
+ * Handles things when we're in GRAPH, POINT, or SPECIALPOINT mode.
+ *
+ */
+
+ if ((c = nextchar()) < 040) {
+ control(c);
+ return;
+ } /* End if */
+
+ if ((c & 0140) == 040) { /* new hiy */
+ hiy = c & 037;
+ do
+ if (((c = nextchar()) < 040) && ((c = control(c)) == OUTMODED))
+ return;
+ while (c == 0);
+ } /* End if */
+
+ if ((c & 0140) == 0140) { /* new loy */
+ b = c & 037;
+ do
+ if (((c = nextchar()) < 040) && ((c = control(c)) == OUTMODED))
+ return;
+ while (c == 0);
+ if ((c & 0140) == 0140) { /* no, it was extra */
+ extra = b;
+ loy = c & 037;
+ do
+ if (((c = nextchar()) < 040) && ((c = control(c)) == OUTMODED))
+ return;
+ while (c == 0);
+ } else loy = b;
+ } /* End if */
+
+ if ((c & 0140) == 040) { /* new hix */
+ hix = c & 037;
+ do
+ if (((c = nextchar()) < 040) && ((c = control(c)) == OUTMODED))
+ return;
+ while (c == 0);
+ } /* End if */
+
+ lox = c & 037; /* this should be lox */
+ if (extra & 020)
+ margin = TEKXMAX/2;
+
+ x = (hix<<7) | (lox<<2) | (extra & 03);
+ y = (hiy<<7) | (loy<<2) | ((extra & 014)>>2);
+
+ if ( points > 100 ) { /* don't put too much on the stack */
+ draw();
+ points = 1;
+ } /* End if */
+
+ if ( points++ )
+ fprintf(fp_out, "%d %d\n", cursor.x - x, cursor.y - y);
+
+ move(x, y); /* adjust the cursor */
+
+} /* End of graph */
+
+/*****************************************************************************/
+
+point()
+
+{
+
+ int c; /* next input character */
+
+/*
+ *
+ * Special point mode permits gray scaling by varying the size of the stored
+ * point, which is controlled by an intensity character that preceeds each point
+ * address.
+ *
+ */
+
+ if ( dispmode == SPECIALPOINT ) {
+ if ( (c = nextchar()) < 040 || c > 0175 )
+ return(control(c));
+
+ fprintf(fp_out, "%d %d i\n", intensity[c - ' '], c & 0100);
+ } /* End if */
+
+ graph();
+ draw();
+
+} /* End of point */
+
+/*****************************************************************************/
+
+incremental()
+
+{
+
+ int c; /* for the next few characters */
+ int x, y; /* cursor position when we're done */
+
+/*
+ *
+ * Handles incremental plot mode. It's entered after the RS control code and is
+ * used to mark points relative to our current position. It's typically followed
+ * by one or two bytes that set the pen state and are used to increment the
+ * current position.
+ *
+ */
+
+ if ( (c = nextchar()) == OUTMODED )
+ return;
+
+ if ( (c < 040) && ((c = control(c)) <= 0) )
+ return;
+
+ x = cursor.x; /* where we are right now */
+ y = cursor.y;
+
+ if ( c & 060 )
+ pen = ( c & 040 ) ? UP : DOWN;
+
+ if ( c & 04 ) y++;
+ if ( c & 010 ) y--;
+ if ( c & 01 ) x++;
+ if ( c & 02 ) x--;
+
+ move(x, y);
+
+ if ( pen == DOWN ) {
+ points = 1;
+ draw();
+ } /* End if */
+
+} /* End of incremental */
+
+/*****************************************************************************/
+
+gin()
+
+{
+
+/*
+ *
+ * All we really have to do for GIN mode is make sure it's properly ended.
+ *
+ */
+
+ control(nextchar());
+
+} /* End of gin */
+
+/*****************************************************************************/
+
+control(c)
+
+ int c; /* check this control character */
+
+{
+
+/*
+ *
+ * Checks character c and does special things, like mode changes, that depend
+ * not only on the character, but also on the current state. If the mode changed
+ * becuase of c, OUTMODED is returned to the caller. In all other cases the
+ * return value is c or 0, if c doesn't make sense in the current mode.
+ *
+ */
+
+ switch ( c ) {
+ case BEL:
+ return(0);
+
+ case BS:
+ case HT:
+ case VT:
+ return(dispmode == ALPHA ? c : 0);
+
+ case CR:
+ if ( dispmode != ALPHA ) {
+ setmode(ALPHA);
+ ungetc(c, fp_in);
+ return(OUTMODED);
+ } else return(c);
+
+ case FS:
+ if ( (dispmode == ALPHA) || (dispmode == GRAPH) ) {
+ setmode(POINT);
+ return(OUTMODED);
+ } /* End if */
+ return(0);
+
+ case GS:
+ if ( (dispmode == ALPHA) || (dispmode == GRAPH) ) {
+ setmode(GRAPH);
+ return(OUTMODED);
+ } /* End if */
+ return(0);
+
+ case NL:
+ ungetc(CR, fp_in);
+ return(dispmode == ALPHA ? c : 0);
+
+ case RS:
+ if ( dispmode != GIN ) {
+ setmode(INCREMENTAL);
+ return(OUTMODED);
+ } /* End if */
+ return(0);
+
+ case US:
+ if ( dispmode == ALPHA )
+ return(0);
+ setmode(ALPHA);
+ return(OUTMODED);
+
+ case ESC:
+ return(esc());
+
+ case OUTMODED:
+ return(c);
+
+ default:
+ return(c < 040 ? 0 : c);
+ } /* End switch */
+
+} /* End of control */
+
+/*****************************************************************************/
+
+esc()
+
+{
+
+ int c; /* next input character */
+ int ignore; /* skip it if nonzero */
+
+/*
+ *
+ * Handles tektronix escape code. Called from control() whenever an ESC character
+ * is found in the input file.
+ *
+ */
+
+ do {
+ c = nextchar();
+ ignore = 0;
+ switch ( c ) {
+ case CAN:
+ return(0);
+
+ case CR:
+ ignore = 1;
+ break;
+
+ case ENQ:
+ setmode(ALPHA);
+ return(OUTMODED);
+
+ case ETB:
+ return(0);
+
+ case FF:
+ formfeed();
+ setmode(ALPHA);
+ return(OUTMODED);
+
+ case FS:
+ if ( (dispmode == INCREMENTAL) || ( dispmode == GIN) )
+ return(0);
+ setmode(SPECIALPOINT);
+ return(OUTMODED);
+
+ case SI:
+ case SO:
+ return(0);
+
+ case SUB:
+ setmode(GIN);
+ return(OUTMODED);
+
+ case OUTMODED:
+ return(OUTMODED);
+
+ case '8':
+ case '9':
+ case ':':
+ case ';':
+ setfont(c - '8');
+ return(0);
+
+ default:
+ if ( c == '?' && dispmode == GRAPH )
+ return(DEL);
+ if ( (c<'`') || (c>'w') )
+ break;
+ c -= '`';
+ if ( (c & 010) != linetype )
+ fprintf(fp_out, "%d w\n", (linetype = (c & 010))/010);
+ if ( ((c + 1) & 7) >= 6 )
+ break;
+ if ( (c + 1) & 7 )
+ if ( (c & 7) != linestyle ) {
+ linestyle = c & 7;
+ setmode(dispmode);
+ fprintf(fp_out, "%s l\n", styles[linestyle]);
+ } /* End if */
+ return(0);
+ } /* End switch */
+
+ } while (ignore);
+
+ return(0);
+
+} /* End of esc */
+
+/*****************************************************************************/
+
+move(x, y)
+
+ int x, y; /* move the cursor here */
+
+{
+
+/*
+ *
+ * Moves the cursor to the point (x, y).
+ *
+ */
+
+ cursor.x = x;
+ cursor.y = y;
+
+} /* End of move */
+
+/*****************************************************************************/
+
+setmode(mode)
+
+ int mode; /* this should be the new mode */
+
+{
+
+/*
+ *
+ * Makes sure the current mode is properly ended and then sets dispmode to mode.
+ *
+ */
+
+ switch ( dispmode ) {
+ case ALPHA:
+ text();
+ break;
+
+ case GRAPH:
+ draw();
+ break;
+
+ case INCREMENTAL:
+ pen = UP;
+ break;
+ } /* End switch */
+
+ dispmode = mode;
+
+} /* End of setmode */
+
+/*****************************************************************************/
+
+home()
+
+{
+
+/*
+ *
+ * Makes sure the cursor is positioned at the upper left corner of the page.
+ *
+ */
+
+ margin = 0;
+ move(0, TEKYMAX);
+
+} /* End of home */
+
+/*****************************************************************************/
+
+setfont(newfont)
+
+ int newfont; /* use this font next */
+
+{
+
+/*
+ *
+ * Generates the call to the procedure that's responsible for changing the
+ * tektronix font (really just the size).
+ *
+ */
+
+ if ( newfont != tekfont ) {
+ setmode(dispmode);
+ fprintf(fp_out, "%d f\n", charwidth[newfont]);
+ } /* End if */
+
+ tekfont = newfont;
+
+} /* End of setfont */
+
+/*****************************************************************************/
+
+text()
+
+{
+
+/*
+ *
+ * Makes sure any text we've put on the stack is printed.
+ *
+ */
+
+ if ( dispmode == ALPHA && characters > 0 )
+ fprintf(fp_out, ") t\n");
+
+ characters = 0;
+
+} /* End of text */
+
+/*****************************************************************************/
+
+draw()
+
+{
+
+/*
+ *
+ * Called whenever we need to draw a vector or plot a point. Nothing will be
+ * done if points is 0 or if it's 1 and we're in GRAPH mode.
+ *
+ */
+
+ if ( points > 1 ) /* it's a vector */
+ fprintf(fp_out, "%d %d v\n", cursor.x, cursor.y);
+ else if ( points == 1 && dispmode != GRAPH )
+ fprintf(fp_out, "%d %d p\n", cursor.x, cursor.y);
+
+ points = 0;
+
+} /* End of draw */
+
+/*****************************************************************************/
+
+formfeed()
+
+{
+
+/*
+ *
+ * Usually called when we've finished the last page and want to get ready for the
+ * next one. Also used at the beginning and end of each input file, so we have to
+ * be careful about exactly what's done.
+ *
+ */
+
+ setmode(dispmode); /* end any outstanding text or graphics */
+
+ if ( fp_out == stdout ) /* count the last page */
+ printed++;
+
+ fprintf(fp_out, "cleartomark\n");
+ fprintf(fp_out, "showpage\n");
+ fprintf(fp_out, "saveobj restore\n");
+ fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
+
+ if ( ungetc(getc(fp_in), fp_in) == EOF )
+ redirect(-1);
+ else redirect(++page);
+
+ fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
+ fprintf(fp_out, "/saveobj save def\n");
+ fprintf(fp_out, "mark\n");
+ writerequest(printed+1, fp_out);
+ fprintf(fp_out, "%d pagesetup\n", printed+1);
+ fprintf(fp_out, "%d f\n", charwidth[tekfont]);
+ fprintf(fp_out, "%s l\n", styles[linestyle]);
+
+ home();
+
+} /* End of formfeed */
+
+/*****************************************************************************/
+
+nextchar()
+
+{
+
+ int ch; /* next input character */
+
+/*
+ *
+ * Reads the next character from the current input file and returns it to the
+ * caller. When we're finished with the file dispmode is set to EXIT and OUTMODED
+ * is returned to the caller.
+ *
+ */
+
+ if ( (ch = getc(fp_in)) == EOF ) {
+ setmode(EXIT);
+ ch = OUTMODED;
+ } /* End if */
+
+ return(ch);
+
+} /* End of nextchar */
+
+/*****************************************************************************/
+
+redirect(pg)
+
+ int pg; /* next page we're printing */
+
+{
+
+ static FILE *fp_null = NULL; /* if output is turned off */
+
+/*
+ *
+ * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
+ * otherwise output goes to stdout.
+ *
+ */
+
+ if ( pg >= 0 && in_olist(pg) == ON )
+ fp_out = stdout;
+ else if ( (fp_out = fp_null) == NULL )
+ fp_out = fp_null = fopen("/dev/null", "w");
+
+} /* End of redirect */
+
+/*****************************************************************************/
+
diff --git a/sys/src/cmd/postscript/posttek/posttek.h b/sys/src/cmd/postscript/posttek/posttek.h
new file mode 100755
index 000000000..99d10133c
--- /dev/null
+++ b/sys/src/cmd/postscript/posttek/posttek.h
@@ -0,0 +1,183 @@
+/*
+ *
+ * Tektronix 4014 control codes.
+ *
+ */
+
+#define NUL '\000'
+#define SOH '\001'
+#define STX '\002'
+#define ETX '\003'
+#define EOT '\004'
+#define ENQ '\005'
+#define ACK '\006'
+#define BEL '\007'
+#define BS '\010'
+#define HT '\011'
+#define NL '\012'
+#define VT '\013'
+#define FF '\014'
+#define CR '\015'
+#define SO '\016'
+#define SI '\017'
+#define DLE '\020'
+#define DC1 '\021'
+#define DC2 '\022'
+#define DC3 '\023'
+#define DC4 '\024'
+#define NAK '\025'
+#define SYN '\026'
+#define ETB '\027'
+#define CAN '\030'
+#define EM '\031'
+#define SUB '\032'
+#define ESC '\033'
+#define FS '\034'
+#define GS '\035'
+#define RS '\036'
+#define US '\037'
+#define DEL '\177'
+
+/*
+ *
+ * A few definitions used to classify the different tektronix states. OUTMODED
+ * is returned by control() and esc(), and typically means the state has changed.
+ *
+ */
+
+#define OUTMODED -1
+#define ALPHA 0
+#define GIN 1
+#define GRAPH 2
+#define POINT 3
+#define SPECIALPOINT 4
+#define INCREMENTAL 5
+#define RESET 6
+#define EXIT 7
+
+/*
+ *
+ * The pen state, either UP or DOWN, controls whether vectors are drawn.
+ *
+ */
+
+#define UP 0
+#define DOWN 1
+
+/*
+ *
+ * Coordinates of the upper right corner of the screen - almost the real screen
+ * dimensions.
+ *
+ */
+
+#define TEKXMAX 4096
+#define TEKYMAX 3120
+
+/*
+ *
+ * The size of the spot in SPECIALPOINT mode is controlled by a non-linear
+ * function that has a domain that consists of the integers from 040 to 0175.
+ * The next definition is used to initialize the special point mode intensity
+ * array that implements the function. Data came from table F-6 in the tektronix
+ * 4014 manual.
+ *
+ */
+
+#define INTENSITY \
+ \
+ { \
+ 14, 16, 17, 19, 20, 22, 23, 25, \
+ 28, 31, 34, 38, 41, 44, 47, 50, \
+ 56, 62, 69, 75, 81, 88, 94,100, \
+ 56, 62, 69, 75, 81, 88, 94,100, \
+ 0, 1, 1, 1, 1, 1, 1, 2, \
+ 2, 2, 2, 2, 3, 3, 3, 3, \
+ 4, 4, 4, 5, 5, 5, 6, 6, \
+ 7, 8, 9, 10, 11, 12, 12, 13, \
+ 14, 16, 17, 19, 20, 22, 23, 25, \
+ 28, 31, 34, 38, 41, 44, 47, 50, \
+ 56, 62, 69, 75, 81, 88, 94,100, \
+ 56, 62, 69, 75, 81, 88, 94,100, \
+ }
+
+/*
+ *
+ * The next two definitions give the height and width of characters in the four
+ * different sizes available on tektronix terminals. TEKFONT is the default index
+ * into CHARHEIGHT and CHARWIDTH.
+ *
+ */
+
+#define CHARHEIGHT {88, 82, 53, 48}
+#define CHARWIDTH {56, 51, 34, 31}
+#define TEKFONT 2
+
+/*
+ *
+ * The entries defined in STYLES are passed on to the PostScript operator setdash.
+ * They're used to implement the different tektronix line styles. Belongs in the
+ * prologue!
+ *
+ */
+
+#define STYLES \
+ \
+ { \
+ "[]", \
+ "[.5 2]", \
+ "[.5 2 4 2]", \
+ "[4 4]", \
+ "[8 4]", \
+ "[]" \
+ }
+
+/*
+ *
+ * Variables of type Point are used to keep track of the cursor position.
+ *
+ */
+
+typedef struct {
+ int x;
+ int y;
+} Point;
+
+/*
+ *
+ * An array of type Fontmap helps convert font names requested by users into
+ * legitimate PostScript names. The array is initialized using FONTMAP, which must
+ * end with an entry that has NULL defined as its name field.
+ *
+ */
+
+typedef struct {
+ char *name; /* user's font name */
+ char *val; /* corresponding PostScript name */
+} Fontmap;
+
+#define FONTMAP \
+ \
+ { \
+ "R", "Courier", \
+ "I", "Courier-Oblique", \
+ "B", "Courier-Bold", \
+ "CO", "Courier", \
+ "CI", "Courier-Oblique", \
+ "CB", "Courier-Bold", \
+ "CW", "Courier", \
+ "PO", "Courier", \
+ "courier", "Courier", \
+ "cour", "Courier", \
+ "co", "Courier", \
+ NULL, NULL \
+ }
+
+/*
+ *
+ * Some of the non-integer valued functions in posttek.c.
+ *
+ */
+
+char *get_font();
+
diff --git a/sys/src/cmd/postscript/posttek/posttek.ps b/sys/src/cmd/postscript/posttek/posttek.ps
new file mode 100755
index 000000000..ee2428ced
--- /dev/null
+++ b/sys/src/cmd/postscript/posttek/posttek.ps
@@ -0,0 +1,106 @@
+%
+% Version 3.3.2 prologue for tektronix 4014 files.
+%
+
+/#copies 1 store
+/aspectratio 1 def
+/fixlinewidth true def
+/font /Courier def
+/formsperpage 1 def
+/landscape true def
+/linewidth 0 def
+/magnification 1 def
+/margin 10 def
+/orientation 0 def
+/rotation 1 def
+/screenheight 3120 def
+/screenwidth 4150 def
+/spotsize 1 def
+/xoffset 0 def
+/yoffset 0 def
+
+/useclippath true def
+/pagebbox [0 0 612 792] def
+
+/inch {72 mul} bind def
+/min {2 copy gt {exch} if pop} bind def
+
+/kshow {kshow} bind def % so later references don't bind
+
+/setup {
+ counttomark 2 idiv {def} repeat pop
+
+ landscape {/orientation 90 orientation add def} if
+
+ pagedimensions
+ /scaling
+ height margin sub screenheight div
+ width margin sub screenwidth div
+ min def
+ xcenter ycenter translate
+ orientation rotation mul rotate
+ xoffset inch yoffset inch translate
+ magnification dup aspectratio mul scale
+ scaling scaling scale
+ screenwidth 2 div neg screenheight 2 div neg translate
+
+ tietodevicespace
+ linewidth scaling div setlinewidth
+ 1 setlinecap
+ newpath
+} def
+
+/pagedimensions {
+ useclippath {
+ /pagebbox [clippath pathbbox newpath] def
+ } if
+ pagebbox aload pop
+ 4 -1 roll exch 4 1 roll 4 copy
+ landscape {4 2 roll} if
+ sub /width exch def
+ sub /height exch def
+ add 2 div /xcenter exch def
+ add 2 div /ycenter exch def
+ userdict /gotpagebbox true put
+} def
+
+/pagesetup {/page exch def} bind def
+
+/tietodevicespace {
+ fixlinewidth linewidth 0 gt and linewidth 1 lt and {
+ /moveto {
+ 2 copy /Y exch def /X exch def
+ transform round exch round exch itransform
+ moveto
+ } bind def
+ /lineto {
+ 2 copy /Y exch def /X exch def
+ transform round exch round exch itransform
+ lineto
+ } bind def
+ /rlineto {Y add exch X add exch lineto} bind def
+ /v V 0 get bind def
+ } if
+} def
+
+/V [{moveto counttomark 2 idiv {rlineto} repeat stroke}] def
+/v V 0 get bind def
+/p {newpath spotsize 0 360 arc fill} bind def
+
+/l {{scaling div} forall counttomark array astore 0 setdash} bind def
+/w {linewidth 0 eq {.3} {linewidth} ifelse mul linewidth add scaling div setlinewidth} bind def
+/i {3 mul 4 sub -100 div mul .5 add /spotsize exch def} bind def
+
+/f {/charwidth exch def font findfont charwidth .6 div scalefont setfont} bind def
+
+/t {
+ 3 1 roll moveto
+ currentpoint {
+ pop pop
+ exch charwidth add exch
+ moveto currentpoint
+ } 4 -1 roll kshow
+ pop pop
+} bind def
+
+/done {/lastpage where {pop lastpage} if} def