commit 3ebbd193dce0724e106ec9af627a3782676ae510 from: Russ Cox date: Mon Jun 19 13:51:14 2017 UTC 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 commit - a9530c00e860cbbac75b03a1c6bce93ddf0a26f6 commit + 3ebbd193dce0724e106ec9af627a3782676ae510 blob - f47fa42aa8755bd75de3cc86983bb0a011a79c2b blob + de443cc0f4a219a2385a8433be8edf7cae52e6b5 --- man/man1/pic.1 +++ man/man1/pic.1 @@ -23,6 +23,11 @@ pic, tpic \- troff and tex preprocessors for drawing p [ .I files ] +.PP +.B svgpic +[ +.I files +] .SH DESCRIPTION .I Pic is a @@ -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 @@ -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. blob - /dev/null blob + bec41e47a7d930ec79bd12cdedfc421b2e876bd4 (mode 644) --- /dev/null +++ src/cmd/svgpic/arcgen.c @@ -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 */ +} + blob - /dev/null blob + 9015d56d2ae001131c56a825e7f631a0c2834559 (mode 644) --- /dev/null +++ src/cmd/svgpic/blockgen.c @@ -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; + } + } +} blob - /dev/null blob + b575cd2f4251383fff6bb332bc76ac33ccbc3b4a (mode 644) --- /dev/null +++ src/cmd/svgpic/boxgen.c @@ -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); +} blob - /dev/null blob + 6b1f15486dc20bf2d7d0b6b10c917cb35ebb8560 (mode 644) --- /dev/null +++ src/cmd/svgpic/circgen.c @@ -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); +} blob - /dev/null blob + cece48b573589ae5db4c4e9c3b990fa1fa939332 (mode 644) --- /dev/null +++ src/cmd/svgpic/for.c @@ -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; + } +} blob - /dev/null blob + a81f622ef5e3d536ec3a72eec30cc735f4ad19f1 (mode 644) --- /dev/null +++ src/cmd/svgpic/input.c @@ -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); +} blob - /dev/null blob + e0db5fc6c5502b902f5c259e9aafdbadcaa19a1f (mode 644) --- /dev/null +++ src/cmd/svgpic/linegen.c @@ -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); +} blob - /dev/null blob + 1c7f5f655f440113dac0a0d4d9df76bd49499bad (mode 644) --- /dev/null +++ src/cmd/svgpic/main.c @@ -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; +} blob - /dev/null blob + e888fafc827612055d05f390f5eb05f21508489e (mode 644) --- /dev/null +++ src/cmd/svgpic/misc.c @@ -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; +} blob - /dev/null blob + b0284ca1db156b230eded9f4bb52d0b28b1ebefe (mode 644) --- /dev/null +++ src/cmd/svgpic/mkfile @@ -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 blob - /dev/null blob + 5ff44d46b01dcca94e57d5a0f90faa83b78b043d (mode 644) --- /dev/null +++ src/cmd/svgpic/movegen.c @@ -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); +} blob - /dev/null blob + 928d38dd1b8f034f38f4358d2bfbf521168b98c6 (mode 644) --- /dev/null +++ src/cmd/svgpic/pic.h @@ -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") blob - /dev/null blob + 45130f24cdca5f73217e7927f497286da647ad2a (mode 644) --- /dev/null +++ src/cmd/svgpic/picl.lx @@ -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]); + +%% blob - /dev/null blob + b534b5a53e437f63849a6d153eb01f39a22c9868 (mode 644) --- /dev/null +++ src/cmd/svgpic/picy.y @@ -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; } + ; blob - /dev/null blob + 3f342e68b56079d6fadc1016ef2aece4f5615d2e (mode 644) --- /dev/null +++ src/cmd/svgpic/plsvg.c @@ -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); +} blob - /dev/null blob + 1be7386723b26fb3f40e7c4f9f0914d18914126c (mode 644) --- /dev/null +++ src/cmd/svgpic/print.c @@ -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; + } +} blob - /dev/null blob + 059d3be7b852d2568da47f04cfddf2a7d7e66126 (mode 644) --- /dev/null +++ src/cmd/svgpic/symtab.c @@ -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; */ +} blob - /dev/null blob + 082f8ee05e0a6556d146897288145efcf1c8c1c5 (mode 644) --- /dev/null +++ src/cmd/svgpic/textgen.c @@ -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++; +}