Merge from vendor branch GCC:
[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.20 2006/11/19 07:49:34 sephe 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 usage(void);
74
75 /*
76  * Config builds a set of files for building a UNIX
77  * system given a description of the desired system.
78  */
79 int
80 main(int argc, char *argv[])
81 {
82         struct stat buf;
83         int ch, len;
84         unsigned int i;
85         char *p;
86         char linksrc[64], linkdest[MAXPATHLEN];
87         static const char *emus[] = { "linux" };
88
89         while ((ch = getopt(argc, argv, "d:gprn")) != -1)
90                 switch (ch) {
91                 case 'd':
92                         if (*destdir == '\0')
93                                 strlcpy(destdir, optarg, sizeof(destdir));
94                         else
95                                 errx(2, "directory already set");
96                         break;
97                 case 'g':
98                         debugging++;
99                         break;
100                 case 'p':
101                         profiling++;
102                         break;
103                 case 'n':
104                         /* no_config_clobber is now true by default, no-op */
105                         fprintf(stderr,
106                                 "*** Using obsolete config option '-n' ***\n");
107                         break;
108                 case 'r':
109                         no_config_clobber = FALSE;
110                         break;
111                 case '?':
112                 default:
113                         usage();
114                 }
115         argc -= optind;
116         argv += optind;
117
118         if (argc != 1)
119                 usage();
120
121         if (freopen(PREFIX = *argv, "r", stdin) == NULL)
122                 err(2, "%s", PREFIX);
123
124         if (*destdir != '\0') {
125                 len = strlen(destdir);
126                 while (len > 1 && destdir[len - 1] == '/')
127                         destdir[--len] = '\0';
128                 get_srcdir();
129         } else {
130                 strlcpy(destdir, CDIR, sizeof(destdir));
131                 strlcat(destdir, PREFIX, sizeof(destdir));
132         }
133
134         p = path((char *)NULL);
135         if (stat(p, &buf)) {
136                 if (mkdir(p, 0777))
137                         err(2, "%s", p);
138         }
139         else if ((buf.st_mode & S_IFMT) != S_IFDIR) {
140                 errx(2, "%s isn't a directory", p);
141         }
142         else if (!no_config_clobber) {
143                 char tmp[strlen(p) + 8];
144
145                 fprintf(stderr, "Removing old directory %s:  ", p);
146                 fflush(stderr);
147                 snprintf(tmp, sizeof(tmp), "rm -rf %s", p);
148                 if (system(tmp)) {
149                         fprintf(stderr, "Failed!\n");
150                         err(2, "%s", tmp);
151                 }
152                 fprintf(stderr, "Done.\n");
153                 if (mkdir(p, 0777))
154                         err(2, "%s", p);
155         }
156
157         dtab = NULL;
158         if (yyparse())
159                 exit(3);
160         if (machinename == NULL) {
161                 printf("Specify platform architecture, e.g. 'machine pc32'\n");
162                 exit(1);
163         }
164         if (machinearchname == NULL) {
165                 printf("Specify cpu architecture, e.g. 'machine_arch i386'\n");
166                 exit(1);
167         }
168         newbus_ioconf();
169         
170         /*
171          * "machine" points into machine/<MACHINE>/include
172          */
173         if (*srcdir == '\0')
174                 snprintf(linkdest, sizeof(linkdest), "../../machine/%s/include",
175                     machinename);
176         else
177                 snprintf(linkdest, sizeof(linkdest), "%s/machine/%s/include",
178                     srcdir, machinename);
179         symlink(linkdest, path("machine"));
180
181         /*
182          * "machine_base" points into machine/<MACHINE>
183          */
184         if (*srcdir == '\0')
185                 snprintf(linkdest, sizeof(linkdest), "../../machine/%s",
186                     machinename);
187         else
188                 snprintf(linkdest, sizeof(linkdest), "%s/machine/%s",
189                     srcdir, machinename);
190         symlink(linkdest, path("machine_base"));
191
192         /*
193          * "cpu" points to cpu/<MACHINE_ARCH>/include
194          */
195         if (*srcdir == '\0')
196                 snprintf(linkdest, sizeof(linkdest),
197                          "../../cpu/%s/include", machinearchname);
198         else
199                 snprintf(linkdest, sizeof(linkdest),
200                          "%s/cpu/%s/include", srcdir, machinearchname);
201         symlink(linkdest, path("cpu"));
202
203         /*
204          * "cpu_base" points to cpu/<MACHINE_ARCH>
205          */
206         if (*srcdir == '\0')
207                 snprintf(linkdest, sizeof(linkdest), "../../cpu/%s",
208                     machinearchname);
209         else
210                 snprintf(linkdest, sizeof(linkdest), "%s/cpu/%s",
211                     srcdir, machinearchname);
212         symlink(linkdest, path("cpu_base"));
213
214         /*
215          * XXX check directory structure for architecture subdirectories and
216          * create the symlinks automatically XXX
217          */
218         if (*srcdir == '\0')
219                 snprintf(linkdest, sizeof(linkdest),
220                     "../../../../../net/i4b/include/%s",
221                     machinearchname);
222         else
223                 snprintf(linkdest, sizeof(linkdest), "%s/net/i4b/include/%s",
224                     srcdir, machinearchname);
225         mkdir(path("net"), 0755);
226         mkdir(path("net/i4b"), 0755);
227         mkdir(path("net/i4b/include"), 0755);
228         symlink(linkdest, path("net/i4b/include/machine"));
229
230         for (i = 0; i < sizeof(emus) / sizeof(emus[0]); ++i) {
231                 if (*srcdir == 0)  {
232                         snprintf(linkdest, sizeof(linkdest),
233                             "../../emulation/%s/%s",
234                             emus[i], machinearchname);
235                 } else {
236                         snprintf(linkdest, sizeof(linkdest),
237                             "%s/emulation/%s/%s",
238                             srcdir, emus[i], machinearchname);
239                 }
240                 snprintf(linksrc, sizeof(linksrc), "arch_%s", emus[i]);
241                 symlink(linkdest, path(linksrc));
242         }
243
244         options();                      /* make options .h files */
245         makefile();                     /* build Makefile */
246         headers();                      /* make a lot of .h files */
247         configfile();                   /* put config file into kernel*/
248         printf("Kernel build directory is %s\n", p);
249         exit(EX_OK);
250 }
251
252 /*
253  * get_srcdir
254  *      determine the root of the kernel source tree
255  *      and save that in srcdir.
256  */
257 static void
258 get_srcdir(void)
259 {
260         
261         if (realpath("..", srcdir) == NULL)
262                 errx(2, "Unable to find root of source tree");
263 }
264
265 static void
266 usage(void)
267 {
268         
269         fprintf(stderr, "usage: config [-gpr] [-d destdir] sysname\n");
270         exit(1);
271 }
272
273 /*
274  * get_word
275  *      returns EOF on end of file
276  *      NULL on end of line
277  *      pointer to the word otherwise
278  */
279 char *
280 get_word(FILE *fp)
281 {
282         static char line[80];
283         int ch;
284         char *cp;
285         int escaped_nl = 0;
286
287 begin:
288         while ((ch = getc(fp)) != EOF)
289                 if (ch != ' ' && ch != '\t')
290                         break;
291         if (ch == EOF)
292                 return((char *)EOF);
293         if (ch == '\\') {
294                 escaped_nl = 1;
295                 goto begin;
296         }
297         if (ch == '\n') {
298                 if (escaped_nl) {
299                         escaped_nl = 0;
300                         goto begin;
301                 }
302                 else
303                         return(NULL);
304         }
305         cp = line;
306         *cp++ = ch;
307         while ((ch = getc(fp)) != EOF) {
308                 if (isspace(ch))
309                         break;
310                 *cp++ = ch;
311         }
312         *cp = 0;
313         if (ch == EOF)
314                 return((char *)EOF);
315         ungetc(ch, fp);
316         return(line);
317 }
318
319 /*
320  * get_quoted_word
321  *      like get_word but will accept something in double or single quotes
322  *      (to allow embedded spaces).
323  */
324 char *
325 get_quoted_word(FILE *fp)
326 {
327         static char line[256];
328         int ch;
329         char *cp;
330         int escaped_nl = 0;
331
332 begin:
333         while ((ch = getc(fp)) != EOF)
334                 if (ch != ' ' && ch != '\t')
335                         break;
336         if (ch == EOF)
337                 return((char *)EOF);
338         if (ch == '\\') {
339                 escaped_nl = 1;
340                 goto begin;
341         }
342         if (ch == '\n') {
343                 if (escaped_nl) {
344                         escaped_nl = 0;
345                         goto begin;
346                 }
347                 else
348                         return(NULL);
349         }
350         cp = line;
351         if (ch == '"' || ch == '\'') {
352                 int quote = ch;
353
354                 while ((ch = getc(fp)) != EOF) {
355                         if (ch == quote)
356                                 break;
357                         if (ch == '\n') {
358                                 *cp = 0;
359                                 printf("config: missing quote reading `%s'\n",
360                                         line);
361                                 exit(2);
362                         }
363                         *cp++ = ch;
364                 }
365         } else {
366                 *cp++ = ch;
367                 while ((ch = getc(fp)) != EOF) {
368                         if (isspace(ch))
369                                 break;
370                         *cp++ = ch;
371                 }
372                 if (ch != EOF)
373                         ungetc(ch, fp);
374         }
375         *cp = 0;
376         if (ch == EOF)
377                 return((char *)EOF);
378         return(line);
379 }
380
381 /*
382  * prepend the path to a filename
383  */
384 char *
385 path(const char *file)
386 {
387         char *cp;
388
389         cp = malloc((size_t)(strlen(destdir) + (file ? strlen(file) : 0) + 2));
390         strcpy(cp, destdir);
391         if (file != NULL) {
392                 strcat(cp, "/");
393                 strcat(cp, file);
394         }
395         return(cp);
396 }
397
398 static void
399 configfile(void)
400 {
401         FILE *fi, *fo;
402         char *p;
403         int i;
404         
405         fi = fopen(PREFIX, "r");
406         if (fi == NULL)
407                 err(2, "%s", PREFIX);
408         fo = fopen(p = path("config.c.new"), "w");
409         if (fo == NULL)
410                 err(2, "%s", p);
411         fprintf(fo, "#include \"opt_config.h\"\n");
412         fprintf(fo, "#ifdef INCLUDE_CONFIG_FILE \n");
413         fprintf(fo, "static const char config[] = \"\\\n");
414         fprintf(fo, "START CONFIG FILE %s\\n\\\n___", PREFIX);
415         while (EOF != (i = getc(fi))) {
416                 if (i == '\n') {
417                         fprintf(fo, "\\n\\\n___");
418                 } else if (i == '\"') {
419                         fprintf(fo, "\\\"");
420                 } else if (i == '\\') {
421                         fprintf(fo, "\\\\");
422                 } else {
423                         putc(i, fo);
424                 }
425         }
426         fprintf(fo, "\\n\\\nEND CONFIG FILE %s\\n\\\n", PREFIX);
427         fprintf(fo, "\";\n");
428         fprintf(fo, "\n#endif /* INCLUDE_CONFIG_FILE */\n");
429         fclose(fi);
430         fclose(fo);
431         moveifchanged(path("config.c.new"), path("config.c"));
432 }
433
434 /*
435  * moveifchanged --
436  *      compare two files; rename if changed.
437  */
438 void
439 moveifchanged(const char *from_name, const char *to_name)
440 {
441         char *p, *q;
442         int changed;
443         size_t tsize;
444         struct stat from_sb, to_sb;
445         int from_fd, to_fd;
446
447         changed = 0;
448
449         if ((from_fd = open(from_name, O_RDONLY)) < 0)
450                 err(EX_OSERR, "moveifchanged open(%s)", from_name);
451
452         if ((to_fd = open(to_name, O_RDONLY)) < 0)
453                 changed++;
454
455         if (!changed && fstat(from_fd, &from_sb) < 0)
456                 err(EX_OSERR, "moveifchanged fstat(%s)", from_name);
457
458         if (!changed && fstat(to_fd, &to_sb) < 0)
459                 err(EX_OSERR, "moveifchanged fstat(%s)", to_name);
460
461         if (!changed && from_sb.st_size != to_sb.st_size)
462                 changed++;
463
464         tsize = (size_t)from_sb.st_size;
465
466         if (!changed) {
467                 p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
468 #ifndef MAP_FAILED
469 #define MAP_FAILED ((caddr_t)-1)
470 #endif
471                 if (p == MAP_FAILED)
472                         err(EX_OSERR, "mmap %s", from_name);
473                 q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
474                 if (q == MAP_FAILED)
475                         err(EX_OSERR, "mmap %s", to_name);
476
477                 changed = memcmp(p, q, tsize);
478                 munmap(p, tsize);
479                 munmap(q, tsize);
480         }
481         if (changed) {
482                 if (rename(from_name, to_name) < 0)
483                         err(EX_OSERR, "rename(%s, %s)", from_name, to_name);
484         } else {
485                 if (unlink(from_name) < 0)
486                         err(EX_OSERR, "unlink(%s)", from_name);
487         }
488 }