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