Graphviz  2.41.20170921.2350
pend.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 <cghdr.h>
15 
16 static char DRName[] = "_AG_pending";
17 
18 typedef struct symlist_s {
20  struct symlist_s *link;
21 } symlist_t;
22 
23 /* this record describes one pending callback on one object */
24 typedef struct {
26  IDTYPE key; /* universal key for main or sub-object */
29  symlist_t *symlist; /* attributes involved */
30 } pending_cb_t;
31 
32 typedef struct {
34  struct {
35  Dict_t *g, *n, *e;
36  } ins, mod, del;
37 } pendingset_t;
38 
39 static void free_symlist(pending_cb_t * pcb)
40 {
41  symlist_t *s, *t;
42 
43  for (s = pcb->symlist; s; s = t) {
44  t = s->link;
45  agfree(pcb->g, s);
46  }
47 }
48 
49 static void freef(Dict_t * dict, void *ptr, Dtdisc_t * disc)
50 {
51  pending_cb_t *pcb;
52 
53  NOTUSED(dict);
54  NOTUSED(disc);
55  pcb = ptr;
56  free_symlist(pcb);
57  agfree(pcb->g, pcb);
58 }
59 
60 static Dtdisc_t Disc = {
61  offsetof(pending_cb_t, key), /* sort by 'key' */
62  sizeof(uint64_t),
63  0, /* link offset */
64  NIL(Dtmake_f),
65  freef,
66  NIL(Dtcompar_f),
67  NIL(Dthash_f)
68 };
69 
70 static Dict_t *dictof(pendingset_t * ds, Agobj_t * obj, int kind)
71 {
72  Dict_t **dict_ref = NIL(Dict_t **);
73 
74  dict_ref = 0;
75  switch (AGTYPE(obj)) {
76  case AGRAPH:
77  switch (kind) {
78  case CB_INITIALIZE:
79  dict_ref = &(ds->ins.g);
80  break;
81  case CB_UPDATE:
82  dict_ref = &(ds->mod.g);
83  break;
84  case CB_DELETION:
85  dict_ref = &(ds->del.g);
86  break;
87  default:
88  break;
89  }
90  break;
91  case AGNODE:
92  switch (kind) {
93  case CB_INITIALIZE:
94  dict_ref = &(ds->ins.n);
95  break;
96  case CB_UPDATE:
97  dict_ref = &(ds->mod.n);
98  break;
99  case CB_DELETION:
100  dict_ref = &(ds->del.n);
101  break;
102  default:
103  break;
104  }
105  break;
106  case AGEDGE:
107  switch (kind) {
108  case CB_INITIALIZE:
109  dict_ref = &(ds->ins.e);
110  break;
111  case CB_UPDATE:
112  dict_ref = &(ds->mod.e);
113  break;
114  case CB_DELETION:
115  dict_ref = &(ds->del.e);
116  break;
117  default:
118  break;
119  }
120  break;
121  default:
122  break;
123  }
124 
125  if (dict_ref == 0)
126  agerr(AGERR, "pend dictof a bad object");
127  if (*dict_ref == NIL(Dict_t *))
128  *dict_ref = agdtopen(agraphof(obj), &Disc, Dttree);
129  return *dict_ref;
130 }
131 
132 static IDTYPE genkey(Agobj_t * obj)
133 {
134  return obj->tag.id;
135 }
136 
137 static pending_cb_t *lookup(Dict_t * dict, Agobj_t * obj)
138 {
139  pending_cb_t key, *rv;
140 
141  key.key = genkey(obj);
142  rv = (pending_cb_t *) dtsearch(dict, &key);
143  return rv;
144 }
145 
146 static void record_sym(Agobj_t * obj, pending_cb_t * handle,
147  Agsym_t * optsym)
148 {
149  symlist_t *sym, *nsym, *psym;
150 
151  psym = NIL(symlist_t *);
152  for (sym = handle->symlist; sym; psym = sym, sym = sym->link) {
153  if (sym->sym == optsym)
154  break;
155  if (sym == NIL(symlist_t *)) {
156  nsym = agalloc(agraphof(obj), sizeof(symlist_t));
157  nsym->sym = optsym;
158  if (psym)
159  psym->link = nsym;
160  else
161  handle->symlist = nsym;
162  }
163  /* else we already have a callback registered */
164  }
165 }
166 
167 static pending_cb_t *insert(Dict_t * dict, Agraph_t * g, Agobj_t * obj,
168  Agsym_t * optsym)
169 {
170  pending_cb_t *handle;
171  handle = agalloc(agraphof(obj), sizeof(pending_cb_t));
172  handle->obj = obj;
173  handle->key = genkey(obj);
174  handle->g = g;
175  if (optsym) {
176  handle->symlist =
177  (symlist_t *) agalloc(handle->g, sizeof(symlist_t));
178  handle->symlist->sym = optsym;
179  }
180  dtinsert(dict, handle);
181  return handle;
182 }
183 
184 static void purge(Dict_t * dict, Agobj_t * obj)
185 {
186  pending_cb_t *handle;
187 
188  if ((handle = lookup(dict, obj))) {
189  dtdelete(dict, handle);
190  }
191 }
192 
193 void agrecord_callback(Agraph_t * g, Agobj_t * obj, int kind,
194  Agsym_t * optsym)
195 {
196  pendingset_t *pending;
197  Dict_t *dict;
198  pending_cb_t *handle;
199 
200  pending =
201  (pendingset_t *) agbindrec(g, DRName, sizeof(pendingset_t), FALSE);
202 
203  switch (kind) {
204  case CB_INITIALIZE:
205  assert(lookup(dictof(pending, obj, CB_UPDATE), obj) == 0);
206  assert(lookup(dictof(pending, obj, CB_DELETION), obj) == 0);
207  dict = dictof(pending, obj, CB_INITIALIZE);
208  handle = lookup(dict, obj);
209  if (handle == 0)
210  handle = insert(dict, g, obj, optsym);
211  break;
212  case CB_UPDATE:
213  if (lookup(dictof(pending, obj, CB_INITIALIZE), obj))
214  break;
215  if (lookup(dictof(pending, obj, CB_DELETION), obj))
216  break;
217  dict = dictof(pending, obj, CB_UPDATE);
218  handle = lookup(dict, obj);
219  if (handle == 0)
220  handle = insert(dict, g, obj, optsym);
221  record_sym(obj, handle, optsym);
222  break;
223  case CB_DELETION:
224  purge(dictof(pending, obj, CB_INITIALIZE), obj);
225  purge(dictof(pending, obj, CB_UPDATE), obj);
226  dict = dictof(pending, obj, CB_DELETION);
227  handle = lookup(dict, obj);
228  if (handle == 0)
229  handle = insert(dict, g, obj, optsym);
230  break;
231  default:
232  agerr(AGERR,"agrecord_callback of a bad object");
233  }
234 }
235 
236 static void cb(Dict_t * dict, int callback_kind)
237 {
238  pending_cb_t *pcb;
239  Agraph_t *g;
240  symlist_t *psym;
241  Agcbstack_t *stack;
242 
243  if (dict)
244  while ((pcb = (pending_cb_t *) dtfirst(dict))) {
245  g = pcb->g;
246  stack = g->clos->cb;
247  switch (callback_kind) {
248  case CB_INITIALIZE:
249  aginitcb(g, pcb->obj, stack);
250  break;
251  case CB_UPDATE:
252  for (psym = pcb->symlist; psym; psym = psym->link)
253  agupdcb(g, pcb->obj, psym->sym, stack);
254  break;
255  case CB_DELETION:
256  agdelcb(g, pcb->obj, stack);
257  break;
258  }
259  dtdelete(dict, pcb);
260  }
261 }
262 
263 static void agrelease_callbacks(Agraph_t * g)
264 {
265  pendingset_t *pending;
266  if (NOT(g->clos->callbacks_enabled)) {
268  pending =
269  (pendingset_t *) agbindrec(g, DRName, sizeof(pendingset_t),
270  FALSE);
271  /* this destroys objects in the opposite of their order of creation */
272  cb(pending->ins.g, CB_INITIALIZE);
273  cb(pending->ins.n, CB_INITIALIZE);
274  cb(pending->ins.e, CB_INITIALIZE);
275 
276  cb(pending->mod.g, CB_UPDATE);
277  cb(pending->mod.n, CB_UPDATE);
278  cb(pending->mod.e, CB_UPDATE);
279 
280  cb(pending->del.e, CB_DELETION);
281  cb(pending->del.n, CB_DELETION);
282  cb(pending->del.g, CB_DELETION);
283  }
284 }
285 
286 int agcallbacks(Agraph_t * g, int flag)
287 {
288  if (flag && NOT(g->clos->callbacks_enabled))
289  agrelease_callbacks(g);
290  if (g->clos->callbacks_enabled) {
291  g->clos->callbacks_enabled = flag;
292  return TRUE;
293  }
294  g->clos->callbacks_enabled = flag;
295  return FALSE;
296 }
int(* Dtcompar_f)(Dt_t *, void *, void *, Dtdisc_t *)
Definition: cdt.h:40
unsigned int(* Dthash_f)(Dt_t *, void *, Dtdisc_t *)
Definition: cdt.h:41
Definition: cgraph.h:388
void agdelcb(Agraph_t *g, void *obj, Agcbstack_t *disc)
Definition: obj.c:146
void *(* Dtmake_f)(Dt_t *, void *, Dtdisc_t *)
Definition: cdt.h:38
Dict_t * e
Definition: pend.c:35
#define dtdelete(d, o)
Definition: cdt.h:264
#define assert(x)
Definition: cghdr.h:47
#define NOTUSED(var)
Definition: cghdr.h:54
Definition: cdt.h:80
#define dtfirst(d)
Definition: cdt.h:254
Agtag_t tag
Definition: cgraph.h:108
struct symlist_s symlist_t
int agerr(agerrlevel_t level, const char *fmt,...)
Definition: agerror.c:141
struct symlist_s * link
Definition: pend.c:20
unsigned char callbacks_enabled
Definition: cgraph.h:234
CGRAPH_API int agcallbacks(Agraph_t *g, int flag)
Definition: pend.c:286
IDTYPE id
Definition: cgraph.h:96
CGRAPH_API void agfree(Agraph_t *g, void *ptr)
Definition: mem.c:89
#define AGTYPE(obj)
Definition: cgraph.h:113
void agrecord_callback(Agraph_t *g, Agobj_t *obj, int kind, Agsym_t *optsym)
Definition: pend.c:193
void aginitcb(Agraph_t *g, void *obj, Agcbstack_t *disc)
Definition: obj.c:84
CGRAPH_API Agraph_t * agraphof(void *obj)
Definition: obj.c:185
Agrec_t h
Definition: pend.c:33
uint64_t IDTYPE
Definition: cgraph.h:51
Definition: pend.c:18
#define NIL(t)
Definition: dthdr.h:13
#define CB_UPDATE
Definition: cghdr.h:161
IDTYPE key
Definition: pend.c:26
#define dtsearch(d, o)
Definition: cdt.h:260
Dtlink_t link
Definition: pend.c:25
Agraph_t * g
Definition: pend.c:27
void agupdcb(Agraph_t *g, void *obj, Agsym_t *sym, Agcbstack_t *disc)
Definition: obj.c:115
struct pendingset_t::@4 del
#define CB_INITIALIZE
Definition: cghdr.h:160
Definition: grammar.c:79
struct pendingset_t::@4 mod
Definition: cgraph.h:83
#define AGNODE
Definition: cgraph.h:101
Agsym_t * sym
Definition: pend.c:19
#define dtinsert(d, o)
Definition: cdt.h:262
Dict_t * g
Definition: pend.c:35
Dict_t * agdtopen(Agraph_t *g, Dtdisc_t *disc, Dtmethod_t *method)
Definition: utils.c:53
CDT_API Dtmethod_t * Dttree
Definition: cdt.h:176
symlist_t * symlist
Definition: pend.c:29
#define NOT(x)
Definition: cgraph.h:41
CGRAPH_API void * agalloc(Agraph_t *g, size_t size)
Definition: mem.c:62
Agcbstack_t * cb
Definition: cgraph.h:233
CGRAPH_API void * agbindrec(void *obj, char *name, unsigned int size, int move_to_front)
Definition: rec.c:86
Definition: cdt.h:99
Dict_t * n
Definition: pend.c:35
struct pendingset_t::@4 ins
Agclos_t * clos
Definition: cgraph.h:248
#define AGEDGE
Definition: cgraph.h:104
#define FALSE
Definition: cgraph.h:35
Agobj_t * obj
Definition: pend.c:28
#define CB_DELETION
Definition: cghdr.h:162
#define AGRAPH
Definition: cgraph.h:100
#define TRUE
Definition: cgraph.h:38