aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/tpic
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2006-04-14 00:04:03 +0000
committerrsc <devnull@localhost>2006-04-14 00:04:03 +0000
commitb6d98463b416e9d95bc88f948b4abdff6b24aede (patch)
tree6ae4dabb948c5b3c42a48b34964bdde7b0afa5c2 /src/cmd/tpic
parent434d1b41b61a9771f4cec2ca6c0a00b067a50fa0 (diff)
downloadplan9port-b6d98463b416e9d95bc88f948b4abdff6b24aede.tar.gz
plan9port-b6d98463b416e9d95bc88f948b4abdff6b24aede.tar.bz2
plan9port-b6d98463b416e9d95bc88f948b4abdff6b24aede.zip
tpic
Diffstat (limited to 'src/cmd/tpic')
-rw-r--r--src/cmd/tpic/Changes24
-rw-r--r--src/cmd/tpic/arcgen.c214
-rw-r--r--src/cmd/tpic/blockgen.c223
-rw-r--r--src/cmd/tpic/boxgen.c113
-rw-r--r--src/cmd/tpic/circgen.c127
-rw-r--r--src/cmd/tpic/for.c94
-rw-r--r--src/cmd/tpic/input.c608
-rw-r--r--src/cmd/tpic/linegen.c210
-rw-r--r--src/cmd/tpic/main.c277
-rw-r--r--src/cmd/tpic/misc.c463
-rw-r--r--src/cmd/tpic/mkfile39
-rw-r--r--src/cmd/tpic/movegen.c87
-rw-r--r--src/cmd/tpic/pic.h285
-rw-r--r--src/cmd/tpic/picl.l261
-rw-r--r--src/cmd/tpic/picy.c1239
-rw-r--r--src/cmd/tpic/picy.y320
-rw-r--r--src/cmd/tpic/pltex.c149
-rw-r--r--src/cmd/tpic/print.c210
-rw-r--r--src/cmd/tpic/symtab.c109
-rw-r--r--src/cmd/tpic/tex.c203
-rw-r--r--src/cmd/tpic/tex.h50
-rw-r--r--src/cmd/tpic/textgen.c114
22 files changed, 5419 insertions, 0 deletions
diff --git a/src/cmd/tpic/Changes b/src/cmd/tpic/Changes
new file mode 100644
index 00000000..1e0a12d4
--- /dev/null
+++ b/src/cmd/tpic/Changes
@@ -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.
diff --git a/src/cmd/tpic/arcgen.c b/src/cmd/tpic/arcgen.c
new file mode 100644
index 00000000..9c98d197
--- /dev/null
+++ b/src/cmd/tpic/arcgen.c
@@ -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 */
+}
+
diff --git a/src/cmd/tpic/blockgen.c b/src/cmd/tpic/blockgen.c
new file mode 100644
index 00000000..4b4bfe98
--- /dev/null
+++ b/src/cmd/tpic/blockgen.c
@@ -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;
+ }
+ }
+}
diff --git a/src/cmd/tpic/boxgen.c b/src/cmd/tpic/boxgen.c
new file mode 100644
index 00000000..95d4046d
--- /dev/null
+++ b/src/cmd/tpic/boxgen.c
@@ -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);
+}
diff --git a/src/cmd/tpic/circgen.c b/src/cmd/tpic/circgen.c
new file mode 100644
index 00000000..a1dbc937
--- /dev/null
+++ b/src/cmd/tpic/circgen.c
@@ -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);
+}
diff --git a/src/cmd/tpic/for.c b/src/cmd/tpic/for.c
new file mode 100644
index 00000000..6990314e
--- /dev/null
+++ b/src/cmd/tpic/for.c
@@ -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;
+ }
+}
diff --git a/src/cmd/tpic/input.c b/src/cmd/tpic/input.c
new file mode 100644
index 00000000..817488c8
--- /dev/null
+++ b/src/cmd/tpic/input.c
@@ -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);
+}
diff --git a/src/cmd/tpic/linegen.c b/src/cmd/tpic/linegen.c
new file mode 100644
index 00000000..a5394232
--- /dev/null
+++ b/src/cmd/tpic/linegen.c
@@ -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);
+}
diff --git a/src/cmd/tpic/main.c b/src/cmd/tpic/main.c
new file mode 100644
index 00000000..ed310d5d
--- /dev/null
+++ b/src/cmd/tpic/main.c
@@ -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;
+}
diff --git a/src/cmd/tpic/misc.c b/src/cmd/tpic/misc.c
new file mode 100644
index 00000000..72e83303
--- /dev/null
+++ b/src/cmd/tpic/misc.c
@@ -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;
+}
diff --git a/src/cmd/tpic/mkfile b/src/cmd/tpic/mkfile
new file mode 100644
index 00000000..76bdcb36
--- /dev/null
+++ b/src/cmd/tpic/mkfile
@@ -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
+
diff --git a/src/cmd/tpic/movegen.c b/src/cmd/tpic/movegen.c
new file mode 100644
index 00000000..3a7e2021
--- /dev/null
+++ b/src/cmd/tpic/movegen.c
@@ -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);
+}
diff --git a/src/cmd/tpic/pic.h b/src/cmd/tpic/pic.h
new file mode 100644
index 00000000..50e58009
--- /dev/null
+++ b/src/cmd/tpic/pic.h
@@ -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"
+
diff --git a/src/cmd/tpic/picl.l b/src/cmd/tpic/picl.l
new file mode 100644
index 00000000..8a706dc5
--- /dev/null
+++ b/src/cmd/tpic/picl.l
@@ -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]);
+
+%%
diff --git a/src/cmd/tpic/picy.c b/src/cmd/tpic/picy.c
new file mode 100644
index 00000000..25ffd4f9
--- /dev/null
+++ b/src/cmd/tpic/picy.c
@@ -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 */
+}
diff --git a/src/cmd/tpic/picy.y b/src/cmd/tpic/picy.y
new file mode 100644
index 00000000..46b2b4da
--- /dev/null
+++ b/src/cmd/tpic/picy.y
@@ -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; }
+ ;
diff --git a/src/cmd/tpic/pltex.c b/src/cmd/tpic/pltex.c
new file mode 100644
index 00000000..e757f195
--- /dev/null
+++ b/src/cmd/tpic/pltex.c
@@ -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
diff --git a/src/cmd/tpic/print.c b/src/cmd/tpic/print.c
new file mode 100644
index 00000000..a5b6b280
--- /dev/null
+++ b/src/cmd/tpic/print.c
@@ -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;
+ }
+}
diff --git a/src/cmd/tpic/symtab.c b/src/cmd/tpic/symtab.c
new file mode 100644
index 00000000..32e3bf0f
--- /dev/null
+++ b/src/cmd/tpic/symtab.c
@@ -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; */
+}
diff --git a/src/cmd/tpic/tex.c b/src/cmd/tpic/tex.c
new file mode 100644
index 00000000..ade5df6b
--- /dev/null
+++ b/src/cmd/tpic/tex.c
@@ -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;
+ }
+}
diff --git a/src/cmd/tpic/tex.h b/src/cmd/tpic/tex.h
new file mode 100644
index 00000000..873616a0
--- /dev/null
+++ b/src/cmd/tpic/tex.h
@@ -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);
diff --git a/src/cmd/tpic/textgen.c b/src/cmd/tpic/textgen.c
new file mode 100644
index 00000000..e9ba8034
--- /dev/null
+++ b/src/cmd/tpic/textgen.c
@@ -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++;
+}