lst.c revision 1.34 1 /* $NetBSD: lst.c,v 1.34 2020/08/22 22:41:42 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.34 2020/08/22 22:41:42 rillig Exp $";
41 #else
42 #include <sys/cdefs.h>
43 #ifndef lint
44 __RCSID("$NetBSD: lst.c,v 1.34 2020/08/22 22:41:42 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 /* Move all nodes from list2 to the end of list1.
601 * List2 is destroyed and freed. */
602 void
603 Lst_MoveAllS(Lst list1, Lst list2)
604 {
605 assert(LstIsValid(list1));
606 assert(LstIsValid(list2));
607
608 if (list2->first != NULL) {
609 list2->first->prev = list1->last;
610 if (list1->last != NULL) {
611 list1->last->next = list2->first;
612 } else {
613 list1->first = list2->first;
614 }
615 list1->last = list2->last;
616 }
617 free(list2);
618 }
619
620 /* Copy the element data from src to the start of dst. */
621 void
622 Lst_PrependAllS(Lst dst, Lst src)
623 {
624 LstNode node;
625 for (node = src->last; node != NULL; node = node->prev)
626 Lst_PrependS(dst, node->datum);
627 }
628
629 /* Copy the element data from src to the end of dst. */
630 void
631 Lst_AppendAllS(Lst dst, Lst src)
632 {
633 LstNode node;
634 for (node = src->first; node != NULL; node = node->next)
635 Lst_AppendS(dst, node->datum);
636 }
637
638 /*
639 * these functions are for dealing with a list as a table, of sorts.
640 * An idea of the "current element" is kept and used by all the functions
641 * between Lst_Open() and Lst_Close().
642 *
643 * The sequential functions access the list in a slightly different way.
644 * CurPtr points to their idea of the current node in the list and they
645 * access the list based on it.
646 */
647
648 /* Open a list for sequential access. A list can still be searched, etc.,
649 * without confusing these functions. */
650 ReturnStatus
651 Lst_Open(Lst list)
652 {
653 if (!LstIsValid(list)) {
654 return FAILURE;
655 }
656 Lst_OpenS(list);
657 return SUCCESS;
658 }
659
660 /* Open a list for sequential access. A list can still be searched, etc.,
661 * without confusing these functions. */
662 void
663 Lst_OpenS(Lst list)
664 {
665 assert(LstIsValid(list));
666
667 /* XXX: This assertion fails for NetBSD's "build.sh -j1 tools", somewhere
668 * between "dependall ===> compat" and "dependall ===> binstall".
669 * Building without the "-j1" succeeds though. */
670 if (DEBUG(LINT) && list->isOpen)
671 Parse_Error(PARSE_WARNING, "Internal inconsistency: list opened twice");
672
673 list->isOpen = TRUE;
674 list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
675 list->curr = NULL;
676 }
677
678 /* Return the next node for the given list, or NULL if the end has been
679 * reached. */
680 LstNode
681 Lst_NextS(Lst list)
682 {
683 LstNode node;
684
685 assert(LstIsValid(list));
686 assert(list->isOpen);
687
688 list->prev = list->curr;
689
690 if (list->curr == NULL) {
691 if (list->lastAccess == Unknown) {
692 /*
693 * If we're just starting out, lastAccess will be Unknown.
694 * Then we want to start this thing off in the right
695 * direction -- at the start with lastAccess being Middle.
696 */
697 list->curr = node = list->first;
698 list->lastAccess = Middle;
699 } else {
700 node = NULL;
701 list->lastAccess = Tail;
702 }
703 } else {
704 node = list->curr->next;
705 list->curr = node;
706
707 if (node == list->first || node == NULL) {
708 /*
709 * If back at the front, then we've hit the end...
710 */
711 list->lastAccess = Tail;
712 } else {
713 /*
714 * Reset to Middle if gone past first.
715 */
716 list->lastAccess = Middle;
717 }
718 }
719
720 return node;
721 }
722
723 /* Close a list which was opened for sequential access. */
724 void
725 Lst_CloseS(Lst list)
726 {
727 assert(LstIsValid(list));
728 assert(list->isOpen);
729
730 list->isOpen = FALSE;
731 list->lastAccess = Unknown;
732 }
733
734
735 /*
736 * for using the list as a queue
737 */
738
739 /* Add the datum to the tail of the given list. */
740 void
741 Lst_EnqueueS(Lst list, void *datum)
742 {
743 Lst_AppendS(list, datum);
744 }
745
746 /* Remove and return the datum at the head of the given list. */
747 void *
748 Lst_DequeueS(Lst list)
749 {
750 void *datum;
751
752 assert(LstIsValid(list));
753 assert(!LstIsEmpty(list));
754
755 datum = list->first->datum;
756 Lst_RemoveS(list, list->first);
757 assert(datum != NULL);
758 return datum;
759 }
760