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