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
21typedef 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
32static 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}
47static 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
72static 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 */
85static void agxbfree(agxbuf * xb)
86{
87 if (xb->dyna)
88 free(xb->buf);
89}
90
91/* the parse functions should return NULL on error */
92static 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
105static 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
122static 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
134static 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
144static 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
182static 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
215static 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
243static 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
259static 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 */
433xdot *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
491xdot *parseXDotF(char *s, drawfunc_t fns[], int sz)
492{
493 return parseXDotFOn (s, fns, sz, NULL);
494}
495
496xdot *parseXDot(char *s)
497{
498 return parseXDotF(s, 0, 0);
499}
500
501typedef void (*pf) (char *, void *);
502
503/* trim:
504 * Trailing zeros are removed and decimal point, if possible.
505 */
506static 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
523static 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
541static 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
558static 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
567static 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
575static 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
587static 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
602static void
603gradprint (char* s, void* v)
604{
605 agxbput(s, (agxbuf*)v);
606}
607
608static void
609toGradString (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
646typedef void (*print_op)(xdot_op * op, pf print, void *info, int more);
647
648static 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
733static 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
741static 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
755static 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
773static 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
870static 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
881char *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
894void fprintXDot(FILE * fp, xdot * x)
895{
896 _printXDot(x, (pf) fputs, fp, printXDot_Op);
897}
898
899void jsonXDot(FILE * fp, xdot * x)
900{
901 fputs ("[\n", fp);
902 _printXDot(x, (pf) fputs, fp, jsonXDot_Op);
903 fputs ("]\n", fp);
904}
905
906static 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
945void 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
963int 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
1025xdot_grad_type
1026colorType (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 */
1050static char*
1051radGradient (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 */
1091static char*
1092linGradient (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 */
1129char*
1130parseXDotColor (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
1158void 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
1177static void execOp(xdot_op * op, int param)
1178{
1179 op->drawfunc(op, param);
1180}
1181#endif
1182