Graphviz  2.31.20130524.0447
lib/graph/attribs.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 #define EXTERN
00017 #include "libgraph.h"
00018 
00019 #ifdef DMALLOC
00020 #include "dmalloc.h"
00021 #endif
00022 
00023 Agdict_t *agdictof(void *obj)
00024 {
00025     Agdict_t *d = NULL;
00026 
00027     switch (TAG_OF(obj)) {
00028     case TAG_GRAPH:
00029         d = ((Agraph_t *) obj)->univ->globattr;
00030         break;
00031     case TAG_NODE:
00032         d = ((Agnode_t *) obj)->graph->univ->nodeattr;
00033         break;
00034     case TAG_EDGE:
00035         d = ((Agedge_t *) obj)->tail->graph->univ->edgeattr;
00036         break;
00037     }
00038     return d;
00039 }
00040 
00041 Agsym_t *agNEWsym(Agdict_t * dict, char *name, char *value)
00042 {
00043     Agsym_t *a;
00044     int i;
00045 
00046     a = NEW(Agsym_t);
00047     a->name = agstrdup(name);
00048     a->value = agstrdup(value);
00049     a->printed = TRUE;
00050     i = a->index = dtsize(dict->dict);
00051     dict->list = ALLOC(i + 2, dict->list, Agsym_t *);
00052     dict->list[i++] = a;
00053     dict->list[i++] = NULL;
00054     dtinsert(dict->dict, a);
00055     return a;
00056 }
00057 
00058 static void obj_init_attr(void *obj, Agsym_t * attr, int isnew)
00059 {
00060     int i;
00061     Agraph_t *gobj;             /* generic object */
00062 
00063     gobj = (Agraph_t *) obj;
00064     i = attr->index;
00065         if (isnew) {
00066                 gobj->attr = ALLOC(i + 1, gobj->attr, char *);
00067                 gobj->attr[i] = agstrdup(attr->value);
00068                 if (i % CHAR_BIT == 0) {
00069                         /* allocate in chunks of CHAR_BIT bits */
00070                         gobj->didset = ALLOC(i / CHAR_BIT + 1, gobj->didset, char);
00071                         gobj->didset[i / CHAR_BIT] = 0;
00072                 }
00073         }
00074         else if ((gobj->didset[i / CHAR_BIT] & (1 << (i % CHAR_BIT))) == 0) {
00075                 /* the i-th attr was not set by agxset, so we can replace it */
00076                 agstrfree(gobj->attr[i]);
00077                 gobj->attr[i] = agstrdup(attr->value);
00078         }
00079 }
00080 
00081 static void add_graph_attr(Agraph_t * g, Agsym_t * attr, int isnew)
00082 {
00083     Agnode_t *n;
00084 
00085     if (g->meta_node) {
00086         for (n = agfstnode(g->meta_node->graph); n;
00087              n = agnxtnode(g->meta_node->graph, n))
00088             obj_init_attr(agusergraph(n), attr, isnew);
00089     } else
00090         obj_init_attr(g, attr, isnew);
00091 }
00092 
00093 static void add_node_attr(Agraph_t * g, Agsym_t * attr, int isnew)
00094 {
00095     Agnode_t *n;
00096     Agproto_t *proto;
00097 
00098     for (n = agfstnode(g); n; n = agnxtnode(g, n))
00099         obj_init_attr(n, attr, isnew);
00100     if (g->meta_node) {
00101         for (n = agfstnode(g->meta_node->graph); n;
00102              n = agnxtnode(g->meta_node->graph, n))
00103             for (proto = agusergraph(n)->proto; proto; proto = proto->prev)
00104                 obj_init_attr(proto->n, attr, isnew);
00105     } else
00106         for (proto = g->proto; proto; proto = proto->prev)
00107             obj_init_attr(proto->n, attr, isnew);
00108 }
00109 
00110 static void add_edge_attr(Agraph_t * g, Agsym_t * attr, int isnew)
00111 {
00112     Agnode_t *n;
00113     Agedge_t *e;
00114     Agproto_t *proto;
00115 
00116     for (n = agfstnode(g); n; n = agnxtnode(g, n))
00117         for (e = agfstout(g, n); e; e = agnxtout(g, e))
00118             obj_init_attr(e, attr, isnew);
00119     if (g->meta_node) {
00120         for (n = agfstnode(g->meta_node->graph); n;
00121              n = agnxtnode(g->meta_node->graph, n))
00122             for (proto = agusergraph(n)->proto; proto; proto = proto->prev)
00123                 obj_init_attr(proto->e, attr, isnew);
00124     } else
00125         for (proto = g->proto; proto; proto = proto->prev)
00126             obj_init_attr(proto->e, attr, isnew);
00127 }
00128 
00129 Agsym_t *agattr(void *obj, char *name, char *value)
00130 {
00131     Agsym_t *rv;
00132     int isnew = 1;
00133 
00134     rv = agfindattr(obj, name);
00135     if (rv) {
00136         if (strcmp(rv->value, value)) {
00137             agstrfree(rv->value);
00138             rv->value = agstrdup(value);
00139             isnew = 0;
00140         }
00141         else
00142             return rv;
00143     }
00144     else
00145         rv = agNEWsym(agdictof(obj), name, value);
00146     if (rv) {
00147         switch (TAG_OF(obj)) {
00148         case TAG_GRAPH:
00149             add_graph_attr((Agraph_t *) obj, rv, isnew);
00150             break;
00151         case TAG_NODE:
00152             add_node_attr(((Agnode_t *) obj)->graph, rv, isnew);
00153             break;
00154         case TAG_EDGE:
00155             add_edge_attr(((Agedge_t *) obj)->head->graph, rv, isnew);
00156             break;
00157         }
00158     }
00159     return rv;
00160 }
00161 
00162 Agraph_t *agprotograph()
00163 {
00164     return AG.proto_g;
00165 }
00166 
00167 Agnode_t *agprotonode(Agraph_t *g)
00168 {
00169         return g->proto->n;
00170 }
00171 
00172 
00173 Agedge_t *agprotoedge(Agraph_t *g)
00174 {
00175         return g->proto->e;
00176 }
00177 
00178 
00179 static int initproto(void)
00180 {
00181     Agsym_t *a;
00182     Agraph_t *g;
00183     g = AG.proto_g = agopen("ProtoGraph", AGRAPH);
00184     a = agattr(g->proto->e, KEY_ID, "");
00185     if (a->index != KEYX)
00186         return 1;
00187     a = agattr(g->proto->e, TAIL_ID, "");
00188     if (a->index != TAILX)
00189         return 1;
00190     a->printed = FALSE;
00191     a = agattr(g->proto->e, HEAD_ID, "");
00192     if (a->index != HEADX)
00193         return 1;
00194     a->printed = FALSE;
00195     return 0;
00196 }
00197 
00198 Agsym_t *agraphattr(Agraph_t * g, char *name, char *value)
00199 {
00200     if (g == NULL)
00201         g = AG.proto_g;
00202     if (g != g->root)
00203         return NULL;
00204     return agattr(g, name, value);
00205 }
00206 
00207 Agsym_t *agnodeattr(Agraph_t * g, char *name, char *value)
00208 {
00209     if (g == NULL)
00210         g = AG.proto_g;
00211     if (g != g->root)
00212         return NULL;
00213     return agattr(g->proto->n, name, value);
00214 }
00215 
00216 Agsym_t *agedgeattr(Agraph_t * g, char *name, char *value)
00217 {
00218     if (g == NULL)
00219         g = AG.proto_g;
00220     if (g != g->root)
00221         return NULL;
00222     return agattr(g->proto->e, name, value);
00223 }
00224 
00225 /* attribute dictionaries */
00226 
00227 static void agfreesym(void *ptr)
00228 {
00229     Agsym_t *a;
00230     a = (Agsym_t *) ptr;
00231     agstrfree(a->name);
00232     agstrfree(a->value);
00233     free(a);
00234 }
00235 
00236 void agFREEdict(Agraph_t * g, Agdict_t * dict)
00237 {
00238     int i;
00239     Agsym_t *a;
00240 
00241     g = g;
00242     dtclose(dict->dict);
00243     if (dict->list) {
00244         i = 0;
00245         while ((a = dict->list[i++]))
00246             agfreesym(a);
00247         free(dict->list);
00248     }
00249     free(dict);
00250 }
00251 
00252 Agdict_t *agNEWdict(char *name)
00253 {
00254     Agdict_t *dict;
00255     static Dtdisc_t symdisc = {
00256         offsetof(Agsym_t, name),        /* key */
00257         -1,                     /* size */
00258         -1,                     /* link */
00259         (Dtmake_f) 0,
00260         (Dtfree_f) 0,
00261         (Dtcompar_f) 0,         /* use strcmp */
00262         (Dthash_f) 0,
00263         (Dtmemory_f) 0,
00264         (Dtevent_f) 0
00265     };
00266 
00267     dict = NEW(Agdict_t);
00268     dict->name = name;
00269     dict->dict = dtopen(&symdisc, Dttree);
00270     dict->list = NULL;
00271     return dict;
00272 }
00273 
00274 void agcopydict(Agdict_t * to_dict, Agdict_t * from_dict)
00275 {
00276     int i, n;
00277     Agsym_t *a, *b;
00278 
00279     n = dtsize(from_dict->dict);
00280     for (i = 0; i < n; i++) {
00281         a = from_dict->list[i];
00282         b = agNEWsym(to_dict, a->name, a->value);
00283         b->printed = a->printed;
00284         b->fixed = a->fixed;
00285 #ifdef WIN32
00286         /* Microsoft C is a thing of wonder. */
00287         fprintf(stderr, "", a->name, a->value);
00288 #endif
00289     }
00290 }
00291 
00292 Agsym_t *agfindattr(void *obj, char *name)
00293 {
00294     Agsym_t *rv;
00295     Agdict_t *dict = agdictof(obj);
00296 
00297     rv = (Agsym_t *) dtmatch(dict->dict, name);
00298     return rv;
00299 }
00300 
00301 Agsym_t *agfstattr(void *obj)
00302 {
00303         Agdict_t *dict = agdictof(obj);
00304         return (Agsym_t *)dtfirst(dict->dict);
00305 }
00306 
00307 Agsym_t *agnxtattr(void *obj, Agsym_t *a)
00308 {
00309         Agdict_t *dict = agdictof(obj);
00310         return (Agsym_t *)dtnext(dict->dict, a);
00311 }
00312 
00313 Agsym_t *aglstattr(void *obj)
00314 {
00315         Agdict_t *dict = agdictof(obj);
00316         return (Agsym_t *)dtlast(dict->dict);
00317 }
00318 
00319 Agsym_t *agprvattr(void *obj, Agsym_t *a)
00320 {
00321         Agdict_t *dict = agdictof(obj);
00322         return (Agsym_t *)dtprev(dict->dict, a);
00323 }
00324 
00325         /* this is normally called by the aginit() macro */
00326 int aginitlib(int gs, int ns, int es)
00327 {
00328     int rv = 0;
00329     if (AG.proto_g == NULL) {
00330         AG.graph_nbytes = gs;
00331         AG.node_nbytes = ns;
00332         AG.edge_nbytes = es;
00333         AG.init_called = TRUE;
00334         if (initproto()) {
00335             agerr(AGERR, "aginitlib: initproto failed\n");
00336             rv = 1;
00337         }
00338     } else
00339         if ((AG.graph_nbytes != gs) || (AG.node_nbytes != ns)
00340             || (AG.edge_nbytes != es))
00341         agerr(AGWARN, "aginit() called multiply with inconsistent args\n");
00342     return rv;
00343 }
00344 
00345 char *agget(void *obj, char *attr)
00346 {
00347     return agxget(obj, agindex(obj, attr));
00348 }
00349 
00350 int agset(void *obj, char *attr, char *value)
00351 {
00352     return agxset(obj, agindex(obj, attr), value);
00353 }
00354 
00355 int agindex(void *obj, char *name)
00356 {
00357     Agsym_t *a;
00358     int rv = -1;
00359 
00360     a = agfindattr(obj, name);
00361     if (a)
00362         rv = a->index;
00363     return rv;
00364 }
00365 
00366 char *agxget(void *obj, int index)
00367 {
00368     if (index >= 0)
00369         return ((Agraph_t *) obj)->attr[index];
00370     return NULL;
00371 }
00372 
00373 int agxset(void *obj, int index, char *buf)
00374 {
00375         char **p;
00376     if (index >= 0) {
00377         Agraph_t *gobj = (Agraph_t *)obj;
00378         p = gobj->attr;
00379         agstrfree(p[index]);
00380         p[index] = agstrdup(buf);
00381         /* the index-th attr was set by agxset */
00382         gobj->didset[index / CHAR_BIT] |= 1 << (index % CHAR_BIT);
00383         return 0;
00384     } else
00385         return -1;
00386 }
00387 
00388 int agsafeset(void* obj, char* name, char* value, char* def)
00389 {
00390     Agsym_t* a = agfindattr(obj, name);
00391 
00392     if (a == NULL) {
00393         if (!def) def = "";
00394         switch (TAG_OF(obj)) {
00395         case TAG_GRAPH:
00396             a = agraphattr(((Agraph_t*)obj)->root, name, def);
00397             break;
00398         case TAG_NODE:
00399             a = agnodeattr(((Agnode_t*)obj)->graph, name, def);
00400             break;
00401         case TAG_EDGE:
00402             a = agedgeattr(((Agedge_t*)obj)->head->graph, name, def);
00403             break;
00404         }
00405     }
00406     return agxset(obj, a->index, value);
00407 }
00408 
00409 /* agcopyattr:
00410  * Assumes attributes have already been declared.
00411  * Do not copy key attribute for edges, as this must be distinct.
00412  * Returns non-zero on failure or if objects have different type.
00413  */
00414 int agcopyattr(void *oldobj, void *newobj)
00415 {
00416     Agdict_t *d = agdictof(oldobj);
00417     Agsym_t **list = d->list;
00418     Agsym_t *sym;
00419     Agsym_t *newsym;
00420     int r = 0;
00421     int isEdge = (TAG_OF(oldobj) == TAG_EDGE);
00422 
00423     if (TAG_OF(oldobj) != TAG_OF(newobj)) return 1;
00424     while (!r && (sym = *list++)) {
00425         if (isEdge && sym->index == KEYX) continue;
00426         newsym = agfindattr(newobj,sym->name);
00427         if (!newsym) return 1;
00428         r = agxset(newobj, newsym->index, agxget(oldobj, sym->index));
00429     }
00430     return r;
00431 }
00432