| /* | 
 |  * list.c: lists handling implementation | 
 |  * | 
 |  * Copyright (C) 2000 Gary Pennington and Daniel Veillard. | 
 |  * | 
 |  * Permission to use, copy, modify, and distribute this software for any | 
 |  * purpose with or without fee is hereby granted, provided that the above | 
 |  * copyright notice and this permission notice appear in all copies. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED | 
 |  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF | 
 |  * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND | 
 |  * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. | 
 |  * | 
 |  * Author: Gary.Pennington@uk.sun.com | 
 |  */ | 
 |  | 
 | #define IN_LIBXML | 
 | #include "libxml.h" | 
 |  | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <libxml/xmlmemory.h> | 
 | #include <libxml/list.h> | 
 | #include <libxml/globals.h> | 
 |  | 
 | /* | 
 |  * Type definition are kept internal | 
 |  */ | 
 |  | 
 | struct _xmlLink | 
 | { | 
 |     struct _xmlLink *next; | 
 |     struct _xmlLink *prev; | 
 |     void *data; | 
 | }; | 
 |  | 
 | struct _xmlList | 
 | { | 
 |     xmlLinkPtr sentinel; | 
 |     void (*linkDeallocator)(xmlLinkPtr ); | 
 |     int (*linkCompare)(const void *, const void*); | 
 | }; | 
 |  | 
 | /************************************************************************ | 
 |  *                                    * | 
 |  *                Interfaces                * | 
 |  *                                    * | 
 |  ************************************************************************/ | 
 |  | 
 | /** | 
 |  * xmlLinkDeallocator: | 
 |  * @l:  a list | 
 |  * @lk:  a link | 
 |  * | 
 |  * Unlink and deallocate @lk from list @l | 
 |  */ | 
 | static void | 
 | xmlLinkDeallocator(xmlListPtr l, xmlLinkPtr lk) | 
 | { | 
 |     (lk->prev)->next = lk->next; | 
 |     (lk->next)->prev = lk->prev; | 
 |     if(l->linkDeallocator) | 
 |         l->linkDeallocator(lk); | 
 |     xmlFree(lk); | 
 | } | 
 |  | 
 | /** | 
 |  * xmlLinkCompare: | 
 |  * @data0:  first data | 
 |  * @data1:  second data | 
 |  * | 
 |  * Compares two arbitrary data | 
 |  * | 
 |  * Returns -1, 0 or 1 depending on whether data1 is greater equal or smaller | 
 |  *          than data0 | 
 |  */ | 
 | static int | 
 | xmlLinkCompare(const void *data0, const void *data1) | 
 | { | 
 |     if (data0 < data1) | 
 |         return (-1); | 
 |     else if (data0 == data1) | 
 | 	return (0); | 
 |     return (1); | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListLowerSearch: | 
 |  * @l:  a list | 
 |  * @data:  a data | 
 |  * | 
 |  * Search data in the ordered list walking from the beginning | 
 |  * | 
 |  * Returns the link containing the data or NULL | 
 |  */ | 
 | static xmlLinkPtr  | 
 | xmlListLowerSearch(xmlListPtr l, void *data)  | 
 | { | 
 |     xmlLinkPtr lk; | 
 |  | 
 |     if (l == NULL) | 
 |         return(NULL); | 
 |     for(lk = l->sentinel->next;lk != l->sentinel && l->linkCompare(lk->data, data) <0 ;lk = lk->next); | 
 |     return lk;     | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListHigherSearch: | 
 |  * @l:  a list | 
 |  * @data:  a data | 
 |  * | 
 |  * Search data in the ordered list walking backward from the end | 
 |  * | 
 |  * Returns the link containing the data or NULL | 
 |  */ | 
 | static xmlLinkPtr  | 
 | xmlListHigherSearch(xmlListPtr l, void *data)  | 
 | { | 
 |     xmlLinkPtr lk; | 
 |  | 
 |     if (l == NULL) | 
 |         return(NULL); | 
 |     for(lk = l->sentinel->prev;lk != l->sentinel && l->linkCompare(lk->data, data) >0 ;lk = lk->prev); | 
 |     return lk;     | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListSearch: | 
 |  * @l:  a list | 
 |  * @data:  a data | 
 |  * | 
 |  * Search data in the list | 
 |  * | 
 |  * Returns the link containing the data or NULL | 
 |  */ | 
 | static xmlLinkPtr  | 
 | xmlListLinkSearch(xmlListPtr l, void *data)  | 
 | { | 
 |     xmlLinkPtr lk; | 
 |     if (l == NULL) | 
 |         return(NULL); | 
 |     lk = xmlListLowerSearch(l, data); | 
 |     if (lk == l->sentinel) | 
 |         return NULL; | 
 |     else { | 
 |         if (l->linkCompare(lk->data, data) ==0) | 
 |             return lk; | 
 |         return NULL; | 
 |     } | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListLinkReverseSearch: | 
 |  * @l:  a list | 
 |  * @data:  a data | 
 |  * | 
 |  * Search data in the list processing backward | 
 |  * | 
 |  * Returns the link containing the data or NULL | 
 |  */ | 
 | static xmlLinkPtr  | 
 | xmlListLinkReverseSearch(xmlListPtr l, void *data)  | 
 | { | 
 |     xmlLinkPtr lk; | 
 |     if (l == NULL) | 
 |         return(NULL); | 
 |     lk = xmlListHigherSearch(l, data); | 
 |     if (lk == l->sentinel) | 
 |         return NULL; | 
 |     else { | 
 |         if (l->linkCompare(lk->data, data) ==0) | 
 |             return lk; | 
 |         return NULL; | 
 |     } | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListCreate: | 
 |  * @deallocator:  an optional deallocator function | 
 |  * @compare:  an optional comparison function | 
 |  * | 
 |  * Create a new list | 
 |  * | 
 |  * Returns the new list or NULL in case of error | 
 |  */ | 
 | xmlListPtr | 
 | xmlListCreate(xmlListDeallocator deallocator, xmlListDataCompare compare) | 
 | { | 
 |     xmlListPtr l; | 
 |     if (NULL == (l = (xmlListPtr )xmlMalloc( sizeof(xmlList)))) { | 
 |         xmlGenericError(xmlGenericErrorContext,  | 
 | 		        "Cannot initialize memory for list"); | 
 |         return (NULL); | 
 |     } | 
 |     /* Initialize the list to NULL */ | 
 |     memset(l, 0, sizeof(xmlList)); | 
 |      | 
 |     /* Add the sentinel */ | 
 |     if (NULL ==(l->sentinel = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) { | 
 |         xmlGenericError(xmlGenericErrorContext,  | 
 | 		        "Cannot initialize memory for sentinel"); | 
 | 	xmlFree(l); | 
 |         return (NULL); | 
 |     } | 
 |     l->sentinel->next = l->sentinel; | 
 |     l->sentinel->prev = l->sentinel; | 
 |     l->sentinel->data = NULL; | 
 |      | 
 |     /* If there is a link deallocator, use it */ | 
 |     if (deallocator != NULL) | 
 |         l->linkDeallocator = deallocator; | 
 |     /* If there is a link comparator, use it */ | 
 |     if (compare != NULL) | 
 |         l->linkCompare = compare; | 
 |     else /* Use our own */ | 
 |         l->linkCompare = xmlLinkCompare; | 
 |     return l; | 
 | } | 
 |      | 
 | /** | 
 |  * xmlListSearch: | 
 |  * @l:  a list | 
 |  * @data:  a search value | 
 |  * | 
 |  * Search the list for an existing value of @data | 
 |  * | 
 |  * Returns the value associated to @data or NULL in case of error | 
 |  */ | 
 | void * | 
 | xmlListSearch(xmlListPtr l, void *data)  | 
 | { | 
 |     xmlLinkPtr lk; | 
 |     if (l == NULL) | 
 |         return(NULL); | 
 |     lk = xmlListLinkSearch(l, data); | 
 |     if (lk) | 
 |         return (lk->data); | 
 |     return NULL; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListReverseSearch: | 
 |  * @l:  a list | 
 |  * @data:  a search value | 
 |  * | 
 |  * Search the list in reverse order for an existing value of @data | 
 |  * | 
 |  * Returns the value associated to @data or NULL in case of error | 
 |  */ | 
 | void * | 
 | xmlListReverseSearch(xmlListPtr l, void *data)  | 
 | { | 
 |     xmlLinkPtr lk; | 
 |     if (l == NULL) | 
 |         return(NULL); | 
 |     lk = xmlListLinkReverseSearch(l, data); | 
 |     if (lk) | 
 |         return (lk->data); | 
 |     return NULL; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListInsert: | 
 |  * @l:  a list | 
 |  * @data:  the data | 
 |  * | 
 |  * Insert data in the ordered list at the beginning for this value | 
 |  * | 
 |  * Returns 0 in case of success, 1 in case of failure | 
 |  */ | 
 | int | 
 | xmlListInsert(xmlListPtr l, void *data)  | 
 | { | 
 |     xmlLinkPtr lkPlace, lkNew; | 
 |  | 
 |     if (l == NULL) | 
 |         return(1); | 
 |     lkPlace = xmlListLowerSearch(l, data); | 
 |     /* Add the new link */ | 
 |     lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink)); | 
 |     if (lkNew == NULL) { | 
 |         xmlGenericError(xmlGenericErrorContext,  | 
 | 		        "Cannot initialize memory for new link"); | 
 |         return (1); | 
 |     } | 
 |     lkNew->data = data; | 
 |     lkPlace = lkPlace->prev; | 
 |     lkNew->next = lkPlace->next; | 
 |     (lkPlace->next)->prev = lkNew; | 
 |     lkPlace->next = lkNew; | 
 |     lkNew->prev = lkPlace; | 
 |     return 0; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListAppend: | 
 |  * @l:  a list | 
 |  * @data:  the data | 
 |  * | 
 |  * Insert data in the ordered list at the end for this value | 
 |  * | 
 |  * Returns 0 in case of success, 1 in case of failure | 
 |  */ | 
 | int xmlListAppend(xmlListPtr l, void *data)  | 
 | { | 
 |     xmlLinkPtr lkPlace, lkNew; | 
 |  | 
 |     if (l == NULL) | 
 |         return(1); | 
 |     lkPlace = xmlListHigherSearch(l, data); | 
 |     /* Add the new link */ | 
 |     lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink)); | 
 |     if (lkNew == NULL) { | 
 |         xmlGenericError(xmlGenericErrorContext,  | 
 | 		        "Cannot initialize memory for new link"); | 
 |         return (1); | 
 |     } | 
 |     lkNew->data = data; | 
 |     lkNew->next = lkPlace->next; | 
 |     (lkPlace->next)->prev = lkNew; | 
 |     lkPlace->next = lkNew; | 
 |     lkNew->prev = lkPlace; | 
 |     return 0; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListDelete: | 
 |  * @l:  a list | 
 |  * | 
 |  * Deletes the list and its associated data | 
 |  */ | 
 | void xmlListDelete(xmlListPtr l) | 
 | { | 
 |     if (l == NULL) | 
 |         return; | 
 |  | 
 |     xmlListClear(l); | 
 |     xmlFree(l->sentinel); | 
 |     xmlFree(l); | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListRemoveFirst: | 
 |  * @l:  a list | 
 |  * @data:  list data | 
 |  * | 
 |  * Remove the first instance associated to data in the list | 
 |  * | 
 |  * Returns 1 if a deallocation occured, or 0 if not found | 
 |  */ | 
 | int | 
 | xmlListRemoveFirst(xmlListPtr l, void *data) | 
 | { | 
 |     xmlLinkPtr lk; | 
 |      | 
 |     if (l == NULL) | 
 |         return(0); | 
 |     /*Find the first instance of this data */ | 
 |     lk = xmlListLinkSearch(l, data); | 
 |     if (lk != NULL) { | 
 |         xmlLinkDeallocator(l, lk); | 
 |         return 1; | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListRemoveLast: | 
 |  * @l:  a list | 
 |  * @data:  list data | 
 |  * | 
 |  * Remove the last instance associated to data in the list | 
 |  * | 
 |  * Returns 1 if a deallocation occured, or 0 if not found | 
 |  */ | 
 | int | 
 | xmlListRemoveLast(xmlListPtr l, void *data) | 
 | { | 
 |     xmlLinkPtr lk; | 
 |      | 
 |     if (l == NULL) | 
 |         return(0); | 
 |     /*Find the last instance of this data */ | 
 |     lk = xmlListLinkReverseSearch(l, data); | 
 |     if (lk != NULL) { | 
 | 	xmlLinkDeallocator(l, lk); | 
 |         return 1; | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListRemoveAll: | 
 |  * @l:  a list | 
 |  * @data:  list data | 
 |  * | 
 |  * Remove the all instance associated to data in the list | 
 |  * | 
 |  * Returns the number of deallocation, or 0 if not found | 
 |  */ | 
 | int | 
 | xmlListRemoveAll(xmlListPtr l, void *data) | 
 | { | 
 |     int count=0; | 
 |      | 
 |     if (l == NULL) | 
 |         return(0); | 
 |  | 
 |     while(xmlListRemoveFirst(l, data)) | 
 |         count++; | 
 |     return count; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListClear: | 
 |  * @l:  a list | 
 |  * | 
 |  * Remove the all data in the list | 
 |  */ | 
 | void | 
 | xmlListClear(xmlListPtr l) | 
 | { | 
 |     xmlLinkPtr  lk; | 
 |      | 
 |     if (l == NULL) | 
 |         return; | 
 |     lk = l->sentinel->next; | 
 |     while(lk != l->sentinel) { | 
 |         xmlLinkPtr next = lk->next; | 
 |  | 
 |         xmlLinkDeallocator(l, lk); | 
 |         lk = next; | 
 |     } | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListEmpty: | 
 |  * @l:  a list | 
 |  * | 
 |  * Is the list empty ? | 
 |  * | 
 |  * Returns 1 if the list is empty, 0 if not empty and -1 in case of error | 
 |  */ | 
 | int | 
 | xmlListEmpty(xmlListPtr l) | 
 | { | 
 |     if (l == NULL) | 
 |         return(-1); | 
 |     return (l->sentinel->next == l->sentinel); | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListFront: | 
 |  * @l:  a list | 
 |  * | 
 |  * Get the first element in the list | 
 |  * | 
 |  * Returns the first element in the list, or NULL | 
 |  */ | 
 | xmlLinkPtr  | 
 | xmlListFront(xmlListPtr l) | 
 | { | 
 |     if (l == NULL) | 
 |         return(NULL); | 
 |     return (l->sentinel->next); | 
 | } | 
 |      | 
 | /** | 
 |  * xmlListEnd: | 
 |  * @l:  a list | 
 |  * | 
 |  * Get the last element in the list | 
 |  * | 
 |  * Returns the last element in the list, or NULL | 
 |  */ | 
 | xmlLinkPtr  | 
 | xmlListEnd(xmlListPtr l) | 
 | { | 
 |     if (l == NULL) | 
 |         return(NULL); | 
 |     return (l->sentinel->prev); | 
 | } | 
 |      | 
 | /** | 
 |  * xmlListSize: | 
 |  * @l:  a list | 
 |  * | 
 |  * Get the number of elements in the list | 
 |  * | 
 |  * Returns the number of elements in the list or -1 in case of error | 
 |  */ | 
 | int | 
 | xmlListSize(xmlListPtr l) | 
 | { | 
 |     xmlLinkPtr lk; | 
 |     int count=0; | 
 |  | 
 |     if (l == NULL) | 
 |         return(-1); | 
 |     /* TODO: keep a counter in xmlList instead */ | 
 |     for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next, count++); | 
 |     return count; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListPopFront: | 
 |  * @l:  a list | 
 |  * | 
 |  * Removes the first element in the list | 
 |  */ | 
 | void | 
 | xmlListPopFront(xmlListPtr l) | 
 | { | 
 |     if(!xmlListEmpty(l)) | 
 |         xmlLinkDeallocator(l, l->sentinel->next); | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListPopBack: | 
 |  * @l:  a list | 
 |  * | 
 |  * Removes the last element in the list | 
 |  */ | 
 | void | 
 | xmlListPopBack(xmlListPtr l) | 
 | { | 
 |     if(!xmlListEmpty(l)) | 
 |         xmlLinkDeallocator(l, l->sentinel->prev); | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListPushFront: | 
 |  * @l:  a list | 
 |  * @data:  new data | 
 |  * | 
 |  * add the new data at the beginning of the list | 
 |  * | 
 |  * Returns 1 if successful, 0 otherwise | 
 |  */ | 
 | int | 
 | xmlListPushFront(xmlListPtr l, void *data)  | 
 | { | 
 |     xmlLinkPtr lkPlace, lkNew; | 
 |  | 
 |     if (l == NULL) | 
 |         return(0); | 
 |     lkPlace = l->sentinel; | 
 |     /* Add the new link */ | 
 |     lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink)); | 
 |     if (lkNew == NULL) { | 
 |         xmlGenericError(xmlGenericErrorContext,  | 
 | 		        "Cannot initialize memory for new link"); | 
 |         return (0); | 
 |     } | 
 |     lkNew->data = data; | 
 |     lkNew->next = lkPlace->next; | 
 |     (lkPlace->next)->prev = lkNew; | 
 |     lkPlace->next = lkNew; | 
 |     lkNew->prev = lkPlace; | 
 |     return 1; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListPushBack: | 
 |  * @l:  a list | 
 |  * @data:  new data | 
 |  * | 
 |  * add the new data at the end of the list | 
 |  * | 
 |  * Returns 1 if successful, 0 otherwise | 
 |  */ | 
 | int | 
 | xmlListPushBack(xmlListPtr l, void *data)  | 
 | { | 
 |     xmlLinkPtr lkPlace, lkNew; | 
 |  | 
 |     if (l == NULL) | 
 |         return(0); | 
 |     lkPlace = l->sentinel->prev; | 
 |     /* Add the new link */ | 
 |     if (NULL ==(lkNew = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) { | 
 |         xmlGenericError(xmlGenericErrorContext,  | 
 | 		        "Cannot initialize memory for new link"); | 
 |         return (0); | 
 |     } | 
 |     lkNew->data = data; | 
 |     lkNew->next = lkPlace->next; | 
 |     (lkPlace->next)->prev = lkNew; | 
 |     lkPlace->next = lkNew; | 
 |     lkNew->prev = lkPlace; | 
 |     return 1; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlLinkGetData: | 
 |  * @lk:  a link | 
 |  * | 
 |  * See Returns. | 
 |  * | 
 |  * Returns a pointer to the data referenced from this link | 
 |  */ | 
 | void * | 
 | xmlLinkGetData(xmlLinkPtr lk) | 
 | { | 
 |     if (lk == NULL) | 
 |         return(NULL); | 
 |     return lk->data; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListReverse: | 
 |  * @l:  a list | 
 |  * | 
 |  * Reverse the order of the elements in the list | 
 |  */ | 
 | void | 
 | xmlListReverse(xmlListPtr l) | 
 | { | 
 |     xmlLinkPtr lk; | 
 |     xmlLinkPtr lkPrev; | 
 |  | 
 |     if (l == NULL) | 
 |         return; | 
 |     lkPrev = l->sentinel; | 
 |     for (lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) { | 
 |         lkPrev->next = lkPrev->prev; | 
 |         lkPrev->prev = lk; | 
 |         lkPrev = lk; | 
 |     } | 
 |     /* Fix up the last node */ | 
 |     lkPrev->next = lkPrev->prev; | 
 |     lkPrev->prev = lk; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListSort: | 
 |  * @l:  a list | 
 |  * | 
 |  * Sort all the elements in the list | 
 |  */ | 
 | void | 
 | xmlListSort(xmlListPtr l) | 
 | { | 
 |     xmlListPtr lTemp; | 
 |      | 
 |     if (l == NULL) | 
 |         return; | 
 |     if(xmlListEmpty(l)) | 
 |         return; | 
 |  | 
 |     /* I think that the real answer is to implement quicksort, the | 
 |      * alternative is to implement some list copying procedure which | 
 |      * would be based on a list copy followed by a clear followed by | 
 |      * an insert. This is slow... | 
 |      */ | 
 |  | 
 |     if (NULL ==(lTemp = xmlListDup(l))) | 
 |         return; | 
 |     xmlListClear(l); | 
 |     xmlListMerge(l, lTemp); | 
 |     xmlListDelete(lTemp); | 
 |     return; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListWalk: | 
 |  * @l:  a list | 
 |  * @walker:  a processing function | 
 |  * @user:  a user parameter passed to the walker function | 
 |  * | 
 |  * Walk all the element of the first from first to last and | 
 |  * apply the walker function to it | 
 |  */ | 
 | void | 
 | xmlListWalk(xmlListPtr l, xmlListWalker walker, const void *user) { | 
 |     xmlLinkPtr lk; | 
 |  | 
 |     if ((l == NULL) || (walker == NULL)) | 
 |         return; | 
 |     for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) { | 
 |         if((walker(lk->data, user)) == 0) | 
 |                 break; | 
 |     } | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListReverseWalk: | 
 |  * @l:  a list | 
 |  * @walker:  a processing function | 
 |  * @user:  a user parameter passed to the walker function | 
 |  * | 
 |  * Walk all the element of the list in reverse order and | 
 |  * apply the walker function to it | 
 |  */ | 
 | void | 
 | xmlListReverseWalk(xmlListPtr l, xmlListWalker walker, const void *user) { | 
 |     xmlLinkPtr lk; | 
 |  | 
 |     if ((l == NULL) || (walker == NULL)) | 
 |         return; | 
 |     for(lk = l->sentinel->prev; lk != l->sentinel; lk = lk->prev) { | 
 |         if((walker(lk->data, user)) == 0) | 
 |                 break; | 
 |     } | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListMerge: | 
 |  * @l1:  the original list | 
 |  * @l2:  the new list | 
 |  * | 
 |  * include all the elements of the second list in the first one and | 
 |  * clear the second list | 
 |  */ | 
 | void | 
 | xmlListMerge(xmlListPtr l1, xmlListPtr l2) | 
 | { | 
 |     xmlListCopy(l1, l2); | 
 |     xmlListClear(l2); | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListDup: | 
 |  * @old:  the list | 
 |  * | 
 |  * Duplicate the list | 
 |  *  | 
 |  * Returns a new copy of the list or NULL in case of error | 
 |  */ | 
 | xmlListPtr  | 
 | xmlListDup(const xmlListPtr old) | 
 | { | 
 |     xmlListPtr cur; | 
 |  | 
 |     if (old == NULL) | 
 |         return(NULL); | 
 |     /* Hmmm, how to best deal with allocation issues when copying | 
 |      * lists. If there is a de-allocator, should responsibility lie with | 
 |      * the new list or the old list. Surely not both. I'll arbitrarily | 
 |      * set it to be the old list for the time being whilst I work out | 
 |      * the answer | 
 |      */ | 
 |     if (NULL ==(cur = xmlListCreate(NULL, old->linkCompare))) | 
 |         return (NULL); | 
 |     if (0 != xmlListCopy(cur, old)) | 
 |         return NULL; | 
 |     return cur; | 
 | } | 
 |  | 
 | /** | 
 |  * xmlListCopy: | 
 |  * @cur:  the new list | 
 |  * @old:  the old list | 
 |  * | 
 |  * Move all the element from the old list in the new list | 
 |  *  | 
 |  * Returns 0 in case of success 1 in case of error | 
 |  */ | 
 | int | 
 | xmlListCopy(xmlListPtr cur, const xmlListPtr old) | 
 | { | 
 |     /* Walk the old tree and insert the data into the new one */ | 
 |     xmlLinkPtr lk; | 
 |  | 
 |     if ((old == NULL) || (cur == NULL)) | 
 |         return(1); | 
 |     for(lk = old->sentinel->next; lk != old->sentinel; lk = lk->next) { | 
 |         if (0 !=xmlListInsert(cur, lk->data)) { | 
 |             xmlListDelete(cur); | 
 |             return (1); | 
 |         } | 
 |     } | 
 |     return (0);     | 
 | } | 
 | /* xmlListUnique() */ | 
 | /* xmlListSwap */ | 
 | #define bottom_list | 
 | #include "elfgcchack.h" |