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