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