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