Graphviz  2.35.20130930.0449
attribs.c
Go to the documentation of this file.
1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3 
4 /*************************************************************************
5  * Copyright (c) 2011 AT&T Intellectual Property
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the Eclipse Public License v1.0
8  * which accompanies this distribution, and is available at
9  * http://www.eclipse.org/legal/epl-v10.html
10  *
11  * Contributors: See CVS logs. Details at http://www.graphviz.org/
12  *************************************************************************/
13 
14 #include <limits.h>
15 
16 #define EXTERN
17 #include "libgraph.h"
18 
19 #ifdef DMALLOC
20 #include "dmalloc.h"
21 #endif
22 
23 Agdict_t *agdictof(void *obj)
24 {
25  Agdict_t *d = NULL;
26 
27  switch (TAG_OF(obj)) {
28  case TAG_GRAPH:
29  d = ((Agraph_t *) obj)->univ->globattr;
30  break;
31  case TAG_NODE:
32  d = ((Agnode_t *) obj)->graph->univ->nodeattr;
33  break;
34  case TAG_EDGE:
35  d = ((Agedge_t *) obj)->tail->graph->univ->edgeattr;
36  break;
37  }
38  return d;
39 }
40 
41 Agsym_t *agNEWsym(Agdict_t * dict, char *name, char *value)
42 {
43  Agsym_t *a;
44  int i;
45 
46  a = NEW(Agsym_t);
47  a->name = agstrdup(name);
48  a->value = agstrdup(value);
49  a->printed = TRUE;
50  i = a->index = dtsize(dict->dict);
51  dict->list = ALLOC(i + 2, dict->list, Agsym_t *);
52  dict->list[i++] = a;
53  dict->list[i++] = NULL;
54  dtinsert(dict->dict, a);
55  return a;
56 }
57 
58 static void obj_init_attr(void *obj, Agsym_t * attr, int isnew)
59 {
60  int i;
61  Agraph_t *gobj; /* generic object */
62 
63  gobj = (Agraph_t *) obj;
64  i = attr->index;
65  if (isnew) {
66  gobj->attr = ALLOC(i + 1, gobj->attr, char *);
67  gobj->attr[i] = agstrdup(attr->value);
68  if (i % CHAR_BIT == 0) {
69  /* allocate in chunks of CHAR_BIT bits */
70  gobj->didset = ALLOC(i / CHAR_BIT + 1, gobj->didset, char);
71  gobj->didset[i / CHAR_BIT] = 0;
72  }
73  }
74  else if ((gobj->didset[i / CHAR_BIT] & (1 << (i % CHAR_BIT))) == 0) {
75  /* the i-th attr was not set by agxset, so we can replace it */
76  agstrfree(gobj->attr[i]);
77  gobj->attr[i] = agstrdup(attr->value);
78  }
79 }
80 
81 static void add_graph_attr(Agraph_t * g, Agsym_t * attr, int isnew)
82 {
83  Agnode_t *n;
84 
85  if (g->meta_node) {
86  for (n = agfstnode(g->meta_node->graph); n;
87  n = agnxtnode(g->meta_node->graph, n))
88  obj_init_attr(agusergraph(n), attr, isnew);
89  } else
90  obj_init_attr(g, attr, isnew);
91 }
92 
93 static void add_node_attr(Agraph_t * g, Agsym_t * attr, int isnew)
94 {
95  Agnode_t *n;
96  Agproto_t *proto;
97 
98  for (n = agfstnode(g); n; n = agnxtnode(g, n))
99  obj_init_attr(n, attr, isnew);
100  if (g->meta_node) {
101  for (n = agfstnode(g->meta_node->graph); n;
102  n = agnxtnode(g->meta_node->graph, n))
103  for (proto = agusergraph(n)->proto; proto; proto = proto->prev)
104  obj_init_attr(proto->n, attr, isnew);
105  } else
106  for (proto = g->proto; proto; proto = proto->prev)
107  obj_init_attr(proto->n, attr, isnew);
108 }
109 
110 static void add_edge_attr(Agraph_t * g, Agsym_t * attr, int isnew)
111 {
112  Agnode_t *n;
113  Agedge_t *e;
114  Agproto_t *proto;
115 
116  for (n = agfstnode(g); n; n = agnxtnode(g, n))
117  for (e = agfstout(g, n); e; e = agnxtout(g, e))
118  obj_init_attr(e, attr, isnew);
119  if (g->meta_node) {
120  for (n = agfstnode(g->meta_node->graph); n;
121  n = agnxtnode(g->meta_node->graph, n))
122  for (proto = agusergraph(n)->proto; proto; proto = proto->prev)
123  obj_init_attr(proto->e, attr, isnew);
124  } else
125  for (proto = g->proto; proto; proto = proto->prev)
126  obj_init_attr(proto->e, attr, isnew);
127 }
128 
129 Agsym_t *agattr(void *obj, char *name, char *value)
130 {
131  Agsym_t *rv;
132  int isnew = 1;
133 
134  rv = agfindattr(obj, name);
135  if (rv) {
136  if (strcmp(rv->value, value)) {
137  agstrfree(rv->value);
138  rv->value = agstrdup(value);
139  isnew = 0;
140  }
141  else
142  return rv;
143  }
144  else
145  rv = agNEWsym(agdictof(obj), name, value);
146  if (rv) {
147  switch (TAG_OF(obj)) {
148  case TAG_GRAPH:
149  add_graph_attr((Agraph_t *) obj, rv, isnew);
150  break;
151  case TAG_NODE:
152  add_node_attr(((Agnode_t *) obj)->graph, rv, isnew);
153  break;
154  case TAG_EDGE:
155  add_edge_attr(((Agedge_t *) obj)->head->graph, rv, isnew);
156  break;
157  }
158  }
159  return rv;
160 }
161 
163 {
164  return AG.proto_g;
165 }
166 
168 {
169  return g->proto->n;
170 }
171 
172 
174 {
175  return g->proto->e;
176 }
177 
178 
179 static int initproto(void)
180 {
181  Agsym_t *a;
182  Agraph_t *g;
183  g = AG.proto_g = agopen("ProtoGraph", AGRAPH);
184  a = agattr(g->proto->e, KEY_ID, "");
185  if (a->index != KEYX)
186  return 1;
187  a = agattr(g->proto->e, TAIL_ID, "");
188  if (a->index != TAILX)
189  return 1;
190  a->printed = FALSE;
191  a = agattr(g->proto->e, HEAD_ID, "");
192  if (a->index != HEADX)
193  return 1;
194  a->printed = FALSE;
195  return 0;
196 }
197 
198 Agsym_t *agraphattr(Agraph_t * g, char *name, char *value)
199 {
200  if (g == NULL)
201  g = AG.proto_g;
202  if (g != g->root)
203  return NULL;
204  return agattr(g, name, value);
205 }
206 
207 Agsym_t *agnodeattr(Agraph_t * g, char *name, char *value)
208 {
209  if (g == NULL)
210  g = AG.proto_g;
211  if (g != g->root)
212  return NULL;
213  return agattr(g->proto->n, name, value);
214 }
215 
216 Agsym_t *agedgeattr(Agraph_t * g, char *name, char *value)
217 {
218  if (g == NULL)
219  g = AG.proto_g;
220  if (g != g->root)
221  return NULL;
222  return agattr(g->proto->e, name, value);
223 }
224 
225 /* attribute dictionaries */
226 
227 static void agfreesym(void *ptr)
228 {
229  Agsym_t *a;
230  a = (Agsym_t *) ptr;
231  agstrfree(a->name);
232  agstrfree(a->value);
233  free(a);
234 }
235 
236 void agFREEdict(Agraph_t * g, Agdict_t * dict)
237 {
238  int i;
239  Agsym_t *a;
240 
241  g = g;
242  dtclose(dict->dict);
243  if (dict->list) {
244  i = 0;
245  while ((a = dict->list[i++]))
246  agfreesym(a);
247  free(dict->list);
248  }
249  free(dict);
250 }
251 
252 Agdict_t *agNEWdict(char *name)
253 {
254  Agdict_t *dict;
255  static Dtdisc_t symdisc = {
256  offsetof(Agsym_t, name), /* key */
257  -1, /* size */
258  -1, /* link */
259  (Dtmake_f) 0,
260  (Dtfree_f) 0,
261  (Dtcompar_f) 0, /* use strcmp */
262  (Dthash_f) 0,
263  (Dtmemory_f) 0,
264  (Dtevent_f) 0
265  };
266 
267  dict = NEW(Agdict_t);
268  dict->name = name;
269  dict->dict = dtopen(&symdisc, Dttree);
270  dict->list = NULL;
271  return dict;
272 }
273 
274 void agcopydict(Agdict_t * to_dict, Agdict_t * from_dict)
275 {
276  int i, n;
277  Agsym_t *a, *b;
278 
279  n = dtsize(from_dict->dict);
280  for (i = 0; i < n; i++) {
281  a = from_dict->list[i];
282  b = agNEWsym(to_dict, a->name, a->value);
283  b->printed = a->printed;
284  b->fixed = a->fixed;
285 #ifdef WIN32
286  /* Microsoft C is a thing of wonder. */
287  fprintf(stderr, "", a->name, a->value);
288 #endif
289  }
290 }
291 
292 Agsym_t *agfindattr(void *obj, char *name)
293 {
294  Agsym_t *rv;
295  Agdict_t *dict = agdictof(obj);
296 
297  rv = (Agsym_t *) dtmatch(dict->dict, name);
298  return rv;
299 }
300 
301 Agsym_t *agfstattr(void *obj)
302 {
303  Agdict_t *dict = agdictof(obj);
304  return (Agsym_t *)dtfirst(dict->dict);
305 }
306 
307 Agsym_t *agnxtattr(void *obj, Agsym_t *a)
308 {
309  Agdict_t *dict = agdictof(obj);
310  return (Agsym_t *)dtnext(dict->dict, a);
311 }
312 
313 Agsym_t *aglstattr(void *obj)
314 {
315  Agdict_t *dict = agdictof(obj);
316  return (Agsym_t *)dtlast(dict->dict);
317 }
318 
319 Agsym_t *agprvattr(void *obj, Agsym_t *a)
320 {
321  Agdict_t *dict = agdictof(obj);
322  return (Agsym_t *)dtprev(dict->dict, a);
323 }
324 
325  /* this is normally called by the aginit() macro */
326 int aginitlib(int gs, int ns, int es)
327 {
328  int rv = 0;
329  if (AG.proto_g == NULL) {
330  AG.graph_nbytes = gs;
331  AG.node_nbytes = ns;
332  AG.edge_nbytes = es;
333  AG.init_called = TRUE;
334  if (initproto()) {
335  agerr(AGERR, "aginitlib: initproto failed\n");
336  rv = 1;
337  }
338  } else
339  if ((AG.graph_nbytes != gs) || (AG.node_nbytes != ns)
340  || (AG.edge_nbytes != es))
341  agerr(AGWARN, "aginit() called multiply with inconsistent args\n");
342  return rv;
343 }
344 
345 char *agget(void *obj, char *attr)
346 {
347  return agxget(obj, agindex(obj, attr));
348 }
349 
350 int agset(void *obj, char *attr, char *value)
351 {
352  return agxset(obj, agindex(obj, attr), value);
353 }
354 
355 int agindex(void *obj, char *name)
356 {
357  Agsym_t *a;
358  int rv = -1;
359 
360  a = agfindattr(obj, name);
361  if (a)
362  rv = a->index;
363  return rv;
364 }
365 
366 char *agxget(void *obj, int index)
367 {
368  if (index >= 0)
369  return ((Agraph_t *) obj)->attr[index];
370  return NULL;
371 }
372 
373 int agxset(void *obj, int index, char *buf)
374 {
375  char **p;
376  if (index >= 0) {
377  Agraph_t *gobj = (Agraph_t *)obj;
378  p = gobj->attr;
379  agstrfree(p[index]);
380  p[index] = agstrdup(buf);
381  /* the index-th attr was set by agxset */
382  gobj->didset[index / CHAR_BIT] |= 1 << (index % CHAR_BIT);
383  return 0;
384  } else
385  return -1;
386 }
387 
388 int agsafeset(void* obj, char* name, char* value, char* def)
389 {
390  Agsym_t* a = agfindattr(obj, name);
391 
392  if (a == NULL) {
393  if (!def) def = "";
394  switch (TAG_OF(obj)) {
395  case TAG_GRAPH:
396  a = agraphattr(((Agraph_t*)obj)->root, name, def);
397  break;
398  case TAG_NODE:
399  a = agnodeattr(((Agnode_t*)obj)->graph, name, def);
400  break;
401  case TAG_EDGE:
402  a = agedgeattr(((Agedge_t*)obj)->head->graph, name, def);
403  break;
404  }
405  }
406  return agxset(obj, a->index, value);
407 }
408 
409 /* agcopyattr:
410  * Assumes attributes have already been declared.
411  * Do not copy key attribute for edges, as this must be distinct.
412  * Returns non-zero on failure or if objects have different type.
413  */
414 int agcopyattr(void *oldobj, void *newobj)
415 {
416  Agdict_t *d = agdictof(oldobj);
417  Agsym_t **list = d->list;
418  Agsym_t *sym;
419  Agsym_t *newsym;
420  int r = 0;
421  int isEdge = (TAG_OF(oldobj) == TAG_EDGE);
422 
423  if (TAG_OF(oldobj) != TAG_OF(newobj)) return 1;
424  while (!r && (sym = *list++)) {
425  if (isEdge && sym->index == KEYX) continue;
426  newsym = agfindattr(newobj,sym->name);
427  if (!newsym) return 1;
428  r = agxset(newobj, newsym->index, agxget(oldobj, sym->index));
429  }
430  return r;
431 }
432