xref: /plan9-contrib/sys/src/cmd/plot/plot.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
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 
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
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 	for(;;){
195 		m=emouse();
196 		if(m.buttons&4 && emenuhit(3, &m, &menu)==0) exits(0);
197 	}
198 }
199 int isalpha(int c)
200 {
201 	return ('a'<=c && c<='z') || ('A'<=c && c<='Z');
202 }
203 int isupper(int c)
204 {
205 	return 'A'<=c && c<='Z';
206 }
207 int isdigit(int c)
208 {
209 	return '0'<=c && c<='9';
210 }
211 int ispunct(int c)
212 {
213 	return strchr("!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~", c)!=0;
214 }
215 int isspace(int c)
216 {
217 	return strchr(" \t\n\v\f\r", c)!=0;
218 }
219 int nextc(void){
220 	int c;
221 	Rune r;
222 	for(;;){
223 		if(fsp->peekc!=Beof){
224 			c=fsp->peekc;
225 			fsp->peekc=Beof;
226 			return c;
227 		}
228 		if(fsp->fd)
229 			c=Bgetrune(fsp->fd);
230 		else if(*fsp->corebuf){
231 			fsp->corebuf+=chartorune(&r, fsp->corebuf);
232 			c=r;
233 		}else
234 			c=Beof;
235 		if(c!=Beof || fsp==fstack) break;
236 		if(fsp->fd) Bterm(fsp->fd);
237 		--fsp;
238 	}
239 	if(c=='\n') fsp->lineno++;
240 	return c;
241 }
242 /*
243  * Read a string into argstr -- ignores leading spaces
244  * and an optional leading quote-mark
245  */
246 void
247 strarg(void){
248 	int c;
249 	Rune r;
250 	int quote=0;
251 	char *s=argstr;
252 	do
253 		c=nextc();
254 	while(c==' ' || c=='\t');
255 	if(c=='\'' || c=='"'){
256 		quote=c;
257 		c=nextc();
258 	}
259 	r = 0;
260 	while(c!='\n' && c!=Beof){
261 		r=c;
262 		s+=runetochar(s, &r);
263 		c=nextc();
264 	}
265 	if(quote && s!=argstr && r==quote) --s;
266 	*s='\0';
267 }
268 /*
269  * Read a floating point number into argstr
270  */
271 numstring(void){
272 	int ndp=0;
273 	int ndig=0;
274 	char *s=argstr;
275 	int c=nextc();
276 	if(c=='+' || c=='-'){
277 		*s++=c;
278 		c=nextc();
279 	}
280 	while(isdigit(c) || c=='.'){
281 		if(s!=&argstr[NARGSTR]) *s++=c;
282 		if(c=='.') ndp++;
283 		else ndig++;
284 		c=nextc();
285 	}
286 	if(ndp>1 || ndig==0){
287 		fsp->peekc=c;
288 		return 0;
289 	}
290 	if(c=='e' || c=='E'){
291 		if(s!=&argstr[NARGSTR]) *s++=c;
292 		c=nextc();
293 		if(c=='+' || c=='-'){
294 			if(s!=&argstr[NARGSTR]) *s++=c;
295 			c=nextc();
296 		}
297 		if(!isdigit(c)){
298 			fsp->peekc=c;
299 			return 0;
300 		}
301 		while(isdigit(c)){
302 			if(s!=&argstr[NARGSTR]) *s++=c;
303 			c=nextc();
304 		}
305 	}
306 	fsp->peekc=c;
307 	*s='\0';
308 	return 1;
309 }
310 /*
311  * Read n numeric arguments, storing them in
312  * x[0], ..., x[n-1]
313  */
314 void
315 numargs(int n){
316 	int i, c;
317 	for(i=0;i!=n;i++){
318 		do{
319 			c=nextc();
320 		}while(strchr(" \t\n", c) || c!='.' && c!='+' && c!='-' && ispunct(c));
321 		fsp->peekc=c;
322 		if(!numstring()){
323 			fprint(2, "line %d: number expected\n", fsp->lineno);
324 			exits("input error");
325 		}
326 		x[i]=atof(argstr)*fsp->scale;
327 	}
328 }
329 /*
330  * Read a list of lists of control vertices, storing points in x[.],
331  * pointers in pts[.] and counts in cnt[.]
332  */
333 void
334 polyarg(void){
335 	int nleft, l, r, c;
336 	double **ptsp=pts, *xp=x;
337 	int *cntp=cnt;
338 	do{
339 		c=nextc();
340 	}while(c==' ' || c=='\t');
341 	if(c=='{'){
342 		l='{';
343 		r='}';
344 	}
345 	else{
346 		l=r='\n';
347 		fsp->peekc=c;
348 	}
349 	nleft=1;
350 	*cntp=0;
351 	*ptsp=xp;
352 	for(;;){
353 		c=nextc();
354 		if(c==r){
355 			if(*cntp){
356 				if(*cntp&1){
357 					fprint(2, "line %d: phase error\n",
358 						fsp->lineno);
359 					exits("bad input");
360 				}
361 				*cntp/=2;
362 				if(ptsp==&pts[NPTS]){
363 					fprint(2, "line %d: out of polygons\n",
364 						fsp->lineno);
365 					exits("exceeded limit");
366 				}
367 				*++ptsp=xp;
368 				*++cntp=0;
369 			}
370 			if(--nleft==0) return;
371 		}
372 		else switch(c){
373 		case Beof:  return;
374 		case ' ':  break;
375 		case '\t': break;
376 		case '\n': break;
377 		case '.': case '+': case '-':
378 		case '0': case '1': case '2': case '3': case '4':
379 		case '5': case '6': case '7': case '8': case '9':
380 			fsp->peekc=c;
381 			if(!numstring()){
382 				fprint(2, "line %d: expected number\n", fsp->lineno);
383 				exits("bad input");
384 			}
385 			if(xp==&x[NX]){
386 				fprint(2, "line %d: out of space\n", fsp->lineno);
387 				exits("exceeded limit");
388 			}
389 			*xp++=atof(argstr);
390 			++*cntp;
391 			break;
392 		default:
393 			if(c==l) nleft++;
394 			else if(!ispunct(c)){
395 				fsp->peekc=c;
396 				return;
397 			}
398 		}
399 	}
400 }
401 
402 process(Biobuf *fd){
403 	char *s;
404 	int c;
405 	fsp=fstack;
406 	fsp->fd=fd;
407 	fsp->corebuf=0;
408 	fsp->peekc=Beof;
409 	fsp->lineno=1;
410 	fsp->scale=1.;
411 	for(;;){
412 		do
413 			c=nextc();
414 		while(c==' ' || c=='\t');
415 		if(c==':'){
416 			do
417 				c=nextc();
418 			while(c!='\n' && c!=Beof);
419 			if(c==Beof) break;
420 			continue;
421 		}
422 		while(c=='.'){
423 			c=nextc();
424 			if(isdigit(c)){
425 				if(fsp->fd) Bungetc(fsp->fd);
426 				else --fsp->corebuf;
427 				c='.';
428 				break;
429 			}
430 		}
431 		if(c==Beof) break;
432 		if(c=='\n') continue;
433 		if(isalpha(c)){
434 			s=argstr;
435 			do{
436 				if(isupper(c)) c=tolower(c);
437 				if(s!=&argstr[NARGSTR]) *s++=c;
438 				c=nextc();
439 			}while(isalpha(c));
440 			fsp->peekc=c;
441 			*s='\0';
442 			for(pplots=plots;pplots->cc;pplots++)
443 				if(strncmp(argstr, pplots->cc, pplots->numc)==0)
444 					break;
445 			if(pplots->cc==0){
446 				fprint(2, "line %d, %s unknown\n", fsp->lineno,
447 					argstr);
448 				exits("bad command");
449 			}
450 		}
451 		else{
452 			fsp->peekc=c;
453 		}
454 		if(!pplots){
455 			fprint(2, "line %d, no command!\n", fsp->lineno);
456 			exits("no command");
457 		}
458 		switch(pplots-plots){
459 		case ARC:	numargs(7); rarc(x[0],x[1],x[2],x[3],x[4],x[5],x[6]); break;
460 		case BOX:	numargs(4); box(x[0], x[1], x[2], x[3]); break;
461 		case CALL:	strarg();   call(argstr); pplots=0; break;
462 		case CFILL:	strarg();   cfill(argstr); pplots=0; break;
463 		case CIRC:	numargs(3); circ(x[0], x[1], x[2]); break;
464 		case CLOSEPL:	strarg();   closepl(); pplots=0; break;
465 		case COLOR:	strarg();   color(argstr); pplots=0; break;
466 		case CSPLINE:	polyarg();  splin(4, cnt, pts); break;
467 		case DEFINE:	strarg();   define(argstr); pplots=0; break;
468 		case DISK:	numargs(3); plotdisc(x[0], x[1], x[2]); break;
469 		case DSPLINE:	polyarg();  splin(3, cnt, pts); break;
470 		case ERASE:	strarg();   erase(); pplots=0; break;
471 		case FILL:	polyarg();  fill(cnt, pts); break;
472 		case FRAME:	numargs(4); frame(x[0], x[1], x[2], x[3]); break;
473 		case FSPLINE:	polyarg();  splin(1, cnt, pts); break;
474 		case GRADE:	numargs(1); grade(x[0]); break;
475 		case IDLE:	strarg();   idle(); pplots=0; break;
476 		case INCLUDE:	strarg();   include(argstr); pplots=0; break;
477 		case LINE:	numargs(4); plotline(x[0], x[1], x[2], x[3]); break;
478 		case LSPLINE:	polyarg();  splin(2, cnt, pts); break;
479 		case MOVE:	numargs(2); move(x[0], x[1]); break;
480 		case OPENPL:	strarg();   openpl(argstr); pplots=0; break;
481 		case PARABOLA:	numargs(6); parabola(x[0],x[1],x[2],x[3],x[4],x[5]); break;
482 		case PAUSE:	strarg();   ppause(); pplots=0; break;
483 		case PEN:	strarg();   pen(argstr); pplots=0; break;
484 		case POINT:	numargs(2); dpoint(x[0], x[1]); break;
485 		case POLY:	polyarg();  plotpoly(cnt, pts); break;
486 		case RANGE:	numargs(4); range(x[0], x[1], x[2], x[3]); break;
487 		case RESTORE:	strarg();   restore(); pplots=0; break;
488 		case RMOVE:	numargs(2); rmove(x[0], x[1]); break;
489 		case RVEC:	numargs(2); rvec(x[0], x[1]); break;
490 		case SAVE:	strarg();   save(); pplots=0; break;
491 		case SBOX:	numargs(4); sbox(x[0], x[1], x[2], x[3]); break;
492 		case SPLINE:	polyarg();  splin(0, cnt, pts); break;
493 		case TEXT:	strarg();   text(argstr); pplots=0; break;
494 		case VEC:	numargs(2); vec(x[0], x[1]); break;
495 		default:
496 			fprint(2, "plot: missing case %ld\n", pplots-plots);
497 			exits("internal error");
498 		}
499 	}
500 	return 1;
501 }
502 char *names = 0;
503 char *enames = 0;
504 char *bstash = 0;
505 char *estash = 0;
506 unsigned size = 1024;
507 char *nstash = 0;
508 void define(char *a){
509 	char	*ap;
510 	short	i, j;
511 	int curly = 0;
512 	ap = a;
513 	while(isalpha(*ap))ap++;
514 	if(ap == a){
515 		fprint(2,"no name with define\n");
516 		exits("define");
517 	}
518 	i = ap - a;
519 	if(names+i+1 > enames){
520 		names = malloc((unsigned)512);
521 		enames = names + 512;
522 	}
523 	fptr->name = names;
524 	strncpy(names, a,i);
525 	names += i;
526 	*names++ = '\0';
527 	if(!bstash){
528 		bstash = nstash = malloc(size);
529 		estash = bstash + size;
530 	}
531 	fptr->stash = nstash;
532 	while(*ap != '{')
533 		if(*ap == '\n'){
534 			if((ap=Brdline(fsp->fd, '\n'))==0){
535 				fprint(2,"unexpected end of file\n");
536 				exits("eof");
537 			}
538 		}
539 		else ap++;
540 	while((j=Bgetc(fsp->fd))!= Beof){
541 		if(j == '{')curly++;
542 		else if(j == '}'){
543 			if(curly == 0)break;
544 			else curly--;
545 		}
546 		*nstash++ = j;
547 		if(nstash == estash){
548 			free(bstash);
549 			size += 1024;
550 			bstash = realloc(bstash,size);
551 			estash = bstash+size;
552 		}
553 	}
554 	*nstash++ = '\0';
555 	if(fptr++ >= &flibr[MAXL]){
556 		fprint(2,"Too many objects\n");
557 		exits("too many objects");
558 	}
559 }
560 void call(char *a){
561 	char *ap;
562 	struct fcall *f;
563 	char sav;
564 	double SC;
565 	ap = a;
566 	while(isalpha(*ap))ap++;
567 	sav = *ap;
568 	*ap = '\0';
569 	for(f=flibr;f<fptr;f++){
570 		if (!(strcmp(a, f->name)))
571 			break;
572 	}
573 	if(f == fptr){
574 		fprint(2, "object %s not defined\n",a);
575 		exits("undefined");
576 	}
577 	*ap = sav;
578 	while (isspace(*ap) || *ap == ',')
579 		ap++;
580 	if (*ap != '\0')
581 		SC = atof(ap);
582 	else SC = 1.;
583 	if(++fsp==&fstack[NFSTACK]){
584 		fprint(2, "input stack overflow\n");
585 		exits("blew stack");
586 	}
587 	fsp->peekc=Beof;
588 	fsp->lineno=1;
589 	fsp->corebuf=f->stash;
590 	fsp->fd=0;
591 	fsp->scale=fsp[-1].scale*SC;
592 }
593 void include(char *a){
594 	Biobuf *fd;
595 	fd=Bopen(a, OREAD);
596 	if(fd==0){
597 		perror(a);
598 		exits("can't include");
599 	}
600 	if(++fsp==&fstack[NFSTACK]){
601 		fprint(2, "input stack overflow\n");
602 		exits("blew stack");
603 	}
604 	fsp->peekc=Beof;
605 	fsp->lineno=1;
606 	fsp->corebuf=0;
607 	fsp->fd=fd;
608 }
609 /*
610  * Doesn't work.  Why?
611  */
612 int server(void){
613 	int fd, p[2];
614 	char buf[32];
615 	pipe(p);
616 	fd = create("/srv/plot", 1, 0666);
617 	sprint(buf, "%d", p[1]);
618 	write(fd, buf, strlen(buf));
619 	close(fd);
620 	close(p[1]);
621 	return p[0];
622 }
623