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 
set_data(Agobj_t * obj,Agrec_t * data,int mtflock)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 */
aggetrec(void * obj,char * name,int mtf)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) */
objputrec(Agraph_t * g,Agobj_t * obj,void * arg)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  */
agbindrec(void * arg_obj,char * recname,unsigned int recsize,int mtf)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(?) */
objdelrec(Agraph_t * g,Agobj_t * obj,void * arg_rec)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 */
listdelrec(Agobj_t * obj,Agrec_t * rec)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 
agdelrec(void * arg_obj,char * name)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 
simple_delrec(Agraph_t * g,Agobj_t * obj,void * rec_name)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
agclean(Agraph_t * g,char * graphdata,char * nodedata,char * edgedata)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 
aginit(Agraph_t * g,int kind,char * rec_name,int arg_rec_size,int mtf)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 
agclean(Agraph_t * g,int kind,char * rec_name)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 
agrecclose(Agobj_t * obj)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