1 /* 2 * Copyright (c) 1994 University of Maryland 3 * All Rights Reserved. 4 * 5 * Permission to use, copy, modify, distribute, and sell this software and its 6 * documentation for any purpose is hereby granted without fee, provided that 7 * the above copyright notice appear in all copies and that both that 8 * copyright notice and this permission notice appear in supporting 9 * documentation, and that the name of U.M. not be used in advertising or 10 * publicity pertaining to distribution of the software without specific, 11 * written prior permission. U.M. makes no representations about the 12 * suitability of this software for any purpose. It is provided "as is" 13 * without express or implied warranty. 14 * 15 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. 17 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 19 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 * 22 * Author: James da Silva, Systems Design and Analysis Group 23 * Computer Science Department 24 * University of Maryland at College Park 25 */ 26 /* 27 * ======================================================================== 28 * crunchgen.c 29 * 30 * Generates a Makefile and main C file for a crunched executable, 31 * from specs given in a .conf file. 32 */ 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <stdio.h> 36 #include <ctype.h> 37 #include <string.h> 38 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 #include <sys/param.h> 42 43 #define CRUNCH_VERSION "0.2" 44 45 #define MAXLINELEN 16384 46 #define MAXFIELDS 2048 47 48 49 /* internal representation of conf file: */ 50 51 /* simple lists of strings suffice for most parms */ 52 53 typedef struct strlst { 54 struct strlst *next; 55 char *str; 56 } strlst_t; 57 58 /* progs have structure, each field can be set with "special" or calculated */ 59 60 typedef struct prog { 61 struct prog *next; 62 char *name, *ident; 63 char *srcdir, *objdir; 64 strlst_t *objs, *objpaths; 65 strlst_t *links; 66 int goterror; 67 } prog_t; 68 69 70 /* global state */ 71 72 strlst_t *srcdirs = NULL; 73 strlst_t *libs = NULL; 74 prog_t *progs = NULL; 75 76 char line[MAXLINELEN]; 77 78 char confname[MAXPATHLEN], infilename[MAXPATHLEN]; 79 char outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN]; 80 char tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN]; 81 int linenum = -1; 82 int goterror = 0; 83 84 char *pname = "crunchgen"; 85 86 int verbose, readcache; /* options */ 87 int reading_cache; 88 89 /* general library routines */ 90 91 void status(char *str); 92 void out_of_memory(void); 93 void add_string(strlst_t **listp, char *str); 94 int is_dir(char *pathname); 95 int is_nonempty_file(char *pathname); 96 97 /* helper routines for main() */ 98 99 void usage(void); 100 void parse_conf_file(void); 101 void gen_outputs(void); 102 103 104 int main(int argc, char **argv) 105 { 106 char *p; 107 int optc; 108 extern int optind; 109 extern char *optarg; 110 111 verbose = 1; 112 readcache = 1; 113 *outmkname = *outcfname = *execfname = '\0'; 114 115 if(argc > 0) pname = argv[0]; 116 117 while((optc = getopt(argc, argv, "m:c:e:fq")) != -1) { 118 switch(optc) { 119 case 'f': readcache = 0; break; 120 case 'q': verbose = 0; break; 121 122 case 'm': strcpy(outmkname, optarg); break; 123 case 'c': strcpy(outcfname, optarg); break; 124 case 'e': strcpy(execfname, optarg); break; 125 126 case '?': 127 default: usage(); 128 } 129 } 130 131 argc -= optind; 132 argv += optind; 133 134 if(argc != 1) usage(); 135 136 /* 137 * generate filenames 138 */ 139 140 strcpy(infilename, argv[0]); 141 142 /* confname = `basename infilename .conf` */ 143 144 if((p=strrchr(infilename, '/')) != NULL) strcpy(confname, p+1); 145 else strcpy(confname, infilename); 146 if((p=strrchr(confname, '.')) != NULL && !strcmp(p, ".conf")) *p = '\0'; 147 148 if(!*outmkname) sprintf(outmkname, "%s.mk", confname); 149 if(!*outcfname) sprintf(outcfname, "%s.c", confname); 150 if(!*execfname) sprintf(execfname, "%s", confname); 151 152 sprintf(cachename, "%s.cache", confname); 153 sprintf(tempfname, ".tmp_%sXXXXXX", confname); 154 if(mktemp(tempfname) == NULL) { 155 perror(tempfname); 156 exit(1); 157 } 158 159 parse_conf_file(); 160 gen_outputs(); 161 162 exit(goterror); 163 } 164 165 166 void usage(void) 167 { 168 fprintf(stderr, 169 "%s [-fq] [-m <makefile>] [-c <c file>] [-e <exec file>] <conffile>\n", 170 pname); 171 exit(1); 172 } 173 174 175 /* 176 * ======================================================================== 177 * parse_conf_file subsystem 178 * 179 */ 180 181 /* helper routines for parse_conf_file */ 182 183 void parse_one_file(char *filename); 184 void parse_line(char *line, int *fc, char **fv, int nf); 185 void add_srcdirs(int argc, char **argv); 186 void add_progs(int argc, char **argv); 187 void add_link(int argc, char **argv); 188 void add_libs(int argc, char **argv); 189 void add_special(int argc, char **argv); 190 191 prog_t *find_prog(char *str); 192 void add_prog(char *progname); 193 194 195 void parse_conf_file(void) 196 { 197 if(!is_nonempty_file(infilename)) { 198 fprintf(stderr, "%s: fatal: input file \"%s\" not found.\n", 199 pname, infilename); 200 exit(1); 201 } 202 parse_one_file(infilename); 203 if(readcache && is_nonempty_file(cachename)) { 204 reading_cache = 1; 205 parse_one_file(cachename); 206 } 207 } 208 209 210 void parse_one_file(char *filename) 211 { 212 char *fieldv[MAXFIELDS]; 213 int fieldc; 214 void (*f)(int c, char **v); 215 FILE *cf; 216 217 sprintf(line, "reading %s", filename); 218 status(line); 219 strcpy(curfilename, filename); 220 221 if((cf = fopen(curfilename, "r")) == NULL) { 222 perror(curfilename); 223 goterror = 1; 224 return; 225 } 226 227 linenum = 0; 228 while(fgets(line, MAXLINELEN, cf) != NULL) { 229 linenum++; 230 parse_line(line, &fieldc, fieldv, MAXFIELDS); 231 if(fieldc < 1) continue; 232 if(!strcmp(fieldv[0], "srcdirs")) f = add_srcdirs; 233 else if(!strcmp(fieldv[0], "progs")) f = add_progs; 234 else if(!strcmp(fieldv[0], "ln")) f = add_link; 235 else if(!strcmp(fieldv[0], "libs")) f = add_libs; 236 else if(!strcmp(fieldv[0], "special")) f = add_special; 237 else { 238 fprintf(stderr, "%s:%d: skipping unknown command `%s'.\n", 239 curfilename, linenum, fieldv[0]); 240 goterror = 1; 241 continue; 242 } 243 if(fieldc < 2) { 244 fprintf(stderr, 245 "%s:%d: %s command needs at least 1 argument, skipping.\n", 246 curfilename, linenum, fieldv[0]); 247 goterror = 1; 248 continue; 249 } 250 f(fieldc, fieldv); 251 } 252 253 if(ferror(cf)) { 254 perror(curfilename); 255 goterror = 1; 256 } 257 fclose(cf); 258 } 259 260 261 void parse_line(char *line, int *fc, char **fv, int nf) 262 { 263 char *p; 264 265 p = line; 266 *fc = 0; 267 while(1) { 268 while(isspace(*p)) p++; 269 if(*p == '\0' || *p == '#') break; 270 271 if(*fc < nf) fv[(*fc)++] = p; 272 while(*p && !isspace(*p) && *p != '#') p++; 273 if(*p == '\0' || *p == '#') break; 274 *p++ = '\0'; 275 } 276 if(*p) *p = '\0'; /* needed for '#' case */ 277 } 278 279 280 void add_srcdirs(int argc, char **argv) 281 { 282 int i; 283 284 for(i=1;i<argc;i++) { 285 if(is_dir(argv[i])) 286 add_string(&srcdirs, argv[i]); 287 else { 288 fprintf(stderr, "%s:%d: `%s' is not a directory, skipping it.\n", 289 curfilename, linenum, argv[i]); 290 goterror = 1; 291 } 292 } 293 } 294 295 296 void add_progs(int argc, char **argv) 297 { 298 int i; 299 300 for(i=1;i<argc;i++) 301 add_prog(argv[i]); 302 } 303 304 305 void add_prog(char *progname) 306 { 307 prog_t *p1, *p2; 308 309 /* add to end, but be smart about dups */ 310 311 for(p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next) 312 if(!strcmp(p2->name, progname)) return; 313 314 p2 = malloc(sizeof(prog_t)); 315 if(p2) p2->name = strdup(progname); 316 if(!p2 || !p2->name) 317 out_of_memory(); 318 319 p2->next = NULL; 320 if(p1 == NULL) progs = p2; 321 else p1->next = p2; 322 323 p2->ident = p2->srcdir = p2->objdir = NULL; 324 p2->links = p2->objs = NULL; 325 p2->goterror = 0; 326 } 327 328 329 void add_link(int argc, char **argv) 330 { 331 int i; 332 prog_t *p = find_prog(argv[1]); 333 334 if(p == NULL) { 335 fprintf(stderr, 336 "%s:%d: no prog %s previously declared, skipping link.\n", 337 curfilename, linenum, argv[1]); 338 goterror = 1; 339 return; 340 } 341 for(i=2;i<argc;i++) 342 add_string(&p->links, argv[i]); 343 } 344 345 346 void add_libs(int argc, char **argv) 347 { 348 int i; 349 350 for(i=1;i<argc;i++) 351 add_string(&libs, argv[i]); 352 } 353 354 355 void add_special(int argc, char **argv) 356 { 357 int i; 358 prog_t *p = find_prog(argv[1]); 359 360 if(p == NULL) { 361 if(reading_cache) return; 362 fprintf(stderr, 363 "%s:%d: no prog %s previously declared, skipping special.\n", 364 curfilename, linenum, argv[1]); 365 goterror = 1; 366 return; 367 } 368 369 if(!strcmp(argv[2], "ident")) { 370 if(argc != 4) goto argcount; 371 if((p->ident = strdup(argv[3])) == NULL) 372 out_of_memory(); 373 } 374 else if(!strcmp(argv[2], "srcdir")) { 375 if(argc != 4) goto argcount; 376 if((p->srcdir = strdup(argv[3])) == NULL) 377 out_of_memory(); 378 } 379 else if(!strcmp(argv[2], "objdir")) { 380 if(argc != 4) goto argcount; 381 if((p->objdir = strdup(argv[3])) == NULL) 382 out_of_memory(); 383 } 384 else if(!strcmp(argv[2], "objs")) { 385 p->objs = NULL; 386 for(i=3;i<argc;i++) 387 add_string(&p->objs, argv[i]); 388 } 389 else if(!strcmp(argv[2], "objpaths")) { 390 p->objpaths = NULL; 391 for(i=3;i<argc;i++) 392 add_string(&p->objpaths, argv[i]); 393 } 394 else { 395 fprintf(stderr, "%s:%d: bad parameter name `%s', skipping line.\n", 396 curfilename, linenum, argv[2]); 397 goterror = 1; 398 } 399 return; 400 401 402 argcount: 403 fprintf(stderr, 404 "%s:%d: too %s arguments, expected \"special %s %s <string>\".\n", 405 curfilename, linenum, argc < 4? "few" : "many", argv[1], argv[2]); 406 goterror = 1; 407 } 408 409 410 prog_t *find_prog(char *str) 411 { 412 prog_t *p; 413 414 for(p = progs; p != NULL; p = p->next) 415 if(!strcmp(p->name, str)) return p; 416 417 return NULL; 418 } 419 420 421 /* 422 * ======================================================================== 423 * gen_outputs subsystem 424 * 425 */ 426 427 /* helper subroutines */ 428 429 void remove_error_progs(void); 430 void fillin_program(prog_t *p); 431 void gen_specials_cache(void); 432 void gen_output_makefile(void); 433 void gen_output_cfile(void); 434 435 void fillin_program_objs(prog_t *p, char *path); 436 void top_makefile_rules(FILE *outmk); 437 void prog_makefile_rules(FILE *outmk, prog_t *p); 438 void output_strlst(FILE *outf, strlst_t *lst); 439 char *genident(char *str); 440 char *dir_search(char *progname); 441 442 443 void gen_outputs(void) 444 { 445 prog_t *p; 446 447 for(p = progs; p != NULL; p = p->next) 448 fillin_program(p); 449 450 remove_error_progs(); 451 gen_specials_cache(); 452 gen_output_cfile(); 453 gen_output_makefile(); 454 status(""); 455 fprintf(stderr, 456 "Run \"make -f %s objs exe\" to build crunched binary.\n", 457 outmkname); 458 } 459 460 461 void fillin_program(prog_t *p) 462 { 463 char path[MAXPATHLEN]; 464 char *srcparent; 465 strlst_t *s; 466 467 sprintf(line, "filling in parms for %s", p->name); 468 status(line); 469 470 if(!p->ident) 471 p->ident = genident(p->name); 472 if(!p->srcdir) { 473 srcparent = dir_search(p->name); 474 if(srcparent) 475 sprintf(path, "%s/%s", srcparent, p->name); 476 if(is_dir(path)) 477 p->srcdir = strdup(path); 478 } 479 if(!p->objdir && p->srcdir) { 480 sprintf(path, "%s/obj", p->srcdir); 481 if(is_dir(path)) 482 p->objdir = strdup(path); 483 else { 484 sprintf(path, "%s/obj.%s", p->srcdir, MACHINE); 485 if(is_dir(path)) 486 p->objdir = strdup(path); 487 else 488 p->objdir = p->srcdir; 489 } 490 } 491 492 if(p->srcdir) sprintf(path, "%s/Makefile", p->srcdir); 493 if(!p->objs && p->srcdir && is_nonempty_file(path)) 494 fillin_program_objs(p, path); 495 496 if(!p->objpaths && p->objdir && p->objs) 497 for(s = p->objs; s != NULL; s = s->next) { 498 sprintf(line, "%s/%s", p->objdir, s->str); 499 add_string(&p->objpaths, line); 500 } 501 502 if(!p->srcdir && verbose) 503 fprintf(stderr, "%s: %s: warning: could not find source directory.\n", 504 infilename, p->name); 505 if(!p->objs && verbose) 506 fprintf(stderr, "%s: %s: warning: could not find any .o files.\n", 507 infilename, p->name); 508 509 if(!p->objpaths) { 510 fprintf(stderr, 511 "%s: %s: error: no objpaths specified or calculated.\n", 512 infilename, p->name); 513 p->goterror = goterror = 1; 514 } 515 } 516 517 void fillin_program_objs(prog_t *p, char *path) 518 { 519 char *obj, *cp; 520 int rc; 521 FILE *f; 522 523 /* discover the objs from the srcdir Makefile */ 524 525 if((f = fopen(tempfname, "w")) == NULL) { 526 perror(tempfname); 527 goterror = 1; 528 return; 529 } 530 531 fprintf(f, ".include \"%s\"\n", path); 532 fprintf(f, ".if defined(PROG) && !defined(OBJS)\n"); 533 fprintf(f, "OBJS=${PROG}.o\n"); 534 fprintf(f, ".endif\n"); 535 fprintf(f, "crunchgen_objs:\n\t@echo 'OBJS= '${OBJS}\n"); 536 fclose(f); 537 538 sprintf(line, "make -f %s crunchgen_objs 2>&1", tempfname); 539 if((f = popen(line, "r")) == NULL) { 540 perror("submake pipe"); 541 goterror = 1; 542 return; 543 } 544 545 while(fgets(line, MAXLINELEN, f)) { 546 if(strncmp(line, "OBJS= ", 6)) { 547 if (strcmp(line, 548 "sh: warning: running as root with dot in PATH\n") == 0) 549 continue; 550 fprintf(stderr, "make error: %s", line); 551 goterror = 1; 552 continue; 553 } 554 cp = line + 6; 555 while(isspace(*cp)) cp++; 556 while(*cp) { 557 obj = cp; 558 while(*cp && !isspace(*cp)) cp++; 559 if(*cp) *cp++ = '\0'; 560 add_string(&p->objs, obj); 561 while(isspace(*cp)) cp++; 562 } 563 } 564 if((rc=pclose(f)) != 0) { 565 fprintf(stderr, "make error: make returned %d\n", rc); 566 goterror = 1; 567 } 568 unlink(tempfname); 569 } 570 571 void remove_error_progs(void) 572 { 573 prog_t *p1, *p2; 574 575 p1 = NULL; p2 = progs; 576 while(p2 != NULL) { 577 if(!p2->goterror) 578 p1 = p2, p2 = p2->next; 579 else { 580 /* delete it from linked list */ 581 fprintf(stderr, "%s: %s: ignoring program because of errors.\n", 582 infilename, p2->name); 583 if(p1) p1->next = p2->next; 584 else progs = p2->next; 585 p2 = p2->next; 586 } 587 } 588 } 589 590 void gen_specials_cache(void) 591 { 592 FILE *cachef; 593 prog_t *p; 594 595 sprintf(line, "generating %s", cachename); 596 status(line); 597 598 if((cachef = fopen(cachename, "w")) == NULL) { 599 perror(cachename); 600 goterror = 1; 601 return; 602 } 603 604 fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n", 605 cachename, infilename, CRUNCH_VERSION); 606 607 for(p = progs; p != NULL; p = p->next) { 608 fprintf(cachef, "\n"); 609 if(p->srcdir) 610 fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir); 611 if(p->objdir) 612 fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir); 613 if(p->objs) { 614 fprintf(cachef, "special %s objs", p->name); 615 output_strlst(cachef, p->objs); 616 } 617 fprintf(cachef, "special %s objpaths", p->name); 618 output_strlst(cachef, p->objpaths); 619 } 620 fclose(cachef); 621 } 622 623 624 void gen_output_makefile(void) 625 { 626 prog_t *p; 627 FILE *outmk; 628 629 sprintf(line, "generating %s", outmkname); 630 status(line); 631 632 if((outmk = fopen(outmkname, "w")) == NULL) { 633 perror(outmkname); 634 goterror = 1; 635 return; 636 } 637 638 fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n", 639 outmkname, infilename, CRUNCH_VERSION); 640 641 top_makefile_rules(outmk); 642 643 for(p = progs; p != NULL; p = p->next) 644 prog_makefile_rules(outmk, p); 645 646 fprintf(outmk, "\n# ========\n"); 647 fclose(outmk); 648 } 649 650 651 void gen_output_cfile(void) 652 { 653 extern char *crunched_skel[]; 654 char **cp; 655 FILE *outcf; 656 prog_t *p; 657 strlst_t *s; 658 659 sprintf(line, "generating %s", outcfname); 660 status(line); 661 662 if((outcf = fopen(outcfname, "w")) == NULL) { 663 perror(outcfname); 664 goterror = 1; 665 return; 666 } 667 668 fprintf(outcf, 669 "/* %s - generated from %s by crunchgen %s */\n", 670 outcfname, infilename, CRUNCH_VERSION); 671 672 fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname); 673 for(cp = crunched_skel; *cp != NULL; cp++) 674 fprintf(outcf, "%s\n", *cp); 675 676 for(p = progs; p != NULL; p = p->next) 677 fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident); 678 679 fprintf(outcf, "\nstruct stub entry_points[] = {\n"); 680 for(p = progs; p != NULL; p = p->next) { 681 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 682 p->name, p->ident); 683 for(s = p->links; s != NULL; s = s->next) 684 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 685 s->str, p->ident); 686 } 687 688 fprintf(outcf, "\t{ EXECNAME, crunched_main },\n"); 689 fprintf(outcf, "\t{ NULL, NULL }\n};\n"); 690 fclose(outcf); 691 } 692 693 694 char *genident(char *str) 695 { 696 char *n,*s,*d; 697 698 /* 699 * generates a Makefile/C identifier from a program name, mapping '-' to 700 * '_' and ignoring all other non-identifier characters. This leads to 701 * programs named "foo.bar" and "foobar" to map to the same identifier. 702 */ 703 704 if((n = strdup(str)) == NULL) 705 return NULL; 706 for(d = s = n; *s != '\0'; s++) { 707 if(*s == '-') *d++ = '_'; 708 else if(*s == '_' || isalnum(*s)) *d++ = *s; 709 } 710 *d = '\0'; 711 return n; 712 } 713 714 715 char *dir_search(char *progname) 716 { 717 char path[MAXPATHLEN]; 718 strlst_t *dir; 719 720 for(dir=srcdirs; dir != NULL; dir=dir->next) { 721 sprintf(path, "%s/%s", dir->str, progname); 722 if(is_dir(path)) return dir->str; 723 } 724 return NULL; 725 } 726 727 728 void top_makefile_rules(FILE *outmk) 729 { 730 prog_t *p; 731 732 fprintf(outmk, "LIBS="); 733 output_strlst(outmk, libs); 734 735 fprintf(outmk, "CRUNCHED_OBJS="); 736 for(p = progs; p != NULL; p = p->next) 737 fprintf(outmk, " %s.lo", p->name); 738 fprintf(outmk, "\n"); 739 740 fprintf(outmk, "SUBMAKE_TARGETS="); 741 for(p = progs; p != NULL; p = p->next) 742 fprintf(outmk, " %s_make", p->ident); 743 fprintf(outmk, "\n\n"); 744 745 fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS)\n", 746 execfname, execfname); 747 fprintf(outmk, "\t$(CC) -static -o %s %s.o $(CRUNCHED_OBJS) $(LIBS)\n", 748 execfname, execfname); 749 fprintf(outmk, "\tstrip %s\n", execfname); 750 fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n"); 751 fprintf(outmk, "exe: %s\n", execfname); 752 fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n", 753 execfname); 754 } 755 756 757 void prog_makefile_rules(FILE *outmk, prog_t *p) 758 { 759 fprintf(outmk, "\n# -------- %s\n\n", p->name); 760 761 if(p->srcdir && p->objs) { 762 fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir); 763 fprintf(outmk, "%s_OBJS=", p->ident); 764 output_strlst(outmk, p->objs); 765 fprintf(outmk, "%s_make:\n", p->ident); 766 fprintf(outmk, "\t(cd $(%s_SRCDIR); make $(%s_OBJS))\n\n", 767 p->ident, p->ident); 768 } 769 else 770 fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n", 771 p->ident, p->name); 772 773 fprintf(outmk, "%s_OBJPATHS=", p->ident); 774 output_strlst(outmk, p->objpaths); 775 776 fprintf(outmk, "%s_stub.c:\n", p->name); 777 fprintf(outmk, "\techo \"" 778 "int _crunched_%s_stub(int argc, char **argv, char **envp)" 779 "{return main(argc,argv,envp);}\" >%s_stub.c\n", 780 p->ident, p->name); 781 fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)\n", 782 p->name, p->name, p->ident); 783 fprintf(outmk, "\tld -dc -r -o %s.lo %s_stub.o $(%s_OBJPATHS)\n", 784 p->name, p->name, p->ident); 785 fprintf(outmk, "\tcrunchide -k __crunched_%s_stub %s.lo\n", 786 p->ident, p->name); 787 } 788 789 void output_strlst(FILE *outf, strlst_t *lst) 790 { 791 for(; lst != NULL; lst = lst->next) 792 fprintf(outf, " %s", lst->str); 793 fprintf(outf, "\n"); 794 } 795 796 797 /* 798 * ======================================================================== 799 * general library routines 800 * 801 */ 802 803 void status(char *str) 804 { 805 static int lastlen = 0; 806 int len, spaces; 807 808 if(!verbose) return; 809 810 len = strlen(str); 811 spaces = lastlen - len; 812 if(spaces < 1) spaces = 1; 813 814 fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " "); 815 fflush(stderr); 816 lastlen = len; 817 } 818 819 820 void out_of_memory(void) 821 { 822 fprintf(stderr, "%s: %d: out of memory, stopping.\n", infilename, linenum); 823 exit(1); 824 } 825 826 827 void add_string(strlst_t **listp, char *str) 828 { 829 strlst_t *p1, *p2; 830 831 /* add to end, but be smart about dups */ 832 833 for(p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next) 834 if(!strcmp(p2->str, str)) return; 835 836 p2 = malloc(sizeof(strlst_t)); 837 if(p2) p2->str = strdup(str); 838 if(!p2 || !p2->str) 839 out_of_memory(); 840 841 p2->next = NULL; 842 if(p1 == NULL) *listp = p2; 843 else p1->next = p2; 844 } 845 846 847 int is_dir(char *pathname) 848 { 849 struct stat buf; 850 851 if(stat(pathname, &buf) == -1) 852 return 0; 853 return S_ISDIR(buf.st_mode); 854 } 855 856 int is_nonempty_file(char *pathname) 857 { 858 struct stat buf; 859 860 if(stat(pathname, &buf) == -1) 861 return 0; 862 863 return S_ISREG(buf.st_mode) && buf.st_size > 0; 864 } 865