Graphviz 16.1.1~dev.20261104.1411
Loading...
Searching...
No Matches
utils.c
Go to the documentation of this file.
1
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 v2.0
7 * which accompanies this distribution, and is available at
8 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
9 *
10 * Contributors: Details at https://graphviz.org
11 *************************************************************************/
12
13#include "config.h"
14
15#include <common/entities.h>
16#include <common/geomprocs.h>
17#include <common/htmltable.h>
18#include <common/render.h>
19#include <float.h>
20#include <gvc/gvc.h>
21#include <limits.h>
22#include <math.h>
23#include <stdatomic.h>
24#include <stdbool.h>
25#include <stddef.h>
26#include <stdint.h>
27#include <unistd.h>
28#include <util/agxbuf.h>
29#include <util/alloc.h>
30#include <util/gv_ctype.h>
31#include <util/gv_math.h>
32#include <util/list.h>
33#include <util/path.h>
34#include <util/startswith.h>
35#include <util/strcasecmp.h>
36#include <util/streq.h>
37#include <util/strview.h>
38#include <util/tokenize.h>
39
40int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum) {
41 if (attr == NULL)
42 return defaultValue;
43 char *p = agxget(obj, attr);
44 if (!p || p[0] == '\0')
45 return defaultValue;
46 char *endp;
47 long rv = strtol(p, &endp, 10);
48 if (p == endp || rv > INT_MAX)
49 return defaultValue; /* invalid int format */
50 if (rv < minimum)
51 return minimum;
52 return (int)rv;
53}
54
55double late_double(void *obj, attrsym_t *attr, double defaultValue,
56 double minimum) {
57 if (!attr || !obj)
58 return defaultValue;
59 char *p = agxget(obj, attr);
60 if (!p || p[0] == '\0')
61 return defaultValue;
62 char *endp;
63 double rv = strtod(p, &endp);
64 if (p == endp)
65 return defaultValue; /* invalid double format */
66 if (rv < minimum)
67 return minimum;
68 return rv;
69}
70
79 if (PSinputscale > 0)
80 return PSinputscale; /* command line flag prevails */
81 double d = late_double(g, agfindgraphattr(g, "inputscale"), -1, 0);
83 return POINTS_PER_INCH;
84 return d;
85}
86
87char *late_string(void *obj, attrsym_t *attr, char *defaultValue) {
88 if (!attr || !obj)
89 return defaultValue;
90 return agxget(obj, attr);
91}
92
93char *late_nnstring(void *obj, attrsym_t *attr, char *defaultValue) {
94 char *rv = late_string(obj, attr, defaultValue);
95 if (!rv || (rv[0] == '\0'))
96 return defaultValue;
97 return rv;
98}
99
100bool late_bool(void *obj, attrsym_t *attr, bool defaultValue) {
101 if (attr == NULL)
102 return defaultValue;
103
104 return mapbool(agxget(obj, attr));
105}
106
108 while (ND_UF_parent(n) && ND_UF_parent(n) != n) {
111 n = ND_UF_parent(n);
112 }
113 return n;
114}
115
117 if (u == v)
118 return u;
119 if (ND_UF_parent(u) == NULL) {
120 ND_UF_parent(u) = u;
121 ND_UF_size(u) = 1;
122 } else
123 u = UF_find(u);
124 if (ND_UF_parent(v) == NULL) {
125 ND_UF_parent(v) = v;
126 ND_UF_size(v) = 1;
127 } else
128 v = UF_find(v);
129 /* if we have two copies of the same node, their union is just that node */
130 if (u == v)
131 return u;
132 if (ND_id(u) > ND_id(v)) {
133 ND_UF_parent(u) = v;
134 ND_UF_size(v) += ND_UF_size(u);
135 } else {
136 ND_UF_parent(v) = u;
137 ND_UF_size(u) += ND_UF_size(v);
138 v = u;
139 }
140 return v;
141}
142
144 ND_UF_size(u) = 1;
145 ND_UF_parent(u) = NULL;
146 ND_ranktype(u) = NORMAL;
147}
148
150 assert(u == UF_find(u));
151 ND_UF_parent(u) = v;
152 ND_UF_size(v) += ND_UF_size(u);
153}
154
156 pointf r;
157
158 r.x = POINTS_PER_INCH * ND_pos(n)[0];
159 r.y = POINTS_PER_INCH * ND_pos(n)[1];
160 return r;
161}
162
163/* from Glassner's Graphics Gems */
164#define W_DEGREE 5
165
166/*
167 * Evaluate a Bézier curve at a particular parameter value
168 * Fill in control points for resulting sub-curves if "Left" and
169 * "Right" are non-null.
170 *
171 */
172pointf Bezier(const pointf *V, double t, pointf *Left, pointf *Right) {
173 const int degree = 3;
174 int i, j; /* Index variables */
175 pointf Vtemp[W_DEGREE + 1][W_DEGREE + 1];
176
177 /* Copy control points */
178 for (j = 0; j <= degree; j++) {
179 Vtemp[0][j] = V[j];
180 }
181
182 /* Triangle computation */
183 for (i = 1; i <= degree; i++) {
184 for (j = 0; j <= degree - i; j++) {
185 Vtemp[i][j].x = (1.0 - t) * Vtemp[i - 1][j].x + t * Vtemp[i - 1][j + 1].x;
186 Vtemp[i][j].y = (1.0 - t) * Vtemp[i - 1][j].y + t * Vtemp[i - 1][j + 1].y;
187 }
188 }
189
190 if (Left != NULL)
191 for (j = 0; j <= degree; j++)
192 Left[j] = Vtemp[j][0];
193 if (Right != NULL)
194 for (j = 0; j <= degree; j++)
195 Right[j] = Vtemp[degree - j][j];
196
197 return Vtemp[degree][0];
198}
199
200/* safefile:
201 * Check to make sure it is okay to read in files.
202 * It returns NULL if the filename is trivial.
203 *
204 * If the application has set the SERVER_NAME environment variable,
205 * this indicates it is web-active.
206 *
207 * If filename contains multiple components, the user is
208 * warned, once, that everything to the left is ignored.
209 *
210 * For non-server applications, we use the path list in Gvimagepath to
211 * resolve relative pathnames.
212 *
213 * N.B. safefile uses a fixed buffer, so functions using it should use the
214 * value immediately or make a copy.
215 */
216#ifdef _WIN32
217#define PATHSEP ";"
218#else
219#define PATHSEP ":"
220#endif
221
222typedef LIST(strview_t) strviews_t;
223
224static strviews_t mkDirlist(const char *list) {
225 strviews_t dirs = {0};
226
227 for (tok_t t = tok(list, PATHSEP); !tok_end(&t); tok_next(&t)) {
228 strview_t dir = tok_get(&t);
229 LIST_APPEND(&dirs, dir);
230 }
231 return dirs;
232}
233
234static char *findPath(const strviews_t dirs, const char *str) {
235 static agxbuf safefilename;
236
237 for (size_t i = 0; i < LIST_SIZE(&dirs); ++i) {
238 const strview_t d = LIST_GET(&dirs, i);
239 agxbprint(&safefilename, "%.*s%c%s", (int)d.size, d.data, PATH_SEPARATOR,
240 str);
241 char *filename = agxbuse(&safefilename);
242 if (access(filename, R_OK) == 0)
243 return filename;
244 }
245 return NULL;
246}
247
248const char *safefile(const char *filename) {
249 static atomic_flag onetime;
250
251 if (!filename || !filename[0])
252 return NULL;
253
254 if (HTTPServerEnVar) { /* If used as a server */
255 if (!atomic_flag_test_and_set(&onetime)) {
256 agwarningf("file loading is disabled because the environment contains "
257 "SERVER_NAME=\"%s\"\n",
259 }
260 return NULL;
261 }
262
263 if (Gvfilepath != NULL) {
264 const char *const pathlist = Gvfilepath;
265 strviews_t dirs = mkDirlist(pathlist);
266
267 const char *str = filename;
268 for (const char *sep = "/\\:"; *sep != '\0'; ++sep) {
269 const char *p = strrchr(str, *sep);
270 if (p != NULL) {
271 str = ++p;
272 }
273 }
274
275 const char *const ret = findPath(dirs, str);
276 LIST_FREE(&dirs);
277 return ret;
278 }
279
280 const char *const pathlist = Gvimagepath;
281 strviews_t dirs = {0};
282 if (pathlist && *pathlist)
283 dirs = mkDirlist(pathlist);
284
285 const char *ret;
286 if (*filename == PATH_SEPARATOR || LIST_IS_EMPTY(&dirs)) {
287 ret = filename;
288 } else {
289 ret = findPath(dirs, filename);
290 }
291
292 LIST_FREE(&dirs);
293 return ret;
294}
295
296int maptoken(char *p, char **name, int *val) {
297 char *q;
298
299 int i = 0;
300 for (; (q = name[i]) != 0; i++)
301 if (p && streq(p, q))
302 break;
303 return val[i];
304}
305
306bool mapBool(const char *p, bool defaultValue) {
307 if (!p || *p == '\0')
308 return defaultValue;
309 if (!strcasecmp(p, "false"))
310 return false;
311 if (!strcasecmp(p, "no"))
312 return false;
313 if (!strcasecmp(p, "true"))
314 return true;
315 if (!strcasecmp(p, "yes"))
316 return true;
317 if (gv_isdigit(*p))
318 return atoi(p) != 0;
319 return defaultValue;
320}
321
322bool mapbool(const char *p) { return mapBool(p, false); }
323
325 pointf pt2;
326
327 size_t besti = SIZE_MAX;
328 size_t bestj = SIZE_MAX;
329 double bestdist2 = DBL_MAX;
330 for (size_t i = 0; i < spl->size; i++) {
331 const bezier bz = spl->list[i];
332 for (size_t j = 0; j < bz.size; j++) {
333 const pointf b = bz.list[j];
334 const double d2 = DIST2(b, pt);
335 if (bestj == SIZE_MAX || d2 < bestdist2) {
336 besti = i;
337 bestj = j;
338 bestdist2 = d2;
339 }
340 }
341 }
342
343 const bezier bz = spl->list[besti];
344 /* Pick best Bézier. If bestj is the last point in the B-spline, decrement.
345 * Then set j to be the first point in the corresponding Bézier by dividing
346 * then multiplying be 3. Thus, 0,1,2 => 0; 3,4,5 => 3, etc.
347 */
348 if (bestj == bz.size - 1)
349 bestj--;
350 const size_t j = 3 * (bestj / 3);
351 const pointf c[] = {bz.list[j], bz.list[j + 1], bz.list[j + 2],
352 bz.list[j + 3]};
353 for (double low = 0, high = 1.0, dlow2 = DIST2(c[0], pt),
354 dhigh2 = DIST2(c[3], pt);
355 ;) {
356 const double t = (low + high) / 2.0;
357 pt2 = Bezier(c, t, NULL, NULL);
358 if (fabs(dlow2 - dhigh2) < 1.0)
359 break;
360 if (fabs(high - low) < .00001)
361 break;
362 if (dlow2 < dhigh2) {
363 high = t;
364 dhigh2 = DIST2(pt2, pt);
365 } else {
366 low = t;
367 dlow2 = DIST2(pt2, pt);
368 }
369 }
370 return pt2;
371}
372
373static int Tflag;
374void gvToggle(int s) {
375 (void)s;
376 Tflag = !Tflag;
377#if !defined(_WIN32)
378 signal(SIGUSR1, gvToggle);
379#endif
380}
381
382int test_toggle(void) { return Tflag; }
383
384struct fontinfo {
385 double fontsize;
386 char *fontname;
388};
389
391 struct fontinfo fi;
392 char *str;
396 str = agxget(n, N_label);
401 fi.fontsize, fi.fontname, fi.fontcolor);
402 if (N_xlabel && (str = agxget(n, N_xlabel)) && str[0]) {
403 ND_xlabel(n) = make_label(n, str, aghtmlstr(str), false, fi.fontsize,
404 fi.fontname, fi.fontcolor);
406 }
407
408 {
409 const int showboxes = imin(late_int(n, N_showboxes, 0, 0), UCHAR_MAX);
410 ND_showboxes(n) = (unsigned char)showboxes;
411 }
412 ND_shape(n)->fns->initfn(n);
413}
414
420
421static void initFontLabelEdgeAttr(edge_t *e, struct fontinfo *fi,
422 struct fontinfo *lfi) {
423 if (!fi->fontname)
424 initFontEdgeAttr(e, fi);
428}
429
431static bool noClip(edge_t *e, attrsym_t *sym) {
432 char *str;
433 bool rv = false;
434
435 if (sym) { /* mapbool isn't a good fit, because we want "" to mean true */
436 str = agxget(e, sym);
437 if (str && str[0])
438 rv = !mapbool(str);
439 else
440 rv = false;
441 }
442 return rv;
443}
444
445static port chkPort(port (*pf)(node_t *, char *, char *), node_t *n, char *s) {
446 port pt;
447 char *cp = NULL;
448 if (s)
449 cp = strchr(s, ':');
450 if (cp) {
451 *cp = '\0';
452 pt = pf(n, s, cp + 1);
453 *cp = ':';
454 pt.name = cp + 1;
455 } else {
456 pt = pf(n, s, NULL);
457 pt.name = s;
458 }
459 return pt;
460}
461
462/* return true if edge has label */
464 char *str;
465 struct fontinfo fi;
466 struct fontinfo lfi;
467 graph_t *sg = agraphof(agtail(e));
468
469 fi.fontname = NULL;
470 lfi.fontname = NULL;
471 if (E_label && (str = agxget(e, E_label)) && str[0]) {
472 initFontEdgeAttr(e, &fi);
473 ED_label(e) = make_label(e, str, aghtmlstr(str), false, fi.fontsize,
474 fi.fontname, fi.fontcolor);
477 }
478
479 if (E_xlabel && (str = agxget(e, E_xlabel)) && str[0]) {
480 if (!fi.fontname)
481 initFontEdgeAttr(e, &fi);
482 ED_xlabel(e) = make_label(e, str, aghtmlstr(str), false, fi.fontsize,
483 fi.fontname, fi.fontcolor);
485 }
486
487 if (E_headlabel && (str = agxget(e, E_headlabel)) && str[0]) {
488 initFontLabelEdgeAttr(e, &fi, &lfi);
489 ED_head_label(e) = make_label(e, str, aghtmlstr(str), false, lfi.fontsize,
490 lfi.fontname, lfi.fontcolor);
492 }
493 if (E_taillabel && (str = agxget(e, E_taillabel)) && str[0]) {
494 if (!lfi.fontname)
495 initFontLabelEdgeAttr(e, &fi, &lfi);
496 ED_tail_label(e) = make_label(e, str, aghtmlstr(str), false, lfi.fontsize,
497 lfi.fontname, lfi.fontcolor);
499 }
500
501 /* We still accept ports beginning with colons but this is deprecated
502 * That is, we allow tailport = ":abc" as well as the preferred
503 * tailport = "abc".
504 */
505 str = agget(e, TAIL_ID);
506 /* libgraph always defines tailport/headport; libcgraph doesn't */
507 if (!str)
508 str = "";
509 if (str && str[0])
510 ND_has_port(agtail(e)) = true;
511 ED_tail_port(e) = chkPort(ND_shape(agtail(e))->fns->portfn, agtail(e), str);
512 if (noClip(e, E_tailclip))
513 ED_tail_port(e).clip = false;
514 str = agget(e, HEAD_ID);
515 /* libgraph always defines tailport/headport; libcgraph doesn't */
516 if (!str)
517 str = "";
518 if (str && str[0])
519 ND_has_port(aghead(e)) = true;
520 ED_head_port(e) = chkPort(ND_shape(aghead(e))->fns->portfn, aghead(e), str);
521 if (noClip(e, E_headclip))
522 ED_head_port(e).clip = false;
523}
524
525static boxf addLabelBB(boxf bb, textlabel_t *lp, bool flipxy) {
526 double width, height;
527 pointf p = lp->pos;
528 double min, max;
529
530 if (flipxy) {
531 height = lp->dimen.x;
532 width = lp->dimen.y;
533 } else {
534 width = lp->dimen.x;
535 height = lp->dimen.y;
536 }
537 min = p.x - width / 2.;
538 max = p.x + width / 2.;
539 if (min < bb.LL.x)
540 bb.LL.x = min;
541 if (max > bb.UR.x)
542 bb.UR.x = max;
543
544 min = p.y - height / 2.;
545 max = p.y + height / 2.;
546 if (min < bb.LL.y)
547 bb.LL.y = min;
548 if (max > bb.UR.y)
549 bb.UR.y = max;
550
551 return bb;
552}
553
558 const size_t sides = poly->sides;
559 const size_t peris = MAX(poly->peripheries, (size_t)1);
560 pointf *verts = poly->vertices + (peris - 1) * sides;
561 boxf bb;
562
563 bb.LL = bb.UR = verts[0];
564 for (size_t i = 1; i < sides; i++) {
565 bb.LL.x = MIN(bb.LL.x, verts[i].x);
566 bb.LL.y = MIN(bb.LL.y, verts[i].y);
567 bb.UR.x = MAX(bb.UR.x, verts[i].x);
568 bb.UR.y = MAX(bb.UR.y, verts[i].y);
569 }
570 return bb;
571}
572
577 GD_bb(g) = addLabelBB(GD_bb(g), lp, GD_flip(g));
578}
579
585 node_t *n;
586 edge_t *e;
587 boxf b, bb;
588 boxf BF;
589 pointf ptf, s2;
590
591 if (agnnodes(g) == 0 && GD_n_cluster(g) == 0) {
592 bb.LL = (pointf){0};
593 bb.UR = (pointf){0};
594 return;
595 }
596
597 bb.LL = (pointf){INT_MAX, INT_MAX};
598 bb.UR = (pointf){-INT_MAX, -INT_MAX};
599 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
600 ptf = coord(n);
601 s2.x = ND_xsize(n) / 2.0;
602 s2.y = ND_ysize(n) / 2.0;
603 b.LL = sub_pointf(ptf, s2);
604 b.UR = add_pointf(ptf, s2);
605
606 EXPANDBB(&bb, b);
607 if (ND_xlabel(n) && ND_xlabel(n)->set) {
608 bb = addLabelBB(bb, ND_xlabel(n), GD_flip(g));
609 }
610 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
611 if (ED_spl(e) == 0)
612 continue;
613 for (size_t i = 0; i < ED_spl(e)->size; i++) {
614 for (size_t j = 0; j < (((Agedgeinfo_t *)AGDATA(e))->spl)->list[i].size;
615 j++) {
616 ptf = ED_spl(e)->list[i].list[j];
617 expandbp(&bb, ptf);
618 }
619 }
620 if (ED_label(e) && ED_label(e)->set) {
621 bb = addLabelBB(bb, ED_label(e), GD_flip(g));
622 }
623 if (ED_head_label(e) && ED_head_label(e)->set) {
624 bb = addLabelBB(bb, ED_head_label(e), GD_flip(g));
625 }
626 if (ED_tail_label(e) && ED_tail_label(e)->set) {
627 bb = addLabelBB(bb, ED_tail_label(e), GD_flip(g));
628 }
629 if (ED_xlabel(e) && ED_xlabel(e)->set) {
630 bb = addLabelBB(bb, ED_xlabel(e), GD_flip(g));
631 }
632 }
633 }
634
635 for (int i = 1; i <= GD_n_cluster(g); i++) {
636 B2BF(GD_bb(GD_clust(g)[i]), BF);
637 EXPANDBB(&bb, BF);
638 }
639 if (GD_label(g) && GD_label(g)->set) {
640 bb = addLabelBB(bb, GD_label(g), GD_flip(g));
641 }
642
643 GD_bb(g) = bb;
644}
645
647 return g == g->root || !strncasecmp(agnameof(g), "cluster", 7) ||
648 mapbool(agget(g, "cluster"));
649}
650
654Agsym_t *setAttr(graph_t *g, void *obj, char *name, char *value, Agsym_t *ap) {
655 if (ap == NULL) {
656 switch (agobjkind(obj)) {
657 case AGRAPH:
658 ap = agattr_text(g, AGRAPH, name, "");
659 break;
660 case AGNODE:
661 ap = agattr_text(g, AGNODE, name, "");
662 break;
663 case AGEDGE:
664 ap = agattr_text(g, AGEDGE, name, "");
665 break;
666 }
667 }
668 agxset(obj, ap, value);
669 return ap;
670}
671
680 int *idx) {
681 node_t *cn;
682
683 agxbprint(xb, "__%d:%s", *idx++, agnameof(cg));
684
685 cn = agnode(agroot(cg), agxbuse(xb), 1);
686 agbindrec(cn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
687
688 SET_CLUST_NODE(cn);
689 agsubnode(cg, cn, 1);
690 agsubnode(clg, n, 1);
691
692 /* set attributes */
693 N_label = setAttr(agraphof(cn), cn, "label", "", N_label);
694 N_style = setAttr(agraphof(cn), cn, "style", "invis", N_style);
695 N_shape = setAttr(agraphof(cn), cn, "shape", "box", N_shape);
696
697 return cn;
698}
699
700typedef struct {
701 Dtlink_t link; /* cdt data */
702 void *p[2]; /* key */
705} item;
706
707static int cmpItem(void *pp1, void *pp2) {
708 const void **p1 = pp1;
709 const void **p2 = pp2;
710 if ((uintptr_t)p1[0] < (uintptr_t)p2[0])
711 return -1;
712 if ((uintptr_t)p1[0] > (uintptr_t)p2[0])
713 return 1;
714 if ((uintptr_t)p1[1] < (uintptr_t)p2[1])
715 return -1;
716 if ((uintptr_t)p1[1] > (uintptr_t)p2[1])
717 return 1;
718 return 0;
719}
720
721static void *newItem(void *p, Dtdisc_t *disc) {
722 item *objp = p;
723 item *newp = gv_alloc(sizeof(item));
724
725 (void)disc;
726 newp->p[0] = objp->p[0];
727 newp->p[1] = objp->p[1];
728 newp->t = objp->t;
729 newp->h = objp->h;
730
731 return newp;
732}
733
735 .key = offsetof(item, p),
736 .size = 2 * sizeof(void *),
737 .link = offsetof(item, link),
738 .makef = newItem,
739 .freef = free,
740 .comparf = cmpItem,
741};
742
744static edge_t *cloneEdge(edge_t *e, node_t *ct, node_t *ch) {
745 graph_t *g = agraphof(ct);
746 edge_t *ce = agedge(g, ct, ch, NULL, 1);
747 agbindrec(ce, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true);
748 agcopyattr(e, ce);
749 ED_compound(ce) = true;
750
751 return ce;
752}
753
754static void insertEdge(Dt_t *map, void *t, void *h, edge_t *e) {
755 item dummy1 = {.p = {t, h}, .t = agtail(e), .h = aghead(e)};
756 dtinsert(map, &dummy1);
757
758 item dummy2 = {.p = {h, t}, .t = aghead(e), .h = agtail(e)};
759 dtinsert(map, &dummy2);
760}
761
763static item *mapEdge(Dt_t *map, edge_t *e) {
764 void *key[] = {agtail(e), aghead(e)};
765 return dtmatch(map, &key);
766}
767
768static graph_t *mapc(Dt_t *cmap, node_t *n) {
769 if (startswith(agnameof(n), "cluster")) {
770 return findCluster(cmap, agnameof(n));
771 }
772 return NULL;
773}
774
791static int checkCompound(edge_t *e, graph_t *clg, agxbuf *xb, Dt_t *map,
792 Dt_t *cmap, int *index_counter) {
793 node_t *cn;
794 node_t *cn1;
795 node_t *t = agtail(e);
796 node_t *h = aghead(e);
797 edge_t *ce;
798 item *ip;
799
800 if (IS_CLUST_NODE(h))
801 return 0;
802 graph_t *const tg = mapc(cmap, t);
803 graph_t *const hg = mapc(cmap, h);
804 if (!tg && !hg)
805 return 0;
806 if (tg == hg) {
807 agwarningf("cluster cycle %s -- %s not supported\n", agnameof(t),
808 agnameof(t));
809 return 0;
810 }
811 ip = mapEdge(map, e);
812 if (ip) {
813 cloneEdge(e, ip->t, ip->h);
814 return 1;
815 }
816
817 if (hg) {
818 if (tg) {
819 if (agcontains(hg, tg)) {
820 agwarningf("tail cluster %s inside head cluster %s\n", agnameof(tg),
821 agnameof(hg));
822 return 0;
823 }
824 if (agcontains(tg, hg)) {
825 agwarningf("head cluster %s inside tail cluster %s\n", agnameof(hg),
826 agnameof(tg));
827 return 0;
828 }
829 cn = clustNode(t, tg, xb, clg, index_counter);
830 cn1 = clustNode(h, hg, xb, clg, index_counter);
831 ce = cloneEdge(e, cn, cn1);
832 insertEdge(map, t, h, ce);
833 } else {
834 if (agcontains(hg, t)) {
835 agwarningf("tail node %s inside head cluster %s\n", agnameof(t),
836 agnameof(hg));
837 return 0;
838 }
839 cn = clustNode(h, hg, xb, clg, index_counter);
840 ce = cloneEdge(e, t, cn);
841 insertEdge(map, t, h, ce);
842 }
843 } else {
844 if (agcontains(tg, h)) {
845 agwarningf("head node %s inside tail cluster %s\n", agnameof(h),
846 agnameof(tg));
847 return 0;
848 }
849 cn = clustNode(t, tg, xb, clg, index_counter);
850 ce = cloneEdge(e, cn, h);
851 insertEdge(map, t, h, ce);
852 }
853 return 1;
854}
855
856typedef struct {
859} cl_edge_t;
860
861static int num_clust_edges(graph_t *g) {
862 cl_edge_t *cl_info = (cl_edge_t *)HAS_CLUST_EDGE(g);
863 if (cl_info)
864 return cl_info->n_cluster_edges;
865 return 0;
866}
867
875 int num_cl_edges = 0;
876 node_t *n;
877 node_t *nxt;
878 edge_t *e;
879 graph_t *clg;
880 agxbuf xb = {0};
881 Dt_t *map;
882 Dt_t *cmap = mkClustMap(g);
883 int index_counter = 0;
884
885 map = dtopen(&mapDisc, Dtoset);
886 clg = agsubg(g, "__clusternodes", 1);
887 agbindrec(clg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
888 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
889 if (IS_CLUST_NODE(n))
890 continue;
891 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
892 num_cl_edges += checkCompound(e, clg, &xb, map, cmap, &index_counter);
893 }
894 }
895 agxbfree(&xb);
896 dtclose(map);
897 for (n = agfstnode(clg); n; n = nxt) {
898 nxt = agnxtnode(clg, n);
899 agdelete(g, n);
900 }
901 agclose(clg);
902 if (num_cl_edges) {
903 cl_edge_t *cl_info;
904 cl_info = agbindrec(g, CL_EDGE_TAG, sizeof(cl_edge_t), false);
905 cl_info->n_cluster_edges = num_cl_edges;
906 }
907 dtclose(cmap);
908}
909
917static node_t *mapN(node_t *n, graph_t *clg) {
918 node_t *nn;
919 char *name;
920 graph_t *g = agraphof(n);
921 Agsym_t *sym;
922
923 if (!IS_CLUST_NODE(n))
924 return n;
925 agsubnode(clg, n, 1);
926 name = strchr(agnameof(n), ':');
927 assert(name);
928 name++;
929 if ((nn = agfindnode(g, name)))
930 return nn;
931 nn = agnode(g, name, 1);
932 agbindrec(nn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
933 SET_CLUST_NODE(nn);
934
935 /* Set all attributes to default */
936 for (sym = agnxtattr(g, AGNODE, NULL); sym;
937 (sym = agnxtattr(g, AGNODE, sym))) {
938 if (agxget(nn, sym) != sym->defval)
939 agxset(nn, sym, sym->defval);
940 }
941 return nn;
942}
943
944static void undoCompound(edge_t *e, graph_t *clg) {
945 node_t *t = agtail(e);
946 node_t *h = aghead(e);
947 node_t *ntail;
948 node_t *nhead;
949 edge_t *ce;
950
951 ntail = mapN(t, clg);
952 nhead = mapN(h, clg);
953 ce = cloneEdge(e, ntail, nhead);
954
955 /* transfer drawing information */
956 ED_spl(ce) = ED_spl(e);
957 ED_spl(e) = NULL;
958 ED_label(ce) = ED_label(e);
959 ED_label(e) = NULL;
960 ED_xlabel(ce) = ED_xlabel(e);
961 ED_xlabel(e) = NULL;
963 ED_head_label(e) = NULL;
965 ED_tail_label(e) = NULL;
967}
968
974 node_t *n;
975 node_t *nextn;
976 edge_t *e;
977 graph_t *clg;
978 int ecnt = num_clust_edges(g);
979 int i = 0;
980
981 if (!ecnt)
982 return;
983 clg = agsubg(g, "__clusternodes", 1);
984 agbindrec(clg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
985 edge_t **edgelist = gv_calloc(ecnt, sizeof(edge_t *));
986 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
987 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
988 if (ED_compound(e))
989 edgelist[i++] = e;
990 }
991 }
992 assert(i == ecnt);
993 for (i = 0; i < ecnt; i++)
994 undoCompound(edgelist[i], clg);
995 free(edgelist);
996 for (n = agfstnode(clg); n; n = nextn) {
997 nextn = agnxtnode(clg, n);
999 agdelete(g, n);
1000 }
1001 agclose(clg);
1002}
1003
1008attrsym_t *safe_dcl(graph_t *g, int obj_kind, char *name, char *defaultValue) {
1009 attrsym_t *a = agattr_text(g, obj_kind, name, NULL);
1010 if (!a) /* attribute does not exist */
1011 a = agattr_text(g, obj_kind, name, defaultValue);
1012 return a;
1013}
1014
1015static int comp_entities(const void *e1, const void *e2) {
1016 const strview_t *key = e1;
1017 const struct entities_s *candidate = e2;
1018 return strview_cmp(*key, strview(candidate->name, '\0'));
1019}
1020
1025char *scanEntity(char *t, agxbuf *xb) {
1026 const strview_t key = strview(t, ';');
1027 struct entities_s *res;
1028
1029 agxbputc(xb, '&');
1030 if (key.data[key.size] == '\0')
1031 return t;
1032 if (key.size > ENTITY_NAME_LENGTH_MAX || key.size < 2)
1033 return t;
1034 res = bsearch(&key, entities, NR_OF_ENTITIES, sizeof(entities[0]),
1036 if (!res)
1037 return t;
1038 agxbprint(xb, "#%d;", res->value);
1039 return t + key.size + 1;
1040}
1041
1048static int htmlEntity(char **s) {
1049 struct entities_s *res;
1050 unsigned char *str = *(unsigned char **)s;
1051 unsigned int byte;
1052 int i, n = 0;
1053
1054 byte = *str;
1055 if (byte == '#') {
1056 byte = *(str + 1);
1057 if (byte == 'x' || byte == 'X') {
1058 for (i = 2; i < 8; i++) {
1059 byte = *(str + i);
1060 if (byte >= 'A' && byte <= 'F')
1061 byte = byte - 'A' + 10;
1062 else if (byte >= 'a' && byte <= 'f')
1063 byte = byte - 'a' + 10;
1064 else if (byte >= '0' && byte <= '9')
1065 byte = byte - '0';
1066 else
1067 break;
1068 n = n * 16 + (int)byte;
1069 }
1070 } else {
1071 for (i = 1; i < 8; i++) {
1072 byte = *(str + i);
1073 if (byte >= '0' && byte <= '9')
1074 n = n * 10 + ((int)byte - '0');
1075 else
1076 break;
1077 }
1078 }
1079 if (byte == ';') {
1080 str += i + 1;
1081 } else {
1082 n = 0;
1083 }
1084 } else {
1085 strview_t key = {.data = (char *)str};
1086 for (i = 0; i < ENTITY_NAME_LENGTH_MAX; i++) {
1087 byte = *(str + i);
1088 if (byte == '\0')
1089 break;
1090 if (byte == ';') {
1091 res = bsearch(&key, entities, NR_OF_ENTITIES, sizeof(entities[0]),
1093 if (res) {
1094 n = res->value;
1095 str += i + 1;
1096 }
1097 break;
1098 }
1099 ++key.size;
1100 }
1101 }
1102 *s = (char *)str;
1103 return n;
1104}
1105
1106static unsigned char cvtAndAppend(unsigned char c, agxbuf *xb) {
1107 char buf[] = {c, '\0'};
1108 char *s = latin1ToUTF8(buf);
1109 char *p = s;
1110 size_t len = strlen(s);
1111 while (len-- > 1)
1112 agxbputc(xb, *p++);
1113 c = *p;
1114 free(s);
1115 return c;
1116}
1117
1122char *htmlEntityUTF8(char *s, graph_t *g) {
1123 static graph_t *lastg;
1124 static atomic_flag warned;
1125 unsigned char c;
1126 unsigned int v;
1127
1128 int uc;
1129 int ui;
1130
1131 if (lastg != g) {
1132 lastg = g;
1133 atomic_flag_clear(&warned);
1134 }
1135
1136 agxbuf xb = {0};
1137
1138 while ((c = *(unsigned char *)s++)) {
1139 if (c < 0xC0)
1140 /*
1141 * Handles properly formed UTF-8 characters between
1142 * 0x01 and 0x7F. Also treats \0 and naked trail
1143 * bytes 0x80 to 0xBF as valid characters representing
1144 * themselves.
1145 */
1146 uc = 0;
1147 else if (c < 0xE0)
1148 uc = 1;
1149 else if (c < 0xF0)
1150 uc = 2;
1151 else if (c < 0xF8)
1152 uc = 3;
1153 else {
1154 uc = -1;
1155 if (!atomic_flag_test_and_set(&warned)) {
1156 agwarningf(
1157 "UTF8 codes > 4 bytes are not currently supported (graph %s) - "
1158 "treated as Latin-1. Perhaps \"-Gcharset=latin1\" is needed?\n",
1159 agnameof(g));
1160 }
1161 c = cvtAndAppend(c, &xb);
1162 }
1163
1164 if (uc == 0 && c == '&') {
1165 /* replace html entity sequences like: &amp;
1166 * and: &#123; with their UTF8 equivalents */
1167 v = htmlEntity(&s);
1168 if (v) {
1169 if (v < 0x7F) /* entity needs 1 byte in UTF8 */
1170 c = v;
1171 else if (v < 0x07FF) { /* entity needs 2 bytes in UTF8 */
1172 agxbputc(&xb, (char)((v >> 6) | 0xC0));
1173 c = (v & 0x3F) | 0x80;
1174 } else { /* entity needs 3 bytes in UTF8 */
1175 agxbputc(&xb, (char)((v >> 12) | 0xE0));
1176 agxbputc(&xb, (char)(((v >> 6) & 0x3F) | 0x80));
1177 c = (v & 0x3F) | 0x80;
1178 }
1179 }
1180 } else /* copy n byte UTF8 characters */
1181 for (ui = 0; ui < uc; ++ui)
1182 if ((*s & 0xC0) == 0x80) {
1183 agxbputc(&xb, (char)c);
1184 c = *(unsigned char *)s++;
1185 } else {
1186 if (!atomic_flag_test_and_set(&warned)) {
1187 agwarningf(
1188 "Invalid %d-byte UTF8 found in input of graph %s - treated as "
1189 "Latin-1. Perhaps \"-Gcharset=latin1\" is needed?\n",
1190 uc + 1, agnameof(g));
1191 }
1192 c = cvtAndAppend(c, &xb);
1193 break;
1194 }
1195 agxbputc(&xb, (char)c);
1196 }
1197 return agxbdisown(&xb);
1198}
1199
1201char *latin1ToUTF8(char *s) {
1202 agxbuf xb = {0};
1203 unsigned int v;
1204
1205 /* Values are either a byte (<= 256) or come from htmlEntity, whose
1206 * values are all less than 0x07FF, so we need at most 3 bytes.
1207 */
1208 while ((v = *(unsigned char *)s++)) {
1209 if (v == '&') {
1210 v = htmlEntity(&s);
1211 if (!v)
1212 v = '&';
1213 }
1214 if (v < 0x7F)
1215 agxbputc(&xb, (char)v);
1216 else if (v < 0x07FF) {
1217 agxbputc(&xb, (char)((v >> 6) | 0xC0));
1218 agxbputc(&xb, (char)((v & 0x3F) | 0x80));
1219 } else {
1220 agxbputc(&xb, (char)((v >> 12) | 0xE0));
1221 agxbputc(&xb, (char)(((v >> 6) & 0x3F) | 0x80));
1222 agxbputc(&xb, (char)((v & 0x3F) | 0x80));
1223 }
1224 }
1225 return agxbdisown(&xb);
1226}
1227
1232char *utf8ToLatin1(char *s) {
1233 agxbuf xb = {0};
1234 unsigned char c;
1235
1236 while ((c = *(unsigned char *)s++)) {
1237 if (c < 0x7F)
1238 agxbputc(&xb, (char)c);
1239 else {
1240 unsigned char outc = (c & 0x03) << 6;
1241 c = *(unsigned char *)s++;
1242 outc = outc | (c & 0x3F);
1243 agxbputc(&xb, (char)outc);
1244 }
1245 }
1246 return agxbdisown(&xb);
1247}
1248
1250 if (!OVERLAP(b, ND_bb(n)))
1251 return false;
1252
1253 /* FIXME - need to do something better about CLOSEENOUGH */
1254 pointf p = sub_pointf(ND_coord(n), mid_pointf(b.UR, b.LL));
1255
1256 inside_t ictxt = {.s.n = n};
1257
1258 return ND_shape(n)->fns->insidefn(&ictxt, p);
1259}
1260
1262 const pointf s = {.x = lp->dimen.x / 2.0, .y = lp->dimen.y / 2.0};
1263 boxf bb = {.LL = sub_pointf(lp->pos, s), .UR = add_pointf(lp->pos, s)};
1264 return OVERLAP(b, bb);
1265}
1266
1267static bool overlap_arrow(pointf p, pointf u, double scale, boxf b) {
1268 // FIXME - check inside arrow shape
1269 return OVERLAP(b, arrow_bb(p, u, scale));
1270}
1271
1272static bool overlap_bezier(bezier bz, boxf b) {
1273 assert(bz.size);
1274 pointf u = bz.list[0];
1275 for (size_t i = 1; i < bz.size; i++) {
1276 pointf p = bz.list[i];
1277 if (lineToBox(p, u, b) != -1)
1278 return true;
1279 u = p;
1280 }
1281
1282 /* check arrows */
1283 if (bz.sflag) {
1284 if (overlap_arrow(bz.sp, bz.list[0], 1, b))
1285 return true;
1286 }
1287 if (bz.eflag) {
1288 if (overlap_arrow(bz.ep, bz.list[bz.size - 1], 1, b))
1289 return true;
1290 }
1291 return false;
1292}
1293
1295 splines *spl = ED_spl(e);
1296 if (spl && boxf_overlap(spl->bb, b))
1297 for (size_t i = 0; i < spl->size; i++)
1298 if (overlap_bezier(spl->list[i], b))
1299 return true;
1300
1301 textlabel_t *lp = ED_label(e);
1302 if (lp && overlap_label(lp, b))
1303 return true;
1304
1305 return false;
1306}
1307
1309static int edgeType(const char *s, int defaultValue) {
1310 if (s == NULL || strcmp(s, "") == 0) {
1311 return defaultValue;
1312 }
1313
1314 if (*s == '0') { /* false */
1315 return EDGETYPE_LINE;
1316 } else if (*s >= '1' && *s <= '9') { /* true */
1317 return EDGETYPE_SPLINE;
1318 } else if (strcasecmp(s, "curved") == 0) {
1319 return EDGETYPE_CURVED;
1320 } else if (strcasecmp(s, "compound") == 0) {
1321 return EDGETYPE_COMPOUND;
1322 } else if (strcasecmp(s, "false") == 0) {
1323 return EDGETYPE_LINE;
1324 } else if (strcasecmp(s, "line") == 0) {
1325 return EDGETYPE_LINE;
1326 } else if (strcasecmp(s, "none") == 0) {
1327 return EDGETYPE_NONE;
1328 } else if (strcasecmp(s, "no") == 0) {
1329 return EDGETYPE_LINE;
1330 } else if (strcasecmp(s, "ortho") == 0) {
1331 return EDGETYPE_ORTHO;
1332 } else if (strcasecmp(s, "polyline") == 0) {
1333 return EDGETYPE_PLINE;
1334 } else if (strcasecmp(s, "spline") == 0) {
1335 return EDGETYPE_SPLINE;
1336 } else if (strcasecmp(s, "true") == 0) {
1337 return EDGETYPE_SPLINE;
1338 } else if (strcasecmp(s, "yes") == 0) {
1339 return EDGETYPE_SPLINE;
1340 }
1341
1342 agwarningf("Unknown \"splines\" value: \"%s\" - ignored\n", s);
1343 return defaultValue;
1344}
1345
1358void setEdgeType(graph_t *g, int defaultValue) {
1359 char *s = agget(g, "splines");
1360 int et;
1361
1362 if (!s) {
1363 et = defaultValue;
1364 } else if (*s == '\0') {
1365 et = EDGETYPE_NONE;
1366 } else {
1367 et = edgeType(s, defaultValue);
1368 }
1369 GD_flags(g) |= et;
1370}
1371
1380void get_gradient_points(pointf *A, pointf *G, size_t n, double angle,
1381 int flags) {
1382 pointf min, max, center;
1383 int isRadial = flags & 1;
1384 int isRHS = flags & 2;
1385
1386 if (n == 2) {
1387 double rx = A[1].x - A[0].x;
1388 double ry = A[1].y - A[0].y;
1389 min.x = A[0].x - rx;
1390 max.x = A[0].x + rx;
1391 min.y = A[0].y - ry;
1392 max.y = A[0].y + ry;
1393 } else {
1394 min.x = max.x = A[0].x;
1395 min.y = max.y = A[0].y;
1396 for (size_t i = 0; i < n; i++) {
1397 min.x = MIN(A[i].x, min.x);
1398 min.y = MIN(A[i].y, min.y);
1399 max.x = MAX(A[i].x, max.x);
1400 max.y = MAX(A[i].y, max.y);
1401 }
1402 }
1403 center.x = min.x + (max.x - min.x) / 2;
1404 center.y = min.y + (max.y - min.y) / 2;
1405 if (isRadial) {
1406 double inner_r, outer_r;
1407 outer_r = hypot(center.x - min.x, center.y - min.y);
1408 inner_r = outer_r / 4.;
1409 if (isRHS) {
1410 G[0].y = center.y;
1411 } else {
1412 G[0].y = -center.y;
1413 }
1414 G[0].x = center.x;
1415 G[1].x = inner_r;
1416 G[1].y = outer_r;
1417 } else {
1418 double half_x = max.x - center.x;
1419 double half_y = max.y - center.y;
1420 double sina = sin(angle);
1421 double cosa = cos(angle);
1422 if (isRHS) {
1423 G[0].y = center.y - half_y * sina;
1424 G[1].y = center.y + half_y * sina;
1425 } else {
1426 G[0].y = -center.y + (max.y - center.y) * sin(angle);
1427 G[1].y = -center.y - (center.y - min.y) * sin(angle);
1428 }
1429 G[0].x = center.x - half_x * cosa;
1430 G[1].x = center.x + half_x * cosa;
1431 }
1432}
1433
1435 if (ED_spl(e)) {
1436 for (size_t i = 0; i < ED_spl(e)->size; i++)
1437 free(ED_spl(e)->list[i].list);
1438 free(ED_spl(e)->list);
1439 free(ED_spl(e));
1440 }
1441 ED_spl(e) = NULL;
1442}
1443
1445 free(ED_path(e).ps);
1446 gv_free_splines(e);
1447 free_label(ED_label(e));
1451 /*FIX HERE , shallow cleaning may not be enough here */
1452 agdelrec(e, "Agedgeinfo_t");
1453}
1454
1456 free(ND_pos(n));
1457 if (ND_shape(n))
1458 ND_shape(n)->fns->freefn(n);
1459 free_label(ND_label(n));
1461 /*FIX HERE , shallow cleaning may not be enough here */
1462 agdelrec(n, "Agnodeinfo_t");
1463}
1464
1465void gv_nodesize(node_t *n, bool flip) {
1466 if (flip) {
1467 double w = INCH2PS(ND_height(n));
1468 ND_lw(n) = ND_rw(n) = w / 2;
1469 ND_ht(n) = INCH2PS(ND_width(n));
1470 } else {
1471 double w = INCH2PS(ND_width(n));
1472 ND_lw(n) = ND_rw(n) = w / 2;
1473 ND_ht(n) = INCH2PS(ND_height(n));
1474 }
1475}
1476
1477#ifndef HAVE_DRAND48
1478double drand48(void) {
1479 double d;
1480 d = rand();
1481 d = d / RAND_MAX;
1482 return d;
1483}
1484#endif
1485typedef struct {
1487 char *name;
1489} clust_t;
1490
1492 .key = offsetof(clust_t, name),
1493 .size = -1,
1494 .link = offsetof(clust_t, link),
1495 .freef = free,
1496};
1497
1498static void fillMap(Agraph_t *g, Dt_t *map) {
1499 for (int c = 1; c <= GD_n_cluster(g); c++) {
1500 Agraph_t *cl = GD_clust(g)[c];
1501 char *s = agnameof(cl);
1502 if (dtmatch(map, s)) {
1503 agwarningf("Two clusters named %s - the second will be ignored\n", s);
1504 } else {
1505 clust_t *ip = gv_alloc(sizeof(clust_t));
1506 ip->name = s;
1507 ip->clp = cl;
1508 dtinsert(map, ip);
1509 }
1510 fillMap(cl, map);
1511 }
1512}
1513
1519 Dt_t *map = dtopen(&strDisc, Dtoset);
1520
1521 fillMap(g, map);
1522
1523 return map;
1524}
1525
1526Agraph_t *findCluster(Dt_t *map, char *name) {
1527 clust_t *clp = dtmatch(map, name);
1528 if (clp)
1529 return clp->clp;
1530 return NULL;
1531}
1532
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 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_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define MIN(a, b)
Definition arith.h:28
#define MAX(a, b)
Definition arith.h:33
boxf arrow_bb(pointf p, pointf u, double arrowsize)
Definition arrows.c:1107
#define dtmatch(d, o)
Definition cdt.h:185
#define dtinsert(d, o)
Definition cdt.h:186
CDT_API int dtclose(Dt_t *)
Definition dtclose.c:10
CDT_API Dtmethod_t * Dtoset
ordered set (self-adjusting tree)
Definition dttree.c:306
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
Definition dtopen.c:11
void processClusterEdges(graph_t *g)
Definition utils.c:874
void undoClusterEdges(graph_t *g)
Definition utils.c:973
char * late_nnstring(void *obj, attrsym_t *attr, char *defaultValue)
Definition utils.c:93
char * scanEntity(char *t, agxbuf *xb)
Definition utils.c:1025
#define W_DEGREE
Definition utils.c:164
static edge_t * cloneEdge(edge_t *e, node_t *ct, node_t *ch)
Make a copy of e in e's graph but using ct and ch as nodes.
Definition utils.c:744
bool mapbool(const char *p)
Definition utils.c:322
node_t * UF_union(node_t *u, node_t *v)
Definition utils.c:116
node_t * UF_find(node_t *n)
Definition utils.c:107
static int Tflag
Definition utils.c:373
static node_t * mapN(node_t *n, graph_t *clg)
Definition utils.c:917
Dt_t * mkClustMap(Agraph_t *g)
Definition utils.c:1518
void UF_setname(node_t *u, node_t *v)
Definition utils.c:149
static void undoCompound(edge_t *e, graph_t *clg)
Definition utils.c:944
void setEdgeType(graph_t *g, int defaultValue)
Definition utils.c:1358
static port chkPort(port(*pf)(node_t *, char *, char *), node_t *n, char *s)
Definition utils.c:445
char * late_string(void *obj, attrsym_t *attr, char *defaultValue)
Definition utils.c:87
void gv_free_splines(edge_t *e)
Definition utils.c:1434
boxf polyBB(polygon_t *poly)
Definition utils.c:557
int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum)
Definition utils.c:40
static Dtdisc_t mapDisc
Definition utils.c:734
void gv_cleanup_edge(edge_t *e)
Definition utils.c:1444
static void insertEdge(Dt_t *map, void *t, void *h, edge_t *e)
Definition utils.c:754
void common_init_node(node_t *n)
Definition utils.c:390
pointf Bezier(const pointf *V, double t, pointf *Left, pointf *Right)
Definition utils.c:172
static node_t * clustNode(node_t *n, graph_t *cg, agxbuf *xb, graph_t *clg, int *idx)
Definition utils.c:679
bool overlap_label(textlabel_t *lp, boxf b)
Definition utils.c:1261
int maptoken(char *p, char **name, int *val)
Definition utils.c:296
double late_double(void *obj, attrsym_t *attr, double defaultValue, double minimum)
Definition utils.c:55
static void initFontLabelEdgeAttr(edge_t *e, struct fontinfo *fi, struct fontinfo *lfi)
Definition utils.c:421
bool overlap_node(node_t *n, boxf b)
Definition utils.c:1249
static int comp_entities(const void *e1, const void *e2)
Definition utils.c:1015
void common_init_edge(edge_t *e)
Definition utils.c:463
static bool noClip(edge_t *e, attrsym_t *sym)
Return true if head/tail end of edge should not be clipped to node.
Definition utils.c:431
const char * safefile(const char *filename)
Definition utils.c:248
pointf dotneato_closest(splines *spl, pointf pt)
Definition utils.c:324
static bool overlap_bezier(bezier bz, boxf b)
Definition utils.c:1272
pointf coord(node_t *n)
Definition utils.c:155
static char * findPath(const strviews_t dirs, const char *str)
Definition utils.c:234
static void * newItem(void *p, Dtdisc_t *disc)
Definition utils.c:721
attrsym_t * safe_dcl(graph_t *g, int obj_kind, char *name, char *defaultValue)
Definition utils.c:1008
void UF_singleton(node_t *u)
Definition utils.c:143
static void initFontEdgeAttr(edge_t *e, struct fontinfo *fi)
Definition utils.c:415
char * utf8ToLatin1(char *s)
Definition utils.c:1232
static int cmpItem(void *pp1, void *pp2)
Definition utils.c:707
char * latin1ToUTF8(char *s)
Converts string from Latin1 encoding to utf8. Also translates HTML entities.
Definition utils.c:1201
double get_inputscale(graph_t *g)
Definition utils.c:78
static int edgeType(const char *s, int defaultValue)
Convert string to edge type.
Definition utils.c:1309
static boxf addLabelBB(boxf bb, textlabel_t *lp, bool flipxy)
Definition utils.c:525
void updateBB(graph_t *g, textlabel_t *lp)
Definition utils.c:576
static bool overlap_arrow(pointf p, pointf u, double scale, boxf b)
Definition utils.c:1267
bool overlap_edge(edge_t *e, boxf b)
Definition utils.c:1294
static unsigned char cvtAndAppend(unsigned char c, agxbuf *xb)
Definition utils.c:1106
void compute_bb(graph_t *g)
Definition utils.c:584
static void fillMap(Agraph_t *g, Dt_t *map)
Definition utils.c:1498
static Dtdisc_t strDisc
Definition utils.c:1491
void gv_cleanup_node(node_t *n)
Definition utils.c:1455
bool mapBool(const char *p, bool defaultValue)
Definition utils.c:306
static int checkCompound(edge_t *e, graph_t *clg, agxbuf *xb, Dt_t *map, Dt_t *cmap, int *index_counter)
Definition utils.c:791
char * htmlEntityUTF8(char *s, graph_t *g)
Definition utils.c:1122
static int num_clust_edges(graph_t *g)
Definition utils.c:861
#define PATHSEP
Definition utils.c:219
void get_gradient_points(pointf *A, pointf *G, size_t n, double angle, int flags)
Definition utils.c:1380
void gv_nodesize(node_t *n, bool flip)
Definition utils.c:1465
int test_toggle(void)
Definition utils.c:382
static item * mapEdge(Dt_t *map, edge_t *e)
Check if we already have cluster edge corresponding to t->h, and return it.
Definition utils.c:763
static int htmlEntity(char **s)
Definition utils.c:1048
bool is_a_cluster(Agraph_t *g)
Definition utils.c:646
static graph_t * mapc(Dt_t *cmap, node_t *n)
Definition utils.c:768
bool late_bool(void *obj, attrsym_t *attr, bool defaultValue)
Definition utils.c:100
Agraph_t * findCluster(Dt_t *map, char *name)
Definition utils.c:1526
double drand48(void)
Definition utils.c:1478
Agsym_t * setAttr(graph_t *g, void *obj, char *name, char *value, Agsym_t *ap)
Definition utils.c:654
#define HEAD_LABEL
Definition const.h:168
#define NORMAL
Definition const.h:24
#define EDGETYPE_SPLINE
Definition const.h:239
#define EDGE_XLABEL
Definition const.h:172
#define DEFAULT_NODEHEIGHT
Definition const.h:72
#define TAIL_LABEL
Definition const.h:169
#define EDGE_LABEL
Definition const.h:167
#define EDGETYPE_CURVED
Definition const.h:236
#define DEFAULT_COLOR
Definition const.h:48
#define DEFAULT_NODEWIDTH
Definition const.h:74
#define EDGETYPE_ORTHO
Definition const.h:238
#define MIN_NODEWIDTH
Definition const.h:75
#define DEFAULT_FONTSIZE
Definition const.h:61
#define MIN_FONTSIZE
Definition const.h:63
#define EDGETYPE_PLINE
Definition const.h:237
#define EDGETYPE_LINE
Definition const.h:235
#define DEFAULT_FONTNAME
Definition const.h:67
#define EDGETYPE_NONE
Definition const.h:234
#define EDGETYPE_COMPOUND
Definition const.h:240
#define DEFAULT_NODESHAPE
Definition const.h:76
#define NODE_XLABEL
Definition const.h:171
#define MIN_NODEHEIGHT
Definition const.h:73
#define ENTITY_NAME_LENGTH_MAX
Definition entities.h:274
static const struct entities_s entities[]
#define NR_OF_ENTITIES
Definition entities.h:275
static Dtdisc_t disc
Definition exparse.y:209
#define A(n, t)
Definition expr.h:76
static int flags
Definition gc.c:63
#define G
Definition gdefs.h:7
#define V
Definition gdefs.h:5
int lineToBox(pointf p, pointf q, boxf b)
Definition geom.c:47
#define B2BF(b, bf)
Definition geom.h:69
#define OVERLAP(b0, b1)
Definition geom.h:48
struct pointf_s pointf
#define DIST2(p, q)
Definition geom.h:55
#define POINTS_PER_INCH
Definition geom.h:58
#define INCH2PS(a_inches)
Definition geom.h:63
geometric functions (e.g. on points and boxes)
static void expandbp(boxf *b, pointf p)
expand box b as needed to enclose point p
Definition geomprocs.h:45
static WUR pointf mid_pointf(pointf p, pointf q)
Definition geomprocs.h:104
static WUR pointf sub_pointf(pointf p, pointf q)
Definition geomprocs.h:96
static WUR pointf add_pointf(pointf p, pointf q)
Definition geomprocs.h:88
#define EXPANDBB(b0, b1)
Definition geomprocs.h:65
static WUR pointf scale(double c, pointf p)
Definition geomprocs.h:148
static WUR bool boxf_overlap(boxf b0, boxf b1)
Definition geomprocs.h:136
Agsym_t * N_fontsize
Definition globals.h:78
Agsym_t * E_labelfontsize
Definition globals.h:87
Agsym_t * E_fontcolor
Definition globals.h:84
Agsym_t * N_width
Definition globals.h:77
Agsym_t * E_headclip
Definition globals.h:88
Agsym_t * E_headlabel
Definition globals.h:86
Agsym_t * N_showboxes
Definition globals.h:79
Agsym_t * N_fontname
Definition globals.h:78
Agsym_t * E_fontname
Definition globals.h:84
Agsym_t * N_style
Definition globals.h:79
char * HTTPServerEnVar
Definition globals.h:55
char * Gvimagepath
Definition globals.h:50
Agsym_t * E_label
Definition globals.h:84
double PSinputscale
Definition globals.h:58
char * Gvfilepath
Definition globals.h:48
Agsym_t * N_shape
Definition globals.h:77
Agsym_t * N_xlabel
Definition globals.h:78
Agsym_t * E_label_float
Definition globals.h:86
Agsym_t * E_taillabel
Definition globals.h:87
Agsym_t * N_label
Definition globals.h:78
Agsym_t * E_fontsize
Definition globals.h:84
Agsym_t * E_labelfontname
Definition globals.h:87
Agsym_t * E_xlabel
Definition globals.h:84
Agsym_t * N_fontcolor
Definition globals.h:78
Agsym_t * E_labelfontcolor
Definition globals.h:87
Agsym_t * E_tailclip
Definition globals.h:88
Agsym_t * N_height
Definition globals.h:77
static double len(glCompPoint p)
Definition glutils.c:138
void free(void *)
#define SIZE_MAX
Definition gmlscan.c:347
node NULL
Definition grammar.y:181
int agnnodes(Agraph_t *g)
Definition graph.c:163
Agsym_t * agattr_text(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up text attributes of a graph
Definition attr.c:333
Agsym_t * agnxtattr(Agraph_t *g, int kind, Agsym_t *attr)
permits traversing the list of attributes of a given type
Definition attr.c:362
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:521
char * agget(void *obj, char *name)
Definition attr.c:447
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:457
int agcopyattr(void *oldobj, void *newobj)
copies all of the attributes from one object to another
Definition attr.c:632
#define ED_compound(e)
Definition types.h:583
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:252
#define ED_xlabel(e)
Definition types.h:590
#define ED_label_ontop(e)
Definition types.h:591
#define ED_head_label(e)
Definition types.h:587
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:28
#define ED_spl(e)
Definition types.h:595
#define agtail(e)
Definition cgraph.h:982
#define ED_path(e)
Definition types.h:593
#define ED_tail_label(e)
Definition types.h:596
#define aghead(e)
Definition cgraph.h:983
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:43
#define ED_head_port(e)
Definition types.h:588
#define ED_label(e)
Definition types.h:589
#define ED_tail_port(e)
Definition types.h:597
void agwarningf(const char *fmt,...)
Definition agerror.c:175
#define agfindgraphattr(g, a)
Definition types.h:613
#define GD_has_labels(g)
Definition types.h:368
#define GD_clust(g)
Definition types.h:360
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:97
#define GD_flags(g)
Definition types.h:365
#define GD_bb(g)
Definition types.h:354
#define GD_n_cluster(g)
Definition types.h:389
#define GD_label(g)
Definition types.h:374
#define GD_flip(g)
Definition types.h:378
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:143
#define ND_ht(n)
Definition types.h:500
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:50
#define ND_bb(n)
Definition types.h:488
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:43
#define ND_showboxes(n)
Definition types.h:530
#define ND_has_port(n)
Definition types.h:495
#define ND_ysize(n)
Definition types.h:538
Agnode_t * agsubnode(Agraph_t *g, Agnode_t *n, int createflag)
Definition node.c:254
#define ND_label(n)
Definition types.h:502
#define ND_rw(n)
Definition types.h:525
#define ND_height(n)
Definition types.h:498
#define ND_width(n)
Definition types.h:536
#define ND_lw(n)
Definition types.h:506
#define ND_UF_parent(n)
Definition types.h:485
#define ND_xlabel(n)
Definition types.h:503
#define ND_UF_size(n)
Definition types.h:487
#define ND_pos(n)
Definition types.h:520
#define ND_ranktype(n)
Definition types.h:524
#define agfindnode(g, n)
Definition types.h:611
#define ND_coord(n)
Definition types.h:490
#define ND_shape(n)
Definition types.h:528
#define ND_xsize(n)
Definition types.h:537
Agraph_t * agraphof(void *obj)
Definition obj.c:187
#define AGDATA(obj)
returns Agrec_t
Definition cgraph.h:227
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
int agdelete(Agraph_t *g, void *obj)
deletes object. Equivalent to agclose, agdelnode, and agdeledge for obj being a graph,...
Definition obj.c:22
int agcontains(Agraph_t *, void *obj)
returns non-zero if obj is a member of (sub)graph
Definition obj.c:235
int agobjkind(void *obj)
Definition obj.c:254
Agraph_t * agroot(void *obj)
Definition obj.c:170
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:91
int agdelrec(void *obj, const char *name)
deletes a named record from one object
Definition rec.c:139
int aghtmlstr(const char *)
Definition refstr.c:440
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:52
void gvToggle(int s)
Definition utils.c:374
replacements for ctype.h functions
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
Arithmetic helper functions.
static int imin(int a, int b)
minimum of two integers
Definition gv_math.h:35
static bool is_exactly_pos_zero(double v)
is a value precisely +0.0?
Definition gv_math.h:66
Graphviz context library.
textitem scanner parser str
Definition htmlparse.y:218
textlabel_t * make_label(void *obj, char *str, bool is_html, bool is_record, double fontsize, char *fontname, char *fontcolor)
Definition labels.c:110
void free_label(textlabel_t *p)
Definition labels.c:204
type-generic dynamically expanding list
#define LIST_APPEND(list,...)
Definition list.h:151
#define LIST(type)
Definition list.h:66
#define LIST_SIZE(list)
Definition list.h:92
#define LIST_FREE(list)
Definition list.h:413
#define LIST_IS_EMPTY(list)
Definition list.h:102
#define LIST_GET(list, index)
Definition list.h:197
#define IS_CLUST_NODE(n)
Definition macros.h:23
#define SET_CLUST_NODE(n)
Definition macros.h:22
#define CL_EDGE_TAG
Definition macros.h:21
#define HAS_CLUST_EDGE(g)
Definition macros.h:24
#define ND_id(n)
Definition mm2gv.c:41
File system path helpers.
#define PATH_SEPARATOR
character for separating directory components in a file system path
Definition path.h:10
shape_kind shapeOf(node_t *)
Definition shapes.c:1909
shape_desc * bind_shape(char *name, node_t *)
Definition shapes.c:3970
static double cg(SparseMatrix A, const double *precond, size_t n, int dim, double *x0, double *rhs, double tol, double maxit)
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
platform abstraction for case-insensitive string functions
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
graph or subgraph
Definition cgraph.h:424
Agraph_t * root
subgraphs - ancestors
Definition cgraph.h:433
implementation of Agrec_t
Definition cgraph.h:172
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:641
char * defval
Definition cgraph.h:644
Definition types.h:89
size_t size
Definition types.h:91
pointf sp
Definition types.h:94
pointf * list
Definition types.h:90
uint32_t eflag
Definition types.h:93
pointf ep
Definition types.h:95
uint32_t sflag
Definition types.h:92
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
Agrec_t hdr
Definition utils.c:857
int n_cluster_edges
Definition utils.c:858
Agraph_t * clp
Definition utils.c:1488
char * name
Definition utils.c:1487
Dtlink_t link
Definition utils.c:1486
Definition cdt.h:98
int key
Definition cdt.h:85
char * name
Definition entities.h:17
int value
Definition entities.h:18
double fontsize
Definition utils.c:385
char * fontcolor
Definition utils.c:387
char * fontname
Definition utils.c:386
Definition utils.c:700
node_t * t
Definition utils.c:703
node_t * h
Definition utils.c:704
void * p[2]
Definition utils.c:702
Dtlink_t link
Definition utils.c:701
int y
Definition geom.h:27
int x
Definition geom.h:27
double x
Definition geom.h:29
double y
Definition geom.h:29
Definition types.h:48
char * name
Definition types.h:63
bezier * list
Definition types.h:99
boxf bb
Definition types.h:101
size_t size
Definition types.h:100
a non-owning string reference
Definition strview.h:20
const char * data
start of the pointed to string
Definition strview.h:21
size_t size
extent of the string in bytes
Definition strview.h:22
pointf pos
Definition types.h:114
pointf dimen
Definition types.h:110
state for an in-progress string tokenization
Definition tokenize.h:36
Non-owning string references.
static int strview_cmp(strview_t a, strview_t b)
compare two string references
Definition strview.h:71
static strview_t strview(const char *referent, char terminator)
create a string reference
Definition strview.h:26
static point center(point vertex[], size_t n)
String tokenization.
static strview_t tok_get(const tok_t *t)
get the current token
Definition tokenize.h:76
static tok_t tok(const char *input, const char *separators)
begin tokenization of a new string
Definition tokenize.h:43
static bool tok_end(const tok_t *t)
is this tokenizer exhausted?
Definition tokenize.h:68
static void tok_next(tok_t *t)
advance to the next token in the string being scanned
Definition tokenize.h:85
#define TAIL_ID
Definition types.h:43
@ SH_RECORD
Definition types.h:187
#define HEAD_ID
Definition types.h:44
struct inside_t::@57 s
node_t * n
Definition types.h:160
Definition grammar.c:90
int(* pf)(void *, char *,...)
Definition xdot.c:392