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