crunchgen.c revision 70880
154359Sroberto/*
254359Sroberto * Copyright (c) 1994 University of Maryland
354359Sroberto * All Rights Reserved.
4285612Sdelphij *
5182007Sroberto * Permission to use, copy, modify, distribute, and sell this software and its
6182007Sroberto * documentation for any purpose is hereby granted without fee, provided that
7182007Sroberto * the above copyright notice appear in all copies and that both that
8330567Sgordon * copyright notice and this permission notice appear in supporting
9330567Sgordon * documentation, and that the name of U.M. not be used in advertising or
10182007Sroberto * publicity pertaining to distribution of the software without specific,
11182007Sroberto * written prior permission.  U.M. makes no representations about the
12285612Sdelphij * suitability of this software for any purpose.  It is provided "as is"
13285612Sdelphij * without express or implied warranty.
14285612Sdelphij *
15285612Sdelphij * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
16285612Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
17285612Sdelphij * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18285612Sdelphij * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
19285612Sdelphij * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20285612Sdelphij * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21285612Sdelphij *
22285612Sdelphij * Author: James da Silva, Systems Design and Analysis Group
23182007Sroberto *			   Computer Science Department
2482498Sroberto *			   University of Maryland at College Park
25285612Sdelphij *
26285612Sdelphij * $FreeBSD: head/usr.sbin/crunch/crunchgen/crunchgen.c 70880 2001-01-10 13:27:38Z joe $
2782498Sroberto */
2882498Sroberto/*
2982498Sroberto * ========================================================================
30285612Sdelphij * crunchgen.c
31285612Sdelphij *
32285612Sdelphij * Generates a Makefile and main C file for a crunched executable,
33285612Sdelphij * from specs given in a .conf file.
34285612Sdelphij */
35285612Sdelphij#include <ctype.h>
36285612Sdelphij#include <err.h>
37285612Sdelphij#include <paths.h>
38330567Sgordon#include <stdio.h>
39330567Sgordon#include <stdlib.h>
40330567Sgordon#include <string.h>
41330567Sgordon#include <unistd.h>
42330567Sgordon
43330567Sgordon#include <sys/types.h>
44310419Sdelphij#include <sys/stat.h>
45330567Sgordon#include <sys/param.h>
46330567Sgordon
47285612Sdelphij#define CRUNCH_VERSION	"0.2"
48285612Sdelphij
4982498Sroberto#define MAXLINELEN	16384
50285612Sdelphij#define MAXFIELDS 	 2048
51293650Sglebius
52182007Sroberto
53285612Sdelphij/* internal representation of conf file: */
54182007Sroberto
55182007Sroberto/* simple lists of strings suffice for most parms */
5654359Sroberto
5754359Srobertotypedef struct strlst {
58182007Sroberto    struct strlst *next;
59182007Sroberto    char *str;
60182007Sroberto} strlst_t;
61182007Sroberto
62182007Sroberto/* progs have structure, each field can be set with "special" or calculated */
6354359Sroberto
6454359Srobertotypedef struct prog {
6554359Sroberto    struct prog *next;		/* link field */
6654359Sroberto    char *name;			/* program name */
6754359Sroberto    char *ident;		/* C identifier for the program name */
6854359Sroberto    char *srcdir;
6954359Sroberto    char *realsrcdir;
70285612Sdelphij    char *objdir;
71285612Sdelphij    char *objvar;		/* Makefile variable to replace OBJS */
72285612Sdelphij    strlst_t *objs, *objpaths;
73285612Sdelphij    strlst_t *buildopts;
74285612Sdelphij    strlst_t *keeplist;
75285612Sdelphij    strlst_t *links;
76285612Sdelphij    int goterror;
77285612Sdelphij} prog_t;
7854359Sroberto
79298770Sdelphij
80298770Sdelphij/* global state */
81298770Sdelphij
82298770Sdelphijstrlst_t *buildopts = NULL;
83298770Sdelphijstrlst_t *srcdirs = NULL;
84285612Sdelphijstrlst_t *libs    = NULL;
8554359Srobertoprog_t   *progs   = NULL;
86182007Sroberto
87182007Srobertochar confname[MAXPATHLEN], infilename[MAXPATHLEN];
88182007Srobertochar outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN];
89182007Srobertochar tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN];
90182007Srobertochar outhdrname[MAXPATHLEN] ; /* user-supplied header for *.mk */
9154359Srobertochar *objprefix;	/* where are the objects ? */
9254359Srobertoint linenum = -1;
93132451Srobertoint goterror = 0;
9454359Sroberto
95285612Sdelphijint verbose, readcache;	/* options */
96285612Sdelphijint reading_cache;
9754359Srobertoint makeobj = 0;	/* add 'make obj' rules to the makefile */
9854359Sroberto
9954359Srobertoint list_mode;
10054359Sroberto
10154359Sroberto/* general library routines */
10254359Sroberto
10354359Srobertovoid status(char *str);
10454359Srobertovoid out_of_memory(void);
10554359Srobertovoid add_string(strlst_t **listp, char *str);
10654359Srobertoint is_dir(char *pathname);
10754359Srobertoint is_nonempty_file(char *pathname);
10854359Sroberto
10954359Sroberto/* helper routines for main() */
11054359Sroberto
11154359Srobertovoid usage(void);
11254359Srobertovoid parse_conf_file(void);
11354359Srobertovoid gen_outputs(void);
11454359Sroberto
11554359Sroberto
11654359Srobertoint main(int argc, char **argv)
11754359Sroberto{
11854359Sroberto    char *p;
11954359Sroberto    int optc;
12054359Sroberto
12154359Sroberto    verbose = 1;
12254359Sroberto    readcache = 1;
123285612Sdelphij    *outmkname = *outcfname = *execfname = '\0';
124285612Sdelphij
125285612Sdelphij    p = getenv("MAKEOBJDIRPREFIX");
126285612Sdelphij    if (p == NULL || *p == '\0')
127285612Sdelphij	objprefix = "/usr/obj"; /* default */
128285612Sdelphij    else
129285612Sdelphij	if ((objprefix = strdup(p)) == NULL)
130285612Sdelphij	    out_of_memory();
13154359Sroberto
13254359Sroberto    while((optc = getopt(argc, argv, "lh:m:c:e:p:foq")) != -1) {
13354359Sroberto	switch(optc) {
134285612Sdelphij	case 'f':	readcache = 0; break;
135285612Sdelphij	case 'o':	makeobj = 1; break;
136285612Sdelphij	case 'q':	verbose = 0; break;
13754359Sroberto
138285612Sdelphij	case 'm':	strlcpy(outmkname, optarg, sizeof(outmkname)); break;
139285612Sdelphij	case 'p':	if ((objprefix = strdup(optarg)) == NULL)
140285612Sdelphij				out_of_memory();
141285612Sdelphij			break;
142285612Sdelphij	case 'h':	strlcpy(outhdrname, optarg, sizeof(outhdrname)); break;
143285612Sdelphij	case 'c':	strlcpy(outcfname, optarg, sizeof(outcfname)); break;
144285612Sdelphij	case 'e':	strlcpy(execfname, optarg, sizeof(execfname)); break;
145285612Sdelphij	case 'l':	list_mode++; verbose = 0; break;
146285612Sdelphij
147285612Sdelphij	case '?':
148285612Sdelphij	default:	usage();
149285612Sdelphij	}
150285612Sdelphij    }
151285612Sdelphij
152285612Sdelphij    argc -= optind;
153285612Sdelphij    argv += optind;
154285612Sdelphij
15554359Sroberto    if(argc != 1) usage();
15654359Sroberto
15754359Sroberto    /*
15854359Sroberto     * generate filenames
15954359Sroberto     */
16054359Sroberto
16154359Sroberto    strlcpy(infilename, argv[0], sizeof(infilename));
16254359Sroberto
163182007Sroberto    /* confname = `basename infilename .conf` */
164182007Sroberto
165285612Sdelphij    if((p=strrchr(infilename, '/')) != NULL)
166182007Sroberto	strlcpy(confname, p+1, sizeof(confname));
167182007Sroberto    else
168285612Sdelphij        strlcpy(confname, infilename, sizeof(confname));
169285612Sdelphij    if((p=strrchr(confname, '.')) != NULL && !strcmp(p, ".conf")) *p = '\0';
170182007Sroberto
171182007Sroberto    if(!*outmkname) snprintf(outmkname, sizeof(outmkname), "%s.mk", confname);
172182007Sroberto    if(!*outcfname) snprintf(outcfname, sizeof(outcfname), "%s.c", confname);
173182007Sroberto    if(!*execfname) snprintf(execfname, sizeof(execfname), "%s", confname);
174182007Sroberto
175285612Sdelphij    snprintf(cachename, sizeof(cachename), "%s.cache", confname);
17654359Sroberto    snprintf(tempfname, sizeof(tempfname), "%s/crunchgen_%sXXXXXX",
17754359Sroberto	getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP, confname);
17854359Sroberto
179285612Sdelphij    parse_conf_file();
18054359Sroberto    if (list_mode)
18154359Sroberto	exit(goterror);
18254359Sroberto
183285612Sdelphij    gen_outputs();
184285612Sdelphij
185285612Sdelphij    exit(goterror);
186293650Sglebius}
187293650Sglebius
188285612Sdelphij
189285612Sdelphijvoid usage(void)
190285612Sdelphij{
191285612Sdelphij    fprintf(stderr, "%s\n%s\n",
192293650Sglebius	"usage: crunchgen [-foq] [-h <makefile-header-name>] [-m <makefile>]",
193285612Sdelphij	"	[-p <obj-prefix>] [-c <c-file-name>] [-e <exec-file>] <conffile>");
194285612Sdelphij    exit(1);
195285612Sdelphij}
196285612Sdelphij
197285612Sdelphij
198285612Sdelphij/*
199285612Sdelphij * ========================================================================
200285612Sdelphij * parse_conf_file subsystem
201330567Sgordon *
202285612Sdelphij */
203285612Sdelphij
204285612Sdelphij/* helper routines for parse_conf_file */
205285612Sdelphij
206285612Sdelphijvoid parse_one_file(char *filename);
207285612Sdelphijvoid parse_line(char *line, int *fc, char **fv, int nf);
208285612Sdelphijvoid add_srcdirs(int argc, char **argv);
209285612Sdelphijvoid add_progs(int argc, char **argv);
210285612Sdelphijvoid add_link(int argc, char **argv);
211285612Sdelphijvoid add_libs(int argc, char **argv);
212285612Sdelphijvoid add_buildopts(int argc, char **argv);
213285612Sdelphijvoid add_special(int argc, char **argv);
214285612Sdelphij
215298770Sdelphijprog_t *find_prog(char *str);
216285612Sdelphijvoid add_prog(char *progname);
217285612Sdelphij
218285612Sdelphij
219285612Sdelphijvoid parse_conf_file(void)
220285612Sdelphij{
221285612Sdelphij    if(!is_nonempty_file(infilename))
222285612Sdelphij		errx(1, "fatal: input file \"%s\" not found", infilename);
223285612Sdelphij    parse_one_file(infilename);
224285612Sdelphij    if(readcache && is_nonempty_file(cachename)) {
225285612Sdelphij	reading_cache = 1;
226285612Sdelphij	parse_one_file(cachename);
227293650Sglebius    }
228293650Sglebius}
229285612Sdelphij
230285612Sdelphij
231285612Sdelphijvoid parse_one_file(char *filename)
232285612Sdelphij{
233285612Sdelphij    char *fieldv[MAXFIELDS];
234293650Sglebius    int fieldc;
235285612Sdelphij    void (*f)(int c, char **v);
236294569Sdelphij    FILE *cf;
237285612Sdelphij    char line[MAXLINELEN];
238285612Sdelphij
239330567Sgordon    snprintf(line, sizeof(line), "reading %s", filename);
240330567Sgordon    status(line);
241330567Sgordon    strlcpy(curfilename, filename, sizeof(curfilename));
242330567Sgordon
243330567Sgordon    if((cf = fopen(curfilename, "r")) == NULL) {
244330567Sgordon	warn("%s", curfilename);
245330567Sgordon	goterror = 1;
246330567Sgordon	return;
247330567Sgordon    }
248330567Sgordon
249285612Sdelphij    linenum = 0;
250285612Sdelphij    while(fgets(line, MAXLINELEN, cf) != NULL) {
251285612Sdelphij	linenum++;
252285612Sdelphij	parse_line(line, &fieldc, fieldv, MAXFIELDS);
253285612Sdelphij	if(fieldc < 1) continue;
25454359Sroberto	if(!strcmp(fieldv[0], "srcdirs"))	f = add_srcdirs;
255330567Sgordon	else if(!strcmp(fieldv[0], "progs"))    f = add_progs;
256285612Sdelphij	else if(!strcmp(fieldv[0], "ln"))	f = add_link;
25754359Sroberto	else if(!strcmp(fieldv[0], "libs"))	f = add_libs;
25854359Sroberto	else if(!strcmp(fieldv[0], "buildopts")) f = add_buildopts;
25954359Sroberto	else if(!strcmp(fieldv[0], "special"))	f = add_special;
26054359Sroberto	else {
26154359Sroberto	    warnx("%s:%d: skipping unknown command `%s'",
262182007Sroberto		    curfilename, linenum, fieldv[0]);
26354359Sroberto	    goterror = 1;
26454359Sroberto	    continue;
265182007Sroberto	}
26654359Sroberto	if(fieldc < 2) {
26754359Sroberto	    warnx("%s:%d: %s command needs at least 1 argument, skipping",
268182007Sroberto		    curfilename, linenum, fieldv[0]);
26954359Sroberto	    goterror = 1;
27054359Sroberto	    continue;
271182007Sroberto	}
27254359Sroberto	f(fieldc, fieldv);
27354359Sroberto    }
274182007Sroberto
275132451Sroberto    if(ferror(cf)) {
27654359Sroberto	warn("%s", curfilename);
277182007Sroberto	goterror = 1;
27854359Sroberto    }
27954359Sroberto    fclose(cf);
280285612Sdelphij}
28154359Sroberto
28254359Sroberto
283182007Srobertovoid parse_line(char *line, int *fc, char **fv, int nf)
28454359Sroberto{
28554359Sroberto    char *p;
286182007Sroberto
28754359Sroberto    p = line;
28854359Sroberto    *fc = 0;
28954359Sroberto    while(1) {
29054359Sroberto	while(isspace(*p)) p++;
29154359Sroberto	if(*p == '\0' || *p == '#') break;
29254359Sroberto
29354359Sroberto	if(*fc < nf) fv[(*fc)++] = p;
29454359Sroberto	while(*p && !isspace(*p) && *p != '#') p++;
295182007Sroberto	if(*p == '\0' || *p == '#') break;
29654359Sroberto	*p++ = '\0';
29754359Sroberto    }
298298770Sdelphij    if(*p) *p = '\0';		/* needed for '#' case */
299298770Sdelphij}
300298770Sdelphij
30154359Sroberto
30254359Srobertovoid add_srcdirs(int argc, char **argv)
30354359Sroberto{
30454359Sroberto    int i;
30554359Sroberto
30654359Sroberto    for(i=1;i<argc;i++) {
30754359Sroberto	if(is_dir(argv[i]))
30854359Sroberto	    add_string(&srcdirs, argv[i]);
30954359Sroberto	else {
310182007Sroberto	    warnx("%s:%d: `%s' is not a directory, skipping it",
31154359Sroberto		    curfilename, linenum, argv[i]);
31254359Sroberto	    goterror = 1;
313182007Sroberto	}
31454359Sroberto    }
31554359Sroberto}
316182007Sroberto
317285612Sdelphij
318285612Sdelphijvoid add_progs(int argc, char **argv)
31954359Sroberto{
32054359Sroberto    int i;
32154359Sroberto
32254359Sroberto    for(i=1;i<argc;i++)
32354359Sroberto	add_prog(argv[i]);
32454359Sroberto}
32554359Sroberto
32654359Sroberto
327285612Sdelphijvoid add_prog(char *progname)
328285612Sdelphij{
329285612Sdelphij    prog_t *p1, *p2;
330285612Sdelphij
331285612Sdelphij    /* add to end, but be smart about dups */
332285612Sdelphij
333285612Sdelphij    for(p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next)
334285612Sdelphij	if(!strcmp(p2->name, progname)) return;
335285612Sdelphij
336285612Sdelphij    p2 = malloc(sizeof(prog_t));
337285612Sdelphij    if(p2) {
338285612Sdelphij	memset(p2, 0, sizeof(prog_t));
339285612Sdelphij	p2->name = strdup(progname);
34054359Sroberto    }
34154359Sroberto    if(!p2 || !p2->name)
34254359Sroberto	out_of_memory();
34354359Sroberto
34454359Sroberto    p2->next = NULL;
34554359Sroberto    if(p1 == NULL) progs = p2;
34654359Sroberto    else p1->next = p2;
347285612Sdelphij
34854359Sroberto    p2->ident = p2->srcdir = p2->realsrcdir = p2->objdir = NULL;
349285612Sdelphij    p2->links = p2->objs = p2->keeplist = NULL;
35054359Sroberto    p2->buildopts = NULL;
35154359Sroberto    p2->goterror = 0;
35254359Sroberto    if (list_mode)
35354359Sroberto        printf("%s\n",progname);
354285612Sdelphij}
355285612Sdelphij
35654359Sroberto
35754359Srobertovoid add_link(int argc, char **argv)
358285612Sdelphij{
359285612Sdelphij    int i;
36054359Sroberto    prog_t *p = find_prog(argv[1]);
361285612Sdelphij
36254359Sroberto    if(p == NULL) {
363132451Sroberto	warnx("%s:%d: no prog %s previously declared, skipping link",
364132451Sroberto		curfilename, linenum, argv[1]);
365132451Sroberto	goterror = 1;
36654359Sroberto	return;
367132451Sroberto    }
36854359Sroberto    for(i=2;i<argc;i++) {
36954359Sroberto	if (list_mode)
37054359Sroberto		printf("%s\n",argv[i]);
37154359Sroberto	add_string(&p->links, argv[i]);
37254359Sroberto    }
37354359Sroberto}
37454359Sroberto
37554359Sroberto
37654359Srobertovoid add_libs(int argc, char **argv)
37754359Sroberto{
37854359Sroberto    int i;
37954359Sroberto
38054359Sroberto    for(i=1;i<argc;i++)
38154359Sroberto	add_string(&libs, argv[i]);
38254359Sroberto}
38354359Sroberto
38454359Sroberto
385285612Sdelphijvoid add_buildopts(int argc, char **argv)
386285612Sdelphij{
387285612Sdelphij    int i;
388285612Sdelphij
38954359Sroberto    for (i = 1; i < argc; i++)
390285612Sdelphij	add_string(&buildopts, argv[i]);
391285612Sdelphij}
392285612Sdelphij
39354359Sroberto
39454359Srobertovoid add_special(int argc, char **argv)
39554359Sroberto{
39654359Sroberto    int i;
397316722Sdelphij    prog_t *p = find_prog(argv[1]);
39854359Sroberto
39954359Sroberto    if(p == NULL) {
40054359Sroberto	if(reading_cache) return;
40154359Sroberto	warnx("%s:%d: no prog %s previously declared, skipping special",
40254359Sroberto		curfilename, linenum, argv[1]);
40354359Sroberto	goterror = 1;
40454359Sroberto	return;
405285612Sdelphij    }
40654359Sroberto
407285612Sdelphij    if(!strcmp(argv[2], "ident")) {
408285612Sdelphij	if(argc != 4) goto argcount;
409285612Sdelphij	if((p->ident = strdup(argv[3])) == NULL)
410285612Sdelphij	    out_of_memory();
411285612Sdelphij    }
412285612Sdelphij    else if(!strcmp(argv[2], "srcdir")) {
413285612Sdelphij	if(argc != 4) goto argcount;
414285612Sdelphij	if((p->srcdir = strdup(argv[3])) == NULL)
415285612Sdelphij	    out_of_memory();
416285612Sdelphij    }
41754359Sroberto    else if(!strcmp(argv[2], "objdir")) {
41854359Sroberto	if(argc != 4) goto argcount;
41954359Sroberto	if((p->objdir = strdup(argv[3])) == NULL)
42054359Sroberto	    out_of_memory();
42154359Sroberto    }
42254359Sroberto    else if(!strcmp(argv[2], "objs")) {
42354359Sroberto	p->objs = NULL;
42454359Sroberto	for(i=3;i<argc;i++)
42554359Sroberto	    add_string(&p->objs, argv[i]);
42654359Sroberto    }
42754359Sroberto    else if(!strcmp(argv[2], "objpaths")) {
42854359Sroberto	p->objpaths = NULL;
42954359Sroberto	for(i=3;i<argc;i++)
43054359Sroberto	    add_string(&p->objpaths, argv[i]);
43154359Sroberto    }
43254359Sroberto    else if(!strcmp(argv[2], "keep")) {
43354359Sroberto	p->keeplist = NULL;
43454359Sroberto	for(i=3;i<argc;i++)
43554359Sroberto	    add_string(&p->keeplist, argv[i]);
43654359Sroberto    }
43754359Sroberto    else if(!strcmp(argv[2], "objvar")) {
43854359Sroberto	if(argc != 4)
439289997Sglebius	    goto argcount;
44054359Sroberto	if((p->objvar = strdup(argv[3])) == NULL)
44154359Sroberto	    out_of_memory();
442285612Sdelphij    }
44354359Sroberto    else if (!strcmp(argv[2], "buildopts")) {
44454359Sroberto	p->buildopts = NULL;
44554359Sroberto	for (i = 3; i < argc; i++)
44654359Sroberto		add_string(&p->buildopts, argv[i]);
447285612Sdelphij    }
448285612Sdelphij    else {
449285612Sdelphij	warnx("%s:%d: bad parameter name `%s', skipping line",
450285612Sdelphij		curfilename, linenum, argv[2]);
451285612Sdelphij	goterror = 1;
452285612Sdelphij    }
453285612Sdelphij    return;
454285612Sdelphij
45554359Sroberto
456285612Sdelphij argcount:
457285612Sdelphij    warnx("%s:%d: too %s arguments, expected \"special %s %s <string>\"",
45854359Sroberto	    curfilename, linenum, argc < 4? "few" : "many", argv[1], argv[2]);
45954359Sroberto    goterror = 1;
46054359Sroberto}
46154359Sroberto
46254359Sroberto
46354359Srobertoprog_t *find_prog(char *str)
46454359Sroberto{
46554359Sroberto    prog_t *p;
46654359Sroberto
46754359Sroberto    for(p = progs; p != NULL; p = p->next)
46854359Sroberto	if(!strcmp(p->name, str)) return p;
46954359Sroberto
47054359Sroberto    return NULL;
47154359Sroberto}
47254359Sroberto
473330567Sgordon
474285612Sdelphij/*
47554359Sroberto * ========================================================================
47654359Sroberto * gen_outputs subsystem
47754359Sroberto *
47854359Sroberto */
47954359Sroberto
48054359Sroberto/* helper subroutines */
481285612Sdelphij
482316722Sdelphijvoid remove_error_progs(void);
48354359Srobertovoid fillin_program(prog_t *p);
484285612Sdelphijvoid gen_specials_cache(void);
485182007Srobertovoid gen_output_makefile(void);
486182007Srobertovoid gen_output_cfile(void);
487182007Sroberto
48854359Srobertovoid fillin_program_objs(prog_t *p, char *path);
489182007Srobertovoid top_makefile_rules(FILE *outmk);
49054359Srobertovoid prog_makefile_rules(FILE *outmk, prog_t *p);
49154359Srobertovoid output_strlst(FILE *outf, strlst_t *lst);
49254359Srobertochar *genident(char *str);
493285612Sdelphijchar *dir_search(char *progname);
494285612Sdelphij
495285612Sdelphij
496285612Sdelphijvoid gen_outputs(void)
497285612Sdelphij{
498285612Sdelphij    prog_t *p;
499285612Sdelphij
500285612Sdelphij    for(p = progs; p != NULL; p = p->next)
501285612Sdelphij	fillin_program(p);
502285612Sdelphij
503132451Sroberto    remove_error_progs();
504285612Sdelphij    gen_specials_cache();
505294569Sdelphij    gen_output_cfile();
506132451Sroberto    gen_output_makefile();
507285612Sdelphij    status("");
508330567Sgordon    fprintf(stderr,
509330567Sgordon	    "Run \"make -f %s\" to build crunched binary.\n", outmkname);
510330567Sgordon}
511285612Sdelphij
512330567Sgordon/*
513285612Sdelphij * run the makefile for the program to find which objects are necessary
514285612Sdelphij */
515285612Sdelphijvoid fillin_program(prog_t *p)
516285612Sdelphij{
517330567Sgordon    char path[MAXPATHLEN];
518285612Sdelphij    char line[MAXLINELEN];
519285612Sdelphij    FILE *f;
520285612Sdelphij
521294569Sdelphij    snprintf(line, MAXLINELEN, "filling in parms for %s", p->name);
522294569Sdelphij    status(line);
523294569Sdelphij
524285612Sdelphij    if(!p->ident)
525285612Sdelphij	p->ident = genident(p->name);
526294569Sdelphij
527294569Sdelphij    /* look for the source directory if one wasn't specified by a special */
528294569Sdelphij    if(!p->srcdir) {
529285612Sdelphij	p->srcdir = dir_search(p->name);
530285612Sdelphij    }
531285612Sdelphij
532132451Sroberto    /* Determine the actual srcdir (maybe symlinked). */
533132451Sroberto    if (p->srcdir) {
53454359Sroberto	snprintf(line, MAXLINELEN, "cd %s && echo -n `/bin/pwd`", p->srcdir);
535182007Sroberto	f = popen(line,"r");
536182007Sroberto	if (!f)
537285612Sdelphij	    errx(1, "Can't execute: %s\n", line);
538182007Sroberto
539182007Sroberto	path[0] = '\0';
540182007Sroberto	fgets(path, sizeof path, f);
541182007Sroberto	if (pclose(f))
542285612Sdelphij	    errx(1, "Can't execute: %s\n", line);
543285612Sdelphij
544285612Sdelphij	if (!*path)
545285612Sdelphij	    errx(1, "Can't perform pwd on: %s\n", p->srcdir);
546285612Sdelphij
547285612Sdelphij	p->realsrcdir = strdup(path);
548285612Sdelphij    }
549285612Sdelphij
550182007Sroberto    /* Unless the option to make object files was specified the
551285612Sdelphij     * the objects will be built in the source directory unless
552182007Sroberto     * an object directory already exists.
553285612Sdelphij     */
554182007Sroberto    if(!makeobj && !p->objdir && p->srcdir) {
555182007Sroberto	snprintf(line, sizeof line, "%s/%s", objprefix, p->realsrcdir);
556285612Sdelphij	if (is_dir(line)) {
557182007Sroberto	    if ((p->objdir = strdup(line)) == NULL)
558182007Sroberto		out_of_memory();
559285612Sdelphij	} else
560182007Sroberto	    p->objdir = p->realsrcdir;
561182007Sroberto    }
562285612Sdelphij/*
563285612Sdelphij * XXX look for a Makefile.{name} in local directory first.
564182007Sroberto * This lets us override the original Makefile.
565285612Sdelphij */
566285612Sdelphij    snprintf(path, sizeof(path), "Makefile.%s", p->name);
567298770Sdelphij    if (is_nonempty_file(path)) {
568298770Sdelphij       snprintf(line, MAXLINELEN, "Using %s for %s", path, p->name);
569285612Sdelphij       status(line);
57054359Sroberto    } else
57154359Sroberto    if(p->srcdir) snprintf(path, sizeof(path), "%s/Makefile", p->srcdir);
572285612Sdelphij    if(!p->objs && p->srcdir && is_nonempty_file(path))
573285612Sdelphij	fillin_program_objs(p, path);
574285612Sdelphij
575285612Sdelphij    if(!p->srcdir && verbose)
576285612Sdelphij	warnx("%s: %s: warning: could not find source directory",
577285612Sdelphij		infilename, p->name);
578285612Sdelphij    if(!p->objs && verbose)
579285612Sdelphij	warnx("%s: %s: warning: could not find any .o files",
580285612Sdelphij		infilename, p->name);
581285612Sdelphij
582285612Sdelphij    if(!p->srcdir || !p->objs)
583285612Sdelphij	p->goterror = 1;
584285612Sdelphij}
585285612Sdelphij
586285612Sdelphijvoid fillin_program_objs(prog_t *p, char *path)
587285612Sdelphij{
588285612Sdelphij    char *obj, *cp;
589285612Sdelphij    int fd, rc;
590285612Sdelphij    FILE *f;
591285612Sdelphij    char *objvar="OBJS";
59254359Sroberto    strlst_t *s;
59354359Sroberto    char line[MAXLINELEN];
59454359Sroberto
59554359Sroberto    /* discover the objs from the srcdir Makefile */
59654359Sroberto
59754359Sroberto    if((fd = mkstemp(tempfname)) == -1) {
59854359Sroberto	perror(tempfname);
599293650Sglebius	exit(1);
60054359Sroberto    }
60154359Sroberto    if((f = fdopen(fd, "w")) == NULL) {
602293650Sglebius	warn("%s", tempfname);
60354359Sroberto	goterror = 1;
60454359Sroberto	return;
60554359Sroberto    }
606285612Sdelphij    if (p->objvar)
60754359Sroberto	objvar = p->objvar ;
60854359Sroberto
60954359Sroberto    /*
610330567Sgordon     * XXX include outhdrname (e.g. to contain Make variables)
611330567Sgordon     */
612330567Sgordon    if (outhdrname[0] != '\0')
613330567Sgordon	fprintf(f, ".include \"%s\"\n", outhdrname);
614330567Sgordon    fprintf(f, ".include \"%s\"\n", path);
615330567Sgordon    if (buildopts) {
616285612Sdelphij        fprintf(f, "BUILDOPTS+=");
617330567Sgordon        output_strlst(f, buildopts);
618330567Sgordon    }
61954359Sroberto    fprintf(f, ".if defined(PROG) && !defined(%s)\n", objvar);
62054359Sroberto    fprintf(f, "%s=${PROG}.o\n", objvar);
62154359Sroberto    fprintf(f, ".endif\n");
62254359Sroberto    fprintf(f, "loop:\n\t@echo 'OBJS= '${%s}\n", objvar);
62354359Sroberto
62454359Sroberto    fprintf(f, "crunchgen_objs:\n\t@make -f %s $(BUILDOPTS) $(%s_OPTS)",
62554359Sroberto	tempfname, p->ident);
626285612Sdelphij    for (s = p->buildopts; s != NULL; s = s->next)
62754359Sroberto        fprintf(f, " %s", s->str);
62854359Sroberto    fprintf(f, " loop\n");
62954359Sroberto
63054359Sroberto    fclose(f);
631285612Sdelphij
63254359Sroberto    snprintf(line, MAXLINELEN, "make -f %s crunchgen_objs 2>&1", tempfname);
633285612Sdelphij    if((f = popen(line, "r")) == NULL) {
634285612Sdelphij	warn("submake pipe");
63554359Sroberto	goterror = 1;
63654359Sroberto	return;
637285612Sdelphij    }
63854359Sroberto
639132451Sroberto    while(fgets(line, MAXLINELEN, f)) {
640285612Sdelphij	if(strncmp(line, "OBJS= ", 6)) {
641285612Sdelphij	    warnx("make error: %s", line);
642285612Sdelphij	    goterror = 1;
643132451Sroberto	    continue;
644132451Sroberto	}
64554359Sroberto	cp = line + 6;
646132451Sroberto	while(isspace(*cp)) cp++;
647132451Sroberto	while(*cp) {
648132451Sroberto	    obj = cp;
649285612Sdelphij	    while(*cp && !isspace(*cp)) cp++;
650132451Sroberto	    if(*cp) *cp++ = '\0';
651285612Sdelphij	    add_string(&p->objs, obj);
652285612Sdelphij	    while(isspace(*cp)) cp++;
653132451Sroberto	}
654285612Sdelphij    }
655132451Sroberto    if((rc=pclose(f)) != 0) {
656285612Sdelphij	warnx("make error: make returned %d", rc);
657285612Sdelphij	goterror = 1;
658285612Sdelphij    }
659285612Sdelphij    unlink(tempfname);
660285612Sdelphij}
661285612Sdelphij
66254359Srobertovoid remove_error_progs(void)
66354359Sroberto{
664132451Sroberto    prog_t *p1, *p2;
665132451Sroberto
666132451Sroberto    p1 = NULL; p2 = progs;
667132451Sroberto    while(p2 != NULL) {
668132451Sroberto	if(!p2->goterror)
669132451Sroberto	    p1 = p2, p2 = p2->next;
670132451Sroberto	else {
671285612Sdelphij	    /* delete it from linked list */
672285612Sdelphij	    warnx("%s: %s: ignoring program because of errors",
673132451Sroberto		    infilename, p2->name);
674132451Sroberto	    if(p1) p1->next = p2->next;
675285612Sdelphij	    else progs = p2->next;
676285612Sdelphij	    p2 = p2->next;
677132451Sroberto	}
678285612Sdelphij    }
679182007Sroberto}
680182007Sroberto
681182007Srobertovoid gen_specials_cache(void)
682182007Sroberto{
683182007Sroberto    FILE *cachef;
684132451Sroberto    prog_t *p;
685132451Sroberto    char line[MAXLINELEN];
686132451Sroberto
687132451Sroberto    snprintf(line, MAXLINELEN, "generating %s", cachename);
688285612Sdelphij    status(line);
689132451Sroberto
690285612Sdelphij    if((cachef = fopen(cachename, "w")) == NULL) {
691132451Sroberto	warn("%s", cachename);
692132451Sroberto	goterror = 1;
693285612Sdelphij	return;
694285612Sdelphij    }
695132451Sroberto
696285612Sdelphij    fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n",
697132451Sroberto	    cachename, infilename, CRUNCH_VERSION);
698285612Sdelphij
699132451Sroberto    for(p = progs; p != NULL; p = p->next) {
700132451Sroberto	fprintf(cachef, "\n");
701132451Sroberto	if(p->srcdir)
702285612Sdelphij	    fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir);
703285612Sdelphij	if(p->objdir)
704285612Sdelphij	    fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir);
705285612Sdelphij	if(p->objs) {
706285612Sdelphij	    fprintf(cachef, "special %s objs", p->name);
707132451Sroberto	    output_strlst(cachef, p->objs);
708285612Sdelphij	}
709285612Sdelphij	if(p->objpaths) {
710132451Sroberto	    fprintf(cachef, "special %s objpaths", p->name);
711285612Sdelphij	    output_strlst(cachef, p->objpaths);
712285612Sdelphij	}
713132451Sroberto    }
714132451Sroberto    fclose(cachef);
71554359Sroberto}
716285612Sdelphij
717285612Sdelphij
718285612Sdelphijvoid gen_output_makefile(void)
719285612Sdelphij{
720285612Sdelphij    prog_t *p;
721285612Sdelphij    FILE *outmk;
722285612Sdelphij    char line[MAXLINELEN];
723285612Sdelphij
72454359Sroberto    snprintf(line, MAXLINELEN, "generating %s", outmkname);
72554359Sroberto    status(line);
72654359Sroberto
727285612Sdelphij    if((outmk = fopen(outmkname, "w")) == NULL) {
728285612Sdelphij	warn("%s", outmkname);
72954359Sroberto	goterror = 1;
73054359Sroberto	return;
731285612Sdelphij    }
73254359Sroberto
733132451Sroberto    fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n",
734285612Sdelphij	    outmkname, infilename, CRUNCH_VERSION);
735132451Sroberto    if (outhdrname[0] != '\0')
736132451Sroberto	fprintf(outmk, ".include \"%s\"\n", outhdrname);
737132451Sroberto
738132451Sroberto    top_makefile_rules(outmk);
739132451Sroberto    for(p = progs; p != NULL; p = p->next)
740132451Sroberto	prog_makefile_rules(outmk, p);
741132451Sroberto
742132451Sroberto    fprintf(outmk, "\n# ========\n");
743132451Sroberto    fclose(outmk);
74454359Sroberto}
74554359Sroberto
74654359Sroberto
74754359Srobertovoid gen_output_cfile(void)
74854359Sroberto{
749182007Sroberto    extern char *crunched_skel[];
750285612Sdelphij    char **cp;
751330567Sgordon    FILE *outcf;
752285612Sdelphij    prog_t *p;
753285612Sdelphij    strlst_t *s;
754285612Sdelphij    char line[MAXLINELEN];
755285612Sdelphij
756285612Sdelphij    snprintf(line, MAXLINELEN, "generating %s", outcfname);
75754359Sroberto    status(line);
75854359Sroberto
75954359Sroberto    if((outcf = fopen(outcfname, "w")) == NULL) {
760132451Sroberto	warn("%s", outcfname);
76154359Sroberto	goterror = 1;
762285612Sdelphij	return;
763285612Sdelphij    }
76454359Sroberto
765285612Sdelphij    fprintf(outcf,
766285612Sdelphij	  "/* %s - generated from %s by crunchgen %s */\n",
767285612Sdelphij	    outcfname, infilename, CRUNCH_VERSION);
76854359Sroberto
76954359Sroberto    fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname);
770285612Sdelphij    for(cp = crunched_skel; *cp != NULL; cp++)
77154359Sroberto	fprintf(outcf, "%s\n", *cp);
77254359Sroberto
77354359Sroberto    for(p = progs; p != NULL; p = p->next)
77454359Sroberto	fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident);
775330567Sgordon
776285612Sdelphij    fprintf(outcf, "\nstruct stub entry_points[] = {\n");
77754359Sroberto    for(p = progs; p != NULL; p = p->next) {
77854359Sroberto	fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
77954359Sroberto		p->name, p->ident);
78054359Sroberto	for(s = p->links; s != NULL; s = s->next)
781285612Sdelphij	    fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
782132451Sroberto		    s->str, p->ident);
783285612Sdelphij    }
78454359Sroberto
785132451Sroberto    fprintf(outcf, "\t{ EXECNAME, crunched_main },\n");
786285612Sdelphij    fprintf(outcf, "\t{ NULL, NULL }\n};\n");
787293650Sglebius    fclose(outcf);
788132451Sroberto}
789293650Sglebius
790285612Sdelphij
791132451Srobertochar *genident(char *str)
792285612Sdelphij{
793285612Sdelphij    char *n,*s,*d;
794285612Sdelphij
79554359Sroberto    /*
796285612Sdelphij     * generates a Makefile/C identifier from a program name, mapping '-' to
797285612Sdelphij     * '_' and ignoring all other non-identifier characters.  This leads to
79854359Sroberto     * programs named "foo.bar" and "foobar" to map to the same identifier.
79954359Sroberto     */
80054359Sroberto
80154359Sroberto    if((n = strdup(str)) == NULL)
802285612Sdelphij	return NULL;
803285612Sdelphij    for(d = s = n; *s != '\0'; s++) {
804285612Sdelphij	if(*s == '-') *d++ = '_';
805285612Sdelphij	else if(*s == '_' || isalnum(*s)) *d++ = *s;
806285612Sdelphij    }
807285612Sdelphij    *d = '\0';
808316722Sdelphij    return n;
809316722Sdelphij}
810316722Sdelphij
811316722Sdelphij
812316722Sdelphijchar *dir_search(char *progname)
813316722Sdelphij{
814316722Sdelphij    char path[MAXPATHLEN];
815316722Sdelphij    strlst_t *dir;
816285612Sdelphij    char *srcdir;
817316722Sdelphij
818316722Sdelphij    for(dir=srcdirs; dir != NULL; dir=dir->next) {
819316722Sdelphij	snprintf(path, MAXPATHLEN, "%s/%s", dir->str, progname);
820316722Sdelphij	if (!is_dir(path)) {
821316722Sdelphij	    continue;
822316722Sdelphij	}
823316722Sdelphij
824316722Sdelphij	if ((srcdir = strdup(path)) == NULL)
825316722Sdelphij	    out_of_memory();
826316722Sdelphij	return srcdir;
827316722Sdelphij    }
828285612Sdelphij    return NULL;
829316722Sdelphij}
830316722Sdelphij
831316722Sdelphij
832316722Sdelphijvoid top_makefile_rules(FILE *outmk)
833316722Sdelphij{
834316722Sdelphij    prog_t *p;
835316722Sdelphij
836316722Sdelphij    fprintf(outmk, "LIBS=");
837316722Sdelphij    output_strlst(outmk, libs);
838316722Sdelphij
839316722Sdelphij    if (makeobj) {
840316722Sdelphij	fprintf(outmk, "MAKEOBJDIRPREFIX?=%s\n", objprefix);
841316722Sdelphij	fprintf(outmk, "MAKE=env MAKEOBJDIRPREFIX=$(MAKEOBJDIRPREFIX) make\n");
842316722Sdelphij    } else {
843316722Sdelphij	fprintf(outmk, "MAKE=make\n");
844285612Sdelphij    }
845285612Sdelphij
846285612Sdelphij    if (buildopts) {
847285612Sdelphij	fprintf(outmk, "BUILDOPTS+=");
84854359Sroberto	output_strlst(outmk, buildopts);
84954359Sroberto    }
85054359Sroberto
85154359Sroberto    fprintf(outmk, "CRUNCHED_OBJS=");
85254359Sroberto    for(p = progs; p != NULL; p = p->next)
85354359Sroberto	fprintf(outmk, " %s.lo", p->name);
854285612Sdelphij    fprintf(outmk, "\n");
855285612Sdelphij
85654359Sroberto    fprintf(outmk, "SUBMAKE_TARGETS=");
85754359Sroberto    for(p = progs; p != NULL; p = p->next)
85854359Sroberto	fprintf(outmk, " %s_make", p->ident);
859285612Sdelphij    fprintf(outmk, "\nSUBCLEAN_TARGETS=");
86054359Sroberto    for(p = progs; p != NULL; p = p->next)
861293650Sglebius	fprintf(outmk, " %s_clean", p->ident);
862285612Sdelphij    fprintf(outmk, "\n\n");
86354359Sroberto
86454359Sroberto    fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n");
86554359Sroberto    fprintf(outmk, "exe: %s\n", execfname);
86654359Sroberto    fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS)\n",
867285612Sdelphij	    execfname, execfname);
868285612Sdelphij    fprintf(outmk, "\t$(CC) -static -o %s %s.o $(CRUNCHED_OBJS) $(LIBS)\n",
86954359Sroberto	    execfname, execfname);
87054359Sroberto    fprintf(outmk, "\tstrip %s\n", execfname);
87154359Sroberto    fprintf(outmk, "realclean: clean subclean\n");
87254359Sroberto    fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n",
87354359Sroberto	    execfname);
87454359Sroberto    fprintf(outmk, "subclean: $(SUBCLEAN_TARGETS)\n");
87554359Sroberto}
87654359Sroberto
87754359Sroberto
87854359Srobertovoid prog_makefile_rules(FILE *outmk, prog_t *p)
87954359Sroberto{
88054359Sroberto    strlst_t *lst;
881293650Sglebius
882285612Sdelphij    fprintf(outmk, "\n# -------- %s\n\n", p->name);
88354359Sroberto
88454359Sroberto    if(p->srcdir && p->objs) {
88554359Sroberto	fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir);
88654359Sroberto	fprintf(outmk, "%s_REALSRCDIR=%s\n", p->ident, p->realsrcdir);
887285612Sdelphij
88854359Sroberto	fprintf(outmk, "%s_OBJDIR=", p->ident);
88954359Sroberto	if (p->objdir)
89054359Sroberto		fprintf(outmk, "%s", p->objdir);
89154359Sroberto	else
892285612Sdelphij		fprintf(outmk, "$(MAKEOBJDIRPREFIX)/$(%s_REALSRCDIR)\n",
893285612Sdelphij		    p->ident);
894285612Sdelphij	fprintf(outmk, "\n");
89554359Sroberto
896285612Sdelphij	fprintf(outmk, "%s_OBJS=", p->ident);
89754359Sroberto	output_strlst(outmk, p->objs);
89854359Sroberto	if (p->buildopts != NULL) {
899285612Sdelphij		fprintf(outmk, "%s_OPTS+=", p->ident);
900294569Sdelphij		output_strlst(outmk, p->buildopts);
901294569Sdelphij	}
902294569Sdelphij	fprintf(outmk, "%s_make:\n", p->ident);
903294569Sdelphij	fprintf(outmk, "\t(cd $(%s_SRCDIR) && ", p->ident);
90454359Sroberto	if (makeobj)
905316722Sdelphij		fprintf(outmk, "$(MAKE) obj && ");
906316722Sdelphij	fprintf(outmk, "\\\n");
907316722Sdelphij	fprintf(outmk, "\t\t$(MAKE) $(BUILDOPTS) $(%s_OPTS) depend && \\\n"
90854359Sroberto		"\t\t$(MAKE) $(BUILDOPTS) $(%s_OPTS) $(%s_OBJS))\n",
90954359Sroberto		p->ident, p->ident, p->ident);
91054359Sroberto	fprintf(outmk, "%s_clean:\n", p->ident);
91154359Sroberto	fprintf(outmk, "\t(cd $(%s_SRCDIR) && $(MAKE) clean)\n\n", p->ident);
91254359Sroberto    }
91354359Sroberto    else
91454359Sroberto	fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n",
91554359Sroberto		p->ident, p->name);
91654359Sroberto
917285612Sdelphij    fprintf(outmk, "%s_OBJPATHS=", p->ident);
91854359Sroberto    if (p->objpaths)
91954359Sroberto	output_strlst(outmk, p->objpaths);
92054359Sroberto    else {
92154359Sroberto	for (lst = p->objs; lst != NULL; lst = lst->next) {
92254359Sroberto	    fprintf(outmk, " $(%s_OBJDIR)/%s", p->ident, lst->str);
923294569Sdelphij	}
924294569Sdelphij	fprintf(outmk, "\n");
92554359Sroberto    }
92654359Sroberto
927285612Sdelphij    fprintf(outmk, "%s_stub.c:\n", p->name);
928285612Sdelphij    fprintf(outmk, "\techo \""
929285612Sdelphij	           "int _crunched_%s_stub(int argc, char **argv, char **envp)"
930285612Sdelphij	           "{return main(argc,argv,envp);}\" >%s_stub.c\n",
931285612Sdelphij	    p->ident, p->name);
93254359Sroberto    fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)\n",
933285612Sdelphij	    p->name, p->name, p->ident);
934285612Sdelphij    fprintf(outmk, "\tld -dc -r -o %s.lo %s_stub.o $(%s_OBJPATHS)\n",
935285612Sdelphij	    p->name, p->name, p->ident);
936285612Sdelphij    fprintf(outmk, "\tcrunchide -k _crunched_%s_stub ", p->ident);
937294569Sdelphij    for(lst = p->keeplist; lst != NULL; lst = lst->next)
93854359Sroberto      fprintf(outmk, "-k _%s ", lst->str);
939285612Sdelphij    fprintf(outmk, "%s.lo\n", p->name);
940293650Sglebius}
941285612Sdelphij
942294569Sdelphijvoid output_strlst(FILE *outf, strlst_t *lst)
943294569Sdelphij{
944294569Sdelphij    for(; lst != NULL; lst = lst->next)
945294569Sdelphij	fprintf(outf, " %s", lst->str);
946294569Sdelphij    fprintf(outf, "\n");
947294569Sdelphij}
948294569Sdelphij
949294569Sdelphij
950294569Sdelphij/*
951294569Sdelphij * ========================================================================
952294569Sdelphij * general library routines
953294569Sdelphij *
954294569Sdelphij */
955294569Sdelphij
956294569Sdelphijvoid status(char *str)
957285612Sdelphij{
958285612Sdelphij    static int lastlen = 0;
959285612Sdelphij    int len, spaces;
960294569Sdelphij
961294569Sdelphij    if(!verbose) return;
962294569Sdelphij
963294569Sdelphij    len = strlen(str);
964294569Sdelphij    spaces = lastlen - len;
965294569Sdelphij    if(spaces < 1) spaces = 1;
966294569Sdelphij
967294569Sdelphij    fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " ");
968316722Sdelphij    fflush(stderr);
969316722Sdelphij    lastlen = len;
970316722Sdelphij}
971294569Sdelphij
972294569Sdelphij
973316722Sdelphijvoid out_of_memory(void)
974285612Sdelphij{
975285612Sdelphij    err(1, "%s: %d: out of memory, stopping", infilename, linenum);
976285612Sdelphij}
977285612Sdelphij
978285612Sdelphij
979285612Sdelphijvoid add_string(strlst_t **listp, char *str)
980285612Sdelphij{
981285612Sdelphij    strlst_t *p1, *p2;
982285612Sdelphij
983285612Sdelphij    /* add to end, but be smart about dups */
98454359Sroberto
985285612Sdelphij    for(p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next)
986285612Sdelphij	if(!strcmp(p2->str, str)) return;
987285612Sdelphij
98854359Sroberto    p2 = malloc(sizeof(strlst_t));
989285612Sdelphij    if(p2) {
990285612Sdelphij	p2->next = NULL ;
991285612Sdelphij	p2->str = strdup(str);
992285612Sdelphij    }
993285612Sdelphij    if(!p2 || !p2->str)
994285612Sdelphij	out_of_memory();
995285612Sdelphij
996285612Sdelphij    if(p1 == NULL) *listp = p2;
997285612Sdelphij    else p1->next = p2;
998285612Sdelphij}
999285612Sdelphij
1000285612Sdelphij
1001285612Sdelphijint is_dir(char *pathname)
1002285612Sdelphij{
100354359Sroberto    struct stat buf;
100454359Sroberto
100554359Sroberto    if(stat(pathname, &buf) == -1)
100654359Sroberto	return 0;
1007285612Sdelphij    return S_ISDIR(buf.st_mode);
1008316722Sdelphij}
1009285612Sdelphij
1010285612Sdelphijint is_nonempty_file(char *pathname)
1011285612Sdelphij{
101254359Sroberto    struct stat buf;
1013285612Sdelphij
1014285612Sdelphij    if(stat(pathname, &buf) == -1)
1015285612Sdelphij	return 0;
1016285612Sdelphij
101754359Sroberto    return S_ISREG(buf.st_mode) && buf.st_size > 0;
1018285612Sdelphij}
1019285612Sdelphij