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