ccomps.c 18 KB

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