Initial import from FreeBSD RELENG_4:
[dragonfly.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  * $FreeBSD: src/usr.sbin/crunch/crunchgen/crunchgen.c,v 1.12.2.10 2002/04/14 20:55:21 luigi Exp $
27  */
28 /*
29  * ========================================================================
30  * crunchgen.c
31  *
32  * Generates a Makefile and main C file for a crunched executable,
33  * from specs given in a .conf file.
34  */
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/param.h>
38
39 #include <ctype.h>
40 #include <err.h>
41 #include <paths.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #define CRUNCH_VERSION  "0.2"
48
49 #define MAXLINELEN      16384
50 #define MAXFIELDS        2048
51
52
53 /* internal representation of conf file: */
54
55 /* simple lists of strings suffice for most parms */
56
57 typedef struct strlst {
58         struct strlst *next;
59         char *str;
60 } strlst_t;
61
62 /* progs have structure, each field can be set with "special" or calculated */
63
64 typedef struct prog {
65         struct prog *next;      /* link field */
66         char *name;             /* program name */
67         char *ident;            /* C identifier for the program name */
68         char *srcdir;
69         char *realsrcdir;
70         char *objdir;
71         char *objvar;           /* Makefile variable to replace OBJS */
72         strlst_t *objs, *objpaths;
73         strlst_t *buildopts;
74         strlst_t *keeplist;
75         strlst_t *links;
76         strlst_t *libs;
77         int goterror;
78 } prog_t;
79
80
81 /* global state */
82
83 strlst_t *buildopts = NULL;
84 strlst_t *srcdirs   = NULL;
85 strlst_t *libs      = NULL;
86 prog_t   *progs     = NULL;
87
88 char confname[MAXPATHLEN], infilename[MAXPATHLEN];
89 char outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN];
90 char tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN];
91 char outhdrname[MAXPATHLEN] ;   /* user-supplied header for *.mk */
92 char *objprefix;                /* where are the objects ? */
93 int linenum = -1;
94 int goterror = 0;
95
96 int verbose, readcache;         /* options */
97 int reading_cache;
98 int makeobj = 0;                /* add 'make obj' rules to the makefile */
99
100 int list_mode;
101
102 /* general library routines */
103
104 void status(char *str);
105 void out_of_memory(void);
106 void add_string(strlst_t **listp, char *str);
107 int is_dir(char *pathname);
108 int is_nonempty_file(char *pathname);
109
110 /* helper routines for main() */
111
112 void usage(void);
113 void parse_conf_file(void);
114 void gen_outputs(void);
115
116
117 int main(int argc, char **argv)
118 {
119         char *p;
120         int optc;
121
122         verbose = 1;
123         readcache = 1;
124         *outmkname = *outcfname = *execfname = '\0';
125
126         p = getenv("MAKEOBJDIRPREFIX");
127         if (p == NULL || *p == '\0')
128                 objprefix = "/usr/obj"; /* default */
129         else
130                 if ((objprefix = strdup(p)) == NULL)
131                         out_of_memory();
132
133         while((optc = getopt(argc, argv, "lh:m:c:e:p:foq")) != -1) {
134                 switch(optc) {
135                 case 'f':
136                         readcache = 0;
137                         break;
138                 case 'o':
139                         makeobj = 1;
140                         break;
141                 case 'q':
142                         verbose = 0;
143                         break;
144
145                 case 'm':
146                         strlcpy(outmkname, optarg, sizeof(outmkname));
147                         break;
148                 case 'p':
149                         if ((objprefix = strdup(optarg)) == NULL)
150                                 out_of_memory();
151                         break;
152
153                 case 'h':
154                         strlcpy(outhdrname, optarg, sizeof(outhdrname));
155                         break;
156                 case 'c':
157                         strlcpy(outcfname, optarg, sizeof(outcfname));
158                         break;
159                 case 'e':
160                         strlcpy(execfname, optarg, sizeof(execfname));
161                         break;
162
163                 case 'l':
164                         list_mode++;
165                         verbose = 0;
166                         break;
167
168                 case '?':
169                 default:
170                         usage();
171                 }
172         }
173
174         argc -= optind;
175         argv += optind;
176
177         if (argc != 1)
178                 usage();
179
180         /*
181          * generate filenames
182          */
183
184         strlcpy(infilename, argv[0], sizeof(infilename));
185
186         /* confname = `basename infilename .conf` */
187
188         if ((p=strrchr(infilename, '/')) != NULL)
189                 strlcpy(confname, p + 1, sizeof(confname));
190         else
191                 strlcpy(confname, infilename, sizeof(confname));
192
193         if ((p=strrchr(confname, '.')) != NULL && !strcmp(p, ".conf"))
194                 *p = '\0';
195
196         if (!*outmkname)
197                 snprintf(outmkname, sizeof(outmkname), "%s.mk", confname);
198         if (!*outcfname)
199                 snprintf(outcfname, sizeof(outcfname), "%s.c", confname);
200         if (!*execfname)
201                 snprintf(execfname, sizeof(execfname), "%s", confname);
202
203         snprintf(cachename, sizeof(cachename), "%s.cache", confname);
204         snprintf(tempfname, sizeof(tempfname), "%s/crunchgen_%sXXXXXX",
205         getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP, confname);
206
207         parse_conf_file();
208         if (list_mode)
209                 exit(goterror);
210
211         gen_outputs();
212
213         exit(goterror);
214 }
215
216
217 void usage(void)
218 {
219         fprintf(stderr, "%s%s\n\t%s%s\n", "usage: crunchgen [-foq] ",
220             "[-h <makefile-header-name>] [-m <makefile>]",
221             "[-p <obj-prefix>] [-c <c-file-name>] [-e <exec-file>] ",
222             "<conffile>");
223         exit(1);
224 }
225
226
227 /*
228  * ========================================================================
229  * parse_conf_file subsystem
230  *
231  */
232
233 /* helper routines for parse_conf_file */
234
235 void parse_one_file(char *filename);
236 void parse_line(char *line, int *fc, char **fv, int nf);
237 void add_srcdirs(int argc, char **argv);
238 void add_progs(int argc, char **argv);
239 void add_link(int argc, char **argv);
240 void add_libs(int argc, char **argv);
241 void add_buildopts(int argc, char **argv);
242 void add_special(int argc, char **argv);
243
244 prog_t *find_prog(char *str);
245 void add_prog(char *progname);
246
247
248 void parse_conf_file(void)
249 {
250         if (!is_nonempty_file(infilename))
251                 errx(1, "fatal: input file \"%s\" not found", infilename);
252
253         parse_one_file(infilename);
254         if (readcache && is_nonempty_file(cachename)) {
255                 reading_cache = 1;
256                 parse_one_file(cachename);
257         }
258 }
259
260
261 void parse_one_file(char *filename)
262 {
263         char *fieldv[MAXFIELDS];
264         int fieldc;
265         void (*f)(int c, char **v);
266         FILE *cf;
267         char line[MAXLINELEN];
268
269         snprintf(line, sizeof(line), "reading %s", filename);
270         status(line);
271         strlcpy(curfilename, filename, sizeof(curfilename));
272
273         if ((cf = fopen(curfilename, "r")) == NULL) {
274                 warn("%s", curfilename);
275                 goterror = 1;
276                 return;
277         }
278
279         linenum = 0;
280         while (fgets(line, MAXLINELEN, cf) != NULL) {
281                 linenum++;
282                 parse_line(line, &fieldc, fieldv, MAXFIELDS);
283
284                 if (fieldc < 1)
285                         continue;
286
287                 if (!strcmp(fieldv[0], "srcdirs"))
288                         f = add_srcdirs;
289                 else if(!strcmp(fieldv[0], "progs"))
290                         f = add_progs;
291                 else if(!strcmp(fieldv[0], "ln"))
292                         f = add_link;
293                 else if(!strcmp(fieldv[0], "libs"))
294                         f = add_libs;
295                 else if(!strcmp(fieldv[0], "buildopts"))
296                         f = add_buildopts;
297                 else if(!strcmp(fieldv[0], "special"))
298                         f = add_special;
299                 else {
300                         warnx("%s:%d: skipping unknown command `%s'",
301                             curfilename, linenum, fieldv[0]);
302                         goterror = 1;
303                         continue;
304                 }
305
306                 if (fieldc < 2) {
307                         warnx("%s:%d: %s %s",
308                             curfilename, linenum, fieldv[0],
309                             "command needs at least 1 argument, skipping");
310                         goterror = 1;
311                         continue;
312                 }
313
314                 f(fieldc, fieldv);
315         }
316
317         if (ferror(cf)) {
318                 warn("%s", curfilename);
319                 goterror = 1;
320         }
321         fclose(cf);
322 }
323
324
325 void parse_line(char *line, int *fc, char **fv, int nf)
326 {
327         char *p;
328
329         p = line;
330         *fc = 0;
331
332         while (1) {
333                 while (isspace(*p))
334                         p++;
335
336                 if (*p == '\0' || *p == '#')
337                         break;
338
339                 if (*fc < nf)
340                         fv[(*fc)++] = p;
341
342                 while (*p && !isspace(*p) && *p != '#')
343                         p++;
344
345                 if (*p == '\0' || *p == '#')
346                         break;
347
348                 *p++ = '\0';
349         }
350
351         if (*p)
352                 *p = '\0';              /* needed for '#' case */
353 }
354
355
356 void add_srcdirs(int argc, char **argv)
357 {
358         int i;
359
360         for (i = 1; i < argc; i++) {
361                 if (is_dir(argv[i]))
362                         add_string(&srcdirs, argv[i]);
363                 else {
364                         warnx("%s:%d: `%s' is not a directory, skipping it",
365                             curfilename, linenum, argv[i]);
366                         goterror = 1;
367                 }
368         }
369 }
370
371
372 void add_progs(int argc, char **argv)
373 {
374         int i;
375
376         for (i = 1; i < argc; i++)
377                 add_prog(argv[i]);
378 }
379
380
381 void add_prog(char *progname)
382 {
383         prog_t *p1, *p2;
384
385         /* add to end, but be smart about dups */
386
387         for (p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next)
388                 if (!strcmp(p2->name, progname))
389                         return;
390
391         p2 = malloc(sizeof(prog_t));
392         if(p2) {
393                 memset(p2, 0, sizeof(prog_t));
394                 p2->name = strdup(progname);
395         }
396         if (!p2 || !p2->name)
397                 out_of_memory();
398
399         p2->next = NULL;
400         if (p1 == NULL)
401                 progs = p2;
402         else
403                 p1->next = p2;
404
405         p2->ident = NULL;
406         p2->srcdir = NULL;
407         p2->realsrcdir = NULL;
408         p2->objdir = NULL;
409         p2->links = NULL;
410         p2->libs = NULL;
411         p2->objs = NULL;
412         p2->keeplist = NULL;
413         p2->buildopts = NULL;
414         p2->goterror = 0;
415
416         if (list_mode)
417                 printf("%s\n",progname);
418 }
419
420
421 void add_link(int argc, char **argv)
422 {
423         int i;
424         prog_t *p = find_prog(argv[1]);
425
426         if (p == NULL) {
427                 warnx("%s:%d: no prog %s previously declared, skipping link",
428                     curfilename, linenum, argv[1]);
429                 goterror = 1;
430                 return;
431         }
432
433         for (i = 2; i < argc; i++) {
434                 if (list_mode)
435                         printf("%s\n",argv[i]);
436
437                 add_string(&p->links, argv[i]);
438         }
439 }
440
441
442 void add_libs(int argc, char **argv)
443 {
444         int i;
445
446         for(i = 1; i < argc; i++)
447                 add_string(&libs, argv[i]);
448 }
449
450
451 void add_buildopts(int argc, char **argv)
452 {
453         int i;
454
455         for (i = 1; i < argc; i++)
456                 add_string(&buildopts, argv[i]);
457 }
458
459
460 void add_special(int argc, char **argv)
461 {
462         int i;
463         prog_t *p = find_prog(argv[1]);
464
465         if (p == NULL) {
466                 if (reading_cache)
467                         return;
468
469                 warnx("%s:%d: no prog %s previously declared, skipping special",
470                     curfilename, linenum, argv[1]);
471                 goterror = 1;
472                 return;
473         }
474
475         if (!strcmp(argv[2], "ident")) {
476                 if (argc != 4)
477                         goto argcount;
478                 if ((p->ident = strdup(argv[3])) == NULL)
479                         out_of_memory();
480         } else if (!strcmp(argv[2], "srcdir")) {
481                 if (argc != 4)
482                         goto argcount;
483                 if ((p->srcdir = strdup(argv[3])) == NULL)
484                         out_of_memory();
485         } else if (!strcmp(argv[2], "objdir")) {
486                 if(argc != 4)
487                         goto argcount;
488                 if((p->objdir = strdup(argv[3])) == NULL)
489                         out_of_memory();
490         } else if (!strcmp(argv[2], "objs")) {
491                 p->objs = NULL;
492                 for (i = 3; i < argc; i++)
493                         add_string(&p->objs, argv[i]);
494         } else if (!strcmp(argv[2], "objpaths")) {
495                 p->objpaths = NULL;
496                 for (i = 3; i < argc; i++)
497                         add_string(&p->objpaths, argv[i]);
498         } else if (!strcmp(argv[2], "keep")) {
499                 p->keeplist = NULL;
500                 for(i = 3; i < argc; i++)
501                         add_string(&p->keeplist, argv[i]);
502         } else if (!strcmp(argv[2], "objvar")) {
503                 if(argc != 4)
504                         goto argcount;
505                 if ((p->objvar = strdup(argv[3])) == NULL)
506                         out_of_memory();
507         } else if (!strcmp(argv[2], "buildopts")) {
508                 p->buildopts = NULL;
509                 for (i = 3; i < argc; i++)
510                         add_string(&p->buildopts, argv[i]);
511         } else if (!strcmp(argv[2], "lib")) {
512                 for (i = 3; i < argc; i++)
513                         add_string(&p->libs, argv[i]);
514         } else {
515                 warnx("%s:%d: bad parameter name `%s', skipping line",
516                     curfilename, linenum, argv[2]);
517                 goterror = 1;
518         }
519         return;
520
521  argcount:
522         warnx("%s:%d: too %s arguments, expected \"special %s %s <string>\"",
523             curfilename, linenum, argc < 4? "few" : "many", argv[1], argv[2]);
524         goterror = 1;
525 }
526
527
528 prog_t *find_prog(char *str)
529 {
530         prog_t *p;
531
532         for (p = progs; p != NULL; p = p->next)
533                 if (!strcmp(p->name, str))
534                         return p;
535
536         return NULL;
537 }
538
539
540 /*
541  * ========================================================================
542  * gen_outputs subsystem
543  *
544  */
545
546 /* helper subroutines */
547
548 void remove_error_progs(void);
549 void fillin_program(prog_t *p);
550 void gen_specials_cache(void);
551 void gen_output_makefile(void);
552 void gen_output_cfile(void);
553
554 void fillin_program_objs(prog_t *p, char *path);
555 void top_makefile_rules(FILE *outmk);
556 void prog_makefile_rules(FILE *outmk, prog_t *p);
557 void output_strlst(FILE *outf, strlst_t *lst);
558 char *genident(char *str);
559 char *dir_search(char *progname);
560
561
562 void gen_outputs(void)
563 {
564         prog_t *p;
565
566         for (p = progs; p != NULL; p = p->next)
567                 fillin_program(p);
568
569         remove_error_progs();
570         gen_specials_cache();
571         gen_output_cfile();
572         gen_output_makefile();
573         status("");
574         fprintf(stderr,
575             "Run \"make -f %s\" to build crunched binary.\n", outmkname);
576 }
577
578 /*
579  * run the makefile for the program to find which objects are necessary
580  */
581 void fillin_program(prog_t *p)
582 {
583         char path[MAXPATHLEN];
584         char line[MAXLINELEN];
585         FILE *f;
586
587         snprintf(line, MAXLINELEN, "filling in parms for %s", p->name);
588         status(line);
589
590         if (!p->ident)
591                 p->ident = genident(p->name);
592
593         /* look for the source directory if one wasn't specified by a special */
594         if (!p->srcdir) {
595                 p->srcdir = dir_search(p->name);
596         }
597
598         /* Determine the actual srcdir (maybe symlinked). */
599         if (p->srcdir) {
600                 snprintf(line, MAXLINELEN, "cd %s && echo -n `/bin/pwd`",
601                     p->srcdir);
602                 f = popen(line,"r");
603                 if (!f)
604                         errx(1, "Can't execute: %s\n", line);
605
606                 path[0] = '\0';
607                 fgets(path, sizeof path, f);
608                 if (pclose(f))
609                         errx(1, "Can't execute: %s\n", line);
610
611                 if (!*path)
612                         errx(1, "Can't perform pwd on: %s\n", p->srcdir);
613
614                 p->realsrcdir = strdup(path);
615         }
616
617         /* Unless the option to make object files was specified the
618         * the objects will be built in the source directory unless
619         * an object directory already exists.
620         */
621         if (!makeobj && !p->objdir && p->srcdir) {
622                 snprintf(line, sizeof line, "%s/%s", objprefix, p->realsrcdir);
623                 if (is_dir(line)) {
624                         if ((p->objdir = strdup(line)) == NULL)
625                         out_of_memory();
626                 } else
627                         p->objdir = p->realsrcdir;
628         }
629
630         /*
631         * XXX look for a Makefile.{name} in local directory first.
632         * This lets us override the original Makefile.
633         */
634         snprintf(path, sizeof(path), "Makefile.%s", p->name);
635         if (is_nonempty_file(path)) {
636                 snprintf(line, MAXLINELEN, "Using %s for %s", path, p->name);
637                 status(line);
638         } else
639                 if (p->srcdir)
640                         snprintf(path, sizeof(path), "%s/Makefile", p->srcdir);
641         if (!p->objs && p->srcdir && is_nonempty_file(path))
642                 fillin_program_objs(p, path);
643
644         if (!p->srcdir && !p->objdir && verbose)
645                 warnx("%s: %s: %s",
646                     "warning: could not find source directory",
647                     infilename, p->name);
648         if (!p->objs && verbose)
649                 warnx("%s: %s: warning: could not find any .o files",
650                     infilename, p->name);
651
652         if ((!p->srcdir || !p->objdir) && !p->objs)
653                 p->goterror = 1;
654 }
655
656 void fillin_program_objs(prog_t *p, char *path)
657 {
658         char *obj, *cp;
659         int fd, rc;
660         FILE *f;
661         char *objvar="OBJS";
662         strlst_t *s;
663         char line[MAXLINELEN];
664
665         /* discover the objs from the srcdir Makefile */
666
667         if ((fd = mkstemp(tempfname)) == -1) {
668                 perror(tempfname);
669                 exit(1);
670         }
671         if ((f = fdopen(fd, "w")) == NULL) {
672                 warn("%s", tempfname);
673                 goterror = 1;
674                 return;
675         }
676         if (p->objvar)
677                 objvar = p->objvar;
678
679         /*
680         * XXX include outhdrname (e.g. to contain Make variables)
681         */
682         if (outhdrname[0] != '\0')
683                 fprintf(f, ".include \"%s\"\n", outhdrname);
684         fprintf(f, ".include \"%s\"\n", path);
685         if (buildopts) {
686                 fprintf(f, "BUILDOPTS+=");
687                 output_strlst(f, buildopts);
688         }
689         fprintf(f, ".if defined(PROG) && !defined(%s)\n", objvar);
690         fprintf(f, "%s=${PROG}.o\n", objvar);
691         fprintf(f, ".endif\n");
692         fprintf(f, "loop:\n\t@echo 'OBJS= '${%s}\n", objvar);
693
694         fprintf(f, "crunchgen_objs:\n\t@make -f %s $(BUILDOPTS) $(%s_OPTS)",
695             tempfname, p->ident);
696         for (s = p->buildopts; s != NULL; s = s->next)
697                 fprintf(f, " %s", s->str);
698         fprintf(f, " loop\n");
699
700         fclose(f);
701
702         snprintf(line, MAXLINELEN, "make -f %s crunchgen_objs 2>&1", tempfname);
703         if ((f = popen(line, "r")) == NULL) {
704                 warn("submake pipe");
705                 goterror = 1;
706                 return;
707         }
708
709         while(fgets(line, MAXLINELEN, f)) {
710                 if (strncmp(line, "OBJS= ", 6)) {
711                         warnx("make error: %s", line);
712                         goterror = 1;
713                         continue;
714                 }
715
716                 cp = line + 6;
717                 while (isspace(*cp))
718                         cp++;
719
720                 while(*cp) {
721                         obj = cp;
722                         while (*cp && !isspace(*cp))
723                                 cp++;
724                         if (*cp)
725                                 *cp++ = '\0';
726                         add_string(&p->objs, obj);
727                         while (isspace(*cp))
728                                 cp++;
729                 }
730         }
731
732         if ((rc=pclose(f)) != 0) {
733                 warnx("make error: make returned %d", rc);
734                 goterror = 1;
735         }
736
737         unlink(tempfname);
738 }
739
740 void remove_error_progs(void)
741 {
742         prog_t *p1, *p2;
743
744         p1 = NULL; p2 = progs;
745         while (p2 != NULL) {
746                 if (!p2->goterror)
747                         p1 = p2, p2 = p2->next;
748                 else {
749                         /* delete it from linked list */
750                         warnx("%s: %s: ignoring program because of errors",
751                             infilename, p2->name);
752                         if (p1)
753                                 p1->next = p2->next;
754                         else
755                                 progs = p2->next;
756                         p2 = p2->next;
757                 }
758         }
759 }
760
761 void gen_specials_cache(void)
762 {
763         FILE *cachef;
764         prog_t *p;
765         char line[MAXLINELEN];
766
767         snprintf(line, MAXLINELEN, "generating %s", cachename);
768         status(line);
769
770         if ((cachef = fopen(cachename, "w")) == NULL) {
771                 warn("%s", cachename);
772                 goterror = 1;
773                 return;
774         }
775
776         fprintf(cachef, "# %s - parm cache generated from %s by crunchgen "
777             " %s\n\n",
778             cachename, infilename, CRUNCH_VERSION);
779
780         for (p = progs; p != NULL; p = p->next) {
781                 fprintf(cachef, "\n");
782                 if (p->srcdir)
783                         fprintf(cachef, "special %s srcdir %s\n",
784                             p->name, p->srcdir);
785                 if (p->objdir)
786                         fprintf(cachef, "special %s objdir %s\n",
787                             p->name, p->objdir);
788                 if (p->objs) {
789                         fprintf(cachef, "special %s objs", p->name);
790                         output_strlst(cachef, p->objs);
791                 }
792                 if (p->objpaths) {
793                         fprintf(cachef, "special %s objpaths", p->name);
794                         output_strlst(cachef, p->objpaths);
795                 }
796         }
797         fclose(cachef);
798 }
799
800
801 void gen_output_makefile(void)
802 {
803         prog_t *p;
804         FILE *outmk;
805         char line[MAXLINELEN];
806
807         snprintf(line, MAXLINELEN, "generating %s", outmkname);
808         status(line);
809
810         if ((outmk = fopen(outmkname, "w")) == NULL) {
811                 warn("%s", outmkname);
812                 goterror = 1;
813                 return;
814         }
815
816         fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n",
817             outmkname, infilename, CRUNCH_VERSION);
818
819         if (outhdrname[0] != '\0')
820                 fprintf(outmk, ".include \"%s\"\n", outhdrname);
821
822         top_makefile_rules(outmk);
823         for (p = progs; p != NULL; p = p->next)
824                 prog_makefile_rules(outmk, p);
825
826         fprintf(outmk, "\n# ========\n");
827         fclose(outmk);
828 }
829
830
831 void gen_output_cfile(void)
832 {
833         extern char *crunched_skel[];
834         char **cp;
835         FILE *outcf;
836         prog_t *p;
837         strlst_t *s;
838         char line[MAXLINELEN];
839
840         snprintf(line, MAXLINELEN, "generating %s", outcfname);
841         status(line);
842
843         if((outcf = fopen(outcfname, "w")) == NULL) {
844                 warn("%s", outcfname);
845                 goterror = 1;
846                 return;
847         }
848
849         fprintf(outcf,
850             "/* %s - generated from %s by crunchgen %s */\n",
851             outcfname, infilename, CRUNCH_VERSION);
852
853         fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname);
854         for (cp = crunched_skel; *cp != NULL; cp++)
855                 fprintf(outcf, "%s\n", *cp);
856
857         for (p = progs; p != NULL; p = p->next)
858                 fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident);
859
860         fprintf(outcf, "\nstruct stub entry_points[] = {\n");
861         for (p = progs; p != NULL; p = p->next) {
862                 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
863                     p->name, p->ident);
864                 for (s = p->links; s != NULL; s = s->next)
865                         fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
866                             s->str, p->ident);
867         }
868
869         fprintf(outcf, "\t{ EXECNAME, crunched_main },\n");
870         fprintf(outcf, "\t{ NULL, NULL }\n};\n");
871         fclose(outcf);
872 }
873
874
875 char *genident(char *str)
876 {
877         char *n, *s, *d;
878
879         /*
880          * generates a Makefile/C identifier from a program name,
881          * mapping '-' to '_' and ignoring all other non-identifier
882          * characters.  This leads to programs named "foo.bar" and
883          * "foobar" to map to the same identifier.
884          */
885
886         if ((n = strdup(str)) == NULL)
887                 return NULL;
888         for (d = s = n; *s != '\0'; s++) {
889                 if (*s == '-')
890                         *d++ = '_';
891                 else if (*s == '_' || isalnum(*s))
892                         *d++ = *s;
893         }
894         *d = '\0';
895         return n;
896 }
897
898
899 char *dir_search(char *progname)
900 {
901         char path[MAXPATHLEN];
902         strlst_t *dir;
903         char *srcdir;
904
905         for (dir = srcdirs; dir != NULL; dir = dir->next) {
906                 snprintf(path, MAXPATHLEN, "%s/%s", dir->str, progname);
907                 if (!is_dir(path))
908                         continue;
909
910                 if ((srcdir = strdup(path)) == NULL)
911                         out_of_memory();
912
913                 return srcdir;
914         }
915         return NULL;
916 }
917
918
919 void top_makefile_rules(FILE *outmk)
920 {
921         prog_t *p;
922
923         fprintf(outmk, "LIBS+=");
924         output_strlst(outmk, libs);
925
926         if (makeobj) {
927                 fprintf(outmk, "MAKEOBJDIRPREFIX?=%s\n", objprefix);
928                 fprintf(outmk, "MAKE=env MAKEOBJDIRPREFIX=$(MAKEOBJDIRPREFIX) "
929                     "make\n");
930         } else {
931                 fprintf(outmk, "MAKE=make\n");
932         }
933
934         if (buildopts) {
935                 fprintf(outmk, "BUILDOPTS+=");
936                 output_strlst(outmk, buildopts);
937         }
938
939         fprintf(outmk, "CRUNCHED_OBJS=");
940         for (p = progs; p != NULL; p = p->next)
941                 fprintf(outmk, " %s.lo", p->name);
942         fprintf(outmk, "\n");
943
944         fprintf(outmk, "SUBMAKE_TARGETS=");
945         for (p = progs; p != NULL; p = p->next)
946                 fprintf(outmk, " %s_make", p->ident);
947         fprintf(outmk, "\nSUBCLEAN_TARGETS=");
948         for (p = progs; p != NULL; p = p->next)
949                 fprintf(outmk, " %s_clean", p->ident);
950         fprintf(outmk, "\n\n");
951
952         fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n");
953         fprintf(outmk, "exe: %s\n", execfname);
954         fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS)\n", execfname, execfname);
955         fprintf(outmk, "\t$(CC) -static -o %s %s.o $(CRUNCHED_OBJS) $(LIBS)\n",
956             execfname, execfname);
957         fprintf(outmk, "\tstrip %s\n", execfname);
958         fprintf(outmk, "realclean: clean subclean\n");
959         fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n", execfname);
960         fprintf(outmk, "subclean: $(SUBCLEAN_TARGETS)\n");
961 }
962
963
964 void prog_makefile_rules(FILE *outmk, prog_t *p)
965 {
966         strlst_t *lst;
967
968         fprintf(outmk, "\n# -------- %s\n\n", p->name);
969
970         fprintf(outmk, "%s_OBJDIR=", p->ident);
971         if (p->objdir)
972                 fprintf(outmk, "%s", p->objdir);
973         else
974                 fprintf(outmk, "$(MAKEOBJDIRPREFIX)/$(%s_REALSRCDIR)\n",
975                     p->ident);
976         fprintf(outmk, "\n");
977
978         if (p->srcdir && p->objs) {
979                 fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir);
980                 fprintf(outmk, "%s_REALSRCDIR=%s\n", p->ident, p->realsrcdir);
981
982                 fprintf(outmk, "%s_OBJS=", p->ident);
983                 output_strlst(outmk, p->objs);
984                 if (p->buildopts != NULL) {
985                         fprintf(outmk, "%s_OPTS+=", p->ident);
986                         output_strlst(outmk, p->buildopts);
987                 }
988                 fprintf(outmk, "%s_make:\n", p->ident);
989                 fprintf(outmk, "\t(cd $(%s_SRCDIR) && ", p->ident);
990                 if (makeobj)
991                         fprintf(outmk, "$(MAKE) obj && ");
992                 fprintf(outmk, "\\\n");
993                 fprintf(outmk, "\t\t$(MAKE) $(BUILDOPTS) $(%s_OPTS) depend &&",
994                     p->ident);
995                 fprintf(outmk, "\\\n");
996                 fprintf(outmk, "\t\t$(MAKE) $(BUILDOPTS) $(%s_OPTS) "
997                     "$(%s_OBJS))",
998                     p->ident, p->ident);
999                 fprintf(outmk, "\n");
1000                 fprintf(outmk, "%s_clean:\n", p->ident);
1001                 fprintf(outmk, "\t(cd $(%s_SRCDIR) && $(MAKE) $(BUILDOPTS) clean cleandepend)\n\n",
1002                     p->ident);
1003         } else {
1004                 fprintf(outmk, "%s_make:\n", p->ident);
1005                 fprintf(outmk, "\t@echo \"** cannot make objs for %s\"\n\n",
1006                     p->name);
1007         }
1008
1009         fprintf(outmk, "%s_OBJPATHS=", p->ident);
1010         if (p->objpaths)
1011                 output_strlst(outmk, p->objpaths);
1012         else {
1013                 for (lst = p->objs; lst != NULL; lst = lst->next) {
1014                         fprintf(outmk, " $(%s_OBJDIR)/%s", p->ident, lst->str);
1015                 }
1016                 fprintf(outmk, "\n");
1017         }
1018         if (p->libs) {
1019                 fprintf(outmk, "%s_LIBS=", p->ident);
1020                 output_strlst(outmk, p->libs);
1021         }
1022
1023         fprintf(outmk, "%s_stub.c:\n", p->name);
1024         fprintf(outmk, "\techo \""
1025             "int _crunched_%s_stub(int argc, char **argv, char **envp)"
1026             "{return main(argc,argv,envp);}\" >%s_stub.c\n",
1027             p->ident, p->name);
1028         fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)",
1029             p->name, p->name, p->ident);
1030         if (p->libs)
1031                 fprintf(outmk, " $(%s_LIBS)", p->ident);
1032         fprintf(outmk, "\n");
1033         fprintf(outmk, "\tld -dc -r -o %s.lo %s_stub.o $(%s_OBJPATHS)",
1034             p->name, p->name, p->ident);
1035         if (p->libs)
1036                 fprintf(outmk, " $(%s_LIBS)", p->ident);
1037         fprintf(outmk, "\n");
1038         fprintf(outmk, "\tcrunchide -k _crunched_%s_stub ", p->ident);
1039         for (lst = p->keeplist; lst != NULL; lst = lst->next)
1040                 fprintf(outmk, "-k _%s ", lst->str);
1041         fprintf(outmk, "%s.lo\n", p->name);
1042 }
1043
1044 void output_strlst(FILE *outf, strlst_t *lst)
1045 {
1046         for (; lst != NULL; lst = lst->next)
1047                 fprintf(outf, " %s", lst->str);
1048         fprintf(outf, "\n");
1049 }
1050
1051
1052 /*
1053  * ========================================================================
1054  * general library routines
1055  *
1056  */
1057
1058 void status(char *str)
1059 {
1060         static int lastlen = 0;
1061         int len, spaces;
1062
1063         if (!verbose)
1064                 return;
1065
1066         len = strlen(str);
1067         spaces = lastlen - len;
1068         if (spaces < 1)
1069                 spaces = 1;
1070
1071         fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " ");
1072         fflush(stderr);
1073         lastlen = len;
1074 }
1075
1076
1077 void out_of_memory(void)
1078 {
1079         err(1, "%s: %d: out of memory, stopping", infilename, linenum);
1080 }
1081
1082
1083 void add_string(strlst_t **listp, char *str)
1084 {
1085         strlst_t *p1, *p2;
1086
1087         /* add to end, but be smart about dups */
1088
1089         for (p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next)
1090                 if (!strcmp(p2->str, str))
1091                         return;
1092
1093         p2 = malloc(sizeof(strlst_t));
1094         if (p2) {
1095                 p2->next = NULL;
1096                 p2->str = strdup(str);
1097         }
1098         if (!p2 || !p2->str)
1099                 out_of_memory();
1100
1101         if (p1 == NULL)
1102                 *listp = p2;
1103         else
1104                 p1->next = p2;
1105 }
1106
1107
1108 int is_dir(char *pathname)
1109 {
1110         struct stat buf;
1111
1112         if (stat(pathname, &buf) == -1)
1113                 return 0;
1114
1115         return S_ISDIR(buf.st_mode);
1116 }
1117
1118 int is_nonempty_file(char *pathname)
1119 {
1120         struct stat buf;
1121
1122         if (stat(pathname, &buf) == -1)
1123                 return 0;
1124
1125         return S_ISREG(buf.st_mode) && buf.st_size > 0;
1126 }