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