bcomps.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /**
  2. * @file
  3. * @brief biconnected 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. * Generate biconnected components
  16. *
  17. * Written by Emden Gansner
  18. */
  19. #include "config.h"
  20. #include <stdbool.h>
  21. #include <stdio.h>
  22. #include <string.h>
  23. #include <assert.h>
  24. #include <getopt.h>
  25. #include <stdlib.h>
  26. #include <cgraph/cgraph.h>
  27. #include <cgraph/gv_math.h>
  28. #include <cgraph/ingraphs.h>
  29. #include <cgraph/list.h>
  30. #include <util/agxbuf.h>
  31. #include <util/alloc.h>
  32. #include <util/exit.h>
  33. #include <util/unreachable.h>
  34. typedef struct {
  35. Agrec_t h;
  36. Agraph_t *next;
  37. } Agraphinfo_t;
  38. typedef struct {
  39. Agrec_t h;
  40. } Agedgeinfo_t;
  41. typedef struct {
  42. Agrec_t h;
  43. int low;
  44. int val;
  45. int isCut;
  46. } Agnodeinfo_t;
  47. #define Low(n) (((Agnodeinfo_t*)(n->base.data))->low)
  48. #define Cut(n) (((Agnodeinfo_t*)(n->base.data))->isCut)
  49. #define N(n) (((Agnodeinfo_t*)(n->base.data))->val)
  50. #define NEXTBLK(g) (((Agraphinfo_t*)(g->base.data))->next)
  51. char **Files;
  52. int verbose;
  53. int silent;
  54. char *outfile = 0;
  55. char *path = 0;
  56. char *suffix = 0;
  57. int external; /* emit blocks as root graphs */
  58. int doTree; /* emit block-cutpoint tree */
  59. DEFINE_LIST(edge_stack, Agedge_t *)
  60. typedef struct {
  61. int count;
  62. int nComp;
  63. edge_stack_t stk;
  64. Agraph_t *blks;
  65. } bcstate;
  66. static char *blockName(agxbuf *xb, char *gname, int d) {
  67. if (*gname == '%') /* anonymous graph */
  68. agxbprint(xb, "_%s_bcc_%d", gname, d);
  69. else
  70. agxbprint(xb, "%s_bcc_%d", gname, d);
  71. return agxbuse(xb);
  72. }
  73. /* getName:
  74. * Generate name for output using input template.
  75. * Has form path_<g>_<i>.suffix, for ith write for the gth graph.
  76. * If isTree, use path_<g>_t.suffix.
  77. * If sufcnt is zero and graph 0, use outfile
  78. */
  79. static char *getName(int ng, int nb)
  80. {
  81. agxbuf name = {0};
  82. if (ng == 0 && nb == 0)
  83. agxbput(&name, outfile);
  84. else {
  85. if (suffix) {
  86. if (nb < 0)
  87. agxbprint(&name, "%s_%d_T.%s", path, ng, suffix);
  88. else
  89. agxbprint(&name, "%s_%d_%d.%s", path, ng, nb, suffix);
  90. } else {
  91. if (nb < 0)
  92. agxbprint(&name, "%s_%d_T", path, ng);
  93. else
  94. agxbprint(&name, "%s_%d_%d", path, ng, nb);
  95. }
  96. }
  97. return agxbdisown(&name);
  98. }
  99. static void gwrite(Agraph_t * g, int ng, int nb)
  100. {
  101. FILE *outf;
  102. char *name;
  103. if (silent)
  104. return;
  105. if (!outfile) {
  106. agwrite(g, stdout);
  107. fflush(stdout);
  108. } else {
  109. name = getName(ng, nb);
  110. outf = fopen(name, "w");
  111. if (!outf) {
  112. fprintf(stderr, "Could not open %s for writing\n", name);
  113. perror("bcomps");
  114. free(name);
  115. graphviz_exit(1);
  116. }
  117. free(name);
  118. agwrite(g, outf);
  119. fclose(outf);
  120. }
  121. }
  122. static Agraph_t *mkBlock(Agraph_t * g, bcstate * stp)
  123. {
  124. Agraph_t *sg;
  125. stp->nComp++;
  126. agxbuf xb = {0};
  127. sg = agsubg(g, blockName(&xb, agnameof(g), stp->nComp), 1);
  128. agxbfree(&xb);
  129. agbindrec(sg, "info", sizeof(Agraphinfo_t), true);
  130. NEXTBLK(sg) = stp->blks;
  131. stp->blks = sg;
  132. return sg;
  133. }
  134. static void
  135. dfs(Agraph_t * g, Agnode_t * u, bcstate * stp, Agnode_t * parent)
  136. {
  137. Agnode_t *v;
  138. Agedge_t *e;
  139. Agedge_t *ep;
  140. Agraph_t *sg;
  141. stp->count++;
  142. Low(u) = N(u) = stp->count;
  143. for (e = agfstedge(g, u); e; e = agnxtedge(g, e, u)) {
  144. if ((v = aghead(e)) == u)
  145. v = agtail(e);
  146. if (v == u)
  147. continue;
  148. if (N(v) == 0) {
  149. edge_stack_push_back(&stp->stk, e);
  150. dfs(g, v, stp, u);
  151. Low(u) = imin(Low(u), Low(v));
  152. if (Low(v) >= N(u)) { /* u is an articulation point */
  153. Cut(u) = 1;
  154. sg = mkBlock(g, stp);
  155. do {
  156. ep = edge_stack_pop_back(&stp->stk);
  157. agsubnode(sg, aghead(ep), 1);
  158. agsubnode(sg, agtail(ep), 1);
  159. } while (ep != e);
  160. }
  161. } else if (parent != v) {
  162. Low(u) = imin(Low(u), N(v));
  163. if (N(v) < N(u))
  164. edge_stack_push_back(&stp->stk, e);
  165. }
  166. }
  167. }
  168. static void addCutPts(Agraph_t * tree, Agraph_t * blk)
  169. {
  170. Agnode_t *n;
  171. Agnode_t *bn;
  172. Agnode_t *cn;
  173. bn = agnode(tree, agnameof(blk), 1);
  174. for (n = agfstnode(blk); n; n = agnxtnode(blk, n)) {
  175. if (Cut(n)) {
  176. cn = agnode(tree, agnameof(n), 1);
  177. agedge(tree, bn, cn, 0, 1);
  178. }
  179. }
  180. }
  181. static int process(Agraph_t * g, int gcnt)
  182. {
  183. Agnode_t *n;
  184. bcstate state;
  185. Agraph_t *blk;
  186. Agraph_t *tree;
  187. int bcnt;
  188. aginit(g, AGNODE, "info", sizeof(Agnodeinfo_t), true);
  189. aginit(g, AGEDGE, "info", sizeof(Agedgeinfo_t), true);
  190. aginit(g, AGRAPH, "info", sizeof(Agraphinfo_t), true);
  191. state.count = 0;
  192. state.nComp = 0;
  193. state.stk = (edge_stack_t){0};
  194. state.blks = 0;
  195. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  196. if (N(n) == 0)
  197. dfs(g, n, &state, 0);
  198. }
  199. for (blk = state.blks; blk; blk = NEXTBLK(blk)) {
  200. (void)graphviz_node_induce(blk, g);
  201. }
  202. if (external) {
  203. bcnt = 0;
  204. for (blk = state.blks; blk; blk = NEXTBLK(blk)) {
  205. gwrite(blk, gcnt, bcnt++);
  206. }
  207. } else
  208. gwrite(g, gcnt, 0);
  209. if (doTree) {
  210. tree = agopen("blkcut_tree", Agstrictundirected, 0);
  211. for (blk = state.blks; blk; blk = NEXTBLK(blk))
  212. addCutPts(tree, blk);
  213. gwrite(tree, gcnt, -1);
  214. agclose(tree);
  215. }
  216. if (verbose) {
  217. int cuts = 0;
  218. bcnt = 0;
  219. for (blk = state.blks; blk; blk = NEXTBLK(blk))
  220. bcnt++;
  221. for (n = agfstnode(g); n; n = agnxtnode(g, n))
  222. if (Cut(n))
  223. cuts++;
  224. fprintf(stderr, "%s: %d blocks %d cutpoints\n", agnameof(g), bcnt,
  225. cuts);
  226. }
  227. edge_stack_free(&state.stk);
  228. if (state.blks && NEXTBLK(state.blks))
  229. return 1; /* >= 2 blocks */
  230. else
  231. return 0;
  232. }
  233. static char *useString =
  234. "Usage: bcomps [-stvx?] [-o<out template>] <files>\n\
  235. -o - output file template\n\
  236. -s - don't print components\n\
  237. -t - emit block-cutpoint tree\n\
  238. -v - verbose\n\
  239. -x - external\n\
  240. -? - print usage\n\
  241. If no files are specified, stdin is used\n";
  242. static void usage(int v)
  243. {
  244. printf("%s",useString);
  245. graphviz_exit(v);
  246. }
  247. static void split(char *name)
  248. {
  249. char *sfx = 0;
  250. sfx = strrchr(name, '.');
  251. if (sfx) {
  252. size_t size = (size_t)(sfx - name);
  253. suffix = sfx + 1;
  254. path = gv_strndup(name, size);
  255. } else {
  256. path = name;
  257. }
  258. }
  259. static void init(int argc, char *argv[])
  260. {
  261. int c;
  262. opterr = 0;
  263. while ((c = getopt(argc, argv, ":o:xstv?")) != -1) {
  264. switch (c) {
  265. case 'o':
  266. outfile = optarg;
  267. split(outfile);
  268. break;
  269. case 's':
  270. verbose = 1;
  271. silent = 1;
  272. break;
  273. case 'v':
  274. verbose = 1;
  275. break;
  276. case 't':
  277. doTree = 1;
  278. break;
  279. case 'x':
  280. external = 1;
  281. break;
  282. case ':':
  283. fprintf(stderr, "bcomps: option -%c missing argument - ignored\n", optopt);
  284. break;
  285. case '?':
  286. if (optopt == '\0' || optopt == '?')
  287. usage(0);
  288. else {
  289. fprintf(stderr,
  290. "bcomps: option -%c unrecognized\n", optopt);
  291. usage(1);
  292. }
  293. break;
  294. default:
  295. UNREACHABLE();
  296. }
  297. }
  298. argv += optind;
  299. argc -= optind;
  300. if (argc > 0)
  301. Files = argv;
  302. }
  303. int main(int argc, char *argv[])
  304. {
  305. Agraph_t *g;
  306. ingraph_state ig;
  307. int r = 0;
  308. int gcnt = 0;
  309. init(argc, argv);
  310. newIngraph(&ig, Files);
  311. while ((g = nextGraph(&ig)) != 0) {
  312. r |= process(g, gcnt);
  313. agclose(g);
  314. gcnt++;
  315. }
  316. graphviz_exit(r);
  317. }