• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
get_string(snd_config_t * compound,const char * key,const char ** str)29 static 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 
include_eval_one(snd_use_case_mgr_t * uc_mgr,snd_config_t * inc,snd_config_t ** result,snd_config_t ** before,snd_config_t ** after)40 static 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
84 static 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 
find_position_node(snd_use_case_mgr_t * uc_mgr,snd_config_t ** res,snd_config_t * dst,const char * id,snd_config_t * pos)94 static 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 
merge_it(snd_config_t * dst,snd_config_t * n,snd_config_t ** _dn)117 static 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 
compound_merge(snd_use_case_mgr_t * uc_mgr,const char * id,snd_config_t * dst,snd_config_t * src,snd_config_t * before,snd_config_t * after)137 static 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 
uc_mgr_config_tree_merge(snd_use_case_mgr_t * uc_mgr,snd_config_t * parent,snd_config_t * new_ctx,snd_config_t * before,snd_config_t * after)240 int 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  */
uc_mgr_evaluate_include(snd_use_case_mgr_t * uc_mgr,snd_config_t * parent,snd_config_t * inc)281 int 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 			return err;
310 		err = uc_mgr_config_tree_merge(uc_mgr, parent, a, before, after);
311 		if (err < 0)
312 			return err;
313 		snd_config_delete(a);
314 
315 	}
316 	return 0;
317 }
318