Merge from vendor branch LESS:
[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.21 2007/01/19 07:23:43 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 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 (platformname == NULL) {
161                 printf("Specify platform architecture, e.g. 'platform pc32'\n");
162                 exit(1);
163         }
164         if (machinename == NULL) {
165                 printf("Specify machine architecture, e.g. 'machine i386'\n");
166                 exit(1);
167         }
168         if (machinearchname == NULL) {
169                 printf("Specify cpu architecture, e.g. 'machine_arch i386'\n");
170                 exit(1);
171         }
172         newbus_ioconf();
173         
174         /*
175          * "machine" points into platform/<PLATFORM>/include
176          */
177         if (*srcdir == '\0')
178                 snprintf(linkdest, sizeof(linkdest), "../../platform/%s/include",
179                     platformname);
180         else
181                 snprintf(linkdest, sizeof(linkdest), "%s/platform/%s/include",
182                     srcdir, platformname);
183         symlink(linkdest, path("machine"));
184
185         /*
186          * "machine_base" points into platform/<PLATFORM>
187          */
188         if (*srcdir == '\0')
189                 snprintf(linkdest, sizeof(linkdest), "../../platform/%s",
190                     platformname);
191         else
192                 snprintf(linkdest, sizeof(linkdest), "%s/platform/%s",
193                     srcdir, platformname);
194         symlink(linkdest, path("machine_base"));
195
196         /*
197          * "cpu" points to cpu/<MACHINE_ARCH>/include
198          */
199         if (*srcdir == '\0')
200                 snprintf(linkdest, sizeof(linkdest),
201                          "../../cpu/%s/include", machinearchname);
202         else
203                 snprintf(linkdest, sizeof(linkdest),
204                          "%s/cpu/%s/include", srcdir, machinearchname);
205         symlink(linkdest, path("cpu"));
206
207         /*
208          * "cpu_base" points to cpu/<MACHINE_ARCH>
209          */
210         if (*srcdir == '\0')
211                 snprintf(linkdest, sizeof(linkdest), "../../cpu/%s",
212                     machinearchname);
213         else
214                 snprintf(linkdest, sizeof(linkdest), "%s/cpu/%s",
215                     srcdir, machinearchname);
216         symlink(linkdest, path("cpu_base"));
217
218         /*
219          * XXX check directory structure for architecture subdirectories and
220          * create the symlinks automatically XXX
221          */
222         if (*srcdir == '\0')
223                 snprintf(linkdest, sizeof(linkdest),
224                     "../../../../../net/i4b/include/%s",
225                     machinearchname);
226         else
227                 snprintf(linkdest, sizeof(linkdest), "%s/net/i4b/include/%s",
228                     srcdir, machinearchname);
229         mkdir(path("net"), 0755);
230         mkdir(path("net/i4b"), 0755);
231         mkdir(path("net/i4b/include"), 0755);
232         symlink(linkdest, path("net/i4b/include/machine"));
233
234         for (i = 0; i < sizeof(emus) / sizeof(emus[0]); ++i) {
235                 if (*srcdir == 0)  {
236                         snprintf(linkdest, sizeof(linkdest),
237                             "../../emulation/%s/%s",
238                             emus[i], machinearchname);
239                 } else {
240                         snprintf(linkdest, sizeof(linkdest),
241                             "%s/emulation/%s/%s",
242                             srcdir, emus[i], machinearchname);
243                 }
244                 snprintf(linksrc, sizeof(linksrc), "arch_%s", emus[i]);
245                 symlink(linkdest, path(linksrc));
246         }
247
248         options();                      /* make options .h files */
249         makefile();                     /* build Makefile */
250         headers();                      /* make a lot of .h files */
251         configfile();                   /* put config file into kernel*/
252         printf("Kernel build directory is %s\n", p);
253         exit(EX_OK);
254 }
255
256 /*
257  * get_srcdir
258  *      determine the root of the kernel source tree
259  *      and save that in srcdir.
260  */
261 static void
262 get_srcdir(void)
263 {
264         
265         if (realpath("..", srcdir) == NULL)
266                 errx(2, "Unable to find root of source tree");
267 }
268
269 static void
270 usage(void)
271 {
272         
273         fprintf(stderr, "usage: config [-gpr] [-d destdir] sysname\n");
274         exit(1);
275 }
276
277 /*
278  * get_word
279  *      returns EOF on end of file
280  *      NULL on end of line
281  *      pointer to the word otherwise
282  */
283 char *
284 get_word(FILE *fp)
285 {
286         static char line[80];
287         int ch;
288         char *cp;
289         int escaped_nl = 0;
290
291 begin:
292         while ((ch = getc(fp)) != EOF)
293                 if (ch != ' ' && ch != '\t')
294                         break;
295         if (ch == EOF)
296                 return((char *)EOF);
297         if (ch == '\\') {
298                 escaped_nl = 1;
299                 goto begin;
300         }
301         if (ch == '\n') {
302                 if (escaped_nl) {
303                         escaped_nl = 0;
304                         goto begin;
305                 }
306                 else
307                         return(NULL);
308         }
309         cp = line;
310         *cp++ = ch;
311         while ((ch = getc(fp)) != EOF) {
312                 if (isspace(ch))
313                         break;
314                 *cp++ = ch;
315         }
316         *cp = 0;
317         if (ch == EOF)
318                 return((char *)EOF);
319         ungetc(ch, fp);
320         return(line);
321 }
322
323 /*
324  * get_quoted_word
325  *      like get_word but will accept something in double or single quotes
326  *      (to allow embedded spaces).
327  */
328 char *
329 get_quoted_word(FILE *fp)
330 {
331         static char line[256];
332         int ch;
333         char *cp;
334         int escaped_nl = 0;
335
336 begin:
337         while ((ch = getc(fp)) != EOF)
338                 if (ch != ' ' && ch != '\t')
339                         break;
340         if (ch == EOF)
341                 return((char *)EOF);
342         if (ch == '\\') {
343                 escaped_nl = 1;
344                 goto begin;
345         }
346         if (ch == '\n') {
347                 if (escaped_nl) {
348                         escaped_nl = 0;
349                         goto begin;
350                 }
351                 else
352                         return(NULL);
353         }
354         cp = line;
355         if (ch == '"' || ch == '\'') {
356                 int quote = ch;
357
358                 while ((ch = getc(fp)) != EOF) {
359                         if (ch == quote)
360                                 break;
361                         if (ch == '\n') {
362                                 *cp = 0;
363                                 printf("config: missing quote reading `%s'\n",
364                                         line);
365                                 exit(2);
366                         }
367                         *cp++ = ch;
368                 }
369         } else {
370                 *cp++ = ch;
371                 while ((ch = getc(fp)) != EOF) {
372                         if (isspace(ch))
373                                 break;
374                         *cp++ = ch;
375                 }
376                 if (ch != EOF)
377                         ungetc(ch, fp);
378         }
379         *cp = 0;
380         if (ch == EOF)
381                 return((char *)EOF);
382         return(line);
383 }
384
385 /*
386  * prepend the path to a filename
387  */
388 char *
389 path(const char *file)
390 {
391         char *cp;
392
393         cp = malloc((size_t)(strlen(destdir) + (file ? strlen(file) : 0) + 2));
394         strcpy(cp, destdir);
395         if (file != NULL) {
396                 strcat(cp, "/");
397                 strcat(cp, file);
398         }
399         return(cp);
400 }
401
402 static void
403 configfile(void)
404 {
405         FILE *fi, *fo;
406         char *p;
407         int i;
408         
409         fi = fopen(PREFIX, "r");
410         if (fi == NULL)
411                 err(2, "%s", PREFIX);
412         fo = fopen(p = path("config.c.new"), "w");
413         if (fo == NULL)
414                 err(2, "%s", p);
415         fprintf(fo, "#include \"opt_config.h\"\n");
416         fprintf(fo, "#ifdef INCLUDE_CONFIG_FILE \n");
417         fprintf(fo, "static const char config[] = \"\\\n");
418         fprintf(fo, "START CONFIG FILE %s\\n\\\n___", PREFIX);
419         while (EOF != (i = getc(fi))) {
420                 if (i == '\n') {
421                         fprintf(fo, "\\n\\\n___");
422                 } else if (i == '\"') {
423                         fprintf(fo, "\\\"");
424                 } else if (i == '\\') {
425                         fprintf(fo, "\\\\");
426                 } else {
427                         putc(i, fo);
428                 }
429         }
430         fprintf(fo, "\\n\\\nEND CONFIG FILE %s\\n\\\n", PREFIX);
431         fprintf(fo, "\";\n");
432         fprintf(fo, "\n#endif /* INCLUDE_CONFIG_FILE */\n");
433         fclose(fi);
434         fclose(fo);
435         moveifchanged(path("config.c.new"), path("config.c"));
436 }
437
438 /*
439  * moveifchanged --
440  *      compare two files; rename if changed.
441  */
442 void
443 moveifchanged(const char *from_name, const char *to_name)
444 {
445         char *p, *q;
446         int changed;
447         size_t tsize;
448         struct stat from_sb, to_sb;
449         int from_fd, to_fd;
450
451         changed = 0;
452
453         if ((from_fd = open(from_name, O_RDONLY)) < 0)
454                 err(EX_OSERR, "moveifchanged open(%s)", from_name);
455
456         if ((to_fd = open(to_name, O_RDONLY)) < 0)
457                 changed++;
458
459         if (!changed && fstat(from_fd, &from_sb) < 0)
460                 err(EX_OSERR, "moveifchanged fstat(%s)", from_name);
461
462         if (!changed && fstat(to_fd, &to_sb) < 0)
463                 err(EX_OSERR, "moveifchanged fstat(%s)", to_name);
464
465         if (!changed && from_sb.st_size != to_sb.st_size)
466                 changed++;
467
468         tsize = (size_t)from_sb.st_size;
469
470         if (!changed) {
471                 p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
472 #ifndef MAP_FAILED
473 #define MAP_FAILED ((caddr_t)-1)
474 #endif
475                 if (p == MAP_FAILED)
476                         err(EX_OSERR, "mmap %s", from_name);
477                 q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
478                 if (q == MAP_FAILED)
479                         err(EX_OSERR, "mmap %s", to_name);
480
481                 changed = memcmp(p, q, tsize);
482                 munmap(p, tsize);
483                 munmap(q, tsize);
484         }
485         if (changed) {
486                 if (rename(from_name, to_name) < 0)
487                         err(EX_OSERR, "rename(%s, %s)", from_name, to_name);
488         } else {
489                 if (unlink(from_name) < 0)
490                         err(EX_OSERR, "unlink(%s)", from_name);
491         }
492 }