1 /* vim:set shiftwidth=4 ts=8: */
2 
3 /*************************************************************************
4  * Copyright (c) 2011 AT&T Intellectual Property
5  * All rights reserved. This program and the accompanying materials
6  * are made available under the terms of the Eclipse Public License v1.0
7  * which accompanies this distribution, and is available at
8  * http://www.eclipse.org/legal/epl-v10.html
9  *
10  * Contributors: See CVS logs. Details at http://www.graphviz.org/
11  *************************************************************************/
12 
13 #include <xdot.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <ctype.h>
17 
18 #define NEW(t)           (t*)calloc(1, sizeof(t))
19 #define N_NEW(n,t)       (t*)calloc((n), sizeof(t))
20 
21 typedef struct {
22     unsigned char *buf;		/* start of buffer */
23     unsigned char *ptr;		/* next place to write */
24     unsigned char *eptr;	/* end of buffer */
25     int dyna;			/* true if buffer is malloc'ed */
26 } agxbuf;
27 
28 #define agxbputc(X,C) ((((X)->ptr >= (X)->eptr) ? agxbmore(X,1) : 0), \
29           (void)(*(X)->ptr++ = ((unsigned char)C)))
30 #define agxbuse(X) (agxbputc(X,'\0'),(char*)((X)->ptr = (X)->buf))
31 
agxbinit(agxbuf * xb,unsigned int hint,unsigned char * init)32 static void agxbinit(agxbuf * xb, unsigned int hint, unsigned char *init)
33 {
34     if (init) {
35 	xb->buf = init;
36 	xb->dyna = 0;
37     } else {
38 	if (hint == 0)
39 	    hint = BUFSIZ;
40 	xb->dyna = 1;
41 	xb->buf = N_NEW(hint, unsigned char);
42     }
43     xb->eptr = xb->buf + hint;
44     xb->ptr = xb->buf;
45     *xb->ptr = '\0';
46 }
agxbmore(agxbuf * xb,unsigned int ssz)47 static int agxbmore(agxbuf * xb, unsigned int ssz)
48 {
49     int cnt;			/* current no. of characters in buffer */
50     int size;			/* current buffer size */
51     int nsize;			/* new buffer size */
52     unsigned char *nbuf;	/* new buffer */
53 
54     size = xb->eptr - xb->buf;
55     nsize = 2 * size;
56     if (size + ssz > nsize)
57 	nsize = size + ssz;
58     cnt = xb->ptr - xb->buf;
59     if (xb->dyna) {
60 	nbuf = realloc(xb->buf, nsize);
61     } else {
62 	nbuf = N_NEW(nsize, unsigned char);
63 	memcpy(nbuf, xb->buf, cnt);
64 	xb->dyna = 1;
65     }
66     xb->buf = nbuf;
67     xb->ptr = xb->buf + cnt;
68     xb->eptr = xb->buf + nsize;
69     return 0;
70 }
71 
agxbput(char * s,agxbuf * xb)72 static int agxbput(char *s, agxbuf * xb)
73 {
74     unsigned int ssz = strlen(s);
75     if (xb->ptr + ssz > xb->eptr)
76 	agxbmore(xb, ssz);
77     memcpy(xb->ptr, s, ssz);
78     xb->ptr += ssz;
79     return ssz;
80 }
81 
82 /* agxbfree:
83  * Free any malloced resources.
84  */
agxbfree(agxbuf * xb)85 static void agxbfree(agxbuf * xb)
86 {
87     if (xb->dyna)
88 	free(xb->buf);
89 }
90 
91 /* the parse functions should return NULL on error */
parseReal(char * s,double * fp)92 static char *parseReal(char *s, double *fp)
93 {
94     char *p;
95     double d;
96 
97     d = strtod(s, &p);
98     if (p == s) return 0;
99 
100     *fp = d;
101     return (p);
102 }
103 
104 
parseInt(char * s,int * ip)105 static char *parseInt(char *s, int *ip)
106 {
107     char* endp;
108 
109 #ifdef UNUSED
110     r = sscanf(s, "%d%n", ip, &sz);
111     if (r != 1) return 0;
112     else return (s + sz);
113 #endif
114 
115     *ip = (int)strtol (s, &endp, 10);
116     if (s == endp)
117 	return 0;
118     else
119 	return endp;
120 }
121 
parseUInt(char * s,unsigned int * ip)122 static char *parseUInt(char *s, unsigned int *ip)
123 {
124     char* endp;
125 
126     *ip = (unsigned int)strtoul (s, &endp, 10);
127     if (s == endp)
128 	return 0;
129     else
130 	return endp;
131 }
132 
133 #ifdef UNUSED
parsePoint(char * s,xdot_point * pp)134 static char *parsePoint(char *s, xdot_point * pp)
135 {
136     int r, sz;
137     r = sscanf(s, "%lf %lf%n", &(pp->x), &(pp->y), &sz);
138     if (r != 2) return 0;
139     pp->z = 0;
140     return (s + sz);
141 }
142 #endif
143 
parseRect(char * s,xdot_rect * rp)144 static char *parseRect(char *s, xdot_rect * rp)
145 {
146     char* endp;
147 #ifdef UNUSED
148     int r, sz;
149     r = sscanf(s, "%lf %lf %lf %lf%n", &(rp->x), &(rp->y), &(rp->w),
150 	       &(rp->h), &sz);
151     if (r != 4) return 0;
152     else return (s + sz);
153 #endif
154 
155     rp->x = strtod (s, &endp);
156     if (s == endp)
157 	return 0;
158     else
159 	s = endp;
160 
161     rp->y = strtod (s, &endp);
162     if (s == endp)
163 	return 0;
164     else
165 	s = endp;
166 
167     rp->w = strtod (s, &endp);
168     if (s == endp)
169 	return 0;
170     else
171 	s = endp;
172 
173     rp->h = strtod (s, &endp);
174     if (s == endp)
175 	return 0;
176     else
177 	s = endp;
178 
179     return s;
180 }
181 
parsePolyline(char * s,xdot_polyline * pp)182 static char *parsePolyline(char *s, xdot_polyline * pp)
183 {
184     int i;
185     xdot_point *pts;
186     xdot_point *ps;
187     char* endp;
188 
189     s = parseInt(s, &i);
190     if (!s) return s;
191     pts = ps = N_NEW(i, xdot_point);
192     pp->cnt = i;
193     for (i = 0; i < pp->cnt; i++) {
194 	ps->x = strtod (s, &endp);
195 	if (s == endp) {
196 	    free (pts);
197 	    return 0;
198 	}
199 	else
200 	    s = endp;
201 	ps->y = strtod (s, &endp);
202 	if (s == endp) {
203 	    free (pts);
204 	    return 0;
205 	}
206 	else
207 	    s = endp;
208 	ps->z = 0;
209 	ps++;
210     }
211     pp->pts = pts;
212     return s;
213 }
214 
parseString(char * s,char ** sp)215 static char *parseString(char *s, char **sp)
216 {
217     int i;
218     char *c;
219     char *p;
220     s = parseInt(s, &i);
221     if (!s || (i <= 0)) return 0;
222     while (*s && (*s != '-')) s++;
223     if (*s) s++;
224     else {
225 	return 0;
226     }
227     c = N_NEW(i + 1, char);
228     p = c;
229     while ((i > 0) && *s) {
230 	*p++ = *s++;
231 	i--;
232     }
233     if (i > 0) {
234 	free (c);
235 	return 0;
236     }
237 
238     *p = '\0';
239     *sp = c;
240     return s;
241 }
242 
parseAlign(char * s,xdot_align * ap)243 static char *parseAlign(char *s, xdot_align * ap)
244 {
245     int i;
246     s = parseInt(s, &i);
247 
248     if (i < 0)
249 	*ap = xd_left;
250     else if (i > 0)
251 	*ap = xd_right;
252     else
253 	*ap = xd_center;
254     return s;
255 }
256 
257 #define CHK(s) if(!s){*error=1;return 0;}
258 
parseOp(xdot_op * op,char * s,drawfunc_t ops[],int * error)259 static char *parseOp(xdot_op * op, char *s, drawfunc_t ops[], int* error)
260 {
261     char* cs;
262     xdot_color clr;
263 
264     *error = 0;
265     while (isspace(*s))
266 	s++;
267     switch (*s++) {
268     case 'E':
269 	op->kind = xd_filled_ellipse;
270 	s = parseRect(s, &op->u.ellipse);
271 	CHK(s);
272 	if (ops)
273 	    op->drawfunc = ops[xop_ellipse];
274 	break;
275 
276     case 'e':
277 	op->kind = xd_unfilled_ellipse;
278 	s = parseRect(s, &op->u.ellipse);
279 	CHK(s);
280 	if (ops)
281 	    op->drawfunc = ops[xop_ellipse];
282 	break;
283 
284     case 'P':
285 	op->kind = xd_filled_polygon;
286 	s = parsePolyline(s, &op->u.polygon);
287 	CHK(s);
288 	if (ops)
289 	    op->drawfunc = ops[xop_polygon];
290 	break;
291 
292     case 'p':
293 	op->kind = xd_unfilled_polygon;
294 	s = parsePolyline(s, &op->u.polygon);
295 	CHK(s);
296 	if (ops)
297 	    op->drawfunc = ops[xop_polygon];
298 	break;
299 
300     case 'b':
301 	op->kind = xd_filled_bezier;
302 	s = parsePolyline(s, &op->u.bezier);
303 	CHK(s);
304 	if (ops)
305 	    op->drawfunc = ops[xop_bezier];
306 	break;
307 
308     case 'B':
309 	op->kind = xd_unfilled_bezier;
310 	s = parsePolyline(s, &op->u.bezier);
311 	CHK(s);
312 	if (ops)
313 	    op->drawfunc = ops[xop_bezier];
314 	break;
315 
316     case 'c':
317 	s = parseString(s, &cs);
318 	CHK(s);
319 	cs = parseXDotColor (cs, &clr);
320 	CHK(cs);
321 	if (clr.type == xd_none) {
322 	    op->kind = xd_pen_color;
323 	    op->u.color = clr.u.clr;
324 	    if (ops)
325 		op->drawfunc = ops[xop_pen_color];
326 	}
327 	else {
328 	    op->kind = xd_grad_pen_color;
329 	    op->u.grad_color = clr;
330 	    if (ops)
331 		op->drawfunc = ops[xop_grad_color];
332 	}
333 	break;
334 
335     case 'C':
336 	s = parseString(s, &cs);
337 	CHK(s);
338 	cs = parseXDotColor (cs, &clr);
339 	CHK(cs);
340 	if (clr.type == xd_none) {
341 	    op->kind = xd_fill_color;
342 	    op->u.color = clr.u.clr;
343 	    if (ops)
344 		op->drawfunc = ops[xop_fill_color];
345 	}
346 	else {
347 	    op->kind = xd_grad_fill_color;
348 	    op->u.grad_color = clr;
349 	    if (ops)
350 		op->drawfunc = ops[xop_grad_color];
351 	}
352 	break;
353 
354     case 'L':
355 	op->kind = xd_polyline;
356 	s = parsePolyline(s, &op->u.polyline);
357 	CHK(s);
358 	if (ops)
359 	    op->drawfunc = ops[xop_polyline];
360 	break;
361 
362     case 'T':
363 	op->kind = xd_text;
364 	s = parseReal(s, &op->u.text.x);
365 	CHK(s);
366 	s = parseReal(s, &op->u.text.y);
367 	CHK(s);
368 	s = parseAlign(s, &op->u.text.align);
369 	CHK(s);
370 	s = parseReal(s, &op->u.text.width);
371 	CHK(s);
372 	s = parseString(s, &op->u.text.text);
373 	CHK(s);
374 	if (ops)
375 	    op->drawfunc = ops[xop_text];
376 	break;
377 
378     case 'F':
379 	op->kind = xd_font;
380 	s = parseReal(s, &op->u.font.size);
381 	CHK(s);
382 	s = parseString(s, &op->u.font.name);
383 	CHK(s);
384 	if (ops)
385 	    op->drawfunc = ops[xop_font];
386 	break;
387 
388     case 'S':
389 	op->kind = xd_style;
390 	s = parseString(s, &op->u.style);
391 	CHK(s);
392 	if (ops)
393 	    op->drawfunc = ops[xop_style];
394 	break;
395 
396     case 'I':
397 	op->kind = xd_image;
398 	s = parseRect(s, &op->u.image.pos);
399 	CHK(s);
400 	s = parseString(s, &op->u.image.name);
401 	CHK(s);
402 	if (ops)
403 	    op->drawfunc = ops[xop_image];
404 	break;
405 
406     case 't':
407 	op->kind = xd_fontchar;
408 	s = parseUInt(s, &op->u.fontchar);
409 	CHK(s);
410 	if (ops)
411 	    op->drawfunc = ops[xop_fontchar];
412 	break;
413 
414 
415     case '\0':
416 	s = 0;
417 	break;
418 
419     default:
420 	*error = 1;
421 	s = 0;
422 	break;
423     }
424     return s;
425 }
426 
427 #define XDBSIZE 100
428 
429 /* parseXDotFOn:
430  * Parse and append additional xops onto a given xdot object.
431  * Return x.
432  */
parseXDotFOn(char * s,drawfunc_t fns[],int sz,xdot * x)433 xdot *parseXDotFOn (char *s, drawfunc_t fns[], int sz, xdot* x)
434 {
435     xdot_op op;
436     char *ops;
437     int oldsz, bufsz;
438     int error;
439     int initcnt;
440 
441     if (!s)
442 	return x;
443 
444     if (!x) {
445 	x = NEW(xdot);
446 	if (sz <= sizeof(xdot_op))
447 	    sz = sizeof(xdot_op);
448 
449 	/* cnt, freefunc, ops, flags zeroed by NEW */
450 	x->sz = sz;
451     }
452     initcnt = x->cnt;
453     sz = x->sz;
454 
455     if (initcnt == 0) {
456 	bufsz = XDBSIZE;
457 	ops = (char *) calloc(XDBSIZE, sz);
458     }
459     else {
460 	ops = (char*)(x->ops);
461 	bufsz = initcnt + XDBSIZE;
462 	ops = (char *) realloc(ops, bufsz * sz);
463 	memset(ops + (initcnt*sz), '\0', (bufsz - initcnt)*sz);
464     }
465 
466     while ((s = parseOp(&op, s, fns, &error))) {
467 	if (x->cnt == bufsz) {
468 	    oldsz = bufsz;
469 	    bufsz *= 2;
470 	    ops = (char *) realloc(ops, bufsz * sz);
471 	    memset(ops + (oldsz*sz), '\0', (bufsz - oldsz)*sz);
472 	}
473 	*(xdot_op *) (ops + (x->cnt * sz)) = op;
474 	x->cnt++;
475     }
476     if (error)
477 	x->flags |= XDOT_PARSE_ERROR;
478     if (x->cnt) {
479 	x->ops = (xdot_op *) realloc(ops, x->cnt * sz);
480     }
481     else {
482 	free (ops);
483 	free (x);
484 	x = NULL;
485     }
486 
487     return x;
488 
489 }
490 
parseXDotF(char * s,drawfunc_t fns[],int sz)491 xdot *parseXDotF(char *s, drawfunc_t fns[], int sz)
492 {
493     return parseXDotFOn (s, fns, sz, NULL);
494 }
495 
parseXDot(char * s)496 xdot *parseXDot(char *s)
497 {
498     return parseXDotF(s, 0, 0);
499 }
500 
501 typedef void (*pf) (char *, void *);
502 
503 /* trim:
504  * Trailing zeros are removed and decimal point, if possible.
505  */
trim(char * buf)506 static void trim (char* buf)
507 {
508     char* dotp;
509     char* p;
510 
511     if ((dotp = strchr (buf,'.'))) {
512         p = dotp+1;
513         while (*p) p++;  // find end of string
514         p--;
515         while (*p == '0') *p-- = '\0';
516         if (*p == '.')        // If all decimals were zeros, remove ".".
517             *p = '\0';
518         else
519             p++;
520     }
521 }
522 
printRect(xdot_rect * r,pf print,void * info)523 static void printRect(xdot_rect * r, pf print, void *info)
524 {
525     char buf[128];
526 
527     sprintf(buf, " %.02f", r->x);
528     trim(buf);
529     print(buf, info);
530     sprintf(buf, " %.02f", r->y);
531     trim(buf);
532     print(buf, info);
533     sprintf(buf, " %.02f", r->w);
534     trim(buf);
535     print(buf, info);
536     sprintf(buf, " %.02f", r->h);
537     trim(buf);
538     print(buf, info);
539 }
540 
printPolyline(xdot_polyline * p,pf print,void * info)541 static void printPolyline(xdot_polyline * p, pf print, void *info)
542 {
543     int i;
544     char buf[512];
545 
546     sprintf(buf, " %d", p->cnt);
547     print(buf, info);
548     for (i = 0; i < p->cnt; i++) {
549 	sprintf(buf, " %.02f", p->pts[i].x);
550 	trim(buf);
551 	print(buf, info);
552 	sprintf(buf, " %.02f", p->pts[i].y);
553 	trim(buf);
554 	print(buf, info);
555     }
556 }
557 
printString(char * p,pf print,void * info)558 static void printString(char *p, pf print, void *info)
559 {
560     char buf[30];
561 
562     sprintf(buf, " %d -", (int) strlen(p));
563     print(buf, info);
564     print(p, info);
565 }
566 
printInt(int i,pf print,void * info)567 static void printInt(int i, pf print, void *info)
568 {
569     char buf[30];
570 
571     sprintf(buf, " %d", i);
572     print(buf, info);
573 }
574 
printFloat(float f,pf print,void * info,int space)575 static void printFloat(float f, pf print, void *info, int space)
576 {
577     char buf[128];
578 
579     if (space)
580 	sprintf(buf, " %.02f", f);
581     else
582 	sprintf(buf, "%.02f", f);
583     trim (buf);
584     print(buf, info);
585 }
586 
printAlign(xdot_align a,pf print,void * info)587 static void printAlign(xdot_align a, pf print, void *info)
588 {
589     switch (a) {
590     case xd_left:
591 	print(" -1", info);
592 	break;
593     case xd_right:
594 	print(" 1", info);
595 	break;
596     case xd_center:
597 	print(" 0", info);
598 	break;
599     }
600 }
601 
602 static void
gradprint(char * s,void * v)603 gradprint (char* s, void* v)
604 {
605     agxbput(s, (agxbuf*)v);
606 }
607 
608 static void
toGradString(agxbuf * xb,xdot_color * cp)609 toGradString (agxbuf* xb, xdot_color* cp)
610 {
611     int i, n_stops;
612     xdot_color_stop* stops;
613 
614     if (cp->type == xd_linear) {
615 	agxbputc (xb, '[');
616 	printFloat (cp->u.ling.x0, gradprint, xb, 0);
617 	printFloat (cp->u.ling.y0, gradprint, xb, 1);
618 	printFloat (cp->u.ling.x1, gradprint, xb, 1);
619 	printFloat (cp->u.ling.y1, gradprint, xb, 1);
620 	n_stops = cp->u.ling.n_stops;
621 	stops = cp->u.ling.stops;
622     }
623     else {
624 	agxbputc (xb, '(');
625 	printFloat (cp->u.ring.x0, gradprint, xb, 0);
626 	printFloat (cp->u.ring.y0, gradprint, xb, 1);
627 	printFloat (cp->u.ring.r0, gradprint, xb, 1);
628 	printFloat (cp->u.ring.x1, gradprint, xb, 1);
629 	printFloat (cp->u.ring.y1, gradprint, xb, 1);
630 	printFloat (cp->u.ring.r1, gradprint, xb, 1);
631 	n_stops = cp->u.ring.n_stops;
632 	stops = cp->u.ring.stops;
633     }
634     printInt (n_stops, gradprint, xb);
635     for (i = 0; i < n_stops; i++) {
636 	printFloat (stops[i].frac, gradprint, xb, 1);
637 	printString (stops[i].color, gradprint, xb);
638     }
639 
640     if (cp->type == xd_linear)
641 	agxbputc (xb, ']');
642     else
643 	agxbputc (xb, ')');
644 }
645 
646 typedef void (*print_op)(xdot_op * op, pf print, void *info, int more);
647 
printXDot_Op(xdot_op * op,pf print,void * info,int more)648 static void printXDot_Op(xdot_op * op, pf print, void *info, int more)
649 {
650     agxbuf xb;
651     unsigned char buf[BUFSIZ];
652 
653     agxbinit (&xb, BUFSIZ, buf);
654     switch (op->kind) {
655     case xd_filled_ellipse:
656 	print("E", info);
657 	printRect(&op->u.ellipse, print, info);
658 	break;
659     case xd_unfilled_ellipse:
660 	print("e", info);
661 	printRect(&op->u.ellipse, print, info);
662 	break;
663     case xd_filled_polygon:
664 	print("P", info);
665 	printPolyline(&op->u.polygon, print, info);
666 	break;
667     case xd_unfilled_polygon:
668 	print("p", info);
669 	printPolyline(&op->u.polygon, print, info);
670 	break;
671     case xd_filled_bezier:
672 	print("b", info);
673 	printPolyline(&op->u.bezier, print, info);
674 	break;
675     case xd_unfilled_bezier:
676 	print("B", info);
677 	printPolyline(&op->u.bezier, print, info);
678 	break;
679     case xd_pen_color:
680 	print("c", info);
681 	printString(op->u.color, print, info);
682 	break;
683     case xd_grad_pen_color:
684 	print("c", info);
685 	toGradString (&xb, &op->u.grad_color);
686 	printString(agxbuse(&xb), print, info);
687 	break;
688     case xd_fill_color:
689 	print("C", info);
690 	printString(op->u.color, print, info);
691 	break;
692     case xd_grad_fill_color:
693 	print("C", info);
694 	toGradString (&xb, &op->u.grad_color);
695 	printString(agxbuse(&xb), print, info);
696 	break;
697     case xd_polyline:
698 	print("L", info);
699 	printPolyline(&op->u.polyline, print, info);
700 	break;
701     case xd_text:
702 	print("T", info);
703 	printInt(op->u.text.x, print, info);
704 	printInt(op->u.text.y, print, info);
705 	printAlign(op->u.text.align, print, info);
706 	printInt(op->u.text.width, print, info);
707 	printString(op->u.text.text, print, info);
708 	break;
709     case xd_font:
710 	print("F", info);
711 	printFloat(op->u.font.size, print, info, 1);
712 	printString(op->u.font.name, print, info);
713 	break;
714     case xd_fontchar:
715 	print("t", info);
716 	printInt(op->u.fontchar, print, info);
717 	break;
718     case xd_style:
719 	print("S", info);
720 	printString(op->u.style, print, info);
721 	break;
722     case xd_image:
723 	print("I", info);
724 	printRect(&op->u.image.pos, print, info);
725 	printString(op->u.image.name, print, info);
726 	break;
727     }
728     if (more)
729 	print(" ", info);
730     agxbfree (&xb);
731 }
732 
jsonRect(xdot_rect * r,pf print,void * info)733 static void jsonRect(xdot_rect * r, pf print, void *info)
734 {
735     char buf[128];
736 
737     sprintf(buf, "[%.06f,%.06f,%.06f,%.06f]", r->x, r->y, r->w, r->h);
738     print(buf, info);
739 }
740 
jsonPolyline(xdot_polyline * p,pf print,void * info)741 static void jsonPolyline(xdot_polyline * p, pf print, void *info)
742 {
743     int i;
744     char buf[128];
745 
746     print("[", info);
747     for (i = 0; i < p->cnt; i++) {
748 	sprintf(buf, "%.06f,%.06f", p->pts[i].x, p->pts[i].y);
749 	print(buf, info);
750 	if (i < p->cnt-1) print(",", info);
751     }
752     print("]", info);
753 }
754 
jsonString(char * p,pf print,void * info)755 static void jsonString(char *p, pf print, void *info)
756 {
757     unsigned char c, buf[BUFSIZ];
758     agxbuf xb;
759 
760     agxbinit(&xb, BUFSIZ, buf);
761     agxbputc(&xb, '"');
762     while ((c = *p++)) {
763 	if (c == '"') agxbput("\\\"", &xb);
764 	else if (c == '\\') agxbput("\\\\", &xb);
765 	/* else if (c > 127) handle UTF-8 */
766 	else agxbputc(&xb, c);
767     }
768     agxbputc(&xb, '"');
769     print(agxbuse(&xb), info);
770     agxbfree(&xb);
771 }
772 
jsonXDot_Op(xdot_op * op,pf print,void * info,int more)773 static void jsonXDot_Op(xdot_op * op, pf print, void *info, int more)
774 {
775     agxbuf xb;
776     unsigned char buf[BUFSIZ];
777 
778     agxbinit (&xb, BUFSIZ, buf);
779     switch (op->kind) {
780     case xd_filled_ellipse:
781 	print("{E : ", info);
782 	jsonRect(&op->u.ellipse, print, info);
783 	break;
784     case xd_unfilled_ellipse:
785 	print("{e : ", info);
786 	jsonRect(&op->u.ellipse, print, info);
787 	break;
788     case xd_filled_polygon:
789 	print("{P : ", info);
790 	jsonPolyline(&op->u.polygon, print, info);
791 	break;
792     case xd_unfilled_polygon:
793 	print("{p : ", info);
794 	jsonPolyline(&op->u.polygon, print, info);
795 	break;
796     case xd_filled_bezier:
797 	print("{b : ", info);
798 	jsonPolyline(&op->u.bezier, print, info);
799 	break;
800     case xd_unfilled_bezier:
801 	print("{B : ", info);
802 	jsonPolyline(&op->u.bezier, print, info);
803 	break;
804     case xd_pen_color:
805 	print("{c : ", info);
806 	jsonString(op->u.color, print, info);
807 	break;
808     case xd_grad_pen_color:
809 	print("{c : ", info);
810 	toGradString (&xb, &op->u.grad_color);
811 	jsonString(agxbuse(&xb), print, info);
812 	break;
813     case xd_fill_color:
814 	print("{C : ", info);
815 	jsonString(op->u.color, print, info);
816 	break;
817     case xd_grad_fill_color:
818 	print("{C : ", info);
819 	toGradString (&xb, &op->u.grad_color);
820 	jsonString(agxbuse(&xb), print, info);
821 	break;
822     case xd_polyline:
823 	print("{L :", info);
824 	jsonPolyline(&op->u.polyline, print, info);
825 	break;
826     case xd_text:
827 	print("{T : [", info);
828 	printInt(op->u.text.x, print, info);
829 	print(",", info);
830 	printInt(op->u.text.y, print, info);
831 	print(",", info);
832 	printAlign(op->u.text.align, print, info);
833 	print(",", info);
834 	printInt(op->u.text.width, print, info);
835 	print(",", info);
836 	jsonString(op->u.text.text, print, info);
837 	print("]", info);
838 	break;
839     case xd_font:
840 	print("{F : [", info);
841 	op->kind = xd_font;
842 	printFloat(op->u.font.size, print, info, 1);
843 	print(",", info);
844 	jsonString(op->u.font.name, print, info);
845 	print("]", info);
846 	break;
847     case xd_fontchar:
848 	print("{t : ", info);
849 	printInt(op->u.fontchar, print, info);
850 	break;
851     case xd_style:
852 	print("{S : ", info);
853 	jsonString(op->u.style, print, info);
854 	break;
855     case xd_image:
856 	print("{I : [", info);
857 	jsonRect(&op->u.image.pos, print, info);
858 	print(",", info);
859 	jsonString(op->u.image.name, print, info);
860 	print("]", info);
861 	break;
862     }
863     if (more)
864 	print("},\n", info);
865     else
866 	print("}\n", info);
867     agxbfree (&xb);
868 }
869 
_printXDot(xdot * x,pf print,void * info,print_op ofn)870 static void _printXDot(xdot * x, pf print, void *info, print_op ofn)
871 {
872     int i;
873     xdot_op *op;
874     char *base = (char *) (x->ops);
875     for (i = 0; i < x->cnt; i++) {
876 	op = (xdot_op *) (base + i * x->sz);
877 	ofn(op, print, info, (i < x->cnt - 1));
878     }
879 }
880 
sprintXDot(xdot * x)881 char *sprintXDot(xdot * x)
882 {
883     char *s;
884     unsigned char buf[BUFSIZ];
885     agxbuf xb;
886     agxbinit(&xb, BUFSIZ, buf);
887     _printXDot(x, (pf) agxbput, &xb, printXDot_Op);
888     s = strdup(agxbuse(&xb));
889     agxbfree(&xb);
890 
891     return s;
892 }
893 
fprintXDot(FILE * fp,xdot * x)894 void fprintXDot(FILE * fp, xdot * x)
895 {
896     _printXDot(x, (pf) fputs, fp, printXDot_Op);
897 }
898 
jsonXDot(FILE * fp,xdot * x)899 void jsonXDot(FILE * fp, xdot * x)
900 {
901     fputs ("[\n", fp);
902     _printXDot(x, (pf) fputs, fp, jsonXDot_Op);
903     fputs ("]\n", fp);
904 }
905 
freeXOpData(xdot_op * x)906 static void freeXOpData(xdot_op * x)
907 {
908     switch (x->kind) {
909     case xd_filled_polygon:
910     case xd_unfilled_polygon:
911 	free(x->u.polyline.pts);
912 	break;
913     case xd_filled_bezier:
914     case xd_unfilled_bezier:
915 	free(x->u.polyline.pts);
916 	break;
917     case xd_polyline:
918 	free(x->u.polyline.pts);
919 	break;
920     case xd_text:
921 	free(x->u.text.text);
922 	break;
923     case xd_fill_color:
924     case xd_pen_color:
925 	free(x->u.color);
926 	break;
927     case xd_grad_fill_color:
928     case xd_grad_pen_color:
929 	freeXDotColor (&x->u.grad_color);
930 	break;
931     case xd_font:
932 	free(x->u.font.name);
933 	break;
934     case xd_style:
935 	free(x->u.style);
936 	break;
937     case xd_image:
938 	free(x->u.image.name);
939 	break;
940     default:
941 	break;
942     }
943 }
944 
freeXDot(xdot * x)945 void freeXDot (xdot * x)
946 {
947     int i;
948     xdot_op *op;
949     char *base;
950     freefunc_t ff = x->freefunc;
951 
952     if (!x) return;
953     base = (char *) (x->ops);
954     for (i = 0; i < x->cnt; i++) {
955 	op = (xdot_op *) (base + i * x->sz);
956 	if (ff) ff (op);
957 	freeXOpData(op);
958     }
959     free(base);
960     free(x);
961 }
962 
statXDot(xdot * x,xdot_stats * sp)963 int statXDot (xdot* x, xdot_stats* sp)
964 {
965     int i;
966     xdot_op *op;
967     char *base;
968 
969     if (!x || !sp) return 1;
970     memset(sp, 0, sizeof(xdot_stats));
971     sp->cnt = x->cnt;
972     base = (char *) (x->ops);
973     for (i = 0; i < x->cnt; i++) {
974 	op = (xdot_op *) (base + i * x->sz);
975  	switch (op->kind) {
976 	case xd_filled_ellipse:
977 	case xd_unfilled_ellipse:
978 	    sp->n_ellipse++;
979 	    break;
980 	case xd_filled_polygon:
981 	case xd_unfilled_polygon:
982 	    sp->n_polygon++;
983 	    sp->n_polygon_pts += op->u.polygon.cnt;
984 	    break;
985 	case xd_filled_bezier:
986 	case xd_unfilled_bezier:
987 	    sp->n_bezier++;
988 	    sp->n_bezier_pts += op->u.bezier.cnt;
989 	    break;
990 	case xd_polyline:
991 	    sp->n_polyline++;
992 	    sp->n_polyline_pts += op->u.polyline.cnt;
993 	    break;
994 	case xd_text:
995 	    sp->n_text++;
996 	    break;
997 	case xd_image:
998 	    sp->n_image++;
999 	    break;
1000 	case xd_fill_color:
1001 	case xd_pen_color:
1002 	    sp->n_color++;
1003 	    break;
1004 	case xd_grad_fill_color:
1005 	case xd_grad_pen_color:
1006 	    sp->n_gradcolor++;
1007 	    break;
1008         case xd_font:
1009 	    sp->n_font++;
1010 	    break;
1011         case xd_fontchar:
1012 	    sp->n_fontchar++;
1013 	    break;
1014 	case xd_style:
1015 	    sp->n_style++;
1016 	    break;
1017 	default :
1018 	    break;
1019 	}
1020     }
1021 
1022     return 0;
1023 }
1024 
1025 xdot_grad_type
colorType(char * cp)1026 colorType (char* cp)
1027 {
1028     xdot_grad_type rv;
1029 
1030     switch (*cp) {
1031     case '[' :
1032 	rv = xd_linear;
1033 	break;
1034     case '(' :
1035 	rv = xd_radial;
1036 	break;
1037     default :
1038 	rv = xd_none;
1039 	break;
1040     }
1041     return rv;
1042 }
1043 
1044 #define CHK1(s) if(!s){free(stops);return NULL;}
1045 
1046 /* radGradient:
1047  * Parse radial gradient spec
1048  * Return NULL on failure.
1049  */
1050 static char*
radGradient(char * cp,xdot_color * clr)1051 radGradient (char* cp, xdot_color* clr)
1052 {
1053     char* s = cp;
1054     int i;
1055     double d;
1056     xdot_color_stop* stops = NULL;
1057 
1058     clr->type = xd_radial;
1059     s = parseReal(s, &clr->u.ring.x0);
1060     CHK1(s);
1061     s = parseReal(s, &clr->u.ring.y0);
1062     CHK1(s);
1063     s = parseReal(s, &clr->u.ring.r0);
1064     CHK1(s);
1065     s = parseReal(s, &clr->u.ring.x1);
1066     CHK1(s);
1067     s = parseReal(s, &clr->u.ring.y1);
1068     CHK1(s);
1069     s = parseReal(s, &clr->u.ring.r1);
1070     CHK1(s);
1071     s = parseInt(s, &clr->u.ring.n_stops);
1072     CHK1(s);
1073 
1074     stops = N_NEW(clr->u.ring.n_stops,xdot_color_stop);
1075     for (i = 0; i < clr->u.ring.n_stops; i++) {
1076 	s = parseReal(s, &d);
1077 	CHK1(s);
1078 	stops[i].frac = d;
1079 	s = parseString(s, &stops[i].color);
1080 	CHK1(s);
1081     }
1082     clr->u.ring.stops = stops;
1083 
1084     return cp;
1085 }
1086 
1087 /* linGradient:
1088  * Parse linear gradient spec
1089  * Return NULL on failure.
1090  */
1091 static char*
linGradient(char * cp,xdot_color * clr)1092 linGradient (char* cp, xdot_color* clr)
1093 {
1094     char* s = cp;
1095     int i;
1096     double d;
1097     xdot_color_stop* stops = NULL;
1098 
1099     clr->type = xd_linear;
1100     s = parseReal(s, &clr->u.ling.x0);
1101     CHK1(s);
1102     s = parseReal(s, &clr->u.ling.y0);
1103     CHK1(s);
1104     s = parseReal(s, &clr->u.ling.x1);
1105     CHK1(s);
1106     s = parseReal(s, &clr->u.ling.y1);
1107     CHK1(s);
1108     s = parseInt(s, &clr->u.ling.n_stops);
1109     CHK1(s);
1110 
1111     stops = N_NEW(clr->u.ling.n_stops,xdot_color_stop);
1112     for (i = 0; i < clr->u.ling.n_stops; i++) {
1113 	s = parseReal(s, &d);
1114 	CHK1(s);
1115 	stops[i].frac = d;
1116 	s = parseString(s, &stops[i].color);
1117 	CHK1(s);
1118     }
1119     clr->u.ling.stops = stops;
1120 
1121     return cp;
1122 }
1123 
1124 /* parseXDotColor:
1125  * Parse xdot color spec: ordinary or gradient
1126  * The result is stored in clr.
1127  * Return NULL on failure.
1128  */
1129 char*
parseXDotColor(char * cp,xdot_color * clr)1130 parseXDotColor (char* cp, xdot_color* clr)
1131 {
1132     char c = *cp;
1133 
1134     switch (c) {
1135     case '[' :
1136 	return linGradient (cp+1, clr);
1137 	break;
1138     case '(' :
1139 	return radGradient (cp+1, clr);
1140 	break;
1141     case '#' :
1142     case '/' :
1143 	clr->type = xd_none;
1144 	clr->u.clr = cp;
1145 	return cp;
1146 	break;
1147     default :
1148 	if (isalnum(c)) {
1149 	    clr->type = xd_none;
1150 	    clr->u.clr = cp;
1151 	    return cp;
1152 	}
1153 	else
1154 	    return NULL;
1155     }
1156 }
1157 
freeXDotColor(xdot_color * cp)1158 void freeXDotColor (xdot_color* cp)
1159 {
1160     int i;
1161 
1162     if (cp->type == xd_linear) {
1163 	for (i = 0; i < cp->u.ling.n_stops; i++) {
1164 	    free (cp->u.ling.stops[i].color);
1165 	}
1166 	free (cp->u.ling.stops);
1167     }
1168     else if (cp->type == xd_radial) {
1169 	for (i = 0; i < cp->u.ring.n_stops; i++) {
1170 	    free (cp->u.ring.stops[i].color);
1171 	}
1172 	free (cp->u.ring.stops);
1173     }
1174 }
1175 
1176 #if 0
1177 static void execOp(xdot_op * op, int param)
1178 {
1179     op->drawfunc(op, param);
1180 }
1181 #endif
1182