xref: /third_party/alsa-lib/src/ucm/ucm_include.c (revision d5ac70f0)
1/*
2 *  This library is free software; you can redistribute it and/or
3 *  modify it under the terms of the GNU Lesser General Public
4 *  License as published by the Free Software Foundation; either
5 *  version 2 of the License, or (at your option) any later version.
6 *
7 *  This library is distributed in the hope that it will be useful,
8 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10 *  Lesser General Public License for more details.
11 *
12 *  You should have received a copy of the GNU Lesser General Public
13 *  License along with this library; if not, write to the Free Software
14 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
15 *
16 *  Support for the verb/device/modifier core logic and API,
17 *  command line tool and file parser was kindly sponsored by
18 *  Texas Instruments Inc.
19 *  Support for multiple active modifiers and devices,
20 *  transition sequences, multiple client access and user defined use
21 *  cases was kindly sponsored by Wolfson Microelectronics PLC.
22 *
23 *  Copyright (C) 2020 Red Hat Inc.
24 *  Authors: Jaroslav Kysela <perex@perex.cz>
25 */
26
27#include "ucm_local.h"
28
29static int get_string(snd_config_t *compound, const char *key, const char **str)
30{
31	snd_config_t *node;
32	int err;
33
34	err = snd_config_search(compound, key, &node);
35	if (err < 0)
36		return err;
37	return snd_config_get_string(node, str);
38}
39
40static int include_eval_one(snd_use_case_mgr_t *uc_mgr,
41			    snd_config_t *inc,
42			    snd_config_t **result,
43			    snd_config_t **before,
44			    snd_config_t **after)
45{
46	const char *file;
47	char *s;
48	int err;
49
50	*result = NULL;
51
52	if (snd_config_get_type(inc) != SND_CONFIG_TYPE_COMPOUND) {
53		uc_error("compound type expected for Include.1");
54		return -EINVAL;
55	}
56
57	err = get_string(inc, "File", &file);
58	if (err < 0) {
59		uc_error("file expected (Include)");
60		return -EINVAL;
61	}
62
63	err = snd_config_search(inc, "Before", before);
64	if (err < 0 && err != -ENOENT) {
65		uc_error("before block identifier error");
66		return -EINVAL;
67	}
68
69	err = snd_config_search(inc, "After", after);
70	if (err < 0 && err != -ENOENT) {
71		uc_error("before block identifier error");
72		return -EINVAL;
73	}
74
75	err = uc_mgr_get_substituted_value(uc_mgr, &s, file);
76	if (err < 0)
77		return err;
78	err = uc_mgr_config_load_file(uc_mgr, s, result);
79	free(s);
80	return err;
81}
82
83#if 0
84static void config_dump(snd_config_t *cfg)
85{
86	snd_output_t *out;
87	snd_output_stdio_attach(&out, stderr, 0);
88	snd_output_printf(out, "-----\n");
89	snd_config_save(cfg, out);
90	snd_output_close(out);
91}
92#endif
93
94static int find_position_node(snd_use_case_mgr_t *uc_mgr,
95			      snd_config_t **res, snd_config_t *dst,
96			      const char *id, snd_config_t *pos)
97{
98	const char *s;
99	char *s1;
100	int err;
101
102	err = get_string(pos, id, &s);
103	if (err < 0 && err != -ENOENT)
104		return err;
105	if (err == 0) {
106		err = uc_mgr_get_substituted_value(uc_mgr, &s1, s);
107		if (err < 0)
108			return err;
109		err = snd_config_search(dst, s1, res);
110		free(s1);
111		if (err < 0 && err != -ENOENT)
112			return err;
113	}
114	return 0;
115}
116
117static int merge_it(snd_config_t *dst, snd_config_t *n, snd_config_t **_dn)
118{
119	snd_config_t *dn;
120	const char *id;
121	int err;
122
123	err = snd_config_get_id(n, &id);
124	if (err < 0)
125		return err;
126	err = snd_config_search(dst, id, &dn);
127	if (err < 0)
128		return err;
129	err = snd_config_merge(dn, n, 0); /* merge / append mode */
130	if (err < 0)
131		snd_config_delete(n);
132	else
133		*_dn = dn;
134	return err;
135}
136
137static int compound_merge(snd_use_case_mgr_t *uc_mgr, const char *id,
138			  snd_config_t *dst, snd_config_t *src,
139			  snd_config_t *before, snd_config_t *after)
140{
141	snd_config_iterator_t i, next;
142	snd_config_t *n, *_before = NULL, *_after = NULL;
143	char tmpid[32];
144	int err, array, idx;
145
146	if (snd_config_get_type(src) != SND_CONFIG_TYPE_COMPOUND) {
147		uc_error("compound type expected for the merged block");
148		return -EINVAL;
149	}
150
151	if (before) {
152		err = find_position_node(uc_mgr, &_before, dst, id, before);
153		if (err < 0)
154			return err;
155	}
156	if (after) {
157		err = find_position_node(uc_mgr, &_after, dst, id, after);
158		if (err < 0)
159			return err;
160	}
161
162	/* direct merge? */
163	if (!_before && !_after)
164		return snd_config_merge(dst, src, 0);	/* merge / append mode */
165
166	if (_before && _after) {
167		uc_error("defined both before and after identifiers in the If or Include block");
168		return -EINVAL;
169	}
170
171	array = snd_config_is_array(dst);
172	if (array < 0) {
173		uc_error("destination configuration node is not a compound");
174		return array;
175	}
176	if (array && snd_config_is_array(src) <= 0) {
177		uc_error("source configuration node is not an array");
178		return -EINVAL;
179	}
180
181	idx = 0;
182
183	/* for array, use a temporary non-clashing identifier */
184	if (array > 0) {
185		snd_config_for_each(i, next, dst) {
186			n = snd_config_iterator_entry(i);
187			snprintf(tmpid, sizeof(tmpid), "_tmp_%d", idx++);
188			err = snd_config_set_id(n, tmpid);
189			if (err < 0)
190				return err;
191		}
192	}
193
194	snd_config_for_each(i, next, src) {
195		n = snd_config_iterator_entry(i);
196		err = snd_config_remove(n);
197		if (err < 0)
198			return err;
199		/* for array, use a temporary non-clashing identifier */
200		if (array > 0) {
201			snprintf(tmpid, sizeof(tmpid), "_tmp_%d", idx++);
202			err = snd_config_set_id(n, tmpid);
203			if (err < 0)
204				return err;
205		}
206		if (_before) {
207			err = snd_config_add_before(_before, n);
208			if (err == -EEXIST)
209				err = merge_it(dst, n, &n);
210			if (err < 0)
211				return err;
212			_before = NULL;
213			_after = n;
214		} else if (_after) {
215			err = snd_config_add_after(_after, n);
216			if (err == -EEXIST)
217				err = merge_it(dst, n, &n);
218			if (err < 0)
219				return err;
220			_after = n;
221		}
222	}
223
224	/* set new indexes for the final array */
225	if (array > 0) {
226		idx = 0;
227		snd_config_for_each(i, next, dst) {
228			n = snd_config_iterator_entry(i);
229			snprintf(tmpid, sizeof(tmpid), "%d", idx++);
230			err = snd_config_set_id(n, tmpid);
231			if (err < 0)
232				return err;
233		}
234	}
235
236	snd_config_delete(src);
237	return 0;
238}
239
240int uc_mgr_config_tree_merge(snd_use_case_mgr_t *uc_mgr,
241			     snd_config_t *parent, snd_config_t *new_ctx,
242			     snd_config_t *before, snd_config_t *after)
243{
244	snd_config_iterator_t i, next;
245	snd_config_t *n, *parent2;
246	const char *id;
247	int err;
248
249	err = uc_mgr_substitute_tree(uc_mgr, new_ctx);
250	if (err < 0)
251		return err;
252
253	snd_config_for_each(i, next, new_ctx) {
254		n = snd_config_iterator_entry(i);
255		err = snd_config_remove(n);
256		if (err < 0)
257			return err;
258		err = snd_config_get_id(n, &id);
259		if (err < 0) {
260__add:
261			err = snd_config_add(parent, n);
262			if (err < 0)
263				return err;
264		} else {
265			err = snd_config_search(parent, id, &parent2);
266			if (err == -ENOENT)
267				goto __add;
268			err = compound_merge(uc_mgr, id, parent2, n, before, after);
269			if (err < 0) {
270				snd_config_delete(n);
271				return err;
272			}
273		}
274	}
275	return 0;
276}
277
278/*
279 * put back the included configuration to the parent
280 */
281int uc_mgr_evaluate_include(snd_use_case_mgr_t *uc_mgr,
282			      snd_config_t *parent,
283			      snd_config_t *inc)
284{
285	snd_config_iterator_t i, next;
286	snd_config_t *a, *n, *before, *after;
287	int err;
288
289	if (uc_mgr->conf_format < 3) {
290		uc_error("in-place include is supported in v3+ syntax");
291		return -EINVAL;
292	}
293
294	if (snd_config_get_type(inc) != SND_CONFIG_TYPE_COMPOUND) {
295		uc_error("compound type expected for Include");
296		return -EINVAL;
297	}
298
299	snd_config_for_each(i, next, inc) {
300		n = snd_config_iterator_entry(i);
301		before = after = NULL;
302		err = include_eval_one(uc_mgr, n, &a, &before, &after);
303		if (err < 0)
304			return err;
305		if (a == NULL)
306			continue;
307		err = uc_mgr_evaluate_inplace(uc_mgr, a);
308		if (err < 0) {
309			snd_config_delete(a);
310			return err;
311		}
312		err = uc_mgr_config_tree_merge(uc_mgr, parent, a, before, after);
313		snd_config_delete(a);
314		if (err < 0)
315			return err;
316	}
317	return 0;
318}
319