Graphviz 14.1.3~dev.20260126.0926
Loading...
Searching...
No Matches
ccomps.c
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 * Written by Stephen North
18 * Updated by Emden Gansner
19 */
20
21#include "config.h"
22
23#include <assert.h>
24#include <cgraph/cgraph.h>
25#include <cgraph/ingraphs.h>
26#include <common/render.h>
27#include <common/utils.h>
28#include <getopt.h>
29#include <stdbool.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <util/agxbuf.h>
34#include <util/alloc.h>
35#include <util/exit.h>
36#include <util/gv_ctype.h>
37#include <util/list.h>
38#include <util/prisize_t.h>
39#include <util/strview.h>
40#include <util/unreachable.h>
41
42typedef struct {
44 bool cc_subg;
46
47typedef struct {
49 char mark;
52
53#define GD_cc_subg(g) (((graphinfo_t *)(g->base.data))->cc_subg)
54#define Node_mark(n) (((nodeinfo_t *)(n->base.data))->mark)
55#define ND_ptr(n) (((nodeinfo_t *)(n->base.data))->ptr)
56#define ND_dn(n) ((Agnode_t *)ND_ptr(n))
57#define Node_clust(n) ((Agraph_t *)ND_ptr(n))
58
59static char **Inputs;
60static bool verbose = false;
61static enum {
67static bool useClusters = false;
68static bool doEdges = true;
69static bool doAll = true;
70static char *suffix = 0;
71static char *outfile = 0;
73static int sufcnt = 0;
74static bool sorted = false;
75static int sortIndex = 0;
76static int sortFinal;
77static int x_index = -1;
78static int x_final = -1; // require 0 <= x_index <= x_final or x_final= -1
79static enum { BY_INDEX = 1, BY_SIZE = 2 } x_mode;
80static char *x_node;
81
82static char *useString =
83 "Usage: ccomps [-svenCx?] [-X[#%]s[-f]] [-o<out template>] <files>\n\
84 -s - silent\n\
85 -x - external\n\
86 -X - extract component\n\
87 -C - use clusters\n\
88 -e - do not induce edges\n\
89 -n - do not induce subgraphs\n\
90 -v - verbose\n\
91 -o - output file template\n\
92 -z - sort by size, largest first\n\
93 -? - print usage\n\
94If no files are specified, stdin is used\n";
95
96static void usage(int v) {
97 printf("%s", useString);
99}
100
101static void split(void) {
102 char *sfx = strrchr(outfile, '.');
103 if (sfx) {
104 suffix = sfx + 1;
105 size_t size = (size_t)(sfx - outfile);
106 rootpath = (strview_t){.data = outfile, .size = size};
107 } else {
108 rootpath = strview(outfile, '\0');
109 }
110}
111
112static void init(int argc, char *argv[]) {
113 int c;
114 char *endp;
115
116 opterr = 0;
117 while ((c = getopt(argc, argv, ":zo:xCX:nesv?")) != -1) {
118 switch (c) {
119 case 'o':
120 outfile = optarg;
121 split();
122 break;
123 case 'C':
124 useClusters = true;
125 break;
126 case 'e':
127 doEdges = false;
128 break;
129 case 'n':
130 doAll = false;
131 break;
132 case 'x':
134 break;
135 case 's':
137 break;
138 case 'X':
139 if (*optarg == '#' || *optarg == '%') {
140 char *p = optarg + 1;
141 if (*optarg == '#')
143 else
144 x_mode = BY_SIZE;
145 if (gv_isdigit(*p)) {
146 x_index = (int)strtol(p, &endp, 10);
148 if (*endp == '-') {
149 p = endp + 1;
150 if (gv_isdigit(*p)) {
151 x_final = atoi(p);
152 if (x_final < x_index) {
154 fprintf(stderr,
155 "ccomps: final index %d < start index %d in -X%s flag "
156 "- ignored\n",
157 x_final, x_index, optarg);
158 }
159 } else if (*p) {
161 fprintf(stderr,
162 "ccomps: number expected in -X%s flag - ignored\n",
163 optarg);
164 }
165 } else
167 } else
168 fprintf(stderr, "ccomps: number expected in -X%s flag - ignored\n",
169 optarg);
170 } else {
171 x_node = optarg;
173 }
174 break;
175 case 'v':
176 verbose = true;
177 break;
178 case 'z':
179 sorted = true;
180 break;
181 case ':':
182 fprintf(stderr, "ccomps: option -%c missing argument - ignored\n",
183 optopt);
184 break;
185 case '?':
186 if (optopt == '\0' || optopt == '?')
187 usage(0);
188 else {
189 fprintf(stderr, "ccomps: option -%c unrecognized\n", optopt);
190 usage(1);
191 }
192 break;
193 default:
194 UNREACHABLE();
195 }
196 }
197 argv += optind;
198 argc -= optind;
199
200 if (sorted) {
201 if (printMode == EXTRACT && x_index >= 0) {
205 } else if (printMode == EXTERNAL) {
206 sortIndex = -1;
208 } else
209 sorted = false; // not relevant; turn off
210 }
211 if (argc > 0)
212 Inputs = argv;
213}
214
215static LIST(Agnode_t *) Stk;
216
217static void push(Agnode_t *np) {
218 Node_mark(np) = -1;
219 LIST_PUSH_BACK(&Stk, np);
220}
221
222static Agnode_t *pop(void) {
223 if (LIST_IS_EMPTY(&Stk)) {
224 return NULL;
225 }
226 return LIST_POP_BACK(&Stk);
227}
228
229static int dfs(Agraph_t *g, Agnode_t *n, Agraph_t *out) {
230 Agnode_t *other;
231 int cnt = 0;
232
233 push(n);
234 while ((n = pop())) {
235 Node_mark(n) = 1;
236 cnt++;
237 agsubnode(out, n, 1);
238 for (Agedge_t *e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
239 if ((other = agtail(e)) == n)
240 other = aghead(e);
241 if (Node_mark(other) == 0)
242 push(other);
243 }
244 }
245 return cnt;
246}
247
248static char *getName(void) {
249 agxbuf name = {0};
250
251 if (sufcnt == 0)
252 agxbput(&name, outfile);
253 else {
254 if (suffix)
255 agxbprint(&name, "%.*s_%d.%s", (int)rootpath.size, rootpath.data, sufcnt,
256 suffix);
257 else
258 agxbprint(&name, "%.*s_%d", (int)rootpath.size, rootpath.data, sufcnt);
259 }
260 sufcnt++;
261 return agxbdisown(&name);
262}
263
264static void gwrite(Agraph_t *g) {
265 if (!outfile) {
266 agwrite(g, stdout);
267 fflush(stdout);
268 } else {
269 char *const name = getName();
270 FILE *const outf = fopen(name, "w");
271 if (!outf) {
272 fprintf(stderr, "Could not open %s for writing\n", name);
273 perror("ccomps");
274 free(name);
275 graphviz_exit(EXIT_FAILURE);
276 }
277 free(name);
278 agwrite(g, outf);
279 fclose(outf);
280 }
281}
282
283/* If any nodes of subg are in g, create a subgraph of g
284 * and fill it with all nodes of subg in g and their induced
285 * edges in subg. Copy the attributes of subg to g. Return the subgraph.
286 * If not, return null.
287 */
288static Agraph_t *projectG(Agraph_t *subg, Agraph_t *g, int inCluster) {
289 Agraph_t *proj = 0;
290 Agnode_t *m;
291
292 for (Agnode_t *n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
293 if ((m = agfindnode(g, agnameof(n)))) {
294 if (proj == 0) {
295 proj = agsubg(g, agnameof(subg), 1);
296 }
297 agsubnode(proj, m, 1);
298 }
299 }
300 if (!proj && inCluster) {
301 proj = agsubg(g, agnameof(subg), 1);
302 }
303 if (proj) {
304 if (doEdges)
305 (void)graphviz_node_induce(proj, subg);
306 agcopyattr(subg, proj);
307 }
308
309 return proj;
310}
311
312/* Project subgraphs of root graph on subgraph.
313 * If non-empty, add to subgraph.
314 */
315static void subgInduce(Agraph_t *root, Agraph_t *g, int inCluster) {
316 Agraph_t *proj;
317
318 for (Agraph_t *subg = agfstsubg(root); subg; subg = agnxtsubg(subg)) {
319 if (GD_cc_subg(subg))
320 continue;
321 if ((proj = projectG(subg, g, inCluster))) {
322 const int in_cluster = inCluster || (useClusters && is_a_cluster(subg));
323 subgInduce(subg, proj, in_cluster);
324 }
325 }
326}
327
328static void subGInduce(Agraph_t *g, Agraph_t *out) { subgInduce(g, out, 0); }
329
330#define PFX1 "%s_cc"
331#define PFX2 "%s_cc_%" PRISIZE_T
332
333/* Construct nodes in derived graph corresponding top-level clusters.
334 * Since a cluster might be wrapped in a subgraph, we need to traverse
335 * down into the tree of subgraphs
336 */
337static void deriveClusters(Agraph_t *dg, Agraph_t *g) {
338 for (Agraph_t *subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
339 if (is_a_cluster(subg)) {
340 Agnode_t *const dn = agnode(dg, agnameof(subg), 1);
341 agbindrec(dn, "nodeinfo", sizeof(nodeinfo_t), true);
342 ND_ptr(dn) = &subg->base;
343 for (Agnode_t *n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
344 if (ND_ptr(n)) {
345 fprintf(stderr,
346 "Error: node \"%s\" belongs to two non-nested clusters "
347 "\"%s\" and \"%s\"\n",
348 agnameof(n), agnameof(subg), agnameof(ND_dn(n)));
349 }
350 ND_ptr(n) = &dn->base;
351 }
352 } else {
353 deriveClusters(dg, subg);
354 }
355 }
356}
357
358/* Create derived graph dg of g where nodes correspond to top-level nodes
359 * or clusters, and there is an edge in dg if there is an edge in g
360 * between any nodes in the respective clusters.
361 */
364
365 deriveClusters(dg, g);
366
367 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
368 if (ND_dn(n))
369 continue;
370 Agnode_t *const dn = agnode(dg, agnameof(n), 1);
371 agbindrec(dn, "nodeinfo", sizeof(nodeinfo_t), true);
372 ND_ptr(dn) = &n->base;
373 ND_ptr(n) = &dn->base;
374 }
375
376 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
377 Agnode_t *tl = ND_dn(n);
378 for (Agedge_t *e = agfstout(g, n); e; e = agnxtout(g, e)) {
379 Agnode_t *const hd = ND_dn(aghead(e));
380 if (hd == tl)
381 continue;
382 if (hd > tl)
383 agedge(dg, tl, hd, 0, 1);
384 else
385 agedge(dg, hd, tl, 0, 1);
386 }
387 }
388
389 return dg;
390}
391
393static void unionNodes(Agraph_t *dg, Agraph_t *g) {
394 for (Agnode_t *dn = agfstnode(dg); dn; dn = agnxtnode(dg, dn)) {
395 if (AGTYPE(ND_ptr(dn)) == AGNODE) {
396 agsubnode(g, ND_dn(dn), 1);
397 } else {
398 Agraph_t *const clust = Node_clust(dn);
399 for (Agnode_t *n = agfstnode(clust); n; n = agnxtnode(clust, n))
400 agsubnode(g, n, 1);
401 }
402 }
403}
404
405static int cmp(const void *x, const void *y) {
406// Suppress Clang/GCC -Wcast-qual warning. Casting away const here is acceptable
407// as the later usage is const. We need the cast because `agnnodes` uses
408// non-const pointers.
409#ifdef __GNUC__
410#pragma GCC diagnostic push
411#pragma GCC diagnostic ignored "-Wcast-qual"
412#endif
413 Agraph_t **p0 = (Agraph_t **)x;
414 Agraph_t **p1 = (Agraph_t **)y;
415#ifdef __GNUC__
416#pragma GCC diagnostic pop
417#endif
418 const int n0 = agnnodes(*p0);
419 const int n1 = agnnodes(*p1);
420 if (n0 < n1) {
421 return 1;
422 }
423 if (n0 > n1) {
424 return -1;
425 }
426 return 0;
427}
428
429static void printSorted(Agraph_t *root, size_t c_cnt) {
430 Agraph_t **ccs = gv_calloc(c_cnt, sizeof(Agraph_t *));
431 size_t i = 0;
432
433 for (Agraph_t *subg = agfstsubg(root); subg; subg = agnxtsubg(subg)) {
434 if (GD_cc_subg(subg))
435 ccs[i++] = subg;
436 }
437 /* sort by component size, largest first */
438 qsort(ccs, c_cnt, sizeof(Agraph_t *), cmp);
439
440 if (sortIndex >= 0) {
441 if (x_mode == BY_INDEX) {
442 if ((size_t)sortIndex >= c_cnt) {
443 fprintf(stderr,
444 "ccomps: component %d not found in graph %s - ignored\n",
445 sortIndex, agnameof(root));
446 free(ccs);
447 return;
448 }
449 size_t endi;
450 if (sortFinal >= sortIndex) {
451 endi = (size_t)sortFinal;
452 if (endi >= c_cnt) {
453 assert(c_cnt > 0);
454 endi = c_cnt - 1;
455 }
456 } else
457 endi = c_cnt - 1;
458 for (i = (size_t)sortIndex; i <= endi; i++) {
459 Agraph_t *const subg = ccs[i];
460 if (doAll)
461 subGInduce(root, subg);
462 gwrite(subg);
463 }
464 } else if (x_mode == BY_SIZE) {
465 if (sortFinal == -1)
466 sortFinal = agnnodes(ccs[0]);
467 for (i = 0; i < c_cnt; i++) {
468 Agraph_t *const subg = ccs[i];
469 int sz = agnnodes(subg);
470 if (sz > sortFinal)
471 continue;
472 if (sz < sortIndex)
473 break;
474 if (doAll)
475 subGInduce(root, subg);
476 gwrite(subg);
477 }
478 }
479 } else
480 for (i = 0; i < c_cnt; i++) {
481 Agraph_t *const subg = ccs[i];
482 if (doAll)
483 subGInduce(root, subg);
484 gwrite(subg);
485 }
486 free(ccs);
487}
488
490static int processClusters(Agraph_t *g, char *graphName) {
491 Agraph_t *out;
492 Agraph_t *dout;
493 bool extracted = false;
494
495 Agraph_t *const dg = deriveGraph(g);
496
497 if (x_node) {
498 Agnode_t *const n = agfindnode(g, x_node);
499 if (!n) {
500 fprintf(stderr, "ccomps: node %s not found in graph %s\n", x_node,
501 agnameof(g));
502 return 1;
503 }
504 {
505 agxbuf buf = {0};
506 agxbprint(&buf, PFX1, graphName);
507 char *name = agxbuse(&buf);
508 dout = agsubg(dg, name, 1);
509 out = agsubg(g, name, 1);
510 agxbfree(&buf);
511 }
512 aginit(out, AGRAPH, "graphinfo", sizeof(graphinfo_t), true);
513 GD_cc_subg(out) = true;
514 Agnode_t *const dn = ND_dn(n);
515 const long n_cnt = dfs(dg, dn, dout);
516 unionNodes(dout, out);
517 size_t e_cnt = 0;
518 if (doEdges)
519 e_cnt = graphviz_node_induce(out, out->root);
520 if (doAll)
521 subGInduce(g, out);
522 gwrite(out);
523 if (verbose)
524 fprintf(stderr, " %7ld nodes %7" PRISIZE_T " edges\n", n_cnt, e_cnt);
525 return 0;
526 }
527
528 size_t c_cnt = 0;
529 for (Agnode_t *dn = agfstnode(dg); dn; dn = agnxtnode(dg, dn)) {
530 if (Node_mark(dn))
531 continue;
532 {
533 agxbuf buf = {0};
534 agxbprint(&buf, PFX2, graphName, c_cnt);
535 char *name = agxbuse(&buf);
536 dout = agsubg(dg, name, 1);
537 out = agsubg(g, name, 1);
538 agxbfree(&buf);
539 }
540 aginit(out, AGRAPH, "graphinfo", sizeof(graphinfo_t), true);
541 GD_cc_subg(out) = true;
542 const long n_cnt = dfs(dg, dn, dout);
543 unionNodes(dout, out);
544 size_t e_cnt = 0;
545 if (doEdges)
546 e_cnt = graphviz_node_induce(out, out->root);
547 if (printMode == EXTERNAL) {
548 if (doAll)
549 subGInduce(g, out);
550 gwrite(out);
551 } else if (printMode == EXTRACT) {
552 if (x_mode == BY_INDEX) {
553 if (x_index < 0 || (size_t)x_index <= c_cnt) {
554 extracted = true;
555 if (doAll)
556 subGInduce(g, out);
557 gwrite(out);
558 if (x_final >= 0 && c_cnt == (size_t)x_final)
559 return 0;
560 }
561 } else if (x_mode == BY_SIZE) {
562 int sz = agnnodes(out);
563 if (x_index <= sz && (x_final == -1 || sz <= x_final)) {
564 extracted = true;
565 if (doAll)
566 subGInduce(g, out);
567 gwrite(out);
568 }
569 }
570 }
571 if (printMode != INTERNAL)
572 agdelete(g, out);
573 agdelete(dg, dout);
574 if (verbose)
575 fprintf(stderr, "(%4" PRISIZE_T ") %7ld nodes %7" PRISIZE_T " edges\n",
576 c_cnt, n_cnt, e_cnt);
577 c_cnt++;
578 }
579 if (printMode == EXTRACT && !extracted && x_mode == BY_INDEX) {
580 fprintf(stderr, "ccomps: component %d not found in graph %s - ignored\n",
581 x_index, agnameof(g));
582 return 1;
583 }
584
585 if (sorted)
586 printSorted(g, c_cnt);
587 else if (printMode == INTERNAL)
588 gwrite(g);
589
590 if (verbose)
591 fprintf(stderr,
592 " %7d nodes %7d edges %7" PRISIZE_T " components %s\n",
593 agnnodes(g), agnedges(g), c_cnt, agnameof(g));
594
595 agclose(dg);
596
597 return c_cnt ? 1 : 0;
598}
599
600static void bindGraphinfo(Agraph_t *g) {
601 aginit(g, AGRAPH, "graphinfo", sizeof(graphinfo_t), true);
602 for (Agraph_t *subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
603 bindGraphinfo(subg);
604 }
605}
606
608static int process(Agraph_t *g, char *graphName) {
609 Agraph_t *out;
610 bool extracted = false;
611
612 aginit(g, AGNODE, "nodeinfo", sizeof(nodeinfo_t), true);
613 bindGraphinfo(g);
614
615 if (useClusters)
616 return processClusters(g, graphName);
617
618 if (x_node) {
619 Agnode_t *const n = agfindnode(g, x_node);
620 if (!n) {
621 fprintf(stderr, "ccomps: node %s not found in graph %s - ignored\n",
622 x_node, agnameof(g));
623 return 1;
624 }
625 {
626 agxbuf name = {0};
627 agxbprint(&name, PFX1, graphName);
628 out = agsubg(g, agxbuse(&name), 1);
629 agxbfree(&name);
630 }
631 aginit(out, AGRAPH, "graphinfo", sizeof(graphinfo_t), true);
632 GD_cc_subg(out) = true;
633 const long n_cnt = dfs(g, n, out);
634 size_t e_cnt = 0;
635 if (doEdges)
636 e_cnt = graphviz_node_induce(out, out->root);
637 if (doAll)
638 subGInduce(g, out);
639 gwrite(out);
640 if (verbose)
641 fprintf(stderr, " %7ld nodes %7" PRISIZE_T " edges\n", n_cnt, e_cnt);
642 return 0;
643 }
644
645 size_t c_cnt = 0;
646 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
647 if (Node_mark(n))
648 continue;
649 {
650 agxbuf name = {0};
651 agxbprint(&name, PFX2, graphName, c_cnt);
652 out = agsubg(g, agxbuse(&name), 1);
653 agxbfree(&name);
654 }
655 aginit(out, AGRAPH, "graphinfo", sizeof(graphinfo_t), true);
656 GD_cc_subg(out) = true;
657 const long n_cnt = dfs(g, n, out);
658 size_t e_cnt = 0;
659 if (doEdges)
660 e_cnt = graphviz_node_induce(out, out->root);
661 if (printMode == EXTERNAL) {
662 if (doAll)
663 subGInduce(g, out);
664 gwrite(out);
665 } else if (printMode == EXTRACT) {
666 if (x_mode == BY_INDEX) {
667 if (x_index < 0 || (size_t)x_index <= c_cnt) {
668 extracted = true;
669 if (doAll)
670 subGInduce(g, out);
671 gwrite(out);
672 if (x_final >= 0 && c_cnt == (size_t)x_final)
673 return 0;
674 }
675 } else if (x_mode == BY_SIZE) {
676 int sz = agnnodes(out);
677 if (x_index <= sz && (x_final == -1 || sz <= x_final)) {
678 extracted = true;
679 if (doAll)
680 subGInduce(g, out);
681 gwrite(out);
682 }
683 }
684 }
685 if (printMode != INTERNAL)
686 agdelete(g, out);
687 if (verbose)
688 fprintf(stderr, "(%4" PRISIZE_T ") %7ld nodes %7" PRISIZE_T " edges\n",
689 c_cnt, n_cnt, e_cnt);
690 c_cnt++;
691 }
692 if (printMode == EXTRACT && !extracted && x_mode == BY_INDEX) {
693 fprintf(stderr, "ccomps: component %d not found in graph %s - ignored\n",
694 x_index, agnameof(g));
695 return 1;
696 }
697
698 if (sorted)
699 printSorted(g, c_cnt);
700 else if (printMode == INTERNAL)
701 gwrite(g);
702
703 if (verbose)
704 fprintf(stderr,
705 " %7d nodes %7d edges %7" PRISIZE_T " components %s\n",
706 agnnodes(g), agnedges(g), c_cnt, agnameof(g));
707 return c_cnt > 1;
708}
709
710/* If the graph is anonymous, its name starts with '%'.
711 * If we use this as the prefix for subgraphs, they will not be
712 * emitted in agwrite() because cgraph assumes these were anonymous
713 * structural subgraphs all of whose properties are attached directly
714 * to the nodes and edges involved.
715 *
716 * This function checks for an initial '%' and if found, prepends '_'
717 * to the graph name.
718 * NB: static buffer is used.
719 */
720static char *chkGraphName(Agraph_t *g) {
721 static agxbuf buf;
722 char *s = agnameof(g);
723
724 if (*s != '%')
725 return s;
726 agxbprint(&buf, "_%s", s);
727
728 return agxbuse(&buf);
729}
730
731int main(int argc, char *argv[]) {
732 Agraph_t *g;
733 ingraph_state ig;
734 int r = 0;
735 init(argc, argv);
736 newIngraph(&ig, Inputs);
737
738 while ((g = nextGraph(&ig)) != 0) {
739 r += process(g, chkGraphName(g));
740 agclose(g);
741 }
742
743 LIST_FREE(&Stk);
744
745 graphviz_exit(r);
746}
static void out(agerrlevel_t level, const char *fmt, va_list args)
Report messages using a user-supplied or default write function.
Definition agerror.c:86
Dynamically expanding string buffers.
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:97
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:252
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:325
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:345
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
#define graphName(g)
Definition acyclic.c:34
static void init(int argc, char *argv[])
Definition ccomps.c:112
static char ** Inputs
Definition ccomps.c:59
#define ND_dn(n)
Definition ccomps.c:56
static int processClusters(Agraph_t *g, char *graphName)
return 0 if graph is connected
Definition ccomps.c:490
static char * x_node
Definition ccomps.c:80
static int process(Agraph_t *g, char *graphName)
return 0 if graph is connected
Definition ccomps.c:608
static enum @24 x_mode
static void gwrite(Agraph_t *g)
Definition ccomps.c:264
static enum @23 printMode
static bool doEdges
induce edges
Definition ccomps.c:68
#define ND_ptr(n)
Definition ccomps.c:55
static void split(void)
Definition ccomps.c:101
static char * suffix
Definition ccomps.c:70
static Agnode_t * pop(void)
Definition ccomps.c:222
static void deriveClusters(Agraph_t *dg, Agraph_t *g)
Definition ccomps.c:337
static Agraph_t * deriveGraph(Agraph_t *g)
Definition ccomps.c:362
static int sortIndex
Definition ccomps.c:75
static int sortFinal
Definition ccomps.c:76
static int cmp(const void *x, const void *y)
Definition ccomps.c:405
static void unionNodes(Agraph_t *dg, Agraph_t *g)
add all nodes in cluster nodes of dg to g
Definition ccomps.c:393
#define Node_clust(n)
Definition ccomps.c:57
static Agraph_t * projectG(Agraph_t *subg, Agraph_t *g, int inCluster)
Definition ccomps.c:288
@ BY_SIZE
Definition ccomps.c:79
@ BY_INDEX
Definition ccomps.c:79
#define Node_mark(n)
Definition ccomps.c:54
static char * getName(void)
Definition ccomps.c:248
static void subGInduce(Agraph_t *g, Agraph_t *out)
Definition ccomps.c:328
static void printSorted(Agraph_t *root, size_t c_cnt)
Definition ccomps.c:429
static void subgInduce(Agraph_t *root, Agraph_t *g, int inCluster)
Definition ccomps.c:315
static char * chkGraphName(Agraph_t *g)
Definition ccomps.c:720
@ EXTERNAL
Definition ccomps.c:63
@ SILENT
Definition ccomps.c:64
@ EXTRACT
Definition ccomps.c:65
@ INTERNAL
all components need to be generated before output
Definition ccomps.c:62
static int dfs(Agraph_t *g, Agnode_t *n, Agraph_t *out)
Definition ccomps.c:229
static bool verbose
Definition ccomps.c:60
static int x_final
Definition ccomps.c:78
static char * useString
Definition ccomps.c:82
static char * outfile
Definition ccomps.c:71
static strview_t rootpath
Definition ccomps.c:72
static bool useClusters
Definition ccomps.c:67
static void bindGraphinfo(Agraph_t *g)
Definition ccomps.c:600
static int x_index
Definition ccomps.c:77
#define PFX2
Definition ccomps.c:331
static int sufcnt
Definition ccomps.c:73
static bool doAll
induce subgraphs
Definition ccomps.c:69
#define PFX1
Definition ccomps.c:330
static bool sorted
Definition ccomps.c:74
#define GD_cc_subg(g)
Definition ccomps.c:53
bool is_a_cluster(Agraph_t *g)
Definition utils.c:695
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
void free(void *)
static gstack_t * push(gstack_t *s, Agraph_t *subg)
Definition grammar.c:1655
node NULL
Definition grammar.y:181
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:198
int agnedges(Agraph_t *g)
Definition graph.c:163
int agnnodes(Agraph_t *g)
Definition graph.c:157
size_t graphviz_node_induce(Agraph_t *g, Agraph_t *edgeset)
Definition node_induce.c:12
int agcopyattr(void *oldobj, void *newobj)
copies all of the attributes from one object to another
Definition attr.c:635
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:255
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:28
#define agtail(e)
Definition cgraph.h:977
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:98
#define aghead(e)
Definition cgraph.h:978
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:43
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:89
Agdesc_t Agstrictundirected
strict undirected
Definition graph.c:275
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:97
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:669
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:143
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 agfindnode(g, n)
Definition types.h:611
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
int agdelete(Agraph_t *g, void *obj)
deletes object. Equivalent to agclose, agdelnode, and agdeledge for obj being a graph,...
Definition obj.c:22
@ 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
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:75
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:80
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:55
replacements for ctype.h functions
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
agxbput(xb, staging)
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
type-generic dynamically expanding list
#define LIST(type)
Definition list.h:55
#define LIST_FREE(list)
Definition list.h:370
#define LIST_POP_BACK(list)
Definition list.h:407
#define LIST_IS_EMPTY(list)
Definition list.h:90
#define LIST_PUSH_BACK(list, item)
Definition list.h:384
#define PRISIZE_T
Definition prisize_t.h:25
Agobj_t base
Definition cgraph.h:260
a generic header of Agraph_s, Agnode_s and Agedge_s
Definition cgraph.h:210
graph or subgraph
Definition cgraph.h:424
implementation of Agrec_t
Definition cgraph.h:172
Agrec_t h
Definition ccomps.c:43
bool cc_subg
true iff subgraph corresponds to a component
Definition ccomps.c:44
Agrec_t h
Definition ccomps.c:48
char mark
Definition ccomps.c:49
Agobj_t * ptr
Definition ccomps.c:50
a non-owning string reference
Definition strview.h:20
const char * data
start of the pointed to string
Definition strview.h:21
size_t size
extent of the string in bytes
Definition strview.h:22
Non-owning string references.
static strview_t strview(const char *referent, char terminator)
create a string reference
Definition strview.h:26
int main()
Definition grammar.c:90
#define UNREACHABLE()
Definition unreachable.h:30