ref: bc895417f804bbb78410a365412bcf8ab59a44b4
dir: /sys/src/cmd/plot/plot.c/
#include <u.h> #include <libc.h> #include <bio.h> #include "plot.h" #include <draw.h> #include <thread.h> #include <mouse.h> #include <keyboard.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{ char name[128]; 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 */ extern void m_swapbuf(void); /* reaching into implementation. ick. */ extern Image *offscreen; void resize(Point p) { int fd; fd = open("/dev/wctl", OWRITE); if(fd >= 0){ fprint(fd, "resize -dx %d -dy %d", p.x+4*2, p.y+4*2); close(fd); } } void resizeto(Point p) { Point s; s = (Point){Dx(screen->r), Dy(screen->r)}; if(eqpt(p, s)) return; resize(p); } void eresized(int new) { if(new && getwindow(display, Refnone) < 0) sysfatal("plot: can't reattach to window: %r\n"); // resizeto((Point){Dx(offscreen->r)+4, Dy(offscreen->r)+4}); m_swapbuf(); } char *items[]={ "exit", 0 }; Menu menu={items}; void mouseproc(void*) { void *v; Rune r; Alt alts[4]; Keyboardctl *k; Mousectl *m; Mouse mc; enum{Amouse, Akbd, Aresize, Aend}; m = initmouse(nil, screen); k = initkeyboard(nil); memset(alts, 0, sizeof alts); alts[Amouse].c = m->c; alts[Amouse].v = &mc; alts[Amouse].op = CHANRCV; alts[Akbd].c = k->c; alts[Akbd].v = &r; alts[Akbd].op = CHANRCV; alts[Aresize].c = m->resizec; alts[Aresize].v = &v; alts[Aresize].op = CHANRCV; alts[Aend].op = CHANEND; for(;;) switch(alt(alts)){ default: sysfatal("mouse!"); case Amouse: if(mc.buttons & 4) { if(menuhit(3, m, &menu, nil) == 0) threadexitsall(""); } break; case Akbd: switch(r){ case 'q': case Kdel: case Keof: threadexitsall(""); } break; case Aresize: eresized(1); ; } } void threadmain(int arc, char *arv[]){ char *ap; Biobuf *bp; int fd; int i; int dflag; char *oflag; bp = 0; fd = dup(0, -1); /* because openpl will close 0! */ dflag=0; oflag=""; argv0 = arv[0]; 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); proccreate(mouseproc, nil, 32*1024); 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(); flushimage(display, 1); for(;;) sleep(1000); } int isalpha(int c) { return ('a'<=c && c<='z') || ('A'<=c && c<='Z'); } int isupper(int c) { return 'A'<=c && c<='Z'; } int isdigit(int c) { return '0'<=c && c<='9'; } int ispunct(int c) { return strchr("!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~", c)!=0; } int isspace(int c) { return strchr(" \t\n\v\f\r", c)!=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 */ 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()) sysfatal("%s:%d: number expected\n", fsp->name, fsp->lineno); 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) sysfatal("%s:%d: phase error", fsp->name, fsp->lineno); *cntp/=2; if(ptsp==&pts[NPTS]) sysfatal("%s:%d: out of polygons", fsp->name, fsp->lineno); *++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()) sysfatal("%s:%d: expected number", fsp->name, fsp->lineno); if(xp==&x[NX]) sysfatal("%s:%d: out of space", fsp->name, fsp->lineno); *xp++=atof(argstr); ++*cntp; break; default: if(c==l) nleft++; else if(!ispunct(c)){ fsp->peekc=c; return; } } } } 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) sysfatal("%s:%d: %s unknown", fsp->name, fsp->lineno, argstr); } else{ fsp->peekc=c; } if(!pplots) sysfatal("%s:%d: no command\n", fsp->name, fsp->lineno); 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: sysfatal("%s:%d: plot: missing case %ld\n", fsp->name, fsp->lineno, pplots-plots); } } 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) sysfatal("plot: no name with define\n"); 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) sysfatal("plot: unexpected 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); if(bstash == nil) sysfatal("plot: realloc: %r"); estash = bstash+size; } } *nstash++ = '\0'; if(fptr++ >= &flibr[MAXL]) sysfatal("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) sysfatal("plot: object %s not defined",a); *ap = sav; while (isspace(*ap) || *ap == ',') ap++; if (*ap != '\0') SC = atof(ap); else SC = 1.; if(++fsp==&fstack[NFSTACK]) sysfatal("plot: input stack overflow"); snprint(fsp->name, sizeof fsp->name, "call %s", f->name); 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) sysfatal("plot: cant include %s: %r", a); if(++fsp==&fstack[NFSTACK]) sysfatal("plot: input stack overflow"); snprint(fsp->name, sizeof fsp->name, "%s", a); 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]; }