• 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) 2019 Red Hat Inc.
24  *  Authors: Jaroslav Kysela <perex@perex.cz>
25  */
26 
27 #include "ucm_local.h"
28 #include <stdbool.h>
29 #include <sys/stat.h>
30 #include <limits.h>
31 #include <regex.h>
32 
rval_open_name(snd_use_case_mgr_t * uc_mgr)33 static char *rval_open_name(snd_use_case_mgr_t *uc_mgr)
34 {
35 	const char *name;
36 	if (uc_mgr->conf_format < 3)
37 		return NULL;
38 	name = uc_mgr->card_name;
39 	if (name) {
40 		if (strncmp(name, "strict:", 7) == 0)
41 			name += 7;
42 		return strdup(name);
43 	}
44 	return NULL;
45 }
46 
rval_conf_libdir(snd_use_case_mgr_t * uc_mgr)47 static char *rval_conf_libdir(snd_use_case_mgr_t *uc_mgr)
48 {
49 	if (uc_mgr->conf_format < 4)
50 		return NULL;
51 	return strdup(snd_config_topdir());
52 }
53 
rval_conf_topdir(snd_use_case_mgr_t * uc_mgr)54 static char *rval_conf_topdir(snd_use_case_mgr_t *uc_mgr)
55 {
56 	const char *dir;
57 
58 	if (uc_mgr->conf_format < 3)
59 		return NULL;
60 	dir = uc_mgr_config_dir(uc_mgr->conf_format);
61 	if (dir && dir[0])
62 		return strdup(dir);
63 	return NULL;
64 }
65 
rval_conf_dir(snd_use_case_mgr_t * uc_mgr)66 static char *rval_conf_dir(snd_use_case_mgr_t *uc_mgr)
67 {
68 	if (uc_mgr->conf_format < 3)
69 		return NULL;
70 	if (uc_mgr->conf_dir_name && uc_mgr->conf_dir_name[0])
71 		return strdup(uc_mgr->conf_dir_name);
72 	return NULL;
73 }
74 
rval_conf_name(snd_use_case_mgr_t * uc_mgr)75 static char *rval_conf_name(snd_use_case_mgr_t *uc_mgr)
76 {
77 	if (uc_mgr->conf_file_name && uc_mgr->conf_file_name[0])
78 		return strdup(uc_mgr->conf_file_name);
79 	return NULL;
80 }
81 
get_card_number(struct ctl_list * ctl_list)82 static char *get_card_number(struct ctl_list *ctl_list)
83 {
84 	char num[16];
85 
86 	if (ctl_list == NULL)
87 		return strdup("");
88 	snprintf(num, sizeof(num), "%i", snd_ctl_card_info_get_card(ctl_list->ctl_info));
89 	return strdup(num);
90 }
91 
rval_card_number(snd_use_case_mgr_t * uc_mgr)92 static char *rval_card_number(snd_use_case_mgr_t *uc_mgr)
93 {
94 	if (uc_mgr->conf_format < 3)
95 		return NULL;
96 	return get_card_number(uc_mgr_get_master_ctl(uc_mgr));
97 }
98 
rval_card_id(snd_use_case_mgr_t * uc_mgr)99 static char *rval_card_id(snd_use_case_mgr_t *uc_mgr)
100 {
101 	struct ctl_list *ctl_list;
102 
103 	ctl_list = uc_mgr_get_master_ctl(uc_mgr);
104 	if (ctl_list == NULL)
105 		return NULL;
106 	return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info));
107 }
108 
rval_card_driver(snd_use_case_mgr_t * uc_mgr)109 static char *rval_card_driver(snd_use_case_mgr_t *uc_mgr)
110 {
111 	struct ctl_list *ctl_list;
112 
113 	ctl_list = uc_mgr_get_master_ctl(uc_mgr);
114 	if (ctl_list == NULL)
115 		return NULL;
116 	return strdup(snd_ctl_card_info_get_driver(ctl_list->ctl_info));
117 }
118 
rval_card_name(snd_use_case_mgr_t * uc_mgr)119 static char *rval_card_name(snd_use_case_mgr_t *uc_mgr)
120 {
121 	struct ctl_list *ctl_list;
122 
123 	ctl_list = uc_mgr_get_master_ctl(uc_mgr);
124 	if (ctl_list == NULL)
125 		return NULL;
126 	return strdup(snd_ctl_card_info_get_name(ctl_list->ctl_info));
127 }
128 
rval_card_longname(snd_use_case_mgr_t * uc_mgr)129 static char *rval_card_longname(snd_use_case_mgr_t *uc_mgr)
130 {
131 	struct ctl_list *ctl_list;
132 
133 	ctl_list = uc_mgr_get_master_ctl(uc_mgr);
134 	if (ctl_list == NULL)
135 		return NULL;
136 	return strdup(snd_ctl_card_info_get_longname(ctl_list->ctl_info));
137 }
138 
rval_card_components(snd_use_case_mgr_t * uc_mgr)139 static char *rval_card_components(snd_use_case_mgr_t *uc_mgr)
140 {
141 	struct ctl_list *ctl_list;
142 
143 	ctl_list = uc_mgr_get_master_ctl(uc_mgr);
144 	if (ctl_list == NULL)
145 		return NULL;
146 	return strdup(snd_ctl_card_info_get_components(ctl_list->ctl_info));
147 }
148 
get_ctl_list_by_name(snd_use_case_mgr_t * uc_mgr,const char * id)149 static struct ctl_list *get_ctl_list_by_name(snd_use_case_mgr_t *uc_mgr, const char *id)
150 {
151 	char *name, *index;
152 	long idx = 0;
153 
154 	name = alloca(strlen(id) + 1);
155 	strcpy(name, id);
156 	index = strchr(name, '#');
157 	if (index) {
158 		*index = '\0';
159 		if (safe_strtol(index + 1, &idx))
160 			return NULL;
161 	}
162 	return uc_mgr_get_ctl_by_name(uc_mgr, name, idx);
163 }
164 
rval_card_number_by_name(snd_use_case_mgr_t * uc_mgr,const char * id)165 static char *rval_card_number_by_name(snd_use_case_mgr_t *uc_mgr, const char *id)
166 {
167 	if (uc_mgr->conf_format < 3) {
168 		uc_error("CardNumberByName substitution is supported in v3+ syntax");
169 		return NULL;
170 	}
171 
172 	uc_error("${CardNumberByName} substitution is obsolete - use ${find-card}!");
173 
174 	return get_card_number(get_ctl_list_by_name(uc_mgr, id));
175 }
176 
rval_card_id_by_name(snd_use_case_mgr_t * uc_mgr,const char * id)177 static char *rval_card_id_by_name(snd_use_case_mgr_t *uc_mgr, const char *id)
178 {
179 	struct ctl_list *ctl_list;
180 
181 	if (uc_mgr->conf_format < 3) {
182 		uc_error("CardIdByName substitution is supported in v3+ syntax");
183 		return NULL;
184 	}
185 
186 	uc_error("${CardIdByName} substitution is obsolete - use ${find-card}!");
187 
188 	ctl_list = get_ctl_list_by_name(uc_mgr, id);
189 	if (ctl_list == NULL)
190 		return NULL;
191 	return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info));
192 }
193 
194 typedef struct lookup_iterate *(*lookup_iter_fcn_t)
195 			(snd_use_case_mgr_t *uc_mgr, struct lookup_iterate *iter);
196 typedef const char *(*lookup_fcn_t)(void *);
197 
198 struct lookup_fcn {
199 	char *name;
200 	const char *(*fcn)(void *opaque);
201 };
202 
203 struct lookup_iterate {
204 	int (*init)(snd_use_case_mgr_t *uc_mgr, struct lookup_iterate *iter,
205 		    snd_config_t *config);
206 	void (*done)(struct lookup_iterate *iter);
207 	lookup_iter_fcn_t first;
208 	lookup_iter_fcn_t next;
209 	char *(*retfcn)(struct lookup_iterate *iter, snd_config_t *config);
210 	struct lookup_fcn *fcns;
211 	lookup_fcn_t fcn;
212 	struct ctl_list *ctl_list;
213 	void *info;
214 };
215 
rval_lookup_main(snd_use_case_mgr_t * uc_mgr,const char * query,struct lookup_iterate * iter)216 static char *rval_lookup_main(snd_use_case_mgr_t *uc_mgr,
217 			      const char *query,
218 			      struct lookup_iterate *iter)
219 {
220 	snd_config_t *config, *d;
221 	struct lookup_fcn *fcn;
222 	struct lookup_iterate *curr;
223 	const char *s;
224 	char *result;
225 	regmatch_t match[1];
226 	regex_t re;
227 	int err;
228 
229 	if (uc_mgr->conf_format < 4) {
230 		uc_error("Lookups are supported in v4+ syntax");
231 		return NULL;
232 	}
233 
234 	err = snd_config_load_string(&config, query, 0);
235 	if (err < 0) {
236 		uc_error("The lookup arguments '%s' are invalid", query);
237 		return NULL;
238 	}
239 	if (iter->init && iter->init(uc_mgr, iter, config))
240 		goto null;
241 	if (snd_config_search(config, "field", &d)) {
242 		uc_error("Lookups require field!");
243 		goto null;
244 	}
245 	if (snd_config_get_string(d, &s))
246 		goto null;
247 	for (fcn = iter->fcns ; fcn; fcn++) {
248 		if (strcasecmp(fcn->name, s) == 0) {
249 			iter->fcn = fcn->fcn;
250 			break;
251 		}
252 	}
253 	if (iter->fcn == NULL) {
254 		uc_error("Unknown field value '%s'", s);
255 		goto null;
256 	}
257 	if (snd_config_search(config, "regex", &d)) {
258 		uc_error("Lookups require regex!");
259 		goto null;
260 	}
261 	if (snd_config_get_string(d, &s))
262 		goto null;
263 	err = regcomp(&re, s, REG_EXTENDED | REG_ICASE);
264 	if (err) {
265 		uc_error("Regex '%s' compilation failed (code %d)", s, err);
266 		goto null;
267 	}
268 
269 	result = NULL;
270 	for (curr = iter->first(uc_mgr, iter); curr; curr = iter->next(uc_mgr, iter)) {
271 		s = curr->fcn(iter->info);
272 		if (s == NULL)
273 			continue;
274 		if (regexec(&re, s, ARRAY_SIZE(match), match, 0) == 0) {
275 			result = curr->retfcn(iter, config);
276 			break;
277 		}
278 	}
279 	regfree(&re);
280 fin:
281 	snd_config_delete(config);
282 	if (iter->done)
283 		iter->done(iter);
284 	return result;
285 null:
286 	result = NULL;
287 	goto fin;
288 }
289 
rval_card_lookup1(snd_use_case_mgr_t * uc_mgr,struct lookup_iterate * iter,int card)290 static struct lookup_iterate *rval_card_lookup1(snd_use_case_mgr_t *uc_mgr,
291 						struct lookup_iterate *iter,
292 						int card)
293 {
294 	if (snd_card_next(&card) < 0 || card < 0)
295 		return NULL;
296 	iter->ctl_list = uc_mgr_get_ctl_by_card(uc_mgr, card);
297 	if (iter->ctl_list == NULL)
298 		return NULL;
299 	iter->info = iter->ctl_list->ctl_info;
300 	return iter;
301 }
302 
rval_card_lookup_first(snd_use_case_mgr_t * uc_mgr,struct lookup_iterate * iter)303 static struct lookup_iterate *rval_card_lookup_first(snd_use_case_mgr_t *uc_mgr,
304 						     struct lookup_iterate *iter)
305 {
306 	return rval_card_lookup1(uc_mgr, iter, -1);
307 }
308 
rval_card_lookup_next(snd_use_case_mgr_t * uc_mgr,struct lookup_iterate * iter)309 static struct lookup_iterate *rval_card_lookup_next(snd_use_case_mgr_t *uc_mgr,
310 						    struct lookup_iterate *iter)
311 {
312 	return rval_card_lookup1(uc_mgr, iter, snd_ctl_card_info_get_card(iter->info));
313 }
314 
rval_card_lookup_return(struct lookup_iterate * iter,snd_config_t * config)315 static char *rval_card_lookup_return(struct lookup_iterate *iter, snd_config_t *config)
316 {
317 	snd_config_t *d;
318 	const char *s;
319 
320 	if (snd_config_search(config, "return", &d))
321 		return strdup(snd_ctl_card_info_get_id(iter->info));
322 	else if (snd_config_get_string(d, &s))
323 		return NULL;
324 	else if (strcasecmp(s, "id") == 0)
325 		return strdup(snd_ctl_card_info_get_id(iter->info));
326 	else if (strcasecmp(s, "number") == 0) {
327 		char num[16];
328 		snprintf(num, sizeof(num), "%d", snd_ctl_card_info_get_card(iter->info));
329 		return strdup(num);
330 	} else {
331 		uc_error("Unknown return type '%s'", s);
332 		return NULL;
333 	}
334 }
335 
rval_card_lookup(snd_use_case_mgr_t * uc_mgr,const char * query)336 static char *rval_card_lookup(snd_use_case_mgr_t *uc_mgr, const char *query)
337 {
338 	static struct lookup_fcn fcns[] = {
339 		{ .name = "id", (lookup_fcn_t)snd_ctl_card_info_get_id },
340 		{ .name = "driver", (lookup_fcn_t)snd_ctl_card_info_get_driver },
341 		{ .name = "name", (lookup_fcn_t)snd_ctl_card_info_get_name },
342 		{ .name = "longname", (lookup_fcn_t)snd_ctl_card_info_get_longname },
343 		{ .name = "mixername", (lookup_fcn_t)snd_ctl_card_info_get_mixername },
344 		{ .name = "components", (lookup_fcn_t)snd_ctl_card_info_get_components },
345 		{ 0 },
346 	};
347 	struct lookup_iterate iter = {
348 		.first = rval_card_lookup_first,
349 		.next = rval_card_lookup_next,
350 		.retfcn = rval_card_lookup_return,
351 		.fcns = fcns,
352 	};
353 	return rval_lookup_main(uc_mgr, query, &iter);
354 }
355 
rval_pcm_lookup1(struct lookup_iterate * iter,int device)356 static struct lookup_iterate *rval_pcm_lookup1(struct lookup_iterate *iter,
357 					       int device)
358 {
359 	snd_pcm_info_t *pcminfo;
360 	snd_ctl_t *ctl = iter->ctl_list->ctl;
361 	int err;
362 
363 next:
364 	if (snd_ctl_pcm_next_device(ctl, &device) < 0 || device < 0)
365 		return NULL;
366 	pcminfo = iter->info;
367 	snd_pcm_info_set_device(pcminfo, device);
368 	err = snd_ctl_pcm_info(ctl, pcminfo);
369 	if (err < 0) {
370 		if (err == -ENOENT)
371 			goto next;
372 		uc_error("Unable to obtain PCM info (device %d)", device);
373 		return NULL;
374 	}
375 	return iter;
376 }
377 
rval_pcm_lookup_first(snd_use_case_mgr_t * uc_mgr ATTRIBUTE_UNUSED,struct lookup_iterate * iter)378 static struct lookup_iterate *rval_pcm_lookup_first(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
379 						    struct lookup_iterate *iter)
380 {
381 	return rval_pcm_lookup1(iter, -1);
382 }
383 
rval_pcm_lookup_next(snd_use_case_mgr_t * uc_mgr ATTRIBUTE_UNUSED,struct lookup_iterate * iter)384 static struct lookup_iterate *rval_pcm_lookup_next(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
385 						   struct lookup_iterate *iter)
386 {
387 	return rval_pcm_lookup1(iter, snd_pcm_info_get_device(iter->info));
388 }
389 
rval_pcm_lookup_return(struct lookup_iterate * iter,snd_config_t * config ATTRIBUTE_UNUSED)390 static char *rval_pcm_lookup_return(struct lookup_iterate *iter,
391 				    snd_config_t *config ATTRIBUTE_UNUSED)
392 {
393 	char num[16];
394 	snprintf(num, sizeof(num), "%d", snd_pcm_info_get_device(iter->info));
395 	return strdup(num);
396 }
397 
rval_pcm_lookup_init(struct lookup_iterate * iter,snd_config_t * config)398 static int rval_pcm_lookup_init(struct lookup_iterate *iter,
399 				snd_config_t *config)
400 {
401 	static struct lookup_fcn pcm_fcns[] = {
402 		{ .name = "id", (lookup_fcn_t)snd_pcm_info_get_id },
403 		{ .name = "name", (lookup_fcn_t)snd_pcm_info_get_name },
404 		{ .name = "subname", (lookup_fcn_t)snd_pcm_info_get_subdevice_name },
405 		{ 0 },
406 	};
407 	snd_config_t *d;
408 	const char *s;
409 	snd_pcm_info_t *pcminfo;
410 	snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
411 
412 	if (snd_config_search(config, "stream", &d) == 0 &&
413 	    snd_config_get_string(d, &s) == 0) {
414 		if (strcasecmp(s, "playback") == 0)
415 			stream = SND_PCM_STREAM_PLAYBACK;
416 		else if (strcasecmp(s, "capture") == 0)
417 			stream = SND_PCM_STREAM_CAPTURE;
418 		else {
419 			uc_error("Unknown stream type '%s'", s);
420 			return -EINVAL;
421 		}
422 	}
423 	if (snd_pcm_info_malloc(&pcminfo))
424 		return -ENOMEM;
425 	snd_pcm_info_set_device(pcminfo, 0);
426 	snd_pcm_info_set_subdevice(pcminfo, 0);
427 	snd_pcm_info_set_stream(pcminfo, stream);
428 	iter->first = rval_pcm_lookup_first;
429 	iter->next = rval_pcm_lookup_next;
430 	iter->retfcn = rval_pcm_lookup_return;
431 	iter->fcns = pcm_fcns;
432 	iter->info = pcminfo;
433 	return 0;
434 }
435 
rval_device_lookup_init(snd_use_case_mgr_t * uc_mgr,struct lookup_iterate * iter,snd_config_t * config)436 static int rval_device_lookup_init(snd_use_case_mgr_t *uc_mgr,
437 				   struct lookup_iterate *iter,
438 				   snd_config_t *config)
439 {
440 	static struct {
441 		const char *name;
442 		int (*init)(struct lookup_iterate *iter, snd_config_t *config);
443 	} *t, types[] = {
444 		{ .name = "pcm", .init = rval_pcm_lookup_init },
445 		{ 0 }
446 	};
447 	snd_config_t *d;
448 	const char *s;
449 	int err;
450 
451 	if (snd_config_search(config, "ctl", &d) || snd_config_get_string(d, &s)) {
452 		iter->ctl_list = uc_mgr_get_master_ctl(uc_mgr);
453 		if (iter->ctl_list == NULL) {
454 			uc_error("Control device is not defined!");
455 			return -EINVAL;
456 		}
457 	} else {
458 		err = uc_mgr_open_ctl(uc_mgr, &iter->ctl_list, s, 1);
459 		if (err < 0) {
460 			uc_error("Control device '%s' not found", s);
461 			return -EINVAL;
462 		}
463 	}
464 	if (snd_config_search(config, "type", &d) || snd_config_get_string(d, &s)) {
465 		uc_error("Missing device type!");
466 		return -EINVAL;
467 	}
468 	for (t = types; t->name; t++)
469 		if (strcasecmp(t->name, s) == 0)
470 			return t->init(iter, config);
471 	uc_error("Device type '%s' is invalid", s);
472 	return -EINVAL;
473 }
474 
rval_device_lookup_done(struct lookup_iterate * iter)475 static void rval_device_lookup_done(struct lookup_iterate *iter)
476 {
477 	free(iter->info);
478 }
479 
rval_device_lookup(snd_use_case_mgr_t * uc_mgr,const char * query)480 static char *rval_device_lookup(snd_use_case_mgr_t *uc_mgr, const char *query)
481 {
482 	struct lookup_iterate iter = {
483 		.init = rval_device_lookup_init,
484 		.done = rval_device_lookup_done,
485 	};
486 	return rval_lookup_main(uc_mgr, query, &iter);
487 }
488 
rval_env(snd_use_case_mgr_t * uc_mgr ATTRIBUTE_UNUSED,const char * id)489 static char *rval_env(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id)
490 {
491 	char *e;
492 
493 	e = getenv(id);
494 	if (e)
495 		return strdup(e);
496 	return NULL;
497 }
498 
rval_sysfs(snd_use_case_mgr_t * uc_mgr ATTRIBUTE_UNUSED,const char * id)499 static char *rval_sysfs(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id)
500 {
501 	char path[PATH_MAX], link[PATH_MAX + 1];
502 	struct stat sb;
503 	ssize_t len;
504 	const char *e;
505 	int fd;
506 
507 	e = uc_mgr_sysfs_root();
508 	if (e == NULL)
509 		return NULL;
510 	if (id[0] == '/')
511 		id++;
512 	snprintf(path, sizeof(path), "%s/%s", e, id);
513 	if (lstat(path, &sb) != 0)
514 		return NULL;
515 	if (S_ISLNK(sb.st_mode)) {
516 		len = readlink(path, link, sizeof(link) - 1);
517 		if (len <= 0) {
518 			uc_error("sysfs: cannot read link '%s' (%d)", path, errno);
519 			return NULL;
520 		}
521 		link[len] = '\0';
522 		e = strrchr(link, '/');
523 		if (e)
524 			return strdup(e + 1);
525 		return NULL;
526 	}
527 	if (S_ISDIR(sb.st_mode))
528 		return NULL;
529 	if ((sb.st_mode & S_IRUSR) == 0)
530 		return NULL;
531 
532 	fd = open(path, O_RDONLY);
533 	if (fd < 0) {
534 		uc_error("sysfs open failed for '%s' (%d)", path, errno);
535 		return NULL;
536 	}
537 	len = read(fd, path, sizeof(path)-1);
538 	close(fd);
539 	if (len < 0) {
540 		uc_error("sysfs unable to read value '%s' (%d)", path, errno);
541 		return NULL;
542 	}
543 	while (len > 0 && path[len-1] == '\n')
544 		len--;
545 	path[len] = '\0';
546 	return strdup(path);
547 }
548 
rval_var(snd_use_case_mgr_t * uc_mgr,const char * id)549 static char *rval_var(snd_use_case_mgr_t *uc_mgr, const char *id)
550 {
551 	const char *v;
552 
553 	if (uc_mgr->conf_format < 3) {
554 		uc_error("variable substitution is supported in v3+ syntax");
555 		return NULL;
556 	}
557 
558 	v = uc_mgr_get_variable(uc_mgr, id);
559 	if (v)
560 		return strdup(v);
561 	return NULL;
562 }
563 
564 int _snd_eval_string(snd_config_t **dst, const char *s,
565 		     snd_config_expand_fcn_t fcn, void *private_data);
566 
rval_eval_var_cb(snd_config_t ** dst,const char * s,void * private_data)567 static int rval_eval_var_cb(snd_config_t **dst, const char *s, void *private_data)
568 {
569 	snd_use_case_mgr_t *uc_mgr = private_data;
570 	const char *v;
571 
572 	v = uc_mgr_get_variable(uc_mgr, s);
573 	if (v == NULL)
574 		return -ENOENT;
575 	return snd_config_imake_string(dst, NULL, v);
576 }
577 
rval_eval(snd_use_case_mgr_t * uc_mgr,const char * e)578 static char *rval_eval(snd_use_case_mgr_t *uc_mgr, const char *e)
579 {
580 	snd_config_t *dst;
581 	char *r;
582 	int err;
583 
584 	if (uc_mgr->conf_format < 5) {
585 		uc_error("variable substitution is supported in v5+ syntax");
586 		return NULL;
587 	}
588 	err = _snd_eval_string(&dst, e, rval_eval_var_cb, uc_mgr);
589 	if (err < 0) {
590 		uc_error("unable to evaluate '%s'", e);
591 		return NULL;
592 	}
593 	err = snd_config_get_ascii(dst, &r);
594 	snd_config_delete(dst);
595 	if (err < 0)
596 		return NULL;
597 	return r;
598 }
599 
600 #define MATCH_VARIABLE(name, id, fcn, empty_ok)				\
601 	if (strncmp((name), (id), sizeof(id) - 1) == 0) { 		\
602 		rval = fcn(uc_mgr);					\
603 		idsize = sizeof(id) - 1;				\
604 		allow_empty = (empty_ok);				\
605 		goto __rval;						\
606 	}
607 
608 #define MATCH_VARIABLE2(name, id, fcn, empty_ok)			\
609 	if (strncmp((name), (id), sizeof(id) - 1) == 0) {		\
610 		idsize = sizeof(id) - 1;				\
611 		allow_empty = (empty_ok);				\
612 		fcn2 = (fcn);						\
613 		goto __match2;						\
614 	}
615 
616 /*
617  * skip escaped } character (simple version)
618  */
strchr_with_escape(const char * str,char c)619 static inline const char *strchr_with_escape(const char *str, char c)
620 {
621 	char *s;
622 
623 	while (1) {
624 		s = strchr(str, c);
625 		if (s && s != str) {
626 			if (*(s - 1) == '\\') {
627 				str = s + 1;
628 				continue;
629 			}
630 		}
631 		return s;
632 	}
633 }
634 
635 /*
636  * remove escaped } character (simple version)
637  */
strncpy_with_escape(char * dst,const char * src,size_t len)638 static inline void strncpy_with_escape(char *dst, const char *src, size_t len)
639 {
640 	char c;
641 
642 	c = *src++;
643 	while (c != '\0' && len > 0) {
644 		if (c == '\\' && *src == '}') {
645 			c = *src++;
646 			len--;
647 		}
648 		*dst++ = c;
649 		len--;
650 		c = *src++;
651 	}
652 	*dst = '\0';
653 }
654 
uc_mgr_get_substituted_value(snd_use_case_mgr_t * uc_mgr,char ** _rvalue,const char * value)655 int uc_mgr_get_substituted_value(snd_use_case_mgr_t *uc_mgr,
656 				 char **_rvalue,
657 				 const char *value)
658 {
659 	size_t size, nsize, idsize, rvalsize, dpos = 0;
660 	const char *tmp;
661 	char *r, *nr, *rval, v2[128];
662 	bool ignore_error, allow_empty;
663 	char *(*fcn2)(snd_use_case_mgr_t *, const char *id);
664 	int err;
665 
666 	if (value == NULL)
667 		return -ENOENT;
668 
669 	size = strlen(value) + 1;
670 	r = malloc(size);
671 	if (r == NULL)
672 		return -ENOMEM;
673 
674 	while (*value) {
675 		if (*value != '$') {
676 __std:
677 			r[dpos++] = *value;
678 			value++;
679 			continue;
680 		}
681 		ignore_error = false;
682 		if (value[1] == '$' && value[2] == '{' && uc_mgr->conf_format >= 3) {
683 			value++;
684 			ignore_error = true;
685 		} else if (value[1] != '{') {
686 			goto __std;
687 		}
688 		fcn2 = NULL;
689 		MATCH_VARIABLE(value, "${OpenName}", rval_open_name, false);
690 		MATCH_VARIABLE(value, "${ConfLibDir}", rval_conf_libdir, false);
691 		MATCH_VARIABLE(value, "${ConfTopDir}", rval_conf_topdir, false);
692 		MATCH_VARIABLE(value, "${ConfDir}", rval_conf_dir, false);
693 		MATCH_VARIABLE(value, "${ConfName}", rval_conf_name, false);
694 		MATCH_VARIABLE(value, "${CardNumber}", rval_card_number, true);
695 		MATCH_VARIABLE(value, "${CardId}", rval_card_id, false);
696 		MATCH_VARIABLE(value, "${CardDriver}", rval_card_driver, false);
697 		MATCH_VARIABLE(value, "${CardName}", rval_card_name, false);
698 		MATCH_VARIABLE(value, "${CardLongName}", rval_card_longname, false);
699 		MATCH_VARIABLE(value, "${CardComponents}", rval_card_components, true);
700 		MATCH_VARIABLE2(value, "${env:", rval_env, false);
701 		MATCH_VARIABLE2(value, "${sys:", rval_sysfs, false);
702 		MATCH_VARIABLE2(value, "${var:", rval_var, true);
703 		MATCH_VARIABLE2(value, "${eval:", rval_eval, false);
704 		MATCH_VARIABLE2(value, "${find-card:", rval_card_lookup, false);
705 		MATCH_VARIABLE2(value, "${find-device:", rval_device_lookup, false);
706 		MATCH_VARIABLE2(value, "${CardNumberByName:", rval_card_number_by_name, false);
707 		MATCH_VARIABLE2(value, "${CardIdByName:", rval_card_id_by_name, false);
708 __merr:
709 		err = -EINVAL;
710 		tmp = strchr(value, '}');
711 		if (tmp) {
712 			strncpy(r, value, tmp + 1 - value);
713 			r[tmp + 1 - value] = '\0';
714 			uc_error("variable '%s' is not known!", r);
715 		} else {
716 			uc_error("variable reference '%s' is not complete", value);
717 		}
718 		goto __error;
719 __match2:
720 		tmp = strchr_with_escape(value + idsize, '}');
721 		if (tmp) {
722 			rvalsize = tmp - (value + idsize);
723 			if (rvalsize >= sizeof(v2)) {
724 				err = -ENOMEM;
725 				goto __error;
726 			}
727 			strncpy_with_escape(v2, value + idsize, rvalsize);
728 			idsize += rvalsize + 1;
729 			if (*v2 == '$' && uc_mgr->conf_format >= 3) {
730 				tmp = uc_mgr_get_variable(uc_mgr, v2 + 1);
731 				if (tmp == NULL) {
732 					uc_error("define '%s' is not reachable in this context!", v2 + 1);
733 					rval = NULL;
734 				} else {
735 					rval = fcn2(uc_mgr, tmp);
736 				}
737 			} else {
738 				rval = fcn2(uc_mgr, v2);
739 			}
740 			goto __rval;
741 		}
742 		goto __merr;
743 __rval:
744 		if (rval == NULL || (!allow_empty && rval[0] == '\0')) {
745 			free(rval);
746 			if (ignore_error) {
747 				value += idsize;
748 				continue;
749 			}
750 			strncpy(r, value, idsize);
751 			r[idsize] = '\0';
752 			uc_error("variable '%s' is %s in this context!", r,
753 				 rval ? "empty" : "not defined");
754 			err = -EINVAL;
755 			goto __error;
756 		}
757 		value += idsize;
758 		rvalsize = strlen(rval);
759 		nsize = size + rvalsize - idsize;
760 		if (nsize > size) {
761 			nr = realloc(r, nsize);
762 			if (nr == NULL) {
763 				free(rval);
764 				err = -ENOMEM;
765 				goto __error;
766 			}
767 			size = nsize;
768 			r = nr;
769 		}
770 		strcpy(r + dpos, rval);
771 		dpos += rvalsize;
772 		free(rval);
773 	}
774 	r[dpos] = '\0';
775 
776 	*_rvalue = r;
777 	return 0;
778 
779 __error:
780 	free(r);
781 	return err;
782 }
783 
uc_mgr_substitute_check(const char * s)784 static inline int uc_mgr_substitute_check(const char *s)
785 {
786 	return s && strstr(s, "${") != NULL;
787 }
788 
uc_mgr_substitute_tree(snd_use_case_mgr_t * uc_mgr,snd_config_t * node)789 int uc_mgr_substitute_tree(snd_use_case_mgr_t *uc_mgr, snd_config_t *node)
790 {
791 	snd_config_iterator_t i, next;
792 	snd_config_t *n;
793 	const char *id, *s2;
794 	char *s;
795 	int err;
796 
797 	err = snd_config_get_id(node, &id);
798 	if (err < 0)
799 		return err;
800 	if (uc_mgr_substitute_check(id)) {
801 		err = uc_mgr_get_substituted_value(uc_mgr, &s, id);
802 		if (err < 0)
803 			return err;
804 		err = snd_config_set_id(node, s);
805 		if (err < 0) {
806 			uc_error("unable to set substituted id '%s' (old id '%s')", s, id);
807 			free(s);
808 			return err;
809 		}
810 		free(s);
811 	}
812 	if (snd_config_get_type(node) != SND_CONFIG_TYPE_COMPOUND) {
813 		if (snd_config_get_type(node) == SND_CONFIG_TYPE_STRING) {
814 			err = snd_config_get_string(node, &s2);
815 			if (err < 0)
816 				return err;
817 			if (!uc_mgr_substitute_check(s2))
818 				return 0;
819 			err = uc_mgr_get_substituted_value(uc_mgr, &s, s2);
820 			if (err < 0)
821 				return err;
822 			err = snd_config_set_string(node, s);
823 			free(s);
824 			if (err < 0)
825 				return err;
826 		}
827 		return 0;
828 	}
829 	snd_config_for_each(i, next, node) {
830 		n = snd_config_iterator_entry(i);
831 		err = uc_mgr_substitute_tree(uc_mgr, n);
832 		if (err < 0)
833 			return err;
834 	}
835 	return 0;
836 }
837