162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (C) 2018 Masahiro Yamada <yamada.masahiro@socionext.com> 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <ctype.h> 662306a36Sopenharmony_ci#include <stdarg.h> 762306a36Sopenharmony_ci#include <stdbool.h> 862306a36Sopenharmony_ci#include <stdio.h> 962306a36Sopenharmony_ci#include <stdlib.h> 1062306a36Sopenharmony_ci#include <string.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "list.h" 1362306a36Sopenharmony_ci#include "lkc.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic char *expand_string_with_args(const char *in, int argc, char *argv[]); 1862306a36Sopenharmony_cistatic char *expand_string(const char *in); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic void __attribute__((noreturn)) pperror(const char *format, ...) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci va_list ap; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci fprintf(stderr, "%s:%d: ", current_file->name, yylineno); 2562306a36Sopenharmony_ci va_start(ap, format); 2662306a36Sopenharmony_ci vfprintf(stderr, format, ap); 2762306a36Sopenharmony_ci va_end(ap); 2862306a36Sopenharmony_ci fprintf(stderr, "\n"); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci exit(1); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * Environment variables 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_cistatic LIST_HEAD(env_list); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct env { 3962306a36Sopenharmony_ci char *name; 4062306a36Sopenharmony_ci char *value; 4162306a36Sopenharmony_ci struct list_head node; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void env_add(const char *name, const char *value) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct env *e; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci e = xmalloc(sizeof(*e)); 4962306a36Sopenharmony_ci e->name = xstrdup(name); 5062306a36Sopenharmony_ci e->value = xstrdup(value); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci list_add_tail(&e->node, &env_list); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void env_del(struct env *e) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci list_del(&e->node); 5862306a36Sopenharmony_ci free(e->name); 5962306a36Sopenharmony_ci free(e->value); 6062306a36Sopenharmony_ci free(e); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* The returned pointer must be freed when done */ 6462306a36Sopenharmony_cistatic char *env_expand(const char *name) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct env *e; 6762306a36Sopenharmony_ci const char *value; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (!*name) 7062306a36Sopenharmony_ci return NULL; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci list_for_each_entry(e, &env_list, node) { 7362306a36Sopenharmony_ci if (!strcmp(name, e->name)) 7462306a36Sopenharmony_ci return xstrdup(e->value); 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci value = getenv(name); 7862306a36Sopenharmony_ci if (!value) 7962306a36Sopenharmony_ci return NULL; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* 8262306a36Sopenharmony_ci * We need to remember all referenced environment variables. 8362306a36Sopenharmony_ci * They will be written out to include/config/auto.conf.cmd 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci env_add(name, value); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return xstrdup(value); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_civoid env_write_dep(FILE *f, const char *autoconfig_name) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct env *e, *tmp; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci list_for_each_entry_safe(e, tmp, &env_list, node) { 9562306a36Sopenharmony_ci fprintf(f, "ifneq \"$(%s)\" \"%s\"\n", e->name, e->value); 9662306a36Sopenharmony_ci fprintf(f, "%s: FORCE\n", autoconfig_name); 9762306a36Sopenharmony_ci fprintf(f, "endif\n"); 9862306a36Sopenharmony_ci env_del(e); 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * Built-in functions 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistruct function { 10662306a36Sopenharmony_ci const char *name; 10762306a36Sopenharmony_ci unsigned int min_args; 10862306a36Sopenharmony_ci unsigned int max_args; 10962306a36Sopenharmony_ci char *(*func)(int argc, char *argv[]); 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic char *do_error_if(int argc, char *argv[]) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci if (!strcmp(argv[0], "y")) 11562306a36Sopenharmony_ci pperror("%s", argv[1]); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return xstrdup(""); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic char *do_filename(int argc, char *argv[]) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci return xstrdup(current_file->name); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic char *do_info(int argc, char *argv[]) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci printf("%s\n", argv[0]); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return xstrdup(""); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic char *do_lineno(int argc, char *argv[]) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci char buf[16]; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci sprintf(buf, "%d", yylineno); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return xstrdup(buf); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic char *do_shell(int argc, char *argv[]) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci FILE *p; 14462306a36Sopenharmony_ci char buf[4096]; 14562306a36Sopenharmony_ci char *cmd; 14662306a36Sopenharmony_ci size_t nread; 14762306a36Sopenharmony_ci int i; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci cmd = argv[0]; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci p = popen(cmd, "r"); 15262306a36Sopenharmony_ci if (!p) { 15362306a36Sopenharmony_ci perror(cmd); 15462306a36Sopenharmony_ci exit(1); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci nread = fread(buf, 1, sizeof(buf), p); 15862306a36Sopenharmony_ci if (nread == sizeof(buf)) 15962306a36Sopenharmony_ci nread--; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* remove trailing new lines */ 16262306a36Sopenharmony_ci while (nread > 0 && buf[nread - 1] == '\n') 16362306a36Sopenharmony_ci nread--; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci buf[nread] = 0; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* replace a new line with a space */ 16862306a36Sopenharmony_ci for (i = 0; i < nread; i++) { 16962306a36Sopenharmony_ci if (buf[i] == '\n') 17062306a36Sopenharmony_ci buf[i] = ' '; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (pclose(p) == -1) { 17462306a36Sopenharmony_ci perror(cmd); 17562306a36Sopenharmony_ci exit(1); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return xstrdup(buf); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic char *do_warning_if(int argc, char *argv[]) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci if (!strcmp(argv[0], "y")) 18462306a36Sopenharmony_ci fprintf(stderr, "%s:%d: %s\n", 18562306a36Sopenharmony_ci current_file->name, yylineno, argv[1]); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return xstrdup(""); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic const struct function function_table[] = { 19162306a36Sopenharmony_ci /* Name MIN MAX Function */ 19262306a36Sopenharmony_ci { "error-if", 2, 2, do_error_if }, 19362306a36Sopenharmony_ci { "filename", 0, 0, do_filename }, 19462306a36Sopenharmony_ci { "info", 1, 1, do_info }, 19562306a36Sopenharmony_ci { "lineno", 0, 0, do_lineno }, 19662306a36Sopenharmony_ci { "shell", 1, 1, do_shell }, 19762306a36Sopenharmony_ci { "warning-if", 2, 2, do_warning_if }, 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci#define FUNCTION_MAX_ARGS 16 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic char *function_expand(const char *name, int argc, char *argv[]) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci const struct function *f; 20562306a36Sopenharmony_ci int i; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(function_table); i++) { 20862306a36Sopenharmony_ci f = &function_table[i]; 20962306a36Sopenharmony_ci if (strcmp(f->name, name)) 21062306a36Sopenharmony_ci continue; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (argc < f->min_args) 21362306a36Sopenharmony_ci pperror("too few function arguments passed to '%s'", 21462306a36Sopenharmony_ci name); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (argc > f->max_args) 21762306a36Sopenharmony_ci pperror("too many function arguments passed to '%s'", 21862306a36Sopenharmony_ci name); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return f->func(argc, argv); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return NULL; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* 22762306a36Sopenharmony_ci * Variables (and user-defined functions) 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_cistatic LIST_HEAD(variable_list); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistruct variable { 23262306a36Sopenharmony_ci char *name; 23362306a36Sopenharmony_ci char *value; 23462306a36Sopenharmony_ci enum variable_flavor flavor; 23562306a36Sopenharmony_ci int exp_count; 23662306a36Sopenharmony_ci struct list_head node; 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic struct variable *variable_lookup(const char *name) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct variable *v; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci list_for_each_entry(v, &variable_list, node) { 24462306a36Sopenharmony_ci if (!strcmp(name, v->name)) 24562306a36Sopenharmony_ci return v; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return NULL; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic char *variable_expand(const char *name, int argc, char *argv[]) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct variable *v; 25462306a36Sopenharmony_ci char *res; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci v = variable_lookup(name); 25762306a36Sopenharmony_ci if (!v) 25862306a36Sopenharmony_ci return NULL; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (argc == 0 && v->exp_count) 26162306a36Sopenharmony_ci pperror("Recursive variable '%s' references itself (eventually)", 26262306a36Sopenharmony_ci name); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (v->exp_count > 1000) 26562306a36Sopenharmony_ci pperror("Too deep recursive expansion"); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci v->exp_count++; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (v->flavor == VAR_RECURSIVE) 27062306a36Sopenharmony_ci res = expand_string_with_args(v->value, argc, argv); 27162306a36Sopenharmony_ci else 27262306a36Sopenharmony_ci res = xstrdup(v->value); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci v->exp_count--; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return res; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_civoid variable_add(const char *name, const char *value, 28062306a36Sopenharmony_ci enum variable_flavor flavor) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct variable *v; 28362306a36Sopenharmony_ci char *new_value; 28462306a36Sopenharmony_ci bool append = false; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci v = variable_lookup(name); 28762306a36Sopenharmony_ci if (v) { 28862306a36Sopenharmony_ci /* For defined variables, += inherits the existing flavor */ 28962306a36Sopenharmony_ci if (flavor == VAR_APPEND) { 29062306a36Sopenharmony_ci flavor = v->flavor; 29162306a36Sopenharmony_ci append = true; 29262306a36Sopenharmony_ci } else { 29362306a36Sopenharmony_ci free(v->value); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci } else { 29662306a36Sopenharmony_ci /* For undefined variables, += assumes the recursive flavor */ 29762306a36Sopenharmony_ci if (flavor == VAR_APPEND) 29862306a36Sopenharmony_ci flavor = VAR_RECURSIVE; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci v = xmalloc(sizeof(*v)); 30162306a36Sopenharmony_ci v->name = xstrdup(name); 30262306a36Sopenharmony_ci v->exp_count = 0; 30362306a36Sopenharmony_ci list_add_tail(&v->node, &variable_list); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci v->flavor = flavor; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (flavor == VAR_SIMPLE) 30962306a36Sopenharmony_ci new_value = expand_string(value); 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci new_value = xstrdup(value); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (append) { 31462306a36Sopenharmony_ci v->value = xrealloc(v->value, 31562306a36Sopenharmony_ci strlen(v->value) + strlen(new_value) + 2); 31662306a36Sopenharmony_ci strcat(v->value, " "); 31762306a36Sopenharmony_ci strcat(v->value, new_value); 31862306a36Sopenharmony_ci free(new_value); 31962306a36Sopenharmony_ci } else { 32062306a36Sopenharmony_ci v->value = new_value; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic void variable_del(struct variable *v) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci list_del(&v->node); 32762306a36Sopenharmony_ci free(v->name); 32862306a36Sopenharmony_ci free(v->value); 32962306a36Sopenharmony_ci free(v); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_civoid variable_all_del(void) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct variable *v, *tmp; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci list_for_each_entry_safe(v, tmp, &variable_list, node) 33762306a36Sopenharmony_ci variable_del(v); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/* 34162306a36Sopenharmony_ci * Evaluate a clause with arguments. argc/argv are arguments from the upper 34262306a36Sopenharmony_ci * function call. 34362306a36Sopenharmony_ci * 34462306a36Sopenharmony_ci * Returned string must be freed when done 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_cistatic char *eval_clause(const char *str, size_t len, int argc, char *argv[]) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci char *tmp, *name, *res, *endptr, *prev, *p; 34962306a36Sopenharmony_ci int new_argc = 0; 35062306a36Sopenharmony_ci char *new_argv[FUNCTION_MAX_ARGS]; 35162306a36Sopenharmony_ci int nest = 0; 35262306a36Sopenharmony_ci int i; 35362306a36Sopenharmony_ci unsigned long n; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci tmp = xstrndup(str, len); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* 35862306a36Sopenharmony_ci * If variable name is '1', '2', etc. It is generally an argument 35962306a36Sopenharmony_ci * from a user-function call (i.e. local-scope variable). If not 36062306a36Sopenharmony_ci * available, then look-up global-scope variables. 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_ci n = strtoul(tmp, &endptr, 10); 36362306a36Sopenharmony_ci if (!*endptr && n > 0 && n <= argc) { 36462306a36Sopenharmony_ci res = xstrdup(argv[n - 1]); 36562306a36Sopenharmony_ci goto free_tmp; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci prev = p = tmp; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* 37162306a36Sopenharmony_ci * Split into tokens 37262306a36Sopenharmony_ci * The function name and arguments are separated by a comma. 37362306a36Sopenharmony_ci * For example, if the function call is like this: 37462306a36Sopenharmony_ci * $(foo,$(x),$(y)) 37562306a36Sopenharmony_ci * 37662306a36Sopenharmony_ci * The input string for this helper should be: 37762306a36Sopenharmony_ci * foo,$(x),$(y) 37862306a36Sopenharmony_ci * 37962306a36Sopenharmony_ci * and split into: 38062306a36Sopenharmony_ci * new_argv[0] = 'foo' 38162306a36Sopenharmony_ci * new_argv[1] = '$(x)' 38262306a36Sopenharmony_ci * new_argv[2] = '$(y)' 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci while (*p) { 38562306a36Sopenharmony_ci if (nest == 0 && *p == ',') { 38662306a36Sopenharmony_ci *p = 0; 38762306a36Sopenharmony_ci if (new_argc >= FUNCTION_MAX_ARGS) 38862306a36Sopenharmony_ci pperror("too many function arguments"); 38962306a36Sopenharmony_ci new_argv[new_argc++] = prev; 39062306a36Sopenharmony_ci prev = p + 1; 39162306a36Sopenharmony_ci } else if (*p == '(') { 39262306a36Sopenharmony_ci nest++; 39362306a36Sopenharmony_ci } else if (*p == ')') { 39462306a36Sopenharmony_ci nest--; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci p++; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (new_argc >= FUNCTION_MAX_ARGS) 40162306a36Sopenharmony_ci pperror("too many function arguments"); 40262306a36Sopenharmony_ci new_argv[new_argc++] = prev; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* 40562306a36Sopenharmony_ci * Shift arguments 40662306a36Sopenharmony_ci * new_argv[0] represents a function name or a variable name. Put it 40762306a36Sopenharmony_ci * into 'name', then shift the rest of the arguments. This simplifies 40862306a36Sopenharmony_ci * 'const' handling. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ci name = expand_string_with_args(new_argv[0], argc, argv); 41162306a36Sopenharmony_ci new_argc--; 41262306a36Sopenharmony_ci for (i = 0; i < new_argc; i++) 41362306a36Sopenharmony_ci new_argv[i] = expand_string_with_args(new_argv[i + 1], 41462306a36Sopenharmony_ci argc, argv); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* Search for variables */ 41762306a36Sopenharmony_ci res = variable_expand(name, new_argc, new_argv); 41862306a36Sopenharmony_ci if (res) 41962306a36Sopenharmony_ci goto free; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* Look for built-in functions */ 42262306a36Sopenharmony_ci res = function_expand(name, new_argc, new_argv); 42362306a36Sopenharmony_ci if (res) 42462306a36Sopenharmony_ci goto free; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Last, try environment variable */ 42762306a36Sopenharmony_ci if (new_argc == 0) { 42862306a36Sopenharmony_ci res = env_expand(name); 42962306a36Sopenharmony_ci if (res) 43062306a36Sopenharmony_ci goto free; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci res = xstrdup(""); 43462306a36Sopenharmony_cifree: 43562306a36Sopenharmony_ci for (i = 0; i < new_argc; i++) 43662306a36Sopenharmony_ci free(new_argv[i]); 43762306a36Sopenharmony_ci free(name); 43862306a36Sopenharmony_cifree_tmp: 43962306a36Sopenharmony_ci free(tmp); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return res; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/* 44562306a36Sopenharmony_ci * Expand a string that follows '$' 44662306a36Sopenharmony_ci * 44762306a36Sopenharmony_ci * For example, if the input string is 44862306a36Sopenharmony_ci * ($(FOO)$($(BAR)))$(BAZ) 44962306a36Sopenharmony_ci * this helper evaluates 45062306a36Sopenharmony_ci * $($(FOO)$($(BAR))) 45162306a36Sopenharmony_ci * and returns a new string containing the expansion (note that the string is 45262306a36Sopenharmony_ci * recursively expanded), also advancing 'str' to point to the next character 45362306a36Sopenharmony_ci * after the corresponding closing parenthesis, in this case, *str will be 45462306a36Sopenharmony_ci * $(BAR) 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_cistatic char *expand_dollar_with_args(const char **str, int argc, char *argv[]) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci const char *p = *str; 45962306a36Sopenharmony_ci const char *q; 46062306a36Sopenharmony_ci int nest = 0; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* 46362306a36Sopenharmony_ci * In Kconfig, variable/function references always start with "$(". 46462306a36Sopenharmony_ci * Neither single-letter variables as in $A nor curly braces as in ${CC} 46562306a36Sopenharmony_ci * are supported. '$' not followed by '(' loses its special meaning. 46662306a36Sopenharmony_ci */ 46762306a36Sopenharmony_ci if (*p != '(') { 46862306a36Sopenharmony_ci *str = p; 46962306a36Sopenharmony_ci return xstrdup("$"); 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci p++; 47362306a36Sopenharmony_ci q = p; 47462306a36Sopenharmony_ci while (*q) { 47562306a36Sopenharmony_ci if (*q == '(') { 47662306a36Sopenharmony_ci nest++; 47762306a36Sopenharmony_ci } else if (*q == ')') { 47862306a36Sopenharmony_ci if (nest-- == 0) 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci q++; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (!*q) 48562306a36Sopenharmony_ci pperror("unterminated reference to '%s': missing ')'", p); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* Advance 'str' to after the expanded initial portion of the string */ 48862306a36Sopenharmony_ci *str = q + 1; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return eval_clause(p, q - p, argc, argv); 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cichar *expand_dollar(const char **str) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci return expand_dollar_with_args(str, 0, NULL); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic char *__expand_string(const char **str, bool (*is_end)(char c), 49962306a36Sopenharmony_ci int argc, char *argv[]) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci const char *in, *p; 50262306a36Sopenharmony_ci char *expansion, *out; 50362306a36Sopenharmony_ci size_t in_len, out_len; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci out = xmalloc(1); 50662306a36Sopenharmony_ci *out = 0; 50762306a36Sopenharmony_ci out_len = 1; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci p = in = *str; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci while (1) { 51262306a36Sopenharmony_ci if (*p == '$') { 51362306a36Sopenharmony_ci in_len = p - in; 51462306a36Sopenharmony_ci p++; 51562306a36Sopenharmony_ci expansion = expand_dollar_with_args(&p, argc, argv); 51662306a36Sopenharmony_ci out_len += in_len + strlen(expansion); 51762306a36Sopenharmony_ci out = xrealloc(out, out_len); 51862306a36Sopenharmony_ci strncat(out, in, in_len); 51962306a36Sopenharmony_ci strcat(out, expansion); 52062306a36Sopenharmony_ci free(expansion); 52162306a36Sopenharmony_ci in = p; 52262306a36Sopenharmony_ci continue; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (is_end(*p)) 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci p++; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci in_len = p - in; 53262306a36Sopenharmony_ci out_len += in_len; 53362306a36Sopenharmony_ci out = xrealloc(out, out_len); 53462306a36Sopenharmony_ci strncat(out, in, in_len); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* Advance 'str' to the end character */ 53762306a36Sopenharmony_ci *str = p; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return out; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic bool is_end_of_str(char c) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci return !c; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci/* 54862306a36Sopenharmony_ci * Expand variables and functions in the given string. Undefined variables 54962306a36Sopenharmony_ci * expand to an empty string. 55062306a36Sopenharmony_ci * The returned string must be freed when done. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_cistatic char *expand_string_with_args(const char *in, int argc, char *argv[]) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci return __expand_string(&in, is_end_of_str, argc, argv); 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic char *expand_string(const char *in) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci return expand_string_with_args(in, 0, NULL); 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic bool is_end_of_token(char c) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci return !(isalnum(c) || c == '_' || c == '-'); 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci/* 56862306a36Sopenharmony_ci * Expand variables in a token. The parsing stops when a token separater 56962306a36Sopenharmony_ci * (in most cases, it is a whitespace) is encountered. 'str' is updated to 57062306a36Sopenharmony_ci * point to the next character. 57162306a36Sopenharmony_ci * 57262306a36Sopenharmony_ci * The returned string must be freed when done. 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_cichar *expand_one_token(const char **str) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci return __expand_string(str, is_end_of_token, 0, NULL); 57762306a36Sopenharmony_ci} 578