Graphviz 2.29.20120208.0545
lib/graph/graph.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 #include <limits.h>
00015 
00016 #include "libgraph.h"
00017 
00018 #ifdef DMALLOC
00019 #include "dmalloc.h"
00020 #endif
00021 
00022 Dtdisc_t agNamedisc = {
00023     offsetof(Agnode_t, name),
00024     -1,
00025     -1,                         /* link offset */
00026     NIL(Dtmake_f),
00027     NIL(Dtfree_f),
00028     NIL(Dtcompar_f),            /* use strcmp */
00029     NIL(Dthash_f),
00030     NIL(Dtmemory_f),
00031     NIL(Dtevent_f)
00032 };
00033 
00034 Dtdisc_t agNodedisc = {
00035     offsetof(Agnode_t, id),
00036     sizeof(int),
00037     -1,                         /* link offset */
00038     NIL(Dtmake_f),
00039     NIL(Dtfree_f),
00040     (Dtcompar_f) agcmpid,
00041     NIL(Dthash_f),
00042     NIL(Dtmemory_f),
00043     NIL(Dtevent_f)
00044 };
00045 
00046 Dtdisc_t agIndisc = {
00047     0,                          /* pass whole object as key */
00048     0,
00049     -1,                         /* link offset */
00050     NIL(Dtmake_f),
00051     NIL(Dtfree_f),
00052     (Dtcompar_f) agcmpin,
00053     NIL(Dthash_f),
00054     NIL(Dtmemory_f),
00055     NIL(Dtevent_f)
00056 };
00057 
00058 Dtdisc_t agOutdisc = {
00059     0,                          /* pass whole object as key */
00060     0,
00061     -1,                         /* link offset */
00062     (Dtmake_f) 0,
00063     (Dtfree_f) 0,
00064     (Dtcompar_f) agcmpout,
00065     (Dthash_f) 0,
00066     (Dtmemory_f) 0,
00067     (Dtevent_f) 0
00068 };
00069 
00070 int agcmpid(Dt_t * dict, int *id0, int *id1, Dtdisc_t * disc)
00071 {
00072     return (*id0) - *(id1);
00073 }
00074 
00075 #ifdef DEBUG
00076 static int myinedgecmp(e0, e1)
00077 Agedge_t *e0, *e1;
00078 {
00079     int rv = myinedgecmp(e0, e1);
00080     printf("compare (%s,%s:%s),(%s,%s:%s) = %d\n",
00081            e0->head ? e0->head->name : "nil",
00082            e0->tail ? e0->tail->name : "nil",
00083            e0->attr && e0->attr[KEYX]? e0->attr[KEYX] : "nil",
00084            e1->head ? e1->head->name : "nil",
00085            e1->tail ? e1->tail->name : "nil",
00086            e1->attr && e1->attr[KEYX]? e1->attr[KEYX] : "nil",
00087            rv);
00088     return rv;
00089 }
00090 #endif
00091 
00092 static int keycmp(Agedge_t * e0, Agedge_t * e1)
00093 {
00094     char *key0, *key1;
00095     key0 = e0->attr ? e0->attr[KEYX] : NULL;
00096     key1 = e1->attr ? e1->attr[KEYX] : NULL;
00097     if (key0 == NULL)
00098         return (key1 ? -1 : 0);
00099     if (key1 == NULL)
00100         return 1;
00101     return strcmp(key0, key1);
00102 }
00103 
00104 int agcmpin(Dict_t * d, Agedge_t * e0, Agedge_t * e1, Dtdisc_t * disc)
00105 {
00106     int e0tailid, e0headid, e1tailid, e1headid;
00107 
00108     e0tailid = e0->tail ? e0->tail->id : -1;
00109     e0headid = e0->head ? e0->head->id : -1;
00110     e1tailid = e1->tail ? e1->tail->id : -1;
00111     e1headid = e1->head ? e1->head->id : -1;
00112 
00113     if (e0headid != e1headid)
00114         return e0headid - e1headid;
00115     if (e0tailid != e1tailid)
00116         return e0tailid - e1tailid;
00117     return keycmp(e0, e1);
00118 }
00119 
00120 int agcmpout(Dict_t * d, Agedge_t * e0, Agedge_t * e1, Dtdisc_t * disc)
00121 {
00122     int e0tailid, e0headid, e1tailid, e1headid;
00123 
00124     e0tailid = e0->tail ? e0->tail->id : -1;
00125     e0headid = e0->head ? e0->head->id : -1;
00126     e1tailid = e1->tail ? e1->tail->id : -1;
00127     e1headid = e1->head ? e1->head->id : -1;
00128 
00129     if (e0tailid != e1tailid)
00130         return e0tailid - e1tailid;
00131     if (e0headid != e1headid)
00132         return e0headid - e1headid;
00133     return keycmp(e0, e1);
00134 }
00135 
00136 static Agdata_t *agnewdata(void)
00137 {
00138     Agdata_t *rv;
00139 
00140     rv = NEW(Agdata_t);
00141     rv->node_dict = dtopen(&agNamedisc, Dttree);
00142     rv->globattr = agNEWdict("graph");
00143     rv->nodeattr = agNEWdict("node");
00144     rv->edgeattr = agNEWdict("edge");
00145     if (AG.proto_g) {
00146         agcopydict(rv->globattr, AG.proto_g->univ->globattr);
00147         agcopydict(rv->nodeattr, AG.proto_g->univ->nodeattr);
00148         agcopydict(rv->edgeattr, AG.proto_g->univ->edgeattr);
00149     }
00150     return rv;
00151 }
00152 
00153 static void agfreedata(Agraph_t * g)
00154 {
00155     agFREEdict(g, g->univ->globattr);
00156     agFREEdict(g, g->univ->nodeattr);
00157     agFREEdict(g, g->univ->edgeattr);
00158     dtclose(g->univ->node_dict);
00159     free(g->univ);
00160 }
00161 
00162 static void dup_proto(Agraph_t * g, Agproto_t * proto)
00163 {
00164     Agnode_t *n = NULL;
00165     Agedge_t *e = NULL;
00166     Agproto_t *s = NEW(Agproto_t);
00167 
00168     s->prev = g->proto;
00169     if (proto) {
00170         n = proto->n;
00171         e = proto->e;
00172     }
00173     s->n = agNEWnode(g, "\001proto", n);
00174     s->e = agNEWedge(g, s->n, s->n, e);
00175     g->proto = s;
00176 }
00177 
00178 void agpushproto(Agraph_t * g)
00179 {
00180     dup_proto(g, g->proto);
00181 }
00182 
00183 void agpopproto(Agraph_t * g)
00184 {
00185     Agproto_t *s = g->proto;
00186     if (s != NULL) {
00187         g->proto = s->prev;
00188         s->e->tail = s->e->head = s->n;
00189         agFREEedge(s->e);
00190         agFREEnode(s->n);
00191         free(s);
00192     }
00193 }
00194 
00195 static Agraph_t *agNEWgraph(char *name, Agraph_t * parent, int kind)
00196 {
00197     int i, nobj;
00198     Agraph_t *g;
00199 
00200     if (AG.init_called == FALSE) {
00201         agerr(AGERR, "libag error -- aginit() was not called\n");
00202         return 0;
00203     }
00204     g = (Agraph_t *) calloc(1, AG.graph_nbytes);
00205     g->tag = TAG_GRAPH;
00206     g->kind = kind;
00207     g->nodes = dtopen(&agNodedisc, Dttree);
00208     g->inedges = dtopen(&agIndisc, Dttree);
00209     g->outedges = dtopen(&agOutdisc, Dttree);
00210 
00211     if (parent == NULL) {
00212         g->univ = agnewdata();
00213         g->root = g;
00214         nobj = dtsize(g->univ->globattr->dict);
00215         if (nobj) {
00216             g->attr = N_NEW(nobj, char *);
00217                 g->didset = N_NEW((nobj + CHAR_BIT - 1) / CHAR_BIT, char);
00218         }
00219         else {
00220             g->attr = NULL;
00221                 g->didset = NULL;
00222         }
00223         for (i = 0; i < nobj; i++)
00224             g->attr[i] = agstrdup(AG.proto_g->attr[i]);
00225         } else {
00226         g->univ = parent->univ;
00227         g->root = parent->root;
00228         nobj = dtsize(parent->univ->globattr->dict);
00229         if (nobj) {
00230             g->attr = N_NEW(nobj, char *);
00231                 g->didset = N_NEW((nobj + CHAR_BIT - 1) / CHAR_BIT, char);
00232         }
00233         else {
00234             g->attr = NULL;
00235                 g->didset = NULL;
00236         }
00237         for (i = 0; i < nobj; i++)
00238             g->attr[i] = agstrdup(parent->attr[i]);
00239 
00240     }
00241 
00242     g->meta_node = NULL;
00243     g->name = agstrdup(name);
00244     g->proto = NULL;
00245 
00246     if (parent)
00247         dup_proto(g, parent->proto);
00248     else
00249         agpushproto(g);
00250     return g;
00251 }
00252 
00253 static int reach0(Dict_t * m, Agnode_t * from, Agnode_t * to)
00254 {
00255     Agedge_t *e;
00256 
00257     if (from == to)
00258         return TRUE;
00259     if (agfindedge(from->graph->root, from, to))
00260         return TRUE;
00261     dtinsert(m, from);
00262     for (e = agfstout(from->graph, from); e; e = agnxtout(from->graph, e))
00263         if ((dtsearch(m, e->head) == NULL) && reach0(m, e->head, to))
00264             return TRUE;
00265     return FALSE;
00266 }
00267 
00268 static int reach(Agnode_t * from, Agnode_t * to)
00269 {
00270     Dict_t *m;
00271     int rv;
00272 
00273     m = dtopen(&agNodedisc, Dttree);
00274     rv = reach0(m, from, to);
00275     dtclose(m);
00276     return rv;
00277 }
00278 
00279 Agraph_t *agusergraph(Agnode_t * n)
00280 {
00281     return (n->graph->meta_node ? NULL : (Agraph_t *) (n->attr[0]));
00282 }
00283 
00284 
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, (Agnode_t*)obj);
00416         break;
00417     case TAG_EDGE:
00418         agINSedge(g, (Agedge_t*)obj);
00419         break;
00420     case TAG_GRAPH:
00421         agINSgraph(g, (Agraph_t*)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, (Agnode_t*)obj);
00431         break;
00432     case TAG_EDGE:
00433         agDELedge(g, (Agedge_t*)obj);
00434         break;
00435     case TAG_GRAPH:
00436         agclose((Agraph_t*)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 }