main.c revision 144352
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 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31static const char copyright[] =
32"@(#) Copyright (c) 1980, 1993\n\
33	The Regents of the University of California.  All rights reserved.\n";
34#endif /* not lint */
35
36#ifndef lint
37#if 0
38static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/6/93";
39#endif
40static const char rcsid[] =
41  "$FreeBSD: head/usr.sbin/config/main.c 144352 2005-03-30 21:45:08Z imp $";
42#endif /* not lint */
43
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <sys/file.h>
47#include <sys/mman.h>
48#include <sys/param.h>
49#include <ctype.h>
50#include <err.h>
51#include <stdio.h>
52#include <sysexits.h>
53#include <unistd.h>
54#include <dirent.h>
55#include "y.tab.h"
56#include "config.h"
57
58#ifndef TRUE
59#define TRUE	(1)
60#endif
61
62#ifndef FALSE
63#define FALSE	(0)
64#endif
65
66#define	CDIR	"../compile/"
67
68char *	PREFIX;
69char 	destdir[MAXPATHLEN];
70char 	srcdir[MAXPATHLEN];
71
72int	debugging;
73int	profiling;
74
75static void configfile(void);
76static void get_srcdir(void);
77static void usage(void);
78static void cleanheaders(char *);
79
80struct hdr_list {
81	char *h_name;
82	struct hdr_list *h_next;
83} *htab;
84
85/*
86 * Config builds a set of files for building a UNIX
87 * system given a description of the desired system.
88 */
89int
90main(int argc, char **argv)
91{
92
93	struct stat buf;
94	int ch, len;
95	char *p;
96	char xxx[MAXPATHLEN];
97
98	while ((ch = getopt(argc, argv, "d:gp")) != -1)
99		switch (ch) {
100		case 'd':
101			if (*destdir == '\0')
102				strlcpy(destdir, optarg, sizeof(destdir));
103			else
104				errx(2, "directory already set");
105			break;
106		case 'g':
107			debugging++;
108			break;
109		case 'p':
110			profiling++;
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	STAILQ_INIT(&dtab);
144	STAILQ_INIT(&fntab);
145	SLIST_INIT(&cputype);
146	STAILQ_INIT(&ftab);
147	yyfile = *argv;
148	if (yyparse())
149		exit(3);
150	if (machinename == NULL) {
151		printf("Specify machine type, e.g. ``machine i386''\n");
152		exit(1);
153	}
154	/*
155	 * make symbolic links in compilation directory
156	 * for "sys" (to make genassym.c work along with #include <sys/xxx>)
157	 * and similarly for "machine".
158	 */
159	if (*srcdir == '\0')
160		(void)snprintf(xxx, sizeof(xxx), "../../include");
161	else
162		(void)snprintf(xxx, sizeof(xxx), "%s/%s/include",
163		    srcdir, machinename);
164	(void) unlink(path("machine"));
165	(void) symlink(xxx, path("machine"));
166	options();			/* make options .h files */
167	makefile();			/* build Makefile */
168	headers();			/* make a lot of .h files */
169	configfile();			/* put config file into kernel*/
170	cleanheaders(p);
171	printf("Kernel build directory is %s\n", p);
172	printf("Don't forget to do a ``make depend''\n");
173	exit(0);
174}
175
176/*
177 * get_srcdir
178 *	determine the root of the kernel source tree
179 *	and save that in srcdir.
180 */
181static void
182get_srcdir(void)
183{
184
185	if (realpath("../..", srcdir) == NULL)
186		errx(2, "Unable to find root of source tree");
187}
188
189static void
190usage(void)
191{
192
193	fprintf(stderr, "usage: config [-gp] [-d destdir] sysname\n");
194	exit(1);
195}
196
197/*
198 * get_word
199 *	returns EOF on end of file
200 *	NULL on end of line
201 *	pointer to the word otherwise
202 */
203char *
204get_word(FILE *fp)
205{
206	static char line[80];
207	int ch;
208	char *cp;
209	int escaped_nl = 0;
210
211begin:
212	while ((ch = getc(fp)) != EOF)
213		if (ch != ' ' && ch != '\t')
214			break;
215	if (ch == EOF)
216		return ((char *)EOF);
217	if (ch == '\\'){
218		escaped_nl = 1;
219		goto begin;
220	}
221	if (ch == '\n') {
222		if (escaped_nl){
223			escaped_nl = 0;
224			goto begin;
225		}
226		else
227			return (NULL);
228	}
229	cp = line;
230	*cp++ = ch;
231	while ((ch = getc(fp)) != EOF) {
232		if (isspace(ch))
233			break;
234		*cp++ = ch;
235	}
236	*cp = 0;
237	if (ch == EOF)
238		return ((char *)EOF);
239	(void) ungetc(ch, fp);
240	return (line);
241}
242
243/*
244 * get_quoted_word
245 *	like get_word but will accept something in double or single quotes
246 *	(to allow embedded spaces).
247 */
248char *
249get_quoted_word(FILE *fp)
250{
251	static char line[256];
252	int ch;
253	char *cp;
254	int escaped_nl = 0;
255
256begin:
257	while ((ch = getc(fp)) != EOF)
258		if (ch != ' ' && ch != '\t')
259			break;
260	if (ch == EOF)
261		return ((char *)EOF);
262	if (ch == '\\'){
263		escaped_nl = 1;
264		goto begin;
265	}
266	if (ch == '\n') {
267		if (escaped_nl){
268			escaped_nl = 0;
269			goto begin;
270		}
271		else
272			return (NULL);
273	}
274	cp = line;
275	if (ch == '"' || ch == '\'') {
276		int quote = ch;
277
278		while ((ch = getc(fp)) != EOF) {
279			if (ch == quote)
280				break;
281			if (ch == '\n') {
282				*cp = 0;
283				printf("config: missing quote reading `%s'\n",
284					line);
285				exit(2);
286			}
287			*cp++ = ch;
288		}
289	} else {
290		*cp++ = ch;
291		while ((ch = getc(fp)) != EOF) {
292			if (isspace(ch))
293				break;
294			*cp++ = ch;
295		}
296		if (ch != EOF)
297			(void) ungetc(ch, fp);
298	}
299	*cp = 0;
300	if (ch == EOF)
301		return ((char *)EOF);
302	return (line);
303}
304
305/*
306 * prepend the path to a filename
307 */
308char *
309path(const char *file)
310{
311	char *cp = NULL;
312
313	if (file)
314		asprintf(&cp, "%s/%s", destdir, file);
315	else
316		cp = strdup(destdir);
317	return (cp);
318}
319
320static void
321configfile(void)
322{
323	FILE *fi, *fo;
324	char *p;
325	int i;
326
327	fi = fopen(PREFIX, "r");
328	if (!fi)
329		err(2, "%s", PREFIX);
330	fo = fopen(p=path("config.c.new"), "w");
331	if (!fo)
332		err(2, "%s", p);
333	fprintf(fo, "#include \"opt_config.h\"\n");
334	fprintf(fo, "#ifdef INCLUDE_CONFIG_FILE \n");
335	fprintf(fo, "const char config[] = \"\\\n");
336	fprintf(fo, "START CONFIG FILE %s\\n\\\n___", PREFIX);
337	while (EOF != (i=getc(fi))) {
338		if (i == '\n') {
339			fprintf(fo, "\\n\\\n___");
340		} else if (i == '\"') {
341			fprintf(fo, "\\\"");
342		} else if (i == '\\') {
343			fprintf(fo, "\\\\");
344		} else {
345			putc(i, fo);
346		}
347	}
348	fprintf(fo, "\\n\\\nEND CONFIG FILE %s\\n\\\n", PREFIX);
349	fprintf(fo, "\";\n");
350	fprintf(fo, "\n#endif /* INCLUDE_CONFIG_FILE */\n");
351	fclose(fi);
352	fclose(fo);
353	moveifchanged(path("config.c.new"), path("config.c"));
354}
355
356/*
357 * moveifchanged --
358 *	compare two files; rename if changed.
359 */
360void
361moveifchanged(const char *from_name, const char *to_name)
362{
363	char *p, *q;
364	int changed;
365	size_t tsize;
366	struct stat from_sb, to_sb;
367	int from_fd, to_fd;
368
369	changed = 0;
370
371	if ((from_fd = open(from_name, O_RDONLY)) < 0)
372		err(EX_OSERR, "moveifchanged open(%s)", from_name);
373
374	if ((to_fd = open(to_name, O_RDONLY)) < 0)
375		changed++;
376
377	if (!changed && fstat(from_fd, &from_sb) < 0)
378		err(EX_OSERR, "moveifchanged fstat(%s)", from_name);
379
380	if (!changed && fstat(to_fd, &to_sb) < 0)
381		err(EX_OSERR, "moveifchanged fstat(%s)", to_name);
382
383	if (!changed && from_sb.st_size != to_sb.st_size)
384		changed++;
385
386	tsize = (size_t)from_sb.st_size;
387
388	if (!changed) {
389		p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
390#ifndef MAP_FAILED
391#define MAP_FAILED ((caddr_t) -1)
392#endif
393		if (p == MAP_FAILED)
394			err(EX_OSERR, "mmap %s", from_name);
395		q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
396		if (q == MAP_FAILED)
397			err(EX_OSERR, "mmap %s", to_name);
398
399		changed = memcmp(p, q, tsize);
400		munmap(p, tsize);
401		munmap(q, tsize);
402	}
403	if (changed) {
404		if (rename(from_name, to_name) < 0)
405			err(EX_OSERR, "rename(%s, %s)", from_name, to_name);
406	} else {
407		if (unlink(from_name) < 0)
408			err(EX_OSERR, "unlink(%s)", from_name);
409	}
410}
411
412static void
413cleanheaders(char *p)
414{
415	DIR *dirp;
416	struct dirent *dp;
417	struct file_list *fl;
418	struct hdr_list *hl;
419	int i;
420
421	remember("y.tab.h");
422	remember("setdefs.h");
423	STAILQ_FOREACH(fl, &ftab, f_next)
424		remember(fl->f_fn);
425
426	/*
427	 * Scan the build directory and clean out stuff that looks like
428	 * it might have been a leftover NFOO header, etc.
429	 */
430	if ((dirp = opendir(p)) == NULL)
431		err(EX_OSERR, "opendir %s", p);
432	while ((dp = readdir(dirp)) != NULL) {
433		i = dp->d_namlen - 2;
434		/* Skip non-headers */
435		if (dp->d_name[i] != '.' || dp->d_name[i + 1] != 'h')
436			continue;
437		/* Skip special stuff, eg: bus_if.h, but check opt_*.h */
438		if (index(dp->d_name, '_') &&
439		    strncmp(dp->d_name, "opt_", 4) != 0)
440			continue;
441		/* Check if it is a target file */
442		for (hl = htab; hl != NULL; hl = hl->h_next) {
443			if (strcmp(dp->d_name, hl->h_name) == 0) {
444				break;
445			}
446		}
447		if (hl)
448			continue;
449		printf("Removing stale header: %s\n", dp->d_name);
450		if (unlink(path(dp->d_name)) == -1)
451			warn("unlink %s", dp->d_name);
452	}
453	(void)closedir(dirp);
454}
455
456void
457remember(const char *file)
458{
459	char *s;
460	struct hdr_list *hl;
461
462	if ((s = strrchr(file, '/')) != NULL)
463		s = ns(s + 1);
464	else
465		s = ns(file);
466
467	if (index(s, '_') && strncmp(s, "opt_", 4) != 0) {
468		free(s);
469		return;
470	}
471	for (hl = htab; hl != NULL; hl = hl->h_next) {
472		if (strcmp(s, hl->h_name) == 0) {
473			free(s);
474			return;
475		}
476	}
477	hl = malloc(sizeof(*hl));
478	bzero(hl, sizeof(*hl));
479	hl->h_name = s;
480	hl->h_next = htab;
481	htab = hl;
482}
483