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