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