xref: /plan9/sys/src/cmd/plot/plot.c (revision a84536681645e23c630ce4ef2e5c3b284d4c590b)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include "plot.h"
5 #include <draw.h>
6 #include <event.h>
7 
8 void	define(char*);
9 void	call(char*);
10 void	include(char*);
11 int	process(Biobuf*);
12 int	server(void);
13 
14 enum{
15 	ARC,
16 	BOX,
17 	CALL,
18 	CFILL,
19 	CIRC,
20 	CLOSEPL,
21 	COLOR,
22 	CSPLINE,
23 	DEFINE,
24 	DISK,
25 	DSPLINE,
26 	ERASE,
27 	FILL,
28 	FRAME,
29 	FSPLINE,
30 	GRADE,
31 	IDLE,
32 	INCLUDE,
33 	LINE,
34 	LSPLINE,
35 	MOVE,
36 	OPENPL,
37 	PARABOLA,
38 	PEN,
39 	PAUSE,
40 	POINT,
41 	POLY,
42 	RANGE,
43 	RESTORE,
44 	RMOVE,
45 	RVEC,
46 	SAVE,
47 	SBOX,
48 	SPLINE,
49 	TEXT,
50 	VEC,
51 	LAST
52 };
53 
54 struct pcall {
55 	char	*cc;
56 	int	numc;
57 } plots[] = {
58 	[ARC] 		"a", 	1,
59 	[BOX] 		"bo", 	2,
60 	[CALL]		"ca",	2,
61 	[CFILL] 	"cf", 	2,
62 	[CIRC] 		"ci", 	2,
63 	[CLOSEPL] 	"cl", 	2,
64 	[COLOR] 	"co", 	2,
65 	[CSPLINE]	"cs",	2,
66 	[DEFINE]	"de",	2,
67 	[DISK]		"di",	2,
68 	[DSPLINE]	"ds",	2,
69 	[ERASE] 	"e", 	1,
70 	[FILL] 		"fi", 	2,
71 	[FRAME] 	"fr", 	2,
72 	[FSPLINE]	"fs",	2,
73 	[GRADE] 	"g", 	1,
74 	[IDLE] 		"id", 	2,
75 	[INCLUDE]	"in",	2,
76 	[LINE] 		"li", 	2,
77 	[LSPLINE]	"ls",	2,
78 	[MOVE] 		"m", 	1,
79 	[OPENPL] 	"o", 	1,
80 	[PARABOLA] 	"par", 	3,
81 	[PEN] 		"pe", 	2,
82 	[PAUSE] 	"pau", 	3,
83 	[POINT] 	"poi", 	3,
84 	[POLY] 		"pol", 	3,
85 	[RANGE] 	"ra", 	2,
86 	[RESTORE] 	"re", 	2,
87 	[RMOVE] 	"rm", 	2,
88 	[RVEC] 		"rv", 	2,
89 	[SAVE] 		"sa", 	2,
90 	[SBOX] 		"sb", 	2,
91 	[SPLINE] 	"sp", 	2,
92 	[TEXT] 		"t", 	1,
93 	[VEC] 		"v", 	1,
94 	[LAST]	 	0, 	0,
95 };
96 
97 struct pcall *pplots;		/* last command read */
98 
99 #define MAXL 16
100 struct fcall {
101 	char *name;
102 	char *stash;
103 } flibr[MAXL];			/* define strings */
104 
105 struct fcall *fptr = flibr;
106 
107 #define	NFSTACK	50
108 struct fstack{
109 	int peekc;
110 	int lineno;
111 	char *corebuf;
112 	Biobuf *fd;
113 	double scale;
114 }fstack[NFSTACK];		/* stack of open input files & defines */
115 struct fstack *fsp=fstack;
116 
117 #define	NARGSTR	8192
118 char argstr[NARGSTR+1];		/* string arguments */
119 
120 #define	NX	8192
121 double x[NX];			/* numeric arguments */
122 
123 #define	NPTS	256
124 int cnt[NPTS];			/* control-polygon vertex counts */
125 double *pts[NPTS];		/* control-polygon vertex pointers */
126 
eresized(int new)127 void eresized(int new){
128 	if(new && getwindow(display, Refnone) < 0){
129 		fprint(2, "Can't reattach to window: %r\n");
130 		exits("resize");
131 	}
132 }
133 char *items[]={
134 	"exit",
135 	0
136 };
137 Menu menu={items};
138 void
main(int arc,char * arv[])139 main(int arc, char *arv[]){
140 	char *ap;
141 	Biobuf *bp;
142 	int fd;
143 	int i;
144 	int dflag;
145 	char *oflag;
146 	Mouse m;
147 	bp = 0;
148 	fd = dup(0, -1);		/* because openpl will close 0! */
149 	dflag=0;
150 	oflag="";
151 	for(i=1;i!=arc;i++) if(arv[i][0]=='-') switch(arv[i][1]){
152 	case 'd': dflag=1; break;
153 	case 'o': oflag=arv[i]+2; break;
154 	case 's': fd=server(); break;
155 	}
156 	openpl(oflag);
157 	if(dflag) doublebuffer();
158 	for (; arc > 1; arc--, arv++) {
159 		if (arv[1][0] == '-') {
160 			ap = arv[1];
161 			ap++;
162 			switch (*ap) {
163 			default:
164 				fprint(2, "%s not allowed as argument\n", ap);
165 				exits("usage");
166 			case 'T': break;
167 			case 'D': break;
168 			case 'd': break;
169 			case 'o': break;
170 			case 'W': break;
171 			case 's': break;
172 			case 'e': erase(); break;
173 			case 'C': closepl(); break;
174 			case 'w': ppause(); break;
175 			case 'c': color(ap+1); break;
176 			case 'f': cfill(ap+1); break;
177 			case 'p': pen(ap+1); break;
178 			case 'g': grade(atof(ap+1)); break;
179 			}
180 		}
181 		else if ((bp = Bopen(arv[1], OREAD)) == 0) {
182 			perror(arv[1]);
183 			fprint(2, "Cannot find file %s\n", arv[1]);
184 		}
185 		else if(process(bp)) Bterm(fsp->fd);
186 		else break;
187 	}
188 	if (bp == 0){
189 		bp = malloc(sizeof *bp);
190 		Binit(bp, fd, OREAD);
191 		process(bp);
192 	}
193 	closepl();
194 	flushimage(display, 1);
195 	for(;;){
196 		m=emouse();
197 		if(m.buttons&4 && emenuhit(3, &m, &menu)==0) exits(0);
198 	}
199 }
isalpha(int c)200 int isalpha(int c)
201 {
202 	return ('a'<=c && c<='z') || ('A'<=c && c<='Z');
203 }
isupper(int c)204 int isupper(int c)
205 {
206 	return 'A'<=c && c<='Z';
207 }
isdigit(int c)208 int isdigit(int c)
209 {
210 	return '0'<=c && c<='9';
211 }
ispunct(int c)212 int ispunct(int c)
213 {
214 	return strchr("!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~", c)!=0;
215 }
isspace(int c)216 int isspace(int c)
217 {
218 	return strchr(" \t\n\v\f\r", c)!=0;
219 }
nextc(void)220 int nextc(void){
221 	int c;
222 	Rune r;
223 	for(;;){
224 		if(fsp->peekc!=Beof){
225 			c=fsp->peekc;
226 			fsp->peekc=Beof;
227 			return c;
228 		}
229 		if(fsp->fd)
230 			c=Bgetrune(fsp->fd);
231 		else if(*fsp->corebuf){
232 			fsp->corebuf+=chartorune(&r, fsp->corebuf);
233 			c=r;
234 		}else
235 			c=Beof;
236 		if(c!=Beof || fsp==fstack) break;
237 		if(fsp->fd) Bterm(fsp->fd);
238 		--fsp;
239 	}
240 	if(c=='\n') fsp->lineno++;
241 	return c;
242 }
243 /*
244  * Read a string into argstr -- ignores leading spaces
245  * and an optional leading quote-mark
246  */
247 void
strarg(void)248 strarg(void){
249 	int c;
250 	Rune r;
251 	int quote=0;
252 	char *s=argstr;
253 	do
254 		c=nextc();
255 	while(c==' ' || c=='\t');
256 	if(c=='\'' || c=='"'){
257 		quote=c;
258 		c=nextc();
259 	}
260 	r = 0;
261 	while(c!='\n' && c!=Beof){
262 		r=c;
263 		s+=runetochar(s, &r);
264 		c=nextc();
265 	}
266 	if(quote && s!=argstr && r==quote) --s;
267 	*s='\0';
268 }
269 /*
270  * Read a floating point number into argstr
271  */
numstring(void)272 numstring(void){
273 	int ndp=0;
274 	int ndig=0;
275 	char *s=argstr;
276 	int c=nextc();
277 	if(c=='+' || c=='-'){
278 		*s++=c;
279 		c=nextc();
280 	}
281 	while(isdigit(c) || c=='.'){
282 		if(s!=&argstr[NARGSTR]) *s++=c;
283 		if(c=='.') ndp++;
284 		else ndig++;
285 		c=nextc();
286 	}
287 	if(ndp>1 || ndig==0){
288 		fsp->peekc=c;
289 		return 0;
290 	}
291 	if(c=='e' || c=='E'){
292 		if(s!=&argstr[NARGSTR]) *s++=c;
293 		c=nextc();
294 		if(c=='+' || c=='-'){
295 			if(s!=&argstr[NARGSTR]) *s++=c;
296 			c=nextc();
297 		}
298 		if(!isdigit(c)){
299 			fsp->peekc=c;
300 			return 0;
301 		}
302 		while(isdigit(c)){
303 			if(s!=&argstr[NARGSTR]) *s++=c;
304 			c=nextc();
305 		}
306 	}
307 	fsp->peekc=c;
308 	*s='\0';
309 	return 1;
310 }
311 /*
312  * Read n numeric arguments, storing them in
313  * x[0], ..., x[n-1]
314  */
315 void
numargs(int n)316 numargs(int n){
317 	int i, c;
318 	for(i=0;i!=n;i++){
319 		do{
320 			c=nextc();
321 		}while(strchr(" \t\n", c) || c!='.' && c!='+' && c!='-' && ispunct(c));
322 		fsp->peekc=c;
323 		if(!numstring()){
324 			fprint(2, "line %d: number expected\n", fsp->lineno);
325 			exits("input error");
326 		}
327 		x[i]=atof(argstr)*fsp->scale;
328 	}
329 }
330 /*
331  * Read a list of lists of control vertices, storing points in x[.],
332  * pointers in pts[.] and counts in cnt[.]
333  */
334 void
polyarg(void)335 polyarg(void){
336 	int nleft, l, r, c;
337 	double **ptsp=pts, *xp=x;
338 	int *cntp=cnt;
339 	do{
340 		c=nextc();
341 	}while(c==' ' || c=='\t');
342 	if(c=='{'){
343 		l='{';
344 		r='}';
345 	}
346 	else{
347 		l=r='\n';
348 		fsp->peekc=c;
349 	}
350 	nleft=1;
351 	*cntp=0;
352 	*ptsp=xp;
353 	for(;;){
354 		c=nextc();
355 		if(c==r){
356 			if(*cntp){
357 				if(*cntp&1){
358 					fprint(2, "line %d: phase error\n",
359 						fsp->lineno);
360 					exits("bad input");
361 				}
362 				*cntp/=2;
363 				if(ptsp==&pts[NPTS]){
364 					fprint(2, "line %d: out of polygons\n",
365 						fsp->lineno);
366 					exits("exceeded limit");
367 				}
368 				*++ptsp=xp;
369 				*++cntp=0;
370 			}
371 			if(--nleft==0) return;
372 		}
373 		else switch(c){
374 		case Beof:  return;
375 		case ' ':  break;
376 		case '\t': break;
377 		case '\n': break;
378 		case '.': case '+': case '-':
379 		case '0': case '1': case '2': case '3': case '4':
380 		case '5': case '6': case '7': case '8': case '9':
381 			fsp->peekc=c;
382 			if(!numstring()){
383 				fprint(2, "line %d: expected number\n", fsp->lineno);
384 				exits("bad input");
385 			}
386 			if(xp==&x[NX]){
387 				fprint(2, "line %d: out of space\n", fsp->lineno);
388 				exits("exceeded limit");
389 			}
390 			*xp++=atof(argstr);
391 			++*cntp;
392 			break;
393 		default:
394 			if(c==l) nleft++;
395 			else if(!ispunct(c)){
396 				fsp->peekc=c;
397 				return;
398 			}
399 		}
400 	}
401 }
402 
process(Biobuf * fd)403 process(Biobuf *fd){
404 	char *s;
405 	int c;
406 	fsp=fstack;
407 	fsp->fd=fd;
408 	fsp->corebuf=0;
409 	fsp->peekc=Beof;
410 	fsp->lineno=1;
411 	fsp->scale=1.;
412 	for(;;){
413 		do
414 			c=nextc();
415 		while(c==' ' || c=='\t');
416 		if(c==':'){
417 			do
418 				c=nextc();
419 			while(c!='\n' && c!=Beof);
420 			if(c==Beof) break;
421 			continue;
422 		}
423 		while(c=='.'){
424 			c=nextc();
425 			if(isdigit(c)){
426 				if(fsp->fd) Bungetc(fsp->fd);
427 				else --fsp->corebuf;
428 				c='.';
429 				break;
430 			}
431 		}
432 		if(c==Beof) break;
433 		if(c=='\n') continue;
434 		if(isalpha(c)){
435 			s=argstr;
436 			do{
437 				if(isupper(c)) c=tolower(c);
438 				if(s!=&argstr[NARGSTR]) *s++=c;
439 				c=nextc();
440 			}while(isalpha(c));
441 			fsp->peekc=c;
442 			*s='\0';
443 			for(pplots=plots;pplots->cc;pplots++)
444 				if(strncmp(argstr, pplots->cc, pplots->numc)==0)
445 					break;
446 			if(pplots->cc==0){
447 				fprint(2, "line %d, %s unknown\n", fsp->lineno,
448 					argstr);
449 				exits("bad command");
450 			}
451 		}
452 		else{
453 			fsp->peekc=c;
454 		}
455 		if(!pplots){
456 			fprint(2, "line %d, no command!\n", fsp->lineno);
457 			exits("no command");
458 		}
459 		switch(pplots-plots){
460 		case ARC:	numargs(7); rarc(x[0],x[1],x[2],x[3],x[4],x[5],x[6]); break;
461 		case BOX:	numargs(4); box(x[0], x[1], x[2], x[3]); break;
462 		case CALL:	strarg();   call(argstr); pplots=0; break;
463 		case CFILL:	strarg();   cfill(argstr); pplots=0; break;
464 		case CIRC:	numargs(3); circ(x[0], x[1], x[2]); break;
465 		case CLOSEPL:	strarg();   closepl(); pplots=0; break;
466 		case COLOR:	strarg();   color(argstr); pplots=0; break;
467 		case CSPLINE:	polyarg();  splin(4, cnt, pts); break;
468 		case DEFINE:	strarg();   define(argstr); pplots=0; break;
469 		case DISK:	numargs(3); plotdisc(x[0], x[1], x[2]); break;
470 		case DSPLINE:	polyarg();  splin(3, cnt, pts); break;
471 		case ERASE:	strarg();   erase(); pplots=0; break;
472 		case FILL:	polyarg();  fill(cnt, pts); break;
473 		case FRAME:	numargs(4); frame(x[0], x[1], x[2], x[3]); break;
474 		case FSPLINE:	polyarg();  splin(1, cnt, pts); break;
475 		case GRADE:	numargs(1); grade(x[0]); break;
476 		case IDLE:	strarg();   idle(); pplots=0; break;
477 		case INCLUDE:	strarg();   include(argstr); pplots=0; break;
478 		case LINE:	numargs(4); plotline(x[0], x[1], x[2], x[3]); break;
479 		case LSPLINE:	polyarg();  splin(2, cnt, pts); break;
480 		case MOVE:	numargs(2); move(x[0], x[1]); break;
481 		case OPENPL:	strarg();   openpl(argstr); pplots=0; break;
482 		case PARABOLA:	numargs(6); parabola(x[0],x[1],x[2],x[3],x[4],x[5]); break;
483 		case PAUSE:	strarg();   ppause(); pplots=0; break;
484 		case PEN:	strarg();   pen(argstr); pplots=0; break;
485 		case POINT:	numargs(2); dpoint(x[0], x[1]); break;
486 		case POLY:	polyarg();  plotpoly(cnt, pts); break;
487 		case RANGE:	numargs(4); range(x[0], x[1], x[2], x[3]); break;
488 		case RESTORE:	strarg();   restore(); pplots=0; break;
489 		case RMOVE:	numargs(2); rmove(x[0], x[1]); break;
490 		case RVEC:	numargs(2); rvec(x[0], x[1]); break;
491 		case SAVE:	strarg();   save(); pplots=0; break;
492 		case SBOX:	numargs(4); sbox(x[0], x[1], x[2], x[3]); break;
493 		case SPLINE:	polyarg();  splin(0, cnt, pts); break;
494 		case TEXT:	strarg();   text(argstr); pplots=0; break;
495 		case VEC:	numargs(2); vec(x[0], x[1]); break;
496 		default:
497 			fprint(2, "plot: missing case %ld\n", pplots-plots);
498 			exits("internal error");
499 		}
500 	}
501 	return 1;
502 }
503 char *names = 0;
504 char *enames = 0;
505 char *bstash = 0;
506 char *estash = 0;
507 unsigned size = 1024;
508 char *nstash = 0;
define(char * a)509 void define(char *a){
510 	char	*ap;
511 	short	i, j;
512 	int curly = 0;
513 	ap = a;
514 	while(isalpha(*ap))ap++;
515 	if(ap == a){
516 		fprint(2,"no name with define\n");
517 		exits("define");
518 	}
519 	i = ap - a;
520 	if(names+i+1 > enames){
521 		names = malloc((unsigned)512);
522 		enames = names + 512;
523 	}
524 	fptr->name = names;
525 	strncpy(names, a,i);
526 	names += i;
527 	*names++ = '\0';
528 	if(!bstash){
529 		bstash = nstash = malloc(size);
530 		estash = bstash + size;
531 	}
532 	fptr->stash = nstash;
533 	while(*ap != '{')
534 		if(*ap == '\n'){
535 			if((ap=Brdline(fsp->fd, '\n'))==0){
536 				fprint(2,"unexpected end of file\n");
537 				exits("eof");
538 			}
539 		}
540 		else ap++;
541 	while((j=Bgetc(fsp->fd))!= Beof){
542 		if(j == '{')curly++;
543 		else if(j == '}'){
544 			if(curly == 0)break;
545 			else curly--;
546 		}
547 		*nstash++ = j;
548 		if(nstash == estash){
549 			free(bstash);
550 			size += 1024;
551 			bstash = realloc(bstash,size);
552 			estash = bstash+size;
553 		}
554 	}
555 	*nstash++ = '\0';
556 	if(fptr++ >= &flibr[MAXL]){
557 		fprint(2,"Too many objects\n");
558 		exits("too many objects");
559 	}
560 }
call(char * a)561 void call(char *a){
562 	char *ap;
563 	struct fcall *f;
564 	char sav;
565 	double SC;
566 	ap = a;
567 	while(isalpha(*ap))ap++;
568 	sav = *ap;
569 	*ap = '\0';
570 	for(f=flibr;f<fptr;f++){
571 		if (!(strcmp(a, f->name)))
572 			break;
573 	}
574 	if(f == fptr){
575 		fprint(2, "object %s not defined\n",a);
576 		exits("undefined");
577 	}
578 	*ap = sav;
579 	while (isspace(*ap) || *ap == ',')
580 		ap++;
581 	if (*ap != '\0')
582 		SC = atof(ap);
583 	else SC = 1.;
584 	if(++fsp==&fstack[NFSTACK]){
585 		fprint(2, "input stack overflow\n");
586 		exits("blew stack");
587 	}
588 	fsp->peekc=Beof;
589 	fsp->lineno=1;
590 	fsp->corebuf=f->stash;
591 	fsp->fd=0;
592 	fsp->scale=fsp[-1].scale*SC;
593 }
include(char * a)594 void include(char *a){
595 	Biobuf *fd;
596 	fd=Bopen(a, OREAD);
597 	if(fd==0){
598 		perror(a);
599 		exits("can't include");
600 	}
601 	if(++fsp==&fstack[NFSTACK]){
602 		fprint(2, "input stack overflow\n");
603 		exits("blew stack");
604 	}
605 	fsp->peekc=Beof;
606 	fsp->lineno=1;
607 	fsp->corebuf=0;
608 	fsp->fd=fd;
609 }
610 /*
611  * Doesn't work.  Why?
612  */
server(void)613 int server(void){
614 	int fd, p[2];
615 	char buf[32];
616 	pipe(p);
617 	fd = create("/srv/plot", 1, 0666);
618 	sprint(buf, "%d", p[1]);
619 	write(fd, buf, strlen(buf));
620 	close(fd);
621 	close(p[1]);
622 	return p[0];
623 }
624