1 /* This file is part of GNU Radius.
2    Copyright (C) 2003,2004,2007,2008 Free Software Foundation
3 
4    GNU Radius is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8 
9    GNU Radius is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public
15    License along with GNU Radius; if not, write to the Free
16    Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17    Boston, MA 02110-1301 USA. */
18 
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22 #include <sys/types.h>
23 #include <stdlib.h>
24 #include <radius/radius.h>
25 #include <radius/list.h>
26 
27 struct grad_list_entry {
28 	struct grad_list_entry *next;
29 	void *data;
30 };
31 
32 struct grad_list {
33 	size_t count;
34 	struct grad_list_entry *head, *tail;
35 	struct grad_iterator *itr;
36 };
37 
38 struct grad_iterator {
39 	struct grad_iterator *next;
40 	grad_list_t *list;
41 	struct grad_list_entry *cur;
42 	int advanced;
43 };
44 
45 struct grad_list *
grad_list_create()46 grad_list_create()
47 {
48 	struct grad_list *p = grad_emalloc(sizeof(*p));
49 	p->head = p->tail = NULL;
50 	p->itr = NULL;
51 	return p;
52 }
53 
54 void
grad_list_destroy(struct grad_list ** plist,list_iterator_t user_free,void * data)55 grad_list_destroy(struct grad_list **plist, list_iterator_t user_free, void *data)
56 {
57 	struct grad_list_entry *p;
58 	struct grad_list *list;
59 
60 	if (!*plist)
61 		return;
62 
63 	list = *plist;
64 	*plist = NULL;
65 	p = list->head;
66 	while (p) {
67 		struct grad_list_entry *next = p->next;
68 		if (user_free)
69 			user_free(p->data, data);
70 		grad_free(p);
71 		p = next;
72 	}
73 	grad_free(list);
74 }
75 
76 void *
grad_iterator_current(grad_iterator_t * ip)77 grad_iterator_current(grad_iterator_t *ip)
78 {
79 	if (!ip)
80 		return NULL;
81 	return ip->cur ? ip->cur->data : NULL;
82 }
83 
84 static void
grad_iterator_attach(grad_iterator_t * itr,grad_list_t * list)85 grad_iterator_attach(grad_iterator_t *itr, grad_list_t *list)
86 {
87 	itr->list = list;
88 	itr->cur = NULL;
89 	itr->next = list->itr;
90 	itr->advanced = 0;
91 	list->itr = itr;
92 }
93 
94 static grad_iterator_t *
grad_iterator_detach(grad_iterator_t * iter)95 grad_iterator_detach(grad_iterator_t *iter)
96 {
97 	grad_iterator_t *cur, *prev;
98 
99 	for (cur = iter->list->itr, prev = NULL;
100 	     cur;
101 	     prev = cur, cur = cur->next)
102 		if (cur == iter)
103 			break;
104 
105 	if (cur) {
106 		if (prev)
107 			prev->next = cur->next;
108 		else
109 			cur->list->itr = cur->next;
110 	}
111 	return cur;
112 }
113 
114 grad_iterator_t *
grad_iterator_create(grad_list_t * list)115 grad_iterator_create(grad_list_t *list)
116 {
117 	grad_iterator_t *itr;
118 
119 	if (!list)
120 		return NULL;
121 	itr = grad_emalloc(sizeof(*itr));
122 	grad_iterator_attach(itr, list);
123 	return itr;
124 }
125 
126 void
grad_iterator_destroy(grad_iterator_t ** ip)127 grad_iterator_destroy(grad_iterator_t **ip)
128 {
129 	grad_iterator_t *itr;
130 
131 	if (!ip || !*ip)
132 		return;
133 	itr = grad_iterator_detach(*ip);
134 	if (itr)
135 		grad_free(itr);
136 	*ip = NULL;
137 }
138 
139 void *
grad_iterator_first(grad_iterator_t * ip)140 grad_iterator_first(grad_iterator_t *ip)
141 {
142 	if (!ip)
143 		return NULL;
144 	ip->cur = ip->list->head;
145 	ip->advanced = 0;
146 	return grad_iterator_current(ip);
147 }
148 
149 void *
grad_iterator_next(grad_iterator_t * ip)150 grad_iterator_next(grad_iterator_t *ip)
151 {
152 	if (!ip || !ip->cur)
153 		return NULL;
154 	if (!ip->advanced)
155 		ip->cur = ip->cur->next;
156 	ip->advanced = 0;
157 	return grad_iterator_current(ip);
158 }
159 
160 static void
_iterator_advance(grad_iterator_t * ip,struct grad_list_entry * e)161 _iterator_advance(grad_iterator_t *ip, struct grad_list_entry *e)
162 {
163 	for (; ip; ip = ip->next) {
164 		if (ip->cur == e) {
165 			ip->cur = e->next;
166 			ip->advanced++;
167 		}
168 	}
169 }
170 
171 void *
grad_list_item(struct grad_list * list,size_t n)172 grad_list_item(struct grad_list *list, size_t n)
173 {
174 	struct grad_list_entry *p;
175 	if (!list || n >= list->count)
176 		return NULL;
177 	for (p = list->head; n > 0 && p; p = p->next, n--)
178 		;
179 	return p->data;
180 }
181 
182 size_t
grad_list_count(struct grad_list * list)183 grad_list_count(struct grad_list *list)
184 {
185 	if (!list)
186 		return 0;
187 	return list->count;
188 }
189 
190 void
grad_list_append(struct grad_list * list,void * data)191 grad_list_append(struct grad_list *list, void *data)
192 {
193 	struct grad_list_entry *ep;
194 
195 	if (!list)
196 		return;
197 	ep = grad_emalloc(sizeof(*ep));
198 	ep->next = NULL;
199 	ep->data = data;
200 	if (list->tail)
201 		list->tail->next = ep;
202 	else
203 		list->head = ep;
204 	list->tail = ep;
205 	list->count++;
206 }
207 
208 void
grad_list_prepend(struct grad_list * list,void * data)209 grad_list_prepend(struct grad_list *list, void *data)
210 {
211 	struct grad_list_entry *ep;
212 
213 	if (!list)
214 		return;
215 	ep = grad_emalloc(sizeof(*ep));
216 	ep->data = data;
217 	ep->next = list->head;
218 	list->head = ep;
219 	if (!list->tail)
220 		list->tail = list->head;
221 	list->count++;
222 }
223 
224 static int
cmp_ptr(const void * a,const void * b)225 cmp_ptr(const void *a, const void *b)
226 {
227 	return a != b;
228 }
229 
230 void *
grad_list_remove(struct grad_list * list,void * data,list_comp_t cmp)231 grad_list_remove(struct grad_list *list, void *data, list_comp_t cmp)
232 {
233 	struct grad_list_entry *p, *prev;
234 
235 	if (!list)
236 		return NULL;
237 	if (!list->head)
238 		return NULL;
239 	if (!cmp)
240 		cmp = cmp_ptr;
241 	for (p = list->head, prev = NULL; p; prev = p, p = p->next)
242 		if (cmp(p->data, data) == 0)
243 			break;
244 
245 	if (!p)
246 		return 0;
247 	_iterator_advance(list->itr, p);
248 	if (p == list->head) {
249 		list->head = list->head->next;
250 		if (!list->head)
251 			list->tail = NULL;
252 	} else
253 		prev->next = p->next;
254 
255 	if (p == list->tail)
256 		list->tail = prev;
257 
258 	grad_free(p);
259 	list->count--;
260 
261 	return data;
262 }
263 
264 /* Note: if modifying this function, make sure it does not allocate any
265    memory! */
266 void
grad_list_iterate(struct grad_list * list,list_iterator_t func,void * data)267 grad_list_iterate(struct grad_list *list, list_iterator_t func, void *data)
268 {
269 	grad_iterator_t itr;
270 	void *p;
271 
272 	if (!list)
273 		return;
274 	grad_iterator_attach(&itr, list);
275 	for (p = grad_iterator_first(&itr); p; p = grad_iterator_next(&itr)) {
276 		if (func(p, data))
277 			break;
278 	}
279 	grad_iterator_detach(&itr);
280 }
281 
282 void *
grad_list_locate(struct grad_list * list,void * data,list_comp_t cmp)283 grad_list_locate(struct grad_list *list, void *data, list_comp_t cmp)
284 {
285 	struct grad_list_entry *cur;
286 	if (!list)
287 		return NULL;
288 	if (!cmp)
289 		cmp = cmp_ptr;
290 	for (cur = list->head; cur; cur = cur->next)
291 		if (cmp(cur->data, data) == 0)
292 			break;
293 	return cur ? cur->data : NULL;
294 }
295 
296 int
grad_list_insert_sorted(struct grad_list * list,void * data,list_comp_t cmp)297 grad_list_insert_sorted(struct grad_list *list, void *data, list_comp_t cmp)
298 {
299 	struct grad_list_entry *cur, *prev;
300 
301 	if (!list)
302 		return -1;
303 	if (!cmp)
304 		return -1;
305 
306 	for (cur = list->head, prev = NULL; cur; prev = cur, cur = cur->next)
307 		if (cmp(cur->data, data) > 0)
308 			break;
309 
310 	if (!prev) {
311 		grad_list_prepend(list, data);
312 	} else if (!cur) {
313 		grad_list_append(list, data);
314 	} else {
315 		struct grad_list_entry *ep = grad_emalloc(sizeof(*ep));
316 		ep->data = data;
317 		ep->next = cur;
318 		list->count++;
319 		prev->next = ep;
320 	}
321 	return 0;
322 }
323 
324