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 
agdelete(Agraph_t * g,void * obj)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 
agrename(Agobj_t * obj,char * newname)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 
agmethod_init(Agraph_t * g,void * obj)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 
aginitcb(Agraph_t * g,void * obj,Agcbstack_t * cbstack)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 
agmethod_upd(Agraph_t * g,void * obj,Agsym_t * sym)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 
agupdcb(Agraph_t * g,void * obj,Agsym_t * sym,Agcbstack_t * cbstack)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 
agmethod_delete(Agraph_t * g,void * obj)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 
agdelcb(Agraph_t * g,void * obj,Agcbstack_t * cbstack)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 
agroot(void * obj)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 
agraphof(void * obj)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 */
agpushdisc(Agraph_t * g,Agcbdisc_t * cbd,void * state)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 
agpopdisc(Agraph_t * g,Agcbdisc_t * cbd)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 
aggetuserptr(Agraph_t * g,Agcbdisc_t * cbd)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 
agcontains(Agraph_t * g,void * obj)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 
agobjkind(void * arg)266 int agobjkind(void *arg)
267 {
268 	return AGTYPE(arg);
269 }
270