Home | History | Annotate | Line # | Download | only in crunchgen
crunchgen.c revision 1.1
      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