/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/graph/graph.c

Go to the documentation of this file.
00001 /* $Id: graph.c,v 1.4 2008/02/26 03:35:43 glenlow Exp $ $Revision: 1.4 $ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /**********************************************************
00005 *      This software is part of the graphviz package      *
00006 *                http://www.graphviz.org/                 *
00007 *                                                         *
00008 *            Copyright (c) 1994-2004 AT&T Corp.           *
00009 *                and is licensed under the                *
00010 *            Common Public License, Version 1.0           *
00011 *                      by AT&T Corp.                      *
00012 *                                                         *
00013 *        Information and Software Systems Research        *
00014 *              AT&T Research, Florham Park NJ             *
00015 **********************************************************/
00016 
00017 #include <limits.h>
00018 
00019 #include "libgraph.h"
00020 
00021 #ifdef DMALLOC
00022 #include "dmalloc.h"
00023 #endif
00024 
00025 Dtdisc_t agNamedisc = {
00026     offsetof(Agnode_t, name),
00027     -1,
00028     -1,                         /* link offset */
00029     NIL(Dtmake_f),
00030     NIL(Dtfree_f),
00031     NIL(Dtcompar_f),            /* use strcmp */
00032     NIL(Dthash_f),
00033     NIL(Dtmemory_f),
00034     NIL(Dtevent_f)
00035 };
00036 
00037 Dtdisc_t agNodedisc = {
00038     offsetof(Agnode_t, id),
00039     sizeof(int),
00040     -1,                         /* link offset */
00041     NIL(Dtmake_f),
00042     NIL(Dtfree_f),
00043     (Dtcompar_f) agcmpid,
00044     NIL(Dthash_f),
00045     NIL(Dtmemory_f),
00046     NIL(Dtevent_f)
00047 };
00048 
00049 Dtdisc_t agIndisc = {
00050     0,                          /* pass whole object as key */
00051     0,
00052     -1,                         /* link offset */
00053     NIL(Dtmake_f),
00054     NIL(Dtfree_f),
00055     (Dtcompar_f) agcmpin,
00056     NIL(Dthash_f),
00057     NIL(Dtmemory_f),
00058     NIL(Dtevent_f)
00059 };
00060 
00061 Dtdisc_t agOutdisc = {
00062     0,                          /* pass whole object as key */
00063     0,
00064     -1,                         /* link offset */
00065     (Dtmake_f) 0,
00066     (Dtfree_f) 0,
00067     (Dtcompar_f) agcmpout,
00068     (Dthash_f) 0,
00069     (Dtmemory_f) 0,
00070     (Dtevent_f) 0
00071 };
00072 
00073 int agcmpid(Dt_t * dict, int *id0, int *id1, Dtdisc_t * disc)
00074 {
00075     return (*id0) - *(id1);
00076 }
00077 
00078 #ifdef DEBUG
00079 static int myinedgecmp(e0, e1)
00080 Agedge_t *e0, *e1;
00081 {
00082     int rv = myinedgecmp(e0, e1);
00083     printf("compare (%s,%s:%s),(%s,%s:%s) = %d\n",
00084            e0->head ? e0->head->name : "nil",
00085            e0->tail ? e0->tail->name : "nil",
00086            e0->attr && e0->attr[KEYX]? e0->attr[KEYX] : "nil",
00087            e1->head ? e1->head->name : "nil",
00088            e1->tail ? e1->tail->name : "nil",
00089            e1->attr && e1->attr[KEYX]? e1->attr[KEYX] : "nil",
00090            rv);
00091     return rv;
00092 }
00093 #endif
00094 
00095 static int keycmp(Agedge_t * e0, Agedge_t * e1)
00096 {
00097     char *key0, *key1;
00098     key0 = e0->attr ? e0->attr[KEYX] : NULL;
00099     key1 = e1->attr ? e1->attr[KEYX] : NULL;
00100     if (key0 == NULL)
00101         return (key1 ? -1 : 0);
00102     if (key1 == NULL)
00103         return 1;
00104     return strcmp(key0, key1);
00105 }
00106 
00107 int agcmpin(Dict_t * d, Agedge_t * e0, Agedge_t * e1, Dtdisc_t * disc)
00108 {
00109     int e0tailid, e0headid, e1tailid, e1headid;
00110 
00111     e0tailid = e0->tail ? e0->tail->id : -1;
00112     e0headid = e0->head ? e0->head->id : -1;
00113     e1tailid = e1->tail ? e1->tail->id : -1;
00114     e1headid = e1->head ? e1->head->id : -1;
00115 
00116     if (e0headid != e1headid)
00117         return e0headid - e1headid;
00118     if (e0tailid != e1tailid)
00119         return e0tailid - e1tailid;
00120     return keycmp(e0, e1);
00121 }
00122 
00123 int agcmpout(Dict_t * d, Agedge_t * e0, Agedge_t * e1, Dtdisc_t * disc)
00124 {
00125     int e0tailid, e0headid, e1tailid, e1headid;
00126 
00127     e0tailid = e0->tail ? e0->tail->id : -1;
00128     e0headid = e0->head ? e0->head->id : -1;
00129     e1tailid = e1->tail ? e1->tail->id : -1;
00130     e1headid = e1->head ? e1->head->id : -1;
00131 
00132     if (e0tailid != e1tailid)
00133         return e0tailid - e1tailid;
00134     if (e0headid != e1headid)
00135         return e0headid - e1headid;
00136     return keycmp(e0, e1);
00137 }
00138 
00139 static Agdata_t *agnewdata(void)
00140 {
00141     Agdata_t *rv;
00142 
00143     rv = NEW(Agdata_t);
00144     rv->node_dict = dtopen(&agNamedisc, Dttree);
00145     rv->globattr = agNEWdict("graph");
00146     rv->nodeattr = agNEWdict("node");
00147     rv->edgeattr = agNEWdict("edge");
00148     if (AG.proto_g) {
00149         agcopydict(rv->globattr, AG.proto_g->univ->globattr);
00150         agcopydict(rv->nodeattr, AG.proto_g->univ->nodeattr);
00151         agcopydict(rv->edgeattr, AG.proto_g->univ->edgeattr);
00152     }
00153     return rv;
00154 }
00155 
00156 static void agfreedata(Agraph_t * g)
00157 {
00158     agFREEdict(g, g->univ->globattr);
00159     agFREEdict(g, g->univ->nodeattr);
00160     agFREEdict(g, g->univ->edgeattr);
00161     dtclose(g->univ->node_dict);
00162     free(g->univ);
00163 }
00164 
00165 static void dup_proto(Agraph_t * g, Agproto_t * proto)
00166 {
00167     Agnode_t *n = NULL;
00168     Agedge_t *e = NULL;
00169     Agproto_t *s = NEW(Agproto_t);
00170 
00171     s->prev = g->proto;
00172     if (proto) {
00173         n = proto->n;
00174         e = proto->e;
00175     }
00176     s->n = agNEWnode(g, "\001proto", n);
00177     s->e = agNEWedge(g, s->n, s->n, e);
00178     g->proto = s;
00179 }
00180 
00181 void agpushproto(Agraph_t * g)
00182 {
00183     dup_proto(g, g->proto);
00184 }
00185 
00186 void agpopproto(Agraph_t * g)
00187 {
00188     Agproto_t *s = g->proto;
00189     if (s != NULL) {
00190         g->proto = s->prev;
00191         s->e->tail = s->e->head = s->n;
00192         agFREEedge(s->e);
00193         agFREEnode(s->n);
00194         free(s);
00195     }
00196 }
00197 
00198 static Agraph_t *agNEWgraph(char *name, Agraph_t * parent, int kind)
00199 {
00200     int i, nobj;
00201     Agraph_t *g;
00202 
00203     if (AG.init_called == FALSE) {
00204         agerr(AGERR, "libag error -- aginit() was not called\n");
00205         return 0;
00206     }
00207     g = (Agraph_t *) calloc(1, AG.graph_nbytes);
00208     g->tag = TAG_GRAPH;
00209     g->kind = kind;
00210     g->nodes = dtopen(&agNodedisc, Dttree);
00211     g->inedges = dtopen(&agIndisc, Dttree);
00212     g->outedges = dtopen(&agOutdisc, Dttree);
00213 
00214     if (parent == NULL) {
00215         g->univ = agnewdata();
00216         g->root = g;
00217         nobj = dtsize(g->univ->globattr->dict);
00218         if (nobj) {
00219             g->attr = N_NEW(nobj, char *);
00220                 g->didset = N_NEW((nobj + CHAR_BIT - 1) / CHAR_BIT, char);
00221         }
00222         else {
00223             g->attr = NULL;
00224                 g->didset = NULL;
00225         }
00226         for (i = 0; i < nobj; i++)
00227             g->attr[i] = agstrdup(AG.proto_g->attr[i]);
00228         } else {
00229         g->univ = parent->univ;
00230         g->root = parent->root;
00231         nobj = dtsize(parent->univ->globattr->dict);
00232         if (nobj) {
00233             g->attr = N_NEW(nobj, char *);
00234                 g->didset = N_NEW((nobj + CHAR_BIT - 1) / CHAR_BIT, char);
00235         }
00236         else {
00237             g->attr = NULL;
00238                 g->didset = NULL;
00239         }
00240         for (i = 0; i < nobj; i++)
00241             g->attr[i] = agstrdup(parent->attr[i]);
00242 
00243     }
00244 
00245     g->meta_node = NULL;
00246     g->name = agstrdup(name);
00247     g->proto = NULL;
00248 
00249     if (parent)
00250         dup_proto(g, parent->proto);
00251     else
00252         agpushproto(g);
00253     return g;
00254 }
00255 
00256 static int reach0(Dict_t * m, Agnode_t * from, Agnode_t * to)
00257 {
00258     Agedge_t *e;
00259 
00260     if (from == to)
00261         return TRUE;
00262     if (agfindedge(from->graph->root, from, to))
00263         return TRUE;
00264     dtinsert(m, from);
00265     for (e = agfstout(from->graph, from); e; e = agnxtout(from->graph, e))
00266         if ((dtsearch(m, e->head) == NULL) && reach0(m, e->head, to))
00267             return TRUE;
00268     return FALSE;
00269 }
00270 
00271 static int reach(Agnode_t * from, Agnode_t * to)
00272 {
00273     Dict_t *m;
00274     int rv;
00275 
00276     m = dtopen(&agNodedisc, Dttree);
00277     rv = reach0(m, from, to);
00278     dtclose(m);
00279     return rv;
00280 }
00281 
00282 Agraph_t *agusergraph(Agnode_t * n)
00283 {
00284     return (n->graph->meta_node ? NULL : (Agraph_t *) (n->attr[0]));
00285 }
00286 
00287 Agraph_t *agopen(char *name, int kind)
00288 {
00289     Agraph_t *g, *meta;
00290 
00291     g = agNEWgraph(name, NULL, kind);
00292     meta = agNEWgraph(name, NULL, AGMETAGRAPH);
00293     if (!g || !meta)
00294         return 0;
00295     agnodeattr(meta, "agusergraph", NULL);
00296     g->meta_node = agnode(meta, name);
00297     g->meta_node->attr[0] = (char *) g;
00298     return g;
00299 }
00300 
00301 Agraph_t *agsubg(Agraph_t * g, char *name)
00302 {
00303     Agraph_t *subg, *meta;
00304     Agnode_t *n;
00305 
00306     meta = g->meta_node->graph;
00307     n = agfindnode(meta, name);
00308     if (n)
00309         subg = agusergraph(n);
00310     else {
00311         subg = agNEWgraph(name, g, g->kind);
00312         if (!subg)
00313             return 0;
00314         n = agnode(meta, name);
00315         subg->meta_node = n;
00316         n->attr[0] = (char *) subg;
00317     }
00318     agINSgraph(g, subg);
00319     return subg;
00320 }
00321 
00322 Agraph_t *agfindsubg(Agraph_t * g, char *name)
00323 {
00324     Agnode_t *n;
00325 
00326     if (g->meta_node) {
00327         n = agfindnode(g->meta_node->graph, name);
00328         if (n)
00329             return agusergraph(n);
00330     }
00331     return NULL;
00332 }
00333 
00334 void agINSgraph(Agraph_t * g, Agraph_t * subg)
00335 {
00336     Agnode_t *h, *t;
00337     t = g->meta_node;
00338     h = subg->meta_node;
00339     if (t && h && (reach(h, t) == FALSE))
00340         agedge(t->graph, t, h);
00341 }
00342 
00343 void agclose(Agraph_t * g)
00344 {
00345     Agedge_t *e, *f;
00346     Agnode_t *n, *nn;
00347     Agraph_t *meta = NULL;
00348     int i, nobj, flag, is_meta;
00349 
00350     if ((g == NULL) || (TAG_OF(g) != TAG_GRAPH))
00351         return;
00352     is_meta = AG_IS_METAGRAPH(g);
00353     if (is_meta == FALSE) {
00354         meta = g->meta_node->graph;
00355         /* recursively remove its subgraphs */
00356         do {                    /* better semantics would be to find strong component */
00357             flag = FALSE;
00358             for (e = agfstout(meta, g->meta_node); e; e = f) {
00359                 f = agnxtout(meta, e);
00360                 if (agnxtin(meta, agfstin(meta, e->head)) == NULL) {
00361                     agclose(agusergraph(e->head));
00362                     flag = TRUE;
00363                 }
00364             }
00365         } while (flag);
00366     }
00367     while (g->proto)
00368         agpopproto(g);
00369     if (is_meta == FALSE) {
00370         nobj = dtsize(g->univ->globattr->dict);
00371         for (i = 0; i < nobj; i++)
00372             agstrfree(g->attr[i]);
00373     }
00374     if (g->attr)
00375         free(g->attr);
00376         if (g->didset)
00377                 free(g->didset);
00378     if (g == g->root) {
00379         for (n = agfstnode(g); n; n = nn) {
00380             nn = agnxtnode(g, n);
00381             agDELnode(g, n);
00382         }
00383         if (is_meta == FALSE)
00384             agclose(g->meta_node->graph);
00385         agfreedata(g);
00386     } else {
00387         if (is_meta == FALSE)
00388             agdelete(meta, g->meta_node);
00389     }
00390     dtclose(g->nodes);
00391     dtclose(g->inedges);
00392     dtclose(g->outedges);
00393     agstrfree(g->name);
00394     TAG_OF(g) = -1;
00395     free(g);
00396 }
00397 
00398 int agcontains(Agraph_t * g, void *obj)
00399 {
00400     switch (TAG_OF(obj)) {
00401     case TAG_NODE:
00402         return (agidnode(g, ((Agnode_t *) obj)->id) != NULL);
00403     case TAG_EDGE:
00404         return (dtsearch(g->inedges, (Agedge_t *) obj) != NULL);
00405     case TAG_GRAPH:
00406         return (reach(g->meta_node, ((Agraph_t *) obj)->meta_node));
00407     }
00408     return FALSE;
00409 }
00410 
00411 void aginsert(Agraph_t * g, void *obj)
00412 {
00413     switch (TAG_OF(obj)) {
00414     case TAG_NODE:
00415         agINSnode(g, obj);
00416         break;
00417     case TAG_EDGE:
00418         agINSedge(g, obj);
00419         break;
00420     case TAG_GRAPH:
00421         agINSgraph(g, obj);
00422         break;
00423     }
00424 }
00425 
00426 void agdelete(Agraph_t * g, void *obj)
00427 {
00428     switch (TAG_OF(obj)) {
00429     case TAG_NODE:
00430         agDELnode(g, obj);
00431         break;
00432     case TAG_EDGE:
00433         agDELedge(g, obj);
00434         break;
00435     case TAG_GRAPH:
00436         agclose(obj);
00437         break;
00438     }
00439 }
00440 
00441 int agnnodes(Agraph_t * g)
00442 {
00443     return dtsize(g->nodes);
00444 }
00445 
00446 int agnedges(Agraph_t * g)
00447 {
00448     return dtsize(g->outedges);
00449 }

Generated on Mon Mar 31 19:03:26 2008 for Graphviz by  doxygen 1.5.1