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