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