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