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