1 /* libxml2 - Library for parsing XML documents
2  * Copyright (C) 2006-2019 Free Software Foundation, Inc.
3  *
4  * This file is not part of the GNU gettext program, but is used with
5  * GNU gettext.
6  *
7  * The original copyright notice is as follows:
8  */
9 
10 /*
11  * Copyright (C) 2000 Gary Pennington and Daniel Veillard.
12  *
13  * Permission to use, copy, modify, and distribute this software for any
14  * purpose with or without fee is hereby granted, provided that the above
15  * copyright notice and this permission notice appear in all copies.
16  *
17  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19  * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
20  * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
21  *
22  * Author: Gary.Pennington@uk.sun.com
23  */
24 
25 /*
26  * list.c: lists handling implementation
27  */
28 
29 #define IN_LIBXML
30 #include "libxml.h"
31 
32 #include <stdlib.h>
33 #include <string.h>
34 #include <libxml/xmlmemory.h>
35 #include <libxml/list.h>
36 #include <libxml/globals.h>
37 
38 /*
39  * Type definition are kept internal
40  */
41 
42 struct _xmlLink
43 {
44     struct _xmlLink *next;
45     struct _xmlLink *prev;
46     void *data;
47 };
48 
49 struct _xmlList
50 {
51     xmlLinkPtr sentinel;
52     void (*linkDeallocator)(xmlLinkPtr );
53     int (*linkCompare)(const void *, const void*);
54 };
55 
56 /************************************************************************
57  *                                    *
58  *                Interfaces                *
59  *                                    *
60  ************************************************************************/
61 
62 /**
63  * xmlLinkDeallocator:
64  * @l:  a list
65  * @lk:  a link
66  *
67  * Unlink and deallocate @lk from list @l
68  */
69 static void
xmlLinkDeallocator(xmlListPtr l,xmlLinkPtr lk)70 xmlLinkDeallocator(xmlListPtr l, xmlLinkPtr lk)
71 {
72     (lk->prev)->next = lk->next;
73     (lk->next)->prev = lk->prev;
74     if(l->linkDeallocator)
75         l->linkDeallocator(lk);
76     xmlFree(lk);
77 }
78 
79 /**
80  * xmlLinkCompare:
81  * @data0:  first data
82  * @data1:  second data
83  *
84  * Compares two arbitrary data
85  *
86  * Returns -1, 0 or 1 depending on whether data1 is greater equal or smaller
87  *          than data0
88  */
89 static int
xmlLinkCompare(const void * data0,const void * data1)90 xmlLinkCompare(const void *data0, const void *data1)
91 {
92     if (data0 < data1)
93         return (-1);
94     else if (data0 == data1)
95 	return (0);
96     return (1);
97 }
98 
99 /**
100  * xmlListLowerSearch:
101  * @l:  a list
102  * @data:  a data
103  *
104  * Search data in the ordered list walking from the beginning
105  *
106  * Returns the link containing the data or NULL
107  */
108 static xmlLinkPtr
xmlListLowerSearch(xmlListPtr l,void * data)109 xmlListLowerSearch(xmlListPtr l, void *data)
110 {
111     xmlLinkPtr lk;
112 
113     if (l == NULL)
114         return(NULL);
115     for(lk = l->sentinel->next;lk != l->sentinel && l->linkCompare(lk->data, data) <0 ;lk = lk->next);
116     return lk;
117 }
118 
119 /**
120  * xmlListHigherSearch:
121  * @l:  a list
122  * @data:  a data
123  *
124  * Search data in the ordered list walking backward from the end
125  *
126  * Returns the link containing the data or NULL
127  */
128 static xmlLinkPtr
xmlListHigherSearch(xmlListPtr l,void * data)129 xmlListHigherSearch(xmlListPtr l, void *data)
130 {
131     xmlLinkPtr lk;
132 
133     if (l == NULL)
134         return(NULL);
135     for(lk = l->sentinel->prev;lk != l->sentinel && l->linkCompare(lk->data, data) >0 ;lk = lk->prev);
136     return lk;
137 }
138 
139 /**
140  * xmlListSearch:
141  * @l:  a list
142  * @data:  a data
143  *
144  * Search data in the list
145  *
146  * Returns the link containing the data or NULL
147  */
148 static xmlLinkPtr
xmlListLinkSearch(xmlListPtr l,void * data)149 xmlListLinkSearch(xmlListPtr l, void *data)
150 {
151     xmlLinkPtr lk;
152     if (l == NULL)
153         return(NULL);
154     lk = xmlListLowerSearch(l, data);
155     if (lk == l->sentinel)
156         return NULL;
157     else {
158         if (l->linkCompare(lk->data, data) ==0)
159             return lk;
160         return NULL;
161     }
162 }
163 
164 /**
165  * xmlListLinkReverseSearch:
166  * @l:  a list
167  * @data:  a data
168  *
169  * Search data in the list processing backward
170  *
171  * Returns the link containing the data or NULL
172  */
173 static xmlLinkPtr
xmlListLinkReverseSearch(xmlListPtr l,void * data)174 xmlListLinkReverseSearch(xmlListPtr l, void *data)
175 {
176     xmlLinkPtr lk;
177     if (l == NULL)
178         return(NULL);
179     lk = xmlListHigherSearch(l, data);
180     if (lk == l->sentinel)
181         return NULL;
182     else {
183         if (l->linkCompare(lk->data, data) ==0)
184             return lk;
185         return NULL;
186     }
187 }
188 
189 /**
190  * xmlListCreate:
191  * @deallocator:  an optional deallocator function
192  * @compare:  an optional comparison function
193  *
194  * Create a new list
195  *
196  * Returns the new list or NULL in case of error
197  */
198 xmlListPtr
xmlListCreate(xmlListDeallocator deallocator,xmlListDataCompare compare)199 xmlListCreate(xmlListDeallocator deallocator, xmlListDataCompare compare)
200 {
201     xmlListPtr l;
202     if (NULL == (l = (xmlListPtr )xmlMalloc( sizeof(xmlList)))) {
203         xmlGenericError(xmlGenericErrorContext,
204 		        "Cannot initialize memory for list");
205         return (NULL);
206     }
207     /* Initialize the list to NULL */
208     memset(l, 0, sizeof(xmlList));
209 
210     /* Add the sentinel */
211     if (NULL ==(l->sentinel = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {
212         xmlGenericError(xmlGenericErrorContext,
213 		        "Cannot initialize memory for sentinel");
214 	xmlFree(l);
215         return (NULL);
216     }
217     l->sentinel->next = l->sentinel;
218     l->sentinel->prev = l->sentinel;
219     l->sentinel->data = NULL;
220 
221     /* If there is a link deallocator, use it */
222     if (deallocator != NULL)
223         l->linkDeallocator = deallocator;
224     /* If there is a link comparator, use it */
225     if (compare != NULL)
226         l->linkCompare = compare;
227     else /* Use our own */
228         l->linkCompare = xmlLinkCompare;
229     return l;
230 }
231 
232 /**
233  * xmlListSearch:
234  * @l:  a list
235  * @data:  a search value
236  *
237  * Search the list for an existing value of @data
238  *
239  * Returns the value associated to @data or NULL in case of error
240  */
241 void *
xmlListSearch(xmlListPtr l,void * data)242 xmlListSearch(xmlListPtr l, void *data)
243 {
244     xmlLinkPtr lk;
245     if (l == NULL)
246         return(NULL);
247     lk = xmlListLinkSearch(l, data);
248     if (lk)
249         return (lk->data);
250     return NULL;
251 }
252 
253 /**
254  * xmlListReverseSearch:
255  * @l:  a list
256  * @data:  a search value
257  *
258  * Search the list in reverse order for an existing value of @data
259  *
260  * Returns the value associated to @data or NULL in case of error
261  */
262 void *
xmlListReverseSearch(xmlListPtr l,void * data)263 xmlListReverseSearch(xmlListPtr l, void *data)
264 {
265     xmlLinkPtr lk;
266     if (l == NULL)
267         return(NULL);
268     lk = xmlListLinkReverseSearch(l, data);
269     if (lk)
270         return (lk->data);
271     return NULL;
272 }
273 
274 /**
275  * xmlListInsert:
276  * @l:  a list
277  * @data:  the data
278  *
279  * Insert data in the ordered list at the beginning for this value
280  *
281  * Returns 0 in case of success, 1 in case of failure
282  */
283 int
xmlListInsert(xmlListPtr l,void * data)284 xmlListInsert(xmlListPtr l, void *data)
285 {
286     xmlLinkPtr lkPlace, lkNew;
287 
288     if (l == NULL)
289         return(1);
290     lkPlace = xmlListLowerSearch(l, data);
291     /* Add the new link */
292     lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
293     if (lkNew == NULL) {
294         xmlGenericError(xmlGenericErrorContext,
295 		        "Cannot initialize memory for new link");
296         return (1);
297     }
298     lkNew->data = data;
299     lkPlace = lkPlace->prev;
300     lkNew->next = lkPlace->next;
301     (lkPlace->next)->prev = lkNew;
302     lkPlace->next = lkNew;
303     lkNew->prev = lkPlace;
304     return 0;
305 }
306 
307 /**
308  * xmlListAppend:
309  * @l:  a list
310  * @data:  the data
311  *
312  * Insert data in the ordered list at the end for this value
313  *
314  * Returns 0 in case of success, 1 in case of failure
315  */
xmlListAppend(xmlListPtr l,void * data)316 int xmlListAppend(xmlListPtr l, void *data)
317 {
318     xmlLinkPtr lkPlace, lkNew;
319 
320     if (l == NULL)
321         return(1);
322     lkPlace = xmlListHigherSearch(l, data);
323     /* Add the new link */
324     lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
325     if (lkNew == NULL) {
326         xmlGenericError(xmlGenericErrorContext,
327 		        "Cannot initialize memory for new link");
328         return (1);
329     }
330     lkNew->data = data;
331     lkNew->next = lkPlace->next;
332     (lkPlace->next)->prev = lkNew;
333     lkPlace->next = lkNew;
334     lkNew->prev = lkPlace;
335     return 0;
336 }
337 
338 /**
339  * xmlListDelete:
340  * @l:  a list
341  *
342  * Deletes the list and its associated data
343  */
xmlListDelete(xmlListPtr l)344 void xmlListDelete(xmlListPtr l)
345 {
346     if (l == NULL)
347         return;
348 
349     xmlListClear(l);
350     xmlFree(l->sentinel);
351     xmlFree(l);
352 }
353 
354 /**
355  * xmlListRemoveFirst:
356  * @l:  a list
357  * @data:  list data
358  *
359  * Remove the first instance associated to data in the list
360  *
361  * Returns 1 if a deallocation occurred, or 0 if not found
362  */
363 int
xmlListRemoveFirst(xmlListPtr l,void * data)364 xmlListRemoveFirst(xmlListPtr l, void *data)
365 {
366     xmlLinkPtr lk;
367 
368     if (l == NULL)
369         return(0);
370     /*Find the first instance of this data */
371     lk = xmlListLinkSearch(l, data);
372     if (lk != NULL) {
373         xmlLinkDeallocator(l, lk);
374         return 1;
375     }
376     return 0;
377 }
378 
379 /**
380  * xmlListRemoveLast:
381  * @l:  a list
382  * @data:  list data
383  *
384  * Remove the last instance associated to data in the list
385  *
386  * Returns 1 if a deallocation occurred, or 0 if not found
387  */
388 int
xmlListRemoveLast(xmlListPtr l,void * data)389 xmlListRemoveLast(xmlListPtr l, void *data)
390 {
391     xmlLinkPtr lk;
392 
393     if (l == NULL)
394         return(0);
395     /*Find the last instance of this data */
396     lk = xmlListLinkReverseSearch(l, data);
397     if (lk != NULL) {
398 	xmlLinkDeallocator(l, lk);
399         return 1;
400     }
401     return 0;
402 }
403 
404 /**
405  * xmlListRemoveAll:
406  * @l:  a list
407  * @data:  list data
408  *
409  * Remove the all instance associated to data in the list
410  *
411  * Returns the number of deallocation, or 0 if not found
412  */
413 int
xmlListRemoveAll(xmlListPtr l,void * data)414 xmlListRemoveAll(xmlListPtr l, void *data)
415 {
416     int count=0;
417 
418     if (l == NULL)
419         return(0);
420 
421     while(xmlListRemoveFirst(l, data))
422         count++;
423     return count;
424 }
425 
426 /**
427  * xmlListClear:
428  * @l:  a list
429  *
430  * Remove the all data in the list
431  */
432 void
xmlListClear(xmlListPtr l)433 xmlListClear(xmlListPtr l)
434 {
435     xmlLinkPtr  lk;
436 
437     if (l == NULL)
438         return;
439     lk = l->sentinel->next;
440     while(lk != l->sentinel) {
441         xmlLinkPtr next = lk->next;
442 
443         xmlLinkDeallocator(l, lk);
444         lk = next;
445     }
446 }
447 
448 /**
449  * xmlListEmpty:
450  * @l:  a list
451  *
452  * Is the list empty ?
453  *
454  * Returns 1 if the list is empty, 0 if not empty and -1 in case of error
455  */
456 int
xmlListEmpty(xmlListPtr l)457 xmlListEmpty(xmlListPtr l)
458 {
459     if (l == NULL)
460         return(-1);
461     return (l->sentinel->next == l->sentinel);
462 }
463 
464 /**
465  * xmlListFront:
466  * @l:  a list
467  *
468  * Get the first element in the list
469  *
470  * Returns the first element in the list, or NULL
471  */
472 xmlLinkPtr
xmlListFront(xmlListPtr l)473 xmlListFront(xmlListPtr l)
474 {
475     if (l == NULL)
476         return(NULL);
477     return (l->sentinel->next);
478 }
479 
480 /**
481  * xmlListEnd:
482  * @l:  a list
483  *
484  * Get the last element in the list
485  *
486  * Returns the last element in the list, or NULL
487  */
488 xmlLinkPtr
xmlListEnd(xmlListPtr l)489 xmlListEnd(xmlListPtr l)
490 {
491     if (l == NULL)
492         return(NULL);
493     return (l->sentinel->prev);
494 }
495 
496 /**
497  * xmlListSize:
498  * @l:  a list
499  *
500  * Get the number of elements in the list
501  *
502  * Returns the number of elements in the list or -1 in case of error
503  */
504 int
xmlListSize(xmlListPtr l)505 xmlListSize(xmlListPtr l)
506 {
507     xmlLinkPtr lk;
508     int count=0;
509 
510     if (l == NULL)
511         return(-1);
512     /* TODO: keep a counter in xmlList instead */
513     for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next, count++);
514     return count;
515 }
516 
517 /**
518  * xmlListPopFront:
519  * @l:  a list
520  *
521  * Removes the first element in the list
522  */
523 void
xmlListPopFront(xmlListPtr l)524 xmlListPopFront(xmlListPtr l)
525 {
526     if(!xmlListEmpty(l))
527         xmlLinkDeallocator(l, l->sentinel->next);
528 }
529 
530 /**
531  * xmlListPopBack:
532  * @l:  a list
533  *
534  * Removes the last element in the list
535  */
536 void
xmlListPopBack(xmlListPtr l)537 xmlListPopBack(xmlListPtr l)
538 {
539     if(!xmlListEmpty(l))
540         xmlLinkDeallocator(l, l->sentinel->prev);
541 }
542 
543 /**
544  * xmlListPushFront:
545  * @l:  a list
546  * @data:  new data
547  *
548  * add the new data at the beginning of the list
549  *
550  * Returns 1 if successful, 0 otherwise
551  */
552 int
xmlListPushFront(xmlListPtr l,void * data)553 xmlListPushFront(xmlListPtr l, void *data)
554 {
555     xmlLinkPtr lkPlace, lkNew;
556 
557     if (l == NULL)
558         return(0);
559     lkPlace = l->sentinel;
560     /* Add the new link */
561     lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
562     if (lkNew == NULL) {
563         xmlGenericError(xmlGenericErrorContext,
564 		        "Cannot initialize memory for new link");
565         return (0);
566     }
567     lkNew->data = data;
568     lkNew->next = lkPlace->next;
569     (lkPlace->next)->prev = lkNew;
570     lkPlace->next = lkNew;
571     lkNew->prev = lkPlace;
572     return 1;
573 }
574 
575 /**
576  * xmlListPushBack:
577  * @l:  a list
578  * @data:  new data
579  *
580  * add the new data at the end of the list
581  *
582  * Returns 1 if successful, 0 otherwise
583  */
584 int
xmlListPushBack(xmlListPtr l,void * data)585 xmlListPushBack(xmlListPtr l, void *data)
586 {
587     xmlLinkPtr lkPlace, lkNew;
588 
589     if (l == NULL)
590         return(0);
591     lkPlace = l->sentinel->prev;
592     /* Add the new link */
593     if (NULL ==(lkNew = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {
594         xmlGenericError(xmlGenericErrorContext,
595 		        "Cannot initialize memory for new link");
596         return (0);
597     }
598     lkNew->data = data;
599     lkNew->next = lkPlace->next;
600     (lkPlace->next)->prev = lkNew;
601     lkPlace->next = lkNew;
602     lkNew->prev = lkPlace;
603     return 1;
604 }
605 
606 /**
607  * xmlLinkGetData:
608  * @lk:  a link
609  *
610  * See Returns.
611  *
612  * Returns a pointer to the data referenced from this link
613  */
614 void *
xmlLinkGetData(xmlLinkPtr lk)615 xmlLinkGetData(xmlLinkPtr lk)
616 {
617     if (lk == NULL)
618         return(NULL);
619     return lk->data;
620 }
621 
622 /**
623  * xmlListReverse:
624  * @l:  a list
625  *
626  * Reverse the order of the elements in the list
627  */
628 void
xmlListReverse(xmlListPtr l)629 xmlListReverse(xmlListPtr l)
630 {
631     xmlLinkPtr lk;
632     xmlLinkPtr lkPrev;
633 
634     if (l == NULL)
635         return;
636     lkPrev = l->sentinel;
637     for (lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
638         lkPrev->next = lkPrev->prev;
639         lkPrev->prev = lk;
640         lkPrev = lk;
641     }
642     /* Fix up the last node */
643     lkPrev->next = lkPrev->prev;
644     lkPrev->prev = lk;
645 }
646 
647 /**
648  * xmlListSort:
649  * @l:  a list
650  *
651  * Sort all the elements in the list
652  */
653 void
xmlListSort(xmlListPtr l)654 xmlListSort(xmlListPtr l)
655 {
656     xmlListPtr lTemp;
657 
658     if (l == NULL)
659         return;
660     if(xmlListEmpty(l))
661         return;
662 
663     /* I think that the real answer is to implement quicksort, the
664      * alternative is to implement some list copying procedure which
665      * would be based on a list copy followed by a clear followed by
666      * an insert. This is slow...
667      */
668 
669     if (NULL ==(lTemp = xmlListDup(l)))
670         return;
671     xmlListClear(l);
672     xmlListMerge(l, lTemp);
673     xmlListDelete(lTemp);
674     return;
675 }
676 
677 /**
678  * xmlListWalk:
679  * @l:  a list
680  * @walker:  a processing function
681  * @user:  a user parameter passed to the walker function
682  *
683  * Walk all the element of the first from first to last and
684  * apply the walker function to it
685  */
686 void
xmlListWalk(xmlListPtr l,xmlListWalker walker,void * user)687 xmlListWalk(xmlListPtr l, xmlListWalker walker, void *user) {
688     xmlLinkPtr lk;
689 
690     if ((l == NULL) || (walker == NULL))
691         return;
692     for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
693         if((walker(lk->data, user)) == 0)
694                 break;
695     }
696 }
697 
698 /**
699  * xmlListReverseWalk:
700  * @l:  a list
701  * @walker:  a processing function
702  * @user:  a user parameter passed to the walker function
703  *
704  * Walk all the element of the list in reverse order and
705  * apply the walker function to it
706  */
707 void
xmlListReverseWalk(xmlListPtr l,xmlListWalker walker,void * user)708 xmlListReverseWalk(xmlListPtr l, xmlListWalker walker, void *user) {
709     xmlLinkPtr lk;
710 
711     if ((l == NULL) || (walker == NULL))
712         return;
713     for(lk = l->sentinel->prev; lk != l->sentinel; lk = lk->prev) {
714         if((walker(lk->data, user)) == 0)
715                 break;
716     }
717 }
718 
719 /**
720  * xmlListMerge:
721  * @l1:  the original list
722  * @l2:  the new list
723  *
724  * include all the elements of the second list in the first one and
725  * clear the second list
726  */
727 void
xmlListMerge(xmlListPtr l1,xmlListPtr l2)728 xmlListMerge(xmlListPtr l1, xmlListPtr l2)
729 {
730     xmlListCopy(l1, l2);
731     xmlListClear(l2);
732 }
733 
734 /**
735  * xmlListDup:
736  * @old:  the list
737  *
738  * Duplicate the list
739  *
740  * Returns a new copy of the list or NULL in case of error
741  */
742 xmlListPtr
xmlListDup(const xmlListPtr old)743 xmlListDup(const xmlListPtr old)
744 {
745     xmlListPtr cur;
746 
747     if (old == NULL)
748         return(NULL);
749     /* Hmmm, how to best deal with allocation issues when copying
750      * lists. If there is a de-allocator, should responsibility lie with
751      * the new list or the old list. Surely not both. I'll arbitrarily
752      * set it to be the old list for the time being whilst I work out
753      * the answer
754      */
755     if (NULL ==(cur = xmlListCreate(NULL, old->linkCompare)))
756         return (NULL);
757     if (0 != xmlListCopy(cur, old))
758         return NULL;
759     return cur;
760 }
761 
762 /**
763  * xmlListCopy:
764  * @cur:  the new list
765  * @old:  the old list
766  *
767  * Move all the element from the old list in the new list
768  *
769  * Returns 0 in case of success 1 in case of error
770  */
771 int
xmlListCopy(xmlListPtr cur,const xmlListPtr old)772 xmlListCopy(xmlListPtr cur, const xmlListPtr old)
773 {
774     /* Walk the old tree and insert the data into the new one */
775     xmlLinkPtr lk;
776 
777     if ((old == NULL) || (cur == NULL))
778         return(1);
779     for(lk = old->sentinel->next; lk != old->sentinel; lk = lk->next) {
780         if (0 !=xmlListInsert(cur, lk->data)) {
781             xmlListDelete(cur);
782             return (1);
783         }
784     }
785     return (0);
786 }
787 /* xmlListUnique() */
788 /* xmlListSwap */
789 #define bottom_list
790 #include "elfgcchack.h"
791