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