18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (C) 2018 Masahiro Yamada <yamada.masahiro@socionext.com> 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <ctype.h> 68c2ecf20Sopenharmony_ci#include <stdarg.h> 78c2ecf20Sopenharmony_ci#include <stdbool.h> 88c2ecf20Sopenharmony_ci#include <stdio.h> 98c2ecf20Sopenharmony_ci#include <stdlib.h> 108c2ecf20Sopenharmony_ci#include <string.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "list.h" 138c2ecf20Sopenharmony_ci#include "lkc.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic char *expand_string_with_args(const char *in, int argc, char *argv[]); 188c2ecf20Sopenharmony_cistatic char *expand_string(const char *in); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic void __attribute__((noreturn)) pperror(const char *format, ...) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci va_list ap; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci fprintf(stderr, "%s:%d: ", current_file->name, yylineno); 258c2ecf20Sopenharmony_ci va_start(ap, format); 268c2ecf20Sopenharmony_ci vfprintf(stderr, format, ap); 278c2ecf20Sopenharmony_ci va_end(ap); 288c2ecf20Sopenharmony_ci fprintf(stderr, "\n"); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci exit(1); 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * Environment variables 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cistatic LIST_HEAD(env_list); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct env { 398c2ecf20Sopenharmony_ci char *name; 408c2ecf20Sopenharmony_ci char *value; 418c2ecf20Sopenharmony_ci struct list_head node; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void env_add(const char *name, const char *value) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct env *e; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci e = xmalloc(sizeof(*e)); 498c2ecf20Sopenharmony_ci e->name = xstrdup(name); 508c2ecf20Sopenharmony_ci e->value = xstrdup(value); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci list_add_tail(&e->node, &env_list); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void env_del(struct env *e) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci list_del(&e->node); 588c2ecf20Sopenharmony_ci free(e->name); 598c2ecf20Sopenharmony_ci free(e->value); 608c2ecf20Sopenharmony_ci free(e); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* The returned pointer must be freed when done */ 648c2ecf20Sopenharmony_cistatic char *env_expand(const char *name) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct env *e; 678c2ecf20Sopenharmony_ci const char *value; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (!*name) 708c2ecf20Sopenharmony_ci return NULL; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci list_for_each_entry(e, &env_list, node) { 738c2ecf20Sopenharmony_ci if (!strcmp(name, e->name)) 748c2ecf20Sopenharmony_ci return xstrdup(e->value); 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci value = getenv(name); 788c2ecf20Sopenharmony_ci if (!value) 798c2ecf20Sopenharmony_ci return NULL; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* 828c2ecf20Sopenharmony_ci * We need to remember all referenced environment variables. 838c2ecf20Sopenharmony_ci * They will be written out to include/config/auto.conf.cmd 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ci env_add(name, value); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return xstrdup(value); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_civoid env_write_dep(FILE *f, const char *autoconfig_name) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct env *e, *tmp; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci list_for_each_entry_safe(e, tmp, &env_list, node) { 958c2ecf20Sopenharmony_ci fprintf(f, "ifneq \"$(%s)\" \"%s\"\n", e->name, e->value); 968c2ecf20Sopenharmony_ci fprintf(f, "%s: FORCE\n", autoconfig_name); 978c2ecf20Sopenharmony_ci fprintf(f, "endif\n"); 988c2ecf20Sopenharmony_ci env_del(e); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* 1038c2ecf20Sopenharmony_ci * Built-in functions 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_cistruct function { 1068c2ecf20Sopenharmony_ci const char *name; 1078c2ecf20Sopenharmony_ci unsigned int min_args; 1088c2ecf20Sopenharmony_ci unsigned int max_args; 1098c2ecf20Sopenharmony_ci char *(*func)(int argc, char *argv[]); 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic char *do_error_if(int argc, char *argv[]) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci if (!strcmp(argv[0], "y")) 1158c2ecf20Sopenharmony_ci pperror("%s", argv[1]); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return xstrdup(""); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic char *do_filename(int argc, char *argv[]) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci return xstrdup(current_file->name); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic char *do_info(int argc, char *argv[]) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci printf("%s\n", argv[0]); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return xstrdup(""); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic char *do_lineno(int argc, char *argv[]) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci char buf[16]; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci sprintf(buf, "%d", yylineno); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return xstrdup(buf); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic char *do_shell(int argc, char *argv[]) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci FILE *p; 1448c2ecf20Sopenharmony_ci char buf[4096]; 1458c2ecf20Sopenharmony_ci char *cmd; 1468c2ecf20Sopenharmony_ci size_t nread; 1478c2ecf20Sopenharmony_ci int i; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci cmd = argv[0]; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci p = popen(cmd, "r"); 1528c2ecf20Sopenharmony_ci if (!p) { 1538c2ecf20Sopenharmony_ci perror(cmd); 1548c2ecf20Sopenharmony_ci exit(1); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci nread = fread(buf, 1, sizeof(buf), p); 1588c2ecf20Sopenharmony_ci if (nread == sizeof(buf)) 1598c2ecf20Sopenharmony_ci nread--; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* remove trailing new lines */ 1628c2ecf20Sopenharmony_ci while (nread > 0 && buf[nread - 1] == '\n') 1638c2ecf20Sopenharmony_ci nread--; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci buf[nread] = 0; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* replace a new line with a space */ 1688c2ecf20Sopenharmony_ci for (i = 0; i < nread; i++) { 1698c2ecf20Sopenharmony_ci if (buf[i] == '\n') 1708c2ecf20Sopenharmony_ci buf[i] = ' '; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (pclose(p) == -1) { 1748c2ecf20Sopenharmony_ci perror(cmd); 1758c2ecf20Sopenharmony_ci exit(1); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return xstrdup(buf); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic char *do_warning_if(int argc, char *argv[]) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci if (!strcmp(argv[0], "y")) 1848c2ecf20Sopenharmony_ci fprintf(stderr, "%s:%d: %s\n", 1858c2ecf20Sopenharmony_ci current_file->name, yylineno, argv[1]); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return xstrdup(""); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic const struct function function_table[] = { 1918c2ecf20Sopenharmony_ci /* Name MIN MAX Function */ 1928c2ecf20Sopenharmony_ci { "error-if", 2, 2, do_error_if }, 1938c2ecf20Sopenharmony_ci { "filename", 0, 0, do_filename }, 1948c2ecf20Sopenharmony_ci { "info", 1, 1, do_info }, 1958c2ecf20Sopenharmony_ci { "lineno", 0, 0, do_lineno }, 1968c2ecf20Sopenharmony_ci { "shell", 1, 1, do_shell }, 1978c2ecf20Sopenharmony_ci { "warning-if", 2, 2, do_warning_if }, 1988c2ecf20Sopenharmony_ci}; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci#define FUNCTION_MAX_ARGS 16 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic char *function_expand(const char *name, int argc, char *argv[]) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci const struct function *f; 2058c2ecf20Sopenharmony_ci int i; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(function_table); i++) { 2088c2ecf20Sopenharmony_ci f = &function_table[i]; 2098c2ecf20Sopenharmony_ci if (strcmp(f->name, name)) 2108c2ecf20Sopenharmony_ci continue; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (argc < f->min_args) 2138c2ecf20Sopenharmony_ci pperror("too few function arguments passed to '%s'", 2148c2ecf20Sopenharmony_ci name); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (argc > f->max_args) 2178c2ecf20Sopenharmony_ci pperror("too many function arguments passed to '%s'", 2188c2ecf20Sopenharmony_ci name); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return f->func(argc, argv); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return NULL; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/* 2278c2ecf20Sopenharmony_ci * Variables (and user-defined functions) 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_cistatic LIST_HEAD(variable_list); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistruct variable { 2328c2ecf20Sopenharmony_ci char *name; 2338c2ecf20Sopenharmony_ci char *value; 2348c2ecf20Sopenharmony_ci enum variable_flavor flavor; 2358c2ecf20Sopenharmony_ci int exp_count; 2368c2ecf20Sopenharmony_ci struct list_head node; 2378c2ecf20Sopenharmony_ci}; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic struct variable *variable_lookup(const char *name) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct variable *v; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci list_for_each_entry(v, &variable_list, node) { 2448c2ecf20Sopenharmony_ci if (!strcmp(name, v->name)) 2458c2ecf20Sopenharmony_ci return v; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return NULL; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic char *variable_expand(const char *name, int argc, char *argv[]) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct variable *v; 2548c2ecf20Sopenharmony_ci char *res; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci v = variable_lookup(name); 2578c2ecf20Sopenharmony_ci if (!v) 2588c2ecf20Sopenharmony_ci return NULL; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (argc == 0 && v->exp_count) 2618c2ecf20Sopenharmony_ci pperror("Recursive variable '%s' references itself (eventually)", 2628c2ecf20Sopenharmony_ci name); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (v->exp_count > 1000) 2658c2ecf20Sopenharmony_ci pperror("Too deep recursive expansion"); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci v->exp_count++; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (v->flavor == VAR_RECURSIVE) 2708c2ecf20Sopenharmony_ci res = expand_string_with_args(v->value, argc, argv); 2718c2ecf20Sopenharmony_ci else 2728c2ecf20Sopenharmony_ci res = xstrdup(v->value); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci v->exp_count--; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return res; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_civoid variable_add(const char *name, const char *value, 2808c2ecf20Sopenharmony_ci enum variable_flavor flavor) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct variable *v; 2838c2ecf20Sopenharmony_ci char *new_value; 2848c2ecf20Sopenharmony_ci bool append = false; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci v = variable_lookup(name); 2878c2ecf20Sopenharmony_ci if (v) { 2888c2ecf20Sopenharmony_ci /* For defined variables, += inherits the existing flavor */ 2898c2ecf20Sopenharmony_ci if (flavor == VAR_APPEND) { 2908c2ecf20Sopenharmony_ci flavor = v->flavor; 2918c2ecf20Sopenharmony_ci append = true; 2928c2ecf20Sopenharmony_ci } else { 2938c2ecf20Sopenharmony_ci free(v->value); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci } else { 2968c2ecf20Sopenharmony_ci /* For undefined variables, += assumes the recursive flavor */ 2978c2ecf20Sopenharmony_ci if (flavor == VAR_APPEND) 2988c2ecf20Sopenharmony_ci flavor = VAR_RECURSIVE; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci v = xmalloc(sizeof(*v)); 3018c2ecf20Sopenharmony_ci v->name = xstrdup(name); 3028c2ecf20Sopenharmony_ci v->exp_count = 0; 3038c2ecf20Sopenharmony_ci list_add_tail(&v->node, &variable_list); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci v->flavor = flavor; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (flavor == VAR_SIMPLE) 3098c2ecf20Sopenharmony_ci new_value = expand_string(value); 3108c2ecf20Sopenharmony_ci else 3118c2ecf20Sopenharmony_ci new_value = xstrdup(value); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (append) { 3148c2ecf20Sopenharmony_ci v->value = xrealloc(v->value, 3158c2ecf20Sopenharmony_ci strlen(v->value) + strlen(new_value) + 2); 3168c2ecf20Sopenharmony_ci strcat(v->value, " "); 3178c2ecf20Sopenharmony_ci strcat(v->value, new_value); 3188c2ecf20Sopenharmony_ci free(new_value); 3198c2ecf20Sopenharmony_ci } else { 3208c2ecf20Sopenharmony_ci v->value = new_value; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic void variable_del(struct variable *v) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci list_del(&v->node); 3278c2ecf20Sopenharmony_ci free(v->name); 3288c2ecf20Sopenharmony_ci free(v->value); 3298c2ecf20Sopenharmony_ci free(v); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_civoid variable_all_del(void) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct variable *v, *tmp; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci list_for_each_entry_safe(v, tmp, &variable_list, node) 3378c2ecf20Sopenharmony_ci variable_del(v); 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/* 3418c2ecf20Sopenharmony_ci * Evaluate a clause with arguments. argc/argv are arguments from the upper 3428c2ecf20Sopenharmony_ci * function call. 3438c2ecf20Sopenharmony_ci * 3448c2ecf20Sopenharmony_ci * Returned string must be freed when done 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_cistatic char *eval_clause(const char *str, size_t len, int argc, char *argv[]) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci char *tmp, *name, *res, *endptr, *prev, *p; 3498c2ecf20Sopenharmony_ci int new_argc = 0; 3508c2ecf20Sopenharmony_ci char *new_argv[FUNCTION_MAX_ARGS]; 3518c2ecf20Sopenharmony_ci int nest = 0; 3528c2ecf20Sopenharmony_ci int i; 3538c2ecf20Sopenharmony_ci unsigned long n; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci tmp = xstrndup(str, len); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* 3588c2ecf20Sopenharmony_ci * If variable name is '1', '2', etc. It is generally an argument 3598c2ecf20Sopenharmony_ci * from a user-function call (i.e. local-scope variable). If not 3608c2ecf20Sopenharmony_ci * available, then look-up global-scope variables. 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_ci n = strtoul(tmp, &endptr, 10); 3638c2ecf20Sopenharmony_ci if (!*endptr && n > 0 && n <= argc) { 3648c2ecf20Sopenharmony_ci res = xstrdup(argv[n - 1]); 3658c2ecf20Sopenharmony_ci goto free_tmp; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci prev = p = tmp; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* 3718c2ecf20Sopenharmony_ci * Split into tokens 3728c2ecf20Sopenharmony_ci * The function name and arguments are separated by a comma. 3738c2ecf20Sopenharmony_ci * For example, if the function call is like this: 3748c2ecf20Sopenharmony_ci * $(foo,$(x),$(y)) 3758c2ecf20Sopenharmony_ci * 3768c2ecf20Sopenharmony_ci * The input string for this helper should be: 3778c2ecf20Sopenharmony_ci * foo,$(x),$(y) 3788c2ecf20Sopenharmony_ci * 3798c2ecf20Sopenharmony_ci * and split into: 3808c2ecf20Sopenharmony_ci * new_argv[0] = 'foo' 3818c2ecf20Sopenharmony_ci * new_argv[1] = '$(x)' 3828c2ecf20Sopenharmony_ci * new_argv[2] = '$(y)' 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci while (*p) { 3858c2ecf20Sopenharmony_ci if (nest == 0 && *p == ',') { 3868c2ecf20Sopenharmony_ci *p = 0; 3878c2ecf20Sopenharmony_ci if (new_argc >= FUNCTION_MAX_ARGS) 3888c2ecf20Sopenharmony_ci pperror("too many function arguments"); 3898c2ecf20Sopenharmony_ci new_argv[new_argc++] = prev; 3908c2ecf20Sopenharmony_ci prev = p + 1; 3918c2ecf20Sopenharmony_ci } else if (*p == '(') { 3928c2ecf20Sopenharmony_ci nest++; 3938c2ecf20Sopenharmony_ci } else if (*p == ')') { 3948c2ecf20Sopenharmony_ci nest--; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci p++; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (new_argc >= FUNCTION_MAX_ARGS) 4018c2ecf20Sopenharmony_ci pperror("too many function arguments"); 4028c2ecf20Sopenharmony_ci new_argv[new_argc++] = prev; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * Shift arguments 4068c2ecf20Sopenharmony_ci * new_argv[0] represents a function name or a variable name. Put it 4078c2ecf20Sopenharmony_ci * into 'name', then shift the rest of the arguments. This simplifies 4088c2ecf20Sopenharmony_ci * 'const' handling. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci name = expand_string_with_args(new_argv[0], argc, argv); 4118c2ecf20Sopenharmony_ci new_argc--; 4128c2ecf20Sopenharmony_ci for (i = 0; i < new_argc; i++) 4138c2ecf20Sopenharmony_ci new_argv[i] = expand_string_with_args(new_argv[i + 1], 4148c2ecf20Sopenharmony_ci argc, argv); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* Search for variables */ 4178c2ecf20Sopenharmony_ci res = variable_expand(name, new_argc, new_argv); 4188c2ecf20Sopenharmony_ci if (res) 4198c2ecf20Sopenharmony_ci goto free; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* Look for built-in functions */ 4228c2ecf20Sopenharmony_ci res = function_expand(name, new_argc, new_argv); 4238c2ecf20Sopenharmony_ci if (res) 4248c2ecf20Sopenharmony_ci goto free; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* Last, try environment variable */ 4278c2ecf20Sopenharmony_ci if (new_argc == 0) { 4288c2ecf20Sopenharmony_ci res = env_expand(name); 4298c2ecf20Sopenharmony_ci if (res) 4308c2ecf20Sopenharmony_ci goto free; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci res = xstrdup(""); 4348c2ecf20Sopenharmony_cifree: 4358c2ecf20Sopenharmony_ci for (i = 0; i < new_argc; i++) 4368c2ecf20Sopenharmony_ci free(new_argv[i]); 4378c2ecf20Sopenharmony_ci free(name); 4388c2ecf20Sopenharmony_cifree_tmp: 4398c2ecf20Sopenharmony_ci free(tmp); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return res; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci/* 4458c2ecf20Sopenharmony_ci * Expand a string that follows '$' 4468c2ecf20Sopenharmony_ci * 4478c2ecf20Sopenharmony_ci * For example, if the input string is 4488c2ecf20Sopenharmony_ci * ($(FOO)$($(BAR)))$(BAZ) 4498c2ecf20Sopenharmony_ci * this helper evaluates 4508c2ecf20Sopenharmony_ci * $($(FOO)$($(BAR))) 4518c2ecf20Sopenharmony_ci * and returns a new string containing the expansion (note that the string is 4528c2ecf20Sopenharmony_ci * recursively expanded), also advancing 'str' to point to the next character 4538c2ecf20Sopenharmony_ci * after the corresponding closing parenthesis, in this case, *str will be 4548c2ecf20Sopenharmony_ci * $(BAR) 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_cistatic char *expand_dollar_with_args(const char **str, int argc, char *argv[]) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci const char *p = *str; 4598c2ecf20Sopenharmony_ci const char *q; 4608c2ecf20Sopenharmony_ci int nest = 0; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* 4638c2ecf20Sopenharmony_ci * In Kconfig, variable/function references always start with "$(". 4648c2ecf20Sopenharmony_ci * Neither single-letter variables as in $A nor curly braces as in ${CC} 4658c2ecf20Sopenharmony_ci * are supported. '$' not followed by '(' loses its special meaning. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ci if (*p != '(') { 4688c2ecf20Sopenharmony_ci *str = p; 4698c2ecf20Sopenharmony_ci return xstrdup("$"); 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci p++; 4738c2ecf20Sopenharmony_ci q = p; 4748c2ecf20Sopenharmony_ci while (*q) { 4758c2ecf20Sopenharmony_ci if (*q == '(') { 4768c2ecf20Sopenharmony_ci nest++; 4778c2ecf20Sopenharmony_ci } else if (*q == ')') { 4788c2ecf20Sopenharmony_ci if (nest-- == 0) 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci q++; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (!*q) 4858c2ecf20Sopenharmony_ci pperror("unterminated reference to '%s': missing ')'", p); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* Advance 'str' to after the expanded initial portion of the string */ 4888c2ecf20Sopenharmony_ci *str = q + 1; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci return eval_clause(p, q - p, argc, argv); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cichar *expand_dollar(const char **str) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci return expand_dollar_with_args(str, 0, NULL); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic char *__expand_string(const char **str, bool (*is_end)(char c), 4998c2ecf20Sopenharmony_ci int argc, char *argv[]) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci const char *in, *p; 5028c2ecf20Sopenharmony_ci char *expansion, *out; 5038c2ecf20Sopenharmony_ci size_t in_len, out_len; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci out = xmalloc(1); 5068c2ecf20Sopenharmony_ci *out = 0; 5078c2ecf20Sopenharmony_ci out_len = 1; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci p = in = *str; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci while (1) { 5128c2ecf20Sopenharmony_ci if (*p == '$') { 5138c2ecf20Sopenharmony_ci in_len = p - in; 5148c2ecf20Sopenharmony_ci p++; 5158c2ecf20Sopenharmony_ci expansion = expand_dollar_with_args(&p, argc, argv); 5168c2ecf20Sopenharmony_ci out_len += in_len + strlen(expansion); 5178c2ecf20Sopenharmony_ci out = xrealloc(out, out_len); 5188c2ecf20Sopenharmony_ci strncat(out, in, in_len); 5198c2ecf20Sopenharmony_ci strcat(out, expansion); 5208c2ecf20Sopenharmony_ci free(expansion); 5218c2ecf20Sopenharmony_ci in = p; 5228c2ecf20Sopenharmony_ci continue; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (is_end(*p)) 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci p++; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci in_len = p - in; 5328c2ecf20Sopenharmony_ci out_len += in_len; 5338c2ecf20Sopenharmony_ci out = xrealloc(out, out_len); 5348c2ecf20Sopenharmony_ci strncat(out, in, in_len); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* Advance 'str' to the end character */ 5378c2ecf20Sopenharmony_ci *str = p; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return out; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic bool is_end_of_str(char c) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci return !c; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci/* 5488c2ecf20Sopenharmony_ci * Expand variables and functions in the given string. Undefined variables 5498c2ecf20Sopenharmony_ci * expand to an empty string. 5508c2ecf20Sopenharmony_ci * The returned string must be freed when done. 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_cistatic char *expand_string_with_args(const char *in, int argc, char *argv[]) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci return __expand_string(&in, is_end_of_str, argc, argv); 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic char *expand_string(const char *in) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci return expand_string_with_args(in, 0, NULL); 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic bool is_end_of_token(char c) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci return !(isalnum(c) || c == '_' || c == '-'); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci/* 5688c2ecf20Sopenharmony_ci * Expand variables in a token. The parsing stops when a token separater 5698c2ecf20Sopenharmony_ci * (in most cases, it is a whitespace) is encountered. 'str' is updated to 5708c2ecf20Sopenharmony_ci * point to the next character. 5718c2ecf20Sopenharmony_ci * 5728c2ecf20Sopenharmony_ci * The returned string must be freed when done. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_cichar *expand_one_token(const char **str) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci return __expand_string(str, is_end_of_token, 0, NULL); 5778c2ecf20Sopenharmony_ci} 578