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