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