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