xref: /openbmc/linux/scripts/kconfig/confdata.c (revision 96de0e252cedffad61b3cb5e05662c591898e69a)
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 <fcntl.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <time.h>
13 #include <unistd.h>
14 
15 #define LKC_DIRECT_LINK
16 #include "lkc.h"
17 
18 static void conf_warning(const char *fmt, ...)
19 	__attribute__ ((format (printf, 1, 2)));
20 
21 static const char *conf_filename;
22 static int conf_lineno, conf_warnings, conf_unsaved;
23 
24 const char conf_defname[] = "arch/$ARCH/defconfig";
25 
26 static void conf_warning(const char *fmt, ...)
27 {
28 	va_list ap;
29 	va_start(ap, fmt);
30 	fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno);
31 	vfprintf(stderr, fmt, ap);
32 	fprintf(stderr, "\n");
33 	va_end(ap);
34 	conf_warnings++;
35 }
36 
37 const char *conf_get_configname(void)
38 {
39 	char *name = getenv("KCONFIG_CONFIG");
40 
41 	return name ? name : ".config";
42 }
43 
44 static 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 
70 char *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 
86 int conf_read_simple(const char *name, int def)
87 {
88 	FILE *in = NULL;
89 	char line[1024];
90 	char *p, *p2;
91 	struct symbol *sym;
92 	int i, def_flags;
93 
94 	if (name) {
95 		in = zconf_fopen(name);
96 	} else {
97 		struct property *prop;
98 
99 		name = conf_get_configname();
100 		in = zconf_fopen(name);
101 		if (in)
102 			goto load;
103 		sym_add_change_count(1);
104 		if (!sym_defconfig_list)
105 			return 1;
106 
107 		for_all_defaults(sym_defconfig_list, prop) {
108 			if (expr_calc_value(prop->visible.expr) == no ||
109 			    prop->expr->type != E_SYMBOL)
110 				continue;
111 			name = conf_expand_value(prop->expr->left.sym->name);
112 			in = zconf_fopen(name);
113 			if (in) {
114 				printf(_("#\n"
115 					 "# using defaults found in %s\n"
116 					 "#\n"), name);
117 				goto load;
118 			}
119 		}
120 	}
121 	if (!in)
122 		return 1;
123 
124 load:
125 	conf_filename = name;
126 	conf_lineno = 0;
127 	conf_warnings = 0;
128 	conf_unsaved = 0;
129 
130 	def_flags = SYMBOL_DEF << def;
131 	for_all_symbols(i, sym) {
132 		sym->flags |= SYMBOL_CHANGED;
133 		sym->flags &= ~(def_flags|SYMBOL_VALID);
134 		if (sym_is_choice(sym))
135 			sym->flags |= def_flags;
136 		switch (sym->type) {
137 		case S_INT:
138 		case S_HEX:
139 		case S_STRING:
140 			if (sym->def[def].val)
141 				free(sym->def[def].val);
142 		default:
143 			sym->def[def].val = NULL;
144 			sym->def[def].tri = no;
145 		}
146 	}
147 
148 	while (fgets(line, sizeof(line), in)) {
149 		conf_lineno++;
150 		sym = NULL;
151 		switch (line[0]) {
152 		case '#':
153 			if (memcmp(line + 2, "CONFIG_", 7))
154 				continue;
155 			p = strchr(line + 9, ' ');
156 			if (!p)
157 				continue;
158 			*p++ = 0;
159 			if (strncmp(p, "is not set", 10))
160 				continue;
161 			if (def == S_DEF_USER) {
162 				sym = sym_find(line + 9);
163 				if (!sym) {
164 					conf_warning("trying to assign nonexistent symbol %s", line + 9);
165 					break;
166 				}
167 			} else {
168 				sym = sym_lookup(line + 9, 0);
169 				if (sym->type == S_UNKNOWN)
170 					sym->type = S_BOOLEAN;
171 			}
172 			if (sym->flags & def_flags) {
173 				conf_warning("trying to reassign symbol %s", sym->name);
174 				break;
175 			}
176 			switch (sym->type) {
177 			case S_BOOLEAN:
178 			case S_TRISTATE:
179 				sym->def[def].tri = no;
180 				sym->flags |= def_flags;
181 				break;
182 			default:
183 				;
184 			}
185 			break;
186 		case 'C':
187 			if (memcmp(line, "CONFIG_", 7)) {
188 				conf_warning("unexpected data");
189 				continue;
190 			}
191 			p = strchr(line + 7, '=');
192 			if (!p)
193 				continue;
194 			*p++ = 0;
195 			p2 = strchr(p, '\n');
196 			if (p2) {
197 				*p2-- = 0;
198 				if (*p2 == '\r')
199 					*p2 = 0;
200 			}
201 			if (def == S_DEF_USER) {
202 				sym = sym_find(line + 7);
203 				if (!sym) {
204 					conf_warning("trying to assign nonexistent symbol %s", line + 7);
205 					break;
206 				}
207 			} else {
208 				sym = sym_lookup(line + 7, 0);
209 				if (sym->type == S_UNKNOWN)
210 					sym->type = S_OTHER;
211 			}
212 			if (sym->flags & def_flags) {
213 				conf_warning("trying to reassign symbol %s", sym->name);
214 				break;
215 			}
216 			switch (sym->type) {
217 			case S_TRISTATE:
218 				if (p[0] == 'm') {
219 					sym->def[def].tri = mod;
220 					sym->flags |= def_flags;
221 					break;
222 				}
223 			case S_BOOLEAN:
224 				if (p[0] == 'y') {
225 					sym->def[def].tri = yes;
226 					sym->flags |= def_flags;
227 					break;
228 				}
229 				if (p[0] == 'n') {
230 					sym->def[def].tri = no;
231 					sym->flags |= def_flags;
232 					break;
233 				}
234 				conf_warning("symbol value '%s' invalid for %s", p, sym->name);
235 				break;
236 			case S_OTHER:
237 				if (*p != '"') {
238 					for (p2 = p; *p2 && !isspace(*p2); p2++)
239 						;
240 					sym->type = S_STRING;
241 					goto done;
242 				}
243 			case S_STRING:
244 				if (*p++ != '"')
245 					break;
246 				for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
247 					if (*p2 == '"') {
248 						*p2 = 0;
249 						break;
250 					}
251 					memmove(p2, p2 + 1, strlen(p2));
252 				}
253 				if (!p2) {
254 					conf_warning("invalid string found");
255 					continue;
256 				}
257 			case S_INT:
258 			case S_HEX:
259 			done:
260 				if (sym_string_valid(sym, p)) {
261 					sym->def[def].val = strdup(p);
262 					sym->flags |= def_flags;
263 				} else {
264 					conf_warning("symbol value '%s' invalid for %s", p, sym->name);
265 					continue;
266 				}
267 				break;
268 			default:
269 				;
270 			}
271 			break;
272 		case '\r':
273 		case '\n':
274 			break;
275 		default:
276 			conf_warning("unexpected data");
277 			continue;
278 		}
279 		if (sym && sym_is_choice_value(sym)) {
280 			struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
281 			switch (sym->def[def].tri) {
282 			case no:
283 				break;
284 			case mod:
285 				if (cs->def[def].tri == yes) {
286 					conf_warning("%s creates inconsistent choice state", sym->name);
287 					cs->flags &= ~def_flags;
288 				}
289 				break;
290 			case yes:
291 				if (cs->def[def].tri != no) {
292 					conf_warning("%s creates inconsistent choice state", sym->name);
293 					cs->flags &= ~def_flags;
294 				} else
295 					cs->def[def].val = sym;
296 				break;
297 			}
298 			cs->def[def].tri = E_OR(cs->def[def].tri, sym->def[def].tri);
299 		}
300 	}
301 	fclose(in);
302 
303 	if (modules_sym)
304 		sym_calc_value(modules_sym);
305 	return 0;
306 }
307 
308 int conf_read(const char *name)
309 {
310 	struct symbol *sym;
311 	struct property *prop;
312 	struct expr *e;
313 	int i, flags;
314 
315 	sym_set_change_count(0);
316 
317 	if (conf_read_simple(name, S_DEF_USER))
318 		return 1;
319 
320 	for_all_symbols(i, sym) {
321 		sym_calc_value(sym);
322 		if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO))
323 			goto sym_ok;
324 		if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) {
325 			/* check that calculated value agrees with saved value */
326 			switch (sym->type) {
327 			case S_BOOLEAN:
328 			case S_TRISTATE:
329 				if (sym->def[S_DEF_USER].tri != sym_get_tristate_value(sym))
330 					break;
331 				if (!sym_is_choice(sym))
332 					goto sym_ok;
333 			default:
334 				if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val))
335 					goto sym_ok;
336 				break;
337 			}
338 		} else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE))
339 			/* no previous value and not saved */
340 			goto sym_ok;
341 		conf_unsaved++;
342 		/* maybe print value in verbose mode... */
343 	sym_ok:
344 		if (!sym_is_choice(sym))
345 			continue;
346 		/* The choice symbol only has a set value (and thus is not new)
347 		 * if all its visible childs have values.
348 		 */
349 		prop = sym_get_choice_prop(sym);
350 		flags = sym->flags;
351 		for (e = prop->expr; e; e = e->left.expr)
352 			if (e->right.sym->visible != no)
353 				flags &= e->right.sym->flags;
354 		sym->flags &= flags | ~SYMBOL_DEF_USER;
355 	}
356 
357 	for_all_symbols(i, sym) {
358 		if (sym_has_value(sym) && !sym_is_choice_value(sym)) {
359 			/* Reset values of generates values, so they'll appear
360 			 * as new, if they should become visible, but that
361 			 * doesn't quite work if the Kconfig and the saved
362 			 * configuration disagree.
363 			 */
364 			if (sym->visible == no && !conf_unsaved)
365 				sym->flags &= ~SYMBOL_DEF_USER;
366 			switch (sym->type) {
367 			case S_STRING:
368 			case S_INT:
369 			case S_HEX:
370 				/* Reset a string value if it's out of range */
371 				if (sym_string_within_range(sym, sym->def[S_DEF_USER].val))
372 					break;
373 				sym->flags &= ~(SYMBOL_VALID|SYMBOL_DEF_USER);
374 				conf_unsaved++;
375 				break;
376 			default:
377 				break;
378 			}
379 		}
380 	}
381 
382 	sym_add_change_count(conf_warnings || conf_unsaved);
383 
384 	return 0;
385 }
386 
387 int conf_write(const char *name)
388 {
389 	FILE *out;
390 	struct symbol *sym;
391 	struct menu *menu;
392 	const char *basename;
393 	char dirname[128], tmpname[128], newname[128];
394 	int type, l;
395 	const char *str;
396 	time_t now;
397 	int use_timestamp = 1;
398 	char *env;
399 
400 	dirname[0] = 0;
401 	if (name && name[0]) {
402 		struct stat st;
403 		char *slash;
404 
405 		if (!stat(name, &st) && S_ISDIR(st.st_mode)) {
406 			strcpy(dirname, name);
407 			strcat(dirname, "/");
408 			basename = conf_get_configname();
409 		} else if ((slash = strrchr(name, '/'))) {
410 			int size = slash - name + 1;
411 			memcpy(dirname, name, size);
412 			dirname[size] = 0;
413 			if (slash[1])
414 				basename = slash + 1;
415 			else
416 				basename = conf_get_configname();
417 		} else
418 			basename = name;
419 	} else
420 		basename = conf_get_configname();
421 
422 	sprintf(newname, "%s%s", dirname, basename);
423 	env = getenv("KCONFIG_OVERWRITECONFIG");
424 	if (!env || !*env) {
425 		sprintf(tmpname, "%s.tmpconfig.%d", dirname, (int)getpid());
426 		out = fopen(tmpname, "w");
427 	} else {
428 		*tmpname = 0;
429 		out = fopen(newname, "w");
430 	}
431 	if (!out)
432 		return 1;
433 
434 	sym = sym_lookup("KERNELVERSION", 0);
435 	sym_calc_value(sym);
436 	time(&now);
437 	env = getenv("KCONFIG_NOTIMESTAMP");
438 	if (env && *env)
439 		use_timestamp = 0;
440 
441 	fprintf(out, _("#\n"
442 		       "# Automatically generated make config: don't edit\n"
443 		       "# Linux kernel version: %s\n"
444 		       "%s%s"
445 		       "#\n"),
446 		     sym_get_string_value(sym),
447 		     use_timestamp ? "# " : "",
448 		     use_timestamp ? ctime(&now) : "");
449 
450 	if (!conf_get_changed())
451 		sym_clear_all_valid();
452 
453 	menu = rootmenu.list;
454 	while (menu) {
455 		sym = menu->sym;
456 		if (!sym) {
457 			if (!menu_is_visible(menu))
458 				goto next;
459 			str = menu_get_prompt(menu);
460 			fprintf(out, "\n"
461 				     "#\n"
462 				     "# %s\n"
463 				     "#\n", str);
464 		} else if (!(sym->flags & SYMBOL_CHOICE)) {
465 			sym_calc_value(sym);
466 			if (!(sym->flags & SYMBOL_WRITE))
467 				goto next;
468 			sym->flags &= ~SYMBOL_WRITE;
469 			type = sym->type;
470 			if (type == S_TRISTATE) {
471 				sym_calc_value(modules_sym);
472 				if (modules_sym->curr.tri == no)
473 					type = S_BOOLEAN;
474 			}
475 			switch (type) {
476 			case S_BOOLEAN:
477 			case S_TRISTATE:
478 				switch (sym_get_tristate_value(sym)) {
479 				case no:
480 					fprintf(out, "# CONFIG_%s is not set\n", sym->name);
481 					break;
482 				case mod:
483 					fprintf(out, "CONFIG_%s=m\n", sym->name);
484 					break;
485 				case yes:
486 					fprintf(out, "CONFIG_%s=y\n", sym->name);
487 					break;
488 				}
489 				break;
490 			case S_STRING:
491 				str = sym_get_string_value(sym);
492 				fprintf(out, "CONFIG_%s=\"", sym->name);
493 				while (1) {
494 					l = strcspn(str, "\"\\");
495 					if (l) {
496 						fwrite(str, l, 1, out);
497 						str += l;
498 					}
499 					if (!*str)
500 						break;
501 					fprintf(out, "\\%c", *str++);
502 				}
503 				fputs("\"\n", out);
504 				break;
505 			case S_HEX:
506 				str = sym_get_string_value(sym);
507 				if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) {
508 					fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
509 					break;
510 				}
511 			case S_INT:
512 				str = sym_get_string_value(sym);
513 				fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
514 				break;
515 			}
516 		}
517 
518 	next:
519 		if (menu->list) {
520 			menu = menu->list;
521 			continue;
522 		}
523 		if (menu->next)
524 			menu = menu->next;
525 		else while ((menu = menu->parent)) {
526 			if (menu->next) {
527 				menu = menu->next;
528 				break;
529 			}
530 		}
531 	}
532 	fclose(out);
533 
534 	if (*tmpname) {
535 		strcat(dirname, basename);
536 		strcat(dirname, ".old");
537 		rename(newname, dirname);
538 		if (rename(tmpname, newname))
539 			return 1;
540 	}
541 
542 	printf(_("#\n"
543 		 "# configuration written to %s\n"
544 		 "#\n"), newname);
545 
546 	sym_set_change_count(0);
547 
548 	return 0;
549 }
550 
551 int conf_split_config(void)
552 {
553 	char *name, path[128];
554 	char *s, *d, c;
555 	struct symbol *sym;
556 	struct stat sb;
557 	int res, i, fd;
558 
559 	name = getenv("KCONFIG_AUTOCONFIG");
560 	if (!name)
561 		name = "include/config/auto.conf";
562 	conf_read_simple(name, S_DEF_AUTO);
563 
564 	if (chdir("include/config"))
565 		return 1;
566 
567 	res = 0;
568 	for_all_symbols(i, sym) {
569 		sym_calc_value(sym);
570 		if ((sym->flags & SYMBOL_AUTO) || !sym->name)
571 			continue;
572 		if (sym->flags & SYMBOL_WRITE) {
573 			if (sym->flags & SYMBOL_DEF_AUTO) {
574 				/*
575 				 * symbol has old and new value,
576 				 * so compare them...
577 				 */
578 				switch (sym->type) {
579 				case S_BOOLEAN:
580 				case S_TRISTATE:
581 					if (sym_get_tristate_value(sym) ==
582 					    sym->def[S_DEF_AUTO].tri)
583 						continue;
584 					break;
585 				case S_STRING:
586 				case S_HEX:
587 				case S_INT:
588 					if (!strcmp(sym_get_string_value(sym),
589 						    sym->def[S_DEF_AUTO].val))
590 						continue;
591 					break;
592 				default:
593 					break;
594 				}
595 			} else {
596 				/*
597 				 * If there is no old value, only 'no' (unset)
598 				 * is allowed as new value.
599 				 */
600 				switch (sym->type) {
601 				case S_BOOLEAN:
602 				case S_TRISTATE:
603 					if (sym_get_tristate_value(sym) == no)
604 						continue;
605 					break;
606 				default:
607 					break;
608 				}
609 			}
610 		} else if (!(sym->flags & SYMBOL_DEF_AUTO))
611 			/* There is neither an old nor a new value. */
612 			continue;
613 		/* else
614 		 *	There is an old value, but no new value ('no' (unset)
615 		 *	isn't saved in auto.conf, so the old value is always
616 		 *	different from 'no').
617 		 */
618 
619 		/* Replace all '_' and append ".h" */
620 		s = sym->name;
621 		d = path;
622 		while ((c = *s++)) {
623 			c = tolower(c);
624 			*d++ = (c == '_') ? '/' : c;
625 		}
626 		strcpy(d, ".h");
627 
628 		/* Assume directory path already exists. */
629 		fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
630 		if (fd == -1) {
631 			if (errno != ENOENT) {
632 				res = 1;
633 				break;
634 			}
635 			/*
636 			 * Create directory components,
637 			 * unless they exist already.
638 			 */
639 			d = path;
640 			while ((d = strchr(d, '/'))) {
641 				*d = 0;
642 				if (stat(path, &sb) && mkdir(path, 0755)) {
643 					res = 1;
644 					goto out;
645 				}
646 				*d++ = '/';
647 			}
648 			/* Try it again. */
649 			fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
650 			if (fd == -1) {
651 				res = 1;
652 				break;
653 			}
654 		}
655 		close(fd);
656 	}
657 out:
658 	if (chdir("../.."))
659 		return 1;
660 
661 	return res;
662 }
663 
664 int conf_write_autoconf(void)
665 {
666 	struct symbol *sym;
667 	const char *str;
668 	char *name;
669 	FILE *out, *out_h;
670 	time_t now;
671 	int i, l;
672 
673 	sym_clear_all_valid();
674 
675 	file_write_dep("include/config/auto.conf.cmd");
676 
677 	if (conf_split_config())
678 		return 1;
679 
680 	out = fopen(".tmpconfig", "w");
681 	if (!out)
682 		return 1;
683 
684 	out_h = fopen(".tmpconfig.h", "w");
685 	if (!out_h) {
686 		fclose(out);
687 		return 1;
688 	}
689 
690 	sym = sym_lookup("KERNELVERSION", 0);
691 	sym_calc_value(sym);
692 	time(&now);
693 	fprintf(out, "#\n"
694 		     "# Automatically generated make config: don't edit\n"
695 		     "# Linux kernel version: %s\n"
696 		     "# %s"
697 		     "#\n",
698 		     sym_get_string_value(sym), ctime(&now));
699 	fprintf(out_h, "/*\n"
700 		       " * Automatically generated C config: don't edit\n"
701 		       " * Linux kernel version: %s\n"
702 		       " * %s"
703 		       " */\n"
704 		       "#define AUTOCONF_INCLUDED\n",
705 		       sym_get_string_value(sym), ctime(&now));
706 
707 	for_all_symbols(i, sym) {
708 		sym_calc_value(sym);
709 		if (!(sym->flags & SYMBOL_WRITE) || !sym->name)
710 			continue;
711 		switch (sym->type) {
712 		case S_BOOLEAN:
713 		case S_TRISTATE:
714 			switch (sym_get_tristate_value(sym)) {
715 			case no:
716 				break;
717 			case mod:
718 				fprintf(out, "CONFIG_%s=m\n", sym->name);
719 				fprintf(out_h, "#define CONFIG_%s_MODULE 1\n", sym->name);
720 				break;
721 			case yes:
722 				fprintf(out, "CONFIG_%s=y\n", sym->name);
723 				fprintf(out_h, "#define CONFIG_%s 1\n", sym->name);
724 				break;
725 			}
726 			break;
727 		case S_STRING:
728 			str = sym_get_string_value(sym);
729 			fprintf(out, "CONFIG_%s=\"", sym->name);
730 			fprintf(out_h, "#define CONFIG_%s \"", sym->name);
731 			while (1) {
732 				l = strcspn(str, "\"\\");
733 				if (l) {
734 					fwrite(str, l, 1, out);
735 					fwrite(str, l, 1, out_h);
736 					str += l;
737 				}
738 				if (!*str)
739 					break;
740 				fprintf(out, "\\%c", *str);
741 				fprintf(out_h, "\\%c", *str);
742 				str++;
743 			}
744 			fputs("\"\n", out);
745 			fputs("\"\n", out_h);
746 			break;
747 		case S_HEX:
748 			str = sym_get_string_value(sym);
749 			if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) {
750 				fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
751 				fprintf(out_h, "#define CONFIG_%s 0x%s\n", sym->name, str);
752 				break;
753 			}
754 		case S_INT:
755 			str = sym_get_string_value(sym);
756 			fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
757 			fprintf(out_h, "#define CONFIG_%s %s\n", sym->name, str);
758 			break;
759 		default:
760 			break;
761 		}
762 	}
763 	fclose(out);
764 	fclose(out_h);
765 
766 	name = getenv("KCONFIG_AUTOHEADER");
767 	if (!name)
768 		name = "include/linux/autoconf.h";
769 	if (rename(".tmpconfig.h", name))
770 		return 1;
771 	name = getenv("KCONFIG_AUTOCONFIG");
772 	if (!name)
773 		name = "include/config/auto.conf";
774 	/*
775 	 * This must be the last step, kbuild has a dependency on auto.conf
776 	 * and this marks the successful completion of the previous steps.
777 	 */
778 	if (rename(".tmpconfig", name))
779 		return 1;
780 
781 	return 0;
782 }
783 
784 static int sym_change_count;
785 static void (*conf_changed_callback)(void);
786 
787 void sym_set_change_count(int count)
788 {
789 	int _sym_change_count = sym_change_count;
790 	sym_change_count = count;
791 	if (conf_changed_callback &&
792 	    (bool)_sym_change_count != (bool)count)
793 		conf_changed_callback();
794 }
795 
796 void sym_add_change_count(int count)
797 {
798 	sym_set_change_count(count + sym_change_count);
799 }
800 
801 bool conf_get_changed(void)
802 {
803 	return sym_change_count;
804 }
805 
806 void conf_set_changed_callback(void (*fn)(void))
807 {
808 	conf_changed_callback = fn;
809 }
810