/*1* list.c: lists handling implementation2*3* Copyright (C) 2000 Gary Pennington and Daniel Veillard.4*5* Permission to use, copy, modify, and distribute this software for any6* purpose with or without fee is hereby granted, provided that the above7* copyright notice and this permission notice appear in all copies.8*9* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED10* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF11* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND12* CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.13*14* Author: [email protected]15*/1617#define IN_LIBXML18#include "libxml.h"1920#include <stdlib.h>21#include <string.h>22#include <libxml/xmlmemory.h>23#include <libxml/xmlerror.h>24#include <libxml/list.h>2526/*27* Type definition are kept internal28*/2930struct _xmlLink31{32struct _xmlLink *next;33struct _xmlLink *prev;34void *data;35};3637struct _xmlList38{39xmlLinkPtr sentinel;40void (*linkDeallocator)(xmlLinkPtr );41int (*linkCompare)(const void *, const void*);42};4344/************************************************************************45* *46* Interfaces *47* *48************************************************************************/4950/**51* xmlLinkDeallocator:52* @l: a list53* @lk: a link54*55* Unlink and deallocate @lk from list @l56*/57static void58xmlLinkDeallocator(xmlListPtr l, xmlLinkPtr lk)59{60(lk->prev)->next = lk->next;61(lk->next)->prev = lk->prev;62if(l->linkDeallocator)63l->linkDeallocator(lk);64xmlFree(lk);65}6667/**68* xmlLinkCompare:69* @data0: first data70* @data1: second data71*72* Compares two arbitrary data73*74* Returns -1, 0 or 1 depending on whether data1 is greater equal or smaller75* than data076*/77static int78xmlLinkCompare(const void *data0, const void *data1)79{80if (data0 < data1)81return (-1);82else if (data0 == data1)83return (0);84return (1);85}8687/**88* xmlListLowerSearch:89* @l: a list90* @data: a data91*92* Search data in the ordered list walking from the beginning93*94* Returns the link containing the data or NULL95*/96static xmlLinkPtr97xmlListLowerSearch(xmlListPtr l, void *data)98{99xmlLinkPtr lk;100101if (l == NULL)102return(NULL);103for(lk = l->sentinel->next;lk != l->sentinel && l->linkCompare(lk->data, data) <0 ;lk = lk->next);104return lk;105}106107/**108* xmlListHigherSearch:109* @l: a list110* @data: a data111*112* Search data in the ordered list walking backward from the end113*114* Returns the link containing the data or NULL115*/116static xmlLinkPtr117xmlListHigherSearch(xmlListPtr l, void *data)118{119xmlLinkPtr lk;120121if (l == NULL)122return(NULL);123for(lk = l->sentinel->prev;lk != l->sentinel && l->linkCompare(lk->data, data) >0 ;lk = lk->prev);124return lk;125}126127/**128* xmlListSearch:129* @l: a list130* @data: a data131*132* Search data in the list133*134* Returns the link containing the data or NULL135*/136static xmlLinkPtr137xmlListLinkSearch(xmlListPtr l, void *data)138{139xmlLinkPtr lk;140if (l == NULL)141return(NULL);142lk = xmlListLowerSearch(l, data);143if (lk == l->sentinel)144return NULL;145else {146if (l->linkCompare(lk->data, data) ==0)147return lk;148return NULL;149}150}151152/**153* xmlListLinkReverseSearch:154* @l: a list155* @data: a data156*157* Search data in the list processing backward158*159* Returns the link containing the data or NULL160*/161static xmlLinkPtr162xmlListLinkReverseSearch(xmlListPtr l, void *data)163{164xmlLinkPtr lk;165if (l == NULL)166return(NULL);167lk = xmlListHigherSearch(l, data);168if (lk == l->sentinel)169return NULL;170else {171if (l->linkCompare(lk->data, data) ==0)172return lk;173return NULL;174}175}176177/**178* xmlListCreate:179* @deallocator: an optional deallocator function180* @compare: an optional comparison function181*182* Create a new list183*184* Returns the new list or NULL in case of error185*/186xmlListPtr187xmlListCreate(xmlListDeallocator deallocator, xmlListDataCompare compare)188{189xmlListPtr l;190if (NULL == (l = (xmlListPtr )xmlMalloc( sizeof(xmlList)))) {191xmlGenericError(xmlGenericErrorContext,192"Cannot initialize memory for list");193return (NULL);194}195/* Initialize the list to NULL */196memset(l, 0, sizeof(xmlList));197198/* Add the sentinel */199if (NULL ==(l->sentinel = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {200xmlGenericError(xmlGenericErrorContext,201"Cannot initialize memory for sentinel");202xmlFree(l);203return (NULL);204}205l->sentinel->next = l->sentinel;206l->sentinel->prev = l->sentinel;207l->sentinel->data = NULL;208209/* If there is a link deallocator, use it */210if (deallocator != NULL)211l->linkDeallocator = deallocator;212/* If there is a link comparator, use it */213if (compare != NULL)214l->linkCompare = compare;215else /* Use our own */216l->linkCompare = xmlLinkCompare;217return l;218}219220/**221* xmlListSearch:222* @l: a list223* @data: a search value224*225* Search the list for an existing value of @data226*227* Returns the value associated to @data or NULL in case of error228*/229void *230xmlListSearch(xmlListPtr l, void *data)231{232xmlLinkPtr lk;233if (l == NULL)234return(NULL);235lk = xmlListLinkSearch(l, data);236if (lk)237return (lk->data);238return NULL;239}240241/**242* xmlListReverseSearch:243* @l: a list244* @data: a search value245*246* Search the list in reverse order for an existing value of @data247*248* Returns the value associated to @data or NULL in case of error249*/250void *251xmlListReverseSearch(xmlListPtr l, void *data)252{253xmlLinkPtr lk;254if (l == NULL)255return(NULL);256lk = xmlListLinkReverseSearch(l, data);257if (lk)258return (lk->data);259return NULL;260}261262/**263* xmlListInsert:264* @l: a list265* @data: the data266*267* Insert data in the ordered list at the beginning for this value268*269* Returns 0 in case of success, 1 in case of failure270*/271int272xmlListInsert(xmlListPtr l, void *data)273{274xmlLinkPtr lkPlace, lkNew;275276if (l == NULL)277return(1);278lkPlace = xmlListLowerSearch(l, data);279/* Add the new link */280lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));281if (lkNew == NULL) {282xmlGenericError(xmlGenericErrorContext,283"Cannot initialize memory for new link");284return (1);285}286lkNew->data = data;287lkPlace = lkPlace->prev;288lkNew->next = lkPlace->next;289(lkPlace->next)->prev = lkNew;290lkPlace->next = lkNew;291lkNew->prev = lkPlace;292return 0;293}294295/**296* xmlListAppend:297* @l: a list298* @data: the data299*300* Insert data in the ordered list at the end for this value301*302* Returns 0 in case of success, 1 in case of failure303*/304int xmlListAppend(xmlListPtr l, void *data)305{306xmlLinkPtr lkPlace, lkNew;307308if (l == NULL)309return(1);310lkPlace = xmlListHigherSearch(l, data);311/* Add the new link */312lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));313if (lkNew == NULL) {314xmlGenericError(xmlGenericErrorContext,315"Cannot initialize memory for new link");316return (1);317}318lkNew->data = data;319lkNew->next = lkPlace->next;320(lkPlace->next)->prev = lkNew;321lkPlace->next = lkNew;322lkNew->prev = lkPlace;323return 0;324}325326/**327* xmlListDelete:328* @l: a list329*330* Deletes the list and its associated data331*/332void xmlListDelete(xmlListPtr l)333{334if (l == NULL)335return;336337xmlListClear(l);338xmlFree(l->sentinel);339xmlFree(l);340}341342/**343* xmlListRemoveFirst:344* @l: a list345* @data: list data346*347* Remove the first instance associated to data in the list348*349* Returns 1 if a deallocation occurred, or 0 if not found350*/351int352xmlListRemoveFirst(xmlListPtr l, void *data)353{354xmlLinkPtr lk;355356if (l == NULL)357return(0);358/*Find the first instance of this data */359lk = xmlListLinkSearch(l, data);360if (lk != NULL) {361xmlLinkDeallocator(l, lk);362return 1;363}364return 0;365}366367/**368* xmlListRemoveLast:369* @l: a list370* @data: list data371*372* Remove the last instance associated to data in the list373*374* Returns 1 if a deallocation occurred, or 0 if not found375*/376int377xmlListRemoveLast(xmlListPtr l, void *data)378{379xmlLinkPtr lk;380381if (l == NULL)382return(0);383/*Find the last instance of this data */384lk = xmlListLinkReverseSearch(l, data);385if (lk != NULL) {386xmlLinkDeallocator(l, lk);387return 1;388}389return 0;390}391392/**393* xmlListRemoveAll:394* @l: a list395* @data: list data396*397* Remove the all instance associated to data in the list398*399* Returns the number of deallocation, or 0 if not found400*/401int402xmlListRemoveAll(xmlListPtr l, void *data)403{404int count=0;405406if (l == NULL)407return(0);408409while(xmlListRemoveFirst(l, data))410count++;411return count;412}413414/**415* xmlListClear:416* @l: a list417*418* Remove the all data in the list419*/420void421xmlListClear(xmlListPtr l)422{423xmlLinkPtr lk;424425if (l == NULL)426return;427lk = l->sentinel->next;428while(lk != l->sentinel) {429xmlLinkPtr next = lk->next;430431xmlLinkDeallocator(l, lk);432lk = next;433}434}435436/**437* xmlListEmpty:438* @l: a list439*440* Is the list empty ?441*442* Returns 1 if the list is empty, 0 if not empty and -1 in case of error443*/444int445xmlListEmpty(xmlListPtr l)446{447if (l == NULL)448return(-1);449return (l->sentinel->next == l->sentinel);450}451452/**453* xmlListFront:454* @l: a list455*456* Get the first element in the list457*458* Returns the first element in the list, or NULL459*/460xmlLinkPtr461xmlListFront(xmlListPtr l)462{463if (l == NULL)464return(NULL);465return (l->sentinel->next);466}467468/**469* xmlListEnd:470* @l: a list471*472* Get the last element in the list473*474* Returns the last element in the list, or NULL475*/476xmlLinkPtr477xmlListEnd(xmlListPtr l)478{479if (l == NULL)480return(NULL);481return (l->sentinel->prev);482}483484/**485* xmlListSize:486* @l: a list487*488* Get the number of elements in the list489*490* Returns the number of elements in the list or -1 in case of error491*/492int493xmlListSize(xmlListPtr l)494{495xmlLinkPtr lk;496int count=0;497498if (l == NULL)499return(-1);500/* TODO: keep a counter in xmlList instead */501for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next, count++);502return count;503}504505/**506* xmlListPopFront:507* @l: a list508*509* Removes the first element in the list510*/511void512xmlListPopFront(xmlListPtr l)513{514if(!xmlListEmpty(l))515xmlLinkDeallocator(l, l->sentinel->next);516}517518/**519* xmlListPopBack:520* @l: a list521*522* Removes the last element in the list523*/524void525xmlListPopBack(xmlListPtr l)526{527if(!xmlListEmpty(l))528xmlLinkDeallocator(l, l->sentinel->prev);529}530531/**532* xmlListPushFront:533* @l: a list534* @data: new data535*536* add the new data at the beginning of the list537*538* Returns 1 if successful, 0 otherwise539*/540int541xmlListPushFront(xmlListPtr l, void *data)542{543xmlLinkPtr lkPlace, lkNew;544545if (l == NULL)546return(0);547lkPlace = l->sentinel;548/* Add the new link */549lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));550if (lkNew == NULL) {551xmlGenericError(xmlGenericErrorContext,552"Cannot initialize memory for new link");553return (0);554}555lkNew->data = data;556lkNew->next = lkPlace->next;557(lkPlace->next)->prev = lkNew;558lkPlace->next = lkNew;559lkNew->prev = lkPlace;560return 1;561}562563/**564* xmlListPushBack:565* @l: a list566* @data: new data567*568* add the new data at the end of the list569*570* Returns 1 if successful, 0 otherwise571*/572int573xmlListPushBack(xmlListPtr l, void *data)574{575xmlLinkPtr lkPlace, lkNew;576577if (l == NULL)578return(0);579lkPlace = l->sentinel->prev;580/* Add the new link */581if (NULL ==(lkNew = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {582xmlGenericError(xmlGenericErrorContext,583"Cannot initialize memory for new link");584return (0);585}586lkNew->data = data;587lkNew->next = lkPlace->next;588(lkPlace->next)->prev = lkNew;589lkPlace->next = lkNew;590lkNew->prev = lkPlace;591return 1;592}593594/**595* xmlLinkGetData:596* @lk: a link597*598* See Returns.599*600* Returns a pointer to the data referenced from this link601*/602void *603xmlLinkGetData(xmlLinkPtr lk)604{605if (lk == NULL)606return(NULL);607return lk->data;608}609610/**611* xmlListReverse:612* @l: a list613*614* Reverse the order of the elements in the list615*/616void617xmlListReverse(xmlListPtr l)618{619xmlLinkPtr lk;620xmlLinkPtr lkPrev;621622if (l == NULL)623return;624lkPrev = l->sentinel;625for (lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {626lkPrev->next = lkPrev->prev;627lkPrev->prev = lk;628lkPrev = lk;629}630/* Fix up the last node */631lkPrev->next = lkPrev->prev;632lkPrev->prev = lk;633}634635/**636* xmlListSort:637* @l: a list638*639* Sort all the elements in the list640*/641void642xmlListSort(xmlListPtr l)643{644xmlListPtr lTemp;645646if (l == NULL)647return;648if(xmlListEmpty(l))649return;650651/* I think that the real answer is to implement quicksort, the652* alternative is to implement some list copying procedure which653* would be based on a list copy followed by a clear followed by654* an insert. This is slow...655*/656657if (NULL ==(lTemp = xmlListDup(l)))658return;659xmlListClear(l);660xmlListMerge(l, lTemp);661xmlListDelete(lTemp);662return;663}664665/**666* xmlListWalk:667* @l: a list668* @walker: a processing function669* @user: a user parameter passed to the walker function670*671* Walk all the element of the first from first to last and672* apply the walker function to it673*/674void675xmlListWalk(xmlListPtr l, xmlListWalker walker, void *user) {676xmlLinkPtr lk;677678if ((l == NULL) || (walker == NULL))679return;680for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {681if((walker(lk->data, user)) == 0)682break;683}684}685686/**687* xmlListReverseWalk:688* @l: a list689* @walker: a processing function690* @user: a user parameter passed to the walker function691*692* Walk all the element of the list in reverse order and693* apply the walker function to it694*/695void696xmlListReverseWalk(xmlListPtr l, xmlListWalker walker, void *user) {697xmlLinkPtr lk;698699if ((l == NULL) || (walker == NULL))700return;701for(lk = l->sentinel->prev; lk != l->sentinel; lk = lk->prev) {702if((walker(lk->data, user)) == 0)703break;704}705}706707/**708* xmlListMerge:709* @l1: the original list710* @l2: the new list711*712* include all the elements of the second list in the first one and713* clear the second list714*/715void716xmlListMerge(xmlListPtr l1, xmlListPtr l2)717{718xmlListCopy(l1, l2);719xmlListClear(l2);720}721722/**723* xmlListDup:724* @old: the list725*726* Duplicate the list727*728* Returns a new copy of the list or NULL in case of error729*/730xmlListPtr731xmlListDup(const xmlListPtr old)732{733xmlListPtr cur;734735if (old == NULL)736return(NULL);737/* Hmmm, how to best deal with allocation issues when copying738* lists. If there is a de-allocator, should responsibility lie with739* the new list or the old list. Surely not both. I'll arbitrarily740* set it to be the old list for the time being whilst I work out741* the answer742*/743if (NULL ==(cur = xmlListCreate(NULL, old->linkCompare)))744return (NULL);745if (0 != xmlListCopy(cur, old))746return NULL;747return cur;748}749750/**751* xmlListCopy:752* @cur: the new list753* @old: the old list754*755* Move all the element from the old list in the new list756*757* Returns 0 in case of success 1 in case of error758*/759int760xmlListCopy(xmlListPtr cur, const xmlListPtr old)761{762/* Walk the old tree and insert the data into the new one */763xmlLinkPtr lk;764765if ((old == NULL) || (cur == NULL))766return(1);767for(lk = old->sentinel->next; lk != old->sentinel; lk = lk->next) {768if (0 !=xmlListInsert(cur, lk->data)) {769xmlListDelete(cur);770return (1);771}772}773return (0);774}775/* xmlListUnique() */776/* xmlListSwap */777778779