|
Graphviz 2.29.20120208.0545
|
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 }
1.7.4