Graphviz 16.0.1~dev.20260815.2250
Loading...
Searching...
No Matches
xdot.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12
13#include <stdarg.h>
14#include <stdlib.h>
15#include <string.h>
16#include <util/agxbuf.h>
17#include <util/alloc.h>
18#include <util/gv_ctype.h>
19#include <util/prisize_t.h>
20#include <util/unreachable.h>
21#include <xdot/xdot.h>
22
23/* the parse functions should return NULL on error */
24static char *parseReal(const char *s, double *fp) {
25 char *p;
26
27 const double d = strtod(s, &p);
28 if (p == s)
29 return 0;
30
31 *fp = d;
32 return p;
33}
34
35static char *parseInt(const char *s, int *ip) {
36 char *endp;
37
38 *ip = (int)strtol(s, &endp, 10);
39 if (s == endp)
40 return NULL;
41 return endp;
42}
43
44static char *parseUInt(const char *s, unsigned int *ip) {
45 char *endp;
46
47 *ip = (unsigned int)strtoul(s, &endp, 10);
48 if (s == endp)
49 return NULL;
50 return endp;
51}
52
53static char *parseRect(char *s, xdot_rect *rp) {
54 char *endp;
55
56 rp->x = strtod(s, &endp);
57 if (s == endp)
58 return NULL;
59 s = endp;
60
61 rp->y = strtod(s, &endp);
62 if (s == endp)
63 return NULL;
64 s = endp;
65
66 rp->w = strtod(s, &endp);
67 if (s == endp)
68 return NULL;
69 s = endp;
70
71 rp->h = strtod(s, &endp);
72 if (s == endp)
73 return NULL;
74 s = endp;
75
76 return s;
77}
78
79static char *parsePolyline(char *s, xdot_polyline *pp) {
80 unsigned i;
81 xdot_point *pts;
82 xdot_point *ps;
83 char *endp;
84
85 s = parseUInt(s, &i);
86 if (!s)
87 return NULL;
88 pts = ps = gv_calloc(i, sizeof(ps[0]));
89 pp->cnt = i;
90 for (i = 0; i < pp->cnt; i++) {
91 ps->x = strtod(s, &endp);
92 if (s == endp) {
93 free(pts);
94 return NULL;
95 } else {
96 s = endp;
97 }
98 ps->y = strtod(s, &endp);
99 if (s == endp) {
100 free(pts);
101 return NULL;
102 } else {
103 s = endp;
104 }
105 ps->z = 0;
106 ps++;
107 }
108 pp->pts = pts;
109 return s;
110}
111
112static char *parseString(char *s, char **sp) {
113 int i;
114 s = parseInt(s, &i);
115 if (!s || i <= 0)
116 return NULL;
117 while (*s && *s != '-')
118 s++;
119 if (*s) {
120 s++;
121 } else {
122 return NULL;
123 }
124
125 // The leading number we just read indicates the count of originating
126 // characters in the upcoming string. But the string may contain \-escaped
127 // characters. So the count alone does not tell us how many bytes we need to
128 // now read.
129 agxbuf c = {0};
130 int j = 0;
131 for (int accounted = 0; accounted < i; ++j) {
132 if (s[j] == '\0') {
133 agxbfree(&c);
134 return NULL;
135 }
136 agxbputc(&c, s[j]);
137 // only count this character if it was not an escape prefix
138 if (s[j] != '\\' || (j > 0 && s[j - 1] == '\\')) {
139 ++accounted;
140 }
141 }
142
143 *sp = agxbdisown(&c);
144 return s + j;
145}
146
147static char *parseAlign(char *s, xdot_align *ap) {
148 int i;
149 s = parseInt(s, &i);
150
151 if (i < 0)
152 *ap = xd_left;
153 else if (i > 0)
154 *ap = xd_right;
155 else
156 *ap = xd_center;
157 return s;
158}
159
160#define CHK(s) \
161 if (!s) { \
162 *error = 1; \
163 return 0; \
164 }
165
166static char *parseOp(xdot_op *op, char *s, drawfunc_t ops[], int *error) {
167 char *cs;
168 xdot_color clr;
169
170 *error = 0;
171 while (gv_isspace(*s))
172 s++;
173 switch (*s++) {
174 case 'E':
176 s = parseRect(s, &op->u.ellipse);
177 CHK(s);
178 if (ops)
179 op->drawfunc = ops[xop_ellipse];
180 break;
181
182 case 'e':
184 s = parseRect(s, &op->u.ellipse);
185 CHK(s);
186 if (ops)
187 op->drawfunc = ops[xop_ellipse];
188 break;
189
190 case 'P':
192 s = parsePolyline(s, &op->u.polygon);
193 CHK(s);
194 if (ops)
195 op->drawfunc = ops[xop_polygon];
196 break;
197
198 case 'p':
200 s = parsePolyline(s, &op->u.polygon);
201 CHK(s);
202 if (ops)
203 op->drawfunc = ops[xop_polygon];
204 break;
205
206 case 'b':
208 s = parsePolyline(s, &op->u.bezier);
209 CHK(s);
210 if (ops)
211 op->drawfunc = ops[xop_bezier];
212 break;
213
214 case 'B':
216 s = parsePolyline(s, &op->u.bezier);
217 CHK(s);
218 if (ops)
219 op->drawfunc = ops[xop_bezier];
220 break;
221
222 case 'c':
223 s = parseString(s, &cs);
224 CHK(s);
225 cs = parseXDotColor(cs, &clr);
226 CHK(cs);
227 if (clr.type == xd_none) {
228 op->kind = xd_pen_color;
229 op->u.color = clr.u.clr;
230 if (ops)
232 } else {
234 op->u.grad_color = clr;
235 if (ops)
237 }
238 break;
239
240 case 'C':
241 s = parseString(s, &cs);
242 CHK(s);
243 cs = parseXDotColor(cs, &clr);
244 CHK(cs);
245 if (clr.type == xd_none) {
246 op->kind = xd_fill_color;
247 op->u.color = clr.u.clr;
248 if (ops)
250 } else {
252 op->u.grad_color = clr;
253 if (ops)
255 }
256 break;
257
258 case 'L':
259 op->kind = xd_polyline;
260 s = parsePolyline(s, &op->u.polyline);
261 CHK(s);
262 if (ops)
264 break;
265
266 case 'T':
267 op->kind = xd_text;
268 s = parseReal(s, &op->u.text.x);
269 CHK(s);
270 s = parseReal(s, &op->u.text.y);
271 CHK(s);
272 s = parseAlign(s, &op->u.text.align);
273 CHK(s);
274 s = parseReal(s, &op->u.text.width);
275 CHK(s);
276 s = parseString(s, &op->u.text.text);
277 CHK(s);
278 if (ops)
279 op->drawfunc = ops[xop_text];
280 break;
281
282 case 'F':
283 op->kind = xd_font;
284 s = parseReal(s, &op->u.font.size);
285 CHK(s);
286 s = parseString(s, &op->u.font.name);
287 CHK(s);
288 if (ops)
289 op->drawfunc = ops[xop_font];
290 break;
291
292 case 'S':
293 op->kind = xd_style;
294 s = parseString(s, &op->u.style);
295 CHK(s);
296 if (ops)
297 op->drawfunc = ops[xop_style];
298 break;
299
300 case 'I':
301 op->kind = xd_image;
302 s = parseRect(s, &op->u.image.pos);
303 CHK(s);
304 s = parseString(s, &op->u.image.name);
305 CHK(s);
306 if (ops)
307 op->drawfunc = ops[xop_image];
308 break;
309
310 case 't':
311 op->kind = xd_fontchar;
312 s = parseUInt(s, &op->u.fontchar);
313 CHK(s);
314 if (ops)
316 break;
317
318 case '\0':
319 s = NULL;
320 break;
321
322 default:
323 *error = 1;
324 s = NULL;
325 break;
326 }
327 return s;
328}
329
330#define XDBSIZE 100
331
332/* parseXDotFOn:
333 * Parse and append additional xops onto a given xdot object.
334 * Return x.
335 */
336xdot *parseXDotFOn(char *s, drawfunc_t fns[], size_t sz, xdot *x) {
337 xdot_op op;
338 char *ops;
339 size_t oldsz, bufsz;
340 int error;
341
342 if (!s)
343 return x;
344
345 if (!x) {
346 x = gv_alloc(sizeof(*x));
347 if (sz <= sizeof(xdot_op))
348 sz = sizeof(xdot_op);
349
350 /* cnt, freefunc, ops, flags zeroed by gv_alloc */
351 x->sz = sz;
352 }
353 size_t initcnt = x->cnt;
354 sz = x->sz;
355
356 if (initcnt == 0) {
357 bufsz = XDBSIZE;
358 ops = gv_calloc(XDBSIZE, sz);
359 } else {
360 bufsz = initcnt + XDBSIZE;
361 ops = gv_recalloc(x->ops, initcnt, bufsz, sz);
362 }
363
364 while ((s = parseOp(&op, s, fns, &error))) {
365 if (x->cnt == bufsz) {
366 oldsz = bufsz;
367 bufsz *= 2;
368 ops = gv_recalloc(ops, oldsz, bufsz, sz);
369 }
370 *(xdot_op *)(ops + x->cnt * sz) = op;
371 x->cnt++;
372 }
373 if (error)
375 if (x->cnt) {
376 x->ops = gv_recalloc(ops, bufsz, x->cnt, sz);
377 } else {
378 free(ops);
379 free(x);
380 x = NULL;
381 }
382
383 return x;
384}
385
386xdot *parseXDotF(char *s, drawfunc_t fns[], size_t sz) {
387 return parseXDotFOn(s, fns, sz, NULL);
388}
389
390xdot *parseXDot(char *s) { return parseXDotF(s, NULL, 0); }
391
392typedef int (*pf)(void *, char *, ...);
393
394static void printRect(xdot_rect *r, pf print, void *info) {
395 agxbuf buf = {0};
396
397 agxbprint(&buf, " %.02f", r->x);
398 agxbuf_trim_zeros(&buf);
399 print(info, "%s", agxbuse(&buf));
400 agxbprint(&buf, " %.02f", r->y);
401 agxbuf_trim_zeros(&buf);
402 print(info, "%s", agxbuse(&buf));
403 agxbprint(&buf, " %.02f", r->w);
404 agxbuf_trim_zeros(&buf);
405 print(info, "%s", agxbuse(&buf));
406 agxbprint(&buf, " %.02f", r->h);
407 agxbuf_trim_zeros(&buf);
408 print(info, "%s", agxbuse(&buf));
409 agxbfree(&buf);
410}
411
412static void printPolyline(xdot_polyline *p, pf print, void *info) {
413 agxbuf buf = {0};
414
415 print(info, " %" PRISIZE_T, p->cnt);
416 for (size_t i = 0; i < p->cnt; i++) {
417 agxbprint(&buf, " %.02f", p->pts[i].x);
418 agxbuf_trim_zeros(&buf);
419 print(info, "%s", agxbuse(&buf));
420 agxbprint(&buf, " %.02f", p->pts[i].y);
421 agxbuf_trim_zeros(&buf);
422 print(info, "%s", agxbuse(&buf));
423 }
424 agxbfree(&buf);
425}
426
427static void printString(char *p, pf print, void *info) {
428 print(info, " %" PRISIZE_T " -%s", strlen(p), p);
429}
430
431static void printFloat(double f, pf print, void *info, int space) {
432 agxbuf buf = {0};
433
434 if (space)
435 agxbprint(&buf, " %.02f", f);
436 else
437 agxbprint(&buf, "%.02f", f);
438 agxbuf_trim_zeros(&buf);
439 print(info, "%s", agxbuse(&buf));
440 agxbfree(&buf);
441}
442
443static void printAlign(xdot_align a, pf print, void *info) {
444 switch (a) {
445 case xd_left:
446 print(info, " -1");
447 break;
448 case xd_right:
449 print(info, " 1");
450 break;
451 case xd_center:
452 print(info, " 0");
453 break;
454 default:
455 UNREACHABLE();
456 }
457}
458
460static int pf_agxbprint(void *xb, char *fmt, ...) {
461 va_list ap;
462 va_start(ap, fmt);
463 const int result = vagxbprint(xb, fmt, ap);
464 va_end(ap);
465 return result;
466}
467
468static void toGradString(agxbuf *xb, xdot_color *cp) {
469 unsigned n_stops;
470 xdot_color_stop *stops;
471
472 if (cp->type == xd_linear) {
473 agxbputc(xb, '[');
474 printFloat(cp->u.ling.x0, pf_agxbprint, xb, 0);
475 printFloat(cp->u.ling.y0, pf_agxbprint, xb, 1);
476 printFloat(cp->u.ling.x1, pf_agxbprint, xb, 1);
477 printFloat(cp->u.ling.y1, pf_agxbprint, xb, 1);
478 n_stops = cp->u.ling.n_stops;
479 stops = cp->u.ling.stops;
480 } else {
481 agxbputc(xb, '(');
482 printFloat(cp->u.ring.x0, pf_agxbprint, xb, 0);
483 printFloat(cp->u.ring.y0, pf_agxbprint, xb, 1);
484 printFloat(cp->u.ring.r0, pf_agxbprint, xb, 1);
485 printFloat(cp->u.ring.x1, pf_agxbprint, xb, 1);
486 printFloat(cp->u.ring.y1, pf_agxbprint, xb, 1);
487 printFloat(cp->u.ring.r1, pf_agxbprint, xb, 1);
488 n_stops = cp->u.ring.n_stops;
489 stops = cp->u.ring.stops;
490 }
491 agxbprint(xb, " %u", n_stops);
492 for (unsigned i = 0; i < n_stops; i++) {
493 printFloat(stops[i].frac, pf_agxbprint, xb, 1);
494 printString(stops[i].color, pf_agxbprint, xb);
495 }
496
497 if (cp->type == xd_linear)
498 agxbputc(xb, ']');
499 else
500 agxbputc(xb, ')');
501}
502
503typedef void (*print_op)(xdot_op *op, pf print, void *info, int more);
504
505static void printXDot_Op(xdot_op *op, pf print, void *info, int more) {
506 agxbuf xb = {0};
507 switch (op->kind) {
509 print(info, "E");
510 printRect(&op->u.ellipse, print, info);
511 break;
513 print(info, "e");
514 printRect(&op->u.ellipse, print, info);
515 break;
517 print(info, "P");
519 break;
521 print(info, "p");
523 break;
524 case xd_filled_bezier:
525 print(info, "b");
527 break;
529 print(info, "B");
531 break;
532 case xd_pen_color:
533 print(info, "c");
535 break;
537 print(info, "c");
538 toGradString(&xb, &op->u.grad_color);
540 break;
541 case xd_fill_color:
542 print(info, "C");
544 break;
546 print(info, "C");
547 toGradString(&xb, &op->u.grad_color);
549 break;
550 case xd_polyline:
551 print(info, "L");
553 break;
554 case xd_text:
555 print(info, "T %.f %.f", op->u.text.x, op->u.text.y);
557 print(info, " %.f", op->u.text.width);
559 break;
560 case xd_font:
561 print(info, "F");
562 printFloat(op->u.font.size, print, info, 1);
564 break;
565 case xd_fontchar:
566 print(info, "t %u", op->u.fontchar);
567 break;
568 case xd_style:
569 print(info, "S");
571 break;
572 case xd_image:
573 print(info, "I");
574 printRect(&op->u.image.pos, print, info);
576 break;
577 default: // invalid type; ignore
578 break;
579 }
580 if (more)
581 print(info, " ");
582 agxbfree(&xb);
583}
584
585static void jsonRect(xdot_rect *r, pf print, void *info) {
586 print(info, "[%.06f,%.06f,%.06f,%.06f]", r->x, r->y, r->w, r->h);
587}
588
589static void jsonPolyline(xdot_polyline *p, pf print, void *info) {
590 print(info, "[");
591 for (size_t i = 0; i < p->cnt; i++) {
592 print(info, "%.06f,%.06f", p->pts[i].x, p->pts[i].y);
593 if (i < p->cnt - 1)
594 print(info, ",");
595 }
596 print(info, "]");
597}
598
599static void jsonString(char *p, pf print, void *info) {
600 char c;
601
602 print(info, "\"");
603 while ((c = *p++)) {
604 if (c == '"')
605 print(info, "\\\"");
606 else if (c == '\\')
607 print(info, "\\\\");
608 else
609 print(info, "%c", c);
610 }
611 print(info, "\"");
612}
613
614static void jsonXDot_Op(xdot_op *op, pf print, void *info, int more) {
615 agxbuf xb = {0};
616 switch (op->kind) {
618 print(info, "{\"E\" : ");
619 jsonRect(&op->u.ellipse, print, info);
620 break;
622 print(info, "{\"e\" : ");
623 jsonRect(&op->u.ellipse, print, info);
624 break;
626 print(info, "{\"P\" : ");
628 break;
630 print(info, "{\"p\" : ");
632 break;
633 case xd_filled_bezier:
634 print(info, "{\"b\" : ");
636 break;
638 print(info, "{\"B\" : ");
640 break;
641 case xd_pen_color:
642 print(info, "{\"c\" : ");
643 jsonString(op->u.color, print, info);
644 break;
646 print(info, "{\"c\" : ");
647 toGradString(&xb, &op->u.grad_color);
649 break;
650 case xd_fill_color:
651 print(info, "{\"C\" : ");
652 jsonString(op->u.color, print, info);
653 break;
655 print(info, "{\"C\" : ");
656 toGradString(&xb, &op->u.grad_color);
658 break;
659 case xd_polyline:
660 print(info, "{\"L\" :");
662 break;
663 case xd_text:
664 print(info, "{\"T\" : [ %.f, %.f,", op->u.text.x, op->u.text.y);
666 print(info, ", %.f,", op->u.text.width);
668 print(info, "]");
669 break;
670 case xd_font:
671 print(info, "{\"F\" : [");
672 op->kind = xd_font;
673 printFloat(op->u.font.size, print, info, 1);
674 print(info, ",");
676 print(info, "]");
677 break;
678 case xd_fontchar:
679 print(info, "{\"t\" : %u", op->u.fontchar);
680 break;
681 case xd_style:
682 print(info, "{\"S\" : ");
683 jsonString(op->u.style, print, info);
684 break;
685 case xd_image:
686 print(info, "{\"I\" : [");
687 jsonRect(&op->u.image.pos, print, info);
688 print(info, ",");
690 print(info, "]");
691 break;
692 default: // invalid type; ignore
693 break;
694 }
695 if (more)
696 print(info, "},\n");
697 else
698 print(info, "}\n");
699 agxbfree(&xb);
700}
701
702static void printXDot(xdot *x, pf print, void *info, print_op ofn) {
703 char *base = (char *)x->ops;
704 for (size_t i = 0; i < x->cnt; i++) {
705 xdot_op *const op = (xdot_op *)(base + i * x->sz);
706 ofn(op, print, info, i < x->cnt - 1);
707 }
708}
709
710char *sprintXDot(xdot *x) {
711 agxbuf xb = {0};
713 return agxbdisown(&xb);
714}
715
717static int pf_fprintf(void *stream, char *format, ...) {
718 va_list ap;
719 va_start(ap, format);
720 const int r = vfprintf(stream, format, ap);
721 va_end(ap);
722 return r;
723}
724
725void fprintXDot(FILE *fp, xdot *x) {
727}
728
729void jsonXDot(FILE *fp, xdot *x) {
730 fputs("[\n", fp);
732 fputs("]\n", fp);
733}
734
735static void freeXOpData(xdot_op *x) {
736 switch (x->kind) {
739 free(x->u.polyline.pts);
740 break;
741 case xd_filled_bezier:
743 free(x->u.polyline.pts);
744 break;
745 case xd_polyline:
746 free(x->u.polyline.pts);
747 break;
748 case xd_text:
749 free(x->u.text.text);
750 break;
751 case xd_fill_color:
752 case xd_pen_color:
753 free(x->u.color);
754 break;
758 break;
759 case xd_font:
760 free(x->u.font.name);
761 break;
762 case xd_style:
763 free(x->u.style);
764 break;
765 case xd_image:
766 free(x->u.image.name);
767 break;
768 default:
769 break;
770 }
771}
772
773void freeXDot(xdot *x) {
774 if (!x)
775 return;
776 freefunc_t ff = x->freefunc;
777 char *const base = (char *)x->ops;
778 for (size_t i = 0; i < x->cnt; i++) {
779 void *const op = base + i * x->sz;
780 if (ff)
781 ff(op);
782 freeXOpData(op);
783 }
784 free(base);
785 free(x);
786}
787
789 if (!x || !sp)
790 return 1;
791 *sp = (xdot_stats){0};
792 sp->cnt = x->cnt;
793 char *const base = (char *)x->ops;
794 for (size_t i = 0; i < x->cnt; i++) {
795 xdot_op *const op = (xdot_op *)(base + i * x->sz);
796 switch (op->kind) {
799 sp->n_ellipse++;
800 break;
803 sp->n_polygon++;
804 sp->n_polygon_pts += op->u.polygon.cnt;
805 break;
806 case xd_filled_bezier:
808 sp->n_bezier++;
809 sp->n_bezier_pts += op->u.bezier.cnt;
810 break;
811 case xd_polyline:
812 sp->n_polyline++;
813 sp->n_polyline_pts += op->u.polyline.cnt;
814 break;
815 case xd_text:
816 sp->n_text++;
817 break;
818 case xd_image:
819 sp->n_image++;
820 break;
821 case xd_fill_color:
822 case xd_pen_color:
823 sp->n_color++;
824 break;
827 sp->n_gradcolor++;
828 break;
829 case xd_font:
830 sp->n_font++;
831 break;
832 case xd_fontchar:
833 sp->n_fontchar++;
834 break;
835 case xd_style:
836 sp->n_style++;
837 break;
838 default:
839 break;
840 }
841 }
842
843 return 0;
844}
845
846#define CHK1(s) \
847 if (!s) { \
848 free(stops); \
849 return NULL; \
850 }
851
852/* radGradient:
853 * Parse radial gradient spec
854 * Return NULL on failure.
855 */
856static char *radGradient(char *cp, xdot_color *clr) {
857 char *s = cp;
858 xdot_color_stop *stops = NULL;
859
860 clr->type = xd_radial;
861 s = parseReal(s, &clr->u.ring.x0);
862 CHK1(s);
863 s = parseReal(s, &clr->u.ring.y0);
864 CHK1(s);
865 s = parseReal(s, &clr->u.ring.r0);
866 CHK1(s);
867 s = parseReal(s, &clr->u.ring.x1);
868 CHK1(s);
869 s = parseReal(s, &clr->u.ring.y1);
870 CHK1(s);
871 s = parseReal(s, &clr->u.ring.r1);
872 CHK1(s);
873 s = parseUInt(s, &clr->u.ring.n_stops);
874 CHK1(s);
875
876 stops = gv_calloc(clr->u.ring.n_stops, sizeof(stops[0]));
877 for (unsigned int i = 0; i < clr->u.ring.n_stops; i++) {
878 s = parseReal(s, &stops[i].frac);
879 CHK1(s);
880 s = parseString(s, &stops[i].color);
881 CHK1(s);
882 }
883 clr->u.ring.stops = stops;
884
885 return cp;
886}
887
888/* linGradient:
889 * Parse linear gradient spec
890 * Return NULL on failure.
891 */
892static char *linGradient(char *cp, xdot_color *clr) {
893 char *s = cp;
894 xdot_color_stop *stops = NULL;
895
896 clr->type = xd_linear;
897 s = parseReal(s, &clr->u.ling.x0);
898 CHK1(s);
899 s = parseReal(s, &clr->u.ling.y0);
900 CHK1(s);
901 s = parseReal(s, &clr->u.ling.x1);
902 CHK1(s);
903 s = parseReal(s, &clr->u.ling.y1);
904 CHK1(s);
905 s = parseUInt(s, &clr->u.ling.n_stops);
906 CHK1(s);
907
908 stops = gv_calloc(clr->u.ling.n_stops, sizeof(stops[0]));
909 for (unsigned i = 0; i < clr->u.ling.n_stops; i++) {
910 s = parseReal(s, &stops[i].frac);
911 CHK1(s);
912 s = parseString(s, &stops[i].color);
913 CHK1(s);
914 }
915 clr->u.ling.stops = stops;
916
917 return cp;
918}
919
920/* parseXDotColor:
921 * Parse xdot color spec: ordinary or gradient
922 * The result is stored in clr.
923 * Return NULL on failure.
924 */
925char *parseXDotColor(char *cp, xdot_color *clr) {
926 char c = *cp;
927
928 switch (c) {
929 case '[':
930 return linGradient(cp + 1, clr);
931 case '(':
932 return radGradient(cp + 1, clr);
933 case '#':
934 case '/':
935 clr->type = xd_none;
936 clr->u.clr = cp;
937 return cp;
938 default:
939 if (gv_isalnum(c)) {
940 clr->type = xd_none;
941 clr->u.clr = cp;
942 return cp;
943 }
944 return NULL;
945 }
946}
947
949 if (cp->type == xd_linear) {
950 for (unsigned i = 0; i < cp->u.ling.n_stops; i++) {
951 free(cp->u.ling.stops[i].color);
952 }
953 free(cp->u.ling.stops);
954 } else if (cp->type == xd_radial) {
955 for (unsigned i = 0; i < cp->u.ring.n_stops; i++) {
956 free(cp->u.ring.stops[i].color);
957 }
958 free(cp->u.ring.stops);
959 }
960}
961
Dynamically expanding string buffers.
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:97
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:252
static int vagxbprint(agxbuf *xb, const char *fmt, va_list ap)
vprintf-style output to an agxbuf
Definition agxbuf.h:181
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:325
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:295
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:345
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
static void print(Excc_t *cc, Exnode_t *exnode)
Definition excc.c:109
void free(void *)
node NULL
Definition grammar.y:181
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:204
replacements for ctype.h functions
static bool gv_isalnum(int c)
Definition gv_ctype.h:43
static bool gv_isspace(int c)
Definition gv_ctype.h:55
static void color(Agraph_t *g)
Definition gvcolor.c:118
agxbuf_trim_zeros(xb)
GVIO_API const char * format
Definition gvio.h:51
table Syntax error
Definition htmlparse.y:288
#define PRISIZE_T
Definition prisize_t.h:25
static Ppoint_t * ops
Definition route.c:35
char * style
Definition xdot.h:158
union _xdot_op::@106 u
xdot_font font
Definition xdot.h:157
xdot_polyline polygon
Definition xdot.h:150
xdot_rect ellipse
Definition xdot.h:149
xdot_image image
Definition xdot.h:154
char * color
Definition xdot.h:155
unsigned int fontchar
Definition xdot.h:159
xdot_kind kind
Definition xdot.h:147
xdot_text text
Definition xdot.h:153
xdot_polyline bezier
Definition xdot.h:152
xdot_polyline polyline
Definition xdot.h:151
xdot_color grad_color
Definition xdot.h:156
drawfunc_t drawfunc
Definition xdot.h:161
char * color
Definition xdot.h:50
xdot_grad_type type
Definition xdot.h:68
xdot_linear_grad ling
Definition xdot.h:71
xdot_radial_grad ring
Definition xdot.h:72
char * clr
Definition xdot.h:70
union xdot_color::@105 u
double size
Definition xdot.h:104
char * name
Definition xdot.h:105
char * name
Definition xdot.h:100
xdot_rect pos
Definition xdot.h:99
double y1
Definition xdot.h:55
xdot_color_stop * stops
Definition xdot.h:57
double y0
Definition xdot.h:54
double x0
Definition xdot.h:54
unsigned n_stops
Definition xdot.h:56
double x1
Definition xdot.h:55
double x
Definition xdot.h:79
double z
Definition xdot.h:79
double y
Definition xdot.h:79
size_t cnt
Definition xdot.h:87
xdot_point * pts
Definition xdot.h:88
double r0
Definition xdot.h:61
double x0
Definition xdot.h:61
double y0
Definition xdot.h:61
double x1
Definition xdot.h:62
unsigned n_stops
Definition xdot.h:63
double y1
Definition xdot.h:62
double r1
Definition xdot.h:62
xdot_color_stop * stops
Definition xdot.h:64
double x
Definition xdot.h:83
double w
Definition xdot.h:83
double y
Definition xdot.h:83
double h
Definition xdot.h:83
size_t n_image
Definition xdot.h:187
size_t n_polygon_pts
Definition xdot.h:178
size_t cnt
Definition xdot.h:175
size_t n_text
Definition xdot.h:183
size_t n_style
Definition xdot.h:185
size_t n_polyline_pts
Definition xdot.h:180
size_t n_bezier
Definition xdot.h:181
size_t n_polygon
Definition xdot.h:177
size_t n_fontchar
Definition xdot.h:189
size_t n_font
Definition xdot.h:184
size_t n_polyline
Definition xdot.h:179
size_t n_color
Definition xdot.h:186
size_t n_gradcolor
Definition xdot.h:188
size_t n_ellipse
Definition xdot.h:176
size_t n_bezier_pts
Definition xdot.h:182
double width
Definition xdot.h:94
char * text
Definition xdot.h:95
double x
Definition xdot.h:92
xdot_align align
Definition xdot.h:93
double y
Definition xdot.h:92
Definition xdot.h:166
freefunc_t freefunc
Definition xdot.h:170
xdot_op * ops
Definition xdot.h:169
size_t cnt
Definition xdot.h:167
size_t sz
Definition xdot.h:168
int flags
Definition xdot.h:171
Definition grammar.c:90
#define UNREACHABLE()
Definition unreachable.h:30
static void printAlign(xdot_align a, pf print, void *info)
Definition xdot.c:443
void(* print_op)(xdot_op *op, pf print, void *info, int more)
Definition xdot.c:503
static void printFloat(double f, pf print, void *info, int space)
Definition xdot.c:431
static void jsonXDot_Op(xdot_op *op, pf print, void *info, int more)
Definition xdot.c:614
xdot * parseXDotFOn(char *s, drawfunc_t fns[], size_t sz, xdot *x)
Definition xdot.c:336
static char * linGradient(char *cp, xdot_color *clr)
Definition xdot.c:892
static void printRect(xdot_rect *r, pf print, void *info)
Definition xdot.c:394
int(* pf)(void *, char *,...)
Definition xdot.c:392
char * parseXDotColor(char *cp, xdot_color *clr)
Definition xdot.c:925
static char * parseString(char *s, char **sp)
Definition xdot.c:112
static void printXDot_Op(xdot_op *op, pf print, void *info, int more)
Definition xdot.c:505
static void jsonPolyline(xdot_polyline *p, pf print, void *info)
Definition xdot.c:589
static int pf_agxbprint(void *xb, char *fmt,...)
wrapper to translate pf calling convention to agxbprint
Definition xdot.c:460
#define CHK1(s)
Definition xdot.c:846
int statXDot(xdot *x, xdot_stats *sp)
Definition xdot.c:788
static void printString(char *p, pf print, void *info)
Definition xdot.c:427
static void printXDot(xdot *x, pf print, void *info, print_op ofn)
Definition xdot.c:702
void freeXDot(xdot *x)
Definition xdot.c:773
static char * parseRect(char *s, xdot_rect *rp)
Definition xdot.c:53
static char * parseReal(const char *s, double *fp)
Definition xdot.c:24
static char * parseAlign(char *s, xdot_align *ap)
Definition xdot.c:147
#define XDBSIZE
Definition xdot.c:330
static void freeXOpData(xdot_op *x)
Definition xdot.c:735
static void printPolyline(xdot_polyline *p, pf print, void *info)
Definition xdot.c:412
char * sprintXDot(xdot *x)
Definition xdot.c:710
static void jsonString(char *p, pf print, void *info)
Definition xdot.c:599
#define CHK(s)
Definition xdot.c:160
static char * parseUInt(const char *s, unsigned int *ip)
Definition xdot.c:44
void freeXDotColor(xdot_color *cp)
Definition xdot.c:948
static int pf_fprintf(void *stream, char *format,...)
wrapper to translate pf calling convention to fprintf
Definition xdot.c:717
void fprintXDot(FILE *fp, xdot *x)
Definition xdot.c:725
static char * parseOp(xdot_op *op, char *s, drawfunc_t ops[], int *error)
Definition xdot.c:166
xdot * parseXDotF(char *s, drawfunc_t fns[], size_t sz)
Definition xdot.c:386
xdot * parseXDot(char *s)
Definition xdot.c:390
static void jsonRect(xdot_rect *r, pf print, void *info)
Definition xdot.c:585
static void toGradString(agxbuf *xb, xdot_color *cp)
Definition xdot.c:468
static char * parsePolyline(char *s, xdot_polyline *pp)
Definition xdot.c:79
static char * radGradient(char *cp, xdot_color *clr)
Definition xdot.c:856
static char * parseInt(const char *s, int *ip)
Definition xdot.c:35
void jsonXDot(FILE *fp, xdot *x)
Definition xdot.c:729
parsing and deparsing of xdot operations
@ xd_none
Definition xdot.h:46
@ xd_radial
Definition xdot.h:46
@ xd_linear
Definition xdot.h:46
struct _xdot_op xdot_op
Definition xdot.h:142
#define XDOT_PARSE_ERROR
Definition xdot.h:164
void(* freefunc_t)(xdot_op *)
Definition xdot.h:144
@ xop_polygon
Definition xdot.h:129
@ xop_font
Definition xdot.h:135
@ xop_image
Definition xdot.h:137
@ xop_bezier
Definition xdot.h:130
@ xop_fill_color
Definition xdot.h:133
@ xop_text
Definition xdot.h:132
@ xop_fontchar
Definition xdot.h:139
@ xop_style
Definition xdot.h:136
@ xop_grad_color
Definition xdot.h:138
@ xop_pen_color
Definition xdot.h:134
@ xop_ellipse
Definition xdot.h:128
@ xop_polyline
Definition xdot.h:131
xdot_align
Definition xdot.h:76
@ xd_left
Definition xdot.h:76
@ xd_right
Definition xdot.h:76
@ xd_center
Definition xdot.h:76
void(* drawfunc_t)(xdot_op *, int)
Definition xdot.h:143
@ xd_filled_polygon
Definition xdot.h:111
@ xd_pen_color
Definition xdot.h:118
@ xd_unfilled_ellipse
Definition xdot.h:110
@ xd_fontchar
Definition xdot.h:124
@ xd_font
Definition xdot.h:119
@ xd_fill_color
Definition xdot.h:117
@ xd_unfilled_bezier
Definition xdot.h:114
@ xd_grad_fill_color
Definition xdot.h:122
@ xd_polyline
Definition xdot.h:115
@ xd_text
Definition xdot.h:116
@ xd_filled_ellipse
Definition xdot.h:109
@ xd_image
Definition xdot.h:121
@ xd_unfilled_polygon
Definition xdot.h:112
@ xd_grad_pen_color
Definition xdot.h:123
@ xd_filled_bezier
Definition xdot.h:113
@ xd_style
Definition xdot.h:120