1/*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 */
5
6#include <sys/stat.h>
7#include <ctype.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <time.h>
12#include <unistd.h>
13
14#define LKC_DIRECT_LINK
15#include "lkc.h"
16
17static void conf_warning(const char *fmt, ...)
18	__attribute__ ((format (printf, 1, 2)));
19
20static const char *conf_filename;
21static int conf_lineno, conf_warnings, conf_unsaved;
22
23const char conf_def_filename[] = ".config";
24
25const char conf_defname[] = "scripts/defconfig";
26
27const char *conf_confnames[] = {
28	conf_def_filename,
29	conf_defname,
30	NULL,
31};
32
33static void conf_warning(const char *fmt, ...)
34{
35	va_list ap;
36	va_start(ap, fmt);
37	fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno);
38	vfprintf(stderr, fmt, ap);
39	fprintf(stderr, "\n");
40	va_end(ap);
41	conf_warnings++;
42}
43
44static char *conf_expand_value(const char *in)
45{
46	struct symbol *sym;
47	const char *src;
48	static char res_value[SYMBOL_MAXLENGTH];
49	char *dst, name[SYMBOL_MAXLENGTH];
50
51	res_value[0] = 0;
52	dst = name;
53	while ((src = strchr(in, '$'))) {
54		strncat(res_value, in, src - in);
55		src++;
56		dst = name;
57		while (isalnum(*src) || *src == '_')
58			*dst++ = *src++;
59		*dst = 0;
60		sym = sym_lookup(name, 0);
61		sym_calc_value(sym);
62		strcat(res_value, sym_get_string_value(sym));
63		in = src;
64	}
65	strcat(res_value, in);
66
67	return res_value;
68}
69
70char *conf_get_default_confname(void)
71{
72	struct stat buf;
73	static char fullname[PATH_MAX+1];
74	char *env, *name;
75
76	name = conf_expand_value(conf_defname);
77	env = getenv(SRCTREE);
78	if (env) {
79		sprintf(fullname, "%s/%s", env, name);
80		if (!stat(fullname, &buf))
81			return fullname;
82	}
83	return name;
84}
85
86int conf_read_simple(const char *name)
87{
88	FILE *in = NULL;
89	char line[1024];
90	char *p, *p2;
91	struct symbol *sym;
92	int i;
93
94	if (name) {
95		in = zconf_fopen(name);
96	} else {
97		const char **names = conf_confnames;
98		while ((name = *names++)) {
99			name = conf_expand_value(name);
100			in = zconf_fopen(name);
101			if (in) {
102				printf(_("#\n"
103				         "# using defaults found in %s\n"
104				         "#\n"), name);
105				break;
106			}
107		}
108	}
109	if (!in)
110		return 1;
111
112	conf_filename = name;
113	conf_lineno = 0;
114	conf_warnings = 0;
115	conf_unsaved = 0;
116
117	for_all_symbols(i, sym) {
118		sym->flags |= SYMBOL_NEW | SYMBOL_CHANGED;
119		if (sym_is_choice(sym))
120			sym->flags &= ~SYMBOL_NEW;
121		sym->flags &= ~SYMBOL_VALID;
122		switch (sym->type) {
123		case S_INT:
124		case S_HEX:
125		case S_STRING:
126			if (sym->user.val)
127				free(sym->user.val);
128		default:
129			sym->user.val = NULL;
130			sym->user.tri = no;
131		}
132	}
133
134	while (fgets(line, sizeof(line), in)) {
135		conf_lineno++;
136		sym = NULL;
137		switch (line[0]) {
138		case '#':
139			if (memcmp(line + 2, "CONFIG_", 7))
140				continue;
141			p = strchr(line + 9, ' ');
142			if (!p)
143				continue;
144			*p++ = 0;
145			if (strncmp(p, "is not set", 10))
146				continue;
147			sym = sym_find(line + 9);
148			if (!sym) {
149				conf_warning("trying to assign nonexistent symbol %s", line + 9);
150				break;
151			} else if (!(sym->flags & SYMBOL_NEW)) {
152				conf_warning("trying to reassign symbol %s", sym->name);
153				break;
154			}
155			switch (sym->type) {
156			case S_BOOLEAN:
157			case S_TRISTATE:
158				sym->user.tri = no;
159				sym->flags &= ~SYMBOL_NEW;
160				break;
161			default:
162				;
163			}
164			break;
165		case 'C':
166			if (memcmp(line, "CONFIG_", 7)) {
167				conf_warning("unexpected data");
168				continue;
169			}
170			p = strchr(line + 7, '=');
171			if (!p)
172				continue;
173			*p++ = 0;
174			p2 = strchr(p, '\n');
175			if (p2)
176				*p2 = 0;
177			sym = sym_find(line + 7);
178			if (!sym) {
179				conf_warning("trying to assign nonexistent symbol %s", line + 7);
180				break;
181			} else if (!(sym->flags & SYMBOL_NEW)) {
182				conf_warning("trying to reassign symbol %s", sym->name);
183				break;
184			}
185			switch (sym->type) {
186			case S_TRISTATE:
187				if (p[0] == 'm') {
188					sym->user.tri = mod;
189					sym->flags &= ~SYMBOL_NEW;
190					break;
191				}
192			case S_BOOLEAN:
193				if (p[0] == 'y') {
194					sym->user.tri = yes;
195					sym->flags &= ~SYMBOL_NEW;
196					break;
197				}
198				if (p[0] == 'n') {
199					sym->user.tri = no;
200					sym->flags &= ~SYMBOL_NEW;
201					break;
202				}
203				conf_warning("symbol value '%s' invalid for %s", p, sym->name);
204				break;
205			case S_STRING:
206				if (*p++ != '"')
207					break;
208				for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
209					if (*p2 == '"') {
210						*p2 = 0;
211						break;
212					}
213					memmove(p2, p2 + 1, strlen(p2));
214				}
215				if (!p2) {
216					conf_warning("invalid string found");
217					continue;
218				}
219			case S_INT:
220			case S_HEX:
221				if (sym_string_valid(sym, p)) {
222					sym->user.val = strdup(p);
223					sym->flags &= ~SYMBOL_NEW;
224				} else {
225					if (p[0]) /* bbox */
226						conf_warning("symbol value '%s' invalid for %s", p, sym->name);
227					continue;
228				}
229				break;
230			default:
231				;
232			}
233			break;
234		case '\n':
235			break;
236		default:
237			conf_warning("unexpected data");
238			continue;
239		}
240		if (sym && sym_is_choice_value(sym)) {
241			struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
242			switch (sym->user.tri) {
243			case no:
244				break;
245			case mod:
246				if (cs->user.tri == yes) {
247					conf_warning("%s creates inconsistent choice state", sym->name);
248					cs->flags |= SYMBOL_NEW;
249				}
250				break;
251			case yes:
252				if (cs->user.tri != no) {
253					conf_warning("%s creates inconsistent choice state", sym->name);
254					cs->flags |= SYMBOL_NEW;
255				} else
256					cs->user.val = sym;
257				break;
258			}
259			cs->user.tri = E_OR(cs->user.tri, sym->user.tri);
260		}
261	}
262	fclose(in);
263
264	if (modules_sym)
265		sym_calc_value(modules_sym);
266	return 0;
267}
268
269int conf_read(const char *name)
270{
271	struct symbol *sym;
272	struct property *prop;
273	struct expr *e;
274	int i;
275
276	if (conf_read_simple(name))
277		return 1;
278
279	for_all_symbols(i, sym) {
280		sym_calc_value(sym);
281		if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO))
282			goto sym_ok;
283		if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) {
284			/* check that calculated value agrees with saved value */
285			switch (sym->type) {
286			case S_BOOLEAN:
287			case S_TRISTATE:
288				if (sym->user.tri != sym_get_tristate_value(sym))
289					break;
290				if (!sym_is_choice(sym))
291					goto sym_ok;
292			default:
293				if (!strcmp(sym->curr.val, sym->user.val))
294					goto sym_ok;
295				break;
296			}
297		} else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE))
298			/* no previous value and not saved */
299			goto sym_ok;
300		conf_unsaved++;
301		/* maybe print value in verbose mode... */
302	sym_ok:
303		if (sym_has_value(sym) && !sym_is_choice_value(sym)) {
304			if (sym->visible == no)
305				sym->flags |= SYMBOL_NEW;
306			switch (sym->type) {
307			case S_STRING:
308			case S_INT:
309			case S_HEX:
310				if (!sym_string_within_range(sym, sym->user.val)) {
311					sym->flags |= SYMBOL_NEW;
312					sym->flags &= ~SYMBOL_VALID;
313				}
314			default:
315				break;
316			}
317		}
318		if (!sym_is_choice(sym))
319			continue;
320		prop = sym_get_choice_prop(sym);
321		for (e = prop->expr; e; e = e->left.expr)
322			if (e->right.sym->visible != no)
323				sym->flags |= e->right.sym->flags & SYMBOL_NEW;
324	}
325
326	sym_change_count = conf_warnings || conf_unsaved;
327
328	return 0;
329}
330
331int conf_write(const char *name)
332{
333	FILE *out, *out_h;
334	struct symbol *sym;
335	struct menu *menu;
336	const char *basename;
337	char dirname[128], tmpname[128], newname[128];
338	int type, l;
339	const char *str;
340	time_t now;
341	int use_timestamp = 1;
342	char *env;
343
344	dirname[0] = 0;
345	if (name && name[0]) {
346		struct stat st;
347		char *slash;
348
349		if (!stat(name, &st) && S_ISDIR(st.st_mode)) {
350			strcpy(dirname, name);
351			strcat(dirname, "/");
352			basename = conf_def_filename;
353		} else if ((slash = strrchr(name, '/'))) {
354			int size = slash - name + 1;
355			memcpy(dirname, name, size);
356			dirname[size] = 0;
357			if (slash[1])
358				basename = slash + 1;
359			else
360				basename = conf_def_filename;
361		} else
362			basename = name;
363	} else
364		basename = conf_def_filename;
365
366	sprintf(newname, "%s.tmpconfig.%d", dirname, (int)getpid());
367	out = fopen(newname, "w");
368	if (!out)
369		return 1;
370	out_h = NULL;
371	if (!name) {
372		out_h = fopen(".tmpconfig.h", "w");
373		if (!out_h)
374			return 1;
375		file_write_dep(NULL);
376	}
377	sym = sym_lookup("KERNELVERSION", 0);
378	sym_calc_value(sym);
379	time(&now);
380	env = getenv("KCONFIG_NOTIMESTAMP");
381	if (env && *env)
382		use_timestamp = 0;
383
384	fprintf(out, _("#\n"
385		       "# Automatically generated make config: don't edit\n"
386		       "# Busybox version: %s\n"
387		       "%s%s"
388		       "#\n"),
389		     sym_get_string_value(sym),
390		     use_timestamp ? "# " : "",
391		     use_timestamp ? ctime(&now) : "");
392	if (out_h) {
393		char buf[sizeof("#define AUTOCONF_TIMESTAMP "
394				"\"YYYY-MM-DD HH:MM:SS some_timezone\"\n")];
395		buf[0] = '\0';
396		if (use_timestamp) {
397			size_t ret = \
398				strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP "
399					"\"%Y-%m-%d %H:%M:%S %Z\"\n", localtime(&now));
400			/* if user has Factory timezone or some other odd install, the
401			 * %Z above will overflow the string leaving us with undefined
402			 * results ... so let's try again without the timezone.
403			 */
404			if (ret == 0)
405				strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP "
406					"\"%Y-%m-%d %H:%M:%S\"\n", localtime(&now));
407		}
408		fprintf(out_h, "/*\n"
409			       " * Automatically generated C config: don't edit\n"
410			       " * Busybox version: %s\n"
411			       " */\n"
412			       "%s"
413			       "\n",
414			       sym_get_string_value(sym),
415			       buf);
416	}
417	if (!sym_change_count)
418		sym_clear_all_valid();
419
420	menu = rootmenu.list;
421	while (menu) {
422		sym = menu->sym;
423		if (!sym) {
424			if (!menu_is_visible(menu))
425				goto next;
426			str = menu_get_prompt(menu);
427			fprintf(out, "\n"
428				     "#\n"
429				     "# %s\n"
430				     "#\n", str);
431			if (out_h)
432				fprintf(out_h, "\n"
433					       "/*\n"
434					       " * %s\n"
435					       " */\n", str);
436		} else if (!(sym->flags & SYMBOL_CHOICE)) {
437			sym_calc_value(sym);
438/* bbox: we want to all syms
439			if (!(sym->flags & SYMBOL_WRITE))
440				goto next;
441*/
442			sym->flags &= ~SYMBOL_WRITE;
443			type = sym->type;
444			if (type == S_TRISTATE) {
445				sym_calc_value(modules_sym);
446				if (modules_sym->curr.tri == no)
447					type = S_BOOLEAN;
448			}
449			switch (type) {
450			case S_BOOLEAN:
451			case S_TRISTATE:
452				switch (sym_get_tristate_value(sym)) {
453				case no:
454					fprintf(out, "# CONFIG_%s is not set\n", sym->name);
455					if (out_h) {
456						fprintf(out_h, "#undef CONFIG_%s\n", sym->name);
457						/* bbox */
458						fprintf(out_h, "#define ENABLE_%s 0\n", sym->name);
459						fprintf(out_h, "#define USE_%s(...)\n", sym->name);
460						fprintf(out_h, "#define SKIP_%s(...) __VA_ARGS__\n", sym->name);
461					}
462					break;
463				case mod:
464					fprintf(out, "CONFIG_%s=m\n", sym->name);
465					if (out_h)
466						fprintf(out_h, "#define CONFIG_%s_MODULE 1\n", sym->name);
467					break;
468				case yes:
469					fprintf(out, "CONFIG_%s=y\n", sym->name);
470					if (out_h) {
471						fprintf(out_h, "#define CONFIG_%s 1\n", sym->name);
472						/* bbox */
473						fprintf(out_h, "#define ENABLE_%s 1\n", sym->name);
474						fprintf(out_h, "#define USE_%s(...) __VA_ARGS__\n", sym->name);
475						fprintf(out_h, "#define SKIP_%s(...)\n", sym->name);
476					}
477					break;
478				}
479				break;
480			case S_STRING:
481				// fix me
482				str = sym_get_string_value(sym);
483				fprintf(out, "CONFIG_%s=\"", sym->name);
484				if (out_h)
485					fprintf(out_h, "#define CONFIG_%s \"", sym->name);
486				do {
487					l = strcspn(str, "\"\\");
488					if (l) {
489						fwrite(str, l, 1, out);
490						if (out_h)
491							fwrite(str, l, 1, out_h);
492					}
493					str += l;
494					while (*str == '\\' || *str == '"') {
495						fprintf(out, "\\%c", *str);
496						if (out_h)
497							fprintf(out_h, "\\%c", *str);
498						str++;
499					}
500				} while (*str);
501				fputs("\"\n", out);
502				if (out_h) {
503					fputs("\"\n", out_h);
504					/* bbox */
505					fprintf(out_h, "#define ENABLE_%s 1\n", sym->name);
506					fprintf(out_h, "#define USE_%s(...) __VA_ARGS__\n", sym->name);
507					fprintf(out_h, "#define SKIP_%s(...)\n", sym->name);
508				}
509				break;
510			case S_HEX:
511				str = sym_get_string_value(sym);
512				if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) {
513					fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
514					if (out_h) {
515						fprintf(out_h, "#define CONFIG_%s 0x%s\n", sym->name, str);
516						/* bbox */
517						fprintf(out_h, "#define ENABLE_%s 1\n", sym->name);
518						fprintf(out_h, "#define USE_%s(...) __VA_ARGS__\n", sym->name);
519						fprintf(out_h, "#define SKIP_%s(...)\n", sym->name);
520					}
521					break;
522				}
523			case S_INT:
524				str = sym_get_string_value(sym);
525				fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
526				if (out_h) {
527					fprintf(out_h, "#define CONFIG_%s %s\n", sym->name, str);
528					/* bbox */
529					fprintf(out_h, "#define ENABLE_%s 1\n", sym->name);
530					fprintf(out_h, "#define USE_%s(...) __VA_ARGS__\n", sym->name);
531					fprintf(out_h, "#define SKIP_%s(...)\n", sym->name);
532				}
533				break;
534			}
535		}
536
537	next:
538		if (menu->list) {
539			menu = menu->list;
540			continue;
541		}
542		if (menu->next)
543			menu = menu->next;
544		else while ((menu = menu->parent)) {
545			if (menu->next) {
546				menu = menu->next;
547				break;
548			}
549		}
550	}
551	fclose(out);
552	if (out_h) {
553		fclose(out_h);
554		rename(".tmpconfig.h", "include/autoconf.h");
555	}
556	if (!name || basename != conf_def_filename) {
557		if (!name)
558			name = conf_def_filename;
559		sprintf(tmpname, "%s.old", name);
560		rename(name, tmpname);
561	}
562	sprintf(tmpname, "%s%s", dirname, basename);
563	if (rename(newname, tmpname))
564		return 1;
565
566	sym_change_count = 0;
567
568	return 0;
569}
570