1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> |
4 | */ |
5 | |
6 | #include <sys/mman.h> |
7 | #include <sys/stat.h> |
8 | #include <sys/types.h> |
9 | #include <ctype.h> |
10 | #include <errno.h> |
11 | #include <fcntl.h> |
12 | #include <limits.h> |
13 | #include <stdarg.h> |
14 | #include <stdbool.h> |
15 | #include <stdio.h> |
16 | #include <stdlib.h> |
17 | #include <string.h> |
18 | #include <time.h> |
19 | #include <unistd.h> |
20 | |
21 | #include "internal.h" |
22 | #include "lkc.h" |
23 | |
24 | struct gstr autoconf_cmd; |
25 | |
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(file: path, buf: &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(file: path, buf: &st)) |
40 | return false; |
41 | |
42 | return S_ISDIR(st.st_mode); |
43 | } |
44 | |
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(file: file1, O_RDONLY); |
54 | if (fd1 < 0) |
55 | return ret; |
56 | |
57 | fd2 = open(file: file2, O_RDONLY); |
58 | if (fd2 < 0) |
59 | goto close1; |
60 | |
61 | ret = fstat(fd: fd1, buf: &st1); |
62 | if (ret) |
63 | goto close2; |
64 | ret = fstat(fd: fd2, buf: &st2); |
65 | if (ret) |
66 | goto close2; |
67 | |
68 | if (st1.st_size != st2.st_size) |
69 | goto close2; |
70 | |
71 | map1 = mmap(NULL, len: st1.st_size, PROT_READ, MAP_PRIVATE, fd: fd1, offset: 0); |
72 | if (map1 == MAP_FAILED) |
73 | goto close2; |
74 | |
75 | map2 = mmap(NULL, len: st2.st_size, PROT_READ, MAP_PRIVATE, fd: fd2, offset: 0); |
76 | if (map2 == MAP_FAILED) |
77 | goto close2; |
78 | |
79 | if (bcmp(s1: map1, s2: map2, n: st1.st_size)) |
80 | goto close2; |
81 | |
82 | ret = true; |
83 | close2: |
84 | close(fd: fd2); |
85 | close1: |
86 | close(fd: fd1); |
87 | |
88 | return ret; |
89 | } |
90 | |
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(dest: tmp, src: path, n: sizeof(tmp)); |
102 | tmp[sizeof(tmp) - 1] = 0; |
103 | |
104 | /* Remove the base name. Just return if nothing is left */ |
105 | p = strrchr(s: tmp, c: '/'); |
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(s: p, c: '/'))) { |
116 | *p = 0; |
117 | |
118 | /* skip if the directory exists */ |
119 | if (!is_dir(path: tmp) && mkdir(path: tmp, mode: 0755)) |
120 | return -1; |
121 | |
122 | *p = '/'; |
123 | while (*p == '/') |
124 | p++; |
125 | } |
126 | |
127 | return 0; |
128 | } |
129 | |
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 | { |
136 | int fd; |
137 | |
138 | /* check overflow: prefix + name + '\0' must fit in buffer. */ |
139 | if (depfile_prefix_len + strlen(s: name) + 1 > sizeof(depfile_path)) |
140 | return -1; |
141 | |
142 | strcpy(dest: depfile_path + depfile_prefix_len, src: name); |
143 | |
144 | fd = open(file: depfile_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); |
145 | if (fd == -1) |
146 | return -1; |
147 | close(fd: fd); |
148 | |
149 | return 0; |
150 | } |
151 | |
152 | static void conf_warning(const char *fmt, ...) |
153 | __attribute__ ((format (printf, 1, 2))); |
154 | |
155 | static void conf_message(const char *fmt, ...) |
156 | __attribute__ ((format (printf, 1, 2))); |
157 | |
158 | static const char *conf_filename; |
159 | static int conf_lineno, conf_warnings; |
160 | |
161 | bool conf_errors(void) |
162 | { |
163 | if (conf_warnings) |
164 | return getenv(name: "KCONFIG_WERROR" ); |
165 | return false; |
166 | } |
167 | |
168 | static void conf_warning(const char *fmt, ...) |
169 | { |
170 | va_list ap; |
171 | va_start(ap, fmt); |
172 | fprintf(stderr, format: "%s:%d:warning: " , conf_filename, conf_lineno); |
173 | vfprintf(stderr, format: fmt, arg: ap); |
174 | fprintf(stderr, format: "\n" ); |
175 | va_end(ap); |
176 | conf_warnings++; |
177 | } |
178 | |
179 | static void conf_default_message_callback(const char *s) |
180 | { |
181 | printf(format: "#\n# " ); |
182 | printf(format: "%s" , s); |
183 | printf(format: "\n#\n" ); |
184 | } |
185 | |
186 | static void (*conf_message_callback)(const char *s) = |
187 | conf_default_message_callback; |
188 | void conf_set_message_callback(void (*fn)(const char *s)) |
189 | { |
190 | conf_message_callback = fn; |
191 | } |
192 | |
193 | static void conf_message(const char *fmt, ...) |
194 | { |
195 | va_list ap; |
196 | char buf[4096]; |
197 | |
198 | if (!conf_message_callback) |
199 | return; |
200 | |
201 | va_start(ap, fmt); |
202 | |
203 | vsnprintf(s: buf, maxlen: sizeof(buf), format: fmt, arg: ap); |
204 | conf_message_callback(buf); |
205 | va_end(ap); |
206 | } |
207 | |
208 | const char *conf_get_configname(void) |
209 | { |
210 | char *name = getenv(name: "KCONFIG_CONFIG" ); |
211 | |
212 | return name ? name : ".config" ; |
213 | } |
214 | |
215 | static const char *conf_get_autoconfig_name(void) |
216 | { |
217 | char *name = getenv(name: "KCONFIG_AUTOCONFIG" ); |
218 | |
219 | return name ? name : "include/config/auto.conf" ; |
220 | } |
221 | |
222 | static const char *(void) |
223 | { |
224 | char *name = getenv(name: "KCONFIG_AUTOHEADER" ); |
225 | |
226 | return name ? name : "include/generated/autoconf.h" ; |
227 | } |
228 | |
229 | static const char *conf_get_rustccfg_name(void) |
230 | { |
231 | char *name = getenv(name: "KCONFIG_RUSTCCFG" ); |
232 | |
233 | return name ? name : "include/generated/rustc_cfg" ; |
234 | } |
235 | |
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 | } |
247 | /* fall through */ |
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 | } |
259 | if (def != S_DEF_AUTO) |
260 | conf_warning(fmt: "symbol value '%s' invalid for %s" , |
261 | p, sym->name); |
262 | return 1; |
263 | case S_STRING: |
264 | /* No escaping for S_DEF_AUTO (include/config/auto.conf) */ |
265 | if (def != S_DEF_AUTO) { |
266 | if (*p++ != '"') |
267 | break; |
268 | for (p2 = p; (p2 = strpbrk(s: p2, accept: "\"\\" )); p2++) { |
269 | if (*p2 == '"') { |
270 | *p2 = 0; |
271 | break; |
272 | } |
273 | memmove(dest: p2, src: p2 + 1, n: strlen(s: p2)); |
274 | } |
275 | if (!p2) { |
276 | conf_warning(fmt: "invalid string found" ); |
277 | return 1; |
278 | } |
279 | } |
280 | /* fall through */ |
281 | case S_INT: |
282 | case S_HEX: |
283 | if (sym_string_valid(sym, newval: p)) { |
284 | sym->def[def].val = xstrdup(s: p); |
285 | sym->flags |= def_flags; |
286 | } else { |
287 | if (def != S_DEF_AUTO) |
288 | conf_warning(fmt: "symbol value '%s' invalid for %s" , |
289 | p, sym->name); |
290 | return 1; |
291 | } |
292 | break; |
293 | default: |
294 | ; |
295 | } |
296 | return 0; |
297 | } |
298 | |
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 | |
304 | len = getline(lineptr: lineptr, n: n, stream: stream); |
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 | |
319 | int conf_read_simple(const char *name, int def) |
320 | { |
321 | FILE *in = NULL; |
322 | char *line = NULL; |
323 | size_t line_asize = 0; |
324 | char *p, *val; |
325 | struct symbol *sym; |
326 | int def_flags; |
327 | const char *warn_unknown, *sym_name; |
328 | |
329 | warn_unknown = getenv(name: "KCONFIG_WARN_UNKNOWN_SYMBOLS" ); |
330 | if (name) { |
331 | in = zconf_fopen(name); |
332 | } else { |
333 | char *env; |
334 | |
335 | name = conf_get_configname(); |
336 | in = zconf_fopen(name); |
337 | if (in) |
338 | goto load; |
339 | conf_set_changed(true); |
340 | |
341 | env = getenv(name: "KCONFIG_DEFCONFIG_LIST" ); |
342 | if (!env) |
343 | return 1; |
344 | |
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(name: env); |
363 | if (in) { |
364 | conf_message(fmt: "using defaults found in %s" , |
365 | env); |
366 | goto load; |
367 | } |
368 | |
369 | if (is_last) |
370 | break; |
371 | |
372 | env = p + 1; |
373 | } |
374 | } |
375 | if (!in) |
376 | return 1; |
377 | |
378 | load: |
379 | conf_filename = name; |
380 | conf_lineno = 0; |
381 | conf_warnings = 0; |
382 | |
383 | def_flags = SYMBOL_DEF << def; |
384 | for_all_symbols(sym) { |
385 | sym->flags |= SYMBOL_CHANGED; |
386 | sym->flags &= ~(def_flags|SYMBOL_VALID); |
387 | if (sym_is_choice(sym)) |
388 | sym->flags |= def_flags; |
389 | switch (sym->type) { |
390 | case S_INT: |
391 | case S_HEX: |
392 | case S_STRING: |
393 | free(ptr: sym->def[def].val); |
394 | /* fall through */ |
395 | default: |
396 | sym->def[def].val = NULL; |
397 | sym->def[def].tri = no; |
398 | } |
399 | } |
400 | |
401 | while (getline_stripped(lineptr: &line, n: &line_asize, stream: in) != -1) { |
402 | conf_lineno++; |
403 | |
404 | if (!line[0]) /* blank line */ |
405 | continue; |
406 | |
407 | if (line[0] == '#') { |
408 | if (line[1] != ' ') |
409 | continue; |
410 | p = line + 2; |
411 | if (memcmp(s1: p, CONFIG_, n: strlen(CONFIG_))) |
412 | continue; |
413 | sym_name = p + strlen(CONFIG_); |
414 | p = strchr(s: sym_name, c: ' '); |
415 | if (!p) |
416 | continue; |
417 | *p++ = 0; |
418 | if (strcmp(s1: p, s2: "is not set" )) |
419 | continue; |
420 | |
421 | val = "n" ; |
422 | } else { |
423 | if (memcmp(s1: line, CONFIG_, n: strlen(CONFIG_))) { |
424 | conf_warning(fmt: "unexpected data: %s" , line); |
425 | continue; |
426 | } |
427 | |
428 | sym_name = line + strlen(CONFIG_); |
429 | p = strchr(s: sym_name, c: '='); |
430 | if (!p) { |
431 | conf_warning(fmt: "unexpected data: %s" , line); |
432 | continue; |
433 | } |
434 | *p = 0; |
435 | val = p + 1; |
436 | } |
437 | |
438 | sym = sym_find(name: 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(name: sym_name); |
448 | } else { |
449 | if (warn_unknown) |
450 | conf_warning(fmt: "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(fmt: "override: reassigning to symbol %s" , sym->name); |
459 | |
460 | if (conf_set_sym_val(sym, def, def_flags, p: val)) |
461 | continue; |
462 | |
463 | if (sym && sym_is_choice_value(sym)) { |
464 | struct symbol *cs = prop_get_symbol(prop: sym_get_choice_prop(sym)); |
465 | switch (sym->def[def].tri) { |
466 | case no: |
467 | break; |
468 | case mod: |
469 | if (cs->def[def].tri == yes) { |
470 | conf_warning(fmt: "%s creates inconsistent choice state" , sym->name); |
471 | cs->flags &= ~def_flags; |
472 | } |
473 | break; |
474 | case yes: |
475 | if (cs->def[def].tri != no) |
476 | conf_warning(fmt: "override: %s changes choice state" , sym->name); |
477 | cs->def[def].val = sym; |
478 | break; |
479 | } |
480 | cs->def[def].tri = EXPR_OR(cs->def[def].tri, sym->def[def].tri); |
481 | } |
482 | } |
483 | free(ptr: line); |
484 | fclose(stream: in); |
485 | |
486 | return 0; |
487 | } |
488 | |
489 | int conf_read(const char *name) |
490 | { |
491 | struct symbol *sym; |
492 | int conf_unsaved = 0; |
493 | |
494 | conf_set_changed(false); |
495 | |
496 | if (conf_read_simple(name, def: S_DEF_USER)) { |
497 | sym_calc_value(sym: modules_sym); |
498 | return 1; |
499 | } |
500 | |
501 | sym_calc_value(sym: modules_sym); |
502 | |
503 | for_all_symbols(sym) { |
504 | sym_calc_value(sym); |
505 | if (sym_is_choice(sym) || (sym->flags & SYMBOL_NO_WRITE)) |
506 | continue; |
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: |
512 | if (sym->def[S_DEF_USER].tri == sym_get_tristate_value(sym)) |
513 | continue; |
514 | break; |
515 | default: |
516 | if (!strcmp(s1: sym->curr.val, s2: sym->def[S_DEF_USER].val)) |
517 | continue; |
518 | break; |
519 | } |
520 | } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE)) |
521 | /* no previous value and not saved */ |
522 | continue; |
523 | conf_unsaved++; |
524 | /* maybe print value in verbose mode... */ |
525 | } |
526 | |
527 | for_all_symbols(sym) { |
528 | if (sym_has_value(sym) && !sym_is_choice_value(sym)) { |
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) |
535 | sym->flags &= ~SYMBOL_DEF_USER; |
536 | switch (sym->type) { |
537 | case S_STRING: |
538 | case S_INT: |
539 | case S_HEX: |
540 | /* Reset a string value if it's out of range */ |
541 | if (sym_string_within_range(sym, str: sym->def[S_DEF_USER].val)) |
542 | break; |
543 | sym->flags &= ~SYMBOL_VALID; |
544 | conf_unsaved++; |
545 | break; |
546 | default: |
547 | break; |
548 | } |
549 | } |
550 | } |
551 | |
552 | if (conf_warnings || conf_unsaved) |
553 | conf_set_changed(true); |
554 | |
555 | return 0; |
556 | } |
557 | |
558 | struct { |
559 | const char *; |
560 | const char *; |
561 | const char *; |
562 | }; |
563 | |
564 | static const struct comment_style = { |
565 | .decoration = "#" , |
566 | .prefix = "#" , |
567 | .postfix = "#" , |
568 | }; |
569 | |
570 | static const struct comment_style = { |
571 | .decoration = " *" , |
572 | .prefix = "/*" , |
573 | .postfix = " */" , |
574 | }; |
575 | |
576 | static void conf_write_heading(FILE *fp, const struct comment_style *cs) |
577 | { |
578 | if (!cs) |
579 | return; |
580 | |
581 | fprintf(stream: fp, format: "%s\n" , cs->prefix); |
582 | |
583 | fprintf(stream: fp, format: "%s Automatically generated file; DO NOT EDIT.\n" , |
584 | cs->decoration); |
585 | |
586 | fprintf(stream: fp, format: "%s %s\n" , cs->decoration, rootmenu.prompt->text); |
587 | |
588 | fprintf(stream: fp, format: "%s\n" , cs->postfix); |
589 | } |
590 | |
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(s: in) + strlen(s: "\"\"" ) + 1; |
599 | |
600 | p = in; |
601 | while (1) { |
602 | p += strcspn(s: p, reject: "\"\\" ); |
603 | |
604 | if (p[0] == '\0') |
605 | break; |
606 | |
607 | len++; |
608 | p++; |
609 | } |
610 | |
611 | out = xmalloc(size: len); |
612 | out[0] = '\0'; |
613 | |
614 | strcat(dest: out, src: "\"" ); |
615 | |
616 | p = in; |
617 | while (1) { |
618 | len = strcspn(s: p, reject: "\"\\" ); |
619 | strncat(dest: out, src: p, n: len); |
620 | p += len; |
621 | |
622 | if (p[0] == '\0') |
623 | break; |
624 | |
625 | strcat(dest: out, src: "\\" ); |
626 | strncat(dest: out, src: p++, n: 1); |
627 | } |
628 | |
629 | strcat(dest: out, src: "\"" ); |
630 | |
631 | return out; |
632 | } |
633 | |
634 | enum output_n { OUTPUT_N, OUTPUT_N_AS_UNSET, OUTPUT_N_NONE }; |
635 | |
636 | static void __print_symbol(FILE *fp, struct symbol *sym, enum output_n output_n, |
637 | bool escape_string) |
638 | { |
639 | const char *val; |
640 | char *escaped = NULL; |
641 | |
642 | if (sym->type == S_UNKNOWN) |
643 | return; |
644 | |
645 | val = sym_get_string_value(sym); |
646 | |
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(stream: fp, format: "# %s%s is not set\n" , CONFIG_, sym->name); |
651 | return; |
652 | } |
653 | |
654 | if (sym->type == S_STRING && escape_string) { |
655 | escaped = escape_string_value(in: val); |
656 | val = escaped; |
657 | } |
658 | |
659 | fprintf(stream: fp, format: "%s%s=%s\n" , CONFIG_, sym->name, val); |
660 | |
661 | free(ptr: escaped); |
662 | } |
663 | |
664 | static void print_symbol_for_dotconfig(FILE *fp, struct symbol *sym) |
665 | { |
666 | __print_symbol(fp, sym, output_n: OUTPUT_N_AS_UNSET, true); |
667 | } |
668 | |
669 | static void print_symbol_for_autoconf(FILE *fp, struct symbol *sym) |
670 | { |
671 | __print_symbol(fp, sym, output_n: OUTPUT_N_NONE, false); |
672 | } |
673 | |
674 | void print_symbol_for_listconfig(struct symbol *sym) |
675 | { |
676 | __print_symbol(stdout, sym, output_n: OUTPUT_N, true); |
677 | } |
678 | |
679 | static void print_symbol_for_c(FILE *fp, struct symbol *sym) |
680 | { |
681 | const char *val; |
682 | const char *sym_suffix = "" ; |
683 | const char *val_prefix = "" ; |
684 | char *escaped = NULL; |
685 | |
686 | if (sym->type == S_UNKNOWN) |
687 | return; |
688 | |
689 | val = sym_get_string_value(sym); |
690 | |
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: |
709 | escaped = escape_string_value(in: val); |
710 | val = escaped; |
711 | default: |
712 | break; |
713 | } |
714 | |
715 | fprintf(stream: fp, format: "#define %s%s%s %s%s\n" , CONFIG_, sym->name, sym_suffix, |
716 | val_prefix, val); |
717 | |
718 | free(ptr: escaped); |
719 | } |
720 | |
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(stream: fp, format: "--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(s: val_prefix) > 0) { |
764 | val_prefixed_len = strlen(s: val) + strlen(s: val_prefix) + 1; |
765 | val_prefixed = xmalloc(size: val_prefixed_len); |
766 | snprintf(s: val_prefixed, maxlen: val_prefixed_len, format: "%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(in: val); |
772 | val = escaped; |
773 | |
774 | fprintf(stream: fp, format: "--cfg=%s%s=%s\n" , CONFIG_, sym->name, val); |
775 | |
776 | free(ptr: escaped); |
777 | free(ptr: val_prefixed); |
778 | } |
779 | |
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 *; |
788 | FILE *out; |
789 | |
790 | out = fopen(filename: filename, modes: "w" ); |
791 | if (!out) |
792 | return 1; |
793 | |
794 | sym_clear_all_valid(); |
795 | |
796 | /* Traverse all menus to find all relevant symbols */ |
797 | menu = rootmenu.list; |
798 | |
799 | while (menu != NULL) |
800 | { |
801 | sym = menu->sym; |
802 | if (sym && !sym_is_choice(sym)) { |
803 | sym_calc_value(sym); |
804 | if (!(sym->flags & SYMBOL_WRITE)) |
805 | goto next_menu; |
806 | sym->flags &= ~SYMBOL_WRITE; |
807 | /* If we cannot change the symbol - skip */ |
808 | if (!sym_is_changeable(sym)) |
809 | goto next_menu; |
810 | /* If symbol equals to default value - skip */ |
811 | if (strcmp(s1: sym_get_string_value(sym), s2: sym_get_string_default(sym)) == 0) |
812 | goto next_menu; |
813 | |
814 | /* |
815 | * If symbol is a choice value and equals to the |
816 | * default for a choice - skip. |
817 | * But only if value is bool and equal to "y" and |
818 | * choice is not "optional". |
819 | * (If choice is "optional" then all values can be "n") |
820 | */ |
821 | if (sym_is_choice_value(sym)) { |
822 | struct symbol *cs; |
823 | struct symbol *ds; |
824 | |
825 | cs = prop_get_symbol(prop: sym_get_choice_prop(sym)); |
826 | ds = sym_choice_default(sym: cs); |
827 | if (!sym_is_optional(sym: cs) && sym == ds) { |
828 | if ((sym->type == S_BOOLEAN) && |
829 | sym_get_tristate_value(sym) == yes) |
830 | goto next_menu; |
831 | } |
832 | } |
833 | print_symbol_for_dotconfig(fp: out, sym); |
834 | } |
835 | : |
836 | if (menu->list != NULL) { |
837 | menu = menu->list; |
838 | } |
839 | else if (menu->next != NULL) { |
840 | menu = menu->next; |
841 | } else { |
842 | while ((menu = menu->parent)) { |
843 | if (menu->next != NULL) { |
844 | menu = menu->next; |
845 | break; |
846 | } |
847 | } |
848 | } |
849 | } |
850 | fclose(stream: out); |
851 | return 0; |
852 | } |
853 | |
854 | int conf_write(const char *name) |
855 | { |
856 | FILE *out; |
857 | struct symbol *sym; |
858 | struct menu *; |
859 | const char *str; |
860 | char tmpname[PATH_MAX + 1], oldname[PATH_MAX + 1]; |
861 | char *env; |
862 | bool need_newline = false; |
863 | |
864 | if (!name) |
865 | name = conf_get_configname(); |
866 | |
867 | if (!*name) { |
868 | fprintf(stderr, format: "config name is empty\n" ); |
869 | return -1; |
870 | } |
871 | |
872 | if (is_dir(path: name)) { |
873 | fprintf(stderr, format: "%s: Is a directory\n" , name); |
874 | return -1; |
875 | } |
876 | |
877 | if (make_parent_dir(path: name)) |
878 | return -1; |
879 | |
880 | env = getenv(name: "KCONFIG_OVERWRITECONFIG" ); |
881 | if (env && *env) { |
882 | *tmpname = 0; |
883 | out = fopen(filename: name, modes: "w" ); |
884 | } else { |
885 | snprintf(s: tmpname, maxlen: sizeof(tmpname), format: "%s.%d.tmp" , |
886 | name, (int)getpid()); |
887 | out = fopen(filename: tmpname, modes: "w" ); |
888 | } |
889 | if (!out) |
890 | return 1; |
891 | |
892 | conf_write_heading(fp: out, cs: &comment_style_pound); |
893 | |
894 | if (!conf_get_changed()) |
895 | sym_clear_all_valid(); |
896 | |
897 | menu = rootmenu.list; |
898 | while (menu) { |
899 | sym = menu->sym; |
900 | if (!sym) { |
901 | if (!menu_is_visible(menu)) |
902 | goto next; |
903 | str = menu_get_prompt(menu); |
904 | fprintf(stream: out, format: "\n" |
905 | "#\n" |
906 | "# %s\n" |
907 | "#\n" , str); |
908 | need_newline = false; |
909 | } else if (!(sym->flags & SYMBOL_CHOICE) && |
910 | !(sym->flags & SYMBOL_WRITTEN)) { |
911 | sym_calc_value(sym); |
912 | if (!(sym->flags & SYMBOL_WRITE)) |
913 | goto next; |
914 | if (need_newline) { |
915 | fprintf(stream: out, format: "\n" ); |
916 | need_newline = false; |
917 | } |
918 | sym->flags |= SYMBOL_WRITTEN; |
919 | print_symbol_for_dotconfig(fp: out, sym); |
920 | } |
921 | |
922 | next: |
923 | if (menu->list) { |
924 | menu = menu->list; |
925 | continue; |
926 | } |
927 | |
928 | end_check: |
929 | if (!menu->sym && menu_is_visible(menu) && menu != &rootmenu && |
930 | menu->prompt->type == P_MENU) { |
931 | fprintf(stream: out, format: "# end of %s\n" , menu_get_prompt(menu)); |
932 | need_newline = true; |
933 | } |
934 | |
935 | if (menu->next) { |
936 | menu = menu->next; |
937 | } else { |
938 | menu = menu->parent; |
939 | if (menu) |
940 | goto end_check; |
941 | } |
942 | } |
943 | fclose(stream: out); |
944 | |
945 | for_all_symbols(sym) |
946 | sym->flags &= ~SYMBOL_WRITTEN; |
947 | |
948 | if (*tmpname) { |
949 | if (is_same(file1: name, file2: tmpname)) { |
950 | conf_message(fmt: "No change to %s" , name); |
951 | unlink(name: tmpname); |
952 | conf_set_changed(false); |
953 | return 0; |
954 | } |
955 | |
956 | snprintf(s: oldname, maxlen: sizeof(oldname), format: "%s.old" , name); |
957 | rename(old: name, new: oldname); |
958 | if (rename(old: tmpname, new: name)) |
959 | return 1; |
960 | } |
961 | |
962 | conf_message(fmt: "configuration written to %s" , name); |
963 | |
964 | conf_set_changed(false); |
965 | |
966 | return 0; |
967 | } |
968 | |
969 | /* write a dependency file as used by kbuild to track dependencies */ |
970 | static int conf_write_autoconf_cmd(const char *autoconf_name) |
971 | { |
972 | char name[PATH_MAX], tmp[PATH_MAX]; |
973 | FILE *out; |
974 | int ret; |
975 | |
976 | ret = snprintf(s: name, maxlen: sizeof(name), format: "%s.cmd" , autoconf_name); |
977 | if (ret >= sizeof(name)) /* check truncation */ |
978 | return -1; |
979 | |
980 | if (make_parent_dir(path: name)) |
981 | return -1; |
982 | |
983 | ret = snprintf(s: tmp, maxlen: sizeof(tmp), format: "%s.cmd.tmp" , autoconf_name); |
984 | if (ret >= sizeof(tmp)) /* check truncation */ |
985 | return -1; |
986 | |
987 | out = fopen(filename: tmp, modes: "w" ); |
988 | if (!out) { |
989 | perror(s: "fopen" ); |
990 | return -1; |
991 | } |
992 | |
993 | fprintf(stream: out, format: "autoconfig := %s\n" , autoconf_name); |
994 | |
995 | fputs(s: str_get(gs: &autoconf_cmd), stream: out); |
996 | |
997 | fflush(stream: out); |
998 | ret = ferror(stream: out); /* error check for all fprintf() calls */ |
999 | fclose(stream: out); |
1000 | if (ret) |
1001 | return -1; |
1002 | |
1003 | if (rename(old: tmp, new: name)) { |
1004 | perror(s: "rename" ); |
1005 | return -1; |
1006 | } |
1007 | |
1008 | return 0; |
1009 | } |
1010 | |
1011 | static int conf_touch_deps(void) |
1012 | { |
1013 | const char *name, *tmp; |
1014 | struct symbol *sym; |
1015 | int res; |
1016 | |
1017 | name = conf_get_autoconfig_name(); |
1018 | tmp = strrchr(s: name, c: '/'); |
1019 | depfile_prefix_len = tmp ? tmp - name + 1 : 0; |
1020 | if (depfile_prefix_len + 1 > sizeof(depfile_path)) |
1021 | return -1; |
1022 | |
1023 | strncpy(dest: depfile_path, src: name, n: depfile_prefix_len); |
1024 | depfile_path[depfile_prefix_len] = 0; |
1025 | |
1026 | conf_read_simple(name, def: S_DEF_AUTO); |
1027 | sym_calc_value(sym: modules_sym); |
1028 | |
1029 | for_all_symbols(sym) { |
1030 | sym_calc_value(sym); |
1031 | if ((sym->flags & SYMBOL_NO_WRITE) || !sym->name) |
1032 | continue; |
1033 | if (sym->flags & SYMBOL_WRITE) { |
1034 | if (sym->flags & SYMBOL_DEF_AUTO) { |
1035 | /* |
1036 | * symbol has old and new value, |
1037 | * so compare them... |
1038 | */ |
1039 | switch (sym->type) { |
1040 | case S_BOOLEAN: |
1041 | case S_TRISTATE: |
1042 | if (sym_get_tristate_value(sym) == |
1043 | sym->def[S_DEF_AUTO].tri) |
1044 | continue; |
1045 | break; |
1046 | case S_STRING: |
1047 | case S_HEX: |
1048 | case S_INT: |
1049 | if (!strcmp(s1: sym_get_string_value(sym), |
1050 | s2: sym->def[S_DEF_AUTO].val)) |
1051 | continue; |
1052 | break; |
1053 | default: |
1054 | break; |
1055 | } |
1056 | } else { |
1057 | /* |
1058 | * If there is no old value, only 'no' (unset) |
1059 | * is allowed as new value. |
1060 | */ |
1061 | switch (sym->type) { |
1062 | case S_BOOLEAN: |
1063 | case S_TRISTATE: |
1064 | if (sym_get_tristate_value(sym) == no) |
1065 | continue; |
1066 | break; |
1067 | default: |
1068 | break; |
1069 | } |
1070 | } |
1071 | } else if (!(sym->flags & SYMBOL_DEF_AUTO)) |
1072 | /* There is neither an old nor a new value. */ |
1073 | continue; |
1074 | /* else |
1075 | * There is an old value, but no new value ('no' (unset) |
1076 | * isn't saved in auto.conf, so the old value is always |
1077 | * different from 'no'). |
1078 | */ |
1079 | |
1080 | res = conf_touch_dep(name: sym->name); |
1081 | if (res) |
1082 | return res; |
1083 | } |
1084 | |
1085 | return 0; |
1086 | } |
1087 | |
1088 | static int __conf_write_autoconf(const char *filename, |
1089 | void (*print_symbol)(FILE *, struct symbol *), |
1090 | const struct comment_style *) |
1091 | { |
1092 | char tmp[PATH_MAX]; |
1093 | FILE *file; |
1094 | struct symbol *sym; |
1095 | int ret; |
1096 | |
1097 | if (make_parent_dir(path: filename)) |
1098 | return -1; |
1099 | |
1100 | ret = snprintf(s: tmp, maxlen: sizeof(tmp), format: "%s.tmp" , filename); |
1101 | if (ret >= sizeof(tmp)) /* check truncation */ |
1102 | return -1; |
1103 | |
1104 | file = fopen(filename: tmp, modes: "w" ); |
1105 | if (!file) { |
1106 | perror(s: "fopen" ); |
1107 | return -1; |
1108 | } |
1109 | |
1110 | conf_write_heading(fp: file, cs: comment_style); |
1111 | |
1112 | for_all_symbols(sym) |
1113 | if ((sym->flags & SYMBOL_WRITE) && sym->name) |
1114 | print_symbol(file, sym); |
1115 | |
1116 | fflush(stream: file); |
1117 | /* check possible errors in conf_write_heading() and print_symbol() */ |
1118 | ret = ferror(stream: file); |
1119 | fclose(stream: file); |
1120 | if (ret) |
1121 | return -1; |
1122 | |
1123 | if (rename(old: tmp, new: filename)) { |
1124 | perror(s: "rename" ); |
1125 | return -1; |
1126 | } |
1127 | |
1128 | return 0; |
1129 | } |
1130 | |
1131 | int conf_write_autoconf(int overwrite) |
1132 | { |
1133 | struct symbol *sym; |
1134 | const char *autoconf_name = conf_get_autoconfig_name(); |
1135 | int ret; |
1136 | |
1137 | if (!overwrite && is_present(path: autoconf_name)) |
1138 | return 0; |
1139 | |
1140 | ret = conf_write_autoconf_cmd(autoconf_name); |
1141 | if (ret) |
1142 | return -1; |
1143 | |
1144 | if (conf_touch_deps()) |
1145 | return 1; |
1146 | |
1147 | for_all_symbols(sym) |
1148 | sym_calc_value(sym); |
1149 | |
1150 | ret = __conf_write_autoconf(filename: conf_get_autoheader_name(), |
1151 | print_symbol: print_symbol_for_c, |
1152 | comment_style: &comment_style_c); |
1153 | if (ret) |
1154 | return ret; |
1155 | |
1156 | ret = __conf_write_autoconf(filename: conf_get_rustccfg_name(), |
1157 | print_symbol: print_symbol_for_rustccfg, |
1158 | NULL); |
1159 | if (ret) |
1160 | return ret; |
1161 | |
1162 | /* |
1163 | * Create include/config/auto.conf. This must be the last step because |
1164 | * Kbuild has a dependency on auto.conf and this marks the successful |
1165 | * completion of the previous steps. |
1166 | */ |
1167 | ret = __conf_write_autoconf(filename: conf_get_autoconfig_name(), |
1168 | print_symbol: print_symbol_for_autoconf, |
1169 | comment_style: &comment_style_pound); |
1170 | if (ret) |
1171 | return ret; |
1172 | |
1173 | return 0; |
1174 | } |
1175 | |
1176 | static bool conf_changed; |
1177 | static void (*conf_changed_callback)(void); |
1178 | |
1179 | void conf_set_changed(bool val) |
1180 | { |
1181 | bool changed = conf_changed != val; |
1182 | |
1183 | conf_changed = val; |
1184 | |
1185 | if (conf_changed_callback && changed) |
1186 | conf_changed_callback(); |
1187 | } |
1188 | |
1189 | bool conf_get_changed(void) |
1190 | { |
1191 | return conf_changed; |
1192 | } |
1193 | |
1194 | void conf_set_changed_callback(void (*fn)(void)) |
1195 | { |
1196 | conf_changed_callback = fn; |
1197 | } |
1198 | |
1199 | void set_all_choice_values(struct symbol *csym) |
1200 | { |
1201 | struct property *prop; |
1202 | struct symbol *sym; |
1203 | struct expr *e; |
1204 | |
1205 | prop = sym_get_choice_prop(sym: csym); |
1206 | |
1207 | /* |
1208 | * Set all non-assinged choice values to no |
1209 | */ |
1210 | expr_list_for_each_sym(prop->expr, e, sym) { |
1211 | if (!sym_has_value(sym)) |
1212 | sym->def[S_DEF_USER].tri = no; |
1213 | } |
1214 | csym->flags |= SYMBOL_DEF_USER; |
1215 | /* clear VALID to get value calculated */ |
1216 | csym->flags &= ~(SYMBOL_VALID | SYMBOL_NEED_SET_CHOICE_VALUES); |
1217 | } |
1218 | |