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