1 /* $NetBSD: crunchgen.c,v 1.31 2002/01/31 19:24:47 tv 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.31 2002/01/31 19:24:47 tv 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 tempfname[MAXPATHLEN], 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 (void)snprintf(tempfname, sizeof(tempfname), "/tmp/%sXXXXXX", confname); 196 197 parse_conf_file(); 198 gen_outputs(); 199 200 exit(goterror); 201 } 202 203 204 void usage(void) 205 { 206 fprintf(stderr, 207 "%s [-fq] [-m <makefile>] [-c <c file>] [-e <exec file>] <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 prog_makefile_rules(FILE *outmk, prog_t *p); 498 void output_strlst(FILE *outf, strlst_t *lst); 499 char *genident(char *str); 500 char *dir_search(char *progname); 501 502 503 void gen_outputs(void) 504 { 505 prog_t *p; 506 507 for(p = progs; p != NULL; p = p->next) 508 fillin_program(p); 509 510 remove_error_progs(); 511 gen_specials_cache(); 512 gen_output_cfile(); 513 gen_output_makefile(); 514 status(""); 515 fprintf(stderr, 516 "Run \"make -f %s objs exe\" to build crunched binary.\n", 517 outmkname); 518 } 519 520 521 void fillin_program(prog_t *p) 522 { 523 char path[MAXPATHLEN]; 524 char *srcparent; 525 strlst_t *s; 526 527 (void)snprintf(line, sizeof(line), "filling in parms for %s", p->name); 528 status(line); 529 530 if(!p->ident) 531 p->ident = genident(p->name); 532 if(!p->srcdir) { 533 srcparent = dir_search(p->name); 534 if(srcparent) { 535 (void)snprintf(path, sizeof(path), "%s/%s", srcparent, p->name); 536 if(is_dir(path)) 537 p->srcdir = strdup(path); 538 } 539 } 540 if(!p->objdir && p->srcdir && useobjs) { 541 if (makeobjdirprefix) { 542 (void)snprintf(path, sizeof(path), "%s/%s", makeobjdirprefix, p->srcdir); 543 if (is_dir(path)) 544 p->objdir = strdup(path); 545 } 546 if (!p->objdir) { 547 (void)snprintf(path, sizeof(path), "%s/obj.%s", p->srcdir, machine); 548 if (is_dir(path)) 549 p->objdir = strdup(path); 550 } 551 if (!p->objdir) { 552 (void)snprintf(path, sizeof(path), "%s/obj", p->srcdir); 553 if(is_dir(path)) 554 p->objdir = strdup(path); 555 } 556 if (!p->objdir) { 557 p->objdir = p->srcdir; 558 } 559 } 560 561 if(p->srcdir) 562 (void)snprintf(path, sizeof(path), "%s/Makefile", p->srcdir); 563 if(!p->objs && p->srcdir && is_nonempty_file(path)) 564 fillin_program_objs(p, p->srcdir); 565 566 if(!p->objpaths && p->objs) { 567 if (p->objdir && useobjs) { 568 for(s = p->objs; s != NULL; s = s->next) { 569 (void)snprintf(line, sizeof(line), "%s/%s", p->objdir, s->str); 570 add_string(&p->objpaths, line); 571 } 572 } else { 573 for(s = p->objs; s != NULL; s = s->next) { 574 (void)snprintf(line, sizeof(line), "%s/%s", p->ident, s->str); 575 add_string(&p->objpaths, line); 576 } 577 } 578 } 579 580 if(!p->srcdir && verbose) 581 fprintf(stderr, "%s: %s: warning: could not find source directory.\n", 582 infilename, p->name); 583 if(!p->objs && verbose) 584 fprintf(stderr, "%s: %s: warning: could not find any .o files.\n", 585 infilename, p->name); 586 587 if(!p->objpaths) { 588 fprintf(stderr, 589 "%s: %s: error: no objpaths specified or calculated.\n", 590 infilename, p->name); 591 p->goterror = goterror = 1; 592 } 593 } 594 595 void fillin_program_objs(prog_t *p, char *dirpath) 596 { 597 char *obj, *cp; 598 int rc; 599 int fd; 600 FILE *f; 601 602 /* discover the objs from the srcdir Makefile */ 603 604 if((fd = mkstemp(tempfname)) < 0) { 605 perror(tempfname); 606 exit(1); 607 } 608 609 if((f = fdopen(fd, "w")) == NULL) { 610 perror(tempfname); 611 goterror = 1; 612 return; 613 } 614 615 fprintf(f, ".include \"${.CURDIR}/Makefile\"\n"); 616 fprintf(f, ".if defined(PROG)\n"); 617 fprintf(f, "OBJS?= ${PROG}.o\n"); 618 fprintf(f, ".endif\n"); 619 fprintf(f, "crunchgen_objs:\n\t@echo 'OBJS= '${OBJS}\n"); 620 fclose(f); 621 622 (void)snprintf(line, sizeof(line), 623 "cd %s && %s -f %s %s crunchgen_objs 2>&1", dirpath, makebin, 624 tempfname, makeflags); 625 if((f = popen(line, "r+")) == NULL) { 626 perror("submake pipe"); 627 goterror = 1; 628 unlink(tempfname); 629 return; 630 } 631 632 while(fgets(line, MAXLINELEN, f)) { 633 if(strncmp(line, "OBJS= ", 6)) { 634 if (strcmp(line, 635 "sh: warning: running as root with dot in PATH\n") == 0) 636 continue; 637 fprintf(stderr, "make error: %s", line); 638 goterror = 1; 639 continue; 640 } 641 cp = line + 6; 642 while(isspace(*cp)) cp++; 643 while(*cp) { 644 obj = cp; 645 while(*cp && !isspace(*cp)) cp++; 646 if(*cp) *cp++ = '\0'; 647 add_string(&p->objs, obj); 648 while(isspace(*cp)) cp++; 649 } 650 } 651 if((rc=pclose(f)) != 0) { 652 fprintf(stderr, "make error: make returned %d\n", rc); 653 goterror = 1; 654 } 655 unlink(tempfname); 656 } 657 658 void remove_error_progs(void) 659 { 660 prog_t *p1, *p2; 661 662 p1 = NULL; p2 = progs; 663 while(p2 != NULL) { 664 if(!p2->goterror) 665 p1 = p2, p2 = p2->next; 666 else { 667 /* delete it from linked list */ 668 fprintf(stderr, "%s: %s: ignoring program because of errors.\n", 669 infilename, p2->name); 670 if(p1) p1->next = p2->next; 671 else progs = p2->next; 672 p2 = p2->next; 673 } 674 } 675 } 676 677 void gen_specials_cache(void) 678 { 679 FILE *cachef; 680 prog_t *p; 681 682 (void)snprintf(line, sizeof(line), "generating %s", cachename); 683 status(line); 684 685 if((cachef = fopen(cachename, "w")) == NULL) { 686 perror(cachename); 687 goterror = 1; 688 return; 689 } 690 691 fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n", 692 cachename, infilename, CRUNCH_VERSION); 693 694 for(p = progs; p != NULL; p = p->next) { 695 fprintf(cachef, "\n"); 696 if(p->srcdir) 697 fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir); 698 if(p->objdir && useobjs) 699 fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir); 700 if(p->objs) { 701 fprintf(cachef, "special %s objs", p->name); 702 output_strlst(cachef, p->objs); 703 } 704 fprintf(cachef, "special %s objpaths", p->name); 705 output_strlst(cachef, p->objpaths); 706 } 707 fclose(cachef); 708 } 709 710 711 void gen_output_makefile(void) 712 { 713 prog_t *p; 714 FILE *outmk; 715 716 (void)snprintf(line, sizeof(line), "generating %s", outmkname); 717 status(line); 718 719 if((outmk = fopen(outmkname, "w")) == NULL) { 720 perror(outmkname); 721 goterror = 1; 722 return; 723 } 724 725 fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n", 726 outmkname, infilename, CRUNCH_VERSION); 727 728 top_makefile_rules(outmk); 729 730 for(p = progs; p != NULL; p = p->next) 731 prog_makefile_rules(outmk, p); 732 733 fprintf(outmk, "\n.include <bsd.prog.mk>\n"); 734 fprintf(outmk, "\n# ========\n"); 735 fclose(outmk); 736 } 737 738 739 void gen_output_cfile(void) 740 { 741 char **cp; 742 FILE *outcf; 743 prog_t *p; 744 strlst_t *s; 745 746 (void)snprintf(line, sizeof(line), "generating %s", outcfname); 747 status(line); 748 749 if((outcf = fopen(outcfname, "w")) == NULL) { 750 perror(outcfname); 751 goterror = 1; 752 return; 753 } 754 755 fprintf(outcf, 756 "/* %s - generated from %s by crunchgen %s */\n", 757 outcfname, infilename, CRUNCH_VERSION); 758 759 fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname); 760 for(cp = crunched_skel; *cp != NULL; cp++) 761 fprintf(outcf, "%s\n", *cp); 762 763 for(p = progs; p != NULL; p = p->next) 764 fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident); 765 766 fprintf(outcf, "\nstruct stub entry_points[] = {\n"); 767 for(p = progs; p != NULL; p = p->next) { 768 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 769 p->name, p->ident); 770 for(s = p->links; s != NULL; s = s->next) 771 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 772 s->str, p->ident); 773 } 774 775 fprintf(outcf, "\t{ EXECNAME, crunched_main },\n"); 776 fprintf(outcf, "\t{ NULL, NULL }\n};\n"); 777 fclose(outcf); 778 } 779 780 781 char *genident(char *str) 782 { 783 char *n,*s,*d; 784 785 /* 786 * generates a Makefile/C identifier from a program name, mapping '-' to 787 * '_' and ignoring all other non-identifier characters. This leads to 788 * programs named "foo.bar" and "foobar" to map to the same identifier. 789 */ 790 791 if((n = strdup(str)) == NULL) 792 return NULL; 793 for(d = s = n; *s != '\0'; s++) { 794 if(*s == '-') *d++ = '_'; 795 else if(*s == '_' || isalnum(*s)) *d++ = *s; 796 } 797 *d = '\0'; 798 return n; 799 } 800 801 802 char *dir_search(char *progname) 803 { 804 char path[MAXPATHLEN]; 805 strlst_t *dir; 806 807 for(dir=srcdirs; dir != NULL; dir=dir->next) { 808 (void)snprintf(path, sizeof(path), "%s/%s", dir->str, progname); 809 if(is_dir(path)) return dir->str; 810 } 811 return NULL; 812 } 813 814 815 void top_makefile_rules(FILE *outmk) 816 { 817 prog_t *p; 818 819 fprintf(outmk, "DBG=%s\n", dbg); 820 fprintf(outmk, "STRIP?=strip\n"); 821 fprintf(outmk, "MAKE?=make\n"); 822 #ifdef NEW_TOOLCHAIN 823 fprintf(outmk, "OBJCOPY?=objcopy\n"); 824 #else 825 fprintf(outmk, "CRUNCHIDE?=crunchide\n"); 826 #endif 827 828 fprintf(outmk, "CRUNCHED_OBJS="); 829 for(p = progs; p != NULL; p = p->next) 830 fprintf(outmk, " %s.cro", p->name); 831 fprintf(outmk, "\n"); 832 fprintf(outmk, "LDSTATIC=-static\n"); 833 fprintf(outmk, "DPADD+= ${CRUNCHED_OBJS}\n"); 834 fprintf(outmk, "LDADD+= ${CRUNCHED_OBJS} "); 835 output_strlst(outmk, libs); 836 fprintf(outmk, "CRUNCHEDOBJSDIRS=${CRUNCHED_OBJS:R}\n\n"); 837 838 fprintf(outmk, "SUBMAKE_TARGETS="); 839 for(p = progs; p != NULL; p = p->next) 840 fprintf(outmk, " %s_make", p->ident); 841 fprintf(outmk, "\n\n"); 842 843 fprintf(outmk, "PROG=%s\n", execfname); 844 fprintf(outmk, "MKMAN=no\n\n"); 845 846 fprintf(outmk, "all: ${PROG}\n\t${STRIP} ${PROG}\n"); 847 fprintf(outmk, "objs: $(SUBMAKE_TARGETS)\n"); 848 fprintf(outmk, "exe: %s\n", execfname); 849 fprintf(outmk, "clean:\n\trm -rf %s *.cro *.o *_stub.c ${CRUNCHEDOBJSDIRS}\n", 850 execfname); 851 } 852 853 854 void prog_makefile_rules(FILE *outmk, prog_t *p) 855 { 856 strlst_t *lst; 857 858 fprintf(outmk, "\n# -------- %s\n\n", p->name); 859 860 fprintf(outmk, "%s_OBJPATHS=", p->ident); 861 output_strlst(outmk, p->objpaths); 862 863 if(p->srcdir && p->objs && !useobjs) { 864 fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir); 865 fprintf(outmk, "%s_OBJS=", p->ident); 866 output_strlst(outmk, p->objs); 867 fprintf(outmk, "%s_make: ${%s_OBJPATHS}\n", p->ident, p->ident); 868 fprintf(outmk, "${%s_OBJPATHS}: \n", p->ident); 869 fprintf(outmk, "\tif [ \\! -d %s ]; then mkdir %s; fi; cd %s; \\\n", 870 p->ident, p->ident, p->ident); 871 fprintf(outmk, "\tprintf \".PATH: ${%s_SRCDIR}\\n.CURDIR:= ${%s_SRCDIR}\\n" 872 ".include \\\"\\$${.CURDIR}/Makefile\\\"\\n\" \\\n", p->ident, p->ident); 873 fprintf(outmk, "\t| ${MAKE} DBG=\"${DBG}\" -f- depend ${%s_OBJS}\n\n", 874 p->ident); 875 } 876 else 877 fprintf(outmk, "%s_make:\n\t@echo \"** Using existing objs for %s\"\n\n", 878 p->ident, p->name); 879 880 881 fprintf(outmk, "%s_stub.c:\n", p->name); 882 fprintf(outmk, "\techo \"" 883 "int _crunched_%s_stub(int argc, char **argv, char **envp)" 884 "{return main(argc,argv,envp);}\" >%s_stub.c\n", 885 p->ident, p->name); 886 if (useobjs) 887 fprintf(outmk, "%s.cro: %s_stub.o\n", 888 p->name, p->name); 889 else 890 fprintf(outmk, "%s.cro: %s_stub.o ${%s_OBJPATHS}\n", 891 p->name, p->name, p->ident); 892 fprintf(outmk, "\t${LD} -dc -r -o %s.cro %s_stub.o $(%s_OBJPATHS)\n", 893 p->name, p->name, p->ident); 894 #ifdef NEW_TOOLCHAIN 895 fprintf(outmk, "\t${OBJCOPY} --keep-global-symbol _crunched_%s_stub ", 896 p->ident); 897 for (lst = p->keepsymbols; lst != NULL; lst = lst->next) 898 fprintf(outmk, "--keep-global-symbol %s ", lst->str); 899 #else 900 fprintf(outmk, "\t${CRUNCHIDE} -k _crunched_%s_stub ", p->ident); 901 for (lst = p->keepsymbols; lst != NULL; lst = lst->next) 902 fprintf(outmk, "-k %s ", lst->str); 903 #endif 904 fprintf(outmk, "%s.cro\n", p->name); 905 } 906 907 void output_strlst(FILE *outf, strlst_t *lst) 908 { 909 for(; lst != NULL; lst = lst->next) 910 fprintf(outf, " %s", lst->str); 911 fprintf(outf, "\n"); 912 } 913 914 915 /* 916 * ======================================================================== 917 * general library routines 918 * 919 */ 920 921 void status(char *str) 922 { 923 static int lastlen = 0; 924 int len, spaces; 925 926 if(!verbose) return; 927 928 len = strlen(str); 929 spaces = lastlen - len; 930 if(spaces < 1) spaces = 1; 931 932 fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " "); 933 fflush(stderr); 934 lastlen = len; 935 } 936 937 938 void out_of_memory(void) 939 { 940 fprintf(stderr, "%s: %d: out of memory, stopping.\n", infilename, linenum); 941 exit(1); 942 } 943 944 945 void add_string(strlst_t **listp, char *str) 946 { 947 strlst_t *p1, *p2; 948 949 /* add to end, but be smart about dups */ 950 951 for(p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next) 952 if(!strcmp(p2->str, str)) return; 953 954 p2 = malloc(sizeof(strlst_t)); 955 if(p2) p2->str = strdup(str); 956 if(!p2 || !p2->str) 957 out_of_memory(); 958 959 p2->next = NULL; 960 if(p1 == NULL) *listp = p2; 961 else p1->next = p2; 962 } 963 964 965 int is_dir(char *pathname) 966 { 967 struct stat buf; 968 969 if(stat(pathname, &buf) == -1) 970 return 0; 971 return S_ISDIR(buf.st_mode); 972 } 973 974 int is_nonempty_file(char *pathname) 975 { 976 struct stat buf; 977 978 if(stat(pathname, &buf) == -1) 979 return 0; 980 981 return S_ISREG(buf.st_mode) && buf.st_size > 0; 982 } 983