crunchgen.c revision 1722
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 53typedef 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 60typedef 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 72strlst_t *srcdirs = NULL; 73strlst_t *libs = NULL; 74prog_t *progs = NULL; 75 76char line[MAXLINELEN]; 77 78char confname[MAXPATHLEN], infilename[MAXPATHLEN]; 79char outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN]; 80char tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN]; 81int linenum = -1; 82int goterror = 0; 83 84char *pname = "crunchgen"; 85 86int verbose, readcache; /* options */ 87int reading_cache; 88 89/* general library routines */ 90 91void status(char *str); 92void out_of_memory(void); 93void add_string(strlst_t **listp, char *str); 94int is_dir(char *pathname); 95int is_nonempty_file(char *pathname); 96 97/* helper routines for main() */ 98 99void usage(void); 100void parse_conf_file(void); 101void gen_outputs(void); 102 103 104int 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 166void 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 183void parse_one_file(char *filename); 184void parse_line(char *line, int *fc, char **fv, int nf); 185void add_srcdirs(int argc, char **argv); 186void add_progs(int argc, char **argv); 187void add_link(int argc, char **argv); 188void add_libs(int argc, char **argv); 189void add_special(int argc, char **argv); 190 191prog_t *find_prog(char *str); 192void add_prog(char *progname); 193 194 195void 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 210void 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 261void 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 280void 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 296void 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 305void 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 329void 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 346void 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 355void 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 410prog_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 429void remove_error_progs(void); 430void fillin_program(prog_t *p); 431void gen_specials_cache(void); 432void gen_output_makefile(void); 433void gen_output_cfile(void); 434 435void fillin_program_objs(prog_t *p, char *path); 436void top_makefile_rules(FILE *outmk); 437void prog_makefile_rules(FILE *outmk, prog_t *p); 438void output_strlst(FILE *outf, strlst_t *lst); 439char *genident(char *str); 440char *dir_search(char *progname); 441 442 443void 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 461void 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 512void 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 563void 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 582void 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 616void 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 643void 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 686char *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 707char *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 720void 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 749void 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 781void 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 795void 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 812void out_of_memory(void) 813{ 814 fprintf(stderr, "%s: %d: out of memory, stopping.\n", infilename, linenum); 815 exit(1); 816} 817 818 819void 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 839int 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 848int 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