lst.c revision 1.30 1 /* $NetBSD: lst.c,v 1.30 2020/08/22 16:00:52 skrll Exp $ */
2
3 /*
4 * Copyright (c) 1988, 1989, 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Adam de Boor.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <assert.h>
36 #include <stdint.h>
37
38 #include "make.h"
39
40 #ifndef MAKE_NATIVE
41 static char rcsid[] = "$NetBSD: lst.c,v 1.30 2020/08/22 16:00:52 skrll Exp $";
42 #else
43 #include <sys/cdefs.h>
44 #ifndef lint
45 __RCSID("$NetBSD: lst.c,v 1.30 2020/08/22 16:00:52 skrll Exp $");
46 #endif /* not lint */
47 #endif
48
49 struct ListNode {
50 struct ListNode *prev; /* previous element in list */
51 struct ListNode *next; /* next in list */
52 uint8_t useCount; /* Count of functions using the node.
53 * node may not be deleted until count
54 * goes to 0 */
55 Boolean deleted; /* List node should be removed when done */
56 void *datum; /* datum associated with this element */
57 };
58
59 typedef enum {
60 Head, Middle, Tail, Unknown
61 } Where;
62
63 struct List {
64 LstNode first; /* first node in list */
65 LstNode last; /* last node in list */
66
67 /* fields for sequential access */
68 Boolean isOpen; /* true if list has been Lst_Open'ed */
69 Where lastAccess; /* Where in the list the last access was */
70 LstNode curr; /* current node, if open. NULL if
71 * *just* opened */
72 LstNode prev; /* Previous node, if open. Used by Lst_Remove */
73 };
74
75 static ReturnStatus Lst_AtEnd(Lst, void *);
76
77 static Boolean
78 LstIsValid(Lst list)
79 {
80 return list != NULL;
81 }
82
83 static Boolean
84 LstNodeIsValid(LstNode node)
85 {
86 return node != NULL;
87 }
88
89 /* Allocate and initialize a list node.
90 *
91 * The fields 'prev' and 'next' must be initialized by the caller.
92 */
93 static LstNode
94 LstNodeNew(void *datum)
95 {
96 LstNode node = bmake_malloc(sizeof *node);
97 node->useCount = 0;
98 node->deleted = FALSE;
99 node->datum = datum;
100 return node;
101 }
102
103 static Boolean
104 LstIsEmpty(Lst list)
105 {
106 return list->first == NULL;
107 }
108
109 /* Create and initialize a new, empty list. */
110 Lst
111 Lst_Init(void)
112 {
113 Lst list = bmake_malloc(sizeof *list);
114
115 list->first = NULL;
116 list->last = NULL;
117 list->isOpen = FALSE;
118 list->lastAccess = Unknown;
119
120 return list;
121 }
122
123 /* Duplicate an entire list, usually by copying the datum pointers.
124 * If copyProc is given, that function is used to create the new datum from the
125 * old datum, usually by creating a copy of it.
126 * Return the new list, or NULL on failure. */
127 Lst
128 Lst_Duplicate(Lst list, DuplicateProc *copyProc)
129 {
130 Lst newList;
131 LstNode node;
132
133 if (!LstIsValid(list)) {
134 return NULL;
135 }
136
137 newList = Lst_Init();
138
139 node = list->first;
140 while (node != NULL) {
141 if (copyProc != NULL) {
142 if (Lst_AtEnd(newList, copyProc(node->datum)) == FAILURE) {
143 return NULL;
144 }
145 } else if (Lst_AtEnd(newList, node->datum) == FAILURE) {
146 return NULL;
147 }
148
149 node = node->next;
150 }
151
152 return newList;
153 }
154
155 /* Destroy a list and free all its resources. If the freeProc is given, it is
156 * called with the datum from each node in turn before the node is freed. */
157 void
158 Lst_Destroy(Lst list, FreeProc *freeProc)
159 {
160 LstNode node;
161 LstNode next = NULL;
162
163 if (list == NULL)
164 return;
165
166 /* To ease scanning */
167 if (list->last != NULL)
168 list->last->next = NULL;
169 else {
170 free(list);
171 return;
172 }
173
174 if (freeProc) {
175 for (node = list->first; node != NULL; node = next) {
176 next = node->next;
177 freeProc(node->datum);
178 free(node);
179 }
180 } else {
181 for (node = list->first; node != NULL; node = next) {
182 next = node->next;
183 free(node);
184 }
185 }
186
187 free(list);
188 }
189
190 /*
191 * Functions to modify a list
192 */
193
194 /* Insert a new node with the given piece of data before the given node in the
195 * given list. */
196 static ReturnStatus
197 LstInsertBefore(Lst list, LstNode node, void *datum)
198 {
199 LstNode newNode;
200
201 /*
202 * check validity of arguments
203 */
204 if (LstIsValid(list) && (LstIsEmpty(list) && node == NULL))
205 goto ok;
206
207 if (!LstIsValid(list) || LstIsEmpty(list) || !LstNodeIsValid(node)) {
208 return FAILURE;
209 }
210
211 ok:
212 newNode = LstNodeNew(datum);
213
214 if (node == NULL) {
215 newNode->prev = newNode->next = NULL;
216 list->first = list->last = newNode;
217 } else {
218 newNode->prev = node->prev;
219 newNode->next = node;
220
221 if (newNode->prev != NULL) {
222 newNode->prev->next = newNode;
223 }
224 node->prev = newNode;
225
226 if (node == list->first) {
227 list->first = newNode;
228 }
229 }
230
231 return SUCCESS;
232 }
233
234 /* Insert a new node with the given piece of data before the given node in the
235 * given list. */
236 void
237 Lst_InsertBeforeS(Lst list, LstNode node, void *datum)
238 {
239 LstNode newNode;
240
241 assert(LstIsValid(list));
242 assert(!LstIsEmpty(list));
243 assert(LstNodeIsValid(node));
244 assert(datum != NULL);
245
246 newNode = LstNodeNew(datum);
247 newNode->prev = node->prev;
248 newNode->next = node;
249
250 if (node->prev != NULL) {
251 node->prev->next = newNode;
252 }
253 node->prev = newNode;
254
255 if (node == list->first) {
256 list->first = newNode;
257 }
258 }
259
260 /* Insert a new node with the given piece of data after the given node in the
261 * given list. */
262 static ReturnStatus
263 LstInsertAfter(Lst list, LstNode node, void *datum)
264 {
265 LstNode newNode;
266
267 if (LstIsValid(list) && (node == NULL && LstIsEmpty(list))) {
268 goto ok;
269 }
270
271 if (!LstIsValid(list) || LstIsEmpty(list) || !LstNodeIsValid(node)) {
272 return FAILURE;
273 }
274 ok:
275
276 newNode = LstNodeNew(datum);
277
278 if (node == NULL) {
279 newNode->next = newNode->prev = NULL;
280 list->first = list->last = newNode;
281 } else {
282 newNode->prev = node;
283 newNode->next = node->next;
284
285 node->next = newNode;
286 if (newNode->next != NULL) {
287 newNode->next->prev = newNode;
288 }
289
290 if (node == list->last) {
291 list->last = newNode;
292 }
293 }
294
295 return SUCCESS;
296 }
297
298 /* Add a piece of data at the front of the given list. */
299 ReturnStatus
300 Lst_AtFront(Lst list, void *datum)
301 {
302 LstNode front = Lst_First(list);
303 return LstInsertBefore(list, front, datum);
304 }
305
306 /* Add a piece of data at the end of the given list. */
307 ReturnStatus
308 Lst_AtEnd(Lst list, void *datum)
309 {
310 LstNode end = Lst_Last(list);
311 return LstInsertAfter(list, end, datum);
312 }
313
314 /* Add a piece of data at the start of the given list. */
315 void
316 Lst_PrependS(Lst list, void *datum)
317 {
318 LstNode node;
319
320 assert(LstIsValid(list));
321 assert(datum != NULL);
322
323 node = LstNodeNew(datum);
324 node->prev = NULL;
325 node->next = list->first;
326
327 if (list->first == NULL) {
328 list->first = node;
329 list->last = node;
330 } else {
331 list->first->prev = node;
332 list->first = node;
333 }
334 }
335
336 /* Add a piece of data at the end of the given list. */
337 void
338 Lst_AppendS(Lst list, void *datum)
339 {
340 LstNode node;
341
342 assert(LstIsValid(list));
343 assert(datum != NULL);
344
345 node = LstNodeNew(datum);
346 node->prev = list->last;
347 node->next = NULL;
348
349 if (list->last == NULL) {
350 list->first = node;
351 list->last = node;
352 } else {
353 list->last->next = node;
354 list->last = node;
355 }
356 }
357
358 /* Remove the given node from the given list.
359 * The datum stored in the node must be freed by the caller, if necessary. */
360 void
361 Lst_RemoveS(Lst list, LstNode node)
362 {
363 assert(LstIsValid(list));
364 assert(LstNodeIsValid(node));
365
366 /*
367 * unlink it from the list
368 */
369 if (node->next != NULL) {
370 node->next->prev = node->prev;
371 }
372 if (node->prev != NULL) {
373 node->prev->next = node->next;
374 }
375
376 /*
377 * if either the first or last of the list point to this node,
378 * adjust them accordingly
379 */
380 if (list->first == node) {
381 list->first = node->next;
382 }
383 if (list->last == node) {
384 list->last = node->prev;
385 }
386
387 /*
388 * Sequential access stuff. If the node we're removing is the current
389 * node in the list, reset the current node to the previous one. If the
390 * previous one was non-existent (prev == NULL), we set the
391 * end to be Unknown, since it is.
392 */
393 if (list->isOpen && list->curr == node) {
394 list->curr = list->prev;
395 if (list->curr == NULL) {
396 list->lastAccess = Unknown;
397 }
398 }
399
400 /*
401 * note that the datum is unmolested. The caller must free it as
402 * necessary and as expected.
403 */
404 if (node->useCount == 0) {
405 free(node);
406 } else {
407 node->deleted = TRUE;
408 }
409 }
410
411 /* Replace the datum in the given node with the new datum. */
412 void
413 Lst_ReplaceS(LstNode node, void *datum)
414 {
415 node->datum = datum;
416 }
417
418
419 /*
420 * Node-specific functions
421 */
422
423 /* Return the first node from the given list, or NULL if the list is empty or
424 * invalid. */
425 LstNode
426 Lst_First(Lst list)
427 {
428 if (!LstIsValid(list) || LstIsEmpty(list)) {
429 return NULL;
430 } else {
431 return list->first;
432 }
433 }
434
435 /* Return the last node from the given list, or NULL if the list is empty or
436 * invalid. */
437 LstNode
438 Lst_Last(Lst list)
439 {
440 if (!LstIsValid(list) || LstIsEmpty(list)) {
441 return NULL;
442 } else {
443 return list->last;
444 }
445 }
446
447 /* Return the successor to the given node on its list, or NULL. */
448 LstNode
449 Lst_Succ(LstNode node)
450 {
451 if (node == NULL) {
452 return NULL;
453 } else {
454 return node->next;
455 }
456 }
457
458 /* Return the predecessor to the given node on its list, or NULL. */
459 LstNode
460 Lst_Prev(LstNode node)
461 {
462 if (node == NULL) {
463 return NULL;
464 } else {
465 return node->prev;
466 }
467 }
468
469 /* Return the datum stored in the given node. */
470 void *
471 Lst_DatumS(LstNode node)
472 {
473 assert(LstNodeIsValid(node));
474 return node->datum;
475 }
476
477
478 /*
479 * Functions for entire lists
480 */
481
482 /* Return TRUE if the given list is empty or invalid. */
483 Boolean
484 Lst_IsEmpty(Lst list)
485 {
486 return !LstIsValid(list) || LstIsEmpty(list);
487 }
488
489 /* Return the first node from the given list for which the given comparison
490 * function returns 0, or NULL if none of the nodes matches. */
491 LstNode
492 Lst_Find(Lst list, const void *cmpData, int (*cmp)(const void *, const void *))
493 {
494 return Lst_FindFrom(list, Lst_First(list), cmpData, cmp);
495 }
496
497 /* Return the first node from the given list, starting at the given node, for
498 * which the given comparison function returns 0, or NULL if none of the nodes
499 * matches. */
500 LstNode
501 Lst_FindFrom(Lst list, LstNode node, const void *cmpData,
502 int (*cmp)(const void *, const void *))
503 {
504 LstNode tln;
505
506 if (!LstIsValid(list) || LstIsEmpty(list) || !LstNodeIsValid(node)) {
507 return NULL;
508 }
509
510 tln = node;
511
512 do {
513 if ((*cmp)(tln->datum, cmpData) == 0)
514 return tln;
515 tln = tln->next;
516 } while (tln != node && tln != NULL);
517
518 return NULL;
519 }
520
521 /* Return the first node that contains the given datum, or NULL. */
522 LstNode
523 Lst_MemberS(Lst list, void *datum)
524 {
525 LstNode node;
526
527 assert(LstIsValid(list));
528 assert(datum != NULL);
529
530 for (node = list->first; node != NULL; node = node->next) {
531 if (node->datum == datum) {
532 return node;
533 }
534 }
535
536 return NULL;
537 }
538
539 /* Apply the given function to each element of the given list. The function
540 * should return 0 if traversal should continue and non-zero if it should
541 * abort. */
542 int
543 Lst_ForEach(Lst list, int (*proc)(void *, void *), void *procData)
544 {
545 return Lst_ForEachFrom(list, Lst_First(list), proc, procData);
546 }
547
548 /* Apply the given function to each element of the given list, starting from
549 * the given node. The function should return 0 if traversal should continue,
550 * and non-zero if it should abort. */
551 int
552 Lst_ForEachFrom(Lst list, LstNode node,
553 int (*proc)(void *, void *), void *procData)
554 {
555 LstNode tln = node;
556 LstNode next;
557 Boolean done;
558 int result;
559
560 if (!LstIsValid(list) || LstIsEmpty(list)) {
561 return 0;
562 }
563
564 do {
565 /*
566 * Take care of having the current element deleted out from under
567 * us.
568 */
569
570 next = tln->next;
571
572 /*
573 * We're done with the traversal if
574 * - the next node to examine is the first in the queue or
575 * doesn't exist and
576 * - nothing's been added after the current node (check this
577 * after proc() has been called).
578 */
579 done = (next == NULL || next == list->first);
580
581 tln->useCount++;
582 result = (*proc)(tln->datum, procData);
583 tln->useCount--;
584
585 /*
586 * Now check whether a node has been added.
587 * Note: this doesn't work if this node was deleted before
588 * the new node was added.
589 */
590 if (next != tln->next) {
591 next = tln->next;
592 done = 0;
593 }
594
595 if (tln->deleted) {
596 free((char *)tln);
597 }
598 tln = next;
599 } while (!result && !LstIsEmpty(list) && !done);
600
601 return result;
602 }
603
604 /* Concatenate two lists. New nodes are created to hold the data elements,
605 * if specified, but the data themselves are not copied. If the data
606 * should be duplicated to avoid confusion with another list, the Lst_Duplicate
607 * function should be called first. If LST_CONCLINK is specified, the second
608 * list is destroyed since its pointers have been corrupted and the list is no
609 * longer usable.
610 *
611 * Input:
612 * list1 The list to which list2 is to be appended
613 * list2 The list to append to list1
614 * flags LST_CONCNEW if the list nodes should be duplicated
615 * LST_CONCLINK if the list nodes should just be relinked
616 */
617 ReturnStatus
618 Lst_Concat(Lst list1, Lst list2, int flags)
619 {
620 LstNode node; /* original node */
621 LstNode newNode;
622 LstNode last; /* the last element in the list.
623 * Keeps bookkeeping until the end */
624
625 if (!LstIsValid(list1) || !LstIsValid(list2)) {
626 return FAILURE;
627 }
628
629 if (flags == LST_CONCLINK) {
630 if (list2->first != NULL) {
631 /*
632 * So long as the second list isn't empty, we just link the
633 * first element of the second list to the last element of the
634 * first list. If the first list isn't empty, we then link the
635 * last element of the list to the first element of the second list
636 * The last element of the second list, if it exists, then becomes
637 * the last element of the first list.
638 */
639 list2->first->prev = list1->last;
640 if (list1->last != NULL) {
641 list1->last->next = list2->first;
642 } else {
643 list1->first = list2->first;
644 }
645 list1->last = list2->last;
646 }
647 free(list2);
648 } else if (list2->first != NULL) {
649 /*
650 * We set the 'next' of the last element of list 2 to be nil to make
651 * the loop less difficult. The loop simply goes through the entire
652 * second list creating new LstNodes and filling in the 'next', and
653 * 'prev' to fit into list1 and its datum field from the
654 * datum field of the corresponding element in list2. The 'last' node
655 * follows the last of the new nodes along until the entire list2 has
656 * been appended. Only then does the bookkeeping catch up with the
657 * changes. During the first iteration of the loop, if 'last' is nil,
658 * the first list must have been empty so the newly-created node is
659 * made the first node of the list.
660 */
661 list2->last->next = NULL;
662 for (last = list1->last, node = list2->first;
663 node != NULL;
664 node = node->next)
665 {
666 newNode = LstNodeNew(node->datum);
667 if (last != NULL) {
668 last->next = newNode;
669 } else {
670 list1->first = newNode;
671 }
672 newNode->prev = last;
673 last = newNode;
674 }
675
676 /*
677 * Finish bookkeeping. The last new element becomes the last element
678 * of list one.
679 */
680 list1->last = last;
681 last->next = NULL;
682 }
683
684 return SUCCESS;
685 }
686
687 /* Copy the element data from src to the start of dst. */
688 void
689 Lst_PrependAllS(Lst dst, Lst src)
690 {
691 LstNode node;
692 for (node = src->last; node != NULL; node = node->prev)
693 Lst_PrependS(dst, node->datum);
694 }
695
696 /* Copy the element data from src to the end of dst. */
697 void
698 Lst_AppendAllS(Lst dst, Lst src)
699 {
700 LstNode node;
701 for (node = src->first; node != NULL; node = node->next)
702 Lst_AppendS(dst, node->datum);
703 }
704
705 /*
706 * these functions are for dealing with a list as a table, of sorts.
707 * An idea of the "current element" is kept and used by all the functions
708 * between Lst_Open() and Lst_Close().
709 *
710 * The sequential functions access the list in a slightly different way.
711 * CurPtr points to their idea of the current node in the list and they
712 * access the list based on it.
713 */
714
715 /* Open a list for sequential access. A list can still be searched, etc.,
716 * without confusing these functions. */
717 ReturnStatus
718 Lst_Open(Lst list)
719 {
720 if (!LstIsValid(list)) {
721 return FAILURE;
722 }
723 Lst_OpenS(list);
724 return SUCCESS;
725 }
726
727 /* Open a list for sequential access. A list can still be searched, etc.,
728 * without confusing these functions. */
729 void
730 Lst_OpenS(Lst list)
731 {
732 assert(LstIsValid(list));
733
734 /* XXX: This assertion fails for NetBSD's "build.sh -j1 tools", somewhere
735 * between "dependall ===> compat" and "dependall ===> binstall".
736 * Building without the "-j1" succeeds though. */
737 if (DEBUG(LINT) && list->isOpen)
738 Parse_Error(PARSE_WARNING, "Internal inconsistency: list opened twice");
739
740 list->isOpen = TRUE;
741 list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
742 list->curr = NULL;
743 }
744
745 /* Return the next node for the given list, or NULL if the end has been
746 * reached. */
747 LstNode
748 Lst_NextS(Lst list)
749 {
750 LstNode node;
751
752 assert(LstIsValid(list));
753 assert(list->isOpen);
754
755 list->prev = list->curr;
756
757 if (list->curr == NULL) {
758 if (list->lastAccess == Unknown) {
759 /*
760 * If we're just starting out, lastAccess will be Unknown.
761 * Then we want to start this thing off in the right
762 * direction -- at the start with lastAccess being Middle.
763 */
764 list->curr = node = list->first;
765 list->lastAccess = Middle;
766 } else {
767 node = NULL;
768 list->lastAccess = Tail;
769 }
770 } else {
771 node = list->curr->next;
772 list->curr = node;
773
774 if (node == list->first || node == NULL) {
775 /*
776 * If back at the front, then we've hit the end...
777 */
778 list->lastAccess = Tail;
779 } else {
780 /*
781 * Reset to Middle if gone past first.
782 */
783 list->lastAccess = Middle;
784 }
785 }
786
787 return node;
788 }
789
790 /* Close a list which was opened for sequential access. */
791 void
792 Lst_CloseS(Lst list)
793 {
794 assert(LstIsValid(list));
795 assert(list->isOpen);
796
797 list->isOpen = FALSE;
798 list->lastAccess = Unknown;
799 }
800
801
802 /*
803 * for using the list as a queue
804 */
805
806 /* Add the datum to the tail of the given list. */
807 void
808 Lst_EnqueueS(Lst list, void *datum)
809 {
810 Lst_AppendS(list, datum);
811 }
812
813 /* Remove and return the datum at the head of the given list. */
814 void *
815 Lst_DequeueS(Lst list)
816 {
817 void *datum;
818
819 assert(LstIsValid(list));
820 assert(!LstIsEmpty(list));
821
822 datum = list->first->datum;
823 Lst_RemoveS(list, list->first);
824 assert(datum != NULL);
825 return datum;
826 }
827