xref: /openbsd/usr.sbin/crunchgen/crunchgen.c (revision 404b540a)
1 /* $OpenBSD: crunchgen.c,v 1.5 2009/10/07 13:55:38 fkr Exp $	 */
2 
3 /*
4  * Copyright (c) 1994 University of Maryland
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Author: James da Silva, Systems Design and Analysis Group
25  *			   Computer Science Department
26  *			   University of Maryland at College Park
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 <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <ctype.h>
39 #include <string.h>
40 
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/param.h>
44 
45 #define CRUNCH_VERSION	"0.3"
46 
47 #define MAXLINELEN	16384
48 #define MAXFIELDS 	 2048
49 
50 /* XXX - This should be runtime configurable */
51 /*
52  * We might have more than one makefile
53  * name on any given platform. Make sure
54  * default name is last though.
55  */
56 char	*mf_name[] = {
57 	"Makefile.bsd-wrapper",
58 	"Makefile",
59 	NULL
60 };
61 
62 /* internal representation of conf file: */
63 
64 /* simple lists of strings suffice for most parms */
65 
66 typedef struct strlst {
67 	struct strlst  *next;
68 	char           *str;
69 } strlst_t;
70 
71 /* progs have structure, each field can be set with "special" or calculated */
72 
73 typedef struct prog {
74 	struct prog    *next;
75 	char           *name, *ident, *mf_name;
76 	char           *srcdir, *objdir;
77 	strlst_t       *objs, *objpaths;
78 	strlst_t       *links;
79 	int             goterror;
80 } prog_t;
81 
82 strlst_t       *srcdirs = NULL;
83 strlst_t       *libs = NULL;
84 strlst_t       *libdirs = NULL;
85 char		objdir[MAXPATHLEN] = "obj";
86 prog_t         *progs = NULL;
87 
88 char            line[MAXLINELEN];
89 
90 char            confname[MAXPATHLEN], infilename[MAXPATHLEN];
91 char            outmkname[MAXPATHLEN], outcfname[MAXPATHLEN];
92 char            cachename[MAXPATHLEN], curfilename[MAXPATHLEN];
93 char            topdir[MAXPATHLEN], execfname[MAXPATHLEN];
94 int             linenum = -1;
95 int             goterror = 0;
96 
97 extern char	*__progname;
98 
99 int             verbose = 1, readcache = 1, elf_names, elf_mangle; /* options */
100 int             reading_cache;
101 
102 void            status(char *str);
103 void            out_of_memory(void);
104 void            add_string(strlst_t ** listp, char *str);
105 int             is_dir(char *pathname);
106 int             is_nonempty_file(char *pathname);
107 void            usage(void);
108 void            parse_conf_file(void);
109 void            gen_outputs(void);
110 
111 extern int	crunchide_main(int, char *[]);
112 
113 int
114 main(int argc, char *argv[])
115 {
116 	char           *p;
117 	int             optc;
118 	extern int      optind;
119 	extern char    *optarg;
120 
121 	while ((optc = getopt(argc, argv, "hm:c:e:fqD:EL:O:M")) != -1) {
122 		switch (optc) {
123 		case 'h':
124 			optreset = 1;
125 			return (crunchide_main(argc, argv));
126 			break;
127 		case 'f':
128 			readcache = 0;
129 			break;
130 		case 'q':
131 			verbose = 0;
132 			break;
133 
134 		case 'm':
135 			if (strlcpy(outmkname, optarg, sizeof(outmkname)) >=
136 			    sizeof(outmkname))
137 				usage();
138 			break;
139 		case 'c':
140 			if (strlcpy(outcfname, optarg, sizeof(outcfname)) >=
141 			    sizeof(outcfname))
142 				usage();
143 			break;
144 		case 'e':
145 			if (strlcpy(execfname, optarg, sizeof(execfname)) >=
146 			    sizeof(execfname))
147 				usage();
148 			break;
149 
150 		case 'D':
151 			if (strlcpy(topdir, optarg, sizeof(topdir)) >= sizeof(topdir))
152 				usage();
153 			break;
154 		case 'E':
155 			elf_names = 1;
156 			break;
157 		case 'L':
158 			if (strlen(optarg) >= MAXPATHLEN)
159 				usage();
160 			add_string(&libdirs, optarg);
161 			break;
162 		case 'O':
163 			if (strlcpy(objdir, optarg, sizeof(objdir)) >=
164 			    sizeof(objdir))
165 				usage();
166 			break;
167 		case 'M':
168 			elf_mangle = 1;
169 			break;
170 		default:
171 			usage();
172 		}
173 	}
174 
175 	argc -= optind;
176 	argv += optind;
177 
178 	if (argc != 1)
179 		usage();
180 
181 	if (libdirs == NULL)
182 		add_string(&libdirs, "/usr/lib");
183 	/*
184          * generate filenames
185          */
186 
187 	if (strlcpy(infilename, argv[0], sizeof(infilename)) >=
188 	    sizeof(infilename))
189 		usage();
190 
191 	/* confname = `basename infilename .conf` */
192 
193 	if ((p = strrchr(infilename, '/')) != NULL)
194 		strlcpy(confname, p + 1, sizeof confname);
195 	else
196 		strlcpy(confname, infilename, sizeof confname);
197 	if ((p = strrchr(confname, '.')) != NULL && !strcmp(p, ".conf"))
198 		*p = '\0';
199 
200 	if (!*outmkname)
201 		snprintf(outmkname, sizeof(outmkname), "%s.mk", confname);
202 	if (!*outcfname)
203 		snprintf(outcfname, sizeof(outcfname), "%s.c", confname);
204 	if (!*execfname)
205 		snprintf(execfname, sizeof(execfname), "%s", confname);
206 	snprintf(cachename, sizeof(cachename), "%s.cache", confname);
207 
208 	parse_conf_file();
209 	gen_outputs();
210 
211 	exit(goterror);
212 }
213 
214 void
215 usage(void)
216 {
217 	fprintf(stderr,
218 	    "usage: crunchgen [-EfMq] [-c c-file-name] [-D src-root] [-e exec-file-name]\n"
219 	    "\t[-L lib-dir] [-m makefile-name] [-O objdir-name] conf-file\n");
220 	fprintf(stderr,
221 	    "       crunchgen -h [-f keep-list-file] [-k keep-symbol] object-file ...\n");
222 	exit(1);
223 }
224 
225 void            parse_one_file(char *filename);
226 void            parse_line(char *line, int *fc, char **fv, int nf);
227 void            add_srcdirs(int argc, char **argv);
228 void            add_progs(int argc, char **argv);
229 void            add_link(int argc, char **argv);
230 void            add_libs(int argc, char **argv);
231 void            add_libdirs(int argc, char **argv);
232 void            add_special(int argc, char **argv);
233 
234 prog_t         *find_prog(char *str);
235 void            add_prog(char *progname);
236 
237 void
238 parse_conf_file(void)
239 {
240 	if (!is_nonempty_file(infilename)) {
241 		fprintf(stderr, "%s: fatal: input file \"%s\" not found.\n",
242 		    __progname, infilename);
243 		exit(1);
244 	}
245 	parse_one_file(infilename);
246 	if (readcache && is_nonempty_file(cachename)) {
247 		reading_cache = 1;
248 		parse_one_file(cachename);
249 	}
250 }
251 
252 void
253 parse_one_file(char *filename)
254 {
255 	char           *fieldv[MAXFIELDS];
256 	int             fieldc;
257 	void            (*f) (int c, char **v);
258 	FILE           *cf;
259 
260 	snprintf(line, sizeof(line), "reading %s", filename);
261 	status(line);
262 	strlcpy(curfilename, filename, sizeof curfilename);
263 
264 	if ((cf = fopen(curfilename, "r")) == NULL) {
265 		perror(curfilename);
266 		goterror = 1;
267 		return;
268 	}
269 	linenum = 0;
270 	while (fgets(line, MAXLINELEN, cf) != NULL) {
271 		linenum++;
272 		parse_line(line, &fieldc, fieldv, MAXFIELDS);
273 		if (fieldc < 1)
274 			continue;
275 		if (!strcmp(fieldv[0], "srcdirs"))
276 			f = add_srcdirs;
277 		else if (!strcmp(fieldv[0], "progs"))
278 			f = add_progs;
279 		else if (!strcmp(fieldv[0], "ln"))
280 			f = add_link;
281 		else if (!strcmp(fieldv[0], "libs"))
282 			f = add_libs;
283 		else if (!strcmp(fieldv[0], "special"))
284 			f = add_special;
285 		else if (!strcmp(fieldv[0], "libdirs"))
286 			f = add_libdirs;
287 		else {
288 			fprintf(stderr, "%s:%d: skipping unknown command `%s'.\n",
289 			    curfilename, linenum, fieldv[0]);
290 			goterror = 1;
291 			continue;
292 		}
293 		if (fieldc < 2) {
294 			fprintf(stderr,
295 			    "%s:%d: %s command needs at least 1 "
296 		  	    "argument, skipping.\n",
297 			    curfilename, linenum, fieldv[0]);
298 			goterror = 1;
299 			continue;
300 		}
301 		f(fieldc, fieldv);
302 	}
303 
304 	if (ferror(cf)) {
305 		perror(curfilename);
306 		goterror = 1;
307 	}
308 	fclose(cf);
309 }
310 
311 void
312 parse_line(char *line, int *fc, char **fv, int nf)
313 {
314 	char           *p;
315 
316 	p = line;
317 	*fc = 0;
318 	while (1) {
319 		while (isspace(*p))
320 			p++;
321 		if (*p == '\0' || *p == '#')
322 			break;
323 
324 		if (*fc < nf)
325 			fv[(*fc)++] = p;
326 		while (*p && !isspace(*p) && *p != '#')
327 			p++;
328 		if (*p == '\0' || *p == '#')
329 			break;
330 		*p++ = '\0';
331 	}
332 	if (*p)
333 		*p = '\0';	/* needed for '#' case */
334 }
335 
336 void
337 add_srcdirs(int argc, char **argv)
338 {
339 	int             i;
340 	char            tmppath[MAXPATHLEN];
341 	int             overflow;
342 
343 	for (i = 1; i < argc; i++) {
344 		overflow = 0;
345 		if (argv[i][0] == '/' || topdir[0] == '\0') {
346 			if (strlcpy(tmppath, argv[i], sizeof(tmppath)) >=
347 			    sizeof(tmppath))
348 				overflow = 1;
349 		} else {
350 			if (strlcpy(tmppath, topdir, sizeof(tmppath)) >=
351 			    sizeof(tmppath) ||
352 			    strlcat(tmppath, "/", sizeof(tmppath)) >=
353 			    sizeof(tmppath) ||
354 			    strlcat(tmppath, argv[i], sizeof(tmppath)) >=
355 			    sizeof(tmppath))
356 				overflow = 1;
357 		}
358 		if (overflow) {
359 			goterror = 1;
360 			fprintf(stderr, "%s:%d: `%.40s...' is too long, skipping it.\n",
361 			    curfilename, linenum, argv[i]);
362 			continue;
363 		}
364 		if (is_dir(tmppath))
365 			add_string(&srcdirs, tmppath);
366 		else {
367 			fprintf(stderr, "%s:%d: `%s' is not a directory, skipping it.\n",
368 			    curfilename, linenum, tmppath);
369 			goterror = 1;
370 		}
371 	}
372 }
373 
374 void
375 add_libdirs(int argc, char **argv)
376 {
377 	int             i;
378 	char            tmppath[MAXPATHLEN];
379 	char            tmppath2[MAXPATHLEN];
380 	int             overflow;
381 
382 	for (i = 1; i < argc; i++) {
383 		overflow = 0;
384 		if (argv[i][0] == '/' || topdir[0] == '\0') {
385 			if (strlcpy(tmppath, argv[i], sizeof(tmppath)) >=
386 			    sizeof(tmppath))
387 				overflow = 1;
388 		} else {
389 			if (strlcpy(tmppath, topdir, sizeof(tmppath)) >=
390 			    sizeof(tmppath) ||
391 			    strlcat(tmppath, "/", sizeof(tmppath)) >=
392 			    sizeof(tmppath) ||
393 			    strlcat(tmppath, argv[i], sizeof(tmppath)) >=
394 			    sizeof(tmppath))
395 				overflow = 1;
396 		}
397 		if (overflow) {
398 			goterror = 1;
399 			fprintf(stderr, "%s:%d: `%.40s...' is too long, skipping it.\n",
400 			    curfilename, linenum, argv[i]);
401 			continue;
402 		}
403 		if (is_dir(tmppath)) {
404 			snprintf(tmppath2, sizeof(tmppath2), "%s/%s", tmppath,
405 			    objdir);
406 			if (is_dir(tmppath2))
407 				add_string(&libdirs, tmppath2);
408 			else {
409 				snprintf(tmppath2, sizeof(tmppath2),
410 				    "%s/obj.%s", tmppath, MACHINE);
411 				if (is_dir(tmppath2))
412 					add_string(&libdirs, tmppath2);
413 				else
414 					add_string(&libdirs, tmppath);
415 			}
416 		}
417 		else {
418 			fprintf(stderr, "%s:%d: `%s' is not a directory, skipping it.\n",
419 			    curfilename, linenum, tmppath);
420 			goterror = 1;
421 		}
422 	}
423 }
424 
425 
426 void
427 add_progs(int argc, char **argv)
428 {
429 	int             i;
430 
431 	for (i = 1; i < argc; i++)
432 		add_prog(argv[i]);
433 }
434 
435 void
436 add_prog(char *progname)
437 {
438 	prog_t         *p1, *p2;
439 
440 	/* add to end, but be smart about dups */
441 
442 	for (p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next)
443 		if (!strcmp(p2->name, progname))
444 			return;
445 
446 	p2 = calloc(1, sizeof(prog_t));
447 	if (p2)
448 		p2->name = strdup(progname);
449 	if (!p2 || !p2->name)
450 		out_of_memory();
451 
452 	p2->next = NULL;
453 	if (p1 == NULL)
454 		progs = p2;
455 	else
456 		p1->next = p2;
457 
458 	p2->ident = p2->srcdir = p2->objdir = NULL;
459 	p2->links = p2->objs = NULL;
460 	p2->goterror = 0;
461 }
462 
463 void
464 add_link(int argc, char **argv)
465 {
466 	int             i;
467 	prog_t         *p = find_prog(argv[1]);
468 
469 	if (p == NULL) {
470 		fprintf(stderr,
471 		    "%s:%d: no prog %s previously declared, skipping link.\n",
472 		    curfilename, linenum, argv[1]);
473 		goterror = 1;
474 		return;
475 	}
476 	for (i = 2; i < argc; i++)
477 		add_string(&p->links, argv[i]);
478 }
479 
480 void
481 add_libs(int argc, char **argv)
482 {
483 	int             i;
484 
485 	for (i = 1; i < argc; i++)
486 		add_string(&libs, argv[i]);
487 }
488 
489 void
490 add_special(int argc, char **argv)
491 {
492 	int             i;
493 	prog_t         *p = find_prog(argv[1]);
494 
495 	if (p == NULL) {
496 		if (reading_cache)
497 			return;
498 		fprintf(stderr,
499 		    "%s:%d: no prog %s previously declared, skipping special.\n",
500 		    curfilename, linenum, argv[1]);
501 		goterror = 1;
502 		return;
503 	}
504 	if (!strcmp(argv[2], "ident")) {
505 		if (argc != 4)
506 			goto argcount;
507 		if ((p->ident = strdup(argv[3])) == NULL)
508 			out_of_memory();
509 	} else if (!strcmp(argv[2], "srcdir")) {
510 		if (argc != 4)
511 			goto argcount;
512 		if ((p->srcdir = strdup(argv[3])) == NULL)
513 			out_of_memory();
514 	} else if (!strcmp(argv[2], "mf_name")) {
515 		if (argc != 4)
516 			goto argcount;
517 		if ((p->mf_name = strdup(argv[3])) == NULL)
518 			out_of_memory();
519 	} else if (!strcmp(argv[2], "objdir")) {
520 		if (argc != 4)
521 			goto argcount;
522 		if ((p->objdir = strdup(argv[3])) == NULL)
523 			out_of_memory();
524 	} else if (!strcmp(argv[2], "objs")) {
525 		p->objs = NULL;
526 		for (i = 3; i < argc; i++)
527 			add_string(&p->objs, argv[i]);
528 	} else if (!strcmp(argv[2], "objpaths")) {
529 		p->objpaths = NULL;
530 		for (i = 3; i < argc; i++)
531 			add_string(&p->objpaths, argv[i]);
532 	} else {
533 		fprintf(stderr, "%s:%d: bad parameter name `%s', skipping line.\n",
534 		    curfilename, linenum, argv[2]);
535 		goterror = 1;
536 	}
537 	return;
538 
539 argcount:
540 	fprintf(stderr,
541 	    "%s:%d: too %s arguments, expected \"special %s %s <string>\".\n",
542 	    curfilename, linenum, argc < 4 ? "few" : "many", argv[1], argv[2]);
543 	goterror = 1;
544 }
545 
546 prog_t  *
547 find_prog(char *str)
548 {
549 	prog_t         *p;
550 
551 	for (p = progs; p != NULL; p = p->next)
552 		if (!strcmp(p->name, str))
553 			return p;
554 	return NULL;
555 }
556 
557 void            remove_error_progs(void);
558 void            fillin_program(prog_t * p);
559 void            gen_specials_cache(void);
560 void            gen_output_makefile(void);
561 void            gen_output_cfile(void);
562 
563 void            fillin_program_objs(prog_t * p, char *path);
564 void            top_makefile_rules(FILE * outmk);
565 void            prog_makefile_rules(FILE * outmk, prog_t * p);
566 void            output_strlst(FILE * outf, strlst_t * lst);
567 char           *genident(char *str);
568 char           *dir_search(char *progname);
569 
570 void
571 gen_outputs(void)
572 {
573 	prog_t         *p;
574 
575 	for (p = progs; p != NULL; p = p->next)
576 		fillin_program(p);
577 
578 	remove_error_progs();
579 	gen_specials_cache();
580 	gen_output_cfile();
581 	gen_output_makefile();
582 	status("");
583 	fprintf(stderr,
584 	    "Run \"make -f %s objs exe\" to build crunched binary.\n",
585 	    outmkname);
586 }
587 
588 void
589 fillin_program(prog_t * p)
590 {
591 	char            path[MAXPATHLEN];
592 	char           *srcparent;
593 	strlst_t       *s;
594 	int             i;
595 
596 	snprintf(line, sizeof(line), "filling in parms for %s", p->name);
597 	status(line);
598 
599 	if (!p->ident)
600 		p->ident = genident(p->name);
601 	if (!p->srcdir) {
602 		srcparent = dir_search(p->name);
603 		if (srcparent)
604 			snprintf(path, sizeof(path), "%s/%s", srcparent, p->name);
605 		if (is_dir(path))
606 			p->srcdir = strdup(path);
607 	}
608 	if (!p->objdir && p->srcdir) {
609 		snprintf(path, sizeof(path), "%s/%s", p->srcdir, objdir);
610 		if (is_dir(path))
611 			p->objdir = strdup(path);
612 		else {
613 			snprintf(path, sizeof(path), "%s/obj.%s", p->srcdir, MACHINE);
614 			if (is_dir(path))
615 				p->objdir = strdup(path);
616 			else
617 				p->objdir = p->srcdir;
618 		}
619 		/* Fill p->mf_name so it is not a null pointer */
620 		for (i = 0; mf_name[i] != NULL; i++) {
621 			snprintf(path, sizeof(path), "%s/%s", p->srcdir, mf_name[i]);
622 			if (is_nonempty_file(path)) {
623 				p->mf_name = mf_name[i];
624 				break;
625 			}
626 		}
627 	}
628 	/* We have a sourcedir and no explicit objs, try */
629 	/* to find makefile and get objs from it. */
630 	if (p->srcdir && !p->objs) {
631 		for (i = 0; mf_name[i] != NULL; i++) {
632 			snprintf(path, sizeof(path), "%s/%s", p->srcdir, mf_name[i]);
633 			if (is_nonempty_file(path)) {
634 				p->mf_name = mf_name[i];
635 				fillin_program_objs(p, path);
636 				break;
637 			}
638 		}
639 	}
640 	if (!p->objpaths && p->objdir && p->objs)
641 		for (s = p->objs; s != NULL; s = s->next) {
642 			snprintf(line, sizeof(line), "%s/%s", p->objdir, s->str);
643 			add_string(&p->objpaths, line);
644 		}
645 
646 	if (!p->srcdir && verbose)
647 		fprintf(stderr, "%s: %s: warning: could not find source directory.\n",
648 		    infilename, p->name);
649 	if (!p->objs && verbose)
650 		fprintf(stderr, "%s: %s: warning: could not find any .o files.\n",
651 		    infilename, p->name);
652 
653 	if (!p->objpaths) {
654 		fprintf(stderr,
655 		    "%s: %s: error: no objpaths specified or calculated.\n",
656 		    infilename, p->name);
657 		p->goterror = goterror = 1;
658 	}
659 }
660 
661 void
662 fillin_program_objs(prog_t * p, char *path)
663 {
664 	char           *cp, *obj, tempfname[MAXPATHLEN];
665 	int             fd, rc;
666 	FILE           *f;
667 
668 	/* discover the objs from the srcdir Makefile */
669 
670 	snprintf(tempfname, sizeof(tempfname), ".tmp_%sXXXXXXXXXX", confname);
671 	if ((fd = mkstemp(tempfname)) == -1 || (f = fdopen(fd, "w")) == NULL) {
672 		if (fd != -1)
673 			close(fd);
674 		perror(tempfname);
675 		goterror = 1;
676 		return;
677 	}
678 	fprintf(f, ".include \"%s\"\n", path);
679 	fprintf(f, ".if defined(PROG) && !defined(OBJS)\n");
680 	fprintf(f, "OBJS=${PROG}.o\n");
681 	fprintf(f, ".endif\n");
682 	fprintf(f, "crunchgen_objs:\n\t@echo 'OBJS= '${OBJS}\n");
683 	fclose(f);
684 
685 	snprintf(line, sizeof(line), "make -f %s crunchgen_objs 2>&1", tempfname);
686 	if ((f = popen(line, "r")) == NULL) {
687 		perror("submake pipe");
688 		goterror = 1;
689 		return;
690 	}
691 	while (fgets(line, MAXLINELEN, f)) {
692 		if (strncmp(line, "OBJS= ", 6)) {
693 			if (strcmp(line,
694 			    "sh: warning: running as root with dot in PATH\n") == 0)
695 				continue;
696 			fprintf(stderr, "make error: %s", line);
697 			goterror = 1;
698 			continue;
699 		}
700 		cp = line + 6;
701 		while (isspace(*cp))
702 			cp++;
703 		while (*cp) {
704 			obj = cp;
705 			while (*cp && !isspace(*cp))
706 				cp++;
707 			if (*cp)
708 				*cp++ = '\0';
709 			add_string(&p->objs, obj);
710 			while (isspace(*cp))
711 				cp++;
712 		}
713 	}
714 	if ((rc = pclose(f)) != 0) {
715 		fprintf(stderr, "make error: make returned %d\n", rc);
716 		goterror = 1;
717 	}
718 	unlink(tempfname);
719 }
720 
721 void
722 remove_error_progs(void)
723 {
724 	prog_t         *p1, *p2;
725 
726 	p1 = NULL;
727 	p2 = progs;
728 	while (p2 != NULL) {
729 		if (!p2->goterror)
730 			p1 = p2, p2 = p2->next;
731 		else {
732 			/* delete it from linked list */
733 			fprintf(stderr, "%s: %s: ignoring program because of errors.\n",
734 			    infilename, p2->name);
735 			if (p1)
736 				p1->next = p2->next;
737 			else
738 				progs = p2->next;
739 			p2 = p2->next;
740 		}
741 	}
742 }
743 
744 void
745 gen_specials_cache(void)
746 {
747 	FILE           *cachef;
748 	prog_t         *p;
749 
750 	snprintf(line, sizeof(line), "generating %s", cachename);
751 	status(line);
752 
753 	if ((cachef = fopen(cachename, "w")) == NULL) {
754 		perror(cachename);
755 		goterror = 1;
756 		return;
757 	}
758 	fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n",
759 	    cachename, infilename, CRUNCH_VERSION);
760 
761 	for (p = progs; p != NULL; p = p->next) {
762 		fprintf(cachef, "\n");
763 		if (p->srcdir)
764 			fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir);
765 		if (p->mf_name)
766 			fprintf(cachef, "special %s mf_name %s\n", p->name, p->mf_name);
767 		if (p->objdir)
768 			fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir);
769 		if (p->objs) {
770 			fprintf(cachef, "special %s objs", p->name);
771 			output_strlst(cachef, p->objs);
772 		}
773 		fprintf(cachef, "special %s objpaths", p->name);
774 		output_strlst(cachef, p->objpaths);
775 	}
776 	fclose(cachef);
777 }
778 
779 void
780 gen_output_makefile(void)
781 {
782 	prog_t         *p;
783 	FILE           *outmk;
784 
785 	snprintf(line, sizeof(line), "generating %s", outmkname);
786 	status(line);
787 
788 	if ((outmk = fopen(outmkname, "w")) == NULL) {
789 		perror(outmkname);
790 		goterror = 1;
791 		return;
792 	}
793 	fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n",
794 	    outmkname, infilename, CRUNCH_VERSION);
795 
796 	top_makefile_rules(outmk);
797 
798 	for (p = progs; p != NULL; p = p->next)
799 		prog_makefile_rules(outmk, p);
800 
801 	fprintf(outmk, "\n# ========\n");
802 	fclose(outmk);
803 }
804 
805 void
806 gen_output_cfile(void)
807 {
808 	extern char    *crunched_skel[];
809 	char          **cp;
810 	FILE           *outcf;
811 	prog_t         *p;
812 	strlst_t       *s;
813 
814 	snprintf(line, sizeof(line), "generating %s", outcfname);
815 	status(line);
816 
817 	if ((outcf = fopen(outcfname, "w")) == NULL) {
818 		perror(outcfname);
819 		goterror = 1;
820 		return;
821 	}
822 	fprintf(outcf, "/* %s - generated from %s by crunchgen %s */\n",
823 	    outcfname, infilename, CRUNCH_VERSION);
824 
825 	fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname);
826 	for (cp = crunched_skel; *cp != NULL; cp++)
827 		fprintf(outcf, "%s\n", *cp);
828 
829 	for (p = progs; p != NULL; p = p->next)
830 		fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident);
831 
832 	fprintf(outcf, "\nstruct stub entry_points[] = {\n");
833 	for (p = progs; p != NULL; p = p->next) {
834 		fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
835 			p->name, p->ident);
836 		for (s = p->links; s != NULL; s = s->next)
837 			fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
838 				s->str, p->ident);
839 	}
840 
841 	fprintf(outcf, "\t{ EXECNAME, crunched_main },\n");
842 	fprintf(outcf, "\t{ NULL, NULL }\n};\n");
843 	fclose(outcf);
844 }
845 
846 char           *
847 genident(char *str)
848 {
849 	char           *n, *s, *d;
850 
851 	/*
852          * generates a Makefile/C identifier from a program name, mapping '-' to
853          * '_' and ignoring all other non-identifier characters.  This leads to
854          * programs named "foo.bar" and "foobar" to map to the same identifier.
855          */
856 
857 	if ((n = strdup(str)) == NULL)
858 		return NULL;
859 	for (d = s = n; *s != '\0'; s++) {
860 		if (*s == '-')
861 			*d++ = '_';
862 		else if (*s == '_' || isalnum(*s))
863 			*d++ = *s;
864 	}
865 	*d = '\0';
866 	return n;
867 }
868 
869 char           *
870 dir_search(char *progname)
871 {
872 	char            path[MAXPATHLEN];
873 	strlst_t       *dir;
874 
875 	for (dir = srcdirs; dir != NULL; dir = dir->next) {
876 		snprintf(path, sizeof(path), "%s/%s", dir->str, progname);
877 		if (is_dir(path))
878 			return dir->str;
879 	}
880 	return NULL;
881 }
882 
883 void
884 top_makefile_rules(FILE * outmk)
885 {
886 	prog_t         *p;
887 	strlst_t       *l;
888 
889 
890 	fprintf(outmk, "STRIP?=strip\n");
891 	fprintf(outmk, "LINK=$(LD) -dc -r\n");
892 	fprintf(outmk, "LIBS=");
893 	for (l = libdirs; l != NULL; l = l->next)
894 		fprintf(outmk, " -L%s", l->str);
895 	output_strlst(outmk, libs);
896 
897 	fprintf(outmk, "CRUNCHED_OBJS=");
898 	for (p = progs; p != NULL; p = p->next)
899 		fprintf(outmk, " %s.lo", p->name);
900 	fprintf(outmk, "\n");
901 
902 	fprintf(outmk, "SUBMAKE_TARGETS=");
903 	for (p = progs; p != NULL; p = p->next)
904 		fprintf(outmk, " %s_make", p->ident);
905 	fprintf(outmk, "\n\n");
906 
907 	fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS)\n",
908 	    execfname, execfname);
909 	fprintf(outmk, "\t$(CC) -static -o $@ %s.o $(CRUNCHED_OBJS) $(LIBS)\n",
910 	    execfname);
911 	fprintf(outmk, "\t$(STRIP) %s\n", execfname);
912 	fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n");
913 	fprintf(outmk, "exe: %s\n", execfname);
914 	fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n",
915 	    execfname);
916 	fprintf(outmk, ".PHONY: all objs exe clean $(SUBMAKE_TARGETS)\n\n");
917 }
918 
919 void
920 prog_makefile_rules(FILE * outmk, prog_t * p)
921 {
922 	fprintf(outmk, "\n# -------- %s\n\n", p->name);
923 
924 	if (p->srcdir && p->objs) {
925 		fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir);
926 		fprintf(outmk, "%s_OBJS=", p->ident);
927 		output_strlst(outmk, p->objs);
928 		fprintf(outmk, "%s_make:\n", p->ident);
929 		fprintf(outmk, "\tcd $(%s_SRCDIR) && exec $(MAKE) -f %s $(%s_OBJS)\n\n",
930 		    p->ident, p->mf_name, p->ident);
931 	} else
932 		fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n",
933 		    p->ident, p->name);
934 
935 	fprintf(outmk, "%s_OBJPATHS=", p->ident);
936 	output_strlst(outmk, p->objpaths);
937 
938 	fprintf(outmk, "%s_stub.c:\n", p->name);
939 	fprintf(outmk, "\techo \""
940 	    "int _crunched_%s_stub(int argc, char **argv, char **envp)"
941 	    "{return main(argc,argv,envp);}\" >$@\n",
942 	    p->ident);
943 	fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)\n",
944 	    p->name, p->name, p->ident);
945 	fprintf(outmk, "\t$(LINK) -o $@ %s_stub.o $(%s_OBJPATHS)\n",
946 	    p->name, p->ident);
947 	fprintf(outmk, "\tcrunchgen %s -h -k %s_crunched_%s_stub $@\n",
948 	    elf_mangle ? "-M" : "",
949 	    elf_names ? "" : "_", p->ident);
950 }
951 
952 void
953 output_strlst(FILE * outf, strlst_t * lst)
954 {
955 	for (; lst != NULL; lst = lst->next)
956 		fprintf(outf, " %s", lst->str);
957 	fprintf(outf, "\n");
958 }
959 
960 void
961 status(char *str)
962 {
963 	static int      lastlen = 0;
964 	int             len, spaces;
965 
966 	if (!verbose)
967 		return;
968 
969 	len = strlen(str);
970 	spaces = lastlen - len;
971 	if (spaces < 1)
972 		spaces = 1;
973 
974 	fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " ");
975 	fflush(stderr);
976 	lastlen = len;
977 }
978 
979 void
980 out_of_memory(void)
981 {
982 	fprintf(stderr, "%s: %d: out of memory, stopping.\n", infilename, linenum);
983 	exit(1);
984 }
985 
986 void
987 add_string(strlst_t ** listp, char *str)
988 {
989 	strlst_t       *p1, *p2;
990 
991 	/* add to end, but be smart about dups */
992 
993 	for (p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next)
994 		if (!strcmp(p2->str, str))
995 			return;
996 
997 	p2 = calloc(1, sizeof(strlst_t));
998 	if (p2)
999 		p2->str = strdup(str);
1000 	if (!p2 || !p2->str)
1001 		out_of_memory();
1002 
1003 	p2->next = NULL;
1004 	if (p1 == NULL)
1005 		*listp = p2;
1006 	else
1007 		p1->next = p2;
1008 }
1009 
1010 int
1011 is_dir(char *pathname)
1012 {
1013 	struct stat     buf;
1014 
1015 	if (stat(pathname, &buf) == -1)
1016 		return 0;
1017 	return S_ISDIR(buf.st_mode);
1018 }
1019 
1020 int
1021 is_nonempty_file(char *pathname)
1022 {
1023 	struct stat     buf;
1024 
1025 	if (stat(pathname, &buf) == -1)
1026 		return 0;
1027 
1028 	return S_ISREG(buf.st_mode) && buf.st_size > 0;
1029 }
1030