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