Blame src/ucm/ucm_subs.c

Packit Service db8eaa
/*
Packit Service db8eaa
 *  This library is free software; you can redistribute it and/or
Packit Service db8eaa
 *  modify it under the terms of the GNU Lesser General Public
Packit Service db8eaa
 *  License as published by the Free Software Foundation; either
Packit Service db8eaa
 *  version 2 of the License, or (at your option) any later version.
Packit Service db8eaa
 *
Packit Service db8eaa
 *  This library is distributed in the hope that it will be useful,
Packit Service db8eaa
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service db8eaa
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service db8eaa
 *  Lesser General Public License for more details.
Packit Service db8eaa
 *
Packit Service db8eaa
 *  You should have received a copy of the GNU Lesser General Public
Packit Service db8eaa
 *  License along with this library; if not, write to the Free Software
Packit Service db8eaa
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit Service db8eaa
 *
Packit Service db8eaa
 *  Support for the verb/device/modifier core logic and API,
Packit Service db8eaa
 *  command line tool and file parser was kindly sponsored by
Packit Service db8eaa
 *  Texas Instruments Inc.
Packit Service db8eaa
 *  Support for multiple active modifiers and devices,
Packit Service db8eaa
 *  transition sequences, multiple client access and user defined use
Packit Service db8eaa
 *  cases was kindly sponsored by Wolfson Microelectronics PLC.
Packit Service db8eaa
 *
Packit Service db8eaa
 *  Copyright (C) 2019 Red Hat Inc.
Packit Service db8eaa
 *  Authors: Jaroslav Kysela <perex@perex.cz>
Packit Service db8eaa
 */
Packit Service db8eaa
Packit Service db8eaa
#include "ucm_local.h"
Packit Service db8eaa
#include <stdbool.h>
Packit Service db8eaa
#include <sys/stat.h>
Packit Service db8eaa
#include <limits.h>
Packit Service db8eaa
Packit Service db8eaa
static char *rval_open_name(snd_use_case_mgr_t *uc_mgr)
Packit Service db8eaa
{
Packit Service db8eaa
	const char *name;
Packit Service db8eaa
	if (uc_mgr->conf_format < 3)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	name = uc_mgr->card_name;
Packit Service db8eaa
	if (name) {
Packit Service db8eaa
		if (strncmp(name, "strict:", 7) == 0)
Packit Service db8eaa
			name += 7;
Packit Service db8eaa
		return strdup(name);
Packit Service db8eaa
	}
Packit Service db8eaa
	return NULL;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_conf_topdir(snd_use_case_mgr_t *uc_mgr)
Packit Service db8eaa
{
Packit Service db8eaa
	const char *dir;
Packit Service db8eaa
Packit Service db8eaa
	if (uc_mgr->conf_format < 3)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	dir = uc_mgr_config_dir(uc_mgr->conf_format);
Packit Service db8eaa
	if (dir && dir[0])
Packit Service db8eaa
		return strdup(dir);
Packit Service db8eaa
	return NULL;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_conf_dir(snd_use_case_mgr_t *uc_mgr)
Packit Service db8eaa
{
Packit Service db8eaa
	if (uc_mgr->conf_format < 3)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	if (uc_mgr->conf_dir_name && uc_mgr->conf_dir_name[0])
Packit Service db8eaa
		return strdup(uc_mgr->conf_dir_name);
Packit Service db8eaa
	return NULL;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_conf_name(snd_use_case_mgr_t *uc_mgr)
Packit Service db8eaa
{
Packit Service db8eaa
	if (uc_mgr->conf_file_name && uc_mgr->conf_file_name[0])
Packit Service db8eaa
		return strdup(uc_mgr->conf_file_name);
Packit Service db8eaa
	return NULL;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *get_card_number(struct ctl_list *ctl_list)
Packit Service db8eaa
{
Packit Service db8eaa
	char num[16];
Packit Service db8eaa
Packit Service db8eaa
	if (ctl_list == NULL)
Packit Service db8eaa
		return strdup("");
Packit Service db8eaa
	snprintf(num, sizeof(num), "%i", snd_ctl_card_info_get_card(ctl_list->ctl_info));
Packit Service db8eaa
	return strdup(num);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_card_number(snd_use_case_mgr_t *uc_mgr)
Packit Service db8eaa
{
Packit Service db8eaa
	if (uc_mgr->conf_format < 3)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	return get_card_number(uc_mgr_get_master_ctl(uc_mgr));
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_card_id(snd_use_case_mgr_t *uc_mgr)
Packit Service db8eaa
{
Packit Service db8eaa
	struct ctl_list *ctl_list;
Packit Service db8eaa
Packit Service db8eaa
	ctl_list = uc_mgr_get_master_ctl(uc_mgr);
Packit Service db8eaa
	if (ctl_list == NULL)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info));
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_card_driver(snd_use_case_mgr_t *uc_mgr)
Packit Service db8eaa
{
Packit Service db8eaa
	struct ctl_list *ctl_list;
Packit Service db8eaa
Packit Service db8eaa
	ctl_list = uc_mgr_get_master_ctl(uc_mgr);
Packit Service db8eaa
	if (ctl_list == NULL)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	return strdup(snd_ctl_card_info_get_driver(ctl_list->ctl_info));
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_card_name(snd_use_case_mgr_t *uc_mgr)
Packit Service db8eaa
{
Packit Service db8eaa
	struct ctl_list *ctl_list;
Packit Service db8eaa
Packit Service db8eaa
	ctl_list = uc_mgr_get_master_ctl(uc_mgr);
Packit Service db8eaa
	if (ctl_list == NULL)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	return strdup(snd_ctl_card_info_get_name(ctl_list->ctl_info));
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_card_longname(snd_use_case_mgr_t *uc_mgr)
Packit Service db8eaa
{
Packit Service db8eaa
	struct ctl_list *ctl_list;
Packit Service db8eaa
Packit Service db8eaa
	ctl_list = uc_mgr_get_master_ctl(uc_mgr);
Packit Service db8eaa
	if (ctl_list == NULL)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	return strdup(snd_ctl_card_info_get_longname(ctl_list->ctl_info));
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_card_components(snd_use_case_mgr_t *uc_mgr)
Packit Service db8eaa
{
Packit Service db8eaa
	struct ctl_list *ctl_list;
Packit Service db8eaa
Packit Service db8eaa
	ctl_list = uc_mgr_get_master_ctl(uc_mgr);
Packit Service db8eaa
	if (ctl_list == NULL)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	return strdup(snd_ctl_card_info_get_components(ctl_list->ctl_info));
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static struct ctl_list *get_ctl_list_by_name(snd_use_case_mgr_t *uc_mgr, const char *id)
Packit Service db8eaa
{
Packit Service db8eaa
	char *name, *index;
Packit Service db8eaa
	long idx = 0;
Packit Service db8eaa
Packit Service db8eaa
	name = alloca(strlen(id) + 1);
Packit Service db8eaa
	strcpy(name, id);
Packit Service db8eaa
	index = strchr(name, '#');
Packit Service db8eaa
	if (index) {
Packit Service db8eaa
		*index = '\0';
Packit Service db8eaa
		if (safe_strtol(index + 1, &idx))
Packit Service db8eaa
			return NULL;
Packit Service db8eaa
	}
Packit Service db8eaa
	return uc_mgr_get_ctl_by_name(uc_mgr, name, idx);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_card_number_by_name(snd_use_case_mgr_t *uc_mgr, const char *id)
Packit Service db8eaa
{
Packit Service db8eaa
	if (uc_mgr->conf_format < 3) {
Packit Service db8eaa
		uc_error("CardNumberByName substitution is supported in v3+ syntax");
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return get_card_number(get_ctl_list_by_name(uc_mgr, id));
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_card_id_by_name(snd_use_case_mgr_t *uc_mgr, const char *id)
Packit Service db8eaa
{
Packit Service db8eaa
	struct ctl_list *ctl_list;
Packit Service db8eaa
Packit Service db8eaa
	if (uc_mgr->conf_format < 3) {
Packit Service db8eaa
		uc_error("CardIdByName substitution is supported in v3+ syntax");
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	ctl_list = get_ctl_list_by_name(uc_mgr, id);
Packit Service db8eaa
	if (ctl_list == NULL)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info));
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_env(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id)
Packit Service db8eaa
{
Packit Service db8eaa
	char *e;
Packit Service db8eaa
Packit Service db8eaa
	e = getenv(id);
Packit Service db8eaa
	if (e)
Packit Service db8eaa
		return strdup(e);
Packit Service db8eaa
	return NULL;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_sysfs(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id)
Packit Service db8eaa
{
Packit Service db8eaa
	char path[PATH_MAX], link[PATH_MAX + 1];
Packit Service db8eaa
	struct stat sb;
Packit Service db8eaa
	ssize_t len;
Packit Service db8eaa
	char *e;
Packit Service db8eaa
	int fd;
Packit Service db8eaa
Packit Service db8eaa
	e = getenv("SYSFS_PATH");
Packit Service db8eaa
	if (e == NULL)
Packit Service db8eaa
		e = "/sys";
Packit Service db8eaa
	if (id[0] == '/')
Packit Service db8eaa
		id++;
Packit Service db8eaa
	snprintf(path, sizeof(path), "%s/%s", e, id);
Packit Service db8eaa
	if (lstat(path, &sb) != 0)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	if (S_ISLNK(sb.st_mode)) {
Packit Service db8eaa
		len = readlink(path, link, sizeof(link) - 1);
Packit Service db8eaa
		if (len <= 0) {
Packit Service db8eaa
			uc_error("sysfs: cannot read link '%s' (%d)", path, errno);
Packit Service db8eaa
			return NULL;
Packit Service db8eaa
		}
Packit Service db8eaa
		link[len] = '\0';
Packit Service db8eaa
		e = strrchr(link, '/');
Packit Service db8eaa
		if (e)
Packit Service db8eaa
			return strdup(e + 1);
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (S_ISDIR(sb.st_mode))
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	if ((sb.st_mode & S_IRUSR) == 0)
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
Packit Service db8eaa
	fd = open(path, O_RDONLY);
Packit Service db8eaa
	if (fd < 0) {
Packit Service db8eaa
		uc_error("sysfs open failed for '%s' (%d)", path, errno);
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	}
Packit Service db8eaa
	len = read(fd, path, sizeof(path)-1);
Packit Service db8eaa
	close(fd);
Packit Service db8eaa
	if (len < 0) {
Packit Service db8eaa
		uc_error("sysfs unable to read value '%s' (%d)", path, errno);
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	}
Packit Service db8eaa
	while (len > 0 && path[len-1] == '\n')
Packit Service db8eaa
		len--;
Packit Service db8eaa
	path[len] = '\0';
Packit Service db8eaa
	return strdup(path);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static char *rval_var(snd_use_case_mgr_t *uc_mgr, const char *id)
Packit Service db8eaa
{
Packit Service db8eaa
	const char *v;
Packit Service db8eaa
Packit Service db8eaa
	if (uc_mgr->conf_format < 3) {
Packit Service db8eaa
		uc_error("variable substitution is supported in v3+ syntax");
Packit Service db8eaa
		return NULL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	v = uc_mgr_get_variable(uc_mgr, id);
Packit Service db8eaa
	if (v)
Packit Service db8eaa
		return strdup(v);
Packit Service db8eaa
	return NULL;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
#define MATCH_VARIABLE(name, id, fcn, empty_ok)				\
Packit Service db8eaa
	if (strncmp((name), (id), sizeof(id) - 1) == 0) { 		\
Packit Service db8eaa
		rval = fcn(uc_mgr);					\
Packit Service db8eaa
		idsize = sizeof(id) - 1;				\
Packit Service db8eaa
		allow_empty = (empty_ok);				\
Packit Service db8eaa
		goto __rval;						\
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
#define MATCH_VARIABLE2(name, id, fcn, empty_ok)			\
Packit Service db8eaa
	if (strncmp((name), (id), sizeof(id) - 1) == 0) {		\
Packit Service db8eaa
		idsize = sizeof(id) - 1;				\
Packit Service db8eaa
		allow_empty = (empty_ok);				\
Packit Service db8eaa
		fcn2 = (fcn);						\
Packit Service db8eaa
		goto __match2;						\
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
int uc_mgr_get_substituted_value(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
				 char **_rvalue,
Packit Service db8eaa
				 const char *value)
Packit Service db8eaa
{
Packit Service db8eaa
	size_t size, nsize, idsize, rvalsize, dpos = 0;
Packit Service db8eaa
	const char *tmp;
Packit Service db8eaa
	char *r, *nr, *rval, v2[48];
Packit Service db8eaa
	bool ignore_error, allow_empty;
Packit Service db8eaa
	char *(*fcn2)(snd_use_case_mgr_t *, const char *id);
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (value == NULL)
Packit Service db8eaa
		return -ENOENT;
Packit Service db8eaa
Packit Service db8eaa
	size = strlen(value) + 1;
Packit Service db8eaa
	r = malloc(size);
Packit Service db8eaa
	if (r == NULL)
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
Packit Service db8eaa
	while (*value) {
Packit Service db8eaa
		if (*value != '$') {
Packit Service db8eaa
__std:
Packit Service db8eaa
			r[dpos++] = *value;
Packit Service db8eaa
			value++;
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
		ignore_error = false;
Packit Service db8eaa
		if (value[1] == '$' && value[2] == '{' && uc_mgr->conf_format >= 3) {
Packit Service db8eaa
			value++;
Packit Service db8eaa
			ignore_error = true;
Packit Service db8eaa
		} else if (value[1] != '{') {
Packit Service db8eaa
			goto __std;
Packit Service db8eaa
		}
Packit Service db8eaa
		fcn2 = NULL;
Packit Service db8eaa
		MATCH_VARIABLE(value, "${OpenName}", rval_open_name, false);
Packit Service db8eaa
		MATCH_VARIABLE(value, "${ConfTopDir}", rval_conf_topdir, false);
Packit Service db8eaa
		MATCH_VARIABLE(value, "${ConfDir}", rval_conf_dir, false);
Packit Service db8eaa
		MATCH_VARIABLE(value, "${ConfName}", rval_conf_name, false);
Packit Service db8eaa
		MATCH_VARIABLE(value, "${CardNumber}", rval_card_number, true);
Packit Service db8eaa
		MATCH_VARIABLE(value, "${CardId}", rval_card_id, false);
Packit Service db8eaa
		MATCH_VARIABLE(value, "${CardDriver}", rval_card_driver, false);
Packit Service db8eaa
		MATCH_VARIABLE(value, "${CardName}", rval_card_name, false);
Packit Service db8eaa
		MATCH_VARIABLE(value, "${CardLongName}", rval_card_longname, false);
Packit Service db8eaa
		MATCH_VARIABLE(value, "${CardComponents}", rval_card_components, true);
Packit Service db8eaa
		MATCH_VARIABLE2(value, "${env:", rval_env, false);
Packit Service db8eaa
		MATCH_VARIABLE2(value, "${sys:", rval_sysfs, false);
Packit Service db8eaa
		MATCH_VARIABLE2(value, "${var:", rval_var, true);
Packit Service db8eaa
		MATCH_VARIABLE2(value, "${CardNumberByName:", rval_card_number_by_name, false);
Packit Service db8eaa
		MATCH_VARIABLE2(value, "${CardIdByName:", rval_card_id_by_name, false);
Packit Service db8eaa
__merr:
Packit Service db8eaa
		err = -EINVAL;
Packit Service db8eaa
		tmp = strchr(value, '}');
Packit Service db8eaa
		if (tmp) {
Packit Service db8eaa
			strncpy(r, value, tmp + 1 - value);
Packit Service db8eaa
			r[tmp + 1 - value] = '\0';
Packit Service db8eaa
			uc_error("variable '%s' is not known!", r);
Packit Service db8eaa
		} else {
Packit Service db8eaa
			uc_error("variable reference '%s' is not complete", value);
Packit Service db8eaa
		}
Packit Service db8eaa
		goto __error;
Packit Service db8eaa
__match2:
Packit Service db8eaa
		tmp = strchr(value + idsize, '}');
Packit Service db8eaa
		if (tmp) {
Packit Service db8eaa
			rvalsize = tmp - (value + idsize);
Packit Service db8eaa
			if (rvalsize >= sizeof(v2)) {
Packit Service db8eaa
				err = -ENOMEM;
Packit Service db8eaa
				goto __error;
Packit Service db8eaa
			}
Packit Service db8eaa
			strncpy(v2, value + idsize, rvalsize);
Packit Service db8eaa
			v2[rvalsize] = '\0';
Packit Service db8eaa
			idsize += rvalsize + 1;
Packit Service db8eaa
			if (*v2 == '$' && uc_mgr->conf_format >= 3) {
Packit Service db8eaa
				tmp = uc_mgr_get_variable(uc_mgr, v2 + 1);
Packit Service db8eaa
				if (tmp == NULL) {
Packit Service db8eaa
					uc_error("define '%s' is not reachable in this context!", v2 + 1);
Packit Service db8eaa
					rval = NULL;
Packit Service db8eaa
				} else {
Packit Service db8eaa
					rval = fcn2(uc_mgr, tmp);
Packit Service db8eaa
				}
Packit Service db8eaa
			} else {
Packit Service db8eaa
				rval = fcn2(uc_mgr, v2);
Packit Service db8eaa
			}
Packit Service db8eaa
			goto __rval;
Packit Service db8eaa
		}
Packit Service db8eaa
		goto __merr;
Packit Service db8eaa
__rval:
Packit Service db8eaa
		if (rval == NULL || (!allow_empty && rval[0] == '\0')) {
Packit Service db8eaa
			free(rval);
Packit Service db8eaa
			if (ignore_error) {
Packit Service db8eaa
				value += idsize;
Packit Service db8eaa
				continue;
Packit Service db8eaa
			}
Packit Service db8eaa
			strncpy(r, value, idsize);
Packit Service db8eaa
			r[idsize] = '\0';
Packit Service db8eaa
			uc_error("variable '%s' is %s in this context!", r,
Packit Service db8eaa
				 rval ? "empty" : "not defined");
Packit Service db8eaa
			err = -EINVAL;
Packit Service db8eaa
			goto __error;
Packit Service db8eaa
		}
Packit Service db8eaa
		value += idsize;
Packit Service db8eaa
		rvalsize = strlen(rval);
Packit Service db8eaa
		nsize = size + rvalsize - idsize;
Packit Service db8eaa
		if (nsize > size) {
Packit Service db8eaa
			nr = realloc(r, nsize);
Packit Service db8eaa
			if (nr == NULL) {
Packit Service db8eaa
				free(rval);
Packit Service db8eaa
				err = -ENOMEM;
Packit Service db8eaa
				goto __error;
Packit Service db8eaa
			}
Packit Service db8eaa
			size = nsize;
Packit Service db8eaa
			r = nr;
Packit Service db8eaa
		}
Packit Service db8eaa
		strcpy(r + dpos, rval);
Packit Service db8eaa
		dpos += rvalsize;
Packit Service db8eaa
		free(rval);
Packit Service db8eaa
	}
Packit Service db8eaa
	r[dpos] = '\0';
Packit Service db8eaa
Packit Service db8eaa
	*_rvalue = r;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
Packit Service db8eaa
__error:
Packit Service db8eaa
	free(r);
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static inline int uc_mgr_substitute_check(const char *s)
Packit Service db8eaa
{
Packit Service db8eaa
	return s && strstr(s, "${") != NULL;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
int uc_mgr_substitute_tree(snd_use_case_mgr_t *uc_mgr, snd_config_t *node)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	const char *id, *s2;
Packit Service db8eaa
	char *s;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_config_get_id(node, &id;;
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	if (uc_mgr_substitute_check(id)) {
Packit Service db8eaa
		err = uc_mgr_get_substituted_value(uc_mgr, &s, id);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		err = snd_config_set_id(node, s);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			uc_error("unable to set substituted id '%s' (old id '%s')", s, id);
Packit Service 01c0b7
			free(s);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service 01c0b7
		free(s);
Packit Service db8eaa
	}
Packit Service db8eaa
	if (snd_config_get_type(node) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
		if (snd_config_get_type(node) == SND_CONFIG_TYPE_STRING) {
Packit Service db8eaa
			err = snd_config_get_string(node, &s2;;
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				return err;
Packit Service db8eaa
			if (!uc_mgr_substitute_check(s2))
Packit Service db8eaa
				return 0;
Packit Service db8eaa
			err = uc_mgr_get_substituted_value(uc_mgr, &s, s2);
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				return err;
Packit Service db8eaa
			err = snd_config_set_string(node, s);
Packit Service db8eaa
			free(s);
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				return err;
Packit Service db8eaa
		}
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	}
Packit Service db8eaa
	snd_config_for_each(i, next, node) {
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
		err = uc_mgr_substitute_tree(uc_mgr, n);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}