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