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 * $DragonFly: src/sbin/rcorder/rcorder.c,v 1.7 2005/02/20 19:47:17 dillon Exp $ 35 */ 36 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 40 #include <err.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 #include <libutil.h> 46 47 #include "sprite.h" 48 #include "hash.h" 49 50 #ifdef DEBUG 51 int debug = 0; 52 # define DPRINTF(args) if (debug) { fflush(stdout); fprintf args; } 53 #else 54 # define DPRINTF(args) 55 #endif 56 57 int provide; 58 59 #define REQUIRE_STR "# REQUIRE:" 60 #define REQUIRE_LEN (sizeof(REQUIRE_STR) - 1) 61 #define REQUIRES_STR "# REQUIRES:" 62 #define REQUIRES_LEN (sizeof(REQUIRES_STR) - 1) 63 #define PROVIDE_STR "# PROVIDE:" 64 #define PROVIDE_LEN (sizeof(PROVIDE_STR) - 1) 65 #define PROVIDES_STR "# PROVIDES:" 66 #define PROVIDES_LEN (sizeof(PROVIDES_STR) - 1) 67 #define BEFORE_STR "# BEFORE:" 68 #define BEFORE_LEN (sizeof(BEFORE_STR) - 1) 69 #define KEYWORD_STR "# KEYWORD:" 70 #define KEYWORD_LEN (sizeof(KEYWORD_STR) - 1) 71 #define KEYWORDS_STR "# KEYWORDS:" 72 #define KEYWORDS_LEN (sizeof(KEYWORDS_STR) - 1) 73 74 int exit_code; 75 int file_count; 76 char **file_list; 77 78 typedef int bool; 79 #define TRUE 1 80 #define FALSE 0 81 typedef bool flag; 82 #define SET TRUE 83 #define RESET FALSE 84 85 Hash_Table provide_hash_s, *provide_hash; 86 87 typedef struct provnode provnode; 88 typedef struct filenode filenode; 89 typedef struct f_provnode f_provnode; 90 typedef struct f_reqnode f_reqnode; 91 typedef struct strnodelist strnodelist; 92 93 struct provnode { 94 flag head; 95 flag in_progress; 96 filenode *fnode; 97 provnode *next, *last; 98 char *name; 99 }; 100 101 struct f_provnode { 102 provnode *pnode; 103 f_provnode *next; 104 }; 105 106 struct f_reqnode { 107 Hash_Entry *entry; 108 f_reqnode *next; 109 }; 110 111 struct strnodelist { 112 filenode *node; 113 strnodelist *next; 114 char s[1]; 115 }; 116 117 struct filenode { 118 char *filename; 119 flag in_progress; 120 filenode *next, *last; 121 f_reqnode *req_list; 122 f_provnode *prov_list; 123 strnodelist *keyword_list; 124 }; 125 126 filenode fn_head_s, *fn_head; 127 128 strnodelist *bl_list; 129 strnodelist *keep_list; 130 strnodelist *skip_list; 131 strnodelist *onetime_list; 132 133 void do_file(filenode *fnode); 134 void strnode_add(strnodelist **, char *, filenode *); 135 int skip_ok(filenode *fnode); 136 int keep_ok(filenode *fnode); 137 void satisfy_req(f_reqnode *rnode, char *filename); 138 void crunch_file(char *); 139 void parse_require(filenode *, char *); 140 void parse_provide(filenode *, char *); 141 void parse_before(filenode *, char *); 142 void parse_keywords(filenode *, char *); 143 filenode *filenode_new(char *); 144 void add_require(filenode *, char *); 145 void add_provide(filenode *, char *); 146 void add_before(filenode *, char *); 147 void add_keyword(filenode *, char *); 148 void insert_before(void); 149 Hash_Entry *make_fake_provision(filenode *); 150 void crunch_all_files(void); 151 void initialize(void); 152 void generate_ordering(void); 153 int main(int, char *[]); 154 155 int 156 main(int argc, char **argv) 157 { 158 int ch; 159 160 while ((ch = getopt(argc, argv, "dpk:s:o:")) != -1) 161 switch (ch) { 162 case 'd': 163 #ifdef DEBUG 164 debug = 1; 165 #else 166 warnx("debugging not compiled in, -d ignored"); 167 #endif 168 break; 169 case 'k': 170 strnode_add(&keep_list, optarg, 0); 171 break; 172 case 's': 173 strnode_add(&skip_list, optarg, 0); 174 break; 175 case 'o': 176 strnode_add(&onetime_list, optarg, 0); 177 break; 178 case 'p': 179 provide = 1; 180 break; 181 default: 182 /* XXX should crunch it? */ 183 break; 184 } 185 argc -= optind; 186 argv += optind; 187 188 file_count = argc; 189 file_list = argv; 190 191 DPRINTF((stderr, "parse_args\n")); 192 initialize(); 193 DPRINTF((stderr, "initialize\n")); 194 crunch_all_files(); 195 DPRINTF((stderr, "crunch_all_files\n")); 196 generate_ordering(); 197 DPRINTF((stderr, "generate_ordering\n")); 198 199 exit(exit_code); 200 } 201 202 /* 203 * initialise various variables. 204 */ 205 void 206 initialize(void) 207 { 208 209 fn_head = &fn_head_s; 210 211 provide_hash = &provide_hash_s; 212 Hash_InitTable(provide_hash, file_count); 213 } 214 215 /* generic function to insert a new strnodelist element */ 216 void 217 strnode_add(strnodelist **listp, char *s, filenode *fnode) 218 { 219 strnodelist *ent; 220 221 ent = emalloc(sizeof *ent + strlen(s)); 222 ent->node = fnode; 223 strcpy(ent->s, s); 224 ent->next = *listp; 225 *listp = ent; 226 } 227 228 /* 229 * below are the functions that deal with creating the lists 230 * from the filename's given and the dependancies and provisions 231 * in each of these files. no ordering or checking is done here. 232 */ 233 234 /* 235 * we have a new filename, create a new filenode structure. 236 * fill in the bits, and put it in the filenode linked list 237 */ 238 filenode * 239 filenode_new(char *filename) 240 { 241 filenode *temp; 242 243 temp = emalloc(sizeof(*temp)); 244 memset(temp, 0, sizeof(*temp)); 245 temp->filename = estrdup(filename); 246 temp->req_list = NULL; 247 temp->prov_list = NULL; 248 temp->keyword_list = NULL; 249 temp->in_progress = RESET; 250 /* 251 * link the filenode into the list of filenodes. 252 * note that the double linking means we can delete a 253 * filenode without searching for where it belongs. 254 */ 255 temp->next = fn_head->next; 256 if (temp->next != NULL) 257 temp->next->last = temp; 258 temp->last = fn_head; 259 fn_head->next = temp; 260 return (temp); 261 } 262 263 /* 264 * add a requirement to a filenode. 265 */ 266 void 267 add_require(filenode *fnode, char *s) 268 { 269 Hash_Entry *entry; 270 f_reqnode *rnode; 271 int new; 272 273 entry = Hash_CreateEntry(provide_hash, s, &new); 274 if (new) 275 Hash_SetValue(entry, NULL); 276 rnode = emalloc(sizeof(*rnode)); 277 rnode->entry = entry; 278 rnode->next = fnode->req_list; 279 fnode->req_list = rnode; 280 } 281 282 /* 283 * add a provision to a filenode. if this provision doesn't 284 * have a head node, create one here. 285 */ 286 void 287 add_provide(filenode *fnode, char *s) 288 { 289 Hash_Entry *entry; 290 f_provnode *f_pnode; 291 provnode *pnode, *head; 292 int new; 293 294 entry = Hash_CreateEntry(provide_hash, s, &new); 295 head = Hash_GetValue(entry); 296 297 /* create a head node if necessary. */ 298 if (head == NULL) { 299 head = emalloc(sizeof(*head)); 300 head->head = SET; 301 head->in_progress = RESET; 302 head->fnode = NULL; 303 head->last = head->next = NULL; 304 Hash_SetValue(entry, head); 305 } 306 #if 0 307 /* 308 * Don't warn about this. We want to be able to support 309 * scripts that do two complex things: 310 * 311 * - Two independent scripts which both provide the 312 * same thing. Both scripts must be executed in 313 * any order to meet the barrier. An example: 314 * 315 * Script 1: 316 * 317 * PROVIDE: mail 318 * REQUIRE: LOGIN 319 * 320 * Script 2: 321 * 322 * PROVIDE: mail 323 * REQUIRE: LOGIN 324 * 325 * - Two interdependent scripts which both provide the 326 * same thing. Both scripts must be executed in 327 * graph order to meet the barrier. An example: 328 * 329 * Script 1: 330 * 331 * PROVIDE: nameservice dnscache 332 * REQUIRE: SERVERS 333 * 334 * Script 2: 335 * 336 * PROVIDE: nameservice nscd 337 * REQUIRE: dnscache 338 */ 339 else if (new == 0) { 340 warnx("file `%s' provides `%s'.", fnode->filename, s); 341 warnx("\tpreviously seen in `%s'.", 342 head->next->fnode->filename); 343 } 344 #endif 345 346 pnode = emalloc(sizeof(*pnode)); 347 pnode->head = RESET; 348 pnode->in_progress = RESET; 349 pnode->fnode = fnode; 350 pnode->next = head->next; 351 pnode->last = head; 352 pnode->name = strdup(s); 353 head->next = pnode; 354 if (pnode->next != NULL) 355 pnode->next->last = pnode; 356 357 f_pnode = emalloc(sizeof(*f_pnode)); 358 f_pnode->pnode = pnode; 359 f_pnode->next = fnode->prov_list; 360 fnode->prov_list = f_pnode; 361 } 362 363 /* 364 * put the BEFORE: lines to a list and handle them later. 365 */ 366 void 367 add_before(filenode *fnode, char *s) 368 { 369 strnodelist *bf_ent; 370 371 bf_ent = emalloc(sizeof *bf_ent + strlen(s)); 372 bf_ent->node = fnode; 373 strcpy(bf_ent->s, s); 374 bf_ent->next = bl_list; 375 bl_list = bf_ent; 376 } 377 378 /* 379 * add a key to a filenode. 380 */ 381 void 382 add_keyword(filenode *fnode, char *s) 383 { 384 385 strnode_add(&fnode->keyword_list, s, fnode); 386 } 387 388 /* 389 * loop over the rest of a REQUIRE line, giving each word to 390 * add_require() to do the real work. 391 */ 392 void 393 parse_require(filenode *node, char *buffer) 394 { 395 char *s; 396 397 while ((s = strsep(&buffer, " \t\n")) != NULL) 398 if (*s != '\0') 399 add_require(node, s); 400 } 401 402 /* 403 * loop over the rest of a PROVIDE line, giving each word to 404 * add_provide() to do the real work. 405 */ 406 void 407 parse_provide(filenode *node, char *buffer) 408 { 409 char *s; 410 411 while ((s = strsep(&buffer, " \t\n")) != NULL) 412 if (*s != '\0') 413 add_provide(node, s); 414 } 415 416 /* 417 * loop over the rest of a BEFORE line, giving each word to 418 * add_before() to do the real work. 419 */ 420 void 421 parse_before(filenode *node, char *buffer) 422 { 423 char *s; 424 425 while ((s = strsep(&buffer, " \t\n")) != NULL) 426 if (*s != '\0') 427 add_before(node, s); 428 } 429 430 /* 431 * loop over the rest of a KEYWORD line, giving each word to 432 * add_keyword() to do the real work. 433 */ 434 void 435 parse_keywords(filenode *node, char *buffer) 436 { 437 char *s; 438 439 while ((s = strsep(&buffer, " \t\n")) != NULL) 440 if (*s != '\0') 441 add_keyword(node, s); 442 } 443 444 /* 445 * given a file name, create a filenode for it, read in lines looking 446 * for provision and requirement lines, building the graphs as needed. 447 */ 448 void 449 crunch_file(char *filename) 450 { 451 FILE *fp; 452 char *buf; 453 int require_flag, provide_flag, before_flag, keywords_flag; 454 enum { BEFORE_PARSING, PARSING, PARSING_DONE } state; 455 filenode *node; 456 char delims[3] = { '\\', '\\', '\0' }; 457 struct stat st; 458 459 if ((fp = fopen(filename, "r")) == NULL) { 460 warn("could not open %s", filename); 461 return; 462 } 463 464 if (fstat(fileno(fp), &st) == -1) { 465 warn("could not stat %s", filename); 466 fclose(fp); 467 return; 468 } 469 470 if (!S_ISREG(st.st_mode)) { 471 #if 0 472 warnx("%s is not a file", filename); 473 #endif 474 fclose(fp); 475 return; 476 } 477 478 node = filenode_new(filename); 479 480 /* 481 * we don't care about length, line number, don't want # for comments, 482 * and have no flags. 483 */ 484 for (state = BEFORE_PARSING; state != PARSING_DONE && 485 (buf = fparseln(fp, NULL, NULL, delims, 0)) != NULL; free(buf)) { 486 require_flag = provide_flag = before_flag = keywords_flag = 0; 487 if (strncmp(REQUIRE_STR, buf, REQUIRE_LEN) == 0) 488 require_flag = REQUIRE_LEN; 489 else if (strncmp(REQUIRES_STR, buf, REQUIRES_LEN) == 0) 490 require_flag = REQUIRES_LEN; 491 else if (strncmp(PROVIDE_STR, buf, PROVIDE_LEN) == 0) 492 provide_flag = PROVIDE_LEN; 493 else if (strncmp(PROVIDES_STR, buf, PROVIDES_LEN) == 0) 494 provide_flag = PROVIDES_LEN; 495 else if (strncmp(BEFORE_STR, buf, BEFORE_LEN) == 0) 496 before_flag = BEFORE_LEN; 497 else if (strncmp(KEYWORD_STR, buf, KEYWORD_LEN) == 0) 498 keywords_flag = KEYWORD_LEN; 499 else if (strncmp(KEYWORDS_STR, buf, KEYWORDS_LEN) == 0) 500 keywords_flag = KEYWORDS_LEN; 501 else { 502 if (state == PARSING) 503 state = PARSING_DONE; 504 continue; 505 } 506 507 state = PARSING; 508 if (require_flag) 509 parse_require(node, buf + require_flag); 510 else if (provide_flag) 511 parse_provide(node, buf + provide_flag); 512 else if (before_flag) 513 parse_before(node, buf + before_flag); 514 else if (keywords_flag) 515 parse_keywords(node, buf + keywords_flag); 516 } 517 fclose(fp); 518 } 519 520 Hash_Entry * 521 make_fake_provision(filenode *node) 522 { 523 Hash_Entry *entry; 524 f_provnode *f_pnode; 525 provnode *head, *pnode; 526 static int i = 0; 527 int new; 528 char buffer[30]; 529 530 do { 531 snprintf(buffer, sizeof buffer, "fake_prov_%08d", i++); 532 entry = Hash_CreateEntry(provide_hash, buffer, &new); 533 } while (new == 0); 534 head = emalloc(sizeof(*head)); 535 head->head = SET; 536 head->in_progress = RESET; 537 head->fnode = NULL; 538 head->last = head->next = NULL; 539 Hash_SetValue(entry, head); 540 541 pnode = emalloc(sizeof(*pnode)); 542 pnode->head = RESET; 543 pnode->in_progress = RESET; 544 pnode->fnode = node; 545 pnode->next = head->next; 546 pnode->last = head; 547 pnode->name = strdup(buffer); 548 head->next = pnode; 549 if (pnode->next != NULL) 550 pnode->next->last = pnode; 551 552 f_pnode = emalloc(sizeof(*f_pnode)); 553 f_pnode->pnode = pnode; 554 f_pnode->next = node->prov_list; 555 node->prov_list = f_pnode; 556 557 return (entry); 558 } 559 560 /* 561 * go through the BEFORE list, inserting requirements into the graph(s) 562 * as required. in the before list, for each entry B, we have a file F 563 * and a string S. we create a "fake" provision (P) that F provides. 564 * for each entry in the provision list for S, add a requirement to 565 * that provisions filenode for P. 566 */ 567 void 568 insert_before(void) 569 { 570 Hash_Entry *entry, *fake_prov_entry; 571 provnode *pnode; 572 f_reqnode *rnode; 573 strnodelist *bl; 574 int new; 575 576 while (bl_list != NULL) { 577 bl = bl_list->next; 578 579 fake_prov_entry = make_fake_provision(bl_list->node); 580 581 entry = Hash_CreateEntry(provide_hash, bl_list->s, &new); 582 if (new == 1 && !provide) 583 warnx("file `%s' is before unknown provision `%s'", bl_list->node->filename, bl_list->s); 584 585 for (pnode = Hash_GetValue(entry); pnode; pnode = pnode->next) { 586 if (pnode->head) 587 continue; 588 589 rnode = emalloc(sizeof(*rnode)); 590 rnode->entry = fake_prov_entry; 591 rnode->next = pnode->fnode->req_list; 592 pnode->fnode->req_list = rnode; 593 } 594 595 free(bl_list); 596 bl_list = bl; 597 } 598 } 599 600 /* 601 * loop over all the files calling crunch_file() on them to do the 602 * real work. after we have built all the nodes, insert the BEFORE: 603 * lines into graph(s). 604 */ 605 void 606 crunch_all_files(void) 607 { 608 int i; 609 610 for (i = 0; i < file_count; i++) 611 crunch_file(file_list[i]); 612 insert_before(); 613 } 614 615 /* 616 * below are the functions that traverse the graphs we have built 617 * finding out the desired ordering, printing each file in turn. 618 * if missing requirements, or cyclic graphs are detected, a 619 * warning will be issued, and we will continue on.. 620 */ 621 622 /* 623 * given a requirement node (in a filename) we attempt to satisfy it. 624 * we do some sanity checking first, to ensure that we have providers, 625 * aren't already satisfied and aren't already being satisfied (ie, 626 * cyclic). if we pass all this, we loop over the provision list 627 * calling do_file() (enter recursion) for each filenode in this 628 * provision. 629 */ 630 void 631 satisfy_req(f_reqnode *rnode, char *filename) 632 { 633 Hash_Entry *entry; 634 provnode *head; 635 636 entry = rnode->entry; 637 head = Hash_GetValue(entry); 638 639 if (head == NULL) { 640 warnx("requirement `%s' in file `%s' has no providers.", 641 Hash_GetKey(entry), filename); 642 exit_code = 1; 643 return; 644 } 645 646 /* return if the requirement is already satisfied. */ 647 if (head->next == NULL) 648 return; 649 650 /* 651 * if list is marked as in progress, 652 * print that there is a circular dependency on it and abort 653 */ 654 if (head->in_progress == SET) { 655 warnx("Circular dependency on provision `%s' in file `%s'.", 656 Hash_GetKey(entry), filename); 657 exit_code = 1; 658 return; 659 } 660 661 head->in_progress = SET; 662 663 /* 664 * while provision_list is not empty 665 * do_file(first_member_of(provision_list)); 666 */ 667 while (head->next != NULL) 668 do_file(head->next->fnode); 669 } 670 671 int 672 skip_ok(filenode *fnode) 673 { 674 strnodelist *s; 675 strnodelist *k; 676 677 for (s = skip_list; s; s = s->next) 678 for (k = fnode->keyword_list; k; k = k->next) 679 if (strcmp(k->s, s->s) == 0) 680 return (0); 681 682 return (1); 683 } 684 685 int 686 keep_ok(filenode *fnode) 687 { 688 strnodelist *s; 689 strnodelist *k; 690 691 for (s = keep_list; s; s = s->next) 692 for (k = fnode->keyword_list; k; k = k->next) 693 if (strcmp(k->s, s->s) == 0) 694 return (1); 695 696 /* an empty keep_list means every one */ 697 return (!keep_list); 698 } 699 700 /* 701 * given a filenode, we ensure we are not a cyclic graph. if this 702 * is ok, we loop over the filenodes requirements, calling satisfy_req() 703 * for each of them.. once we have done this, remove this filenode 704 * from each provision table, as we are now done. 705 * 706 * NOTE: do_file() is called recursively from several places and cannot 707 * safely free() anything related to items that may be recursed on. 708 * Circular dependancies will cause problems if we do. 709 */ 710 void 711 do_file(filenode *fnode) 712 { 713 f_reqnode *r, *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