1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // Copyright (C) 2018 Masahiro Yamada <[email protected]> 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 } 239