lst.c revision 1.35 1 /* $NetBSD: lst.c,v 1.35 2020/08/22 22:57:53 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.35 2020/08/22 22:57:53 rillig Exp $";
41 #else
42 #include <sys/cdefs.h>
43 #ifndef lint
44 __RCSID("$NetBSD: lst.c,v 1.35 2020/08/22 22:57:53 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 Boolean
75 LstIsValid(Lst list)
76 {
77 return list != NULL;
78 }
79
80 static Boolean
81 LstNodeIsValid(LstNode node)
82 {
83 return node != NULL;
84 }
85
86 /* Allocate and initialize a list node.
87 *
88 * The fields 'prev' and 'next' must be initialized by the caller.
89 */
90 static LstNode
91 LstNodeNew(void *datum)
92 {
93 LstNode node = bmake_malloc(sizeof *node);
94 node->useCount = 0;
95 node->deleted = FALSE;
96 node->datum = datum;
97 return node;
98 }
99
100 static Boolean
101 LstIsEmpty(Lst list)
102 {
103 return list->first == NULL;
104 }
105
106 /* Create and initialize a new, empty list. */
107 Lst
108 Lst_Init(void)
109 {
110 Lst list = bmake_malloc(sizeof *list);
111
112 list->first = NULL;
113 list->last = NULL;
114 list->isOpen = FALSE;
115 list->lastAccess = Unknown;
116
117 return list;
118 }
119
120 /* Duplicate an entire list, usually by copying the datum pointers.
121 * If copyProc is given, that function is used to create the new datum from the
122 * old datum, usually by creating a copy of it. */
123 Lst
124 Lst_CopyS(Lst list, DuplicateProc *copyProc)
125 {
126 Lst newList;
127 LstNode node;
128
129 assert(LstIsValid(list));
130
131 newList = Lst_Init();
132
133 for (node = list->first; node != NULL; node = node->next) {
134 void *datum = copyProc != NULL ? copyProc(node->datum) : node->datum;
135 Lst_AppendS(newList, datum);
136 }
137
138 return newList;
139 }
140
141 /* Destroy a list and free all its resources. If the freeProc is given, it is
142 * called with the datum from each node in turn before the node is freed. */
143 void
144 Lst_Destroy(Lst list, FreeProc *freeProc)
145 {
146 LstNode node;
147 LstNode next = NULL;
148
149 if (list == NULL)
150 return;
151
152 /* To ease scanning */
153 if (list->last != NULL)
154 list->last->next = NULL;
155 else {
156 free(list);
157 return;
158 }
159
160 if (freeProc) {
161 for (node = list->first; node != NULL; node = next) {
162 next = node->next;
163 freeProc(node->datum);
164 free(node);
165 }
166 } else {
167 for (node = list->first; node != NULL; node = next) {
168 next = node->next;
169 free(node);
170 }
171 }
172
173 free(list);
174 }
175
176 /*
177 * Functions to modify a list
178 */
179
180 /* Insert a new node with the given piece of data before the given node in the
181 * given list. */
182 static ReturnStatus
183 LstInsertBefore(Lst list, LstNode node, void *datum)
184 {
185 LstNode newNode;
186
187 /*
188 * check validity of arguments
189 */
190 if (LstIsValid(list) && (LstIsEmpty(list) && node == NULL))
191 goto ok;
192
193 if (!LstIsValid(list) || LstIsEmpty(list) || !LstNodeIsValid(node)) {
194 return FAILURE;
195 }
196
197 ok:
198 newNode = LstNodeNew(datum);
199
200 if (node == NULL) {
201 newNode->prev = newNode->next = NULL;
202 list->first = list->last = newNode;
203 } else {
204 newNode->prev = node->prev;
205 newNode->next = node;
206
207 if (newNode->prev != NULL) {
208 newNode->prev->next = newNode;
209 }
210 node->prev = newNode;
211
212 if (node == list->first) {
213 list->first = newNode;
214 }
215 }
216
217 return SUCCESS;
218 }
219
220 /* Insert a new node with the given piece of data before the given node in the
221 * given list. */
222 void
223 Lst_InsertBeforeS(Lst list, LstNode node, void *datum)
224 {
225 LstNode newNode;
226
227 assert(LstIsValid(list));
228 assert(!LstIsEmpty(list));
229 assert(LstNodeIsValid(node));
230 assert(datum != NULL);
231
232 newNode = LstNodeNew(datum);
233 newNode->prev = node->prev;
234 newNode->next = node;
235
236 if (node->prev != NULL) {
237 node->prev->next = newNode;
238 }
239 node->prev = newNode;
240
241 if (node == list->first) {
242 list->first = newNode;
243 }
244 }
245
246 /* Add a piece of data at the front of the given list. */
247 ReturnStatus
248 Lst_AtFront(Lst list, void *datum)
249 {
250 LstNode front = Lst_First(list);
251 return LstInsertBefore(list, front, datum);
252 }
253
254 /* Add a piece of data at the start of the given list. */
255 void
256 Lst_PrependS(Lst list, void *datum)
257 {
258 LstNode node;
259
260 assert(LstIsValid(list));
261 assert(datum != NULL);
262
263 node = LstNodeNew(datum);
264 node->prev = NULL;
265 node->next = list->first;
266
267 if (list->first == NULL) {
268 list->first = node;
269 list->last = node;
270 } else {
271 list->first->prev = node;
272 list->first = node;
273 }
274 }
275
276 /* Add a piece of data at the end of the given list. */
277 void
278 Lst_AppendS(Lst list, void *datum)
279 {
280 LstNode node;
281
282 assert(LstIsValid(list));
283 assert(datum != NULL);
284
285 node = LstNodeNew(datum);
286 node->prev = list->last;
287 node->next = NULL;
288
289 if (list->last == NULL) {
290 list->first = node;
291 list->last = node;
292 } else {
293 list->last->next = node;
294 list->last = node;
295 }
296 }
297
298 /* Remove the given node from the given list.
299 * The datum stored in the node must be freed by the caller, if necessary. */
300 void
301 Lst_RemoveS(Lst list, LstNode node)
302 {
303 assert(LstIsValid(list));
304 assert(LstNodeIsValid(node));
305
306 /*
307 * unlink it from the list
308 */
309 if (node->next != NULL) {
310 node->next->prev = node->prev;
311 }
312 if (node->prev != NULL) {
313 node->prev->next = node->next;
314 }
315
316 /*
317 * if either the first or last of the list point to this node,
318 * adjust them accordingly
319 */
320 if (list->first == node) {
321 list->first = node->next;
322 }
323 if (list->last == node) {
324 list->last = node->prev;
325 }
326
327 /*
328 * Sequential access stuff. If the node we're removing is the current
329 * node in the list, reset the current node to the previous one. If the
330 * previous one was non-existent (prev == NULL), we set the
331 * end to be Unknown, since it is.
332 */
333 if (list->isOpen && list->curr == node) {
334 list->curr = list->prev;
335 if (list->curr == NULL) {
336 list->lastAccess = Unknown;
337 }
338 }
339
340 /*
341 * note that the datum is unmolested. The caller must free it as
342 * necessary and as expected.
343 */
344 if (node->useCount == 0) {
345 free(node);
346 } else {
347 node->deleted = TRUE;
348 }
349 }
350
351 /* Replace the datum in the given node with the new datum. */
352 void
353 Lst_ReplaceS(LstNode node, void *datum)
354 {
355 node->datum = datum;
356 }
357
358
359 /*
360 * Node-specific functions
361 */
362
363 /* Return the first node from the given list, or NULL if the list is empty or
364 * invalid. */
365 LstNode
366 Lst_First(Lst list)
367 {
368 if (!LstIsValid(list) || LstIsEmpty(list)) {
369 return NULL;
370 } else {
371 return list->first;
372 }
373 }
374
375 /* Return the last node from the given list, or NULL if the list is empty or
376 * invalid. */
377 LstNode
378 Lst_Last(Lst list)
379 {
380 if (!LstIsValid(list) || LstIsEmpty(list)) {
381 return NULL;
382 } else {
383 return list->last;
384 }
385 }
386
387 /* Return the successor to the given node on its list, or NULL. */
388 LstNode
389 Lst_Succ(LstNode node)
390 {
391 if (node == NULL) {
392 return NULL;
393 } else {
394 return node->next;
395 }
396 }
397
398 /* Return the predecessor to the given node on its list, or NULL. */
399 LstNode
400 Lst_PrevS(LstNode node)
401 {
402 assert(LstNodeIsValid(node));
403 return node->prev;
404 }
405
406 /* Return the datum stored in the given node. */
407 void *
408 Lst_DatumS(LstNode node)
409 {
410 assert(LstNodeIsValid(node));
411 return node->datum;
412 }
413
414
415 /*
416 * Functions for entire lists
417 */
418
419 /* Return TRUE if the given list is empty or invalid. */
420 Boolean
421 Lst_IsEmpty(Lst list)
422 {
423 return !LstIsValid(list) || LstIsEmpty(list);
424 }
425
426 /* Return the first node from the given list for which the given comparison
427 * function returns 0, or NULL if none of the nodes matches. */
428 LstNode
429 Lst_Find(Lst list, const void *cmpData, int (*cmp)(const void *, const void *))
430 {
431 return Lst_FindFrom(list, Lst_First(list), cmpData, cmp);
432 }
433
434 /* Return the first node from the given list, starting at the given node, for
435 * which the given comparison function returns 0, or NULL if none of the nodes
436 * matches. */
437 LstNode
438 Lst_FindFrom(Lst list, LstNode node, const void *cmpData,
439 int (*cmp)(const void *, const void *))
440 {
441 LstNode tln;
442
443 if (!LstIsValid(list) || LstIsEmpty(list) || !LstNodeIsValid(node)) {
444 return NULL;
445 }
446
447 tln = node;
448
449 do {
450 if ((*cmp)(tln->datum, cmpData) == 0)
451 return tln;
452 tln = tln->next;
453 } while (tln != node && tln != NULL);
454
455 return NULL;
456 }
457
458 /* Return the first node that contains the given datum, or NULL. */
459 LstNode
460 Lst_MemberS(Lst list, void *datum)
461 {
462 LstNode node;
463
464 assert(LstIsValid(list));
465 assert(datum != NULL);
466
467 for (node = list->first; node != NULL; node = node->next) {
468 if (node->datum == datum) {
469 return node;
470 }
471 }
472
473 return NULL;
474 }
475
476 /* Apply the given function to each element of the given list. The function
477 * should return 0 if traversal should continue and non-zero if it should
478 * abort. */
479 int
480 Lst_ForEach(Lst list, int (*proc)(void *, void *), void *procData)
481 {
482 return Lst_ForEachFrom(list, Lst_First(list), proc, procData);
483 }
484
485 /* Apply the given function to each element of the given list, starting from
486 * the given node. The function should return 0 if traversal should continue,
487 * and non-zero if it should abort. */
488 int
489 Lst_ForEachFrom(Lst list, LstNode node,
490 int (*proc)(void *, void *), void *procData)
491 {
492 LstNode tln = node;
493 LstNode next;
494 Boolean done;
495 int result;
496
497 if (!LstIsValid(list) || LstIsEmpty(list)) {
498 return 0;
499 }
500
501 do {
502 /*
503 * Take care of having the current element deleted out from under
504 * us.
505 */
506
507 next = tln->next;
508
509 /*
510 * We're done with the traversal if
511 * - the next node to examine is the first in the queue or
512 * doesn't exist and
513 * - nothing's been added after the current node (check this
514 * after proc() has been called).
515 */
516 done = (next == NULL || next == list->first);
517
518 tln->useCount++;
519 result = (*proc)(tln->datum, procData);
520 tln->useCount--;
521
522 /*
523 * Now check whether a node has been added.
524 * Note: this doesn't work if this node was deleted before
525 * the new node was added.
526 */
527 if (next != tln->next) {
528 next = tln->next;
529 done = 0;
530 }
531
532 if (tln->deleted) {
533 free((char *)tln);
534 }
535 tln = next;
536 } while (!result && !LstIsEmpty(list) && !done);
537
538 return result;
539 }
540
541 /* Move all nodes from list2 to the end of list1.
542 * List2 is destroyed and freed. */
543 void
544 Lst_MoveAllS(Lst list1, Lst list2)
545 {
546 assert(LstIsValid(list1));
547 assert(LstIsValid(list2));
548
549 if (list2->first != NULL) {
550 list2->first->prev = list1->last;
551 if (list1->last != NULL) {
552 list1->last->next = list2->first;
553 } else {
554 list1->first = list2->first;
555 }
556 list1->last = list2->last;
557 }
558 free(list2);
559 }
560
561 /* Copy the element data from src to the start of dst. */
562 void
563 Lst_PrependAllS(Lst dst, Lst src)
564 {
565 LstNode node;
566 for (node = src->last; node != NULL; node = node->prev)
567 Lst_PrependS(dst, node->datum);
568 }
569
570 /* Copy the element data from src to the end of dst. */
571 void
572 Lst_AppendAllS(Lst dst, Lst src)
573 {
574 LstNode node;
575 for (node = src->first; node != NULL; node = node->next)
576 Lst_AppendS(dst, node->datum);
577 }
578
579 /*
580 * these functions are for dealing with a list as a table, of sorts.
581 * An idea of the "current element" is kept and used by all the functions
582 * between Lst_Open() and Lst_Close().
583 *
584 * The sequential functions access the list in a slightly different way.
585 * CurPtr points to their idea of the current node in the list and they
586 * access the list based on it.
587 */
588
589 /* Open a list for sequential access. A list can still be searched, etc.,
590 * without confusing these functions. */
591 ReturnStatus
592 Lst_Open(Lst list)
593 {
594 if (!LstIsValid(list)) {
595 return FAILURE;
596 }
597 Lst_OpenS(list);
598 return SUCCESS;
599 }
600
601 /* Open a list for sequential access. A list can still be searched, etc.,
602 * without confusing these functions. */
603 void
604 Lst_OpenS(Lst list)
605 {
606 assert(LstIsValid(list));
607
608 /* XXX: This assertion fails for NetBSD's "build.sh -j1 tools", somewhere
609 * between "dependall ===> compat" and "dependall ===> binstall".
610 * Building without the "-j1" succeeds though. */
611 if (DEBUG(LINT) && list->isOpen)
612 Parse_Error(PARSE_WARNING, "Internal inconsistency: list opened twice");
613
614 list->isOpen = TRUE;
615 list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
616 list->curr = NULL;
617 }
618
619 /* Return the next node for the given list, or NULL if the end has been
620 * reached. */
621 LstNode
622 Lst_NextS(Lst list)
623 {
624 LstNode node;
625
626 assert(LstIsValid(list));
627 assert(list->isOpen);
628
629 list->prev = list->curr;
630
631 if (list->curr == NULL) {
632 if (list->lastAccess == Unknown) {
633 /*
634 * If we're just starting out, lastAccess will be Unknown.
635 * Then we want to start this thing off in the right
636 * direction -- at the start with lastAccess being Middle.
637 */
638 list->curr = node = list->first;
639 list->lastAccess = Middle;
640 } else {
641 node = NULL;
642 list->lastAccess = Tail;
643 }
644 } else {
645 node = list->curr->next;
646 list->curr = node;
647
648 if (node == list->first || node == NULL) {
649 /*
650 * If back at the front, then we've hit the end...
651 */
652 list->lastAccess = Tail;
653 } else {
654 /*
655 * Reset to Middle if gone past first.
656 */
657 list->lastAccess = Middle;
658 }
659 }
660
661 return node;
662 }
663
664 /* Close a list which was opened for sequential access. */
665 void
666 Lst_CloseS(Lst list)
667 {
668 assert(LstIsValid(list));
669 assert(list->isOpen);
670
671 list->isOpen = FALSE;
672 list->lastAccess = Unknown;
673 }
674
675
676 /*
677 * for using the list as a queue
678 */
679
680 /* Add the datum to the tail of the given list. */
681 void
682 Lst_EnqueueS(Lst list, void *datum)
683 {
684 Lst_AppendS(list, datum);
685 }
686
687 /* Remove and return the datum at the head of the given list. */
688 void *
689 Lst_DequeueS(Lst list)
690 {
691 void *datum;
692
693 assert(LstIsValid(list));
694 assert(!LstIsEmpty(list));
695
696 datum = list->first->datum;
697 Lst_RemoveS(list, list->first);
698 assert(datum != NULL);
699 return datum;
700 }
701