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 int main(int, char *[]); 153 154 int 155 main(int argc, char **argv) 156 { 157 int ch; 158 159 while ((ch = getopt(argc, argv, "dpk:s:o:")) != -1) 160 switch (ch) { 161 case 'd': 162 #ifdef DEBUG 163 debug = 1; 164 #else 165 warnx("debugging not compiled in, -d ignored"); 166 #endif 167 break; 168 case 'k': 169 strnode_add(&keep_list, optarg, 0); 170 break; 171 case 's': 172 strnode_add(&skip_list, optarg, 0); 173 break; 174 case 'o': 175 strnode_add(&onetime_list, optarg, 0); 176 break; 177 case 'p': 178 provide = 1; 179 break; 180 default: 181 /* XXX should crunch it? */ 182 break; 183 } 184 argc -= optind; 185 argv += optind; 186 187 file_count = argc; 188 file_list = argv; 189 190 DPRINTF((stderr, "parse_args\n")); 191 initialize(); 192 DPRINTF((stderr, "initialize\n")); 193 crunch_all_files(); 194 DPRINTF((stderr, "crunch_all_files\n")); 195 generate_ordering(); 196 DPRINTF((stderr, "generate_ordering\n")); 197 198 exit(exit_code); 199 } 200 201 /* 202 * initialise various variables. 203 */ 204 void 205 initialize(void) 206 { 207 208 fn_head = &fn_head_s; 209 210 provide_hash = &provide_hash_s; 211 Hash_InitTable(provide_hash, file_count); 212 } 213 214 /* generic function to insert a new strnodelist element */ 215 void 216 strnode_add(strnodelist **listp, char *s, filenode *fnode) 217 { 218 strnodelist *ent; 219 220 ent = emalloc(sizeof *ent + strlen(s)); 221 ent->node = fnode; 222 strcpy(ent->s, s); 223 ent->next = *listp; 224 *listp = ent; 225 } 226 227 /* 228 * below are the functions that deal with creating the lists 229 * from the filename's given and the dependancies and provisions 230 * in each of these files. no ordering or checking is done here. 231 */ 232 233 /* 234 * we have a new filename, create a new filenode structure. 235 * fill in the bits, and put it in the filenode linked list 236 */ 237 filenode * 238 filenode_new(char *filename) 239 { 240 filenode *temp; 241 242 temp = emalloc(sizeof(*temp)); 243 memset(temp, 0, sizeof(*temp)); 244 temp->filename = estrdup(filename); 245 temp->req_list = NULL; 246 temp->prov_list = NULL; 247 temp->keyword_list = NULL; 248 temp->in_progress = RESET; 249 /* 250 * link the filenode into the list of filenodes. 251 * note that the double linking means we can delete a 252 * filenode without searching for where it belongs. 253 */ 254 temp->next = fn_head->next; 255 if (temp->next != NULL) 256 temp->next->last = temp; 257 temp->last = fn_head; 258 fn_head->next = temp; 259 return (temp); 260 } 261 262 /* 263 * add a requirement to a filenode. 264 */ 265 void 266 add_require(filenode *fnode, char *s) 267 { 268 Hash_Entry *entry; 269 f_reqnode *rnode; 270 int new; 271 272 entry = Hash_CreateEntry(provide_hash, s, &new); 273 if (new) 274 Hash_SetValue(entry, NULL); 275 rnode = emalloc(sizeof(*rnode)); 276 rnode->entry = entry; 277 rnode->next = fnode->req_list; 278 fnode->req_list = rnode; 279 } 280 281 /* 282 * add a provision to a filenode. if this provision doesn't 283 * have a head node, create one here. 284 */ 285 void 286 add_provide(filenode *fnode, char *s) 287 { 288 Hash_Entry *entry; 289 f_provnode *f_pnode; 290 provnode *pnode, *head; 291 int new; 292 293 entry = Hash_CreateEntry(provide_hash, s, &new); 294 head = Hash_GetValue(entry); 295 296 /* create a head node if necessary. */ 297 if (head == NULL) { 298 head = emalloc(sizeof(*head)); 299 head->head = SET; 300 head->in_progress = RESET; 301 head->fnode = NULL; 302 head->last = head->next = NULL; 303 Hash_SetValue(entry, head); 304 } 305 #if 0 306 /* 307 * Don't warn about this. We want to be able to support 308 * scripts that do two complex things: 309 * 310 * - Two independent scripts which both provide the 311 * same thing. Both scripts must be executed in 312 * any order to meet the barrier. An example: 313 * 314 * Script 1: 315 * 316 * PROVIDE: mail 317 * REQUIRE: LOGIN 318 * 319 * Script 2: 320 * 321 * PROVIDE: mail 322 * REQUIRE: LOGIN 323 * 324 * - Two interdependent scripts which both provide the 325 * same thing. Both scripts must be executed in 326 * graph order to meet the barrier. An example: 327 * 328 * Script 1: 329 * 330 * PROVIDE: nameservice dnscache 331 * REQUIRE: SERVERS 332 * 333 * Script 2: 334 * 335 * PROVIDE: nameservice nscd 336 * REQUIRE: dnscache 337 */ 338 else if (new == 0) { 339 warnx("file `%s' provides `%s'.", fnode->filename, s); 340 warnx("\tpreviously seen in `%s'.", 341 head->next->fnode->filename); 342 } 343 #endif 344 345 pnode = emalloc(sizeof(*pnode)); 346 pnode->head = RESET; 347 pnode->in_progress = RESET; 348 pnode->fnode = fnode; 349 pnode->next = head->next; 350 pnode->last = head; 351 pnode->name = strdup(s); 352 head->next = pnode; 353 if (pnode->next != NULL) 354 pnode->next->last = pnode; 355 356 f_pnode = emalloc(sizeof(*f_pnode)); 357 f_pnode->pnode = pnode; 358 f_pnode->next = fnode->prov_list; 359 fnode->prov_list = f_pnode; 360 } 361 362 /* 363 * put the BEFORE: lines to a list and handle them later. 364 */ 365 void 366 add_before(filenode *fnode, char *s) 367 { 368 strnodelist *bf_ent; 369 370 bf_ent = emalloc(sizeof *bf_ent + strlen(s)); 371 bf_ent->node = fnode; 372 strcpy(bf_ent->s, s); 373 bf_ent->next = bl_list; 374 bl_list = bf_ent; 375 } 376 377 /* 378 * add a key to a filenode. 379 */ 380 void 381 add_keyword(filenode *fnode, char *s) 382 { 383 384 strnode_add(&fnode->keyword_list, s, fnode); 385 } 386 387 /* 388 * loop over the rest of a REQUIRE line, giving each word to 389 * add_require() to do the real work. 390 */ 391 void 392 parse_require(filenode *node, char *buffer) 393 { 394 char *s; 395 396 while ((s = strsep(&buffer, " \t\n")) != NULL) 397 if (*s != '\0') 398 add_require(node, s); 399 } 400 401 /* 402 * loop over the rest of a PROVIDE line, giving each word to 403 * add_provide() to do the real work. 404 */ 405 void 406 parse_provide(filenode *node, char *buffer) 407 { 408 char *s; 409 410 while ((s = strsep(&buffer, " \t\n")) != NULL) 411 if (*s != '\0') 412 add_provide(node, s); 413 } 414 415 /* 416 * loop over the rest of a BEFORE line, giving each word to 417 * add_before() to do the real work. 418 */ 419 void 420 parse_before(filenode *node, char *buffer) 421 { 422 char *s; 423 424 while ((s = strsep(&buffer, " \t\n")) != NULL) 425 if (*s != '\0') 426 add_before(node, s); 427 } 428 429 /* 430 * loop over the rest of a KEYWORD line, giving each word to 431 * add_keyword() to do the real work. 432 */ 433 void 434 parse_keywords(filenode *node, char *buffer) 435 { 436 char *s; 437 438 while ((s = strsep(&buffer, " \t\n")) != NULL) 439 if (*s != '\0') 440 add_keyword(node, s); 441 } 442 443 /* 444 * given a file name, create a filenode for it, read in lines looking 445 * for provision and requirement lines, building the graphs as needed. 446 */ 447 void 448 crunch_file(char *filename) 449 { 450 FILE *fp; 451 char *buf; 452 int require_flag, provide_flag, before_flag, keywords_flag; 453 enum { BEFORE_PARSING, PARSING, PARSING_DONE } state; 454 filenode *node; 455 char delims[3] = { '\\', '\\', '\0' }; 456 struct stat st; 457 458 if ((fp = fopen(filename, "r")) == NULL) { 459 warn("could not open %s", filename); 460 return; 461 } 462 463 if (fstat(fileno(fp), &st) == -1) { 464 warn("could not stat %s", filename); 465 fclose(fp); 466 return; 467 } 468 469 if (!S_ISREG(st.st_mode)) { 470 #if 0 471 warnx("%s is not a file", filename); 472 #endif 473 fclose(fp); 474 return; 475 } 476 477 node = filenode_new(filename); 478 479 /* 480 * we don't care about length, line number, don't want # for comments, 481 * and have no flags. 482 */ 483 for (state = BEFORE_PARSING; state != PARSING_DONE && 484 (buf = fparseln(fp, NULL, NULL, delims, 0)) != NULL; free(buf)) { 485 require_flag = provide_flag = before_flag = keywords_flag = 0; 486 if (strncmp(REQUIRE_STR, buf, REQUIRE_LEN) == 0) 487 require_flag = REQUIRE_LEN; 488 else if (strncmp(REQUIRES_STR, buf, REQUIRES_LEN) == 0) 489 require_flag = REQUIRES_LEN; 490 else if (strncmp(PROVIDE_STR, buf, PROVIDE_LEN) == 0) 491 provide_flag = PROVIDE_LEN; 492 else if (strncmp(PROVIDES_STR, buf, PROVIDES_LEN) == 0) 493 provide_flag = PROVIDES_LEN; 494 else if (strncmp(BEFORE_STR, buf, BEFORE_LEN) == 0) 495 before_flag = BEFORE_LEN; 496 else if (strncmp(KEYWORD_STR, buf, KEYWORD_LEN) == 0) 497 keywords_flag = KEYWORD_LEN; 498 else if (strncmp(KEYWORDS_STR, buf, KEYWORDS_LEN) == 0) 499 keywords_flag = KEYWORDS_LEN; 500 else { 501 if (state == PARSING) 502 state = PARSING_DONE; 503 continue; 504 } 505 506 state = PARSING; 507 if (require_flag) 508 parse_require(node, buf + require_flag); 509 else if (provide_flag) 510 parse_provide(node, buf + provide_flag); 511 else if (before_flag) 512 parse_before(node, buf + before_flag); 513 else if (keywords_flag) 514 parse_keywords(node, buf + keywords_flag); 515 } 516 fclose(fp); 517 } 518 519 Hash_Entry * 520 make_fake_provision(filenode *node) 521 { 522 Hash_Entry *entry; 523 f_provnode *f_pnode; 524 provnode *head, *pnode; 525 static int i = 0; 526 int new; 527 char buffer[30]; 528 529 do { 530 snprintf(buffer, sizeof buffer, "fake_prov_%08d", i++); 531 entry = Hash_CreateEntry(provide_hash, buffer, &new); 532 } while (new == 0); 533 head = emalloc(sizeof(*head)); 534 head->head = SET; 535 head->in_progress = RESET; 536 head->fnode = NULL; 537 head->last = head->next = NULL; 538 Hash_SetValue(entry, head); 539 540 pnode = emalloc(sizeof(*pnode)); 541 pnode->head = RESET; 542 pnode->in_progress = RESET; 543 pnode->fnode = node; 544 pnode->next = head->next; 545 pnode->last = head; 546 pnode->name = strdup(buffer); 547 head->next = pnode; 548 if (pnode->next != NULL) 549 pnode->next->last = pnode; 550 551 f_pnode = emalloc(sizeof(*f_pnode)); 552 f_pnode->pnode = pnode; 553 f_pnode->next = node->prov_list; 554 node->prov_list = f_pnode; 555 556 return (entry); 557 } 558 559 /* 560 * go through the BEFORE list, inserting requirements into the graph(s) 561 * as required. in the before list, for each entry B, we have a file F 562 * and a string S. we create a "fake" provision (P) that F provides. 563 * for each entry in the provision list for S, add a requirement to 564 * that provisions filenode for P. 565 */ 566 void 567 insert_before(void) 568 { 569 Hash_Entry *entry, *fake_prov_entry; 570 provnode *pnode; 571 f_reqnode *rnode; 572 strnodelist *bl; 573 int new; 574 575 while (bl_list != NULL) { 576 bl = bl_list->next; 577 578 fake_prov_entry = make_fake_provision(bl_list->node); 579 580 entry = Hash_CreateEntry(provide_hash, bl_list->s, &new); 581 if (new == 1 && !provide) 582 warnx("file `%s' is before unknown provision `%s'", 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 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 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 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 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 dependancies will cause problems if we do. 708 */ 709 void 710 do_file(filenode *fnode) 711 { 712 f_reqnode *r; 713 /*f_reqnode *r_tmp;*/ 714 f_provnode *p, *p_tmp; 715 provnode *pnode; 716 int was_set; 717 718 DPRINTF((stderr, "do_file on %s.\n", fnode->filename)); 719 720 /* 721 * if fnode is marked as in progress, 722 * print that fnode; is circularly depended upon and abort. 723 */ 724 if (fnode->in_progress == SET) { 725 warnx("Circular dependency on file `%s'.", 726 fnode->filename); 727 was_set = exit_code = 1; 728 } else 729 was_set = 0; 730 731 /* mark fnode */ 732 fnode->in_progress = SET; 733 734 /* 735 * for each requirement of fnode -> r 736 * satisfy_req(r, filename) 737 */ 738 r = fnode->req_list; 739 while (r != NULL) { 740 /*r_tmp = r;*/ 741 satisfy_req(r, fnode->filename); 742 r = r->next; 743 /*free(r_tmp);*/ 744 } 745 fnode->req_list = NULL; 746 747 /* 748 * for each provision of fnode -> p 749 * remove fnode from provision list for p in hash table 750 */ 751 p = fnode->prov_list; 752 while (p != NULL) { 753 p_tmp = p; 754 pnode = p->pnode; 755 if (pnode->next != NULL) { 756 pnode->next->last = pnode->last; 757 } 758 if (pnode->last != NULL) { 759 pnode->last->next = pnode->next; 760 } 761 free(pnode); 762 p = p->next; 763 free(p_tmp); 764 } 765 fnode->prov_list = NULL; 766 767 /* do_it(fnode) */ 768 DPRINTF((stderr, "next do: ")); 769 770 /* if we were already in progress, don't print again */ 771 if (was_set == 0 && skip_ok(fnode) && keep_ok(fnode)) 772 printf("%s\n", fnode->filename); 773 774 if (fnode->next != NULL) { 775 fnode->next->last = fnode->last; 776 } 777 if (fnode->last != NULL) { 778 fnode->last->next = fnode->next; 779 } 780 781 DPRINTF((stderr, "nuking %s\n", fnode->filename)); 782 /*free(fnode->filename);*/ 783 /*free(fnode);*/ 784 } 785 786 void 787 generate_ordering(void) 788 { 789 790 /* 791 * while there remain undone files{f}, 792 * pick an arbitrary f, and do_file(f) 793 * Note that the first file in the file list is perfectly 794 * arbitrary, and easy to find, so we use that. 795 */ 796 797 /* 798 * N.B.: the file nodes "self delete" after they execute, so 799 * after each iteration of the loop, the head will be pointing 800 * to something totally different. The loop ends up being 801 * executed only once for every strongly connected set of 802 * nodes. 803 */ 804 if (provide) { 805 /* 806 * List all keywords provided by the listed files 807 */ 808 filenode *file; 809 f_provnode *f_prov; 810 811 for (file = fn_head->next; file; file = file->next) { 812 for (f_prov = file->prov_list; f_prov; f_prov = f_prov->next) { 813 if (strncmp(f_prov->pnode->name, "fake_", 5) != 0) 814 printf("%s\n", f_prov->pnode->name); 815 } 816 } 817 } else if (onetime_list) { 818 /* 819 * Only list dependanacies required to start particular 820 * keywords. 821 */ 822 strnodelist *scan; 823 filenode *file; 824 f_provnode *f_prov; 825 826 for (scan = onetime_list; scan; scan = scan->next) { 827 for (file = fn_head->next; file; file = file->next) { 828 for (f_prov = file->prov_list; f_prov; f_prov = f_prov->next) { 829 if (strcmp(scan->s, f_prov->pnode->name) == 0) { 830 do_file(file); 831 break; 832 } 833 } 834 if (f_prov) 835 break; 836 } 837 } 838 } else { 839 while (fn_head->next != NULL) { 840 DPRINTF((stderr, "generate on %s\n", fn_head->next->filename)); 841 do_file(fn_head->next); 842 } 843 } 844 } 845