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