Home | History | Annotate | Line # | Download | only in make
lst.c revision 1.33
      1 /* $NetBSD: lst.c,v 1.33 2020/08/22 22:00:50 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.33 2020/08/22 22:00:50 rillig Exp $";
     41 #else
     42 #include <sys/cdefs.h>
     43 #ifndef lint
     44 __RCSID("$NetBSD: lst.c,v 1.33 2020/08/22 22:00:50 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 /* Concatenate two lists. New nodes are created to hold the data elements,
    601  * if specified, but the data themselves are not copied. If the data
    602  * should be duplicated to avoid confusion with another list, the Lst_Duplicate
    603  * function should be called first. If LST_CONCLINK is specified, the second
    604  * list is destroyed since its pointers have been corrupted and the list is no
    605  * longer usable.
    606  *
    607  * Input:
    608  *	list1		The list to which list2 is to be appended
    609  *	list2		The list to append to list1
    610  *	flags		LST_CONCNEW if the list nodes should be duplicated
    611  *			LST_CONCLINK if the list nodes should just be relinked
    612  */
    613 ReturnStatus
    614 Lst_Concat(Lst list1, Lst list2, int flags)
    615 {
    616     LstNode node;	/* original node */
    617     LstNode newNode;
    618     LstNode last;	/* the last element in the list.
    619 			 * Keeps bookkeeping until the end */
    620 
    621     if (!LstIsValid(list1) || !LstIsValid(list2)) {
    622 	return FAILURE;
    623     }
    624 
    625     if (flags == LST_CONCLINK) {
    626 	if (list2->first != NULL) {
    627 	    /*
    628 	     * So long as the second list isn't empty, we just link the
    629 	     * first element of the second list to the last element of the
    630 	     * first list. If the first list isn't empty, we then link the
    631 	     * last element of the list to the first element of the second list
    632 	     * The last element of the second list, if it exists, then becomes
    633 	     * the last element of the first list.
    634 	     */
    635 	    list2->first->prev = list1->last;
    636 	    if (list1->last != NULL) {
    637 		list1->last->next = list2->first;
    638 	    } else {
    639 		list1->first = list2->first;
    640 	    }
    641 	    list1->last = list2->last;
    642 	}
    643 	free(list2);
    644     } else if (list2->first != NULL) {
    645 	/*
    646 	 * We set the 'next' of the last element of list 2 to be nil to make
    647 	 * the loop less difficult. The loop simply goes through the entire
    648 	 * second list creating new LstNodes and filling in the 'next', and
    649 	 * 'prev' to fit into list1 and its datum field from the
    650 	 * datum field of the corresponding element in list2. The 'last' node
    651 	 * follows the last of the new nodes along until the entire list2 has
    652 	 * been appended. Only then does the bookkeeping catch up with the
    653 	 * changes. During the first iteration of the loop, if 'last' is nil,
    654 	 * the first list must have been empty so the newly-created node is
    655 	 * made the first node of the list.
    656 	 */
    657 	list2->last->next = NULL;
    658 	for (last = list1->last, node = list2->first;
    659 	     node != NULL;
    660 	     node = node->next)
    661 	{
    662 	    newNode = LstNodeNew(node->datum);
    663 	    if (last != NULL) {
    664 		last->next = newNode;
    665 	    } else {
    666 		list1->first = newNode;
    667 	    }
    668 	    newNode->prev = last;
    669 	    last = newNode;
    670 	}
    671 
    672 	/*
    673 	 * Finish bookkeeping. The last new element becomes the last element
    674 	 * of list one.
    675 	 */
    676 	list1->last = last;
    677 	last->next = NULL;
    678     }
    679 
    680     return SUCCESS;
    681 }
    682 
    683 /* Copy the element data from src to the start of dst. */
    684 void
    685 Lst_PrependAllS(Lst dst, Lst src)
    686 {
    687     LstNode node;
    688     for (node = src->last; node != NULL; node = node->prev)
    689 	Lst_PrependS(dst, node->datum);
    690 }
    691 
    692 /* Copy the element data from src to the end of dst. */
    693 void
    694 Lst_AppendAllS(Lst dst, Lst src)
    695 {
    696     LstNode node;
    697     for (node = src->first; node != NULL; node = node->next)
    698 	Lst_AppendS(dst, node->datum);
    699 }
    700 
    701 /*
    702  * these functions are for dealing with a list as a table, of sorts.
    703  * An idea of the "current element" is kept and used by all the functions
    704  * between Lst_Open() and Lst_Close().
    705  *
    706  * The sequential functions access the list in a slightly different way.
    707  * CurPtr points to their idea of the current node in the list and they
    708  * access the list based on it.
    709  */
    710 
    711 /* Open a list for sequential access. A list can still be searched, etc.,
    712  * without confusing these functions. */
    713 ReturnStatus
    714 Lst_Open(Lst list)
    715 {
    716     if (!LstIsValid(list)) {
    717 	return FAILURE;
    718     }
    719     Lst_OpenS(list);
    720     return SUCCESS;
    721 }
    722 
    723 /* Open a list for sequential access. A list can still be searched, etc.,
    724  * without confusing these functions. */
    725 void
    726 Lst_OpenS(Lst list)
    727 {
    728     assert(LstIsValid(list));
    729 
    730     /* XXX: This assertion fails for NetBSD's "build.sh -j1 tools", somewhere
    731      * between "dependall ===> compat" and "dependall ===> binstall".
    732      * Building without the "-j1" succeeds though. */
    733     if (DEBUG(LINT) && list->isOpen)
    734 	Parse_Error(PARSE_WARNING, "Internal inconsistency: list opened twice");
    735 
    736     list->isOpen = TRUE;
    737     list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
    738     list->curr = NULL;
    739 }
    740 
    741 /* Return the next node for the given list, or NULL if the end has been
    742  * reached. */
    743 LstNode
    744 Lst_NextS(Lst list)
    745 {
    746     LstNode node;
    747 
    748     assert(LstIsValid(list));
    749     assert(list->isOpen);
    750 
    751     list->prev = list->curr;
    752 
    753     if (list->curr == NULL) {
    754 	if (list->lastAccess == Unknown) {
    755 	    /*
    756 	     * If we're just starting out, lastAccess will be Unknown.
    757 	     * Then we want to start this thing off in the right
    758 	     * direction -- at the start with lastAccess being Middle.
    759 	     */
    760 	    list->curr = node = list->first;
    761 	    list->lastAccess = Middle;
    762 	} else {
    763 	    node = NULL;
    764 	    list->lastAccess = Tail;
    765 	}
    766     } else {
    767 	node = list->curr->next;
    768 	list->curr = node;
    769 
    770 	if (node == list->first || node == NULL) {
    771 	    /*
    772 	     * If back at the front, then we've hit the end...
    773 	     */
    774 	    list->lastAccess = Tail;
    775 	} else {
    776 	    /*
    777 	     * Reset to Middle if gone past first.
    778 	     */
    779 	    list->lastAccess = Middle;
    780 	}
    781     }
    782 
    783     return node;
    784 }
    785 
    786 /* Close a list which was opened for sequential access. */
    787 void
    788 Lst_CloseS(Lst list)
    789 {
    790     assert(LstIsValid(list));
    791     assert(list->isOpen);
    792 
    793     list->isOpen = FALSE;
    794     list->lastAccess = Unknown;
    795 }
    796 
    797 
    798 /*
    799  * for using the list as a queue
    800  */
    801 
    802 /* Add the datum to the tail of the given list. */
    803 void
    804 Lst_EnqueueS(Lst list, void *datum)
    805 {
    806     Lst_AppendS(list, datum);
    807 }
    808 
    809 /* Remove and return the datum at the head of the given list. */
    810 void *
    811 Lst_DequeueS(Lst list)
    812 {
    813     void *datum;
    814 
    815     assert(LstIsValid(list));
    816     assert(!LstIsEmpty(list));
    817 
    818     datum = list->first->datum;
    819     Lst_RemoveS(list, list->first);
    820     assert(datum != NULL);
    821     return datum;
    822 }
    823