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