OSDN Git Service

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