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 | /* |
17 | * run time records |
18 | */ |
19 | |
20 | static void set_data(Agobj_t * obj, Agrec_t * data, int mtflock) |
21 | { |
22 | Agedge_t *e; |
23 | |
24 | obj->data = data; |
25 | obj->tag.mtflock = mtflock; |
26 | if ((AGTYPE(obj) == AGINEDGE) || (AGTYPE(obj) == AGOUTEDGE)) { |
27 | e = agopp((Agedge_t *) obj); |
28 | AGDATA(e) = data; |
29 | e->base.tag.mtflock = mtflock; |
30 | } |
31 | } |
32 | |
33 | /* find record in circular list and do optional move-to-front */ |
34 | Agrec_t *aggetrec(void *obj, char *name, int mtf) |
35 | { |
36 | Agobj_t *hdr; |
37 | Agrec_t *d, *first; |
38 | |
39 | hdr = (Agobj_t *) obj; |
40 | first = d = hdr->data; |
41 | while (d) { |
42 | if ((d->name == name) || streq(name, d->name)) |
43 | break; |
44 | d = d->next; |
45 | if (d == first) { |
46 | d = NIL(Agrec_t *); |
47 | break; |
48 | } |
49 | } |
50 | if (d) { |
51 | if (hdr->tag.mtflock) { |
52 | if (mtf && (hdr->data != d)) |
53 | agerr(AGERR, "move to front lock inconsistency" ); |
54 | } else { |
55 | if ((d != first) || (mtf != hdr->tag.mtflock)) |
56 | set_data(hdr, d, mtf); /* Always optimize */ |
57 | } |
58 | } |
59 | return d; |
60 | } |
61 | |
62 | /* insert the record in data list of this object (only) */ |
63 | static void objputrec(Agraph_t * g, Agobj_t * obj, void *arg) |
64 | { |
65 | Agrec_t *firstrec, *newrec; |
66 | |
67 | newrec = arg; |
68 | firstrec = obj->data; |
69 | if (firstrec == NIL(Agrec_t *)) |
70 | newrec->next = newrec; /* 0 elts */ |
71 | else { |
72 | if (firstrec->next == firstrec) { |
73 | firstrec->next = newrec; /* 1 elt */ |
74 | newrec->next = firstrec; |
75 | } else { |
76 | newrec->next = firstrec->next; |
77 | firstrec->next = newrec; |
78 | } |
79 | } |
80 | if (NOT(obj->tag.mtflock)) |
81 | set_data(obj, newrec, FALSE); |
82 | } |
83 | |
84 | /* attach a new record of the given size to the object. |
85 | */ |
86 | void *agbindrec(void *arg_obj, char *recname, unsigned int recsize, |
87 | int mtf) |
88 | { |
89 | Agraph_t *g; |
90 | Agobj_t *obj; |
91 | Agrec_t *rec; |
92 | |
93 | obj = (Agobj_t *) arg_obj; |
94 | g = agraphof(obj); |
95 | rec = aggetrec(obj, recname, FALSE); |
96 | if ((rec == NIL(Agrec_t *)) && (recsize > 0)) { |
97 | rec = (Agrec_t *) agalloc(g, recsize); |
98 | rec->name = agstrdup(g, recname); |
99 | switch (obj->tag.objtype) { |
100 | case AGRAPH: |
101 | objputrec(g, obj, rec); |
102 | break; |
103 | case AGNODE: |
104 | objputrec(g, obj, rec); |
105 | break; |
106 | case AGINEDGE: |
107 | case AGOUTEDGE: |
108 | objputrec(g, obj, rec); |
109 | break; |
110 | } |
111 | } |
112 | if (mtf) |
113 | aggetrec(arg_obj, recname, TRUE); |
114 | return (void *) rec; |
115 | } |
116 | |
117 | |
118 | /* if obj points to rec, move its data pointer. break any mtf lock(?) */ |
119 | static void objdelrec(Agraph_t * g, Agobj_t * obj, void *arg_rec) |
120 | { |
121 | Agrec_t *rec = (Agrec_t *) arg_rec, *newrec; |
122 | if (obj->data == rec) { |
123 | if (rec->next == rec) |
124 | newrec = NIL(Agrec_t *); |
125 | else |
126 | newrec = rec->next; |
127 | set_data(obj, newrec, FALSE); |
128 | } |
129 | } |
130 | |
131 | /* delete a record from a circular data list */ |
132 | static void listdelrec(Agobj_t * obj, Agrec_t * rec) |
133 | { |
134 | Agrec_t *prev; |
135 | |
136 | prev = obj->data; |
137 | while (prev->next != rec) { |
138 | prev = prev->next; |
139 | assert(prev != obj->data); |
140 | } |
141 | /* following is a harmless no-op if the list is trivial */ |
142 | prev->next = rec->next; |
143 | } |
144 | |
145 | int agdelrec(void *arg_obj, char *name) |
146 | { |
147 | Agobj_t *obj; |
148 | Agrec_t *rec; |
149 | Agraph_t *g; |
150 | |
151 | obj = (Agobj_t *) arg_obj; |
152 | g = agraphof(obj); |
153 | rec = aggetrec(obj, name, FALSE); |
154 | if (rec) { |
155 | listdelrec(obj, rec); /* zap it from the circular list */ |
156 | switch (obj->tag.objtype) { /* refresh any stale pointers */ |
157 | case AGRAPH: |
158 | objdelrec(g, obj, rec); |
159 | break; |
160 | case AGNODE: |
161 | case AGINEDGE: |
162 | case AGOUTEDGE: |
163 | agapply(agroot(g), obj, objdelrec, rec, FALSE); |
164 | break; |
165 | } |
166 | agstrfree(g, rec->name); |
167 | agfree(g, rec); |
168 | return SUCCESS; |
169 | } else |
170 | return FAILURE; |
171 | } |
172 | |
173 | static void simple_delrec(Agraph_t * g, Agobj_t * obj, void *rec_name) |
174 | { |
175 | agdelrec(obj, rec_name); |
176 | } |
177 | |
178 | #ifdef OLD |
179 | void agclean(Agraph_t * g, char *graphdata, char *nodedata, char *edgedata) |
180 | { |
181 | Agnode_t *n; |
182 | Agedge_t *e; |
183 | |
184 | if (nodedata || edgedata) { |
185 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) { |
186 | if (edgedata) |
187 | for (e = agfstout(n); e; e = agnxtout(g, e)) { |
188 | agdelrec(e, edgedata); |
189 | } |
190 | if (nodedata) |
191 | agdelrec(n, nodedata); |
192 | } |
193 | } |
194 | agdelrec(g, graphdata); |
195 | } |
196 | #endif |
197 | |
198 | void aginit(Agraph_t * g, int kind, char *rec_name, int arg_rec_size, int mtf) |
199 | { |
200 | Agnode_t *n; |
201 | Agedge_t *e; |
202 | Agraph_t *s; |
203 | unsigned int rec_size; |
204 | int recur; /* if recursive on subgraphs */ |
205 | |
206 | if (arg_rec_size < 0) |
207 | { |
208 | recur = 1; |
209 | } |
210 | else |
211 | { |
212 | recur = 0; |
213 | } |
214 | rec_size = (unsigned int) abs(arg_rec_size); |
215 | switch (kind) { |
216 | case AGRAPH: |
217 | agbindrec(g, rec_name, rec_size, mtf); |
218 | if (recur) |
219 | for (s = agfstsubg(g); s; s = agnxtsubg(s)) |
220 | aginit(s,kind,rec_name,arg_rec_size,mtf); |
221 | break; |
222 | case AGNODE: |
223 | case AGOUTEDGE: |
224 | case AGINEDGE: |
225 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) |
226 | if (kind == AGNODE) |
227 | agbindrec(n, rec_name, rec_size, mtf); |
228 | else { |
229 | for (e = agfstout(g, n); e; e = agnxtout(g, e)) |
230 | agbindrec(e, rec_name, rec_size, mtf); |
231 | } |
232 | break; |
233 | default: |
234 | break; |
235 | } |
236 | } |
237 | |
238 | void agclean(Agraph_t * g, int kind, char *rec_name) |
239 | { |
240 | Agnode_t *n; |
241 | Agedge_t *e; |
242 | |
243 | switch (kind) { |
244 | case AGRAPH: |
245 | agapply(g, (Agobj_t *) g, simple_delrec, rec_name, TRUE); |
246 | break; |
247 | case AGNODE: |
248 | case AGOUTEDGE: |
249 | case AGINEDGE: |
250 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) |
251 | if (kind == AGNODE) |
252 | agdelrec(n, rec_name); |
253 | else { |
254 | for (e = agfstout(g, n); e; e = agnxtout(g, e)) |
255 | agdelrec(e, rec_name); |
256 | } |
257 | break; |
258 | default: |
259 | break; |
260 | } |
261 | } |
262 | |
263 | void agrecclose(Agobj_t * obj) |
264 | { |
265 | Agraph_t *g; |
266 | Agrec_t *rec, *nrec; |
267 | |
268 | g = agraphof(obj); |
269 | if ((rec = obj->data)) { |
270 | do { |
271 | nrec = rec->next; |
272 | agstrfree(g, rec->name); |
273 | agfree(g, rec); |
274 | rec = nrec; |
275 | } while (rec != obj->data); |
276 | } |
277 | obj->data = NIL(Agrec_t *); |
278 | } |
279 | |