1/*	$NetBSD: mkheaders.c,v 1.17 2010/02/03 21:00:49 pooka Exp $	*/
2
3/*
4 * Copyright (c) 1992, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This software was developed by the Computer Systems Engineering group
8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9 * contributed to Berkeley.
10 *
11 * All advertising materials mentioning features or use of this software
12 * must display the following acknowledgement:
13 *	This product includes software developed by the University of
14 *	California, Lawrence Berkeley Laboratories.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 *    notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 *    notice, this list of conditions and the following disclaimer in the
23 *    documentation and/or other materials provided with the distribution.
24 * 3. Neither the name of the University nor the names of its contributors
25 *    may be used to endorse or promote products derived from this software
26 *    without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 *	from: @(#)mkheaders.c	8.1 (Berkeley) 6/6/93
41 */
42
43#if HAVE_NBTOOL_CONFIG_H
44#include "nbtool_config.h"
45#endif
46
47#include <sys/param.h>
48#include <ctype.h>
49#include <errno.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <time.h>
54#include <util.h>
55#include <err.h>
56#include "defs.h"
57
58#include <crc_extern.h>
59
60static int emitcnt(struct nvlist *);
61static int emitopts(void);
62static int emittime(void);
63static int herr(const char *, const char *, FILE *);
64static int defopts_print(const char *, void *, void *);
65static char *cntname(const char *);
66
67/*
68 * We define a global symbol with the name of each option and its value.
69 * This should stop code compiled with different options being linked together.
70 */
71
72/* Unlikely constant for undefined options */
73#define UNDEFINED ('n' << 24 | 0 << 20 | 't' << 12 | 0xdefU)
74/* Value for defined options with value UNDEFINED */
75#define	DEFINED (0xdef1U << 16 | 'n' << 8 | 0xed)
76
77/*
78 * Make the various config-generated header files.
79 */
80int
81mkheaders(void)
82{
83	struct files *fi;
84
85	/*
86	 * Make headers containing counts, as needed.
87	 */
88	TAILQ_FOREACH(fi, &allfiles, fi_next) {
89		if (fi->fi_flags & FI_HIDDEN)
90			continue;
91		if (fi->fi_flags & (FI_NEEDSCOUNT | FI_NEEDSFLAG) &&
92		    emitcnt(fi->fi_optf))
93			return (1);
94	}
95
96	if (emitopts() || emitlocs() || emitioconfh())
97		return (1);
98
99	/*
100	 * If the minimum required version is ever bumped beyond 20090513,
101	 * emittime() can be removed.
102	 */
103	if (version <= 20090513 && emittime())
104		return (1);
105
106	return (0);
107}
108
109static void
110fprint_global(FILE *fp, const char *name, long long value)
111{
112	/*
113	 * We have to doubt the founding fathers here.
114	 * The gas syntax for hppa is 'var .equ value', for all? other
115	 * instruction sets it is ' .equ var,value'.  both have been used in
116	 * various assemblers, but supporting a common syntax would be good.
117	 * Fortunately we can use .equiv since it has a consistent syntax,
118	 * but requires us to detect multiple assignments - event with the
119	 * same value.
120	 */
121	fprintf(fp, "#ifdef _LOCORE\n"
122	    " .ifndef _KERNEL_OPT_%s\n"
123	    " .global _KERNEL_OPT_%s\n"
124	    " .equiv _KERNEL_OPT_%s,0x%llx\n"
125	    " .endif\n"
126	    "#else\n"
127	    "__asm(\" .ifndef _KERNEL_OPT_%s\\n"
128	    " .global _KERNEL_OPT_%s\\n"
129	    " .equiv _KERNEL_OPT_%s,0x%llx\\n"
130	    " .endif\");\n"
131	    "#endif\n",
132	    name, name, name, value,
133	    name, name, name, value);
134}
135
136/* Convert the option argument to a 32bit numder */
137static unsigned int
138global_hash(const char *str)
139{
140        unsigned int h;
141	char *ep;
142
143	/* If the value is a valid numeric, just use it */
144	h = strtoul(str, &ep, 0);
145	if (*ep != 0)
146		/* Otherwise shove through a 32bit CRC function */
147		h = crc_buf(0, str, strlen(str));
148
149	/* Avoid colliding with the value used for undefined options. */
150	/* At least until I stop any options being set to zero */
151	return h != UNDEFINED ? h : DEFINED;
152}
153
154static void
155fprintcnt(FILE *fp, struct nvlist *nv)
156{
157	const char *name = cntname(nv->nv_name);
158
159	fprintf(fp, "#define\t%s\t%lld\n", name, nv->nv_num);
160	fprint_global(fp, name, nv->nv_num);
161}
162
163static int
164emitcnt(struct nvlist *head)
165{
166	char nfname[BUFSIZ], tfname[BUFSIZ];
167	struct nvlist *nv;
168	FILE *fp;
169
170	(void)snprintf(nfname, sizeof(nfname), "%s.h", head->nv_name);
171	(void)snprintf(tfname, sizeof(tfname), "tmp_%s", nfname);
172
173	if ((fp = fopen(tfname, "w")) == NULL)
174		return (herr("open", tfname, NULL));
175
176	for (nv = head; nv != NULL; nv = nv->nv_next)
177		fprintcnt(fp, nv);
178
179	fflush(fp);
180	if (ferror(fp))
181		return herr("writ", tfname, fp);
182
183	if (fclose(fp) == EOF)
184		return (herr("clos", tfname, NULL));
185
186	return (moveifchanged(tfname, nfname));
187}
188
189/*
190 * Output a string, preceded by a tab and possibly unescaping any quotes.
191 * The argument will be output as is if it doesn't start with \".
192 * Otherwise the first backslash in a \? sequence will be dropped.
193 */
194static void
195fprintstr(FILE *fp, const char *str)
196{
197
198	if (strncmp(str, "\\\"", 2) != 0) {
199		(void)fprintf(fp, "\t%s", str);
200		return;
201	}
202
203	(void)fputc('\t', fp);
204
205	for (; *str; str++) {
206		switch (*str) {
207		case '\\':
208			if (!*++str)				/* XXX */
209				str--;
210			/*FALLTHROUGH*/
211		default:
212			(void)fputc(*str, fp);
213			break;
214		}
215	}
216}
217
218/*
219 * Callback function for walking the option file hash table.  We write out
220 * the options defined for this file.
221 */
222static int
223/*ARGSUSED*/
224defopts_print(const char *name, void *value, void *arg)
225{
226	char tfname[BUFSIZ];
227	struct nvlist *nv, *option;
228	const char *opt_value;
229	int isfsoption;
230	FILE *fp;
231
232	(void)snprintf(tfname, sizeof(tfname), "tmp_%s", name);
233	if ((fp = fopen(tfname, "w")) == NULL)
234		return (herr("open", tfname, NULL));
235
236	for (nv = value; nv != NULL; nv = nv->nv_next) {
237		isfsoption = OPT_FSOPT(nv->nv_name);
238
239		if (nv->nv_flags & NV_OBSOLETE) {
240			fprintf(fp, "/* %s `%s' is obsolete */\n",
241			    isfsoption ? "file system" : "option",
242			    nv->nv_name);
243			fprint_global(fp, nv->nv_name, 0xdeadbeef);
244			continue;
245		}
246
247		if (((option = ht_lookup(opttab, nv->nv_name)) == NULL &&
248		    (option = ht_lookup(fsopttab, nv->nv_name)) == NULL) &&
249		    (nv->nv_str == NULL)) {
250			fprintf(fp, "/* %s `%s' not defined */\n",
251			    isfsoption ? "file system" : "option",
252			    nv->nv_name);
253			fprint_global(fp, nv->nv_name, UNDEFINED);
254			continue;
255		}
256
257		opt_value = option != NULL ? option->nv_str : nv->nv_str;
258		if (isfsoption == 1)
259			/* For filesysteme we'd output the lower case name */
260			opt_value = NULL;
261
262		fprintf(fp, "#define\t%s", nv->nv_name);
263		if (opt_value != NULL)
264			fprintstr(fp, opt_value);
265		else if (!isfsoption)
266			fprintstr(fp, "1");
267		fputc('\n', fp);
268		fprint_global(fp, nv->nv_name,
269		    opt_value == NULL ? 1 : global_hash(opt_value));
270	}
271
272	fflush(fp);
273	if (ferror(fp))
274		return herr("writ", tfname, fp);
275
276	if (fclose(fp) == EOF)
277		return (herr("clos", tfname, NULL));
278
279	return (moveifchanged(tfname, name));
280}
281
282/*
283 * Emit the option header files.
284 */
285static int
286emitopts(void)
287{
288
289	return (ht_enumerate(optfiletab, defopts_print, NULL));
290}
291
292/*
293 * A callback function for walking the attribute hash table.
294 * Emit CPP definitions of manifest constants for the locators on the
295 * "name" attribute node (passed as the "value" parameter).
296 */
297static int
298locators_print(const char *name, void *value, void *arg)
299{
300	struct attr *a;
301	struct nvlist *nv;
302	int i;
303	char *locdup, *namedup;
304	char *cp;
305	FILE *fp = arg;
306
307	a = value;
308	if (a->a_locs) {
309		if (strchr(name, ' ') != NULL || strchr(name, '\t') != NULL)
310			/*
311			 * name contains a space; we can't generate
312			 * usable defines, so ignore it.
313			 */
314			return 0;
315		locdup = estrdup(name);
316		for (cp = locdup; *cp; cp++)
317			if (islower((unsigned char)*cp))
318				*cp = toupper((unsigned char)*cp);
319		for (i = 0, nv = a->a_locs; nv; nv = nv->nv_next, i++) {
320			if (strchr(nv->nv_name, ' ') != NULL ||
321			    strchr(nv->nv_name, '\t') != NULL)
322				/*
323				 * name contains a space; we can't generate
324				 * usable defines, so ignore it.
325				 */
326				continue;
327			namedup = estrdup(nv->nv_name);
328			for (cp = namedup; *cp; cp++)
329				if (islower((unsigned char)*cp))
330					*cp = toupper((unsigned char)*cp);
331				else if (*cp == ARRCHR)
332					*cp = '_';
333			fprintf(fp, "#define %sCF_%s %d\n", locdup, namedup, i);
334			if (nv->nv_str != NULL)
335				fprintf(fp, "#define %sCF_%s_DEFAULT %s\n",
336				    locdup, namedup, nv->nv_str);
337			free(namedup);
338		}
339		/* assert(i == a->a_loclen) */
340		fprintf(fp, "#define %sCF_NLOCS %d\n", locdup, a->a_loclen);
341		free(locdup);
342	}
343	return 0;
344}
345
346/*
347 * Build the "locators.h" file with manifest constants for all potential
348 * locators in the configuration.  Do this by enumerating the attribute
349 * hash table and emitting all the locators for each attribute.
350 */
351int
352emitlocs(void)
353{
354	const char *tfname;
355	int rval;
356	FILE *tfp;
357
358	tfname = "tmp_locators.h";
359	if ((tfp = fopen(tfname, "w")) == NULL)
360		return (herr("open", tfname, NULL));
361
362	rval = ht_enumerate(attrtab, locators_print, tfp);
363
364	fflush(tfp);
365	if (ferror(tfp))
366		return (herr("writ", tfname, NULL));
367	if (fclose(tfp) == EOF)
368		return (herr("clos", tfname, NULL));
369	if (rval)
370		return (rval);
371	return (moveifchanged(tfname, "locators.h"));
372}
373
374/*
375 * Build the "ioconf.h" file with extern declarations for all configured
376 * cfdrivers.
377 */
378int
379emitioconfh(void)
380{
381	const char *tfname;
382	FILE *tfp;
383	struct devbase *d;
384
385	tfname = "tmp_ioconf.h";
386	if ((tfp = fopen(tfname, "w")) == NULL)
387		return (herr("open", tfname, NULL));
388
389	TAILQ_FOREACH(d, &allbases, d_next) {
390		if (!devbase_has_instances(d, WILD))
391			continue;
392		fprintf(tfp, "extern struct cfdriver %s_cd;\n", d->d_name);
393	}
394
395	fflush(tfp);
396	if (ferror(tfp))
397		return herr("writ", tfname, tfp);
398
399	if (fclose(tfp) == EOF)
400		return (herr("clos", tfname, NULL));
401
402	return (moveifchanged(tfname, "ioconf.h"));
403}
404
405/*
406 * Make a file that config_time.h can use as a source, if required.
407 */
408static int
409emittime(void)
410{
411	FILE *fp;
412	time_t t;
413	struct tm *tm;
414	char buf[128];
415
416	t = time(NULL);
417	tm = gmtime(&t);
418
419	if ((fp = fopen("config_time.src", "w")) == NULL)
420		return (herr("open", "config_time.src", NULL));
421
422	if (strftime(buf, sizeof(buf), "%c %Z", tm) == 0)
423		return (herr("strftime", "config_time.src", fp));
424
425	fprintf(fp, "/* %s */\n"
426	    "#define CONFIG_TIME\t%2lld\n"
427	    "#define CONFIG_YEAR\t%2d\n"
428	    "#define CONFIG_MONTH\t%2d\n"
429	    "#define CONFIG_DATE\t%2d\n"
430	    "#define CONFIG_HOUR\t%2d\n"
431	    "#define CONFIG_MINS\t%2d\n"
432	    "#define CONFIG_SECS\t%2d\n",
433	    buf, (long long)t,
434	    tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
435	    tm->tm_hour, tm->tm_min, tm->tm_sec);
436
437	fflush(fp);
438	if (ferror(fp))
439		return (herr("fprintf", "config_time.src", fp));
440
441	if (fclose(fp) != 0)
442		return (herr("clos", "config_time.src", NULL));
443
444	/*
445	 * *Don't* moveifchanged this file.  Makefile.kern.inc will
446	 * handle that if it determines such a move is necessary.
447	 */
448	return (0);
449}
450
451/*
452 * Compare two files.  If nfname doesn't exist, or is different from
453 * tfname, move tfname to nfname.  Otherwise, delete tfname.
454 */
455int
456moveifchanged(const char *tfname, const char *nfname)
457{
458	char tbuf[BUFSIZ], nbuf[BUFSIZ];
459	FILE *tfp, *nfp;
460
461	if ((tfp = fopen(tfname, "r")) == NULL)
462		return (herr("open", tfname, NULL));
463
464	if ((nfp = fopen(nfname, "r")) == NULL)
465		goto moveit;
466
467	while (fgets(tbuf, sizeof(tbuf), tfp) != NULL) {
468		if (fgets(nbuf, sizeof(nbuf), nfp) == NULL) {
469			/*
470			 * Old file has fewer lines.
471			 */
472			goto moveit;
473		}
474		if (strcmp(tbuf, nbuf) != 0)
475			goto moveit;
476	}
477
478	/*
479	 * We've reached the end of the new file.  Check to see if new file
480	 * has fewer lines than old.
481	 */
482	if (fgets(nbuf, sizeof(nbuf), nfp) != NULL) {
483		/*
484		 * New file has fewer lines.
485		 */
486		goto moveit;
487	}
488
489	(void) fclose(nfp);
490	(void) fclose(tfp);
491	if (remove(tfname) == -1)
492		return(herr("remov", tfname, NULL));
493	return (0);
494
495 moveit:
496	/*
497	 * They're different, or the file doesn't exist.
498	 */
499	if (nfp)
500		(void) fclose(nfp);
501	if (tfp)
502		(void) fclose(tfp);
503	if (rename(tfname, nfname) == -1)
504		return (herr("renam", tfname, NULL));
505	return (0);
506}
507
508static int
509herr(const char *what, const char *fname, FILE *fp)
510{
511
512	warn("error %sing %s", what, fname);
513	if (fp)
514		(void)fclose(fp);
515	return (1);
516}
517
518static char *
519cntname(const char *src)
520{
521	char *dst;
522	unsigned char c;
523	static char buf[100];
524
525	dst = buf;
526	*dst++ = 'N';
527	while ((c = *src++) != 0)
528		*dst++ = islower(c) ? toupper(c) : c;
529	*dst = 0;
530	return (buf);
531}
532