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