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