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