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