Graphviz  2.31.20130618.0446
lib/pack/ccomps.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 
00015 #include <ctype.h>
00016 #include <setjmp.h>
00017 #include "render.h"
00018 #include "pack.h"
00019 
00020 static jmp_buf jbuf;
00021 
00022 #define MARKED(n) ND_mark(n)
00023 #define MARK(n) (ND_mark(n) = 1)
00024 #define ONSTACK(n) (ND_mark(n) = 1)
00025 #define UNMARK(n) (ND_mark(n) = 0)
00026 
00027 typedef void (*dfsfn) (Agnode_t *, void *);
00028 
00029 #if 0
00030 static void dfs(Agraph_t * g, Agnode_t * n, dfsfn action, void *state)
00031 {
00032     Agedge_t *e;
00033     Agnode_t *other;
00034 
00035     MARK(n);
00036     action(n, state);
00037     for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
00038         if ((other = agtail(e)) == n)
00039             other = aghead(e);
00040         if (!MARKED(other))
00041             dfs(g, other, action, state);
00042     }
00043 }
00044 #endif
00045 
00046 typedef struct blk_t {
00047     Agnode_t **data;
00048     Agnode_t **endp;
00049     struct blk_t *prev;
00050     struct blk_t *next;
00051 } blk_t;
00052 
00053 typedef struct {
00054     blk_t *fstblk;
00055     blk_t *curblk;
00056     Agnode_t **curp;
00057 } stk_t;
00058 
00059 #define INITBUF 1024
00060 #define BIGBUF 1000000
00061 
00062 static void initStk(stk_t* sp, blk_t* bp, Agnode_t** base)
00063 {
00064     bp->data = base;
00065     bp->endp = bp->data + INITBUF;
00066     bp->prev = bp->next = NULL;
00067     sp->curblk = sp->fstblk = bp;
00068     sp->curp = sp->curblk->data;
00069 }
00070 
00071 static void freeBlk (blk_t* bp)
00072 {
00073     free (bp->data);
00074     free (bp);
00075 }
00076 
00077 static void freeStk (stk_t* sp)
00078 {
00079     blk_t* bp;
00080     blk_t* nxtbp;
00081 
00082     for (bp = sp->fstblk->next; bp; bp = nxtbp) {
00083         nxtbp = bp->next;
00084         freeBlk (bp);
00085     }
00086 }
00087 
00088 static void push(stk_t* sp, Agnode_t * np)
00089 {
00090     if (sp->curp == sp->curblk->endp) {
00091         if (sp->curblk->next == NULL) {
00092             blk_t *bp = GNEW(blk_t);
00093             if (bp == 0) {
00094                 agerr(AGERR, "gc: Out of memory\n");
00095                 longjmp(jbuf, 1);
00096             }
00097             bp->prev = sp->curblk;
00098             bp->next = NULL;
00099             bp->data = N_GNEW(BIGBUF, Agnode_t *);
00100             if (bp->data == 0) {
00101                 agerr(AGERR, "gc: Out of memory\n");
00102                 longjmp(jbuf, 1);
00103             }
00104             bp->endp = bp->data + BIGBUF;
00105             sp->curblk->next = bp;
00106         }
00107         sp->curblk = sp->curblk->next;
00108         sp->curp = sp->curblk->data;
00109     }
00110     ONSTACK(np);
00111     *sp->curp++ = np;
00112 }
00113 
00114 static Agnode_t *pop(stk_t* sp)
00115 {
00116     if (sp->curp == sp->curblk->data) {
00117         if (sp->curblk == sp->fstblk)
00118             return 0;
00119         sp->curblk = sp->curblk->prev;
00120         sp->curp = sp->curblk->endp;
00121     }
00122     sp->curp--;
00123     return *sp->curp;
00124 }
00125 
00126 
00127 static void dfs(Agraph_t * g, Agnode_t * n, dfsfn action, void *state, stk_t* stk)
00128 {
00129     Agedge_t *e;
00130     Agnode_t *other;
00131 
00132     push (stk, n);
00133     while ((n = pop(stk))) {
00134         MARK(n);
00135         action(n, state);
00136         for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
00137             if ((other = agtail(e)) == n)
00138                 other = aghead(e);
00139             if (!MARKED(other))
00140                 push(stk, other);
00141         }
00142     }
00143 }
00144 
00145 static int isLegal(char *p)
00146 {
00147     unsigned char c;
00148 
00149     while ((c = *(unsigned char *) p++)) {
00150         if ((c != '_') && !isalnum(c))
00151             return 0;
00152     }
00153 
00154     return 1;
00155 }
00156 
00157 /* insertFn:
00158  */
00159 static void insertFn(Agnode_t * n, void *state)
00160 {
00161 #ifndef WITH_CGRAPH
00162     aginsert((Agraph_t *) state, n);
00163 #else /* WITH_CGRAPH */
00164     agsubnode((Agraph_t *) state,n,1);
00165 #endif /* WITH_CGRAPH */
00166 }
00167 
00168 /* pccomps:
00169  * Return an array of subgraphs consisting of the connected 
00170  * components of graph g. The number of components is returned in ncc. 
00171  * All pinned nodes are in one component.
00172  * If pfx is non-null and a legal graph name, we use it as the prefix
00173  * for the name of the subgraphs created. If not, a simple default is used.
00174  * If pinned is non-null, *pinned set to 1 if pinned nodes found
00175  * and the first component is the one containing the pinned nodes.
00176  * Note that the component subgraphs do not contain any edges. These must
00177  * be obtained from the root graph.
00178  * Return NULL on error or if graph is empty.
00179  */
00180 Agraph_t **pccomps(Agraph_t * g, int *ncc, char *pfx, boolean * pinned)
00181 {
00182     int c_cnt = 0;
00183     char buffer[SMALLBUF];
00184     char *name;
00185     Agraph_t *out = 0;
00186     Agnode_t *n;
00187     Agraph_t **ccs;
00188     int len;
00189     int bnd = 10;
00190     boolean pin = FALSE;
00191     stk_t stk;
00192     blk_t blk;
00193     Agnode_t* base[INITBUF];
00194     int error = 0;
00195 
00196     if (agnnodes(g) == 0) {
00197         *ncc = 0;
00198         return 0;
00199     }
00200     if (!pfx || !isLegal(pfx)) {
00201         pfx = "_cc_";
00202     }
00203     len = strlen(pfx);
00204     if (len + 25 <= SMALLBUF)
00205         name = buffer;
00206     else
00207         name = (char *) gmalloc(len + 25);
00208     strcpy(name, pfx);
00209 
00210     for (n = agfstnode(g); n; n = agnxtnode(g, n))
00211         UNMARK(n);
00212 
00213     ccs = N_GNEW(bnd, Agraph_t *);
00214 
00215     initStk (&stk, &blk, base);
00216     if (setjmp(jbuf)) {
00217         error = 1;
00218         goto packerror;
00219     }
00220     /* Component with pinned nodes */
00221     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00222         if (MARKED(n) || !isPinned(n))
00223             continue;
00224         if (!out) {
00225             sprintf(name + len, "%d", c_cnt);
00226 #ifndef WITH_CGRAPH
00227             out = agsubg(g, name);
00228 #else /* WITH_CGRAPH */
00229             out = agsubg(g, name,1);
00230             agbindrec(out, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); //node custom data
00231 #endif /* WITH_CGRAPH */
00232             ccs[c_cnt] = out;
00233             c_cnt++;
00234             pin = TRUE;
00235         }
00236         dfs (g, n, insertFn, out, &stk);
00237     }
00238 
00239     /* Remaining nodes */
00240     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00241         if (MARKED(n))
00242             continue;
00243         sprintf(name + len, "%d", c_cnt);
00244 #ifndef WITH_CGRAPH
00245         out = agsubg(g, name);
00246 #else /* WITH_CGRAPH */
00247         out = agsubg(g, name,1);
00248         agbindrec(out, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE);     //node custom data
00249 #endif /* WITH_CGRAPH */
00250         dfs(g, n, insertFn, out, &stk);
00251         if (c_cnt == bnd) {
00252             bnd *= 2;
00253             ccs = RALLOC(bnd, ccs, Agraph_t *);
00254         }
00255         ccs[c_cnt] = out;
00256         c_cnt++;
00257     }
00258 packerror:
00259     freeStk (&stk);
00260     if (name != buffer)
00261         free(name);
00262     if (error) {
00263         int i;
00264         *ncc = 0;
00265         for (i=0; i < c_cnt; i++) {
00266             agclose (ccs[i]);
00267         }
00268         free (ccs);
00269         ccs = NULL;
00270     }
00271     else {
00272         ccs = RALLOC(c_cnt, ccs, Agraph_t *);
00273         *ncc = c_cnt;
00274         *pinned = pin;
00275     }
00276     return ccs;
00277 }
00278 
00279 /* ccomps:
00280  * Return an array of subgraphs consisting of the connected
00281  * components of graph g. The number of components is returned in ncc.
00282  * If pfx is non-null and a legal graph name, we use it as the prefix
00283  * for the name of the subgraphs created. If not, a simple default is used.
00284  * Note that the component subgraphs do not contain any edges. These must
00285  * be obtained from the root graph.
00286  * Returns NULL on error or if graph is empty.
00287  */
00288 Agraph_t **ccomps(Agraph_t * g, int *ncc, char *pfx)
00289 {
00290     int c_cnt = 0;
00291     char buffer[SMALLBUF];
00292     char *name;
00293     Agraph_t *out;
00294     Agnode_t *n;
00295     Agraph_t **ccs;
00296     int len;
00297     int bnd = 10;
00298     stk_t stk;
00299     blk_t blk;
00300     Agnode_t* base[INITBUF];
00301 
00302     if (agnnodes(g) == 0) {
00303         *ncc = 0;
00304         return 0;
00305     }
00306     if (!pfx || !isLegal(pfx)) {
00307         pfx = "_cc_";
00308     }
00309     len = strlen(pfx);
00310     if (len + 25 <= SMALLBUF)
00311         name = buffer;
00312     else {
00313         if (!(name = (char *) gmalloc(len + 25))) return NULL;
00314     }
00315     strcpy(name, pfx);
00316 
00317     for (n = agfstnode(g); n; n = agnxtnode(g, n))
00318         UNMARK(n);
00319 
00320     ccs = N_GNEW(bnd, Agraph_t *);
00321     initStk (&stk, &blk, base);
00322     if (setjmp(jbuf)) {
00323         freeStk (&stk);
00324         free (ccs);
00325         if (name != buffer)
00326             free(name);
00327         *ncc = 0;
00328         return NULL;
00329     }
00330     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00331         if (MARKED(n))
00332             continue;
00333         sprintf(name + len, "%d", c_cnt);
00334 #ifndef WITH_CGRAPH
00335         out = agsubg(g, name);
00336 #else /* WITH_CGRAPH */
00337         out = agsubg(g, name,1);
00338         agbindrec(out, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE);     //node custom data
00339 #endif /* WITH_CGRAPH */
00340         dfs(g, n, insertFn, out, &stk);
00341         if (c_cnt == bnd) {
00342             bnd *= 2;
00343             ccs = RALLOC(bnd, ccs, Agraph_t *);
00344         }
00345         ccs[c_cnt] = out;
00346         c_cnt++;
00347     }
00348     freeStk (&stk);
00349     ccs = RALLOC(c_cnt, ccs, Agraph_t *);
00350     if (name != buffer)
00351         free(name);
00352     *ncc = c_cnt;
00353     return ccs;
00354 }
00355 
00356 /* cntFn:
00357  */
00358 static void cntFn(Agnode_t * n, void *s)
00359 {
00360     *(int *) s += 1;
00361 }
00362 
00363 /* isConnected:
00364  * Returns 1 if the graph is connected.
00365  * Returns 0 if the graph is not connected.
00366  * Returns -1 if the graph is error.
00367  */
00368 int isConnected(Agraph_t * g)
00369 {
00370     Agnode_t *n;
00371     int ret = 1;
00372     int cnt = 0;
00373     stk_t stk;
00374     blk_t blk;
00375     Agnode_t* base[INITBUF];
00376 
00377     for (n = agfstnode(g); n; n = agnxtnode(g, n))
00378         UNMARK(n);
00379 
00380     n = agfstnode(g);
00381     if (n) {
00382         initStk (&stk, &blk, base);
00383         if (setjmp(jbuf)) {
00384             freeStk (&stk);
00385             return -1;
00386         }
00387         dfs(g, n, cntFn, &cnt, &stk);
00388         if (cnt != agnnodes(g))
00389             ret = 0;
00390         freeStk (&stk);
00391     }
00392     return ret;
00393 }
00394 
00395 /* nodeInduce:
00396  * Given a subgraph, adds all edges in the root graph both of whose
00397  * endpoints are in the subgraph.
00398  * If g is a connected component, this will be all edges attached to
00399  * any node in g.
00400  * Returns the number of edges added.
00401  */
00402 int nodeInduce(Agraph_t * g)
00403 {
00404     Agnode_t *n;
00405     Agraph_t *root = g->root;
00406     Agedge_t *e;
00407     int e_cnt = 0;
00408 
00409     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00410         for (e = agfstout(root, n); e; e = agnxtout(root, e)) {
00411             if (agcontains(g, aghead(e))) {     /* test will always be true */
00412 #ifndef WITH_CGRAPH
00413                 aginsert(g, e); /* for connected component  */
00414 #else /* WITH_CGRAPH */
00415                 agsubedge(g,e,1);
00416 #endif /* WITH_CGRAPH */
00417                 e_cnt++;
00418             }
00419         }
00420     }
00421     return e_cnt;
00422 }