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