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