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