tsvgpic: new program to convert pic to svg - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
commit 3ebbd193dce0724e106ec9af627a3782676ae510
parent a9530c00e860cbbac75b03a1c6bce93ddf0a26f6
Author: Russ Cox 
Date:   Mon,  3 Apr 2017 23:12:14 -0400

svgpic: new program to convert pic to svg

This is an experiment.
Like tpic it's a copy-and-paste fork of pic.

Change-Id: Ia22772bd5881c7904a6d8f8e0b46fde8cea89cbd
Reviewed-on: https://plan9port-review.googlesource.com/2920
Reviewed-by: Russ Cox 

Diffstat:
  M man/man1/pic.1                      |      15 +++++++++++++++
  A src/cmd/svgpic/arcgen.c             |     224 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/blockgen.c           |     226 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/boxgen.c             |     115 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/circgen.c            |     128 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/for.c                |      95 ++++++++++++++++++++++++++++++
  A src/cmd/svgpic/input.c              |     598 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/linegen.c            |     239 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/main.c               |     283 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/misc.c               |     441 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/mkfile               |      36 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/movegen.c            |      86 ++++++++++++++++++++++++++++++
  A src/cmd/svgpic/pic.h                |     222 ++++++++++++++++++++++++++++++
  A src/cmd/svgpic/picl.lx              |     273 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/picy.y               |     328 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/plsvg.c              |     328 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/print.c              |     182 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/symtab.c             |     104 +++++++++++++++++++++++++++++++
  A src/cmd/svgpic/textgen.c            |     111 ++++++++++++++++++++++++++++++

19 files changed, 4034 insertions(+), 0 deletions(-)
---
diff --git a/man/man1/pic.1 b/man/man1/pic.1
t@@ -23,6 +23,11 @@ pic, tpic \- troff and tex preprocessors for drawing pictures
 [
 .I files
 ]
+.PP
+.B svgpic
+[
+.I files
+]
 .SH DESCRIPTION
 .I Pic
 is a
t@@ -306,6 +311,12 @@ The box may be output this way:
 .IP
 .L
 \ecenterline{\ebox\egraph}
+.PP
+.I Svgpic
+accepts
+.IR pic
+language and produces a Scalable Vector Graphics (SVG) image
+suitable for use in HTML documents.
 .SH EXAMPLES
 .EX
 arrow "input" above; box "process"; arrow "output" above
t@@ -342,3 +353,7 @@ B. W. Kernighan,
 .I
 Unix Research System Programmer's Manual,
 Tenth Edition, Volume 2
+.SH BUGS
+.I Svgpic
+is only lightly tested.
+It should handle troff commands in text output.
diff --git a/src/cmd/svgpic/arcgen.c b/src/cmd/svgpic/arcgen.c
t@@ -0,0 +1,224 @@
+#include        
+#include        
+#include        "pic.h"
+#include        "y.tab.h"
+
+void arc_extreme(double, double, double, double, double, double);
+int quadrant(double x, double y);
+
+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, battr;
+        obj *p, *ppos;
+        double fromx, fromy, tox, toy, fillval = 0;
+        Attr *ap;
+
+        tox=toy=0.0; /* Botch? (gcc) */
+
+        prevrad = getfval("arcrad");
+        prevh = getfval("arrowht");
+        prevw = getfval("arrowwid");
+        fromx = curx;
+        fromy = cury;
+        head = to = at = cw = invis = ddtype = battr = 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;
+                case FILL:
+                        battr |= FILLBIT;
+                        if (ap->a_sub == DEFAULT)
+                                fillval = getfval("fillval");
+                        else
+                                fillval = ap->a_val.f;
+                        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 | battr;
+        p->o_fillval = fillval;
+        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)
+                          /* start, end, center */
+{
+        /* 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 = (x0x1)?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/svgpic/blockgen.c b/src/cmd/svgpic/blockgen.c
t@@ -0,0 +1,226 @@
+#include 
+#include 
+#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;
+
+void blockadj(obj *);
+
+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 */
+                if (xmin > xmax)        /* nothing happened */
+                        xmin = xmax;
+                if (ymin > ymax)
+                        ymin = ymax;
+                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/svgpic/boxgen.c b/src/cmd/svgpic/boxgen.c
t@@ -0,0 +1,115 @@
+#include        
+#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 NOEDGE:
+                        battr |= NOEDGEBIT;
+                        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/svgpic/circgen.c b/src/cmd/svgpic/circgen.c
t@@ -0,0 +1,128 @@
+#include        
+#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;
+
+        r = r2 = 0.0; /* Botch? (gcc) */
+
+        battr = at = 0;
+        with = xwith = ywith = fillval = ddval = 0;
+        t = (type == CIRCLE) ? 0 : 1;
+        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 NOEDGE:
+                        battr |= NOEDGEBIT;
+                        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", ro_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/svgpic/for.c b/src/cmd/svgpic/for.c
t@@ -0,0 +1,95 @@
+#include 
+#include 
+#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        setfval(char *, double);
+void        nextfor(void);
+
+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/svgpic/input.c b/src/cmd/svgpic/input.c
t@@ -0,0 +1,598 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+#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        do_thru(void);
+int        nextchar(void);
+int        getarg(char *);
+void        freedef(char *);
+int        baldelim(int, char *);
+
+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 ", (int)(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 ", (int) (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", (int) (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)
+{
+        register 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)
+{
+        register int c;
+
+        c = 0; /* Botch: gcc */
+
+  loop:
+        switch (srcp->type) {
+        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((unsigned char) *srcp->sp)) {
+                        int n = 0;
+                        while (isdigit((unsigned char) *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", (int) (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)
+{
+        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        eprint(void);
+
+void yyerror(char *s)
+{
+        extern char *cmdname;
+        int ern = errno;        /* cause some libraries clobber it */
+
+        if (synerr)
+                return;
+        fflush(stdout);
+        fprintf(stderr, "%s: %s", cmdname, s);
+        if (ern > 0) {
+                errno = ern;
+                perror("???");
+        }
+        fprintf(stderr, " near %s:%d\n",
+                curfile->fname, curfile->lineno+1);
+        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(char *);
+
+        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, "rc -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/svgpic/linegen.c b/src/cmd/svgpic/linegen.c
t@@ -0,0 +1,239 @@
+#include 
+#include 
+#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, battr, with;
+        double ddval, chop1, chop2, x0, y0, x1, y1;
+        double fillval = 0;
+        double theta;
+        double defx, defy, xwith, ywith;
+        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 = battr = with = 0;
+        chop = chop1 = chop2 = 0;
+        ddtype = ddval = xwith = ywith = 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 NOEDGE:
+                        battr |= NOEDGEBIT;
+                        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;
+                case WITH:
+                        with = ap->a_val.i;
+                        break;
+                case CHOP:
+                        if (ap->a_sub != PLACENAME) {
+                                if( chop == 0)
+                                        chop1 = chop2 = ap->a_val.f;
+                                else
+                                        chop2 = 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 (with) {        /* this doesn't work at all */
+                switch (with) {
+                case CENTER:
+                        xwith = (dx[1] - dx[0]) / 2; ywith = (dy[1] - dy[0]) / 2; break;
+                }
+                for (i = 0; i < ndxy; i++) {
+                        dx[i] -= xwith;
+                        dy[i] -= ywith;
+                }
+                curx += xwith;
+                cury += ywith;
+        }
+        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 | battr;
+        p->o_fillval = fillval;
+        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/svgpic/main.c b/src/cmd/svgpic/main.c
t@@ -0,0 +1,283 @@
+#include        
+#include        
+#include        
+#include        
+#include        "pic.h"
+#include        "y.tab.h"
+
+char        *version = "version July 5, 1993";
+
+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 */
+char        *PEstring;        /* "PS" or "PE" picked up by lexer */
+
+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;
+
+void        fpecatch(int);
+void        getdata(void), setdefaults(void);
+void        setfval(char *, double);
+int        getpid(void);
+
+int
+main(int argc, char *argv[])
+{
+        char buf[20];
+
+        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;
+                        fprintf(stderr, "%s\n", version);
+                        break;
+                case 'V':
+                        fprintf(stderr, "%s\n", version);
+                        return 0;
+                }
+                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);
+                }
+        return anyerr;
+}
+
+void fpecatch(int n)
+{
+        ERROR "floating point exception %d", n 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.7, 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;
+        void reset(void), openpl(char *), closepl(char *), print(void);
+        int yyparse(void);
+
+        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;
+                        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(&buf[3]);        /* puts out .PS, with ht & wid stuck in */
+                                printlf(curfile->lineno+1, NULL);
+                                print();        /* assumes \n at end */
+                                closepl(PEstring);        /* does the .PE/F */
+                                free(PEstring);
+                        }
+                        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;
+        extern        void freesymtab(struct symtab *);
+
+        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;
+        PEstring = 0;
+        hvmode = R_DIR;
+        xmin = ymin = 30000;
+        xmax = ymax = -30000;
+}
diff --git a/src/cmd/svgpic/misc.c b/src/cmd/svgpic/misc.c
t@@ -0,0 +1,441 @@
+#include 
+#include 
+#include 
+#include 
+#include "pic.h"
+#include "y.tab.h"
+
+int whatpos(obj *p, int corner, double *px, double *py);
+void makeattr(int type, int sub, YYSTYPE val);
+YYSTYPE getblk(obj *, char *);
+
+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)
+{
+        register 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;
+
+        x1 = y1 = 0.0; /* Botch? (gcc) */
+
+        dprintf("whatpos %p %d %d\n", (void*)p, p->o_type, corner);
+        x = p->o_x;
+        y = p->o_y;
+        if (p->o_type != PLACE && p->o_type != MOVE) {
+                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;
+
+        y = getblk(p, s);
+        return y.f;
+}
+
+obj *getblock(obj *p, char *s)        /* find variable s in block p */
+{
+        YYSTYPE y;
+
+        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/svgpic/mkfile b/src/cmd/svgpic/mkfile
t@@ -0,0 +1,36 @@
+<$PLAN9/src/mkhdr
+
+TARG=svgpic
+OFILES=picy.$O\
+        picl.$O\
+        main.$O\
+        print.$O\
+        misc.$O\
+        symtab.$O\
+        blockgen.$O\
+        boxgen.$O\
+        circgen.$O\
+        arcgen.$O\
+        linegen.$O\
+        movegen.$O\
+        textgen.$O\
+        input.$O\
+        for.$O\
+        plsvg.$O\
+
+HFILES=pic.h\
+        y.tab.h\
+
+YFILES=picy.y\
+
+<$PLAN9/src/mkone
+YFLAGS=-S -d
+
+picy.c:        y.tab.c
+        mv $prereq $target
+
+picl.c:D: picl.lx
+        $LEX -t $prereq > $target
+
+clean:V:
+        rm -f *.[$OS] [$OS].out y.tab.? y.debug $TARG picy.c picl.c
diff --git a/src/cmd/svgpic/movegen.c b/src/cmd/svgpic/movegen.c
t@@ -0,0 +1,86 @@
+#include        
+#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/svgpic/pic.h b/src/cmd/svgpic/pic.h
t@@ -0,0 +1,222 @@
+#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        void        yyerror(char *);
+
+extern        char        errbuf[200];
+
+#undef        sprintf        /* Snow Leopard */
+
+#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 NOEDGEBIT 128        /* no edge on filled object */
+
+#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        char        *PEstring;
+
+char        *tostring(char *);
+char        *grow(char *, char *, int, int);
+double        getfval(char *), getcomp(obj *, int), getblkvar(obj *, char *);
+YYSTYPE        getvar(char *);
+struct        symtab *lookup(char *), *makevar(char *, int, YYSTYPE);
+char        *ifstat(double, char *, char *), *delimstr(char *), *sprintgen(char *);
+void        forloop(char *var, double from, double to, int op, double by, char *_str);
+int        setdir(int), curdir(void);
+void        resetvar(void);
+void        checkscale(char *);
+void        pushsrc(int, char *);
+void        copy(void);
+void        copyuntil(char *);
+void        copyfile(char *);
+void        copydef(struct symtab *);
+void        definition(char *);
+struct symtab *copythru(char *);
+int        input(void);
+int        unput(int);
+void        extreme(double, double);
+
+extern        double        deltx, delty;
+extern        int        lineno;
+extern        int        synerr;
+
+extern        double        xmin, ymin, xmax, ymax;
+
+obj        *leftthing(int), *boxgen(void), *circgen(int), *arcgen(int);
+obj        *linegen(int), *splinegen(void), *movegen(void);
+obj        *textgen(void), *plotgen(void);
+obj        *troffgen(char *), *rightthing(obj *, int), *blockgen(obj *, obj *);
+obj        *makenode(int, int), *makepos(double, double);
+obj        *fixpos(obj *, double, double);
+obj        *addpos(obj *, obj *), *subpos(obj *, obj *);
+obj        *makebetween(double, obj *, obj *);
+obj        *getpos(obj *, int), *gethere(void), *getfirst(int, int);
+obj        *getlast(int, int), *getblock(obj *, char *);
+void        savetext(int, char *);
+void        makeiattr(int, int);
+void        makevattr(char *);
+void        makefattr(int type, int sub, double f);
+void        maketattr(int, char *);
+void        makeoattr(int, obj *);
+void        makeattr(int type, int sub, YYSTYPE val);
+void        printexpr(double);
+void        printpos(obj *);
+void        exprsave(double);
+void        addtattr(int);
+void        printlf(int, char *);
+
+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;
+
+extern        double        errcheck(double, char *);
+#define        Log10(x) errcheck(log10(x), "log")
+#define        Exp(x)        errcheck(exp(x), "exp")
+#define        Sqrt(x)        errcheck(sqrt(x), "sqrt")
diff --git a/src/cmd/svgpic/picl.lx b/src/cmd/svgpic/picl.lx
t@@ -0,0 +1,273 @@
+%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  lex puts one out for us */
+#include 
+#include 
+#include "pic.h"
+#include "y.tab.h"
+
+extern        char        *filename;
+extern        struct        symtab        symtab[];
+
+void        pbstr(char *);
+void        dodef(struct symtab *stp);
+void        undefine(char *s);
+void        shell_init(void), shell_exec(void), shell_text(char *);
+void        endfor(void);
+
+int        yyback(int *, int);
+int        yylook(void);
+int        yywrap(void);
+
+#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(']');
+        }
+
+{WS}                ;
+"\\"\n        ;
+\n                { return(ST); }
+";"                { return(ST); }
+"}"                { BEGIN sc; return(ST); }
+"]"                { BEGIN br; return(ST); }
+"{"{WS}*(#.*)?\n+        { return(yylval.i = yytext[0]); }
+
+^".PS".*        { if (curfile == infile) ERROR ".PS found inside .PS/.PE" WARNING; }
+^".PE".*        { if (curfile == infile) {
+                        yylval.p = PEstring = tostring(yytext);
+                        return(EOF);
+                  }
+                }
+^".".*        { yylval.p = tostring(yytext); return(TROFF); }
+
+print        return(yylval.i = PRINT);
+box                return(yylval.i = BOX);
+circle        return(yylval.i = CIRCLE);
+arc                return(yylval.i = ARC);
+ellipse        return(yylval.i = ELLIPSE);
+arrow        return(yylval.i = ARROW);
+spline        return(yylval.i = SPLINE);
+line                return(yylval.i = LINE);
+move                return(yylval.i = MOVE);
+"[]"                return(yylval.i = BLOCK);
+reset        return(RESET);
+sprintf        return(SPRINTF);
+
+same                return(SAME);
+between        return(BETWEEN);
+and                return(AND);
+
+of                ;
+the                ;
+way                ;
+
+"."(e|east)                { yylval.i = EAST; return(CORNER); }
+"."(r|right)                { yylval.i = EAST; return(CORNER); }
+"."(w|west)                { yylval.i = WEST; return(CORNER); }
+"."(l|left)                { yylval.i = WEST; return(CORNER); }
+"."(n|north)                { yylval.i = NORTH; return(CORNER); }
+"."(t|top)                { yylval.i = NORTH; return(CORNER); }
+"."(s|south)                { yylval.i = SOUTH; return(CORNER); }
+"."(b|bot|bottom)        { yylval.i = SOUTH; return(CORNER); }
+"."(c|center)        { yylval.i = CENTER; return(CORNER); }
+".start"                { yylval.i = START; return(CORNER); }
+".end"                { yylval.i = END; return(CORNER); }
+".ne"                { yylval.i = NE; return(CORNER); }
+".se"                { yylval.i = SE; return(CORNER); }
+".nw"                { yylval.i = NW; return(CORNER); }
+".sw"                { yylval.i = SW; return(CORNER); }
+
+top" "+of                { yylval.i = NORTH; return(CORNER); }
+north" "+of                { yylval.i = NORTH; return(CORNER); }
+bottom" "+of                { yylval.i = SOUTH; return(CORNER); }
+south" "+of                { yylval.i = SOUTH; return(CORNER); }
+left" "+of                { yylval.i = WEST; return(CORNER); }
+west" "+of                { yylval.i = WEST; return(CORNER); }
+right" "+of                { yylval.i = EAST; return(CORNER); }
+east" "+of                { yylval.i = EAST; return(CORNER); }
+center" "+of                { yylval.i = CENTER; return(CORNER); }
+start" "+of                { yylval.i = START; return(CORNER); }
+end" "+of                { yylval.i = END; return(CORNER); }
+
+height|ht        { yylval.i = HEIGHT; return(ATTR); }
+width|wid        { yylval.i = WIDTH; return(ATTR); }
+radius|rad        { yylval.i = RADIUS; return(ATTR); }
+diameter|diam { yylval.i = DIAMETER; return(ATTR); }
+size                { yylval.i = SIZE; return(ATTR); }
+left                { yylval.i = LEFT; return(DIR); }
+right        { yylval.i = RIGHT; return(DIR); }
+up                { yylval.i = UP; return(DIR); }
+down                { yylval.i = DOWN; return(DIR); }
+cw                { yylval.i = CW; return(ATTR); }
+clockwise        { yylval.i = CW; return(ATTR); }
+ccw                { yylval.i = CCW; return(ATTR); }
+invis(ible)?        { yylval.i = INVIS; return(ATTR); }
+noedge        { yylval.i = INVIS; return ATTR; }
+fill                { yylval.i = FILL; return ATTR; }
+solid        ;
+dot(ted)?        return(yylval.i = DOT);
+dash(ed)?        return(yylval.i = DASH);
+chop                return(yylval.i = CHOP);
+
+spread        { yylval.i = SPREAD; return TEXTATTR; }
+ljust        { yylval.i = LJUST; return TEXTATTR; }
+rjust        { yylval.i = RJUST; return TEXTATTR; }
+above        { yylval.i = ABOVE; return TEXTATTR; }
+below        { yylval.i = BELOW; return TEXTATTR; }
+center        { yylval.i = CENTER; return TEXTATTR; }
+
+"<-"                { yylval.i = HEAD1; return(HEAD); }
+"->"                { yylval.i = HEAD2; return(HEAD); }
+"<->"        { yylval.i = HEAD12; return(HEAD); }
+
+".x"                        return(yylval.i = DOTX);
+".y"                        return(yylval.i = DOTY);
+"."(ht|height)        return(yylval.i = DOTHT);
+"."(wid|width)        return(yylval.i = DOTWID);
+"."(rad|radius)        return(yylval.i = DOTRAD);
+
+from                return(yylval.i = FROM);
+to                return(yylval.i = TO);
+at                return(yylval.i = AT);
+by                return(yylval.i = BY);
+with                return(yylval.i = WITH);
+last                return(yylval.i = LAST);
+
+log                return(LOG);
+exp                return(EXP);
+sin                return(SIN);
+cos                return(COS);
+atan2        return(ATAN2);
+sqrt                return(SQRT);
+rand                return(RAND);
+max                return(MAX);
+min                return(MIN);
+int                return(INT);
+
+"=="                return(EQ);
+">="                return(GE);
+"<="                return(LE);
+"!="                return(NEQ);
+">"                return(GT);
+"<"                return(LT);
+"&&"                return(ANDAND);
+"||"                return(OROR);
+"!"                return(NOT);        
+
+Here                return(yylval.i = HERE);
+
+for                return(FOR);
+^Endfor\n        { endfor(); }
+do                { yylval.p = delimstr("loop body"); return(DOSTR); }
+
+copy|include                return(COPY);
+(thru|through){WS}+        { BEGIN thru; return(THRU); }
+{A}{B}*|.                { yylval.st = copythru(yytext); BEGIN A; return(DEFNAME); }
+until                return(UNTIL);
+
+if                { ifsw = 1; return(IF); }
+then                { if (!ifsw) { yylval.i = THEN; return(ATTR); }
+                  yylval.p = delimstr("then part"); ifsw = 0;
+                  return(THENSTR); }
+else                { yylval.p = delimstr("else part"); return(ELSESTR); }
+
+sh{WS}+        { BEGIN sh;
+                  if ((delim = input()) == '{') delim = '}';        /* no nested {} */
+                  shell_init(); }
+{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);
+                  }
+                }
+.|\n        { if (yytext[0] == delim) {
+                        shell_exec();
+                        BEGIN A;
+                  } else
+                        shell_text(yytext);
+                }
+
+define{WS}+                { BEGIN def; }
+{A}{B}*                { definition(yytext); BEGIN A; }
+undef(ine)?{WS}+{A}{B}*        { undefine(yytext); }
+
+first                { yylval.i = 1; return(NTH); }
+{D}+(th|nd|rd|st)        { yylval.i = atoi(yytext); return(NTH); }
+({D}+("."?){D}*|"."{D}+)((e|E)("+"|-)?{D}+)?i? {
+                          yylval.f = atof(yytext); return(NUMBER); }
+
+{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((unsigned char)yytext[0])) {
+                        yylval.p = tostring(yytext);
+                        return(VARNAME);
+                } else {
+                        yylval.p = tostring(yytext);
+                        return(PLACENAME);
+                }
+        }
+
+\"                { BEGIN str; clen=0; }
+\"                { cbuf[clen]=0; yylval.p = tostring(cbuf); BEGIN A; return(TEXT); }
+\n                { cbuf[clen]=0; ERROR "missing quote in string \"%s\"", cbuf WARNING;
+                                BEGIN A; return(ST); }
+"\\\""        { cbuf[clen++]='"'; }
+"\\"t        { cbuf[clen++]='\t'; }
+"\\\\"        { cbuf[clen++]='\\'; }
+.                { CADD; }
+
+#.*                ;
+
+.                return(yylval.i = yytext[0]);
+
+%%
diff --git a/src/cmd/svgpic/picy.y b/src/cmd/svgpic/picy.y
t@@ -0,0 +1,328 @@
+%{
+#include 
+#include "pic.h"
+#include 
+#include 
+#include 
+
+YYSTYPE        y;
+
+extern        void        yyerror(char *);
+extern        int        yylex(void);
+%}
+
+%token                BOX        1        /* DON'T CHANGE THESE! */
+%token                LINE        2
+%token                ARROW        3
+%token                CIRCLE        4
+%token                ELLIPSE        5
+%token                ARC        6
+%token                SPLINE        7
+%token                BLOCK        8
+%token        

TEXT 9 +%token

TROFF 10 +%token MOVE 11 +%token BLOCKEND 12 +%token PLACE 13 +%token PRINT RESET THRU UNTIL +%token FOR IF COPY +%token

THENSTR ELSESTR DOSTR PLACENAME VARNAME SPRINTF +%token DEFNAME +%token ATTR TEXTATTR +%token LEFT RIGHT UP DOWN FROM TO AT BY WITH HEAD CW CCW THEN +%token HEIGHT WIDTH RADIUS DIAMETER LENGTH SIZE +%token CORNER HERE LAST NTH SAME BETWEEN AND +%token EAST WEST NORTH SOUTH NE NW SE SW START END +%token DOTX DOTY DOTHT DOTWID DOTRAD +%token NUMBER +%token LOG EXP SIN COS ATAN2 SQRT RAND MIN MAX INT +%token DIR +%token DOT DASH CHOP FILL NOEDGE +%token ST /* statement terminator */ + +%right '=' +%left OROR +%left ANDAND +%nonassoc GT LT LE GE EQ NEQ +%left '+' '-' +%left '*' '/' '%' +%right UMINUS NOT +%right '^' + +%type expr if_expr asgn +%type

name text +%type optop exprlist +%type if for copy + +/* this is a lie: picture and position are really the whole union */ +%type leftbrace picture piclist position lbracket +%type prim place blockname +%type textlist textattr /* not a sensible value */ +%type 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 ']' { $$=rightthing($1,']'); } attrlist + { $$ = blockgen($1, $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); } + | CHOP PLACENAME { makeattr(CHOP, PLACENAME, getvar($2)); } + | FILL expr { makefattr(FILL, !DEFAULT, $2); } + | FILL { makefattr(FILL, DEFAULT, 0.0); } + | NOEDGE { makeiattr(NOEDGE, 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 %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/svgpic/plsvg.c b/src/cmd/svgpic/plsvg.c
t@@ -0,0 +1,328 @@
+#include 
+#include 
+#include 
+#include "pic.h"
+extern int dbg;
+
+#define        abs(n)        (n >= 0 ? n : -(n))
+#define        max(x,y)        ((x)>(y) ? (x) : (y))
+
+char        *textshift = "\\v'.2m'";        /* move text this far down */
+
+/* scaling stuff defined by s command as X0,Y0 to X1,Y1 */
+/* output dimensions set by -l,-w options to 0,0 to hmax, vmax */
+/* default output is 6x6 inches */
+
+
+double        xscale;
+double        yscale;
+
+double        hpos        = 0;        /* current horizontal position in output coordinate system */
+double        vpos        = 0;        /* current vertical position; 0 is top of page */
+
+double        htrue        = 0;        /* where we really are */
+double        vtrue        = 0;
+
+double        X0, Y0;                /* left bottom of input */
+double        X1, Y1;                /* right top of input */
+
+double        hmax;                /* right end of output */
+double        vmax;                /* top of output (down is positive) */
+
+extern        double        deltx;
+extern        double        delty;
+extern        double        xmin, ymin, xmax, ymax;
+
+double        xconv(double), yconv(double), xsc(double), ysc(double);
+void        space(double, double, double, double);
+void        hgoto(double), vgoto(double), hmot(double), vmot(double);
+void        move(double, double), movehv(double, double);
+
+char        svgfill[40] = "transparent";
+char        svgstroke[40] = "black";
+
+void openpl(char *s)        /* initialize device; s is residue of .PS invocation line */
+{
+        double maxw, maxh, ratio = 1;
+        double odeltx = deltx, odelty = delty;
+
+        hpos = vpos = 0;
+        maxw = getfval("maxpswid");
+        maxh = getfval("maxpsht");
+        if (deltx > maxw) {        /* shrink horizontal */
+                ratio = maxw / deltx;
+                deltx *= ratio;
+                delty *= ratio;
+        }
+        if (delty > maxh) {        /* shrink vertical */
+                ratio = maxh / delty;
+                deltx *= ratio;
+                delty *= ratio;
+        }
+        if (ratio != 1) {
+                fprintf(stderr, "pic: %g X %g picture shrunk to", odeltx, odelty);
+                fprintf(stderr, " %g X %g\n", deltx, delty);
+        }
+        space(xmin, ymin, xmax, ymax);
+
+        printf("\n");
+        printf("\n");
+
+        /*
+        printf("... %g %g %g %g\n", xmin, ymin, xmax, ymax);
+        printf("... %.3fi %.3fi %.3fi %.3fi\n",
+                xconv(xmin), yconv(ymin), xconv(xmax), yconv(ymax));
+        printf(".nr 00 \\n(.u\n");
+        printf(".nf\n");
+        printf(".PS %.3fi %.3fi %s", yconv(ymin), xconv(xmax), s);
+        */
+}
+
+void space(double x0, double y0, double x1, double y1)        /* set limits of page */
+{
+        X0 = x0;
+        Y0 = y0;
+        X1 = x1;
+        Y1 = y1;
+        xscale = deltx == 0.0 ? 1.0 : deltx / (X1-X0);
+        yscale = delty == 0.0 ? 1.0 : delty / (Y1-Y0);
+
+        xscale *= 144;
+        yscale *= 144;
+}
+
+double xconv(double x)        /* convert x from external to internal form */
+{
+        return (x-X0) * xscale;
+}
+
+double xsc(double x)        /* convert x from external to internal form, scaling only */
+{
+
+        return (x) * xscale;
+}
+
+double yconv(double y)        /* convert y from external to internal form */
+{
+        return (Y1-y) * yscale;
+}
+
+double ysc(double y)        /* convert y from external to internal form, scaling only */
+{
+        return (y) * yscale;
+}
+
+void closepl(char *PEline)        /* clean up after finished */
+{
+        printf("\n");
+        printf("\n");
+}
+
+void move(double x, double y)        /* go to position x, y in external coords */
+{
+        hgoto(xconv(x));
+        vgoto(yconv(y));
+}
+
+void movehv(double h, double v)        /* go to internal position h, v */
+{
+        hgoto(h);
+        vgoto(v);
+}
+
+void hmot(double n)        /* generate n units of horizontal motion */
+{
+        hpos += n;
+}
+
+void vmot(double n)        /* generate n units of vertical motion */
+{
+        vpos += n;
+}
+
+void hgoto(double n)
+{
+        hpos = n;
+}
+
+void vgoto(double n)
+{
+        vpos = n;
+}
+
+void hvflush(void)        /* get to proper point for output */
+{
+/*
+        if (fabs(hpos-htrue) >= 0.0005) {
+                printf("\\h'%.3fi'", hpos - htrue);
+                htrue = hpos;
+        }
+        if (fabs(vpos-vtrue) >= 0.0005) {
+                printf("\\v'%.3fi'", vpos - vtrue);
+                vtrue = vpos;
+        }
+*/
+}
+
+void printlf(int n, char *f)
+{
+}
+
+void troff(char *s)        /* output troff right here */
+{
+        printf("%s\n", s);
+}
+
+void label(char *s, int t, int nh)        /* text s of type t nh half-lines up */
+{
+        char *anchor;
+
+        if (!s)
+                return;
+
+        if (t & ABOVE)
+                nh++;
+        else if (t & BELOW)
+                nh--;
+        t &= ~(ABOVE|BELOW);
+        anchor = 0;
+        if (t & LJUST) {
+                // default
+        } else if (t & RJUST) {
+                anchor = "end";
+        } else {        /* CENTER */
+                anchor = "middle";
+        }
+        printf("%s\n", s);
+}
+
+void line(double x0, double y0, double x1, double y1, int attr, double ddval)        /* draw line from x0,y0 to x1,y1 */
+{
+        printf("\n");
+}
+
+void arrow(double x0, double y0, double x1, double y1, double w, double h,
+         double ang, int nhead)         /* draw arrow (without shaft) */
+{
+        double alpha, rot, drot, hyp;
+        double 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;
+        dprintf("rot=%g, hyp=%g, alpha=%g\n", rot, hyp, alpha);
+        printf("= 0; i--) {
+                drot = 2 * rot / (double) (2-1) * (double) i;
+                dx = hyp * cos(alpha + PI - rot + drot);
+                dy = hyp * sin(alpha + PI - rot + drot);
+                dprintf("dx,dy = %g,%g\n", dx, dy);
+                if(i == 1)
+                        printf("M %.3f %.3f L %.3f %.3f", xconv(x1+dx), yconv(y1+dy), xconv(x1), yconv(y1));
+                else
+                        printf(" L %.3f %.3f", xconv(x1+dx), yconv(y1+dy)); 
+        }
+        if (nhead > 2)
+                printf(" Z");
+        printf("\"");
+        if(nhead > 3)
+                printf(" fill=\"black\" stroke=\"black\"");
+        else if(nhead == 3)
+                printf(" fill=\"white\" stroke=\"black\"");
+        else
+                printf(" fill=\"transparent\" stroke=\"black\"");
+        printf("/>\n");
+}
+
+double lastgray = 0;
+
+void fillstart(double v, int vis, int fill)
+{
+        int x;
+
+        if(fill) {
+                x = (int)(v*255.0);
+                sprintf(svgfill, "#%02x%02x%02x", x, x, x);
+        } else
+                strcpy(svgfill, "transparent");
+        if(vis)
+                strcpy(svgstroke, "black");
+        else
+                strcpy(svgstroke, "transparent");
+}
+
+void fillend(void)
+{
+        strcpy(svgfill, "transparent");
+        strcpy(svgstroke, "black");
+}
+
+void box(double x0, double y0, double x1, double y1, int attr, double ddval)
+{
+        printf("\n");
+
+}
+
+void circle(double x, double y, double r)
+{
+        printf("\n", xconv(x), yconv(y), xsc(r), svgfill, svgstroke);
+}
+
+void spline(double x, double y, double n, ofloat *p, int attr, double ddval)
+{
+        int i;
+        double x1, y1, x2, y2;
+
+        printf("\n");
+}
+
+void ellipse(double x, double y, double r1, double r2)
+{
+        printf("\n", xconv(x), yconv(y), xsc(r1), ysc(r2), svgfill, svgstroke);
+}
+
+void arc(double x, double y, double x0, double y0, double x1, double y1, double r)        /* draw arc with center x,y */
+{
+        printf("\n",
+                xconv(x0), yconv(y0),
+                xsc(r), ysc(r), 0, 0, 0, xconv(x1), yconv(y1),
+                svgfill, svgstroke);
+}
diff --git a/src/cmd/svgpic/print.c b/src/cmd/svgpic/print.c
t@@ -0,0 +1,182 @@
+#include 
+#include 
+#include "pic.h"
+#include "y.tab.h"
+
+void dotext(obj *);
+void ellipse(double, double, double, double);
+void circle(double, double, double);
+void arc(double, double, double, double, double, double, double);
+void arrow(double, double, double, double, double, double, double, int);
+void line(double, double, double, double, int, double);
+void box(double, double, double, double, int, double);
+void spline(double x, double y, double n, ofloat *p, int dashed, double ddval);
+void move(double, double);
+void troff(char *);
+void dot(void);
+void fillstart(double, int, int), fillend(void);
+
+void print(void)
+{
+        obj *p;
+        int i, j, k, m;
+        int fill, vis, invis;
+        double x0, y0, x1, y1, ox, oy, dx, dy, ndx, ndy;
+
+        x1 = y1 = 0.0; /* Botch? (gcc) */
+
+        for (i = 0; i < nobj; i++) {
+                p = objlist[i];
+                ox = p->o_x;
+                oy = p->o_y;
+                if (p->o_count >= 1)
+                        x1 = p->o_val[0];
+                if (p->o_count >= 2)
+                        y1 = p->o_val[1];
+                m = p->o_mode;
+                fill = p->o_attr & FILLBIT;
+                invis = p->o_attr & INVIS;
+                vis = !invis;
+                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 (fill) {
+                                move(x0, y0);
+                                fillstart(p->o_fillval, vis, fill);
+                        }
+                        if (p->o_type == BLOCK)
+                                ;        /* nothing at all */
+                        else if (invis && !fill)
+                                ;        /* nothing at all */
+                        else
+                                box(x0, y0, x1, y1, p->o_attr & (DOTBIT|DASHBIT), p->o_ddval);
+                        if (fill)
+                                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 (fill)
+                                fillstart(p->o_fillval, vis, fill);
+                        if (vis || fill)
+                                circle(ox, oy, x1);
+                        if (fill)
+                                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 (fill)
+                                fillstart(p->o_fillval, vis, fill);
+                        if (vis || fill)
+                                ellipse(ox, oy, x1, y1);
+                        if (fill)
+                                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:
+                        if (fill) {
+                                move(ox, oy);
+                                fillstart(p->o_fillval, vis, fill);
+                        }
+                        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 (invis && !fill)
+                                /* probably wrong when it's cw */
+                                move(x1, y1);
+                        else
+                                arc(ox, oy, x1, y1, p->o_val[2], p->o_val[3], p->o_val[6]);
+                        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 (fill)
+                                fillend();
+                        if (p->o_attr & CW_ARC)
+                                move(x1, y1);        /* because drawn backwards */
+                        move(ox, oy);
+                        dotext(p);
+                        break;
+                case LINE:
+                case ARROW:
+                case SPLINE:
+                        if (fill) {
+                                move(ox, oy);
+                                fillstart(p->o_fillval, vis, fill);
+                        }
+                        if (vis && 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 (invis && !fill)
+                                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];
+                                        line(dx, dy, ndx, ndy, p->o_attr & (DOTBIT|DASHBIT), p->o_ddval);
+                                        dx = ndx;
+                                        dy = ndy;
+                                }
+                        }
+                        if (vis && 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);
+                        }
+                        if (fill)
+                                fillend();
+                        move((ox + x1)/2, (oy + y1)/2);        /* center */
+                        dotext(p);
+                        break;
+                case MOVE:
+                        move(ox, oy);
+                        break;
+                case TEXT:
+                        move(ox, oy);
+                        if (vis)
+                                dotext(p);
+                        break;
+                }
+        }
+}
+
+void dotext(obj *p)        /* print text strings of p in proper vertical spacing */
+{
+        int i, nhalf;
+        void label(char *, int, int);
+
+        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/svgpic/symtab.c b/src/cmd/svgpic/symtab.c
t@@ -0,0 +1,104 @@
+#include 
+#include 
+#include 
+#include 
+#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((int) 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/svgpic/textgen.c b/src/cmd/svgpic/textgen.c
t@@ -0,0 +1,111 @@
+#include        
+#include        "pic.h"
+#include        "y.tab.h"
+
+obj *textgen(void)
+{
+        int i, sub, nstr, at, with, hset, invis;
+        double xwith, ywith, h, w, x0, y0, x1, y1;
+        obj *p, *ppos;
+        Attr *ap;
+
+        at = with = nstr = hset = invis = 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 INVIS:
+                        invis = INVIS;
+                        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_attr = invis;
+        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;
+        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++;
+}