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