Add graph, plot
diff --git a/src/cmd/graph/graph.c b/src/cmd/graph/graph.c
new file mode 100644
index 0000000..d1b6938
--- /dev/null
+++ b/src/cmd/graph/graph.c
@@ -0,0 +1,708 @@
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+#include "iplot.h"
+#define	INF	1.e+37
+#define	F	.25
+
+struct xy {
+	int	xlbf;		/*flag:explicit lower bound*/
+	int 	xubf;		/*flag:explicit upper bound*/
+	int	xqf;		/*flag:explicit quantum*/
+	double (*xf)(double);	/*transform function, e.g. log*/
+	float	xa,xb;		/*scaling coefficients*/
+	float	xlb,xub;	/*lower and upper bound*/
+	float	xquant;		/*quantum*/
+	float	xoff;		/*screen offset fraction*/
+	float	xsize;		/*screen fraction*/
+	int	xbot,xtop;	/*screen coords of border*/	
+	float	xmult;		/*scaling constant*/
+} xd,yd;
+struct val {
+	float xv;
+	float yv;
+	int lblptr;
+} *xx;
+
+char *labels;
+int labelsiz;
+
+int tick = 50;
+int top = 4000;
+int bot = 200;
+float absbot;
+int	n;
+int	erasf = 1;
+int	gridf = 2;
+int	symbf = 0;
+int	absf = 0;
+int	transf;
+int	equf;
+int	brkf;
+int	ovlay = 1;
+float	dx;
+char	*plotsymb;
+
+#define BSIZ 80
+char	labbuf[BSIZ];
+char	titlebuf[BSIZ];
+
+char *modes[] = {
+	"disconnected",
+	"solid",
+	"dotted",
+	"dotdashed",
+	"shortdashed",
+	"longdashed"
+};
+int mode = 1;
+double ident(double x){
+	return(x);
+}
+
+struct z {
+	float lb,ub,mult,quant;
+};
+void init(struct xy *);
+void setopt(int, char *[]);
+void readin(void);
+void transpose(void);
+void getlim(struct xy *, struct val *);
+void equilibrate(struct xy *, struct xy *);
+void scale(struct xy *);
+void limread(struct xy *, int *, char ***);
+int numb(float *, int *, char ***);
+int copystring(int);
+struct z setloglim(int, int, float, float);
+struct z setlinlim(int, int, float, float);
+void axes(void);
+int setmark(int *, struct xy *);
+void submark(int *, int *, float, struct xy *);
+void plot(void);
+int getfloat(float *);
+int getstring(void);
+void title(void);
+void badarg(void);
+int conv(float, struct xy *, int *);
+int symbol(int, int, int);
+void axlab(char, struct xy *, char *);
+
+void main(int argc,char *argv[]){
+
+	openpl();
+	range(0,0,4096,4096);
+	init(&xd);
+	init(&yd);
+	xd.xsize = yd.xsize = 1.;
+	xx = (struct val *)malloc((unsigned)sizeof(struct val));
+	labels = malloc(1);
+	labels[labelsiz++] = 0;
+	setopt(argc,argv);
+	if(erasf)
+		erase();
+	readin();
+	transpose();
+	getlim(&xd,(struct val *)&xx->xv);
+	getlim(&yd,(struct val *)&xx->yv);
+	if(equf) {
+		equilibrate(&xd,&yd);
+		equilibrate(&yd,&xd);
+	}
+	scale(&xd);
+	scale(&yd);
+	axes();
+	title();
+	plot();
+	closepl();
+	exits(0);
+}
+
+void init(struct xy *p){
+	p->xf = ident;
+	p->xmult = 1;
+}
+
+void setopt(int argc, char *argv[]){
+	char *p1, *p2;
+	float temp;
+
+	xd.xlb = yd.xlb = INF;
+	xd.xub = yd.xub = -INF;
+	while(--argc > 0) {
+		argv++;
+again:		switch(argv[0][0]) {
+		case '-':
+			argv[0]++;
+			goto again;
+		case 'l': /* label for plot */
+			p1 = titlebuf;
+			if (argc>=2) {
+				argv++;
+				argc--;
+				p2 = argv[0];
+				while (*p1++ = *p2++);
+			}
+			break;
+
+		case 'd':	/*disconnected,obsolete option*/
+		case 'm': /*line mode*/
+			mode = 0;
+			if(!numb(&temp,&argc,&argv))
+				break;
+			if(temp>=sizeof(modes)/sizeof(*modes))
+				mode = 1;
+			else if(temp>=-1)
+				mode = temp;
+			break;
+
+		case 'o':
+			if(numb(&temp,&argc,&argv) && temp>=1)
+				ovlay = temp;
+			break;
+		case 'a': /*automatic abscissas*/
+			absf = 1;
+			dx = 1;
+			if(!numb(&dx,&argc,&argv))
+				break;
+			if(numb(&absbot,&argc,&argv))
+				absf = 2;
+			break;
+
+		case 's': /*save screen, overlay plot*/
+			erasf = 0;
+			break;
+
+		case 'g': /*grid style 0 none, 1 ticks, 2 full*/
+			gridf = 0;
+			if(!numb(&temp,&argc,&argv))
+				temp = argv[0][1]-'0';	/*for caompatibility*/
+			if(temp>=0&&temp<=2)
+				gridf = temp;
+			break;
+
+		case 'c': /*character(s) for plotting*/
+			if(argc >= 2) {
+				symbf = 1;
+				plotsymb = argv[1];
+				argv++;
+				argc--;
+			}
+			break;
+
+		case 't':	/*transpose*/
+			transf = 1;
+			break;
+		case 'e':	/*equal scales*/
+			equf = 1;
+			break;
+		case 'b':	/*breaks*/
+			brkf = 1;
+			break;
+		case 'x':	/*x limits */
+			limread(&xd,&argc,&argv);
+			break;
+		case 'y':
+			limread(&yd,&argc,&argv);
+			break;
+		case 'h': /*set height of plot */
+			if(!numb(&yd.xsize, &argc,&argv))
+				badarg();
+			break;
+		case 'w': /*set width of plot */
+			if(!numb(&xd.xsize, &argc, &argv))
+				badarg();
+			break;
+		case 'r': /* set offset to right */
+			if(!numb(&xd.xoff, &argc, &argv))
+				badarg();
+			break;
+		case 'u': /*set offset up the screen*/
+			if(!numb(&yd.xoff,&argc,&argv))
+				badarg();
+			break;
+		default:
+			badarg();
+		}
+	}
+}
+
+void limread(struct xy *p, int *argcp, char ***argvp){
+	if(*argcp>1 && (*argvp)[1][0]=='l') {
+		(*argcp)--;
+		(*argvp)++;
+		p->xf = log10;
+	}
+	if(!numb(&p->xlb,argcp,argvp))
+		return;
+	p->xlbf = 1;
+	if(!numb(&p->xub,argcp,argvp))
+		return;
+	p->xubf = 1;
+	if(!numb(&p->xquant,argcp,argvp))
+		return;
+	p->xqf = 1;
+}
+
+int
+isdigit(char c){
+	return '0'<=c && c<='9';
+}
+
+int
+numb(float *np, int *argcp, char ***argvp){
+	char c;
+
+	if(*argcp <= 1)
+		return(0);
+	while((c=(*argvp)[1][0]) == '+')
+		(*argvp)[1]++;
+	if(!(isdigit(c) || c=='-'&&(*argvp)[1][1]<'A' || c=='.'))
+		return(0);
+	*np = atof((*argvp)[1]);
+	(*argcp)--;
+	(*argvp)++;
+	return(1);
+}
+
+void readin(void){
+	int i, t;
+	struct val *temp;
+
+	if(absf==1) {
+		if(xd.xlbf)
+			absbot = xd.xlb;
+		else if(xd.xf==log10)
+			absbot = 1;
+	}
+	for(;;) {
+		temp = (struct val *)realloc((char*)xx,
+			(unsigned)(n+ovlay)*sizeof(struct val));
+		if(temp==0)
+			return;
+		xx = temp;
+		if(absf)
+			xx[n].xv = n*dx/ovlay + absbot;
+		else
+			if(!getfloat(&xx[n].xv))
+				return;
+		t = 0;	/* silence compiler */
+		for(i=0;i<ovlay;i++) {
+			xx[n+i].xv = xx[n].xv;
+			if(!getfloat(&xx[n+i].yv))
+				return;
+			xx[n+i].lblptr = -1;
+			t = getstring();
+			if(t>0)
+				xx[n+i].lblptr = copystring(t);
+			if(t<0 && i+1<ovlay)
+				return;
+		}
+		n += ovlay;
+		if(t<0)
+			return;
+	}
+}
+
+void transpose(void){
+	int i;
+	float f;
+	struct xy t;
+	if(!transf)
+		return;
+	t = xd; xd = yd; yd = t;
+	for(i= 0;i<n;i++) {
+		f = xx[i].xv; xx[i].xv = xx[i].yv; xx[i].yv = f;
+	}
+}
+
+int copystring(int k){
+	char *temp;
+	int i;
+	int q;
+
+	temp = realloc(labels,(unsigned)(labelsiz+1+k));
+	if(temp==0)
+		return(0);
+	labels = temp;
+	q = labelsiz;
+	for(i=0;i<=k;i++)
+		labels[labelsiz++] = labbuf[i];
+	return(q);
+}
+
+float modceil(float f, float t){
+
+	t = fabs(t);
+	return(ceil(f/t)*t);
+}
+
+float
+modfloor(float f, float t){
+	t = fabs(t);
+	return(floor(f/t)*t);
+}
+
+void getlim(struct xy *p, struct val *v){
+	int i;
+
+	i = 0;
+	do {
+		if(!p->xlbf && p->xlb>v[i].xv)
+			p->xlb = v[i].xv;
+		if(!p->xubf && p->xub<v[i].xv)
+			p->xub = v[i].xv;
+		i++;
+	} while(i < n);
+}
+
+void setlim(struct xy *p){
+	float t,delta,sign;
+	struct z z;
+	int mark[50];
+	float lb,ub;
+	int lbf,ubf;
+
+	lb = p->xlb;
+	ub = p->xub;
+	delta = ub-lb;
+	if(p->xqf) {
+		if(delta*p->xquant <=0 )
+			badarg();
+		return;
+	}
+	sign = 1;
+	lbf = p->xlbf;
+	ubf = p->xubf;
+	if(delta < 0) {
+		sign = -1;
+		t = lb;
+		lb = ub;
+		ub = t;
+		t = lbf;
+		lbf = ubf;
+		ubf = t;
+	}
+	else if(delta == 0) {
+		if(ub > 0) {
+			ub = 2*ub;
+			lb = 0;
+		} 
+		else
+			if(lb < 0) {
+				lb = 2*lb;
+				ub = 0;
+			} 
+			else {
+				ub = 1;
+				lb = -1;
+			}
+	}
+	if(p->xf==log10 && lb>0 && ub>lb) {
+		z = setloglim(lbf,ubf,lb,ub);
+		p->xlb = z.lb;
+		p->xub = z.ub;
+		p->xmult *= z.mult;
+		p->xquant = z.quant;
+		if(setmark(mark,p)<2) {
+			p->xqf = lbf = ubf = 1;
+			lb = z.lb; ub = z.ub;
+		} else
+			return;
+	}
+	z = setlinlim(lbf,ubf,lb,ub);
+	if(sign > 0) {
+		p->xlb = z.lb;
+		p->xub = z.ub;
+	} else {
+		p->xlb = z.ub;
+		p->xub = z.lb;
+	}
+	p->xmult *= z.mult;
+	p->xquant = sign*z.quant;
+}
+
+struct z
+setloglim(int lbf, int ubf, float lb, float ub){
+	float r,s,t;
+	struct z z;
+
+	for(s=1; lb*s<1; s*=10) ;
+	lb *= s;
+	ub *= s;
+	for(r=1; 10*r<=lb; r*=10) ;
+	for(t=1; t<ub; t*=10) ;
+	z.lb = !lbf ? r : lb;
+	z.ub = !ubf ? t : ub;
+	if(ub/lb<100) {
+		if(!lbf) {
+			if(lb >= 5*z.lb)
+				z.lb *= 5;
+			else if(lb >= 2*z.lb)
+				z.lb *= 2;
+		}
+		if(!ubf) {
+			if(ub*5 <= z.ub)
+				z.ub /= 5;
+			else if(ub*2 <= z.ub)
+				z.ub /= 2;
+		}
+	}
+	z.mult = s;
+	z.quant = r;
+	return(z);
+}
+
+struct z
+setlinlim(int lbf, int ubf, float xlb, float xub){
+	struct z z;
+	float r,s,delta;
+	float ub,lb;
+
+loop:
+	ub = xub;
+	lb = xlb;
+	delta = ub - lb;
+	/*scale up by s, a power of 10, so range (delta) exceeds 1*/
+	/*find power of 10 quantum, r, such that delta/10<=r<delta*/
+	r = s = 1;
+	while(delta*s < 10)
+		s *= 10;
+	delta *= s;
+	while(10*r < delta)
+		r *= 10;
+	lb *= s;
+	ub *= s;
+	/*set r=(1,2,5)*10**n so that 3-5 quanta cover range*/
+	if(r>=delta/2)
+		r /= 2;
+	else if(r<delta/5)
+		r *= 2;
+	z.ub = ubf? ub: modceil(ub,r);
+	z.lb = lbf? lb: modfloor(lb,r);
+	if(!lbf && z.lb<=r && z.lb>0) {
+		xlb = 0;
+		goto loop;
+	}
+	else if(!ubf && z.ub>=-r && z.ub<0) {
+		xub = 0;
+		goto loop;
+	}
+	z.quant = r;
+	z.mult = s;
+	return(z);
+}
+
+void scale(struct xy *p){
+	float edge;
+
+	setlim(p);
+	edge = top-bot;
+	p->xa = p->xsize*edge/((*p->xf)(p->xub) - (*p->xf)(p->xlb));
+	p->xbot = bot + edge*p->xoff;
+	p->xtop = p->xbot + (top-bot)*p->xsize;
+	p->xb = p->xbot - (*p->xf)(p->xlb)*p->xa + .5;
+}
+
+void equilibrate(struct xy *p, struct xy *q){
+	if(p->xlbf||	/* needn't test xubf; it implies xlbf*/
+	   q->xubf&&q->xlb>q->xub)
+		return;
+	if(p->xlb>q->xlb) {
+		p->xlb = q->xlb;
+		p->xlbf = q->xlbf;
+	}
+	if(p->xub<q->xub) {
+		p->xub = q->xub;
+		p->xubf = q->xubf;
+	}
+}
+
+void axes(void){
+	int i;
+	int mark[50];
+	int xn, yn;
+	if(gridf==0)
+		return;
+
+	line(xd.xbot,yd.xbot,xd.xtop,yd.xbot);
+	vec(xd.xtop,yd.xtop);
+	vec(xd.xbot,yd.xtop);
+	vec(xd.xbot,yd.xbot);
+
+	xn = setmark(mark,&xd);
+	for(i=0; i<xn; i++) {
+		if(gridf==2)
+			line(mark[i],yd.xbot,mark[i],yd.xtop);
+		if(gridf==1) {
+			line(mark[i],yd.xbot,mark[i],yd.xbot+tick);
+			line(mark[i],yd.xtop-tick,mark[i],yd.xtop);
+		}
+	}
+	yn = setmark(mark,&yd);
+	for(i=0; i<yn; i++) {
+		if(gridf==2)
+			line(xd.xbot,mark[i],xd.xtop,mark[i]);
+		if(gridf==1) {
+			line(xd.xbot,mark[i],xd.xbot+tick,mark[i]);
+			line(xd.xtop-tick,mark[i],xd.xtop,mark[i]);
+		}
+	}
+}
+
+int
+setmark(int *xmark, struct xy *p){
+	int xn = 0;
+	float x,xl,xu;
+	float q;
+	if(p->xf==log10&&!p->xqf) {
+		for(x=p->xquant; x<p->xub; x*=10) {
+			submark(xmark,&xn,x,p);
+			if(p->xub/p->xlb<=100) {
+				submark(xmark,&xn,2*x,p);
+				submark(xmark,&xn,5*x,p);
+			}
+		}
+	} else {
+		xn = 0;
+		q = p->xquant;
+		if(q>0) {
+			xl = modceil(p->xlb+q/6,q);
+			xu = modfloor(p->xub-q/6,q)+q/2;
+		} else {
+			xl = modceil(p->xub-q/6,q);
+			xu = modfloor(p->xlb+q/6,q)-q/2;
+		}
+		for(x=xl; x<=xu; x+=fabs(p->xquant))
+			xmark[xn++] = (*p->xf)(x)*p->xa + p->xb;
+	}
+	return(xn);
+}
+void submark(int *xmark, int *pxn, float x, struct xy *p){
+	if(1.001*p->xlb < x && .999*p->xub > x)
+		xmark[(*pxn)++] = log10(x)*p->xa + p->xb;
+}
+
+void plot(void){
+	int ix,iy;
+	int i,j;
+	int conn;
+
+	for(j=0;j<ovlay;j++) {
+		switch(mode) {
+		case -1:
+			pen(modes[j%(sizeof modes/sizeof *modes-1)+1]);
+			break;
+		case 0:
+			break;
+		default:
+			pen(modes[mode]);
+		}
+		conn = 0;
+		for(i=j; i<n; i+=ovlay) {
+			if(!conv(xx[i].xv,&xd,&ix) ||
+			   !conv(xx[i].yv,&yd,&iy)) {
+				conn = 0;
+				continue;
+			}
+			if(mode!=0) {
+				if(conn != 0)
+					vec(ix,iy);
+				else
+					move(ix,iy);
+				conn = 1;
+			}
+			conn &= symbol(ix,iy,xx[i].lblptr);
+		}
+	}
+	pen(modes[1]);
+}
+
+int
+conv(float xv, struct xy *p, int *ip){
+	long ix;
+	ix = p->xa*(*p->xf)(xv*p->xmult) + p->xb;
+	if(ix<p->xbot || ix>p->xtop)
+		return(0);
+	*ip = ix;
+	return(1);
+}
+
+int
+getfloat(float *p){
+	int i;
+
+	i = scanf("%f",p);
+	return(i==1);
+}
+int
+getstring(void){
+	int i;
+	char junk[20];
+	i = scanf("%1s",labbuf);
+	if(i==-1)
+		return(-1);
+	switch(*labbuf) {
+	default:
+		if(!isdigit(*labbuf)) {
+			ungetc(*labbuf,stdin);
+			i = scanf("%s",labbuf);
+			break;
+		}
+	case '.':
+	case '+':
+	case '-':
+		ungetc(*labbuf,stdin);
+		return(0);
+	case '"':
+		i = scanf("%[^\"\n]",labbuf);
+		scanf("%[\"]",junk);
+		break;
+	}
+	if(i==-1)
+		return(-1);
+	return(strlen(labbuf));
+}
+
+int
+symbol(int ix, int iy, int k){
+
+	if(symbf==0&&k<0) {
+		if(mode==0)
+			point(ix,iy);
+		return(1);
+	} 
+	else {
+		move(ix,iy);
+		text(k>=0?labels+k:plotsymb);
+		move(ix,iy);
+		return(!brkf|k<0);
+	}
+}
+
+void title(void){
+	char buf[BSIZ+100];
+	buf[0] = ' ';
+	buf[1] = ' ';
+	buf[2] = ' ';
+	strcpy(buf+3,titlebuf);
+	if(erasf&&gridf) {
+		axlab('x',&xd,buf);
+		strcat(buf,",");
+		axlab('y',&yd,buf);
+	}
+	move(xd.xbot,yd.xbot-60);
+	text(buf);
+}
+
+void axlab(char c, struct xy *p, char *b){
+	char *dir;
+	dir = p->xlb<p->xub? "<=": ">=";
+	sprintf(b+strlen(b), " %g %s %c%s %s %g", p->xlb/p->xmult,
+		dir, c, p->xf==log10?" (log)":"", dir, p->xub/p->xmult);
+}
+
+void badarg(void){
+	fprintf(stderr,"graph: error in arguments\n");
+	closepl();
+	exits("bad arg");
+}
diff --git a/src/cmd/graph/iplot.h b/src/cmd/graph/iplot.h
new file mode 100644
index 0000000..b6cd50b
--- /dev/null
+++ b/src/cmd/graph/iplot.h
@@ -0,0 +1,39 @@
+#define arc(_x,_y,_X,_Y,_u,_v,_r)  printf("a %d %d %d %d %d %d %d\n",_x,_y,_X,_Y,_u,_v,_r)
+#define box(_x,_y,_X,_Y)  printf("bo %d %d %d %d\n", _x,_y,_X,_Y)
+#define bspline(_num,_ff) {printf("bs {\n"); putnum(_num,_ff); printf("}\n");}
+#define call(_sname,_x)  printf("ca %s %d\n", _sname,_x)
+#define cfill(_s)  printf("cf %s\n", _s)
+#define circle(_x,_y,_r)  printf("ci %d %d %d\n", _x,_y,_r)
+#define closepl()  printf("cl\n")
+#define color(_s)  printf("co %s\n", _s)
+#define cspline(_num,_ff) {printf("cs {\n"); putnum(_num,_ff); printf("}\n");}
+#define pdefine(_sname, _str)  printf("de %s {\n%s\n",_sname, _str)
+#define dspline(_num,_ff) {printf("ds {\n");putnum(_num,_ff);printf("}\n");}
+#define erase() printf("e\n")
+#define ffill(_s)  printf("ff %s\n", _s)
+#define fill(_num,_ff)  {printf("fi {\n"); putnum(_num,_ff); printf("}\n");}
+#define fpoly(_s)  printf("fp %s\n", _s)
+#define frame(_x,_y,_X,_Y)  printf("fr %g %g %g %g\n", _x,_y,_X,_Y)
+#define fspline(_s)  printf("fs %s\n", _s)
+#define grade(_x)  printf("g %d\n", _x)
+#define idle()
+#define line(_x1,_y1,_x2,_y2)  printf("li %d %d %d %d\n", _x1,_y1,_x2,_y2)
+#define lspline(_num,_ff) {printf("ls {\n"); putnum(_num,_ff); printf("}\n");}
+#define move(_x, _y)  printf("m %d %d\n", _x, _y)
+#define openpl()  printf("o\n")
+#define parabola(_x,_y,_X,_Y,_u,_v)  printf("\npa %d %d %d %d %d %d\n", _x,_y,_X,_Y,_u,_v)
+#define pen(_s)  printf("pe %s\n", _s)
+#define point(_x,_y)  printf("poi %d %d\n", _x,_y)
+#define poly(_num,_ff)  {printf("pol {\n"); putnum(_num,_ff); printf("}\n");}
+#define ppause() printf("pp\n")
+#define range(_x,_y,_X,_Y)  printf("ra %d %d %d %d\n", _x,_y,_X,_Y)
+#define restore() printf("re\n")
+#define rmove(_x,_y)  printf("rm %d %d\n", _x,_y)
+#define rvec(_x,_y)  printf("rv %d %d\n", _x,_y)
+#define save() printf( "sa\n")
+#define sbox(_x,_y,_X,_Y)  printf("sb %d %d %d %d\n", _x,_y,_X,_Y)
+#define spline(_num,_ff)  {printf("sp {\n"); putnum(_num,_ff); printf("}\n");}
+#define text(_s)  {if(*(_s) == ' ')printf("t \"%s\"\n",_s); else printf("t %s\n", _s); }
+#define vec(_x,_y)  printf("v %d %d\n", _x,_y)
+void putnum(int [], double *[]);
+char *whoami(void);
diff --git a/src/cmd/graph/mkfile b/src/cmd/graph/mkfile
new file mode 100644
index 0000000..4a45add
--- /dev/null
+++ b/src/cmd/graph/mkfile
@@ -0,0 +1,9 @@
+PLAN9=../../..
+<$PLAN9/src/mkhdr
+
+TARG=graph
+OFILES=graph.$O\
+	subr.$O\
+	whoami.$O\
+
+<$PLAN9/src/mkone
diff --git a/src/cmd/graph/subr.c b/src/cmd/graph/subr.c
new file mode 100644
index 0000000..9531523
--- /dev/null
+++ b/src/cmd/graph/subr.c
@@ -0,0 +1,19 @@
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+#include "iplot.h"
+void putnum(int num[], double *ff[]){
+	double **fp, *xp;
+	int *np, n,i;
+	np = num;
+	fp = ff;
+	while( (n = *np++)){
+		xp = *fp++;
+		printf("{ ");
+		for(i=0; i<n;i++){
+			printf("%g %g ",*xp, *(xp+1));
+			if(i&1)printf("\n");
+		}
+		printf("}\n");
+	}
+}
diff --git a/src/cmd/graph/whoami.c b/src/cmd/graph/whoami.c
new file mode 100644
index 0000000..5371dc5
--- /dev/null
+++ b/src/cmd/graph/whoami.c
@@ -0,0 +1,5 @@
+#include "iplot.h"
+char *
+whoami(void){
+	return("general");
+}