Add graph, plot
diff --git a/src/cmd/plot/libplot/box.c b/src/cmd/plot/libplot/box.c
new file mode 100644
index 0000000..4654bc8
--- /dev/null
+++ b/src/cmd/plot/libplot/box.c
@@ -0,0 +1,8 @@
+#include "mplot.h"
+void box(double x0, double y0, double x1, double y1){
+	move(x0, y0);
+	vec(x0, y1);
+	vec(x1, y1);
+	vec(x1, y0);
+	vec(x0, y0);
+}
diff --git a/src/cmd/plot/libplot/cfill.c b/src/cmd/plot/libplot/cfill.c
new file mode 100644
index 0000000..8f700c7
--- /dev/null
+++ b/src/cmd/plot/libplot/cfill.c
@@ -0,0 +1,5 @@
+#include "mplot.h"
+void cfill(char *s){
+	int k=bcolor(s);
+	if(k>=0) e1->backgr=k;
+}
diff --git a/src/cmd/plot/libplot/circ.c b/src/cmd/plot/libplot/circ.c
new file mode 100644
index 0000000..5333711
--- /dev/null
+++ b/src/cmd/plot/libplot/circ.c
@@ -0,0 +1,12 @@
+#include "mplot.h"
+void circ(double xc, double yc, double r){
+	Point p;
+	int rad;
+	p.x=SCX(xc);
+	p.y=SCY(yc);
+	if (r < 0) 
+		rad=SCR(-r);
+	else
+		rad=SCR(r);
+	ellipse(screen, p, rad, rad, 0, getcolor(e1->foregr), ZP);
+}
diff --git a/src/cmd/plot/libplot/closepl.c b/src/cmd/plot/libplot/closepl.c
new file mode 100644
index 0000000..b7a8c99
--- /dev/null
+++ b/src/cmd/plot/libplot/closepl.c
@@ -0,0 +1,4 @@
+#include "mplot.h"
+void closepl(void){
+	m_finish();
+}
diff --git a/src/cmd/plot/libplot/color.c b/src/cmd/plot/libplot/color.c
new file mode 100644
index 0000000..bd9be4e
--- /dev/null
+++ b/src/cmd/plot/libplot/color.c
@@ -0,0 +1,4 @@
+#include "mplot.h"
+void color(char *s){
+	e1->foregr=bcolor(s);
+}
diff --git a/src/cmd/plot/libplot/disk.c b/src/cmd/plot/libplot/disk.c
new file mode 100644
index 0000000..47b3901
--- /dev/null
+++ b/src/cmd/plot/libplot/disk.c
@@ -0,0 +1,12 @@
+#include "mplot.h"
+void plotdisc(double xc, double yc, double r){
+	Point p;
+	int rad;
+	p.x=SCX(xc);
+	p.y=SCY(yc);
+	if (r < 0) 
+		rad=SCR(-r);
+	else
+		rad=SCR(r);
+	fillellipse(screen, p, rad, rad, getcolor(e1->foregr), ZP);
+}
diff --git a/src/cmd/plot/libplot/doublebuffer.c b/src/cmd/plot/libplot/doublebuffer.c
new file mode 100644
index 0000000..485894f
--- /dev/null
+++ b/src/cmd/plot/libplot/doublebuffer.c
@@ -0,0 +1,4 @@
+#include "mplot.h"
+void doublebuffer(void){
+	m_dblbuf();
+}
diff --git a/src/cmd/plot/libplot/dpoint.c b/src/cmd/plot/libplot/dpoint.c
new file mode 100644
index 0000000..7d5b6ff
--- /dev/null
+++ b/src/cmd/plot/libplot/dpoint.c
@@ -0,0 +1,6 @@
+#include "mplot.h"
+void dpoint(double x, double y){
+	draw(screen, Rect(SCX(x), SCY(y), SCX(x)+1, SCY(y)+1), getcolor(e1->foregr),
+		nil, ZP);
+	move(x, y);
+}
diff --git a/src/cmd/plot/libplot/erase.c b/src/cmd/plot/libplot/erase.c
new file mode 100644
index 0000000..ab91347
--- /dev/null
+++ b/src/cmd/plot/libplot/erase.c
@@ -0,0 +1,5 @@
+#include "mplot.h"
+void erase(void){
+	m_swapbuf();
+	m_clrwin(clipminx, clipminy, clipmaxx, clipmaxy, e1->backgr);
+}
diff --git a/src/cmd/plot/libplot/fill.c b/src/cmd/plot/libplot/fill.c
new file mode 100644
index 0000000..a33294e
--- /dev/null
+++ b/src/cmd/plot/libplot/fill.c
@@ -0,0 +1,167 @@
+/*
+ * fill -- polygon tiler
+ * Updating the edgelist from scanline to scanline could be quicker if no
+ * edges cross:  we can just merge the incoming edges.  If the scan-line
+ * filling routine were a parameter, we could do textured
+ * polygons, polyblt, and other such stuff.
+ */
+#include "mplot.h"
+typedef enum{
+	Odd=1,
+	Nonzero=~0
+}Windrule;
+typedef struct edge Edge;
+struct edge{
+	Point p;	/* point of crossing current scan-line */
+	int maxy;	/* scan line at which to discard edge */
+	int dx;		/* x increment if x fraction<1 */
+	int dx1;	/* x increment if x fraction>=1 */
+	int x;		/* x fraction, scaled by den */
+	int num;	/* x fraction increment for unit y change, scaled by den */
+	int den;	/* x fraction increment for unit x change, scaled by num */
+	int dwind;	/* increment of winding number on passing this edge */
+	Edge *next;	/* next edge on current scanline */
+	Edge *prev;	/* previous edge on current scanline */
+};
+static void insert(Edge *ep, Edge **yp){
+	while(*yp && (*yp)->p.x<ep->p.x) yp=&(*yp)->next;
+	ep->next=*yp;
+	*yp=ep;
+	if(ep->next){
+		ep->prev=ep->next->prev;
+		ep->next->prev=ep;
+		if(ep->prev)
+			ep->prev->next=ep;
+	}
+	else
+		ep->prev=0;
+}
+static void polygon(int cnt[], double *pts[], Windrule w, int v){
+	Edge *edges, *ep, *nextep, **ylist, **eylist, **yp;
+	Point p, q, p0, p1, p10;
+	int i, dy, nbig, y, left, right, wind, nwind, nvert;
+	int *cntp;
+	double **ptsp, *xp;
+	nvert=0;
+	for(cntp=cnt;*cntp;cntp++) nvert+=*cntp;
+	edges=(Edge *)malloc(nvert*sizeof(Edge));
+	if(edges==0){
+	NoSpace:
+		fprintf(stderr, "polygon: no space\n");
+		exits("malloc failed");
+	}
+	ylist=(Edge **)malloc(Dy(screen->r)*sizeof(Edge *));
+	if(ylist==0) goto NoSpace;
+	eylist=ylist+Dy(screen->r);
+	for(yp=ylist;yp!=eylist;yp++) *yp=0;
+	ep=edges;
+	for(cntp=cnt,ptsp=pts;*cntp;cntp++,ptsp++){
+		p.x=SCX((*ptsp)[*cntp*2-2]);
+		p.y=SCY((*ptsp)[*cntp*2-1]);
+		nvert=*cntp;
+		for(xp=*ptsp,i=0;i!=nvert;xp+=2,i++){
+			q=p;
+			p.x=SCX(xp[0]);
+			p.y=SCY(xp[1]);
+			if(p.y==q.y) continue;
+			if(p.y<q.y){
+				p0=p;
+				p1=q;
+				ep->dwind=1;
+			}
+			else{
+				p0=q;
+				p1=p;
+				ep->dwind=-1;
+			}
+			if(p1.y<=screen->r.min.y) continue;
+			if(p0.y>=screen->r.max.y) continue;
+			ep->p=p0;
+			if(p1.y>screen->r.max.y)
+				ep->maxy=screen->r.max.y;
+			else
+				ep->maxy=p1.y;
+			p10=subpt(p1, p0);
+			if(p10.x>=0){
+				ep->dx=p10.x/p10.y;
+				ep->dx1=ep->dx+1;
+			}
+			else{
+				p10.x=-p10.x;
+				ep->dx=-(p10.x/p10.y); /* this nonsense rounds toward zero */
+				ep->dx1=ep->dx-1;
+			}
+			ep->x=0;
+			ep->num=p10.x%p10.y;
+			ep->den=p10.y;
+			if(ep->p.y<screen->r.min.y){
+				dy=screen->r.min.y-ep->p.y;
+				ep->x+=dy*ep->num;
+				nbig=ep->x/ep->den;
+				ep->p.x+=ep->dx1*nbig+ep->dx*(dy-nbig);
+				ep->x%=ep->den;
+				ep->p.y=screen->r.min.y;
+			}
+			insert(ep, ylist+(ep->p.y-screen->r.min.y));
+			ep++;
+		}
+	}
+	left = 0;
+	for(yp=ylist,y=screen->r.min.y;yp!=eylist;yp++,y++){
+		wind=0;
+		for(ep=*yp;ep;ep=nextep){
+			nwind=wind+ep->dwind;
+			if(nwind&w){	/* inside */
+				if(!(wind&w)){
+					left=ep->p.x;
+					if(left<screen->r.min.x) left=screen->r.min.x;
+				}
+			}
+			else if(wind&w){
+				right=ep->p.x;
+				if(right>=screen->r.max.x) right=screen->r.max.x;
+#define BART_BUG_FIXED	/* what goes on here?? -rob */
+#ifdef BART_BUG_FIXED
+				if(right>left)
+					line(screen, Pt(left, y), Pt(right, y), Endsquare, Endsquare, 0, getcolor(v), ZP);
+#else
+				if(right>left){
+					switch(v){
+					default:
+						segment(&screen, Pt(left, y), Pt(right, y),
+							~0, D&~S);
+						segment(&screen, Pt(left, y), Pt(right, y),
+							v, f);
+						break;
+					case 0:
+						segment(&screen, Pt(left, y), Pt(right, y),
+							~0, D&~S);
+						break;
+					case 3:
+						segment(&screen, Pt(left, y), Pt(right, y),
+							v, f);
+						break;
+					}
+				}
+#endif
+			}
+			wind=nwind;
+			nextep=ep->next;
+			if(++ep->p.y!=ep->maxy){
+				ep->x+=ep->num;
+				if(ep->x>=ep->den){
+					ep->x-=ep->den;
+					ep->p.x+=ep->dx1;
+				}
+				else
+					ep->p.x+=ep->dx;
+				insert(ep, yp+1);
+			}
+		}
+	}
+	free((char *)edges);
+	free((char *)ylist);
+}
+void fill(int num[], double *ff[]){
+	polygon(num, ff, Odd, e1->foregr);
+}
diff --git a/src/cmd/plot/libplot/frame.c b/src/cmd/plot/libplot/frame.c
new file mode 100644
index 0000000..bbc37a1
--- /dev/null
+++ b/src/cmd/plot/libplot/frame.c
@@ -0,0 +1,16 @@
+#include "mplot.h"
+void frame(double xs, double ys, double xf, double yf){
+	register double	osidex, osidey;
+	osidex = e1->sidex;
+	osidey = e1->sidey;
+	e1->left = e0->left + xs * e0->sidex;
+	e1->bottom = e0->bottom + ys * e0->sidey;
+	e1->sidex = (xf-xs)*e0->sidex;
+	e1->sidey = (yf-ys)*e0->sidey;
+	e1->scalex *= (e1->sidex / osidex);
+	e1->scaley *= (e1->sidey / osidey);
+	e1->quantum=e0->quantum/sqrt(e1->scalex*e1->scalex +
+		e1->scaley*e1->scaley);
+	if(e1->quantum < .01)
+		e1->quantum = .01;
+}
diff --git a/src/cmd/plot/libplot/grade.c b/src/cmd/plot/libplot/grade.c
new file mode 100644
index 0000000..0afe7f4
--- /dev/null
+++ b/src/cmd/plot/libplot/grade.c
@@ -0,0 +1,4 @@
+#include "mplot.h"
+void grade(double x){
+	e1->grade = x;
+}
diff --git a/src/cmd/plot/libplot/line.c b/src/cmd/plot/libplot/line.c
new file mode 100644
index 0000000..416d2ee
--- /dev/null
+++ b/src/cmd/plot/libplot/line.c
@@ -0,0 +1,5 @@
+#include "mplot.h"
+void plotline(double x0, double y0, double x1, double y1){
+	move(x0, y0);
+	vec(x1, y1);
+}
diff --git a/src/cmd/plot/libplot/machdep.c b/src/cmd/plot/libplot/machdep.c
new file mode 100644
index 0000000..abcb761
--- /dev/null
+++ b/src/cmd/plot/libplot/machdep.c
@@ -0,0 +1,141 @@
+#include "mplot.h"
+Image *offscreen;
+/*
+ * Clear the window from x0, y0 to x1, y1 (inclusive) to color c
+ */
+void m_clrwin(int x0, int y0, int x1, int y1, int c){
+	draw(offscreen, Rect(x0, y0, x1+1, y1+1), getcolor(c), nil, ZP);
+}
+/*
+ * Draw text between pointers p and q with first character centered at x, y.
+ * Use color c.  Centered if cen is non-zero, right-justified if right is non-zero.
+ * Returns the y coordinate for any following line of text.
+ */
+int m_text(int x, int y, char *p, char *q, int c, int cen, int right){
+	Point tsize;
+	USED(c);
+	tsize=stringsize(font, p);
+	if(cen) x -= tsize.x/2;
+	else if(right) x -= tsize.x;
+	stringn(offscreen, Pt(x, y-tsize.y/2), getcolor(c), ZP, font, p, q-p);
+	return y+tsize.y;
+}
+/*
+ * Draw the vector from x0, y0 to x1, y1 in color c.
+ * Clipped by caller
+ */
+void m_vector(int x0, int y0, int x1, int y1, int c){
+	line(offscreen, Pt(x0, y0), Pt(x1, y1), Endsquare, Endsquare, 0, getcolor(c), ZP);
+}
+char *scanint(char *s, int *n){
+	while(*s<'0' || '9'<*s){
+		if(*s=='\0'){
+			fprint(2, "plot: bad -Wxmin,ymin,xmax,ymax\n");
+			exits("bad arg");
+		}
+		s++;
+	}
+	*n=0;
+	while('0'<=*s && *s<='9'){
+		*n=*n*10+*s-'0';
+		s++;
+	}
+	return s;
+}
+char *rdenv(char *name){
+	char *v;
+	int fd, size;
+	fd=open(name, OREAD);
+	if(fd<0) return 0;
+	size=seek(fd, 0, 2);
+	v=malloc(size+1);
+	if(v==0){
+		fprint(2, "Can't malloc: %r\n");
+		exits("no mem");
+	}
+	seek(fd, 0, 0);
+	read(fd, v, size);
+	v[size]=0;
+	close(fd);
+	return v;
+}
+/*
+ * Startup initialization
+ */
+void m_initialize(char *s){
+	static int first=1;
+	int dx, dy;
+	USED(s);
+	if(first){
+		initdraw(0,0,"plot");
+		einit(Emouse);
+		clipminx=mapminx=screen->r.min.x+4;
+		clipminy=mapminy=screen->r.min.y+4;
+		clipmaxx=mapmaxx=screen->r.max.x-5;
+		clipmaxy=mapmaxy=screen->r.max.y-5;
+		dx=clipmaxx-clipminx;
+		dy=clipmaxy-clipminy;
+		if(dx>dy){
+			mapminx+=(dx-dy)/2;
+			mapmaxx=mapminx+dy;
+		}
+		else{
+			mapminy+=(dy-dx)/2;
+			mapmaxy=mapminy+dx;
+		}
+		first=0;
+		offscreen = screen;
+	}
+}
+/*
+ * Clean up when finished
+ */
+void m_finish(void){
+	m_swapbuf();
+}
+void m_swapbuf(void){
+	if(offscreen!=screen)
+		draw(screen, offscreen->r, offscreen, nil, offscreen->r.min);
+	flushimage(display, 1);
+}
+void m_dblbuf(void){
+	if(offscreen==screen){
+		offscreen=allocimage(display, insetrect(screen->r, 4), screen->chan, 0, -1);
+		if(offscreen==0){
+			fprintf(stderr, "Can't double buffer\n");
+			offscreen=screen;
+		}
+	}
+}
+/* Assume colormap entry because
+ * Use cache to avoid repeated allocation.
+ */
+struct{
+	int		v;
+	Image	*i;
+}icache[32];
+
+Image*
+getcolor(int v)
+{
+	Image *i;
+	int j;
+
+	for(j=0; j<nelem(icache); j++)
+		if(icache[j].v==v && icache[j].i!=nil)
+			return icache[j].i;
+
+	i = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, v);
+	if(i == nil){
+		fprint(2, "plot: can't allocate image for color: %r\n");
+		exits("allocimage");
+	}
+	for(j=0; j<nelem(icache); j++)
+		if(icache[j].i == nil){
+			icache[j].v = v;
+			icache[j].i = i;
+			break;
+		}
+
+	return i;
+}
diff --git a/src/cmd/plot/libplot/mkfile b/src/cmd/plot/libplot/mkfile
new file mode 100644
index 0000000..a73bd61
--- /dev/null
+++ b/src/cmd/plot/libplot/mkfile
@@ -0,0 +1,42 @@
+PLAN9=../../../..
+<$PLAN9/src/mkhdr
+
+LIB=../libplot.a
+OFILES=box.$O\
+	cfill.$O\
+	circ.$O\
+	closepl.$O\
+	color.$O\
+	disk.$O\
+	doublebuffer.$O\
+	dpoint.$O\
+	erase.$O\
+	fill.$O\
+	frame.$O\
+	grade.$O\
+	line.$O\
+	machdep.$O\
+	move.$O\
+	openpl.$O\
+	parabola.$O\
+	pen.$O\
+	poly.$O\
+	ppause.$O\
+	pprompt.$O\
+	range.$O\
+	rarc.$O\
+	restore.$O\
+	rmove.$O\
+	rvec.$O\
+	save.$O\
+	sbox.$O\
+	spline.$O\
+	subr.$O\
+	text.$O\
+	vec.$O\
+	whoami.$O\
+
+HFILES=mplot.h\
+
+<$PLAN9/src/mklib
+
diff --git a/src/cmd/plot/libplot/move.c b/src/cmd/plot/libplot/move.c
new file mode 100644
index 0000000..4e60260
--- /dev/null
+++ b/src/cmd/plot/libplot/move.c
@@ -0,0 +1,5 @@
+#include "mplot.h"
+void move(double xx, double yy){
+	e1->copyx = xx;	
+	e1->copyy = yy;
+}
diff --git a/src/cmd/plot/libplot/mplot.h b/src/cmd/plot/libplot/mplot.h
new file mode 100644
index 0000000..99e27ef
--- /dev/null
+++ b/src/cmd/plot/libplot/mplot.h
@@ -0,0 +1,47 @@
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+#include <draw.h>
+#include <event.h>
+#define SCX(A) ((((A) - e1->xmin)*e1->scalex  + e1->left)+.5)
+#define SCY(A) ((((A) - e1->ymin)*e1->scaley + e1->bottom)+.5)
+#define	SCR(A) ((A)*e1->scalex+.5)
+#define unorm(y)	(double)(e1->sidey - y)
+#define	BIGINT	0x3FFFFFFF	/* a large, but valid, int */
+extern struct penvir {
+	double left, bottom;
+	double xmin, ymin;
+	double scalex, scaley;
+	double sidex, sidey;
+	double copyx, copyy;
+	double quantum;
+	double grade;
+	int pgap;
+	double pslant;
+	int pmode, foregr, backgr;
+} *e0, *e1, *esave;
+#define RADIAN 57.3	/* radians per degree */
+struct seg {
+	int x, y, X, Y;
+	char stat;
+};
+
+/*
+ * display parameters
+ */
+int clipminx, clipminy, clipmaxx, clipmaxy;	/* clipping rectangle */
+int mapminx, mapminy, mapmaxx, mapmaxy;		/* centered square */
+/*
+ * Prototypes
+ */
+#include "../plot.h"
+void m_clrwin(int, int, int, int, int);
+void m_finish(void);
+void m_initialize(char *);
+int m_text(int, int, char *, char *, int, int, int);
+void m_vector(int, int, int, int, int);
+void m_swapbuf(void);
+void m_dblbuf(void);
+int bcolor(char *);
+void sscpy(struct penvir *, struct penvir *);
+Image *getcolor(int);
diff --git a/src/cmd/plot/libplot/openpl.c b/src/cmd/plot/libplot/openpl.c
new file mode 100644
index 0000000..6972f95
--- /dev/null
+++ b/src/cmd/plot/libplot/openpl.c
@@ -0,0 +1,12 @@
+#include "mplot.h"
+void openpl(char *s){
+	m_initialize(s);
+	e0->left=mapminx;
+	e0->bottom=mapmaxy;
+	e0->sidex=mapmaxx-mapminx;
+	e0->sidey=mapminy-mapmaxy;
+	e0->scalex=e0->sidex;
+	e0->scaley=e0->sidey;
+	sscpy(e0, e1);
+	move(0., 0.);
+}
diff --git a/src/cmd/plot/libplot/parabola.c b/src/cmd/plot/libplot/parabola.c
new file mode 100644
index 0000000..212b9c9
--- /dev/null
+++ b/src/cmd/plot/libplot/parabola.c
@@ -0,0 +1,32 @@
+#include "mplot.h"
+void parabola(double x0, double y0, double x1, double y1, double xb, double yb){
+	register double x, y, t;
+	double	c0x, c0y, c1x, c1y;
+	double	dt, d2, d1;
+	d1 = sqrt((xb - x0) * (xb - x0) + (yb - y0) * (yb - y0));
+	d2 = sqrt((xb - x1) * (xb - x1) + (yb - y1) * (yb - y1));
+	if (d1 <= e1->quantum || d2 <= e1->quantum) { 
+		plotline(x0, y0, x1, y1); 
+		return; 
+	}
+	c0x = x0 + x1 - 2. * xb; 
+	c1x = 2. * (xb - x0);
+	c0y = y0 + y1 - 2. * yb; 
+	c1y = 2. * (yb - y0);
+	move(x0, y0);
+	dt = e1->quantum / d1;
+	dt /= e1->grade;
+	for (t = dt; t < 0.5; t += dt) {
+		x = (c0x * t + c1x) * t + x0;
+		y = (c0y * t + c1y) * t + y0;
+		vec(x, y);
+	}
+	dt = e1->quantum / d2;
+	dt /= e1->grade;
+	for (; t < 1.0; t += dt) {
+		x = (c0x * t + c1x) * t + x0;
+		y = (c0y * t + c1y) * t + y0;
+		vec(x, y);
+	}
+	vec(x1, y1);
+}
diff --git a/src/cmd/plot/libplot/pen.c b/src/cmd/plot/libplot/pen.c
new file mode 100644
index 0000000..4e13e5c
--- /dev/null
+++ b/src/cmd/plot/libplot/pen.c
@@ -0,0 +1,6 @@
+#include "mplot.h"
+void pen(char *s){
+	/* BUG: NO OP */
+	USED(s);
+	/* was this error:	color(s); */
+}
diff --git a/src/cmd/plot/libplot/poly.c b/src/cmd/plot/libplot/poly.c
new file mode 100644
index 0000000..e3ff734
--- /dev/null
+++ b/src/cmd/plot/libplot/poly.c
@@ -0,0 +1,17 @@
+#include "mplot.h"
+void plotpoly(int num[], double *ff[]){
+	double *xp, *yp, **fp;
+	int i, *n;
+	n = num;
+	fp = ff;
+	while((i = *n++)){
+		xp = *fp++;
+		yp = xp+1;
+		move(*xp, *yp);
+		while(--i){
+			xp += 2;
+			yp += 2;
+			vec(*xp, *yp);
+		}
+	}
+}
diff --git a/src/cmd/plot/libplot/ppause.c b/src/cmd/plot/libplot/ppause.c
new file mode 100644
index 0000000..9fb8863
--- /dev/null
+++ b/src/cmd/plot/libplot/ppause.c
@@ -0,0 +1,7 @@
+#include "mplot.h"
+void ppause(void){ 
+	char	aa[4]; 
+	fflush(stdout); 
+	read(0, aa, 4);  
+	erase(); 
+}
diff --git a/src/cmd/plot/libplot/pprompt.c b/src/cmd/plot/libplot/pprompt.c
new file mode 100644
index 0000000..2678ead
--- /dev/null
+++ b/src/cmd/plot/libplot/pprompt.c
@@ -0,0 +1,5 @@
+#include "mplot.h"
+void
+pprompt(void){
+	fprintf(stderr, ":");
+}
diff --git a/src/cmd/plot/libplot/range.c b/src/cmd/plot/libplot/range.c
new file mode 100644
index 0000000..c448556
--- /dev/null
+++ b/src/cmd/plot/libplot/range.c
@@ -0,0 +1,11 @@
+#include "mplot.h"
+void range(double x0, double y0, double x1, double y1){
+	e1->xmin = x0;
+	e1->ymin = y0;
+	e1->scalex = e1->sidex / (x1 - x0 );
+	e1->scaley = e1->sidey / (y1 - y0 );
+	e1->quantum=e0->quantum/sqrt(e1->scalex*e1->scalex +
+		e1->scaley*e1->scaley);
+	if(e1->quantum < .01)
+		e1->quantum = .01;
+}
diff --git a/src/cmd/plot/libplot/rarc.c b/src/cmd/plot/libplot/rarc.c
new file mode 100644
index 0000000..9c8ccf1
--- /dev/null
+++ b/src/cmd/plot/libplot/rarc.c
@@ -0,0 +1,44 @@
+#include "mplot.h"
+/*		arc plotting routine		*/
+/*		from x1,y1 to x2,y2		*/
+/*	with center xc,yc and radius rr	*/
+/*	integrates difference equation		*/
+/*	negative rr draws counterclockwise	*/
+#define PI4 0.7854
+void rarc(double x1, double y1, double x2, double y2, double xc, double yc, double rr){
+	register double dx, dy, a, b;
+	double	ph, dph, rd, xnext;
+	register int	n;
+	dx = x1 - xc;
+	dy = y1 - yc;
+	rd = sqrt(dx * dx + dy * dy);
+	if (rd / e1->quantum < 1.0) { 
+		move(xc, yc); 
+		vec(xc, yc); 
+		return;
+	}
+	dph = acos(1.0 - (e1->quantum / rd));
+	if (dph > PI4) 
+		dph = PI4;
+	ph=atan2((y2-yc),(x2 - xc)) - atan2(dy, dx);
+	if (ph < 0) 
+		ph += 6.2832; 
+	if (rr < 0) 
+		ph = 6.2832 - ph;
+	if (ph < dph) 
+		plotline(x1, y1, x2, y2);
+	else {
+		n = ph / dph; 
+		a = cos(dph); 
+		b = sin(dph); 
+		if (rr < 0) 
+			b = -b;
+		move(x1, y1);
+		while ((n--) >= 0) {
+			xnext = dx * a - dy * b; 
+			dy = dx * b + dy * a; 
+			dx = xnext;
+			vec(dx + xc, dy + yc);
+		}
+	}
+}
diff --git a/src/cmd/plot/libplot/restore.c b/src/cmd/plot/libplot/restore.c
new file mode 100644
index 0000000..81bdd42
--- /dev/null
+++ b/src/cmd/plot/libplot/restore.c
@@ -0,0 +1,5 @@
+#include "mplot.h"
+void restore(void){
+	e1--;
+	move(e1->copyx, e1->copyy);
+}
diff --git a/src/cmd/plot/libplot/rmove.c b/src/cmd/plot/libplot/rmove.c
new file mode 100644
index 0000000..faae7a1
--- /dev/null
+++ b/src/cmd/plot/libplot/rmove.c
@@ -0,0 +1,6 @@
+#include "mplot.h"
+void rmove(double xx, double yy){
+	e1->copyx += xx;
+	e1->copyy += yy;
+	move(e1->copyx, e1->copyy);
+}
diff --git a/src/cmd/plot/libplot/rvec.c b/src/cmd/plot/libplot/rvec.c
new file mode 100644
index 0000000..2e23cdb
--- /dev/null
+++ b/src/cmd/plot/libplot/rvec.c
@@ -0,0 +1,6 @@
+#include "mplot.h"
+void rvec(double xx, double yy){
+	e1->copyx += xx;
+	e1->copyy += yy;
+	vec(e1->copyx, e1->copyy);
+}
diff --git a/src/cmd/plot/libplot/save.c b/src/cmd/plot/libplot/save.c
new file mode 100644
index 0000000..e3dc405
--- /dev/null
+++ b/src/cmd/plot/libplot/save.c
@@ -0,0 +1,5 @@
+#include "mplot.h"
+void save(void){ 
+	sscpy(e1, e1 + 1); 
+	e1++; 
+}
diff --git a/src/cmd/plot/libplot/sbox.c b/src/cmd/plot/libplot/sbox.c
new file mode 100644
index 0000000..479790b
--- /dev/null
+++ b/src/cmd/plot/libplot/sbox.c
@@ -0,0 +1,13 @@
+#include "mplot.h"
+void sbox(double xx0, double yy0, double xx1, double yy1){
+	int x0=SCX(xx0), y0=SCY(yy0), x1=SCX(xx1), y1=SCY(yy1);
+	int t;
+	if(x1<x0){ t=x0; x0=x1; x1=t; }
+	if(y1<y0){ t=y0; y0=y1; y1=t; }
+	if(x0<clipminx) x0=clipminx;
+	if(y0<clipminy) y0=clipminy;
+	if(x1>clipmaxx) x1=clipmaxx;
+	if(y1>clipmaxy) y1=clipmaxy;
+	if(x1<x0 || y1<y0) return;
+	m_clrwin(x0, y0, x1, y1, e1->backgr);
+}
diff --git a/src/cmd/plot/libplot/spline.c b/src/cmd/plot/libplot/spline.c
new file mode 100644
index 0000000..8cfa83d
--- /dev/null
+++ b/src/cmd/plot/libplot/spline.c
@@ -0,0 +1,51 @@
+/*
+Produce spline (uniform knots, second order)
+from guiding points
+*/
+#include "mplot.h"
+void splin(int mode, int num[], double *ff[]){
+	int	i,  *np, n;
+	double	xa, ya, xc, yc, *xp, *yp, *xp0, *yp0, *xpe, *ype;
+	double **fp;
+	np = num;
+	fp = ff;
+	while((n = *np++)){
+		xp = *fp++; 
+		yp = xp + 1; 
+		xp0 = xp; 
+		yp0 = yp;
+		xpe = xp0 + 2 * (n - 1); 
+		ype = yp0 + 2 * (n - 1);
+		if (n < 3) { 
+			plotline(*xp, *yp, *(xp + 2), *(yp + 2)); 
+			continue;
+		}
+		if (mode == 4) {	/*closed curve*/
+			xa = 0.5 * (*xpe + *(xpe - 2)); 
+			xc = 0.5 * (*xpe + *xp0);
+			ya = 0.5 * (*ype + *(ype - 2)); 
+			yc = 0.5 * (*ype + *yp0);
+			parabola(xa, ya, xc, yc, *xpe, *ype);
+			xa = 0.5 * (*xpe + *xp0); 
+			xc = 0.5 * (*(xp0 + 2) + *xp0);
+			ya = 0.5 * (*ype + *yp0); 
+			yc = 0.5 * (*(yp0 + 2) + *yp0);
+			parabola(xa, ya, xc, yc, *xp0, *yp0);
+		}
+		else {	/*open curve with multiple endpoints*/
+			if (mode % 2) /*odd mode makes first point double*/
+				plotline(*xp0,*yp0,0.5*(*xp0+*(xp0+2)),0.5*(*yp0+*(yp0+2)));
+		}
+		xp += 2; 
+		yp += 2;
+		for (i = 1; i < (n - 1); i++, xp += 2, yp += 2) {
+			xa = 0.5 * (*(xp - 2) + *xp); 
+			xc = 0.5 * ( *xp + *(xp + 2));
+			ya = 0.5 * (*(yp - 2) + *yp); 
+			yc = 0.5 * ( *yp + *(yp + 2));
+			parabola(xa, ya, xc, yc, *xp, *yp);
+		}
+		if(mode >= 2 && mode != 4)
+			plotline(0.5*(*(xpe-2)+*xpe),0.5*(*(ype-2)+*ype),*xpe,*ype);
+	}
+}
diff --git a/src/cmd/plot/libplot/subr.c b/src/cmd/plot/libplot/subr.c
new file mode 100644
index 0000000..cf4cf89
--- /dev/null
+++ b/src/cmd/plot/libplot/subr.c
@@ -0,0 +1,79 @@
+#include "mplot.h"
+#define pSMALL    0.5
+struct penvir  E[9] = {
+{ 0., 1024., 0., 0., 1., -1.,1024., -1024., 0., 0., pSMALL, 1., 1, 0.,1, DBlack, DWhite},
+{ 0., 1024., 0., 0., 1., -1.,1024., -1024., 0., 0., pSMALL, 1., 1, 0.,1, DBlack, DWhite},
+{ 0., 1024., 0., 0., 1., -1.,1024., -1024., 0., 0., pSMALL, 1., 1, 0.,1, DBlack, DWhite},
+{ 0., 1024., 0., 0., 1., -1.,1024., -1024., 0., 0., pSMALL, 1., 1, 0.,1, DBlack, DWhite},
+{ 0., 1024., 0., 0., 1., -1.,1024., -1024., 0., 0., pSMALL, 1., 1, 0.,1, DBlack, DWhite},
+{ 0., 1024., 0., 0., 1., -1.,1024., -1024., 0., 0., pSMALL, 1., 1, 0.,1, DBlack, DWhite},
+{ 0., 1024., 0., 0., 1., -1.,1024., -1024., 0., 0., pSMALL, 1., 1, 0.,1, DBlack, DWhite},
+{ 0., 1024., 0., 0., 1., -1.,1024., -1024., 0., 0., pSMALL, 1., 1, 0.,1, DBlack, DWhite}
+};
+struct penvir *e0 = E, *e1 = &E[1], *esave;
+bcolor(char *s){
+	int c;
+	while (*s != NULL) {
+		switch (*s) {
+		case '0': case '1': case '2': case '3': case '4':
+		case '5': case '6': case '7': case '8': case '9':
+			c=0;
+			while('0'<=*s && *s<='9')
+				c=c*10+*s++-'0';
+			if(c < 0)
+				return DBlack;
+			return cmap2rgba(c);
+		case 'k':  case 'z':	/* zero was old name for kblack */
+			return(DBlack);
+		case 'r':
+print("RED");
+			return(DRed);
+		case 'g': 
+			return(DGreen);
+		case 'b': 
+			return(DBlue);
+		case 'm': 
+			return(DMagenta);
+		case 'y': 
+			return(DYellow);
+		case 'c': 
+			return(DCyan);
+		case 'w': 
+			return(DWhite);
+		case 'R': 
+			return(atoi(s + 1));
+		case 'G': 
+			e1->pgap = atof(s + 1); 
+			return(-1);
+		case 'A': 
+			e1->pslant = (180. - atof(s + 1)) / RADIAN; 
+			return(-1);
+		}
+		while (*++s != NULL) 
+			if (*s == '/') {
+				s++;
+				break;
+			}
+	}
+	return DBlack;
+}
+void sscpy(struct penvir *a, struct penvir *b){ /* copy 'a' onto 'b' */
+	b->left = a->left; 
+	b->bottom = a->bottom; 
+	b->xmin = a->xmin; 
+	b->ymin = a->ymin;
+	b->scalex = a->scalex; 
+	b->scaley = a->scaley;
+	b->sidex = a->sidex; 
+	b->sidey = a->sidey;
+	b->copyx = a->copyx; 
+	b->copyy = a->copyy;
+	b->quantum = a->quantum;
+	b->grade = a->grade;
+	b->pmode = a->pmode; 
+	b->foregr = a->foregr; 
+	b->backgr = a->backgr;
+}
+void idle(void){}
+
+void ptype(char *s){USED(s);}
diff --git a/src/cmd/plot/libplot/text.c b/src/cmd/plot/libplot/text.c
new file mode 100644
index 0000000..530d5f9
--- /dev/null
+++ b/src/cmd/plot/libplot/text.c
@@ -0,0 +1,39 @@
+/*
+          t string  Place the string so that its first character is
+                    centered on the current point (default).  If
+                    string begins with `\C' (`\R'), it is centered
+                    (right-adjusted) on the current point.  A
+                    backslash at the beginning of the string may be
+                    escaped with another backslash.
+ */
+#include "mplot.h"
+void text(char *s){
+	register int	kx, ky;
+	int centered, right, more;
+	char *ss;
+	ss=s;
+	for(;;){
+		centered=right=more=0;
+		if(*ss=='\\'){
+			ss++;
+			switch(*ss){
+			case 'C': centered++; ss++; break;
+			case 'R': right++; ss++; break;
+			case 'L': ss++; break;
+			case 'n': --ss; break;
+			}
+		}
+		for(s=ss;*ss!='\0';ss++)
+			if(ss[0]=='\\' && ss[1]=='n'){
+				more++;
+				break;
+			}
+		kx = SCX(e1->copyx);
+		ky = SCY(e1->copyy);
+		ky=m_text(kx, ky, s, ss, e1->foregr, centered, right);
+		if(!more)break;
+		e1->copyy = ( (double)(ky) - e1->bottom)/e1->scaley + e1->ymin + .5;
+		move(e1->copyx, e1->copyy);
+		ss+=2;
+	}
+}
diff --git a/src/cmd/plot/libplot/vec.c b/src/cmd/plot/libplot/vec.c
new file mode 100644
index 0000000..89a5178
--- /dev/null
+++ b/src/cmd/plot/libplot/vec.c
@@ -0,0 +1,26 @@
+#include "mplot.h"
+#define	code(x, y)	((x<clipminx?1:x>clipmaxx?2:0)|(y<clipminy?4:y>clipmaxy?8:0))
+void vec(double xx, double yy){
+	int x0, y0, x1, y1, c0, c1, c, tx, ty;
+	double t;
+	t=SCX(e1->copyx); if(fabs(t)>BIGINT) return; x0=t;
+	t=SCY(e1->copyy); if(fabs(t)>BIGINT) return; y0=t;
+	t=SCX(xx); if(fabs(t)>BIGINT) return; x1=t;
+	t=SCY(yy); if(fabs(t)>BIGINT) return; y1=t;
+	e1->copyx=xx;
+	e1->copyy=yy;
+	/* clipping -- what a concept */
+	c0=code(x0, y0);
+	c1=code(x1, y1);
+	while(c0|c1){
+		if(c0&c1) return;
+		c=c0?c0:c1;
+		if(c&1)      ty=y0+(y1-y0)*(clipminx-x0)/(x1-x0), tx=clipminx;
+		else if(c&2) ty=y0+(y1-y0)*(clipmaxx-x0)/(x1-x0), tx=clipmaxx;
+		else if(c&4) tx=x0+(x1-x0)*(clipminy-y0)/(y1-y0), ty=clipminy;
+		else         tx=x0+(x1-x0)*(clipmaxy-y0)/(y1-y0), ty=clipmaxy;
+		if(c==c0) x0=tx, y0=ty, c0=code(x0, y0);
+		else      x1=tx, y1=ty, c1=code(x1, y1);
+	}
+	m_vector(x0, y0, x1, y1, e1->foregr);
+}
diff --git a/src/cmd/plot/libplot/whoami.c b/src/cmd/plot/libplot/whoami.c
new file mode 100644
index 0000000..0672ecd
--- /dev/null
+++ b/src/cmd/plot/libplot/whoami.c
@@ -0,0 +1,4 @@
+#include "mplot.h"
+char *whoami(void){
+	return("ramtek");
+}
diff --git a/src/cmd/plot/mkfile b/src/cmd/plot/mkfile
new file mode 100644
index 0000000..0eb1a79
--- /dev/null
+++ b/src/cmd/plot/mkfile
@@ -0,0 +1,27 @@
+PLAN9=../../..
+<$PLAN9/src/mkhdr
+
+TARG=plot
+OFILES=plot.$O\
+
+HFILES=plot.h\
+
+LIB=libplot.a
+
+SHORTLIB=frame draw bio 9
+LDFLAGS=$LDFLAGS -L$X11/lib -lX11
+
+<$PLAN9/src/mkone
+
+$LIB:
+	cd libplot
+	mk install
+
+clean:V:
+	rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG libplot.a
+	cd libplot
+	mk clean
+
+nuke:V:
+	mk clean
+	rm -f libplot.a[$OS]
diff --git a/src/cmd/plot/plot.c b/src/cmd/plot/plot.c
new file mode 100644
index 0000000..04e5cf5
--- /dev/null
+++ b/src/cmd/plot/plot.c
@@ -0,0 +1,605 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "plot.h"
+#include <draw.h>
+#include <event.h>
+#include <ctype.h>
+
+void	define(char*);
+void	call(char*);
+void	include(char*);
+int	process(Biobuf*);
+int	server(void);
+
+enum{
+	ARC,
+	BOX,
+	CALL,
+	CFILL,
+	CIRC,
+	CLOSEPL,
+	COLOR,
+	CSPLINE,
+	DEFINE,
+	DISK,
+	DSPLINE,
+	ERASE,
+	FILL,
+	FRAME,
+	FSPLINE,
+	GRADE,
+	IDLE,
+	INCLUDE,
+	LINE,
+	LSPLINE,
+	MOVE,
+	OPENPL,
+	PARABOLA,
+	PEN,
+	PAUSE,
+	POINT,
+	POLY,
+	RANGE,
+	RESTORE,
+	RMOVE,
+	RVEC,
+	SAVE,
+	SBOX,
+	SPLINE,
+	TEXT,
+	VEC,
+	LAST
+};
+
+struct pcall {
+	char	*cc;
+	int	numc;
+} plots[] = {
+	/*ARC*/ 		"a", 	1,
+	/*BOX*/ 		"bo", 	2,
+	/*CALL*/		"ca",	2,
+	/*CFILL*/ 	"cf", 	2,
+	/*CIRC*/ 		"ci", 	2,
+	/*CLOSEPL*/ 	"cl", 	2,
+	/*COLOR*/ 	"co", 	2,
+	/*CSPLINE*/	"cs",	2,
+	/*DEFINE*/	"de",	2,
+	/*DISK*/		"di",	2,
+	/*DSPLINE*/	"ds",	2,
+	/*ERASE*/ 	"e", 	1,
+	/*FILL*/ 		"fi", 	2,
+	/*FRAME*/ 	"fr", 	2,
+	/*FSPLINE*/	"fs",	2,
+	/*GRADE*/ 	"g", 	1,
+	/*IDLE*/ 		"id", 	2,
+	/*INCLUDE*/	"in",	2,
+	/*LINE*/ 		"li", 	2,
+	/*LSPLINE*/	"ls",	2,
+	/*MOVE*/ 		"m", 	1,
+	/*OPENPL*/ 	"o", 	1,
+	/*PARABOLA*/ 	"par", 	3,
+	/*PEN*/ 		"pe", 	2,
+	/*PAUSE*/ 	"pau", 	3,
+	/*POINT*/ 	"poi", 	3,
+	/*POLY*/ 		"pol", 	3,
+	/*RANGE*/ 	"ra", 	2,
+	/*RESTORE*/ 	"re", 	2,
+	/*RMOVE*/ 	"rm", 	2,
+	/*RVEC*/ 		"rv", 	2,
+	/*SAVE*/ 		"sa", 	2,
+	/*SBOX*/ 		"sb", 	2,
+	/*SPLINE*/ 	"sp", 	2,
+	/*TEXT*/ 		"t", 	1,
+	/*VEC*/ 		"v", 	1,
+	/*LAST*/	 	0, 	0,
+};
+
+struct pcall *pplots;		/* last command read */
+
+#define MAXL 16
+struct fcall {
+	char *name;
+	char *stash;
+} flibr[MAXL];			/* define strings */
+
+struct fcall *fptr = flibr;
+
+#define	NFSTACK	50
+struct fstack{
+	int peekc;
+	int lineno;
+	char *corebuf;
+	Biobuf *fd;
+	double scale;
+}fstack[NFSTACK];		/* stack of open input files & defines */
+struct fstack *fsp=fstack;
+
+#define	NARGSTR	8192
+char argstr[NARGSTR+1];		/* string arguments */
+
+#define	NX	8192
+double x[NX];			/* numeric arguments */
+
+#define	NPTS	256
+int cnt[NPTS];			/* control-polygon vertex counts */
+double *pts[NPTS];		/* control-polygon vertex pointers */
+
+void eresized(int new){
+	if(new && getwindow(display, Refnone) < 0){
+		fprint(2, "Can't reattach to window: %r\n");
+		exits("resize");
+	}
+}
+char *items[]={
+	"exit",
+	0
+};
+Menu menu={items};
+void
+main(int arc, char *arv[]){
+	char *ap;
+	Biobuf *bp;
+	int fd;
+	int i;
+	int dflag;
+	char *oflag;
+	Mouse m;
+	bp = 0;
+	fd = dup(0, -1);		/* because openpl will close 0! */
+	dflag=0;
+	oflag="";
+	for(i=1;i!=arc;i++) if(arv[i][0]=='-') switch(arv[i][1]){
+	case 'd': dflag=1; break;
+	case 'o': oflag=arv[i]+2; break;
+	case 's': fd=server(); break;
+	}
+	openpl(oflag);
+	if(dflag) doublebuffer();
+	for (; arc > 1; arc--, arv++) {
+		if (arv[1][0] == '-') {
+			ap = arv[1];
+			ap++;
+			switch (*ap) {
+			default:
+				fprint(2, "%s not allowed as argument\n", ap);
+				exits("usage");
+			case 'T': break;
+			case 'D': break;
+			case 'd': break;
+			case 'o': break;
+			case 'W': break;
+			case 's': break;
+			case 'e': erase(); break;
+			case 'C': closepl(); break;
+			case 'w': ppause(); break;
+			case 'c': color(ap+1); break;
+			case 'f': cfill(ap+1); break;
+			case 'p': pen(ap+1); break;
+			case 'g': grade(atof(ap+1)); break;
+			}
+		}
+		else if ((bp = Bopen(arv[1], OREAD)) == 0) {
+			perror(arv[1]);
+			fprint(2, "Cannot find file %s\n", arv[1]);
+		}
+		else if(process(bp)) Bterm(fsp->fd);
+		else break;
+	}
+	if (bp == 0){
+		bp = malloc(sizeof *bp);
+		Binit(bp, fd, OREAD);
+		process(bp);
+	}
+	closepl();
+	for(;;){
+		m=emouse();
+		if(m.buttons&4 && emenuhit(3, &m, &menu)==0) exits(0);
+	}
+}
+int nextc(void){
+	int c;
+	Rune r;
+	for(;;){
+		if(fsp->peekc!=Beof){
+			c=fsp->peekc;
+			fsp->peekc=Beof;
+			return c;
+		}
+		if(fsp->fd)
+			c=Bgetrune(fsp->fd);
+		else if(*fsp->corebuf){
+			fsp->corebuf+=chartorune(&r, fsp->corebuf);
+			c=r;
+		}else
+			c=Beof;
+		if(c!=Beof || fsp==fstack) break;
+		if(fsp->fd) Bterm(fsp->fd);
+		--fsp;
+	}
+	if(c=='\n') fsp->lineno++;
+	return c;
+}
+/*
+ * Read a string into argstr -- ignores leading spaces
+ * and an optional leading quote-mark
+ */
+void
+strarg(void){
+	int c;
+	Rune r;
+	int quote=0;
+	char *s=argstr;
+	do
+		c=nextc();
+	while(c==' ' || c=='\t');
+	if(c=='\'' || c=='"'){
+		quote=c;
+		c=nextc();
+	}
+	r = 0;
+	while(c!='\n' && c!=Beof){
+		r=c;
+		s+=runetochar(s, &r);
+		c=nextc();
+	}
+	if(quote && s!=argstr && r==quote) --s;
+	*s='\0';
+}
+/*
+ * Read a floating point number into argstr
+ */
+int
+numstring(void){
+	int ndp=0;
+	int ndig=0;
+	char *s=argstr;
+	int c=nextc();
+	if(c=='+' || c=='-'){
+		*s++=c;
+		c=nextc();
+	}
+	while(isdigit(c) || c=='.'){
+		if(s!=&argstr[NARGSTR]) *s++=c;
+		if(c=='.') ndp++;
+		else ndig++;
+		c=nextc();
+	}
+	if(ndp>1 || ndig==0){
+		fsp->peekc=c;
+		return 0;
+	}
+	if(c=='e' || c=='E'){
+		if(s!=&argstr[NARGSTR]) *s++=c;
+		c=nextc();
+		if(c=='+' || c=='-'){
+			if(s!=&argstr[NARGSTR]) *s++=c;
+			c=nextc();
+		}
+		if(!isdigit(c)){
+			fsp->peekc=c;
+			return 0;
+		}
+		while(isdigit(c)){
+			if(s!=&argstr[NARGSTR]) *s++=c;
+			c=nextc();
+		}
+	}
+	fsp->peekc=c;
+	*s='\0';
+	return 1;
+}
+/*
+ * Read n numeric arguments, storing them in
+ * x[0], ..., x[n-1]
+ */
+void
+numargs(int n){
+	int i, c;
+	for(i=0;i!=n;i++){
+		do{
+			c=nextc();
+		}while(strchr(" \t\n", c) || c!='.' && c!='+' && c!='-' && ispunct(c));
+		fsp->peekc=c;
+		if(!numstring()){
+			fprint(2, "line %d: number expected\n", fsp->lineno);
+			exits("input error");
+		}
+		x[i]=atof(argstr)*fsp->scale;
+	}
+}
+/*
+ * Read a list of lists of control vertices, storing points in x[.],
+ * pointers in pts[.] and counts in cnt[.]
+ */
+void
+polyarg(void){
+	int nleft, l, r, c;
+	double **ptsp=pts, *xp=x;
+	int *cntp=cnt;
+	do{
+		c=nextc();
+	}while(c==' ' || c=='\t');
+	if(c=='{'){
+		l='{';
+		r='}';
+	}
+	else{
+		l=r='\n';
+		fsp->peekc=c;
+	}
+	nleft=1;
+	*cntp=0;
+	*ptsp=xp;
+	for(;;){
+		c=nextc();
+		if(c==r){
+			if(*cntp){
+				if(*cntp&1){
+					fprint(2, "line %d: phase error\n",
+						fsp->lineno);
+					exits("bad input");
+				}
+				*cntp/=2;
+				if(ptsp==&pts[NPTS]){
+					fprint(2, "line %d: out of polygons\n",
+						fsp->lineno);
+					exits("exceeded limit");
+				}
+				*++ptsp=xp;
+				*++cntp=0;
+			}
+			if(--nleft==0) return;
+		}
+		else switch(c){
+		case Beof:  return;
+		case ' ':  break;
+		case '\t': break;
+		case '\n': break;
+		case '.': case '+': case '-':
+		case '0': case '1': case '2': case '3': case '4':
+		case '5': case '6': case '7': case '8': case '9':
+			fsp->peekc=c;
+			if(!numstring()){
+				fprint(2, "line %d: expected number\n", fsp->lineno);
+				exits("bad input");
+			}
+			if(xp==&x[NX]){
+				fprint(2, "line %d: out of space\n", fsp->lineno);
+				exits("exceeded limit");
+			}
+			*xp++=atof(argstr);
+			++*cntp;
+			break;
+		default:
+			if(c==l) nleft++;
+			else if(!ispunct(c)){
+				fsp->peekc=c;
+				return;
+			}
+		}
+	}
+}
+
+int
+process(Biobuf *fd){
+	char *s;
+	int c;
+	fsp=fstack;
+	fsp->fd=fd;
+	fsp->corebuf=0;
+	fsp->peekc=Beof;
+	fsp->lineno=1;
+	fsp->scale=1.;
+	for(;;){
+		do
+			c=nextc();
+		while(c==' ' || c=='\t');
+		if(c==':'){
+			do
+				c=nextc();
+			while(c!='\n' && c!=Beof);
+			if(c==Beof) break;
+			continue;
+		}
+		while(c=='.'){
+			c=nextc();
+			if(isdigit(c)){
+				if(fsp->fd) Bungetc(fsp->fd);
+				else --fsp->corebuf;
+				c='.';
+				break;
+			}
+		}
+		if(c==Beof) break;
+		if(c=='\n') continue;
+		if(isalpha(c)){
+			s=argstr;
+			do{
+				if(isupper(c)) c=tolower(c);
+				if(s!=&argstr[NARGSTR]) *s++=c;
+				c=nextc();
+			}while(isalpha(c));
+			fsp->peekc=c;
+			*s='\0';
+			for(pplots=plots;pplots->cc;pplots++)
+				if(strncmp(argstr, pplots->cc, pplots->numc)==0)
+					break;
+			if(pplots->cc==0){
+				fprint(2, "line %d, %s unknown\n", fsp->lineno,
+					argstr);
+				exits("bad command");
+			}
+		}
+		else{
+			fsp->peekc=c;
+		}
+		if(!pplots){
+			fprint(2, "line %d, no command!\n", fsp->lineno);
+			exits("no command");
+		}
+		switch(pplots-plots){
+		case ARC:	numargs(7); rarc(x[0],x[1],x[2],x[3],x[4],x[5],x[6]); break;
+		case BOX:	numargs(4); box(x[0], x[1], x[2], x[3]); break;
+		case CALL:	strarg();   call(argstr); pplots=0; break;
+		case CFILL:	strarg();   cfill(argstr); pplots=0; break;
+		case CIRC:	numargs(3); circ(x[0], x[1], x[2]); break;
+		case CLOSEPL:	strarg();   closepl(); pplots=0; break;
+		case COLOR:	strarg();   color(argstr); pplots=0; break;
+		case CSPLINE:	polyarg();  splin(4, cnt, pts); break;
+		case DEFINE:	strarg();   define(argstr); pplots=0; break;
+		case DISK:	numargs(3); plotdisc(x[0], x[1], x[2]); break;
+		case DSPLINE:	polyarg();  splin(3, cnt, pts); break;
+		case ERASE:	strarg();   erase(); pplots=0; break;
+		case FILL:	polyarg();  fill(cnt, pts); break;
+		case FRAME:	numargs(4); frame(x[0], x[1], x[2], x[3]); break;
+		case FSPLINE:	polyarg();  splin(1, cnt, pts); break;
+		case GRADE:	numargs(1); grade(x[0]); break;
+		case IDLE:	strarg();   idle(); pplots=0; break;
+		case INCLUDE:	strarg();   include(argstr); pplots=0; break;
+		case LINE:	numargs(4); plotline(x[0], x[1], x[2], x[3]); break;
+		case LSPLINE:	polyarg();  splin(2, cnt, pts); break;
+		case MOVE:	numargs(2); move(x[0], x[1]); break;
+		case OPENPL:	strarg();   openpl(argstr); pplots=0; break;
+		case PARABOLA:	numargs(6); parabola(x[0],x[1],x[2],x[3],x[4],x[5]); break;
+		case PAUSE:	strarg();   ppause(); pplots=0; break;
+		case PEN:	strarg();   pen(argstr); pplots=0; break;
+		case POINT:	numargs(2); dpoint(x[0], x[1]); break;
+		case POLY:	polyarg();  plotpoly(cnt, pts); break;
+		case RANGE:	numargs(4); range(x[0], x[1], x[2], x[3]); break;
+		case RESTORE:	strarg();   restore(); pplots=0; break;
+		case RMOVE:	numargs(2); rmove(x[0], x[1]); break;
+		case RVEC:	numargs(2); rvec(x[0], x[1]); break;
+		case SAVE:	strarg();   save(); pplots=0; break;
+		case SBOX:	numargs(4); sbox(x[0], x[1], x[2], x[3]); break;
+		case SPLINE:	polyarg();  splin(0, cnt, pts); break;
+		case TEXT:	strarg();   text(argstr); pplots=0; break;
+		case VEC:	numargs(2); vec(x[0], x[1]); break;
+		default:
+			fprint(2, "plot: missing case %ld\n", pplots-plots);
+			exits("internal error");
+		}
+	}
+	return 1;
+}
+char *names = 0;
+char *enames = 0;
+char *bstash = 0;
+char *estash = 0;
+unsigned size = 1024;
+char *nstash = 0;
+void define(char *a){
+	char	*ap;
+	short	i, j;
+	int curly = 0;
+	ap = a;
+	while(isalpha(*ap))ap++;
+	if(ap == a){
+		fprint(2,"no name with define\n");
+		exits("define");
+	}
+	i = ap - a;
+	if(names+i+1 > enames){
+		names = malloc((unsigned)512);
+		enames = names + 512;
+	}
+	fptr->name = names;
+	strncpy(names, a,i);
+	names += i;
+	*names++ = '\0';
+	if(!bstash){
+		bstash = nstash = malloc(size);
+		estash = bstash + size;
+	}
+	fptr->stash = nstash;
+	while(*ap != '{')
+		if(*ap == '\n'){
+			if((ap=Brdline(fsp->fd, '\n'))==0){
+				fprint(2,"unexpected end of file\n");
+				exits("eof");
+			}
+		}
+		else ap++;
+	while((j=Bgetc(fsp->fd))!= Beof){
+		if(j == '{')curly++;
+		else if(j == '}'){
+			if(curly == 0)break;
+			else curly--;
+		}
+		*nstash++ = j;
+		if(nstash == estash){
+			free(bstash);
+			size += 1024;
+			bstash = realloc(bstash,size);
+			estash = bstash+size;
+		}
+	}
+	*nstash++ = '\0';
+	if(fptr++ >= &flibr[MAXL]){
+		fprint(2,"Too many objects\n");
+		exits("too many objects");
+	}
+}
+void call(char *a){
+	char *ap;
+	struct fcall *f;
+	char sav;
+	double SC;
+	ap = a;
+	while(isalpha(*ap))ap++;
+	sav = *ap;
+	*ap = '\0';
+	for(f=flibr;f<fptr;f++){
+		if (!(strcmp(a, f->name)))
+			break;
+	}
+	if(f == fptr){
+		fprint(2, "object %s not defined\n",a);
+		exits("undefined");
+	}
+	*ap = sav;
+	while (isspace(*ap) || *ap == ',') 
+		ap++;
+	if (*ap != '\0')
+		SC = atof(ap);
+	else SC = 1.;
+	if(++fsp==&fstack[NFSTACK]){
+		fprint(2, "input stack overflow\n");
+		exits("blew stack");
+	}
+	fsp->peekc=Beof;
+	fsp->lineno=1;
+	fsp->corebuf=f->stash;
+	fsp->fd=0;
+	fsp->scale=fsp[-1].scale*SC;
+}
+void include(char *a){
+	Biobuf *fd;
+	fd=Bopen(a, OREAD);
+	if(fd==0){
+		perror(a);
+		exits("can't include");
+	}
+	if(++fsp==&fstack[NFSTACK]){
+		fprint(2, "input stack overflow\n");
+		exits("blew stack");
+	}
+	fsp->peekc=Beof;
+	fsp->lineno=1;
+	fsp->corebuf=0;
+	fsp->fd=fd;
+}
+/*
+ * Doesn't work.  Why?
+ */
+int server(void){
+	int fd, p[2];
+	char buf[32];
+	pipe(p);
+	fd = create("/srv/plot", 1, 0666);
+	sprint(buf, "%d", p[1]);
+	write(fd, buf, strlen(buf));
+	close(fd);
+	close(p[1]);
+	return p[0];
+}
diff --git a/src/cmd/plot/plot.h b/src/cmd/plot/plot.h
new file mode 100644
index 0000000..81b41c9
--- /dev/null
+++ b/src/cmd/plot/plot.h
@@ -0,0 +1,35 @@
+/*
+ * Prototypes for libplot functions.
+ */
+void rarc(double, double, double, double, double, double, double);
+void box(double, double, double, double);
+void cfill(char *);
+void circ(double, double, double);
+void closepl(void);
+void color(char *);
+void plotdisc(double, double, double);
+void dpoint(double, double);
+void erase(void);
+void fill(int [], double *[]);
+void frame(double, double, double, double);
+void grade(double);
+void idle(void);
+void plotline(double, double, double, double);
+void move(double, double);
+void openpl(char *);
+void parabola(double, double, double, double, double, double);
+void pen(char *);
+void plotpoly(int [], double *[]);
+void ppause(void);
+void ptype(char *);
+void range(double, double, double, double);
+void restore(void);
+void rmove(double, double);
+void rvec(double, double);
+void sbox(double, double, double, double);
+void splin(int, int [], double *[]);
+void text(char *);
+void vec(double, double);
+void save(void);
+char *whoami(void);
+void doublebuffer(void);