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