1/*-
2 * Copyright (c) 1988, 1989, 1990, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Adam de Boor.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD: releng/10.2/usr.bin/make/lst.c 218909 2011-02-21 09:01:34Z brucec $
33 */
34
35/*-
36 * lst.c --
37 *	Routines to maintain a linked list of objects.
38 */
39
40#include <stdio.h>
41#include <stdlib.h>
42
43#include "lst.h"
44#include "util.h"
45
46/**
47 * Lst_Append
48 *	Create a new node and add it to the given list after the given node.
49 *
50 * Arguments:
51 *	l	affected list
52 *	ln	node after which to append the datum
53 *	d	said datum
54 *
55 * Side Effects:
56 *	A new LstNode is created and linked in to the List. The lastPtr
57 *	field of the List will be altered if ln is the last node in the
58 *	list. lastPtr and firstPtr will alter if the list was empty and
59 *	ln was NULL.
60 */
61void
62Lst_Append(Lst *list, LstNode *ln, void *d)
63{
64	LstNode *nLNode;
65
66	nLNode = emalloc(sizeof(*nLNode));
67	nLNode->datum = d;
68
69	if (ln == NULL) {
70		nLNode->nextPtr = nLNode->prevPtr = NULL;
71		list->firstPtr = list->lastPtr = nLNode;
72	} else {
73		nLNode->prevPtr = ln;
74		nLNode->nextPtr = ln->nextPtr;
75
76		ln->nextPtr = nLNode;
77		if (nLNode->nextPtr != NULL) {
78			nLNode->nextPtr->prevPtr = nLNode;
79		}
80
81		if (ln == list->lastPtr) {
82			list->lastPtr = nLNode;
83		}
84	}
85}
86
87/**
88 * Lst_Concat
89 *	Concatenate two lists. New elements are created to hold the data
90 *	elements, if specified, but the elements themselves are not copied.
91 *	If the elements should be duplicated to avoid confusion with another
92 *	list, the Lst_Duplicate function should be called first.
93 *
94 * Arguments:
95 *	list1	The list to which list2 is to be appended
96 *	list2	The list to append to list1
97 *	flags	LST_CONCNEW if LstNode's should be duplicated
98 *		LST_CONCLINK if should just be relinked
99 *
100 * Side Effects:
101 *	New elements are created and appended the first list.
102 */
103void
104Lst_Concat(Lst *list1, Lst *list2, int flags)
105{
106	LstNode	*ln;	/* original LstNode */
107	LstNode	*nln;	/* new LstNode */
108	LstNode	*last;	/* the last element in the list. Keeps
109			 * bookkeeping until the end */
110
111	if (list2->firstPtr == NULL)
112		return;
113
114	if (flags == LST_CONCLINK) {
115		/*
116		 * Link the first element of the second list to the last
117		 * element of the first list. If the first list isn't empty,
118		 * we then link the last element of the list to the first
119		 * element of the second list. The last element of the second
120		 * list, if it exists, then becomes the last element of the
121		 * first list.
122		 */
123		list2->firstPtr->prevPtr = list1->lastPtr;
124		if (list1->lastPtr != NULL)
125			list1->lastPtr->nextPtr = list2->firstPtr;
126		else
127			list1->firstPtr = list2->firstPtr;
128		list1->lastPtr = list2->lastPtr;
129
130		Lst_Init(list2);
131	} else {
132		/*
133		 * The loop simply goes through the entire second list creating
134		 * new LstNodes and filling in the nextPtr, and prevPtr to fit
135		 * into list1 and its datum field from the datum field of the
136		 * corresponding element in list2. The 'last' node follows the
137		 * last of the new nodes along until the entire list2 has been
138		 * appended. Only then does the bookkeeping catch up with the
139		 * changes. During the first iteration of the loop, if 'last'
140		 * is NULL, the first list must have been empty so the
141		 * newly-created node is made the first node of the list.
142		 */
143		for (last = list1->lastPtr, ln = list2->firstPtr;
144		    ln != NULL;
145		    ln = ln->nextPtr) {
146			nln = emalloc(sizeof(*nln));
147			nln->datum = ln->datum;
148			if (last != NULL) {
149				last->nextPtr = nln;
150			} else {
151				list1->firstPtr = nln;
152			}
153			nln->prevPtr = last;
154			last = nln;
155		}
156
157		/*
158		 * Finish bookkeeping. The last new element becomes the last
159		 * element of list one.
160		 */
161		list1->lastPtr = last;
162		last->nextPtr = NULL;
163	}
164}
165
166/**
167 * Lst_DeQueue
168 *	Remove and return the datum at the head of the given list.
169 *
170 * Results:
171 *	The datum in the node at the head or (ick) NULL if the list
172 *	is empty.
173 *
174 * Side Effects:
175 *	The head node is removed from the list.
176 */
177void *
178Lst_DeQueue(Lst *l)
179{
180	void *rd;
181	LstNode *tln;
182
183	tln = Lst_First(l);
184	if (tln == NULL) {
185		return (NULL);
186	}
187
188	rd = tln->datum;
189	Lst_Remove(l, tln);
190	return (rd);
191}
192
193/**
194 * Lst_Destroy
195 *	Destroy a list and free all its resources. If the freeProc is
196 *	given, it is called with the datum from each node in turn before
197 *	the node is freed.
198 *
199 * Side Effects:
200 *	The given list is freed in its entirety.
201 */
202void
203Lst_Destroy(Lst *list, FreeProc *freeProc)
204{
205	LstNode *ln;
206
207	if (list->firstPtr == NULL)
208		return;
209
210	if (freeProc != NOFREE) {
211		while ((ln = list->firstPtr) != NULL) {
212			list->firstPtr = ln->nextPtr;
213			(*freeProc)(ln->datum);
214			free(ln);
215		}
216	} else {
217		while ((ln = list->firstPtr) != NULL) {
218			list->firstPtr = ln->nextPtr;
219			free(ln);
220		}
221	}
222	list->lastPtr = NULL;
223}
224
225/**
226 * Lst_Duplicate
227 *	Duplicate an entire list. If a function to copy a void * is
228 *	given, the individual client elements will be duplicated as well.
229 *
230 * Arguments:
231 *	dst	the destination list (initialized)
232 *	src	the list to duplicate
233 *	copyProc A function to duplicate each void
234 */
235void
236Lst_Duplicate(Lst *dst, Lst *src, DuplicateProc *copyProc)
237{
238	LstNode *ln;
239
240	ln = src->firstPtr;
241	while (ln != NULL) {
242		if (copyProc != NOCOPY)
243			Lst_AtEnd(dst, (*copyProc)(ln->datum));
244		else
245			Lst_AtEnd(dst, ln->datum);
246		ln = ln->nextPtr;
247	}
248}
249
250/**
251 * Lst_Insert
252 *	Insert a new node with the given piece of data before the given
253 *	node in the given list.
254 *
255 * Parameters:
256 *	l	list to manipulate
257 *	ln	node before which to insert d
258 *	d	datum to be inserted
259 *
260 * Side Effects:
261 *	the firstPtr field will be changed if ln is the first node in the
262 *	list.
263 */
264void
265Lst_Insert(Lst *list, LstNode *ln, void *d)
266{
267	LstNode *nLNode;	/* new lnode for d */
268
269	nLNode = emalloc(sizeof(*nLNode));
270	nLNode->datum = d;
271
272	if (ln == NULL) {
273		nLNode->prevPtr = nLNode->nextPtr = NULL;
274		list->firstPtr = list->lastPtr = nLNode;
275	} else {
276		nLNode->prevPtr = ln->prevPtr;
277		nLNode->nextPtr = ln;
278
279		if (nLNode->prevPtr != NULL) {
280			nLNode->prevPtr->nextPtr = nLNode;
281		}
282		ln->prevPtr = nLNode;
283
284		if (ln == list->firstPtr) {
285			list->firstPtr = nLNode;
286		}
287	}
288}
289
290LstNode *
291Lst_Member(Lst *list, void *d)
292{
293	LstNode *lNode;
294
295	lNode = list->firstPtr;
296	if (lNode == NULL) {
297		return (NULL);
298	}
299
300	do {
301		if (lNode->datum == d) {
302			return (lNode);
303		}
304		lNode = lNode->nextPtr;
305	} while (lNode != NULL && lNode != list->firstPtr);
306
307	return (NULL);
308}
309
310/**
311 * Lst_Remove
312 *	Remove the given node from the given list.
313 *
314 * Side Effects:
315 *	The list's firstPtr will be set to NULL if ln is the last
316 *	node on the list. firsPtr and lastPtr will be altered if ln is
317 *	either the first or last node, respectively, on the list.
318 */
319void
320Lst_Remove(Lst *list, LstNode *ln)
321{
322	/*
323	 * unlink it from the list
324	 */
325	if (ln->nextPtr != NULL)
326		/* unlink from the backward chain */
327		ln->nextPtr->prevPtr = ln->prevPtr;
328	else
329		/* this was the last element */
330		list->lastPtr = ln->prevPtr;
331
332	if (ln->prevPtr != NULL)
333		/* unlink from the forward chain */
334		ln->prevPtr->nextPtr = ln->nextPtr;
335	else
336		/* this was the first element */
337		list->firstPtr = ln->nextPtr;
338
339	/*
340	 * note that the datum is unmolested. The caller must free it as
341	 * necessary and as expected.
342	 */
343	free(ln);
344}
345