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