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