Merge from vendor branch FILE:
[dragonfly.git] / usr.sbin / config / main.c
1 /*
2  * Copyright (c) 1980, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1980, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)main.c   8.1 (Berkeley) 6/6/93
35  * $FreeBSD: src/usr.sbin/config/main.c,v 1.37.2.3 2001/06/13 00:25:53 cg Exp $
36  * $DragonFly: src/usr.sbin/config/main.c,v 1.19 2006/11/07 06:57:02 dillon Exp $
37  */
38
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/file.h>
42 #include <sys/mman.h>
43 #include <sys/param.h>
44 #include <ctype.h>
45 #include <err.h>
46 #include <stdio.h>
47 #include <dirent.h>
48 #include <sysexits.h>
49 #include <unistd.h>
50 #include "y.tab.h"
51 #include "config.h"
52
53 #ifndef TRUE
54 #define TRUE    (1)
55 #endif
56
57 #ifndef FALSE
58 #define FALSE   (0)
59 #endif
60
61 #define CDIR    "../compile/"
62
63 char *  PREFIX;
64 char    destdir[MAXPATHLEN];
65 char    srcdir[MAXPATHLEN];
66
67 static int no_config_clobber = TRUE;
68 int     debugging;
69 int     profiling;
70
71 static void configfile(void);
72 static void get_srcdir(void);
73 static void generate_forwarding_headers(void);
74 static void usage(void);
75
76 /*
77  * Config builds a set of files for building a UNIX
78  * system given a description of the desired system.
79  */
80 int
81 main(int argc, char *argv[])
82 {
83         struct stat buf;
84         int ch, len;
85         unsigned int i;
86         char *p;
87         char linksrc[64], linkdest[MAXPATHLEN];
88         static const char *emus[] = { "linux" };
89
90         while ((ch = getopt(argc, argv, "d:gprn")) != -1)
91                 switch (ch) {
92                 case 'd':
93                         if (*destdir == '\0')
94                                 strlcpy(destdir, optarg, sizeof(destdir));
95                         else
96                                 errx(2, "directory already set");
97                         break;
98                 case 'g':
99                         debugging++;
100                         break;
101                 case 'p':
102                         profiling++;
103                         break;
104                 case 'n':
105                         /* no_config_clobber is now true by default, no-op */
106                         fprintf(stderr,
107                                 "*** Using obsolete config option '-n' ***\n");
108                         break;
109                 case 'r':
110                         no_config_clobber = FALSE;
111                         break;
112                 case '?':
113                 default:
114                         usage();
115                 }
116         argc -= optind;
117         argv += optind;
118
119         if (argc != 1)
120                 usage();
121
122         if (freopen(PREFIX = *argv, "r", stdin) == NULL)
123                 err(2, "%s", PREFIX);
124
125         if (*destdir != '\0') {
126                 len = strlen(destdir);
127                 while (len > 1 && destdir[len - 1] == '/')
128                         destdir[--len] = '\0';
129                 get_srcdir();
130         } else {
131                 strlcpy(destdir, CDIR, sizeof(destdir));
132                 strlcat(destdir, PREFIX, sizeof(destdir));
133         }
134
135         p = path((char *)NULL);
136         if (stat(p, &buf)) {
137                 if (mkdir(p, 0777))
138                         err(2, "%s", p);
139         }
140         else if ((buf.st_mode & S_IFMT) != S_IFDIR) {
141                 errx(2, "%s isn't a directory", p);
142         }
143         else if (!no_config_clobber) {
144                 char tmp[strlen(p) + 8];
145
146                 fprintf(stderr, "Removing old directory %s:  ", p);
147                 fflush(stderr);
148                 snprintf(tmp, sizeof(tmp), "rm -rf %s", p);
149                 if (system(tmp)) {
150                         fprintf(stderr, "Failed!\n");
151                         err(2, "%s", tmp);
152                 }
153                 fprintf(stderr, "Done.\n");
154                 if (mkdir(p, 0777))
155                         err(2, "%s", p);
156         }
157
158         dtab = NULL;
159         if (yyparse())
160                 exit(3);
161         if (machinename == NULL) {
162                 printf("Specify platform architecture, e.g. 'machine pc32'\n");
163                 exit(1);
164         }
165         if (machinearchname == NULL) {
166                 printf("Specify cpu architecture, e.g. 'machine_arch i386'\n");
167                 exit(1);
168         }
169         newbus_ioconf();
170         
171         /*
172          * "machine" points into machine/<MACHINE>/include
173          */
174         if (*srcdir == '\0')
175                 snprintf(linkdest, sizeof(linkdest), "../../machine/%s/include",
176                     machinename);
177         else
178                 snprintf(linkdest, sizeof(linkdest), "%s/machine/%s/include",
179                     srcdir, machinename);
180         symlink(linkdest, path("machine"));
181
182         /*
183          * "machine_base" points into machine/<MACHINE>
184          */
185         if (*srcdir == '\0')
186                 snprintf(linkdest, sizeof(linkdest), "../../machine/%s",
187                     machinename);
188         else
189                 snprintf(linkdest, sizeof(linkdest), "%s/machine/%s",
190                     srcdir, machinename);
191         symlink(linkdest, path("machine_base"));
192
193         /*
194          * "cpu" points to cpu/<MACHINE_ARCH>/include
195          */
196         if (*srcdir == '\0')
197                 snprintf(linkdest, sizeof(linkdest),
198                          "../../cpu/%s/include", machinearchname);
199         else
200                 snprintf(linkdest, sizeof(linkdest),
201                          "%s/cpu/%s/include", srcdir, machinearchname);
202         symlink(linkdest, path("cpu"));
203
204         /*
205          * "cpu_base" points to cpu/<MACHINE_ARCH>
206          */
207         if (*srcdir == '\0')
208                 snprintf(linkdest, sizeof(linkdest), "../../cpu/%s",
209                     machinearchname);
210         else
211                 snprintf(linkdest, sizeof(linkdest), "%s/cpu/%s",
212                     srcdir, machinearchname);
213         symlink(linkdest, path("cpu_base"));
214
215         /*
216          * Create include/machine, in which we place forwarding header files
217          * for headers that exist in the cpu architecture but have not been
218          * overridden in the platform architecture.
219          */
220         mkdir(path("include"), 0755);
221         mkdir(path("include/machine"), 0755);
222         generate_forwarding_headers();
223
224         /*
225          * XXX check directory structure for architecture subdirectories and
226          * create the symlinks automatically XXX
227          */
228         if (*srcdir == '\0')
229                 snprintf(linkdest, sizeof(linkdest),
230                     "../../../../../net/i4b/include/%s",
231                     machinearchname);
232         else
233                 snprintf(linkdest, sizeof(linkdest), "%s/net/i4b/include/%s",
234                     srcdir, machinearchname);
235         mkdir(path("net"), 0755);
236         mkdir(path("net/i4b"), 0755);
237         mkdir(path("net/i4b/include"), 0755);
238         symlink(linkdest, path("net/i4b/include/machine"));
239
240         for (i = 0; i < sizeof(emus) / sizeof(emus[0]); ++i) {
241                 if (*srcdir == 0)  {
242                         snprintf(linkdest, sizeof(linkdest),
243                             "../../emulation/%s/%s",
244                             emus[i], machinearchname);
245                 } else {
246                         snprintf(linkdest, sizeof(linkdest),
247                             "%s/emulation/%s/%s",
248                             srcdir, emus[i], machinearchname);
249                 }
250                 snprintf(linksrc, sizeof(linksrc), "arch_%s", emus[i]);
251                 symlink(linkdest, path(linksrc));
252         }
253
254         options();                      /* make options .h files */
255         makefile();                     /* build Makefile */
256         headers();                      /* make a lot of .h files */
257         configfile();                   /* put config file into kernel*/
258         printf("Kernel build directory is %s\n", p);
259         exit(EX_OK);
260 }
261
262 /*
263  * get_srcdir
264  *      determine the root of the kernel source tree
265  *      and save that in srcdir.
266  */
267 static void
268 get_srcdir(void)
269 {
270         
271         if (realpath("..", srcdir) == NULL)
272                 errx(2, "Unable to find root of source tree");
273 }
274
275 static void
276 usage(void)
277 {
278         
279         fprintf(stderr, "usage: config [-gpr] [-d destdir] sysname\n");
280         exit(1);
281 }
282
283 /*
284  * get_word
285  *      returns EOF on end of file
286  *      NULL on end of line
287  *      pointer to the word otherwise
288  */
289 char *
290 get_word(FILE *fp)
291 {
292         static char line[80];
293         int ch;
294         char *cp;
295         int escaped_nl = 0;
296
297 begin:
298         while ((ch = getc(fp)) != EOF)
299                 if (ch != ' ' && ch != '\t')
300                         break;
301         if (ch == EOF)
302                 return((char *)EOF);
303         if (ch == '\\') {
304                 escaped_nl = 1;
305                 goto begin;
306         }
307         if (ch == '\n') {
308                 if (escaped_nl) {
309                         escaped_nl = 0;
310                         goto begin;
311                 }
312                 else
313                         return(NULL);
314         }
315         cp = line;
316         *cp++ = ch;
317         while ((ch = getc(fp)) != EOF) {
318                 if (isspace(ch))
319                         break;
320                 *cp++ = ch;
321         }
322         *cp = 0;
323         if (ch == EOF)
324                 return((char *)EOF);
325         ungetc(ch, fp);
326         return(line);
327 }
328
329 /*
330  * get_quoted_word
331  *      like get_word but will accept something in double or single quotes
332  *      (to allow embedded spaces).
333  */
334 char *
335 get_quoted_word(FILE *fp)
336 {
337         static char line[256];
338         int ch;
339         char *cp;
340         int escaped_nl = 0;
341
342 begin:
343         while ((ch = getc(fp)) != EOF)
344                 if (ch != ' ' && ch != '\t')
345                         break;
346         if (ch == EOF)
347                 return((char *)EOF);
348         if (ch == '\\') {
349                 escaped_nl = 1;
350                 goto begin;
351         }
352         if (ch == '\n') {
353                 if (escaped_nl) {
354                         escaped_nl = 0;
355                         goto begin;
356                 }
357                 else
358                         return(NULL);
359         }
360         cp = line;
361         if (ch == '"' || ch == '\'') {
362                 int quote = ch;
363
364                 while ((ch = getc(fp)) != EOF) {
365                         if (ch == quote)
366                                 break;
367                         if (ch == '\n') {
368                                 *cp = 0;
369                                 printf("config: missing quote reading `%s'\n",
370                                         line);
371                                 exit(2);
372                         }
373                         *cp++ = ch;
374                 }
375         } else {
376                 *cp++ = ch;
377                 while ((ch = getc(fp)) != EOF) {
378                         if (isspace(ch))
379                                 break;
380                         *cp++ = ch;
381                 }
382                 if (ch != EOF)
383                         ungetc(ch, fp);
384         }
385         *cp = 0;
386         if (ch == EOF)
387                 return((char *)EOF);
388         return(line);
389 }
390
391 /*
392  * prepend the path to a filename
393  */
394 char *
395 path(const char *file)
396 {
397         char *cp;
398
399         cp = malloc((size_t)(strlen(destdir) + (file ? strlen(file) : 0) + 2));
400         strcpy(cp, destdir);
401         if (file != NULL) {
402                 strcat(cp, "/");
403                 strcat(cp, file);
404         }
405         return(cp);
406 }
407
408 static void
409 configfile(void)
410 {
411         FILE *fi, *fo;
412         char *p;
413         int i;
414         
415         fi = fopen(PREFIX, "r");
416         if (fi == NULL)
417                 err(2, "%s", PREFIX);
418         fo = fopen(p = path("config.c.new"), "w");
419         if (fo == NULL)
420                 err(2, "%s", p);
421         fprintf(fo, "#include \"opt_config.h\"\n");
422         fprintf(fo, "#ifdef INCLUDE_CONFIG_FILE \n");
423         fprintf(fo, "static const char config[] = \"\\\n");
424         fprintf(fo, "START CONFIG FILE %s\\n\\\n___", PREFIX);
425         while (EOF != (i = getc(fi))) {
426                 if (i == '\n') {
427                         fprintf(fo, "\\n\\\n___");
428                 } else if (i == '\"') {
429                         fprintf(fo, "\\\"");
430                 } else if (i == '\\') {
431                         fprintf(fo, "\\\\");
432                 } else {
433                         putc(i, fo);
434                 }
435         }
436         fprintf(fo, "\\n\\\nEND CONFIG FILE %s\\n\\\n", PREFIX);
437         fprintf(fo, "\";\n");
438         fprintf(fo, "\n#endif /* INCLUDE_CONFIG_FILE */\n");
439         fclose(fi);
440         fclose(fo);
441         moveifchanged(path("config.c.new"), path("config.c"));
442 }
443
444 /*
445  * moveifchanged --
446  *      compare two files; rename if changed.
447  */
448 void
449 moveifchanged(const char *from_name, const char *to_name)
450 {
451         char *p, *q;
452         int changed;
453         size_t tsize;
454         struct stat from_sb, to_sb;
455         int from_fd, to_fd;
456
457         changed = 0;
458
459         if ((from_fd = open(from_name, O_RDONLY)) < 0)
460                 err(EX_OSERR, "moveifchanged open(%s)", from_name);
461
462         if ((to_fd = open(to_name, O_RDONLY)) < 0)
463                 changed++;
464
465         if (!changed && fstat(from_fd, &from_sb) < 0)
466                 err(EX_OSERR, "moveifchanged fstat(%s)", from_name);
467
468         if (!changed && fstat(to_fd, &to_sb) < 0)
469                 err(EX_OSERR, "moveifchanged fstat(%s)", to_name);
470
471         if (!changed && from_sb.st_size != to_sb.st_size)
472                 changed++;
473
474         tsize = (size_t)from_sb.st_size;
475
476         if (!changed) {
477                 p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
478 #ifndef MAP_FAILED
479 #define MAP_FAILED ((caddr_t)-1)
480 #endif
481                 if (p == MAP_FAILED)
482                         err(EX_OSERR, "mmap %s", from_name);
483                 q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
484                 if (q == MAP_FAILED)
485                         err(EX_OSERR, "mmap %s", to_name);
486
487                 changed = memcmp(p, q, tsize);
488                 munmap(p, tsize);
489                 munmap(q, tsize);
490         }
491         if (changed) {
492                 if (rename(from_name, to_name) < 0)
493                         err(EX_OSERR, "rename(%s, %s)", from_name, to_name);
494         } else {
495                 if (unlink(from_name) < 0)
496                         err(EX_OSERR, "unlink(%s)", from_name);
497         }
498 }
499
500 static
501 void
502 generate_forwarding_headers(void)
503 {
504         char buf[MAXPATHLEN];
505         char upper[256];
506         char *ptr;
507         struct dirent *den;
508         DIR *dir;
509         FILE *fp;
510
511         if (*srcdir == '\0')
512                 snprintf(buf, sizeof(buf), "../../cpu/%s/include",
513                     machinearchname);
514         else
515                 snprintf(buf, sizeof(buf), "%s/cpu/%s/include",
516                     srcdir, machinearchname);
517
518         if ((dir = opendir(buf)) != NULL) {
519                 while ((den = readdir(dir)) != NULL) {
520                         if (den->d_name[0] == '.')
521                                 continue;
522
523                         /*
524                          * Is this a header file?
525                          */
526                         snprintf(upper, sizeof(upper), "%s", den->d_name);
527                         ptr = strrchr(upper, '.');
528                         if (ptr == NULL || strcmp(ptr, ".h") != 0)
529                                 continue;
530
531                         /*
532                          * Generate an upper case symbol 
533                          */
534                         *ptr = 0;
535                         for (ptr = upper; *ptr; ++ptr) {
536                             if (*ptr == '.')
537                                 *ptr = '_';
538                             else if (islower(*ptr))
539                                 *ptr = toupper(*ptr);
540                         }
541
542                         /*
543                          * Generate a forwarding header file
544                          */
545                         snprintf(buf, sizeof(buf), "%s/%s",
546                                 path("include/machine"), den->d_name);
547                         fp = fopen(buf, "w");
548                         fprintf(fp, "/*\n"
549                                     " * CONFIG-GENERATED FILE, DO NOT EDIT\n"
550                                     " */\n"
551                                     "\n"
552                                     "#ifndef _MACHINE_%s_H_\n"
553                                     "#define _MACHINE_%s_H_\n"
554                                     "#include <cpu/%s>\n"
555                                     "#endif\n"
556                                     "\n",
557                                     upper,
558                                     upper,
559                                     den->d_name
560                         );
561                         fclose(fp);
562                 }
563                 closedir(dir);
564         }
565 }
566