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