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