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
16int 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
37int 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
76void 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
84void 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
107void 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
115void 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
138void 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
146void 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
169Agraph_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
187Agraph_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 */
204void 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
215int 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
237void *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
247int 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
266int agobjkind(void *arg)
267{
268 return AGTYPE(arg);
269}
270