crunchgen.c revision 68750
11722Sjkh/*
21722Sjkh * Copyright (c) 1994 University of Maryland
31722Sjkh * All Rights Reserved.
41722Sjkh *
51722Sjkh * Permission to use, copy, modify, distribute, and sell this software and its
61722Sjkh * documentation for any purpose is hereby granted without fee, provided that
71722Sjkh * the above copyright notice appear in all copies and that both that
81722Sjkh * copyright notice and this permission notice appear in supporting
91722Sjkh * documentation, and that the name of U.M. not be used in advertising or
101722Sjkh * publicity pertaining to distribution of the software without specific,
111722Sjkh * written prior permission.  U.M. makes no representations about the
121722Sjkh * suitability of this software for any purpose.  It is provided "as is"
131722Sjkh * without express or implied warranty.
141722Sjkh *
151722Sjkh * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
161722Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
171722Sjkh * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
181722Sjkh * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
191722Sjkh * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
201722Sjkh * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
211722Sjkh *
221722Sjkh * Author: James da Silva, Systems Design and Analysis Group
231722Sjkh *			   Computer Science Department
241722Sjkh *			   University of Maryland at College Park
2556995Sluigi *
2656995Sluigi * $FreeBSD: head/usr.sbin/crunch/crunchgen/crunchgen.c 68750 2000-11-15 14:33:29Z joe $
271722Sjkh */
281722Sjkh/*
291722Sjkh * ========================================================================
301722Sjkh * crunchgen.c
311722Sjkh *
321722Sjkh * Generates a Makefile and main C file for a crunched executable,
338857Srgrimes * from specs given in a .conf file.
341722Sjkh */
3529453Scharnier#include <ctype.h>
3629453Scharnier#include <err.h>
3729453Scharnier#include <stdio.h>
381722Sjkh#include <stdlib.h>
3929453Scharnier#include <string.h>
401722Sjkh#include <unistd.h>
411722Sjkh
421722Sjkh#include <sys/types.h>
431722Sjkh#include <sys/stat.h>
441722Sjkh#include <sys/param.h>
451722Sjkh
461722Sjkh#define CRUNCH_VERSION	"0.2"
471722Sjkh
481722Sjkh#define MAXLINELEN	16384
491722Sjkh#define MAXFIELDS 	 2048
501722Sjkh
511722Sjkh
521722Sjkh/* internal representation of conf file: */
531722Sjkh
541722Sjkh/* simple lists of strings suffice for most parms */
551722Sjkh
561722Sjkhtypedef struct strlst {
571722Sjkh    struct strlst *next;
581722Sjkh    char *str;
591722Sjkh} strlst_t;
601722Sjkh
611722Sjkh/* progs have structure, each field can be set with "special" or calculated */
621722Sjkh
631722Sjkhtypedef struct prog {
6456995Sluigi    struct prog *next;		/* link field */
6556995Sluigi    char *name;			/* program name */
6656995Sluigi    char *ident;		/* C identifier for the program name */
6756995Sluigi    char *srcdir;
6856995Sluigi    char *objdir;
6956995Sluigi    char *objvar;		/* Makefile variable to replace OBJS */
701722Sjkh    strlst_t *objs, *objpaths;
7168569Sjoe    strlst_t *buildopts;
7230120Sjoerg    strlst_t *keeplist;
731722Sjkh    strlst_t *links;
741722Sjkh    int goterror;
751722Sjkh} prog_t;
761722Sjkh
771722Sjkh
781722Sjkh/* global state */
791722Sjkh
8068750Sjoestrlst_t *buildopts = NULL;
811722Sjkhstrlst_t *srcdirs = NULL;
821722Sjkhstrlst_t *libs    = NULL;
831722Sjkhprog_t   *progs   = NULL;
841722Sjkh
851722Sjkhchar line[MAXLINELEN];
861722Sjkh
871722Sjkhchar confname[MAXPATHLEN], infilename[MAXPATHLEN];
881722Sjkhchar outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN];
891722Sjkhchar tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN];
9056995Sluigichar outhdrname[MAXPATHLEN] ; /* user-supplied header for *.mk */
911722Sjkhint linenum = -1;
921722Sjkhint goterror = 0;
931722Sjkh
941722Sjkhint verbose, readcache;	/* options */
951722Sjkhint reading_cache;
9668286Sjoeint makeobj = 0;	/* add 'make obj' rules to the makefile */
971722Sjkh
986696Sphkint list_mode;
996696Sphk
1001722Sjkh/* general library routines */
1011722Sjkh
1021722Sjkhvoid status(char *str);
1031722Sjkhvoid out_of_memory(void);
1041722Sjkhvoid add_string(strlst_t **listp, char *str);
1051722Sjkhint is_dir(char *pathname);
1061722Sjkhint is_nonempty_file(char *pathname);
1071722Sjkh
1081722Sjkh/* helper routines for main() */
1091722Sjkh
1108857Srgrimesvoid usage(void);
1111722Sjkhvoid parse_conf_file(void);
1121722Sjkhvoid gen_outputs(void);
1131722Sjkh
1141722Sjkh
1151722Sjkhint main(int argc, char **argv)
1161722Sjkh{
1171722Sjkh    char *p;
1181722Sjkh    int optc;
1191722Sjkh
1201722Sjkh    verbose = 1;
1211722Sjkh    readcache = 1;
1221722Sjkh    *outmkname = *outcfname = *execfname = '\0';
1238857Srgrimes
12468286Sjoe    while((optc = getopt(argc, argv, "lh:m:c:e:foq")) != -1) {
1251722Sjkh	switch(optc) {
1261722Sjkh	case 'f':	readcache = 0; break;
12768286Sjoe	case 'o':	makeobj = 1; break;
1281722Sjkh	case 'q':	verbose = 0; break;
1291722Sjkh
1301722Sjkh	case 'm':	strcpy(outmkname, optarg); break;
13156995Sluigi	case 'h':	strcpy(outhdrname, optarg); break;
1321722Sjkh	case 'c':	strcpy(outcfname, optarg); break;
1331722Sjkh	case 'e':	strcpy(execfname, optarg); break;
1346696Sphk	case 'l':	list_mode++; verbose = 0; break;
1351722Sjkh
1361722Sjkh	case '?':
1371722Sjkh	default:	usage();
1381722Sjkh	}
1391722Sjkh    }
1401722Sjkh
1411722Sjkh    argc -= optind;
1421722Sjkh    argv += optind;
1431722Sjkh
1441722Sjkh    if(argc != 1) usage();
1451722Sjkh
1468857Srgrimes    /*
1471722Sjkh     * generate filenames
1481722Sjkh     */
1491722Sjkh
1501722Sjkh    strcpy(infilename, argv[0]);
1511722Sjkh
1521722Sjkh    /* confname = `basename infilename .conf` */
1531722Sjkh
1541722Sjkh    if((p=strrchr(infilename, '/')) != NULL) strcpy(confname, p+1);
1551722Sjkh    else strcpy(confname, infilename);
1561722Sjkh    if((p=strrchr(confname, '.')) != NULL && !strcmp(p, ".conf")) *p = '\0';
1571722Sjkh
1581722Sjkh    if(!*outmkname) sprintf(outmkname, "%s.mk", confname);
1591722Sjkh    if(!*outcfname) sprintf(outcfname, "%s.c", confname);
1601722Sjkh    if(!*execfname) sprintf(execfname, "%s", confname);
1611722Sjkh
1621722Sjkh    sprintf(cachename, "%s.cache", confname);
1631722Sjkh    sprintf(tempfname, ".tmp_%sXXXXXX", confname);
1641722Sjkh    if(mktemp(tempfname) == NULL) {
1651722Sjkh	perror(tempfname);
1661722Sjkh	exit(1);
1671722Sjkh    }
1681722Sjkh
1691722Sjkh    parse_conf_file();
1706696Sphk    if (list_mode)
1716696Sphk	exit(goterror);
1726696Sphk
1731722Sjkh    gen_outputs();
1741722Sjkh
1751722Sjkh    exit(goterror);
1761722Sjkh}
1771722Sjkh
1781722Sjkh
1791722Sjkhvoid usage(void)
1801722Sjkh{
18129453Scharnier    fprintf(stderr, "%s\n%s\n",
18268286Sjoe		"usage: crunchgen [-foq] [-m <makefile>] [-c <c file>]",
18329453Scharnier		"                 [-e <exec file>] <conffile>");
1841722Sjkh    exit(1);
1851722Sjkh}
1861722Sjkh
1871722Sjkh
1881722Sjkh/*
1891722Sjkh * ========================================================================
1901722Sjkh * parse_conf_file subsystem
1911722Sjkh *
1921722Sjkh */
1931722Sjkh
1941722Sjkh/* helper routines for parse_conf_file */
1951722Sjkh
1961722Sjkhvoid parse_one_file(char *filename);
1978857Srgrimesvoid parse_line(char *line, int *fc, char **fv, int nf);
1981722Sjkhvoid add_srcdirs(int argc, char **argv);
1991722Sjkhvoid add_progs(int argc, char **argv);
2001722Sjkhvoid add_link(int argc, char **argv);
2011722Sjkhvoid add_libs(int argc, char **argv);
20268750Sjoevoid add_buildopts(int argc, char **argv);
2031722Sjkhvoid add_special(int argc, char **argv);
2041722Sjkh
2051722Sjkhprog_t *find_prog(char *str);
2061722Sjkhvoid add_prog(char *progname);
2071722Sjkh
2081722Sjkh
2091722Sjkhvoid parse_conf_file(void)
2101722Sjkh{
21129453Scharnier    if(!is_nonempty_file(infilename))
21229453Scharnier		errx(1, "fatal: input file \"%s\" not found", infilename);
2131722Sjkh    parse_one_file(infilename);
2141722Sjkh    if(readcache && is_nonempty_file(cachename)) {
2151722Sjkh	reading_cache = 1;
2161722Sjkh	parse_one_file(cachename);
2171722Sjkh    }
2181722Sjkh}
2191722Sjkh
2201722Sjkh
2211722Sjkhvoid parse_one_file(char *filename)
2221722Sjkh{
2231722Sjkh    char *fieldv[MAXFIELDS];
2241722Sjkh    int fieldc;
2251722Sjkh    void (*f)(int c, char **v);
2261722Sjkh    FILE *cf;
2271722Sjkh
2281722Sjkh    sprintf(line, "reading %s", filename);
2291722Sjkh    status(line);
2301722Sjkh    strcpy(curfilename, filename);
2311722Sjkh
2321722Sjkh    if((cf = fopen(curfilename, "r")) == NULL) {
23329453Scharnier	warn("%s", curfilename);
2341722Sjkh	goterror = 1;
2351722Sjkh	return;
2361722Sjkh    }
2371722Sjkh
2381722Sjkh    linenum = 0;
2391722Sjkh    while(fgets(line, MAXLINELEN, cf) != NULL) {
2401722Sjkh	linenum++;
2411722Sjkh	parse_line(line, &fieldc, fieldv, MAXFIELDS);
2421722Sjkh	if(fieldc < 1) continue;
2431722Sjkh	if(!strcmp(fieldv[0], "srcdirs"))	f = add_srcdirs;
2441722Sjkh	else if(!strcmp(fieldv[0], "progs"))    f = add_progs;
2451722Sjkh	else if(!strcmp(fieldv[0], "ln"))	f = add_link;
2461722Sjkh	else if(!strcmp(fieldv[0], "libs"))	f = add_libs;
24768750Sjoe	else if(!strcmp(fieldv[0], "buildopts")) f = add_buildopts;
2481722Sjkh	else if(!strcmp(fieldv[0], "special"))	f = add_special;
2491722Sjkh	else {
25029453Scharnier	    warnx("%s:%d: skipping unknown command `%s'",
2511722Sjkh		    curfilename, linenum, fieldv[0]);
2521722Sjkh	    goterror = 1;
2531722Sjkh	    continue;
2541722Sjkh	}
2551722Sjkh	if(fieldc < 2) {
25629453Scharnier	    warnx("%s:%d: %s command needs at least 1 argument, skipping",
2571722Sjkh		    curfilename, linenum, fieldv[0]);
2581722Sjkh	    goterror = 1;
2591722Sjkh	    continue;
2601722Sjkh	}
2611722Sjkh	f(fieldc, fieldv);
2621722Sjkh    }
2631722Sjkh
2641722Sjkh    if(ferror(cf)) {
26529453Scharnier	warn("%s", curfilename);
2661722Sjkh	goterror = 1;
2671722Sjkh    }
2681722Sjkh    fclose(cf);
2691722Sjkh}
2701722Sjkh
2711722Sjkh
2721722Sjkhvoid parse_line(char *line, int *fc, char **fv, int nf)
2731722Sjkh{
2741722Sjkh    char *p;
2751722Sjkh
2761722Sjkh    p = line;
2771722Sjkh    *fc = 0;
2781722Sjkh    while(1) {
2791722Sjkh	while(isspace(*p)) p++;
2801722Sjkh	if(*p == '\0' || *p == '#') break;
2811722Sjkh
2821722Sjkh	if(*fc < nf) fv[(*fc)++] = p;
2831722Sjkh	while(*p && !isspace(*p) && *p != '#') p++;
2841722Sjkh	if(*p == '\0' || *p == '#') break;
2851722Sjkh	*p++ = '\0';
2861722Sjkh    }
2871722Sjkh    if(*p) *p = '\0';		/* needed for '#' case */
2881722Sjkh}
2891722Sjkh
2901722Sjkh
2911722Sjkhvoid add_srcdirs(int argc, char **argv)
2921722Sjkh{
2931722Sjkh    int i;
2941722Sjkh
2951722Sjkh    for(i=1;i<argc;i++) {
2961722Sjkh	if(is_dir(argv[i]))
2971722Sjkh	    add_string(&srcdirs, argv[i]);
2981722Sjkh	else {
29929453Scharnier	    warnx("%s:%d: `%s' is not a directory, skipping it",
3001722Sjkh		    curfilename, linenum, argv[i]);
3011722Sjkh	    goterror = 1;
3021722Sjkh	}
3031722Sjkh    }
3041722Sjkh}
3051722Sjkh
3061722Sjkh
3071722Sjkhvoid add_progs(int argc, char **argv)
3081722Sjkh{
3091722Sjkh    int i;
3101722Sjkh
3111722Sjkh    for(i=1;i<argc;i++)
3121722Sjkh	add_prog(argv[i]);
3131722Sjkh}
3141722Sjkh
3151722Sjkh
3161722Sjkhvoid add_prog(char *progname)
3171722Sjkh{
3181722Sjkh    prog_t *p1, *p2;
3191722Sjkh
3201722Sjkh    /* add to end, but be smart about dups */
3211722Sjkh
3221722Sjkh    for(p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next)
3231722Sjkh	if(!strcmp(p2->name, progname)) return;
3241722Sjkh
3251722Sjkh    p2 = malloc(sizeof(prog_t));
32619822Sjoerg    if(p2) {
32719822Sjoerg	memset(p2, 0, sizeof(prog_t));
32819822Sjoerg	p2->name = strdup(progname);
32919822Sjoerg    }
3308857Srgrimes    if(!p2 || !p2->name)
3311722Sjkh	out_of_memory();
3321722Sjkh
3331722Sjkh    p2->next = NULL;
3341722Sjkh    if(p1 == NULL) progs = p2;
3351722Sjkh    else p1->next = p2;
3361722Sjkh
3371722Sjkh    p2->ident = p2->srcdir = p2->objdir = NULL;
33830120Sjoerg    p2->links = p2->objs = p2->keeplist = NULL;
33968569Sjoe    p2->buildopts = NULL;
3401722Sjkh    p2->goterror = 0;
3416696Sphk    if (list_mode)
3426696Sphk        printf("%s\n",progname);
3431722Sjkh}
3441722Sjkh
3451722Sjkh
3461722Sjkhvoid add_link(int argc, char **argv)
3471722Sjkh{
3481722Sjkh    int i;
3491722Sjkh    prog_t *p = find_prog(argv[1]);
3501722Sjkh
3511722Sjkh    if(p == NULL) {
35229453Scharnier	warnx("%s:%d: no prog %s previously declared, skipping link",
3531722Sjkh		curfilename, linenum, argv[1]);
3541722Sjkh	goterror = 1;
3551722Sjkh	return;
3561722Sjkh    }
3576696Sphk    for(i=2;i<argc;i++) {
3586696Sphk	if (list_mode)
3596696Sphk		printf("%s\n",argv[i]);
3601722Sjkh	add_string(&p->links, argv[i]);
3616696Sphk    }
3621722Sjkh}
3631722Sjkh
3641722Sjkh
3651722Sjkhvoid add_libs(int argc, char **argv)
3661722Sjkh{
3671722Sjkh    int i;
3681722Sjkh
3691722Sjkh    for(i=1;i<argc;i++)
3701722Sjkh	add_string(&libs, argv[i]);
3711722Sjkh}
3721722Sjkh
3731722Sjkh
37468750Sjoevoid add_buildopts(int argc, char **argv)
37568750Sjoe{
37668750Sjoe    int i;
37768750Sjoe
37868750Sjoe    for (i = 1; i < argc; i++)
37968750Sjoe	add_string(&buildopts, argv[i]);
38068750Sjoe}
38168750Sjoe
38268750Sjoe
3831722Sjkhvoid add_special(int argc, char **argv)
3841722Sjkh{
3851722Sjkh    int i;
3861722Sjkh    prog_t *p = find_prog(argv[1]);
3871722Sjkh
3881722Sjkh    if(p == NULL) {
3891722Sjkh	if(reading_cache) return;
39029453Scharnier	warnx("%s:%d: no prog %s previously declared, skipping special",
3911722Sjkh		curfilename, linenum, argv[1]);
3921722Sjkh	goterror = 1;
3931722Sjkh	return;
3941722Sjkh    }
3951722Sjkh
3961722Sjkh    if(!strcmp(argv[2], "ident")) {
3971722Sjkh	if(argc != 4) goto argcount;
3981722Sjkh	if((p->ident = strdup(argv[3])) == NULL)
3991722Sjkh	    out_of_memory();
4001722Sjkh    }
4011722Sjkh    else if(!strcmp(argv[2], "srcdir")) {
4021722Sjkh	if(argc != 4) goto argcount;
4031722Sjkh	if((p->srcdir = strdup(argv[3])) == NULL)
4041722Sjkh	    out_of_memory();
4051722Sjkh    }
4061722Sjkh    else if(!strcmp(argv[2], "objdir")) {
4071722Sjkh	if(argc != 4) goto argcount;
4081722Sjkh	if((p->objdir = strdup(argv[3])) == NULL)
4091722Sjkh	    out_of_memory();
4101722Sjkh    }
4111722Sjkh    else if(!strcmp(argv[2], "objs")) {
4121722Sjkh	p->objs = NULL;
4131722Sjkh	for(i=3;i<argc;i++)
4141722Sjkh	    add_string(&p->objs, argv[i]);
4151722Sjkh    }
4161722Sjkh    else if(!strcmp(argv[2], "objpaths")) {
4171722Sjkh	p->objpaths = NULL;
4181722Sjkh	for(i=3;i<argc;i++)
4191722Sjkh	    add_string(&p->objpaths, argv[i]);
4201722Sjkh    }
42130120Sjoerg    else if(!strcmp(argv[2], "keep")) {
42230120Sjoerg	p->keeplist = NULL;
42330120Sjoerg	for(i=3;i<argc;i++)
42430120Sjoerg	    add_string(&p->keeplist, argv[i]);
42530120Sjoerg    }
42656995Sluigi    else if(!strcmp(argv[2], "objvar")) {
42756995Sluigi	if(argc != 4)
42856995Sluigi	    goto argcount;
42956995Sluigi	if((p->objvar = strdup(argv[3])) == NULL)
43056995Sluigi	    out_of_memory();
43156995Sluigi    }
43268569Sjoe    else if (!strcmp(argv[2], "buildopts")) {
43368569Sjoe	p->buildopts = NULL;
43468569Sjoe	for (i = 3; i < argc; i++)
43568569Sjoe		add_string(&p->buildopts, argv[i]);
43668569Sjoe    }
4371722Sjkh    else {
43829453Scharnier	warnx("%s:%d: bad parameter name `%s', skipping line",
4391722Sjkh		curfilename, linenum, argv[2]);
4401722Sjkh	goterror = 1;
4411722Sjkh    }
4421722Sjkh    return;
4431722Sjkh
4441722Sjkh
4451722Sjkh argcount:
44629453Scharnier    warnx("%s:%d: too %s arguments, expected \"special %s %s <string>\"",
4471722Sjkh	    curfilename, linenum, argc < 4? "few" : "many", argv[1], argv[2]);
4481722Sjkh    goterror = 1;
4491722Sjkh}
4501722Sjkh
4511722Sjkh
4521722Sjkhprog_t *find_prog(char *str)
4531722Sjkh{
4541722Sjkh    prog_t *p;
4551722Sjkh
4561722Sjkh    for(p = progs; p != NULL; p = p->next)
4571722Sjkh	if(!strcmp(p->name, str)) return p;
4581722Sjkh
4591722Sjkh    return NULL;
4601722Sjkh}
4611722Sjkh
4621722Sjkh
4631722Sjkh/*
4641722Sjkh * ========================================================================
4651722Sjkh * gen_outputs subsystem
4661722Sjkh *
4671722Sjkh */
4681722Sjkh
4691722Sjkh/* helper subroutines */
4701722Sjkh
4711722Sjkhvoid remove_error_progs(void);
4721722Sjkhvoid fillin_program(prog_t *p);
4731722Sjkhvoid gen_specials_cache(void);
4741722Sjkhvoid gen_output_makefile(void);
4751722Sjkhvoid gen_output_cfile(void);
4761722Sjkh
4771722Sjkhvoid fillin_program_objs(prog_t *p, char *path);
4781722Sjkhvoid top_makefile_rules(FILE *outmk);
4791722Sjkhvoid prog_makefile_rules(FILE *outmk, prog_t *p);
4801722Sjkhvoid output_strlst(FILE *outf, strlst_t *lst);
4811722Sjkhchar *genident(char *str);
4821722Sjkhchar *dir_search(char *progname);
4831722Sjkh
4841722Sjkh
4851722Sjkhvoid gen_outputs(void)
4861722Sjkh{
4871722Sjkh    prog_t *p;
4881722Sjkh
4891722Sjkh    for(p = progs; p != NULL; p = p->next)
4901722Sjkh	fillin_program(p);
4911722Sjkh
4921722Sjkh    remove_error_progs();
4931722Sjkh    gen_specials_cache();
4941722Sjkh    gen_output_cfile();
4951722Sjkh    gen_output_makefile();
4961722Sjkh    status("");
4978857Srgrimes    fprintf(stderr,
4981722Sjkh	    "Run \"make -f %s objs exe\" to build crunched binary.\n",
4991722Sjkh	    outmkname);
5001722Sjkh}
5011722Sjkh
50256995Sluigi/*
50356995Sluigi * run the makefile for the program to find which objects are necessary
50456995Sluigi */
5051722Sjkhvoid fillin_program(prog_t *p)
5061722Sjkh{
5071722Sjkh    char path[MAXPATHLEN];
5081722Sjkh    char *srcparent;
5091722Sjkh    strlst_t *s;
5101722Sjkh
5111722Sjkh    sprintf(line, "filling in parms for %s", p->name);
5121722Sjkh    status(line);
5131722Sjkh
5148857Srgrimes    if(!p->ident)
5151722Sjkh	p->ident = genident(p->name);
5161722Sjkh    if(!p->srcdir) {
5171722Sjkh	srcparent = dir_search(p->name);
5181722Sjkh	if(srcparent)
5191722Sjkh	    sprintf(path, "%s/%s", srcparent, p->name);
5201722Sjkh	if(is_dir(path))
5211722Sjkh	    p->srcdir = strdup(path);
5221722Sjkh    }
5231722Sjkh    if(!p->objdir && p->srcdir) {
52417430Sphk	FILE *f;
52517430Sphk
52632704Ssos	sprintf(path, "cd %s && echo -n /usr/obj`/bin/pwd`", p->srcdir);
52717430Sphk        p->objdir = p->srcdir;
52817430Sphk	f = popen(path,"r");
52917430Sphk	if (f) {
53017430Sphk	    fgets(path,sizeof path, f);
53117430Sphk	    if (!pclose(f)) {
53217430Sphk		if(is_dir(path))
53317430Sphk		    p->objdir = strdup(path);
53417430Sphk	    }
53517430Sphk	}
5361722Sjkh    }
53756995Sluigi/*
53856995Sluigi * XXX look for a Makefile.{name} in local directory first.
53956995Sluigi * This lets us override the original Makefile.
54056995Sluigi */
54156995Sluigi    sprintf(path, "Makefile.%s", p->name);
54256995Sluigi    if (is_nonempty_file(path)) {
54356995Sluigi       sprintf(line, "Using %s for %s", path, p->name);
54456995Sluigi       status(line);
54556995Sluigi    } else
5461722Sjkh    if(p->srcdir) sprintf(path, "%s/Makefile", p->srcdir);
5471722Sjkh    if(!p->objs && p->srcdir && is_nonempty_file(path))
5481722Sjkh	fillin_program_objs(p, path);
5491722Sjkh
5501722Sjkh    if(!p->objpaths && p->objdir && p->objs)
5511722Sjkh	for(s = p->objs; s != NULL; s = s->next) {
5521722Sjkh	    sprintf(line, "%s/%s", p->objdir, s->str);
5531722Sjkh	    add_string(&p->objpaths, line);
5541722Sjkh	}
5551722Sjkh
5561722Sjkh    if(!p->srcdir && verbose)
55729453Scharnier	warnx("%s: %s: warning: could not find source directory",
5581722Sjkh		infilename, p->name);
5591722Sjkh    if(!p->objs && verbose)
56029453Scharnier	warnx("%s: %s: warning: could not find any .o files",
5611722Sjkh		infilename, p->name);
5621722Sjkh
5631722Sjkh    if(!p->objpaths) {
56429453Scharnier	warnx("%s: %s: error: no objpaths specified or calculated",
5651722Sjkh		infilename, p->name);
5661722Sjkh	p->goterror = goterror = 1;
5671722Sjkh    }
5681722Sjkh}
5691722Sjkh
5701722Sjkhvoid fillin_program_objs(prog_t *p, char *path)
5711722Sjkh{
5721722Sjkh    char *obj, *cp;
5731722Sjkh    int rc;
5741722Sjkh    FILE *f;
57556995Sluigi    char *objvar="OBJS";
57668569Sjoe    strlst_t *s;
5771722Sjkh
5781722Sjkh    /* discover the objs from the srcdir Makefile */
5791722Sjkh
5801722Sjkh    if((f = fopen(tempfname, "w")) == NULL) {
58129453Scharnier	warn("%s", tempfname);
5821722Sjkh	goterror = 1;
5831722Sjkh	return;
5841722Sjkh    }
58556995Sluigi    if (p->objvar)
58656995Sluigi	objvar = p->objvar ;
5878857Srgrimes
58856995Sluigi    /*
58956995Sluigi     * XXX include outhdrname (e.g. to contain Make variables)
59056995Sluigi     */
59156995Sluigi    if (outhdrname[0] != '\0')
59256995Sluigi	fprintf(f, ".include \"%s\"\n", outhdrname);
5931722Sjkh    fprintf(f, ".include \"%s\"\n", path);
59468750Sjoe    if (buildopts) {
59568750Sjoe        fprintf(f, "BUILDOPTS+=");
59668750Sjoe        output_strlst(f, buildopts);
59768750Sjoe    }
59856995Sluigi    fprintf(f, ".if defined(PROG) && !defined(%s)\n", objvar);
59956995Sluigi    fprintf(f, "%s=${PROG}.o\n", objvar);
6001722Sjkh    fprintf(f, ".endif\n");
60168285Sjoe    fprintf(f, "loop:\n\t@echo 'OBJS= '${%s}\n", objvar);
60268569Sjoe
60368750Sjoe    fprintf(f, "crunchgen_objs:\n\t@make -f %s $(BUILDOPTS) $(%s_OPTS)",
60468569Sjoe	tempfname, p->ident);
60568569Sjoe    for (s = p->buildopts; s != NULL; s = s->next)
60668569Sjoe        fprintf(f, " %s", s->str);
60768569Sjoe    fprintf(f, " loop\n");
60868569Sjoe
6091722Sjkh    fclose(f);
6101722Sjkh
6111722Sjkh    sprintf(line, "make -f %s crunchgen_objs 2>&1", tempfname);
6121722Sjkh    if((f = popen(line, "r")) == NULL) {
61329453Scharnier	warn("submake pipe");
6141722Sjkh	goterror = 1;
6151722Sjkh	return;
6161722Sjkh    }
6171722Sjkh
6181722Sjkh    while(fgets(line, MAXLINELEN, f)) {
6191722Sjkh	if(strncmp(line, "OBJS= ", 6)) {
62029453Scharnier	    warnx("make error: %s", line);
6218857Srgrimes	    goterror = 1;
6221722Sjkh	    continue;
6231722Sjkh	}
6241722Sjkh	cp = line + 6;
6251722Sjkh	while(isspace(*cp)) cp++;
6261722Sjkh	while(*cp) {
6271722Sjkh	    obj = cp;
6281722Sjkh	    while(*cp && !isspace(*cp)) cp++;
6291722Sjkh	    if(*cp) *cp++ = '\0';
6301722Sjkh	    add_string(&p->objs, obj);
6311722Sjkh	    while(isspace(*cp)) cp++;
6321722Sjkh	}
6331722Sjkh    }
6341722Sjkh    if((rc=pclose(f)) != 0) {
63529453Scharnier	warnx("make error: make returned %d", rc);
6361722Sjkh	goterror = 1;
6371722Sjkh    }
6381722Sjkh    unlink(tempfname);
6391722Sjkh}
6401722Sjkh
6411722Sjkhvoid remove_error_progs(void)
6421722Sjkh{
6431722Sjkh    prog_t *p1, *p2;
6441722Sjkh
6458857Srgrimes    p1 = NULL; p2 = progs;
6468857Srgrimes    while(p2 != NULL) {
6471722Sjkh	if(!p2->goterror)
6481722Sjkh	    p1 = p2, p2 = p2->next;
6491722Sjkh	else {
6501722Sjkh	    /* delete it from linked list */
65129453Scharnier	    warnx("%s: %s: ignoring program because of errors",
6521722Sjkh		    infilename, p2->name);
6531722Sjkh	    if(p1) p1->next = p2->next;
6541722Sjkh	    else progs = p2->next;
6551722Sjkh	    p2 = p2->next;
6561722Sjkh	}
6571722Sjkh    }
6581722Sjkh}
6591722Sjkh
6601722Sjkhvoid gen_specials_cache(void)
6611722Sjkh{
6621722Sjkh    FILE *cachef;
6631722Sjkh    prog_t *p;
6641722Sjkh
6651722Sjkh    sprintf(line, "generating %s", cachename);
6661722Sjkh    status(line);
6671722Sjkh
6681722Sjkh    if((cachef = fopen(cachename, "w")) == NULL) {
66929453Scharnier	warn("%s", cachename);
6701722Sjkh	goterror = 1;
6711722Sjkh	return;
6721722Sjkh    }
6731722Sjkh
6741722Sjkh    fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n",
6751722Sjkh	    cachename, infilename, CRUNCH_VERSION);
6761722Sjkh
6771722Sjkh    for(p = progs; p != NULL; p = p->next) {
6781722Sjkh	fprintf(cachef, "\n");
6791722Sjkh	if(p->srcdir)
6801722Sjkh	    fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir);
6811722Sjkh	if(p->objdir)
6821722Sjkh	    fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir);
6831722Sjkh	if(p->objs) {
6841722Sjkh	    fprintf(cachef, "special %s objs", p->name);
6851722Sjkh	    output_strlst(cachef, p->objs);
6861722Sjkh	}
6871722Sjkh	fprintf(cachef, "special %s objpaths", p->name);
6881722Sjkh	output_strlst(cachef, p->objpaths);
6891722Sjkh    }
6901722Sjkh    fclose(cachef);
6911722Sjkh}
6921722Sjkh
6931722Sjkh
6941722Sjkhvoid gen_output_makefile(void)
6951722Sjkh{
6961722Sjkh    prog_t *p;
6971722Sjkh    FILE *outmk;
6981722Sjkh
6991722Sjkh    sprintf(line, "generating %s", outmkname);
7001722Sjkh    status(line);
7011722Sjkh
7021722Sjkh    if((outmk = fopen(outmkname, "w")) == NULL) {
70329453Scharnier	warn("%s", outmkname);
7041722Sjkh	goterror = 1;
7051722Sjkh	return;
7061722Sjkh    }
7071722Sjkh
7081722Sjkh    fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n",
7091722Sjkh	    outmkname, infilename, CRUNCH_VERSION);
71056995Sluigi    if (outhdrname[0] != '\0')
71156995Sluigi	fprintf(outmk, ".include \"%s\"\n", outhdrname);
7121722Sjkh
7131722Sjkh    top_makefile_rules(outmk);
7141722Sjkh
7151722Sjkh    for(p = progs; p != NULL; p = p->next)
7168857Srgrimes	prog_makefile_rules(outmk, p);
7171722Sjkh
7181722Sjkh    fprintf(outmk, "\n# ========\n");
7191722Sjkh    fclose(outmk);
7201722Sjkh}
7211722Sjkh
7221722Sjkh
7231722Sjkhvoid gen_output_cfile(void)
7241722Sjkh{
7251722Sjkh    extern char *crunched_skel[];
7261722Sjkh    char **cp;
7271722Sjkh    FILE *outcf;
7281722Sjkh    prog_t *p;
7291722Sjkh    strlst_t *s;
7301722Sjkh
7311722Sjkh    sprintf(line, "generating %s", outcfname);
7321722Sjkh    status(line);
7331722Sjkh
7341722Sjkh    if((outcf = fopen(outcfname, "w")) == NULL) {
73529453Scharnier	warn("%s", outcfname);
7361722Sjkh	goterror = 1;
7371722Sjkh	return;
7381722Sjkh    }
7391722Sjkh
7408857Srgrimes    fprintf(outcf,
7411722Sjkh	  "/* %s - generated from %s by crunchgen %s */\n",
7421722Sjkh	    outcfname, infilename, CRUNCH_VERSION);
7431722Sjkh
7441722Sjkh    fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname);
7451722Sjkh    for(cp = crunched_skel; *cp != NULL; cp++)
7461722Sjkh	fprintf(outcf, "%s\n", *cp);
7471722Sjkh
7481722Sjkh    for(p = progs; p != NULL; p = p->next)
7491722Sjkh	fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident);
7501722Sjkh
7511722Sjkh    fprintf(outcf, "\nstruct stub entry_points[] = {\n");
7521722Sjkh    for(p = progs; p != NULL; p = p->next) {
7531722Sjkh	fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
7541722Sjkh		p->name, p->ident);
7551722Sjkh	for(s = p->links; s != NULL; s = s->next)
7561722Sjkh	    fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
7571722Sjkh		    s->str, p->ident);
7581722Sjkh    }
7598857Srgrimes
7601722Sjkh    fprintf(outcf, "\t{ EXECNAME, crunched_main },\n");
7611722Sjkh    fprintf(outcf, "\t{ NULL, NULL }\n};\n");
7621722Sjkh    fclose(outcf);
7631722Sjkh}
7641722Sjkh
7651722Sjkh
7661722Sjkhchar *genident(char *str)
7671722Sjkh{
7681722Sjkh    char *n,*s,*d;
7691722Sjkh
7701722Sjkh    /*
7711722Sjkh     * generates a Makefile/C identifier from a program name, mapping '-' to
7721722Sjkh     * '_' and ignoring all other non-identifier characters.  This leads to
7731722Sjkh     * programs named "foo.bar" and "foobar" to map to the same identifier.
7741722Sjkh     */
7751722Sjkh
7761722Sjkh    if((n = strdup(str)) == NULL)
7771722Sjkh	return NULL;
7781722Sjkh    for(d = s = n; *s != '\0'; s++) {
7791722Sjkh	if(*s == '-') *d++ = '_';
7801722Sjkh	else if(*s == '_' || isalnum(*s)) *d++ = *s;
7811722Sjkh    }
7821722Sjkh    *d = '\0';
7831722Sjkh    return n;
7841722Sjkh}
7851722Sjkh
7861722Sjkh
7871722Sjkhchar *dir_search(char *progname)
7881722Sjkh{
7891722Sjkh    char path[MAXPATHLEN];
7901722Sjkh    strlst_t *dir;
7911722Sjkh
7921722Sjkh    for(dir=srcdirs; dir != NULL; dir=dir->next) {
7931722Sjkh	sprintf(path, "%s/%s", dir->str, progname);
7941722Sjkh	if(is_dir(path)) return dir->str;
7951722Sjkh    }
7961722Sjkh    return NULL;
7971722Sjkh}
7981722Sjkh
7991722Sjkh
8001722Sjkhvoid top_makefile_rules(FILE *outmk)
8011722Sjkh{
8021722Sjkh    prog_t *p;
8031722Sjkh
8041722Sjkh    fprintf(outmk, "LIBS=");
8051722Sjkh    output_strlst(outmk, libs);
8061722Sjkh
80768750Sjoe    if (buildopts) {
80868750Sjoe	fprintf(outmk, "BUILDOPTS+=");
80968750Sjoe	output_strlst(outmk, buildopts);
81068750Sjoe    }
81168750Sjoe
8121722Sjkh    fprintf(outmk, "CRUNCHED_OBJS=");
8131722Sjkh    for(p = progs; p != NULL; p = p->next)
8141722Sjkh	fprintf(outmk, " %s.lo", p->name);
8151722Sjkh    fprintf(outmk, "\n");
8161722Sjkh
8171722Sjkh    fprintf(outmk, "SUBMAKE_TARGETS=");
8181722Sjkh    for(p = progs; p != NULL; p = p->next)
8191722Sjkh	fprintf(outmk, " %s_make", p->ident);
82032589Sbrian    fprintf(outmk, "\nSUBCLEAN_TARGETS=");
82132589Sbrian    for(p = progs; p != NULL; p = p->next)
82232589Sbrian	fprintf(outmk, " %s_clean", p->ident);
8231722Sjkh    fprintf(outmk, "\n\n");
8241722Sjkh
8258857Srgrimes    fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS)\n",
8261722Sjkh	    execfname, execfname);
8271722Sjkh    fprintf(outmk, "\t$(CC) -static -o %s %s.o $(CRUNCHED_OBJS) $(LIBS)\n",
8281722Sjkh	    execfname, execfname);
8291722Sjkh    fprintf(outmk, "\tstrip %s\n", execfname);
8301722Sjkh    fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n");
8311722Sjkh    fprintf(outmk, "exe: %s\n", execfname);
83232589Sbrian    fprintf(outmk, "realclean: clean subclean\n");
8331722Sjkh    fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n",
8341722Sjkh	    execfname);
83532589Sbrian    fprintf(outmk, "subclean: $(SUBCLEAN_TARGETS)\n");
8361722Sjkh}
8371722Sjkh
8381722Sjkh
8391722Sjkhvoid prog_makefile_rules(FILE *outmk, prog_t *p)
8401722Sjkh{
84130120Sjoerg    strlst_t *lst;
84230120Sjoerg
8431722Sjkh    fprintf(outmk, "\n# -------- %s\n\n", p->name);
8441722Sjkh
8451722Sjkh    if(p->srcdir && p->objs) {
8461722Sjkh	fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir);
8471722Sjkh	fprintf(outmk, "%s_OBJS=", p->ident);
8481722Sjkh	output_strlst(outmk, p->objs);
84968569Sjoe	if (p->buildopts != NULL) {
85068569Sjoe		fprintf(outmk, "%s_OPTS+=", p->ident);
85168569Sjoe		output_strlst(outmk, p->buildopts);
85268569Sjoe	}
8531722Sjkh	fprintf(outmk, "%s_make:\n", p->ident);
85468286Sjoe	fprintf(outmk, "\t(cd $(%s_SRCDIR) && ", p->ident);
85568286Sjoe	if (makeobj)
85668286Sjoe		fprintf(outmk, "make obj && ");
85768286Sjoe	fprintf(outmk, "\\\n");
85868750Sjoe	fprintf(outmk, "\t\tmake $(BUILDOPTS) $(%s_OPTS) depend && \\\n"
85968750Sjoe		"\t\tmake $(BUILDOPTS) $(%s_OPTS) $(%s_OBJS))\n",
86068286Sjoe		p->ident, p->ident, p->ident);
86132589Sbrian	fprintf(outmk, "%s_clean:\n", p->ident);
86232589Sbrian	fprintf(outmk, "\t(cd $(%s_SRCDIR) && make clean)\n\n", p->ident);
8631722Sjkh    }
8641722Sjkh    else
8658857Srgrimes	fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n",
8661722Sjkh		p->ident, p->name);
8671722Sjkh
8681722Sjkh    fprintf(outmk,   "%s_OBJPATHS=", p->ident);
8691722Sjkh    output_strlst(outmk, p->objpaths);
8701722Sjkh
8711722Sjkh    fprintf(outmk, "%s_stub.c:\n", p->name);
8721722Sjkh    fprintf(outmk, "\techo \""
8731722Sjkh	           "int _crunched_%s_stub(int argc, char **argv, char **envp)"
8741722Sjkh	           "{return main(argc,argv,envp);}\" >%s_stub.c\n",
8751722Sjkh	    p->ident, p->name);
8761722Sjkh    fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)\n",
8771722Sjkh	    p->name, p->name, p->ident);
8788857Srgrimes    fprintf(outmk, "\tld -dc -r -o %s.lo %s_stub.o $(%s_OBJPATHS)\n",
8791722Sjkh	    p->name, p->name, p->ident);
88039171Sjkh    fprintf(outmk, "\tcrunchide -k _crunched_%s_stub ", p->ident);
88130120Sjoerg    for(lst = p->keeplist; lst != NULL; lst = lst->next)
88230120Sjoerg      fprintf(outmk, "-k _%s ", lst->str);
88330120Sjoerg    fprintf(outmk, "%s.lo\n", p->name);
8841722Sjkh}
8851722Sjkh
8861722Sjkhvoid output_strlst(FILE *outf, strlst_t *lst)
8871722Sjkh{
8881722Sjkh    for(; lst != NULL; lst = lst->next)
8891722Sjkh	fprintf(outf, " %s", lst->str);
8901722Sjkh    fprintf(outf, "\n");
8911722Sjkh}
8921722Sjkh
8931722Sjkh
8941722Sjkh/*
8951722Sjkh * ========================================================================
8961722Sjkh * general library routines
8971722Sjkh *
8981722Sjkh */
8991722Sjkh
9001722Sjkhvoid status(char *str)
9011722Sjkh{
9021722Sjkh    static int lastlen = 0;
9031722Sjkh    int len, spaces;
9041722Sjkh
9051722Sjkh    if(!verbose) return;
9061722Sjkh
9071722Sjkh    len = strlen(str);
9081722Sjkh    spaces = lastlen - len;
9091722Sjkh    if(spaces < 1) spaces = 1;
9101722Sjkh
9111722Sjkh    fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " ");
9121722Sjkh    fflush(stderr);
9131722Sjkh    lastlen = len;
9141722Sjkh}
9151722Sjkh
9161722Sjkh
9171722Sjkhvoid out_of_memory(void)
9181722Sjkh{
91929453Scharnier    errx(1, "%s: %d: out of memory, stopping", infilename, linenum);
9201722Sjkh}
9211722Sjkh
9221722Sjkh
9231722Sjkhvoid add_string(strlst_t **listp, char *str)
9241722Sjkh{
9251722Sjkh    strlst_t *p1, *p2;
9261722Sjkh
9271722Sjkh    /* add to end, but be smart about dups */
9281722Sjkh
9291722Sjkh    for(p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next)
9301722Sjkh	if(!strcmp(p2->str, str)) return;
9311722Sjkh
9321722Sjkh    p2 = malloc(sizeof(strlst_t));
93319822Sjoerg    if(p2) {
93419822Sjoerg	memset(p2, 0, sizeof(strlst_t));
93519822Sjoerg	p2->str = strdup(str);
93619822Sjoerg    }
9371722Sjkh    if(!p2 || !p2->str)
9381722Sjkh	out_of_memory();
9391722Sjkh
9401722Sjkh    p2->next = NULL;
9411722Sjkh    if(p1 == NULL) *listp = p2;
9421722Sjkh    else p1->next = p2;
9431722Sjkh}
9441722Sjkh
9451722Sjkh
9461722Sjkhint is_dir(char *pathname)
9471722Sjkh{
9481722Sjkh    struct stat buf;
9491722Sjkh
9501722Sjkh    if(stat(pathname, &buf) == -1)
9511722Sjkh	return 0;
9521722Sjkh    return S_ISDIR(buf.st_mode);
9531722Sjkh}
9541722Sjkh
9551722Sjkhint is_nonempty_file(char *pathname)
9561722Sjkh{
9571722Sjkh    struct stat buf;
9581722Sjkh
9591722Sjkh    if(stat(pathname, &buf) == -1)
9601722Sjkh	return 0;
9611722Sjkh
9621722Sjkh    return S_ISREG(buf.st_mode) && buf.st_size > 0;
9631722Sjkh}
964