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