Graphviz 13.1.0~dev.20250626.0830
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 char *getoutputbuffer(const char *str)
228{
229 static char *rv;
230 static size_t len = 0;
231 const size_t req = MAX(agstrcanon_bytes(str), BUFSIZ);
232 if (req > len) {
233 char *r = realloc(rv, req);
234 if (r == NULL)
235 return NULL;
236 rv = r;
237 len = req;
238 }
239 return rv;
240}
241
246char *agcanonStr(char *str)
247{
248 char *buffer = getoutputbuffer(str);
249 if (buffer == NULL)
250 return NULL;
251 return agstrcanon(str, buffer);
252}
253
254static int _write_canonstr(Agraph_t *g, iochan_t *ofile, char *str, bool chk) {
255
256 // maximum bytes required for canonicalized string
257 const size_t required = agstrcanon_bytes(str);
258
259 // allocate space to stage the canonicalized string
260 char *const scratch = malloc(required);
261 if (scratch == NULL) {
262 return EOF;
263 }
264
265 char *const canonicalized =
266 chk ? agstrcanon(str, scratch) : _agstrcanon(str, scratch);
267 const int rc = ioput(g, ofile, canonicalized);
268 free(scratch);
269 return rc;
270}
271
273static int write_canonstr(Agraph_t *g, iochan_t *ofile, char *str, bool known) {
274 char *s;
275
276 /* str may not have been allocated by agstrdup, so we first need to turn it
277 * into a valid refstr
278 */
279 s = known ? str : agstrdup(g, str);
280
281 int r = _write_canonstr(g, ofile, s, true);
282
283 if (!known) {
284 agstrfree(g, s, false);
285 }
286 return r;
287}
288
289static int write_dict(Agraph_t * g, iochan_t * ofile, char *name,
290 Dict_t * dict, bool top) {
291 int cnt = 0;
292 Dict_t *view;
293 Agsym_t *sym, *psym;
294
295 if (!top)
296 view = dtview(dict, NULL);
297 else
298 view = 0;
299 for (sym = dtfirst(dict); sym; sym = dtnext(dict, sym)) {
300 if (EMPTY(sym->defval) && !sym->print) { /* try to skip empty str (default) */
301 if (view == NULL)
302 continue; /* no parent */
303 psym = dtsearch(view, sym);
304 assert(psym);
305 if (EMPTY(psym->defval) && psym->print)
306 continue; /* also empty in parent */
307 }
308 if (cnt++ == 0) {
309 CHKRV(indent(g, ofile));
310 CHKRV(ioput(g, ofile, name));
311 CHKRV(ioput(g, ofile, " ["));
312 Level++;
313 } else {
314 CHKRV(ioput(g, ofile, ",\n"));
315 CHKRV(indent(g, ofile));
316 }
317 CHKRV(write_canonstr(g, ofile, sym->name, true));
318 CHKRV(ioput(g, ofile, "="));
319 CHKRV(write_canonstr(g, ofile, sym->defval, true));
320 }
321 if (cnt > 0) {
322 Level--;
323 if (cnt > 1) {
324 CHKRV(ioput(g, ofile, "\n"));
325 CHKRV(indent(g, ofile));
326 }
327 CHKRV(ioput(g, ofile, "];\n"));
328 }
329 if (!top)
330 dtview(dict, view); /* restore previous view */
331 return 0;
332}
333
334static int write_dicts(Agraph_t *g, iochan_t *ofile, bool top) {
335 Agdatadict_t *def;
336 if ((def = agdatadict(g, false))) {
337 CHKRV(write_dict(g, ofile, "graph", def->dict.g, top));
338 CHKRV(write_dict(g, ofile, "node", def->dict.n, top));
339 CHKRV(write_dict(g, ofile, "edge", def->dict.e, top));
340 }
341 return 0;
342}
343
344static int write_hdr(Agraph_t *g, iochan_t *ofile, bool top) {
345 char *name, *sep, *kind, *strict;
346 bool root = false;
347 bool hasName = true;
348
349 strict = "";
350 if (!top && agparent(g))
351 kind = "sub";
352 else {
353 root = true;
354 if (g->desc.directed)
355 kind = "di";
356 else
357 kind = "";
358 if (agisstrict(g))
359 strict = "strict ";
362 }
363 name = agnameof(g);
364 sep = " ";
365 if (!name || name[0] == LOCALNAMEPREFIX) {
366 sep = name = "";
367 hasName = false;
368 }
369 CHKRV(indent(g, ofile));
370 CHKRV(ioput(g, ofile, strict));
371
372 /* output "<kind>graph" only for root graphs or graphs with names */
373 if (root || hasName) {
374 CHKRV(ioput(g, ofile, kind));
375 CHKRV(ioput(g, ofile, "graph "));
376 }
377 if (hasName)
378 CHKRV(write_canonstr(g, ofile, name, false));
379 CHKRV(ioput(g, ofile, sep));
380 CHKRV(ioput(g, ofile, "{\n"));
381 Level++;
382 CHKRV(write_dicts(g, ofile, top));
383 AGATTRWF(g) = true;
384 return 0;
385}
386
387static int write_trl(Agraph_t * g, iochan_t * ofile)
388{
389 (void)g;
390 Level--;
391 CHKRV(indent(g, ofile));
392 CHKRV(ioput(g, ofile, "}\n"));
393 return 0;
394}
395
400static bool is_anonymous(Agraph_t *g) {
401 assert(g != NULL);
402
403 // handle the common case inline for performance
404 if (AGDISC(g, id) == &AgIdDisc) {
405 // replicate `idprint`
406 const IDTYPE id = AGID(g);
407 if (id % 2 != 0) {
408 return true;
409 }
410 return *(char *)(uintptr_t)id == LOCALNAMEPREFIX;
411 }
412
413 const char *const name = agnameof(g);
414 return name == NULL || name[0] == LOCALNAMEPREFIX;
415}
416
418{
419 int i, n;
420 Agattr_t *sdata, *pdata, *rdata;
421 Agdatadict_t *dd;
422
423 if (!is_anonymous(g))
424 return false;
425 if ((sdata = agattrrec(g)) && (pdata = agattrrec(agparent(g)))) {
426 rdata = agattrrec(agroot(g));
427 n = dtsize(rdata->dict);
428 for (i = 0; i < n; i++)
429 if (sdata->str[i] && pdata->str[i]
430 && strcmp(sdata->str[i], pdata->str[i]))
431 return false;
432 }
433 dd = agdatadict(g, false);
434 if (!dd)
435 return true;
436 if (dtsize(dd->dict.n) > 0 || dtsize(dd->dict.e) > 0)
437 return false;
438 return true;
439}
440
441static bool has_no_edges(Agraph_t * g, Agnode_t * n)
442{
443 return agfstin(g, n) == NULL && agfstout(g, n) == NULL;
444}
445
447{
448 Agattr_t *data;
449 Agsym_t *sym;
450
451 (void)g;
452 if ((data = agattrrec(n))) {
453 for (sym = dtfirst(data->dict); sym; sym = dtnext(data->dict, sym)) {
454 if (data->str[sym->id] != sym->defval)
455 return true;
456 }
457 }
458 return false;
459}
460
461static int write_subgs(Agraph_t *g, iochan_t *ofile, write_info_t *wr_info) {
462 Agraph_t *subg;
463
464 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
465 if (irrelevant_subgraph(subg)) {
466 write_subgs(subg, ofile, wr_info);
467 }
468 else {
469 CHKRV(write_hdr(subg, ofile, false));
470 CHKRV(write_body(subg, ofile, wr_info));
471 CHKRV(write_trl(subg, ofile));
472 }
473 }
474 return 0;
475}
476
477static int write_edge_name(Agedge_t *e, iochan_t *ofile, bool terminate) {
478 char *p;
479 Agraph_t *g;
480
481 p = agnameof(e);
482 g = agraphof(e);
483 if (!EMPTY(p)) {
484 if (!terminate) {
485 Level++;
486 }
487 CHKRV(ioput(g, ofile, "\t[key="));
488 CHKRV(write_canonstr(g, ofile, p, false));
489 if (terminate)
490 CHKRV(ioput(g, ofile, "]"));
491 return 1;
492 }
493 return 0;
494}
495
496
497static int write_nondefault_attrs(void *obj, iochan_t * ofile,
498 Dict_t * defdict)
499{
500 Agattr_t *data;
501 Agsym_t *sym;
502 Agraph_t *g;
503 int cnt = 0;
504 int rv;
505
506 if (AGTYPE(obj) == AGINEDGE || AGTYPE(obj) == AGOUTEDGE) {
507 CHKRV(rv = write_edge_name(obj, ofile, false));
508 if (rv)
509 cnt++;
510 }
511 data = agattrrec(obj);
512 g = agraphof(obj);
513 if (data)
514 for (sym = dtfirst(defdict); sym; sym = dtnext(defdict, sym)) {
515 if (AGTYPE(obj) == AGINEDGE || AGTYPE(obj) == AGOUTEDGE) {
516 if (Tailport && sym->id == Tailport->id)
517 continue;
518 if (Headport && sym->id == Headport->id)
519 continue;
520 }
521 if (data->str[sym->id] != sym->defval) {
522 if (cnt++ == 0) {
523 CHKRV(ioput(g, ofile, "\t["));
524 Level++;
525 } else {
526 CHKRV(ioput(g, ofile, ",\n"));
527 CHKRV(indent(g, ofile));
528 }
529 CHKRV(write_canonstr(g, ofile, sym->name, true));
530 CHKRV(ioput(g, ofile, "="));
531 CHKRV(write_canonstr(g, ofile, data->str[sym->id], true));
532 }
533 }
534 if (cnt > 0) {
535 CHKRV(ioput(g, ofile, "]"));
536 Level--;
537 }
538 AGATTRWF(obj) = true;
539 return 0;
540}
541
542static int write_nodename(Agnode_t * n, iochan_t * ofile)
543{
544 char *name;
545 Agraph_t *g;
546
547 name = agnameof(n);
548 g = agraphof(n);
549 if (name) {
550 CHKRV(write_canonstr(g, ofile, name, false));
551 } else {
552 char buf[sizeof("__SUSPECT") + 20];
553 snprintf(buf, sizeof(buf), "_%" PRIu64 "_SUSPECT", AGID(n)); /* could be deadly wrong */
554 CHKRV(ioput(g, ofile, buf));
555 }
556 return 0;
557}
558
559static int attrs_written(void *obj)
560{
561 return AGATTRWF(obj);
562}
563
564static int write_node(Agraph_t *subg, Agnode_t *n, iochan_t *ofile, Dict_t *d,
565 write_info_t *wr_info) {
566 Agraph_t *g;
567
568 g = agraphof(n);
569 CHKRV(indent(g, ofile));
570 CHKRV(write_nodename(n, ofile));
571 if (!attrs_written(n))
572 CHKRV(write_nondefault_attrs(n, ofile, d));
573 wr_info->node_last_written[AGSEQ(n)] =
574 wr_info->preorder_number[AGSEQ(subg)];
575 return ioput(g, ofile, ";\n");
576}
577
578/* node must be written if it wasn't already emitted because of
579 * a subgraph or one of its predecessors, and if it is a singleton
580 * or has non-default attributes.
581 */
582static bool write_node_test(Agraph_t *g, Agnode_t *n, write_info_t *wr_info) {
583 /* test if node was already written in g or a subgraph of g */
584 if (wr_info->node_last_written[AGSEQ(n)] >=
585 wr_info->preorder_number[AGSEQ(g)]) return false;
586
587 if (has_no_edges(g, n) || not_default_attrs(g, n))
588 return true;
589 return false;
590}
591
592static int write_port(Agedge_t * e, iochan_t * ofile, Agsym_t * port)
593{
594 char *val;
595 Agraph_t *g;
596
597 if (!port)
598 return 0;
599 g = agraphof(e);
600 val = agxget(e, port);
601 if (val[0] == '\0')
602 return 0;
603
604 CHKRV(ioput(g, ofile, ":"));
605 if (aghtmlstr(val)) {
606 CHKRV(write_canonstr(g, ofile, val, true));
607 } else {
608 char *s = strchr(val, ':');
609 if (s) {
610 *s = '\0';
611 CHKRV(_write_canonstr(g, ofile, val, false));
612 CHKRV(ioput(g, ofile, ":"));
613 CHKRV(_write_canonstr(g, ofile, s + 1, false));
614 *s = ':';
615 } else {
616 CHKRV(_write_canonstr(g, ofile, val, false));
617 }
618 }
619 return 0;
620}
621
622static bool write_edge_test(Agraph_t *g, Agedge_t *e, write_info_t *wr_info) {
623 if (wr_info->edge_last_written[AGSEQ(e)] >=
624 wr_info->preorder_number[AGSEQ(g)]) return false;
625 return true;
626}
627
628static int write_edge(Agraph_t *subg, Agedge_t *e, iochan_t *ofile, Dict_t *d,
629 write_info_t *wr_info) {
630 Agnode_t *t, *h;
631 Agraph_t *g;
632
633 t = AGTAIL(e);
634 h = AGHEAD(e);
635 g = agraphof(t);
636 CHKRV(indent(g, ofile));
637 CHKRV(write_nodename(t, ofile));
638 CHKRV(write_port(e, ofile, Tailport));
639 CHKRV(ioput(g, ofile, (agisdirected(agraphof(t)) ? " -> " : " -- ")));
640 CHKRV(write_nodename(h, ofile));
641 CHKRV(write_port(e, ofile, Headport));
642 if (!attrs_written(e)) {
643 CHKRV(write_nondefault_attrs(e, ofile, d));
644 } else {
645 CHKRV(write_edge_name(e, ofile, true));
646 }
647 wr_info->edge_last_written[AGSEQ(e)] =
648 wr_info->preorder_number[AGSEQ(subg)];
649 return ioput(g, ofile, ";\n");
650}
651
652static int write_body(Agraph_t *g, iochan_t *ofile, write_info_t *wr_info) {
653 Agnode_t *n, *prev;
654 Agedge_t *e;
655 Agdatadict_t *dd;
656
657 CHKRV(write_subgs(g, ofile, wr_info));
658 dd = agdatadict(g, false);
659 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
660 if (write_node_test(g, n, wr_info))
661 CHKRV(write_node(g, n, ofile, dd ? dd->dict.n : 0, wr_info));
662 prev = n;
663 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
664 if (prev != aghead(e) && write_node_test(g, aghead(e), wr_info)) {
665 CHKRV(write_node(g, aghead(e), ofile, dd ? dd->dict.n : 0, wr_info));
666 prev = aghead(e);
667 }
668 if (write_edge_test(g, e, wr_info))
669 CHKRV(write_edge(g, e, ofile, dd ? dd->dict.e : 0, wr_info));
670 }
671
672 }
673 return 0;
674}
675
676static void set_attrwf(Agraph_t * g, bool toplevel, bool value)
677{
678 Agraph_t *subg;
679 Agnode_t *n;
680 Agedge_t *e;
681
682 AGATTRWF(g) = value;
683 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
684 set_attrwf(subg, false, value);
685 }
686 if (toplevel) {
687 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
688 AGATTRWF(n) = value;
689 for (e = agfstout(g, n); e; e = agnxtout(g, e))
690 AGATTRWF(e) = value;
691 }
692 }
693}
694
696int agwrite(Agraph_t * g, void *ofile)
697{
698 char* s;
699 Level = 0; /* re-initialize tab level */
700 s = agget(g, "linelength");
701 if (s != NULL && gv_isdigit(*s)) {
702 unsigned long len = strtoul(s, NULL, 10);
703 if ((len == 0 || len >= MIN_OUTPUTLINE) && len <= INT_MAX)
704 Max_outputline = (int)len;
705 }
706 write_info_t wr_info = before_write(g);
707 CHKRV(write_hdr(g, ofile, true));
708 CHKRV(write_body(g, ofile, &wr_info));
709 CHKRV(write_trl(g, ofile));
710 after_write(wr_info);
712 return AGDISC(g, io)->flush(ofile);
713}
714
715static uint64_t subgdfs(Agraph_t *g, uint64_t ix, write_info_t *wr_info) {
716 uint64_t ix0 = ix;
717 Agraph_t *subg;
718
719 wr_info->preorder_number[AGSEQ(g)] = ix0;
720 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
721 ix0 = subgdfs(subg, ix0, wr_info);
722 }
723 return ix0 + 1;
724}
725
727 write_info_t wr_info = {0};
728 set_attrwf(g, true, false);
729
730 wr_info.preorder_number = gv_calloc(g->clos->seq[AGRAPH] + 1, sizeof(uint64_t));
731 wr_info.node_last_written = gv_calloc(g->clos->seq[AGNODE] + 1, sizeof(uint64_t));
732 wr_info.edge_last_written = gv_calloc(g->clos->seq[AGEDGE] + 1, sizeof(uint64_t));
733 subgdfs(g, 1, &wr_info);
734 return wr_info;
735}
736
737static void after_write(write_info_t wr_info) {
738 free(wr_info.preorder_number);
739 free(wr_info.node_last_written);
740 free(wr_info.edge_last_written);
741}
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:183
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:180
#define dtfirst(d)
Definition cdt.h:179
cgraph.h additions
#define AGDISC(g, d)
Definition cghdr.h:49
#define LOCALNAMEPREFIX
Definition cghdr.h:47
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:198
Agattr_t * agattrrec(void *obj)
Definition attr.c:220
Agsym_t * agattr_text(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up text attributes of a graph
Definition attr.c:348
char * agget(void *obj, char *name)
Definition attr.c:462
Agdatadict_t * agdatadict(Agraph_t *g, bool cflag)
Definition attr.c:47
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:472
Agiddisc_t AgIdDisc
Definition id.c:91
#define TAILPORT_ID
Definition cgraph.h:995
#define HEADPORT_ID
Definition cgraph.h:996
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:26
#define aghead(e)
Definition cgraph.h:989
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:41
#define AGTAIL(e)
Definition cgraph.h:984
Agedge_t * agfstin(Agraph_t *g, Agnode_t *n)
Definition edge.c:57
#define AGHEAD(e)
Definition cgraph.h:985
int agisdirected(Agraph_t *g)
Definition graph.c:178
int agisstrict(Agraph_t *g)
Definition graph.c:188
int agwrite(Agraph_t *g, void *ofile)
Return 0 on success, EOF on failure.
Definition write.c:696
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
char * agcanonStr(char *str)
Definition write.c:246
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 u p prev
Definition htmlparse.y:297
textitem scanner parser str
Definition htmlparse.y:224
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:643
char ** str
the attribute string values indexed by Agsym_s.id
Definition cgraph.h:646
uint64_t seq[3]
Definition cgraph.h:414
Dict_t * n
Definition cgraph.h:666
Dict_t * e
Definition cgraph.h:666
struct Agdatadict_s::@66 dict
Dict_t * g
Definition cgraph.h:666
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:651
char * name
Definition cgraph.h:653
int id
index in Agattr_s.str
Definition cgraph.h:655
char * defval
Definition cgraph.h:654
unsigned char print
Definition cgraph.h:658
Definition legal.c:50
Definition cdt.h:100
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:89
static bool is_anonymous(Agraph_t *g)
Definition write.c:400
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:273
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:446
static uint64_t subgdfs(Agraph_t *g, uint64_t ix, write_info_t *wr_info)
Definition write.c:715
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:559
static int write_dicts(Agraph_t *g, iochan_t *ofile, bool top)
Definition write.c:334
#define CHKRV(v)
Definition write.c:33
static int write_port(Agedge_t *e, iochan_t *ofile, Agsym_t *port)
Definition write.c:592
static int write_dict(Agraph_t *g, iochan_t *ofile, char *name, Dict_t *dict, bool top)
Definition write.c:289
static void set_attrwf(Agraph_t *g, bool toplevel, bool value)
Definition write.c:676
static int write_nodename(Agnode_t *n, iochan_t *ofile)
Definition write.c:542
static bool is_escape(const char *str)
Definition write.c:75
static void after_write(write_info_t)
Definition write.c:737
static int write_trl(Agraph_t *g, iochan_t *ofile)
Definition write.c:387
static bool has_no_edges(Agraph_t *g, Agnode_t *n)
Definition write.c:441
static int Level
Definition write.c:45
static bool irrelevant_subgraph(Agraph_t *g)
Definition write.c:417
static int write_edge(Agraph_t *subg, Agedge_t *e, iochan_t *ofile, Dict_t *d, write_info_t *wr_info)
Definition write.c:628
static int write_edge_name(Agedge_t *e, iochan_t *ofile, bool terminate)
Definition write.c:477
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:254
static int write_body(Agraph_t *g, iochan_t *ofile, write_info_t *wr_info)
Definition write.c:652
#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:582
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:461
static char * getoutputbuffer(const char *str)
Definition write.c:227
static int write_nondefault_attrs(void *obj, iochan_t *ofile, Dict_t *defdict)
Definition write.c:497
static bool write_edge_test(Agraph_t *g, Agedge_t *e, write_info_t *wr_info)
Definition write.c:622
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:564
static int Max_outputline
Definition write.c:46
static write_info_t before_write(Agraph_t *)
Definition write.c:726
#define MAX(a, b)
Definition write.c:32
static int write_hdr(Agraph_t *g, iochan_t *ofile, bool top)
Definition write.c:344