crunchgen.c revision 69413
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 69413 2000-11-30 21:14:54Z luigi $
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 *objdir;
69    char *objvar;		/* Makefile variable to replace OBJS */
70    strlst_t *objs, *objpaths;
71    strlst_t *buildopts;
72    strlst_t *keeplist;
73    strlst_t *links;
74    int goterror;
75} prog_t;
76
77
78/* global state */
79
80strlst_t *buildopts = NULL;
81strlst_t *srcdirs = NULL;
82strlst_t *libs    = NULL;
83prog_t   *progs   = NULL;
84
85char confname[MAXPATHLEN], infilename[MAXPATHLEN];
86char outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN];
87char tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN];
88char outhdrname[MAXPATHLEN] ; /* user-supplied header for *.mk */
89char *objprefix;	/* where are the objects ? */
90int linenum = -1;
91int goterror = 0;
92
93int verbose, readcache;	/* options */
94int reading_cache;
95int makeobj = 0;	/* add 'make obj' rules to the makefile */
96
97int list_mode;
98
99/* general library routines */
100
101void status(char *str);
102void out_of_memory(void);
103void add_string(strlst_t **listp, char *str);
104int is_dir(char *pathname);
105int is_nonempty_file(char *pathname);
106
107/* helper routines for main() */
108
109void usage(void);
110void parse_conf_file(void);
111void gen_outputs(void);
112
113
114int main(int argc, char **argv)
115{
116    char *p;
117    int optc;
118
119    verbose = 1;
120    readcache = 1;
121    *outmkname = *outcfname = *execfname = '\0';
122
123    p = getenv("MAKEOBJDIRPREFIX");
124    if (p == NULL || *p == '\0')
125	objprefix = "/usr/obj"; /* default */
126    else
127	objprefix = strdup(p);
128
129    while((optc = getopt(argc, argv, "lh:m:c:e:p:foq")) != -1) {
130	switch(optc) {
131	case 'f':	readcache = 0; break;
132	case 'o':	makeobj = 1; break;
133	case 'q':	verbose = 0; break;
134
135	case 'm':	strcpy(outmkname, optarg); break;
136	case 'p':	objprefix = strdup(optarg); break;
137	case 'h':	strcpy(outhdrname, optarg); break;
138	case 'c':	strcpy(outcfname, optarg); break;
139	case 'e':	strcpy(execfname, optarg); break;
140	case 'l':	list_mode++; verbose = 0; break;
141
142	case '?':
143	default:	usage();
144	}
145    }
146
147    argc -= optind;
148    argv += optind;
149
150    if(argc != 1) usage();
151
152    /*
153     * generate filenames
154     */
155
156    strcpy(infilename, argv[0]);
157
158    /* confname = `basename infilename .conf` */
159
160    if((p=strrchr(infilename, '/')) != NULL) strcpy(confname, p+1);
161    else strcpy(confname, infilename);
162    if((p=strrchr(confname, '.')) != NULL && !strcmp(p, ".conf")) *p = '\0';
163
164    if(!*outmkname) sprintf(outmkname, "%s.mk", confname);
165    if(!*outcfname) sprintf(outcfname, "%s.c", confname);
166    if(!*execfname) sprintf(execfname, "%s", confname);
167
168    snprintf(cachename, sizeof(cachename), "%s.cache", confname);
169    snprintf(tempfname, sizeof(tempfname), ".tmp_%sXXXXXX", confname);
170    if(mktemp(tempfname) == NULL) {
171	perror(tempfname);
172	exit(1);
173    }
174
175    parse_conf_file();
176    if (list_mode)
177	exit(goterror);
178
179    gen_outputs();
180
181    exit(goterror);
182}
183
184
185void usage(void)
186{
187    fprintf(stderr, "%s\n%s\n",
188	"usage: crunchgen [-foq] [-h <makefile-header-name>] [-m <makefile>]",
189	"	[-p <obj-prefix>] [-c <c-file-name>] [-e <exec-file>] <conffile>");
190    exit(1);
191}
192
193
194/*
195 * ========================================================================
196 * parse_conf_file subsystem
197 *
198 */
199
200/* helper routines for parse_conf_file */
201
202void parse_one_file(char *filename);
203void parse_line(char *line, int *fc, char **fv, int nf);
204void add_srcdirs(int argc, char **argv);
205void add_progs(int argc, char **argv);
206void add_link(int argc, char **argv);
207void add_libs(int argc, char **argv);
208void add_buildopts(int argc, char **argv);
209void add_special(int argc, char **argv);
210
211prog_t *find_prog(char *str);
212void add_prog(char *progname);
213
214
215void parse_conf_file(void)
216{
217    if(!is_nonempty_file(infilename))
218		errx(1, "fatal: input file \"%s\" not found", infilename);
219    parse_one_file(infilename);
220    if(readcache && is_nonempty_file(cachename)) {
221	reading_cache = 1;
222	parse_one_file(cachename);
223    }
224}
225
226
227void parse_one_file(char *filename)
228{
229    char *fieldv[MAXFIELDS];
230    int fieldc;
231    void (*f)(int c, char **v);
232    FILE *cf;
233    char line[MAXLINELEN];
234
235    sprintf(line, "reading %s", filename);
236    status(line);
237    strcpy(curfilename, filename);
238
239    if((cf = fopen(curfilename, "r")) == NULL) {
240	warn("%s", curfilename);
241	goterror = 1;
242	return;
243    }
244
245    linenum = 0;
246    while(fgets(line, MAXLINELEN, cf) != NULL) {
247	linenum++;
248	parse_line(line, &fieldc, fieldv, MAXFIELDS);
249	if(fieldc < 1) continue;
250	if(!strcmp(fieldv[0], "srcdirs"))	f = add_srcdirs;
251	else if(!strcmp(fieldv[0], "progs"))    f = add_progs;
252	else if(!strcmp(fieldv[0], "ln"))	f = add_link;
253	else if(!strcmp(fieldv[0], "libs"))	f = add_libs;
254	else if(!strcmp(fieldv[0], "buildopts")) f = add_buildopts;
255	else if(!strcmp(fieldv[0], "special"))	f = add_special;
256	else {
257	    warnx("%s:%d: skipping unknown command `%s'",
258		    curfilename, linenum, fieldv[0]);
259	    goterror = 1;
260	    continue;
261	}
262	if(fieldc < 2) {
263	    warnx("%s:%d: %s command needs at least 1 argument, skipping",
264		    curfilename, linenum, fieldv[0]);
265	    goterror = 1;
266	    continue;
267	}
268	f(fieldc, fieldv);
269    }
270
271    if(ferror(cf)) {
272	warn("%s", curfilename);
273	goterror = 1;
274    }
275    fclose(cf);
276}
277
278
279void parse_line(char *line, int *fc, char **fv, int nf)
280{
281    char *p;
282
283    p = line;
284    *fc = 0;
285    while(1) {
286	while(isspace(*p)) p++;
287	if(*p == '\0' || *p == '#') break;
288
289	if(*fc < nf) fv[(*fc)++] = p;
290	while(*p && !isspace(*p) && *p != '#') p++;
291	if(*p == '\0' || *p == '#') break;
292	*p++ = '\0';
293    }
294    if(*p) *p = '\0';		/* needed for '#' case */
295}
296
297
298void add_srcdirs(int argc, char **argv)
299{
300    int i;
301
302    for(i=1;i<argc;i++) {
303	if(is_dir(argv[i]))
304	    add_string(&srcdirs, argv[i]);
305	else {
306	    warnx("%s:%d: `%s' is not a directory, skipping it",
307		    curfilename, linenum, argv[i]);
308	    goterror = 1;
309	}
310    }
311}
312
313
314void add_progs(int argc, char **argv)
315{
316    int i;
317
318    for(i=1;i<argc;i++)
319	add_prog(argv[i]);
320}
321
322
323void add_prog(char *progname)
324{
325    prog_t *p1, *p2;
326
327    /* add to end, but be smart about dups */
328
329    for(p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next)
330	if(!strcmp(p2->name, progname)) return;
331
332    p2 = malloc(sizeof(prog_t));
333    if(p2) {
334	memset(p2, 0, sizeof(prog_t));
335	p2->name = strdup(progname);
336    }
337    if(!p2 || !p2->name)
338	out_of_memory();
339
340    p2->next = NULL;
341    if(p1 == NULL) progs = p2;
342    else p1->next = p2;
343
344    p2->ident = p2->srcdir = p2->objdir = NULL;
345    p2->links = p2->objs = p2->keeplist = NULL;
346    p2->buildopts = NULL;
347    p2->goterror = 0;
348    if (list_mode)
349        printf("%s\n",progname);
350}
351
352
353void add_link(int argc, char **argv)
354{
355    int i;
356    prog_t *p = find_prog(argv[1]);
357
358    if(p == NULL) {
359	warnx("%s:%d: no prog %s previously declared, skipping link",
360		curfilename, linenum, argv[1]);
361	goterror = 1;
362	return;
363    }
364    for(i=2;i<argc;i++) {
365	if (list_mode)
366		printf("%s\n",argv[i]);
367	add_string(&p->links, argv[i]);
368    }
369}
370
371
372void add_libs(int argc, char **argv)
373{
374    int i;
375
376    for(i=1;i<argc;i++)
377	add_string(&libs, argv[i]);
378}
379
380
381void add_buildopts(int argc, char **argv)
382{
383    int i;
384
385    for (i = 1; i < argc; i++)
386	add_string(&buildopts, argv[i]);
387}
388
389
390void add_special(int argc, char **argv)
391{
392    int i;
393    prog_t *p = find_prog(argv[1]);
394
395    if(p == NULL) {
396	if(reading_cache) return;
397	warnx("%s:%d: no prog %s previously declared, skipping special",
398		curfilename, linenum, argv[1]);
399	goterror = 1;
400	return;
401    }
402
403    if(!strcmp(argv[2], "ident")) {
404	if(argc != 4) goto argcount;
405	if((p->ident = strdup(argv[3])) == NULL)
406	    out_of_memory();
407    }
408    else if(!strcmp(argv[2], "srcdir")) {
409	if(argc != 4) goto argcount;
410	if((p->srcdir = strdup(argv[3])) == NULL)
411	    out_of_memory();
412    }
413    else if(!strcmp(argv[2], "objdir")) {
414	if(argc != 4) goto argcount;
415	if((p->objdir = strdup(argv[3])) == NULL)
416	    out_of_memory();
417    }
418    else if(!strcmp(argv[2], "objs")) {
419	p->objs = NULL;
420	for(i=3;i<argc;i++)
421	    add_string(&p->objs, argv[i]);
422    }
423    else if(!strcmp(argv[2], "objpaths")) {
424	p->objpaths = NULL;
425	for(i=3;i<argc;i++)
426	    add_string(&p->objpaths, argv[i]);
427    }
428    else if(!strcmp(argv[2], "keep")) {
429	p->keeplist = NULL;
430	for(i=3;i<argc;i++)
431	    add_string(&p->keeplist, argv[i]);
432    }
433    else if(!strcmp(argv[2], "objvar")) {
434	if(argc != 4)
435	    goto argcount;
436	if((p->objvar = strdup(argv[3])) == NULL)
437	    out_of_memory();
438    }
439    else if (!strcmp(argv[2], "buildopts")) {
440	p->buildopts = NULL;
441	for (i = 3; i < argc; i++)
442		add_string(&p->buildopts, argv[i]);
443    }
444    else {
445	warnx("%s:%d: bad parameter name `%s', skipping line",
446		curfilename, linenum, argv[2]);
447	goterror = 1;
448    }
449    return;
450
451
452 argcount:
453    warnx("%s:%d: too %s arguments, expected \"special %s %s <string>\"",
454	    curfilename, linenum, argc < 4? "few" : "many", argv[1], argv[2]);
455    goterror = 1;
456}
457
458
459prog_t *find_prog(char *str)
460{
461    prog_t *p;
462
463    for(p = progs; p != NULL; p = p->next)
464	if(!strcmp(p->name, str)) return p;
465
466    return NULL;
467}
468
469
470/*
471 * ========================================================================
472 * gen_outputs subsystem
473 *
474 */
475
476/* helper subroutines */
477
478void remove_error_progs(void);
479void fillin_program(prog_t *p);
480void gen_specials_cache(void);
481void gen_output_makefile(void);
482void gen_output_cfile(void);
483
484void fillin_program_objs(prog_t *p, char *path);
485void top_makefile_rules(FILE *outmk);
486void prog_makefile_rules(FILE *outmk, prog_t *p);
487void output_strlst(FILE *outf, strlst_t *lst);
488char *genident(char *str);
489char *dir_search(char *progname);
490
491
492void gen_outputs(void)
493{
494    prog_t *p;
495
496    for(p = progs; p != NULL; p = p->next)
497	fillin_program(p);
498
499    remove_error_progs();
500    gen_specials_cache();
501    gen_output_cfile();
502    gen_output_makefile();
503    status("");
504    fprintf(stderr,
505	    "Run \"make -f %s objs exe\" to build crunched binary.\n",
506	    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    strlst_t *s;
517    char line[MAXLINELEN];
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    if(!p->objdir && p->srcdir) {
532	FILE *f;
533        p->objdir = p->srcdir;
534	snprintf(line, MAXLINELEN, "cd %s && echo -n %s`/bin/pwd`",
535		 p->srcdir, objprefix);
536	f = popen(line,"r");
537	if (f) {
538	    path[0]='\0';
539	    fgets(path,sizeof path, f);
540	    if (!pclose(f)) {
541		if(is_dir(path))
542		    p->objdir = strdup(path);
543	    }
544	}
545    }
546/*
547 * XXX look for a Makefile.{name} in local directory first.
548 * This lets us override the original Makefile.
549 */
550    snprintf(path, sizeof(path), "Makefile.%s", p->name);
551    if (is_nonempty_file(path)) {
552       snprintf(line, MAXLINELEN, "Using %s for %s", path, p->name);
553       status(line);
554    } else
555    if(p->srcdir) snprintf(path, sizeof(path), "%s/Makefile", p->srcdir);
556    if(!p->objs && p->srcdir && is_nonempty_file(path))
557	fillin_program_objs(p, path);
558
559    if(!p->objpaths && p->objdir && p->objs)
560	for(s = p->objs; s != NULL; s = s->next) {
561	    snprintf(line, MAXLINELEN, "%s/%s", p->objdir, s->str);
562	    add_string(&p->objpaths, line);
563	}
564
565    if(!p->srcdir && verbose)
566	warnx("%s: %s: warning: could not find source directory",
567		infilename, p->name);
568    if(!p->objs && verbose)
569	warnx("%s: %s: warning: could not find any .o files",
570		infilename, p->name);
571
572    if(!p->objpaths) {
573	warnx("%s: %s: error: no objpaths specified or calculated",
574		infilename, p->name);
575	p->goterror = goterror = 1;
576    }
577}
578
579void fillin_program_objs(prog_t *p, char *path)
580{
581    char *obj, *cp;
582    int rc;
583    FILE *f;
584    char *objvar="OBJS";
585    strlst_t *s;
586    char line[MAXLINELEN];
587
588    /* discover the objs from the srcdir Makefile */
589
590    if((f = fopen(tempfname, "w")) == NULL) {
591	warn("%s", tempfname);
592	goterror = 1;
593	return;
594    }
595    if (p->objvar)
596	objvar = p->objvar ;
597
598    /*
599     * XXX include outhdrname (e.g. to contain Make variables)
600     */
601    if (outhdrname[0] != '\0')
602	fprintf(f, ".include \"%s\"\n", outhdrname);
603    fprintf(f, ".include \"%s\"\n", path);
604    if (buildopts) {
605        fprintf(f, "BUILDOPTS+=");
606        output_strlst(f, buildopts);
607    }
608    fprintf(f, ".if defined(PROG) && !defined(%s)\n", objvar);
609    fprintf(f, "%s=${PROG}.o\n", objvar);
610    fprintf(f, ".endif\n");
611    fprintf(f, "loop:\n\t@echo 'OBJS= '${%s}\n", objvar);
612
613    fprintf(f, "crunchgen_objs:\n\t@make -f %s $(BUILDOPTS) $(%s_OPTS)",
614	tempfname, p->ident);
615    for (s = p->buildopts; s != NULL; s = s->next)
616        fprintf(f, " %s", s->str);
617    fprintf(f, " loop\n");
618
619    fclose(f);
620
621    snprintf(line, MAXLINELEN, "make -f %s crunchgen_objs 2>&1", tempfname);
622    if((f = popen(line, "r")) == NULL) {
623	warn("submake pipe");
624	goterror = 1;
625	return;
626    }
627
628    while(fgets(line, MAXLINELEN, f)) {
629	if(strncmp(line, "OBJS= ", 6)) {
630	    warnx("make error: %s", line);
631	    goterror = 1;
632	    continue;
633	}
634	cp = line + 6;
635	while(isspace(*cp)) cp++;
636	while(*cp) {
637	    obj = cp;
638	    while(*cp && !isspace(*cp)) cp++;
639	    if(*cp) *cp++ = '\0';
640	    add_string(&p->objs, obj);
641	    while(isspace(*cp)) cp++;
642	}
643    }
644    if((rc=pclose(f)) != 0) {
645	warnx("make error: make returned %d", rc);
646	goterror = 1;
647    }
648    unlink(tempfname);
649}
650
651void remove_error_progs(void)
652{
653    prog_t *p1, *p2;
654
655    p1 = NULL; p2 = progs;
656    while(p2 != NULL) {
657	if(!p2->goterror)
658	    p1 = p2, p2 = p2->next;
659	else {
660	    /* delete it from linked list */
661	    warnx("%s: %s: ignoring program because of errors",
662		    infilename, p2->name);
663	    if(p1) p1->next = p2->next;
664	    else progs = p2->next;
665	    p2 = p2->next;
666	}
667    }
668}
669
670void gen_specials_cache(void)
671{
672    FILE *cachef;
673    prog_t *p;
674    char line[MAXLINELEN];
675
676    snprintf(line, MAXLINELEN, "generating %s", cachename);
677    status(line);
678
679    if((cachef = fopen(cachename, "w")) == NULL) {
680	warn("%s", cachename);
681	goterror = 1;
682	return;
683    }
684
685    fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n",
686	    cachename, infilename, CRUNCH_VERSION);
687
688    for(p = progs; p != NULL; p = p->next) {
689	fprintf(cachef, "\n");
690	if(p->srcdir)
691	    fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir);
692	if(p->objdir)
693	    fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir);
694	if(p->objs) {
695	    fprintf(cachef, "special %s objs", p->name);
696	    output_strlst(cachef, p->objs);
697	}
698	fprintf(cachef, "special %s objpaths", p->name);
699	output_strlst(cachef, p->objpaths);
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 (buildopts) {
820	fprintf(outmk, "BUILDOPTS+=");
821	output_strlst(outmk, buildopts);
822    }
823
824    fprintf(outmk, "CRUNCHED_OBJS=");
825    for(p = progs; p != NULL; p = p->next)
826	fprintf(outmk, " %s.lo", p->name);
827    fprintf(outmk, "\n");
828
829    fprintf(outmk, "SUBMAKE_TARGETS=");
830    for(p = progs; p != NULL; p = p->next)
831	fprintf(outmk, " %s_make", p->ident);
832    fprintf(outmk, "\nSUBCLEAN_TARGETS=");
833    for(p = progs; p != NULL; p = p->next)
834	fprintf(outmk, " %s_clean", p->ident);
835    fprintf(outmk, "\n\n");
836
837    fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS)\n",
838	    execfname, execfname);
839    fprintf(outmk, "\t$(CC) -static -o %s %s.o $(CRUNCHED_OBJS) $(LIBS)\n",
840	    execfname, execfname);
841    fprintf(outmk, "\tstrip %s\n", execfname);
842    fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n");
843    fprintf(outmk, "exe: %s\n", execfname);
844    fprintf(outmk, "realclean: clean subclean\n");
845    fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n",
846	    execfname);
847    fprintf(outmk, "subclean: $(SUBCLEAN_TARGETS)\n");
848}
849
850
851void prog_makefile_rules(FILE *outmk, prog_t *p)
852{
853    strlst_t *lst;
854
855    fprintf(outmk, "\n# -------- %s\n\n", p->name);
856
857    if(p->srcdir && p->objs) {
858	fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir);
859	fprintf(outmk, "%s_OBJS=", p->ident);
860	output_strlst(outmk, p->objs);
861	if (p->buildopts != NULL) {
862		fprintf(outmk, "%s_OPTS+=", p->ident);
863		output_strlst(outmk, p->buildopts);
864	}
865	fprintf(outmk, "%s_make:\n", p->ident);
866	fprintf(outmk, "\t(cd $(%s_SRCDIR) && ", p->ident);
867	if (makeobj)
868		fprintf(outmk, "make obj && ");
869	fprintf(outmk, "\\\n");
870	fprintf(outmk, "\t\tmake $(BUILDOPTS) $(%s_OPTS) depend && \\\n"
871		"\t\tmake $(BUILDOPTS) $(%s_OPTS) $(%s_OBJS))\n",
872		p->ident, p->ident, p->ident);
873	fprintf(outmk, "%s_clean:\n", p->ident);
874	fprintf(outmk, "\t(cd $(%s_SRCDIR) && make clean)\n\n", p->ident);
875    }
876    else
877	fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n",
878		p->ident, p->name);
879
880    fprintf(outmk,   "%s_OBJPATHS=", p->ident);
881    output_strlst(outmk, p->objpaths);
882
883    fprintf(outmk, "%s_stub.c:\n", p->name);
884    fprintf(outmk, "\techo \""
885	           "int _crunched_%s_stub(int argc, char **argv, char **envp)"
886	           "{return main(argc,argv,envp);}\" >%s_stub.c\n",
887	    p->ident, p->name);
888    fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)\n",
889	    p->name, p->name, p->ident);
890    fprintf(outmk, "\tld -dc -r -o %s.lo %s_stub.o $(%s_OBJPATHS)\n",
891	    p->name, p->name, p->ident);
892    fprintf(outmk, "\tcrunchide -k _crunched_%s_stub ", p->ident);
893    for(lst = p->keeplist; lst != NULL; lst = lst->next)
894      fprintf(outmk, "-k _%s ", lst->str);
895    fprintf(outmk, "%s.lo\n", p->name);
896}
897
898void output_strlst(FILE *outf, strlst_t *lst)
899{
900    for(; lst != NULL; lst = lst->next)
901	fprintf(outf, " %s", lst->str);
902    fprintf(outf, "\n");
903}
904
905
906/*
907 * ========================================================================
908 * general library routines
909 *
910 */
911
912void status(char *str)
913{
914    static int lastlen = 0;
915    int len, spaces;
916
917    if(!verbose) return;
918
919    len = strlen(str);
920    spaces = lastlen - len;
921    if(spaces < 1) spaces = 1;
922
923    fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " ");
924    fflush(stderr);
925    lastlen = len;
926}
927
928
929void out_of_memory(void)
930{
931    errx(1, "%s: %d: out of memory, stopping", infilename, linenum);
932}
933
934
935void add_string(strlst_t **listp, char *str)
936{
937    strlst_t *p1, *p2;
938
939    /* add to end, but be smart about dups */
940
941    for(p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next)
942	if(!strcmp(p2->str, str)) return;
943
944    p2 = malloc(sizeof(strlst_t));
945    if(p2) {
946	p2->next = NULL ;
947	p2->str = strdup(str);
948    }
949    if(!p2 || !p2->str)
950	out_of_memory();
951
952    if(p1 == NULL) *listp = p2;
953    else p1->next = p2;
954}
955
956
957int is_dir(char *pathname)
958{
959    struct stat buf;
960
961    if(stat(pathname, &buf) == -1)
962	return 0;
963    return S_ISDIR(buf.st_mode);
964}
965
966int is_nonempty_file(char *pathname)
967{
968    struct stat buf;
969
970    if(stat(pathname, &buf) == -1)
971	return 0;
972
973    return S_ISREG(buf.st_mode) && buf.st_size > 0;
974}
975