Home | History | Annotate | Line # | Download | only in make
lst.c revision 1.25
      1 /* $NetBSD: lst.c,v 1.25 2020/08/22 14:39:12 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 "make.h"
     38 
     39 #ifndef MAKE_NATIVE
     40 static char rcsid[] = "$NetBSD: lst.c,v 1.25 2020/08/22 14:39:12 rillig Exp $";
     41 #else
     42 #include <sys/cdefs.h>
     43 #ifndef lint
     44 __RCSID("$NetBSD: lst.c,v 1.25 2020/08/22 14:39:12 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 ReturnStatus
    196 Lst_InsertBefore(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 after the given node in the
    234  * given list. */
    235 ReturnStatus
    236 Lst_InsertAfter(Lst list, LstNode node, void *datum)
    237 {
    238     LstNode newNode;
    239 
    240     if (LstIsValid(list) && (node == NULL && LstIsEmpty(list))) {
    241 	goto ok;
    242     }
    243 
    244     if (!LstIsValid(list) || LstIsEmpty(list) || !LstNodeIsValid(node)) {
    245 	return FAILURE;
    246     }
    247     ok:
    248 
    249     newNode = LstNodeNew(datum);
    250 
    251     if (node == NULL) {
    252 	newNode->next = newNode->prev = NULL;
    253 	list->first = list->last = newNode;
    254     } else {
    255 	newNode->prev = node;
    256 	newNode->next = node->next;
    257 
    258 	node->next = newNode;
    259 	if (newNode->next != NULL) {
    260 	    newNode->next->prev = newNode;
    261 	}
    262 
    263 	if (node == list->last) {
    264 	    list->last = newNode;
    265 	}
    266     }
    267 
    268     return SUCCESS;
    269 }
    270 
    271 /* Add a piece of data at the front of the given list. */
    272 ReturnStatus
    273 Lst_AtFront(Lst list, void *datum)
    274 {
    275     LstNode front = Lst_First(list);
    276     return Lst_InsertBefore(list, front, datum);
    277 }
    278 
    279 /* Add a piece of data at the end of the given list. */
    280 ReturnStatus
    281 Lst_AtEnd(Lst list, void *datum)
    282 {
    283     LstNode end = Lst_Last(list);
    284     return Lst_InsertAfter(list, end, datum);
    285 }
    286 
    287 /* Add a piece of data at the start of the given list. */
    288 void
    289 Lst_PrependS(Lst list, void *datum)
    290 {
    291     LstNode node;
    292 
    293     assert(LstIsValid(list));
    294     assert(datum != NULL);
    295 
    296     node = LstNodeNew(datum);
    297     node->prev = NULL;
    298     node->next = list->first;
    299 
    300     if (list->first == NULL) {
    301 	list->first = node;
    302 	list->last = node;
    303     } else {
    304 	list->first->prev = node;
    305 	list->first = node;
    306     }
    307 }
    308 
    309 /* Add a piece of data at the end of the given list. */
    310 void
    311 Lst_AppendS(Lst list, void *datum)
    312 {
    313     LstNode node;
    314 
    315     assert(LstIsValid(list));
    316     assert(datum != NULL);
    317 
    318     node = LstNodeNew(datum);
    319     node->prev = list->last;
    320     node->next = NULL;
    321 
    322     if (list->last == NULL) {
    323 	list->first = node;
    324 	list->last = node;
    325     } else {
    326 	list->last->next = node;
    327 	list->last = node;
    328     }
    329 }
    330 
    331 /* Remove the given node from the given list.
    332  * The datum stored in the node must be freed by the caller, if necessary. */
    333 void
    334 Lst_RemoveS(Lst list, LstNode node)
    335 {
    336     assert(LstIsValid(list));
    337     assert(LstNodeIsValid(node));
    338 
    339     /*
    340      * unlink it from the list
    341      */
    342     if (node->next != NULL) {
    343 	node->next->prev = node->prev;
    344     }
    345     if (node->prev != NULL) {
    346 	node->prev->next = node->next;
    347     }
    348 
    349     /*
    350      * if either the first or last of the list point to this node,
    351      * adjust them accordingly
    352      */
    353     if (list->first == node) {
    354 	list->first = node->next;
    355     }
    356     if (list->last == node) {
    357 	list->last = node->prev;
    358     }
    359 
    360     /*
    361      * Sequential access stuff. If the node we're removing is the current
    362      * node in the list, reset the current node to the previous one. If the
    363      * previous one was non-existent (prev == NULL), we set the
    364      * end to be Unknown, since it is.
    365      */
    366     if (list->isOpen && list->curr == node) {
    367 	list->curr = list->prev;
    368 	if (list->curr == NULL) {
    369 	    list->lastAccess = Unknown;
    370 	}
    371     }
    372 
    373     /*
    374      * note that the datum is unmolested. The caller must free it as
    375      * necessary and as expected.
    376      */
    377     if (node->useCount == 0) {
    378 	free(node);
    379     } else {
    380 	node->deleted = TRUE;
    381     }
    382 }
    383 
    384 /* Replace the datum in the given node with the new datum. */
    385 void
    386 Lst_ReplaceS(LstNode node, void *datum)
    387 {
    388     node->datum = datum;
    389 }
    390 
    391 
    392 /*
    393  * Node-specific functions
    394  */
    395 
    396 /* Return the first node from the given list, or NULL if the list is empty or
    397  * invalid. */
    398 LstNode
    399 Lst_First(Lst list)
    400 {
    401     if (!LstIsValid(list) || LstIsEmpty(list)) {
    402 	return NULL;
    403     } else {
    404 	return list->first;
    405     }
    406 }
    407 
    408 /* Return the last node from the given list, or NULL if the list is empty or
    409  * invalid. */
    410 LstNode
    411 Lst_Last(Lst list)
    412 {
    413     if (!LstIsValid(list) || LstIsEmpty(list)) {
    414 	return NULL;
    415     } else {
    416 	return list->last;
    417     }
    418 }
    419 
    420 /* Return the successor to the given node on its list, or NULL. */
    421 LstNode
    422 Lst_Succ(LstNode node)
    423 {
    424     if (node == NULL) {
    425 	return NULL;
    426     } else {
    427 	return node->next;
    428     }
    429 }
    430 
    431 /* Return the predecessor to the given node on its list, or NULL. */
    432 LstNode
    433 Lst_Prev(LstNode node)
    434 {
    435     if (node == NULL) {
    436 	return NULL;
    437     } else {
    438 	return node->prev;
    439     }
    440 }
    441 
    442 /* Return the datum stored in the given node, or NULL if the node is invalid. */
    443 void *
    444 Lst_Datum(LstNode node)
    445 {
    446     if (node != NULL) {
    447 	return node->datum;
    448     } else {
    449 	return NULL;
    450     }
    451 }
    452 
    453 
    454 /*
    455  * Functions for entire lists
    456  */
    457 
    458 /* Return TRUE if the given list is empty or invalid. */
    459 Boolean
    460 Lst_IsEmpty(Lst list)
    461 {
    462     return !LstIsValid(list) || LstIsEmpty(list);
    463 }
    464 
    465 /* Return the first node from the given list for which the given comparison
    466  * function returns 0, or NULL if none of the nodes matches. */
    467 LstNode
    468 Lst_Find(Lst list, const void *cmpData, int (*cmp)(const void *, const void *))
    469 {
    470     return Lst_FindFrom(list, Lst_First(list), cmpData, cmp);
    471 }
    472 
    473 /* Return the first node from the given list, starting at the given node, for
    474  * which the given comparison function returns 0, or NULL if none of the nodes
    475  * matches. */
    476 LstNode
    477 Lst_FindFrom(Lst list, LstNode node, const void *cmpData,
    478 	     int (*cmp)(const void *, const void *))
    479 {
    480     LstNode tln;
    481 
    482     if (!LstIsValid(list) || LstIsEmpty(list) || !LstNodeIsValid(node)) {
    483 	return NULL;
    484     }
    485 
    486     tln = node;
    487 
    488     do {
    489 	if ((*cmp)(tln->datum, cmpData) == 0)
    490 	    return tln;
    491 	tln = tln->next;
    492     } while (tln != node && tln != NULL);
    493 
    494     return NULL;
    495 }
    496 
    497 /* Return the first node that contains the given datum, or NULL. */
    498 LstNode
    499 Lst_Member(Lst list, void *datum)
    500 {
    501     LstNode node;
    502 
    503     if (list == NULL) {
    504 	return NULL;
    505     }
    506     node = list->first;
    507     if (node == NULL) {
    508 	return NULL;
    509     }
    510 
    511     do {
    512 	if (node->datum == datum) {
    513 	    return node;
    514 	}
    515 	node = node->next;
    516     } while (node != NULL && node != list->first);
    517 
    518     return NULL;
    519 }
    520 
    521 /* Apply the given function to each element of the given list. The function
    522  * should return 0 if traversal should continue and non-zero if it should
    523  * abort. */
    524 int
    525 Lst_ForEach(Lst list, int (*proc)(void *, void *), void *procData)
    526 {
    527     return Lst_ForEachFrom(list, Lst_First(list), proc, procData);
    528 }
    529 
    530 /* Apply the given function to each element of the given list, starting from
    531  * the given node. The function should return 0 if traversal should continue,
    532  * and non-zero if it should abort. */
    533 int
    534 Lst_ForEachFrom(Lst list, LstNode node,
    535 		int (*proc)(void *, void *), void *procData)
    536 {
    537     LstNode tln = node;
    538     LstNode next;
    539     Boolean done;
    540     int result;
    541 
    542     if (!LstIsValid(list) || LstIsEmpty(list)) {
    543 	return 0;
    544     }
    545 
    546     do {
    547 	/*
    548 	 * Take care of having the current element deleted out from under
    549 	 * us.
    550 	 */
    551 
    552 	next = tln->next;
    553 
    554 	/*
    555 	 * We're done with the traversal if
    556 	 *  - the next node to examine is the first in the queue or
    557 	 *    doesn't exist and
    558 	 *  - nothing's been added after the current node (check this
    559 	 *    after proc() has been called).
    560 	 */
    561 	done = (next == NULL || next == list->first);
    562 
    563 	tln->useCount++;
    564 	result = (*proc)(tln->datum, procData);
    565 	tln->useCount--;
    566 
    567 	/*
    568 	 * Now check whether a node has been added.
    569 	 * Note: this doesn't work if this node was deleted before
    570 	 *       the new node was added.
    571 	 */
    572 	if (next != tln->next) {
    573 	    next = tln->next;
    574 	    done = 0;
    575 	}
    576 
    577 	if (tln->deleted) {
    578 	    free((char *)tln);
    579 	}
    580 	tln = next;
    581     } while (!result && !LstIsEmpty(list) && !done);
    582 
    583     return result;
    584 }
    585 
    586 /* Concatenate two lists. New nodes are created to hold the data elements,
    587  * if specified, but the data themselves are not copied. If the data
    588  * should be duplicated to avoid confusion with another list, the Lst_Duplicate
    589  * function should be called first. If LST_CONCLINK is specified, the second
    590  * list is destroyed since its pointers have been corrupted and the list is no
    591  * longer usable.
    592  *
    593  * Input:
    594  *	list1		The list to which list2 is to be appended
    595  *	list2		The list to append to list1
    596  *	flags		LST_CONCNEW if the list nodes should be duplicated
    597  *			LST_CONCLINK if the list nodes should just be relinked
    598  */
    599 ReturnStatus
    600 Lst_Concat(Lst list1, Lst list2, int flags)
    601 {
    602     LstNode node;	/* original node */
    603     LstNode newNode;
    604     LstNode last;	/* the last element in the list.
    605 			 * Keeps bookkeeping until the end */
    606 
    607     if (!LstIsValid(list1) || !LstIsValid(list2)) {
    608 	return FAILURE;
    609     }
    610 
    611     if (flags == LST_CONCLINK) {
    612 	if (list2->first != NULL) {
    613 	    /*
    614 	     * So long as the second list isn't empty, we just link the
    615 	     * first element of the second list to the last element of the
    616 	     * first list. If the first list isn't empty, we then link the
    617 	     * last element of the list to the first element of the second list
    618 	     * The last element of the second list, if it exists, then becomes
    619 	     * the last element of the first list.
    620 	     */
    621 	    list2->first->prev = list1->last;
    622 	    if (list1->last != NULL) {
    623 		list1->last->next = list2->first;
    624 	    } else {
    625 		list1->first = list2->first;
    626 	    }
    627 	    list1->last = list2->last;
    628 	}
    629 	free(list2);
    630     } else if (list2->first != NULL) {
    631 	/*
    632 	 * We set the 'next' of the last element of list 2 to be nil to make
    633 	 * the loop less difficult. The loop simply goes through the entire
    634 	 * second list creating new LstNodes and filling in the 'next', and
    635 	 * 'prev' to fit into list1 and its datum field from the
    636 	 * datum field of the corresponding element in list2. The 'last' node
    637 	 * follows the last of the new nodes along until the entire list2 has
    638 	 * been appended. Only then does the bookkeeping catch up with the
    639 	 * changes. During the first iteration of the loop, if 'last' is nil,
    640 	 * the first list must have been empty so the newly-created node is
    641 	 * made the first node of the list.
    642 	 */
    643 	list2->last->next = NULL;
    644 	for (last = list1->last, node = list2->first;
    645 	     node != NULL;
    646 	     node = node->next)
    647 	{
    648 	    newNode = LstNodeNew(node->datum);
    649 	    if (last != NULL) {
    650 		last->next = newNode;
    651 	    } else {
    652 		list1->first = newNode;
    653 	    }
    654 	    newNode->prev = last;
    655 	    last = newNode;
    656 	}
    657 
    658 	/*
    659 	 * Finish bookkeeping. The last new element becomes the last element
    660 	 * of list one.
    661 	 */
    662 	list1->last = last;
    663 	last->next = NULL;
    664     }
    665 
    666     return SUCCESS;
    667 }
    668 
    669 /* Copy the element data from src to the start of dst. */
    670 void
    671 Lst_PrependAllS(Lst dst, Lst src)
    672 {
    673     LstNode node;
    674     for (node = src->last; node != NULL; node = node->prev)
    675         Lst_PrependS(dst, node->datum);
    676 }
    677 
    678 /* Copy the element data from src to the end of dst. */
    679 void
    680 Lst_AppendAllS(Lst dst, Lst src)
    681 {
    682     LstNode node;
    683     for (node = src->first; node != NULL; node = node->next)
    684         Lst_AppendS(dst, node->datum);
    685 }
    686 
    687 /*
    688  * these functions are for dealing with a list as a table, of sorts.
    689  * An idea of the "current element" is kept and used by all the functions
    690  * between Lst_Open() and Lst_Close().
    691  *
    692  * The sequential functions access the list in a slightly different way.
    693  * CurPtr points to their idea of the current node in the list and they
    694  * access the list based on it.
    695  */
    696 
    697 /* Open a list for sequential access. A list can still be searched, etc.,
    698  * without confusing these functions. */
    699 ReturnStatus
    700 Lst_Open(Lst list)
    701 {
    702     if (!LstIsValid(list)) {
    703 	return FAILURE;
    704     }
    705     Lst_OpenS(list);
    706     return SUCCESS;
    707 }
    708 
    709 /* Open a list for sequential access. A list can still be searched, etc.,
    710  * without confusing these functions. */
    711 void
    712 Lst_OpenS(Lst list)
    713 {
    714     assert(LstIsValid(list));
    715 
    716     /* XXX: This assertion fails for NetBSD's "build.sh -j1 tools", somewhere
    717      * between "dependall ===> compat" and "dependall ===> binstall".
    718      * Building without the "-j1" succeeds though. */
    719     if (DEBUG(LINT) && list->isOpen)
    720 	Parse_Error(PARSE_WARNING, "Internal inconsistency: list opened twice");
    721 
    722     list->isOpen = TRUE;
    723     list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
    724     list->curr = NULL;
    725 }
    726 
    727 /* Return the next node for the given list, or NULL if the end has been
    728  * reached. */
    729 LstNode
    730 Lst_NextS(Lst list)
    731 {
    732     LstNode node;
    733 
    734     assert(LstIsValid(list));
    735     assert(list->isOpen);
    736 
    737     list->prev = list->curr;
    738 
    739     if (list->curr == NULL) {
    740 	if (list->lastAccess == Unknown) {
    741 	    /*
    742 	     * If we're just starting out, lastAccess will be Unknown.
    743 	     * Then we want to start this thing off in the right
    744 	     * direction -- at the start with lastAccess being Middle.
    745 	     */
    746 	    list->curr = node = list->first;
    747 	    list->lastAccess = Middle;
    748 	} else {
    749 	    node = NULL;
    750 	    list->lastAccess = Tail;
    751 	}
    752     } else {
    753 	node = list->curr->next;
    754 	list->curr = node;
    755 
    756 	if (node == list->first || node == NULL) {
    757 	    /*
    758 	     * If back at the front, then we've hit the end...
    759 	     */
    760 	    list->lastAccess = Tail;
    761 	} else {
    762 	    /*
    763 	     * Reset to Middle if gone past first.
    764 	     */
    765 	    list->lastAccess = Middle;
    766 	}
    767     }
    768 
    769     return node;
    770 }
    771 
    772 /* Close a list which was opened for sequential access. */
    773 void
    774 Lst_CloseS(Lst list)
    775 {
    776     assert(LstIsValid(list));
    777     assert(list->isOpen);
    778 
    779     list->isOpen = FALSE;
    780     list->lastAccess = Unknown;
    781 }
    782 
    783 
    784 /*
    785  * for using the list as a queue
    786  */
    787 
    788 /* Add the datum to the tail of the given list. */
    789 void
    790 Lst_EnqueueS(Lst list, void *datum)
    791 {
    792     Lst_AppendS(list, datum);
    793 }
    794 
    795 /* Remove and return the datum at the head of the given list. */
    796 void *
    797 Lst_DequeueS(Lst list)
    798 {
    799     void *datum;
    800 
    801     assert(LstIsValid(list));
    802     assert(!LstIsEmpty(list));
    803 
    804     datum = list->first->datum;
    805     Lst_RemoveS(list, list->first);
    806     assert(datum != NULL);
    807     return datum;
    808 }
    809