Commit | Line | Data |
---|---|---|
0c874100 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | /* |
3 | * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> | |
1da177e4 LT |
4 | */ |
5 | ||
67424f61 | 6 | #include <sys/mman.h> |
1da177e4 | 7 | #include <sys/stat.h> |
78cb0907 | 8 | #include <sys/types.h> |
1da177e4 | 9 | #include <ctype.h> |
94bedeca | 10 | #include <errno.h> |
2e3646e5 | 11 | #include <fcntl.h> |
558e78e3 | 12 | #include <limits.h> |
10a4b277 | 13 | #include <stdarg.h> |
6ce45a91 | 14 | #include <stdbool.h> |
1da177e4 LT |
15 | #include <stdio.h> |
16 | #include <stdlib.h> | |
17 | #include <string.h> | |
18 | #include <time.h> | |
19 | #include <unistd.h> | |
20 | ||
91b69454 | 21 | #include "internal.h" |
1da177e4 LT |
22 | #include "lkc.h" |
23 | ||
526396b7 MY |
24 | struct gstr autoconf_cmd; |
25 | ||
0608182a MY |
26 | /* return true if 'path' exists, false otherwise */ |
27 | static bool is_present(const char *path) | |
28 | { | |
29 | struct stat st; | |
30 | ||
31 | return !stat(path, &st); | |
32 | } | |
33 | ||
34 | /* return true if 'path' exists and it is a directory, false otherwise */ | |
35 | static bool is_dir(const char *path) | |
36 | { | |
37 | struct stat st; | |
38 | ||
39 | if (stat(path, &st)) | |
a69b191f | 40 | return false; |
0608182a MY |
41 | |
42 | return S_ISDIR(st.st_mode); | |
43 | } | |
44 | ||
67424f61 MY |
45 | /* return true if the given two files are the same, false otherwise */ |
46 | static bool is_same(const char *file1, const char *file2) | |
47 | { | |
48 | int fd1, fd2; | |
49 | struct stat st1, st2; | |
50 | void *map1, *map2; | |
51 | bool ret = false; | |
52 | ||
53 | fd1 = open(file1, O_RDONLY); | |
54 | if (fd1 < 0) | |
55 | return ret; | |
56 | ||
57 | fd2 = open(file2, O_RDONLY); | |
58 | if (fd2 < 0) | |
59 | goto close1; | |
60 | ||
61 | ret = fstat(fd1, &st1); | |
62 | if (ret) | |
63 | goto close2; | |
64 | ret = fstat(fd2, &st2); | |
65 | if (ret) | |
66 | goto close2; | |
67 | ||
68 | if (st1.st_size != st2.st_size) | |
69 | goto close2; | |
70 | ||
71 | map1 = mmap(NULL, st1.st_size, PROT_READ, MAP_PRIVATE, fd1, 0); | |
72 | if (map1 == MAP_FAILED) | |
73 | goto close2; | |
74 | ||
75 | map2 = mmap(NULL, st2.st_size, PROT_READ, MAP_PRIVATE, fd2, 0); | |
76 | if (map2 == MAP_FAILED) | |
77 | goto close2; | |
78 | ||
79 | if (bcmp(map1, map2, st1.st_size)) | |
80 | goto close2; | |
81 | ||
82 | ret = true; | |
83 | close2: | |
84 | close(fd2); | |
85 | close1: | |
86 | close(fd1); | |
87 | ||
88 | return ret; | |
89 | } | |
90 | ||
0608182a MY |
91 | /* |
92 | * Create the parent directory of the given path. | |
93 | * | |
94 | * For example, if 'include/config/auto.conf' is given, create 'include/config'. | |
95 | */ | |
96 | static int make_parent_dir(const char *path) | |
97 | { | |
98 | char tmp[PATH_MAX + 1]; | |
99 | char *p; | |
100 | ||
101 | strncpy(tmp, path, sizeof(tmp)); | |
102 | tmp[sizeof(tmp) - 1] = 0; | |
103 | ||
104 | /* Remove the base name. Just return if nothing is left */ | |
105 | p = strrchr(tmp, '/'); | |
106 | if (!p) | |
107 | return 0; | |
108 | *(p + 1) = 0; | |
109 | ||
110 | /* Just in case it is an absolute path */ | |
111 | p = tmp; | |
112 | while (*p == '/') | |
113 | p++; | |
114 | ||
115 | while ((p = strchr(p, '/'))) { | |
116 | *p = 0; | |
117 | ||
118 | /* skip if the directory exists */ | |
119 | if (!is_dir(tmp) && mkdir(tmp, 0755)) | |
120 | return -1; | |
121 | ||
122 | *p = '/'; | |
123 | while (*p == '/') | |
124 | p++; | |
125 | } | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
1508fec8 MY |
130 | static char depfile_path[PATH_MAX]; |
131 | static size_t depfile_prefix_len; | |
132 | ||
133 | /* touch depfile for symbol 'name' */ | |
134 | static int conf_touch_dep(const char *name) | |
135 | { | |
fee762d6 | 136 | int fd; |
1508fec8 | 137 | |
0e0345b7 AD |
138 | /* check overflow: prefix + name + '\0' must fit in buffer. */ |
139 | if (depfile_prefix_len + strlen(name) + 1 > sizeof(depfile_path)) | |
1508fec8 MY |
140 | return -1; |
141 | ||
fee762d6 | 142 | strcpy(depfile_path + depfile_prefix_len, name); |
1508fec8 | 143 | |
1508fec8 | 144 | fd = open(depfile_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); |
fee762d6 MY |
145 | if (fd == -1) |
146 | return -1; | |
1508fec8 MY |
147 | close(fd); |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
c1a0f5e3 RZ |
152 | static void conf_warning(const char *fmt, ...) |
153 | __attribute__ ((format (printf, 1, 2))); | |
154 | ||
42368c37 MM |
155 | static void conf_message(const char *fmt, ...) |
156 | __attribute__ ((format (printf, 1, 2))); | |
157 | ||
c1a0f5e3 | 158 | static const char *conf_filename; |
84dd95d4 | 159 | static int conf_lineno, conf_warnings; |
c1a0f5e3 | 160 | |
15d3f766 SS |
161 | bool conf_errors(void) |
162 | { | |
163 | if (conf_warnings) | |
164 | return getenv("KCONFIG_WERROR"); | |
165 | return false; | |
166 | } | |
167 | ||
c1a0f5e3 RZ |
168 | static void conf_warning(const char *fmt, ...) |
169 | { | |
170 | va_list ap; | |
171 | va_start(ap, fmt); | |
172 | fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno); | |
173 | vfprintf(stderr, fmt, ap); | |
174 | fprintf(stderr, "\n"); | |
175 | va_end(ap); | |
176 | conf_warnings++; | |
177 | } | |
178 | ||
5accd7f3 | 179 | static void conf_default_message_callback(const char *s) |
42368c37 MM |
180 | { |
181 | printf("#\n# "); | |
5accd7f3 | 182 | printf("%s", s); |
42368c37 MM |
183 | printf("\n#\n"); |
184 | } | |
185 | ||
5accd7f3 | 186 | static void (*conf_message_callback)(const char *s) = |
42368c37 | 187 | conf_default_message_callback; |
5accd7f3 | 188 | void conf_set_message_callback(void (*fn)(const char *s)) |
42368c37 MM |
189 | { |
190 | conf_message_callback = fn; | |
191 | } | |
192 | ||
193 | static void conf_message(const char *fmt, ...) | |
194 | { | |
195 | va_list ap; | |
5accd7f3 MY |
196 | char buf[4096]; |
197 | ||
198 | if (!conf_message_callback) | |
199 | return; | |
42368c37 MM |
200 | |
201 | va_start(ap, fmt); | |
5accd7f3 MY |
202 | |
203 | vsnprintf(buf, sizeof(buf), fmt, ap); | |
204 | conf_message_callback(buf); | |
b6a2ab2c | 205 | va_end(ap); |
42368c37 MM |
206 | } |
207 | ||
14cdd3c4 RZ |
208 | const char *conf_get_configname(void) |
209 | { | |
210 | char *name = getenv("KCONFIG_CONFIG"); | |
211 | ||
212 | return name ? name : ".config"; | |
213 | } | |
214 | ||
9b9f5948 | 215 | static const char *conf_get_autoconfig_name(void) |
12122f62 MH |
216 | { |
217 | char *name = getenv("KCONFIG_AUTOCONFIG"); | |
218 | ||
219 | return name ? name : "include/config/auto.conf"; | |
220 | } | |
221 | ||
8499f2dd MY |
222 | static const char *conf_get_autoheader_name(void) |
223 | { | |
224 | char *name = getenv("KCONFIG_AUTOHEADER"); | |
225 | ||
226 | return name ? name : "include/generated/autoconf.h"; | |
227 | } | |
228 | ||
2f7ab126 MO |
229 | static const char *conf_get_rustccfg_name(void) |
230 | { | |
231 | char *name = getenv("KCONFIG_RUSTCCFG"); | |
232 | ||
233 | return name ? name : "include/generated/rustc_cfg"; | |
234 | } | |
235 | ||
9c900a9c SR |
236 | static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p) |
237 | { | |
238 | char *p2; | |
239 | ||
240 | switch (sym->type) { | |
241 | case S_TRISTATE: | |
242 | if (p[0] == 'm') { | |
243 | sym->def[def].tri = mod; | |
244 | sym->flags |= def_flags; | |
245 | break; | |
246 | } | |
d8fc3200 | 247 | /* fall through */ |
9c900a9c SR |
248 | case S_BOOLEAN: |
249 | if (p[0] == 'y') { | |
250 | sym->def[def].tri = yes; | |
251 | sym->flags |= def_flags; | |
252 | break; | |
253 | } | |
254 | if (p[0] == 'n') { | |
255 | sym->def[def].tri = no; | |
256 | sym->flags |= def_flags; | |
257 | break; | |
258 | } | |
04b19b77 YM |
259 | if (def != S_DEF_AUTO) |
260 | conf_warning("symbol value '%s' invalid for %s", | |
261 | p, sym->name); | |
75f1468b | 262 | return 1; |
9c900a9c | 263 | case S_STRING: |
129ab0d2 MY |
264 | /* No escaping for S_DEF_AUTO (include/config/auto.conf) */ |
265 | if (def != S_DEF_AUTO) { | |
266 | if (*p++ != '"') | |
9c900a9c | 267 | break; |
129ab0d2 MY |
268 | for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) { |
269 | if (*p2 == '"') { | |
270 | *p2 = 0; | |
271 | break; | |
272 | } | |
273 | memmove(p2, p2 + 1, strlen(p2)); | |
9c900a9c | 274 | } |
129ab0d2 | 275 | if (!p2) { |
04b19b77 | 276 | conf_warning("invalid string found"); |
129ab0d2 MY |
277 | return 1; |
278 | } | |
9c900a9c | 279 | } |
d8fc3200 | 280 | /* fall through */ |
9c900a9c SR |
281 | case S_INT: |
282 | case S_HEX: | |
9c900a9c | 283 | if (sym_string_valid(sym, p)) { |
cd81fc82 | 284 | sym->def[def].val = xstrdup(p); |
9c900a9c SR |
285 | sym->flags |= def_flags; |
286 | } else { | |
04b19b77 YM |
287 | if (def != S_DEF_AUTO) |
288 | conf_warning("symbol value '%s' invalid for %s", | |
289 | p, sym->name); | |
9c900a9c SR |
290 | return 1; |
291 | } | |
292 | break; | |
293 | default: | |
294 | ; | |
295 | } | |
296 | return 0; | |
297 | } | |
298 | ||
9925d6b7 MY |
299 | /* like getline(), but the newline character is stripped away */ |
300 | static ssize_t getline_stripped(char **lineptr, size_t *n, FILE *stream) | |
301 | { | |
302 | ssize_t len; | |
303 | ||
aa8427fb | 304 | len = getline(lineptr, n, stream); |
9925d6b7 MY |
305 | |
306 | if (len > 0 && (*lineptr)[len - 1] == '\n') { | |
307 | len--; | |
308 | (*lineptr)[len] = '\0'; | |
309 | ||
310 | if (len > 0 && (*lineptr)[len - 1] == '\r') { | |
311 | len--; | |
312 | (*lineptr)[len] = '\0'; | |
313 | } | |
314 | } | |
315 | ||
316 | return len; | |
317 | } | |
318 | ||
669bfad9 | 319 | int conf_read_simple(const char *name, int def) |
1da177e4 LT |
320 | { |
321 | FILE *in = NULL; | |
1a7a8c6f CS |
322 | char *line = NULL; |
323 | size_t line_asize = 0; | |
9925d6b7 | 324 | char *p, *val; |
1da177e4 | 325 | struct symbol *sym; |
91b69454 | 326 | int def_flags; |
15d3f766 | 327 | const char *warn_unknown, *sym_name; |
1da177e4 | 328 | |
7cd34300 | 329 | warn_unknown = getenv("KCONFIG_WARN_UNKNOWN_SYMBOLS"); |
1da177e4 LT |
330 | if (name) { |
331 | in = zconf_fopen(name); | |
332 | } else { | |
b75b0a81 | 333 | char *env; |
face4374 | 334 | |
14cdd3c4 | 335 | name = conf_get_configname(); |
ddc97cac RZ |
336 | in = zconf_fopen(name); |
337 | if (in) | |
338 | goto load; | |
5ee54659 | 339 | conf_set_changed(true); |
b75b0a81 MY |
340 | |
341 | env = getenv("KCONFIG_DEFCONFIG_LIST"); | |
342 | if (!env) | |
face4374 RZ |
343 | return 1; |
344 | ||
b75b0a81 MY |
345 | while (1) { |
346 | bool is_last; | |
347 | ||
348 | while (isspace(*env)) | |
349 | env++; | |
350 | ||
351 | if (!*env) | |
352 | break; | |
353 | ||
354 | p = env; | |
355 | while (*p && !isspace(*p)) | |
356 | p++; | |
357 | ||
358 | is_last = (*p == '\0'); | |
359 | ||
360 | *p = '\0'; | |
361 | ||
362 | in = zconf_fopen(env); | |
1da177e4 | 363 | if (in) { |
694c49a7 | 364 | conf_message("using defaults found in %s", |
b75b0a81 | 365 | env); |
ddc97cac | 366 | goto load; |
1da177e4 | 367 | } |
b75b0a81 MY |
368 | |
369 | if (is_last) | |
370 | break; | |
371 | ||
372 | env = p + 1; | |
1da177e4 LT |
373 | } |
374 | } | |
1da177e4 LT |
375 | if (!in) |
376 | return 1; | |
377 | ||
ddc97cac | 378 | load: |
c1a0f5e3 RZ |
379 | conf_filename = name; |
380 | conf_lineno = 0; | |
381 | conf_warnings = 0; | |
c1a0f5e3 | 382 | |
669bfad9 | 383 | def_flags = SYMBOL_DEF << def; |
91b69454 | 384 | for_all_symbols(sym) { |
669bfad9 RZ |
385 | sym->flags |= SYMBOL_CHANGED; |
386 | sym->flags &= ~(def_flags|SYMBOL_VALID); | |
490f1617 YM |
387 | if (sym_is_choice(sym)) |
388 | sym->flags |= def_flags; | |
1da177e4 LT |
389 | switch (sym->type) { |
390 | case S_INT: | |
391 | case S_HEX: | |
392 | case S_STRING: | |
407868de | 393 | free(sym->def[def].val); |
d8fc3200 | 394 | /* fall through */ |
1da177e4 | 395 | default: |
669bfad9 RZ |
396 | sym->def[def].val = NULL; |
397 | sym->def[def].tri = no; | |
1da177e4 LT |
398 | } |
399 | } | |
400 | ||
9925d6b7 | 401 | while (getline_stripped(&line, &line_asize, in) != -1) { |
c1a0f5e3 | 402 | conf_lineno++; |
48ab6c9c MY |
403 | |
404 | if (!line[0]) /* blank line */ | |
405 | continue; | |
406 | ||
8baefd30 | 407 | if (line[0] == '#') { |
4d137ab0 MY |
408 | if (line[1] != ' ') |
409 | continue; | |
d854b4b2 MY |
410 | p = line + 2; |
411 | if (memcmp(p, CONFIG_, strlen(CONFIG_))) | |
1da177e4 | 412 | continue; |
d854b4b2 MY |
413 | sym_name = p + strlen(CONFIG_); |
414 | p = strchr(sym_name, ' '); | |
1da177e4 LT |
415 | if (!p) |
416 | continue; | |
417 | *p++ = 0; | |
4aced3ec | 418 | if (strcmp(p, "is not set")) |
1da177e4 | 419 | continue; |
92d4fe0a | 420 | |
d854b4b2 | 421 | val = "n"; |
48ab6c9c MY |
422 | } else { |
423 | if (memcmp(line, CONFIG_, strlen(CONFIG_))) { | |
424 | conf_warning("unexpected data: %s", line); | |
425 | continue; | |
426 | } | |
427 | ||
d854b4b2 MY |
428 | sym_name = line + strlen(CONFIG_); |
429 | p = strchr(sym_name, '='); | |
48ab6c9c MY |
430 | if (!p) { |
431 | conf_warning("unexpected data: %s", line); | |
1da177e4 | 432 | continue; |
48ab6c9c | 433 | } |
9925d6b7 MY |
434 | *p = 0; |
435 | val = p + 1; | |
1da177e4 | 436 | } |
75889e9b | 437 | |
d854b4b2 MY |
438 | sym = sym_find(sym_name); |
439 | if (!sym) { | |
440 | if (def == S_DEF_AUTO) { | |
441 | /* | |
442 | * Reading from include/config/auto.conf. | |
443 | * If CONFIG_FOO previously existed in auto.conf | |
444 | * but it is missing now, include/config/FOO | |
445 | * must be touched. | |
446 | */ | |
447 | conf_touch_dep(sym_name); | |
448 | } else { | |
449 | if (warn_unknown) | |
450 | conf_warning("unknown symbol: %s", sym_name); | |
451 | ||
452 | conf_set_changed(true); | |
453 | } | |
454 | continue; | |
455 | } | |
456 | ||
457 | if (sym->flags & def_flags) | |
458 | conf_warning("override: reassigning to symbol %s", sym->name); | |
459 | ||
460 | if (conf_set_sym_val(sym, def, def_flags, val)) | |
461 | continue; | |
462 | ||
1da177e4 LT |
463 | if (sym && sym_is_choice_value(sym)) { |
464 | struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); | |
669bfad9 | 465 | switch (sym->def[def].tri) { |
1da177e4 LT |
466 | case no: |
467 | break; | |
468 | case mod: | |
669bfad9 | 469 | if (cs->def[def].tri == yes) { |
c1a0f5e3 | 470 | conf_warning("%s creates inconsistent choice state", sym->name); |
490f1617 | 471 | cs->flags &= ~def_flags; |
c1a0f5e3 | 472 | } |
1da177e4 LT |
473 | break; |
474 | case yes: | |
d84876f9 JE |
475 | if (cs->def[def].tri != no) |
476 | conf_warning("override: %s changes choice state", sym->name); | |
477 | cs->def[def].val = sym; | |
1da177e4 LT |
478 | break; |
479 | } | |
d6ee3576 | 480 | cs->def[def].tri = EXPR_OR(cs->def[def].tri, sym->def[def].tri); |
1da177e4 LT |
481 | } |
482 | } | |
1a7a8c6f | 483 | free(line); |
1da177e4 | 484 | fclose(in); |
7cd34300 | 485 | |
90389160 RZ |
486 | return 0; |
487 | } | |
488 | ||
489 | int conf_read(const char *name) | |
490 | { | |
5d09598d | 491 | struct symbol *sym; |
84dd95d4 | 492 | int conf_unsaved = 0; |
90389160 | 493 | |
5ee54659 | 494 | conf_set_changed(false); |
ddc97cac | 495 | |
6b87b70c AV |
496 | if (conf_read_simple(name, S_DEF_USER)) { |
497 | sym_calc_value(modules_sym); | |
90389160 | 498 | return 1; |
6b87b70c AV |
499 | } |
500 | ||
501 | sym_calc_value(modules_sym); | |
90389160 | 502 | |
91b69454 | 503 | for_all_symbols(sym) { |
1da177e4 | 504 | sym_calc_value(sym); |
693359f7 | 505 | if (sym_is_choice(sym) || (sym->flags & SYMBOL_NO_WRITE)) |
5d09598d | 506 | continue; |
c1a0f5e3 RZ |
507 | if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) { |
508 | /* check that calculated value agrees with saved value */ | |
509 | switch (sym->type) { | |
510 | case S_BOOLEAN: | |
511 | case S_TRISTATE: | |
e3cd5136 | 512 | if (sym->def[S_DEF_USER].tri == sym_get_tristate_value(sym)) |
5d09598d | 513 | continue; |
e3cd5136 | 514 | break; |
c1a0f5e3 | 515 | default: |
0c1822e6 | 516 | if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val)) |
5d09598d | 517 | continue; |
c1a0f5e3 RZ |
518 | break; |
519 | } | |
520 | } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE)) | |
521 | /* no previous value and not saved */ | |
5d09598d | 522 | continue; |
c1a0f5e3 RZ |
523 | conf_unsaved++; |
524 | /* maybe print value in verbose mode... */ | |
d8982ba1 RZ |
525 | } |
526 | ||
91b69454 | 527 | for_all_symbols(sym) { |
1da177e4 | 528 | if (sym_has_value(sym) && !sym_is_choice_value(sym)) { |
d8982ba1 RZ |
529 | /* Reset values of generates values, so they'll appear |
530 | * as new, if they should become visible, but that | |
531 | * doesn't quite work if the Kconfig and the saved | |
532 | * configuration disagree. | |
533 | */ | |
534 | if (sym->visible == no && !conf_unsaved) | |
669bfad9 | 535 | sym->flags &= ~SYMBOL_DEF_USER; |
1da177e4 LT |
536 | switch (sym->type) { |
537 | case S_STRING: | |
538 | case S_INT: | |
539 | case S_HEX: | |
d8982ba1 RZ |
540 | /* Reset a string value if it's out of range */ |
541 | if (sym_string_within_range(sym, sym->def[S_DEF_USER].val)) | |
542 | break; | |
ce1fc934 | 543 | sym->flags &= ~SYMBOL_VALID; |
d8982ba1 RZ |
544 | conf_unsaved++; |
545 | break; | |
1da177e4 LT |
546 | default: |
547 | break; | |
548 | } | |
549 | } | |
1da177e4 LT |
550 | } |
551 | ||
5ee54659 MY |
552 | if (conf_warnings || conf_unsaved) |
553 | conf_set_changed(true); | |
1da177e4 LT |
554 | |
555 | return 0; | |
556 | } | |
557 | ||
ca51b26b MY |
558 | struct comment_style { |
559 | const char *decoration; | |
560 | const char *prefix; | |
561 | const char *postfix; | |
562 | }; | |
563 | ||
564 | static const struct comment_style comment_style_pound = { | |
565 | .decoration = "#", | |
566 | .prefix = "#", | |
567 | .postfix = "#", | |
568 | }; | |
569 | ||
570 | static const struct comment_style comment_style_c = { | |
571 | .decoration = " *", | |
572 | .prefix = "/*", | |
573 | .postfix = " */", | |
574 | }; | |
575 | ||
576 | static void conf_write_heading(FILE *fp, const struct comment_style *cs) | |
577 | { | |
2f7ab126 MO |
578 | if (!cs) |
579 | return; | |
580 | ||
ca51b26b MY |
581 | fprintf(fp, "%s\n", cs->prefix); |
582 | ||
583 | fprintf(fp, "%s Automatically generated file; DO NOT EDIT.\n", | |
584 | cs->decoration); | |
585 | ||
586 | fprintf(fp, "%s %s\n", cs->decoration, rootmenu.prompt->text); | |
587 | ||
588 | fprintf(fp, "%s\n", cs->postfix); | |
589 | } | |
590 | ||
80f7bc77 MY |
591 | /* The returned pointer must be freed on the caller side */ |
592 | static char *escape_string_value(const char *in) | |
593 | { | |
594 | const char *p; | |
595 | char *out; | |
596 | size_t len; | |
597 | ||
598 | len = strlen(in) + strlen("\"\"") + 1; | |
599 | ||
600 | p = in; | |
601 | while (1) { | |
602 | p += strcspn(p, "\"\\"); | |
603 | ||
604 | if (p[0] == '\0') | |
605 | break; | |
606 | ||
607 | len++; | |
608 | p++; | |
609 | } | |
610 | ||
611 | out = xmalloc(len); | |
612 | out[0] = '\0'; | |
613 | ||
614 | strcat(out, "\""); | |
615 | ||
616 | p = in; | |
617 | while (1) { | |
618 | len = strcspn(p, "\"\\"); | |
619 | strncat(out, p, len); | |
620 | p += len; | |
621 | ||
622 | if (p[0] == '\0') | |
623 | break; | |
624 | ||
625 | strcat(out, "\\"); | |
626 | strncat(out, p++, 1); | |
627 | } | |
628 | ||
629 | strcat(out, "\""); | |
630 | ||
631 | return out; | |
632 | } | |
633 | ||
6ce45a91 | 634 | enum output_n { OUTPUT_N, OUTPUT_N_AS_UNSET, OUTPUT_N_NONE }; |
e54e692b | 635 | |
6ce45a91 MY |
636 | static void __print_symbol(FILE *fp, struct symbol *sym, enum output_n output_n, |
637 | bool escape_string) | |
49192f26 | 638 | { |
6ce45a91 MY |
639 | const char *val; |
640 | char *escaped = NULL; | |
e54e692b | 641 | |
6ce45a91 MY |
642 | if (sym->type == S_UNKNOWN) |
643 | return; | |
49192f26 | 644 | |
6ce45a91 | 645 | val = sym_get_string_value(sym); |
eb4cf5a6 | 646 | |
6ce45a91 MY |
647 | if ((sym->type == S_BOOLEAN || sym->type == S_TRISTATE) && |
648 | output_n != OUTPUT_N && *val == 'n') { | |
649 | if (output_n == OUTPUT_N_AS_UNSET) | |
650 | fprintf(fp, "# %s%s is not set\n", CONFIG_, sym->name); | |
651 | return; | |
eb4cf5a6 | 652 | } |
eb4cf5a6 | 653 | |
6ce45a91 | 654 | if (sym->type == S_STRING && escape_string) { |
80f7bc77 | 655 | escaped = escape_string_value(val); |
6ce45a91 | 656 | val = escaped; |
e54e692b AL |
657 | } |
658 | ||
6ce45a91 MY |
659 | fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, val); |
660 | ||
661 | free(escaped); | |
e54e692b AL |
662 | } |
663 | ||
6ce45a91 | 664 | static void print_symbol_for_dotconfig(FILE *fp, struct symbol *sym) |
e54e692b | 665 | { |
6ce45a91 MY |
666 | __print_symbol(fp, sym, OUTPUT_N_AS_UNSET, true); |
667 | } | |
668 | ||
669 | static void print_symbol_for_autoconf(FILE *fp, struct symbol *sym) | |
670 | { | |
129ab0d2 | 671 | __print_symbol(fp, sym, OUTPUT_N_NONE, false); |
6ce45a91 | 672 | } |
e54e692b | 673 | |
51d792cb MY |
674 | void print_symbol_for_listconfig(struct symbol *sym) |
675 | { | |
676 | __print_symbol(stdout, sym, OUTPUT_N, true); | |
677 | } | |
678 | ||
6ce45a91 | 679 | static void print_symbol_for_c(FILE *fp, struct symbol *sym) |
e54e692b | 680 | { |
229d0cfa | 681 | const char *val; |
6ce45a91 MY |
682 | const char *sym_suffix = ""; |
683 | const char *val_prefix = ""; | |
229d0cfa | 684 | char *escaped = NULL; |
e54e692b | 685 | |
229d0cfa MY |
686 | if (sym->type == S_UNKNOWN) |
687 | return; | |
688 | ||
689 | val = sym_get_string_value(sym); | |
690 | ||
6ce45a91 MY |
691 | switch (sym->type) { |
692 | case S_BOOLEAN: | |
693 | case S_TRISTATE: | |
694 | switch (*val) { | |
695 | case 'n': | |
696 | return; | |
697 | case 'm': | |
698 | sym_suffix = "_MODULE"; | |
699 | /* fall through */ | |
700 | default: | |
701 | val = "1"; | |
702 | } | |
703 | break; | |
704 | case S_HEX: | |
705 | if (val[0] != '0' || (val[1] != 'x' && val[1] != 'X')) | |
706 | val_prefix = "0x"; | |
707 | break; | |
708 | case S_STRING: | |
80f7bc77 | 709 | escaped = escape_string_value(val); |
229d0cfa | 710 | val = escaped; |
6ce45a91 MY |
711 | default: |
712 | break; | |
49192f26 | 713 | } |
229d0cfa | 714 | |
6ce45a91 MY |
715 | fprintf(fp, "#define %s%s%s %s%s\n", CONFIG_, sym->name, sym_suffix, |
716 | val_prefix, val); | |
229d0cfa MY |
717 | |
718 | free(escaped); | |
49192f26 SR |
719 | } |
720 | ||
2f7ab126 MO |
721 | static void print_symbol_for_rustccfg(FILE *fp, struct symbol *sym) |
722 | { | |
723 | const char *val; | |
724 | const char *val_prefix = ""; | |
725 | char *val_prefixed = NULL; | |
726 | size_t val_prefixed_len; | |
727 | char *escaped = NULL; | |
728 | ||
729 | if (sym->type == S_UNKNOWN) | |
730 | return; | |
731 | ||
732 | val = sym_get_string_value(sym); | |
733 | ||
734 | switch (sym->type) { | |
735 | case S_BOOLEAN: | |
736 | case S_TRISTATE: | |
737 | /* | |
738 | * We do not care about disabled ones, i.e. no need for | |
739 | * what otherwise are "comments" in other printers. | |
740 | */ | |
741 | if (*val == 'n') | |
742 | return; | |
743 | ||
744 | /* | |
745 | * To have similar functionality to the C macro `IS_ENABLED()` | |
746 | * we provide an empty `--cfg CONFIG_X` here in both `y` | |
747 | * and `m` cases. | |
748 | * | |
749 | * Then, the common `fprintf()` below will also give us | |
750 | * a `--cfg CONFIG_X="y"` or `--cfg CONFIG_X="m"`, which can | |
751 | * be used as the equivalent of `IS_BUILTIN()`/`IS_MODULE()`. | |
752 | */ | |
753 | fprintf(fp, "--cfg=%s%s\n", CONFIG_, sym->name); | |
754 | break; | |
755 | case S_HEX: | |
756 | if (val[0] != '0' || (val[1] != 'x' && val[1] != 'X')) | |
757 | val_prefix = "0x"; | |
758 | break; | |
759 | default: | |
760 | break; | |
761 | } | |
762 | ||
763 | if (strlen(val_prefix) > 0) { | |
764 | val_prefixed_len = strlen(val) + strlen(val_prefix) + 1; | |
765 | val_prefixed = xmalloc(val_prefixed_len); | |
766 | snprintf(val_prefixed, val_prefixed_len, "%s%s", val_prefix, val); | |
767 | val = val_prefixed; | |
768 | } | |
769 | ||
770 | /* All values get escaped: the `--cfg` option only takes strings */ | |
771 | escaped = escape_string_value(val); | |
772 | val = escaped; | |
773 | ||
774 | fprintf(fp, "--cfg=%s%s=%s\n", CONFIG_, sym->name, val); | |
775 | ||
776 | free(escaped); | |
777 | free(val_prefixed); | |
778 | } | |
779 | ||
7cf3d73b SR |
780 | /* |
781 | * Write out a minimal config. | |
782 | * All values that has default values are skipped as this is redundant. | |
783 | */ | |
784 | int conf_write_defconfig(const char *filename) | |
785 | { | |
786 | struct symbol *sym; | |
787 | struct menu *menu; | |
788 | FILE *out; | |
789 | ||
790 | out = fopen(filename, "w"); | |
791 | if (!out) | |
792 | return 1; | |
793 | ||
794 | sym_clear_all_valid(); | |
795 | ||
03c4ecaa | 796 | menu_for_each_entry(menu) { |
7cf3d73b | 797 | sym = menu->sym; |
b27a9138 | 798 | if (sym && !sym_is_choice(sym)) { |
7cf3d73b SR |
799 | sym_calc_value(sym); |
800 | if (!(sym->flags & SYMBOL_WRITE)) | |
03c4ecaa | 801 | continue; |
7cf3d73b SR |
802 | sym->flags &= ~SYMBOL_WRITE; |
803 | /* If we cannot change the symbol - skip */ | |
baa23ec8 | 804 | if (!sym_is_changeable(sym)) |
03c4ecaa | 805 | continue; |
7cf3d73b SR |
806 | /* If symbol equals to default value - skip */ |
807 | if (strcmp(sym_get_string_value(sym), sym_get_string_default(sym)) == 0) | |
03c4ecaa | 808 | continue; |
7cf3d73b SR |
809 | |
810 | /* | |
811 | * If symbol is a choice value and equals to the | |
812 | * default for a choice - skip. | |
7cf3d73b SR |
813 | */ |
814 | if (sym_is_choice_value(sym)) { | |
815 | struct symbol *cs; | |
816 | struct symbol *ds; | |
817 | ||
818 | cs = prop_get_symbol(sym_get_choice_prop(sym)); | |
819 | ds = sym_choice_default(cs); | |
6a121588 | 820 | if (sym == ds) { |
801690ca SR |
821 | if ((sym->type == S_BOOLEAN) && |
822 | sym_get_tristate_value(sym) == yes) | |
03c4ecaa | 823 | continue; |
7cf3d73b SR |
824 | } |
825 | } | |
6ce45a91 | 826 | print_symbol_for_dotconfig(out, sym); |
7cf3d73b | 827 | } |
7cf3d73b SR |
828 | } |
829 | fclose(out); | |
830 | return 0; | |
831 | } | |
832 | ||
1da177e4 LT |
833 | int conf_write(const char *name) |
834 | { | |
c955ccaf | 835 | FILE *out; |
1da177e4 LT |
836 | struct symbol *sym; |
837 | struct menu *menu; | |
1da177e4 | 838 | const char *str; |
ceb7f329 | 839 | char tmpname[PATH_MAX + 1], oldname[PATH_MAX + 1]; |
1da177e4 | 840 | char *env; |
aff11cd9 | 841 | bool need_newline = false; |
1da177e4 | 842 | |
ceb7f329 MY |
843 | if (!name) |
844 | name = conf_get_configname(); | |
845 | ||
846 | if (!*name) { | |
847 | fprintf(stderr, "config name is empty\n"); | |
848 | return -1; | |
849 | } | |
850 | ||
851 | if (is_dir(name)) { | |
852 | fprintf(stderr, "%s: Is a directory\n", name); | |
853 | return -1; | |
854 | } | |
855 | ||
580c5b3e MY |
856 | if (make_parent_dir(name)) |
857 | return -1; | |
858 | ||
14cdd3c4 | 859 | env = getenv("KCONFIG_OVERWRITECONFIG"); |
ceb7f329 | 860 | if (env && *env) { |
14cdd3c4 | 861 | *tmpname = 0; |
ceb7f329 MY |
862 | out = fopen(name, "w"); |
863 | } else { | |
864 | snprintf(tmpname, sizeof(tmpname), "%s.%d.tmp", | |
865 | name, (int)getpid()); | |
866 | out = fopen(tmpname, "w"); | |
14cdd3c4 | 867 | } |
1da177e4 LT |
868 | if (!out) |
869 | return 1; | |
14cdd3c4 | 870 | |
ca51b26b | 871 | conf_write_heading(out, &comment_style_pound); |
1da177e4 | 872 | |
b3214293 | 873 | if (!conf_get_changed()) |
1da177e4 LT |
874 | sym_clear_all_valid(); |
875 | ||
876 | menu = rootmenu.list; | |
877 | while (menu) { | |
878 | sym = menu->sym; | |
879 | if (!sym) { | |
880 | if (!menu_is_visible(menu)) | |
881 | goto next; | |
882 | str = menu_get_prompt(menu); | |
883 | fprintf(out, "\n" | |
884 | "#\n" | |
885 | "# %s\n" | |
886 | "#\n", str); | |
aff11cd9 | 887 | need_newline = false; |
1da251c6 | 888 | } else if (!sym_is_choice(sym) && |
8e2442a5 | 889 | !(sym->flags & SYMBOL_WRITTEN)) { |
1da177e4 LT |
890 | sym_calc_value(sym); |
891 | if (!(sym->flags & SYMBOL_WRITE)) | |
892 | goto next; | |
aff11cd9 AP |
893 | if (need_newline) { |
894 | fprintf(out, "\n"); | |
895 | need_newline = false; | |
896 | } | |
8e2442a5 | 897 | sym->flags |= SYMBOL_WRITTEN; |
6ce45a91 | 898 | print_symbol_for_dotconfig(out, sym); |
1da177e4 LT |
899 | } |
900 | ||
49192f26 | 901 | next: |
1da177e4 LT |
902 | if (menu->list) { |
903 | menu = menu->list; | |
904 | continue; | |
905 | } | |
a7d4f58e MY |
906 | |
907 | end_check: | |
908 | if (!menu->sym && menu_is_visible(menu) && menu != &rootmenu && | |
909 | menu->prompt->type == P_MENU) { | |
910 | fprintf(out, "# end of %s\n", menu_get_prompt(menu)); | |
911 | need_newline = true; | |
912 | } | |
913 | ||
914 | if (menu->next) { | |
1da177e4 | 915 | menu = menu->next; |
a7d4f58e MY |
916 | } else { |
917 | menu = menu->parent; | |
918 | if (menu) | |
919 | goto end_check; | |
1da177e4 LT |
920 | } |
921 | } | |
922 | fclose(out); | |
14cdd3c4 | 923 | |
91b69454 | 924 | for_all_symbols(sym) |
0c5b6c28 VB |
925 | sym->flags &= ~SYMBOL_WRITTEN; |
926 | ||
14cdd3c4 | 927 | if (*tmpname) { |
67424f61 MY |
928 | if (is_same(name, tmpname)) { |
929 | conf_message("No change to %s", name); | |
930 | unlink(tmpname); | |
5ee54659 | 931 | conf_set_changed(false); |
67424f61 MY |
932 | return 0; |
933 | } | |
934 | ||
ceb7f329 MY |
935 | snprintf(oldname, sizeof(oldname), "%s.old", name); |
936 | rename(name, oldname); | |
937 | if (rename(tmpname, name)) | |
14cdd3c4 | 938 | return 1; |
1da177e4 | 939 | } |
1da177e4 | 940 | |
ceb7f329 | 941 | conf_message("configuration written to %s", name); |
ddc97cac | 942 | |
5ee54659 | 943 | conf_set_changed(false); |
1da177e4 LT |
944 | |
945 | return 0; | |
946 | } | |
c955ccaf | 947 | |
a2ff4040 | 948 | /* write a dependency file as used by kbuild to track dependencies */ |
00d674cb | 949 | static int conf_write_autoconf_cmd(const char *autoconf_name) |
a2ff4040 | 950 | { |
00d674cb | 951 | char name[PATH_MAX], tmp[PATH_MAX]; |
a2ff4040 | 952 | FILE *out; |
00d674cb | 953 | int ret; |
a2ff4040 | 954 | |
00d674cb MY |
955 | ret = snprintf(name, sizeof(name), "%s.cmd", autoconf_name); |
956 | if (ret >= sizeof(name)) /* check truncation */ | |
957 | return -1; | |
958 | ||
959 | if (make_parent_dir(name)) | |
960 | return -1; | |
961 | ||
962 | ret = snprintf(tmp, sizeof(tmp), "%s.cmd.tmp", autoconf_name); | |
963 | if (ret >= sizeof(tmp)) /* check truncation */ | |
964 | return -1; | |
965 | ||
966 | out = fopen(tmp, "w"); | |
967 | if (!out) { | |
968 | perror("fopen"); | |
969 | return -1; | |
a2ff4040 | 970 | } |
a2ff4040 | 971 | |
56e634b0 | 972 | fprintf(out, "autoconfig := %s\n", autoconf_name); |
00d674cb | 973 | |
56e634b0 | 974 | fputs(str_get(&autoconf_cmd), out); |
00d674cb | 975 | |
868653f4 | 976 | fflush(out); |
d23a0c37 | 977 | ret = ferror(out); /* error check for all fprintf() calls */ |
a2ff4040 | 978 | fclose(out); |
d23a0c37 MY |
979 | if (ret) |
980 | return -1; | |
79123b13 | 981 | |
00d674cb MY |
982 | if (rename(tmp, name)) { |
983 | perror("rename"); | |
984 | return -1; | |
985 | } | |
986 | ||
a2ff4040 MY |
987 | return 0; |
988 | } | |
989 | ||
0849d212 | 990 | static int conf_touch_deps(void) |
2e3646e5 | 991 | { |
1b9e740a | 992 | const char *name, *tmp; |
2e3646e5 | 993 | struct symbol *sym; |
91b69454 | 994 | int res; |
1508fec8 | 995 | |
12122f62 | 996 | name = conf_get_autoconfig_name(); |
1b9e740a JL |
997 | tmp = strrchr(name, '/'); |
998 | depfile_prefix_len = tmp ? tmp - name + 1 : 0; | |
999 | if (depfile_prefix_len + 1 > sizeof(depfile_path)) | |
1000 | return -1; | |
1001 | ||
1002 | strncpy(depfile_path, name, depfile_prefix_len); | |
1003 | depfile_path[depfile_prefix_len] = 0; | |
1004 | ||
2e3646e5 | 1005 | conf_read_simple(name, S_DEF_AUTO); |
6b87b70c | 1006 | sym_calc_value(modules_sym); |
2e3646e5 | 1007 | |
91b69454 | 1008 | for_all_symbols(sym) { |
2e3646e5 | 1009 | sym_calc_value(sym); |
693359f7 | 1010 | if ((sym->flags & SYMBOL_NO_WRITE) || !sym->name) |
2e3646e5 RZ |
1011 | continue; |
1012 | if (sym->flags & SYMBOL_WRITE) { | |
1013 | if (sym->flags & SYMBOL_DEF_AUTO) { | |
1014 | /* | |
1015 | * symbol has old and new value, | |
1016 | * so compare them... | |
1017 | */ | |
1018 | switch (sym->type) { | |
1019 | case S_BOOLEAN: | |
1020 | case S_TRISTATE: | |
1021 | if (sym_get_tristate_value(sym) == | |
1022 | sym->def[S_DEF_AUTO].tri) | |
1023 | continue; | |
1024 | break; | |
1025 | case S_STRING: | |
1026 | case S_HEX: | |
1027 | case S_INT: | |
1028 | if (!strcmp(sym_get_string_value(sym), | |
1029 | sym->def[S_DEF_AUTO].val)) | |
1030 | continue; | |
1031 | break; | |
1032 | default: | |
1033 | break; | |
1034 | } | |
1035 | } else { | |
1036 | /* | |
1037 | * If there is no old value, only 'no' (unset) | |
1038 | * is allowed as new value. | |
1039 | */ | |
1040 | switch (sym->type) { | |
1041 | case S_BOOLEAN: | |
1042 | case S_TRISTATE: | |
1043 | if (sym_get_tristate_value(sym) == no) | |
1044 | continue; | |
1045 | break; | |
1046 | default: | |
1047 | break; | |
1048 | } | |
1049 | } | |
1050 | } else if (!(sym->flags & SYMBOL_DEF_AUTO)) | |
1051 | /* There is neither an old nor a new value. */ | |
1052 | continue; | |
1053 | /* else | |
1054 | * There is an old value, but no new value ('no' (unset) | |
1055 | * isn't saved in auto.conf, so the old value is always | |
1056 | * different from 'no'). | |
1057 | */ | |
1058 | ||
1508fec8 MY |
1059 | res = conf_touch_dep(sym->name); |
1060 | if (res) | |
1061 | return res; | |
2e3646e5 | 1062 | } |
2e3646e5 | 1063 | |
1508fec8 | 1064 | return 0; |
2e3646e5 RZ |
1065 | } |
1066 | ||
57ddd07c MY |
1067 | static int __conf_write_autoconf(const char *filename, |
1068 | void (*print_symbol)(FILE *, struct symbol *), | |
1069 | const struct comment_style *comment_style) | |
1070 | { | |
1071 | char tmp[PATH_MAX]; | |
1072 | FILE *file; | |
1073 | struct symbol *sym; | |
91b69454 | 1074 | int ret; |
57ddd07c MY |
1075 | |
1076 | if (make_parent_dir(filename)) | |
1077 | return -1; | |
1078 | ||
1079 | ret = snprintf(tmp, sizeof(tmp), "%s.tmp", filename); | |
1080 | if (ret >= sizeof(tmp)) /* check truncation */ | |
1081 | return -1; | |
1082 | ||
1083 | file = fopen(tmp, "w"); | |
1084 | if (!file) { | |
1085 | perror("fopen"); | |
1086 | return -1; | |
1087 | } | |
1088 | ||
1089 | conf_write_heading(file, comment_style); | |
1090 | ||
91b69454 | 1091 | for_all_symbols(sym) |
57ddd07c MY |
1092 | if ((sym->flags & SYMBOL_WRITE) && sym->name) |
1093 | print_symbol(file, sym); | |
1094 | ||
868653f4 | 1095 | fflush(file); |
57ddd07c | 1096 | /* check possible errors in conf_write_heading() and print_symbol() */ |
d23a0c37 | 1097 | ret = ferror(file); |
57ddd07c | 1098 | fclose(file); |
d23a0c37 MY |
1099 | if (ret) |
1100 | return -1; | |
57ddd07c MY |
1101 | |
1102 | if (rename(tmp, filename)) { | |
1103 | perror("rename"); | |
1104 | return -1; | |
1105 | } | |
1106 | ||
1107 | return 0; | |
1108 | } | |
1109 | ||
00c864f8 | 1110 | int conf_write_autoconf(int overwrite) |
c955ccaf RZ |
1111 | { |
1112 | struct symbol *sym; | |
00c864f8 | 1113 | const char *autoconf_name = conf_get_autoconfig_name(); |
91b69454 | 1114 | int ret; |
c955ccaf | 1115 | |
00c864f8 MY |
1116 | if (!overwrite && is_present(autoconf_name)) |
1117 | return 0; | |
1118 | ||
00d674cb MY |
1119 | ret = conf_write_autoconf_cmd(autoconf_name); |
1120 | if (ret) | |
1121 | return -1; | |
c955ccaf | 1122 | |
0849d212 | 1123 | if (conf_touch_deps()) |
2e3646e5 RZ |
1124 | return 1; |
1125 | ||
91b69454 | 1126 | for_all_symbols(sym) |
c955ccaf | 1127 | sym_calc_value(sym); |
49192f26 | 1128 | |
57ddd07c MY |
1129 | ret = __conf_write_autoconf(conf_get_autoheader_name(), |
1130 | print_symbol_for_c, | |
1131 | &comment_style_c); | |
1132 | if (ret) | |
1133 | return ret; | |
79123b13 | 1134 | |
2f7ab126 MO |
1135 | ret = __conf_write_autoconf(conf_get_rustccfg_name(), |
1136 | print_symbol_for_rustccfg, | |
1137 | NULL); | |
1138 | if (ret) | |
1139 | return ret; | |
1140 | ||
c955ccaf | 1141 | /* |
57ddd07c MY |
1142 | * Create include/config/auto.conf. This must be the last step because |
1143 | * Kbuild has a dependency on auto.conf and this marks the successful | |
1144 | * completion of the previous steps. | |
c955ccaf | 1145 | */ |
57ddd07c MY |
1146 | ret = __conf_write_autoconf(conf_get_autoconfig_name(), |
1147 | print_symbol_for_autoconf, | |
1148 | &comment_style_pound); | |
1149 | if (ret) | |
1150 | return ret; | |
c955ccaf RZ |
1151 | |
1152 | return 0; | |
1153 | } | |
b3214293 | 1154 | |
5ee54659 | 1155 | static bool conf_changed; |
3b354c55 | 1156 | static void (*conf_changed_callback)(void); |
bfc10001 | 1157 | |
5ee54659 | 1158 | void conf_set_changed(bool val) |
bfc10001 | 1159 | { |
ee06a3ef | 1160 | bool changed = conf_changed != val; |
bfc10001 | 1161 | |
5ee54659 | 1162 | conf_changed = val; |
ee06a3ef JV |
1163 | |
1164 | if (conf_changed_callback && changed) | |
1165 | conf_changed_callback(); | |
bfc10001 KW |
1166 | } |
1167 | ||
b3214293 KW |
1168 | bool conf_get_changed(void) |
1169 | { | |
5ee54659 | 1170 | return conf_changed; |
b3214293 | 1171 | } |
3b354c55 KW |
1172 | |
1173 | void conf_set_changed_callback(void (*fn)(void)) | |
1174 | { | |
1175 | conf_changed_callback = fn; | |
1176 | } | |
dc7862e5 | 1177 | |
fbe98bb9 | 1178 | void set_all_choice_values(struct symbol *csym) |
dc7862e5 | 1179 | { |
dc7862e5 | 1180 | struct property *prop; |
a64b44ea | 1181 | struct symbol *sym; |
dc7862e5 | 1182 | struct expr *e; |
a64b44ea SR |
1183 | |
1184 | prop = sym_get_choice_prop(csym); | |
1185 | ||
1186 | /* | |
1187 | * Set all non-assinged choice values to no | |
1188 | */ | |
1189 | expr_list_for_each_sym(prop->expr, e, sym) { | |
1190 | if (!sym_has_value(sym)) | |
1191 | sym->def[S_DEF_USER].tri = no; | |
1192 | } | |
1193 | csym->flags |= SYMBOL_DEF_USER; | |
1194 | /* clear VALID to get value calculated */ | |
fbe98bb9 | 1195 | csym->flags &= ~(SYMBOL_VALID | SYMBOL_NEED_SET_CHOICE_VALUES); |
a64b44ea | 1196 | } |