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