Home | History | Annotate | Line # | Download | only in make
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