Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 *
 * 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 */

/*****************************************************************************/