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