1 /*======================================================================
2 FILE: pvl.c
3 CREATOR: eric November, 1995
4
5
6 (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org>
7 http://www.softwarestudio.org
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of either:
11
12 The LGPL as published by the Free Software Foundation, version
13 2.1, available at: http://www.fsf.org/copyleft/lesser.html
14
15 Or:
16
17 The Mozilla Public License Version 1.0. You may obtain a copy of
18 the License at http://www.mozilla.org/MPL/
19
20 ======================================================================*/
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "pvl.h"
27 #include <errno.h>
28 #include <assert.h>
29 #include <stdlib.h>
30
31 /**
32 struct pvl_list_t
33
34 The list structure. This is the hanlde for the entire list
35
36 This type is also private. Use pvl_list instead
37
38 */
39
40 typedef struct pvl_list_t
41 {
42 int MAGIC; /**< Magic Identifier */
43 struct pvl_elem_t *head; /**< Head of list */
44 struct pvl_elem_t *tail; /**< Tail of list */
45 int count; /**< Number of items in the list */
46 struct pvl_elem_t *p; /**< Pointer used for iterators */
47 } pvl_list_t;
48
49
50
51
52 /**
53 * This global is incremented for each call to pvl_new_element(); it gives each
54 * list a unique identifer
55 */
56
57 int pvl_elem_count = 0;
58 int pvl_list_count = 0;
59
60
61 /**
62 * @brief Creates a new list, clears the pointers and assigns a magic number
63 *
64 * @return Pointer to the new list, 0 if there is no available memory.
65 */
66
67 pvl_list
pvl_newlist()68 pvl_newlist()
69 {
70 struct pvl_list_t *L;
71
72 if ( ( L = (struct pvl_list_t*)malloc(sizeof(struct pvl_list_t))) == 0)
73 {
74 errno = ENOMEM;
75 return 0;
76 }
77
78 L->MAGIC = pvl_list_count;
79 pvl_list_count++;
80 L->head = 0;
81 L->tail = 0;
82 L->count = 0;
83 L->p = 0;
84
85 return L;
86 }
87
88 void
pvl_free(pvl_list l)89 pvl_free(pvl_list l)
90 {
91 struct pvl_list_t *L = (struct pvl_list_t *)l;
92
93 pvl_clear(l);
94
95 free(L);
96 }
97
98 /**
99 * @brief Creates a new list element, assigns a magic number, and assigns
100 * the next and previous pointers.
101 *
102 * Passing in the next and previous points may seem odd, but it allos the user
103 * to set them while keeping the internal data hidden. In nearly all cases,
104 * the user is the pvl library itself.
105 *
106 * @param d The data item to be stored in the list
107 * @param next Pointer value to assign to the member "next"
108 * @param prior Pointer value to assign to the member "prior"
109 *
110 * @return A pointer to the new element, 0 if there is no memory available.
111 */
112
113 pvl_elem
pvl_new_element(void * d,pvl_elem next,pvl_elem prior)114 pvl_new_element(void *d, pvl_elem next, pvl_elem prior)
115 {
116 struct pvl_elem_t *E;
117
118 if ( ( E = (struct pvl_elem_t*)malloc(sizeof(struct pvl_elem_t))) == 0)
119 {
120 errno = ENOMEM;
121 return 0;
122 }
123
124 E->MAGIC = pvl_elem_count++;
125 E->d = d;
126 E->next = next;
127 E->prior = prior;
128
129 return (pvl_elem)E;
130 }
131
132 /**
133 * @brief Add a new element to the from of the list
134 *
135 * @param L The list to add the item to
136 * @param d Pointer to the item to add
137 */
138
139 void
pvl_unshift(pvl_list L,void * d)140 pvl_unshift(pvl_list L,void *d)
141 {
142 struct pvl_elem_t *E = pvl_new_element(d,L->head,0);
143
144 if (E->next != 0)
145 {
146 /* Link the head node to it */
147 E->next->prior = E;
148 }
149
150 /* move the head */
151 L->head = E;
152
153 /* maybe move the tail */
154
155 if (L->tail == 0)
156 {
157 L->tail = E;
158 }
159
160 L->count++;
161 }
162
163 /**
164 * @brief Remove an element from the front of the list
165 *
166 * @param L The list to operate on
167 *
168 * @return the entry on the front of the list
169 */
170
171 void*
pvl_shift(pvl_list L)172 pvl_shift(pvl_list L)
173 {
174 if (L->head == 0)
175 {
176 return 0;
177 }
178
179 return pvl_remove(L,(void*)L->head);
180
181 }
182
183 /**
184 * @brief Add a new item to the tail of the list
185 *
186 * @param L The list to operate on
187 * @param d Pointer to the item to add
188 *
189 */
190
191 void
pvl_push(pvl_list L,void * d)192 pvl_push(pvl_list L,void *d)
193 {
194 struct pvl_elem_t *E = pvl_new_element(d,0,L->tail);
195
196 /* These are done in pvl_new_element
197 E->next = 0;
198 E->prior = L->tail;
199 */
200
201 if (L->tail != 0)
202 {
203 L->tail->next = E;
204 }
205
206 if (L->head == 0)
207 {
208 L->head = E;
209 }
210
211 L->tail = E;
212
213 L->count++;
214
215 }
216
217 /**
218 * @brief Remove an element from the tail of the list
219 *
220 * @param L The list to operate on
221 */
222
223 void*
pvl_pop(pvl_list L)224 pvl_pop(pvl_list L)
225 {
226 if ( L->tail == 0)
227 {
228 return 0;
229 }
230
231 return pvl_remove(L,(void*) L->tail);;
232
233 }
234
235
236 /**
237 * Add a new item to a list that is ordered by a comparison function.
238 * This routine assumes that the list is properly ordered.
239 *
240 * @param L The list to operate on
241 * @param f Pointer to a comparison function
242 * @param d Pointer to data to pass to the comparison function
243 */
244
245 void
pvl_insert_ordered(pvl_list L,pvl_comparef f,void * d)246 pvl_insert_ordered(pvl_list L,pvl_comparef f,void *d)
247 {
248 struct pvl_elem_t *P;
249
250 L->count++;
251
252 /* Empty list, add to head */
253
254 if(L->head == 0)
255 {
256 pvl_unshift(L,d);
257 return;
258 }
259
260 /* smaller than head, add to head */
261
262 if ( ((*f)(d,L->head->d)) <= 0)
263 {
264 pvl_unshift(L,d);
265 return;
266 }
267
268 /* larger than tail, add to tail */
269 if ( (*f)(d,L->tail->d) >= 0)
270 {
271 pvl_push(L,d);
272 return;
273 }
274
275
276 /* Search for the first element that is smaller, and add before it */
277
278 for (P=L->head; P != 0; P = P->next)
279 {
280 if ( (*f)(P->d,d) >= 0)
281 {
282 pvl_insert_before(L,P,d);
283 return;
284 }
285 }
286
287 /* badness, choke */
288 #ifndef lint
289 assert(0);
290 #endif
291 }
292
293 /**
294 * @brief Add a new item after the referenced element.
295 * @param L The list to operate on
296 * @param P The list element to add the item after
297 * @param d Pointer to the item to add.
298 */
299
300 void
pvl_insert_after(pvl_list L,pvl_elem P,void * d)301 pvl_insert_after(pvl_list L,pvl_elem P,void *d)
302 {
303 struct pvl_elem_t *E = 0;
304
305 L->count++;
306
307 if (P == 0)
308 {
309 pvl_unshift(L,d);
310 return;
311 }
312
313 if ( P == L->tail)
314 {
315 E = pvl_new_element(d,0,P);
316 L->tail = E;
317 E->prior->next = E;
318 }
319 else
320 {
321 E = pvl_new_element(d,P->next,P);
322 E->next->prior = E;
323 E->prior->next = E;
324 }
325 }
326
327 /**
328 * @brief Add an item after a referenced item
329 *
330 * @param L The list to operate on
331 * @param P The list element to add the item before
332 * @param d Pointer to the data to be added.
333 */
334
335 void
pvl_insert_before(pvl_list L,pvl_elem P,void * d)336 pvl_insert_before(pvl_list L,pvl_elem P,void *d)
337 {
338 struct pvl_elem_t *E = 0;
339
340 L->count++;
341
342 if (P == 0)
343 {
344 pvl_unshift(L,d);
345 return;
346 }
347
348 if ( P == L->head)
349 {
350 E = pvl_new_element(d,P,0);
351 E->next->prior = E;
352 L->head = E;
353 }
354 else
355 {
356 E = pvl_new_element(d,P,P->prior);
357 E->prior->next = E;
358 E->next->prior = E;
359 }
360 }
361
362 /**
363 * @brief Remove the referenced item from the list.
364 *
365 * This routine will free the element, but not the data item that the
366 * element contains.
367 *
368 * @param L The list to operate on
369 * @param E The element to remove.
370 */
371
372 void*
pvl_remove(pvl_list L,pvl_elem E)373 pvl_remove(pvl_list L,pvl_elem E)
374 {
375 void* data;
376
377 if (E == L->head)
378 {
379 if (E->next != 0)
380 {
381 E->next->prior = 0;
382 L->head = E->next;
383 } else {
384 /* E Also points to tail -> only one element in list */
385 L->tail = 0;
386 L->head = 0;
387 }
388 }
389 else if (E == L->tail)
390 {
391 if (E->prior != 0)
392 {
393 E->prior->next = 0;
394 L->tail = E->prior;
395 } else {
396 /* E points to the head, so it was the last element */
397 /* This case should be taken care of in the previous clause */
398 L->head = 0;
399 L->tail = 0;
400 }
401 }
402 else
403 {
404 E->prior->next = E->next;
405 E->next->prior = E->prior;
406 }
407
408
409 L->count--;
410
411 data = E->d;
412
413 E->prior = 0;
414 E->next = 0;
415 E->d = 0;
416
417 free(E);
418
419 return data;
420
421 }
422
423 /**
424 * @brief Return a pointer to data that satisfies a function.
425 *
426 * This routine will interate through the entire list and call the
427 * find function for each item. It will break and return a pointer to the
428 * data that causes the find function to return 1.
429 *
430 * @param l The list to operate on
431 * @param f Pointer to the find function
432 * @param v Pointer to constant data to pass into the function
433 *
434 * @return Pointer to the element that the find function found.
435 */
436
437 pvl_elem
pvl_find(pvl_list l,pvl_findf f,void * v)438 pvl_find(pvl_list l,pvl_findf f,void* v)
439 {
440 pvl_elem e;
441
442 for (e=pvl_head(l); e!= 0; e = pvl_next(e))
443 {
444 if ( (*f)(((struct pvl_elem_t *)e)->d,v) == 1)
445 {
446 /* Save this elem for a call to find_next */
447 ((struct pvl_list_t *)l)->p = e;
448 return e;
449 }
450 }
451
452 return 0;
453
454 }
455
456 /**
457 * @brief Like pvl_find(), but continues the search where the last find() or
458 * find_next() left off.
459 *
460 * @param l The list to operate on
461 * @param f Pointer to the find function
462 * @param v Pointer to constant data to pass into the function
463 *
464 * @return Pointer to the element that the find function found.
465 */
466
467 pvl_elem
pvl_find_next(pvl_list l,pvl_findf f,void * v)468 pvl_find_next(pvl_list l,pvl_findf f,void* v)
469 {
470
471 pvl_elem e;
472
473 for (e=pvl_head(l); e!= 0; e = pvl_next(e))
474 {
475 if ( (*f)(((struct pvl_elem_t *)e)->d,v) == 1)
476 {
477 /* Save this elem for a call to find_next */
478 ((struct pvl_list_t *)l)->p = e;
479 return e;
480 }
481 }
482
483 return 0;
484
485 }
486
487 /**
488 * @brief Remove the all the elements in the list. The does not free
489 * the data items the elements hold.
490 */
491
492 void
pvl_clear(pvl_list l)493 pvl_clear(pvl_list l)
494 {
495 pvl_elem e = pvl_head(l);
496 pvl_elem next;
497
498 if (e == 0) {
499 return;
500 }
501
502 while(e != 0)
503 {
504 next = pvl_next(e);
505 pvl_remove(l,e);
506 e = next;
507 }
508 }
509
510
511 /**
512 * @brief Returns the number of items in the list.
513 */
514
515 int
pvl_count(pvl_list L)516 pvl_count(pvl_list L)
517 {
518 return L->count;
519 }
520
521
522 /**
523 * @brief Returns a pointer to the given element
524 */
525
526 pvl_elem
pvl_next(pvl_elem E)527 pvl_next(pvl_elem E)
528 {
529 if (E == 0){
530 return 0;
531 }
532
533 return (pvl_elem)E->next;
534 }
535
536
537 /**
538 * @brief Returns a pointer to the element previous to the element given.
539 */
540
541 pvl_elem
pvl_prior(pvl_elem E)542 pvl_prior(pvl_elem E)
543 {
544 return (pvl_elem)E->prior;
545 }
546
547
548 /**
549 * @brief Returns a pointer to the first item in the list.
550 */
551
552 pvl_elem
pvl_head(pvl_list L)553 pvl_head(pvl_list L )
554 {
555 return (pvl_elem)L->head;
556 }
557
558 /**
559 * @brief Returns a pointer to the last item in the list.
560 */
561 pvl_elem
pvl_tail(pvl_list L)562 pvl_tail(pvl_list L)
563 {
564 return (pvl_elem)L->tail;
565 }
566
567 #ifndef PVL_USE_MACROS
568 void*
pvl_data(pvl_elem E)569 pvl_data(pvl_elem E)
570 {
571 if ( E == 0){
572 return 0;
573 }
574
575 return E->d;
576 }
577 #endif
578
579 /**
580 * @brief Call a function for every item in the list.
581 *
582 * @param l The list to operate on
583 * @param f Pointer to the function to call
584 * @param v Data to pass to the function on every iteration
585 */
586
587 void
pvl_apply(pvl_list l,pvl_applyf f,void * v)588 pvl_apply(pvl_list l,pvl_applyf f, void *v)
589 {
590 pvl_elem e;
591
592 for (e=pvl_head(l); e!= 0; e = pvl_next(e))
593 {
594 (*f)(((struct pvl_elem_t *)e)->d,v);
595 }
596
597 }
598