1 /*
2   Copyright(c) 2021 Intel Corporation
3   All rights reserved.
4 
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of version 2 of the GNU General Public License as
7   published by the Free Software Foundation.
8 
9   This program is distributed in the hope that it will be useful, but
10   WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   General Public License for more details.
13 
14   You should have received a copy of the GNU General Public License
15   along with this program; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17   The full GNU General Public License is included in this distribution
18   in the file called LICENSE.GPL.
19 */
20 
21 #include "aconfig.h"
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stddef.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <regex.h>
29 #include <dlfcn.h>
30 
31 #include <alsa/asoundlib.h>
32 #include "gettext.h"
33 #include "topology.h"
34 #include "pre-processor.h"
35 #include "pre-process-external.h"
36 
37 #define SND_TOPOLOGY_MAX_PLUGINS 32
38 
get_plugin_string(struct tplg_pre_processor *tplg_pp, char **plugin_string)39 static int get_plugin_string(struct tplg_pre_processor *tplg_pp, char **plugin_string)
40 {
41 	const char *lib_names_t = NULL;
42 	snd_config_t *defines;
43 	int ret;
44 
45 	ret = snd_config_search(tplg_pp->input_cfg, "Define.PREPROCESS_PLUGINS", &defines);
46 	if (ret < 0)
47 		return ret;
48 
49 	ret = snd_config_get_string(defines, &lib_names_t);
50 	if (ret < 0)
51 		return ret;
52 
53 	*plugin_string = strdup(lib_names_t);
54 
55 	if (!*plugin_string)
56 		return -ENOMEM;
57 
58 	return 0;
59 }
60 
run_plugin(struct tplg_pre_processor *tplg_pp, char *plugin)61 static int run_plugin(struct tplg_pre_processor *tplg_pp, char *plugin)
62 {
63 	plugin_pre_process process;
64 	char *xlib, *xfunc, *path;
65 	void *h = NULL;
66 	int ret = 0;
67 
68 	/* compose the plugin path, if not from environment, then from default plugins dir */
69 	path = getenv("ALSA_TOPOLOGY_PLUGIN_DIR");
70 	if (!path)
71 		path = ALSA_TOPOLOGY_PLUGIN_DIR;
72 
73 	xlib = tplg_snprintf("%s/%s%s%s", path, PROCESS_LIB_PREFIX, plugin,
74 			     PROCESS_LIB_POSTFIX);
75 	xfunc = tplg_snprintf("%s%s%s", PROCESS_FUNC_PREFIX, plugin,
76 			      PROCESS_FUNC_POSTFIX);
77 
78 	if (!xlib || !xfunc) {
79 		fprintf(stderr, "can't reserve memory for plugin paths and func names\n");
80 		ret = -ENOMEM;
81 		goto err;
82 	}
83 
84 	/* open plugin */
85 	h = dlopen(xlib, RTLD_NOW);
86 	if (!h) {
87 		fprintf(stderr, "unable to open library '%s'\n", xlib);
88 		ret = -EINVAL;
89 		goto err;
90 	}
91 
92 	/* find function */
93 	process = dlsym(h, xfunc);
94 
95 	if (!process) {
96 		fprintf(stderr, "symbol 'topology_process' was not found in %s\n", xlib);
97 		ret = -EINVAL;
98 		goto err;
99 	}
100 
101 	/* process plugin */
102 	ret = process(tplg_pp->input_cfg, tplg_pp->output_cfg);
103 
104 err:
105 	if (h)
106 		dlclose(h);
107 	if (xlib)
108 		free(xlib);
109 	if (xfunc)
110 		free(xfunc);
111 
112 	return ret;
113 }
114 
pre_process_plugins(struct tplg_pre_processor *tplg_pp)115 static int pre_process_plugins(struct tplg_pre_processor *tplg_pp)
116 {
117 	char *plugins[SND_TOPOLOGY_MAX_PLUGINS];
118 	char *plugin_string;
119 	int count;
120 	int ret;
121 	int i;
122 
123 	/* parse plugin names */
124 	ret = get_plugin_string(tplg_pp, &plugin_string);
125 
126 	/* no plugins defined, so just return */
127 	if (ret < 0)
128 		return 0;
129 
130 	count = 0;
131 	plugins[count] = strtok(plugin_string, ":");
132 	while ((count < SND_TOPOLOGY_MAX_PLUGINS - 1) && plugins[count]) {
133 		count++;
134 		plugins[count] = strtok(NULL, ":");
135 	}
136 
137 	/* run all plugins */
138 	for (i = 0; i < count; i++) {
139 		ret = run_plugin(tplg_pp, plugins[i]);
140 		if (ret < 0)
141 			return ret;
142 	}
143 
144 	free(plugin_string);
145 
146 	return 0;
147 }
148 
149 /*
150  * Helper function to find config by id.
151  * Topology2.0 object names are constructed with attribute values separated by '.'.
152  * So snd_config_search() cannot be used as it interprets the '.' as the node separator.
153  */
tplg_find_config(snd_config_t *config, const char *name)154 snd_config_t *tplg_find_config(snd_config_t *config, const char *name)
155 {
156 	snd_config_iterator_t i, next;
157 	snd_config_t *n;
158 	const char *id;
159 
160 	snd_config_for_each(i, next, config) {
161 		n = snd_config_iterator_entry(i);
162 		if (snd_config_get_id(n, &id) < 0)
163 			continue;
164 
165 		if (!strcmp(id, name))
166 			return n;
167 	}
168 
169 	return NULL;
170 }
171 
172 /* make a new config and add it to parent */
tplg_config_make_add(snd_config_t **config, const char *id, snd_config_type_t type, snd_config_t *parent)173 int tplg_config_make_add(snd_config_t **config, const char *id, snd_config_type_t type,
174 			 snd_config_t *parent)
175 {
176 	int ret;
177 
178 	ret = snd_config_make(config, id, type);
179 	if (ret < 0)
180 		return ret;
181 
182 	ret = snd_config_add(parent, *config);
183 	if (ret < 0)
184 		snd_config_delete(*config);
185 
186 	return ret;
187 }
188 
189 /*
190  * The pre-processor will need to concat multiple strings separate by '.' to construct the object
191  * name and search for configs with ID's separated by '.'.
192  * This function helps concat input strings in the specified input format
193  */
tplg_snprintf(char *fmt, ...)194 char *tplg_snprintf(char *fmt, ...)
195 {
196 	char *string;
197 	int len = 1;
198 
199 	va_list va;
200 
201 	va_start(va, fmt);
202 	len += vsnprintf(NULL, 0, fmt, va);
203 	va_end(va);
204 
205 	string = calloc(1, len);
206 	if (!string)
207 		return NULL;
208 
209 	va_start(va, fmt);
210 	vsnprintf(string, len, fmt, va);
211 	va_end(va);
212 
213 	return string;
214 }
215 
216 #ifdef TPLG_DEBUG
tplg_pp_debug(char *fmt, ...)217 void tplg_pp_debug(char *fmt, ...)
218 {
219 	char msg[DEBUG_MAX_LENGTH];
220 	va_list va;
221 
222 	va_start(va, fmt);
223 	vsnprintf(msg, DEBUG_MAX_LENGTH, fmt, va);
224 	va_end(va);
225 
226 	fprintf(stdout, "%s\n", msg);
227 }
228 
tplg_pp_config_debug(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg)229 void tplg_pp_config_debug(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg)
230 {
231 	snd_config_save(cfg, tplg_pp->dbg_output);
232 }
233 #else
tplg_pp_debug(char *fmt ATTRIBUTE_UNUSED, ...)234 void tplg_pp_debug(char *fmt ATTRIBUTE_UNUSED, ...) {}
tplg_pp_config_debug(struct tplg_pre_processor *tplg_pp ATTRIBUTE_UNUSED, snd_config_t *cfg ATTRIBUTE_UNUSED)235 void tplg_pp_config_debug(struct tplg_pre_processor *tplg_pp ATTRIBUTE_UNUSED,
236 			  snd_config_t *cfg ATTRIBUTE_UNUSED) {}
237 #endif
238 
pre_process_config(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg)239 static int pre_process_config(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg)
240 {
241 	snd_config_iterator_t i, next, i2, next2;
242 	snd_config_t *n, *n2;
243 	const char *id;
244 	int err;
245 
246 	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
247 		fprintf(stderr, "compound type expected at top level");
248 		return -EINVAL;
249 	}
250 
251 	/* parse topology objects */
252 	snd_config_for_each(i, next, cfg) {
253 		n = snd_config_iterator_entry(i);
254 		if (snd_config_get_id(n, &id) < 0)
255 			continue;
256 
257 		if (strcmp(id, "Object"))
258 			continue;
259 
260 		if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
261 			fprintf(stderr, "compound type expected for %s", id);
262 			return -EINVAL;
263 		}
264 
265 		snd_config_for_each(i2, next2, n) {
266 			n2 = snd_config_iterator_entry(i2);
267 
268 			if (snd_config_get_id(n2, &id) < 0)
269 				continue;
270 
271 			if (snd_config_get_type(n2) != SND_CONFIG_TYPE_COMPOUND) {
272 				fprintf(stderr, "compound type expected for %s", id);
273 				return -EINVAL;
274 			}
275 
276 			/* pre-process Object instance. Top-level object have no parent */
277 			err = tplg_pre_process_objects(tplg_pp, n2, NULL);
278 			if (err < 0)
279 				return err;
280 		}
281 	}
282 
283 	return 0;
284 }
285 
free_pre_processor(struct tplg_pre_processor *tplg_pp)286 void free_pre_processor(struct tplg_pre_processor *tplg_pp)
287 {
288 	snd_output_close(tplg_pp->output);
289 	snd_output_close(tplg_pp->dbg_output);
290 	snd_config_delete(tplg_pp->output_cfg);
291 	if (tplg_pp->define_cfg)
292 		snd_config_delete(tplg_pp->define_cfg);
293 	free(tplg_pp->inc_path);
294 	free(tplg_pp);
295 }
296 
init_pre_processor(struct tplg_pre_processor **tplg_pp, snd_output_type_t type, const char *output_file)297 int init_pre_processor(struct tplg_pre_processor **tplg_pp, snd_output_type_t type,
298 		       const char *output_file)
299 {
300 	struct tplg_pre_processor *_tplg_pp;
301 	int ret;
302 
303 	_tplg_pp = calloc(1, sizeof(struct tplg_pre_processor));
304 	if (!_tplg_pp)
305 		return -ENOMEM;
306 
307 	*tplg_pp = _tplg_pp;
308 
309 	/* create output top-level config node */
310 	ret = snd_config_top(&_tplg_pp->output_cfg);
311 	if (ret < 0)
312 		goto err;
313 
314 	/* open output based on type */
315 	if (type == SND_OUTPUT_STDIO) {
316 		ret = snd_output_stdio_open(&_tplg_pp->output, output_file, "w");
317 		if (ret < 0) {
318 			fprintf(stderr, "failed to open file output\n");
319 			goto open_err;
320 		}
321 	} else {
322 		ret = snd_output_buffer_open(&_tplg_pp->output);
323 		if (ret < 0) {
324 			fprintf(stderr, "failed to open buffer output\n");
325 			goto open_err;
326 		}
327 	}
328 
329 	/* debug output */
330 	ret = snd_output_stdio_attach(&_tplg_pp->dbg_output, stdout, 0);
331 	if (ret < 0) {
332 		fprintf(stderr, "failed to open stdout output\n");
333 		goto out_close;
334 	}
335 
336 	return 0;
337 out_close:
338 	snd_output_close(_tplg_pp->output);
339 open_err:
340 	snd_config_delete(_tplg_pp->output_cfg);
341 err:
342 	free(_tplg_pp);
343 	return ret;
344 }
345 
346 #if SND_LIB_VER(1, 2, 5) < SND_LIB_VERSION
pre_process_set_defines(struct tplg_pre_processor *tplg_pp, const char *pre_processor_defs)347 static int pre_process_set_defines(struct tplg_pre_processor *tplg_pp, const char *pre_processor_defs)
348 {
349 	int ret;
350 
351 	/*
352 	 * load the command line defines to the configuration tree
353 	 */
354 	if (pre_processor_defs != NULL) {
355 		ret = snd_config_load_string(&tplg_pp->define_cfg, pre_processor_defs, 0);
356 		if (ret < 0) {
357 			fprintf(stderr, "Failed to load pre-processor command line definitions\n");
358 			return ret;
359 		}
360 	} else {
361 		tplg_pp->define_cfg = NULL;
362 	}
363 
364 	return 0;
365 }
366 
pre_process_add_defines(struct tplg_pre_processor *tplg_pp, snd_config_t *from)367 static int pre_process_add_defines(struct tplg_pre_processor *tplg_pp, snd_config_t *from)
368 {
369 	snd_config_t *conf_defines, *conf_tmp;
370 	int ret;
371 
372 	ret = snd_config_search(from, "Define", &conf_defines);
373 	if (ret == -ENOENT) {
374 		if (tplg_pp->input_cfg == from && tplg_pp->define_cfg) {
375 			conf_defines = NULL;
376 			goto create;
377 		}
378 	}
379 	if (ret < 0)
380 		return ret;
381 
382 	if (snd_config_get_type(conf_defines) != SND_CONFIG_TYPE_COMPOUND) {
383 		fprintf(stderr, "Define must be a compound!\n");
384 		return -EINVAL;
385 	}
386 
387 	if (tplg_pp->input_cfg == from)
388 		tplg_pp->define_cfg_merged = conf_defines;
389 
390 	if (tplg_pp->define_cfg_merged == NULL) {
391 create:
392 		ret = snd_config_make_compound(&tplg_pp->define_cfg_merged, "Define", 0);
393 		if (ret < 0)
394 			return ret;
395 		ret = snd_config_add(tplg_pp->input_cfg, tplg_pp->define_cfg_merged);
396 		if (ret < 0)
397 			return ret;
398 	}
399 
400 	if (tplg_pp->define_cfg_merged != conf_defines) {
401 		/*
402 		 * merge back to the main configuration tree (Define subtree)
403 		 */
404 		ret = snd_config_merge(tplg_pp->define_cfg_merged, conf_defines, true);
405 		if (ret < 0) {
406 			fprintf(stderr, "Failed to override main variable definitions\n");
407 			return ret;
408 		}
409 	}
410 
411 	/*
412 	 * merge the command line defines with the variables in the conf file to override
413 	 * default values; use a copy (merge deletes the source tree)
414 	 */
415 	if (tplg_pp->define_cfg) {
416 		ret = snd_config_copy(&conf_tmp, tplg_pp->define_cfg);
417 		if (ret < 0) {
418 			fprintf(stderr, "Failed to copy variable definitions\n");
419 			return ret;
420 		}
421 		ret = snd_config_merge(tplg_pp->define_cfg_merged, conf_tmp, true);
422 		if (ret < 0) {
423 			fprintf(stderr, "Failed to override variable definitions\n");
424 			snd_config_delete(conf_tmp);
425 			return ret;
426 		}
427 	}
428 
429 	return 0;
430 }
431 
432 static int pre_process_includes(struct tplg_pre_processor *tplg_pp, snd_config_t *top);
433 
pre_process_include_conf(struct tplg_pre_processor *tplg_pp, snd_config_t *config, snd_config_t **new, snd_config_t *variable)434 static int pre_process_include_conf(struct tplg_pre_processor *tplg_pp, snd_config_t *config,
435 				    snd_config_t **new, snd_config_t *variable)
436 {
437 	snd_config_iterator_t i, next;
438 	const char *variable_name;
439 	char *value;
440 	int ret;
441 
442 	if (snd_config_get_id(variable, &variable_name) < 0)
443 		return 0;
444 
445 	switch(snd_config_get_type(variable)) {
446 	case SND_CONFIG_TYPE_STRING:
447 	{
448 		const char *s;
449 
450 		if (snd_config_get_string(variable, &s) < 0) {
451 			SNDERR("Invalid value for variable %s\n", variable_name);
452 			return -EINVAL;
453 		}
454 		value = strdup(s);
455 		if (!value)
456 			return -ENOMEM;
457 		break;
458 	}
459 	case SND_CONFIG_TYPE_INTEGER:
460 	{
461 		long v;
462 
463 		ret = snd_config_get_integer(variable, &v);
464 		if (ret < 0) {
465 			SNDERR("Invalid value for variable %s\n", variable_name);
466 			return ret;
467 		}
468 
469 		value = tplg_snprintf("%ld", v);
470 		if (!value)
471 			return -ENOMEM;
472 		break;
473 	}
474 	default:
475 		SNDERR("Invalid type for variable definition %s\n", variable_name);
476 		return -EINVAL;
477 	}
478 
479 	/* create top-level config node */
480 	ret = snd_config_top(new);
481 	if (ret < 0) {
482 		SNDERR("failed to create top-level node for include conf %s\n", variable_name);
483 		goto err;
484 	}
485 
486 	snd_config_for_each(i, next, config) {
487 		snd_input_t *in;
488 		snd_config_t *n;
489 		regex_t regex;
490 		const char *filename;
491 		const char *id;
492 		char *full_path;
493 
494 		n = snd_config_iterator_entry(i);
495 		if (snd_config_get_id(n, &id) < 0)
496 			continue;
497 
498 		ret = regcomp(&regex, id, REG_EXTENDED | REG_ICASE);
499 		if (ret) {
500 			fprintf(stderr, "Could not compile regex\n");
501 			goto err;
502 		}
503 
504 		/* Execute regular expression */
505 		ret = regexec(&regex, value, 0, NULL, 0);
506 		if (ret)
507 			continue;
508 
509 		/* regex matched. now include or use the configuration */
510 		if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) {
511 			/* configuration block */
512 			ret = snd_config_merge(*new, n, 0);
513 			if (ret < 0) {
514 				fprintf(stderr, "Unable to merge key '%s'\n", value);
515 				goto err;
516 			}
517 		} else {
518 			ret = snd_config_get_string(n, &filename);
519 			if (ret < 0)
520 				goto err;
521 
522 			if (filename && filename[0] != '/')
523 				full_path = tplg_snprintf("%s/%s", tplg_pp->inc_path, filename);
524 			else
525 				full_path = tplg_snprintf("%s", filename);
526 
527 			ret = snd_input_stdio_open(&in, full_path, "r");
528 			if (ret < 0) {
529 				fprintf(stderr, "Unable to open included conf file %s\n", full_path);
530 				free(full_path);
531 				goto err;
532 			}
533 			free(full_path);
534 
535 			/* load config */
536 			ret = snd_config_load(*new, in);
537 			snd_input_close(in);
538 			if (ret < 0) {
539 				fprintf(stderr, "Unable to load included configuration\n");
540 				goto err;
541 			}
542 		}
543 
544 		/* forcefully overwrite with defines from the command line */
545 		ret = pre_process_add_defines(tplg_pp, *new);
546 		if (ret < 0 && ret != -ENOENT) {
547 			fprintf(stderr, "Failed to parse arguments in input config\n");
548 			goto err;
549 		}
550 
551 		/* recursively process any nested includes */
552 		ret = pre_process_includes(tplg_pp, *new);
553 		if (ret < 0)
554 			goto err;
555 	}
556 
557 err:
558 	free(value);
559 	return ret;
560 }
561 
pre_process_includes(struct tplg_pre_processor *tplg_pp, snd_config_t *top)562 static int pre_process_includes(struct tplg_pre_processor *tplg_pp, snd_config_t *top)
563 {
564 	snd_config_iterator_t i, next;
565 	snd_config_t *includes;
566 	const char *top_id;
567 	int ret;
568 
569 	if (tplg_pp->define_cfg_merged == NULL)
570 		return 0;
571 
572 	ret = snd_config_search(top, "IncludeByKey", &includes);
573 	if (ret < 0)
574 		return 0;
575 
576 	snd_config_get_id(top, &top_id);
577 
578 	snd_config_for_each(i, next, includes) {
579 		snd_config_t *n, *new, *define;
580 		const char *id;
581 
582 		n = snd_config_iterator_entry(i);
583 		if (snd_config_get_id(n, &id) < 0)
584 			continue;
585 
586 		/* find id from variable definitions */
587 		ret = snd_config_search(tplg_pp->define_cfg_merged, id, &define);
588 		if (ret < 0) {
589 			fprintf(stderr, "No variable defined for %s\n", id);
590 			return ret;
591 		}
592 
593 		/* create conf node from included file */
594 		ret = pre_process_include_conf(tplg_pp, n, &new, define);
595 		if (ret < 0) {
596 			fprintf(stderr, "Unable to process include file \n");
597 			return ret;
598 		}
599 
600 		/* merge the included conf file with the top-level conf */
601 		ret = snd_config_merge(top, new, 0);
602 		if (ret < 0) {
603 			fprintf(stderr, "Failed to add included conf\n");
604 			return ret;
605 		}
606 	}
607 
608 	/* delete all includes from current top */
609 	snd_config_delete(includes);
610 
611 	return 0;
612 }
613 
pre_process_includes_all(struct tplg_pre_processor *tplg_pp, snd_config_t *top)614 static int pre_process_includes_all(struct tplg_pre_processor *tplg_pp, snd_config_t *top)
615 {
616 	snd_config_iterator_t i, next;
617 	int ret;
618 
619 	if (snd_config_get_type(top) != SND_CONFIG_TYPE_COMPOUND)
620 		return 0;
621 
622 	/* process includes at this node */
623 	ret = pre_process_includes(tplg_pp, top);
624 	if (ret < 0) {
625 		fprintf(stderr, "Failed to process includes\n");
626 		return ret;
627 	}
628 
629 	/* process includes at all child nodes */
630 	snd_config_for_each(i, next, top) {
631 		snd_config_t *n;
632 
633 		n = snd_config_iterator_entry(i);
634 
635 		ret = pre_process_includes_all(tplg_pp, n);
636 		if (ret < 0)
637 			return ret;
638 	}
639 
640 	return 0;
641 }
642 
643 /* duplicate the existing objects in src into dest and update with new attribute */
pre_process_add_objects(struct tplg_pre_processor *tplg_pp ATTRIBUTE_UNUSED, int *object_count, snd_config_t *src, snd_config_t *dest, snd_config_t *attr_cfg)644 static int pre_process_add_objects(struct tplg_pre_processor *tplg_pp ATTRIBUTE_UNUSED,
645 				   int *object_count, snd_config_t *src,
646 				   snd_config_t *dest, snd_config_t *attr_cfg)
647 {
648 	snd_config_iterator_t i, next;
649 	int ret;
650 
651 	snd_config_for_each(i, next, src) {
652 		snd_config_t *n, *new, *new_attr;
653 		char* new_id = tplg_snprintf("%d", (*object_count)++);
654 
655 		n = snd_config_iterator_entry(i);
656 
657 		/* duplicate the existing object */
658 		ret = snd_config_copy(&new, n);
659 		if (ret < 0) {
660 			free(new_id);
661 			return ret;
662 		}
663 
664 		ret = snd_config_set_id(new, new_id);
665 		free(new_id);
666 		if (ret < 0) {
667 			snd_config_delete(new);
668 			return ret;
669 		}
670 
671 		ret = snd_config_add(dest, new);
672 		if (ret < 0) {
673 			snd_config_delete(new);
674 			return ret;
675 		}
676 
677 		/* and update the new attribute */
678 		ret = snd_config_copy(&new_attr, attr_cfg);
679 		if (ret < 0)
680 			return ret;
681 
682 		ret = snd_config_add(new, new_attr);
683 		if (ret < 0) {
684 			snd_config_delete(new_attr);
685 			return ret;
686 		}
687 	}
688 
689 	return 0;
690 }
691 
692 /* Create config based on the number of items in the array */
pre_process_create_items(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg, snd_config_t *top, int *object_count_offset)693 static int pre_process_create_items(struct tplg_pre_processor *tplg_pp,
694 				    snd_config_t *cfg, snd_config_t *top,
695 				    int *object_count_offset)
696 {
697 	snd_config_iterator_t i, next;
698 	snd_config_type_t type;
699 	const char *class_id;
700 	char *class_id_local;
701 	int attr_count = 0;
702 	int object_count = *object_count_offset;
703 	int ret;
704 
705 	snd_config_get_id(top, &class_id);
706 	class_id_local = strdup(class_id);
707 
708 	snd_config_for_each(i, next, cfg) {
709 		snd_config_iterator_t i2, next2;
710 		snd_config_t *n, *local_top;
711 		const char *attribute;
712 		int attr_val_count = 0;
713 		object_count = *object_count_offset;
714 
715 		n = snd_config_iterator_entry(i);
716 		type = snd_config_get_type(n);
717 		if (type != SND_CONFIG_TYPE_COMPOUND)
718 			continue;
719 		if (snd_config_get_id(n, &attribute) < 0)
720 			continue;
721 
722 		ret = snd_config_make(&local_top, class_id_local, SND_CONFIG_TYPE_COMPOUND);
723 
724 		snd_config_for_each(i2, next2, n) {
725 			snd_config_t *n2, *new, *new_obj;
726 			snd_config_type_t attr_type;
727 			char *new_id;
728 
729 			n2 = snd_config_iterator_entry(i2);
730 
731 			attr_type = snd_config_get_type(n2);
732 
733 			/* create new config based on type */
734 			if (attr_type == SND_CONFIG_TYPE_INTEGER) {
735 				long val;
736 
737 				ret = snd_config_get_integer(n2, &val);
738 				if (ret < 0)
739 					return ret;
740 
741 				ret = snd_config_make(&new, attribute, SND_CONFIG_TYPE_INTEGER);
742 				if (ret < 0)
743 					return ret;
744 
745 				ret = snd_config_set_integer(new, val);
746 			} else {
747 				const char *s;
748 
749 				ret = snd_config_get_string(n2, &s);
750 				if (ret < 0)
751 					return ret;
752 				ret = snd_config_make(&new, attribute, SND_CONFIG_TYPE_STRING);
753 				if (ret < 0)
754 					return ret;
755 
756 				ret = snd_config_set_string(new, s);
757 			}
758 
759 			if (ret < 0)
760 				goto err;
761 
762 			/* for the first array simply create new conf nodes */
763 			if (!attr_count) {
764 				new_id = tplg_snprintf("%d", object_count++);
765 				ret = snd_config_make(&new_obj, new_id, SND_CONFIG_TYPE_COMPOUND);
766 				free(new_id);
767 
768 				ret = snd_config_add(new_obj, new);
769 				if (ret < 0) {
770 					snd_config_delete(new_obj);
771 					goto err;
772 				}
773 
774 				ret = snd_config_add(local_top, new_obj);
775 				if (ret < 0) {
776 					snd_config_delete(new_obj);
777 					goto err;
778 				}
779 
780 				continue;
781 			}
782 
783 			/*
784 			 * for the subsequent arrays, duplicate the existing objects
785 			 * and update them with the new ones
786 			 */
787 			ret = pre_process_add_objects(tplg_pp, &object_count, top,
788 						      local_top, new);
789 			if (ret < 0) {
790 				SNDERR("failed to add objects of type %s\n", class_id_local);
791 				goto err;
792 			}
793 
794 			attr_val_count++;
795 err:
796 			snd_config_delete(new);
797 			if (ret < 0) {
798 				snd_config_delete(local_top);
799 				return ret;
800 			}
801 		}
802 
803 		/* substitute current list of configs with the updated list */
804 		ret = snd_config_substitute(top, local_top);
805 		if (ret < 0) {
806 			snd_config_delete(local_top);
807 			return ret;
808 		}
809 		attr_count++;
810 	}
811 
812 	*object_count_offset = object_count;
813 	return 0;
814 }
815 
pre_process_array_item(struct tplg_pre_processor *tplg_pp, snd_config_t *top, snd_config_t *array)816 static int pre_process_array_item(struct tplg_pre_processor *tplg_pp, snd_config_t *top,
817 				  snd_config_t *array)
818 {
819 	snd_config_iterator_t i, next;
820 	int object_count;
821 	int ret;
822 
823 	snd_config_for_each(i, next, array) {
824 		snd_config_iterator_t i3, next3;
825 		snd_config_t *n, *new;
826 		const char *id;
827 
828 		n = snd_config_iterator_entry(i);
829 		if (snd_config_get_id(n, &id) < 0)
830 			continue;
831 
832 		/* Create a new node if it doesn't exist already */
833 		if (snd_config_search(top, id, &new) < 0) {
834 			ret = snd_config_make(&new, id, SND_CONFIG_TYPE_COMPOUND);
835 			if (ret < 0)
836 				return ret;
837 
838 			/* add the list of objects to the current top */
839 			ret = snd_config_add(top, new);
840 			if (ret < 0) {
841 				snd_config_delete(new);
842 				return ret;
843 			}
844 		}
845 
846 		/* if the conf node is not an array, move on to parse child nodes */
847 		if (snd_config_is_array(n) <= 0)
848 			return pre_process_array_item(tplg_pp, new, n);
849 
850 		object_count = 0;
851 		snd_config_for_each(i3, next3, n) {
852 			snd_config_t *n3, *local_top;
853 
854 			n3 = snd_config_iterator_entry(i3);
855 
856 			ret = snd_config_make(&local_top, id, SND_CONFIG_TYPE_COMPOUND);
857 			if (ret < 0)
858 				return ret;
859 
860 			ret = pre_process_create_items(tplg_pp, n3, local_top,
861 							 &object_count);
862 			if (ret < 0) {
863 				SNDERR("failed to create objects of type %s\n", id);
864 				return ret;
865 			}
866 
867 			ret = snd_config_merge(new, local_top, 0);
868 			if (ret < 0) {
869 				snd_config_delete(local_top);
870 				return ret;
871 			}
872 		}
873 	}
874 
875 	return 0;
876 }
877 
pre_process_array(struct tplg_pre_processor *tplg_pp, snd_config_t *top)878 static int pre_process_array(struct tplg_pre_processor *tplg_pp, snd_config_t *top)
879 {
880 	snd_config_t *arrays;
881 	int ret;
882 
883 	ret = snd_config_search(top, "CombineArrays", &arrays);
884 	if (ret < 0)
885 		return 0;
886 
887 	ret = pre_process_array_item(tplg_pp, top, arrays);
888 	if (ret < 0)
889 		return ret;
890 
891 	snd_config_delete(arrays);
892 	return 0;
893 }
894 
pre_process_arrays(struct tplg_pre_processor *tplg_pp, snd_config_t *top)895 static int pre_process_arrays(struct tplg_pre_processor *tplg_pp, snd_config_t *top)
896 {
897 	snd_config_iterator_t i, next;
898 	int ret;
899 
900 	if (snd_config_get_type(top) != SND_CONFIG_TYPE_COMPOUND)
901 		return 0;
902 
903 	/* process object arrays at this node */
904 	ret = pre_process_array(tplg_pp, top);
905 	if (ret < 0) {
906 		fprintf(stderr, "Failed to process object arrays\n");
907 		return ret;
908 	}
909 
910 	/* process object arrays at all child nodes */
911 	snd_config_for_each(i, next, top) {
912 		snd_config_t *n;
913 
914 		n = snd_config_iterator_entry(i);
915 
916 		ret = pre_process_arrays(tplg_pp, n);
917 		if (ret < 0)
918 			return ret;
919 	}
920 
921 	return 0;
922 }
923 
924 #endif /* version < 1.2.6 */
925 
pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size, const char *pre_processor_defs, const char *inc_path)926 int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size,
927 		const char *pre_processor_defs, const char *inc_path)
928 {
929 	snd_input_t *in;
930 	snd_config_t *top;
931 	int err;
932 
933 	/* create input buffer */
934 	err = snd_input_buffer_open(&in, config, config_size);
935 	if (err < 0) {
936 		fprintf(stderr, "Unable to open input buffer\n");
937 		return err;
938 	}
939 
940 	/* create top-level config node */
941 	err = snd_config_top(&top);
942 	if (err < 0)
943 		goto input_close;
944 
945 	/* load config */
946 	err = snd_config_load(top, in);
947 	if (err < 0) {
948 		fprintf(stderr, "Unable not load configuration\n");
949 		goto err;
950 	}
951 
952 	tplg_pp->input_cfg = top;
953 	tplg_pp->inc_path = inc_path ? strdup(inc_path) : NULL;
954 
955 #if SND_LIB_VER(1, 2, 5) < SND_LIB_VERSION
956 	/* parse command line definitions */
957 	err = pre_process_set_defines(tplg_pp, pre_processor_defs);
958 	if (err < 0) {
959 		fprintf(stderr, "Failed to parse arguments in input config\n");
960 		goto err;
961 	}
962 
963 	/* parse command line definitions */
964 	err = pre_process_add_defines(tplg_pp, top);
965 	if (err < 0) {
966 		fprintf(stderr, "Failed to parse arguments in input config\n");
967 		goto err;
968 	}
969 
970 	/* include conditional conf files */
971 	err = pre_process_includes_all(tplg_pp, tplg_pp->input_cfg);
972 	if (err < 0) {
973 		fprintf(stderr, "Failed to process conditional includes in input config\n");
974 		goto err;
975 	}
976 
977 	/* expand object arrays */
978 	err = pre_process_arrays(tplg_pp, tplg_pp->input_cfg);
979 	if (err < 0) {
980 		fprintf(stderr, "Failed to process object arrays in input config\n");
981 		goto err;
982 	}
983 #endif
984 
985 	err = pre_process_config(tplg_pp, tplg_pp->input_cfg);
986 	if (err < 0) {
987 		fprintf(stderr, "Unable to pre-process configuration\n");
988 		goto err;
989 	}
990 
991 	/* process topology plugins */
992 	err = pre_process_plugins(tplg_pp);
993 	if (err < 0) {
994 		fprintf(stderr, "Unable to run pre-process plugins or plugins return error\n");
995 		goto err;
996 	}
997 
998 	/* save config to output */
999 	err = snd_config_save(tplg_pp->output_cfg, tplg_pp->output);
1000 	if (err < 0)
1001 		fprintf(stderr, "failed to save pre-processed output file\n");
1002 
1003 err:
1004 	snd_config_delete(top);
1005 input_close:
1006 	snd_input_close(in);
1007 
1008 	return err;
1009 }
1010