1 /* 2 * Copyright (c) 1998, 1999 Matthew R. Green 3 * All rights reserved. 4 * Copyright (c) 1998 5 * Perry E. Metzger. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed for the NetBSD Project 18 * by Perry E. Metzger. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 * $NetBSD: rcorder.c,v 1.7 2000/08/04 07:33:55 enami Exp $ 34 */ 35 36 #include <sys/types.h> 37 #include <sys/stat.h> 38 39 #include <err.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 #include <libutil.h> 45 46 #include "sprite.h" 47 #include "hash.h" 48 49 #ifdef DEBUG 50 int debug = 0; 51 # define DPRINTF(args) if (debug) { fflush(stdout); fprintf args; } 52 #else 53 # define DPRINTF(args) 54 #endif 55 56 int provide; 57 58 #define REQUIRE_STR "# REQUIRE:" 59 #define REQUIRE_LEN (sizeof(REQUIRE_STR) - 1) 60 #define REQUIRES_STR "# REQUIRES:" 61 #define REQUIRES_LEN (sizeof(REQUIRES_STR) - 1) 62 #define PROVIDE_STR "# PROVIDE:" 63 #define PROVIDE_LEN (sizeof(PROVIDE_STR) - 1) 64 #define PROVIDES_STR "# PROVIDES:" 65 #define PROVIDES_LEN (sizeof(PROVIDES_STR) - 1) 66 #define BEFORE_STR "# BEFORE:" 67 #define BEFORE_LEN (sizeof(BEFORE_STR) - 1) 68 #define KEYWORD_STR "# KEYWORD:" 69 #define KEYWORD_LEN (sizeof(KEYWORD_STR) - 1) 70 #define KEYWORDS_STR "# KEYWORDS:" 71 #define KEYWORDS_LEN (sizeof(KEYWORDS_STR) - 1) 72 73 int exit_code; 74 int file_count; 75 char **file_list; 76 77 typedef int bool; 78 #define TRUE 1 79 #define FALSE 0 80 typedef bool flag; 81 #define SET TRUE 82 #define RESET FALSE 83 84 Hash_Table provide_hash_s, *provide_hash; 85 86 typedef struct provnode provnode; 87 typedef struct filenode filenode; 88 typedef struct f_provnode f_provnode; 89 typedef struct f_reqnode f_reqnode; 90 typedef struct strnodelist strnodelist; 91 92 struct provnode { 93 flag head; 94 flag in_progress; 95 filenode *fnode; 96 provnode *next, *last; 97 char *name; 98 }; 99 100 struct f_provnode { 101 provnode *pnode; 102 f_provnode *next; 103 }; 104 105 struct f_reqnode { 106 Hash_Entry *entry; 107 f_reqnode *next; 108 }; 109 110 struct strnodelist { 111 filenode *node; 112 strnodelist *next; 113 char s[1]; 114 }; 115 116 struct filenode { 117 char *filename; 118 flag in_progress; 119 filenode *next, *last; 120 f_reqnode *req_list; 121 f_provnode *prov_list; 122 strnodelist *keyword_list; 123 }; 124 125 filenode fn_head_s, *fn_head; 126 127 strnodelist *bl_list; 128 strnodelist *keep_list; 129 strnodelist *skip_list; 130 strnodelist *onetime_list; 131 132 void do_file(filenode *fnode); 133 void strnode_add(strnodelist **, char *, filenode *); 134 int skip_ok(filenode *fnode); 135 int keep_ok(filenode *fnode); 136 void satisfy_req(f_reqnode *rnode, char *filename); 137 void crunch_file(char *); 138 void parse_require(filenode *, char *); 139 void parse_provide(filenode *, char *); 140 void parse_before(filenode *, char *); 141 void parse_keywords(filenode *, char *); 142 filenode *filenode_new(char *); 143 void add_require(filenode *, char *); 144 void add_provide(filenode *, char *); 145 void add_before(filenode *, char *); 146 void add_keyword(filenode *, char *); 147 void insert_before(void); 148 Hash_Entry *make_fake_provision(filenode *); 149 void crunch_all_files(void); 150 void initialize(void); 151 void generate_ordering(void); 152 153 int 154 main(int argc, char **argv) 155 { 156 int ch; 157 158 while ((ch = getopt(argc, argv, "dpk:s:o:")) != -1) 159 switch (ch) { 160 case 'd': 161 #ifdef DEBUG 162 debug = 1; 163 #else 164 warnx("debugging not compiled in, -d ignored"); 165 #endif 166 break; 167 case 'k': 168 strnode_add(&keep_list, optarg, 0); 169 break; 170 case 's': 171 strnode_add(&skip_list, optarg, 0); 172 break; 173 case 'o': 174 strnode_add(&onetime_list, optarg, 0); 175 break; 176 case 'p': 177 provide = 1; 178 break; 179 default: 180 /* XXX should crunch it? */ 181 break; 182 } 183 argc -= optind; 184 argv += optind; 185 186 file_count = argc; 187 file_list = argv; 188 189 DPRINTF((stderr, "parse_args\n")); 190 initialize(); 191 DPRINTF((stderr, "initialize\n")); 192 crunch_all_files(); 193 DPRINTF((stderr, "crunch_all_files\n")); 194 generate_ordering(); 195 DPRINTF((stderr, "generate_ordering\n")); 196 197 exit(exit_code); 198 } 199 200 /* 201 * initialise various variables. 202 */ 203 void 204 initialize(void) 205 { 206 207 fn_head = &fn_head_s; 208 209 provide_hash = &provide_hash_s; 210 Hash_InitTable(provide_hash, file_count); 211 } 212 213 /* generic function to insert a new strnodelist element */ 214 void 215 strnode_add(strnodelist **listp, char *s, filenode *fnode) 216 { 217 strnodelist *ent; 218 219 ent = emalloc(sizeof *ent + strlen(s)); 220 ent->node = fnode; 221 strcpy(ent->s, s); 222 ent->next = *listp; 223 *listp = ent; 224 } 225 226 /* 227 * below are the functions that deal with creating the lists 228 * from the filename's given and the dependancies and provisions 229 * in each of these files. no ordering or checking is done here. 230 */ 231 232 /* 233 * we have a new filename, create a new filenode structure. 234 * fill in the bits, and put it in the filenode linked list 235 */ 236 filenode * 237 filenode_new(char *filename) 238 { 239 filenode *temp; 240 241 temp = emalloc(sizeof(*temp)); 242 memset(temp, 0, sizeof(*temp)); 243 temp->filename = estrdup(filename); 244 temp->req_list = NULL; 245 temp->prov_list = NULL; 246 temp->keyword_list = NULL; 247 temp->in_progress = RESET; 248 /* 249 * link the filenode into the list of filenodes. 250 * note that the double linking means we can delete a 251 * filenode without searching for where it belongs. 252 */ 253 temp->next = fn_head->next; 254 if (temp->next != NULL) 255 temp->next->last = temp; 256 temp->last = fn_head; 257 fn_head->next = temp; 258 return (temp); 259 } 260 261 /* 262 * add a requirement to a filenode. 263 */ 264 void 265 add_require(filenode *fnode, char *s) 266 { 267 Hash_Entry *entry; 268 f_reqnode *rnode; 269 int new; 270 271 entry = Hash_CreateEntry(provide_hash, s, &new); 272 if (new) 273 Hash_SetValue(entry, NULL); 274 rnode = emalloc(sizeof(*rnode)); 275 rnode->entry = entry; 276 rnode->next = fnode->req_list; 277 fnode->req_list = rnode; 278 } 279 280 /* 281 * add a provision to a filenode. if this provision doesn't 282 * have a head node, create one here. 283 */ 284 void 285 add_provide(filenode *fnode, char *s) 286 { 287 Hash_Entry *entry; 288 f_provnode *f_pnode; 289 provnode *pnode, *head; 290 int new; 291 292 entry = Hash_CreateEntry(provide_hash, s, &new); 293 head = Hash_GetValue(entry); 294 295 /* create a head node if necessary. */ 296 if (head == NULL) { 297 head = emalloc(sizeof(*head)); 298 head->head = SET; 299 head->in_progress = RESET; 300 head->fnode = NULL; 301 head->last = head->next = NULL; 302 Hash_SetValue(entry, head); 303 } 304 #if 0 305 /* 306 * Don't warn about this. We want to be able to support 307 * scripts that do two complex things: 308 * 309 * - Two independent scripts which both provide the 310 * same thing. Both scripts must be executed in 311 * any order to meet the barrier. An example: 312 * 313 * Script 1: 314 * 315 * PROVIDE: mail 316 * REQUIRE: LOGIN 317 * 318 * Script 2: 319 * 320 * PROVIDE: mail 321 * REQUIRE: LOGIN 322 * 323 * - Two interdependent scripts which both provide the 324 * same thing. Both scripts must be executed in 325 * graph order to meet the barrier. An example: 326 * 327 * Script 1: 328 * 329 * PROVIDE: nameservice dnscache 330 * REQUIRE: SERVERS 331 * 332 * Script 2: 333 * 334 * PROVIDE: nameservice nscd 335 * REQUIRE: dnscache 336 */ 337 else if (new == 0) { 338 warnx("file `%s' provides `%s'.", fnode->filename, s); 339 warnx("\tpreviously seen in `%s'.", 340 head->next->fnode->filename); 341 } 342 #endif 343 344 pnode = emalloc(sizeof(*pnode)); 345 pnode->head = RESET; 346 pnode->in_progress = RESET; 347 pnode->fnode = fnode; 348 pnode->next = head->next; 349 pnode->last = head; 350 pnode->name = strdup(s); 351 head->next = pnode; 352 if (pnode->next != NULL) 353 pnode->next->last = pnode; 354 355 f_pnode = emalloc(sizeof(*f_pnode)); 356 f_pnode->pnode = pnode; 357 f_pnode->next = fnode->prov_list; 358 fnode->prov_list = f_pnode; 359 } 360 361 /* 362 * put the BEFORE: lines to a list and handle them later. 363 */ 364 void 365 add_before(filenode *fnode, char *s) 366 { 367 strnodelist *bf_ent; 368 369 bf_ent = emalloc(sizeof *bf_ent + strlen(s)); 370 bf_ent->node = fnode; 371 strcpy(bf_ent->s, s); 372 bf_ent->next = bl_list; 373 bl_list = bf_ent; 374 } 375 376 /* 377 * add a key to a filenode. 378 */ 379 void 380 add_keyword(filenode *fnode, char *s) 381 { 382 383 strnode_add(&fnode->keyword_list, s, fnode); 384 } 385 386 /* 387 * loop over the rest of a REQUIRE line, giving each word to 388 * add_require() to do the real work. 389 */ 390 void 391 parse_require(filenode *node, char *buffer) 392 { 393 char *s; 394 395 while ((s = strsep(&buffer, " \t\n")) != NULL) 396 if (*s != '\0') 397 add_require(node, s); 398 } 399 400 /* 401 * loop over the rest of a PROVIDE line, giving each word to 402 * add_provide() to do the real work. 403 */ 404 void 405 parse_provide(filenode *node, char *buffer) 406 { 407 char *s; 408 409 while ((s = strsep(&buffer, " \t\n")) != NULL) 410 if (*s != '\0') 411 add_provide(node, s); 412 } 413 414 /* 415 * loop over the rest of a BEFORE line, giving each word to 416 * add_before() to do the real work. 417 */ 418 void 419 parse_before(filenode *node, char *buffer) 420 { 421 char *s; 422 423 while ((s = strsep(&buffer, " \t\n")) != NULL) 424 if (*s != '\0') 425 add_before(node, s); 426 } 427 428 /* 429 * loop over the rest of a KEYWORD line, giving each word to 430 * add_keyword() to do the real work. 431 */ 432 void 433 parse_keywords(filenode *node, char *buffer) 434 { 435 char *s; 436 437 while ((s = strsep(&buffer, " \t\n")) != NULL) 438 if (*s != '\0') 439 add_keyword(node, s); 440 } 441 442 /* 443 * given a file name, create a filenode for it, read in lines looking 444 * for provision and requirement lines, building the graphs as needed. 445 */ 446 void 447 crunch_file(char *filename) 448 { 449 FILE *fp; 450 char *buf; 451 int require_flag, provide_flag, before_flag, keywords_flag; 452 enum { BEFORE_PARSING, PARSING, PARSING_DONE } state; 453 filenode *node; 454 char delims[3] = { '\\', '\\', '\0' }; 455 struct stat st; 456 457 if ((fp = fopen(filename, "r")) == NULL) { 458 warn("could not open %s", filename); 459 return; 460 } 461 462 if (fstat(fileno(fp), &st) == -1) { 463 warn("could not stat %s", filename); 464 fclose(fp); 465 return; 466 } 467 468 if (!S_ISREG(st.st_mode)) { 469 #if 0 470 warnx("%s is not a file", filename); 471 #endif 472 fclose(fp); 473 return; 474 } 475 476 node = filenode_new(filename); 477 478 /* 479 * we don't care about length, line number, don't want # for comments, 480 * and have no flags. 481 */ 482 for (state = BEFORE_PARSING; state != PARSING_DONE && 483 (buf = fparseln(fp, NULL, NULL, delims, 0)) != NULL; free(buf)) { 484 require_flag = provide_flag = before_flag = keywords_flag = 0; 485 if (strncmp(REQUIRE_STR, buf, REQUIRE_LEN) == 0) 486 require_flag = REQUIRE_LEN; 487 else if (strncmp(REQUIRES_STR, buf, REQUIRES_LEN) == 0) 488 require_flag = REQUIRES_LEN; 489 else if (strncmp(PROVIDE_STR, buf, PROVIDE_LEN) == 0) 490 provide_flag = PROVIDE_LEN; 491 else if (strncmp(PROVIDES_STR, buf, PROVIDES_LEN) == 0) 492 provide_flag = PROVIDES_LEN; 493 else if (strncmp(BEFORE_STR, buf, BEFORE_LEN) == 0) 494 before_flag = BEFORE_LEN; 495 else if (strncmp(KEYWORD_STR, buf, KEYWORD_LEN) == 0) 496 keywords_flag = KEYWORD_LEN; 497 else if (strncmp(KEYWORDS_STR, buf, KEYWORDS_LEN) == 0) 498 keywords_flag = KEYWORDS_LEN; 499 else { 500 if (state == PARSING) 501 state = PARSING_DONE; 502 continue; 503 } 504 505 state = PARSING; 506 if (require_flag) 507 parse_require(node, buf + require_flag); 508 else if (provide_flag) 509 parse_provide(node, buf + provide_flag); 510 else if (before_flag) 511 parse_before(node, buf + before_flag); 512 else if (keywords_flag) 513 parse_keywords(node, buf + keywords_flag); 514 } 515 fclose(fp); 516 } 517 518 Hash_Entry * 519 make_fake_provision(filenode *node) 520 { 521 Hash_Entry *entry; 522 f_provnode *f_pnode; 523 provnode *head, *pnode; 524 static int i = 0; 525 int new; 526 char buffer[30]; 527 528 do { 529 snprintf(buffer, sizeof buffer, "fake_prov_%08d", i++); 530 entry = Hash_CreateEntry(provide_hash, buffer, &new); 531 } while (new == 0); 532 head = emalloc(sizeof(*head)); 533 head->head = SET; 534 head->in_progress = RESET; 535 head->fnode = NULL; 536 head->last = head->next = NULL; 537 Hash_SetValue(entry, head); 538 539 pnode = emalloc(sizeof(*pnode)); 540 pnode->head = RESET; 541 pnode->in_progress = RESET; 542 pnode->fnode = node; 543 pnode->next = head->next; 544 pnode->last = head; 545 pnode->name = strdup(buffer); 546 head->next = pnode; 547 if (pnode->next != NULL) 548 pnode->next->last = pnode; 549 550 f_pnode = emalloc(sizeof(*f_pnode)); 551 f_pnode->pnode = pnode; 552 f_pnode->next = node->prov_list; 553 node->prov_list = f_pnode; 554 555 return (entry); 556 } 557 558 /* 559 * go through the BEFORE list, inserting requirements into the graph(s) 560 * as required. in the before list, for each entry B, we have a file F 561 * and a string S. we create a "fake" provision (P) that F provides. 562 * for each entry in the provision list for S, add a requirement to 563 * that provisions filenode for P. 564 */ 565 void 566 insert_before(void) 567 { 568 Hash_Entry *entry, *fake_prov_entry; 569 provnode *pnode; 570 f_reqnode *rnode; 571 strnodelist *bl; 572 int new; 573 574 while (bl_list != NULL) { 575 bl = bl_list->next; 576 577 fake_prov_entry = make_fake_provision(bl_list->node); 578 579 entry = Hash_CreateEntry(provide_hash, bl_list->s, &new); 580 if (new == 1 && !provide) 581 warnx("file `%s' is before unknown provision `%s'", bl_list->node->filename, bl_list->s); 582 583 for (pnode = Hash_GetValue(entry); pnode; pnode = pnode->next) { 584 if (pnode->head) 585 continue; 586 587 rnode = emalloc(sizeof(*rnode)); 588 rnode->entry = fake_prov_entry; 589 rnode->next = pnode->fnode->req_list; 590 pnode->fnode->req_list = rnode; 591 } 592 593 free(bl_list); 594 bl_list = bl; 595 } 596 } 597 598 /* 599 * loop over all the files calling crunch_file() on them to do the 600 * real work. after we have built all the nodes, insert the BEFORE: 601 * lines into graph(s). 602 */ 603 void 604 crunch_all_files(void) 605 { 606 int i; 607 608 for (i = 0; i < file_count; i++) 609 crunch_file(file_list[i]); 610 insert_before(); 611 } 612 613 /* 614 * below are the functions that traverse the graphs we have built 615 * finding out the desired ordering, printing each file in turn. 616 * if missing requirements, or cyclic graphs are detected, a 617 * warning will be issued, and we will continue on.. 618 */ 619 620 /* 621 * given a requirement node (in a filename) we attempt to satisfy it. 622 * we do some sanity checking first, to ensure that we have providers, 623 * aren't already satisfied and aren't already being satisfied (ie, 624 * cyclic). if we pass all this, we loop over the provision list 625 * calling do_file() (enter recursion) for each filenode in this 626 * provision. 627 */ 628 void 629 satisfy_req(f_reqnode *rnode, char *filename) 630 { 631 Hash_Entry *entry; 632 provnode *head; 633 634 entry = rnode->entry; 635 head = Hash_GetValue(entry); 636 637 if (head == NULL) { 638 warnx("requirement `%s' in file `%s' has no providers.", 639 Hash_GetKey(entry), filename); 640 exit_code = 1; 641 return; 642 } 643 644 /* return if the requirement is already satisfied. */ 645 if (head->next == NULL) 646 return; 647 648 /* 649 * if list is marked as in progress, 650 * print that there is a circular dependency on it and abort 651 */ 652 if (head->in_progress == SET) { 653 warnx("Circular dependency on provision `%s' in file `%s'.", 654 Hash_GetKey(entry), filename); 655 exit_code = 1; 656 return; 657 } 658 659 head->in_progress = SET; 660 661 /* 662 * while provision_list is not empty 663 * do_file(first_member_of(provision_list)); 664 */ 665 while (head->next != NULL) 666 do_file(head->next->fnode); 667 } 668 669 int 670 skip_ok(filenode *fnode) 671 { 672 strnodelist *s; 673 strnodelist *k; 674 675 for (s = skip_list; s; s = s->next) 676 for (k = fnode->keyword_list; k; k = k->next) 677 if (strcmp(k->s, s->s) == 0) 678 return (0); 679 680 return (1); 681 } 682 683 int 684 keep_ok(filenode *fnode) 685 { 686 strnodelist *s; 687 strnodelist *k; 688 689 for (s = keep_list; s; s = s->next) 690 for (k = fnode->keyword_list; k; k = k->next) 691 if (strcmp(k->s, s->s) == 0) 692 return (1); 693 694 /* an empty keep_list means every one */ 695 return (!keep_list); 696 } 697 698 /* 699 * given a filenode, we ensure we are not a cyclic graph. if this 700 * is ok, we loop over the filenodes requirements, calling satisfy_req() 701 * for each of them.. once we have done this, remove this filenode 702 * from each provision table, as we are now done. 703 * 704 * NOTE: do_file() is called recursively from several places and cannot 705 * safely free() anything related to items that may be recursed on. 706 * Circular dependancies will cause problems if we do. 707 */ 708 void 709 do_file(filenode *fnode) 710 { 711 f_reqnode *r; 712 /*f_reqnode *r_tmp;*/ 713 f_provnode *p, *p_tmp; 714 provnode *pnode; 715 int was_set; 716 717 DPRINTF((stderr, "do_file on %s.\n", fnode->filename)); 718 719 /* 720 * if fnode is marked as in progress, 721 * print that fnode; is circularly depended upon and abort. 722 */ 723 if (fnode->in_progress == SET) { 724 warnx("Circular dependency on file `%s'.", 725 fnode->filename); 726 was_set = exit_code = 1; 727 } else 728 was_set = 0; 729 730 /* mark fnode */ 731 fnode->in_progress = SET; 732 733 /* 734 * for each requirement of fnode -> r 735 * satisfy_req(r, filename) 736 */ 737 r = fnode->req_list; 738 while (r != NULL) { 739 /*r_tmp = r;*/ 740 satisfy_req(r, fnode->filename); 741 r = r->next; 742 /*free(r_tmp);*/ 743 } 744 fnode->req_list = NULL; 745 746 /* 747 * for each provision of fnode -> p 748 * remove fnode from provision list for p in hash table 749 */ 750 p = fnode->prov_list; 751 while (p != NULL) { 752 p_tmp = p; 753 pnode = p->pnode; 754 if (pnode->next != NULL) { 755 pnode->next->last = pnode->last; 756 } 757 if (pnode->last != NULL) { 758 pnode->last->next = pnode->next; 759 } 760 free(pnode); 761 p = p->next; 762 free(p_tmp); 763 } 764 fnode->prov_list = NULL; 765 766 /* do_it(fnode) */ 767 DPRINTF((stderr, "next do: ")); 768 769 /* if we were already in progress, don't print again */ 770 if (was_set == 0 && skip_ok(fnode) && keep_ok(fnode)) 771 printf("%s\n", fnode->filename); 772 773 if (fnode->next != NULL) { 774 fnode->next->last = fnode->last; 775 } 776 if (fnode->last != NULL) { 777 fnode->last->next = fnode->next; 778 } 779 780 DPRINTF((stderr, "nuking %s\n", fnode->filename)); 781 /*free(fnode->filename);*/ 782 /*free(fnode);*/ 783 } 784 785 void 786 generate_ordering(void) 787 { 788 789 /* 790 * while there remain undone files{f}, 791 * pick an arbitrary f, and do_file(f) 792 * Note that the first file in the file list is perfectly 793 * arbitrary, and easy to find, so we use that. 794 */ 795 796 /* 797 * N.B.: the file nodes "self delete" after they execute, so 798 * after each iteration of the loop, the head will be pointing 799 * to something totally different. The loop ends up being 800 * executed only once for every strongly connected set of 801 * nodes. 802 */ 803 if (provide) { 804 /* 805 * List all keywords provided by the listed files 806 */ 807 filenode *file; 808 f_provnode *f_prov; 809 810 for (file = fn_head->next; file; file = file->next) { 811 for (f_prov = file->prov_list; f_prov; f_prov = f_prov->next) { 812 if (strncmp(f_prov->pnode->name, "fake_", 5) != 0) 813 printf("%s\n", f_prov->pnode->name); 814 } 815 } 816 } else if (onetime_list) { 817 /* 818 * Only list dependanacies required to start particular 819 * keywords. 820 */ 821 strnodelist *scan; 822 filenode *file; 823 f_provnode *f_prov; 824 825 for (scan = onetime_list; scan; scan = scan->next) { 826 for (file = fn_head->next; file; file = file->next) { 827 for (f_prov = file->prov_list; f_prov; f_prov = f_prov->next) { 828 if (strcmp(scan->s, f_prov->pnode->name) == 0) { 829 do_file(file); 830 break; 831 } 832 } 833 if (f_prov) 834 break; 835 } 836 } 837 } else { 838 while (fn_head->next != NULL) { 839 DPRINTF((stderr, "generate on %s\n", fn_head->next->filename)); 840 do_file(fn_head->next); 841 } 842 } 843 } 844