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