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