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