1 /* $NetBSD: crunchgen.c,v 1.20 2001/02/05 01:40:51 christos Exp $ */ 2 /* 3 * Copyright (c) 1994 University of Maryland 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify, distribute, and sell this software and its 7 * documentation for any purpose is hereby granted without fee, provided that 8 * the above copyright notice appear in all copies and that both that 9 * copyright notice and this permission notice appear in supporting 10 * documentation, and that the name of U.M. not be used in advertising or 11 * publicity pertaining to distribution of the software without specific, 12 * written prior permission. U.M. makes no representations about the 13 * suitability of this software for any purpose. It is provided "as is" 14 * without express or implied warranty. 15 * 16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. 18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 * 23 * Author: James da Silva, Systems Design and Analysis Group 24 * Computer Science Department 25 * University of Maryland at College Park 26 */ 27 /* 28 * ======================================================================== 29 * crunchgen.c 30 * 31 * Generates a Makefile and main C file for a crunched executable, 32 * from specs given in a .conf file. 33 */ 34 #include <sys/cdefs.h> 35 #ifndef lint 36 __RCSID("$NetBSD: crunchgen.c,v 1.20 2001/02/05 01:40:51 christos Exp $"); 37 #endif 38 39 #include <stdlib.h> 40 #include <unistd.h> 41 #include <stdio.h> 42 #include <ctype.h> 43 #include <string.h> 44 45 #include <sys/types.h> 46 #include <sys/stat.h> 47 #include <sys/param.h> 48 #include <sys/utsname.h> 49 50 #define CRUNCH_VERSION "0.2" 51 52 #define MAXLINELEN 16384 53 #define MAXFIELDS 2048 54 55 56 /* internal representation of conf file: */ 57 58 /* simple lists of strings suffice for most parms */ 59 60 typedef struct strlst { 61 struct strlst *next; 62 char *str; 63 } strlst_t; 64 65 /* progs have structure, each field can be set with "special" or calculated */ 66 67 typedef struct prog { 68 struct prog *next; 69 char *name, *ident; 70 char *srcdir, *objdir; 71 strlst_t *objs, *objpaths; 72 strlst_t *links; 73 int goterror; 74 } prog_t; 75 76 77 /* global state */ 78 79 strlst_t *srcdirs = NULL; 80 strlst_t *libs = NULL; 81 prog_t *progs = NULL; 82 83 char line[MAXLINELEN]; 84 85 char confname[MAXPATHLEN], infilename[MAXPATHLEN]; 86 char outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN]; 87 char tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN]; 88 char topdir[MAXPATHLEN]; 89 char libdir[MAXPATHLEN] = "/usr/lib"; 90 int linenum = -1; 91 int goterror = 0; 92 93 char *pname = "crunchgen"; 94 95 int verbose, readcache; /* options */ 96 int reading_cache; 97 char *machine; 98 char *makeobjdirprefix; 99 char *makebin; 100 101 /* general library routines */ 102 103 void status(char *str); 104 void out_of_memory(void); 105 void add_string(strlst_t **listp, char *str); 106 int is_dir(char *pathname); 107 int is_nonempty_file(char *pathname); 108 109 /* helper routines for main() */ 110 111 void usage(void); 112 void parse_conf_file(void); 113 void gen_outputs(void); 114 115 extern char *crunched_skel[]; 116 117 int main(int argc, char **argv) 118 { 119 char *p; 120 int optc; 121 122 if ((makebin = getenv("MAKE")) == NULL) 123 makebin = strdup("make"); 124 125 if ((machine = getenv("MACHINE")) == NULL) { 126 struct utsname utsname; 127 128 if (uname(&utsname) == -1) { 129 perror("uname"); 130 exit(1); 131 } 132 machine = utsname.machine; 133 } 134 makeobjdirprefix = getenv("MAKEOBJDIRPREFIX"); 135 verbose = 1; 136 readcache = 1; 137 *outmkname = *outcfname = *execfname = '\0'; 138 139 if(argc > 0) pname = argv[0]; 140 141 while((optc = getopt(argc, argv, "m:c:e:fqD:L:")) != -1) { 142 switch(optc) { 143 case 'f': readcache = 0; break; 144 case 'q': verbose = 0; break; 145 146 case 'm': strcpy(outmkname, optarg); break; 147 case 'c': strcpy(outcfname, optarg); break; 148 case 'e': strcpy(execfname, optarg); break; 149 150 case 'D': strcpy(topdir, optarg); break; 151 case 'L': strcpy(libdir, optarg); break; 152 153 case '?': 154 default: usage(); 155 } 156 } 157 158 argc -= optind; 159 argv += optind; 160 161 if(argc != 1) usage(); 162 163 /* 164 * generate filenames 165 */ 166 167 strcpy(infilename, argv[0]); 168 169 /* confname = `basename infilename .conf` */ 170 171 if((p=strrchr(infilename, '/')) != NULL) strcpy(confname, p+1); 172 else strcpy(confname, infilename); 173 if((p=strrchr(confname, '.')) != NULL && !strcmp(p, ".conf")) *p = '\0'; 174 175 if (!*outmkname) 176 (void)snprintf(outmkname, sizeof(outmkname), "%s.mk", confname); 177 if (!*outcfname) 178 (void)snprintf(outcfname, sizeof(outcfname), "%s.c", confname); 179 if (!*execfname) 180 (void)snprintf(execfname, sizeof(execfname), "%s", confname); 181 182 (void)snprintf(cachename, sizeof(cachename), "%s.cache", confname); 183 (void)snprintf(tempfname, sizeof(tempfname), "/tmp/%sXXXXXX", confname); 184 185 parse_conf_file(); 186 gen_outputs(); 187 188 exit(goterror); 189 } 190 191 192 void usage(void) 193 { 194 fprintf(stderr, 195 "%s [-fq] [-m <makefile>] [-c <c file>] [-e <exec file>] <conffile>\n", 196 pname); 197 exit(1); 198 } 199 200 201 /* 202 * ======================================================================== 203 * parse_conf_file subsystem 204 * 205 */ 206 207 /* helper routines for parse_conf_file */ 208 209 void parse_one_file(char *filename); 210 void parse_line(char *line, int *fc, char **fv, int nf); 211 void add_srcdirs(int argc, char **argv); 212 void add_progs(int argc, char **argv); 213 void add_link(int argc, char **argv); 214 void add_libs(int argc, char **argv); 215 void add_special(int argc, char **argv); 216 217 prog_t *find_prog(char *str); 218 void add_prog(char *progname); 219 220 221 void parse_conf_file(void) 222 { 223 if(!is_nonempty_file(infilename)) { 224 fprintf(stderr, "%s: fatal: input file \"%s\" not found.\n", 225 pname, infilename); 226 exit(1); 227 } 228 parse_one_file(infilename); 229 if(readcache && is_nonempty_file(cachename)) { 230 reading_cache = 1; 231 parse_one_file(cachename); 232 } 233 } 234 235 236 void parse_one_file(char *filename) 237 { 238 char *fieldv[MAXFIELDS]; 239 int fieldc; 240 void (*f)(int c, char **v); 241 FILE *cf; 242 243 (void)snprintf(line, sizeof(line), "reading %s", filename); 244 status(line); 245 strcpy(curfilename, filename); 246 247 if((cf = fopen(curfilename, "r")) == NULL) { 248 perror(curfilename); 249 goterror = 1; 250 return; 251 } 252 253 linenum = 0; 254 while(fgets(line, MAXLINELEN, cf) != NULL) { 255 linenum++; 256 parse_line(line, &fieldc, fieldv, MAXFIELDS); 257 if(fieldc < 1) continue; 258 if(!strcmp(fieldv[0], "srcdirs")) f = add_srcdirs; 259 else if(!strcmp(fieldv[0], "progs")) f = add_progs; 260 else if(!strcmp(fieldv[0], "ln")) f = add_link; 261 else if(!strcmp(fieldv[0], "libs")) f = add_libs; 262 else if(!strcmp(fieldv[0], "special")) f = add_special; 263 else { 264 fprintf(stderr, "%s:%d: skipping unknown command `%s'.\n", 265 curfilename, linenum, fieldv[0]); 266 goterror = 1; 267 continue; 268 } 269 if(fieldc < 2) { 270 fprintf(stderr, 271 "%s:%d: %s command needs at least 1 argument, skipping.\n", 272 curfilename, linenum, fieldv[0]); 273 goterror = 1; 274 continue; 275 } 276 f(fieldc, fieldv); 277 } 278 279 if(ferror(cf)) { 280 perror(curfilename); 281 goterror = 1; 282 } 283 fclose(cf); 284 } 285 286 287 void parse_line(char *line, int *fc, char **fv, int nf) 288 { 289 char *p; 290 291 p = line; 292 *fc = 0; 293 while(1) { 294 while(isspace(*p)) p++; 295 if(*p == '\0' || *p == '#') break; 296 297 if(*fc < nf) fv[(*fc)++] = p; 298 while(*p && !isspace(*p) && *p != '#') p++; 299 if(*p == '\0' || *p == '#') break; 300 *p++ = '\0'; 301 } 302 if(*p) *p = '\0'; /* needed for '#' case */ 303 } 304 305 306 void add_srcdirs(int argc, char **argv) 307 { 308 int i; 309 char tmppath[MAXPATHLEN]; 310 311 for(i=1;i<argc;i++) { 312 if (argv[i][0] == '/' || topdir[0] == '\0') 313 strcpy(tmppath, argv[i]); 314 else { 315 strcpy(tmppath, topdir); 316 strcat(tmppath, "/"); 317 strcat(tmppath, argv[i]); 318 } 319 if(is_dir(tmppath)) 320 add_string(&srcdirs, tmppath); 321 else { 322 fprintf(stderr, "%s:%d: `%s' is not a directory, skipping it.\n", 323 curfilename, linenum, tmppath); 324 goterror = 1; 325 } 326 } 327 } 328 329 330 void add_progs(int argc, char **argv) 331 { 332 int i; 333 334 for(i=1;i<argc;i++) 335 add_prog(argv[i]); 336 } 337 338 339 void add_prog(char *progname) 340 { 341 prog_t *p1, *p2; 342 343 /* add to end, but be smart about dups */ 344 345 for(p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next) 346 if(!strcmp(p2->name, progname)) return; 347 348 p2 = malloc(sizeof(prog_t)); 349 if(p2) p2->name = strdup(progname); 350 if(!p2 || !p2->name) 351 out_of_memory(); 352 353 p2->next = NULL; 354 if(p1 == NULL) progs = p2; 355 else p1->next = p2; 356 357 p2->ident = p2->srcdir = p2->objdir = NULL; 358 p2->objs = p2->objpaths = p2->links = NULL; 359 p2->goterror = 0; 360 } 361 362 363 void add_link(int argc, char **argv) 364 { 365 int i; 366 prog_t *p = find_prog(argv[1]); 367 368 if(p == NULL) { 369 fprintf(stderr, 370 "%s:%d: no prog %s previously declared, skipping link.\n", 371 curfilename, linenum, argv[1]); 372 goterror = 1; 373 return; 374 } 375 for(i=2;i<argc;i++) 376 add_string(&p->links, argv[i]); 377 } 378 379 380 void add_libs(int argc, char **argv) 381 { 382 int i; 383 384 for(i=1;i<argc;i++) 385 add_string(&libs, argv[i]); 386 } 387 388 389 void add_special(int argc, char **argv) 390 { 391 int i; 392 prog_t *p = find_prog(argv[1]); 393 394 if(p == NULL) { 395 if(reading_cache) return; 396 fprintf(stderr, 397 "%s:%d: no prog %s previously declared, skipping special.\n", 398 curfilename, linenum, argv[1]); 399 goterror = 1; 400 return; 401 } 402 403 if(!strcmp(argv[2], "ident")) { 404 if(argc != 4) goto argcount; 405 if((p->ident = strdup(argv[3])) == NULL) 406 out_of_memory(); 407 } 408 else if(!strcmp(argv[2], "srcdir")) { 409 if(argc != 4) goto argcount; 410 if (argv[3][0] == '/' || topdir[0] == '\0') { 411 if((p->srcdir = strdup(argv[3])) == NULL) 412 out_of_memory(); 413 } else { 414 char tmppath[MAXPATHLEN]; 415 strcpy(tmppath, topdir); 416 strcat(tmppath, "/"); 417 strcat(tmppath, argv[3]); 418 if((p->srcdir = strdup(tmppath)) == NULL) 419 out_of_memory(); 420 } 421 } 422 else if(!strcmp(argv[2], "objdir")) { 423 if(argc != 4) goto argcount; 424 if((p->objdir = strdup(argv[3])) == NULL) 425 out_of_memory(); 426 } 427 else if(!strcmp(argv[2], "objs")) { 428 p->objs = NULL; 429 for(i=3;i<argc;i++) 430 add_string(&p->objs, argv[i]); 431 } 432 else if(!strcmp(argv[2], "objpaths")) { 433 p->objpaths = NULL; 434 for(i=3;i<argc;i++) 435 add_string(&p->objpaths, argv[i]); 436 } 437 else { 438 fprintf(stderr, "%s:%d: bad parameter name `%s', skipping line.\n", 439 curfilename, linenum, argv[2]); 440 goterror = 1; 441 } 442 return; 443 444 445 argcount: 446 fprintf(stderr, 447 "%s:%d: too %s arguments, expected \"special %s %s <string>\".\n", 448 curfilename, linenum, argc < 4? "few" : "many", argv[1], argv[2]); 449 goterror = 1; 450 } 451 452 453 prog_t *find_prog(char *str) 454 { 455 prog_t *p; 456 457 for(p = progs; p != NULL; p = p->next) 458 if(!strcmp(p->name, str)) return p; 459 460 return NULL; 461 } 462 463 464 /* 465 * ======================================================================== 466 * gen_outputs subsystem 467 * 468 */ 469 470 /* helper subroutines */ 471 472 void remove_error_progs(void); 473 void fillin_program(prog_t *p); 474 void gen_specials_cache(void); 475 void gen_output_makefile(void); 476 void gen_output_cfile(void); 477 478 void fillin_program_objs(prog_t *p, char *path); 479 void top_makefile_rules(FILE *outmk); 480 void prog_makefile_rules(FILE *outmk, prog_t *p); 481 void output_strlst(FILE *outf, strlst_t *lst); 482 char *genident(char *str); 483 char *dir_search(char *progname); 484 485 486 void gen_outputs(void) 487 { 488 prog_t *p; 489 490 for(p = progs; p != NULL; p = p->next) 491 fillin_program(p); 492 493 remove_error_progs(); 494 gen_specials_cache(); 495 gen_output_cfile(); 496 gen_output_makefile(); 497 status(""); 498 fprintf(stderr, 499 "Run \"make -f %s objs exe\" to build crunched binary.\n", 500 outmkname); 501 } 502 503 504 void fillin_program(prog_t *p) 505 { 506 char path[MAXPATHLEN]; 507 char *srcparent; 508 strlst_t *s; 509 510 (void)snprintf(line, sizeof(line), "filling in parms for %s", p->name); 511 status(line); 512 513 if(!p->ident) 514 p->ident = genident(p->name); 515 if(!p->srcdir) { 516 srcparent = dir_search(p->name); 517 if(srcparent) { 518 (void)snprintf(path, sizeof(path), "%s/%s", srcparent, p->name); 519 if(is_dir(path)) 520 p->srcdir = strdup(path); 521 } 522 } 523 if(!p->objdir && p->srcdir) { 524 if (makeobjdirprefix) { 525 (void)snprintf(path, sizeof(path), "%s/%s", makeobjdirprefix, p->srcdir); 526 if (is_dir(path)) 527 p->objdir = strdup(path); 528 } 529 if (!p->objdir) { 530 (void)snprintf(path, sizeof(path), "%s/obj.%s", p->srcdir, machine); 531 if (is_dir(path)) 532 p->objdir = strdup(path); 533 } 534 if (!p->objdir) { 535 (void)snprintf(path, sizeof(path), "%s/obj", p->srcdir); 536 if(is_dir(path)) 537 p->objdir = strdup(path); 538 } 539 if (!p->objdir) { 540 p->objdir = p->srcdir; 541 } 542 } 543 544 if(p->srcdir) 545 (void)snprintf(path, sizeof(path), "%s/Makefile", p->srcdir); 546 if(!p->objs && p->srcdir && is_nonempty_file(path)) 547 fillin_program_objs(p, p->srcdir); 548 549 if(!p->objpaths && p->objdir && p->objs) 550 for(s = p->objs; s != NULL; s = s->next) { 551 (void)snprintf(line, sizeof(line), "%s/%s", p->objdir, s->str); 552 add_string(&p->objpaths, line); 553 } 554 555 if(!p->srcdir && verbose) 556 fprintf(stderr, "%s: %s: warning: could not find source directory.\n", 557 infilename, p->name); 558 if(!p->objs && verbose) 559 fprintf(stderr, "%s: %s: warning: could not find any .o files.\n", 560 infilename, p->name); 561 562 if(!p->objpaths) { 563 fprintf(stderr, 564 "%s: %s: error: no objpaths specified or calculated.\n", 565 infilename, p->name); 566 p->goterror = goterror = 1; 567 } 568 } 569 570 void fillin_program_objs(prog_t *p, char *dirpath) 571 { 572 char *obj, *cp; 573 int rc; 574 int fd; 575 FILE *f; 576 577 /* discover the objs from the srcdir Makefile */ 578 579 if((fd = mkstemp(tempfname)) < 0) { 580 perror(tempfname); 581 exit(1); 582 } 583 584 if((f = fdopen(fd, "w")) == NULL) { 585 perror(tempfname); 586 goterror = 1; 587 return; 588 } 589 590 fprintf(f, ".include \"${.CURDIR}/Makefile\"\n"); 591 fprintf(f, ".if defined(PROG)\n"); 592 fprintf(f, "OBJS?= ${PROG}.o\n"); 593 fprintf(f, ".endif\n"); 594 fprintf(f, "crunchgen_objs:\n\t@echo 'OBJS= '${OBJS}\n"); 595 fclose(f); 596 597 (void)snprintf(line, sizeof(line), 598 "cd %s && %s -f %s crunchgen_objs 2>&1", dirpath, makebin, tempfname); 599 if((f = popen(line, "r+")) == NULL) { 600 perror("submake pipe"); 601 goterror = 1; 602 return; 603 } 604 605 while(fgets(line, MAXLINELEN, f)) { 606 if(strncmp(line, "OBJS= ", 6)) { 607 if (strcmp(line, 608 "sh: warning: running as root with dot in PATH\n") == 0) 609 continue; 610 fprintf(stderr, "make error: %s", line); 611 goterror = 1; 612 continue; 613 } 614 cp = line + 6; 615 while(isspace(*cp)) cp++; 616 while(*cp) { 617 obj = cp; 618 while(*cp && !isspace(*cp)) cp++; 619 if(*cp) *cp++ = '\0'; 620 add_string(&p->objs, obj); 621 while(isspace(*cp)) cp++; 622 } 623 } 624 if((rc=pclose(f)) != 0) { 625 fprintf(stderr, "make error: make returned %d\n", rc); 626 goterror = 1; 627 } 628 unlink(tempfname); 629 } 630 631 void remove_error_progs(void) 632 { 633 prog_t *p1, *p2; 634 635 p1 = NULL; p2 = progs; 636 while(p2 != NULL) { 637 if(!p2->goterror) 638 p1 = p2, p2 = p2->next; 639 else { 640 /* delete it from linked list */ 641 fprintf(stderr, "%s: %s: ignoring program because of errors.\n", 642 infilename, p2->name); 643 if(p1) p1->next = p2->next; 644 else progs = p2->next; 645 p2 = p2->next; 646 } 647 } 648 } 649 650 void gen_specials_cache(void) 651 { 652 FILE *cachef; 653 prog_t *p; 654 655 (void)snprintf(line, sizeof(line), "generating %s", cachename); 656 status(line); 657 658 if((cachef = fopen(cachename, "w")) == NULL) { 659 perror(cachename); 660 goterror = 1; 661 return; 662 } 663 664 fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n", 665 cachename, infilename, CRUNCH_VERSION); 666 667 for(p = progs; p != NULL; p = p->next) { 668 fprintf(cachef, "\n"); 669 if(p->srcdir) 670 fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir); 671 if(p->objdir) 672 fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir); 673 if(p->objs) { 674 fprintf(cachef, "special %s objs", p->name); 675 output_strlst(cachef, p->objs); 676 } 677 fprintf(cachef, "special %s objpaths", p->name); 678 output_strlst(cachef, p->objpaths); 679 } 680 fclose(cachef); 681 } 682 683 684 void gen_output_makefile(void) 685 { 686 prog_t *p; 687 FILE *outmk; 688 689 (void)snprintf(line, sizeof(line), "generating %s", outmkname); 690 status(line); 691 692 if((outmk = fopen(outmkname, "w")) == NULL) { 693 perror(outmkname); 694 goterror = 1; 695 return; 696 } 697 698 fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n", 699 outmkname, infilename, CRUNCH_VERSION); 700 701 top_makefile_rules(outmk); 702 703 for(p = progs; p != NULL; p = p->next) 704 prog_makefile_rules(outmk, p); 705 706 fprintf(outmk, "\n.include <bsd.sys.mk>\n"); 707 fprintf(outmk, "\n# ========\n"); 708 fclose(outmk); 709 } 710 711 712 void gen_output_cfile(void) 713 { 714 char **cp; 715 FILE *outcf; 716 prog_t *p; 717 strlst_t *s; 718 719 (void)snprintf(line, sizeof(line), "generating %s", outcfname); 720 status(line); 721 722 if((outcf = fopen(outcfname, "w")) == NULL) { 723 perror(outcfname); 724 goterror = 1; 725 return; 726 } 727 728 fprintf(outcf, 729 "/* %s - generated from %s by crunchgen %s */\n", 730 outcfname, infilename, CRUNCH_VERSION); 731 732 fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname); 733 for(cp = crunched_skel; *cp != NULL; cp++) 734 fprintf(outcf, "%s\n", *cp); 735 736 for(p = progs; p != NULL; p = p->next) 737 fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident); 738 739 fprintf(outcf, "\nstruct stub entry_points[] = {\n"); 740 for(p = progs; p != NULL; p = p->next) { 741 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 742 p->name, p->ident); 743 for(s = p->links; s != NULL; s = s->next) 744 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 745 s->str, p->ident); 746 } 747 748 fprintf(outcf, "\t{ EXECNAME, crunched_main },\n"); 749 fprintf(outcf, "\t{ NULL, NULL }\n};\n"); 750 fclose(outcf); 751 } 752 753 754 char *genident(char *str) 755 { 756 char *n,*s,*d; 757 758 /* 759 * generates a Makefile/C identifier from a program name, mapping '-' to 760 * '_' and ignoring all other non-identifier characters. This leads to 761 * programs named "foo.bar" and "foobar" to map to the same identifier. 762 */ 763 764 if((n = strdup(str)) == NULL) 765 return NULL; 766 for(d = s = n; *s != '\0'; s++) { 767 if(*s == '-') *d++ = '_'; 768 else if(*s == '_' || isalnum(*s)) *d++ = *s; 769 } 770 *d = '\0'; 771 return n; 772 } 773 774 775 char *dir_search(char *progname) 776 { 777 char path[MAXPATHLEN]; 778 strlst_t *dir; 779 780 for(dir=srcdirs; dir != NULL; dir=dir->next) { 781 (void)snprintf(path, sizeof(path), "%s/%s", dir->str, progname); 782 if(is_dir(path)) return dir->str; 783 } 784 return NULL; 785 } 786 787 788 void top_makefile_rules(FILE *outmk) 789 { 790 prog_t *p; 791 792 fprintf(outmk, "STRIP?=strip\n"); 793 fprintf(outmk, "CRUNCHIDE?=crunchide\n"); 794 fprintf(outmk, "LIBS="); 795 fprintf(outmk, "-L%s ", libdir); 796 output_strlst(outmk, libs); 797 798 fprintf(outmk, "CRUNCHED_OBJS="); 799 for(p = progs; p != NULL; p = p->next) 800 fprintf(outmk, " %s.cro", p->name); 801 fprintf(outmk, "\n"); 802 803 fprintf(outmk, "SUBMAKE_TARGETS="); 804 for(p = progs; p != NULL; p = p->next) 805 fprintf(outmk, " %s_make", p->ident); 806 fprintf(outmk, "\n\n"); 807 808 fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS)\n", 809 execfname, execfname); 810 fprintf(outmk, "\t$(CC) -static -o %s %s.o $(CRUNCHED_OBJS) $(LIBS)\n", 811 execfname, execfname); 812 fprintf(outmk, "\t$(STRIP) %s\n", execfname); 813 fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n"); 814 fprintf(outmk, "exe: %s\n", execfname); 815 fprintf(outmk, "clean:\n\trm -f %s *.cro *.o *_stub.c\n", 816 execfname); 817 } 818 819 820 void prog_makefile_rules(FILE *outmk, prog_t *p) 821 { 822 fprintf(outmk, "\n# -------- %s\n\n", p->name); 823 824 if(p->srcdir && p->objs) { 825 fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir); 826 fprintf(outmk, "%s_OBJS=", p->ident); 827 output_strlst(outmk, p->objs); 828 fprintf(outmk, "%s_make:\n", p->ident); 829 fprintf(outmk, "\t(cd $(%s_SRCDIR); %s $(%s_OBJS))\n\n", 830 p->ident, makebin, p->ident); 831 } 832 else 833 fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n", 834 p->ident, p->name); 835 836 fprintf(outmk, "%s_OBJPATHS=", p->ident); 837 output_strlst(outmk, p->objpaths); 838 839 fprintf(outmk, "%s_stub.c:\n", p->name); 840 fprintf(outmk, "\techo \"" 841 "int _crunched_%s_stub(int argc, char **argv, char **envp)" 842 "{return main(argc,argv,envp);}\" >%s_stub.c\n", 843 p->ident, p->name); 844 fprintf(outmk, "%s.cro: %s_stub.o $(%s_OBJPATHS)\n", 845 p->name, p->name, p->ident); 846 fprintf(outmk, "\t${LD} -dc -r -o %s.cro %s_stub.o $(%s_OBJPATHS)\n", 847 p->name, p->name, p->ident); 848 fprintf(outmk, "\t${CRUNCHIDE} -k _crunched_%s_stub %s.cro\n", 849 p->ident, p->name); 850 } 851 852 void output_strlst(FILE *outf, strlst_t *lst) 853 { 854 for(; lst != NULL; lst = lst->next) 855 fprintf(outf, " %s", lst->str); 856 fprintf(outf, "\n"); 857 } 858 859 860 /* 861 * ======================================================================== 862 * general library routines 863 * 864 */ 865 866 void status(char *str) 867 { 868 static int lastlen = 0; 869 int len, spaces; 870 871 if(!verbose) return; 872 873 len = strlen(str); 874 spaces = lastlen - len; 875 if(spaces < 1) spaces = 1; 876 877 fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " "); 878 fflush(stderr); 879 lastlen = len; 880 } 881 882 883 void out_of_memory(void) 884 { 885 fprintf(stderr, "%s: %d: out of memory, stopping.\n", infilename, linenum); 886 exit(1); 887 } 888 889 890 void add_string(strlst_t **listp, char *str) 891 { 892 strlst_t *p1, *p2; 893 894 /* add to end, but be smart about dups */ 895 896 for(p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next) 897 if(!strcmp(p2->str, str)) return; 898 899 p2 = malloc(sizeof(strlst_t)); 900 if(p2) p2->str = strdup(str); 901 if(!p2 || !p2->str) 902 out_of_memory(); 903 904 p2->next = NULL; 905 if(p1 == NULL) *listp = p2; 906 else p1->next = p2; 907 } 908 909 910 int is_dir(char *pathname) 911 { 912 struct stat buf; 913 914 if(stat(pathname, &buf) == -1) 915 return 0; 916 return S_ISDIR(buf.st_mode); 917 } 918 919 int is_nonempty_file(char *pathname) 920 { 921 struct stat buf; 922 923 if(stat(pathname, &buf) == -1) 924 return 0; 925 926 return S_ISREG(buf.st_mode) && buf.st_size > 0; 927 } 928