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