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