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