xref: /openbsd-src/usr.sbin/crunchgen/crunchgen.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /* $OpenBSD: crunchgen.c,v 1.4 2008/11/24 17:23:26 drahn 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 	}
620 	/* We have a sourcedir and no explicit objs, try */
621 	/* to find makefile and get objs from it. */
622 	if (p->srcdir && !p->objs) {
623 		for (i = 0; mf_name[i] != NULL; i++) {
624 			snprintf(path, sizeof(path), "%s/%s", p->srcdir, mf_name[i]);
625 			if (is_nonempty_file(path)) {
626 				p->mf_name = mf_name[i];
627 				fillin_program_objs(p, path);
628 				break;
629 			}
630 		}
631 	}
632 	if (!p->objpaths && p->objdir && p->objs)
633 		for (s = p->objs; s != NULL; s = s->next) {
634 			snprintf(line, sizeof(line), "%s/%s", p->objdir, s->str);
635 			add_string(&p->objpaths, line);
636 		}
637 
638 	if (!p->srcdir && verbose)
639 		fprintf(stderr, "%s: %s: warning: could not find source directory.\n",
640 		    infilename, p->name);
641 	if (!p->objs && verbose)
642 		fprintf(stderr, "%s: %s: warning: could not find any .o files.\n",
643 		    infilename, p->name);
644 
645 	if (!p->objpaths) {
646 		fprintf(stderr,
647 		    "%s: %s: error: no objpaths specified or calculated.\n",
648 		    infilename, p->name);
649 		p->goterror = goterror = 1;
650 	}
651 }
652 
653 void
654 fillin_program_objs(prog_t * p, char *path)
655 {
656 	char           *cp, *obj, tempfname[MAXPATHLEN];
657 	int             fd, rc;
658 	FILE           *f;
659 
660 	/* discover the objs from the srcdir Makefile */
661 
662 	snprintf(tempfname, sizeof(tempfname), ".tmp_%sXXXXXXXXXX", confname);
663 	if ((fd = mkstemp(tempfname)) == -1 || (f = fdopen(fd, "w")) == NULL) {
664 		if (fd != -1)
665 			close(fd);
666 		perror(tempfname);
667 		goterror = 1;
668 		return;
669 	}
670 	fprintf(f, ".include \"%s\"\n", path);
671 	fprintf(f, ".if defined(PROG) && !defined(OBJS)\n");
672 	fprintf(f, "OBJS=${PROG}.o\n");
673 	fprintf(f, ".endif\n");
674 	fprintf(f, "crunchgen_objs:\n\t@echo 'OBJS= '${OBJS}\n");
675 	fclose(f);
676 
677 	snprintf(line, sizeof(line), "make -f %s crunchgen_objs 2>&1", tempfname);
678 	if ((f = popen(line, "r")) == NULL) {
679 		perror("submake pipe");
680 		goterror = 1;
681 		return;
682 	}
683 	while (fgets(line, MAXLINELEN, f)) {
684 		if (strncmp(line, "OBJS= ", 6)) {
685 			if (strcmp(line,
686 			    "sh: warning: running as root with dot in PATH\n") == 0)
687 				continue;
688 			fprintf(stderr, "make error: %s", line);
689 			goterror = 1;
690 			continue;
691 		}
692 		cp = line + 6;
693 		while (isspace(*cp))
694 			cp++;
695 		while (*cp) {
696 			obj = cp;
697 			while (*cp && !isspace(*cp))
698 				cp++;
699 			if (*cp)
700 				*cp++ = '\0';
701 			add_string(&p->objs, obj);
702 			while (isspace(*cp))
703 				cp++;
704 		}
705 	}
706 	if ((rc = pclose(f)) != 0) {
707 		fprintf(stderr, "make error: make returned %d\n", rc);
708 		goterror = 1;
709 	}
710 	unlink(tempfname);
711 }
712 
713 void
714 remove_error_progs(void)
715 {
716 	prog_t         *p1, *p2;
717 
718 	p1 = NULL;
719 	p2 = progs;
720 	while (p2 != NULL) {
721 		if (!p2->goterror)
722 			p1 = p2, p2 = p2->next;
723 		else {
724 			/* delete it from linked list */
725 			fprintf(stderr, "%s: %s: ignoring program because of errors.\n",
726 			    infilename, p2->name);
727 			if (p1)
728 				p1->next = p2->next;
729 			else
730 				progs = p2->next;
731 			p2 = p2->next;
732 		}
733 	}
734 }
735 
736 void
737 gen_specials_cache(void)
738 {
739 	FILE           *cachef;
740 	prog_t         *p;
741 
742 	snprintf(line, sizeof(line), "generating %s", cachename);
743 	status(line);
744 
745 	if ((cachef = fopen(cachename, "w")) == NULL) {
746 		perror(cachename);
747 		goterror = 1;
748 		return;
749 	}
750 	fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n",
751 	    cachename, infilename, CRUNCH_VERSION);
752 
753 	for (p = progs; p != NULL; p = p->next) {
754 		fprintf(cachef, "\n");
755 		if (p->srcdir)
756 			fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir);
757 		if (p->mf_name)
758 			fprintf(cachef, "special %s mf_name %s\n", p->name, p->mf_name);
759 		if (p->objdir)
760 			fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir);
761 		if (p->objs) {
762 			fprintf(cachef, "special %s objs", p->name);
763 			output_strlst(cachef, p->objs);
764 		}
765 		fprintf(cachef, "special %s objpaths", p->name);
766 		output_strlst(cachef, p->objpaths);
767 	}
768 	fclose(cachef);
769 }
770 
771 void
772 gen_output_makefile(void)
773 {
774 	prog_t         *p;
775 	FILE           *outmk;
776 
777 	snprintf(line, sizeof(line), "generating %s", outmkname);
778 	status(line);
779 
780 	if ((outmk = fopen(outmkname, "w")) == NULL) {
781 		perror(outmkname);
782 		goterror = 1;
783 		return;
784 	}
785 	fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n",
786 	    outmkname, infilename, CRUNCH_VERSION);
787 
788 	top_makefile_rules(outmk);
789 
790 	for (p = progs; p != NULL; p = p->next)
791 		prog_makefile_rules(outmk, p);
792 
793 	fprintf(outmk, "\n# ========\n");
794 	fclose(outmk);
795 }
796 
797 void
798 gen_output_cfile(void)
799 {
800 	extern char    *crunched_skel[];
801 	char          **cp;
802 	FILE           *outcf;
803 	prog_t         *p;
804 	strlst_t       *s;
805 
806 	snprintf(line, sizeof(line), "generating %s", outcfname);
807 	status(line);
808 
809 	if ((outcf = fopen(outcfname, "w")) == NULL) {
810 		perror(outcfname);
811 		goterror = 1;
812 		return;
813 	}
814 	fprintf(outcf, "/* %s - generated from %s by crunchgen %s */\n",
815 	    outcfname, infilename, CRUNCH_VERSION);
816 
817 	fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname);
818 	for (cp = crunched_skel; *cp != NULL; cp++)
819 		fprintf(outcf, "%s\n", *cp);
820 
821 	for (p = progs; p != NULL; p = p->next)
822 		fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident);
823 
824 	fprintf(outcf, "\nstruct stub entry_points[] = {\n");
825 	for (p = progs; p != NULL; p = p->next) {
826 		fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
827 			p->name, p->ident);
828 		for (s = p->links; s != NULL; s = s->next)
829 			fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
830 				s->str, p->ident);
831 	}
832 
833 	fprintf(outcf, "\t{ EXECNAME, crunched_main },\n");
834 	fprintf(outcf, "\t{ NULL, NULL }\n};\n");
835 	fclose(outcf);
836 }
837 
838 char           *
839 genident(char *str)
840 {
841 	char           *n, *s, *d;
842 
843 	/*
844          * generates a Makefile/C identifier from a program name, mapping '-' to
845          * '_' and ignoring all other non-identifier characters.  This leads to
846          * programs named "foo.bar" and "foobar" to map to the same identifier.
847          */
848 
849 	if ((n = strdup(str)) == NULL)
850 		return NULL;
851 	for (d = s = n; *s != '\0'; s++) {
852 		if (*s == '-')
853 			*d++ = '_';
854 		else if (*s == '_' || isalnum(*s))
855 			*d++ = *s;
856 	}
857 	*d = '\0';
858 	return n;
859 }
860 
861 char           *
862 dir_search(char *progname)
863 {
864 	char            path[MAXPATHLEN];
865 	strlst_t       *dir;
866 
867 	for (dir = srcdirs; dir != NULL; dir = dir->next) {
868 		snprintf(path, sizeof(path), "%s/%s", dir->str, progname);
869 		if (is_dir(path))
870 			return dir->str;
871 	}
872 	return NULL;
873 }
874 
875 void
876 top_makefile_rules(FILE * outmk)
877 {
878 	prog_t         *p;
879 	strlst_t       *l;
880 
881 
882 	fprintf(outmk, "STRIP?=strip\n");
883 	fprintf(outmk, "LINK=$(LD) -dc -r\n");
884 	fprintf(outmk, "LIBS=");
885 	for (l = libdirs; l != NULL; l = l->next)
886 		fprintf(outmk, " -L%s", l->str);
887 	output_strlst(outmk, libs);
888 
889 	fprintf(outmk, "CRUNCHED_OBJS=");
890 	for (p = progs; p != NULL; p = p->next)
891 		fprintf(outmk, " %s.lo", p->name);
892 	fprintf(outmk, "\n");
893 
894 	fprintf(outmk, "SUBMAKE_TARGETS=");
895 	for (p = progs; p != NULL; p = p->next)
896 		fprintf(outmk, " %s_make", p->ident);
897 	fprintf(outmk, "\n\n");
898 
899 	fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS)\n",
900 	    execfname, execfname);
901 	fprintf(outmk, "\t$(CC) -static -o $@ %s.o $(CRUNCHED_OBJS) $(LIBS)\n",
902 	    execfname);
903 	fprintf(outmk, "\t$(STRIP) %s\n", execfname);
904 	fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n");
905 	fprintf(outmk, "exe: %s\n", execfname);
906 	fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n",
907 	    execfname);
908 	fprintf(outmk, ".PHONY: all objs exe clean $(SUBMAKE_TARGETS)\n\n");
909 }
910 
911 void
912 prog_makefile_rules(FILE * outmk, prog_t * p)
913 {
914 	fprintf(outmk, "\n# -------- %s\n\n", p->name);
915 
916 	if (p->srcdir && p->objs) {
917 		fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir);
918 		fprintf(outmk, "%s_OBJS=", p->ident);
919 		output_strlst(outmk, p->objs);
920 		fprintf(outmk, "%s_make:\n", p->ident);
921 		fprintf(outmk, "\tcd $(%s_SRCDIR) && exec $(MAKE) -f %s $(%s_OBJS)\n\n",
922 		    p->ident, p->mf_name, p->ident);
923 	} else
924 		fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n",
925 		    p->ident, p->name);
926 
927 	fprintf(outmk, "%s_OBJPATHS=", p->ident);
928 	output_strlst(outmk, p->objpaths);
929 
930 	fprintf(outmk, "%s_stub.c:\n", p->name);
931 	fprintf(outmk, "\techo \""
932 	    "int _crunched_%s_stub(int argc, char **argv, char **envp)"
933 	    "{return main(argc,argv,envp);}\" >$@\n",
934 	    p->ident);
935 	fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)\n",
936 	    p->name, p->name, p->ident);
937 	fprintf(outmk, "\t$(LINK) -o $@ %s_stub.o $(%s_OBJPATHS)\n",
938 	    p->name, p->ident);
939 	fprintf(outmk, "\tcrunchgen %s -h -k %s_crunched_%s_stub $@\n",
940 	    elf_mangle ? "-M" : "",
941 	    elf_names ? "" : "_", p->ident);
942 }
943 
944 void
945 output_strlst(FILE * outf, strlst_t * lst)
946 {
947 	for (; lst != NULL; lst = lst->next)
948 		fprintf(outf, " %s", lst->str);
949 	fprintf(outf, "\n");
950 }
951 
952 void
953 status(char *str)
954 {
955 	static int      lastlen = 0;
956 	int             len, spaces;
957 
958 	if (!verbose)
959 		return;
960 
961 	len = strlen(str);
962 	spaces = lastlen - len;
963 	if (spaces < 1)
964 		spaces = 1;
965 
966 	fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " ");
967 	fflush(stderr);
968 	lastlen = len;
969 }
970 
971 void
972 out_of_memory(void)
973 {
974 	fprintf(stderr, "%s: %d: out of memory, stopping.\n", infilename, linenum);
975 	exit(1);
976 }
977 
978 void
979 add_string(strlst_t ** listp, char *str)
980 {
981 	strlst_t       *p1, *p2;
982 
983 	/* add to end, but be smart about dups */
984 
985 	for (p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next)
986 		if (!strcmp(p2->str, str))
987 			return;
988 
989 	p2 = calloc(1, sizeof(strlst_t));
990 	if (p2)
991 		p2->str = strdup(str);
992 	if (!p2 || !p2->str)
993 		out_of_memory();
994 
995 	p2->next = NULL;
996 	if (p1 == NULL)
997 		*listp = p2;
998 	else
999 		p1->next = p2;
1000 }
1001 
1002 int
1003 is_dir(char *pathname)
1004 {
1005 	struct stat     buf;
1006 
1007 	if (stat(pathname, &buf) == -1)
1008 		return 0;
1009 	return S_ISDIR(buf.st_mode);
1010 }
1011 
1012 int
1013 is_nonempty_file(char *pathname)
1014 {
1015 	struct stat     buf;
1016 
1017 	if (stat(pathname, &buf) == -1)
1018 		return 0;
1019 
1020 	return S_ISREG(buf.st_mode) && buf.st_size > 0;
1021 }
1022