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