lst.c revision 1.25 1 /* $NetBSD: lst.c,v 1.25 2020/08/22 14:39:12 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.25 2020/08/22 14:39:12 rillig Exp $";
41 #else
42 #include <sys/cdefs.h>
43 #ifndef lint
44 __RCSID("$NetBSD: lst.c,v 1.25 2020/08/22 14:39:12 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 ReturnStatus
196 Lst_InsertBefore(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 after the given node in the
234 * given list. */
235 ReturnStatus
236 Lst_InsertAfter(Lst list, LstNode node, void *datum)
237 {
238 LstNode newNode;
239
240 if (LstIsValid(list) && (node == NULL && LstIsEmpty(list))) {
241 goto ok;
242 }
243
244 if (!LstIsValid(list) || LstIsEmpty(list) || !LstNodeIsValid(node)) {
245 return FAILURE;
246 }
247 ok:
248
249 newNode = LstNodeNew(datum);
250
251 if (node == NULL) {
252 newNode->next = newNode->prev = NULL;
253 list->first = list->last = newNode;
254 } else {
255 newNode->prev = node;
256 newNode->next = node->next;
257
258 node->next = newNode;
259 if (newNode->next != NULL) {
260 newNode->next->prev = newNode;
261 }
262
263 if (node == list->last) {
264 list->last = newNode;
265 }
266 }
267
268 return SUCCESS;
269 }
270
271 /* Add a piece of data at the front of the given list. */
272 ReturnStatus
273 Lst_AtFront(Lst list, void *datum)
274 {
275 LstNode front = Lst_First(list);
276 return Lst_InsertBefore(list, front, datum);
277 }
278
279 /* Add a piece of data at the end of the given list. */
280 ReturnStatus
281 Lst_AtEnd(Lst list, void *datum)
282 {
283 LstNode end = Lst_Last(list);
284 return Lst_InsertAfter(list, end, datum);
285 }
286
287 /* Add a piece of data at the start of the given list. */
288 void
289 Lst_PrependS(Lst list, void *datum)
290 {
291 LstNode node;
292
293 assert(LstIsValid(list));
294 assert(datum != NULL);
295
296 node = LstNodeNew(datum);
297 node->prev = NULL;
298 node->next = list->first;
299
300 if (list->first == NULL) {
301 list->first = node;
302 list->last = node;
303 } else {
304 list->first->prev = node;
305 list->first = node;
306 }
307 }
308
309 /* Add a piece of data at the end of the given list. */
310 void
311 Lst_AppendS(Lst list, void *datum)
312 {
313 LstNode node;
314
315 assert(LstIsValid(list));
316 assert(datum != NULL);
317
318 node = LstNodeNew(datum);
319 node->prev = list->last;
320 node->next = NULL;
321
322 if (list->last == NULL) {
323 list->first = node;
324 list->last = node;
325 } else {
326 list->last->next = node;
327 list->last = node;
328 }
329 }
330
331 /* Remove the given node from the given list.
332 * The datum stored in the node must be freed by the caller, if necessary. */
333 void
334 Lst_RemoveS(Lst list, LstNode node)
335 {
336 assert(LstIsValid(list));
337 assert(LstNodeIsValid(node));
338
339 /*
340 * unlink it from the list
341 */
342 if (node->next != NULL) {
343 node->next->prev = node->prev;
344 }
345 if (node->prev != NULL) {
346 node->prev->next = node->next;
347 }
348
349 /*
350 * if either the first or last of the list point to this node,
351 * adjust them accordingly
352 */
353 if (list->first == node) {
354 list->first = node->next;
355 }
356 if (list->last == node) {
357 list->last = node->prev;
358 }
359
360 /*
361 * Sequential access stuff. If the node we're removing is the current
362 * node in the list, reset the current node to the previous one. If the
363 * previous one was non-existent (prev == NULL), we set the
364 * end to be Unknown, since it is.
365 */
366 if (list->isOpen && list->curr == node) {
367 list->curr = list->prev;
368 if (list->curr == NULL) {
369 list->lastAccess = Unknown;
370 }
371 }
372
373 /*
374 * note that the datum is unmolested. The caller must free it as
375 * necessary and as expected.
376 */
377 if (node->useCount == 0) {
378 free(node);
379 } else {
380 node->deleted = TRUE;
381 }
382 }
383
384 /* Replace the datum in the given node with the new datum. */
385 void
386 Lst_ReplaceS(LstNode node, void *datum)
387 {
388 node->datum = datum;
389 }
390
391
392 /*
393 * Node-specific functions
394 */
395
396 /* Return the first node from the given list, or NULL if the list is empty or
397 * invalid. */
398 LstNode
399 Lst_First(Lst list)
400 {
401 if (!LstIsValid(list) || LstIsEmpty(list)) {
402 return NULL;
403 } else {
404 return list->first;
405 }
406 }
407
408 /* Return the last node from the given list, or NULL if the list is empty or
409 * invalid. */
410 LstNode
411 Lst_Last(Lst list)
412 {
413 if (!LstIsValid(list) || LstIsEmpty(list)) {
414 return NULL;
415 } else {
416 return list->last;
417 }
418 }
419
420 /* Return the successor to the given node on its list, or NULL. */
421 LstNode
422 Lst_Succ(LstNode node)
423 {
424 if (node == NULL) {
425 return NULL;
426 } else {
427 return node->next;
428 }
429 }
430
431 /* Return the predecessor to the given node on its list, or NULL. */
432 LstNode
433 Lst_Prev(LstNode node)
434 {
435 if (node == NULL) {
436 return NULL;
437 } else {
438 return node->prev;
439 }
440 }
441
442 /* Return the datum stored in the given node, or NULL if the node is invalid. */
443 void *
444 Lst_Datum(LstNode node)
445 {
446 if (node != NULL) {
447 return node->datum;
448 } else {
449 return NULL;
450 }
451 }
452
453
454 /*
455 * Functions for entire lists
456 */
457
458 /* Return TRUE if the given list is empty or invalid. */
459 Boolean
460 Lst_IsEmpty(Lst list)
461 {
462 return !LstIsValid(list) || LstIsEmpty(list);
463 }
464
465 /* Return the first node from the given list for which the given comparison
466 * function returns 0, or NULL if none of the nodes matches. */
467 LstNode
468 Lst_Find(Lst list, const void *cmpData, int (*cmp)(const void *, const void *))
469 {
470 return Lst_FindFrom(list, Lst_First(list), cmpData, cmp);
471 }
472
473 /* Return the first node from the given list, starting at the given node, for
474 * which the given comparison function returns 0, or NULL if none of the nodes
475 * matches. */
476 LstNode
477 Lst_FindFrom(Lst list, LstNode node, const void *cmpData,
478 int (*cmp)(const void *, const void *))
479 {
480 LstNode tln;
481
482 if (!LstIsValid(list) || LstIsEmpty(list) || !LstNodeIsValid(node)) {
483 return NULL;
484 }
485
486 tln = node;
487
488 do {
489 if ((*cmp)(tln->datum, cmpData) == 0)
490 return tln;
491 tln = tln->next;
492 } while (tln != node && tln != NULL);
493
494 return NULL;
495 }
496
497 /* Return the first node that contains the given datum, or NULL. */
498 LstNode
499 Lst_Member(Lst list, void *datum)
500 {
501 LstNode node;
502
503 if (list == NULL) {
504 return NULL;
505 }
506 node = list->first;
507 if (node == NULL) {
508 return NULL;
509 }
510
511 do {
512 if (node->datum == datum) {
513 return node;
514 }
515 node = node->next;
516 } while (node != NULL && node != list->first);
517
518 return NULL;
519 }
520
521 /* Apply the given function to each element of the given list. The function
522 * should return 0 if traversal should continue and non-zero if it should
523 * abort. */
524 int
525 Lst_ForEach(Lst list, int (*proc)(void *, void *), void *procData)
526 {
527 return Lst_ForEachFrom(list, Lst_First(list), proc, procData);
528 }
529
530 /* Apply the given function to each element of the given list, starting from
531 * the given node. The function should return 0 if traversal should continue,
532 * and non-zero if it should abort. */
533 int
534 Lst_ForEachFrom(Lst list, LstNode node,
535 int (*proc)(void *, void *), void *procData)
536 {
537 LstNode tln = node;
538 LstNode next;
539 Boolean done;
540 int result;
541
542 if (!LstIsValid(list) || LstIsEmpty(list)) {
543 return 0;
544 }
545
546 do {
547 /*
548 * Take care of having the current element deleted out from under
549 * us.
550 */
551
552 next = tln->next;
553
554 /*
555 * We're done with the traversal if
556 * - the next node to examine is the first in the queue or
557 * doesn't exist and
558 * - nothing's been added after the current node (check this
559 * after proc() has been called).
560 */
561 done = (next == NULL || next == list->first);
562
563 tln->useCount++;
564 result = (*proc)(tln->datum, procData);
565 tln->useCount--;
566
567 /*
568 * Now check whether a node has been added.
569 * Note: this doesn't work if this node was deleted before
570 * the new node was added.
571 */
572 if (next != tln->next) {
573 next = tln->next;
574 done = 0;
575 }
576
577 if (tln->deleted) {
578 free((char *)tln);
579 }
580 tln = next;
581 } while (!result && !LstIsEmpty(list) && !done);
582
583 return result;
584 }
585
586 /* Concatenate two lists. New nodes are created to hold the data elements,
587 * if specified, but the data themselves are not copied. If the data
588 * should be duplicated to avoid confusion with another list, the Lst_Duplicate
589 * function should be called first. If LST_CONCLINK is specified, the second
590 * list is destroyed since its pointers have been corrupted and the list is no
591 * longer usable.
592 *
593 * Input:
594 * list1 The list to which list2 is to be appended
595 * list2 The list to append to list1
596 * flags LST_CONCNEW if the list nodes should be duplicated
597 * LST_CONCLINK if the list nodes should just be relinked
598 */
599 ReturnStatus
600 Lst_Concat(Lst list1, Lst list2, int flags)
601 {
602 LstNode node; /* original node */
603 LstNode newNode;
604 LstNode last; /* the last element in the list.
605 * Keeps bookkeeping until the end */
606
607 if (!LstIsValid(list1) || !LstIsValid(list2)) {
608 return FAILURE;
609 }
610
611 if (flags == LST_CONCLINK) {
612 if (list2->first != NULL) {
613 /*
614 * So long as the second list isn't empty, we just link the
615 * first element of the second list to the last element of the
616 * first list. If the first list isn't empty, we then link the
617 * last element of the list to the first element of the second list
618 * The last element of the second list, if it exists, then becomes
619 * the last element of the first list.
620 */
621 list2->first->prev = list1->last;
622 if (list1->last != NULL) {
623 list1->last->next = list2->first;
624 } else {
625 list1->first = list2->first;
626 }
627 list1->last = list2->last;
628 }
629 free(list2);
630 } else if (list2->first != NULL) {
631 /*
632 * We set the 'next' of the last element of list 2 to be nil to make
633 * the loop less difficult. The loop simply goes through the entire
634 * second list creating new LstNodes and filling in the 'next', and
635 * 'prev' to fit into list1 and its datum field from the
636 * datum field of the corresponding element in list2. The 'last' node
637 * follows the last of the new nodes along until the entire list2 has
638 * been appended. Only then does the bookkeeping catch up with the
639 * changes. During the first iteration of the loop, if 'last' is nil,
640 * the first list must have been empty so the newly-created node is
641 * made the first node of the list.
642 */
643 list2->last->next = NULL;
644 for (last = list1->last, node = list2->first;
645 node != NULL;
646 node = node->next)
647 {
648 newNode = LstNodeNew(node->datum);
649 if (last != NULL) {
650 last->next = newNode;
651 } else {
652 list1->first = newNode;
653 }
654 newNode->prev = last;
655 last = newNode;
656 }
657
658 /*
659 * Finish bookkeeping. The last new element becomes the last element
660 * of list one.
661 */
662 list1->last = last;
663 last->next = NULL;
664 }
665
666 return SUCCESS;
667 }
668
669 /* Copy the element data from src to the start of dst. */
670 void
671 Lst_PrependAllS(Lst dst, Lst src)
672 {
673 LstNode node;
674 for (node = src->last; node != NULL; node = node->prev)
675 Lst_PrependS(dst, node->datum);
676 }
677
678 /* Copy the element data from src to the end of dst. */
679 void
680 Lst_AppendAllS(Lst dst, Lst src)
681 {
682 LstNode node;
683 for (node = src->first; node != NULL; node = node->next)
684 Lst_AppendS(dst, node->datum);
685 }
686
687 /*
688 * these functions are for dealing with a list as a table, of sorts.
689 * An idea of the "current element" is kept and used by all the functions
690 * between Lst_Open() and Lst_Close().
691 *
692 * The sequential functions access the list in a slightly different way.
693 * CurPtr points to their idea of the current node in the list and they
694 * access the list based on it.
695 */
696
697 /* Open a list for sequential access. A list can still be searched, etc.,
698 * without confusing these functions. */
699 ReturnStatus
700 Lst_Open(Lst list)
701 {
702 if (!LstIsValid(list)) {
703 return FAILURE;
704 }
705 Lst_OpenS(list);
706 return SUCCESS;
707 }
708
709 /* Open a list for sequential access. A list can still be searched, etc.,
710 * without confusing these functions. */
711 void
712 Lst_OpenS(Lst list)
713 {
714 assert(LstIsValid(list));
715
716 /* XXX: This assertion fails for NetBSD's "build.sh -j1 tools", somewhere
717 * between "dependall ===> compat" and "dependall ===> binstall".
718 * Building without the "-j1" succeeds though. */
719 if (DEBUG(LINT) && list->isOpen)
720 Parse_Error(PARSE_WARNING, "Internal inconsistency: list opened twice");
721
722 list->isOpen = TRUE;
723 list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
724 list->curr = NULL;
725 }
726
727 /* Return the next node for the given list, or NULL if the end has been
728 * reached. */
729 LstNode
730 Lst_NextS(Lst list)
731 {
732 LstNode node;
733
734 assert(LstIsValid(list));
735 assert(list->isOpen);
736
737 list->prev = list->curr;
738
739 if (list->curr == NULL) {
740 if (list->lastAccess == Unknown) {
741 /*
742 * If we're just starting out, lastAccess will be Unknown.
743 * Then we want to start this thing off in the right
744 * direction -- at the start with lastAccess being Middle.
745 */
746 list->curr = node = list->first;
747 list->lastAccess = Middle;
748 } else {
749 node = NULL;
750 list->lastAccess = Tail;
751 }
752 } else {
753 node = list->curr->next;
754 list->curr = node;
755
756 if (node == list->first || node == NULL) {
757 /*
758 * If back at the front, then we've hit the end...
759 */
760 list->lastAccess = Tail;
761 } else {
762 /*
763 * Reset to Middle if gone past first.
764 */
765 list->lastAccess = Middle;
766 }
767 }
768
769 return node;
770 }
771
772 /* Close a list which was opened for sequential access. */
773 void
774 Lst_CloseS(Lst list)
775 {
776 assert(LstIsValid(list));
777 assert(list->isOpen);
778
779 list->isOpen = FALSE;
780 list->lastAccess = Unknown;
781 }
782
783
784 /*
785 * for using the list as a queue
786 */
787
788 /* Add the datum to the tail of the given list. */
789 void
790 Lst_EnqueueS(Lst list, void *datum)
791 {
792 Lst_AppendS(list, datum);
793 }
794
795 /* Remove and return the datum at the head of the given list. */
796 void *
797 Lst_DequeueS(Lst list)
798 {
799 void *datum;
800
801 assert(LstIsValid(list));
802 assert(!LstIsEmpty(list));
803
804 datum = list->first->datum;
805 Lst_RemoveS(list, list->first);
806 assert(datum != NULL);
807 return datum;
808 }
809