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