Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
bcomps.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 * Generate biconnected components
19 *
20 * Written by Emden Gansner
21 */
22#include "config.h"
23
24#include <stdbool.h>
25#include <stdio.h>
26#include <string.h>
27#include <assert.h>
28#include <getopt.h>
29
30#include <stdlib.h>
31#include <cgraph/agxbuf.h>
32#include <cgraph/alloc.h>
33#include <cgraph/cgraph.h>
34#include <cgraph/exit.h>
35#include <cgraph/gv_math.h>
36#include <cgraph/ingraphs.h>
37#include <cgraph/stack.h>
38#include <cgraph/unreachable.h>
39
40typedef struct {
44
45typedef struct {
48
49typedef struct {
51 int low;
52 int val;
53 int isCut;
55
56#define Low(n) (((Agnodeinfo_t*)(n->base.data))->low)
57#define Cut(n) (((Agnodeinfo_t*)(n->base.data))->isCut)
58#define N(n) (((Agnodeinfo_t*)(n->base.data))->val)
59#define NEXTBLK(g) (((Agraphinfo_t*)(g->base.data))->next)
60
61char **Files;
64char *outfile = 0;
65char *path = 0;
66char *suffix = 0;
67int external; /* emit blocks as root graphs */
68int doTree; /* emit block-cutpoint tree */
69
70typedef struct {
71 int count;
72 int nComp;
73 gv_stack_t stk;
75} bcstate;
76
77static char *blockName(agxbuf *xb, char *gname, int d) {
78 if (*gname == '%') /* anonymous graph */
79 agxbprint(xb, "_%s_bcc_%d", gname, d);
80 else
81 agxbprint(xb, "%s_bcc_%d", gname, d);
82 return agxbuse(xb);
83}
84
85/* getName:
86 * Generate name for output using input template.
87 * Has form path_<g>_<i>.suffix, for ith write for the gth graph.
88 * If isTree, use path_<g>_t.suffix.
89 * If sufcnt is zero and graph 0, use outfile
90 */
91static char *getName(int ng, int nb)
92{
93 agxbuf name = {0};
94
95 if (ng == 0 && nb == 0)
96 agxbput(&name, outfile);
97 else {
98 if (suffix) {
99 if (nb < 0)
100 agxbprint(&name, "%s_%d_T.%s", path, ng, suffix);
101 else
102 agxbprint(&name, "%s_%d_%d.%s", path, ng, nb, suffix);
103 } else {
104 if (nb < 0)
105 agxbprint(&name, "%s_%d_T", path, ng);
106 else
107 agxbprint(&name, "%s_%d_%d", path, ng, nb);
108 }
109 }
110 return agxbdisown(&name);
111}
112
113static void gwrite(Agraph_t * g, int ng, int nb)
114{
115 FILE *outf;
116 char *name;
117
118 if (silent)
119 return;
120 if (!outfile) {
121 agwrite(g, stdout);
122 fflush(stdout);
123 } else {
124 name = getName(ng, nb);
125 outf = fopen(name, "w");
126 if (!outf) {
127 fprintf(stderr, "Could not open %s for writing\n", name);
128 perror("bcomps");
129 free(name);
130 graphviz_exit(1);
131 }
132 free(name);
133 agwrite(g, outf);
134 fclose(outf);
135 }
136}
137
138static Agraph_t *mkBlock(Agraph_t * g, bcstate * stp)
139{
140 Agraph_t *sg;
141
142 stp->nComp++;
143 agxbuf xb = {0};
144 sg = agsubg(g, blockName(&xb, agnameof(g), stp->nComp), 1);
145 agxbfree(&xb);
146 agbindrec(sg, "info", sizeof(Agraphinfo_t), true);
147 NEXTBLK(sg) = stp->blks;
148 stp->blks = sg;
149 return sg;
150}
151
152static void
154{
155 Agnode_t *v;
156 Agedge_t *e;
157 Agedge_t *ep;
158 Agraph_t *sg;
159
160 stp->count++;
161 Low(u) = N(u) = stp->count;
162 for (e = agfstedge(g, u); e; e = agnxtedge(g, e, u)) {
163 if ((v = aghead(e)) == u)
164 v = agtail(e);
165 if (v == u)
166 continue;
167 if (N(v) == 0) {
168 stack_push(&stp->stk, e);
169 dfs(g, v, stp, u);
170 Low(u) = imin(Low(u), Low(v));
171 if (Low(v) >= N(u)) { /* u is an articulation point */
172 Cut(u) = 1;
173 sg = mkBlock(g, stp);
174 do {
175 ep = stack_pop(&stp->stk);
176 agsubnode(sg, aghead(ep), 1);
177 agsubnode(sg, agtail(ep), 1);
178 } while (ep != e);
179 }
180 } else if (parent != v) {
181 Low(u) = imin(Low(u), N(v));
182 if (N(v) < N(u))
183 stack_push(&stp->stk, e);
184 }
185 }
186}
187
188static void addCutPts(Agraph_t * tree, Agraph_t * blk)
189{
190 Agnode_t *n;
191 Agnode_t *bn;
192 Agnode_t *cn;
193
194 bn = agnode(tree, agnameof(blk), 1);
195 for (n = agfstnode(blk); n; n = agnxtnode(blk, n)) {
196 if (Cut(n)) {
197 cn = agnode(tree, agnameof(n), 1);
198 agedge(tree, bn, cn, 0, 1);
199 }
200 }
201}
202
203static int process(Agraph_t * g, int gcnt)
204{
205 Agnode_t *n;
207 Agraph_t *blk;
208 Agraph_t *tree;
209 int bcnt;
210
211 aginit(g, AGNODE, "info", sizeof(Agnodeinfo_t), true);
212 aginit(g, AGEDGE, "info", sizeof(Agedgeinfo_t), true);
213 aginit(g, AGRAPH, "info", sizeof(Agraphinfo_t), true);
214
215 state.count = 0;
216 state.nComp = 0;
217 state.stk = (gv_stack_t){0};
218 state.blks = 0;
219
220 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
221 if (N(n) == 0)
222 dfs(g, n, &state, 0);
223 }
224 for (blk = state.blks; blk; blk = NEXTBLK(blk)) {
225 (void)graphviz_node_induce(blk, g);
226 }
227 if (external) {
228 bcnt = 0;
229 for (blk = state.blks; blk; blk = NEXTBLK(blk)) {
230 gwrite(blk, gcnt, bcnt++);
231 }
232 } else
233 gwrite(g, gcnt, 0);
234 if (doTree) {
235 tree = agopen("blkcut_tree", Agstrictundirected, 0);
236 for (blk = state.blks; blk; blk = NEXTBLK(blk))
237 addCutPts(tree, blk);
238 gwrite(tree, gcnt, -1);
239 agclose(tree);
240 }
241 if (verbose) {
242 int cuts = 0;
243 bcnt = 0;
244 for (blk = state.blks; blk; blk = NEXTBLK(blk))
245 bcnt++;
246 for (n = agfstnode(g); n; n = agnxtnode(g, n))
247 if (Cut(n))
248 cuts++;
249 fprintf(stderr, "%s: %d blocks %d cutpoints\n", agnameof(g), bcnt,
250 cuts);
251 }
252 stack_reset(&state.stk);
253 if (state.blks && NEXTBLK(state.blks))
254 return 1; /* >= 2 blocks */
255 else
256 return 0;
257}
258
259static char *useString =
260 "Usage: bcomps [-stvx?] [-o<out template>] <files>\n\
261 -o - output file template\n\
262 -s - don't print components\n\
263 -t - emit block-cutpoint tree\n\
264 -v - verbose\n\
265 -x - external\n\
266 -? - print usage\n\
267If no files are specified, stdin is used\n";
268
269static void usage(int v)
270{
271 printf("%s",useString);
272 graphviz_exit(v);
273}
274
275static void split(char *name)
276{
277 char *sfx = 0;
278
279 sfx = strrchr(name, '.');
280 if (sfx) {
281 size_t size = (size_t)(sfx - name);
282 suffix = sfx + 1;
283 path = gv_strndup(name, size);
284 } else {
285 path = name;
286 }
287}
288
289static void init(int argc, char *argv[])
290{
291 int c;
292
293 opterr = 0;
294 while ((c = getopt(argc, argv, ":o:xstv?")) != -1) {
295 switch (c) {
296 case 'o':
297 outfile = optarg;
298 split(outfile);
299 break;
300 case 's':
301 verbose = 1;
302 silent = 1;
303 break;
304 case 'v':
305 verbose = 1;
306 break;
307 case 't':
308 doTree = 1;
309 break;
310 case 'x':
311 external = 1;
312 break;
313 case ':':
314 fprintf(stderr, "bcomps: option -%c missing argument - ignored\n", optopt);
315 break;
316 case '?':
317 if (optopt == '\0' || optopt == '?')
318 usage(0);
319 else {
320 fprintf(stderr,
321 "bcomps: option -%c unrecognized\n", optopt);
322 usage(1);
323 }
324 break;
325 default:
326 UNREACHABLE();
327 }
328 }
329 argv += optind;
330 argc -= optind;
331
332 if (argc > 0)
333 Files = argv;
334}
335
336int main(int argc, char *argv[])
337{
338 Agraph_t *g;
339 ingraph_state ig;
340 int r = 0;
341 int gcnt = 0;
342
343 init(argc, argv);
344 newIngraph(&ig, Files);
345
346 while ((g = nextGraph(&ig)) != 0) {
347 r |= process(g, gcnt);
348 agclose(g);
349 gcnt++;
350 }
351
352 graphviz_exit(r);
353}
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:77
static size_t agxbput(agxbuf *xb, const char *s)
append string s into xb
Definition agxbuf.h:249
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:213
static char * agxbuse(agxbuf *xb)
Definition agxbuf.h:286
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:299
Memory allocation wrappers that exit on failure.
static char * gv_strndup(const char *original, size_t length)
Definition alloc.h:114
int doTree
Definition bcomps.c:68
static void init(int argc, char *argv[])
Definition bcomps.c:289
int verbose
Definition bcomps.c:62
#define Low(n)
Definition bcomps.c:56
#define N(n)
Definition bcomps.c:58
int silent
Definition bcomps.c:63
char * suffix
Definition bcomps.c:66
static void gwrite(Agraph_t *g, int ng, int nb)
Definition bcomps.c:113
int external
Definition bcomps.c:67
static int process(Agraph_t *g, int gcnt)
Definition bcomps.c:203
static char * blockName(agxbuf *xb, char *gname, int d)
Definition bcomps.c:77
static char * useString
Definition bcomps.c:259
static void dfs(Agraph_t *g, Agnode_t *u, bcstate *stp, Agnode_t *parent)
Definition bcomps.c:153
char * outfile
Definition bcomps.c:64
#define NEXTBLK(g)
Definition bcomps.c:59
static void addCutPts(Agraph_t *tree, Agraph_t *blk)
Definition bcomps.c:188
char ** Files
Definition bcomps.c:61
static Agraph_t * mkBlock(Agraph_t *g, bcstate *stp)
Definition bcomps.c:138
#define Cut(n)
Definition bcomps.c:57
abstract graph C library, Cgraph API
#define parent(i)
Definition closest.c:78
static void split(void)
Definition ccomps.c:108
static char * getName(void)
Definition ccomps.c:266
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
static char * gname
Definition gml2gv.c:23
void free(void *)
size_t graphviz_node_induce(Agraph_t *g, Agraph_t *edgeset)
Definition node_induce.c:9
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:260
#define agtail(e)
Definition cgraph.h:889
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:93
#define aghead(e)
Definition cgraph.h:890
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:84
Agdesc_t Agstrictundirected
strict undirected
Definition graph.c:276
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:96
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:708
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:147
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:261
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
@ AGEDGE
Definition cgraph.h:207
@ 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:169
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:88
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:57
Arithmetic helper functions.
static int imin(int a, int b)
minimum of two integers
Definition gv_math.h:27
@ tree
Definition gvgen.c:33
static const char * usage
Definition gvpr.c:53
static lexstate_t state
Definition htmllex.c:61
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
Implementation of a dynamically expanding stack data structure.
static void stack_push(gv_stack_t *stack, void *item)
Definition stack.h:21
static void * stack_pop(gv_stack_t *stack)
Definition stack.h:33
static void stack_reset(gv_stack_t *stack)
Definition stack.h:35
Agrec_t h
Definition bcomps.c:46
Agrec_t h
Definition bcomps.c:50
int isCut
Definition bcomps.c:53
graph or subgraph
Definition cgraph.h:425
Agraph_t * next
Definition bcomps.c:42
Agrec_t h
Definition bcomps.c:41
implementation of Agrec_t
Definition cgraph.h:172
int count
Definition bcomps.c:71
int nComp
Definition bcomps.c:72
Agraph_t * blks
Definition bcomps.c:74
gv_stack_t stk
Definition bcomps.c:73
Definition types.h:81
int main()
#define UNREACHABLE()
Definition unreachable.h:30