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