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 | int agdelete(Agraph_t * g, void *obj) |
17 | { |
18 | if ((AGTYPE((Agobj_t *) obj) == AGRAPH) && (g != agparent(obj))) { |
19 | agerr(AGERR, "agdelete on wrong graph" ); |
20 | return FAILURE; |
21 | } |
22 | |
23 | switch (AGTYPE((Agobj_t *) obj)) { |
24 | case AGNODE: |
25 | return agdelnode(g, obj); |
26 | case AGINEDGE: |
27 | case AGOUTEDGE: |
28 | return agdeledge(g, obj); |
29 | case AGRAPH: |
30 | return agclose(obj); |
31 | default: |
32 | agerr(AGERR, "agdelete on bad object" ); |
33 | } |
34 | return SUCCESS; /* not reached */ |
35 | } |
36 | |
37 | int agrename(Agobj_t * obj, char *newname) |
38 | { |
39 | Agraph_t *g; |
40 | IDTYPE old_id, new_id; |
41 | |
42 | switch (AGTYPE(obj)) { |
43 | case AGRAPH: |
44 | old_id = AGID(obj); |
45 | g = agraphof(obj); |
46 | /* can we reserve the id corresponding to newname? */ |
47 | if (agmapnametoid(agroot(g), AGTYPE(obj), newname, |
48 | &new_id, FALSE) == 0) |
49 | return FAILURE; |
50 | if (new_id == old_id) |
51 | return SUCCESS; |
52 | if (agmapnametoid(agroot(g), AGTYPE(obj), newname, |
53 | &new_id, TRUE) == 0) |
54 | return FAILURE; |
55 | /* obj* is unchanged, so no need to re agregister() */ |
56 | if (agparent(g) && agidsubg(agparent(g), new_id, 0)) |
57 | return FAILURE; |
58 | agfreeid(g, AGRAPH, old_id); |
59 | AGID(g) = new_id; |
60 | break; |
61 | case AGNODE: |
62 | return agrelabel_node((Agnode_t *) obj, newname); |
63 | agrename(obj, newname); |
64 | break; |
65 | case AGINEDGE: |
66 | case AGOUTEDGE: |
67 | return FAILURE; |
68 | } |
69 | return SUCCESS; |
70 | } |
71 | |
72 | /* perform initialization/update/finalization method invocation. |
73 | * skip over nil pointers to next method below. |
74 | */ |
75 | |
76 | void agmethod_init(Agraph_t * g, void *obj) |
77 | { |
78 | if (g->clos->callbacks_enabled) |
79 | aginitcb(g, obj, g->clos->cb); |
80 | else |
81 | agrecord_callback(g, obj, CB_INITIALIZE, NILsym); |
82 | } |
83 | |
84 | void aginitcb(Agraph_t * g, void *obj, Agcbstack_t * cbstack) |
85 | { |
86 | agobjfn_t fn; |
87 | |
88 | if (cbstack == NIL(Agcbstack_t *)) |
89 | return; |
90 | aginitcb(g, obj, cbstack->prev); |
91 | fn = NIL(agobjfn_t); |
92 | switch (AGTYPE(obj)) { |
93 | case AGRAPH: |
94 | fn = cbstack->f->graph.ins; |
95 | break; |
96 | case AGNODE: |
97 | fn = cbstack->f->node.ins; |
98 | break; |
99 | case AGEDGE: |
100 | fn = cbstack->f->edge.ins; |
101 | break; |
102 | } |
103 | if (fn) |
104 | fn(g, obj, cbstack->state); |
105 | } |
106 | |
107 | void agmethod_upd(Agraph_t * g, void *obj, Agsym_t * sym) |
108 | { |
109 | if (g->clos->callbacks_enabled) |
110 | agupdcb(g, obj, sym, g->clos->cb); |
111 | else |
112 | agrecord_callback(g, obj, CB_UPDATE, sym); |
113 | } |
114 | |
115 | void agupdcb(Agraph_t * g, void *obj, Agsym_t * sym, Agcbstack_t * cbstack) |
116 | { |
117 | agobjupdfn_t fn; |
118 | |
119 | if (cbstack == NIL(Agcbstack_t *)) |
120 | return; |
121 | agupdcb(g, obj, sym, cbstack->prev); |
122 | fn = NIL(agobjupdfn_t); |
123 | switch (AGTYPE(obj)) { |
124 | case AGRAPH: |
125 | fn = cbstack->f->graph.mod; |
126 | break; |
127 | case AGNODE: |
128 | fn = cbstack->f->node.mod; |
129 | break; |
130 | case AGEDGE: |
131 | fn = cbstack->f->edge.mod; |
132 | break; |
133 | } |
134 | if (fn) |
135 | fn(g, obj, cbstack->state, sym); |
136 | } |
137 | |
138 | void agmethod_delete(Agraph_t * g, void *obj) |
139 | { |
140 | if (g->clos->callbacks_enabled) |
141 | agdelcb(g, obj, g->clos->cb); |
142 | else |
143 | agrecord_callback(g, obj, CB_DELETION, NILsym); |
144 | } |
145 | |
146 | void agdelcb(Agraph_t * g, void *obj, Agcbstack_t * cbstack) |
147 | { |
148 | agobjfn_t fn; |
149 | |
150 | if (cbstack == NIL(Agcbstack_t *)) |
151 | return; |
152 | agdelcb(g, obj, cbstack->prev); |
153 | fn = NIL(agobjfn_t); |
154 | switch (AGTYPE(obj)) { |
155 | case AGRAPH: |
156 | fn = cbstack->f->graph.del; |
157 | break; |
158 | case AGNODE: |
159 | fn = cbstack->f->node.del; |
160 | break; |
161 | case AGEDGE: |
162 | fn = cbstack->f->edge.del; |
163 | break; |
164 | } |
165 | if (fn) |
166 | fn(g, obj, cbstack->state); |
167 | } |
168 | |
169 | Agraph_t *agroot(void* obj) |
170 | { |
171 | // fixes CVE-2019-11023 by moving the problem to the caller :-) |
172 | if (obj == 0) return NILgraph; |
173 | switch (AGTYPE(obj)) { |
174 | case AGINEDGE: |
175 | case AGOUTEDGE: |
176 | return ((Agedge_t *) obj)->node->root; |
177 | case AGNODE: |
178 | return ((Agnode_t *) obj)->root; |
179 | case AGRAPH: |
180 | return ((Agraph_t *) obj)->root; |
181 | default: /* actually can't occur if only 2 bit tags */ |
182 | agerr(AGERR, "agroot of a bad object" ); |
183 | return NILgraph; |
184 | } |
185 | } |
186 | |
187 | Agraph_t *agraphof(void *obj) |
188 | { |
189 | switch (AGTYPE(obj)) { |
190 | case AGINEDGE: |
191 | case AGOUTEDGE: |
192 | return ((Agedge_t *) obj)->node->root; |
193 | case AGNODE: |
194 | return ((Agnode_t *) obj)->root; |
195 | case AGRAPH: |
196 | return (Agraph_t *) obj; |
197 | default: /* actually can't occur if only 2 bit tags */ |
198 | agerr(AGERR, "agraphof a bad object" ); |
199 | return NILgraph; |
200 | } |
201 | } |
202 | |
203 | /* to manage disciplines */ |
204 | void agpushdisc(Agraph_t * g, Agcbdisc_t * cbd, void *state) |
205 | { |
206 | Agcbstack_t *stack_ent; |
207 | |
208 | stack_ent = AGNEW(g, Agcbstack_t); |
209 | stack_ent->f = cbd; |
210 | stack_ent->state = state; |
211 | stack_ent->prev = g->clos->cb; |
212 | g->clos->cb = stack_ent; |
213 | } |
214 | |
215 | int agpopdisc(Agraph_t * g, Agcbdisc_t * cbd) |
216 | { |
217 | Agcbstack_t *stack_ent; |
218 | |
219 | stack_ent = g->clos->cb; |
220 | if (stack_ent) { |
221 | if (stack_ent->f == cbd) |
222 | g->clos->cb = stack_ent->prev; |
223 | else { |
224 | while (stack_ent && (stack_ent->prev->f != cbd)) |
225 | stack_ent = stack_ent->prev; |
226 | if (stack_ent && stack_ent->prev) |
227 | stack_ent->prev = stack_ent->prev->prev; |
228 | } |
229 | if (stack_ent) { |
230 | agfree(g, stack_ent); |
231 | return SUCCESS; |
232 | } |
233 | } |
234 | return FAILURE; |
235 | } |
236 | |
237 | void *aggetuserptr(Agraph_t * g, Agcbdisc_t * cbd) |
238 | { |
239 | Agcbstack_t *stack_ent; |
240 | |
241 | for (stack_ent = g->clos->cb; stack_ent; stack_ent = stack_ent->prev) |
242 | if (stack_ent->f == cbd) |
243 | return stack_ent->state; |
244 | return NIL(void *); |
245 | } |
246 | |
247 | int agcontains(Agraph_t* g, void* obj) |
248 | { |
249 | Agraph_t* subg; |
250 | |
251 | if (agroot (g) != agroot (obj)) return 0; |
252 | switch (AGTYPE(obj)) { |
253 | case AGRAPH: |
254 | subg = (Agraph_t *) obj; |
255 | do { |
256 | if (subg == g) return 1; |
257 | } while ((subg = agparent (subg))); |
258 | return 0; |
259 | case AGNODE: |
260 | return (agidnode(g, AGID(obj), 0) != 0); |
261 | default: |
262 | return (agsubedge(g, (Agedge_t *) obj, 0) != 0); |
263 | } |
264 | } |
265 | |
266 | int agobjkind(void *arg) |
267 | { |
268 | return AGTYPE(arg); |
269 | } |
270 | |