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