Graphviz 13.0.0~dev.20241220.2304
Loading...
Searching...
No Matches
actions.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 v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11/*
12 * Code for main functions in gpr
13 */
14
15#include <ast/ast.h>
16#include <ast/error.h>
17#include <gvpr/actions.h>
18#include <gvpr/compile.h>
19#include <limits.h>
20#include <stdbool.h>
21#include <stddef.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <string.h>
25#include <unistd.h>
26#include <util/agxbuf.h>
27#include <util/alloc.h>
28#include <util/gv_ctype.h>
29#include <util/strcasecmp.h>
30#include <util/unreachable.h>
31#include <util/unused.h>
32
33#define KINDS(p) \
34 ((AGTYPE(p) == AGRAPH) ? "graph" : (AGTYPE(p) == AGNODE) ? "node" : "edge")
35
36static int iofread(void *chan, char *buf, int bufsize) {
37 FILE *fp = chan;
38
39 return (int)read(fileno(fp), buf, bufsize);
40}
41
42static int ioputstr(void *chan, const char *str) { return fputs(str, chan); }
43
44static int ioflush(void *chan) { return fflush(chan); }
45
47
48/* sameG:
49 * Return common root if objects belong to same root graph.
50 * NULL otherwise
51 */
52Agraph_t *sameG(void *p1, void *p2, char *fn, char *msg) {
53 Agobj_t *obj1 = OBJ(p1);
54 Agobj_t *obj2 = OBJ(p2);
55 Agraph_t *root;
56
57 root = agroot(agraphof(obj1));
58 if (root != agroot(agraphof(obj2))) {
59 if (msg)
60 error(ERROR_WARNING, "%s in %s() belong to different graphs", msg, fn);
61 else
62 error(ERROR_WARNING, "%s and %s in %s() belong to different graphs",
63 KINDS(obj1), KINDS(obj2), fn);
64 return 0;
65 } else
66 return root;
67}
68
69/* indexOf:
70 * Return index of leftmost string s2 in string s1, or -1
71 */
72int indexOf(char *s1, char *s2) {
73 char *s = strstr(s1, s2);
74 return s == NULL ? -1 : (int)(s - s1);
75}
76
77/* rindexOf:
78 * Return index of rightmost string s2 in string s1, or -1
79 */
80long rindexOf(char *s1, char *s2) {
81 char c1 = *s2;
82 char *p;
83 size_t len1 = strlen(s1);
84 size_t len2 = strlen(s2);
85
86 if (c1 == '\0') {
87 assert(len1 <= LONG_MAX);
88 return (long)len1;
89 }
90 if (len2 > len1)
91 return -1;
92 p = s1 + (len1 - len2);
93 while (true) {
94 if (strncmp(p, s2, len2) == 0)
95 return p - s1;
96 if (p == s1)
97 break;
98 p--;
99 }
100 return -1;
101}
102
103/* match:
104 * Return index of pattern pat in string str, or SIZE_MAX
105 */
106size_t match(char *str, char *pat) {
107 size_t sub[2];
108
109 if (strgrpmatch(str, pat, sub, 1, 0)) {
110 return (sub[0]);
111 } else
112 return SIZE_MAX;
113}
114
115/* copyAttr:
116 * Copy attributes from src to tgt. Overrides currently
117 * defined values.
118 * FIX: we should probably use the default value of the source
119 * graph when initializing the attribute, rather than "".
120 * NOTE: We do not assume src and tgt have the same kind.
121 */
122int copyAttr(Agobj_t *src, Agobj_t *tgt) {
123 Agraph_t *srcg;
124 Agraph_t *tgtg;
125 Agsym_t *sym = 0;
126 Agsym_t *tsym = 0;
127 int skind = AGTYPE(src);
128 int tkind = AGTYPE(tgt);
129 char *val;
130
131 srcg = agraphof(src);
132 tgtg = agraphof(tgt);
133 while ((sym = agnxtattr(srcg, skind, sym))) {
134 tsym = agattrsym(tgt, sym->name);
135 if (!tsym)
136 tsym = agattr(tgtg, tkind, sym->name, sym->defval);
137 val = agxget(src, sym);
138 if (aghtmlstr(val)) {
139 val = agstrdup_html(tgtg, val);
140 agxset(tgt, tsym, val);
141 agstrfree(tgtg, val);
142 } else
143 agxset(tgt, tsym, val);
144 }
145 return 0;
146}
147
148/* copy:
149 * Create new object of type AGTYPE(obj) with all of its
150 * attributes.
151 * If obj is an edge, only create end nodes if necessary.
152 * If obj is a graph, if g is null, create a top-level
153 * graph. Otherwise, create a subgraph of g.
154 * Assume obj != NULL.
155 */
157 Agobj_t *nobj = 0;
158 Agedge_t *e;
159 Agnode_t *h;
160 Agnode_t *t;
161 int kind = AGTYPE(obj);
162 char *name;
163
164 if (kind != AGRAPH && !g) {
165 exerror("NULL graph with non-graph object in copy()");
166 return 0;
167 }
168
169 switch (kind) {
170 case AGNODE:
171 name = agnameof(obj);
172 nobj = (Agobj_t *)openNode(g, name);
173 break;
174 case AGRAPH:
175 name = agnameof(obj);
176 if (g)
177 nobj = (Agobj_t *)openSubg(g, name);
178 else
179 nobj = (Agobj_t *)openG(name, ((Agraph_t *)obj)->desc);
180 break;
181 case AGINEDGE:
182 case AGOUTEDGE:
183 e = (Agedge_t *)obj;
184 t = openNode(g, agnameof(agtail(e)));
185 h = openNode(g, agnameof(aghead(e)));
186 name = agnameof(AGMKOUT(e));
187 nobj = (Agobj_t *)openEdge(g, t, h, name);
188 break;
189 default:
190 UNREACHABLE();
191 }
192 if (nobj)
193 copyAttr(obj, nobj);
194
195 return nobj;
196}
197
203
204static Agedge_t *mapEdge(Dt_t *emap, Agedge_t *e) {
205 edgepair_t *ep = dtmatch(emap, &e);
206 if (ep)
207 return ep->val;
208 else
209 return NULL;
210}
211
212/* cloneSubg:
213 * Clone subgraph sg in tgt.
214 */
215static Agraph_t *cloneSubg(Agraph_t *tgt, Agraph_t *g, Dt_t *emap) {
216 Agraph_t *ng;
217 Agraph_t *sg;
218 Agnode_t *t;
219 Agnode_t *newt;
220 Agedge_t *e;
221 Agedge_t *newe;
222 char *name;
223
224 ng = (Agraph_t *)copy(tgt, OBJ(g));
225 if (!ng)
226 return 0;
227 for (t = agfstnode(g); t; t = agnxtnode(g, t)) {
228 newt = agnode(tgt, agnameof(t), 0);
229 if (!newt) {
230 exerror("node %s not found in cloned graph %s", agnameof(t),
231 agnameof(tgt));
232 return 0;
233 } else
234 agsubnode(ng, newt, 1);
235 }
236 for (t = agfstnode(g); t; t = agnxtnode(g, t)) {
237 for (e = agfstout(g, t); e; e = agnxtout(g, e)) {
238 newe = mapEdge(emap, e);
239 if (!newe) {
240 name = agnameof(AGMKOUT(e));
241 if (name)
242 exerror("edge (%s,%s)[%s] not found in cloned graph %s",
243 agnameof(agtail(e)), agnameof(aghead(e)), name,
244 agnameof(tgt));
245 else
246 exerror("edge (%s,%s) not found in cloned graph %s",
247 agnameof(agtail(e)), agnameof(aghead(e)), agnameof(tgt));
248 return 0;
249 } else
250 agsubedge(ng, newe, 1);
251 }
252 }
253 for (sg = agfstsubg(g); sg; sg = agnxtsubg(sg)) {
254 if (!cloneSubg(ng, sg, emap)) {
255 exerror("error cloning subgraph %s from graph %s", agnameof(sg),
256 agnameof(g));
257 return 0;
258 }
259 }
260 return ng;
261}
262
263static int cmppair(void *k1, void *k2) {
264 const Agedge_t **key1 = k1;
265 const Agedge_t **key2 = k2;
266 if (*key1 > *key2)
267 return 1;
268 else if (*key1 < *key2)
269 return -1;
270 else
271 return 0;
272}
273
275 .key = offsetof(edgepair_t, key),
276 .size = sizeof(Agedge_t *),
277 .link = offsetof(edgepair_t, link),
278 .comparf = cmppair,
279};
280
281/* cloneGraph:
282 * Clone node, edge and subgraph structure from src to tgt.
283 */
284static void cloneGraph(Agraph_t *tgt, Agraph_t *src) {
285 Agedge_t *e;
286 Agedge_t *ne;
287 Agnode_t *t;
288 Agraph_t *sg;
289 char *name;
290 Dt_t *emap = dtopen(&edgepair, Dtoset);
291 edgepair_t *data = gv_calloc(agnedges(src), sizeof(edgepair_t));
292 edgepair_t *ep = data;
293
294 for (t = agfstnode(src); t; t = agnxtnode(src, t)) {
295 if (!copy(tgt, OBJ(t))) {
296 exerror("error cloning node %s from graph %s", agnameof(t),
297 agnameof(src));
298 }
299 }
300 for (t = agfstnode(src); t; t = agnxtnode(src, t)) {
301 for (e = agfstout(src, t); e; e = agnxtout(src, e)) {
302 if (!(ne = (Agedge_t *)copy(tgt, OBJ(e)))) {
303 name = agnameof(AGMKOUT(e));
304 if (name)
305 exerror("error cloning edge (%s,%s)[%s] from graph %s",
306 agnameof(agtail(e)), agnameof(aghead(e)), name,
307 agnameof(src));
308 else
309 exerror("error cloning edge (%s,%s) from graph %s",
310 agnameof(agtail(e)), agnameof(aghead(e)), agnameof(src));
311 goto done;
312 }
313 ep->key = e;
314 ep->val = ne;
315 dtinsert(emap, ep++);
316 }
317 }
318 for (sg = agfstsubg(src); sg; sg = agnxtsubg(sg)) {
319 if (!cloneSubg(tgt, sg, emap)) {
320 exerror("error cloning subgraph %s from graph %s", agnameof(sg),
321 agnameof(src));
322 }
323 }
324
325done:
326 dtclose(emap);
327 free(data);
328}
329
330/* cloneG:
331 */
332Agraph_t *cloneG(Agraph_t *g, char *name) {
333 Agraph_t *ng;
334
335 if (!name || *name == '\0')
336 name = agnameof(g);
337 ng = openG(name, g->desc);
338 if (ng) {
339 copyAttr((Agobj_t *)g, (Agobj_t *)ng);
340 cloneGraph(ng, g);
341 }
342 return ng;
343}
344
345/* cloneO:
346 * Create new object of type AGTYPE(obj) with all of its
347 * attributes and substructure.
348 * If obj is an edge, end nodes are cloned if necessary.
349 * If obj is a graph, if g is null, create a clone top-level
350 * graph. Otherwise, create a clone subgraph of g.
351 * Assume obj != NULL.
352 */
354 Agobj_t *nobj = 0;
355 Agedge_t *e;
356 Agnode_t *h;
357 Agnode_t *t;
358 int kind = AGTYPE(obj);
359 char *name;
360
361 if (kind != AGRAPH && !g) {
362 exerror("NULL graph with non-graph object in clone()");
363 return 0;
364 }
365
366 switch (kind) {
367 case AGNODE: /* same as copy node */
368 name = agnameof(obj);
369 nobj = (Agobj_t *)openNode(g, name);
370 if (nobj)
371 copyAttr(obj, nobj);
372 break;
373 case AGRAPH:
374 name = agnameof(obj);
375 if (g)
376 nobj = (Agobj_t *)openSubg(g, name);
377 else
378 nobj = (Agobj_t *)openG(name, ((Agraph_t *)obj)->desc);
379 if (nobj) {
380 copyAttr(obj, nobj);
381 cloneGraph((Agraph_t *)nobj, (Agraph_t *)obj);
382 }
383 break;
384 case AGINEDGE:
385 case AGOUTEDGE:
386 e = (Agedge_t *)obj;
387 t = (Agnode_t *)cloneO(g, OBJ(agtail(e)));
388 h = (Agnode_t *)cloneO(g, OBJ(aghead(e)));
389 name = agnameof(AGMKOUT(e));
390 nobj = (Agobj_t *)openEdge(g, t, h, name);
391 if (nobj)
392 copyAttr(obj, nobj);
393 break;
394 default:
395 UNREACHABLE();
396 }
397
398 return nobj;
399}
400
401#define CCMARKED(n) (((nData(n))->iu.integer) & 2)
402#define CCMARK(n) (((nData(n))->iu.integer) |= 2)
403#define CCUNMARK(n) (((nData(n))->iu.integer) &= ~2)
404
405static void cc_dfs(Agraph_t *g, Agraph_t *comp, Agnode_t *n) {
406 Agedge_t *e;
407 Agnode_t *other;
408
409 CCMARK(n);
410 agidnode(comp, AGID(n), 1);
411 for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
412 if (agtail(e) == n)
413 other = aghead(e);
414 else
415 other = agtail(e);
416 if (!CCMARKED(other))
417 cc_dfs(g, comp, other);
418 }
419}
420
421/* compOf:
422 * Return connected component of node.
423 */
425 Agraph_t *cg;
426 Agnode_t *np;
427 static int id;
428 char name[64];
429
430 if (!(n = agidnode(g, AGID(n), 0)))
431 return 0; /* n not in g */
432 for (np = agfstnode(g); np; np = agnxtnode(g, np))
433 CCUNMARK(np);
434
435 snprintf(name, sizeof(name), "_cc_%d", id++);
436 cg = openSubg(g, name);
437 cc_dfs(g, cg, n);
438
439 return cg;
440}
441
442/* isEdge:
443 * Return edge, if any, between t and h with given key.
444 * Edge is in g.
445 */
446Agedge_t *isEdge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *key) {
447 Agraph_t *root;
448
449 root = sameG(t, h, "isEdge", "tail and head node");
450 if (!root)
451 return 0;
452 if (g) {
453 if (root != agroot(g))
454 return 0;
455 } else
456 g = root;
457
458 return agedge(g, t, h, key, 0);
459}
460
461/* addNode:
462 * Insert node n into subgraph g.
463 * Return image of n
464 */
465Agnode_t *addNode(Agraph_t *gp, Agnode_t *np, int doAdd) {
466 if (!sameG(gp, np, "addNode", 0))
467 return 0;
468 return agsubnode(gp, np, doAdd);
469}
470
471/* addEdge:
472 * Insert edge e into subgraph g.
473 * Return image of e
474 */
475Agedge_t *addEdge(Agraph_t *gp, Agedge_t *ep, int doAdd) {
476 if (!sameG(gp, ep, "addEdge", 0))
477 return 0;
478 return agsubedge(gp, ep, doAdd);
479}
480
481/* lockGraph:
482 * Set lock so that graph g will not be deleted.
483 * g must be a root graph.
484 * If v > 0, set lock
485 * If v = 0, unset lock and delete graph is necessary.
486 * If v < 0, no op
487 * Always return previous lock state.
488 * Return -1 on error.
489 */
490int lockGraph(Agraph_t *g, int v) {
491 gdata *data;
492
493 if (g != agroot(g)) {
494 error(ERROR_WARNING, "Graph argument to lock() is not a root graph");
495 return -1;
496 }
497 data = gData(g);
498 const int oldv = data->lock.locked;
499 if (v > 0)
500 data->lock.locked = true;
501 else if (v == 0 && oldv) {
502 if (data->lock.zombie)
503 agclose(g);
504 else
505 data->lock = (lock_t){0};
506 }
507 return oldv;
508}
509
510/* deleteObj:
511 * Remove obj from g.
512 * obj may belong to a subgraph of g, so we first must map
513 * obj to its version in g.
514 * If g is null, remove object from root graph.
515 * If obj is a (sub)graph, close it. The g parameter is unused.
516 * Return 0 on success, non-zero on failure.
517 */
519 gdata *data;
520 if (AGTYPE(obj) == AGRAPH) {
521 g = (Agraph_t *)obj;
522 if (g != agroot(g))
523 return agclose(g);
524 data = gData(g);
525 if (data->lock.locked) {
526 error(ERROR_WARNING, "Cannot delete locked graph %s", agnameof(g));
527 data->lock.zombie = true;
528 return -1;
529 } else
530 return agclose(g);
531 }
532
533 /* node or edge */
534 if (!g)
535 g = agroot(agraphof(obj));
536 if (obj)
537 return agdelete(g, obj);
538 else
539 return -1;
540}
541
542/* sfioWrite:
543 * If the graph is passed in from a library, its output discipline
544 * might not use sfio. In this case, we push an sfio discipline on
545 * the graph, write it, and then pop it off.
546 */
547int sfioWrite(Agraph_t *g, FILE *fp) {
548 int rv;
549
550 Agiodisc_t *saveio = g->clos->disc.io;
551 g->clos->disc.io = &gprIoDisc;
552 rv = agwrite(g, fp);
553 g->clos->disc.io = saveio;
554 return rv;
555}
556
557/* writeFile:
558 * Write graph into file f.
559 * Return 0 on success
560 */
561int writeFile(Agraph_t *g, char *f) {
562 int rv;
563
564 if (!f) {
565 exerror("NULL string passed to writeG");
566 return 1;
567 }
568 FILE *fp = fopen(f, "w");
569 if (!fp) {
570 exwarn("Could not open %s for writing in writeG", f);
571 return 1;
572 }
573 rv = sfioWrite(g, fp);
574 fclose(fp);
575 return rv;
576}
577
578/* readFile:
579 * Read graph from file f.
580 * Return 0 on failure
581 */
583 Agraph_t *gp;
584
585 if (!f) {
586 exerror("NULL string passed to readG");
587 return 0;
588 }
589 FILE *fp = fopen(f, "r");
590 if (!fp) {
591 exwarn("Could not open %s for reading in readG", f);
592 return 0;
593 }
594 gp = readG(fp);
595 fclose(fp);
596
597 return gp;
598}
599
600int fwriteFile(Expr_t *ex, Agraph_t *g, long long fd) {
601 FILE *sp;
602
603 if (fd < 0 || fd >= (long long)elementsof(ex->file) || !(sp = ex->file[fd])) {
604 exerror("fwriteG: %lld: invalid descriptor", fd);
605 return 0;
606 }
607 return sfioWrite(g, sp);
608}
609
610Agraph_t *freadFile(Expr_t *ex, long long fd) {
611 FILE *sp;
612
613 if (fd < 0 || fd >= (long long)elementsof(ex->file) || !(sp = ex->file[fd])) {
614 exerror("freadG: %lld: invalid descriptor", fd);
615 return 0;
616 }
617 return readG(sp);
618}
619
620int openFile(Expr_t *ex, const char *fname, const char *mode) {
621 int idx;
622
623 /* find open index */
624 for (idx = 3; idx < elementsof(ex->file); idx++)
625 if (!ex->file[idx])
626 break;
627 if (idx == elementsof(ex->file)) {
628 exerror("openF: no available descriptors");
629 return -1;
630 }
631 ex->file[idx] = fopen(fname, mode);
632 if (ex->file[idx])
633 return idx;
634 else
635 return -1;
636}
637
638int closeFile(Expr_t *ex, long long fd) {
639 int rv;
640
641 if (0 <= fd && fd <= 2) {
642 exerror("closeF: cannot close standard stream %lld", fd);
643 return -1;
644 }
645 if (fd < 0 || fd >= (long long)elementsof(ex->file)) {
646 exerror("closeG: %lld: invalid descriptor", fd);
647 return -1;
648 }
649 if (!ex->file[fd]) {
650 exerror("closeF: stream %lld not open", fd);
651 return -1;
652 }
653 rv = fclose(ex->file[fd]);
654 if (!rv)
655 ex->file[fd] = 0;
656 return rv;
657}
658
659/*
660 * Read single line from stream.
661 * Return "" on EOF.
662 */
663char *readLine(Expr_t *ex, long long fd) {
664 FILE *sp;
665 int c;
666 char *line;
667
668 if (fd < 0 || fd >= (long long)elementsof(ex->file) || !(sp = ex->file[fd])) {
669 exerror("readL: %lld: invalid descriptor", fd);
670 return "";
671 }
672
673 agxbuf tmps = {0};
674 while ((c = getc(sp)) > 0 && c != '\n')
675 agxbputc(&tmps, (char)c);
676 if (c == '\n')
677 agxbputc(&tmps, (char)c);
678 line = exstring(ex, agxbuse(&tmps));
679 agxbfree(&tmps);
680 return line;
681}
682
683/* compare:
684 * Lexicographic ordering of objects.
685 */
687 char lkind, rkind;
688 if (l == NULL) {
689 if (r == NULL)
690 return 0;
691 else
692 return -1;
693 } else if (r == NULL) {
694 return 1;
695 }
696 if (AGID(l) < AGID(r))
697 return -1;
698 else if (AGID(l) > AGID(r))
699 return 1;
700 lkind = AGTYPE(l);
701 rkind = AGTYPE(r);
702 if (lkind == 3)
703 lkind = 2;
704 if (rkind == 3)
705 rkind = 2;
706 if (lkind == rkind)
707 return 0;
708 else if (lkind < rkind)
709 return -1;
710 else
711 return 1;
712}
713
714/* toLower:
715 * Convert characters to lowercase
716 */
717char *toLower(Expr_t *pgm, char *src) {
718
719 const size_t len = strlen(src);
720 char *dst = exstralloc(pgm, len + 1);
721 if (dst == NULL) {
722 return NULL;
723 }
724
725 for (size_t i = 0; i < len; ++i) {
726 dst[i] = gv_tolower(src[i]);
727 }
728
729 dst[len] = '\0';
730 return dst;
731}
732
733/* toUpper:
734 * Convert characters to uppercase
735 */
736char *toUpper(Expr_t *pgm, char *src) {
737
738 const size_t len = strlen(src);
739 char *dst = exstralloc(pgm, len + 1);
740 if (dst == NULL) {
741 return NULL;
742 }
743
744 for (size_t i = 0; i < len; ++i) {
745 dst[i] = gv_toupper(src[i]);
746 }
747
748 dst[len] = '\0';
749 return dst;
750}
751
752/* toHtml:
753 * Create a string marked as HTML
754 */
755char *toHtml(Agraph_t *g, char *arg) { return agstrdup_html(g, arg); }
756
757/* canon:
758 * Canonicalize a string for printing.
759 */
760char *canon(Expr_t *pgm, char *arg) {
761 char *p;
762
763 p = agcanonStr(arg);
764 if (p != arg)
765 p = exstring(pgm, p);
766
767 return p;
768}
769
770#undef S
771
772// force the upcoming ../common/colxlate.c functions to not be exported
773#ifdef COLORPROCS_API
774#undef COLORPROCS_API
775#endif
776#ifdef GVDLL
777#undef GVDLL
778#endif
779#ifdef GVC_EXPORTS
780#undef GVC_EXPORTS
781#endif
782#define COLORPROCS_API static UNUSED
783
784#include "../common/colxlate.c"
785
786/* colorx:
787 * RGB, RGBA, HSV, HSVA
788 */
789char *colorx(Expr_t *ex, const char *incolor, char *fmt) {
790 gvcolor_t color = {{{0}}, 0};
792 int rc;
793 int alpha;
794
795 if (*fmt == '\0' || *incolor == '\0')
796 return "";
797 if (*fmt == 'R') {
798 type = RGBA_BYTE;
799 if (!strcmp(fmt, "RGBA"))
800 alpha = 1;
801 else
802 alpha = 0;
803 } else if (*fmt == 'H') {
805 if (!strcmp(fmt, "HSVA"))
806 alpha = 1;
807 else
808 alpha = 0;
809 } else
810 return "";
811
812 rc = colorxlate(incolor, &color, type);
813 if (rc != COLOR_OK)
814 return "";
815
816 agxbuf fp = {0};
817
818 switch (type) {
819 case HSVA_DOUBLE:
820 agxbprint(&fp, "%.03f %.03f %.03f", color.u.HSVA[0], color.u.HSVA[1],
821 color.u.HSVA[2]);
822 if (alpha)
823 agxbprint(&fp, " %.03f", color.u.HSVA[3]);
824 break;
825 case RGBA_BYTE:
826 agxbprint(&fp, "#%02x%02x%02x", color.u.rgba[0], color.u.rgba[1],
827 color.u.rgba[2]);
828 if (alpha)
829 agxbprint(&fp, "%02x", color.u.rgba[3]);
830 break;
831 default:
832 break;
833 }
834
835 char *result = exstring(ex, agxbuse(&fp));
836 agxbfree(&fp);
837 return result;
838}
839
840#ifndef _WIN32
841
842#include <sys/param.h>
843#include <sys/times.h>
844#include <sys/types.h>
845#include <unistd.h>
846
847#ifndef HZ
848#define HZ 60
849#endif
850typedef struct tms mytime_t;
851#define GET_TIME(S) times(&(S))
852#define DIFF_IN_SECS(S, T) \
853 ((S.tms_utime + S.tms_stime - T.tms_utime - T.tms_stime) / (double)HZ)
854
855#else
856
857#include <time.h>
858
859typedef clock_t mytime_t;
860#define GET_TIME(S) S = clock()
861#define DIFF_IN_SECS(S, T) ((S - T) / (double)CLOCKS_PER_SEC)
862
863#endif
864
865static mytime_t T;
866
867void gvstart_timer(void) { GET_TIME(T); }
868
869double gvelapsed_sec(void) {
870 mytime_t S;
871 double rv;
872
873 GET_TIME(S);
874 rv = DIFF_IN_SECS(S, T);
875 return rv;
876}
Agedge_t * isEdge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *key)
Definition actions.c:446
int indexOf(char *s1, char *s2)
Definition actions.c:72
int openFile(Expr_t *ex, const char *fname, const char *mode)
Definition actions.c:620
Agraph_t * sameG(void *p1, void *p2, char *fn, char *msg)
Definition actions.c:52
static Agedge_t * mapEdge(Dt_t *emap, Agedge_t *e)
Definition actions.c:204
Agraph_t * freadFile(Expr_t *ex, long long fd)
Definition actions.c:610
Agobj_t * cloneO(Agraph_t *g, Agobj_t *obj)
Definition actions.c:353
#define CCUNMARK(n)
Definition actions.c:403
size_t match(char *str, char *pat)
Definition actions.c:106
int deleteObj(Agraph_t *g, Agobj_t *obj)
Definition actions.c:518
int writeFile(Agraph_t *g, char *f)
Definition actions.c:561
int fwriteFile(Expr_t *ex, Agraph_t *g, long long fd)
Definition actions.c:600
long rindexOf(char *s1, char *s2)
Definition actions.c:80
int sfioWrite(Agraph_t *g, FILE *fp)
Definition actions.c:547
struct tms mytime_t
Definition actions.c:850
static void cloneGraph(Agraph_t *tgt, Agraph_t *src)
Definition actions.c:284
#define DIFF_IN_SECS(S, T)
Definition actions.c:852
void gvstart_timer(void)
Definition actions.c:867
#define CCMARKED(n)
Definition actions.c:401
static int iofread(void *chan, char *buf, int bufsize)
Definition actions.c:36
char * toLower(Expr_t *pgm, char *src)
Definition actions.c:717
Agraph_t * cloneG(Agraph_t *g, char *name)
Definition actions.c:332
int lockGraph(Agraph_t *g, int v)
Definition actions.c:490
char * canon(Expr_t *pgm, char *arg)
Definition actions.c:760
#define CCMARK(n)
Definition actions.c:402
int copyAttr(Agobj_t *src, Agobj_t *tgt)
Definition actions.c:122
static void cc_dfs(Agraph_t *g, Agraph_t *comp, Agnode_t *n)
Definition actions.c:405
char * toHtml(Agraph_t *g, char *arg)
Definition actions.c:755
Agnode_t * addNode(Agraph_t *gp, Agnode_t *np, int doAdd)
Definition actions.c:465
static Dtdisc_t edgepair
Definition actions.c:274
int compare(Agobj_t *l, Agobj_t *r)
Definition actions.c:686
Agobj_t * copy(Agraph_t *g, Agobj_t *obj)
Definition actions.c:156
static int cmppair(void *k1, void *k2)
Definition actions.c:263
double gvelapsed_sec(void)
Definition actions.c:869
Agraph_t * readFile(char *f)
Definition actions.c:582
static mytime_t T
Definition actions.c:865
Agraph_t * compOf(Agraph_t *g, Agnode_t *n)
Definition actions.c:424
char * colorx(Expr_t *ex, const char *incolor, char *fmt)
Definition actions.c:789
static Agraph_t * cloneSubg(Agraph_t *tgt, Agraph_t *g, Dt_t *emap)
Definition actions.c:215
Agedge_t * addEdge(Agraph_t *gp, Agedge_t *ep, int doAdd)
Definition actions.c:475
static int ioflush(void *chan)
Definition actions.c:44
static Agiodisc_t gprIoDisc
Definition actions.c:46
char * toUpper(Expr_t *pgm, char *src)
Definition actions.c:736
#define GET_TIME(S)
Definition actions.c:851
static int ioputstr(void *chan, const char *str)
Definition actions.c:42
#define KINDS(p)
Definition actions.c:33
char * readLine(Expr_t *ex, long long fd)
Definition actions.c:663
int closeFile(Expr_t *ex, long long fd)
Definition actions.c:638
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:78
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:234
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:307
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:277
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define elementsof(x)
Definition ast.h:33
int strgrpmatch(char *, char *, size_t *, int, int)
Definition strmatch.c:505
#define dtmatch(d, o)
Definition cdt.h:184
#define dtinsert(d, o)
Definition cdt.h:185
CDT_API int dtclose(Dt_t *)
Definition dtclose.c:8
CDT_API Dtmethod_t * Dtoset
ordered set (self-adjusting tree)
Definition dttree.c:304
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
Definition dtopen.c:9
#define sub(h, i)
Definition closest.c:67
color_type_t
Definition color.h:26
@ HSVA_DOUBLE
Definition color.h:26
@ RGBA_BYTE
Definition color.h:26
#define COLOR_OK
Definition color.h:44
void colorxlate(char *str, agxbuf *buf)
Definition colxlate.c:46
Agedge_t * openEdge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *key)
Definition compile.c:2526
Agraph_t * openG(char *name, Agdesc_t desc)
Definition compile.c:2487
Agraph_t * openSubg(Agraph_t *g, char *name)
Definition compile.c:2502
Agnode_t * openNode(Agraph_t *g, char *name)
Definition compile.c:2514
Agraph_t * readG(FILE *fp)
Definition compile.c:2469
#define OBJ(p)
Definition compile.h:52
#define gData(g)
Definition compile.h:58
mode
Definition cvtgxl.c:33
static char * fname
#define ERROR_WARNING
Definition error.h:35
void exwarn(const char *format,...)
Definition exerror.c:79
void exerror(const char *format,...)
Definition exerror.c:62
char * exstring(Expr_t *ex, char *s)
Definition exeval.c:2000
void * exstralloc(Expr_t *ex, size_t sz)
Definition exeval.c:2008
expr procedure type
Definition exparse.y:208
#define S
Definition expr.h:72
static double len(glCompPoint p)
Definition glutils.c:150
void free(void *)
#define SIZE_MAX
Definition gmlscan.c:347
node NULL
Definition grammar.y:163
int agnedges(Agraph_t *g)
Definition graph.c:175
Agsym_t * agattrsym(void *obj, char *name)
looks up a string attribute for a graph object given as an argument
Definition attr.c:156
Agsym_t * agattr(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up attributes of a graph
Definition attr.c:338
Agsym_t * agnxtattr(Agraph_t *g, int kind, Agsym_t *attr)
permits traversing the list of attributes of a given type
Definition attr.c:353
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:478
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:455
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:260
#define AGMKOUT(e)
Definition cgraph.h:874
Agedge_t * agsubedge(Agraph_t *g, Agedge_t *e, int createflag)
Definition edge.c:355
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:24
#define agtail(e)
Definition cgraph.h:880
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:94
#define aghead(e)
Definition cgraph.h:881
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:39
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:85
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:102
int agwrite(Agraph_t *g, void *chan)
Return 0 on success, EOF on failure.
Definition write.c:730
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:145
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
Agnode_t * agsubnode(Agraph_t *g, Agnode_t *n, int createflag)
Definition node.c:259
Agnode_t * agidnode(Agraph_t *g, IDTYPE id, int createflag)
Definition node.c:122
Agraph_t * agraphof(void *obj)
Definition obj.c:185
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
#define AGID(obj)
returns the unique integer ID associated with the object
Definition cgraph.h:221
#define AGTYPE(obj)
returns AGRAPH, AGNODE, or AGEDGE depending on the type of the object
Definition cgraph.h:216
int agdelete(Agraph_t *g, void *obj)
deletes object. Equivalent to agclose, agdelnode, and agdeledge for obj being a graph,...
Definition obj.c:20
Agraph_t * agroot(void *obj)
Definition obj.c:168
@ AGOUTEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGINEDGE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
int aghtmlstr(const char *)
Definition refstr.c:164
int agstrfree(Agraph_t *, const char *)
Definition refstr.c:139
char * agcanonStr(char *str)
Definition write.c:239
char * agstrdup_html(Agraph_t *, const char *)
Definition refstr.c:135
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:78
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:83
static uint64_t id
Definition gv2gml.c:42
Agraph_t * read(FILE *f)
Definition gv.cpp:60
replacements for ctype.h functions
static char gv_toupper(int c)
Definition gv_ctype.h:93
static char gv_tolower(int c)
Definition gv_ctype.h:81
static void color(Agraph_t *g)
Definition gvcolor.c:129
textitem scanner parser str
Definition htmlparse.y:224
table Syntax error
Definition htmlparse.y:294
NEATOPROCS_API void s1(graph_t *, node_t *)
Definition stuff.c:671
#define alpha
Definition shapes.c:4058
static double cg(SparseMatrix A, const double *precond, int n, int dim, double *x0, double *rhs, double tol, double maxit)
platform abstraction for case-insensitive string functions
Agdisc_t disc
Definition cgraph.h:412
Agiodisc_t * io
Definition cgraph.h:339
IO services.
Definition cgraph.h:327
a generic header of Agraph_s, Agnode_s and Agedge_s
Definition cgraph.h:210
graph or subgraph
Definition cgraph.h:425
Agclos_t * clos
shared resources
Definition cgraph.h:435
Agdesc_t desc
Definition cgraph.h:427
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:637
char * name
Definition cgraph.h:639
char * defval
Definition cgraph.h:640
Definition expr.h:200
FILE * file[10]
Definition expr.h:203
union agxbuf::@121 u
Definition legal.c:50
Definition cdt.h:100
int key
Definition cdt.h:85
Dtlink_t link
Definition actions.c:199
Agedge_t * key
Definition actions.c:200
Agedge_t * val
Definition actions.c:201
struct tms mytime_t
Definition timing.c:24
Definition grammar.c:93
#define UNREACHABLE()
Definition unreachable.h:30
abstraction for squashing compiler warnings for unused symbols