Graphviz 16.1.1~dev.20261104.1411
Loading...
Searching...
No Matches
gvpack.cpp
Go to the documentation of this file.
1
6/*************************************************************************
7 * Copyright (c) 2011 AT&T Intellectual Property
8 * All rights reserved. This program and the accompanying materials
9 * are made available under the terms of the Eclipse Public License v2.0
10 * which accompanies this distribution, and is available at
11 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
12 *
13 * Contributors: Details at https://graphviz.org
14 *************************************************************************/
15
16/*
17 * Written by Emden R. Gansner
18 */
19
20#include "config.h"
21
22#include "openFile.h"
23#include <algorithm>
24#include <cassert>
25#include <cgraph/cgraph.h>
26#include <cgraph/ingraphs.h>
27#include <common/render.h>
28#include <common/utils.h>
29#include <cstdio>
30#include <getopt.h>
31#include <gvc/gvc.h>
32#include <iostream>
33#include <limits>
34#include <map>
35#include <neatogen/neatoprocs.h>
36#include <optional>
37#include <pack/pack.h>
38#include <set>
39#include <stddef.h>
40#include <string>
41#include <util/alloc.h>
42#include <util/exit.h>
43#include <util/gv_ctype.h>
44#include <utility>
45#include <vector>
46
47extern "C" {
48#ifdef GVDLL
49__declspec(dllimport)
50#endif
52}
53
54lt_symlist_t lt_preloaded_symbols[] = {{"gvplugin_neato_layout_LTX_library", 0},
55 {0, 0}};
56
57/* gvpack:
58 * Input consists of graphs in dot format.
59 * The graphs should have pos, width and height set for nodes,
60 * bb set for clusters, and, optionally, spline info for edges.
61 * The graphs are packed geometrically and combined
62 * into a single output graph, ready to be sent to neato -s -n2.
63 * -m <i> specifies the margin, in points, about each graph.
64 */
65
66typedef struct {
67 char *name;
68 char *value;
69} attr_t;
70
71static int verbose = 0;
72static char **myFiles = 0;
73static FILE *outfp; /* output; stdout by default */
74static std::vector<attr_t> G_args; // Storage for -G arguments
75static bool doPack; /* Do packing if true */
76static char *gname = const_cast<char *>("root");
77static bool errored;
78
83static int handle_error(char *message) {
84
85 errored = true;
86
87 // `fputs`, escaping characters that may interfere with a terminal
88 for (const char *p = message; *p != '\0'; ++p) {
89
90 if (gv_iscntrl(*p) && !gv_isspace(*p)) {
91 const int rc = fprintf(stderr, "\\%03o", (unsigned)*p);
92 if (rc < 0) {
93 return rc;
94 }
95 continue;
96 }
97
98 const int rc = putc(*p, stderr);
99 if (rc < 0) {
100 return rc;
101 }
102 }
103 return 0;
104}
105
106#define NEWNODE(n) ((node_t *)ND_alg(n))
107
108static const char useString[] =
109 "Usage: gvpack [-gnuv?] [-m<margin>] {-array[_rc][n]] [-o<outf>] <files>\n\
110 -n - use node granularity\n\
111 -g - use graph granularity\n\
112 -array* - pack as array of graphs\n\
113 -G<n>=<v> - attach name/value attribute to output graph\n\
114 -m<n> - set margin to <n> points\n\
115 -s<gname> - use <gname> for name of root graph\n\
116 -o<outfile> - write output to <outfile>\n\
117 -u - no packing; just combine graphs\n\
118 -v - verbose\n\
119 -? - print usage\n\
120If no files are specified, stdin is used\n";
121
122static void usage(int v) {
123 std::cout << useString;
124 graphviz_exit(v);
125}
126
127/* If arg is a name-value pair, add it to the list
128 * and return 0; otherwise, return 1.
129 */
130static int setNameValue(char *arg) {
131 char *rhs = const_cast<char *>("true");
132
133 if (char *p = strchr(arg, '=')) {
134 *p++ = '\0';
135 rhs = p;
136 }
137 G_args.push_back(attr_t{arg, rhs});
138
139 return 0;
140}
141
142/* If arg is an integer, value is stored in v
143 * and function returns 0; otherwise, returns 1.
144 */
145static int setUInt(unsigned int *v, char *arg) {
146 char *p;
147 unsigned int i;
148
149 i = (unsigned int)strtol(arg, &p, 10);
150 if (p == arg) {
151 std::cerr << "Error: bad value in flag -" << (arg - 1) << " - ignored\n";
152 return 1;
153 }
154 *v = i;
155 return 0;
156}
157
158static Agsym_t *agraphattr(Agraph_t *g, char *name, const char *value) {
159 return agattr_text(g, AGRAPH, name, value);
160}
161
162static Agsym_t *agnodeattr(Agraph_t *g, char *name, const char *value) {
163 return agattr_text(g, AGNODE, name, value);
164}
165
166static Agsym_t *agedgeattr(Agraph_t *g, char *name, const char *value) {
167 return agattr_text(g, AGEDGE, name, value);
168}
169
170static void init(int argc, char *argv[], pack_info *pinfo) {
171 int c;
172
173 agnodeattr(nullptr, const_cast<char *>("label"), NODENAME_ESC);
174 pinfo->mode = l_clust;
175 pinfo->margin = CL_OFFSET;
176 pinfo->doSplines = true; // Use edges in packing
177 pinfo->fixed = nullptr;
178 pinfo->sz = 0;
179
180 opterr = 0;
181 while ((c = getopt(argc, argv, ":na:gvum:s:o:G:?")) != -1) {
182 switch (c) {
183 case 'a': {
184 auto buf = std::string("a") + optarg + "\n";
185 parsePackModeInfo(buf.c_str(), pinfo->mode, pinfo);
186 break;
187 }
188 case 'n':
189 parsePackModeInfo("node", pinfo->mode, pinfo);
190 break;
191 case 's':
192 gname = optarg;
193 break;
194 case 'g':
195 parsePackModeInfo("graph", pinfo->mode, pinfo);
196 break;
197 case 'm':
198 setUInt(&pinfo->margin, optarg);
199 break;
200 case 'o':
201 if (outfp != nullptr)
202 fclose(outfp);
203 outfp = openFile("gvpack", optarg, "w");
204 break;
205 case 'u':
206 pinfo->mode = l_undef;
207 break;
208 case 'G':
209 if (*optarg)
210 setNameValue(optarg);
211 else
212 std::cerr << "gvpack: option -G missing argument - ignored\n";
213 break;
214 case 'v':
215 verbose = 1;
216 Verbose = 1;
217 break;
218 case ':':
219 std::cerr << "gvpack: option -" << (char)optopt
220 << " missing argument - ignored\n";
221 break;
222 case '?':
223 if (optopt == '\0' || optopt == '?')
224 usage(0);
225 else {
226 std::cerr << "gvpack: option -" << (char)optopt << " unrecognized\n";
227 usage(1);
228 }
229 break;
230 }
231 }
232 argv += optind;
233 argc -= optind;
234
235 if (argc > 0) {
236 myFiles = argv;
237 }
238 if (!outfp)
239 outfp = stdout; /* stdout the default */
240 if (verbose)
241 std::cerr << " margin " << pinfo->margin << '\n';
242}
243
245static void init_node_edge(Agraph_t *g) {
246 node_t *n;
247 edge_t *e;
248 int nG = agnnodes(g);
249 attrsym_t *N_pos = agfindnodeattr(g, const_cast<char *>("pos"));
250 attrsym_t *N_pin = agfindnodeattr(g, const_cast<char *>("pin"));
251
252 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
254 user_pos(N_pos, N_pin, n, nG); /* set user position if given */
255 }
256 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
257 for (e = agfstout(g, n); e; e = agnxtout(g, e))
259 }
260}
261
262/* Initialize attributes. We always do the minimum required by
263 * libcommon. If fill is true, we use init_nop (neato -n) to
264 * read in attributes relevant to the layout.
265 */
266static void init_graph(Agraph_t *g, bool fill, GVC_t *gvc) {
267 int d;
268 node_t *n;
269 edge_t *e;
270
271 aginit(g, AGRAPH, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
272 aginit(g, AGNODE, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
273 aginit(g, AGEDGE, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true);
274 GD_gvc(g) = gvc;
275 if (errored)
276 graphviz_exit(1);
277 graph_init(g, false);
278 d = late_int(g, agfindgraphattr(g, const_cast<char *>("dim")), 2, 2);
279 if (d != 2) {
280 std::cerr << "Error: graph " << agnameof(g) << " has dim = " << d
281 << " (!= 2)\n";
282 graphviz_exit(1);
283 }
284 Ndim = GD_ndim(g) = 2;
286 if (fill) {
287 if (int ret = init_nop(g, 0)) {
288 if (ret < 0)
289 std::cerr << "Error loading layout info from graph " << agnameof(g)
290 << '\n';
291 else if (ret > 0)
292 std::cerr << "gvpack does not support backgrounds as found in graph "
293 << agnameof(g) << '\n';
294 graphviz_exit(1);
295 }
296 if (Concentrate) { /* check for edges without pos info */
297 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
298 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
299 if (ED_spl(e) == nullptr)
301 }
302 }
303 }
304 }
305}
306
307/* Copy all attributes from old object to new. Assume
308 * attributes have been initialized.
309 */
310static void cloneDfltAttrs(Agraph_t *old, Agraph_t *new_graph, int attr_kind) {
311 for (Agsym_t *a = agnxtattr(old, attr_kind, 0); a;
312 a = agnxtattr(old, attr_kind, a)) {
313 if (aghtmlstr(a->defval)) {
314 agattr_html(new_graph, attr_kind, a->name, a->defval);
315 } else {
316 agattr_text(new_graph, attr_kind, a->name, a->defval);
317 }
318 }
319}
320static void cloneAttrs(void *old, void *new_graph) {
321 int attr_kind = AGTYPE(old);
322 char *s;
323 Agraph_t *g = agroot(old);
324 Agraph_t *ng = agroot(new_graph);
325
326 for (Agsym_t *a = agnxtattr(g, attr_kind, 0); a;
327 a = agnxtattr(g, attr_kind, a)) {
328 s = agxget(old, a);
329 if (aghtmlstr(s)) {
330 char *scopy = agstrdup_html(ng, s);
331 agset(new_graph, a->name, scopy);
332 agstrfree(ng, scopy,
333 true); // drop the extra reference count we bumped for scopy
334 } else {
335 agset(new_graph, a->name, s);
336 }
337 }
338}
339
340/* Note that here, and in cloneNode and cloneCluster,
341 * we do a shallow copy. We thus assume that attributes
342 * are not disturbed. In particular, we assume they are
343 * not deallocated.
344 */
345static void cloneEdge(Agedge_t *old, Agedge_t *new_edge) {
346 cloneAttrs(old, new_edge);
347 ED_spl(new_edge) = ED_spl(old);
348 ED_edge_type(new_edge) = ED_edge_type(old);
349 ED_label(new_edge) = ED_label(old);
350 ED_head_label(new_edge) = ED_head_label(old);
351 ED_tail_label(new_edge) = ED_tail_label(old);
352 ED_xlabel(new_edge) = ED_xlabel(old);
353}
354
355static void cloneNode(Agnode_t *old, Agnode_t *new_node) {
356 cloneAttrs(old, new_node);
357 ND_coord(new_node).x = POINTS(ND_pos(old)[0]);
358 ND_coord(new_node).y = POINTS(ND_pos(old)[1]);
359 ND_height(new_node) = ND_height(old);
360 ND_ht(new_node) = ND_ht(old);
361 ND_width(new_node) = ND_width(old);
362 ND_lw(new_node) = ND_lw(old);
363 ND_rw(new_node) = ND_rw(old);
364 ND_shape(new_node) = ND_shape(old);
365 ND_shape_info(new_node) = ND_shape_info(old);
366 ND_xlabel(new_node) = ND_xlabel(old);
367}
368
369static void cloneCluster(Agraph_t *old, Agraph_t *new_cluster) {
370 // string attributes were cloned as subgraphs
371 GD_label(new_cluster) = GD_label(old);
372 GD_bb(new_cluster) = GD_bb(old);
373}
374
375namespace {
377struct AttributeValue {
378 std::string value;
379 size_t instances;
380};
381} // namespace
382
384using attr_map_t = std::map<std::string, AttributeValue>;
385
386/* Fill newdict with all the name-value attributes of
387 * objp. If the attribute has already been defined and
388 * has a different default, set default to "".
389 */
390static void fillDict(attr_map_t &newdict, Agraph_t *g, int attr_kind) {
391
392 for (Agsym_t *a = agnxtattr(g, attr_kind, 0); a;
393 a = agnxtattr(g, attr_kind, a)) {
394 char *name = a->name;
395 char *value = a->defval;
396 auto it = newdict.find(name);
397 if (it == newdict.end()) {
398 newdict.insert({name, AttributeValue{value, 1}});
399 } else if (it->second.value == value)
400 ++it->second.instances;
401 }
402}
403
404/* Use all the name-value entries in the dictionary d to define
405 * to define universal node/edge/graph attributes for g.
406 * For a non-empty default value, the attribute must be defined and the
407 * same in all graphs.
408 */
409static void fillGraph(Agraph_t *g, const attr_map_t &d,
410 Agsym_t *(*setf)(Agraph_t *, char *, const char *),
411 size_t cnt) {
412 for (const auto &kv : d) {
413 const std::string &name = kv.first;
414 const std::string &value = kv.second.value;
415 const size_t &attr_cnt = kv.second.instances;
416 if (cnt == attr_cnt)
417 setf(g, const_cast<char *>(name.c_str()), value.c_str());
418 else
419 setf(g, const_cast<char *>(name.c_str()), "");
420 }
421}
422
423/* Initialize the attributes of root as the union of the
424 * attributes in the graphs gs.
425 */
426static void initAttrs(Agraph_t *root, std::vector<Agraph_t *> &gs) {
427 attr_map_t n_attrs;
428 attr_map_t e_attrs;
429 attr_map_t g_attrs;
430
431 for (Agraph_t *g : gs) {
432 fillDict(g_attrs, g, AGRAPH);
433 fillDict(n_attrs, g, AGNODE);
434 fillDict(e_attrs, g, AGEDGE);
435 }
436
437 fillGraph(root, g_attrs, agraphattr, gs.size());
438 fillGraph(root, n_attrs, agnodeattr, gs.size());
439 fillGraph(root, e_attrs, agedgeattr, gs.size());
440}
441
442static void cloneGraphAttr(Agraph_t *g, Agraph_t *ng) {
443 cloneAttrs(g, ng);
444 cloneDfltAttrs(g, ng, AGNODE);
445 cloneDfltAttrs(g, ng, AGEDGE);
446}
447
449using used_t = std::multiset<std::string>;
450
451/* Create a name for an object in the new graph using the
452 * dictionary names and the old name. If the old name has not
453 * been used, use it and add it to names. If it has been used,
454 * create a new name using the old name and a number.
455 * Note that returned string will immediately made into an agstring.
456 */
457static std::string xName(used_t &names, char *oldname) {
458 size_t previous_instances = names.count(oldname);
459 names.insert(oldname);
460 if (previous_instances > 0) {
461 return std::string(oldname) + "_gv" + std::to_string(previous_instances);
462 }
463 return oldname;
464}
465
466#define MARK(e) (ED_alg(e) = e)
467#define MARKED(e) (ED_alg(e))
468#define SETCLUST(g, h) (GD_alg(g) = h)
469#define GETCLUST(g) ((Agraph_t *)GD_alg(g))
470
471/* Create a copy of g in ng, copying attributes, inserting nodes
472 * and adding edges.
473 */
474static void cloneSubg(Agraph_t *g, Agraph_t *ng, Agsym_t *G_bb,
475 used_t &gnames) {
476 node_t *n;
477 node_t *nn;
478 edge_t *e;
479 edge_t *ne;
480 node_t *nt;
481 node_t *nh;
482 Agraph_t *subg;
483 Agraph_t *nsubg;
484
485 cloneGraphAttr(g, ng);
486 if (doPack)
487 agxset(ng, G_bb, ""); /* Unset all subgraph bb */
488
489 /* clone subgraphs */
490 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
491 nsubg = agsubg(ng, xName(gnames, agnameof(subg)).data(), 1);
492 agbindrec(nsubg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
493 cloneSubg(subg, nsubg, G_bb, gnames);
494 /* if subgraphs are clusters, point to the new
495 * one so we can find it later.
496 */
497 if (is_a_cluster(subg))
498 SETCLUST(subg, nsubg);
499 }
500
501 /* add remaining nodes */
502 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
503 nn = NEWNODE(n);
504 agsubnode(ng, nn, 1);
505 }
506
507 /* add remaining edges. libgraph doesn't provide a way to find
508 * multiedges, so we add edges bottom up, marking edges when added.
509 */
510 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
511 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
512 if (MARKED(e))
513 continue;
514 nt = NEWNODE(agtail(e));
515 nh = NEWNODE(aghead(e));
516 ne = agedge(ng, nt, nh, nullptr, 1);
517 agbindrec(ne, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true);
518 cloneEdge(e, ne);
519 MARK(e);
520 }
521 }
522}
523
524/* Given old and new subgraphs which are corresponding
525 * clusters, recursively create the subtree of clusters
526 * under ng using the subtree of clusters under g.
527 */
528static void cloneClusterTree(Agraph_t *g, Agraph_t *ng) {
529 int i;
530
531 cloneCluster(g, ng);
532
533 if (GD_n_cluster(g)) {
534 GD_n_cluster(ng) = GD_n_cluster(g);
535 void *const p = gv_calloc(1 + GD_n_cluster(g), sizeof(Agraph_t *));
536 GD_clust(ng) = new (p) Agraph_t *[1 + GD_n_cluster(g)]{};
537 for (i = 1; i <= GD_n_cluster(g); i++) {
538 Agraph_t *c = GETCLUST(GD_clust(g)[i]);
539 GD_clust(ng)[i] = c;
540 cloneClusterTree(GD_clust(g)[i], c);
541 }
542 }
543}
544
545/* Create and return a new graph which is the logical union
546 * of the graphs gs.
547 */
548static Agraph_t *cloneGraph(std::vector<Agraph_t *> &gs, GVC_t *gvc,
549 Agdesc_t kind) {
550 Agraph_t *root;
551 Agraph_t *subg;
552 Agnode_t *n;
553 Agnode_t *np;
554 bool doWarn = true;
555
556 if (verbose)
557 std::cerr << "Creating clone graph\n";
558 root = agopen(gname, kind, &AgDefaultDisc);
559 initAttrs(root, gs);
560 Agsym_t *const G_bb = agfindgraphattr(root, const_cast<char *>("bb"));
561 if (doPack)
562 assert(G_bb);
563
564 /* add command-line attributes */
565 for (attr_t &a : G_args) {
566 if (Agsym_t *rv = agfindgraphattr(root, a.name))
567 agxset(root, rv, a.value);
568 else
569 agattr_text(root, AGRAPH, a.name, a.value);
570 }
571
572 /* do common initialization. This will handle root's label. */
573 init_graph(root, false, gvc);
575
576 used_t gnames; // dict of used subgraph names
577 used_t nnames; // dict of used node names
578 for (size_t i = 0; i < gs.size(); i++) {
579 Agraph_t *g = gs[i];
580 if (verbose)
581 std::cerr << "Cloning graph " << agnameof(g) << '\n';
582 GD_n_cluster(root) += GD_n_cluster(g);
583 GD_has_labels(root) |= GD_has_labels(g);
584
585 /* Clone nodes, checking for node name conflicts */
586 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
587 if (doWarn && agfindnode(root, agnameof(n))) {
588 std::cerr << "Warning: node " << agnameof(n) << " in graph[" << i
589 << "] " << agnameof(g) << " already defined\n"
590 << "Some nodes will be renamed.\n";
591 doWarn = false;
592 }
593 np = agnode(root, xName(nnames, agnameof(n)).data(), 1);
594 agbindrec(np, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
595 ND_alg(n) = np;
596 cloneNode(n, np);
597 }
598
599 /* wrap the clone of g in a subgraph of root */
600 subg = agsubg(root, xName(gnames, agnameof(g)).data(), 1);
601 agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
602 cloneSubg(g, subg, G_bb, gnames);
603 }
604
605 /* set up cluster tree */
606 if (GD_n_cluster(root)) {
607 int j, idx;
608 void *const p = gv_calloc(1 + GD_n_cluster(root), sizeof(graph_t *));
609 GD_clust(root) = new (p) graph_t *[1 + GD_n_cluster(root)]{};
610
611 idx = 1;
612 for (Agraph_t *g : gs) {
613 for (j = 1; j <= GD_n_cluster(g); j++) {
614 Agraph_t *c = GETCLUST(GD_clust(g)[j]);
615 GD_clust(root)[idx++] = c;
616 cloneClusterTree(GD_clust(g)[j], c);
617 }
618 }
619 }
620
621 return root;
622}
623
624/* Read input, parse the graphs, use init_nop (neato -n) to
625 * read in all attributes need for layout.
626 * Return the list of graphs. If cp != nullptr, set it to the number
627 * of graphs read.
628 * We keep track of the types of graphs read. They all must be
629 * either directed or undirected. If all graphs are strict, the
630 * combined graph will be strict; other, the combined graph will
631 * be non-strict.
632 *
633 * @param kind [out] The type to use for the combined graph
634 */
635static std::vector<Agraph_t *> readGraphs(GVC_t *gvc,
636 std::optional<Agdesc_t> &kind) {
637 std::vector<Agraph_t *> gs;
638 ingraph_state ig;
639
640 /* set various state values */
642 Nop = 2;
643
644 newIngraph(&ig, myFiles);
645 while (Agraph_t *g = nextGraph(&ig)) {
646 if (verbose)
647 std::cerr << "Reading graph " << agnameof(g) << '\n';
648 if (agnnodes(g) == 0) {
649 std::cerr << "Graph " << agnameof(g) << " is empty - ignoring\n";
650 continue;
651 }
652 if (!kind.has_value()) {
653 kind = g->desc;
654 } else if (kind->directed != g->desc.directed) {
655 std::cerr << "Error: all graphs must be directed or undirected\n";
656 graphviz_exit(1);
657 } else if (!agisstrict(g))
658 kind = g->desc;
659 init_graph(g, doPack, gvc);
660 gs.push_back(g);
661 }
662
663 return gs;
664}
665
666/* Compute the bounding box containing the graphs.
667 * We can just use the bounding boxes of the graphs.
668 */
669static boxf compBB(std::vector<Agraph_t *> &gs) {
670 boxf bb, bb2;
671
672 bb = GD_bb(gs[0]);
673
674 for (size_t i = 1; i < gs.size(); i++) {
675 bb2 = GD_bb(gs[i]);
676 bb.LL.x = std::min(bb.LL.x, bb2.LL.x);
677 bb.LL.y = std::min(bb.LL.y, bb2.LL.y);
678 bb.UR.x = std::max(bb.UR.x, bb2.UR.x);
679 bb.UR.y = std::max(bb.UR.y, bb2.UR.y);
680 }
681
682 return bb;
683}
684
685int main(int argc, char *argv[]) {
686 Agraph_t *g;
687 pack_info pinfo;
688 GVC_t *gvc;
689
690 init(argc, argv, &pinfo);
691
692 (void)agseterrf(handle_error);
693
694 doPack = (pinfo.mode != l_undef);
695
697 gvc = gvContextPlugins(lt_preloaded_symbols, DEMAND_LOADING);
698 std::optional<Agdesc_t> kind; // type of graph
699 std::vector<Agraph_t *> gs = readGraphs(gvc, kind);
700 if (gs.empty())
701 graphviz_exit(0);
702
703 /* pack graphs */
704 if (doPack) {
705 if (packGraphs(gs.size(), gs.data(), 0, &pinfo)) {
706 std::cerr << "gvpack: packing of graphs failed.\n";
707 graphviz_exit(1);
708 }
709 }
710
711 /* create union graph and copy attributes */
712 assert(kind.has_value());
713 g = cloneGraph(gs, gvc, *kind);
714
715 /* compute new top-level bb and set */
716 if (doPack) {
717 GD_bb(g) = compBB(gs);
719 attach_attrs(g);
720 }
721 agwrite(g, outfp);
722 graphviz_exit(0);
723}
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
abstract graph C library, Cgraph API
int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum)
Definition utils.c:40
void common_init_edge(edge_t *e)
Definition utils.c:463
bool is_a_cluster(Agraph_t *g)
Definition utils.c:646
#define CL_OFFSET
Definition const.h:142
#define NODENAME_ESC
Definition const.h:80
#define IGNORED
Definition const.h:30
#define GVSPLINES
Definition const.h:164
#define agnodeattr(g, n, v)
Definition dotinit.c:239
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
#define POINTS(a_inches)
Definition geom.h:62
#define POINTS_PER_INCH
Definition geom.h:58
int State
Definition globals.h:66
bool Concentrate
Definition globals.h:62
int Nop
Definition globals.h:57
double PSinputscale
Definition globals.h:58
unsigned short Ndim
Definition globals.h:65
static bool Verbose
Definition gml2gv.c:26
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:204
int agnnodes(Agraph_t *g)
Definition graph.c:163
Agsym_t * agattr_text(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up text attributes of a graph
Definition attr.c:333
int agset(void *obj, char *name, const char *value)
Definition attr.c:474
Agsym_t * agnxtattr(Agraph_t *g, int kind, Agsym_t *attr)
permits traversing the list of attributes of a given type
Definition attr.c:362
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:521
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:457
Agsym_t * agattr_html(Agraph_t *g, int kind, char *name, const char *value)
agattr_text, but creates HTML-like values
Definition attr.c:337
Agdisc_t AgDefaultDisc
Definition graph.c:283
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:252
#define ED_xlabel(e)
Definition types.h:590
#define ED_head_label(e)
Definition types.h:587
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:28
#define ED_spl(e)
Definition types.h:595
#define agtail(e)
Definition cgraph.h:982
#define ED_edge_type(e)
Definition types.h:582
#define ED_tail_label(e)
Definition types.h:596
#define aghead(e)
Definition cgraph.h:983
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:43
#define ED_label(e)
Definition types.h:589
agusererrf agseterrf(agusererrf newf)
Definition agerror.c:34
#define agfindgraphattr(g, a)
Definition types.h:613
#define GD_has_labels(g)
Definition types.h:368
#define GD_clust(g)
Definition types.h:360
int agisstrict(Agraph_t *g)
Definition graph.c:194
#define GD_bb(g)
Definition types.h:354
#define GD_n_cluster(g)
Definition types.h:389
Agraph_t * agopen(char *name, Agdesc_t desc, Agdisc_t *disc)
creates a new graph with the given name and kind
Definition graph.c:44
int agwrite(Agraph_t *g, void *chan)
Return 0 on success, EOF on failure.
Definition write.c:736
#define GD_ndim(g)
Definition types.h:390
#define GD_label(g)
Definition types.h:374
#define GD_gvc(g)
Definition types.h:355
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:143
#define ND_ht(n)
Definition types.h:500
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:50
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:43
Agnode_t * agsubnode(Agraph_t *g, Agnode_t *n, int createflag)
Definition node.c:254
#define agfindnodeattr(g, a)
Definition types.h:615
#define ND_alg(n)
Definition types.h:484
#define ND_rw(n)
Definition types.h:525
#define ND_height(n)
Definition types.h:498
#define ND_width(n)
Definition types.h:536
#define ND_lw(n)
Definition types.h:506
#define ND_xlabel(n)
Definition types.h:503
#define ND_shape_info(n)
Definition types.h:529
#define ND_pos(n)
Definition types.h:520
#define agfindnode(g, n)
Definition types.h:611
#define ND_coord(n)
Definition types.h:490
#define ND_shape(n)
Definition types.h:528
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
#define AGTYPE(obj)
returns AGRAPH, AGNODE, or AGEDGE depending on the type of the object
Definition cgraph.h:216
Agraph_t * agroot(void *obj)
Definition obj.c:170
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
void aginit(Agraph_t *g, int kind, const char *rec_name, int rec_size, int move_to_front)
attach new records to objects of specified kind
Definition rec.c:172
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:91
int aghtmlstr(const char *)
Definition refstr.c:440
int agstrfree(Agraph_t *, const char *, bool is_html)
Definition refstr.c:417
char * agstrdup_html(Agraph_t *, const char *)
returns a pointer to a reference-counted HTML-like copy of the argument string, creating one if neces...
Definition refstr.c:397
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:72
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:77
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:52
GVC_t * gvContextPlugins(const lt_symlist_t *builtins, int demand_loading)
Definition gvc.c:35
void attach_attrs(graph_t *g)
Definition output.c:416
static GVC_t * gvc
Definition gv.cpp:27
#define agraphattr(g, n, s)
Definition gv.cpp:23
replacements for ctype.h functions
static bool gv_iscntrl(int c)
Definition gv_ctype.h:33
static bool gv_isspace(int c)
Definition gv_ctype.h:55
Graphviz context library.
static int verbose
Definition gvpack.cpp:71
static void cloneClusterTree(Agraph_t *g, Agraph_t *ng)
Definition gvpack.cpp:528
#define MARK(e)
Definition gvpack.cpp:466
static void cloneCluster(Agraph_t *old, Agraph_t *new_cluster)
Definition gvpack.cpp:369
static void cloneDfltAttrs(Agraph_t *old, Agraph_t *new_graph, int attr_kind)
Definition gvpack.cpp:310
static Agraph_t * cloneGraph(std::vector< Agraph_t * > &gs, GVC_t *gvc, Agdesc_t kind)
Definition gvpack.cpp:548
std::multiset< std::string > used_t
names that have already been used during generation
Definition gvpack.cpp:449
static void init_node_edge(Agraph_t *g)
initialize node and edge attributes
Definition gvpack.cpp:245
lt_symlist_t lt_preloaded_symbols[]
Definition gvpack.cpp:54
static std::vector< attr_t > G_args
Definition gvpack.cpp:74
static char * gname
Definition gvpack.cpp:76
static int setNameValue(char *arg)
Definition gvpack.cpp:130
static void init_graph(Agraph_t *g, bool fill, GVC_t *gvc)
Definition gvpack.cpp:266
#define NEWNODE(n)
Definition gvpack.cpp:106
static void cloneSubg(Agraph_t *g, Agraph_t *ng, Agsym_t *G_bb, used_t &gnames)
Definition gvpack.cpp:474
#define MARKED(e)
Definition gvpack.cpp:467
static void cloneNode(Agnode_t *old, Agnode_t *new_node)
Definition gvpack.cpp:355
static FILE * outfp
Definition gvpack.cpp:73
static boxf compBB(std::vector< Agraph_t * > &gs)
Definition gvpack.cpp:669
static bool doPack
Definition gvpack.cpp:75
static void fillDict(attr_map_t &newdict, Agraph_t *g, int attr_kind)
Definition gvpack.cpp:390
static std::string xName(used_t &names, char *oldname)
Definition gvpack.cpp:457
static void cloneEdge(Agedge_t *old, Agedge_t *new_edge)
Definition gvpack.cpp:345
static void fillGraph(Agraph_t *g, const attr_map_t &d, Agsym_t *(*setf)(Agraph_t *, char *, const char *), size_t cnt)
Definition gvpack.cpp:409
#define SETCLUST(g, h)
Definition gvpack.cpp:468
static int setUInt(unsigned int *v, char *arg)
Definition gvpack.cpp:145
static const char useString[]
Definition gvpack.cpp:108
static void cloneGraphAttr(Agraph_t *g, Agraph_t *ng)
Definition gvpack.cpp:442
static void initAttrs(Agraph_t *root, std::vector< Agraph_t * > &gs)
Definition gvpack.cpp:426
static void cloneAttrs(void *old, void *new_graph)
Definition gvpack.cpp:320
static std::vector< Agraph_t * > readGraphs(GVC_t *gvc, std::optional< Agdesc_t > &kind)
Definition gvpack.cpp:635
static void init(int argc, char *argv[], pack_info *pinfo)
Definition gvpack.cpp:170
static char ** myFiles
Definition gvpack.cpp:72
static int handle_error(char *message)
Definition gvpack.cpp:83
std::map< std::string, AttributeValue > attr_map_t
attribute name → value collection of those we have seen
Definition gvpack.cpp:384
gvplugin_library_t gvplugin_neato_layout_LTX_library
static Agsym_t * agedgeattr(Agraph_t *g, char *name, const char *value)
Definition gvpack.cpp:166
static bool errored
have we seen an error from libcgraph?
Definition gvpack.cpp:77
#define GETCLUST(g)
Definition gvpack.cpp:469
static const char * usage
Definition gvpr.c:54
Agraph_t * nextGraph(ingraph_state *sp)
Definition ingraphs.c:61
ingraph_state * newIngraph(ingraph_state *sp, char **files)
Definition ingraphs.c:140
supports user-supplied data
void graph_init(graph_t *g, bool use_rankdir)
Definition input.c:600
bool user_pos(attrsym_t *posptr, attrsym_t *pinptr, node_t *np, int nG)
Definition neatoinit.c:75
int init_nop(Agraph_t *g, int adjust)
Definition neatoinit.c:511
static attrsym_t * N_pos
Definition neatoinit.c:54
void neato_init_node(node_t *n)
Definition neatoinit.c:60
static FILE * openFile(const char *argv0, const char *name, const char *mode)
Definition openFile.h:8
pack_mode parsePackModeInfo(const char *p, pack_mode dflt, pack_info *pinfo)
Definition pack.c:1212
int packGraphs(size_t ng, Agraph_t **gs, Agraph_t *root, pack_info *info)
Definition pack.c:1093
support for connected components
@ l_clust
Definition pack.h:55
@ l_undef
Definition pack.h:55
void dotneato_postprocess(Agraph_t *g)
Definition postproc.c:691
graph descriptor
Definition cgraph.h:284
unsigned directed
Definition cgraph.h:285
graph or subgraph
Definition cgraph.h:424
Agdesc_t desc
Definition cgraph.h:426
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:641
Definition gvcint.h:81
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
void * address
Definition gvcext.h:41
pack_mode mode
Definition pack.h:72
int sz
Definition pack.h:69
bool doSplines
use splines in constructing graph shape
Definition pack.h:71
bool * fixed
Definition pack.h:73
unsigned int margin
Definition pack.h:70
double x
Definition geom.h:29
double y
Definition geom.h:29
int main()
Definition grammar.c:90