lst.c revision 1.28 1 /* $NetBSD: lst.c,v 1.28 2020/08/22 15:17:09 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 <assert.h>
36
37 #include "make.h"
38
39 #ifndef MAKE_NATIVE
40 static char rcsid[] = "$NetBSD: lst.c,v 1.28 2020/08/22 15:17:09 rillig Exp $";
41 #else
42 #include <sys/cdefs.h>
43 #ifndef lint
44 __RCSID("$NetBSD: lst.c,v 1.28 2020/08/22 15:17:09 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_Prev(LstNode node)
460 {
461 if (node == NULL) {
462 return NULL;
463 } else {
464 return node->prev;
465 }
466 }
467
468 /* Return the datum stored in the given node. */
469 void *
470 Lst_DatumS(LstNode node)
471 {
472 assert(LstNodeIsValid(node));
473 return node->datum;
474 }
475
476
477 /*
478 * Functions for entire lists
479 */
480
481 /* Return TRUE if the given list is empty or invalid. */
482 Boolean
483 Lst_IsEmpty(Lst list)
484 {
485 return !LstIsValid(list) || LstIsEmpty(list);
486 }
487
488 /* Return the first node from the given list for which the given comparison
489 * function returns 0, or NULL if none of the nodes matches. */
490 LstNode
491 Lst_Find(Lst list, const void *cmpData, int (*cmp)(const void *, const void *))
492 {
493 return Lst_FindFrom(list, Lst_First(list), cmpData, cmp);
494 }
495
496 /* Return the first node from the given list, starting at the given node, for
497 * which the given comparison function returns 0, or NULL if none of the nodes
498 * matches. */
499 LstNode
500 Lst_FindFrom(Lst list, LstNode node, const void *cmpData,
501 int (*cmp)(const void *, const void *))
502 {
503 LstNode tln;
504
505 if (!LstIsValid(list) || LstIsEmpty(list) || !LstNodeIsValid(node)) {
506 return NULL;
507 }
508
509 tln = node;
510
511 do {
512 if ((*cmp)(tln->datum, cmpData) == 0)
513 return tln;
514 tln = tln->next;
515 } while (tln != node && tln != NULL);
516
517 return NULL;
518 }
519
520 /* Return the first node that contains the given datum, or NULL. */
521 LstNode
522 Lst_Member(Lst list, void *datum)
523 {
524 LstNode node;
525
526 if (list == NULL) {
527 return NULL;
528 }
529 node = list->first;
530 if (node == NULL) {
531 return NULL;
532 }
533
534 do {
535 if (node->datum == datum) {
536 return node;
537 }
538 node = node->next;
539 } while (node != NULL && node != list->first);
540
541 return NULL;
542 }
543
544 /* Apply the given function to each element of the given list. The function
545 * should return 0 if traversal should continue and non-zero if it should
546 * abort. */
547 int
548 Lst_ForEach(Lst list, int (*proc)(void *, void *), void *procData)
549 {
550 return Lst_ForEachFrom(list, Lst_First(list), proc, procData);
551 }
552
553 /* Apply the given function to each element of the given list, starting from
554 * the given node. The function should return 0 if traversal should continue,
555 * and non-zero if it should abort. */
556 int
557 Lst_ForEachFrom(Lst list, LstNode node,
558 int (*proc)(void *, void *), void *procData)
559 {
560 LstNode tln = node;
561 LstNode next;
562 Boolean done;
563 int result;
564
565 if (!LstIsValid(list) || LstIsEmpty(list)) {
566 return 0;
567 }
568
569 do {
570 /*
571 * Take care of having the current element deleted out from under
572 * us.
573 */
574
575 next = tln->next;
576
577 /*
578 * We're done with the traversal if
579 * - the next node to examine is the first in the queue or
580 * doesn't exist and
581 * - nothing's been added after the current node (check this
582 * after proc() has been called).
583 */
584 done = (next == NULL || next == list->first);
585
586 tln->useCount++;
587 result = (*proc)(tln->datum, procData);
588 tln->useCount--;
589
590 /*
591 * Now check whether a node has been added.
592 * Note: this doesn't work if this node was deleted before
593 * the new node was added.
594 */
595 if (next != tln->next) {
596 next = tln->next;
597 done = 0;
598 }
599
600 if (tln->deleted) {
601 free((char *)tln);
602 }
603 tln = next;
604 } while (!result && !LstIsEmpty(list) && !done);
605
606 return result;
607 }
608
609 /* Concatenate two lists. New nodes are created to hold the data elements,
610 * if specified, but the data themselves are not copied. If the data
611 * should be duplicated to avoid confusion with another list, the Lst_Duplicate
612 * function should be called first. If LST_CONCLINK is specified, the second
613 * list is destroyed since its pointers have been corrupted and the list is no
614 * longer usable.
615 *
616 * Input:
617 * list1 The list to which list2 is to be appended
618 * list2 The list to append to list1
619 * flags LST_CONCNEW if the list nodes should be duplicated
620 * LST_CONCLINK if the list nodes should just be relinked
621 */
622 ReturnStatus
623 Lst_Concat(Lst list1, Lst list2, int flags)
624 {
625 LstNode node; /* original node */
626 LstNode newNode;
627 LstNode last; /* the last element in the list.
628 * Keeps bookkeeping until the end */
629
630 if (!LstIsValid(list1) || !LstIsValid(list2)) {
631 return FAILURE;
632 }
633
634 if (flags == LST_CONCLINK) {
635 if (list2->first != NULL) {
636 /*
637 * So long as the second list isn't empty, we just link the
638 * first element of the second list to the last element of the
639 * first list. If the first list isn't empty, we then link the
640 * last element of the list to the first element of the second list
641 * The last element of the second list, if it exists, then becomes
642 * the last element of the first list.
643 */
644 list2->first->prev = list1->last;
645 if (list1->last != NULL) {
646 list1->last->next = list2->first;
647 } else {
648 list1->first = list2->first;
649 }
650 list1->last = list2->last;
651 }
652 free(list2);
653 } else if (list2->first != NULL) {
654 /*
655 * We set the 'next' of the last element of list 2 to be nil to make
656 * the loop less difficult. The loop simply goes through the entire
657 * second list creating new LstNodes and filling in the 'next', and
658 * 'prev' to fit into list1 and its datum field from the
659 * datum field of the corresponding element in list2. The 'last' node
660 * follows the last of the new nodes along until the entire list2 has
661 * been appended. Only then does the bookkeeping catch up with the
662 * changes. During the first iteration of the loop, if 'last' is nil,
663 * the first list must have been empty so the newly-created node is
664 * made the first node of the list.
665 */
666 list2->last->next = NULL;
667 for (last = list1->last, node = list2->first;
668 node != NULL;
669 node = node->next)
670 {
671 newNode = LstNodeNew(node->datum);
672 if (last != NULL) {
673 last->next = newNode;
674 } else {
675 list1->first = newNode;
676 }
677 newNode->prev = last;
678 last = newNode;
679 }
680
681 /*
682 * Finish bookkeeping. The last new element becomes the last element
683 * of list one.
684 */
685 list1->last = last;
686 last->next = NULL;
687 }
688
689 return SUCCESS;
690 }
691
692 /* Copy the element data from src to the start of dst. */
693 void
694 Lst_PrependAllS(Lst dst, Lst src)
695 {
696 LstNode node;
697 for (node = src->last; node != NULL; node = node->prev)
698 Lst_PrependS(dst, node->datum);
699 }
700
701 /* Copy the element data from src to the end of dst. */
702 void
703 Lst_AppendAllS(Lst dst, Lst src)
704 {
705 LstNode node;
706 for (node = src->first; node != NULL; node = node->next)
707 Lst_AppendS(dst, node->datum);
708 }
709
710 /*
711 * these functions are for dealing with a list as a table, of sorts.
712 * An idea of the "current element" is kept and used by all the functions
713 * between Lst_Open() and Lst_Close().
714 *
715 * The sequential functions access the list in a slightly different way.
716 * CurPtr points to their idea of the current node in the list and they
717 * access the list based on it.
718 */
719
720 /* Open a list for sequential access. A list can still be searched, etc.,
721 * without confusing these functions. */
722 ReturnStatus
723 Lst_Open(Lst list)
724 {
725 if (!LstIsValid(list)) {
726 return FAILURE;
727 }
728 Lst_OpenS(list);
729 return SUCCESS;
730 }
731
732 /* Open a list for sequential access. A list can still be searched, etc.,
733 * without confusing these functions. */
734 void
735 Lst_OpenS(Lst list)
736 {
737 assert(LstIsValid(list));
738
739 /* XXX: This assertion fails for NetBSD's "build.sh -j1 tools", somewhere
740 * between "dependall ===> compat" and "dependall ===> binstall".
741 * Building without the "-j1" succeeds though. */
742 if (DEBUG(LINT) && list->isOpen)
743 Parse_Error(PARSE_WARNING, "Internal inconsistency: list opened twice");
744
745 list->isOpen = TRUE;
746 list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
747 list->curr = NULL;
748 }
749
750 /* Return the next node for the given list, or NULL if the end has been
751 * reached. */
752 LstNode
753 Lst_NextS(Lst list)
754 {
755 LstNode node;
756
757 assert(LstIsValid(list));
758 assert(list->isOpen);
759
760 list->prev = list->curr;
761
762 if (list->curr == NULL) {
763 if (list->lastAccess == Unknown) {
764 /*
765 * If we're just starting out, lastAccess will be Unknown.
766 * Then we want to start this thing off in the right
767 * direction -- at the start with lastAccess being Middle.
768 */
769 list->curr = node = list->first;
770 list->lastAccess = Middle;
771 } else {
772 node = NULL;
773 list->lastAccess = Tail;
774 }
775 } else {
776 node = list->curr->next;
777 list->curr = node;
778
779 if (node == list->first || node == NULL) {
780 /*
781 * If back at the front, then we've hit the end...
782 */
783 list->lastAccess = Tail;
784 } else {
785 /*
786 * Reset to Middle if gone past first.
787 */
788 list->lastAccess = Middle;
789 }
790 }
791
792 return node;
793 }
794
795 /* Close a list which was opened for sequential access. */
796 void
797 Lst_CloseS(Lst list)
798 {
799 assert(LstIsValid(list));
800 assert(list->isOpen);
801
802 list->isOpen = FALSE;
803 list->lastAccess = Unknown;
804 }
805
806
807 /*
808 * for using the list as a queue
809 */
810
811 /* Add the datum to the tail of the given list. */
812 void
813 Lst_EnqueueS(Lst list, void *datum)
814 {
815 Lst_AppendS(list, datum);
816 }
817
818 /* Remove and return the datum at the head of the given list. */
819 void *
820 Lst_DequeueS(Lst list)
821 {
822 void *datum;
823
824 assert(LstIsValid(list));
825 assert(!LstIsEmpty(list));
826
827 datum = list->first->datum;
828 Lst_RemoveS(list, list->first);
829 assert(datum != NULL);
830 return datum;
831 }
832