Coverage Report

Created: 2024-09-06 07:53

/src/libxml2/list.c
Line
Count
Source (jump to first uncovered line)
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
 * MERCHANTABILITY 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/xmlerror.h>
25
#include <libxml/list.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
724
{
61
724
    (lk->prev)->next = lk->next;
62
724
    (lk->next)->prev = lk->prev;
63
724
    if(l->linkDeallocator)
64
724
        l->linkDeallocator(lk);
65
724
    xmlFree(lk);
66
724
}
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
0
{
81
0
    if (data0 < data1)
82
0
        return (-1);
83
0
    else if (data0 == data1)
84
0
  return (0);
85
0
    return (1);
86
0
}
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
0
{
100
0
    xmlLinkPtr lk;
101
102
0
    if (l == NULL)
103
0
        return(NULL);
104
0
    for(lk = l->sentinel->next;lk != l->sentinel && l->linkCompare(lk->data, data) <0 ;lk = lk->next);
105
0
    return lk;
106
0
}
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
724
{
120
724
    xmlLinkPtr lk;
121
122
724
    if (l == NULL)
123
0
        return(NULL);
124
724
    for(lk = l->sentinel->prev;lk != l->sentinel && l->linkCompare(lk->data, data) >0 ;lk = lk->prev);
125
724
    return lk;
126
724
}
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
0
{
140
0
    xmlLinkPtr lk;
141
0
    if (l == NULL)
142
0
        return(NULL);
143
0
    lk = xmlListLowerSearch(l, data);
144
0
    if (lk == l->sentinel)
145
0
        return NULL;
146
0
    else {
147
0
        if (l->linkCompare(lk->data, data) ==0)
148
0
            return lk;
149
0
        return NULL;
150
0
    }
151
0
}
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
0
{
165
0
    xmlLinkPtr lk;
166
0
    if (l == NULL)
167
0
        return(NULL);
168
0
    lk = xmlListHigherSearch(l, data);
169
0
    if (lk == l->sentinel)
170
0
        return NULL;
171
0
    else {
172
0
        if (l->linkCompare(lk->data, data) ==0)
173
0
            return lk;
174
0
        return NULL;
175
0
    }
176
0
}
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
98
{
190
98
    xmlListPtr l;
191
98
    l = (xmlListPtr)xmlMalloc(sizeof(xmlList));
192
98
    if (l == NULL)
193
0
        return (NULL);
194
    /* Initialize the list to NULL */
195
98
    memset(l, 0, sizeof(xmlList));
196
197
    /* Add the sentinel */
198
98
    l->sentinel = (xmlLinkPtr)xmlMalloc(sizeof(xmlLink));
199
98
    if (l->sentinel == NULL) {
200
0
  xmlFree(l);
201
0
        return (NULL);
202
0
    }
203
98
    l->sentinel->next = l->sentinel;
204
98
    l->sentinel->prev = l->sentinel;
205
98
    l->sentinel->data = NULL;
206
207
    /* If there is a link deallocator, use it */
208
98
    if (deallocator != NULL)
209
98
        l->linkDeallocator = deallocator;
210
    /* If there is a link comparator, use it */
211
98
    if (compare != NULL)
212
98
        l->linkCompare = compare;
213
0
    else /* Use our own */
214
0
        l->linkCompare = xmlLinkCompare;
215
98
    return l;
216
98
}
217
218
/**
219
 * xmlListSearch:
220
 * @l:  a list
221
 * @data:  a search value
222
 *
223
 * Search the list for an existing value of @data
224
 *
225
 * Returns the value associated to @data or NULL in case of error
226
 */
227
void *
228
xmlListSearch(xmlListPtr l, void *data)
229
0
{
230
0
    xmlLinkPtr lk;
231
0
    if (l == NULL)
232
0
        return(NULL);
233
0
    lk = xmlListLinkSearch(l, data);
234
0
    if (lk)
235
0
        return (lk->data);
236
0
    return NULL;
237
0
}
238
239
/**
240
 * xmlListReverseSearch:
241
 * @l:  a list
242
 * @data:  a search value
243
 *
244
 * Search the list in reverse order for an existing value of @data
245
 *
246
 * Returns the value associated to @data or NULL in case of error
247
 */
248
void *
249
xmlListReverseSearch(xmlListPtr l, void *data)
250
0
{
251
0
    xmlLinkPtr lk;
252
0
    if (l == NULL)
253
0
        return(NULL);
254
0
    lk = xmlListLinkReverseSearch(l, data);
255
0
    if (lk)
256
0
        return (lk->data);
257
0
    return NULL;
258
0
}
259
260
/**
261
 * xmlListInsert:
262
 * @l:  a list
263
 * @data:  the data
264
 *
265
 * Insert data in the ordered list at the beginning for this value
266
 *
267
 * Returns 0 in case of success, 1 in case of failure
268
 */
269
int
270
xmlListInsert(xmlListPtr l, void *data)
271
0
{
272
0
    xmlLinkPtr lkPlace, lkNew;
273
274
0
    if (l == NULL)
275
0
        return(1);
276
0
    lkPlace = xmlListLowerSearch(l, data);
277
    /* Add the new link */
278
0
    lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
279
0
    if (lkNew == NULL)
280
0
        return (1);
281
0
    lkNew->data = data;
282
0
    lkPlace = lkPlace->prev;
283
0
    lkNew->next = lkPlace->next;
284
0
    (lkPlace->next)->prev = lkNew;
285
0
    lkPlace->next = lkNew;
286
0
    lkNew->prev = lkPlace;
287
0
    return 0;
288
0
}
289
290
/**
291
 * xmlListAppend:
292
 * @l:  a list
293
 * @data:  the data
294
 *
295
 * Insert data in the ordered list at the end for this value
296
 *
297
 * Returns 0 in case of success, 1 in case of failure
298
 */
299
int xmlListAppend(xmlListPtr l, void *data)
300
724
{
301
724
    xmlLinkPtr lkPlace, lkNew;
302
303
724
    if (l == NULL)
304
0
        return(1);
305
724
    lkPlace = xmlListHigherSearch(l, data);
306
    /* Add the new link */
307
724
    lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
308
724
    if (lkNew == NULL)
309
0
        return (1);
310
724
    lkNew->data = data;
311
724
    lkNew->next = lkPlace->next;
312
724
    (lkPlace->next)->prev = lkNew;
313
724
    lkPlace->next = lkNew;
314
724
    lkNew->prev = lkPlace;
315
724
    return 0;
316
724
}
317
318
/**
319
 * xmlListDelete:
320
 * @l:  a list
321
 *
322
 * Deletes the list and its associated data
323
 */
324
void xmlListDelete(xmlListPtr l)
325
98
{
326
98
    if (l == NULL)
327
0
        return;
328
329
98
    xmlListClear(l);
330
98
    xmlFree(l->sentinel);
331
98
    xmlFree(l);
332
98
}
333
334
/**
335
 * xmlListRemoveFirst:
336
 * @l:  a list
337
 * @data:  list data
338
 *
339
 * Remove the first instance associated to data in the list
340
 *
341
 * Returns 1 if a deallocation occurred, or 0 if not found
342
 */
343
int
344
xmlListRemoveFirst(xmlListPtr l, void *data)
345
0
{
346
0
    xmlLinkPtr lk;
347
348
0
    if (l == NULL)
349
0
        return(0);
350
    /*Find the first instance of this data */
351
0
    lk = xmlListLinkSearch(l, data);
352
0
    if (lk != NULL) {
353
0
        xmlLinkDeallocator(l, lk);
354
0
        return 1;
355
0
    }
356
0
    return 0;
357
0
}
358
359
/**
360
 * xmlListRemoveLast:
361
 * @l:  a list
362
 * @data:  list data
363
 *
364
 * Remove the last instance associated to data in the list
365
 *
366
 * Returns 1 if a deallocation occurred, or 0 if not found
367
 */
368
int
369
xmlListRemoveLast(xmlListPtr l, void *data)
370
0
{
371
0
    xmlLinkPtr lk;
372
373
0
    if (l == NULL)
374
0
        return(0);
375
    /*Find the last instance of this data */
376
0
    lk = xmlListLinkReverseSearch(l, data);
377
0
    if (lk != NULL) {
378
0
  xmlLinkDeallocator(l, lk);
379
0
        return 1;
380
0
    }
381
0
    return 0;
382
0
}
383
384
/**
385
 * xmlListRemoveAll:
386
 * @l:  a list
387
 * @data:  list data
388
 *
389
 * Remove the all instance associated to data in the list
390
 *
391
 * Returns the number of deallocation, or 0 if not found
392
 */
393
int
394
xmlListRemoveAll(xmlListPtr l, void *data)
395
0
{
396
0
    int count=0;
397
398
0
    if (l == NULL)
399
0
        return(0);
400
401
0
    while(xmlListRemoveFirst(l, data))
402
0
        count++;
403
0
    return count;
404
0
}
405
406
/**
407
 * xmlListClear:
408
 * @l:  a list
409
 *
410
 * Remove the all data in the list
411
 */
412
void
413
xmlListClear(xmlListPtr l)
414
98
{
415
98
    xmlLinkPtr  lk;
416
417
98
    if (l == NULL)
418
0
        return;
419
98
    lk = l->sentinel->next;
420
822
    while(lk != l->sentinel) {
421
724
        xmlLinkPtr next = lk->next;
422
423
724
        xmlLinkDeallocator(l, lk);
424
724
        lk = next;
425
724
    }
426
98
}
427
428
/**
429
 * xmlListEmpty:
430
 * @l:  a list
431
 *
432
 * Is the list empty ?
433
 *
434
 * Returns 1 if the list is empty, 0 if not empty and -1 in case of error
435
 */
436
int
437
xmlListEmpty(xmlListPtr l)
438
0
{
439
0
    if (l == NULL)
440
0
        return(-1);
441
0
    return (l->sentinel->next == l->sentinel);
442
0
}
443
444
/**
445
 * xmlListFront:
446
 * @l:  a list
447
 *
448
 * Get the first element in the list
449
 *
450
 * Returns the first element in the list, or NULL
451
 */
452
xmlLinkPtr
453
xmlListFront(xmlListPtr l)
454
0
{
455
0
    if (l == NULL)
456
0
        return(NULL);
457
0
    return (l->sentinel->next);
458
0
}
459
460
/**
461
 * xmlListEnd:
462
 * @l:  a list
463
 *
464
 * Get the last element in the list
465
 *
466
 * Returns the last element in the list, or NULL
467
 */
468
xmlLinkPtr
469
xmlListEnd(xmlListPtr l)
470
0
{
471
0
    if (l == NULL)
472
0
        return(NULL);
473
0
    return (l->sentinel->prev);
474
0
}
475
476
/**
477
 * xmlListSize:
478
 * @l:  a list
479
 *
480
 * Get the number of elements in the list
481
 *
482
 * Returns the number of elements in the list or -1 in case of error
483
 */
484
int
485
xmlListSize(xmlListPtr l)
486
0
{
487
0
    xmlLinkPtr lk;
488
0
    int count=0;
489
490
0
    if (l == NULL)
491
0
        return(-1);
492
    /* TODO: keep a counter in xmlList instead */
493
0
    for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next, count++);
494
0
    return count;
495
0
}
496
497
/**
498
 * xmlListPopFront:
499
 * @l:  a list
500
 *
501
 * Removes the first element in the list
502
 */
503
void
504
xmlListPopFront(xmlListPtr l)
505
0
{
506
0
    if(!xmlListEmpty(l))
507
0
        xmlLinkDeallocator(l, l->sentinel->next);
508
0
}
509
510
/**
511
 * xmlListPopBack:
512
 * @l:  a list
513
 *
514
 * Removes the last element in the list
515
 */
516
void
517
xmlListPopBack(xmlListPtr l)
518
0
{
519
0
    if(!xmlListEmpty(l))
520
0
        xmlLinkDeallocator(l, l->sentinel->prev);
521
0
}
522
523
/**
524
 * xmlListPushFront:
525
 * @l:  a list
526
 * @data:  new data
527
 *
528
 * add the new data at the beginning of the list
529
 *
530
 * Returns 1 if successful, 0 otherwise
531
 */
532
int
533
xmlListPushFront(xmlListPtr l, void *data)
534
0
{
535
0
    xmlLinkPtr lkPlace, lkNew;
536
537
0
    if (l == NULL)
538
0
        return(0);
539
0
    lkPlace = l->sentinel;
540
    /* Add the new link */
541
0
    lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
542
0
    if (lkNew == NULL)
543
0
        return (0);
544
0
    lkNew->data = data;
545
0
    lkNew->next = lkPlace->next;
546
0
    (lkPlace->next)->prev = lkNew;
547
0
    lkPlace->next = lkNew;
548
0
    lkNew->prev = lkPlace;
549
0
    return 1;
550
0
}
551
552
/**
553
 * xmlListPushBack:
554
 * @l:  a list
555
 * @data:  new data
556
 *
557
 * add the new data at the end of the list
558
 *
559
 * Returns 1 if successful, 0 otherwise
560
 */
561
int
562
xmlListPushBack(xmlListPtr l, void *data)
563
0
{
564
0
    xmlLinkPtr lkPlace, lkNew;
565
566
0
    if (l == NULL)
567
0
        return(0);
568
0
    lkPlace = l->sentinel->prev;
569
    /* Add the new link */
570
0
    lkNew = (xmlLinkPtr)xmlMalloc(sizeof(xmlLink));
571
0
    if (lkNew == NULL)
572
0
        return (0);
573
0
    lkNew->data = data;
574
0
    lkNew->next = lkPlace->next;
575
0
    (lkPlace->next)->prev = lkNew;
576
0
    lkPlace->next = lkNew;
577
0
    lkNew->prev = lkPlace;
578
0
    return 1;
579
0
}
580
581
/**
582
 * xmlLinkGetData:
583
 * @lk:  a link
584
 *
585
 * See Returns.
586
 *
587
 * Returns a pointer to the data referenced from this link
588
 */
589
void *
590
xmlLinkGetData(xmlLinkPtr lk)
591
724
{
592
724
    if (lk == NULL)
593
0
        return(NULL);
594
724
    return lk->data;
595
724
}
596
597
/**
598
 * xmlListReverse:
599
 * @l:  a list
600
 *
601
 * Reverse the order of the elements in the list
602
 */
603
void
604
xmlListReverse(xmlListPtr l)
605
0
{
606
0
    xmlLinkPtr lk;
607
0
    xmlLinkPtr lkPrev;
608
609
0
    if (l == NULL)
610
0
        return;
611
0
    lkPrev = l->sentinel;
612
0
    for (lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
613
0
        lkPrev->next = lkPrev->prev;
614
0
        lkPrev->prev = lk;
615
0
        lkPrev = lk;
616
0
    }
617
    /* Fix up the last node */
618
0
    lkPrev->next = lkPrev->prev;
619
0
    lkPrev->prev = lk;
620
0
}
621
622
/**
623
 * xmlListSort:
624
 * @l:  a list
625
 *
626
 * Sort all the elements in the list
627
 */
628
void
629
xmlListSort(xmlListPtr l)
630
0
{
631
0
    xmlListPtr lTemp;
632
633
0
    if (l == NULL)
634
0
        return;
635
0
    if(xmlListEmpty(l))
636
0
        return;
637
638
    /* I think that the real answer is to implement quicksort, the
639
     * alternative is to implement some list copying procedure which
640
     * would be based on a list copy followed by a clear followed by
641
     * an insert. This is slow...
642
     */
643
644
0
    lTemp = xmlListDup(l);
645
0
    if (lTemp == NULL)
646
0
        return;
647
0
    xmlListClear(l);
648
0
    xmlListMerge(l, lTemp);
649
0
    xmlListDelete(lTemp);
650
0
}
651
652
/**
653
 * xmlListWalk:
654
 * @l:  a list
655
 * @walker:  a processing function
656
 * @user:  a user parameter passed to the walker function
657
 *
658
 * Walk all the element of the first from first to last and
659
 * apply the walker function to it
660
 */
661
void
662
0
xmlListWalk(xmlListPtr l, xmlListWalker walker, void *user) {
663
0
    xmlLinkPtr lk;
664
665
0
    if ((l == NULL) || (walker == NULL))
666
0
        return;
667
0
    for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
668
0
        if((walker(lk->data, user)) == 0)
669
0
                break;
670
0
    }
671
0
}
672
673
/**
674
 * xmlListReverseWalk:
675
 * @l:  a list
676
 * @walker:  a processing function
677
 * @user:  a user parameter passed to the walker function
678
 *
679
 * Walk all the element of the list in reverse order and
680
 * apply the walker function to it
681
 */
682
void
683
0
xmlListReverseWalk(xmlListPtr l, xmlListWalker walker, void *user) {
684
0
    xmlLinkPtr lk;
685
686
0
    if ((l == NULL) || (walker == NULL))
687
0
        return;
688
0
    for(lk = l->sentinel->prev; lk != l->sentinel; lk = lk->prev) {
689
0
        if((walker(lk->data, user)) == 0)
690
0
                break;
691
0
    }
692
0
}
693
694
/**
695
 * xmlListMerge:
696
 * @l1:  the original list
697
 * @l2:  the new list
698
 *
699
 * include all the elements of the second list in the first one and
700
 * clear the second list
701
 */
702
void
703
xmlListMerge(xmlListPtr l1, xmlListPtr l2)
704
0
{
705
0
    xmlListCopy(l1, l2);
706
0
    xmlListClear(l2);
707
0
}
708
709
/**
710
 * xmlListDup:
711
 * @old:  the list
712
 *
713
 * Duplicate the list
714
 *
715
 * Returns a new copy of the list or NULL in case of error
716
 */
717
xmlListPtr
718
xmlListDup(xmlListPtr old)
719
0
{
720
0
    xmlListPtr cur;
721
722
0
    if (old == NULL)
723
0
        return(NULL);
724
    /* Hmmm, how to best deal with allocation issues when copying
725
     * lists. If there is a de-allocator, should responsibility lie with
726
     * the new list or the old list. Surely not both. I'll arbitrarily
727
     * set it to be the old list for the time being whilst I work out
728
     * the answer
729
     */
730
0
    cur = xmlListCreate(NULL, old->linkCompare);
731
0
    if (cur == NULL)
732
0
        return (NULL);
733
0
    if (0 != xmlListCopy(cur, old))
734
0
        return NULL;
735
0
    return cur;
736
0
}
737
738
/**
739
 * xmlListCopy:
740
 * @cur:  the new list
741
 * @old:  the old list
742
 *
743
 * Move all the element from the old list in the new list
744
 *
745
 * Returns 0 in case of success 1 in case of error
746
 */
747
int
748
xmlListCopy(xmlListPtr cur, xmlListPtr old)
749
0
{
750
    /* Walk the old tree and insert the data into the new one */
751
0
    xmlLinkPtr lk;
752
753
0
    if ((old == NULL) || (cur == NULL))
754
0
        return(1);
755
0
    for(lk = old->sentinel->next; lk != old->sentinel; lk = lk->next) {
756
0
        if (0 !=xmlListInsert(cur, lk->data)) {
757
0
            xmlListDelete(cur);
758
0
            return (1);
759
0
        }
760
0
    }
761
0
    return (0);
762
0
}
763
/* xmlListUnique() */
764
/* xmlListSwap */