diff options
Diffstat (limited to 'scripts/kconfig/preprocess.c')
-rw-r--r-- | scripts/kconfig/preprocess.c | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/scripts/kconfig/preprocess.c b/scripts/kconfig/preprocess.c new file mode 100644 index 000000000000..a2eb2eb02929 --- /dev/null +++ b/scripts/kconfig/preprocess.c | |||
@@ -0,0 +1,238 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // | ||
3 | // Copyright (C) 2018 Masahiro Yamada <yamada.masahiro@socionext.com> | ||
4 | |||
5 | #include <stdarg.h> | ||
6 | #include <stdbool.h> | ||
7 | #include <stdio.h> | ||
8 | #include <stdlib.h> | ||
9 | #include <string.h> | ||
10 | |||
11 | #include "list.h" | ||
12 | |||
13 | static void __attribute__((noreturn)) pperror(const char *format, ...) | ||
14 | { | ||
15 | va_list ap; | ||
16 | |||
17 | fprintf(stderr, "%s:%d: ", current_file->name, yylineno); | ||
18 | va_start(ap, format); | ||
19 | vfprintf(stderr, format, ap); | ||
20 | va_end(ap); | ||
21 | fprintf(stderr, "\n"); | ||
22 | |||
23 | exit(1); | ||
24 | } | ||
25 | |||
26 | /* | ||
27 | * Environment variables | ||
28 | */ | ||
29 | static LIST_HEAD(env_list); | ||
30 | |||
31 | struct env { | ||
32 | char *name; | ||
33 | char *value; | ||
34 | struct list_head node; | ||
35 | }; | ||
36 | |||
37 | static void env_add(const char *name, const char *value) | ||
38 | { | ||
39 | struct env *e; | ||
40 | |||
41 | e = xmalloc(sizeof(*e)); | ||
42 | e->name = xstrdup(name); | ||
43 | e->value = xstrdup(value); | ||
44 | |||
45 | list_add_tail(&e->node, &env_list); | ||
46 | } | ||
47 | |||
48 | static void env_del(struct env *e) | ||
49 | { | ||
50 | list_del(&e->node); | ||
51 | free(e->name); | ||
52 | free(e->value); | ||
53 | free(e); | ||
54 | } | ||
55 | |||
56 | /* The returned pointer must be freed when done */ | ||
57 | static char *env_expand(const char *name) | ||
58 | { | ||
59 | struct env *e; | ||
60 | const char *value; | ||
61 | |||
62 | if (!*name) | ||
63 | return NULL; | ||
64 | |||
65 | list_for_each_entry(e, &env_list, node) { | ||
66 | if (!strcmp(name, e->name)) | ||
67 | return xstrdup(e->value); | ||
68 | } | ||
69 | |||
70 | value = getenv(name); | ||
71 | if (!value) | ||
72 | return NULL; | ||
73 | |||
74 | /* | ||
75 | * We need to remember all referenced environment variables. | ||
76 | * They will be written out to include/config/auto.conf.cmd | ||
77 | */ | ||
78 | env_add(name, value); | ||
79 | |||
80 | return xstrdup(value); | ||
81 | } | ||
82 | |||
83 | void env_write_dep(FILE *f, const char *autoconfig_name) | ||
84 | { | ||
85 | struct env *e, *tmp; | ||
86 | |||
87 | list_for_each_entry_safe(e, tmp, &env_list, node) { | ||
88 | fprintf(f, "ifneq \"$(%s)\" \"%s\"\n", e->name, e->value); | ||
89 | fprintf(f, "%s: FORCE\n", autoconfig_name); | ||
90 | fprintf(f, "endif\n"); | ||
91 | env_del(e); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | static char *eval_clause(const char *str, size_t len) | ||
96 | { | ||
97 | char *tmp, *name, *res; | ||
98 | |||
99 | tmp = xstrndup(str, len); | ||
100 | |||
101 | name = expand_string(tmp); | ||
102 | |||
103 | res = env_expand(name); | ||
104 | if (res) | ||
105 | goto free; | ||
106 | |||
107 | res = xstrdup(""); | ||
108 | free: | ||
109 | free(name); | ||
110 | free(tmp); | ||
111 | |||
112 | return res; | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * Expand a string that follows '$' | ||
117 | * | ||
118 | * For example, if the input string is | ||
119 | * ($(FOO)$($(BAR)))$(BAZ) | ||
120 | * this helper evaluates | ||
121 | * $($(FOO)$($(BAR))) | ||
122 | * and returns a new string containing the expansion (note that the string is | ||
123 | * recursively expanded), also advancing 'str' to point to the next character | ||
124 | * after the corresponding closing parenthesis, in this case, *str will be | ||
125 | * $(BAR) | ||
126 | */ | ||
127 | char *expand_dollar(const char **str) | ||
128 | { | ||
129 | const char *p = *str; | ||
130 | const char *q; | ||
131 | int nest = 0; | ||
132 | |||
133 | /* | ||
134 | * In Kconfig, variable references always start with "$(". | ||
135 | * Neither single-letter variables as in $A nor curly braces as in ${CC} | ||
136 | * are supported. '$' not followed by '(' loses its special meaning. | ||
137 | */ | ||
138 | if (*p != '(') { | ||
139 | *str = p; | ||
140 | return xstrdup("$"); | ||
141 | } | ||
142 | |||
143 | p++; | ||
144 | q = p; | ||
145 | while (*q) { | ||
146 | if (*q == '(') { | ||
147 | nest++; | ||
148 | } else if (*q == ')') { | ||
149 | if (nest-- == 0) | ||
150 | break; | ||
151 | } | ||
152 | q++; | ||
153 | } | ||
154 | |||
155 | if (!*q) | ||
156 | pperror("unterminated reference to '%s': missing ')'", p); | ||
157 | |||
158 | /* Advance 'str' to after the expanded initial portion of the string */ | ||
159 | *str = q + 1; | ||
160 | |||
161 | return eval_clause(p, q - p); | ||
162 | } | ||
163 | |||
164 | static char *__expand_string(const char **str, bool (*is_end)(char c)) | ||
165 | { | ||
166 | const char *in, *p; | ||
167 | char *expansion, *out; | ||
168 | size_t in_len, out_len; | ||
169 | |||
170 | out = xmalloc(1); | ||
171 | *out = 0; | ||
172 | out_len = 1; | ||
173 | |||
174 | p = in = *str; | ||
175 | |||
176 | while (1) { | ||
177 | if (*p == '$') { | ||
178 | in_len = p - in; | ||
179 | p++; | ||
180 | expansion = expand_dollar(&p); | ||
181 | out_len += in_len + strlen(expansion); | ||
182 | out = xrealloc(out, out_len); | ||
183 | strncat(out, in, in_len); | ||
184 | strcat(out, expansion); | ||
185 | free(expansion); | ||
186 | in = p; | ||
187 | continue; | ||
188 | } | ||
189 | |||
190 | if (is_end(*p)) | ||
191 | break; | ||
192 | |||
193 | p++; | ||
194 | } | ||
195 | |||
196 | in_len = p - in; | ||
197 | out_len += in_len; | ||
198 | out = xrealloc(out, out_len); | ||
199 | strncat(out, in, in_len); | ||
200 | |||
201 | /* Advance 'str' to the end character */ | ||
202 | *str = p; | ||
203 | |||
204 | return out; | ||
205 | } | ||
206 | |||
207 | static bool is_end_of_str(char c) | ||
208 | { | ||
209 | return !c; | ||
210 | } | ||
211 | |||
212 | /* | ||
213 | * Expand variables in the given string. Undefined variables | ||
214 | * expand to an empty string. | ||
215 | * The returned string must be freed when done. | ||
216 | */ | ||
217 | char *expand_string(const char *in) | ||
218 | { | ||
219 | return __expand_string(&in, is_end_of_str); | ||
220 | } | ||
221 | |||
222 | static bool is_end_of_token(char c) | ||
223 | { | ||
224 | /* Why are '.' and '/' valid characters for symbols? */ | ||
225 | return !(isalnum(c) || c == '_' || c == '-' || c == '.' || c == '/'); | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * Expand variables in a token. The parsing stops when a token separater | ||
230 | * (in most cases, it is a whitespace) is encountered. 'str' is updated to | ||
231 | * point to the next character. | ||
232 | * | ||
233 | * The returned string must be freed when done. | ||
234 | */ | ||
235 | char *expand_one_token(const char **str) | ||
236 | { | ||
237 | return __expand_string(str, is_end_of_token); | ||
238 | } | ||