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 static int debug = 0; 51 # define DPRINTF(args) if (debug) { fflush(stdout); fprintf args; } 52 #else 53 # define DPRINTF(args) 54 #endif 55 56 #define REQUIRE_STR "# REQUIRE:" 57 #define REQUIRE_LEN (sizeof(REQUIRE_STR) - 1) 58 #define REQUIRES_STR "# REQUIRES:" 59 #define REQUIRES_LEN (sizeof(REQUIRES_STR) - 1) 60 #define PROVIDE_STR "# PROVIDE:" 61 #define PROVIDE_LEN (sizeof(PROVIDE_STR) - 1) 62 #define PROVIDES_STR "# PROVIDES:" 63 #define PROVIDES_LEN (sizeof(PROVIDES_STR) - 1) 64 #define BEFORE_STR "# BEFORE:" 65 #define BEFORE_LEN (sizeof(BEFORE_STR) - 1) 66 #define KEYWORD_STR "# KEYWORD:" 67 #define KEYWORD_LEN (sizeof(KEYWORD_STR) - 1) 68 #define KEYWORDS_STR "# KEYWORDS:" 69 #define KEYWORDS_LEN (sizeof(KEYWORDS_STR) - 1) 70 71 static int exit_code; 72 static int file_count; 73 static char **file_list; 74 75 typedef int bool; 76 #define TRUE 1 77 #define FALSE 0 78 typedef bool flag; 79 #define SET TRUE 80 #define RESET FALSE 81 82 static Hash_Table provide_hash_s, *provide_hash; 83 84 typedef struct provnode provnode; 85 typedef struct filenode filenode; 86 typedef struct f_provnode f_provnode; 87 typedef struct f_reqnode f_reqnode; 88 typedef struct strnodelist strnodelist; 89 90 struct provnode { 91 flag head; 92 flag in_progress; 93 filenode *fnode; 94 provnode *next, *last; 95 char *name; 96 }; 97 98 struct f_provnode { 99 provnode *pnode; 100 f_provnode *next; 101 }; 102 103 struct f_reqnode { 104 Hash_Entry *entry; 105 f_reqnode *next; 106 }; 107 108 struct strnodelist { 109 filenode *node; 110 strnodelist *next; 111 char s[1]; 112 }; 113 114 struct filenode { 115 char *filename; 116 flag in_progress; 117 filenode *next, *last; 118 f_reqnode *req_list; 119 f_provnode *prov_list; 120 strnodelist *keyword_list; 121 }; 122 123 static filenode fn_head_s, *fn_head; 124 125 static strnodelist *bl_list; 126 static strnodelist *keep_list; 127 static strnodelist *skip_list; 128 static strnodelist *onetime_list; 129 130 static int provide; 131 132 static void do_file(filenode *fnode); 133 static void strnode_add(strnodelist **, char *, filenode *); 134 static int skip_ok(filenode *fnode); 135 static int keep_ok(filenode *fnode); 136 static void satisfy_req(f_reqnode *rnode, char *filename); 137 static void crunch_file(char *); 138 static void parse_require(filenode *, char *); 139 static void parse_provide(filenode *, char *); 140 static void parse_before(filenode *, char *); 141 static void parse_keywords(filenode *, char *); 142 static filenode *filenode_new(char *); 143 static void add_require(filenode *, char *); 144 static void add_provide(filenode *, char *); 145 static void add_before(filenode *, char *); 146 static void add_keyword(filenode *, char *); 147 static void insert_before(void); 148 static Hash_Entry *make_fake_provision(filenode *); 149 static void crunch_all_files(void); 150 static void initialize(void); 151 static 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 static 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 static 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 dependencies 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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'", 582 bl_list->node->filename, bl_list->s); 583 584 for (pnode = Hash_GetValue(entry); pnode; pnode = pnode->next) { 585 if (pnode->head) 586 continue; 587 588 rnode = emalloc(sizeof(*rnode)); 589 rnode->entry = fake_prov_entry; 590 rnode->next = pnode->fnode->req_list; 591 pnode->fnode->req_list = rnode; 592 } 593 594 free(bl_list); 595 bl_list = bl; 596 } 597 } 598 599 /* 600 * loop over all the files calling crunch_file() on them to do the 601 * real work. after we have built all the nodes, insert the BEFORE: 602 * lines into graph(s). 603 */ 604 static void 605 crunch_all_files(void) 606 { 607 int i; 608 609 for (i = 0; i < file_count; i++) 610 crunch_file(file_list[i]); 611 insert_before(); 612 } 613 614 /* 615 * below are the functions that traverse the graphs we have built 616 * finding out the desired ordering, printing each file in turn. 617 * if missing requirements, or cyclic graphs are detected, a 618 * warning will be issued, and we will continue on.. 619 */ 620 621 /* 622 * given a requirement node (in a filename) we attempt to satisfy it. 623 * we do some sanity checking first, to ensure that we have providers, 624 * aren't already satisfied and aren't already being satisfied (ie, 625 * cyclic). if we pass all this, we loop over the provision list 626 * calling do_file() (enter recursion) for each filenode in this 627 * provision. 628 */ 629 static void 630 satisfy_req(f_reqnode *rnode, char *filename) 631 { 632 Hash_Entry *entry; 633 provnode *head; 634 635 entry = rnode->entry; 636 head = Hash_GetValue(entry); 637 638 if (head == NULL) { 639 warnx("requirement `%s' in file `%s' has no providers.", 640 Hash_GetKey(entry), filename); 641 exit_code = 1; 642 return; 643 } 644 645 /* return if the requirement is already satisfied. */ 646 if (head->next == NULL) 647 return; 648 649 /* 650 * if list is marked as in progress, 651 * print that there is a circular dependency on it and abort 652 */ 653 if (head->in_progress == SET) { 654 warnx("Circular dependency on provision `%s' in file `%s'.", 655 Hash_GetKey(entry), filename); 656 exit_code = 1; 657 return; 658 } 659 660 head->in_progress = SET; 661 662 /* 663 * while provision_list is not empty 664 * do_file(first_member_of(provision_list)); 665 */ 666 while (head->next != NULL) 667 do_file(head->next->fnode); 668 } 669 670 static int 671 skip_ok(filenode *fnode) 672 { 673 strnodelist *s; 674 strnodelist *k; 675 676 for (s = skip_list; s; s = s->next) 677 for (k = fnode->keyword_list; k; k = k->next) 678 if (strcmp(k->s, s->s) == 0) 679 return (0); 680 681 return (1); 682 } 683 684 static int 685 keep_ok(filenode *fnode) 686 { 687 strnodelist *s; 688 strnodelist *k; 689 690 for (s = keep_list; s; s = s->next) 691 for (k = fnode->keyword_list; k; k = k->next) 692 if (strcmp(k->s, s->s) == 0) 693 return (1); 694 695 /* an empty keep_list means every one */ 696 return (!keep_list); 697 } 698 699 /* 700 * given a filenode, we ensure we are not a cyclic graph. if this 701 * is ok, we loop over the filenodes requirements, calling satisfy_req() 702 * for each of them.. once we have done this, remove this filenode 703 * from each provision table, as we are now done. 704 * 705 * NOTE: do_file() is called recursively from several places and cannot 706 * safely free() anything related to items that may be recursed on. 707 * Circular dependencies will cause problems if we do. 708 */ 709 static void 710 do_file(filenode *fnode) 711 { 712 f_reqnode *r; 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 satisfy_req(r, fnode->filename); 740 r = r->next; 741 } 742 fnode->req_list = NULL; 743 744 /* 745 * for each provision of fnode -> p 746 * remove fnode from provision list for p in hash table 747 */ 748 p = fnode->prov_list; 749 while (p != NULL) { 750 p_tmp = p; 751 pnode = p->pnode; 752 if (pnode->next != NULL) { 753 pnode->next->last = pnode->last; 754 } 755 if (pnode->last != NULL) { 756 pnode->last->next = pnode->next; 757 } 758 free(pnode); 759 p = p->next; 760 free(p_tmp); 761 } 762 fnode->prov_list = NULL; 763 764 /* do_it(fnode) */ 765 DPRINTF((stderr, "next do: ")); 766 767 /* if we were already in progress, don't print again */ 768 if (was_set == 0 && skip_ok(fnode) && keep_ok(fnode)) 769 printf("%s\n", fnode->filename); 770 771 if (fnode->next != NULL) { 772 fnode->next->last = fnode->last; 773 } 774 if (fnode->last != NULL) { 775 fnode->last->next = fnode->next; 776 } 777 778 DPRINTF((stderr, "nuking %s\n", fnode->filename)); 779 } 780 781 static void 782 generate_ordering(void) 783 { 784 785 /* 786 * while there remain undone files{f}, 787 * pick an arbitrary f, and do_file(f) 788 * Note that the first file in the file list is perfectly 789 * arbitrary, and easy to find, so we use that. 790 */ 791 792 /* 793 * N.B.: the file nodes "self delete" after they execute, so 794 * after each iteration of the loop, the head will be pointing 795 * to something totally different. The loop ends up being 796 * executed only once for every strongly connected set of 797 * nodes. 798 */ 799 if (provide) { 800 /* 801 * List all keywords provided by the listed files 802 */ 803 filenode *file; 804 f_provnode *f_prov; 805 806 for (file = fn_head->next; file; file = file->next) { 807 for (f_prov = file->prov_list; f_prov; f_prov = f_prov->next) { 808 if (strncmp(f_prov->pnode->name, "fake_", 5) != 0) 809 printf("%s\n", f_prov->pnode->name); 810 } 811 } 812 } else if (onetime_list) { 813 /* 814 * Only list dependanacies required to start particular 815 * keywords. 816 */ 817 strnodelist *scan; 818 filenode *file; 819 f_provnode *f_prov; 820 821 for (scan = onetime_list; scan; scan = scan->next) { 822 for (file = fn_head->next; file; file = file->next) { 823 for (f_prov = file->prov_list; f_prov; f_prov = f_prov->next) { 824 if (strcmp(scan->s, f_prov->pnode->name) == 0) { 825 do_file(file); 826 break; 827 } 828 } 829 if (f_prov) 830 break; 831 } 832 } 833 } else { 834 while (fn_head->next != NULL) { 835 DPRINTF((stderr, "generate on %s\n", 836 fn_head->next->filename)); 837 do_file(fn_head->next); 838 } 839 } 840 } 841