Graphviz 14.0.1~dev.20250923.2039
Loading...
Searching...
No Matches
write.c
Go to the documentation of this file.
1
9/*************************************************************************
10 * Copyright (c) 2011 AT&T Intellectual Property
11 * All rights reserved. This program and the accompanying materials
12 * are made available under the terms of the Eclipse Public License v1.0
13 * which accompanies this distribution, and is available at
14 * https://www.eclipse.org/legal/epl-v10.html
15 *
16 * Contributors: Details at https://graphviz.org
17 *************************************************************************/
18
19#include <assert.h>
20#include <limits.h>
21#include <stdbool.h>
22#include <stdio.h> /* need sprintf() */
23#include <stdlib.h>
24#include <ctype.h>
25#include <cgraph/agstrcanon.h>
26#include <cgraph/cghdr.h>
27#include <inttypes.h>
28#include <util/gv_ctype.h>
29#include <util/strcasecmp.h>
30
31#define EMPTY(s) (((s) == 0) || (s)[0] == '\0')
32#define MAX(a,b) ((a)>(b)?(a):(b))
33#define CHKRV(v) {if ((v) == EOF) return EOF;}
34
35typedef void iochan_t;
36
37static int ioput(Agraph_t * g, iochan_t * ofile, char *str)
38{
39 return AGDISC(g, io)->putstr(ofile, str);
40
41}
42
43#define MAX_OUTPUTLINE 128
44#define MIN_OUTPUTLINE 60
45static int Level;
48
49typedef struct {
50 uint64_t *preorder_number; // of a graph or subgraph
51 uint64_t *node_last_written; // postorder number of subg when node was last written
52 uint64_t *edge_last_written; // postorder number of subg when edge was last written
54
55static int write_body(Agraph_t *g, iochan_t *ofile, write_info_t *wr_info);
56
58static void after_write(write_info_t);
59
60static int indent(Agraph_t * g, iochan_t * ofile)
61{
62 int i;
63 for (i = Level; i > 0; i--)
64 CHKRV(ioput(g, ofile, "\t"));
65 return 0;
66}
67
68// alphanumeric, '.', '-', or non-ascii; basically, chars used in unquoted ids
69static bool is_id_char(char c) {
70 return gv_isalnum(c) || c == '.' || c == '-' || !isascii(c);
71}
72
73// is the prefix of this string a recognized Graphviz escape sequence?
74// https://graphviz.org/docs/attr-types/escString/
75static bool is_escape(const char *str) {
76 assert(str != NULL);
77
78 if (*str != '\\')
79 return false;
80
81 if (str[1] == 'E')
82 return true;
83 if (str[1] == 'G')
84 return true;
85 if (str[1] == 'H')
86 return true;
87 if (str[1] == 'L')
88 return true;
89 if (str[1] == 'N')
90 return true;
91 if (str[1] == 'T')
92 return true;
93
94 if (str[1] == 'l')
95 return true;
96 if (str[1] == 'n')
97 return true;
98 if (str[1] == 'r')
99 return true;
100
101 if (str[1] == '\\')
102 return true;
103
104 if (str[1] == '"')
105 return true;
106
107 return false;
108}
109
110/* Canonicalize ordinary strings.
111 * Assumes buf is large enough to hold output.
112 */
113static char *_agstrcanon(char *arg, char *buf)
114{
115 char *s, *p;
116 char uc;
117 int cnt = 0, dotcnt = 0;
118 bool needs_quotes = false;
119 bool part_of_escape = false;
120 bool maybe_num;
121 bool backslash_pending = false;
122 static const char *tokenlist[] /* must agree with scan.l */
123 = { "node", "edge", "strict", "graph", "digraph", "subgraph",
124 NULL
125 };
126 const char **tok;
127
128 if (EMPTY(arg))
129 return "\"\"";
130 s = arg;
131 p = buf;
132 *p++ = '\"';
133 uc = *s++;
134 maybe_num = gv_isdigit(uc) || uc == '.' || uc == '-';
135 while (uc) {
136 if (uc == '\"' && !part_of_escape) {
137 *p++ = '\\';
138 needs_quotes = true;
139 } else if (!part_of_escape && is_escape(&s[-1])) {
140 needs_quotes = true;
141 part_of_escape = true;
142 } else if (maybe_num) {
143 if (uc == '-') {
144 if (cnt) {
145 maybe_num = false;
146 needs_quotes = true;
147 }
148 }
149 else if (uc == '.') {
150 if (dotcnt++) {
151 maybe_num = false;
152 needs_quotes = true;
153 }
154 }
155 else if (!gv_isdigit(uc)) {
156 maybe_num = false;
157 needs_quotes = true;
158 }
159 part_of_escape = false;
160 }
161 else if (!(gv_isalnum(uc) || uc == '_' || !isascii(uc))) {
162 needs_quotes = true;
163 part_of_escape = false;
164 } else {
165 part_of_escape = false;
166 }
167 *p++ = uc;
168 uc = *s++;
169 cnt++;
170
171 /* If breaking long strings into multiple lines, only allow breaks after a non-id char, not a backslash, where the next char is an
172 * id char.
173 */
174 if (Max_outputline) {
175 if (uc && backslash_pending && !(is_id_char(p[-1]) || p[-1] == '\\') && is_id_char(uc)) {
176 *p++ = '\\';
177 *p++ = '\n';
178 needs_quotes = true;
179 backslash_pending = false;
180 cnt = 0;
181 } else if (uc && (cnt >= Max_outputline)) {
182 if (!(is_id_char(p[-1]) || p[-1] == '\\') && is_id_char(uc)) {
183 *p++ = '\\';
184 *p++ = '\n';
185 needs_quotes = true;
186 cnt = 0;
187 } else {
188 backslash_pending = true;
189 }
190 }
191 }
192 }
193 *p++ = '\"';
194 *p = '\0';
195 if (needs_quotes || (cnt == 1 && (*arg == '.' || *arg == '-')))
196 return buf;
197
198 /* Use quotes to protect tokens (example, a node named "node") */
199 /* It would be great if it were easier to use flex here. */
200 for (tok = tokenlist; *tok; tok++)
201 if (!strcasecmp(*tok, arg))
202 return buf;
203 return arg;
204}
205
209static char *agcanonhtmlstr(const char *arg, char *buf)
210{
211 sprintf(buf, "<%s>", arg);
212 return buf;
213}
214
219char *agstrcanon(char *arg, char *buf)
220{
221 if (aghtmlstr(arg))
222 return agcanonhtmlstr(arg, buf);
223 else
224 return _agstrcanon(arg, buf);
225}
226
227static int _write_canonstr(Agraph_t *g, iochan_t *ofile, char *str, bool chk) {
228
229 // maximum bytes required for canonicalized string
230 const size_t required = agstrcanon_bytes(str);
231
232 // allocate space to stage the canonicalized string
233 char *const scratch = malloc(required);
234 if (scratch == NULL) {
235 return EOF;
236 }
237
238 char *const canonicalized =
239 chk ? agstrcanon(str, scratch) : _agstrcanon(str, scratch);
240 const int rc = ioput(g, ofile, canonicalized);
241 free(scratch);
242 return rc;
243}
244
246static int write_canonstr(Agraph_t *g, iochan_t *ofile, char *str, bool known) {
247 char *s;
248
249 /* str may not have been allocated by agstrdup, so we first need to turn it
250 * into a valid refstr
251 */
252 s = known ? str : agstrdup(g, str);
253
254 int r = _write_canonstr(g, ofile, s, true);
255
256 if (!known) {
257 agstrfree(g, s, false);
258 }
259 return r;
260}
261
262static int write_dict(Agraph_t * g, iochan_t * ofile, char *name,
263 Dict_t * dict, bool top) {
264 int cnt = 0;
265 Dict_t *view;
266 Agsym_t *sym, *psym;
267
268 if (!top)
269 view = dtview(dict, NULL);
270 else
271 view = 0;
272 for (sym = dtfirst(dict); sym; sym = dtnext(dict, sym)) {
273 if (EMPTY(sym->defval) && !sym->print) { /* try to skip empty str (default) */
274 if (view == NULL)
275 continue; /* no parent */
276 psym = dtsearch(view, sym);
277 assert(psym);
278 if (EMPTY(psym->defval) && psym->print)
279 continue; /* also empty in parent */
280 }
281 if (cnt++ == 0) {
282 CHKRV(indent(g, ofile));
283 CHKRV(ioput(g, ofile, name));
284 CHKRV(ioput(g, ofile, " ["));
285 Level++;
286 } else {
287 CHKRV(ioput(g, ofile, ",\n"));
288 CHKRV(indent(g, ofile));
289 }
290 CHKRV(write_canonstr(g, ofile, sym->name, true));
291 CHKRV(ioput(g, ofile, "="));
292 CHKRV(write_canonstr(g, ofile, sym->defval, true));
293 }
294 if (cnt > 0) {
295 Level--;
296 if (cnt > 1) {
297 CHKRV(ioput(g, ofile, "\n"));
298 CHKRV(indent(g, ofile));
299 }
300 CHKRV(ioput(g, ofile, "];\n"));
301 }
302 if (!top)
303 dtview(dict, view); /* restore previous view */
304 return 0;
305}
306
307static int write_dicts(Agraph_t *g, iochan_t *ofile, bool top) {
308 Agdatadict_t *def;
309 if ((def = agdatadict(g, false))) {
310 CHKRV(write_dict(g, ofile, "graph", def->dict.g, top));
311 CHKRV(write_dict(g, ofile, "node", def->dict.n, top));
312 CHKRV(write_dict(g, ofile, "edge", def->dict.e, top));
313 }
314 return 0;
315}
316
317static int write_hdr(Agraph_t *g, iochan_t *ofile, bool top) {
318 char *name, *sep, *kind, *strict;
319 bool root = false;
320 bool hasName = true;
321
322 strict = "";
323 if (!top && agparent(g))
324 kind = "sub";
325 else {
326 root = true;
327 if (g->desc.directed)
328 kind = "di";
329 else
330 kind = "";
331 if (agisstrict(g))
332 strict = "strict ";
335 }
336 name = agnameof(g);
337 sep = " ";
338 if (!name || name[0] == LOCALNAMEPREFIX) {
339 sep = name = "";
340 hasName = false;
341 }
342 CHKRV(indent(g, ofile));
343 CHKRV(ioput(g, ofile, strict));
344
345 /* output "<kind>graph" only for root graphs or graphs with names */
346 if (root || hasName) {
347 CHKRV(ioput(g, ofile, kind));
348 CHKRV(ioput(g, ofile, "graph "));
349 }
350 if (hasName)
351 CHKRV(write_canonstr(g, ofile, name, false));
352 CHKRV(ioput(g, ofile, sep));
353 CHKRV(ioput(g, ofile, "{\n"));
354 Level++;
355 CHKRV(write_dicts(g, ofile, top));
356 AGATTRWF(g) = true;
357 return 0;
358}
359
360static int write_trl(Agraph_t * g, iochan_t * ofile)
361{
362 (void)g;
363 Level--;
364 CHKRV(indent(g, ofile));
365 CHKRV(ioput(g, ofile, "}\n"));
366 return 0;
367}
368
373static bool is_anonymous(Agraph_t *g) {
374 assert(g != NULL);
375
376 // handle the common case inline for performance
377 if (AGDISC(g, id) == &AgIdDisc) {
378 // replicate `idprint`
379 const IDTYPE id = AGID(g);
380 if (id % 2 != 0) {
381 return true;
382 }
383 return *(char *)(uintptr_t)id == LOCALNAMEPREFIX;
384 }
385
386 const char *const name = agnameof(g);
387 return name == NULL || name[0] == LOCALNAMEPREFIX;
388}
389
391{
392 int i, n;
393 Agattr_t *sdata, *pdata, *rdata;
394 Agdatadict_t *dd;
395
396 if (!is_anonymous(g))
397 return false;
398 if ((sdata = agattrrec(g)) && (pdata = agattrrec(agparent(g)))) {
399 rdata = agattrrec(agroot(g));
400 n = dtsize(rdata->dict);
401 for (i = 0; i < n; i++)
402 if (sdata->str[i] && pdata->str[i]
403 && strcmp(sdata->str[i], pdata->str[i]))
404 return false;
405 }
406 dd = agdatadict(g, false);
407 if (!dd)
408 return true;
409 if (dtsize(dd->dict.n) > 0 || dtsize(dd->dict.e) > 0)
410 return false;
411 return true;
412}
413
414static bool has_no_edges(Agraph_t * g, Agnode_t * n)
415{
416 return agfstin(g, n) == NULL && agfstout(g, n) == NULL;
417}
418
420{
421 Agattr_t *data;
422 Agsym_t *sym;
423
424 (void)g;
425 if ((data = agattrrec(n))) {
426 for (sym = dtfirst(data->dict); sym; sym = dtnext(data->dict, sym)) {
427 if (data->str[sym->id] != sym->defval)
428 return true;
429 }
430 }
431 return false;
432}
433
434static int write_subgs(Agraph_t *g, iochan_t *ofile, write_info_t *wr_info) {
435 Agraph_t *subg;
436
437 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
438 if (irrelevant_subgraph(subg)) {
439 write_subgs(subg, ofile, wr_info);
440 }
441 else {
442 CHKRV(write_hdr(subg, ofile, false));
443 CHKRV(write_body(subg, ofile, wr_info));
444 CHKRV(write_trl(subg, ofile));
445 }
446 }
447 return 0;
448}
449
450static int write_edge_name(Agedge_t *e, iochan_t *ofile, bool terminate) {
451 char *p;
452 Agraph_t *g;
453
454 p = agnameof(e);
455 g = agraphof(e);
456 if (!EMPTY(p)) {
457 if (!terminate) {
458 Level++;
459 }
460 CHKRV(ioput(g, ofile, "\t[key="));
461 CHKRV(write_canonstr(g, ofile, p, false));
462 if (terminate)
463 CHKRV(ioput(g, ofile, "]"));
464 return 1;
465 }
466 return 0;
467}
468
469
470static int write_nondefault_attrs(void *obj, iochan_t * ofile,
471 Dict_t * defdict)
472{
473 Agattr_t *data;
474 Agsym_t *sym;
475 Agraph_t *g;
476 int cnt = 0;
477 int rv;
478
479 if (AGTYPE(obj) == AGINEDGE || AGTYPE(obj) == AGOUTEDGE) {
480 CHKRV(rv = write_edge_name(obj, ofile, false));
481 if (rv)
482 cnt++;
483 }
484 data = agattrrec(obj);
485 g = agraphof(obj);
486 if (data)
487 for (sym = dtfirst(defdict); sym; sym = dtnext(defdict, sym)) {
488 if (AGTYPE(obj) == AGINEDGE || AGTYPE(obj) == AGOUTEDGE) {
489 if (Tailport && sym->id == Tailport->id)
490 continue;
491 if (Headport && sym->id == Headport->id)
492 continue;
493 }
494 if (data->str[sym->id] != sym->defval) {
495 if (cnt++ == 0) {
496 CHKRV(ioput(g, ofile, "\t["));
497 Level++;
498 } else {
499 CHKRV(ioput(g, ofile, ",\n"));
500 CHKRV(indent(g, ofile));
501 }
502 CHKRV(write_canonstr(g, ofile, sym->name, true));
503 CHKRV(ioput(g, ofile, "="));
504 CHKRV(write_canonstr(g, ofile, data->str[sym->id], true));
505 }
506 }
507 if (cnt > 0) {
508 CHKRV(ioput(g, ofile, "]"));
509 Level--;
510 }
511 AGATTRWF(obj) = true;
512 return 0;
513}
514
515static int write_nodename(Agnode_t * n, iochan_t * ofile)
516{
517 char *name;
518 Agraph_t *g;
519
520 name = agnameof(n);
521 g = agraphof(n);
522 if (name) {
523 CHKRV(write_canonstr(g, ofile, name, false));
524 } else {
525 char buf[sizeof("__SUSPECT") + 20];
526 snprintf(buf, sizeof(buf), "_%" PRIu64 "_SUSPECT", AGID(n)); /* could be deadly wrong */
527 CHKRV(ioput(g, ofile, buf));
528 }
529 return 0;
530}
531
532static int attrs_written(void *obj)
533{
534 return AGATTRWF(obj);
535}
536
537static int write_node(Agraph_t *subg, Agnode_t *n, iochan_t *ofile, Dict_t *d,
538 write_info_t *wr_info) {
539 Agraph_t *g;
540
541 g = agraphof(n);
542 CHKRV(indent(g, ofile));
543 CHKRV(write_nodename(n, ofile));
544 if (!attrs_written(n))
545 CHKRV(write_nondefault_attrs(n, ofile, d));
546 wr_info->node_last_written[AGSEQ(n)] =
547 wr_info->preorder_number[AGSEQ(subg)];
548 return ioput(g, ofile, ";\n");
549}
550
551/* node must be written if it wasn't already emitted because of
552 * a subgraph or one of its predecessors, and if it is a singleton
553 * or has non-default attributes.
554 */
555static bool write_node_test(Agraph_t *g, Agnode_t *n, write_info_t *wr_info) {
556 /* test if node was already written in g or a subgraph of g */
557 if (wr_info->node_last_written[AGSEQ(n)] >=
558 wr_info->preorder_number[AGSEQ(g)]) return false;
559
560 if (has_no_edges(g, n) || not_default_attrs(g, n))
561 return true;
562 return false;
563}
564
565static int write_port(Agedge_t * e, iochan_t * ofile, Agsym_t * port)
566{
567 char *val;
568 Agraph_t *g;
569
570 if (!port)
571 return 0;
572 g = agraphof(e);
573 val = agxget(e, port);
574 if (val[0] == '\0')
575 return 0;
576
577 CHKRV(ioput(g, ofile, ":"));
578 if (aghtmlstr(val)) {
579 CHKRV(write_canonstr(g, ofile, val, true));
580 } else {
581 char *s = strchr(val, ':');
582 if (s) {
583 *s = '\0';
584 CHKRV(_write_canonstr(g, ofile, val, false));
585 CHKRV(ioput(g, ofile, ":"));
586 CHKRV(_write_canonstr(g, ofile, s + 1, false));
587 *s = ':';
588 } else {
589 CHKRV(_write_canonstr(g, ofile, val, false));
590 }
591 }
592 return 0;
593}
594
595static bool write_edge_test(Agraph_t *g, Agedge_t *e, write_info_t *wr_info) {
596 if (wr_info->edge_last_written[AGSEQ(e)] >=
597 wr_info->preorder_number[AGSEQ(g)]) return false;
598 return true;
599}
600
601static int write_edge(Agraph_t *subg, Agedge_t *e, iochan_t *ofile, Dict_t *d,
602 write_info_t *wr_info) {
603 Agnode_t *t, *h;
604 Agraph_t *g;
605
606 t = AGTAIL(e);
607 h = AGHEAD(e);
608 g = agraphof(t);
609 CHKRV(indent(g, ofile));
610 CHKRV(write_nodename(t, ofile));
611 CHKRV(write_port(e, ofile, Tailport));
612 CHKRV(ioput(g, ofile, (agisdirected(agraphof(t)) ? " -> " : " -- ")));
613 CHKRV(write_nodename(h, ofile));
614 CHKRV(write_port(e, ofile, Headport));
615 if (!attrs_written(e)) {
616 CHKRV(write_nondefault_attrs(e, ofile, d));
617 } else {
618 CHKRV(write_edge_name(e, ofile, true));
619 }
620 wr_info->edge_last_written[AGSEQ(e)] =
621 wr_info->preorder_number[AGSEQ(subg)];
622 return ioput(g, ofile, ";\n");
623}
624
625static int write_body(Agraph_t *g, iochan_t *ofile, write_info_t *wr_info) {
626 Agnode_t *n, *prev;
627 Agedge_t *e;
628 Agdatadict_t *dd;
629
630 CHKRV(write_subgs(g, ofile, wr_info));
631 dd = agdatadict(g, false);
632 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
633 if (write_node_test(g, n, wr_info))
634 CHKRV(write_node(g, n, ofile, dd ? dd->dict.n : 0, wr_info));
635 prev = n;
636 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
637 if (prev != aghead(e) && write_node_test(g, aghead(e), wr_info)) {
638 CHKRV(write_node(g, aghead(e), ofile, dd ? dd->dict.n : 0, wr_info));
639 prev = aghead(e);
640 }
641 if (write_edge_test(g, e, wr_info))
642 CHKRV(write_edge(g, e, ofile, dd ? dd->dict.e : 0, wr_info));
643 }
644
645 }
646 return 0;
647}
648
649static void set_attrwf(Agraph_t * g, bool toplevel, bool value)
650{
651 Agraph_t *subg;
652 Agnode_t *n;
653 Agedge_t *e;
654
655 AGATTRWF(g) = value;
656 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
657 set_attrwf(subg, false, value);
658 }
659 if (toplevel) {
660 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
661 AGATTRWF(n) = value;
662 for (e = agfstout(g, n); e; e = agnxtout(g, e))
663 AGATTRWF(e) = value;
664 }
665 }
666}
667
669int agwrite(Agraph_t * g, void *ofile)
670{
671 char* s;
672 Level = 0; /* re-initialize tab level */
673 s = agget(g, "linelength");
674 if (s != NULL && gv_isdigit(*s)) {
675 unsigned long len = strtoul(s, NULL, 10);
676 if ((len == 0 || len >= MIN_OUTPUTLINE) && len <= INT_MAX)
677 Max_outputline = (int)len;
678 }
679 write_info_t wr_info = before_write(g);
680 CHKRV(write_hdr(g, ofile, true));
681 CHKRV(write_body(g, ofile, &wr_info));
682 CHKRV(write_trl(g, ofile));
683 after_write(wr_info);
685 return AGDISC(g, io)->flush(ofile);
686}
687
688static uint64_t subgdfs(Agraph_t *g, uint64_t ix, write_info_t *wr_info) {
689 uint64_t ix0 = ix;
690 Agraph_t *subg;
691
692 wr_info->preorder_number[AGSEQ(g)] = ix0;
693 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
694 ix0 = subgdfs(subg, ix0, wr_info);
695 }
696 return ix0 + 1;
697}
698
700 write_info_t wr_info = {0};
701 set_attrwf(g, true, false);
702
703 wr_info.preorder_number = gv_calloc(g->clos->seq[AGRAPH] + 1, sizeof(uint64_t));
704 wr_info.node_last_written = gv_calloc(g->clos->seq[AGNODE] + 1, sizeof(uint64_t));
705 wr_info.edge_last_written = gv_calloc(g->clos->seq[AGEDGE] + 1, sizeof(uint64_t));
706 subgdfs(g, 1, &wr_info);
707 return wr_info;
708}
709
710static void after_write(write_info_t wr_info) {
711 free(wr_info.preorder_number);
712 free(wr_info.node_last_written);
713 free(wr_info.edge_last_written);
714}
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 * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define dtsearch(d, o)
Definition cdt.h:184
CDT_API int dtsize(Dt_t *)
Definition dtsize.c:12
CDT_API Dt_t * dtview(Dt_t *, Dt_t *)
Definition dtview.c:91
#define dtnext(d, o)
Definition cdt.h:181
#define dtfirst(d)
Definition cdt.h:180
cgraph.h additions
#define AGDISC(g, d)
Definition cghdr.h:48
#define LOCALNAMEPREFIX
Definition cghdr.h:46
static double len(glCompPoint p)
Definition glutils.c:136
void * malloc(YYSIZE_T)
void free(void *)
node NULL
Definition grammar.y:181
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:196
Agattr_t * agattrrec(void *obj)
Definition attr.c:206
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:334
char * agget(void *obj, char *name)
Definition attr.c:448
Agdatadict_t * agdatadict(Agraph_t *g, bool cflag)
Definition attr.c:47
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:458
Agiddisc_t AgIdDisc
Definition id.c:91
#define TAILPORT_ID
Definition cgraph.h:984
#define HEADPORT_ID
Definition cgraph.h:985
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:26
#define aghead(e)
Definition cgraph.h:978
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:41
#define AGTAIL(e)
Definition cgraph.h:973
Agedge_t * agfstin(Agraph_t *g, Agnode_t *n)
Definition edge.c:57
#define AGHEAD(e)
Definition cgraph.h:974
int agisdirected(Agraph_t *g)
Definition graph.c:176
int agisstrict(Agraph_t *g)
Definition graph.c:186
int agwrite(Agraph_t *g, void *ofile)
Return 0 on success, EOF on failure.
Definition write.c:669
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:48
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:41
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
uint64_t IDTYPE
unique per main graph ID
Definition cgraph.h:73
#define AGTYPE(obj)
returns AGRAPH, AGNODE, or AGEDGE depending on the type of the object
Definition cgraph.h:216
#define AGATTRWF(obj)
Definition cgraph.h:226
Agraph_t * agroot(void *obj)
Definition obj.c:168
#define AGSEQ(obj)
Definition cgraph.h:225
@ AGOUTEDGE
Definition cgraph.h:207
@ AGEDGE
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 *arg, char *buf)
Definition write.c:219
int agstrfree(Agraph_t *, const char *, bool is_html)
Definition refstr.c:415
char * agstrdup(Agraph_t *, const char *)
returns a pointer to a reference-counted copy of the argument string, creating one if necessary
Definition refstr.c:399
Agraph_t * agparent(Agraph_t *g)
Definition subg.c:86
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:73
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:78
replacements for ctype.h functions
static bool gv_isalnum(int c)
Definition gv_ctype.h:43
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
$2 prev
Definition htmlparse.y:291
textitem scanner parser str
Definition htmlparse.y:218
static Agedge_t * top(edge_stack_t *sp)
Definition tred.c:73
ViewInfo * view
Definition viewport.c:37
platform abstraction for case-insensitive string functions
string attribute container
Definition cgraph.h:632
char ** str
the attribute string values indexed by Agsym_s.id
Definition cgraph.h:635
Dict_t * dict
shared dict of Agsym_s to interpret Agattr_s.str
Definition cgraph.h:634
uint64_t seq[3]
Definition cgraph.h:414
Dict_t * n
Definition cgraph.h:655
Dict_t * e
Definition cgraph.h:655
Dict_t * g
Definition cgraph.h:655
struct Agdatadict_s::@63 dict
unsigned directed
Definition cgraph.h:285
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:640
char * name
Definition cgraph.h:642
int id
index in Agattr_s.str
Definition cgraph.h:644
char * defval
Definition cgraph.h:643
unsigned char print
Definition cgraph.h:647
Definition cdt.h:98
Definition types.h:48
uint64_t * node_last_written
Definition write.c:51
uint64_t * preorder_number
Definition write.c:50
uint64_t * edge_last_written
Definition write.c:52
static tok_t tok(const char *input, const char *separators)
begin tokenization of a new string
Definition tokenize.h:43
Definition grammar.c:90
static bool is_anonymous(Agraph_t *g)
Definition write.c:373
static Agsym_t * Headport
Definition write.c:47
static int write_canonstr(Agraph_t *g, iochan_t *ofile, char *str, bool known)
Definition write.c:246
void iochan_t
Definition write.c:35
#define MIN_OUTPUTLINE
Definition write.c:44
static bool not_default_attrs(Agraph_t *g, Agnode_t *n)
Definition write.c:419
static uint64_t subgdfs(Agraph_t *g, uint64_t ix, write_info_t *wr_info)
Definition write.c:688
static int ioput(Agraph_t *g, iochan_t *ofile, char *str)
Definition write.c:37
static char * _agstrcanon(char *arg, char *buf)
Definition write.c:113
static int attrs_written(void *obj)
Definition write.c:532
static int write_dicts(Agraph_t *g, iochan_t *ofile, bool top)
Definition write.c:307
#define CHKRV(v)
Definition write.c:33
static int write_port(Agedge_t *e, iochan_t *ofile, Agsym_t *port)
Definition write.c:565
static int write_dict(Agraph_t *g, iochan_t *ofile, char *name, Dict_t *dict, bool top)
Definition write.c:262
static void set_attrwf(Agraph_t *g, bool toplevel, bool value)
Definition write.c:649
static int write_nodename(Agnode_t *n, iochan_t *ofile)
Definition write.c:515
static bool is_escape(const char *str)
Definition write.c:75
static void after_write(write_info_t)
Definition write.c:710
static int write_trl(Agraph_t *g, iochan_t *ofile)
Definition write.c:360
static bool has_no_edges(Agraph_t *g, Agnode_t *n)
Definition write.c:414
static int Level
Definition write.c:45
static bool irrelevant_subgraph(Agraph_t *g)
Definition write.c:390
static int write_edge(Agraph_t *subg, Agedge_t *e, iochan_t *ofile, Dict_t *d, write_info_t *wr_info)
Definition write.c:601
static int write_edge_name(Agedge_t *e, iochan_t *ofile, bool terminate)
Definition write.c:450
static int indent(Agraph_t *g, iochan_t *ofile)
Definition write.c:60
static int _write_canonstr(Agraph_t *g, iochan_t *ofile, char *str, bool chk)
Definition write.c:227
static int write_body(Agraph_t *g, iochan_t *ofile, write_info_t *wr_info)
Definition write.c:625
#define MAX_OUTPUTLINE
Definition write.c:43
static bool is_id_char(char c)
Definition write.c:69
static bool write_node_test(Agraph_t *g, Agnode_t *n, write_info_t *wr_info)
Definition write.c:555
static Agsym_t * Tailport
Definition write.c:47
#define EMPTY(s)
Definition write.c:31
static int write_subgs(Agraph_t *g, iochan_t *ofile, write_info_t *wr_info)
Definition write.c:434
static int write_nondefault_attrs(void *obj, iochan_t *ofile, Dict_t *defdict)
Definition write.c:470
static bool write_edge_test(Agraph_t *g, Agedge_t *e, write_info_t *wr_info)
Definition write.c:595
static char * agcanonhtmlstr(const char *arg, char *buf)
Definition write.c:209
static int write_node(Agraph_t *subg, Agnode_t *n, iochan_t *ofile, Dict_t *d, write_info_t *wr_info)
Definition write.c:537
static int Max_outputline
Definition write.c:46
static write_info_t before_write(Agraph_t *)
Definition write.c:699
static int write_hdr(Agraph_t *g, iochan_t *ofile, bool top)
Definition write.c:317