• 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 #ifndef DOC_HIDDEN
195 typedef struct lookup_iterate *(*lookup_iter_fcn_t)
196 			(snd_use_case_mgr_t *uc_mgr, struct lookup_iterate *iter);
197 typedef const char *(*lookup_fcn_t)(void *);
198 
199 struct lookup_fcn {
200 	char *name;
201 	const char *(*fcn)(void *opaque);
202 };
203 
204 struct lookup_iterate {
205 	int (*init)(snd_use_case_mgr_t *uc_mgr, struct lookup_iterate *iter,
206 		    snd_config_t *config);
207 	void (*done)(struct lookup_iterate *iter);
208 	lookup_iter_fcn_t first;
209 	lookup_iter_fcn_t next;
210 	char *(*retfcn)(struct lookup_iterate *iter, snd_config_t *config);
211 	struct lookup_fcn *fcns;
212 	lookup_fcn_t fcn;
213 	struct ctl_list *ctl_list;
214 	void *info;
215 };
216 #endif /* DOC_HIDDEN */
217 
rval_lookup_main(snd_use_case_mgr_t * uc_mgr,const char * query,struct lookup_iterate * iter)218 static char *rval_lookup_main(snd_use_case_mgr_t *uc_mgr,
219 			      const char *query,
220 			      struct lookup_iterate *iter)
221 {
222 	snd_config_t *config, *d;
223 	struct lookup_fcn *fcn;
224 	struct lookup_iterate *curr;
225 	const char *s;
226 	char *result;
227 	regmatch_t match[1];
228 	regex_t re;
229 	int err;
230 
231 	if (uc_mgr->conf_format < 4) {
232 		uc_error("Lookups are supported in v4+ syntax");
233 		return NULL;
234 	}
235 
236 	err = snd_config_load_string(&config, query, 0);
237 	if (err < 0) {
238 		uc_error("The lookup arguments '%s' are invalid", query);
239 		return NULL;
240 	}
241 	if (iter->init && iter->init(uc_mgr, iter, config))
242 		goto null;
243 	if (snd_config_search(config, "field", &d)) {
244 		uc_error("Lookups require field!");
245 		goto null;
246 	}
247 	if (snd_config_get_string(d, &s))
248 		goto null;
249 	for (fcn = iter->fcns ; fcn; fcn++) {
250 		if (strcasecmp(fcn->name, s) == 0) {
251 			iter->fcn = fcn->fcn;
252 			break;
253 		}
254 	}
255 	if (iter->fcn == NULL) {
256 		uc_error("Unknown field value '%s'", s);
257 		goto null;
258 	}
259 	if (snd_config_search(config, "regex", &d)) {
260 		uc_error("Lookups require regex!");
261 		goto null;
262 	}
263 	if (snd_config_get_string(d, &s))
264 		goto null;
265 	err = regcomp(&re, s, REG_EXTENDED | REG_ICASE);
266 	if (err) {
267 		uc_error("Regex '%s' compilation failed (code %d)", s, err);
268 		goto null;
269 	}
270 
271 	result = NULL;
272 	for (curr = iter->first(uc_mgr, iter); curr; curr = iter->next(uc_mgr, iter)) {
273 		s = curr->fcn(iter->info);
274 		if (s == NULL)
275 			continue;
276 		if (regexec(&re, s, ARRAY_SIZE(match), match, 0) == 0) {
277 			result = curr->retfcn(iter, config);
278 			break;
279 		}
280 	}
281 	regfree(&re);
282 fin:
283 	snd_config_delete(config);
284 	if (iter->done)
285 		iter->done(iter);
286 	return result;
287 null:
288 	result = NULL;
289 	goto fin;
290 }
291 
rval_card_lookup1(snd_use_case_mgr_t * uc_mgr,struct lookup_iterate * iter,int card)292 static struct lookup_iterate *rval_card_lookup1(snd_use_case_mgr_t *uc_mgr,
293 						struct lookup_iterate *iter,
294 						int card)
295 {
296 	if (snd_card_next(&card) < 0 || card < 0)
297 		return NULL;
298 	iter->ctl_list = uc_mgr_get_ctl_by_card(uc_mgr, card);
299 	if (iter->ctl_list == NULL)
300 		return NULL;
301 	iter->info = iter->ctl_list->ctl_info;
302 	return iter;
303 }
304 
rval_card_lookup_first(snd_use_case_mgr_t * uc_mgr,struct lookup_iterate * iter)305 static struct lookup_iterate *rval_card_lookup_first(snd_use_case_mgr_t *uc_mgr,
306 						     struct lookup_iterate *iter)
307 {
308 	return rval_card_lookup1(uc_mgr, iter, -1);
309 }
310 
rval_card_lookup_next(snd_use_case_mgr_t * uc_mgr,struct lookup_iterate * iter)311 static struct lookup_iterate *rval_card_lookup_next(snd_use_case_mgr_t *uc_mgr,
312 						    struct lookup_iterate *iter)
313 {
314 	return rval_card_lookup1(uc_mgr, iter, snd_ctl_card_info_get_card(iter->info));
315 }
316 
rval_card_lookup_return(struct lookup_iterate * iter,snd_config_t * config)317 static char *rval_card_lookup_return(struct lookup_iterate *iter, snd_config_t *config)
318 {
319 	snd_config_t *d;
320 	const char *s;
321 
322 	if (snd_config_search(config, "return", &d))
323 		return strdup(snd_ctl_card_info_get_id(iter->info));
324 	else if (snd_config_get_string(d, &s))
325 		return NULL;
326 	else if (strcasecmp(s, "id") == 0)
327 		return strdup(snd_ctl_card_info_get_id(iter->info));
328 	else if (strcasecmp(s, "number") == 0) {
329 		char num[16];
330 		snprintf(num, sizeof(num), "%d", snd_ctl_card_info_get_card(iter->info));
331 		return strdup(num);
332 	} else {
333 		uc_error("Unknown return type '%s'", s);
334 		return NULL;
335 	}
336 }
337 
rval_card_lookup(snd_use_case_mgr_t * uc_mgr,const char * query)338 static char *rval_card_lookup(snd_use_case_mgr_t *uc_mgr, const char *query)
339 {
340 	static struct lookup_fcn fcns[] = {
341 		{ .name = "id", (lookup_fcn_t)snd_ctl_card_info_get_id },
342 		{ .name = "driver", (lookup_fcn_t)snd_ctl_card_info_get_driver },
343 		{ .name = "name", (lookup_fcn_t)snd_ctl_card_info_get_name },
344 		{ .name = "longname", (lookup_fcn_t)snd_ctl_card_info_get_longname },
345 		{ .name = "mixername", (lookup_fcn_t)snd_ctl_card_info_get_mixername },
346 		{ .name = "components", (lookup_fcn_t)snd_ctl_card_info_get_components },
347 		{ 0 },
348 	};
349 	struct lookup_iterate iter = {
350 		.first = rval_card_lookup_first,
351 		.next = rval_card_lookup_next,
352 		.retfcn = rval_card_lookup_return,
353 		.fcns = fcns,
354 	};
355 	return rval_lookup_main(uc_mgr, query, &iter);
356 }
357 
rval_pcm_lookup1(struct lookup_iterate * iter,int device)358 static struct lookup_iterate *rval_pcm_lookup1(struct lookup_iterate *iter,
359 					       int device)
360 {
361 	snd_pcm_info_t *pcminfo;
362 	snd_ctl_t *ctl = iter->ctl_list->ctl;
363 	int err;
364 
365 next:
366 	if (snd_ctl_pcm_next_device(ctl, &device) < 0 || device < 0)
367 		return NULL;
368 	pcminfo = iter->info;
369 	snd_pcm_info_set_device(pcminfo, device);
370 	err = snd_ctl_pcm_info(ctl, pcminfo);
371 	if (err < 0) {
372 		if (err == -ENOENT)
373 			goto next;
374 		uc_error("Unable to obtain PCM info (device %d)", device);
375 		return NULL;
376 	}
377 	return iter;
378 }
379 
rval_pcm_lookup_first(snd_use_case_mgr_t * uc_mgr ATTRIBUTE_UNUSED,struct lookup_iterate * iter)380 static struct lookup_iterate *rval_pcm_lookup_first(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
381 						    struct lookup_iterate *iter)
382 {
383 	return rval_pcm_lookup1(iter, -1);
384 }
385 
rval_pcm_lookup_next(snd_use_case_mgr_t * uc_mgr ATTRIBUTE_UNUSED,struct lookup_iterate * iter)386 static struct lookup_iterate *rval_pcm_lookup_next(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
387 						   struct lookup_iterate *iter)
388 {
389 	return rval_pcm_lookup1(iter, snd_pcm_info_get_device(iter->info));
390 }
391 
rval_pcm_lookup_return(struct lookup_iterate * iter,snd_config_t * config ATTRIBUTE_UNUSED)392 static char *rval_pcm_lookup_return(struct lookup_iterate *iter,
393 				    snd_config_t *config ATTRIBUTE_UNUSED)
394 {
395 	char num[16];
396 	snprintf(num, sizeof(num), "%d", snd_pcm_info_get_device(iter->info));
397 	return strdup(num);
398 }
399 
rval_pcm_lookup_init(struct lookup_iterate * iter,snd_config_t * config)400 static int rval_pcm_lookup_init(struct lookup_iterate *iter,
401 				snd_config_t *config)
402 {
403 	static struct lookup_fcn pcm_fcns[] = {
404 		{ .name = "id", (lookup_fcn_t)snd_pcm_info_get_id },
405 		{ .name = "name", (lookup_fcn_t)snd_pcm_info_get_name },
406 		{ .name = "subname", (lookup_fcn_t)snd_pcm_info_get_subdevice_name },
407 		{ 0 },
408 	};
409 	snd_config_t *d;
410 	const char *s;
411 	snd_pcm_info_t *pcminfo;
412 	snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
413 
414 	if (snd_config_search(config, "stream", &d) == 0 &&
415 	    snd_config_get_string(d, &s) == 0) {
416 		if (strcasecmp(s, "playback") == 0)
417 			stream = SND_PCM_STREAM_PLAYBACK;
418 		else if (strcasecmp(s, "capture") == 0)
419 			stream = SND_PCM_STREAM_CAPTURE;
420 		else {
421 			uc_error("Unknown stream type '%s'", s);
422 			return -EINVAL;
423 		}
424 	}
425 	if (snd_pcm_info_malloc(&pcminfo))
426 		return -ENOMEM;
427 	snd_pcm_info_set_device(pcminfo, 0);
428 	snd_pcm_info_set_subdevice(pcminfo, 0);
429 	snd_pcm_info_set_stream(pcminfo, stream);
430 	iter->first = rval_pcm_lookup_first;
431 	iter->next = rval_pcm_lookup_next;
432 	iter->retfcn = rval_pcm_lookup_return;
433 	iter->fcns = pcm_fcns;
434 	iter->info = pcminfo;
435 	return 0;
436 }
437 
rval_device_lookup_init(snd_use_case_mgr_t * uc_mgr,struct lookup_iterate * iter,snd_config_t * config)438 static int rval_device_lookup_init(snd_use_case_mgr_t *uc_mgr,
439 				   struct lookup_iterate *iter,
440 				   snd_config_t *config)
441 {
442 	static struct {
443 		const char *name;
444 		int (*init)(struct lookup_iterate *iter, snd_config_t *config);
445 	} *t, types[] = {
446 		{ .name = "pcm", .init = rval_pcm_lookup_init },
447 		{ 0 }
448 	};
449 	snd_config_t *d;
450 	const char *s;
451 	int err;
452 
453 	if (snd_config_search(config, "ctl", &d) || snd_config_get_string(d, &s)) {
454 		iter->ctl_list = uc_mgr_get_master_ctl(uc_mgr);
455 		if (iter->ctl_list == NULL) {
456 			uc_error("Control device is not defined!");
457 			return -EINVAL;
458 		}
459 	} else {
460 		err = uc_mgr_open_ctl(uc_mgr, &iter->ctl_list, s, 1);
461 		if (err < 0) {
462 			uc_error("Control device '%s' not found", s);
463 			return -EINVAL;
464 		}
465 	}
466 	if (snd_config_search(config, "type", &d) || snd_config_get_string(d, &s)) {
467 		uc_error("Missing device type!");
468 		return -EINVAL;
469 	}
470 	for (t = types; t->name; t++)
471 		if (strcasecmp(t->name, s) == 0)
472 			return t->init(iter, config);
473 	uc_error("Device type '%s' is invalid", s);
474 	return -EINVAL;
475 }
476 
rval_device_lookup_done(struct lookup_iterate * iter)477 static void rval_device_lookup_done(struct lookup_iterate *iter)
478 {
479 	free(iter->info);
480 }
481 
rval_device_lookup(snd_use_case_mgr_t * uc_mgr,const char * query)482 static char *rval_device_lookup(snd_use_case_mgr_t *uc_mgr, const char *query)
483 {
484 	struct lookup_iterate iter = {
485 		.init = rval_device_lookup_init,
486 		.done = rval_device_lookup_done,
487 	};
488 	return rval_lookup_main(uc_mgr, query, &iter);
489 }
490 
rval_env(snd_use_case_mgr_t * uc_mgr ATTRIBUTE_UNUSED,const char * id)491 static char *rval_env(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id)
492 {
493 	char *e;
494 
495 	if (*id == '-') {
496 		e = getenv(id + 1);
497 		if (e == NULL)
498 			e = "";
499 	} else {
500 		e = getenv(id);
501 	}
502 	if (e)
503 		return strdup(e);
504 	return NULL;
505 }
506 
rval_sysfs(snd_use_case_mgr_t * uc_mgr ATTRIBUTE_UNUSED,const char * id)507 static char *rval_sysfs(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id)
508 {
509 	char path[PATH_MAX], link[PATH_MAX + 1];
510 	struct stat64 sb;
511 	ssize_t len;
512 	const char *e;
513 	int fd;
514 
515 	e = uc_mgr_sysfs_root();
516 	if (e == NULL)
517 		return NULL;
518 	if (id[0] == '/')
519 		id++;
520 	snprintf(path, sizeof(path), "%s/%s", e, id);
521 	if (lstat64(path, &sb) != 0)
522 		return NULL;
523 	if (S_ISLNK(sb.st_mode)) {
524 		len = readlink(path, link, sizeof(link) - 1);
525 		if (len <= 0) {
526 			uc_error("sysfs: cannot read link '%s' (%d)", path, errno);
527 			return NULL;
528 		}
529 		link[len] = '\0';
530 		e = strrchr(link, '/');
531 		if (e)
532 			return strdup(e + 1);
533 		return NULL;
534 	}
535 	if (S_ISDIR(sb.st_mode))
536 		return NULL;
537 	if ((sb.st_mode & S_IRUSR) == 0)
538 		return NULL;
539 
540 	fd = open(path, O_RDONLY);
541 	if (fd < 0) {
542 		uc_error("sysfs open failed for '%s' (%d)", path, errno);
543 		return NULL;
544 	}
545 	len = read(fd, path, sizeof(path)-1);
546 	close(fd);
547 	if (len < 0) {
548 		uc_error("sysfs unable to read value '%s' (%d)", path, errno);
549 		return NULL;
550 	}
551 	while (len > 0 && path[len-1] == '\n')
552 		len--;
553 	path[len] = '\0';
554 	return strdup(path);
555 }
556 
rval_var(snd_use_case_mgr_t * uc_mgr,const char * id)557 static char *rval_var(snd_use_case_mgr_t *uc_mgr, const char *id)
558 {
559 	const char *v;
560 	bool ignore_not_found = false;
561 
562 	if (uc_mgr->conf_format < 3) {
563 		uc_error("variable substitution is supported in v3+ syntax");
564 		return NULL;
565 	}
566 
567 	if (id[0] == '-') {
568 		ignore_not_found = true;
569 		id++;
570 	} else if (id[0] == '@') {
571 		ignore_not_found = true;
572 	}
573 	v = uc_mgr_get_variable(uc_mgr, id);
574 	if (v == NULL && ignore_not_found)
575 		v = "";
576 	if (v)
577 		return strdup(v);
578 	return NULL;
579 }
580 
581 int _snd_eval_string(snd_config_t **dst, const char *s,
582 		     snd_config_expand_fcn_t fcn, void *private_data);
583 
rval_eval_var_cb(snd_config_t ** dst,const char * s,void * private_data)584 static int rval_eval_var_cb(snd_config_t **dst, const char *s, void *private_data)
585 {
586 	snd_use_case_mgr_t *uc_mgr = private_data;
587 	const char *v;
588 
589 	v = uc_mgr_get_variable(uc_mgr, s);
590 	if (v == NULL)
591 		return -ENOENT;
592 	return snd_config_imake_string(dst, NULL, v);
593 }
594 
rval_eval(snd_use_case_mgr_t * uc_mgr,const char * e)595 static char *rval_eval(snd_use_case_mgr_t *uc_mgr, const char *e)
596 {
597 	snd_config_t *dst;
598 	char *r;
599 	int err;
600 
601 	if (uc_mgr->conf_format < 5) {
602 		uc_error("variable evaluation is supported in v5+ syntax");
603 		return NULL;
604 	}
605 	err = _snd_eval_string(&dst, e, rval_eval_var_cb, uc_mgr);
606 	if (err < 0) {
607 		uc_error("unable to evaluate '%s'", e);
608 		return NULL;
609 	}
610 	err = snd_config_get_ascii(dst, &r);
611 	snd_config_delete(dst);
612 	if (err < 0)
613 		return NULL;
614 	return r;
615 }
616 
rval_evali(snd_use_case_mgr_t * uc_mgr,snd_config_t * node,const char * e)617 static int rval_evali(snd_use_case_mgr_t *uc_mgr, snd_config_t *node, const char *e)
618 {
619 	snd_config_t *dst;
620 	const char *id;
621 	char *s;
622 	size_t l;
623 	int err;
624 
625 	if (uc_mgr->conf_format < 6) {
626 		uc_error("variable evaluation is supported in v6+ syntax");
627 		return -EINVAL;
628 	}
629 	err = snd_config_get_id(node, &id);
630 	if (err < 0)
631 		return err;
632 	l = strlen(e);
633 	if (e[l-1] != '}')
634 		return -EINVAL;
635 	s = malloc(l + 1);
636 	if (s == NULL)
637 		return -ENOMEM;
638 	strcpy(s, e);
639 	s[l-1] = '\0';
640 	err = _snd_eval_string(&dst, s + 8, rval_eval_var_cb, uc_mgr);
641 	free(s);
642 	if (err < 0) {
643 		uc_error("unable to evaluate '%s'", e);
644 		return err;
645 	}
646 	err = snd_config_set_id(dst, id);
647 	if (err < 0)
648 		return err;
649 	return snd_config_substitute(node, dst);
650 }
651 
652 #define MATCH_VARIABLE(name, id, fcn, empty_ok)				\
653 	if (strncmp((name), (id), sizeof(id) - 1) == 0) { 		\
654 		rval = fcn(uc_mgr);					\
655 		idsize = sizeof(id) - 1;				\
656 		allow_empty = (empty_ok);				\
657 		goto __rval;						\
658 	}
659 
660 #define MATCH_VARIABLE2(name, id, fcn, empty_ok)			\
661 	if (strncmp((name), (id), sizeof(id) - 1) == 0) {		\
662 		idsize = sizeof(id) - 1;				\
663 		allow_empty = (empty_ok);				\
664 		fcn2 = (fcn);						\
665 		goto __match2;						\
666 	}
667 
668 /*
669  * skip escaped } character (simple version)
670  */
strchr_with_escape(const char * str,char c)671 static inline const char *strchr_with_escape(const char *str, char c)
672 {
673 	char *s;
674 
675 	while (1) {
676 		s = strchr(str, c);
677 		if (s && s != str) {
678 			if (*(s - 1) == '\\') {
679 				str = s + 1;
680 				continue;
681 			}
682 		}
683 		return s;
684 	}
685 }
686 
687 /*
688  * remove escaped } character (simple version)
689  */
strncpy_with_escape(char * dst,const char * src,size_t len)690 static inline void strncpy_with_escape(char *dst, const char *src, size_t len)
691 {
692 	char c;
693 
694 	c = *src++;
695 	while (c != '\0' && len > 0) {
696 		if (c == '\\' && *src == '}') {
697 			c = *src++;
698 			len--;
699 		}
700 		*dst++ = c;
701 		len--;
702 		c = *src++;
703 	}
704 	*dst = '\0';
705 }
706 
uc_mgr_get_substituted_value(snd_use_case_mgr_t * uc_mgr,char ** _rvalue,const char * value)707 int uc_mgr_get_substituted_value(snd_use_case_mgr_t *uc_mgr,
708 				 char **_rvalue,
709 				 const char *value)
710 {
711 	size_t size, nsize, idsize, rvalsize, dpos = 0;
712 	const char *tmp;
713 	char *r, *nr, *rval, v2[128];
714 	bool ignore_error, allow_empty;
715 	char *(*fcn2)(snd_use_case_mgr_t *, const char *id);
716 	int err;
717 
718 	if (value == NULL)
719 		return -ENOENT;
720 
721 	size = strlen(value) + 1;
722 	r = malloc(size);
723 	if (r == NULL)
724 		return -ENOMEM;
725 
726 	while (*value) {
727 		if (*value != '$') {
728 __std:
729 			r[dpos++] = *value;
730 			value++;
731 			continue;
732 		}
733 		ignore_error = false;
734 		if (value[1] == '$' && value[2] == '{' && uc_mgr->conf_format >= 3) {
735 			value++;
736 			ignore_error = true;
737 		} else if (value[1] != '{') {
738 			goto __std;
739 		}
740 		fcn2 = NULL;
741 		MATCH_VARIABLE(value, "${OpenName}", rval_open_name, false);
742 		MATCH_VARIABLE(value, "${ConfLibDir}", rval_conf_libdir, false);
743 		MATCH_VARIABLE(value, "${ConfTopDir}", rval_conf_topdir, false);
744 		MATCH_VARIABLE(value, "${ConfDir}", rval_conf_dir, false);
745 		MATCH_VARIABLE(value, "${ConfName}", rval_conf_name, false);
746 		MATCH_VARIABLE(value, "${CardNumber}", rval_card_number, true);
747 		MATCH_VARIABLE(value, "${CardId}", rval_card_id, false);
748 		MATCH_VARIABLE(value, "${CardDriver}", rval_card_driver, false);
749 		MATCH_VARIABLE(value, "${CardName}", rval_card_name, false);
750 		MATCH_VARIABLE(value, "${CardLongName}", rval_card_longname, false);
751 		MATCH_VARIABLE(value, "${CardComponents}", rval_card_components, true);
752 		MATCH_VARIABLE2(value, "${env:", rval_env, false);
753 		MATCH_VARIABLE2(value, "${sys:", rval_sysfs, false);
754 		MATCH_VARIABLE2(value, "${var:", rval_var, true);
755 		MATCH_VARIABLE2(value, "${eval:", rval_eval, false);
756 		MATCH_VARIABLE2(value, "${find-card:", rval_card_lookup, false);
757 		MATCH_VARIABLE2(value, "${find-device:", rval_device_lookup, false);
758 		MATCH_VARIABLE2(value, "${CardNumberByName:", rval_card_number_by_name, false);
759 		MATCH_VARIABLE2(value, "${CardIdByName:", rval_card_id_by_name, false);
760 __merr:
761 		err = -EINVAL;
762 		tmp = strchr(value, '}');
763 		if (tmp) {
764 			strncpy(r, value, tmp + 1 - value);
765 			r[tmp + 1 - value] = '\0';
766 			uc_error("variable '%s' is not known!", r);
767 		} else {
768 			uc_error("variable reference '%s' is not complete", value);
769 		}
770 		goto __error;
771 __match2:
772 		tmp = strchr_with_escape(value + idsize, '}');
773 		if (tmp) {
774 			rvalsize = tmp - (value + idsize);
775 			if (rvalsize >= sizeof(v2)) {
776 				err = -ENOMEM;
777 				goto __error;
778 			}
779 			strncpy_with_escape(v2, value + idsize, rvalsize);
780 			idsize += rvalsize + 1;
781 			if (*v2 == '$' && uc_mgr->conf_format >= 3) {
782 				if (strncmp(value, "${eval:", 7) == 0)
783 					goto __direct_fcn2;
784 				tmp = uc_mgr_get_variable(uc_mgr, v2 + 1);
785 				if (tmp == NULL) {
786 					uc_error("define '%s' is not reachable in this context!", v2 + 1);
787 					rval = NULL;
788 				} else {
789 					rval = fcn2(uc_mgr, tmp);
790 				}
791 			} else {
792 __direct_fcn2:
793 				rval = fcn2(uc_mgr, v2);
794 			}
795 			goto __rval;
796 		}
797 		goto __merr;
798 __rval:
799 		if (rval == NULL || (!allow_empty && rval[0] == '\0')) {
800 			free(rval);
801 			if (ignore_error) {
802 				value += idsize;
803 				continue;
804 			}
805 			strncpy(r, value, idsize);
806 			r[idsize] = '\0';
807 			uc_error("variable '%s' is %s in this context!", r,
808 				 rval ? "empty" : "not defined");
809 			err = -EINVAL;
810 			goto __error;
811 		}
812 		value += idsize;
813 		rvalsize = strlen(rval);
814 		nsize = size + rvalsize - idsize;
815 		if (nsize > size) {
816 			nr = realloc(r, nsize);
817 			if (nr == NULL) {
818 				free(rval);
819 				err = -ENOMEM;
820 				goto __error;
821 			}
822 			size = nsize;
823 			r = nr;
824 		}
825 		strcpy(r + dpos, rval);
826 		dpos += rvalsize;
827 		free(rval);
828 	}
829 	r[dpos] = '\0';
830 
831 	*_rvalue = r;
832 	return 0;
833 
834 __error:
835 	free(r);
836 	return err;
837 }
838 
uc_mgr_substitute_check(const char * s)839 static inline int uc_mgr_substitute_check(const char *s)
840 {
841 	return s && strstr(s, "${") != NULL;
842 }
843 
uc_mgr_substitute_tree(snd_use_case_mgr_t * uc_mgr,snd_config_t * node)844 int uc_mgr_substitute_tree(snd_use_case_mgr_t *uc_mgr, snd_config_t *node)
845 {
846 	snd_config_iterator_t i, next;
847 	snd_config_t *n;
848 	const char *id, *s2;
849 	char *s;
850 	int err;
851 
852 	err = snd_config_get_id(node, &id);
853 	if (err < 0)
854 		return err;
855 	if (uc_mgr_substitute_check(id)) {
856 		err = uc_mgr_get_substituted_value(uc_mgr, &s, id);
857 		if (err < 0)
858 			return err;
859 		err = snd_config_set_id(node, s);
860 		if (err < 0) {
861 			uc_error("unable to set substituted id '%s' (old id '%s')", s, id);
862 			free(s);
863 			return err;
864 		}
865 		free(s);
866 	}
867 	if (snd_config_get_type(node) != SND_CONFIG_TYPE_COMPOUND) {
868 		if (snd_config_get_type(node) == SND_CONFIG_TYPE_STRING) {
869 			err = snd_config_get_string(node, &s2);
870 			if (err < 0)
871 				return err;
872 			if (!uc_mgr_substitute_check(s2))
873 				return 0;
874 			if (strncmp(s2, "${evali:", 8) == 0)
875 				return rval_evali(uc_mgr, node, s2);
876 			err = uc_mgr_get_substituted_value(uc_mgr, &s, s2);
877 			if (err < 0)
878 				return err;
879 			err = snd_config_set_string(node, s);
880 			free(s);
881 			if (err < 0)
882 				return err;
883 		}
884 		return 0;
885 	}
886 	/* exception - macros are evaluated when instantied */
887 	err = snd_config_get_id(node, &id);
888 	if (err < 0)
889 		return err;
890 	if (id && strcmp(id, "DefineMacro") == 0)
891 		return 0;
892 	snd_config_for_each(i, next, node) {
893 		n = snd_config_iterator_entry(i);
894 		err = uc_mgr_substitute_tree(uc_mgr, n);
895 		if (err < 0)
896 			return err;
897 	}
898 	return 0;
899 }
900