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 p->objdir = p->srcdir; 485 } 486 487 if(p->srcdir) sprintf(path, "%s/Makefile", p->srcdir); 488 if(!p->objs && p->srcdir && is_nonempty_file(path)) 489 fillin_program_objs(p, path); 490 491 if(!p->objpaths && p->objdir && p->objs) 492 for(s = p->objs; s != NULL; s = s->next) { 493 sprintf(line, "%s/%s", p->objdir, s->str); 494 add_string(&p->objpaths, line); 495 } 496 497 if(!p->srcdir && verbose) 498 fprintf(stderr, "%s: %s: warning: could not find source directory.\n", 499 infilename, p->name); 500 if(!p->objs && verbose) 501 fprintf(stderr, "%s: %s: warning: could not find any .o files.\n", 502 infilename, p->name); 503 504 if(!p->objpaths) { 505 fprintf(stderr, 506 "%s: %s: error: no objpaths specified or calculated.\n", 507 infilename, p->name); 508 p->goterror = goterror = 1; 509 } 510 } 511 512 void fillin_program_objs(prog_t *p, char *path) 513 { 514 char *obj, *cp; 515 int rc; 516 FILE *f; 517 518 /* discover the objs from the srcdir Makefile */ 519 520 if((f = fopen(tempfname, "w")) == NULL) { 521 perror(tempfname); 522 goterror = 1; 523 return; 524 } 525 526 fprintf(f, ".include \"%s\"\n", path); 527 fprintf(f, ".if defined(PROG) && !defined(OBJS)\n"); 528 fprintf(f, "OBJS=${PROG}.o\n"); 529 fprintf(f, ".endif\n"); 530 fprintf(f, "crunchgen_objs:\n\t@echo 'OBJS= '${OBJS}\n"); 531 fclose(f); 532 533 sprintf(line, "make -f %s crunchgen_objs 2>&1", tempfname); 534 if((f = popen(line, "r")) == NULL) { 535 perror("submake pipe"); 536 goterror = 1; 537 return; 538 } 539 540 while(fgets(line, MAXLINELEN, f)) { 541 if(strncmp(line, "OBJS= ", 6)) { 542 fprintf(stderr, "make error: %s", line); 543 goterror = 1; 544 continue; 545 } 546 cp = line + 6; 547 while(isspace(*cp)) cp++; 548 while(*cp) { 549 obj = cp; 550 while(*cp && !isspace(*cp)) cp++; 551 if(*cp) *cp++ = '\0'; 552 add_string(&p->objs, obj); 553 while(isspace(*cp)) cp++; 554 } 555 } 556 if((rc=pclose(f)) != 0) { 557 fprintf(stderr, "make error: make returned %d\n", rc); 558 goterror = 1; 559 } 560 unlink(tempfname); 561 } 562 563 void remove_error_progs(void) 564 { 565 prog_t *p1, *p2; 566 567 p1 = NULL; p2 = progs; 568 while(p2 != NULL) { 569 if(!p2->goterror) 570 p1 = p2, p2 = p2->next; 571 else { 572 /* delete it from linked list */ 573 fprintf(stderr, "%s: %s: ignoring program because of errors.\n", 574 infilename, p2->name); 575 if(p1) p1->next = p2->next; 576 else progs = p2->next; 577 p2 = p2->next; 578 } 579 } 580 } 581 582 void gen_specials_cache(void) 583 { 584 FILE *cachef; 585 prog_t *p; 586 587 sprintf(line, "generating %s", cachename); 588 status(line); 589 590 if((cachef = fopen(cachename, "w")) == NULL) { 591 perror(cachename); 592 goterror = 1; 593 return; 594 } 595 596 fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n", 597 cachename, infilename, CRUNCH_VERSION); 598 599 for(p = progs; p != NULL; p = p->next) { 600 fprintf(cachef, "\n"); 601 if(p->srcdir) 602 fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir); 603 if(p->objdir) 604 fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir); 605 if(p->objs) { 606 fprintf(cachef, "special %s objs", p->name); 607 output_strlst(cachef, p->objs); 608 } 609 fprintf(cachef, "special %s objpaths", p->name); 610 output_strlst(cachef, p->objpaths); 611 } 612 fclose(cachef); 613 } 614 615 616 void gen_output_makefile(void) 617 { 618 prog_t *p; 619 FILE *outmk; 620 621 sprintf(line, "generating %s", outmkname); 622 status(line); 623 624 if((outmk = fopen(outmkname, "w")) == NULL) { 625 perror(outmkname); 626 goterror = 1; 627 return; 628 } 629 630 fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n", 631 outmkname, infilename, CRUNCH_VERSION); 632 633 top_makefile_rules(outmk); 634 635 for(p = progs; p != NULL; p = p->next) 636 prog_makefile_rules(outmk, p); 637 638 fprintf(outmk, "\n# ========\n"); 639 fclose(outmk); 640 } 641 642 643 void gen_output_cfile(void) 644 { 645 extern char *crunched_skel[]; 646 char **cp; 647 FILE *outcf; 648 prog_t *p; 649 strlst_t *s; 650 651 sprintf(line, "generating %s", outcfname); 652 status(line); 653 654 if((outcf = fopen(outcfname, "w")) == NULL) { 655 perror(outcfname); 656 goterror = 1; 657 return; 658 } 659 660 fprintf(outcf, 661 "/* %s - generated from %s by crunchgen %s */\n", 662 outcfname, infilename, CRUNCH_VERSION); 663 664 fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname); 665 for(cp = crunched_skel; *cp != NULL; cp++) 666 fprintf(outcf, "%s\n", *cp); 667 668 for(p = progs; p != NULL; p = p->next) 669 fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident); 670 671 fprintf(outcf, "\nstruct stub entry_points[] = {\n"); 672 for(p = progs; p != NULL; p = p->next) { 673 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 674 p->name, p->ident); 675 for(s = p->links; s != NULL; s = s->next) 676 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 677 s->str, p->ident); 678 } 679 680 fprintf(outcf, "\t{ EXECNAME, crunched_main },\n"); 681 fprintf(outcf, "\t{ NULL, NULL }\n};\n"); 682 fclose(outcf); 683 } 684 685 686 char *genident(char *str) 687 { 688 char *n,*s,*d; 689 690 /* 691 * generates a Makefile/C identifier from a program name, mapping '-' to 692 * '_' and ignoring all other non-identifier characters. This leads to 693 * programs named "foo.bar" and "foobar" to map to the same identifier. 694 */ 695 696 if((n = strdup(str)) == NULL) 697 return NULL; 698 for(d = s = n; *s != '\0'; s++) { 699 if(*s == '-') *d++ = '_'; 700 else if(*s == '_' || isalnum(*s)) *d++ = *s; 701 } 702 *d = '\0'; 703 return n; 704 } 705 706 707 char *dir_search(char *progname) 708 { 709 char path[MAXPATHLEN]; 710 strlst_t *dir; 711 712 for(dir=srcdirs; dir != NULL; dir=dir->next) { 713 sprintf(path, "%s/%s", dir->str, progname); 714 if(is_dir(path)) return dir->str; 715 } 716 return NULL; 717 } 718 719 720 void top_makefile_rules(FILE *outmk) 721 { 722 prog_t *p; 723 724 fprintf(outmk, "LIBS="); 725 output_strlst(outmk, libs); 726 727 fprintf(outmk, "CRUNCHED_OBJS="); 728 for(p = progs; p != NULL; p = p->next) 729 fprintf(outmk, " %s.lo", p->name); 730 fprintf(outmk, "\n"); 731 732 fprintf(outmk, "SUBMAKE_TARGETS="); 733 for(p = progs; p != NULL; p = p->next) 734 fprintf(outmk, " %s_make", p->ident); 735 fprintf(outmk, "\n\n"); 736 737 fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS)\n", 738 execfname, execfname); 739 fprintf(outmk, "\t$(CC) -static -o %s %s.o $(CRUNCHED_OBJS) $(LIBS)\n", 740 execfname, execfname); 741 fprintf(outmk, "\tstrip %s\n", execfname); 742 fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n"); 743 fprintf(outmk, "exe: %s\n", execfname); 744 fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n", 745 execfname); 746 } 747 748 749 void prog_makefile_rules(FILE *outmk, prog_t *p) 750 { 751 fprintf(outmk, "\n# -------- %s\n\n", p->name); 752 753 if(p->srcdir && p->objs) { 754 fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir); 755 fprintf(outmk, "%s_OBJS=", p->ident); 756 output_strlst(outmk, p->objs); 757 fprintf(outmk, "%s_make:\n", p->ident); 758 fprintf(outmk, "\t(cd $(%s_SRCDIR); make $(%s_OBJS))\n\n", 759 p->ident, p->ident); 760 } 761 else 762 fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n", 763 p->ident, p->name); 764 765 fprintf(outmk, "%s_OBJPATHS=", p->ident); 766 output_strlst(outmk, p->objpaths); 767 768 fprintf(outmk, "%s_stub.c:\n", p->name); 769 fprintf(outmk, "\techo \"" 770 "int _crunched_%s_stub(int argc, char **argv, char **envp)" 771 "{return main(argc,argv,envp);}\" >%s_stub.c\n", 772 p->ident, p->name); 773 fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)\n", 774 p->name, p->name, p->ident); 775 fprintf(outmk, "\tld -dc -r -o %s.lo %s_stub.o $(%s_OBJPATHS)\n", 776 p->name, p->name, p->ident); 777 fprintf(outmk, "\tcrunchide -k __crunched_%s_stub %s.lo\n", 778 p->ident, p->name); 779 } 780 781 void output_strlst(FILE *outf, strlst_t *lst) 782 { 783 for(; lst != NULL; lst = lst->next) 784 fprintf(outf, " %s", lst->str); 785 fprintf(outf, "\n"); 786 } 787 788 789 /* 790 * ======================================================================== 791 * general library routines 792 * 793 */ 794 795 void status(char *str) 796 { 797 static int lastlen = 0; 798 int len, spaces; 799 800 if(!verbose) return; 801 802 len = strlen(str); 803 spaces = lastlen - len; 804 if(spaces < 1) spaces = 1; 805 806 fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " "); 807 fflush(stderr); 808 lastlen = len; 809 } 810 811 812 void out_of_memory(void) 813 { 814 fprintf(stderr, "%s: %d: out of memory, stopping.\n", infilename, linenum); 815 exit(1); 816 } 817 818 819 void add_string(strlst_t **listp, char *str) 820 { 821 strlst_t *p1, *p2; 822 823 /* add to end, but be smart about dups */ 824 825 for(p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next) 826 if(!strcmp(p2->str, str)) return; 827 828 p2 = malloc(sizeof(strlst_t)); 829 if(p2) p2->str = strdup(str); 830 if(!p2 || !p2->str) 831 out_of_memory(); 832 833 p2->next = NULL; 834 if(p1 == NULL) *listp = p2; 835 else p1->next = p2; 836 } 837 838 839 int is_dir(char *pathname) 840 { 841 struct stat buf; 842 843 if(stat(pathname, &buf) == -1) 844 return 0; 845 return S_ISDIR(buf.st_mode); 846 } 847 848 int is_nonempty_file(char *pathname) 849 { 850 struct stat buf; 851 852 if(stat(pathname, &buf) == -1) 853 return 0; 854 855 return S_ISREG(buf.st_mode) && buf.st_size > 0; 856 } 857