ttpic - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit b6d98463b416e9d95bc88f948b4abdff6b24aede
 (DIR) parent 434d1b41b61a9771f4cec2ca6c0a00b067a50fa0
 (HTM) Author: rsc <devnull@localhost>
       Date:   Fri, 14 Apr 2006 00:04:03 +0000
       
       ttpic
       
       Diffstat:
         A src/cmd/tpic/Changes                |      24 ++++++++++++++++++++++++
         A src/cmd/tpic/arcgen.c               |     214 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/blockgen.c             |     223 ++++++++++++++++++++++++++++++
         A src/cmd/tpic/boxgen.c               |     113 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/circgen.c              |     127 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/for.c                  |      94 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/input.c                |     608 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/linegen.c              |     210 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/main.c                 |     277 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/misc.c                 |     463 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/mkfile                 |      39 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/movegen.c              |      87 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/pic.h                  |     285 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/picl.l                 |     261 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/picy.c                 |    1239 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/picy.y                 |     320 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/pltex.c                |     149 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/print.c                |     210 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/symtab.c               |     109 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/tex.c                  |     203 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/tex.h                  |      50 +++++++++++++++++++++++++++++++
         A src/cmd/tpic/textgen.c              |     114 +++++++++++++++++++++++++++++++
       
       22 files changed, 5419 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/src/cmd/tpic/Changes b/src/cmd/tpic/Changes
       t@@ -0,0 +1,24 @@
       +30 Nov 1988        ehg
       +Copied from /n/bowell/usr/src/cmd/pic.
       +
       +16 Jan 1989        ehg
       +Updates from /n/coma/usr/bwk/pic.
       +Brian picked up printlf() and I moved space() in openpl() in pixel and 4014,
       +        so no changes were needed in the source imported from bowell and bwk.
       +bwk's print.c calls arc(), which is translated in pl$DEV.c into a call to
       +        devarc() whose code is in lib$DEV/arc.c.
       +I didn't understand howard's dotline change and it conflicted
       +        with using pure bwk code, so I commented it out in pltex.c.
       +
       +8 Sep 1989        hwt
       +Updates from /n/bowell/usr/src/cmd/pic
       +Some changes in linegen.c (bigger buffers), misc.c, picl.l, picy.y, symtab.c
       +I removed the \t-><tab> transformation in picl.l (bwk agreed it was bogus).
       +Put the dotline change back in (to get smaller output files, and avoid
       +        problems where lines were bunching together).
       +
       +11 Sep 1989        hwt
       +Went back to troff dotline (the postscript one doesn't act the same
       +        as troff when scale!=1).
       +Changed default pen size from 8 to 9 (to give same thickness lines
       +        as default pic), and made dots change size with pen size.
 (DIR) diff --git a/src/cmd/tpic/arcgen.c b/src/cmd/tpic/arcgen.c
       t@@ -0,0 +1,214 @@
       +#include        <stdio.h>
       +#include        <math.h>
       +#include        "pic.h"
       +#include        "y.tab.h"
       +
       +obj*
       +arcgen(int type)        /* handles circular and (eventually) elliptical arcs */
       +{
       +        static double prevw = HT10;
       +        static double prevh = HT5;
       +        static double prevrad = HT2;
       +        static int dtox[2][4] ={ 1, -1, -1, 1, 1, 1, -1, -1 };
       +        static int dtoy[2][4] ={ 1, 1, -1, -1, -1, 1, 1, -1 };
       +        static int dctrx[2][4] ={ 0, -1, 0, 1, 0, 1, 0, -1 };
       +        static int dctry[2][4] ={ 1, 0, -1, 0, -1, 0, 1, 0 };
       +        static int nexthv[2][4] ={ U_DIR, L_DIR, D_DIR, R_DIR, D_DIR, R_DIR, U_DIR, L_DIR };
       +        double dx2, dy2, ht, phi, r, d;
       +        int i, head, to, at, cw, invis, ddtype;
       +        obj *p, *ppos;
       +        double fromx, fromy, tox, toy;
       +        Attr *ap;
       +
       +        tox = 0;
       +        toy = 0;
       +        prevrad = getfval("arcrad");
       +        prevh = getfval("arrowht");
       +        prevw = getfval("arrowwid");
       +        fromx = curx;
       +        fromy = cury;
       +        head = to = at = cw = invis = ddtype = 0;
       +        for (i = 0; i < nattr; i++) {
       +                ap = &attr[i];
       +                switch (ap->a_type) {
       +                case TEXTATTR:
       +                        savetext(ap->a_sub, ap->a_val.p);
       +                        break;
       +                case HEAD:
       +                        head += ap->a_val.i;
       +                        break;
       +                case INVIS:
       +                        invis = INVIS;
       +                        break;
       +                case HEIGHT:        /* length of arrowhead */
       +                        prevh = ap->a_val.f;
       +                        break;
       +                case WIDTH:        /* width of arrowhead */
       +                        prevw = ap->a_val.f;
       +                        break;
       +                case RADIUS:
       +                        prevrad = ap->a_val.f;
       +                        break;
       +                case DIAMETER:
       +                        prevrad = ap->a_val.f / 2;
       +                        break;
       +                case CW:
       +                        cw = 1;
       +                        break;
       +                case FROM:        /* start point of arc */
       +                        ppos = ap->a_val.o;
       +                        fromx = ppos->o_x;
       +                        fromy = ppos->o_y;
       +                        break;
       +                case TO:        /* end point of arc */
       +                        ppos = ap->a_val.o;
       +                        tox = ppos->o_x;
       +                        toy = ppos->o_y;
       +                        to++;
       +                        break;
       +                case AT:        /* center of arc */
       +                        ppos = ap->a_val.o;
       +                        curx = ppos->o_x;
       +                        cury = ppos->o_y;
       +                        at = 1;
       +                        break;
       +                case UP:
       +                        hvmode = U_DIR;
       +                        break;
       +                case DOWN:
       +                        hvmode = D_DIR;
       +                        break;
       +                case RIGHT:
       +                        hvmode = R_DIR;
       +                        break;
       +                case LEFT:
       +                        hvmode = L_DIR;
       +                        break;
       +                }
       +        }
       +        if (!at && !to) {        /* the defaults are mostly OK */
       +                curx = fromx + prevrad * dctrx[cw][hvmode];
       +                cury = fromy + prevrad * dctry[cw][hvmode];
       +                tox = fromx + prevrad * dtox[cw][hvmode];
       +                toy = fromy + prevrad * dtoy[cw][hvmode];
       +                hvmode = nexthv[cw][hvmode];
       +        }
       +        else if (!at) {
       +                dx2 = (tox - fromx) / 2;
       +                dy2 = (toy - fromy) / 2;
       +                phi = atan2(dy2, dx2) + (cw ? -PI/2 : PI/2);
       +                if (prevrad <= 0.0)
       +                        prevrad = dx2*dx2+dy2*dy2;
       +                for (r=prevrad; (d = r*r - (dx2*dx2+dy2*dy2)) <= 0.0; r *= 2)
       +                        ;        /* this kludge gets around too-small radii */
       +                prevrad = r;
       +                ht = sqrt(d);
       +                curx = fromx + dx2 + ht * cos(phi);
       +                cury = fromy + dy2 + ht * sin(phi);
       +                dprintf("dx2,dy2=%g,%g, phi=%g, r,ht=%g,%g\n",
       +                        dx2, dy2, phi, r, ht);
       +        }
       +        else if (at && !to) {        /* do we have all the cases??? */
       +                tox = fromx + prevrad * dtox[cw][hvmode];
       +                toy = fromy + prevrad * dtoy[cw][hvmode];
       +                hvmode = nexthv[cw][hvmode];
       +        }
       +        if (cw) {        /* interchange roles of from-to and heads */
       +                double temp;
       +                temp = fromx; fromx = tox; tox = temp;
       +                temp = fromy; fromy = toy; toy = temp;
       +                if (head == HEAD1)
       +                        head = HEAD2;
       +                else if (head == HEAD2)
       +                        head = HEAD1;
       +        }
       +        p = makenode(type, 7);
       +        arc_extreme(fromx, fromy, tox, toy, curx, cury);
       +        p->o_val[0] = fromx;
       +        p->o_val[1] = fromy;
       +        p->o_val[2] = tox;
       +        p->o_val[3] = toy;
       +        if (cw) {
       +                curx = fromx;
       +                cury = fromy;
       +        } else {
       +                curx = tox;
       +                cury = toy;
       +        }
       +        p->o_val[4] = prevw;
       +        p->o_val[5] = prevh;
       +        p->o_val[6] = prevrad;
       +        p->o_attr = head | (cw ? CW_ARC : 0) | invis | ddtype;
       +        if (head)
       +                p->o_nhead = getfval("arrowhead");
       +        dprintf("arc rad %g at %g %g from %g %g to %g %g head %g %g\n",
       +                prevrad, p->o_x, p->o_y,
       +                p->o_val[0], p->o_val[1], p->o_val[2], p->o_val[3], p->o_val[4], p->o_val[5]);
       +        return(p);
       +}
       +
       +/***************************************************************************
       +   bounding box of a circular arc             Eric Grosse  24 May 84
       +
       +Conceptually, this routine generates a list consisting of the start,
       +end, and whichever north, east, south, and west points lie on the arc.
       +The bounding box is then the range of this list.
       +    list = {start,end}
       +    j = quadrant(start)
       +    k = quadrant(end)
       +    if( j==k && long way 'round )  append north,west,south,east
       +    else
       +      while( j != k )
       +         append center+radius*[j-th of north,west,south,east unit vectors]
       +         j += 1  (mod 4)
       +    return( bounding box of list )
       +The following code implements this, with simple optimizations.
       +***********************************************************************/
       +
       +
       +void
       +arc_extreme(double x0, double y0, double x1, double y1, double xc, double yc)
       +{
       +        /* assumes center isn't too far out */
       +        double r, xmin, ymin, xmax, ymax;
       +        int j, k;
       +        x0 -= xc; y0 -= yc;        /* move to center */
       +        x1 -= xc; y1 -= yc;
       +        xmin = (x0<x1)?x0:x1; ymin = (y0<y1)?y0:y1;
       +        xmax = (x0>x1)?x0:x1; ymax = (y0>y1)?y0:y1;
       +        r = sqrt(x0*x0 + y0*y0);
       +        if (r > 0.0) {
       +                j = quadrant(x0,y0);
       +                k = quadrant(x1,y1);
       +                if (j == k && y1*x0 < x1*y0) {
       +                        /* viewed as complex numbers, if Im(z1/z0)<0, arc is big */
       +                        if( xmin > -r) xmin = -r; if( ymin > -r) ymin = -r;
       +                        if( xmax <  r) xmax =  r; if( ymax <  r) ymax =  r;
       +                } else {
       +                        while (j != k) {
       +                                switch (j) {
       +                                        case 1: if( ymax <  r) ymax =  r; break; /* north */
       +                                        case 2: if( xmin > -r) xmin = -r; break; /* west */
       +                                        case 3: if( ymin > -r) ymin = -r; break; /* south */
       +                                        case 4: if( xmax <  r) xmax =  r; break; /* east */
       +                                }
       +                                j = j%4 + 1;
       +                        }
       +                }
       +        }
       +        xmin += xc; ymin += yc;
       +        xmax += xc; ymax += yc;
       +        extreme(xmin, ymin);
       +        extreme(xmax, ymax);
       +}
       +
       +int
       +quadrant(double x, double y)
       +{
       +        if (     x>=0.0 && y> 0.0) return(1);
       +        else if( x< 0.0 && y>=0.0) return(2);
       +        else if( x<=0.0 && y< 0.0) return(3);
       +        else if( x> 0.0 && y<=0.0) return(4);
       +        else                           return 0;        /* shut up lint */
       +}
       +
 (DIR) diff --git a/src/cmd/tpic/blockgen.c b/src/cmd/tpic/blockgen.c
       t@@ -0,0 +1,223 @@
       +#include        <stdio.h>
       +#include        "pic.h"
       +#include        "y.tab.h"
       +
       +#define        NBRACK        20        /* depth of [...] */
       +#define        NBRACE        20        /* depth of {...} */
       +
       +struct pushstack stack[NBRACK];
       +int        nstack        = 0;
       +struct pushstack bracestack[NBRACE];
       +int        nbstack        = 0;
       +
       +obj*
       +leftthing(int c)        /* called for {... or [... */
       +                        /* really ought to be separate functions */
       +{
       +        obj *p;
       +
       +        if (c == '[') {
       +                if (nstack >= NBRACK)
       +                        ERROR "[...] nested too deep" FATAL;
       +                stack[nstack].p_x = curx;
       +                stack[nstack].p_y = cury;
       +                stack[nstack].p_hvmode = hvmode;
       +                curx = cury = 0;
       +                stack[nstack].p_xmin = xmin;
       +                stack[nstack].p_xmax = xmax;
       +                stack[nstack].p_ymin = ymin;
       +                stack[nstack].p_ymax = ymax;
       +                nstack++;
       +                xmin = ymin = 30000;
       +                xmax = ymax = -30000;
       +                p = makenode(BLOCK, 7);
       +                p->o_val[4] = nobj;        /* 1st item within [...] */
       +                if (p->o_nobj != nobj-1)
       +                        fprintf(stderr, "nobjs wrong%d %d\n", p->o_nobj, nobj);
       +        } else {
       +                if (nbstack >= NBRACK)
       +                        ERROR "{...} nested too deep" FATAL;
       +                bracestack[nbstack].p_x = curx;
       +                bracestack[nbstack].p_y = cury;
       +                bracestack[nbstack].p_hvmode = hvmode;
       +                nbstack++;
       +                p = NULL;
       +        }
       +        return(p);
       +}
       +
       +obj*
       +rightthing(obj *p, int c)        /* called for ... ] or ... } */
       +{
       +        obj *q;
       +
       +        if (c == '}') {
       +                nbstack--;
       +                curx = bracestack[nbstack].p_x;
       +                cury = bracestack[nbstack].p_y;
       +                hvmode = bracestack[nbstack].p_hvmode;
       +                q = makenode(MOVE, 0);
       +                dprintf("M %g %g\n", curx, cury);
       +        } else {
       +                nstack--;
       +                curx = stack[nstack].p_x;
       +                cury = stack[nstack].p_y;
       +                hvmode = stack[nstack].p_hvmode;
       +                q = makenode(BLOCKEND, 7);
       +                q->o_val[4] = p->o_nobj + 1;        /* back pointer */
       +                p->o_val[5] = q->o_nobj - 1;        /* forward pointer */
       +                p->o_val[0] = xmin; p->o_val[1] = ymin;
       +                p->o_val[2] = xmax; p->o_val[3] = ymax;
       +                p->o_symtab = q->o_symtab = stack[nstack+1].p_symtab;
       +                xmin = stack[nstack].p_xmin;
       +                ymin = stack[nstack].p_ymin;
       +                xmax = stack[nstack].p_xmax;
       +                ymax = stack[nstack].p_ymax;
       +        }
       +        return(q);
       +}
       +
       +obj*
       +blockgen(obj *p, obj *q)        /* handles [...] */
       +{
       +        int i, invis, at, with;
       +        double ddval, h, w, xwith, ywith;
       +        double x0, y0, x1, y1, cx, cy;
       +        obj *ppos;
       +        Attr *ap;
       +
       +        invis = at = 0;
       +        with = xwith = ywith = 0;
       +        ddval = 0;
       +        w = p->o_val[2] - p->o_val[0];
       +        h = p->o_val[3] - p->o_val[1];
       +        cx = (p->o_val[2] + p->o_val[0]) / 2;        /* geom ctr of [] wrt local orogin */
       +        cy = (p->o_val[3] + p->o_val[1]) / 2;
       +        dprintf("cx,cy=%g,%g\n", cx, cy);
       +        for (i = 0; i < nattr; i++) {
       +                ap = &attr[i];
       +                switch (ap->a_type) {
       +                case HEIGHT:
       +                        h = ap->a_val.f;
       +                        break;
       +                case WIDTH:
       +                        w = ap->a_val.f;
       +                        break;
       +                case WITH:
       +                        with = ap->a_val.i;        /* corner */
       +                        break;
       +                case PLACE:        /* actually with position ... */
       +                        ppos = ap->a_val.o;
       +                        xwith = cx - ppos->o_x;
       +                        ywith = cy - ppos->o_y;
       +                        with = PLACE;
       +                        break;
       +                case AT:
       +                case FROM:
       +                        ppos = ap->a_val.o;
       +                        curx = ppos->o_x;
       +                        cury = ppos->o_y;
       +                        at++;
       +                        break;
       +                case INVIS:
       +                        invis = INVIS;
       +                        break;
       +                case TEXTATTR:
       +                        savetext(ap->a_sub, ap->a_val.p);
       +                        break;
       +                }
       +        }
       +        if (with) {
       +                switch (with) {
       +                case NORTH:        ywith = -h / 2; break;
       +                case SOUTH:        ywith = h / 2; break;
       +                case EAST:        xwith = -w / 2; break;
       +                case WEST:        xwith = w / 2; break;
       +                case NE:        xwith = -w / 2; ywith = -h / 2; break;
       +                case SE:        xwith = -w / 2; ywith = h / 2; break;
       +                case NW:        xwith = w / 2; ywith = -h / 2; break;
       +                case SW:        xwith = w / 2; ywith = h / 2; break;
       +                }
       +                curx += xwith;
       +                cury += ywith;
       +        }
       +        if (!at) {
       +                if (isright(hvmode))
       +                        curx += w / 2;
       +                else if (isleft(hvmode))
       +                        curx -= w / 2;
       +                else if (isup(hvmode))
       +                        cury += h / 2;
       +                else
       +                        cury -= h / 2;
       +        }
       +        x0 = curx - w / 2;
       +        y0 = cury - h / 2;
       +        x1 = curx + w / 2;
       +        y1 = cury + h / 2;
       +        extreme(x0, y0);
       +        extreme(x1, y1);
       +        p->o_x = curx;
       +        p->o_y = cury;
       +        p->o_nt1 = ntext1;
       +        p->o_nt2 = ntext;
       +        ntext1 = ntext;
       +        p->o_val[0] = w;
       +        p->o_val[1] = h;
       +        p->o_val[2] = cx;
       +        p->o_val[3] = cy;
       +        p->o_val[5] = q->o_nobj - 1;                /* last item in [...] */
       +        p->o_ddval = ddval;
       +        p->o_attr = invis;
       +        dprintf("[] %g %g %g %g at %g %g, h=%g, w=%g\n", x0, y0, x1, y1, curx, cury, h, w);
       +        if (isright(hvmode))
       +                curx = x1;
       +        else if (isleft(hvmode))
       +                curx = x0;
       +        else if (isup(hvmode))
       +                cury = y1;
       +        else
       +                cury = y0;
       +        for (i = 0; i <= 5; i++)
       +                q->o_val[i] = p->o_val[i];
       +        stack[nstack+1].p_symtab = NULL;        /* so won't be found again */
       +        blockadj(p);        /* fix up coords for enclosed blocks */
       +        return(p);
       +}
       +
       +void
       +blockadj(obj *p)        /* adjust coords in block starting at p */
       +{
       +        double dx, dy;
       +        int n, lev;
       +
       +        dx = p->o_x - p->o_val[2];
       +        dy = p->o_y - p->o_val[3];
       +        n = p->o_nobj + 1;
       +        dprintf("into blockadj: dx,dy=%g,%g\n", dx, dy);
       +        for (lev = 1; lev > 0; n++) {
       +                p = objlist[n];
       +                if (p->o_type == BLOCK)
       +                        lev++;
       +                else if (p->o_type == BLOCKEND)
       +                        lev--;
       +                dprintf("blockadj: type=%d o_x,y=%g,%g;", p->o_type, p->o_x, p->o_y);
       +                p->o_x += dx;
       +                p->o_y += dy;
       +                dprintf(" becomes %g,%g\n", p->o_x, p->o_y);
       +                switch (p->o_type) {        /* other absolute coords */
       +                case LINE:
       +                case ARROW:
       +                case SPLINE:
       +                        p->o_val[0] += dx;
       +                        p->o_val[1] += dy;
       +                        break;
       +                case ARC:
       +                        p->o_val[0] += dx;
       +                        p->o_val[1] += dy;
       +                        p->o_val[2] += dx;
       +                        p->o_val[3] += dy;
       +                        break;
       +                }
       +        }
       +}
 (DIR) diff --git a/src/cmd/tpic/boxgen.c b/src/cmd/tpic/boxgen.c
       t@@ -0,0 +1,113 @@
       +#include        <stdio.h>
       +#include        "pic.h"
       +#include        "y.tab.h"
       +
       +obj*
       +boxgen(void)
       +{
       +        static double prevh = HT;
       +        static double prevw = WID;        /* golden mean, sort of */
       +        int i, at, battr, with;
       +        double ddval, fillval, xwith, ywith;
       +        double h, w, x0, y0, x1, y1;
       +        obj *p, *ppos;
       +        Attr *ap;
       +
       +        h = getfval("boxht");
       +        w = getfval("boxwid");
       +        at = battr = with = 0;
       +        ddval = fillval = xwith = ywith = 0;
       +        for (i = 0; i < nattr; i++) {
       +                ap = &attr[i];
       +                switch (ap->a_type) {
       +                case HEIGHT:
       +                        h = ap->a_val.f;
       +                        break;
       +                case WIDTH:
       +                        w = ap->a_val.f;
       +                        break;
       +                case SAME:
       +                        h = prevh;
       +                        w = prevw;
       +                        break;
       +                case WITH:
       +                        with = ap->a_val.i;        /* corner */
       +                        break;
       +                case AT:
       +                        ppos = ap->a_val.o;
       +                        curx = ppos->o_x;
       +                        cury = ppos->o_y;
       +                        at++;
       +                        break;
       +                case INVIS:
       +                        battr |= INVIS;
       +                        break;
       +                case DOT:
       +                case DASH:
       +                        battr |= ap->a_type==DOT ? DOTBIT : DASHBIT;
       +                        if (ap->a_sub == DEFAULT)
       +                                ddval = getfval("dashwid");
       +                        else
       +                                ddval = ap->a_val.f;
       +                        break;
       +                case FILL:
       +                        battr |= FILLBIT;
       +                        if (ap->a_sub == DEFAULT)
       +                                fillval = getfval("fillval");
       +                        else
       +                                fillval = ap->a_val.f;
       +                        break;
       +                case TEXTATTR:
       +                        savetext(ap->a_sub, ap->a_val.p);
       +                        break;
       +                }
       +        }
       +        if (with) {
       +                switch (with) {
       +                case NORTH:        ywith = -h / 2; break;
       +                case SOUTH:        ywith = h / 2; break;
       +                case EAST:        xwith = -w / 2; break;
       +                case WEST:        xwith = w / 2; break;
       +                case NE:        xwith = -w / 2; ywith = -h / 2; break;
       +                case SE:        xwith = -w / 2; ywith = h / 2; break;
       +                case NW:        xwith = w / 2; ywith = -h / 2; break;
       +                case SW:        xwith = w / 2; ywith = h / 2; break;
       +                }
       +                curx += xwith;
       +                cury += ywith;
       +        }
       +        if (!at) {
       +                if (isright(hvmode))
       +                        curx += w / 2;
       +                else if (isleft(hvmode))
       +                        curx -= w / 2;
       +                else if (isup(hvmode))
       +                        cury += h / 2;
       +                else
       +                        cury -= h / 2;
       +        }
       +        x0 = curx - w / 2;
       +        y0 = cury - h / 2;
       +        x1 = curx + w / 2;
       +        y1 = cury + h / 2;
       +        extreme(x0, y0);
       +        extreme(x1, y1);
       +        p = makenode(BOX, 2);
       +        p->o_val[0] = w;
       +        p->o_val[1] = h;
       +        p->o_attr = battr;
       +        p->o_ddval = ddval;
       +        p->o_fillval = fillval;
       +        dprintf("B %g %g %g %g at %g %g, h=%g, w=%g\n", x0, y0, x1, y1, curx, cury, h, w);
       +        if (isright(hvmode))
       +                curx = x1;
       +        else if (isleft(hvmode))
       +                curx = x0;
       +        else if (isup(hvmode))
       +                cury = y1;
       +        else
       +                cury = y0;
       +        prevh = h;
       +        prevw = w;
       +        return(p);
       +}
 (DIR) diff --git a/src/cmd/tpic/circgen.c b/src/cmd/tpic/circgen.c
       t@@ -0,0 +1,127 @@
       +#include        <stdio.h>
       +#include        "pic.h"
       +#include        "y.tab.h"
       +
       +obj*
       +circgen(int type)
       +{
       +        static double rad[2] = { HT2, WID2 };
       +        static double rad2[2] = { HT2, HT2 };
       +        int i, at, t, with, battr;
       +        double xwith, ywith;
       +        double r, r2, ddval, fillval;
       +        obj *p, *ppos;
       +        Attr *ap;
       +
       +        battr = at = 0;
       +        with = xwith = ywith = fillval = 0;
       +        t = (type == CIRCLE) ? 0 : 1;
       +        r = 0;
       +        r2 = 0;
       +        ddval = 0;
       +        if (type == CIRCLE)
       +                r = r2 = getfval("circlerad");
       +        else if (type == ELLIPSE) {
       +                r = getfval("ellipsewid") / 2;
       +                r2 = getfval("ellipseht") / 2;
       +        }
       +        for (i = 0; i < nattr; i++) {
       +                ap = &attr[i];
       +                switch (ap->a_type) {
       +                case TEXTATTR:
       +                        savetext(ap->a_sub, ap->a_val.p);
       +                        break;
       +                case RADIUS:
       +                        r = ap->a_val.f;
       +                        break;
       +                case DIAMETER:
       +                case WIDTH:
       +                        r = ap->a_val.f / 2;
       +                        break;
       +                case HEIGHT:
       +                        r2 = ap->a_val.f / 2;
       +                        break;
       +                case SAME:
       +                        r = rad[t];
       +                        r2 = rad2[t];
       +                        break;
       +                case WITH:
       +                        with = ap->a_val.i;
       +                        break;
       +                case AT:
       +                        ppos = ap->a_val.o;
       +                        curx = ppos->o_x;
       +                        cury = ppos->o_y;
       +                        at++;
       +                        break;
       +                case INVIS:
       +                        battr |= INVIS;
       +                        break;
       +                case DOT:
       +                case DASH:
       +                        battr |= ap->a_type==DOT ? DOTBIT : DASHBIT;
       +                        if (ap->a_sub == DEFAULT)
       +                                ddval = getfval("dashwid");
       +                        else
       +                                ddval = ap->a_val.f;
       +                        break;
       +                case FILL:
       +                        battr |= FILLBIT;
       +                        if (ap->a_sub == DEFAULT)
       +                                fillval = getfval("fillval");
       +                        else
       +                                fillval = ap->a_val.f;
       +                        break;
       +                }
       +        }
       +        if (type == CIRCLE)
       +                r2 = r;        /* probably superfluous */
       +        if (with) {
       +                switch (with) {
       +                case NORTH:        ywith = -r2; break;
       +                case SOUTH:        ywith = r2; break;
       +                case EAST:        xwith = -r; break;
       +                case WEST:        xwith = r; break;
       +                case NE:        xwith = -r * 0.707; ywith = -r2 * 0.707; break;
       +                case SE:        xwith = -r * 0.707; ywith = r2 * 0.707; break;
       +                case NW:        xwith = r * 0.707; ywith = -r2 * 0.707; break;
       +                case SW:        xwith = r * 0.707; ywith = r2 * 0.707; break;
       +                }
       +                curx += xwith;
       +                cury += ywith;
       +        }
       +        if (!at) {
       +                if (isright(hvmode))
       +                        curx += r;
       +                else if (isleft(hvmode))
       +                        curx -= r;
       +                else if (isup(hvmode))
       +                        cury += r2;
       +                else
       +                        cury -= r2;
       +        }
       +        p = makenode(type, 2);
       +        p->o_val[0] = rad[t] = r;
       +        p->o_val[1] = rad2[t] = r2;
       +        if (r <= 0 || r2 <= 0) {
       +                ERROR "%s has invalid radius %g\n", (type==CIRCLE) ? "circle" : "ellipse", r<r2 ? r : r2 WARNING;
       +        }
       +        p->o_attr = battr;
       +        p->o_ddval = ddval;
       +        p->o_fillval = fillval;
       +        extreme(curx+r, cury+r2);
       +        extreme(curx-r, cury-r2);
       +        if (type == CIRCLE)
       +                dprintf("C %g %g %g\n", curx, cury, r);
       +        if (type == ELLIPSE)
       +                dprintf("E %g %g %g %g\n", curx, cury, r, r2);
       +        if (isright(hvmode))
       +                curx += r;
       +        else if (isleft(hvmode))
       +                curx -= r;
       +        else if (isup(hvmode))
       +                cury += r2;
       +        else
       +                cury -= r2;
       +        return(p);
       +}
 (DIR) diff --git a/src/cmd/tpic/for.c b/src/cmd/tpic/for.c
       t@@ -0,0 +1,94 @@
       +#include <stdio.h>
       +#include "pic.h"
       +#include "y.tab.h"
       +
       +#define        SLOP        1.001
       +
       +typedef struct {
       +        char        *var;        /* index variable */
       +        double        to;        /* limit */
       +        double        by;
       +        int        op;        /* operator */
       +        char        *str;        /* string to push back */
       +} For;
       +
       +For        forstk[10];        /* stack of for loops */
       +For        *forp = forstk;        /* pointer to current top */
       +
       +void
       +forloop(char *var, double from, double to, int op, double by, char *str)        /* set up a for loop */
       +{
       +        dprintf("# for %s from %g to %g by %c %g \n",
       +                var, from, to, op, by);
       +        if (++forp >= forstk+10)
       +                ERROR "for loop nested too deep" FATAL;
       +        forp->var = var;
       +        forp->to = to;
       +        forp->op = op;
       +        forp->by = by;
       +        forp->str = str;
       +        setfval(var, from);
       +        nextfor();
       +        unput('\n');
       +}
       +
       +void
       +nextfor(void)        /* do one iteration of a for loop */
       +{
       +        /* BUG:  this should depend on op and direction */
       +        if (getfval(forp->var) > SLOP * forp->to) {        /* loop is done */
       +                free(forp->str);
       +                if (--forp < forstk)
       +                        ERROR "forstk popped too far" FATAL;
       +        } else {                /* another iteration */
       +                pushsrc(String, "\nEndfor\n");
       +                pushsrc(String, forp->str);
       +        }
       +}
       +
       +void
       +endfor(void)        /* end one iteration of for loop */
       +{
       +        struct symtab *p = lookup(forp->var);
       +
       +        switch (forp->op) {
       +        case '+':
       +        case ' ':
       +                p->s_val.f += forp->by;
       +                break;
       +        case '-':
       +                p->s_val.f -= forp->by;
       +                break;
       +        case '*':
       +                p->s_val.f *= forp->by;
       +                break;
       +        case '/':
       +                p->s_val.f /= forp->by;
       +                break;
       +        }
       +        nextfor();
       +}
       +
       +char*
       +ifstat(double expr, char *thenpart, char *elsepart)
       +{
       +        dprintf("if %g then <%s> else <%s>\n", expr, thenpart, elsepart? elsepart : "");
       +        if (expr) {
       +                unput('\n');
       +                pushsrc(Free, thenpart);
       +                pushsrc(String, thenpart);
       +                unput('\n');
       +                  if (elsepart)
       +                        free(elsepart);
       +                return thenpart;        /* to be freed later */
       +        } else {
       +                free(thenpart);
       +                if (elsepart) {
       +                        unput('\n');
       +                        pushsrc(Free, elsepart);
       +                        pushsrc(String, elsepart);
       +                        unput('\n');
       +                }
       +                return elsepart;
       +        }
       +}
 (DIR) diff --git a/src/cmd/tpic/input.c b/src/cmd/tpic/input.c
       t@@ -0,0 +1,608 @@
       +#include <stdio.h>
       +#include <ctype.h>
       +#include <errno.h>
       +#include "pic.h"
       +#include "y.tab.h"
       +
       +Infile        infile[10];
       +Infile        *curfile = infile;
       +
       +#define        MAXSRC        50
       +Src        src[MAXSRC];        /* input source stack */
       +Src        *srcp        = src;
       +
       +void
       +pushsrc(int type, char *ptr)        /* new input source */
       +{
       +        if (++srcp >= src + MAXSRC)
       +                ERROR "inputs nested too deep" FATAL;
       +        srcp->type = type;
       +        srcp->sp = ptr;
       +        if (dbg > 1) {
       +                printf("\n%3d ", srcp - src);
       +                switch (srcp->type) {
       +                case File:
       +                        printf("push file %s\n", ((Infile *)ptr)->fname);
       +                        break;
       +                case Macro:
       +                        printf("push macro <%s>\n", ptr);
       +                        break;
       +                case Char:
       +                        printf("push char <%c>\n", *ptr);
       +                        break;
       +                case Thru:
       +                        printf("push thru\n");
       +                        break;
       +                case String:
       +                        printf("push string <%s>\n", ptr);
       +                        break;
       +                case Free:
       +                        printf("push free <%s>\n", ptr);
       +                        break;
       +                default:
       +                        ERROR "pushed bad type %d", srcp->type FATAL;
       +                }
       +        }
       +}
       +
       +void
       +popsrc(void)        /* restore an old one */
       +{
       +        if (srcp <= src)
       +                ERROR "too many inputs popped" FATAL;
       +        if (dbg > 1) {
       +                printf("%3d ", srcp - src);
       +                switch (srcp->type) {
       +                case File:
       +                        printf("pop file\n");
       +                        break;
       +                case Macro:
       +                        printf("pop macro\n");
       +                        break;
       +                case Char:
       +                        printf("pop char <%c>\n", *srcp->sp);
       +                        break;
       +                case Thru:
       +                        printf("pop thru\n");
       +                        break;
       +                case String:
       +                        printf("pop string\n");
       +                        break;
       +                case Free:
       +                        printf("pop free\n");
       +                        break;
       +                default:
       +                        ERROR "pop weird input %d", srcp->type FATAL;
       +                }
       +        }
       +        srcp--;
       +}
       +
       +void
       +definition(char *s)        /* collect definition for s and install */
       +        /* definitions picked up lexically */
       +{
       +        char *p;
       +        struct symtab *stp;
       +
       +        p = delimstr("definition");
       +        stp = lookup(s);
       +        if (stp != NULL) {        /* it's there before */
       +                if (stp->s_type != DEFNAME) {
       +                        ERROR "%s used as variable and definition", s WARNING;
       +                        return;
       +                }
       +                free(stp->s_val.p);
       +                stp->s_val.p = p;
       +        } else {
       +                YYSTYPE u;
       +                u.p = p;
       +                makevar(tostring(s), DEFNAME, u);
       +        }
       +        dprintf("installing %s as `%s'\n", s, p);
       +}
       +
       +char*
       +delimstr(char *s)        /* get body of X ... X */
       +                /* message if too big */
       +{
       +        int c, delim, rdelim, n, deep;
       +        static char *buf = NULL;
       +        static int nbuf = 0;
       +        char *p;
       +
       +        if (buf == NULL)
       +                buf = grow(buf, "buf", nbuf += 1000, sizeof(buf[0]));
       +        while ((delim = input()) == ' ' || delim == '\t' || delim == '\n')
       +                ;
       +        rdelim = baldelim(delim, "{}");                /* could be "(){}[]`'" */
       +        deep = 1;
       +        for (p = buf; ; ) {
       +                c = input();
       +                if (c == rdelim)
       +                        if (--deep == 0)
       +                                break;
       +                if (c == delim)
       +                        deep++;
       +                if (p >= buf + nbuf) {
       +                        n = p - buf;
       +                        buf = grow(buf, "buf", nbuf += 1000, sizeof(buf[0]));
       +                        p = buf + n;
       +                }
       +                if (c == EOF)
       +                        ERROR "end of file in %s %c %.20s... %c", s, delim, buf, delim FATAL;
       +                *p++ = c;
       +        }
       +        *p = '\0';
       +        dprintf("delimstr %s %c <%s> %c\n", s, delim, buf, delim);
       +        return tostring(buf);
       +}
       +
       +int
       +baldelim(int c, char *s)        /* replace c by balancing entry in s */
       +{
       +        for ( ; *s; s += 2)
       +                if (*s == c)
       +                        return s[1];
       +        return c;
       +}
       +
       +void
       +undefine(char *s)        /* undefine macro */
       +{
       +        while (*s != ' ' && *s != '\t')                /* skip "undef..." */
       +                s++;
       +        while (*s == ' ' || *s == '\t')
       +                s++;
       +        freedef(s);
       +}
       +
       +
       +Arg        args[10];        /* argument frames */
       +Arg        *argfp = args;        /* frame pointer */
       +int        argcnt;                /* number of arguments seen so far */
       +
       +void
       +dodef(struct symtab *stp)        /* collect args and switch input to defn */
       +{
       +        int i, len;
       +        char *p;
       +        Arg *ap;
       +
       +        ap = argfp+1;
       +        if (ap >= args+10)
       +                ERROR "arguments too deep" FATAL;
       +        argcnt = 0;
       +        if (input() != '(')
       +                ERROR "disaster in dodef" FATAL;
       +        if (ap->argval == 0)
       +                ap->argval = malloc(1000);
       +        for (p = ap->argval; (len = getarg(p)) != -1; p += len) {
       +                ap->argstk[argcnt++] = p;
       +                if (input() == ')')
       +                        break;
       +        }
       +        for (i = argcnt; i < MAXARGS; i++)
       +                ap->argstk[i] = "";
       +        if (dbg)
       +                for (i = 0; i < argcnt; i++)
       +                        printf("arg %d.%d = <%s>\n", ap-args, i+1, ap->argstk[i]);
       +        argfp = ap;
       +        pushsrc(Macro, stp->s_val.p);
       +}
       +
       +int
       +getarg(char *p)        /* pick up single argument, store in p, return length */
       +{
       +        int n, c, npar;
       +
       +        n = npar = 0;
       +        for ( ;; ) {
       +                c = input();
       +                if (c == EOF)
       +                        ERROR "end of file in getarg" FATAL;
       +                if (npar == 0 && (c == ',' || c == ')'))
       +                        break;
       +                if (c == '"')        /* copy quoted stuff intact */
       +                        do {
       +                                *p++ = c;
       +                                n++;
       +                        } while ((c = input()) != '"' && c != EOF);
       +                else if (c == '(')
       +                        npar++;
       +                else if (c == ')')
       +                        npar--;
       +                n++;
       +                *p++ = c;
       +        }
       +        *p = 0;
       +        unput(c);
       +        return(n + 1);
       +}
       +
       +#define        PBSIZE        2000
       +char        pbuf[PBSIZE];                /* pushback buffer */
       +char        *pb        = pbuf-1;        /* next pushed back character */
       +
       +char        ebuf[200];                /* collect input here for error reporting */
       +char        *ep        = ebuf;
       +
       +int        begin        = 0;
       +extern        int        thru;
       +extern        struct symtab        *thrudef;
       +extern        char        *untilstr;
       +
       +int
       +input(void)
       +{
       +        int c;
       +
       +        if (thru && begin) {
       +                do_thru();
       +                begin = 0;
       +        }
       +        c = nextchar();
       +        if (dbg > 1)
       +                printf(" <%c>", c);
       +        if (ep >= ebuf + sizeof ebuf)
       +                ep = ebuf;
       +        return *ep++ = c;
       +}
       +
       +int
       +nextchar(void)
       +{
       +        int c;
       +
       +  loop:
       +        switch (srcp->type) {
       +        default:
       +                c = -1;
       +                break;
       +        case Free:        /* free string */
       +                free(srcp->sp);
       +                popsrc();
       +                goto loop;
       +        case Thru:        /* end of pushed back line */
       +                begin = 1;
       +                popsrc();
       +                c = '\n';
       +                break;
       +        case Char:
       +                if (pb >= pbuf) {
       +                        c = *pb--;
       +                        popsrc();
       +                        break;
       +                } else {        /* can't happen? */
       +                        popsrc();
       +                        goto loop;
       +                }
       +        case String:
       +                c = *srcp->sp++;
       +                if (c == '\0') {
       +                        popsrc();
       +                        goto loop;
       +                } else {
       +                        if (*srcp->sp == '\0')        /* empty, so pop */
       +                                popsrc();
       +                        break;
       +                }
       +        case Macro:
       +                c = *srcp->sp++;
       +                if (c == '\0') {
       +                        if (--argfp < args)
       +                                ERROR "argfp underflow" FATAL;
       +                        popsrc();
       +                        goto loop;
       +                } else if (c == '$' && isdigit(*srcp->sp)) {
       +                        int n = 0;
       +                        while (isdigit(*srcp->sp))
       +                                n = 10 * n + *srcp->sp++ - '0';
       +                        if (n > 0 && n <= MAXARGS)
       +                                pushsrc(String, argfp->argstk[n-1]);
       +                        goto loop;
       +                }
       +                break;
       +        case File:
       +                c = getc(curfile->fin);
       +                if (c == EOF) {
       +                        if (curfile == infile)
       +                                ERROR "end of file inside .PS/.PE" FATAL;
       +                        if (curfile->fin != stdin) {
       +                                fclose(curfile->fin);
       +                                free(curfile->fname);        /* assumes allocated */
       +                        }
       +                        curfile--;
       +                        printlf(curfile->lineno, curfile->fname);
       +                        popsrc();
       +                        thru = 0;        /* chicken out */
       +                        thrudef = 0;
       +                        if (untilstr) {
       +                                free(untilstr);
       +                                untilstr = 0;
       +                        }
       +                        goto loop;
       +                }
       +                if (c == '\n')
       +                        curfile->lineno++;
       +                break;
       +        }
       +        return c;
       +}
       +
       +void
       +do_thru(void)        /* read one line, make into a macro expansion */
       +{
       +        int c, i;
       +        char *p;
       +        Arg *ap;
       +
       +        ap = argfp+1;
       +        if (ap >= args+10)
       +                ERROR "arguments too deep" FATAL;
       +        if (ap->argval == NULL)
       +                ap->argval = malloc(1000);
       +        p = ap->argval;
       +        argcnt = 0;
       +        c = nextchar();
       +        if (thru == 0) {        /* end of file was seen, so thru is done */
       +                unput(c);
       +                return;
       +        }
       +        for ( ; c != '\n' && c != EOF; ) {
       +                if (c == ' ' || c == '\t') {
       +                        c = nextchar();
       +                        continue;
       +                }
       +                ap->argstk[argcnt++] = p;
       +                if (c == '"') {
       +                        do {
       +                                *p++ = c;
       +                                if ((c = nextchar()) == '\\') {
       +                                        *p++ = c;
       +                                        *p++ = nextchar();
       +                                        c = nextchar();
       +                                }
       +                        } while (c != '"' && c != '\n' && c != EOF);
       +                        *p++ = '"';
       +                        if (c == '"')
       +                                c = nextchar();
       +                } else {
       +                        do {
       +                                *p++ = c;
       +                        } while ((c = nextchar())!=' ' && c!='\t' && c!='\n' && c!=',' && c!=EOF);
       +                        if (c == ',')
       +                                c = nextchar();
       +                }
       +                *p++ = '\0';
       +        }
       +        if (c == EOF)
       +                ERROR "unexpected end of file in do_thru" FATAL;
       +        if (argcnt == 0) {        /* ignore blank line */
       +                pushsrc(Thru, (char *) 0);
       +                return;
       +        }
       +        for (i = argcnt; i < MAXARGS; i++)
       +                ap->argstk[i] = "";
       +        if (dbg)
       +                for (i = 0; i < argcnt; i++)
       +                        printf("arg %d.%d = <%s>\n", ap-args, i+1, ap->argstk[i]);
       +        if (strcmp(ap->argstk[0], ".PE") == 0) {
       +                thru = 0;
       +                thrudef = 0;
       +                pushsrc(String, "\n.PE\n");
       +                return;
       +        }
       +        if (untilstr && strcmp(ap->argstk[0], untilstr) == 0) {
       +                thru = 0;
       +                thrudef = 0;
       +                free(untilstr);
       +                untilstr = 0;
       +                return;
       +        }
       +        pushsrc(Thru, (char *) 0);
       +        dprintf("do_thru pushing back <%s>\n", thrudef->s_val.p);
       +        argfp = ap;
       +        pushsrc(Macro, thrudef->s_val.p);
       +}
       +
       +int
       +unput(int c)
       +{
       +        if (++pb >= pbuf + sizeof pbuf)
       +                ERROR "pushback overflow" FATAL;
       +        if (--ep < ebuf)
       +                ep = ebuf + sizeof(ebuf) - 1;
       +        *pb = c;
       +        pushsrc(Char, pb);
       +        return c;
       +}
       +
       +void
       +pbstr(char *s)
       +{
       +        pushsrc(String, s);
       +}
       +
       +double
       +errcheck(double x, char *s)
       +{
       +        extern int errno;
       +
       +        if (errno == EDOM) {
       +                errno = 0;
       +                ERROR "%s argument out of domain", s WARNING;
       +        } else if (errno == ERANGE) {
       +                errno = 0;
       +                ERROR "%s result out of range", s WARNING;
       +        }
       +        return x;
       +}
       +
       +char        errbuf[200];
       +
       +void
       +yyerror(char *s)
       +{
       +        extern char *cmdname;
       +
       +        if (synerr)
       +                return;
       +        fflush(stdout);
       +        fprintf(stderr, "%s: %s", cmdname, s);
       +        if (errno > 0)
       +                fprintf(stderr, " (%s)", strerror(errno));
       +        fprintf(stderr, " near line %d, file %s\n",
       +                curfile->lineno, curfile->fname);
       +        eprint();
       +        synerr = 1;
       +        errno = 0;
       +}
       +
       +void
       +eprint(void)        /* try to print context around error */
       +{
       +        char *p, *q;
       +
       +        p = ep - 1;
       +        if (p > ebuf && *p == '\n')
       +                p--;
       +        for ( ; p >= ebuf && *p != '\n'; p--)
       +                ;
       +        while (*p == '\n')
       +                p++;
       +        fprintf(stderr, " context is\n\t");
       +        for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
       +                ;
       +        while (p < q)
       +                putc(*p++, stderr);
       +        fprintf(stderr, " >>> ");
       +        while (p < ep)
       +                putc(*p++, stderr);
       +        fprintf(stderr, " <<< ");
       +        while (pb >= pbuf)
       +                putc(*pb--, stderr);
       +        fgets(ebuf, sizeof ebuf, curfile->fin);
       +        fprintf(stderr, "%s", ebuf);
       +        pbstr("\n.PE\n");        /* safety first */
       +        ep = ebuf;
       +}
       +
       +void yywrap(void) {;}
       +
       +char        *newfile = 0;                /* filename for file copy */
       +char        *untilstr = 0;                /* string that terminates a thru */
       +int        thru        = 0;                /* 1 if copying thru macro */
       +struct symtab        *thrudef = 0;                /* macro being used */
       +
       +void
       +copyfile(char *s)        /* remember file to start reading from */
       +{
       +        newfile = s;
       +}
       +
       +void
       +copydef(struct symtab *p)        /* remember macro symtab ptr */
       +{
       +        thrudef = p;
       +}
       +
       +struct symtab*
       +copythru(char *s)        /* collect the macro name or body for thru */
       +{
       +        struct symtab *p;
       +        char *q, *addnewline();
       +
       +        p = lookup(s);
       +        if (p != NULL) {
       +                if (p->s_type == DEFNAME) {
       +                        p->s_val.p = addnewline(p->s_val.p);
       +                        return p;
       +                } else
       +                        ERROR "%s used as define and name", s FATAL;
       +        }
       +        /* have to collect the definition */
       +        pbstr(s);        /* first char is the delimiter */
       +        q = delimstr("thru body");
       +        s = "nameless";
       +        p = lookup(s);
       +        if (p != NULL) {
       +                if (p->s_val.p)
       +                        free(p->s_val.p);
       +                p->s_val.p = q;
       +        } else {
       +                YYSTYPE u;
       +                u.p = q;
       +                p = makevar(tostring(s), DEFNAME, u);
       +        }
       +        p->s_val.p = addnewline(p->s_val.p);
       +        dprintf("installing %s as `%s'\n", s, p->s_val.p);
       +        return p;
       +}
       +
       +char*
       +addnewline(char *p)        /* add newline to end of p */
       +{
       +        int n;
       +
       +        n = strlen(p);
       +        if (p[n-1] != '\n') {
       +                p = realloc(p, n+2);
       +                p[n] = '\n';
       +                p[n+1] = '\0';
       +        }
       +        return p;
       +}
       +
       +void
       +copyuntil(char *s)        /* string that terminates a thru */
       +{
       +        untilstr = s;
       +}
       +
       +void
       +copy(void)        /* begin input from file, etc. */
       +{
       +        FILE *fin;
       +
       +        if (newfile) {
       +                if ((fin = fopen(newfile, "r")) == NULL)
       +                        ERROR "can't open file %s", newfile FATAL;
       +                curfile++;
       +                curfile->fin = fin;
       +                curfile->fname = newfile;
       +                curfile->lineno = 0;
       +                printlf(1, curfile->fname);
       +                pushsrc(File, curfile->fname);
       +                newfile = 0;
       +        }
       +        if (thrudef) {
       +                thru = 1;
       +                begin = 1;        /* wrong place */
       +        }
       +}
       +
       +char        shellbuf[1000], *shellp;
       +
       +void
       +shell_init(void)        /* set up to interpret a shell command */
       +{
       +        sprintf(shellbuf, "sh -c '");
       +        shellp = shellbuf + strlen(shellbuf);
       +}
       +
       +void
       +shell_text(char *s)        /* add string to command being collected */
       +{
       +        while (*shellp++ = *s++)
       +                ;
       +        shellp--;
       +}
       +
       +void
       +shell_exec(void)        /* do it */
       +{
       +        *shellp++ = '\'';
       +        *shellp = '\0';
       +        system(shellbuf);
       +}
 (DIR) diff --git a/src/cmd/tpic/linegen.c b/src/cmd/tpic/linegen.c
       t@@ -0,0 +1,210 @@
       +#include        <stdio.h>
       +#include        "pic.h"
       +#include        "y.tab.h"
       +
       +obj*
       +linegen(int type)
       +{
       +        static double prevdx = HT;
       +        static double prevdy = 0;
       +        static double prevw = HT10;
       +        static double prevh = HT5;
       +        int i, j, some, head, ddtype, invis, chop;
       +        double ddval, chop1, chop2, x0, y0, x1, y1;
       +        double sin(), cos(), atan2(), theta;
       +        double defx, defy;
       +        obj *p, *ppos;
       +        static int xtab[] = { 1, 0, -1, 0 };        /* R=0, U=1, L=2, D=3 */
       +        static int ytab[] = { 0, 1, 0, -1 };
       +        double dx[500], dy[500];
       +        int ndxy;
       +        double nx, ny;
       +        Attr *ap;
       +
       +        nx = curx;
       +        ny = cury;
       +        defx = getfval("linewid");
       +        defy = getfval("lineht");
       +        prevh = getfval("arrowht");
       +        prevw = getfval("arrowwid");
       +        dx[0] = dy[0] = ndxy = some = head = invis = 0;
       +        chop = chop1 = chop2 = 0;
       +        ddtype = ddval = 0;
       +        for (i = 0; i < nattr; i++) {
       +                ap = &attr[i];
       +                switch (ap->a_type) {
       +                case TEXTATTR:
       +                        savetext(ap->a_sub, ap->a_val.p);
       +                        break;
       +                case HEAD:
       +                        head += ap->a_val.i;
       +                        break;
       +                case INVIS:
       +                        invis = INVIS;
       +                        break;
       +                case CHOP:
       +                        if (chop++ == 0)
       +                                chop1 = chop2 = ap->a_val.f;
       +                        else
       +                                chop2 = ap->a_val.f;
       +                        break;
       +                case DOT:
       +                case DASH:
       +                        ddtype = ap->a_type==DOT ? DOTBIT : DASHBIT;
       +                        if (ap->a_sub == DEFAULT)
       +                                ddval = getfval("dashwid");
       +                        else
       +                                ddval = ap->a_val.f;
       +                        break;
       +                case SAME:
       +                        dx[ndxy] = prevdx;
       +                        dy[ndxy] = prevdy;
       +                        some++;
       +                        break;
       +                case LEFT:
       +                        dx[ndxy] -= (ap->a_sub==DEFAULT) ? defx : ap->a_val.f;
       +                        some++;
       +                        hvmode = L_DIR;
       +                        break;
       +                case RIGHT:
       +                        dx[ndxy] += (ap->a_sub==DEFAULT) ? defx : ap->a_val.f;
       +                        some++;
       +                        hvmode = R_DIR;
       +                        break;
       +                case UP:
       +                        dy[ndxy] += (ap->a_sub==DEFAULT) ? defy : ap->a_val.f;
       +                        some++;
       +                        hvmode = U_DIR;
       +                        break;
       +                case DOWN:
       +                        dy[ndxy] -= (ap->a_sub==DEFAULT) ? defy : ap->a_val.f;
       +                        some++;
       +                        hvmode = D_DIR;
       +                        break;
       +                case HEIGHT:        /* length of arrowhead */
       +                        prevh = ap->a_val.f;
       +                        break;
       +                case WIDTH:        /* width of arrowhead */
       +                        prevw = ap->a_val.f;
       +                        break;
       +                case TO:
       +                        if (some) {
       +                                nx += dx[ndxy];
       +                                ny += dy[ndxy];
       +                                ndxy++;
       +                                dx[ndxy] = dy[ndxy] = some = 0;
       +                        }
       +                        ppos = attr[i].a_val.o;
       +                        dx[ndxy] = ppos->o_x - nx;
       +                        dy[ndxy] = ppos->o_y - ny;
       +                        some++;
       +                        break;
       +                case BY:
       +                        if (some) {
       +                                nx += dx[ndxy];
       +                                ny += dy[ndxy];
       +                                ndxy++;
       +                                dx[ndxy] = dy[ndxy] = some = 0;
       +                        }
       +                        ppos = ap->a_val.o;
       +                        dx[ndxy] = ppos->o_x;
       +                        dy[ndxy] = ppos->o_y;
       +                        some++;
       +                        break;
       +                case THEN:        /* turn off any previous accumulation */
       +                        if (some) {
       +                                nx += dx[ndxy];
       +                                ny += dy[ndxy];
       +                                ndxy++;
       +                                dx[ndxy] = dy[ndxy] = some = 0;
       +                        }
       +                        break;
       +                case FROM:
       +                case AT:
       +                        ppos = ap->a_val.o;
       +                        nx = curx = ppos->o_x;
       +                        ny = cury = ppos->o_y;
       +                        break;
       +                }
       +        }
       +        if (some) {
       +                nx += dx[ndxy];
       +                ny += dy[ndxy];
       +                ndxy++;
       +                defx = dx[ndxy-1];
       +                defy = dy[ndxy-1];
       +        } else {
       +                defx *= xtab[hvmode];
       +                defy *= ytab[hvmode];
       +                dx[ndxy] = defx;
       +                dy[ndxy] = defy;
       +                ndxy++;
       +                nx += defx;
       +                ny += defy;
       +        }
       +        prevdx = defx;
       +        prevdy = defy;
       +        if (chop) {
       +                if (chop == 1 && chop1 == 0)        /* just said "chop", so use default */
       +                        chop1 = chop2 = getfval("circlerad");
       +                theta = atan2(dy[0], dx[0]);
       +                x0 = chop1 * cos(theta);
       +                y0 = chop1 * sin(theta);
       +                curx += x0;
       +                cury += y0;
       +                dx[0] -= x0;
       +                dy[0] -= y0;
       +
       +                theta = atan2(dy[ndxy-1], dx[ndxy-1]);
       +                x1 = chop2 * cos(theta);
       +                y1 = chop2 * sin(theta);
       +                nx -= x1;
       +                ny -= y1;
       +                dx[ndxy-1] -= x1;
       +                dy[ndxy-1] -= y1;
       +                dprintf("chopping %g %g %g %g; cur=%g,%g end=%g,%g\n",
       +                        x0, y0, x1, y1, curx, cury, nx, ny);
       +        }
       +        p = makenode(type, 5 + 2 * ndxy);
       +        curx = p->o_val[0] = nx;
       +        cury = p->o_val[1] = ny;
       +        if (head || type == ARROW) {
       +                p->o_nhead = getfval("arrowhead");
       +                p->o_val[2] = prevw;
       +                p->o_val[3] = prevh;
       +                if (head == 0)
       +                        head = HEAD2;        /* default arrow head */
       +        }
       +        p->o_attr = head | invis | ddtype;
       +        p->o_val[4] = ndxy;
       +        nx = p->o_x;
       +        ny = p->o_y;
       +        for (i = 0, j = 5; i < ndxy; i++, j += 2) {
       +                p->o_val[j] = dx[i];
       +                p->o_val[j+1] = dy[i];
       +                if (type == LINE || type == ARROW)
       +                        extreme(nx += dx[i], ny += dy[i]);
       +                else if (type == SPLINE && i < ndxy-1) {
       +                        /* to compute approx extreme of spline at p,
       +                        /* compute midway between p-1 and p+1,
       +                        /* then go 3/4 from there to p */
       +                        double ex, ey, xi, yi, xi1, yi1;
       +                        xi = nx + dx[i]; yi = ny + dy[i];        /* p */
       +                        xi1 = xi + dx[i+1]; yi1 = yi + dy[i+1];        /* p+1 */
       +                        ex = (nx+xi1)/2; ey = (ny+yi1)/2;        /* midway */
       +                        ex += 0.75*(xi-ex); ey += 0.75*(yi-ey);
       +                        extreme(ex, ey);
       +                        nx = xi; ny = yi;
       +                }
       +                        
       +        }
       +        p->o_ddval = ddval;
       +        if (dbg) {
       +                printf("S or L from %g %g to %g %g with %d elements:\n", p->o_x, p->o_y, curx, cury, ndxy);
       +                for (i = 0, j = 5; i < ndxy; i++, j += 2)
       +                        printf("%g %g\n", p->o_val[j], p->o_val[j+1]);
       +        }
       +        extreme(p->o_x, p->o_y);
       +        extreme(curx, cury);
       +        return(p);
       +}
 (DIR) diff --git a/src/cmd/tpic/main.c b/src/cmd/tpic/main.c
       t@@ -0,0 +1,277 @@
       +#include        <stdio.h>
       +#include        <signal.h>
       +#include        "pic.h"
       +#include        "y.tab.h"
       +
       +obj        **objlist = 0;                /* store the elements here */
       +int        nobjlist = 0;                /* size of objlist array */
       +int        nobj        = 0;
       +
       +Attr        *attr;        /* attributes stored here as collected */
       +int        nattrlist = 0;
       +int        nattr        = 0;        /* number of entries in attr_list */
       +
       +Text        *text        = 0;        /* text strings stored here as collected */
       +int        ntextlist = 0;                /* size of text[] array */
       +int        ntext        = 0;
       +int        ntext1        = 0;        /* record ntext here on entry to each figure */
       +
       +double        curx        = 0;
       +double        cury        = 0;
       +
       +int        hvmode        = R_DIR;        /* R => join left to right, D => top to bottom, etc. */
       +
       +int        codegen        = 0;        /* 1=>output for this picture; 0=>no output */
       +int        PEseen        = 0;        /* 1=> PE seen during parsing */
       +
       +double        deltx        = 6;        /* max x value in output, for scaling */
       +double        delty        = 6;        /* max y value in output, for scaling */
       +int        dbg        = 0;
       +int        lineno        = 0;
       +char        *filename        = "-";
       +int        synerr        = 0;
       +int        anyerr        = 0;        /* becomes 1 if synerr ever 1 */
       +char        *cmdname;
       +
       +double        xmin        = 30000;        /* min values found in actual data */
       +double        ymin        = 30000;
       +double        xmax        = -30000;        /* max */
       +double        ymax        = -30000;
       +
       +int
       +main(int argc, char **argv)
       +{
       +        char buf[20];
       +        extern void fpecatch(int);
       +
       +        signal(SIGFPE, fpecatch);
       +        cmdname = argv[0];
       +        while (argc > 1 && *argv[1] == '-') {
       +                switch (argv[1][1]) {
       +                case 'd':
       +                        dbg = atoi(&argv[1][2]);
       +                        if (dbg == 0)
       +                                dbg = 1;
       +                        break;
       +                }
       +                argc--;
       +                argv++;
       +        }
       +        setdefaults();
       +        objlist = (obj **) grow((char *)objlist, "objlist", nobjlist += 1000, sizeof(obj *));
       +        text = (Text *) grow((char *)text, "text", ntextlist += 1000, sizeof(Text));
       +        attr = (Attr *) grow((char *)attr, "attr", nattrlist += 100, sizeof(Attr));
       +
       +        sprintf(buf, "/%d/", getpid());
       +        pushsrc(String, buf);
       +        definition("pid");
       +
       +        curfile = infile;
       +        pushsrc(File, curfile->fname);
       +        if (argc <= 1) {
       +                curfile->fin = stdin;
       +                curfile->fname = tostring("-");
       +                getdata();
       +        } else
       +                while (argc-- > 1) {
       +                        if ((curfile->fin = fopen(*++argv, "r")) == NULL) {
       +                                fprintf(stderr, "%s: can't open %s\n", cmdname, *argv);
       +                                exit(1);
       +                        }
       +                        curfile->fname = tostring(*argv);
       +                        getdata();
       +                        fclose(curfile->fin);
       +                        free(curfile->fname);
       +                }
       +        exit(anyerr);
       +        return 0;
       +}
       +
       +void
       +fpecatch(int arg)
       +{
       +        ERROR "floating point exception" FATAL;
       +}
       +
       +char *
       +grow(char *ptr, char *name, int num, int size)        /* make array bigger */
       +{
       +        char *p;
       +
       +        if (ptr == NULL)
       +                p = malloc(num * size);
       +        else
       +                p = realloc(ptr, num * size);
       +        if (p == NULL)
       +                ERROR "can't grow %s to %d", name, num * size FATAL;
       +        return p;
       +}
       +
       +static struct {
       +        char *name;
       +        double val;
       +        short scalable;                /* 1 => adjust when "scale" changes */
       +} defaults[] ={
       +        "scale", SCALE, 1,
       +        "lineht", HT, 1,
       +        "linewid", HT, 1,
       +        "moveht", HT, 1,
       +        "movewid", HT, 1,
       +        "dashwid", HT10, 1,
       +        "boxht", HT, 1,
       +        "boxwid", WID, 1,
       +        "circlerad", HT2, 1,
       +        "arcrad", HT2, 1,
       +        "ellipseht", HT, 1,
       +        "ellipsewid", WID, 1,
       +        "arrowht", HT5, 1,
       +        "arrowwid", HT10, 1,
       +        "arrowhead", 2, 0,                /* arrowhead style */
       +        "textht", 0.0, 1,                /* 6 lines/inch is also a useful value */
       +        "textwid", 0.0, 1,
       +        "maxpsht", MAXHT, 0,
       +        "maxpswid", MAXWID, 0,
       +        "fillval", 0.3, 0,                /* gray value for filling boxes */
       +        NULL, 0, 0
       +};
       +
       +void
       +setdefaults(void)        /* set default sizes for variables like boxht */
       +{
       +        int i;
       +        YYSTYPE v;
       +
       +        for (i = 0; defaults[i].name != NULL; i++) {
       +                v.f = defaults[i].val;
       +                makevar(tostring(defaults[i].name), VARNAME, v);
       +        }
       +}
       +
       +void
       +resetvar(void)        /* reset variables listed */
       +{
       +        int i, j;
       +
       +        if (nattr == 0) {        /* none listed, so do all */
       +                setdefaults();
       +                return;
       +        }
       +        for (i = 0; i < nattr; i++) {
       +                for (j = 0; defaults[j].name != NULL; j++)
       +                        if (strcmp(defaults[j].name, attr[i].a_val.p) == 0) {
       +                                setfval(defaults[j].name, defaults[j].val);
       +                                free(attr[i].a_val.p);
       +                                break;
       +                        }
       +        }
       +}
       +
       +void
       +checkscale(char *s)        /* if s is "scale", adjust default variables */
       +{
       +        int i;
       +        double scale;
       +
       +        if (strcmp(s, "scale") == 0) {
       +                scale = getfval("scale");
       +                for (i = 1; defaults[i].name != NULL; i++)
       +                        if (defaults[i].scalable)
       +                                setfval(defaults[i].name, defaults[i].val * scale);
       +        }
       +}
       +
       +void
       +getdata(void)
       +{
       +        char *p, buf[1000], buf1[100];
       +        int ln;
       +
       +        curfile->lineno = 0;
       +        printlf(1, curfile->fname);
       +        while (fgets(buf, sizeof buf, curfile->fin) != NULL) {
       +                curfile->lineno++;
       +                if (*buf == '.' && *(buf+1) == 'P' && *(buf+2) == 'S') {
       +                        for (p = &buf[3]; *p == ' '; p++)
       +                                ;
       +                        if (*p++ == '<') {
       +                                Infile svfile;
       +                                svfile = *curfile;
       +                                sscanf(p, "%s", buf1);
       +                                if ((curfile->fin=fopen(buf1, "r")) == NULL)
       +                                        ERROR "can't open %s", buf1 FATAL;
       +                                curfile->fname = tostring(buf1);
       +                                getdata();
       +                                fclose(curfile->fin);
       +                                free(curfile->fname);
       +                                *curfile = svfile;
       +                                printlf(curfile->lineno, curfile->fname);
       +                                continue;
       +                        }
       +                        reset();
       +                        yyparse();
       +                        anyerr += synerr;
       +                        /* yylval.i now contains 'E' or 'F' from .PE or .PF */
       +
       +                        deltx = (xmax - xmin) / getfval("scale");
       +                        delty = (ymax - ymin) / getfval("scale");
       +                        if (buf[3] == ' ') {        /* next things are wid & ht */
       +                                if (sscanf(&buf[4],"%lf %lf", &deltx, &delty) < 2)
       +                                        delty = deltx * (ymax-ymin) / (xmax-xmin);
       +                                /* else {
       +                                /*        double xfac, yfac; */
       +                                /*        xfac = deltx / (xmax-xmin);
       +                                /*        yfac = delty / (ymax-ymin);
       +                                /*        if (xfac <= yfac)
       +                                /*                delty = xfac * (ymax-ymin);
       +                                /*        else
       +                                /*                deltx = yfac * (xmax-xmin);
       +                                /*}
       +                                */
       +                        }
       +                        dprintf("deltx = %g, delty = %g\n", deltx, delty);
       +                        if (codegen && !synerr) {
       +                                openpl();        /* puts out .PS, with ht & wid stuck in */
       +                                printlf(curfile->lineno+1, NULL);
       +                                print();        /* assumes \n at end */
       +                                closepl();        /* does the .PE/F */
       +                        }
       +                        printlf(curfile->lineno+1, NULL);
       +                        fflush(stdout);
       +                } else if (buf[0] == '.' && buf[1] == 'l' && buf[2] == 'f') {
       +                        if (sscanf(buf+3, "%d %s", &ln, buf1) == 2) {
       +                                free(curfile->fname);
       +                                printlf(curfile->lineno = ln, curfile->fname = tostring(buf1));
       +                        } else
       +                                printlf(curfile->lineno = ln, NULL);
       +                } else
       +                        fputs(buf, stdout);
       +        }
       +}
       +
       +void
       +reset(void)
       +{
       +        obj *op;
       +        int i;
       +        extern int nstack;
       +
       +        for (i = 0; i < nobj; i++) {
       +                op = objlist[i];
       +                if (op->o_type == BLOCK)
       +                        freesymtab(op->o_symtab);
       +                free((char *)objlist[i]);
       +        }
       +        nobj = 0;
       +        nattr = 0;
       +        for (i = 0; i < ntext; i++)
       +                if (text[i].t_val)
       +                        free(text[i].t_val);
       +        ntext = ntext1 = 0;
       +        codegen = synerr = 0;
       +        nstack = 0;
       +        curx = cury = 0;
       +        PEseen = 0;
       +        hvmode = R_DIR;
       +        xmin = ymin = 30000;
       +        xmax = ymax = -30000;
       +}
 (DIR) diff --git a/src/cmd/tpic/misc.c b/src/cmd/tpic/misc.c
       t@@ -0,0 +1,463 @@
       +#include        <stdio.h>
       +#include        <string.h>
       +#include        <math.h>
       +#include        "pic.h"
       +#include        "y.tab.h"
       +
       +int
       +setdir(int n)        /* set direction (hvmode) from LEFT, RIGHT, etc. */
       +{
       +        switch (n) {
       +        case UP:        hvmode = U_DIR; break;
       +        case DOWN:        hvmode = D_DIR; break;
       +        case LEFT:        hvmode = L_DIR; break;
       +        case RIGHT:        hvmode = R_DIR; break;
       +        }
       +         return(hvmode);
       +}
       +
       +int
       +curdir(void)        /* convert current dir (hvmode) to RIGHT, LEFT, etc. */
       +{
       +        switch (hvmode) {
       +        case R_DIR:        return RIGHT;
       +        case L_DIR:        return LEFT;
       +        case U_DIR:        return UP;
       +        case D_DIR:        return DOWN;
       +        }
       +        ERROR "can't happen curdir" FATAL;
       +        return 0;
       +}
       +
       +double
       +getcomp(obj *p, int t)        /* return component of a position */
       +{
       +        switch (t) {
       +        case DOTX:
       +                return p->o_x;
       +        case DOTY:
       +                return p->o_y;
       +        case DOTWID:
       +                switch (p->o_type) {
       +                case BOX:
       +                case BLOCK:
       +                case TEXT:
       +                        return p->o_val[0];
       +                case CIRCLE:
       +                case ELLIPSE:
       +                        return 2 * p->o_val[0];
       +                case LINE:
       +                case ARROW:
       +                        return p->o_val[0] - p->o_x;
       +                case PLACE:
       +                        return 0;
       +                }
       +        case DOTHT:
       +                switch (p->o_type) {
       +                case BOX:
       +                case BLOCK:
       +                case TEXT:
       +                        return p->o_val[1];
       +                case CIRCLE:
       +                case ELLIPSE:
       +                        return 2 * p->o_val[1];
       +                case LINE:
       +                case ARROW:
       +                        return p->o_val[1] - p->o_y;
       +                case PLACE:
       +                        return 0;
       +                }
       +        case DOTRAD:
       +                switch (p->o_type) {
       +                case CIRCLE:
       +                case ELLIPSE:
       +                        return p->o_val[0];
       +                }
       +        }
       +        ERROR "you asked for a weird dimension or position" WARNING;
       +        return 0;
       +}
       +
       +double        exprlist[100];
       +int        nexpr        = 0;
       +
       +void
       +exprsave(double f)
       +{
       +        exprlist[nexpr++] = f;
       +}
       +
       +char*
       +sprintgen(char *fmt)
       +{
       +        char buf[1000];
       +
       +        sprintf(buf, fmt, exprlist[0], exprlist[1], exprlist[2], exprlist[3], exprlist[4]);
       +        nexpr = 0;
       +        free(fmt);
       +        return tostring(buf);
       +}
       +
       +void
       +makefattr(int type, int sub, double f)        /* double attr */
       +{
       +        YYSTYPE val;
       +        val.f = f;
       +        makeattr(type, sub, val);
       +}
       +
       +void
       +makeoattr(int type, obj *o)        /* obj* attr */
       +{
       +        YYSTYPE val;
       +        val.o = o;
       +        makeattr(type, 0, val);
       +}
       +
       +void
       +makeiattr(int type, int i)        /* int attr */
       +{
       +        YYSTYPE val;
       +        val.i = i;
       +        makeattr(type, 0, val);
       +}
       +
       +void
       +maketattr(int sub, char *p)        /* text attribute: takes two */
       +{
       +        YYSTYPE val;
       +        val.p = p;
       +        makeattr(TEXTATTR, sub, val);
       +}
       +
       +void
       +addtattr(int sub)                /* add text attrib to existing item */
       +{
       +        attr[nattr-1].a_sub |= sub;
       +}
       +
       +void
       +makevattr(char *p)        /* varname attribute */
       +{
       +        YYSTYPE val;
       +        val.p = p;
       +        makeattr(VARNAME, 0, val);
       +}
       +
       +void
       +makeattr(int type, int sub, YYSTYPE val)        /* add attribute type and val */
       +{
       +        if (type == 0 && val.i == 0) {        /* clear table for next stat */
       +                nattr = 0;
       +                return;
       +        }
       +        if (nattr >= nattrlist)
       +                attr = (Attr *) grow((char *)attr, "attr", nattrlist += 100, sizeof(Attr));
       +        dprintf("attr %d:  %d %d %d\n", nattr, type, sub, val.i);
       +        attr[nattr].a_type = type;
       +        attr[nattr].a_sub = sub;
       +        attr[nattr].a_val = val;
       +        nattr++;
       +}
       +
       +void
       +printexpr(double f)        /* print expression for debugging */
       +{
       +        printf("%g\n", f);
       +}
       +
       +void
       +printpos(obj *p)        /* print position for debugging */
       +{
       +        printf("%g, %g\n", p->o_x, p->o_y);
       +}
       +
       +char*
       +tostring(char *s)
       +{
       +        char *p;
       +
       +        p = malloc(strlen(s)+1);
       +        if (p == NULL)
       +                ERROR "out of space in tostring on %s", s FATAL;
       +        strcpy(p, s);
       +        return(p);
       +}
       +
       +obj*
       +makepos(double x, double y)        /* make a position cell */
       +{
       +        obj *p;
       +
       +        p = makenode(PLACE, 0);
       +        p->o_x = x;
       +        p->o_y = y;
       +        return(p);
       +}
       +
       +obj*
       +makebetween(double f, obj *p1, obj* p2)        /* make position between p1 and p2 */
       +{
       +        obj *p;
       +
       +        dprintf("fraction = %.2f\n", f);
       +        p = makenode(PLACE, 0);
       +        p->o_x = p1->o_x + f * (p2->o_x - p1->o_x);
       +        p->o_y = p1->o_y + f * (p2->o_y - p1->o_y);
       +        return(p);
       +}
       +
       +obj*
       +getpos(obj *p, int corner)        /* find position of point */
       +{
       +        double x, y;
       +
       +        whatpos(p, corner, &x, &y);
       +        return makepos(x, y);
       +}
       +
       +int
       +whatpos(obj *p, int corner, double *px, double *py)        /* what is the position (no side effect) */
       +{
       +        double x, y, x1, y1;
       +
       +        dprintf("whatpos %p %d %d\n", p, p->o_type, corner);
       +        x = p->o_x;
       +        y = p->o_y;
       +        x1 = 0;
       +        y1 = 0;
       +        if (p->o_type != PLACE) {
       +                x1 = p->o_val[0];
       +                y1 = p->o_val[1];
       +        }
       +        switch (p->o_type) {
       +        case PLACE:
       +                break;
       +        case BOX:
       +        case BLOCK:
       +        case TEXT:
       +                switch (corner) {
       +                case NORTH:        y += y1 / 2; break;
       +                case SOUTH:        y -= y1 / 2; break;
       +                case EAST:        x += x1 / 2; break;
       +                case WEST:        x -= x1 / 2; break;
       +                case NE:        x += x1 / 2; y += y1 / 2; break;
       +                case SW:        x -= x1 / 2; y -= y1 / 2; break;
       +                case SE:        x += x1 / 2; y -= y1 / 2; break;
       +                case NW:        x -= x1 / 2; y += y1 / 2; break;
       +                case START:
       +                        if (p->o_type == BLOCK)
       +                                return whatpos(objlist[(int)p->o_val[2]], START, px, py);
       +                case END:
       +                        if (p->o_type == BLOCK)
       +                                return whatpos(objlist[(int)p->o_val[3]], END, px, py);
       +                }
       +                break;
       +        case ARC:
       +                switch (corner) {
       +                case START:
       +                        if (p->o_attr & CW_ARC) {
       +                                x = p->o_val[2]; y = p->o_val[3];
       +                        } else {
       +                                x = x1; y = y1;
       +                        }
       +                        break;
       +                case END:
       +                        if (p->o_attr & CW_ARC) {
       +                                x = x1; y = y1;
       +                        } else {
       +                                x = p->o_val[2]; y = p->o_val[3];
       +                        }
       +                        break;
       +                }
       +                if (corner == START || corner == END)
       +                        break;
       +                x1 = y1 = sqrt((x1-x)*(x1-x) + (y1-y)*(y1-y));
       +                /* Fall Through! */
       +        case CIRCLE:
       +        case ELLIPSE:
       +                switch (corner) {
       +                case NORTH:        y += y1; break;
       +                case SOUTH:        y -= y1; break;
       +                case EAST:        x += x1; break;
       +                case WEST:        x -= x1; break;
       +                case NE:        x += 0.707 * x1; y += 0.707 * y1; break;
       +                case SE:        x += 0.707 * x1; y -= 0.707 * y1; break;
       +                case NW:        x -= 0.707 * x1; y += 0.707 * y1; break;
       +                case SW:        x -= 0.707 * x1; y -= 0.707 * y1; break;
       +                }
       +                break;
       +        case LINE:
       +        case SPLINE:
       +        case ARROW:
       +                switch (corner) {
       +                case START:        break;        /* already in place */
       +                case END:        x = x1; y = y1; break;
       +                default: /* change! */
       +                case CENTER:        x = (x+x1)/2; y = (y+y1)/2; break;
       +                case NORTH:        if (y1 > y) { x = x1; y = y1; } break;
       +                case SOUTH:        if (y1 < y) { x = x1; y = y1; } break;
       +                case EAST:        if (x1 > x) { x = x1; y = y1; } break;
       +                case WEST:        if (x1 < x) { x = x1; y = y1; } break;
       +                }
       +                break;
       +        case MOVE:
       +                /* really ought to be same as line... */
       +                break;
       +        }
       +        dprintf("whatpos returns %g %g\n", x, y);
       +        *px = x;
       +        *py = y;
       +        return 1;
       +}
       +
       +obj*
       +gethere(void)        /* make a place for curx,cury */
       +{
       +        dprintf("gethere %g %g\n", curx, cury);
       +        return(makepos(curx, cury));
       +}
       +
       +obj*
       +getlast(int n, int t)        /* find n-th previous occurrence of type t */
       +{
       +        int i, k;
       +        obj *p;
       +
       +        k = n;
       +        for (i = nobj-1; i >= 0; i--) {
       +                p = objlist[i];
       +                if (p->o_type == BLOCKEND) {
       +                        i = p->o_val[4];
       +                        continue;
       +                }
       +                if (p->o_type != t)
       +                        continue;
       +                if (--k > 0)
       +                        continue;        /* not there yet */
       +                dprintf("got a last of x,y= %g,%g\n", p->o_x, p->o_y);
       +                return(p);
       +        }
       +        ERROR "there is no %dth last", n WARNING;
       +        return(NULL);
       +}
       +
       +obj*
       +getfirst(int n, int t)        /* find n-th occurrence of type t */
       +{
       +        int i, k;
       +        obj *p;
       +
       +        k = n;
       +        for (i = 0; i < nobj; i++) {
       +                p = objlist[i];
       +                if (p->o_type == BLOCK && t != BLOCK) {        /* skip whole block */
       +                        i = p->o_val[5] + 1;
       +                        continue;
       +                }
       +                if (p->o_type != t)
       +                        continue;
       +                if (--k > 0)
       +                        continue;        /* not there yet */
       +                dprintf("got a first of x,y= %g,%g\n", p->o_x, p->o_y);
       +                return(p);
       +        }
       +        ERROR "there is no %dth ", n WARNING;
       +        return(NULL);
       +}
       +
       +double
       +getblkvar(obj *p, char *s)        /* find variable s2 in block p */
       +{
       +        YYSTYPE y, getblk();
       +
       +        y = getblk(p, s);
       +        return y.f;
       +}
       +
       +obj*
       +getblock(obj *p, char *s)        /* find variable s in block p */
       +{
       +        YYSTYPE y, getblk();
       +
       +        y = getblk(p, s);
       +        return y.o;
       +}
       +
       +YYSTYPE
       +getblk(obj *p, char *s)        /* find union type for s in p */
       +{
       +        static YYSTYPE bug;
       +        struct symtab *stp;
       +
       +        if (p->o_type != BLOCK) {
       +                ERROR ".%s is not in that block", s WARNING;
       +                return(bug);
       +        }
       +        for (stp = p->o_symtab; stp != NULL; stp = stp->s_next)
       +                if (strcmp(s, stp->s_name) == 0) {
       +                        dprintf("getblk %s found x,y= %g,%g\n",
       +                                s, (stp->s_val.o)->o_x, (stp->s_val.o)->o_y);
       +                        return(stp->s_val);
       +                }
       +        ERROR "there is no .%s in that []", s WARNING;
       +        return(bug);
       +}
       +
       +obj*
       +fixpos(obj *p, double x, double y)
       +{
       +        dprintf("fixpos returns %g %g\n", p->o_x + x, p->o_y + y);
       +        return makepos(p->o_x + x, p->o_y + y);
       +}
       +
       +obj*
       +addpos(obj *p, obj *q)
       +{
       +        dprintf("addpos returns %g %g\n", p->o_x+q->o_x, p->o_y+q->o_y);
       +        return makepos(p->o_x+q->o_x, p->o_y+q->o_y);
       +}
       +
       +obj*
       +subpos(obj *p, obj *q)
       +{
       +        dprintf("subpos returns %g %g\n", p->o_x-q->o_x, p->o_y-q->o_y);
       +        return makepos(p->o_x-q->o_x, p->o_y-q->o_y);
       +}
       +
       +obj*
       +makenode(int type, int n)
       +{
       +        obj *p;
       +
       +        p = (obj *) calloc(1, sizeof(obj) + (n-1)*sizeof(ofloat));
       +        if (p == NULL)
       +                ERROR "out of space in makenode" FATAL;
       +        p->o_type = type;
       +        p->o_count = n;
       +        p->o_nobj = nobj;
       +        p->o_mode = hvmode;
       +        p->o_x = curx;
       +        p->o_y = cury;
       +        p->o_nt1 = ntext1;
       +        p->o_nt2 = ntext;
       +        ntext1 = ntext;        /* ready for next caller */
       +        if (nobj >= nobjlist)
       +                objlist = (obj **) grow((char *) objlist, "objlist",
       +                        nobjlist *= 2, sizeof(obj *));
       +        objlist[nobj++] = p;
       +        return(p);
       +}
       +
       +void
       +extreme(double x, double y)        /* record max and min x and y values */
       +{
       +        if (x > xmax)
       +                xmax = x;
       +        if (y > ymax)
       +                ymax = y;
       +        if (x < xmin)
       +                xmin = x;
       +        if (y < ymin)
       +                ymin = y;
       +}
 (DIR) diff --git a/src/cmd/tpic/mkfile b/src/cmd/tpic/mkfile
       t@@ -0,0 +1,39 @@
       +<$PLAN9/src/mkhdr
       +
       +TARG=tpic
       +YFILES=picy.y
       +
       +OFILES=\
       +        arcgen.$O\
       +        blockgen.$O\
       +        boxgen.$O\
       +        circgen.$O\
       +        for.$O\
       +        input.$O\
       +        linegen.$O\
       +        main.$O\
       +        misc.$O\
       +        movegen.$O\
       +        picl.$O\
       +        picy.$O\
       +        pltex.$O\
       +        print.$O\
       +        symtab.$O\
       +        tex.$O\
       +        textgen.$O\
       +
       +HFILES=pic.h y.tab.h tex.h
       +
       +<$PLAN9/src/mkone
       +
       +YFLAGS=-d -S
       +
       +picy.c: y.tab.c
       +        mv y.tab.c picy.c
       +
       +picl.$O: picl.l
       +        9 lex  picl.l
       +        $CC $CFLAGS lex.yy.c
       +        rm lex.yy.c
       +        mv lex.yy.$O picl.$O
       +
 (DIR) diff --git a/src/cmd/tpic/movegen.c b/src/cmd/tpic/movegen.c
       t@@ -0,0 +1,87 @@
       +#include        <stdio.h>
       +#include        "pic.h"
       +#include        "y.tab.h"
       +
       +obj*
       +movegen(void)
       +{
       +        static double prevdx, prevdy;
       +        int i, some;
       +        double defx, defy, dx, dy;
       +        obj *p;
       +        obj *ppos;
       +        static int xtab[] = { 1, 0, -1, 0 };        /* R=0, U=1, L=2, D=3 */
       +        static int ytab[] = { 0, 1, 0, -1 };
       +        Attr *ap;
       +
       +        defx = getfval("movewid");
       +        defy = getfval("moveht");
       +        dx = dy = some = 0;
       +        for (i = 0; i < nattr; i++) {
       +                ap = &attr[i];
       +                switch (ap->a_type) {
       +                case TEXTATTR:
       +                        savetext(ap->a_sub, ap->a_val.p);
       +                        break;
       +                case SAME:
       +                        dx = prevdx;
       +                        dy = prevdy;
       +                        some++;
       +                        break;
       +                case LEFT:
       +                        dx -= (ap->a_sub==DEFAULT) ? defx : ap->a_val.f;
       +                        some++;
       +                        hvmode = L_DIR;
       +                        break;
       +                case RIGHT:
       +                        dx += (ap->a_sub==DEFAULT) ? defx : ap->a_val.f;
       +                        some++;
       +                        hvmode = R_DIR;
       +                        break;
       +                case UP:
       +                        dy += (ap->a_sub==DEFAULT) ? defy : ap->a_val.f;
       +                        some++;
       +                        hvmode = U_DIR;
       +                        break;
       +                case DOWN:
       +                        dy -= (ap->a_sub==DEFAULT) ? defy : ap->a_val.f;
       +                        some++;
       +                        hvmode = D_DIR;
       +                        break;
       +                case TO:
       +                        ppos = ap->a_val.o;
       +                        dx = ppos->o_x - curx;
       +                        dy = ppos->o_y - cury;
       +                        some++;
       +                        break;
       +                case BY:
       +                        ppos = ap->a_val.o;
       +                        dx = ppos->o_x;
       +                        dy = ppos->o_y;
       +                        some++;
       +                        break;
       +                case FROM:
       +                case AT:
       +                        ppos = ap->a_val.o;
       +                        curx = ppos->o_x;
       +                        cury = ppos->o_y;
       +                        break;
       +                }
       +        }
       +        if (some) {
       +                defx = dx;
       +                defy = dy;
       +        } else {
       +                defx *= xtab[hvmode];
       +                defy *= ytab[hvmode];
       +        }
       +        prevdx = defx;
       +        prevdy = defy;
       +        extreme(curx, cury);
       +        curx += defx;
       +        cury += defy;
       +        extreme(curx, cury);
       +        p = makenode(MOVE, 0);
       +        dprintf("M %g %g\n", curx, cury);
       +        return(p);
       +}
 (DIR) diff --git a/src/cmd/tpic/pic.h b/src/cmd/tpic/pic.h
       t@@ -0,0 +1,285 @@
       +#include <string.h>
       +#include <stdlib.h>
       +#include <unistd.h>
       +
       +#ifndef PI
       +#define PI 3.1415926535897932384626433832795028841971693993751
       +#endif
       +
       +#define        MAXWID        8.5        /* default limits max picture to 8.5 x 11; */
       +#define        MAXHT        11        /* change to taste without peril */
       +
       +#define        dprintf        if(dbg)printf
       +
       +extern        char        errbuf[200];
       +#define        ERROR        sprintf(errbuf,
       +#define        FATAL        ), yyerror(errbuf), exit(1)
       +#define        WARNING        ), yyerror(errbuf)
       +
       +#define        DEFAULT        0
       +
       +#define        HEAD1        1
       +#define        HEAD2        2
       +#define        HEAD12        (HEAD1+HEAD2)
       +#define        INVIS        4
       +#define        CW_ARC        8        /* clockwise arc */
       +#define        DOTBIT        16        /* line styles */
       +#define        DASHBIT        32
       +#define        FILLBIT        64        /* gray-fill on boxes, etc. */
       +
       +#define        CENTER        01        /* text attributes */
       +#define        LJUST        02
       +#define        RJUST        04
       +#define        ABOVE        010
       +#define        BELOW        020
       +#define        SPREAD        040
       +
       +#define        SCALE        1.0        /* default scale: units/inch */
       +#define        WID        0.75        /* default width for boxes and ellipses */
       +#define        WID2        0.375
       +#define        HT        0.5        /* default height and line length */
       +#define        HT2        (HT/2)
       +#define        HT5        (HT/5)
       +#define        HT10        (HT/10)
       +
       +/* these have to be like so, so that we can write */
       +/* things like R & V, etc. */
       +#define        H        0
       +#define        V        1
       +#define        R_DIR        0
       +#define        U_DIR        1
       +#define        L_DIR        2
       +#define        D_DIR        3
       +#define        ishor(n)        (((n) & V) == 0)
       +#define        isvert(n)        (((n) & V) != 0)
       +#define        isright(n)        ((n) == R_DIR)
       +#define        isleft(n)        ((n) == L_DIR)
       +#define        isdown(n)        ((n) == D_DIR)
       +#define        isup(n)                ((n) == U_DIR)
       +
       +typedef        float        ofloat;        /* for o_val[] in obj;  could be double */
       +
       +typedef struct obj {        /* stores various things in variable length */
       +        int        o_type;
       +        int        o_count;        /* number of things */
       +        int        o_nobj;                /* index in objlist */
       +        int        o_mode;                /* hor or vert */
       +        float        o_x;                /* coord of "center" */
       +        float        o_y;
       +        int        o_nt1;                /* 1st index in text[] for this object */
       +        int        o_nt2;                /* 2nd; difference is #text strings */
       +        int        o_attr;                /* HEAD, CW, INVIS, etc., go here */
       +        int        o_size;                /* linesize */
       +        int        o_nhead;        /* arrowhead style */
       +        struct symtab *o_symtab; /* symtab for [...] */
       +        float        o_ddval;        /* value of dot/dash expression */
       +        float        o_fillval;        /* gray scale value */
       +        ofloat        o_val[1];        /* actually this will be > 1 in general */
       +                                /* type is not always FLOAT!!!! */
       +} obj;
       +
       +typedef union {                /* the yacc stack type */
       +        int        i;
       +        char        *p;
       +        obj        *o;
       +        double        f;
       +        struct symtab *st;
       +} YYSTYPE;
       +
       +extern        YYSTYPE        yylval, yyval;
       +
       +struct symtab {
       +        char        *s_name;
       +        int        s_type;
       +        YYSTYPE        s_val;
       +        struct symtab *s_next;
       +};
       +
       +typedef struct {        /* attribute of an object */
       +        int        a_type;
       +        int        a_sub;
       +        YYSTYPE        a_val;
       +} Attr;
       +
       +typedef struct {
       +        int        t_type;                /* CENTER, LJUST, etc. */
       +        char        t_op;                /* optional sign for size changes */
       +        char        t_size;                /* size, abs or rel */
       +        char        *t_val;
       +} Text;
       +
       +#define        String        01
       +#define        Macro        02
       +#define        File        04
       +#define        Char        010
       +#define        Thru        020
       +#define        Free        040
       +
       +typedef struct {        /* input source */
       +        int        type;        /* Macro, String, File */
       +        char        *sp;        /* if String or Macro */
       +} Src;
       +
       +extern        Src        src[], *srcp;        /* input source stack */
       +
       +typedef struct {
       +        FILE        *fin;
       +        char        *fname;
       +        int        lineno;
       +} Infile;
       +
       +extern        Infile        infile[], *curfile;
       +
       +#define        MAXARGS        20
       +typedef struct {        /* argument stack */
       +        char        *argstk[MAXARGS];        /* pointers to args */
       +        char        *argval;        /* points to space containing args */
       +} Arg;
       +
       +extern        int        dbg;
       +extern        obj        **objlist;
       +extern        int        nobj, nobjlist;
       +extern        Attr        *attr;
       +extern        int        nattr, nattrlist;
       +extern        Text        *text;
       +extern        int        ntextlist;
       +extern        int        ntext;
       +extern        int        ntext1;
       +extern        double        curx, cury;
       +extern        int        hvmode;
       +extern        int        codegen;
       +extern        int        PEseen;
       +extern        double        deltx, delty;
       +extern        int        lineno;
       +extern        int        synerr;
       +
       +extern        double        xmin, ymin, xmax, ymax;
       +
       +struct pushstack {
       +        double        p_x;
       +        double        p_y;
       +        int        p_hvmode;
       +        double        p_xmin;
       +        double        p_ymin;
       +        double        p_xmax;
       +        double        p_ymax;
       +        struct symtab *p_symtab;
       +};
       +extern        struct pushstack stack[];
       +extern        int        nstack;
       +extern        int        cw;
       +
       +
       +#define        Log10(x) errcheck(log10(x), "log")
       +#define        Exp(x)        errcheck(exp(x), "exp")
       +#define        Sqrt(x)        errcheck(sqrt(x), "sqrt")
       +
       +
       +char*                addnewline(char *p)        /* add newline to end of p */;
       +obj*                addpos(obj *p, obj *q);
       +void                addtattr(int sub)                /* add text attrib to existing item */;
       +void                arc(double xc, double yc, double x0, double y0, double x1, double y1)        /* draw arc with center xc,yc */;
       +void                arc_extreme(double x0, double y0, double x1, double y1, double xc, double yc);
       +obj*                arcgen(int type)        /* handles circular and (eventually) elliptical arcs */;
       +void                arrow(double x0, double y0, double x1, double y1, double w, double h, double ang, int nhead)         /* draw arrow (without shaft) */ /* head wid w, len h, rotated ang */ /* and drawn with nhead lines */;
       +int                baldelim(int c, char *s)        /* replace c by balancing entry in s */;
       +void                blockadj(obj *p)        /* adjust coords in block starting at p */;
       +obj*                blockgen(obj *p, obj *q)        /* handles [...] */;
       +obj*                boxgen(void);
       +void                checkscale(char *s)        /* if s is "scale", adjust default variables */;
       +obj*                circgen(int type);
       +void                copy(void)        /* begin input from file, etc. */;
       +void                copydef(struct symtab *p)        /* remember macro symtab ptr */;
       +void                copyfile(char *s)        /* remember file to start reading from */;
       +struct symtab*        copythru(char *s)        /* collect the macro name or body for thru */;
       +void                copyuntil(char *s)        /* string that terminates a thru */;
       +int                curdir(void)        /* convert current dir (hvmode) to RIGHT, LEFT, etc. */;
       +void                definition(char *s)        /* collect definition for s and install */ /* definitions picked up lexically */;
       +char*                delimstr(char *s)        /* get body of X ... X */         /* message if too big */;
       +void                do_thru(void)        /* read one line, make into a macro expansion */;
       +void                dodef(struct symtab *stp)        /* collect args and switch input to defn */;
       +void                dot(void);
       +void                dotbox(double x0, double y0, double x1, double y1, int ddtype, double ddval)        /* dotted or dashed box */;
       +void                dotext(obj *p)        /* print text strings of p in proper vertical spacing */;
       +void                dotline(double x0, double y0, double x1, double y1, int ddtype, double ddval);
       +void                dotline(double x0, double y0, double x1, double y1, int ddtype, double ddval) /* dotted line */;
       +void                ellipse(double x, double y, double r1, double r2);
       +void                endfor(void)        /* end one iteration of for loop */;
       +void                eprint(void)        /* try to print context around error */;
       +double                errcheck(double x, char *s);
       +void                exprsave(double f);
       +void                extreme(double x, double y)        /* record max and min x and y values */;
       +void                fillend(void);
       +void                fillstart(double v)        /* only choose black, light grey (.75), or white, for now */;
       +obj*                fixpos(obj *p, double x, double y);
       +void                forloop(char *, double, double, int, double, char *)        /* set up a for loop */;
       +void                fpecatch(int arg);
       +void                freedef(char *s)        /* free definition for string s */;
       +void                freesymtab(struct symtab *p)        /* free space used by symtab at p */;
       +int                getarg(char *p)        /* pick up single argument, store in p, return length */;
       +YYSTYPE                getblk(obj *p, char *s)        /* find union type for s in p */;
       +double                getblkvar(obj *p, char *s)        /* find variable s2 in block p */;
       +obj*                getblock(obj *p, char *s)        /* find variable s in block p */;
       +double                getcomp(obj *p, int t)        /* return component of a position */;
       +void                getdata(void);
       +obj*                getfirst(int n, int t)        /* find n-th occurrence of type t */;
       +double                getfval(char *s)        /* return float value of variable s */;
       +obj*                gethere(void)        /* make a place for curx,cury */;
       +obj*                getlast(int n, int t)        /* find n-th previous occurrence of type t */;
       +obj*                getpos(obj *p, int corner)        /* find position of point */;
       +YYSTYPE                getvar(char *s)        /* return value of variable s (usually pointer) */;
       +char *                grow(char *ptr, char *name, int num, int size)        /* make array bigger */;
       +char*                ifstat(double expr, char *thenpart, char *elsepart);
       +int                input(void);
       +void                label(char *s, int t, int nh)        /* text s of type t nh half-lines up */;
       +obj*                leftthing(int c)        /* called for {... or [... */                 /* really ought to be separate functions */;
       +obj*                linegen(int type);
       +struct symtab*        lookup(char *s)        /* find s in symtab */;
       +int                main(int argc, char **argv);
       +void                makeattr(int type, int sub, YYSTYPE val)        /* add attribute type and val */;
       +obj*                makebetween(double f, obj *p1, obj* p2)        /* make position between p1 and p2 */;
       +void                makefattr(int type, int sub, double f)        /* double attr */;
       +void                makeiattr(int type, int i)        /* int attr */;
       +obj*                makenode(int type, int n);
       +void                makeoattr(int type, obj *o)        /* obj* attr */;
       +obj*                makepos(double x, double y)        /* make a position cell */;
       +void                maketattr(int sub, char *p)        /* text attribute: takes two */;
       +struct symtab*        makevar(char *s, int t, YYSTYPE v)        /* make variable named s in table */         /* assumes s is static or from tostring */;
       +void                makevattr(char *p)        /* varname attribute */;
       +obj*                movegen(void);
       +int                nextchar(void);
       +void                nextfor(void)        /* do one iteration of a for loop */;
       +void                pbstr(char *s);
       +void                popsrc(void)        /* restore an old one */;
       +void                print(void);
       +void                printexpr(double f)        /* print expression for debugging */;
       +void                printlf(int line, char *name);
       +void                printpos(obj *p)        /* print position for debugging */;
       +void                pushsrc(int type, char *ptr)        /* new input source */;
       +int                quadrant(double x, double y);
       +void                reset(void);
       +void                resetvar(void)        /* reset variables listed */;
       +obj*                rightthing(obj *p, int c)        /* called for ... ] or ... } */;
       +void                savetext(int t, char *s)        /* record text elements for current object */;
       +void                setdefaults(void)        /* set default sizes for variables like boxht */;
       +int                setdir(int n)        /* set direction (hvmode) from LEFT, RIGHT, etc. */;
       +void                setfval(char *s, double f)        /* set variable s to f */;
       +void                shell_exec(void)        /* do it */;
       +void                shell_init(void)        /* set up to interpret a shell command */;
       +void                shell_text(char *s)        /* add string to command being collected */;
       +void                space(double x0, double y0, double x1, double y1)        /* set limits of page */;
       +void                spline(double x, double y, double/*sic*/ n, float *p, int dashed, double ddval);
       +char*                sprintgen(char *fmt);
       +obj*                subpos(obj *p, obj *q);
       +obj*                textgen(void);
       +char*                tostring(char *s);
       +void                troff(char *s);
       +obj*                troffgen(char *s)        /* save away a string of troff commands */;
       +void                undefine(char *s)        /* undefine macro */;
       +int                unput(int c);
       +int                whatpos(obj *p, int corner, double *px, double *py)        /* what is the position (no side effect) */;
       +void                yyerror(char *s);
       +int                yyparse(void);
       +
       +#include "tex.h"
       +
 (DIR) diff --git a/src/cmd/tpic/picl.l b/src/cmd/tpic/picl.l
       t@@ -0,0 +1,261 @@
       +%Start A str def sc br thru sh
       +%e 1700
       +%k 120
       +%a 1800
       +%o 1500
       +%p 5000
       +%n 700
       +
       +%{
       +#undef        input
       +#undef        unput
       +#include <stdio.h>
       +#include <ctype.h>
       +#include "pic.h"
       +#include "y.tab.h"
       +
       +extern        double        atof();
       +extern        char        *filename;
       +extern        struct        symtab        symtab[];
       +extern        struct        symtab*copythru();
       +
       +#define        CADD        cbuf[clen++]=yytext[0]; if(clen>=CBUFLEN-1) {ERROR "string too long" WARNING; BEGIN A;}
       +#define        CBUFLEN        500
       +char        cbuf[CBUFLEN];
       +int        c, clen, cflag, delim;
       +int        ifsw        = 0;        /* 1 if if statement in progress */
       +%}
       +
       +A        [a-zA-Z_]
       +B        [a-zA-Z0-9_]
       +D        [0-9]
       +WS        [ \t]
       +
       +%%
       +        switch (yybgin-yysvec-1) {        /* witchcraft */
       +        case 0:
       +                BEGIN A;
       +                break;
       +        case sc:
       +                BEGIN A;
       +                return('}');
       +        case br:
       +                BEGIN A;
       +                return(']');
       +        }
       +
       +<A>{WS}                ;
       +<A>"\\"\n        ;
       +<A>\n                { return(ST); }
       +<A>";"                { return(ST); }
       +<A>"}"                { BEGIN sc; return(ST); }
       +<A>"]"                { BEGIN br; return(ST); }
       +
       +<A>^".PS".*        { if (curfile == infile) ERROR ".PS found inside .PS/.PE" WARNING; }
       +<A>^"."P[EF].*        { if (curfile == infile) {
       +                        yylval.i = yytext[2];
       +                        PEseen = 1;
       +                        return(EOF);
       +                  }
       +                }
       +<A>^".".*        { yylval.p = tostring(yytext); return(TROFF); }
       +
       +<A>print        return(yylval.i = PRINT);
       +<A>box                return(yylval.i = BOX);
       +<A>circle        return(yylval.i = CIRCLE);
       +<A>arc                return(yylval.i = ARC);
       +<A>ellipse        return(yylval.i = ELLIPSE);
       +<A>arrow        return(yylval.i = ARROW);
       +<A>spline        return(yylval.i = SPLINE);
       +<A>line                return(yylval.i = LINE);
       +<A>move                return(yylval.i = MOVE);
       +<A>"[]"                return(yylval.i = BLOCK);
       +<A>reset        return(RESET);
       +<A>sprintf        return(SPRINTF);
       +
       +<A>same                return(SAME);
       +<A>between        return(BETWEEN);
       +<A>and                return(AND);
       +
       +<A>of                ;
       +<A>the                ;
       +<A>way                ;
       +
       +<A>"."(e|east)                { yylval.i = EAST; return(CORNER); }
       +<A>"."(r|right)                { yylval.i = EAST; return(CORNER); }
       +<A>"."(w|west)                { yylval.i = WEST; return(CORNER); }
       +<A>"."(l|left)                { yylval.i = WEST; return(CORNER); }
       +<A>"."(n|north)                { yylval.i = NORTH; return(CORNER); }
       +<A>"."(t|top)                { yylval.i = NORTH; return(CORNER); }
       +<A>"."(s|south)                { yylval.i = SOUTH; return(CORNER); }
       +<A>"."(b|bot|bottom)        { yylval.i = SOUTH; return(CORNER); }
       +<A>"."(c|center)        { yylval.i = CENTER; return(CORNER); }
       +<A>".start"                { yylval.i = START; return(CORNER); }
       +<A>".end"                { yylval.i = END; return(CORNER); }
       +<A>".ne"                { yylval.i = NE; return(CORNER); }
       +<A>".se"                { yylval.i = SE; return(CORNER); }
       +<A>".nw"                { yylval.i = NW; return(CORNER); }
       +<A>".sw"                { yylval.i = SW; return(CORNER); }
       +
       +<A>top" "+of                { yylval.i = NORTH; return(CORNER); }
       +<A>north" "+of                { yylval.i = NORTH; return(CORNER); }
       +<A>bottom" "+of                { yylval.i = SOUTH; return(CORNER); }
       +<A>south" "+of                { yylval.i = SOUTH; return(CORNER); }
       +<A>left" "+of                { yylval.i = WEST; return(CORNER); }
       +<A>west" "+of                { yylval.i = WEST; return(CORNER); }
       +<A>right" "+of                { yylval.i = EAST; return(CORNER); }
       +<A>east" "+of                { yylval.i = EAST; return(CORNER); }
       +<A>center" "+of                { yylval.i = CENTER; return(CORNER); }
       +<A>start" "+of                { yylval.i = START; return(CORNER); }
       +<A>end" "+of                { yylval.i = END; return(CORNER); }
       +
       +<A>height|ht        { yylval.i = HEIGHT; return(ATTR); }
       +<A>width|wid        { yylval.i = WIDTH; return(ATTR); }
       +<A>radius|rad        { yylval.i = RADIUS; return(ATTR); }
       +<A>diameter|diam { yylval.i = DIAMETER; return(ATTR); }
       +<A>size                { yylval.i = SIZE; return(ATTR); }
       +<A>left                { yylval.i = LEFT; return(DIR); }
       +<A>right        { yylval.i = RIGHT; return(DIR); }
       +<A>up                { yylval.i = UP; return(DIR); }
       +<A>down                { yylval.i = DOWN; return(DIR); }
       +<A>cw                { yylval.i = CW; return(ATTR); }
       +<A>clockwise        { yylval.i = CW; return(ATTR); }
       +<A>ccw                { yylval.i = CCW; return(ATTR); }
       +<A>invis(ible)?        { yylval.i = INVIS; return(ATTR); }
       +<A>fill                { yylval.i = FILL; return ATTR; }
       +<A>solid        ;
       +<A>dot(ted)?        return(yylval.i = DOT);
       +<A>dash(ed)?        return(yylval.i = DASH);
       +<A>chop                return(yylval.i = CHOP);
       +
       +<A>spread        { yylval.i = SPREAD; return TEXTATTR; }
       +<A>ljust        { yylval.i = LJUST; return TEXTATTR; }
       +<A>rjust        { yylval.i = RJUST; return TEXTATTR; }
       +<A>above        { yylval.i = ABOVE; return TEXTATTR; }
       +<A>below        { yylval.i = BELOW; return TEXTATTR; }
       +<A>center        { yylval.i = CENTER; return TEXTATTR; }
       +
       +<A>"<-"                { yylval.i = HEAD1; return(HEAD); }
       +<A>"->"                { yylval.i = HEAD2; return(HEAD); }
       +<A>"<->"        { yylval.i = HEAD12; return(HEAD); }
       +
       +<A>".x"                        return(yylval.i = DOTX);
       +<A>".y"                        return(yylval.i = DOTY);
       +<A>"."(ht|height)        return(yylval.i = DOTHT);
       +<A>"."(wid|width)        return(yylval.i = DOTWID);
       +<A>"."(rad|radius)        return(yylval.i = DOTRAD);
       +
       +<A>from                return(yylval.i = FROM);
       +<A>to                return(yylval.i = TO);
       +<A>at                return(yylval.i = AT);
       +<A>by                return(yylval.i = BY);
       +<A>with                return(yylval.i = WITH);
       +<A>last                return(yylval.i = LAST);
       +
       +<A>log                return(LOG);
       +<A>exp                return(EXP);
       +<A>sin                return(SIN);
       +<A>cos                return(COS);
       +<A>atan2        return(ATAN2);
       +<A>sqrt                return(SQRT);
       +<A>rand                return(RAND);
       +<A>max                return(MAX);
       +<A>min                return(MIN);
       +<A>int                return(INT);
       +
       +<A>"=="                return(EQ);
       +<A>">="                return(GE);
       +<A>"<="                return(LE);
       +<A>"!="                return(NEQ);
       +<A>">"                return(GT);
       +<A>"<"                return(LT);
       +<A>"&&"                return(ANDAND);
       +<A>"||"                return(OROR);
       +<A>"!"                return(NOT);        
       +
       +<A>Here                return(yylval.i = HERE);
       +
       +<A>for                return(FOR);
       +<A>^Endfor\n        { endfor(); }
       +<A>do                { yylval.p = delimstr("loop body"); return(DOSTR); }
       +
       +<A>copy|include                return(COPY);
       +<A>(thru|through){WS}+        { BEGIN thru; return(THRU); }
       +<thru>{A}{B}*|.                { yylval.st = copythru(yytext); BEGIN A; return(DEFNAME); }
       +<A>until                return(UNTIL);
       +
       +<A>if                { ifsw = 1; return(IF); }
       +<A>then                { if (!ifsw) { yylval.i = THEN; return(ATTR); }
       +                  yylval.p = delimstr("then part"); ifsw = 0;
       +                  return(THENSTR); }
       +<A>else                { yylval.p = delimstr("else part"); return(ELSESTR); }
       +
       +<A>sh{WS}+        { BEGIN sh;
       +                  if ((delim = input()) == '{') delim = '}';        /* no nested {} */
       +                  shell_init(); }
       +<sh>{A}{B}*        { struct symtab *p;
       +                  if (yytext[0] == delim) {
       +                        shell_exec();
       +                        BEGIN A;
       +                  } else {
       +                        p = lookup(yytext);
       +                        if (p != NULL && p->s_type == DEFNAME) {
       +                                c = input();
       +                                unput(c);
       +                                if (c == '(')
       +                                        dodef(p);
       +                                else
       +                                        pbstr(p->s_val.p);
       +                        } else
       +                                shell_text(yytext);
       +                  }
       +                }
       +<sh>.|\n        { if (yytext[0] == delim) {
       +                        shell_exec();
       +                        BEGIN A;
       +                  } else
       +                        shell_text(yytext);
       +                }
       +
       +<A>define{WS}+                { BEGIN def; }
       +<def>{A}{B}*                { definition(yytext); BEGIN A; }
       +<A>undef(ine)?{WS}+{A}{B}*        { undefine(yytext); }
       +
       +<A>first                { yylval.i = 1; return(NTH); }
       +<A>{D}+(th|nd|rd|st)        { yylval.i = atoi(yytext); return(NTH); }
       +<A>({D}+("."?){D}*|"."{D}+)((e|E)("+"|-)?{D}+)?i? {
       +                          yylval.f = atof(yytext); return(NUMBER); }
       +
       +<A>{A}{B}* {        struct symtab *p;
       +                p = lookup(yytext);
       +                if (p != NULL && p->s_type == DEFNAME) {
       +                        c = input();
       +                        unput(c);
       +                        if (c == '(')        /* it's name(...) */
       +                                dodef(p);
       +                        else {        /* no argument list */
       +                                pbstr(p->s_val.p);
       +                                dprintf("pushing back `%s'\n", p->s_val.p);
       +                        }
       +                } else if (islower(yytext[0])) {
       +                        yylval.p = tostring(yytext);
       +                        return(VARNAME);
       +                } else {
       +                        yylval.p = tostring(yytext);
       +                        return(PLACENAME);
       +                }
       +        }
       +
       +<A>\"                { BEGIN str; clen=0; }
       +<str>\"                { cbuf[clen]=0; yylval.p = tostring(cbuf); BEGIN A; return(TEXT); }
       +<str>\n                { cbuf[clen]=0; ERROR "missing quote in string \"%s\"", cbuf WARNING;
       +                                BEGIN A; return(ST); }
       +<str>"\\\""        { cbuf[clen++]='"'; }
       +<str>"\\\\"        { cbuf[clen++]='\\'; }
       +<str>.                { CADD; }
       +
       +<A>#.*                ;
       +
       +<A>.                return(yylval.i = yytext[0]);
       +
       +%%
 (DIR) diff --git a/src/cmd/tpic/picy.c b/src/cmd/tpic/picy.c
       t@@ -0,0 +1,1239 @@
       +
       +#line        2        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +#include <stdio.h>
       +#include "pic.h"
       +#include <math.h>
       +YYSTYPE        y;
       +int yylex(void);
       +extern        int        yyerrflag;
       +#ifndef        YYMAXDEPTH
       +#define        YYMAXDEPTH        150
       +#endif
       +YYSTYPE        yylval;
       +YYSTYPE        yyval;
       +#define        BOX        1
       +#define        LINE        2
       +#define        ARROW        3
       +#define        CIRCLE        4
       +#define        ELLIPSE        5
       +#define        ARC        6
       +#define        SPLINE        7
       +#define        BLOCK        8
       +#define        TEXT        9
       +#define        TROFF        10
       +#define        MOVE        11
       +#define        BLOCKEND        12
       +#define        PLACE        13
       +#define        PRINT        57359
       +#define        RESET        57360
       +#define        THRU        57361
       +#define        UNTIL        57362
       +#define        FOR        57363
       +#define        IF        57364
       +#define        COPY        57365
       +#define        THENSTR        57366
       +#define        ELSESTR        57367
       +#define        DOSTR        57368
       +#define        PLACENAME        57369
       +#define        VARNAME        57370
       +#define        SPRINTF        57371
       +#define        DEFNAME        57372
       +#define        ATTR        57373
       +#define        TEXTATTR        57374
       +#define        LEFT        57375
       +#define        RIGHT        57376
       +#define        UP        57377
       +#define        DOWN        57378
       +#define        FROM        57379
       +#define        TO        57380
       +#define        AT        57381
       +#define        BY        57382
       +#define        WITH        57383
       +#define        HEAD        57384
       +#define        CW        57385
       +#define        CCW        57386
       +#define        THEN        57387
       +#define        HEIGHT        57388
       +#define        WIDTH        57389
       +#define        RADIUS        57390
       +#define        DIAMETER        57391
       +#define        LENGTH        57392
       +#define        SIZE        57393
       +#define        CORNER        57394
       +#define        HERE        57395
       +#define        LAST        57396
       +#define        NTH        57397
       +#define        SAME        57398
       +#define        BETWEEN        57399
       +#define        AND        57400
       +#define        EAST        57401
       +#define        WEST        57402
       +#define        NORTH        57403
       +#define        SOUTH        57404
       +#define        NE        57405
       +#define        NW        57406
       +#define        SE        57407
       +#define        SW        57408
       +#define        START        57409
       +#define        END        57410
       +#define        DOTX        57411
       +#define        DOTY        57412
       +#define        DOTHT        57413
       +#define        DOTWID        57414
       +#define        DOTRAD        57415
       +#define        NUMBER        57416
       +#define        LOG        57417
       +#define        EXP        57418
       +#define        SIN        57419
       +#define        COS        57420
       +#define        ATAN2        57421
       +#define        SQRT        57422
       +#define        RAND        57423
       +#define        MIN        57424
       +#define        MAX        57425
       +#define        INT        57426
       +#define        DIR        57427
       +#define        DOT        57428
       +#define        DASH        57429
       +#define        CHOP        57430
       +#define        FILL        57431
       +#define        ST        57432
       +#define        OROR        57433
       +#define        ANDAND        57434
       +#define        GT        57435
       +#define        LT        57436
       +#define        LE        57437
       +#define        GE        57438
       +#define        EQ        57439
       +#define        NEQ        57440
       +#define        UMINUS        57441
       +#define        NOT        57442
       +#define YYEOFCODE 1
       +#define YYERRCODE 2
       +static        const        short        yyexca[] =
       +{-1, 0,
       +        1, 2,
       +        -2, 0,
       +-1, 1,
       +        1, -1,
       +        -2, 0,
       +-1, 203,
       +        94, 0,
       +        95, 0,
       +        96, 0,
       +        97, 0,
       +        98, 0,
       +        99, 0,
       +        -2, 156,
       +-1, 210,
       +        94, 0,
       +        95, 0,
       +        96, 0,
       +        97, 0,
       +        98, 0,
       +        99, 0,
       +        -2, 155,
       +-1, 211,
       +        94, 0,
       +        95, 0,
       +        96, 0,
       +        97, 0,
       +        98, 0,
       +        99, 0,
       +        -2, 157,
       +-1, 212,
       +        94, 0,
       +        95, 0,
       +        96, 0,
       +        97, 0,
       +        98, 0,
       +        99, 0,
       +        -2, 158,
       +-1, 213,
       +        94, 0,
       +        95, 0,
       +        96, 0,
       +        97, 0,
       +        98, 0,
       +        99, 0,
       +        -2, 159,
       +-1, 214,
       +        94, 0,
       +        95, 0,
       +        96, 0,
       +        97, 0,
       +        98, 0,
       +        99, 0,
       +        -2, 160,
       +-1, 266,
       +        94, 0,
       +        95, 0,
       +        96, 0,
       +        97, 0,
       +        98, 0,
       +        99, 0,
       +        -2, 156,
       +};
       +#define        YYNPROD        175
       +#define        YYPRIVATE 57344
       +#define        YYLAST        1551
       +static        const        short        yyact[] =
       +{
       + 171, 330, 137,  52, 316,  67, 270, 123, 124, 308,
       + 315,  42, 269, 239, 108,  32, 135, 160, 135, 159,
       + 158, 157,  94, 224, 130, 131, 132, 133, 134,  43,
       + 156, 155,  91,  50, 154, 153, 152, 151, 135,  97,
       +  80, 104, 295, 294, 243, 232, 230,  40, 121, 126,
       + 129,  82, 123, 124, 312, 150, 147, 109, 110, 111,
       + 112, 113, 271,  50, 121, 225,  71, 106,  41, 162,
       + 101, 164, 128,  40, 331, 332, 333, 334, 136, 127,
       + 243, 167, 191, 187,  72,  73,  74,  75,  76,  77,
       +  78,  79, 272, 200, 197, 109, 110, 111, 112, 113,
       + 136, 125, 121, 123, 124, 123, 124, 201, 203, 104,
       + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214,
       + 215, 216, 217,  38, 218, 221, 231, 111, 112, 113,
       +  50,  50, 121, 317, 123, 124, 192, 202, 204, 123,
       + 124, 195, 196, 166,  84, 229, 220, 223, 165,  95,
       +  96,  35, 233, 234, 235, 236, 237, 238,  34, 240,
       + 241, 242, 189, 168, 283, 244, 246, 281,  36,  44,
       + 122, 249, 248, 250, 104, 104, 104, 104, 104,  89,
       + 123, 124, 258, 259, 260, 261,   4,  70,  85,  37,
       +  92, 296, 263, 264, 227, 266,  50,  50,  50,  50,
       +  50,  80, 265, 251, 252, 253, 254, 257, 119, 114,
       + 194, 115, 116, 117, 118, 109, 110, 111, 112, 113,
       + 274, 169, 121, 276, 283, 284,  37,  99, 188, 279,
       + 114, 194, 115, 116, 117, 118, 109, 110, 111, 112,
       + 113, 262,  85, 121, 281, 282, 190,  35, 277, 130,
       + 131, 132, 133, 134,  86,  87, 198, 227, 228, 162,
       + 193, 164,   2,  83,  36,  69,   1,   5,  37,  39,
       + 161, 301, 104, 104, 304,  26, 306,   6, 185,  24,
       +  12,  24,  13, 147,  14,  24, 300, 199,  88,  81,
       + 309,  90, 310, 311,  50,  50, 278,  68, 163, 313,
       + 314, 302, 303,   0,   0,  24, 318,   0, 319, 140,
       + 144, 145, 141, 142, 143, 146, 247, 327,  24,  24,
       +   0,  65,  66,  68, 280,   0,   0, 335,   0, 297,
       +   0, 336,   0,   0,   0,   0, 337,   0,   0,  16,
       +  20,  21,  17,  18,  19,  22,   0,  35,  25,  23,
       +  51,  46,  10,  11, 267, 268,  30,  31,  29, 149,
       +  24,   0, 102,  46,  36,   0,   0,   0,   0,   0,
       +   0,   0,   0,   0,   0,  65,  66,  68,  53,  24,
       +   0,   0,   0,   0,   0,   0,   0,  65,  66,  68,
       +  53,   0,   0,   0,   0,   0,   0,  45,  55,  56,
       +  57,  58,  59,  60,  61,  63,  62,  64,   0,  45,
       +  55,  56,  57,  58,  59,  60,  61,  63,  62,  64,
       +   9,   0,   0,   0,  48, 100,   0,   0, 299,  54,
       +   0,   0,   0,   0,   0,   0,  48,  35,  93,   0,
       +   0,  54,   0,   0,   0,   0,  27,   0,  33,   0,
       +  49,   0,  51,  46,  36,   0, 170, 179,   0,   0,
       +   0,   0, 173, 174, 175, 176, 177, 180, 140, 144,
       + 145, 141, 142, 143, 146, 245,   0,  65,  66,  68,
       +  53, 178, 120, 119, 114, 194, 115, 116, 117, 118,
       + 109, 110, 111, 112, 113,   0,   0, 121,   0,  45,
       +  55,  56,  57,  58,  59,  60,  61,  63,  62,  64,
       + 172, 181, 182, 183, 184,   0,   0,  35, 139,   0,
       +   0,   0,  47,   8,   0,   8,  48,   0,  35,   8,
       +   0,  54,  51,  46,  36,   0,   0,   0,   0,   0,
       +  93,   0,   0,  51,  46,  36,   0,   0,   0,   8,
       +   0,   0,   0,   0,   0,   0,   0,  65,  66,  68,
       +  53,   0,   8, 103,   0,   0, 339,   0,  65,  66,
       +  68,  53,   0,   0,   0,   0,   0,   0,   0,  45,
       +  55,  56,  57,  58,  59,  60,  61,  63,  62,  64,
       +  45,  55,  56,  57,  58,  59,  60,  61,  63,  62,
       +  64,  51,  46,   0,   8,   0,  48,   0,   0,   0,
       +   0,  54,   0,   0,   0,   0,   0,  48,   0,   0,
       +  93,   0,  54,   8,   0,   0, 255,  66,  68,  53,
       +   0,  49, 120, 119, 114, 194, 115, 116, 117, 118,
       + 109, 110, 111, 112, 113,   0,   0, 121,  45,  55,
       +  56,  57,  58,  59,  60,  61,  63,  62,  64,  16,
       +  20,  21,  17,  18,  19,  22,   0,  35,  25,  23,
       +   0,   0,  10,  11,   0,  48,  30,  31,  29,   0,
       +  54,   0,   7,  28,  36,   0,   0,   0, 256,  49,
       +  16,  20,  21,  17,  18,  19,  22,   0,  35,  25,
       +  23,   0,   0,  10,  11,   0,   0,  30,  31,  29,
       +   0,   0,   0,   7,  28,  36,   0,   3,   0,  16,
       +  20,  21,  17,  18,  19,  22,   0,  35,  25,  23,
       +  51,  46,  10,  11,   0,   0,  30,  31,  29,   0,
       +   9,   0,   7,  28,  36,  15, 140, 144, 145, 141,
       + 142, 143, 146, 148,   0,  65,  66,  68,  53,   0,
       +   0,   0,   0,   0,   0,   0,  27, 186,  33,   0,
       +   0,   9,   0,   0,   0,   0,  15,  45,  55,  56,
       +  57,  58,  59,  60,  61,  63,  62,  64,  51,  46,
       +   0,   0,   0,   0,  98,   0, 149,  27,   0,  33,
       +   9,   0,   0,   0,  48,  15,   0,   0,   0,  54,
       +   0,   0,   0,  65,  66,  68,  53,   0,  49,   0,
       +   0,   0,   0,   0,   0,   0,  27,   0,  33,   0,
       +  51,  46,   0,   0,   0,  45,  55,  56,  57,  58,
       +  59,  60,  61,  63,  62,  64,   0,   0,   0,   0,
       +   0,   0,   0,   0,   0,  65,  66,  68,  53,   0,
       +   0,   0,  48,   0,   0,   0,   0,  54,   0,   0,
       +   0,   0,   0,   0,   0,   0, 222,  45,  55,  56,
       +  57,  58,  59,  60,  61,  63,  62,  64,  16,  20,
       +  21,  17,  18,  19,  22, 108,  35,  25,  23,   0,
       +   0,  10,  11,   0,  48,  30,  31,  29,   0,  54,
       +   0,   7,  28,  36,   0,   0,   0,   0, 219,   0,
       +   0, 140, 144, 145, 141, 142, 143, 146, 138,   0,
       + 120, 119, 114, 107, 115, 116, 117, 118, 109, 110,
       + 111, 112, 113,   0,   0, 121,   0,   0, 106,   0,
       +   0,   0,   0,   0, 226, 120, 119, 114, 194, 115,
       + 116, 117, 118, 109, 110, 111, 112, 113,   0,   9,
       + 121, 139,   0, 307,  15,   0,   0,   0,   0, 226,
       +   0, 120, 119, 114, 194, 115, 116, 117, 118, 109,
       + 110, 111, 112, 113,   0,  27, 121,  33,   0, 305,
       +   0,   0,   0,   0,   0, 226, 120, 119, 114, 194,
       + 115, 116, 117, 118, 109, 110, 111, 112, 113,   0,
       +   0, 121,   0,   0,   0,   0,   0,   0,   0,   0,
       + 329, 120, 119, 114, 194, 115, 116, 117, 118, 109,
       + 110, 111, 112, 113,   0,   0, 121,   0,   0,   0,
       +   0,   0,   0,   0,   0, 328, 120, 119, 114, 194,
       + 115, 116, 117, 118, 109, 110, 111, 112, 113,   0,
       +   0, 121,   0,   0,   0,   0,   0,   0,   0,   0,
       + 322, 120, 119, 114, 194, 115, 116, 117, 118, 109,
       + 110, 111, 112, 113,   0,   0, 121,   0,   0,   0,
       +   0,   0,   0,   0,   0, 321, 120, 119, 114, 194,
       + 115, 116, 117, 118, 109, 110, 111, 112, 113,   0,
       +   0, 121,   0,   0,   0,   0,   0,   0,   0,   0,
       + 320, 120, 119, 114, 194, 115, 116, 117, 118, 109,
       + 110, 111, 112, 113,   0,   0, 121,   0,   0,   0,
       +   0,   0,   0,   0,   0, 293, 120, 119, 114, 194,
       + 115, 116, 117, 118, 109, 110, 111, 112, 113,   0,
       +   0, 121,   0,   0,   0,   0,   0,   0,   0,   0,
       + 290, 120, 119, 114, 194, 115, 116, 117, 118, 109,
       + 110, 111, 112, 113,   0,   0, 121,   0,   0,   0,
       +   0,   0,   0,   0,   0, 288, 120, 119, 114, 194,
       + 115, 116, 117, 118, 109, 110, 111, 112, 113,   0,
       +   0, 121,   0,   0,   0,   0,   0,   0,   0,   0,
       + 287, 120, 119, 114, 194, 115, 116, 117, 118, 109,
       + 110, 111, 112, 113,   0,   0, 121,   0,   0,   0,
       +   0,   0,   0,   0,   0, 286, 120, 119, 114, 194,
       + 115, 116, 117, 118, 109, 110, 111, 112, 113,   0,
       +   0, 121,   0, 108,   0,   0,   0,   0,   0,   0,
       + 285, 120, 119, 114, 194, 115, 116, 117, 118, 109,
       + 110, 111, 112, 113, 108,   0, 121,   0,   0,   0,
       +   0,   0,   0,   0,   0, 226, 105,   0, 120, 119,
       + 114, 107, 115, 116, 117, 118, 109, 110, 111, 112,
       + 113,   0,   0, 121,   0,   0, 106,   0,   0, 120,
       + 119, 114, 107, 115, 116, 117, 118, 109, 110, 111,
       + 112, 113,   0,   0, 121,   0,   0, 106, 120, 119,
       + 114, 194, 115, 116, 117, 118, 109, 110, 111, 112,
       + 113,   0,   0, 121,   0,   0, 292, 120, 119, 114,
       + 194, 115, 116, 117, 118, 109, 110, 111, 112, 113,
       +   0,   0, 121,   0,   0, 291, 120, 119, 114, 194,
       + 115, 116, 117, 118, 109, 110, 111, 112, 113,   0,
       +   0, 121, 338,   0, 289, 120, 119, 114, 194, 115,
       + 116, 117, 118, 109, 110, 111, 112, 113,   0,   0,
       + 121,   0,   0, 275, 120, 119, 114, 194, 115, 116,
       + 117, 118, 109, 110, 111, 112, 113, 326,   0, 121,
       +   0,   0, 273,   0,   0,   0,   0,   0,   0,   0,
       +   0, 325,   0, 324,   0,   0,   0,   0,   0,   0,
       +   0,   0,   0,   0,   0,   0,   0, 323, 120, 119,
       + 114, 194, 115, 116, 117, 118, 109, 110, 111, 112,
       + 113, 298,   0, 121, 120, 119, 114, 194, 115, 116,
       + 117, 118, 109, 110, 111, 112, 113,   0,   0, 121,
       +   0,   0,   0, 120, 119, 114, 194, 115, 116, 117,
       + 118, 109, 110, 111, 112, 113,   0,   0, 121, 120,
       + 119, 114, 194, 115, 116, 117, 118, 109, 110, 111,
       + 112, 113,   0,   0, 121, 120, 119, 114, 194, 115,
       + 116, 117, 118, 109, 110, 111, 112, 113,   0,   0,
       + 121
       +};
       +static        const        short        yypact[] =
       +{
       + 715,-1000, 884,-1000,-1000,  33, 884, -62, -22,-1000,
       + 516, 159,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,
       +-1000,-1000,-1000,-1000, 139,-1000, 884,-1000, -40, 235,
       + 151, 505, 117,-1000, 118,-1000, -76,-1000,-1000, 686,
       + 335,-1000,1216,  80,  11,-1000, -40,-1000, 323, 703,
       + 180, -14, 917, 742, 323, -78, -79, -80, -81, -84,
       + -85, -94, -95, -96, -98, 243,-1000,  96,-1000,  53,
       +-1000, 425, 425, 425, 425, 425, 425, 425, 425, 425,
       + 117, 655, 323, 235,-1000,-1000, 132, 139,  45,-1000,
       + 236,1392,  43, 323, 180,-1000,-1000, 139,-1000,-1000,
       + 884,   3, -36, -22,1237,-1000, 323, 703, 703, 323,
       + 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
       + 323, 323,-1000, 803, 761,-1000, -59, -93, -45, 838,
       +-1000,-1000,-1000,-1000,-1000,-1000, 230,  93, -68,-1000,
       +-1000,-1000,-1000,-1000,-1000,-1000,-1000,  74, -69,-1000,
       + -59, 323, 323, 323, 323, 323, 323,-103, 323, 323,
       + 323, -70, 464, 305,-1000,-1000,-1000,-1000, 144,-1000,
       + 323,1392, 323, 703, 703, 703, 703, 574,-1000,-1000,
       +-1000, 323, 323, 323, 323, 139,-1000,1392,-1000,-1000,
       +-1000, 323, 323, 177, 323, 139, 139,1189,-104,-1000,
       +-1000,1392, -48, -43,  34,  25,  25, -59, -59, -59,
       +  -5,  -5,  -5,  -5,  -5, 136, 115, -59,1332, 323,
       + 180,1313, 323, 180,-1000, 269,-1000,-1000,-1000,-1000,
       + 217,-1000, 197,1164,1139,1114,1089,1294,1064,-1000,
       +1275,1256,1039, 167,-1000, -71,-1000, -72,-1000,1392,
       +1392,   5,   5,   5,   5, 243, 164,   5,1392,1392,
       +1392,1392,-1000,1443, 390,-1000,  -5,-1000,-1000,-1000,
       + 323, 703, 703, 323, 889, 323, 863,-107, -34, 464,
       + 305,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000, 323,
       +-1000, 323, 323,-1000, 140, 137,   2, 425, 323, 323,
       +-106,1392,  39,   5,1392, 323,1392, 323,-1000,1014,
       + 989, 964,-1000,1427,1411,-1000, 323,-1000, 939, 914,
       +-1000,-1000,-1000, -26,-1000, -26,-1000,1392,-1000,-1000,
       + 323,-1000,-1000,-1000,-1000, 323,1376, 540,-1000,-1000
       +};
       +static        const        short        yypgo[] =
       +{
       +   0,   0, 291, 522, 288, 158,   1, 286, 284, 282,
       + 280, 277, 186, 262,  29, 275, 267,  22,   5, 278,
       +  15,   3,   2, 266, 265, 263, 144,  66, 241, 221
       +};
       +static        const        short        yyr1[] =
       +{
       +   0,  23,  23,  23,  13,  13,  12,  12,  12,  12,
       +  12,  12,  12,  12,  12,  12,  12,  12,  12,  12,
       +  12,  24,  24,  24,  24,   3,  10,  25,  25,  26,
       +  26,  26,   9,   9,   9,   9,   8,   8,   2,   2,
       +   2,   4,   6,   6,   6,   6,   6,  11,  16,  16,
       +  16,  16,  16,  16,  16,  16,  16,  16,  28,  16,
       +  15,  27,  27,  29,  29,  29,  29,  29,  29,  29,
       +  29,  29,  29,  29,  29,  29,  29,  29,  29,  29,
       +  29,  29,  29,  29,  29,  29,  29,  29,  19,  19,
       +  20,  20,  20,   5,   5,   5,   7,   7,  14,  14,
       +  14,  14,  14,  14,  14,  14,  14,  14,  14,  14,
       +  17,  17,  17,  17,  17,  17,  17,  17,  17,  17,
       +  17,  17,  17,  18,  18,  18,  21,  21,  21,  22,
       +  22,  22,  22,  22,  22,  22,  22,   1,   1,   1,
       +   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
       +   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
       +   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
       +   1,   1,   1,   1,   1
       +};
       +static        const        short        yyr2[] =
       +{
       +   0,   1,   0,   1,   1,   2,   2,   3,   3,   4,
       +   4,   2,   1,   3,   3,   3,   3,   1,   1,   1,
       +   1,   0,   1,   2,   3,   3,   2,   1,   2,   1,
       +   2,   2,  10,   7,  10,   7,   4,   3,   1,   3,
       +   3,   1,   1,   1,   1,   1,   0,   1,   2,   2,
       +   2,   2,   2,   2,   2,   2,   2,   1,   0,   5,
       +   1,   2,   0,   2,   1,   1,   2,   1,   2,   2,
       +   2,   2,   2,   3,   4,   2,   1,   1,   1,   2,
       +   1,   2,   1,   2,   1,   2,   1,   1,   1,   2,
       +   1,   2,   2,   1,   4,   6,   1,   3,   1,   3,
       +   3,   5,   5,   7,   7,   3,   3,   5,   6,   5,
       +   1,   2,   2,   1,   2,   3,   3,   2,   3,   3,
       +   1,   2,   2,   4,   4,   3,   2,   2,   1,   1,
       +   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
       +   3,   3,   3,   3,   3,   2,   3,   2,   2,   2,
       +   2,   2,   3,   4,   4,   3,   3,   3,   3,   3,
       +   3,   3,   3,   2,   4,   4,   3,   4,   4,   6,
       +   4,   3,   6,   6,   4
       +};
       +static        const        short        yychk[] =
       +{
       +-1000, -23, -13,   2, -12, -16, -11,  27,  -3,  85,
       +  17,  18, -10,  -9,  -8,  90,   4,   7,   8,   9,
       +   5,   6,  10,  14, -19,  13, -15, 111,  28,  23,
       +  21,  22, -20, 113,  -5,  12,  29, -12,  90, -13,
       + 109,  90,  -1, -14,  -5,  74,  28,  -3, 101, 115,
       + -17,  27, -21,  55, 106,  75,  76,  77,  78,  79,
       +  80,  81,  83,  82,  84,  52,  53, -18,  54, -24,
       +  28, -27, -27, -27, -27, -27, -27, -27, -27, -27,
       + -20, -13,  91, -25, -26,  -5,  19,  20,  -4,  28,
       +  -2,  -1,  -5, 115, -17,  32,  32, 115, 108, -12,
       +  90, -14,  27,  -3,  -1,  90, 110,  95,  57, 100,
       + 101, 102, 103, 104,  94,  96,  97,  98,  99,  93,
       +  92, 107,  90, 100, 101,  90,  -1, -14, -17,  -1,
       +  69,  70,  71,  72,  73,  52, 114, -22,  11,  54,
       +   4,   7,   8,   9,   5,   6,  10, -22,  11,  54,
       +  -1, 115, 115, 115, 115, 115, 115, 115, 115, 115,
       + 115,  27, -21,  55, -18,  52,  90,  28, 110, -29,
       +  31,  -1,  85,  37,  38,  39,  40,  41,  56,  32,
       +  42,  86,  87,  88,  89, -19, 112,  -1, -26,  30,
       +  -5,  37,  91,  24,  95,  98,  99,  -1,  -5, -12,
       +  90,  -1, -14,  -1, -14,  -1,  -1,  -1,  -1,  -1,
       +  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, 115,
       + -17,  -1, 115, -17, 116, 110, 116,  27,  28,  52,
       + 114,  52, 114,  -1,  -1,  -1,  -1,  -1,  -1, 116,
       +  -1,  -1,  -1, 114, -22,  11, -22,  11,  28,  -1,
       +  -1, -14, -14, -14, -14,  52, 114, -14,  -1,  -1,
       +  -1,  -1, -28,  -1,  -1,  25,  -1,  -5,  -5, 116,
       + 110, 110,  58, 110,  -1, 110,  -1, -17,  27, -21,
       +  55,  27,  28,  27,  28, 116, 116, 116, 116, 110,
       + 116, 110, 110, 116, 114, 114,  27, -27,  38,  38,
       +  -7,  -1, -14, -14,  -1, 110,  -1, 110, 116,  -1,
       +  -1,  -1,  52,  -1,  -1, 116, 110,  94,  -1,  -1,
       + 116, 116, 116,  40,  26,  40,  26,  -1, 116, 116,
       +  -6, 100, 101, 102, 103,  -6,  -1,  -1,  26,  26
       +};
       +static        const        short        yydef[] =
       +{
       +  -2,  -2,   1,   3,   4,   0,   0,   0,   0,  12,
       +   0,  21,  17,  18,  19,  20,  62,  62,  62,  62,
       +  62,  62,  62,  62,  62,  57,   0,  47,   0,   0,
       +   0,   0,  88,  60,  90,  93,   0,   5,   6,   0,
       +   0,  11,   0,   0,   0, 137, 138, 139,   0,   0,
       +  98, 110,   0,   0,   0,   0,   0,   0,   0,   0,
       +   0,   0,   0,   0,   0,   0, 113, 120, 128,   0,
       +  22,  48,  49,  50,  51,  52,  53,  54,  55,  56,
       +  89,   0,   0,  26,  27,  29,   0,   0,   0,  41,
       +   0,  38,   0,   0,   0,  92,  91,   0,   7,   8,
       +  20,   0, 110, 139,   0,  13,   0,   0,   0,   0,
       +   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       +   0,   0,  14,   0,   0,  15, 145,   0,  98,   0,
       + 147, 148, 149, 150, 151, 111,   0, 114, 136, 126,
       + 129, 130, 131, 132, 133, 134, 135, 117, 136, 127,
       + 163,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       +   0, 112,   0,   0, 122, 121,  16,  23,   0,  61,
       +  64,  65,  67,   0,   0,   0,   0,   0,  76,  77,
       +  78,  80,  82,  84,  86,  87,  58,  25,  28,  30,
       +  31,   0,   0,  37,   0,   0,   0,   0,   0,   9,
       +  10, 100,   0,  -2,   0, 140, 141, 142, 143, 144,
       +  -2,  -2,  -2,  -2,  -2, 161, 162, 166,   0,   0,
       + 105,   0,   0, 106,  99,   0, 146, 125, 152, 115,
       +   0, 118,   0,   0,   0,   0,   0,   0,   0, 171,
       +   0,   0,   0,   0, 116, 136, 119, 136,  24,  63,
       +  66,  68,  69,  70,  71,  72,   0,  75,  79,  81,
       +  83,  85,  62,   0,   0,  36,  -2,  39,  40,  94,
       +   0,   0,   0,   0,   0,   0,   0,   0, 110,   0,
       +   0, 123, 153, 124, 154, 164, 165, 167, 168,   0,
       + 170,   0,   0, 174,   0,   0,  73,  59,   0,   0,
       +   0,  96,   0, 109, 101,   0, 102,   0, 107,   0,
       +   0,   0,  74,   0,   0,  95,   0, 108,   0,   0,
       + 169, 172, 173,  46,  33,  46,  35,  97, 103, 104,
       +   0,  42,  43,  44,  45,   0,   0,   0,  32,  34
       +};
       +static        const        short        yytok1[] =
       +{
       +   1,   4,   5,   6,   7,   8,   9,  10,  11,  12,
       +  13,  14,  15,  16,   0,   0,   0,   0,   0,   0,
       +   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       +   0,   0,   0,   0,   0,   0,   0, 104,   0,   0,
       + 115, 116, 102, 100, 110, 101, 114, 103,   0,   0,
       +   0,   0,   0,   0,   0,   0,   0,   0, 109,   0,
       +   0,  91,   0,   0,   0,   0,   0,   0,   0,   0,
       +   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       +   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       +   0, 113,   0, 112, 107,   0,   0,   0,   0,   0,
       +   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       +   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       +   0,   0,   0, 111,   0, 108
       +};
       +static        const        short        yytok2[] =
       +{
       +   2,   3,   0,   0,   0,   0,   0,   0,   0,   0,
       +   0,   0,   0,   0,   0,  17,  18,  19,  20,  21,
       +  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,
       +  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,
       +  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,
       +  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,
       +  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,
       +  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,
       +  82,  83,  84,  85,  86,  87,  88,  89,  90,  92,
       +  93,  94,  95,  96,  97,  98,  99, 105, 106
       +};
       +static        const        long        yytok3[] =
       +{
       +   0
       +};
       +#define YYFLAG                 -1000
       +#define YYERROR                goto yyerrlab
       +#define YYACCEPT        return(0)
       +#define YYABORT                return(1)
       +#define        yyclearin        yychar = -1
       +#define        yyerrok                yyerrflag = 0
       +
       +#ifdef        yydebug
       +#include        "y.debug"
       +#else
       +#define        yydebug                0
       +static        const        char*        yytoknames[1];                /* for debugging */
       +static        const        char*        yystates[1];                /* for debugging */
       +#endif
       +
       +/*        parser for yacc output        */
       +#ifdef YYARG
       +#define        yynerrs                yyarg->yynerrs
       +#define        yyerrflag        yyarg->yyerrflag
       +#define yyval                yyarg->yyval
       +#define yylval                yyarg->yylval
       +#else
       +int        yynerrs = 0;                /* number of errors */
       +int        yyerrflag = 0;                /* error recovery flag */
       +#endif
       +
       +static const char*
       +yytokname(int yyc)
       +{
       +        static char x[10];
       +
       +        if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0]))
       +        if(yytoknames[yyc-1])
       +                return yytoknames[yyc-1];
       +        sprintf(x, "<%d>", yyc);
       +        return x;
       +}
       +
       +static const char*
       +yystatname(int yys)
       +{
       +        static char x[10];
       +
       +        if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0]))
       +        if(yystates[yys])
       +                return yystates[yys];
       +        sprintf(x, "<%d>\n", yys);
       +        return x;
       +}
       +
       +static long
       +#ifdef YYARG
       +yylex1(struct Yyarg *yyarg)
       +#else
       +yylex1(void)
       +#endif
       +{
       +        long yychar;
       +        const long *t3p;
       +        int c;
       +
       +#ifdef YYARG        
       +        yychar = yylex(yyarg);
       +#else
       +        yychar = yylex();
       +#endif
       +        if(yychar <= 0) {
       +                c = yytok1[0];
       +                goto out;
       +        }
       +        if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) {
       +                c = yytok1[yychar];
       +                goto out;
       +        }
       +        if(yychar >= YYPRIVATE)
       +                if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) {
       +                        c = yytok2[yychar-YYPRIVATE];
       +                        goto out;
       +                }
       +        for(t3p=yytok3;; t3p+=2) {
       +                c = t3p[0];
       +                if(c == yychar) {
       +                        c = t3p[1];
       +                        goto out;
       +                }
       +                if(c == 0)
       +                        break;
       +        }
       +        c = 0;
       +
       +out:
       +        if(c == 0)
       +                c = yytok2[1];        /* unknown char */
       +        if(yydebug >= 3)
       +                printf("lex %.4lX %s\n", yychar, yytokname(c));
       +        return c;
       +}
       +
       +int
       +#ifdef YYARG
       +yyparse(struct Yyarg *yyarg)
       +#else
       +yyparse(void)
       +#endif
       +{
       +        struct
       +        {
       +                YYSTYPE        yyv;
       +                int        yys;
       +        } yys[YYMAXDEPTH], *yyp, *yypt;
       +        const short *yyxi;
       +        int yyj, yym, yystate, yyn, yyg;
       +        long yychar;
       +#ifndef YYARG
       +        YYSTYPE save1, save2;
       +        int save3, save4;
       +
       +        save1 = yylval;
       +        save2 = yyval;
       +        save3 = yynerrs;
       +        save4 = yyerrflag;
       +#endif
       +
       +        yystate = 0;
       +        yychar = -1;
       +        yynerrs = 0;
       +        yyerrflag = 0;
       +        yyp = &yys[-1];
       +        goto yystack;
       +
       +ret0:
       +        yyn = 0;
       +        goto ret;
       +
       +ret1:
       +        yyn = 1;
       +        goto ret;
       +
       +ret:
       +#ifndef YYARG
       +        yylval = save1;
       +        yyval = save2;
       +        yynerrs = save3;
       +        yyerrflag = save4;
       +#endif
       +        return yyn;
       +
       +yystack:
       +        /* put a state and value onto the stack */
       +        if(yydebug >= 4)
       +                printf("char %s in %s", yytokname(yychar), yystatname(yystate));
       +
       +        yyp++;
       +        if(yyp >= &yys[YYMAXDEPTH]) {
       +                yyerror("yacc stack overflow");
       +                goto ret1;
       +        }
       +        yyp->yys = yystate;
       +        yyp->yyv = yyval;
       +
       +yynewstate:
       +        yyn = yypact[yystate];
       +        if(yyn <= YYFLAG)
       +                goto yydefault; /* simple state */
       +        if(yychar < 0)
       +#ifdef YYARG
       +                yychar = yylex1(yyarg);
       +#else
       +                yychar = yylex1();
       +#endif
       +        yyn += yychar;
       +        if(yyn < 0 || yyn >= YYLAST)
       +                goto yydefault;
       +        yyn = yyact[yyn];
       +        if(yychk[yyn] == yychar) { /* valid shift */
       +                yychar = -1;
       +                yyval = yylval;
       +                yystate = yyn;
       +                if(yyerrflag > 0)
       +                        yyerrflag--;
       +                goto yystack;
       +        }
       +
       +yydefault:
       +        /* default state action */
       +        yyn = yydef[yystate];
       +        if(yyn == -2) {
       +                if(yychar < 0)
       +#ifdef YYARG
       +                yychar = yylex1(yyarg);
       +#else
       +                yychar = yylex1();
       +#endif
       +
       +                /* look through exception table */
       +                for(yyxi=yyexca;; yyxi+=2)
       +                        if(yyxi[0] == -1 && yyxi[1] == yystate)
       +                                break;
       +                for(yyxi += 2;; yyxi += 2) {
       +                        yyn = yyxi[0];
       +                        if(yyn < 0 || yyn == yychar)
       +                                break;
       +                }
       +                yyn = yyxi[1];
       +                if(yyn < 0)
       +                        goto ret0;
       +        }
       +        if(yyn == 0) {
       +                /* error ... attempt to resume parsing */
       +                switch(yyerrflag) {
       +                case 0:   /* brand new error */
       +                        yyerror("syntax error");
       +                        if(yydebug >= 1) {
       +                                printf("%s", yystatname(yystate));
       +                                printf("saw %s\n", yytokname(yychar));
       +                        }
       +                        goto yyerrlab;
       +                yyerrlab:
       +                        yynerrs++;
       +
       +                case 1:
       +                case 2: /* incompletely recovered error ... try again */
       +                        yyerrflag = 3;
       +
       +                        /* find a state where "error" is a legal shift action */
       +                        while(yyp >= yys) {
       +                                yyn = yypact[yyp->yys] + YYERRCODE;
       +                                if(yyn >= 0 && yyn < YYLAST) {
       +                                        yystate = yyact[yyn];  /* simulate a shift of "error" */
       +                                        if(yychk[yystate] == YYERRCODE)
       +                                                goto yystack;
       +                                }
       +
       +                                /* the current yyp has no shift onn "error", pop stack */
       +                                if(yydebug >= 2)
       +                                        printf("error recovery pops state %d, uncovers %d\n",
       +                                                yyp->yys, (yyp-1)->yys );
       +                                yyp--;
       +                        }
       +                        /* there is no state on the stack with an error shift ... abort */
       +                        goto ret1;
       +
       +                case 3:  /* no shift yet; clobber input char */
       +                        if(yydebug >= YYEOFCODE)
       +                                printf("error recovery discards %s\n", yytokname(yychar));
       +                        if(yychar == YYEOFCODE)
       +                                goto ret1;
       +                        yychar = -1;
       +                        goto yynewstate;   /* try again in the same state */
       +                }
       +        }
       +
       +        /* reduction by production yyn */
       +        if(yydebug >= 2)
       +                printf("reduce %d in:\n\t%s", yyn, yystatname(yystate));
       +
       +        yypt = yyp;
       +        yyp -= yyr2[yyn];
       +        yyval = (yyp+1)->yyv;
       +        yym = yyn;
       +
       +        /* consult goto table to find next state */
       +        yyn = yyr1[yyn];
       +        yyg = yypgo[yyn];
       +        yyj = yyg + yyp->yys + 1;
       +
       +        if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
       +                yystate = yyact[yyg];
       +        switch(yym) {
       +                
       +case 3:
       +#line        63        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ ERROR "syntax error" WARNING; } break;
       +case 6:
       +#line        72        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ codegen = 1; makeiattr(0, 0); } break;
       +case 7:
       +#line        73        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ rightthing(yypt[-2].yyv.o, '}'); yyval.o = yypt[-1].yyv.o; } break;
       +case 8:
       +#line        74        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ y.o=yypt[-0].yyv.o; makevar(yypt[-2].yyv.p,PLACENAME,y); yyval.o = yypt[-0].yyv.o; } break;
       +case 9:
       +#line        75        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ y.o=yypt[-0].yyv.o; makevar(yypt[-3].yyv.p,PLACENAME,y); yyval.o = yypt[-0].yyv.o; } break;
       +case 10:
       +#line        76        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ y.o=yypt[-1].yyv.o; makevar(yypt[-3].yyv.p,PLACENAME,y); yyval.o = yypt[-1].yyv.o; } break;
       +case 11:
       +#line        77        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ y.f = yypt[-1].yyv.f; yyval.o = y.o; yyval.o = makenode(PLACE, 0); } break;
       +case 12:
       +#line        78        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ setdir(yypt[-0].yyv.i); yyval.o = makenode(PLACE, 0); } break;
       +case 13:
       +#line        79        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ printexpr(yypt[-1].yyv.f); yyval.o = makenode(PLACE, 0); } break;
       +case 14:
       +#line        80        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ printpos(yypt[-1].yyv.o); yyval.o = makenode(PLACE, 0); } break;
       +case 15:
       +#line        81        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ printf("%s\n", yypt[-1].yyv.p); free(yypt[-1].yyv.p); yyval.o = makenode(PLACE, 0); } break;
       +case 16:
       +#line        82        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ resetvar(); makeiattr(0, 0); yyval.o = makenode(PLACE, 0); } break;
       +case 22:
       +#line        91        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makevattr(yypt[-0].yyv.p); } break;
       +case 23:
       +#line        92        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makevattr(yypt[-0].yyv.p); } break;
       +case 24:
       +#line        93        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makevattr(yypt[-0].yyv.p); } break;
       +case 25:
       +#line        97        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f=y.f=yypt[-0].yyv.f; makevar(yypt[-2].yyv.p,VARNAME,y); checkscale(yypt[-2].yyv.p); } break;
       +case 26:
       +#line        101        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ copy(); } break;
       +case 29:
       +#line        108        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ copyfile(yypt[-0].yyv.p); } break;
       +case 30:
       +#line        109        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ copydef(yypt[-0].yyv.st); } break;
       +case 31:
       +#line        110        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ copyuntil(yypt[-0].yyv.p); } break;
       +case 32:
       +#line        115        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ forloop(yypt[-8].yyv.p, yypt[-6].yyv.f, yypt[-4].yyv.f, yypt[-2].yyv.i, yypt[-1].yyv.f, yypt[-0].yyv.p); } break;
       +case 33:
       +#line        117        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ forloop(yypt[-5].yyv.p, yypt[-3].yyv.f, yypt[-1].yyv.f, '+', 1.0, yypt[-0].yyv.p); } break;
       +case 34:
       +#line        119        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ forloop(yypt[-8].yyv.p, yypt[-6].yyv.f, yypt[-4].yyv.f, yypt[-2].yyv.i, yypt[-1].yyv.f, yypt[-0].yyv.p); } break;
       +case 35:
       +#line        121        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ forloop(yypt[-5].yyv.p, yypt[-3].yyv.f, yypt[-1].yyv.f, '+', 1.0, yypt[-0].yyv.p); } break;
       +case 36:
       +#line        125        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ ifstat(yypt[-2].yyv.f, yypt[-1].yyv.p, yypt[-0].yyv.p); } break;
       +case 37:
       +#line        126        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ ifstat(yypt[-1].yyv.f, yypt[-0].yyv.p, (char *) 0); } break;
       +case 39:
       +#line        130        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = strcmp(yypt[-2].yyv.p,yypt[-0].yyv.p) == 0; free(yypt[-2].yyv.p); free(yypt[-0].yyv.p); } break;
       +case 40:
       +#line        131        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = strcmp(yypt[-2].yyv.p,yypt[-0].yyv.p) != 0; free(yypt[-2].yyv.p); free(yypt[-0].yyv.p); } break;
       +case 41:
       +#line        135        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ y.f = 0; makevar(yypt[-0].yyv.p, VARNAME, y); } break;
       +case 42:
       +#line        138        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.i = '+'; } break;
       +case 43:
       +#line        139        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.i = '-'; } break;
       +case 44:
       +#line        140        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.i = '*'; } break;
       +case 45:
       +#line        141        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.i = '/'; } break;
       +case 46:
       +#line        142        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.i = ' '; } break;
       +case 47:
       +#line        147        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = leftthing('{'); } break;
       +case 48:
       +#line        151        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = boxgen(); } break;
       +case 49:
       +#line        152        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = circgen(yypt[-1].yyv.i); } break;
       +case 50:
       +#line        153        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = circgen(yypt[-1].yyv.i); } break;
       +case 51:
       +#line        154        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = arcgen(yypt[-1].yyv.i); } break;
       +case 52:
       +#line        155        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = linegen(yypt[-1].yyv.i); } break;
       +case 53:
       +#line        156        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = linegen(yypt[-1].yyv.i); } break;
       +case 54:
       +#line        157        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = linegen(yypt[-1].yyv.i); } break;
       +case 55:
       +#line        158        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = movegen(); } break;
       +case 56:
       +#line        159        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = textgen(); } break;
       +case 57:
       +#line        160        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = troffgen(yypt[-0].yyv.p); } break;
       +case 58:
       +#line        161        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o=rightthing(yypt[-2].yyv.o,']'); } break;
       +case 59:
       +#line        162        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = blockgen(yypt[-4].yyv.o, yypt[-1].yyv.o); } break;
       +case 60:
       +#line        166        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = leftthing('['); } break;
       +case 63:
       +#line        175        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makefattr(yypt[-1].yyv.i, !DEFAULT, yypt[-0].yyv.f); } break;
       +case 64:
       +#line        176        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makefattr(yypt[-0].yyv.i, DEFAULT, 0.0); } break;
       +case 65:
       +#line        177        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makefattr(curdir(), !DEFAULT, yypt[-0].yyv.f); } break;
       +case 66:
       +#line        178        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makefattr(yypt[-1].yyv.i, !DEFAULT, yypt[-0].yyv.f); } break;
       +case 67:
       +#line        179        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makefattr(yypt[-0].yyv.i, DEFAULT, 0.0); } break;
       +case 68:
       +#line        180        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makeoattr(yypt[-1].yyv.i, yypt[-0].yyv.o); } break;
       +case 69:
       +#line        181        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makeoattr(yypt[-1].yyv.i, yypt[-0].yyv.o); } break;
       +case 70:
       +#line        182        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makeoattr(yypt[-1].yyv.i, yypt[-0].yyv.o); } break;
       +case 71:
       +#line        183        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makeoattr(yypt[-1].yyv.i, yypt[-0].yyv.o); } break;
       +case 72:
       +#line        184        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makeiattr(WITH, yypt[-0].yyv.i); } break;
       +case 73:
       +#line        185        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makeoattr(PLACE, getblock(getlast(1,BLOCK), yypt[-0].yyv.p)); } break;
       +case 74:
       +#line        187        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makeoattr(PLACE, getpos(getblock(getlast(1,BLOCK), yypt[-1].yyv.p), yypt[-0].yyv.i)); } break;
       +case 75:
       +#line        188        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makeoattr(PLACE, yypt[-0].yyv.o); } break;
       +case 76:
       +#line        189        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makeiattr(SAME, yypt[-0].yyv.i); } break;
       +case 77:
       +#line        190        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ maketattr(yypt[-0].yyv.i, (char *) 0); } break;
       +case 78:
       +#line        191        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makeiattr(HEAD, yypt[-0].yyv.i); } break;
       +case 79:
       +#line        192        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makefattr(DOT, !DEFAULT, yypt[-0].yyv.f); } break;
       +case 80:
       +#line        193        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makefattr(DOT, DEFAULT, 0.0); } break;
       +case 81:
       +#line        194        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makefattr(DASH, !DEFAULT, yypt[-0].yyv.f); } break;
       +case 82:
       +#line        195        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makefattr(DASH, DEFAULT, 0.0); } break;
       +case 83:
       +#line        196        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makefattr(CHOP, !DEFAULT, yypt[-0].yyv.f); } break;
       +case 84:
       +#line        197        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makefattr(CHOP, DEFAULT, 0.0); } break;
       +case 85:
       +#line        198        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makefattr(FILL, !DEFAULT, yypt[-0].yyv.f); } break;
       +case 86:
       +#line        199        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ makefattr(FILL, DEFAULT, 0.0); } break;
       +case 90:
       +#line        208        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ maketattr(CENTER, yypt[-0].yyv.p); } break;
       +case 91:
       +#line        209        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ maketattr(yypt[-0].yyv.i, yypt[-1].yyv.p); } break;
       +case 92:
       +#line        210        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ addtattr(yypt[-0].yyv.i); } break;
       +case 94:
       +#line        214        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.p = sprintgen(yypt[-1].yyv.p); } break;
       +case 95:
       +#line        215        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.p = sprintgen(yypt[-3].yyv.p); } break;
       +case 96:
       +#line        219        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ exprsave(yypt[-0].yyv.f); yyval.i = 0; } break;
       +case 97:
       +#line        220        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ exprsave(yypt[-0].yyv.f); } break;
       +case 99:
       +#line        225        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = yypt[-1].yyv.o; } break;
       +case 100:
       +#line        226        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = makepos(yypt[-2].yyv.f, yypt[-0].yyv.f); } break;
       +case 101:
       +#line        227        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = fixpos(yypt[-4].yyv.o, yypt[-2].yyv.f, yypt[-0].yyv.f); } break;
       +case 102:
       +#line        228        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = fixpos(yypt[-4].yyv.o, -yypt[-2].yyv.f, -yypt[-0].yyv.f); } break;
       +case 103:
       +#line        229        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = fixpos(yypt[-6].yyv.o, yypt[-3].yyv.f, yypt[-1].yyv.f); } break;
       +case 104:
       +#line        230        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = fixpos(yypt[-6].yyv.o, -yypt[-3].yyv.f, -yypt[-1].yyv.f); } break;
       +case 105:
       +#line        231        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = addpos(yypt[-2].yyv.o, yypt[-0].yyv.o); } break;
       +case 106:
       +#line        232        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = subpos(yypt[-2].yyv.o, yypt[-0].yyv.o); } break;
       +case 107:
       +#line        233        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = makepos(getcomp(yypt[-3].yyv.o,DOTX), getcomp(yypt[-1].yyv.o,DOTY)); } break;
       +case 108:
       +#line        234        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = makebetween(yypt[-5].yyv.f, yypt[-3].yyv.o, yypt[-1].yyv.o); } break;
       +case 109:
       +#line        235        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = makebetween(yypt[-4].yyv.f, yypt[-2].yyv.o, yypt[-0].yyv.o); } break;
       +case 110:
       +#line        239        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ y = getvar(yypt[-0].yyv.p); yyval.o = y.o; } break;
       +case 111:
       +#line        240        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ y = getvar(yypt[-1].yyv.p); yyval.o = getpos(y.o, yypt[-0].yyv.i); } break;
       +case 112:
       +#line        241        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ y = getvar(yypt[-0].yyv.p); yyval.o = getpos(y.o, yypt[-1].yyv.i); } break;
       +case 113:
       +#line        242        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = gethere(); } break;
       +case 114:
       +#line        243        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = getlast(yypt[-1].yyv.i, yypt[-0].yyv.i); } break;
       +case 115:
       +#line        244        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = getpos(getlast(yypt[-2].yyv.i, yypt[-1].yyv.i), yypt[-0].yyv.i); } break;
       +case 116:
       +#line        245        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = getpos(getlast(yypt[-1].yyv.i, yypt[-0].yyv.i), yypt[-2].yyv.i); } break;
       +case 117:
       +#line        246        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = getfirst(yypt[-1].yyv.i, yypt[-0].yyv.i); } break;
       +case 118:
       +#line        247        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = getpos(getfirst(yypt[-2].yyv.i, yypt[-1].yyv.i), yypt[-0].yyv.i); } break;
       +case 119:
       +#line        248        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = getpos(getfirst(yypt[-1].yyv.i, yypt[-0].yyv.i), yypt[-2].yyv.i); } break;
       +case 121:
       +#line        250        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = getpos(yypt[-1].yyv.o, yypt[-0].yyv.i); } break;
       +case 122:
       +#line        251        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = getpos(yypt[-0].yyv.o, yypt[-1].yyv.i); } break;
       +case 123:
       +#line        255        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = getblock(getlast(yypt[-3].yyv.i,yypt[-2].yyv.i), yypt[-0].yyv.p); } break;
       +case 124:
       +#line        256        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.o = getblock(getfirst(yypt[-3].yyv.i,yypt[-2].yyv.i), yypt[-0].yyv.p); } break;
       +case 125:
       +#line        257        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ y = getvar(yypt[-2].yyv.p); yyval.o = getblock(y.o, yypt[-0].yyv.p); } break;
       +case 126:
       +#line        261        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.i = yypt[-1].yyv.i + 1; } break;
       +case 127:
       +#line        262        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.i = yypt[-1].yyv.i; } break;
       +case 128:
       +#line        263        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.i = 1; } break;
       +case 138:
       +#line        279        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = getfval(yypt[-0].yyv.p); } break;
       +case 140:
       +#line        281        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-2].yyv.f + yypt[-0].yyv.f; } break;
       +case 141:
       +#line        282        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-2].yyv.f - yypt[-0].yyv.f; } break;
       +case 142:
       +#line        283        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-2].yyv.f * yypt[-0].yyv.f; } break;
       +case 143:
       +#line        284        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ if (yypt[-0].yyv.f == 0.0) {
       +                                        ERROR "division by 0" WARNING; yypt[-0].yyv.f = 1; }
       +                                  yyval.f = yypt[-2].yyv.f / yypt[-0].yyv.f; } break;
       +case 144:
       +#line        287        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ if ((long)yypt[-0].yyv.f == 0) {
       +                                        ERROR "mod division by 0" WARNING; yypt[-0].yyv.f = 1; }
       +                                  yyval.f = (long)yypt[-2].yyv.f % (long)yypt[-0].yyv.f; } break;
       +case 145:
       +#line        290        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = -yypt[-0].yyv.f; } break;
       +case 146:
       +#line        291        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-1].yyv.f; } break;
       +case 147:
       +#line        292        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = getcomp(yypt[-1].yyv.o, yypt[-0].yyv.i); } break;
       +case 148:
       +#line        293        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = getcomp(yypt[-1].yyv.o, yypt[-0].yyv.i); } break;
       +case 149:
       +#line        294        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = getcomp(yypt[-1].yyv.o, yypt[-0].yyv.i); } break;
       +case 150:
       +#line        295        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = getcomp(yypt[-1].yyv.o, yypt[-0].yyv.i); } break;
       +case 151:
       +#line        296        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = getcomp(yypt[-1].yyv.o, yypt[-0].yyv.i); } break;
       +case 152:
       +#line        297        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ y = getvar(yypt[-2].yyv.p); yyval.f = getblkvar(y.o, yypt[-0].yyv.p); } break;
       +case 153:
       +#line        298        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = getblkvar(getlast(yypt[-3].yyv.i,yypt[-2].yyv.i), yypt[-0].yyv.p); } break;
       +case 154:
       +#line        299        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = getblkvar(getfirst(yypt[-3].yyv.i,yypt[-2].yyv.i), yypt[-0].yyv.p); } break;
       +case 155:
       +#line        300        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-2].yyv.f > yypt[-0].yyv.f; } break;
       +case 156:
       +#line        301        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-2].yyv.f < yypt[-0].yyv.f; } break;
       +case 157:
       +#line        302        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-2].yyv.f <= yypt[-0].yyv.f; } break;
       +case 158:
       +#line        303        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-2].yyv.f >= yypt[-0].yyv.f; } break;
       +case 159:
       +#line        304        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-2].yyv.f == yypt[-0].yyv.f; } break;
       +case 160:
       +#line        305        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-2].yyv.f != yypt[-0].yyv.f; } break;
       +case 161:
       +#line        306        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-2].yyv.f && yypt[-0].yyv.f; } break;
       +case 162:
       +#line        307        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-2].yyv.f || yypt[-0].yyv.f; } break;
       +case 163:
       +#line        308        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = !(yypt[-0].yyv.f); } break;
       +case 164:
       +#line        309        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = Log10(yypt[-1].yyv.f); } break;
       +case 165:
       +#line        310        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = Exp(yypt[-1].yyv.f * log(10.0)); } break;
       +case 166:
       +#line        311        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = pow(yypt[-2].yyv.f, yypt[-0].yyv.f); } break;
       +case 167:
       +#line        312        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = sin(yypt[-1].yyv.f); } break;
       +case 168:
       +#line        313        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = cos(yypt[-1].yyv.f); } break;
       +case 169:
       +#line        314        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = atan2(yypt[-3].yyv.f, yypt[-1].yyv.f); } break;
       +case 170:
       +#line        315        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = Sqrt(yypt[-1].yyv.f); } break;
       +case 171:
       +#line        316        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = (float)rand() / 32767.0; /* might be 2^31-1 */ } break;
       +case 172:
       +#line        317        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-3].yyv.f >= yypt[-1].yyv.f ? yypt[-3].yyv.f : yypt[-1].yyv.f; } break;
       +case 173:
       +#line        318        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = yypt[-3].yyv.f <= yypt[-1].yyv.f ? yypt[-3].yyv.f : yypt[-1].yyv.f; } break;
       +case 174:
       +#line        319        "/usr/local/plan9/src/cmd/tpic/picy.y"
       +{ yyval.f = (long) yypt[-1].yyv.f; } break;
       +        }
       +        goto yystack;  /* stack new state and value */
       +}
 (DIR) diff --git a/src/cmd/tpic/picy.y b/src/cmd/tpic/picy.y
       t@@ -0,0 +1,320 @@
       +%{
       +#include <stdio.h>
       +#include "pic.h"
       +#include <math.h>
       +YYSTYPE        y;
       +int yylex(void);
       +%}
       +
       +%token        <i>        BOX        1        /* DON'T CHANGE THESE! */
       +%token        <i>        LINE        2
       +%token        <i>        ARROW        3
       +%token        <i>        CIRCLE        4
       +%token        <i>        ELLIPSE        5
       +%token        <i>        ARC        6
       +%token        <i>        SPLINE        7
       +%token        <i>        BLOCK        8
       +%token        <p>        TEXT        9
       +%token        <p>        TROFF        10
       +%token        <i>        MOVE        11
       +%token        <i>        BLOCKEND 12
       +%token        <i>        PLACE        13
       +%token        <i>        PRINT RESET THRU UNTIL
       +%token        <o>        FOR IF COPY
       +%token        <p>        THENSTR ELSESTR DOSTR PLACENAME VARNAME SPRINTF
       +%token        <st>        DEFNAME
       +%token        <i>        ATTR TEXTATTR
       +%token        <i>        LEFT RIGHT UP DOWN FROM TO AT BY WITH HEAD CW CCW THEN
       +%token        <i>        HEIGHT WIDTH RADIUS DIAMETER LENGTH SIZE
       +%token        <i>        CORNER HERE LAST NTH SAME BETWEEN AND
       +%token        <i>        EAST WEST NORTH SOUTH NE NW SE SW START END
       +%token        <i>        DOTX DOTY DOTHT DOTWID DOTRAD
       +%token        <f>        NUMBER
       +%token        <f>        LOG EXP SIN COS ATAN2 SQRT RAND MIN MAX INT
       +%token        <i>        DIR
       +%token        <i>        DOT DASH CHOP FILL
       +%token        <o>        ST        /* statement terminator */
       +
       +%right        <f>        '='
       +%left        <f>        OROR
       +%left        <f>        ANDAND
       +%nonassoc <f>        GT LT LE GE EQ NEQ
       +%left        <f>        '+' '-'
       +%left        <f>        '*' '/' '%'
       +%right        <f>        UMINUS NOT
       +%right        <f>        '^'
       +
       +%type        <f>        expr if_expr asgn
       +%type        <p>        name text
       +%type        <i>        optop exprlist
       +%type        <o>        if for copy
       +
       +/* this is a lie:  picture and position are really the whole union */
       +%type        <o>        leftbrace picture piclist position lbracket
       +%type        <o>        prim place blockname
       +%type        <i>        textlist textattr        /* not a sensible value */
       +%type        <i>        last type
       +
       +%%
       +
       +top:
       +          piclist
       +        | /* empty */
       +        | error                { ERROR "syntax error" WARNING; }
       +        ;
       +
       +piclist:
       +          picture
       +        | piclist picture
       +        ;
       +
       +picture:
       +          prim ST                        { codegen = 1; makeiattr(0, 0); }
       +        | leftbrace piclist '}'                { rightthing($1, '}'); $$ = $2; }
       +        | PLACENAME ':' picture                { y.o=$3; makevar($1,PLACENAME,y); $$ = $3; }
       +        | PLACENAME ':' ST picture        { y.o=$4; makevar($1,PLACENAME,y); $$ = $4; }
       +        | PLACENAME ':' position ST        { y.o=$3; makevar($1,PLACENAME,y); $$ = $3; }
       +        | asgn ST                        { y.f = $1; $$ = y.o; $$ = makenode(PLACE, 0); }
       +        | DIR                                { setdir($1); $$ = makenode(PLACE, 0); }
       +        | PRINT expr ST                        { printexpr($2); $$ = makenode(PLACE, 0); }
       +        | PRINT position ST                { printpos($2); $$ = makenode(PLACE, 0); }
       +        | PRINT text ST                        { printf("%s\n", $2); free($2); $$ = makenode(PLACE, 0); }
       +        | RESET varlist ST                { resetvar(); makeiattr(0, 0); $$ = makenode(PLACE, 0); }
       +        | copy
       +        | for
       +        | if
       +        | ST
       +        ;
       +
       +varlist:
       +          /* empty */
       +        | VARNAME                { makevattr($1); }
       +        | varlist VARNAME        { makevattr($2); }
       +        | varlist ',' VARNAME        { makevattr($3); }
       +        ;
       +
       +asgn:
       +          VARNAME '=' expr        { $$=y.f=$3; makevar($1,VARNAME,y); checkscale($1); }
       +        ;
       +
       +copy:
       +          COPY copylist                { copy(); }
       +        ;
       +copylist:
       +          copyattr
       +        | copylist copyattr
       +        ;
       +copyattr:
       +          text                        { copyfile($1); }
       +        | THRU DEFNAME                { copydef($2); }
       +        | UNTIL text                { copyuntil($2); }
       +        ;
       +
       +for:
       +          FOR name FROM expr TO expr BY optop expr DOSTR
       +                { forloop($2, $4, $6, $8, $9, $10); }
       +        | FOR name FROM expr TO expr DOSTR
       +                { forloop($2, $4, $6, '+', 1.0, $7); }
       +        | FOR name '=' expr TO expr BY optop expr DOSTR
       +                { forloop($2, $4, $6, $8, $9, $10); }
       +        | FOR name '=' expr TO expr DOSTR
       +                { forloop($2, $4, $6, '+', 1.0, $7); }
       +        ;
       +
       +if:
       +          IF if_expr THENSTR ELSESTR        { ifstat($2, $3, $4); }
       +        | IF if_expr THENSTR                { ifstat($2, $3, (char *) 0); }
       +        ;
       +if_expr:
       +          expr
       +        | text EQ text                { $$ = strcmp($1,$3) == 0; free($1); free($3); }
       +        | text NEQ text                { $$ = strcmp($1,$3) != 0; free($1); free($3); }
       +        ;
       +
       +name:
       +          VARNAME        { y.f = 0; makevar($1, VARNAME, y); }
       +        ;
       +optop:
       +          '+'                { $$ = '+'; }
       +        | '-'                { $$ = '-'; }
       +        | '*'                { $$ = '*'; }
       +        | '/'                { $$ = '/'; }
       +        | /* empty */        { $$ = ' '; }
       +        ;
       +
       +
       +leftbrace:
       +          '{'                        { $$ = leftthing('{'); }
       +        ;
       +
       +prim:
       +          BOX attrlist                { $$ = boxgen(); }
       +        | CIRCLE attrlist        { $$ = circgen($1); }
       +        | ELLIPSE attrlist        { $$ = circgen($1); }
       +        | ARC attrlist                { $$ = arcgen($1); }
       +        | LINE attrlist                { $$ = linegen($1); }
       +        | ARROW attrlist        { $$ = linegen($1); }
       +        | SPLINE attrlist        { $$ = linegen($1); }
       +        | MOVE attrlist                { $$ = movegen(); }
       +        | textlist attrlist        { $$ = textgen(); }
       +        | TROFF                        { $$ = troffgen($1); }
       +        | lbracket piclist ']' { $<o>$=rightthing($1,']'); } attrlist
       +                                { $$ = blockgen($1, $<o>4); }
       +        ;
       +
       +lbracket:
       +          '['                        { $$ = leftthing('['); }
       +        ;
       +
       +attrlist:
       +          attrlist attr
       +        | /* empty */
       +        ;
       +
       +attr:
       +          ATTR expr                { makefattr($1, !DEFAULT, $2); }
       +        | ATTR                        { makefattr($1, DEFAULT, 0.0); }
       +        | expr                        { makefattr(curdir(), !DEFAULT, $1); }
       +        | DIR expr                { makefattr($1, !DEFAULT, $2); }
       +        | DIR                        { makefattr($1, DEFAULT, 0.0); }
       +        | FROM position                { makeoattr($1, $2); }
       +        | TO position                { makeoattr($1, $2); }
       +        | AT position                { makeoattr($1, $2); }
       +        | BY position                { makeoattr($1, $2); }
       +        | WITH CORNER                { makeiattr(WITH, $2); }
       +        | WITH '.' PLACENAME        { makeoattr(PLACE, getblock(getlast(1,BLOCK), $3)); }
       +        | WITH '.' PLACENAME CORNER
       +                { makeoattr(PLACE, getpos(getblock(getlast(1,BLOCK), $3), $4)); }
       +        | WITH position                { makeoattr(PLACE, $2); }
       +        | SAME                        { makeiattr(SAME, $1); }
       +        | TEXTATTR                { maketattr($1, (char *) 0); }
       +        | HEAD                        { makeiattr(HEAD, $1); }
       +        | DOT expr                { makefattr(DOT, !DEFAULT, $2); }
       +        | DOT                        { makefattr(DOT, DEFAULT, 0.0); }
       +        | DASH expr                { makefattr(DASH, !DEFAULT, $2); }
       +        | DASH                        { makefattr(DASH, DEFAULT, 0.0); }
       +        | CHOP expr                { makefattr(CHOP, !DEFAULT, $2); }
       +        | CHOP                        { makefattr(CHOP, DEFAULT, 0.0); }
       +        | FILL expr                { makefattr(FILL, !DEFAULT, $2); }
       +        | FILL                        { makefattr(FILL, DEFAULT, 0.0); }
       +        | textlist
       +        ;
       +
       +textlist:
       +          textattr
       +        | textlist textattr
       +        ;
       +textattr:
       +          text                        { maketattr(CENTER, $1); }
       +        | text TEXTATTR                { maketattr($2, $1); }
       +        | textattr TEXTATTR        { addtattr($2); }
       +        ;
       +text:
       +          TEXT
       +        | SPRINTF '(' text ')'                        { $$ = sprintgen($3); }
       +        | SPRINTF '(' text ',' exprlist ')'        { $$ = sprintgen($3); }
       +        ;
       +
       +exprlist:
       +          expr                        { exprsave($1); $$ = 0; }
       +        | exprlist ',' expr        { exprsave($3); }
       +        ;
       +
       +position:                /* absolute, not relative */
       +          place
       +        | '(' position ')'                        { $$ = $2; }
       +        | expr ',' expr                                { $$ = makepos($1, $3); }
       +        | position '+' expr ',' expr                { $$ = fixpos($1, $3, $5); }
       +        | position '-' expr ',' expr                { $$ = fixpos($1, -$3, -$5); }
       +        | position '+' '(' expr ',' expr ')'        { $$ = fixpos($1, $4, $6); }
       +        | position '-' '(' expr ',' expr ')'        { $$ = fixpos($1, -$4, -$6); }
       +        | position '+' place                        { $$ = addpos($1, $3); }
       +        | position '-' place                        { $$ = subpos($1, $3); }
       +        | '(' place ',' place ')'        { $$ = makepos(getcomp($2,DOTX), getcomp($4,DOTY)); }
       +        | expr LT position ',' position GT        { $$ = makebetween($1, $3, $5); }
       +        | expr BETWEEN position AND position        { $$ = makebetween($1, $3, $5); }
       +        ;
       +
       +place:
       +          PLACENAME                { y = getvar($1); $$ = y.o; }
       +        | PLACENAME CORNER        { y = getvar($1); $$ = getpos(y.o, $2); }
       +        | CORNER PLACENAME        { y = getvar($2); $$ = getpos(y.o, $1); }
       +        | HERE                        { $$ = gethere(); }
       +        | last type                { $$ = getlast($1, $2); }
       +        | last type CORNER        { $$ = getpos(getlast($1, $2), $3); }
       +        | CORNER last type        { $$ = getpos(getlast($2, $3), $1); }
       +        | NTH type                { $$ = getfirst($1, $2); }
       +        | NTH type CORNER        { $$ = getpos(getfirst($1, $2), $3); }
       +        | CORNER NTH type        { $$ = getpos(getfirst($2, $3), $1); }
       +        | blockname
       +        | blockname CORNER        { $$ = getpos($1, $2); }
       +        | CORNER blockname        { $$ = getpos($2, $1); }
       +        ;
       +
       +blockname:
       +          last BLOCK '.' PLACENAME        { $$ = getblock(getlast($1,$2), $4); }
       +        | NTH BLOCK '.' PLACENAME        { $$ = getblock(getfirst($1,$2), $4); }
       +        | PLACENAME '.' PLACENAME        { y = getvar($1); $$ = getblock(y.o, $3); }
       +        ;
       +
       +last:
       +          last LAST                { $$ = $1 + 1; }
       +        | NTH LAST                { $$ = $1; }
       +        | LAST                        { $$ = 1; }
       +        ;
       +
       +type:
       +          BOX
       +        | CIRCLE
       +        | ELLIPSE
       +        | ARC
       +        | LINE
       +        | ARROW
       +        | SPLINE
       +        | BLOCK
       +        ;
       +
       +expr:
       +          NUMBER
       +        | VARNAME                { $$ = getfval($1); }
       +        | asgn
       +        | expr '+' expr                { $$ = $1 + $3; }
       +        | expr '-' expr                { $$ = $1 - $3; }
       +        | expr '*' expr                { $$ = $1 * $3; }
       +        | expr '/' expr                { if ($3 == 0.0) {
       +                                        ERROR "division by 0" WARNING; $3 = 1; }
       +                                  $$ = $1 / $3; }
       +        | expr '%' expr                { if ((long)$3 == 0) {
       +                                        ERROR "mod division by 0" WARNING; $3 = 1; }
       +                                  $$ = (long)$1 % (long)$3; }
       +        | '-' expr %prec UMINUS        { $$ = -$2; }
       +        | '(' expr ')'                { $$ = $2; }
       +        | place DOTX                { $$ = getcomp($1, $2); }
       +        | place DOTY                { $$ = getcomp($1, $2); }
       +        | place DOTHT                { $$ = getcomp($1, $2); }
       +        | place DOTWID                { $$ = getcomp($1, $2); }
       +        | place DOTRAD                { $$ = getcomp($1, $2); }
       +        | PLACENAME '.' VARNAME        { y = getvar($1); $$ = getblkvar(y.o, $3); }
       +        | last BLOCK '.' VARNAME { $$ = getblkvar(getlast($1,$2), $4); }
       +        | NTH BLOCK '.' VARNAME        { $$ = getblkvar(getfirst($1,$2), $4); }
       +        | expr GT expr                { $$ = $1 > $3; }
       +        | expr LT expr                { $$ = $1 < $3; }
       +        | expr LE expr                { $$ = $1 <= $3; }
       +        | expr GE expr                { $$ = $1 >= $3; }
       +        | expr EQ expr                { $$ = $1 == $3; }
       +        | expr NEQ expr                { $$ = $1 != $3; }
       +        | expr ANDAND expr        { $$ = $1 && $3; }
       +        | expr OROR expr        { $$ = $1 || $3; }
       +        | NOT expr                { $$ = !($2); }
       +        | LOG '(' expr ')'                { $$ = Log10($3); }
       +        | EXP '(' expr ')'                { $$ = Exp($3 * log(10.0)); }
       +        | expr '^' expr                        { $$ = pow($1, $3); }
       +        | SIN '(' expr ')'                { $$ = sin($3); }
       +        | COS '(' expr ')'                { $$ = cos($3); }
       +        | ATAN2 '(' expr ',' expr ')'        { $$ = atan2($3, $5); }
       +        | SQRT '(' expr ')'                { $$ = Sqrt($3); }
       +        | RAND '(' ')'                        { $$ = (float)rand() / 32767.0; /* might be 2^31-1 */ }
       +        | MAX '(' expr ',' expr ')'        { $$ = $3 >= $5 ? $3 : $5; }
       +        | MIN '(' expr ',' expr ')'        { $$ = $3 <= $5 ? $3 : $5; }
       +        | INT '(' expr ')'                { $$ = (long) $3; }
       +        ;
 (DIR) diff --git a/src/cmd/tpic/pltex.c b/src/cmd/tpic/pltex.c
       t@@ -0,0 +1,149 @@
       +/* replacement for pltroff.c to produce a TeX file that makes a box */
       +
       +#include <stdio.h>
       +#include <math.h>
       +#include "pic.h"
       +
       +double rangex, rangey;  /* probably already available inside pic somewhere */
       +extern int dbg;
       +int frameno;
       +
       +/*-----------copied from old version----------*/
       +
       +void
       +arrow(double x0, double y0, double x1, double y1, double w, double h, double ang, int nhead)         /* draw arrow (without shaft) */
       +        /* head wid w, len h, rotated ang */
       +        /* and drawn with nhead lines */
       +{
       +        double alpha, rot, drot, hyp;
       +        float dx, dy;
       +        int i;
       +
       +        rot = atan2(w / 2, h);
       +        hyp = sqrt(w/2 * w/2 + h * h);
       +        alpha = atan2(y1-y0, x1-x0) + ang;
       +        if (nhead < 2)
       +                nhead = 2;
       +        for (i = nhead-1; i >= 0; i--) {
       +                drot = 2 * rot / (double) (nhead-1) * (double) i;
       +                dx = hyp * cos(alpha + PI - rot + drot);
       +                dy = hyp * sin(alpha + PI - rot + drot);
       +                line(x1+dx, y1+dy, x1, y1);
       +        }
       +}
       +
       +
       +/*-----------new code----------*/
       +
       +void
       +printlf(int line, char *name)
       +{
       +}
       +
       +void
       +fillstart(double v)        /* only choose black, light grey (.75), or white, for now */
       +{
       +        if (v<.05)
       +                fprintf(TEXFILE, "    \\special{bk}%%\n");
       +        else if (v>.95)
       +                fprintf(TEXFILE, "    \\special{wh}%%\n");
       +        else
       +                fprintf(TEXFILE, "    \\special{sh}%%\n");
       +}
       +
       +void
       +fillend(void)
       +{
       +}
       +
       +void
       +troff(char *s)
       +{
       +        int size;
       +
       +        if (strncmp(s, ".ps", 3) == 0) {
       +            if (sscanf(&s[3], " %d ", &size) > 0) {
       +                fprintf(TEXFILE, "    \\special{pn %d}%%\n", size);
       +                e1->pdiam = size;
       +            } else fprintf(stderr, "Malformed .ps command: %s\n", s);
       +        }
       +}
       +
       +
       +void
       +space(double x0, double y0, double x1, double y1)        /* set limits of page */
       +{
       +        e0->sidex = e1->sidex = deltx*1000;
       +        e0->sidey = e1->sidey = e0->bottom = e1->bottom = delty*1000;
       +        range(x0, y0, x1, y1);
       +}
       +
       +void
       +dot(void)
       +{
       +        /* use .005" radius at nominal 9pt pen size */
       +        disc(e1->copyx,e1->copyy,(e1->pdiam/9.0)*(4.3/e1->scalex));
       +}
       +
       +void
       +label(char *s, int t, int nh)        /* text s of type t nh half-lines up */
       +{
       +        double nem;
       +
       +        if (t & ABOVE)
       +                nh++;
       +        else if (t & BELOW)
       +                nh--;
       +        nem = .2 - nh*.6;
       +        fprintf(TEXFILE,"    \\rlap{\\kern %6.3fin\\lower%6.3fin\\hbox{\\lower%5.2fem\\hbox to 0pt{",
       +                        INCHES(DTRX(e1->copyx)), INCHES(DTRY(e1->copyy)), nem);
       +        fprintf(TEXFILE,t&LJUST?"%s\\hss":(t&RJUST?"\\hss %s":"\\hss %s\\hss"),s);
       +        fprintf(TEXFILE,"}}}%%\n");
       +}
       +
       +void
       +spline(double x, double y, double/*sic*/ n, float *p, int dashed, double ddval)
       +{
       +        int k, j;
       +
       +        fprintf(TEXFILE,"    \\special{pa %d %d}%%\n",TRX(x),TRY(y));
       +        for(k=0, j=0; k<n; k++, j+=2){
       +                x += p[j];
       +                y += p[j+1];
       +                fprintf(TEXFILE,"    \\special{pa %d %d}%%\n",TRX(x),TRY(y));
       +        }
       +        fprintf(TEXFILE,"    \\special{sp}%%\n");
       +}
       +
       +void
       +ellipse(double x, double y, double r1, double r2)
       +{
       +        fprintf(TEXFILE, "    \\special{ar %d %d %d %d 0.0 6.2832}%%\n",
       +                TRX(x), TRY(y), SCX(r1), -SCY(r2));
       +}
       +
       +void
       +arc(double xc, double yc, double x0, double y0, double x1, double y1)        /* draw arc with center xc,yc */
       +{
       +        devarc(x0, y0, x1, y1, xc, yc, 1 );   /* radius=1 means counterclockwise */
       +}
       +
       +/* If NOEXPANDDASH is defined, use this instead of the normal dotline
       + * in print().  This dotline relies on vec() noticing that e1->pen
       + * is not SOLIDPEN, and putting out a call to a different postscript
       + * routine.
       + */
       +#ifdef NOEXPANDDASH
       +
       +void
       +dotline(double x0, double y0, double x1, double y1, int ddtype, double ddval)
       +{
       +        if (ddval != 0)
       +                e1->dashlen = ddval;
       +        e1->pen = (ddtype&DOTBIT)? DOTPEN : DASHPEN;
       +        move(x0, y0);
       +        vec(x1, y1);
       +        e1->pen = SOLIDPEN;
       +        e1->dashlen = e0->dashlen;
       +}
       +#endif
 (DIR) diff --git a/src/cmd/tpic/print.c b/src/cmd/tpic/print.c
       t@@ -0,0 +1,210 @@
       +#include        <stdio.h>
       +#include        <math.h>
       +#include        "pic.h"
       +#include        "y.tab.h"
       +
       +void
       +print(void)
       +{
       +        obj *p;
       +        int i, j, k, m;
       +        double x0, y0, x1, y1, ox, oy, dx, dy, ndx, ndy;
       +
       +        for (i = 0; i < nobj; i++) {
       +                p = objlist[i];
       +                ox = p->o_x;
       +                oy = p->o_y;
       +                x1 = 0;
       +                if (p->o_count >= 1)
       +                        x1 = p->o_val[0];
       +                y1 = 0;
       +                if (p->o_count >= 2)
       +                        y1 = p->o_val[1];
       +                m = p->o_mode;
       +                switch (p->o_type) {
       +                case TROFF:
       +                        troff(text[p->o_nt1].t_val);
       +                        break;
       +                case BOX:
       +                case BLOCK:
       +                        x0 = ox - x1 / 2;
       +                        y0 = oy - y1 / 2;
       +                        x1 = ox + x1 / 2;
       +                        y1 = oy + y1 / 2;
       +                        if (p->o_attr & FILLBIT) {
       +                                move(x0, y0);
       +                                fillstart(p->o_fillval);
       +                        }
       +                        if (p->o_attr & INVIS || p->o_type == BLOCK)
       +                                ;        /* nothing at all */
       +                        else if (p->o_attr & (DOTBIT|DASHBIT))
       +                                dotbox(x0, y0, x1, y1, p->o_attr, p->o_ddval);
       +                        else
       +                                box(x0, y0, x1, y1);
       +                        if (p->o_attr & FILLBIT)
       +                                fillend();
       +                        move(ox, oy);
       +                        dotext(p);        /* if there are any text strings */
       +                        if (ishor(m))
       +                                move(isright(m) ? x1 : x0, oy);        /* right side */
       +                        else
       +                                move(ox, isdown(m) ? y0 : y1);        /* bottom */
       +                        break;
       +                case BLOCKEND:
       +                        break;
       +                case CIRCLE:
       +                        if (p->o_attr & FILLBIT)
       +                                fillstart(p->o_fillval);
       +                        if ((p->o_attr & INVIS) == 0)
       +                                circle(ox, oy, x1);
       +                        if (p->o_attr & FILLBIT)
       +                                fillend();
       +                        move(ox, oy);
       +                        dotext(p);
       +                        if (ishor(m))
       +                                move(ox + isright(m) ? x1 : -x1, oy);
       +                        else
       +                                move(ox, oy + isup(m) ? x1 : -x1);
       +                        break;
       +                case ELLIPSE:
       +                        if (p->o_attr & FILLBIT)
       +                                fillstart(p->o_fillval);
       +                        if ((p->o_attr & INVIS) == 0)
       +                                ellipse(ox, oy, x1, y1);
       +                        if (p->o_attr & FILLBIT)
       +                                fillend();
       +                        move(ox, oy);
       +                        dotext(p);
       +                        if (ishor(m))
       +                                move(ox + isright(m) ? x1 : -x1, oy);
       +                        else
       +                                move(ox, oy - isdown(m) ? y1 : -y1);
       +                        break;
       +                case ARC:
       +                        move(ox, oy);
       +                        dotext(p);
       +                        if (p->o_attr & HEAD1)
       +                                arrow(x1 - (y1 - oy), y1 + (x1 - ox),
       +                                      x1, y1, p->o_val[4], p->o_val[5], p->o_val[5]/p->o_val[6]/2, p->o_nhead);
       +                        if (p->o_attr & INVIS)
       +                                /* probably wrong when it's cw */
       +                                move(x1, y1);
       +                        else
       +                                arc(ox, oy, x1, y1, p->o_val[2], p->o_val[3]);
       +                        if (p->o_attr & HEAD2)
       +                                arrow(p->o_val[2] + p->o_val[3] - oy, p->o_val[3] - (p->o_val[2] - ox),
       +                                      p->o_val[2], p->o_val[3], p->o_val[4], p->o_val[5], -p->o_val[5]/p->o_val[6]/2, p->o_nhead);
       +                        if (p->o_attr & CW_ARC)
       +                                move(x1, y1);        /* because drawn backwards */
       +                        break;
       +                case LINE:
       +                case ARROW:
       +                case SPLINE:
       +                        move((ox + x1)/2, (oy + y1)/2);        /* center */
       +                        dotext(p);
       +                        if (p->o_attr & HEAD1)
       +                                arrow(ox + p->o_val[5], oy + p->o_val[6], ox, oy, p->o_val[2], p->o_val[3], 0.0, p->o_nhead);
       +                        if (p->o_attr & INVIS)
       +                                move(x1, y1);
       +                        else if (p->o_type == SPLINE)
       +                                spline(ox, oy, p->o_val[4], &p->o_val[5], p->o_attr & (DOTBIT|DASHBIT), p->o_ddval);
       +                        else {
       +                                dx = ox;
       +                                dy = oy;
       +                                for (k=0, j=5; k < p->o_val[4]; k++, j += 2) {
       +                                        ndx = dx + p->o_val[j];
       +                                        ndy = dy + p->o_val[j+1];
       +                                        if (p->o_attr & (DOTBIT|DASHBIT))
       +                                                dotline(dx, dy, ndx, ndy, p->o_attr, p->o_ddval);
       +                                        else
       +                                                line(dx, dy, ndx, ndy);
       +                                        dx = ndx;
       +                                        dy = ndy;
       +                                }
       +                        }
       +                        if (p->o_attr & HEAD2) {
       +                                dx = ox;
       +                                dy = oy;
       +                                for (k = 0, j = 5; k < p->o_val[4] - 1; k++, j += 2) {
       +                                        dx += p->o_val[j];
       +                                        dy += p->o_val[j+1];
       +                                }
       +                                arrow(dx, dy, x1, y1, p->o_val[2], p->o_val[3], 0.0, p->o_nhead);
       +                        }
       +                        break;
       +                case MOVE:
       +                case TEXT:
       +                        move(ox, oy);
       +                        dotext(p);
       +                        break;
       +                }
       +        }
       +}
       +
       +#ifndef NOEXPANDDASH
       +void
       +dotline(double x0, double y0, double x1, double y1, int ddtype, double ddval) /* dotted line */
       +{
       +        static double prevval = 0.05;        /* 20 per inch by default */
       +        int i, numdots;
       +        double a, b, dx, dy;
       +
       +        if (ddval == 0)
       +                ddval = prevval;
       +        prevval = ddval;
       +        /* don't save dot/dash value */
       +        dx = x1 - x0;
       +        dy = y1 - y0;
       +        if (ddtype & DOTBIT) {
       +                numdots = sqrt(dx*dx + dy*dy) / prevval + 0.5;
       +                if (numdots > 0)
       +                        for (i = 0; i <= numdots; i++) {
       +                                a = (double) i / (double) numdots;
       +                                move(x0 + (a * dx), y0 + (a * dy));
       +                                dot();
       +                        }
       +        } else if (ddtype & DASHBIT) {
       +                double d, dashsize, spacesize;
       +                d = sqrt(dx*dx + dy*dy);
       +                if (d <= 2 * prevval) {
       +                        line(x0, y0, x1, y1);
       +                        return;
       +                }
       +                numdots = d / (2 * prevval) + 1;        /* ceiling */
       +                dashsize = prevval;
       +                spacesize = (d - numdots * dashsize) / (numdots - 1);
       +                b = 0;
       +                for (i = 0; i < numdots-1; i++) {
       +                        a = i * (dashsize + spacesize) / d;
       +                        b = a + dashsize / d;
       +                        line(x0 + (a*dx), y0 + (a*dy), x0 + (b*dx), y0 + (b*dy));
       +                        a = b;
       +                        b = a + spacesize / d;
       +                        move(x0 + (a*dx), y0 + (a*dy));
       +                }
       +                line(x0 + (b * dx), y0 + (b * dy), x1, y1);
       +        }
       +        prevval = 0.05;
       +}
       +#endif
       +
       +void
       +dotbox(double x0, double y0, double x1, double y1, int ddtype, double ddval)        /* dotted or dashed box */
       +{
       +        dotline(x0, y0, x1, y0, ddtype, ddval);
       +        dotline(x1, y0, x1, y1, ddtype, ddval);
       +        dotline(x1, y1, x0, y1, ddtype, ddval);
       +        dotline(x0, y1, x0, y0, ddtype, ddval);
       +}
       +
       +void
       +dotext(obj *p)        /* print text strings of p in proper vertical spacing */
       +{
       +        int i, nhalf;
       +
       +        nhalf = p->o_nt2 - p->o_nt1 - 1;
       +        for (i = p->o_nt1; i < p->o_nt2; i++) {
       +                label(text[i].t_val, text[i].t_type, nhalf);
       +                nhalf -= 2;
       +        }
       +}
 (DIR) diff --git a/src/cmd/tpic/symtab.c b/src/cmd/tpic/symtab.c
       t@@ -0,0 +1,109 @@
       +#include        <stdio.h>
       +#include        <ctype.h>
       +#include        "pic.h"
       +#include        "y.tab.h"
       +
       +YYSTYPE
       +getvar(char *s)        /* return value of variable s (usually pointer) */
       +{
       +        struct symtab *p;
       +        static YYSTYPE bug;
       +
       +        p = lookup(s);
       +        if (p == NULL) {
       +                if (islower(s[0]))
       +                        ERROR "no such variable as %s", s WARNING;
       +                else
       +                        ERROR "no such place as %s", s WARNING;
       +                return(bug);
       +        }
       +        return(p->s_val);
       +}
       +
       +double
       +getfval(char *s)        /* return float value of variable s */
       +{
       +        YYSTYPE y;
       +
       +        y = getvar(s);
       +        return y.f;
       +}
       +
       +void
       +setfval(char *s, double f)        /* set variable s to f */
       +{
       +        struct symtab *p;
       +
       +        if ((p = lookup(s)) != NULL)
       +                p->s_val.f = f;
       +}
       +
       +struct symtab*
       +makevar(char *s, int t, YYSTYPE v)        /* make variable named s in table */
       +                /* assumes s is static or from tostring */
       +{
       +        struct symtab *p;
       +
       +        for (p = stack[nstack].p_symtab; p != NULL; p = p->s_next)
       +                if (strcmp(s, p->s_name) == 0)
       +                        break;
       +        if (p == NULL) {        /* it's a new one */
       +                p = (struct symtab *) malloc(sizeof(struct symtab));
       +                if (p == NULL)
       +                        ERROR "out of symtab space with %s", s FATAL;
       +                p->s_next = stack[nstack].p_symtab;
       +                stack[nstack].p_symtab = p;        /* stick it at front */
       +        }
       +        p->s_name = s;
       +        p->s_type = t;
       +        p->s_val = v;
       +        return(p);
       +}
       +
       +struct symtab*
       +lookup(char *s)        /* find s in symtab */
       +{
       +        int i;
       +        struct symtab *p;
       +
       +        for (i = nstack; i >= 0; i--)        /* look in each active symtab */
       +                for (p = stack[i].p_symtab; p != NULL; p = p->s_next)
       +                        if (strcmp(s, p->s_name) == 0)
       +                                return(p);
       +        return(NULL);
       +}
       +
       +void
       +freesymtab(struct symtab *p)        /* free space used by symtab at p */
       +{
       +        struct symtab *q;
       +
       +        for ( ; p != NULL; p = q) {
       +                q = p->s_next;
       +                free(p->s_name);        /* assumes done with tostring */
       +                free((char *)p);
       +        }
       +}
       +
       +void
       +freedef(char *s)        /* free definition for string s */
       +{
       +        struct symtab *p, *q, *op;
       +
       +        for (p = op = q = stack[nstack].p_symtab; p != NULL; p = p->s_next) {
       +                if (strcmp(s, p->s_name) == 0) {         /* got it */
       +                        if (p->s_type != DEFNAME)
       +                                break;
       +                        if (p == op)        /* 1st elem */
       +                                stack[nstack].p_symtab = p->s_next;
       +                        else
       +                                q->s_next = p->s_next;
       +                        free(p->s_name);
       +                        free(p->s_val.p);
       +                        free((char *)p);
       +                        return;
       +                }
       +                q = p;
       +        }
       +        /* ERROR "%s is not defined at this point", s WARNING; */
       +}
 (DIR) diff --git a/src/cmd/tpic/tex.c b/src/cmd/tpic/tex.c
       t@@ -0,0 +1,203 @@
       +#include <math.h>
       +#include <stdio.h>
       +#include "tex.h"
       +
       +void
       +devarc(double x1, double y1, double x2, double y2, double xc, double yc, int r)
       +{
       +        double t, start, stop;
       +        int rad;
       +
       +        /* tpic arcs go clockwise, and angles are measured clockwise */
       +        start = atan2(y2-yc, x2-xc);
       +        stop = atan2(y1-yc, x1-xc);
       +        if (r<0) {
       +                t = start; start = stop; stop = t;
       +        }
       +        rad = SCX(sqrt((x1-xc)*(x1-xc)+(y1-yc)*(y1-yc)));
       +        fprintf(TEXFILE, "    \\special{ar %d %d %d %d %6.3f %6.3f}%%\n",
       +                TRX(xc), TRY(yc), rad, rad, -start, -stop);
       +}
       +
       +void
       +box(double x0, double y0, double x1, double y1) 
       +{
       +        fprintf(TEXFILE,"    \\special{pa %d %d}",TRX(x0),TRY(y0));
       +        fprintf(TEXFILE,"\\special{pa %d %d}",TRX(x1),TRY(y0));
       +        fprintf(TEXFILE,"\\special{pa %d %d}",TRX(x1),TRY(y1));
       +        fprintf(TEXFILE,"\\special{pa %d %d}",TRX(x0),TRY(y1));
       +        fprintf(TEXFILE,"\\special{pa %d %d}",TRX(x0),TRY(y0));
       +        switch(e1->pen){
       +        case DASHPEN:
       +                fprintf(TEXFILE,"\\special{da %6.3f}%%\n", e1->dashlen); break;
       +        case DOTPEN:
       +                fprintf(TEXFILE,"\\special{dt %6.3f}%%\n", e1->dashlen); break;
       +        case SOLIDPEN:
       +        default:
       +                fprintf(TEXFILE,"\\special{fp}%%\n"); break;
       +        }
       +}
       +
       +void
       +circle(double xc, double yc, double r)
       +{
       +        int rad = SCX(r);
       +
       +        fprintf(TEXFILE, "    \\special{ar %d %d %d %d 0.0 6.2832}%%\n",
       +                TRX(xc), TRY(yc), rad, rad);
       +}
       +
       +void
       +closepl(void)
       +{
       +        fprintf(TEXFILE, "    \\kern %6.3fin\n  }\\vss}%%\n", INCHES(e1->sidex));
       +        fprintf(TEXFILE, "  \\kern %6.3fin\n}\n", INCHES(e1->sidey));
       +}
       +
       +void
       +disc(double xc, double yc, double r)
       +{
       +        fprintf(TEXFILE, "    \\special{bk}%%\n");
       +        circle(xc, yc, r);
       +}
       +
       +void
       +erase(void)
       +{
       +}
       +
       +void
       +fill(int num[], double *ff[])
       +{
       +        double *xp, *yp, **fp, x0, y0;
       +        int i, *n;
       +        n = num;
       +        fp = ff;
       +        while((i = *n++)){
       +                xp = *fp++;
       +                yp = xp+1;
       +                x0 = *xp;
       +                y0 = *yp;
       +                move(x0, y0);
       +                while(--i){
       +                        xp += 2;
       +                        yp += 2;
       +                        vec(*xp, *yp);
       +                }
       +                if (*(xp-2) != x0 || *(yp-2) != y0)
       +                        vec(x0, y0);
       +        }
       +}
       +
       +void
       +frame(double xs, double ys, double xf, double yf)
       +{
       +        double        osidex, osidey;
       +        osidex = e1->sidex;
       +        osidey = e1->sidey;
       +        e1->left = xs * (e0->left + e0->sidex);
       +        e1->bottom = ys* (e0->bottom +  e0->sidey);
       +        e1->sidex = (xf-xs) * e0->sidex;
       +        e1->sidey = (yf-ys) * e0->sidey;
       +        e1->scalex *= (e1->sidex / osidex);
       +        e1->scaley *= (e1->sidey / osidey);
       +}
       +
       +void
       +line(double x0, double y0, double x1, double y1) 
       +{
       +        move(x0, y0);
       +        vec(x1, y1);
       +}
       +
       +void
       +move(double xx, double yy) 
       +{
       +        e1->copyx = xx; 
       +        e1->copyy = yy;
       +}
       +
       +extern        double        xmin, ymin, xmax, ymax;
       +
       +/* tpic TeX coord system uses millinches, printer's points for pensize */
       +/* positive y downward, origin at upper left */
       +
       +#define pHEIGHT 5000.
       +#define pWIDTH  5000.
       +#define pPENSIZE 9
       +#define pPSIZE 10
       +#define pDLEN .05
       +struct penvir E[2] = {
       +{0.,pHEIGHT,0.,0.,1.,-1.,pWIDTH,pHEIGHT,0.,0.,0,pPSIZE,SOLIDPEN,pPENSIZE,pDLEN},
       +{0.,pHEIGHT,0.,0.,1.,-1.,pWIDTH,pHEIGHT,0.,0.,0,pPSIZE,SOLIDPEN,pPENSIZE,pDLEN}
       +};
       +struct penvir *e0 = E, *e1 = &E[1];
       +FILE *TEXFILE;
       +
       +void
       +openpl(void)
       +{ 
       +        TEXFILE = stdout;
       +
       +        space(xmin, ymin, xmax, ymax);
       +        fprintf(TEXFILE,"\\catcode`@=11\n");
       +        fprintf(TEXFILE, "\\expandafter\\ifx\\csname graph\\endcsname\\relax");
       +        fprintf(TEXFILE, " \\alloc@4\\box\\chardef\\insc@unt\\graph\\fi\n");
       +        fprintf(TEXFILE, "\\catcode`@=12\n");
       +        fprintf(TEXFILE, "\\setbox\\graph=\\vtop{%%\n");
       +        fprintf(TEXFILE, "  \\baselineskip=0pt \\lineskip=0pt ");
       +        fprintf(TEXFILE, "\\lineskiplimit=0pt\n");
       +        fprintf(TEXFILE, "  \\vbox to0pt{\\hbox{%%\n");
       +        fprintf(TEXFILE, "    \\special{pn %d}%%\n", e1->pdiam);
       +}
       +
       +void
       +range(double x0, double y0, double x1, double y1) 
       +{
       +        e1->xmin = x0;
       +        e1->ymin = y0;
       +        if (x1-x0 < .0000001*e1->sidex)
       +                x1=x0+.0000001;
       +        if (y1-y0 < .0000001*e1->sidey)
       +                y1=y0+.0000001;
       +        e1->scalex = e0->scalex*e1->sidex / (x1 - x0);
       +        e1->scaley = e0->scaley*e1->sidey / (y1 - y0);
       +}
       +
       +void
       +rmove(double xx, double yy) 
       +{
       +        e1->copyx += xx;
       +        e1->copyy += yy;
       +}
       +
       +void
       +rvec(double xx, double yy)  
       +{
       +        vec(xx+e1->copyx, yy+e1->copyy);
       +}
       +
       +void
       +sbox(double x0, double y0, double x1, double y1) 
       +{
       +        fprintf(TEXFILE,"    \\special{bk}%%\n");
       +        box(x0, y0, x1, y1);
       +}
       +
       +void
       +vec(double xx, double yy) 
       +{
       +        fprintf(TEXFILE,"    \\special{pa %d %d}",TRX(e1->copyx),TRY(e1->copyy));
       +        e1->copyx = xx; 
       +        e1->copyy = yy;
       +        fprintf(TEXFILE,"\\special{pa %d %d}",TRX(xx),TRY(yy));
       +        switch(e1->pen){
       +        case DASHPEN:
       +                fprintf(TEXFILE,"\\special{da %6.3f}%%\n", e1->dashlen); break;
       +        case DOTPEN:
       +                fprintf(TEXFILE,"\\special{dt %6.3f}%%\n", e1->dashlen); break;
       +        case SOLIDPEN:
       +        default:
       +                fprintf(TEXFILE,"\\special{fp}%%\n"); break;
       +        }
       +}
 (DIR) diff --git a/src/cmd/tpic/tex.h b/src/cmd/tpic/tex.h
       t@@ -0,0 +1,50 @@
       +#ifndef BUFSIZE
       +#include <stdio.h>
       +#endif
       +#define SCX(A) (int)((A)*e1->scalex+0.5)
       +#define SCY(A) (int)((A)*e1->scaley+0.5)
       +#define TRX(A) (int)(((A) - e1->xmin)*e1->scalex  + e1->left)
       +#define TRY(A) (int)(((A) - e1->ymin)*e1->scaley + e1->bottom)
       +#define DTRX(A) (((A) - e1->xmin)*e1->scalex  + e1->left)
       +#define DTRY(A) (((A) - e1->ymin)*e1->scaley + e1->bottom)
       +#define INCHES(A) ((A)/1000.)
       +extern struct penvir {
       +        double left, bottom;
       +        double xmin, ymin;
       +        double scalex, scaley;
       +        double sidex, sidey;
       +        double copyx, copyy;
       +        char *font;
       +        int psize;
       +        int pen;
       +        int pdiam;
       +        double dashlen;
       +        } *e0, *e1, *e2, *esave;
       +enum {
       +        SOLIDPEN, DASHPEN, DOTPEN
       +};
       +extern FILE *TEXFILE;
       +
       +#define round texround
       +
       +extern int round();
       +
       +void                box(double x0, double y0, double x1, double y1) ;
       +void                circle(double xc, double yc, double r);
       +void                closepl(void);
       +void                devarc(double x1, double y1, double x2, double y2, double xc, double yc, int r);
       +void                disc(double xc, double yc, double r);
       +void                erase(void);
       +void                fill(int num[], double *ff[]);
       +void                frame(double xs, double ys, double xf, double yf);
       +void                line(double x0, double y0, double x1, double y1) ;
       +void                move(double xx, double yy) ;
       +void                openpl(void);
       +void                pen(char *s) ;
       +void                poly(int num[], double *ff[]);
       +void                range(double x0, double y0, double x1, double y1) ;
       +void                rmove(double xx, double yy) ;
       +void                rvec(double xx, double yy)  ;
       +void                sbox(double x0, double y0, double x1, double y1) ;
       +void                vec(double xx, double yy) ;
       +void        space(double x0, double y0, double x1, double y1);
 (DIR) diff --git a/src/cmd/tpic/textgen.c b/src/cmd/tpic/textgen.c
       t@@ -0,0 +1,114 @@
       +#include        <stdio.h>
       +#include        "pic.h"
       +#include        "y.tab.h"
       +
       +obj*
       +textgen(void)
       +{
       +        int i, sub, nstr, at, with, hset;
       +        double xwith, ywith, h, w, x0, y0, x1, y1;
       +        obj *p, *ppos;
       +        static double prevh = 0;
       +        static double prevw = 0;
       +        Attr *ap;
       +
       +        at = with = nstr = hset = 0;
       +        h = getfval("textht");
       +        w = getfval("textwid");
       +        for (i = 0; i < nattr; i++) {
       +                ap = &attr[i];
       +                switch (ap->a_type) {
       +                case HEIGHT:
       +                        h = ap->a_val.f;
       +                        hset++;
       +                        break;
       +                case WIDTH:
       +                        w = ap->a_val.f;
       +                        break;
       +                case WITH:
       +                        with = ap->a_val.i;
       +                        break;
       +                case AT:
       +                        ppos = ap->a_val.o;
       +                        curx = ppos->o_x;
       +                        cury = ppos->o_y;
       +                        at++;
       +                        break;
       +                case TEXTATTR:
       +                        sub = ap->a_sub;
       +                        if (ap->a_val.p == NULL)        /* an isolated modifier */
       +                                text[ntext-1].t_type = sub;
       +                        else {
       +                                savetext(sub, ap->a_val.p);
       +                                nstr++;
       +                        }
       +                        break;
       +                }
       +        }
       +        if (hset == 0)                /* no explicit ht cmd */
       +                h *= nstr;
       +        if (with) {
       +                xwith = ywith = 0.0;
       +                switch (with) {
       +                case NORTH:        ywith = -h / 2; break;
       +                case SOUTH:        ywith = h / 2; break;
       +                case EAST:        xwith = -w / 2; break;
       +                case WEST:        xwith = w / 2; break;
       +                case NE:        xwith = -w / 2; ywith = -h / 2; break;
       +                case SE:        xwith = -w / 2; ywith = h / 2; break;
       +                case NW:        xwith = w / 2; ywith = -h / 2; break;
       +                case SW:        xwith = w / 2; ywith = h / 2; break;
       +                }
       +                curx += xwith;
       +                cury += ywith;
       +        }
       +        if (!at) {
       +                if (isright(hvmode))
       +                        curx += w / 2;
       +                else if (isleft(hvmode))
       +                        curx -= w / 2;
       +                else if (isup(hvmode))
       +                        cury += h / 2;
       +                else
       +                        cury -= h / 2;
       +        }
       +        x0 = curx - w / 2;
       +        y0 = cury - h / 2;
       +        x1 = curx + w / 2;
       +        y1 = cury + h / 2;
       +        extreme(x0, y0);
       +        extreme(x1, y1);
       +        dprintf("Text h %g w %g at %g,%g\n", h, w, curx, cury);
       +        p = makenode(TEXT, 2);
       +        p->o_val[0] = w;
       +        p->o_val[1] = h;
       +        if (isright(hvmode))
       +                curx = x1;
       +        else if (isleft(hvmode))
       +                curx = x0;
       +        else if (isup(hvmode))
       +                cury = y1;
       +        else
       +                cury = y0;
       +        prevh = h;
       +        prevw = w;
       +        return(p);
       +}
       +
       +obj*
       +troffgen(char *s)        /* save away a string of troff commands */
       +{
       +        savetext(CENTER, s);        /* use the existing text mechanism */
       +        return makenode(TROFF, 0);
       +}
       +
       +void
       +savetext(int t, char *s)        /* record text elements for current object */
       +{
       +        if (ntext >= ntextlist)
       +                text = (Text *) grow((char *) text, "text", ntextlist += 200, sizeof(Text));
       +        text[ntext].t_type = t;
       +        text[ntext].t_val = s;
       +        dprintf("saving %d text %s at %d\n", t, s, ntext);
       +        ntext++;
       +}