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