1 /* $NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 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 #ifdef HAVE_CONFIG_H 36 # include "config.h" 37 #endif 38 39 #ifdef HAVE_INTTYPES_H 40 #include <inttypes.h> 41 #elif defined(HAVE_STDINT_H) 42 #include <stdint.h> 43 #endif 44 45 #include "make.h" 46 47 #ifndef MAKE_NATIVE 48 static char rcsid[] = "$NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $"; 49 #else 50 #include <sys/cdefs.h> 51 #ifndef lint 52 __RCSID("$NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $"); 53 #endif /* not lint */ 54 #endif 55 56 struct ListNode { 57 struct ListNode *prev; /* previous element in list */ 58 struct ListNode *next; /* next in list */ 59 uint8_t useCount; /* Count of functions using the node. 60 * node may not be deleted until count 61 * goes to 0 */ 62 Boolean deleted; /* List node should be removed when done */ 63 union { 64 void *datum; /* datum associated with this element */ 65 const GNode *gnode; /* alias, just for debugging */ 66 const char *str; /* alias, just for debugging */ 67 }; 68 }; 69 70 typedef enum { 71 Head, Middle, Tail, Unknown 72 } Where; 73 74 struct List { 75 LstNode first; /* first node in list */ 76 LstNode last; /* last node in list */ 77 78 /* fields for sequential access */ 79 Boolean isOpen; /* true if list has been Lst_Open'ed */ 80 Where lastAccess; /* Where in the list the last access was */ 81 LstNode curr; /* current node, if open. NULL if 82 * *just* opened */ 83 LstNode prev; /* Previous node, if open. Used by Lst_Remove */ 84 }; 85 86 /* Allocate and initialize a list node. 87 * 88 * The fields 'prev' and 'next' must be initialized by the caller. 89 */ 90 static LstNode 91 LstNodeNew(void *datum) 92 { 93 LstNode node = bmake_malloc(sizeof *node); 94 node->useCount = 0; 95 node->deleted = FALSE; 96 node->datum = datum; 97 return node; 98 } 99 100 static Boolean 101 LstIsEmpty(Lst list) 102 { 103 return list->first == NULL; 104 } 105 106 /* Create and initialize a new, empty list. */ 107 Lst 108 Lst_Init(void) 109 { 110 Lst list = bmake_malloc(sizeof *list); 111 112 list->first = NULL; 113 list->last = NULL; 114 list->isOpen = FALSE; 115 list->lastAccess = Unknown; 116 117 return list; 118 } 119 120 /* Duplicate an entire list, usually by copying the datum pointers. 121 * If copyProc is given, that function is used to create the new datum from the 122 * old datum, usually by creating a copy of it. */ 123 Lst 124 Lst_Copy(Lst list, LstCopyProc copyProc) 125 { 126 Lst newList; 127 LstNode node; 128 129 assert(list != NULL); 130 131 newList = Lst_Init(); 132 133 for (node = list->first; node != NULL; node = node->next) { 134 void *datum = copyProc != NULL ? copyProc(node->datum) : node->datum; 135 Lst_Append(newList, datum); 136 } 137 138 return newList; 139 } 140 141 /* Free a list and all its nodes. The list data itself are not freed though. */ 142 void 143 Lst_Free(Lst list) 144 { 145 LstNode node; 146 LstNode next; 147 148 assert(list != NULL); 149 150 for (node = list->first; node != NULL; node = next) { 151 next = node->next; 152 free(node); 153 } 154 155 free(list); 156 } 157 158 /* Destroy a list and free all its resources. The freeProc is called with the 159 * datum from each node in turn before the node is freed. */ 160 void 161 Lst_Destroy(Lst list, LstFreeProc freeProc) 162 { 163 LstNode node; 164 LstNode next; 165 166 assert(list != NULL); 167 assert(freeProc != NULL); 168 169 for (node = list->first; node != NULL; node = next) { 170 next = node->next; 171 freeProc(node->datum); 172 free(node); 173 } 174 175 free(list); 176 } 177 178 /* 179 * Functions to modify a list 180 */ 181 182 /* Insert a new node with the given piece of data before the given node in the 183 * given list. */ 184 void 185 Lst_InsertBefore(Lst list, LstNode node, void *datum) 186 { 187 LstNode newNode; 188 189 assert(list != NULL); 190 assert(!LstIsEmpty(list)); 191 assert(node != NULL); 192 assert(datum != NULL); 193 194 newNode = LstNodeNew(datum); 195 newNode->prev = node->prev; 196 newNode->next = node; 197 198 if (node->prev != NULL) { 199 node->prev->next = newNode; 200 } 201 node->prev = newNode; 202 203 if (node == list->first) { 204 list->first = newNode; 205 } 206 } 207 208 /* Add a piece of data at the start of the given list. */ 209 void 210 Lst_Prepend(Lst list, void *datum) 211 { 212 LstNode node; 213 214 assert(list != NULL); 215 assert(datum != NULL); 216 217 node = LstNodeNew(datum); 218 node->prev = NULL; 219 node->next = list->first; 220 221 if (list->first == NULL) { 222 list->first = node; 223 list->last = node; 224 } else { 225 list->first->prev = node; 226 list->first = node; 227 } 228 } 229 230 /* Add a piece of data at the end of the given list. */ 231 void 232 Lst_Append(Lst list, void *datum) 233 { 234 LstNode node; 235 236 assert(list != NULL); 237 assert(datum != NULL); 238 239 node = LstNodeNew(datum); 240 node->prev = list->last; 241 node->next = NULL; 242 243 if (list->last == NULL) { 244 list->first = node; 245 list->last = node; 246 } else { 247 list->last->next = node; 248 list->last = node; 249 } 250 } 251 252 /* Remove the given node from the given list. 253 * The datum stored in the node must be freed by the caller, if necessary. */ 254 void 255 Lst_Remove(Lst list, LstNode node) 256 { 257 assert(list != NULL); 258 assert(node != NULL); 259 260 /* 261 * unlink it from the list 262 */ 263 if (node->next != NULL) { 264 node->next->prev = node->prev; 265 } 266 if (node->prev != NULL) { 267 node->prev->next = node->next; 268 } 269 270 /* 271 * if either the first or last of the list point to this node, 272 * adjust them accordingly 273 */ 274 if (list->first == node) { 275 list->first = node->next; 276 } 277 if (list->last == node) { 278 list->last = node->prev; 279 } 280 281 /* 282 * Sequential access stuff. If the node we're removing is the current 283 * node in the list, reset the current node to the previous one. If the 284 * previous one was non-existent (prev == NULL), we set the 285 * end to be Unknown, since it is. 286 */ 287 if (list->isOpen && list->curr == node) { 288 list->curr = list->prev; 289 if (list->curr == NULL) { 290 list->lastAccess = Unknown; 291 } 292 } 293 294 /* 295 * note that the datum is unmolested. The caller must free it as 296 * necessary and as expected. 297 */ 298 if (node->useCount == 0) { 299 free(node); 300 } else { 301 node->deleted = TRUE; 302 } 303 } 304 305 /* Replace the datum in the given node with the new datum. */ 306 void 307 LstNode_Set(LstNode node, void *datum) 308 { 309 assert(node != NULL); 310 assert(datum != NULL); 311 312 node->datum = datum; 313 } 314 315 /* Replace the datum in the given node to NULL. */ 316 void 317 LstNode_SetNull(LstNode node) 318 { 319 assert(node != NULL); 320 321 node->datum = NULL; 322 } 323 324 325 /* 326 * Node-specific functions 327 */ 328 329 /* Return the first node from the given list, or NULL if the list is empty. */ 330 LstNode 331 Lst_First(Lst list) 332 { 333 assert(list != NULL); 334 335 return list->first; 336 } 337 338 /* Return the last node from the given list, or NULL if the list is empty. */ 339 LstNode 340 Lst_Last(Lst list) 341 { 342 assert(list != NULL); 343 344 return list->last; 345 } 346 347 /* Return the successor to the given node on its list, or NULL. */ 348 LstNode 349 LstNode_Next(LstNode node) 350 { 351 assert(node != NULL); 352 353 return node->next; 354 } 355 356 /* Return the predecessor to the given node on its list, or NULL. */ 357 LstNode 358 LstNode_Prev(LstNode node) 359 { 360 assert(node != NULL); 361 return node->prev; 362 } 363 364 /* Return the datum stored in the given node. */ 365 void * 366 LstNode_Datum(LstNode node) 367 { 368 assert(node != NULL); 369 return node->datum; 370 } 371 372 373 /* 374 * Functions for entire lists 375 */ 376 377 /* Return TRUE if the given list is empty. */ 378 Boolean 379 Lst_IsEmpty(Lst list) 380 { 381 assert(list != NULL); 382 383 return LstIsEmpty(list); 384 } 385 386 /* Return the first node from the list for which the match function returns 387 * TRUE, or NULL if none of the nodes matched. */ 388 LstNode 389 Lst_Find(Lst list, LstFindProc match, const void *matchArgs) 390 { 391 return Lst_FindFrom(list, Lst_First(list), match, matchArgs); 392 } 393 394 /* Return the first node from the list, starting at the given node, for which 395 * the match function returns TRUE, or NULL if none of the nodes matches. 396 * 397 * The start node may be NULL, in which case nothing is found. This allows 398 * for passing Lst_First or LstNode_Next as the start node. */ 399 LstNode 400 Lst_FindFrom(Lst list, LstNode node, LstFindProc match, const void *matchArgs) 401 { 402 LstNode tln; 403 404 assert(list != NULL); 405 assert(match != NULL); 406 407 for (tln = node; tln != NULL; tln = tln->next) { 408 if (match(tln->datum, matchArgs)) 409 return tln; 410 } 411 412 return NULL; 413 } 414 415 /* Return the first node that contains the given datum, or NULL. */ 416 LstNode 417 Lst_FindDatum(Lst list, const void *datum) 418 { 419 LstNode node; 420 421 assert(list != NULL); 422 assert(datum != NULL); 423 424 for (node = list->first; node != NULL; node = node->next) { 425 if (node->datum == datum) { 426 return node; 427 } 428 } 429 430 return NULL; 431 } 432 433 /* Apply the given function to each element of the given list. The function 434 * should return 0 if traversal should continue and non-zero if it should 435 * abort. */ 436 int 437 Lst_ForEach(Lst list, LstActionProc proc, void *procData) 438 { 439 if (LstIsEmpty(list)) 440 return 0; /* XXX: Document what this value means. */ 441 return Lst_ForEachFrom(list, Lst_First(list), proc, procData); 442 } 443 444 /* Apply the given function to each element of the given list, starting from 445 * the given node. The function should return 0 if traversal should continue, 446 * and non-zero if it should abort. */ 447 int 448 Lst_ForEachFrom(Lst list, LstNode node, 449 LstActionProc proc, void *procData) 450 { 451 LstNode tln = node; 452 LstNode next; 453 Boolean done; 454 int result; 455 456 assert(list != NULL); 457 assert(node != NULL); 458 assert(proc != NULL); 459 460 do { 461 /* 462 * Take care of having the current element deleted out from under 463 * us. 464 */ 465 466 next = tln->next; 467 468 /* 469 * We're done with the traversal if 470 * - the next node to examine doesn't exist and 471 * - nothing's been added after the current node (check this 472 * after proc() has been called). 473 */ 474 done = next == NULL; 475 476 tln->useCount++; 477 result = (*proc)(tln->datum, procData); 478 tln->useCount--; 479 480 /* 481 * Now check whether a node has been added. 482 * Note: this doesn't work if this node was deleted before 483 * the new node was added. 484 */ 485 if (next != tln->next) { 486 next = tln->next; 487 done = 0; 488 } 489 490 if (tln->deleted) { 491 free((char *)tln); 492 } 493 tln = next; 494 } while (!result && !LstIsEmpty(list) && !done); 495 496 return result; 497 } 498 499 /* Move all nodes from list2 to the end of list1. 500 * List2 is destroyed and freed. */ 501 void 502 Lst_MoveAll(Lst list1, Lst list2) 503 { 504 assert(list1 != NULL); 505 assert(list2 != NULL); 506 507 if (list2->first != NULL) { 508 list2->first->prev = list1->last; 509 if (list1->last != NULL) { 510 list1->last->next = list2->first; 511 } else { 512 list1->first = list2->first; 513 } 514 list1->last = list2->last; 515 } 516 free(list2); 517 } 518 519 /* Copy the element data from src to the start of dst. */ 520 void 521 Lst_PrependAll(Lst dst, Lst src) 522 { 523 LstNode node; 524 for (node = src->last; node != NULL; node = node->prev) 525 Lst_Prepend(dst, node->datum); 526 } 527 528 /* Copy the element data from src to the end of dst. */ 529 void 530 Lst_AppendAll(Lst dst, Lst src) 531 { 532 LstNode node; 533 for (node = src->first; node != NULL; node = node->next) 534 Lst_Append(dst, node->datum); 535 } 536 537 /* 538 * these functions are for dealing with a list as a table, of sorts. 539 * An idea of the "current element" is kept and used by all the functions 540 * between Lst_Open() and Lst_Close(). 541 * 542 * The sequential functions access the list in a slightly different way. 543 * CurPtr points to their idea of the current node in the list and they 544 * access the list based on it. 545 */ 546 547 /* Open a list for sequential access. A list can still be searched, etc., 548 * without confusing these functions. */ 549 void 550 Lst_Open(Lst list) 551 { 552 assert(list != NULL); 553 assert(!list->isOpen); 554 555 list->isOpen = TRUE; 556 list->lastAccess = LstIsEmpty(list) ? Head : Unknown; 557 list->curr = NULL; 558 } 559 560 /* Return the next node for the given list, or NULL if the end has been 561 * reached. */ 562 LstNode 563 Lst_Next(Lst list) 564 { 565 LstNode node; 566 567 assert(list != NULL); 568 assert(list->isOpen); 569 570 list->prev = list->curr; 571 572 if (list->curr == NULL) { 573 if (list->lastAccess == Unknown) { 574 /* 575 * If we're just starting out, lastAccess will be Unknown. 576 * Then we want to start this thing off in the right 577 * direction -- at the start with lastAccess being Middle. 578 */ 579 list->curr = node = list->first; 580 list->lastAccess = Middle; 581 } else { 582 node = NULL; 583 list->lastAccess = Tail; 584 } 585 } else { 586 node = list->curr->next; 587 list->curr = node; 588 589 if (node == list->first || node == NULL) { 590 /* 591 * If back at the front, then we've hit the end... 592 */ 593 list->lastAccess = Tail; 594 } else { 595 /* 596 * Reset to Middle if gone past first. 597 */ 598 list->lastAccess = Middle; 599 } 600 } 601 602 return node; 603 } 604 605 /* Close a list which was opened for sequential access. */ 606 void 607 Lst_Close(Lst list) 608 { 609 assert(list != NULL); 610 assert(list->isOpen); 611 612 list->isOpen = FALSE; 613 list->lastAccess = Unknown; 614 } 615 616 617 /* 618 * for using the list as a queue 619 */ 620 621 /* Add the datum to the tail of the given list. */ 622 void 623 Lst_Enqueue(Lst list, void *datum) 624 { 625 Lst_Append(list, datum); 626 } 627 628 /* Remove and return the datum at the head of the given list. */ 629 void * 630 Lst_Dequeue(Lst list) 631 { 632 void *datum; 633 634 assert(list != NULL); 635 assert(!LstIsEmpty(list)); 636 637 datum = list->first->datum; 638 Lst_Remove(list, list->first); 639 assert(datum != NULL); 640 return datum; 641 } 642