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