xref: /third_party/alsa-lib/src/topology/save.c (revision d5ac70f0)
1d5ac70f0Sopenharmony_ci/*
2d5ac70f0Sopenharmony_ci  Copyright(c) 2019 Red Hat Inc.
3d5ac70f0Sopenharmony_ci  All rights reserved.
4d5ac70f0Sopenharmony_ci
5d5ac70f0Sopenharmony_ci  This library is free software; you can redistribute it and/or modify
6d5ac70f0Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as
7d5ac70f0Sopenharmony_ci  published by the Free Software Foundation; either version 2.1 of
8d5ac70f0Sopenharmony_ci  the License, or (at your option) any later version.
9d5ac70f0Sopenharmony_ci
10d5ac70f0Sopenharmony_ci  This program is distributed in the hope that it will be useful,
11d5ac70f0Sopenharmony_ci  but WITHOUT ANY WARRANTY; without even the implied warranty of
12d5ac70f0Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13d5ac70f0Sopenharmony_ci  GNU Lesser General Public License for more details.
14d5ac70f0Sopenharmony_ci
15d5ac70f0Sopenharmony_ci  Authors: Jaroslav Kysela <perex@perex.cz>
16d5ac70f0Sopenharmony_ci*/
17d5ac70f0Sopenharmony_ci
18d5ac70f0Sopenharmony_ci#include "tplg_local.h"
19d5ac70f0Sopenharmony_ci
20d5ac70f0Sopenharmony_ci#define SAVE_ALLOC_SHIFT	(13)	/* 8192 bytes */
21d5ac70f0Sopenharmony_ci#define PRINT_ALLOC_SHIFT	(10)	/* 1024 bytes */
22d5ac70f0Sopenharmony_ci#define PRINT_BUF_SIZE_MAX	(1024 * 1024)
23d5ac70f0Sopenharmony_ci#define NEXT_CHUNK(val, shift)	((((val) >> (shift)) + 1) << (shift))
24d5ac70f0Sopenharmony_ci
25d5ac70f0Sopenharmony_civoid tplg_buf_init(struct tplg_buf *buf)
26d5ac70f0Sopenharmony_ci{
27d5ac70f0Sopenharmony_ci	buf->dst = NULL;
28d5ac70f0Sopenharmony_ci	buf->dst_len = 0;
29d5ac70f0Sopenharmony_ci	buf->printf_buf = NULL;
30d5ac70f0Sopenharmony_ci	buf->printf_buf_size = 0;
31d5ac70f0Sopenharmony_ci}
32d5ac70f0Sopenharmony_ci
33d5ac70f0Sopenharmony_civoid tplg_buf_free(struct tplg_buf *buf)
34d5ac70f0Sopenharmony_ci{
35d5ac70f0Sopenharmony_ci	free(buf->dst);
36d5ac70f0Sopenharmony_ci	free(buf->printf_buf);
37d5ac70f0Sopenharmony_ci}
38d5ac70f0Sopenharmony_ci
39d5ac70f0Sopenharmony_cichar *tplg_buf_detach(struct tplg_buf *buf)
40d5ac70f0Sopenharmony_ci{
41d5ac70f0Sopenharmony_ci	char *ret = buf->dst;
42d5ac70f0Sopenharmony_ci	free(buf->printf_buf);
43d5ac70f0Sopenharmony_ci	return ret;
44d5ac70f0Sopenharmony_ci}
45d5ac70f0Sopenharmony_ci
46d5ac70f0Sopenharmony_ciint tplg_save_printf(struct tplg_buf *dst, const char *pfx, const char *fmt, ...)
47d5ac70f0Sopenharmony_ci{
48d5ac70f0Sopenharmony_ci	va_list va;
49d5ac70f0Sopenharmony_ci	char *s;
50d5ac70f0Sopenharmony_ci	size_t n, l, t, pl;
51d5ac70f0Sopenharmony_ci	int ret = 0;
52d5ac70f0Sopenharmony_ci
53d5ac70f0Sopenharmony_ci	if (pfx == NULL)
54d5ac70f0Sopenharmony_ci		pfx = "";
55d5ac70f0Sopenharmony_ci
56d5ac70f0Sopenharmony_ci	va_start(va, fmt);
57d5ac70f0Sopenharmony_ci	n = vsnprintf(dst->printf_buf, dst->printf_buf_size, fmt, va);
58d5ac70f0Sopenharmony_ci	va_end(va);
59d5ac70f0Sopenharmony_ci
60d5ac70f0Sopenharmony_ci	if (n >= PRINT_BUF_SIZE_MAX) {
61d5ac70f0Sopenharmony_ci		ret = -EOVERFLOW;
62d5ac70f0Sopenharmony_ci		goto end;
63d5ac70f0Sopenharmony_ci	}
64d5ac70f0Sopenharmony_ci
65d5ac70f0Sopenharmony_ci	if (n >= dst->printf_buf_size) {
66d5ac70f0Sopenharmony_ci		t = NEXT_CHUNK(n + 1, PRINT_ALLOC_SHIFT);
67d5ac70f0Sopenharmony_ci		s = realloc(dst->printf_buf, t);
68d5ac70f0Sopenharmony_ci		if (!s) {
69d5ac70f0Sopenharmony_ci			ret = -ENOMEM;
70d5ac70f0Sopenharmony_ci			goto end;
71d5ac70f0Sopenharmony_ci		}
72d5ac70f0Sopenharmony_ci		dst->printf_buf = s;
73d5ac70f0Sopenharmony_ci		dst->printf_buf_size = t;
74d5ac70f0Sopenharmony_ci		va_start(va, fmt);
75d5ac70f0Sopenharmony_ci		n = vsnprintf(dst->printf_buf, n + 1, fmt, va);
76d5ac70f0Sopenharmony_ci		va_end(va);
77d5ac70f0Sopenharmony_ci	}
78d5ac70f0Sopenharmony_ci
79d5ac70f0Sopenharmony_ci	pl = strlen(pfx);
80d5ac70f0Sopenharmony_ci	l = dst->dst_len;
81d5ac70f0Sopenharmony_ci	t = l + pl + n + 1;
82d5ac70f0Sopenharmony_ci	/* allocate chunks */
83d5ac70f0Sopenharmony_ci	if (dst->dst == NULL ||
84d5ac70f0Sopenharmony_ci	    (l >> SAVE_ALLOC_SHIFT) != (t >> SAVE_ALLOC_SHIFT)) {
85d5ac70f0Sopenharmony_ci		s = realloc(dst->dst, NEXT_CHUNK(t, SAVE_ALLOC_SHIFT));
86d5ac70f0Sopenharmony_ci		if (s == NULL) {
87d5ac70f0Sopenharmony_ci			ret = -ENOMEM;
88d5ac70f0Sopenharmony_ci			goto end;
89d5ac70f0Sopenharmony_ci		}
90d5ac70f0Sopenharmony_ci	} else {
91d5ac70f0Sopenharmony_ci		s = dst->dst;
92d5ac70f0Sopenharmony_ci	}
93d5ac70f0Sopenharmony_ci
94d5ac70f0Sopenharmony_ci	if (pl > 0)
95d5ac70f0Sopenharmony_ci		strcpy(s + l, pfx);
96d5ac70f0Sopenharmony_ci	strcpy(s + l + pl, dst->printf_buf);
97d5ac70f0Sopenharmony_ci	dst->dst = s;
98d5ac70f0Sopenharmony_ci	dst->dst_len = t - 1;
99d5ac70f0Sopenharmony_ciend:
100d5ac70f0Sopenharmony_ci	return ret;
101d5ac70f0Sopenharmony_ci}
102d5ac70f0Sopenharmony_ci
103d5ac70f0Sopenharmony_ciint tplg_nice_value_format(char *dst, size_t dst_size, unsigned int value)
104d5ac70f0Sopenharmony_ci{
105d5ac70f0Sopenharmony_ci	if ((value % 1000) != 0) {
106d5ac70f0Sopenharmony_ci		if (value > 0xfffffff0)
107d5ac70f0Sopenharmony_ci			return snprintf(dst, dst_size, "%d", (int)value);
108d5ac70f0Sopenharmony_ci		if (value >= 0xffff0000)
109d5ac70f0Sopenharmony_ci			return snprintf(dst, dst_size, "0x%x", value);
110d5ac70f0Sopenharmony_ci	}
111d5ac70f0Sopenharmony_ci	return snprintf(dst, dst_size, "%u", value);
112d5ac70f0Sopenharmony_ci}
113d5ac70f0Sopenharmony_ci
114d5ac70f0Sopenharmony_cistatic int tplg_pprint_integer(snd_config_t *n, char **ret)
115d5ac70f0Sopenharmony_ci{
116d5ac70f0Sopenharmony_ci	long lval;
117d5ac70f0Sopenharmony_ci	int err, type;
118d5ac70f0Sopenharmony_ci	char buf[16];
119d5ac70f0Sopenharmony_ci
120d5ac70f0Sopenharmony_ci	type = snd_config_get_type(n);
121d5ac70f0Sopenharmony_ci	if (type == SND_CONFIG_TYPE_INTEGER) {
122d5ac70f0Sopenharmony_ci		err = snd_config_get_integer(n, &lval);
123d5ac70f0Sopenharmony_ci		if (err < 0)
124d5ac70f0Sopenharmony_ci			return err;
125d5ac70f0Sopenharmony_ci		if (lval < INT_MIN || lval > UINT_MAX)
126d5ac70f0Sopenharmony_ci			return snd_config_get_ascii(n, ret);
127d5ac70f0Sopenharmony_ci	} else if (type == SND_CONFIG_TYPE_INTEGER64) {
128d5ac70f0Sopenharmony_ci		long long llval;
129d5ac70f0Sopenharmony_ci		err = snd_config_get_integer64(n, &llval);
130d5ac70f0Sopenharmony_ci		if (err < 0)
131d5ac70f0Sopenharmony_ci			return err;
132d5ac70f0Sopenharmony_ci		if (llval < INT_MIN || llval > UINT_MAX)
133d5ac70f0Sopenharmony_ci			return snd_config_get_ascii(n, ret);
134d5ac70f0Sopenharmony_ci		lval = llval;
135d5ac70f0Sopenharmony_ci	} else {
136d5ac70f0Sopenharmony_ci		lval = 0;
137d5ac70f0Sopenharmony_ci	}
138d5ac70f0Sopenharmony_ci	err = tplg_nice_value_format(buf, sizeof(buf), (unsigned int)lval);
139d5ac70f0Sopenharmony_ci	if (err < 0)
140d5ac70f0Sopenharmony_ci		return err;
141d5ac70f0Sopenharmony_ci	*ret = strdup(buf);
142d5ac70f0Sopenharmony_ci	if (*ret == NULL)
143d5ac70f0Sopenharmony_ci		return -ENOMEM;
144d5ac70f0Sopenharmony_ci	return 0;
145d5ac70f0Sopenharmony_ci}
146d5ac70f0Sopenharmony_ci
147d5ac70f0Sopenharmony_cistatic int _compar(const void *a, const void *b)
148d5ac70f0Sopenharmony_ci{
149d5ac70f0Sopenharmony_ci	const snd_config_t *c1 = *(snd_config_t **)a;
150d5ac70f0Sopenharmony_ci	const snd_config_t *c2 = *(snd_config_t **)b;
151d5ac70f0Sopenharmony_ci	const char *id1, *id2;
152d5ac70f0Sopenharmony_ci	if (snd_config_get_id(c1, &id1)) return 0;
153d5ac70f0Sopenharmony_ci	if (snd_config_get_id(c2, &id2)) return 0;
154d5ac70f0Sopenharmony_ci	return strcmp(id1, id2);
155d5ac70f0Sopenharmony_ci}
156d5ac70f0Sopenharmony_ci
157d5ac70f0Sopenharmony_cistatic snd_config_t *sort_config(const char *id, snd_config_t *src)
158d5ac70f0Sopenharmony_ci{
159d5ac70f0Sopenharmony_ci	snd_config_t *dst, **a;
160d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
161d5ac70f0Sopenharmony_ci	int index, array, count;
162d5ac70f0Sopenharmony_ci
163d5ac70f0Sopenharmony_ci	if (snd_config_get_type(src) != SND_CONFIG_TYPE_COMPOUND) {
164d5ac70f0Sopenharmony_ci		if (snd_config_copy(&dst, src) >= 0)
165d5ac70f0Sopenharmony_ci			return dst;
166d5ac70f0Sopenharmony_ci		return NULL;
167d5ac70f0Sopenharmony_ci	}
168d5ac70f0Sopenharmony_ci	count = 0;
169d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, src)
170d5ac70f0Sopenharmony_ci		count++;
171d5ac70f0Sopenharmony_ci	a = malloc(sizeof(dst) * count);
172d5ac70f0Sopenharmony_ci	if (a == NULL)
173d5ac70f0Sopenharmony_ci		return NULL;
174d5ac70f0Sopenharmony_ci	array = snd_config_is_array(src);
175d5ac70f0Sopenharmony_ci	index = 0;
176d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, src) {
177d5ac70f0Sopenharmony_ci		snd_config_t *s = snd_config_iterator_entry(i);
178d5ac70f0Sopenharmony_ci		a[index++] = s;
179d5ac70f0Sopenharmony_ci	}
180d5ac70f0Sopenharmony_ci	if (array <= 0)
181d5ac70f0Sopenharmony_ci		qsort(a, count, sizeof(a[0]), _compar);
182d5ac70f0Sopenharmony_ci	if (snd_config_make_compound(&dst, id, count == 1))
183d5ac70f0Sopenharmony_ci		goto lerr;
184d5ac70f0Sopenharmony_ci	for (index = 0; index < count; index++) {
185d5ac70f0Sopenharmony_ci		snd_config_t *s = a[index];
186d5ac70f0Sopenharmony_ci		const char *id2;
187d5ac70f0Sopenharmony_ci		if (snd_config_get_id(s, &id2)) {
188d5ac70f0Sopenharmony_ci			snd_config_delete(dst);
189d5ac70f0Sopenharmony_ci			goto lerr;
190d5ac70f0Sopenharmony_ci		}
191d5ac70f0Sopenharmony_ci		s = sort_config(id2, s);
192d5ac70f0Sopenharmony_ci		if (s == NULL || snd_config_add(dst, s)) {
193d5ac70f0Sopenharmony_ci			if (s)
194d5ac70f0Sopenharmony_ci				snd_config_delete(s);
195d5ac70f0Sopenharmony_ci			snd_config_delete(dst);
196d5ac70f0Sopenharmony_ci			goto lerr;
197d5ac70f0Sopenharmony_ci		}
198d5ac70f0Sopenharmony_ci	}
199d5ac70f0Sopenharmony_ci	free(a);
200d5ac70f0Sopenharmony_ci	return dst;
201d5ac70f0Sopenharmony_cilerr:
202d5ac70f0Sopenharmony_ci	free(a);
203d5ac70f0Sopenharmony_ci	return NULL;
204d5ac70f0Sopenharmony_ci}
205d5ac70f0Sopenharmony_ci
206d5ac70f0Sopenharmony_cistatic int tplg_check_quoted(const unsigned char *p)
207d5ac70f0Sopenharmony_ci{
208d5ac70f0Sopenharmony_ci	for ( ; *p != '\0'; p++) {
209d5ac70f0Sopenharmony_ci		switch (*p) {
210d5ac70f0Sopenharmony_ci		case ' ':
211d5ac70f0Sopenharmony_ci		case '=':
212d5ac70f0Sopenharmony_ci		case ';':
213d5ac70f0Sopenharmony_ci		case ',':
214d5ac70f0Sopenharmony_ci		case '.':
215d5ac70f0Sopenharmony_ci		case '{':
216d5ac70f0Sopenharmony_ci		case '}':
217d5ac70f0Sopenharmony_ci		case '\'':
218d5ac70f0Sopenharmony_ci		case '"':
219d5ac70f0Sopenharmony_ci			return 1;
220d5ac70f0Sopenharmony_ci		default:
221d5ac70f0Sopenharmony_ci			if (*p <= 31 || *p >= 127)
222d5ac70f0Sopenharmony_ci				return 1;
223d5ac70f0Sopenharmony_ci
224d5ac70f0Sopenharmony_ci		}
225d5ac70f0Sopenharmony_ci	}
226d5ac70f0Sopenharmony_ci	return 0;
227d5ac70f0Sopenharmony_ci}
228d5ac70f0Sopenharmony_ci
229d5ac70f0Sopenharmony_cistatic int tplg_save_quoted(struct tplg_buf *dst, const char *str)
230d5ac70f0Sopenharmony_ci{
231d5ac70f0Sopenharmony_ci	static const char nibble[16] = "0123456789abcdef";
232d5ac70f0Sopenharmony_ci	unsigned char *p, *d, *t;
233d5ac70f0Sopenharmony_ci	int c;
234d5ac70f0Sopenharmony_ci
235d5ac70f0Sopenharmony_ci	d = t = alloca(strlen(str) * 5 + 1 + 1);
236d5ac70f0Sopenharmony_ci	for (p = (unsigned char *)str; *p != '\0'; p++) {
237d5ac70f0Sopenharmony_ci		c = *p;
238d5ac70f0Sopenharmony_ci		switch (c) {
239d5ac70f0Sopenharmony_ci		case '\n':
240d5ac70f0Sopenharmony_ci			*t++ = '\\';
241d5ac70f0Sopenharmony_ci			*t++ = 'n';
242d5ac70f0Sopenharmony_ci			break;
243d5ac70f0Sopenharmony_ci		case '\t':
244d5ac70f0Sopenharmony_ci			*t++ = '\\';
245d5ac70f0Sopenharmony_ci			*t++ = 't';
246d5ac70f0Sopenharmony_ci			break;
247d5ac70f0Sopenharmony_ci		case '\v':
248d5ac70f0Sopenharmony_ci			*t++ = '\\';
249d5ac70f0Sopenharmony_ci			*t++ = 'v';
250d5ac70f0Sopenharmony_ci			break;
251d5ac70f0Sopenharmony_ci		case '\b':
252d5ac70f0Sopenharmony_ci			*t++ = '\\';
253d5ac70f0Sopenharmony_ci			*t++ = 'b';
254d5ac70f0Sopenharmony_ci			break;
255d5ac70f0Sopenharmony_ci		case '\r':
256d5ac70f0Sopenharmony_ci			*t++ = '\\';
257d5ac70f0Sopenharmony_ci			*t++ = 'r';
258d5ac70f0Sopenharmony_ci			break;
259d5ac70f0Sopenharmony_ci		case '\f':
260d5ac70f0Sopenharmony_ci			*t++ = '\\';
261d5ac70f0Sopenharmony_ci			*t++ = 'f';
262d5ac70f0Sopenharmony_ci			break;
263d5ac70f0Sopenharmony_ci		case '\'':
264d5ac70f0Sopenharmony_ci			*t++ = '\\';
265d5ac70f0Sopenharmony_ci			*t++ = c;
266d5ac70f0Sopenharmony_ci			break;
267d5ac70f0Sopenharmony_ci		default:
268d5ac70f0Sopenharmony_ci			if (c >= 32 && c <= 126) {
269d5ac70f0Sopenharmony_ci				*t++ = c;
270d5ac70f0Sopenharmony_ci			} else {
271d5ac70f0Sopenharmony_ci				*t++ = '\\';
272d5ac70f0Sopenharmony_ci				*t++ = 'x';
273d5ac70f0Sopenharmony_ci				*t++ = nibble[(c >> 4) & 0x0f];
274d5ac70f0Sopenharmony_ci				*t++ = nibble[(c >> 0) & 0x0f];
275d5ac70f0Sopenharmony_ci			}
276d5ac70f0Sopenharmony_ci			break;
277d5ac70f0Sopenharmony_ci		}
278d5ac70f0Sopenharmony_ci	}
279d5ac70f0Sopenharmony_ci	*t = '\0';
280d5ac70f0Sopenharmony_ci	return tplg_save_printf(dst, NULL, "'%s'", d);
281d5ac70f0Sopenharmony_ci}
282d5ac70f0Sopenharmony_ci
283d5ac70f0Sopenharmony_cistatic int tplg_save_string(struct tplg_buf *dst, const char *str, int id)
284d5ac70f0Sopenharmony_ci{
285d5ac70f0Sopenharmony_ci	const unsigned char *p = (const unsigned char *)str;
286d5ac70f0Sopenharmony_ci
287d5ac70f0Sopenharmony_ci	if (!p || !*p)
288d5ac70f0Sopenharmony_ci		return tplg_save_printf(dst, NULL, "''");
289d5ac70f0Sopenharmony_ci
290d5ac70f0Sopenharmony_ci	if (!id && ((*p >= '0' && *p <= '9') || *p == '-'))
291d5ac70f0Sopenharmony_ci		return tplg_save_quoted(dst, str);
292d5ac70f0Sopenharmony_ci
293d5ac70f0Sopenharmony_ci	if (tplg_check_quoted(p))
294d5ac70f0Sopenharmony_ci		return tplg_save_quoted(dst, str);
295d5ac70f0Sopenharmony_ci
296d5ac70f0Sopenharmony_ci	return tplg_save_printf(dst, NULL, "%s", str);
297d5ac70f0Sopenharmony_ci}
298d5ac70f0Sopenharmony_ci
299d5ac70f0Sopenharmony_cistatic int save_config(struct tplg_buf *dst, int level, const char *delim, snd_config_t *src)
300d5ac70f0Sopenharmony_ci{
301d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
302d5ac70f0Sopenharmony_ci	snd_config_t *s;
303d5ac70f0Sopenharmony_ci	const char *id;
304d5ac70f0Sopenharmony_ci	char *pfx;
305d5ac70f0Sopenharmony_ci	unsigned int count;
306d5ac70f0Sopenharmony_ci	int type, err, quoted, array;
307d5ac70f0Sopenharmony_ci
308d5ac70f0Sopenharmony_ci	if (delim == NULL)
309d5ac70f0Sopenharmony_ci		delim = "";
310d5ac70f0Sopenharmony_ci
311d5ac70f0Sopenharmony_ci	type = snd_config_get_type(src);
312d5ac70f0Sopenharmony_ci	if (type != SND_CONFIG_TYPE_COMPOUND) {
313d5ac70f0Sopenharmony_ci		char *val;
314d5ac70f0Sopenharmony_ci		if (type == SND_CONFIG_TYPE_INTEGER ||
315d5ac70f0Sopenharmony_ci		    type == SND_CONFIG_TYPE_INTEGER64) {
316d5ac70f0Sopenharmony_ci			err = tplg_pprint_integer(src, &val);
317d5ac70f0Sopenharmony_ci		} else {
318d5ac70f0Sopenharmony_ci			err = snd_config_get_ascii(src, &val);
319d5ac70f0Sopenharmony_ci		}
320d5ac70f0Sopenharmony_ci		if (err < 0)
321d5ac70f0Sopenharmony_ci			return err;
322d5ac70f0Sopenharmony_ci		if (type == SND_CONFIG_TYPE_STRING) {
323d5ac70f0Sopenharmony_ci			/* hexa array pretty print */
324d5ac70f0Sopenharmony_ci			id = strchr(val, '\n');
325d5ac70f0Sopenharmony_ci			if (id) {
326d5ac70f0Sopenharmony_ci				err = tplg_save_printf(dst, NULL, "\n");
327d5ac70f0Sopenharmony_ci				if (err < 0)
328d5ac70f0Sopenharmony_ci					goto retval;
329d5ac70f0Sopenharmony_ci				for (id++; *id == '\t'; id++) {
330d5ac70f0Sopenharmony_ci					err = tplg_save_printf(dst, NULL, "\t");
331d5ac70f0Sopenharmony_ci					if (err < 0)
332d5ac70f0Sopenharmony_ci						goto retval;
333d5ac70f0Sopenharmony_ci				}
334d5ac70f0Sopenharmony_ci				delim = "";
335d5ac70f0Sopenharmony_ci			}
336d5ac70f0Sopenharmony_ci			err = tplg_save_printf(dst, NULL, "%s'%s'\n", delim, val);
337d5ac70f0Sopenharmony_ci		} else {
338d5ac70f0Sopenharmony_ci			err = tplg_save_printf(dst, NULL, "%s%s\n", delim, val);
339d5ac70f0Sopenharmony_ci		}
340d5ac70f0Sopenharmony_ciretval:
341d5ac70f0Sopenharmony_ci		free(val);
342d5ac70f0Sopenharmony_ci		return err;
343d5ac70f0Sopenharmony_ci	}
344d5ac70f0Sopenharmony_ci
345d5ac70f0Sopenharmony_ci	count = 0;
346d5ac70f0Sopenharmony_ci	quoted = 0;
347d5ac70f0Sopenharmony_ci	array = snd_config_is_array(src);
348d5ac70f0Sopenharmony_ci	s = NULL;
349d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, src) {
350d5ac70f0Sopenharmony_ci		s = snd_config_iterator_entry(i);
351d5ac70f0Sopenharmony_ci		err = snd_config_get_id(s, &id);
352d5ac70f0Sopenharmony_ci		if (err < 0)
353d5ac70f0Sopenharmony_ci			return err;
354d5ac70f0Sopenharmony_ci		if (!quoted && tplg_check_quoted((unsigned char *)id))
355d5ac70f0Sopenharmony_ci			quoted = 1;
356d5ac70f0Sopenharmony_ci		count++;
357d5ac70f0Sopenharmony_ci	}
358d5ac70f0Sopenharmony_ci	if (count == 0)
359d5ac70f0Sopenharmony_ci		return 0;
360d5ac70f0Sopenharmony_ci
361d5ac70f0Sopenharmony_ci	if (count == 1) {
362d5ac70f0Sopenharmony_ci		err = snd_config_get_id(s, &id);
363d5ac70f0Sopenharmony_ci		if (err >= 0 && level > 0)
364d5ac70f0Sopenharmony_ci			err = tplg_save_printf(dst, NULL, ".");
365d5ac70f0Sopenharmony_ci		if (err >= 0)
366d5ac70f0Sopenharmony_ci			err = tplg_save_string(dst, id, 1);
367d5ac70f0Sopenharmony_ci		if (err >= 0)
368d5ac70f0Sopenharmony_ci			err = save_config(dst, level, " ", s);
369d5ac70f0Sopenharmony_ci		return err;
370d5ac70f0Sopenharmony_ci	}
371d5ac70f0Sopenharmony_ci
372d5ac70f0Sopenharmony_ci	pfx = alloca(level + 1);
373d5ac70f0Sopenharmony_ci	memset(pfx, '\t', level);
374d5ac70f0Sopenharmony_ci	pfx[level] = '\0';
375d5ac70f0Sopenharmony_ci
376d5ac70f0Sopenharmony_ci	if (level > 0) {
377d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, NULL, "%s%s\n", delim,
378d5ac70f0Sopenharmony_ci				       array > 0 ? "[" : "{");
379d5ac70f0Sopenharmony_ci		if (err < 0)
380d5ac70f0Sopenharmony_ci			return err;
381d5ac70f0Sopenharmony_ci	}
382d5ac70f0Sopenharmony_ci
383d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, src) {
384d5ac70f0Sopenharmony_ci		s = snd_config_iterator_entry(i);
385d5ac70f0Sopenharmony_ci		const char *id;
386d5ac70f0Sopenharmony_ci		err = snd_config_get_id(s, &id);
387d5ac70f0Sopenharmony_ci		if (err < 0)
388d5ac70f0Sopenharmony_ci			return err;
389d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "");
390d5ac70f0Sopenharmony_ci		if (err < 0)
391d5ac70f0Sopenharmony_ci			return err;
392d5ac70f0Sopenharmony_ci		if (array <= 0) {
393d5ac70f0Sopenharmony_ci			delim = " ";
394d5ac70f0Sopenharmony_ci			if (quoted) {
395d5ac70f0Sopenharmony_ci				err = tplg_save_quoted(dst, id);
396d5ac70f0Sopenharmony_ci			} else {
397d5ac70f0Sopenharmony_ci				err = tplg_save_string(dst, id, 1);
398d5ac70f0Sopenharmony_ci			}
399d5ac70f0Sopenharmony_ci			if (err < 0)
400d5ac70f0Sopenharmony_ci				return err;
401d5ac70f0Sopenharmony_ci		} else {
402d5ac70f0Sopenharmony_ci			delim = "";
403d5ac70f0Sopenharmony_ci		}
404d5ac70f0Sopenharmony_ci		err = save_config(dst, level + 1, delim, s);
405d5ac70f0Sopenharmony_ci		if (err < 0)
406d5ac70f0Sopenharmony_ci			return err;
407d5ac70f0Sopenharmony_ci	}
408d5ac70f0Sopenharmony_ci
409d5ac70f0Sopenharmony_ci	if (level > 0) {
410d5ac70f0Sopenharmony_ci		pfx[level - 1] = '\0';
411d5ac70f0Sopenharmony_ci		err = tplg_save_printf(dst, pfx, "%s\n",
412d5ac70f0Sopenharmony_ci				       array > 0 ? "]" : "}");
413d5ac70f0Sopenharmony_ci		if (err < 0)
414d5ac70f0Sopenharmony_ci			return err;
415d5ac70f0Sopenharmony_ci	}
416d5ac70f0Sopenharmony_ci
417d5ac70f0Sopenharmony_ci	return 0;
418d5ac70f0Sopenharmony_ci}
419d5ac70f0Sopenharmony_ci
420d5ac70f0Sopenharmony_cistatic int tplg_save(snd_tplg_t *tplg, struct tplg_buf *dst,
421d5ac70f0Sopenharmony_ci		     int gindex, const char *prefix)
422d5ac70f0Sopenharmony_ci{
423d5ac70f0Sopenharmony_ci	struct tplg_table *tptr;
424d5ac70f0Sopenharmony_ci	struct tplg_elem *elem;
425d5ac70f0Sopenharmony_ci	struct list_head *list, *pos;
426d5ac70f0Sopenharmony_ci	char pfx2[16];
427d5ac70f0Sopenharmony_ci	unsigned int index;
428d5ac70f0Sopenharmony_ci	int err, count;
429d5ac70f0Sopenharmony_ci
430d5ac70f0Sopenharmony_ci	snprintf(pfx2, sizeof(pfx2), "%s\t", prefix ?: "");
431d5ac70f0Sopenharmony_ci
432d5ac70f0Sopenharmony_ci	/* write all blocks */
433d5ac70f0Sopenharmony_ci	for (index = 0; index < tplg_table_items; index++) {
434d5ac70f0Sopenharmony_ci		tptr = &tplg_table[index];
435d5ac70f0Sopenharmony_ci		list = (struct list_head *)((void *)tplg + tptr->loff);
436d5ac70f0Sopenharmony_ci
437d5ac70f0Sopenharmony_ci		/* count elements */
438d5ac70f0Sopenharmony_ci		count = 0;
439d5ac70f0Sopenharmony_ci		list_for_each(pos, list) {
440d5ac70f0Sopenharmony_ci			elem = list_entry(pos, struct tplg_elem, list);
441d5ac70f0Sopenharmony_ci			if (gindex >= 0 && elem->index != gindex)
442d5ac70f0Sopenharmony_ci				continue;
443d5ac70f0Sopenharmony_ci			if (tptr->save == NULL && tptr->gsave == NULL) {
444d5ac70f0Sopenharmony_ci				SNDERR("unable to create %s block (no callback)",
445d5ac70f0Sopenharmony_ci				       tptr->id);
446d5ac70f0Sopenharmony_ci				err = -ENXIO;
447d5ac70f0Sopenharmony_ci				goto _err;
448d5ac70f0Sopenharmony_ci			}
449d5ac70f0Sopenharmony_ci			if (tptr->save)
450d5ac70f0Sopenharmony_ci				count++;
451d5ac70f0Sopenharmony_ci		}
452d5ac70f0Sopenharmony_ci
453d5ac70f0Sopenharmony_ci		if (count == 0)
454d5ac70f0Sopenharmony_ci			continue;
455d5ac70f0Sopenharmony_ci
456d5ac70f0Sopenharmony_ci		if (count > 1) {
457d5ac70f0Sopenharmony_ci			err = tplg_save_printf(dst, prefix, "%s {\n",
458d5ac70f0Sopenharmony_ci					       elem->table ?
459d5ac70f0Sopenharmony_ci						elem->table->id : "_NOID_");
460d5ac70f0Sopenharmony_ci		} else {
461d5ac70f0Sopenharmony_ci			err = tplg_save_printf(dst, prefix, "%s.",
462d5ac70f0Sopenharmony_ci					       elem->table ?
463d5ac70f0Sopenharmony_ci						elem->table->id : "_NOID_");
464d5ac70f0Sopenharmony_ci		}
465d5ac70f0Sopenharmony_ci
466d5ac70f0Sopenharmony_ci		if (err < 0)
467d5ac70f0Sopenharmony_ci			goto _err;
468d5ac70f0Sopenharmony_ci
469d5ac70f0Sopenharmony_ci		list_for_each(pos, list) {
470d5ac70f0Sopenharmony_ci			elem = list_entry(pos, struct tplg_elem, list);
471d5ac70f0Sopenharmony_ci			if (gindex >= 0 && elem->index != gindex)
472d5ac70f0Sopenharmony_ci				continue;
473d5ac70f0Sopenharmony_ci			if (count > 1) {
474d5ac70f0Sopenharmony_ci				err = tplg_save_printf(dst, pfx2, "");
475d5ac70f0Sopenharmony_ci				if (err < 0)
476d5ac70f0Sopenharmony_ci					goto _err;
477d5ac70f0Sopenharmony_ci			}
478d5ac70f0Sopenharmony_ci			err = tptr->save(tplg, elem, dst, count > 1 ? pfx2 : prefix);
479d5ac70f0Sopenharmony_ci			if (err < 0) {
480d5ac70f0Sopenharmony_ci				SNDERR("failed to save %s elements: %s",
481d5ac70f0Sopenharmony_ci				       tptr->id, snd_strerror(-err));
482d5ac70f0Sopenharmony_ci				goto _err;
483d5ac70f0Sopenharmony_ci			}
484d5ac70f0Sopenharmony_ci		}
485d5ac70f0Sopenharmony_ci		if (count > 1) {
486d5ac70f0Sopenharmony_ci			err = tplg_save_printf(dst, prefix, "}\n");
487d5ac70f0Sopenharmony_ci			if (err < 0)
488d5ac70f0Sopenharmony_ci				goto _err;
489d5ac70f0Sopenharmony_ci		}
490d5ac70f0Sopenharmony_ci	}
491d5ac70f0Sopenharmony_ci
492d5ac70f0Sopenharmony_ci	/* save globals */
493d5ac70f0Sopenharmony_ci	for (index = 0; index < tplg_table_items; index++) {
494d5ac70f0Sopenharmony_ci		tptr = &tplg_table[index];
495d5ac70f0Sopenharmony_ci		if (tptr->gsave) {
496d5ac70f0Sopenharmony_ci			err = tptr->gsave(tplg, gindex, dst, prefix);
497d5ac70f0Sopenharmony_ci			if (err < 0)
498d5ac70f0Sopenharmony_ci				goto _err;
499d5ac70f0Sopenharmony_ci		}
500d5ac70f0Sopenharmony_ci	}
501d5ac70f0Sopenharmony_ci
502d5ac70f0Sopenharmony_ci	return 0;
503d5ac70f0Sopenharmony_ci
504d5ac70f0Sopenharmony_ci_err:
505d5ac70f0Sopenharmony_ci	return err;
506d5ac70f0Sopenharmony_ci}
507d5ac70f0Sopenharmony_ci
508d5ac70f0Sopenharmony_cistatic int tplg_index_compar(const void *a, const void *b)
509d5ac70f0Sopenharmony_ci{
510d5ac70f0Sopenharmony_ci	const int *a1 = a, *b1 = b;
511d5ac70f0Sopenharmony_ci	return *a1 - *b1;
512d5ac70f0Sopenharmony_ci}
513d5ac70f0Sopenharmony_ci
514d5ac70f0Sopenharmony_cistatic int tplg_index_groups(snd_tplg_t *tplg, int **indexes)
515d5ac70f0Sopenharmony_ci{
516d5ac70f0Sopenharmony_ci	struct tplg_table *tptr;
517d5ac70f0Sopenharmony_ci	struct tplg_elem *elem;
518d5ac70f0Sopenharmony_ci	struct list_head *list, *pos;
519d5ac70f0Sopenharmony_ci	unsigned int index, j, count, size;
520d5ac70f0Sopenharmony_ci	int *a, *b;
521d5ac70f0Sopenharmony_ci
522d5ac70f0Sopenharmony_ci	count = 0;
523d5ac70f0Sopenharmony_ci	size = 16;
524d5ac70f0Sopenharmony_ci	a = malloc(size * sizeof(a[0]));
525d5ac70f0Sopenharmony_ci
526d5ac70f0Sopenharmony_ci	for (index = 0; index < tplg_table_items; index++) {
527d5ac70f0Sopenharmony_ci		tptr = &tplg_table[index];
528d5ac70f0Sopenharmony_ci		list = (struct list_head *)((void *)tplg + tptr->loff);
529d5ac70f0Sopenharmony_ci		list_for_each(pos, list) {
530d5ac70f0Sopenharmony_ci			elem = list_entry(pos, struct tplg_elem, list);
531d5ac70f0Sopenharmony_ci			for (j = 0; j < count; j++) {
532d5ac70f0Sopenharmony_ci				if (a[j] == elem->index)
533d5ac70f0Sopenharmony_ci					break;
534d5ac70f0Sopenharmony_ci			}
535d5ac70f0Sopenharmony_ci			if (j < count)
536d5ac70f0Sopenharmony_ci				continue;
537d5ac70f0Sopenharmony_ci			if (count + 1 >= size) {
538d5ac70f0Sopenharmony_ci				size += 8;
539d5ac70f0Sopenharmony_ci				b = realloc(a, size * sizeof(a[0]));
540d5ac70f0Sopenharmony_ci				if (b == NULL) {
541d5ac70f0Sopenharmony_ci					free(a);
542d5ac70f0Sopenharmony_ci					return -ENOMEM;
543d5ac70f0Sopenharmony_ci				}
544d5ac70f0Sopenharmony_ci				a = b;
545d5ac70f0Sopenharmony_ci			}
546d5ac70f0Sopenharmony_ci			a[count++] = elem->index;
547d5ac70f0Sopenharmony_ci		}
548d5ac70f0Sopenharmony_ci	}
549d5ac70f0Sopenharmony_ci	a[count] = -1;
550d5ac70f0Sopenharmony_ci
551d5ac70f0Sopenharmony_ci	qsort(a, count, sizeof(a[0]), tplg_index_compar);
552d5ac70f0Sopenharmony_ci
553d5ac70f0Sopenharmony_ci	*indexes = a;
554d5ac70f0Sopenharmony_ci	return 0;
555d5ac70f0Sopenharmony_ci}
556d5ac70f0Sopenharmony_ci
557d5ac70f0Sopenharmony_ciint snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags)
558d5ac70f0Sopenharmony_ci{
559d5ac70f0Sopenharmony_ci	struct tplg_buf buf, buf2;
560d5ac70f0Sopenharmony_ci	snd_input_t *in;
561d5ac70f0Sopenharmony_ci	snd_config_t *top, *top2;
562d5ac70f0Sopenharmony_ci	int *indexes, *a;
563d5ac70f0Sopenharmony_ci	int err;
564d5ac70f0Sopenharmony_ci
565d5ac70f0Sopenharmony_ci	assert(tplg);
566d5ac70f0Sopenharmony_ci	assert(dst);
567d5ac70f0Sopenharmony_ci	*dst = NULL;
568d5ac70f0Sopenharmony_ci
569d5ac70f0Sopenharmony_ci	tplg_buf_init(&buf);
570d5ac70f0Sopenharmony_ci
571d5ac70f0Sopenharmony_ci	if (flags & SND_TPLG_SAVE_GROUPS) {
572d5ac70f0Sopenharmony_ci		err = tplg_index_groups(tplg, &indexes);
573d5ac70f0Sopenharmony_ci		if (err < 0)
574d5ac70f0Sopenharmony_ci			return err;
575d5ac70f0Sopenharmony_ci		for (a = indexes; err >= 0 && *a >= 0; a++) {
576d5ac70f0Sopenharmony_ci			err = tplg_save_printf(&buf, NULL,
577d5ac70f0Sopenharmony_ci					       "IndexGroup.%d {\n",
578d5ac70f0Sopenharmony_ci					       *a);
579d5ac70f0Sopenharmony_ci			if (err >= 0)
580d5ac70f0Sopenharmony_ci				err = tplg_save(tplg, &buf, *a, "\t");
581d5ac70f0Sopenharmony_ci			if (err >= 0)
582d5ac70f0Sopenharmony_ci				err = tplg_save_printf(&buf, NULL, "}\n");
583d5ac70f0Sopenharmony_ci		}
584d5ac70f0Sopenharmony_ci		free(indexes);
585d5ac70f0Sopenharmony_ci	} else {
586d5ac70f0Sopenharmony_ci		err = tplg_save(tplg, &buf, -1, NULL);
587d5ac70f0Sopenharmony_ci	}
588d5ac70f0Sopenharmony_ci
589d5ac70f0Sopenharmony_ci	if (err < 0)
590d5ac70f0Sopenharmony_ci		goto _err;
591d5ac70f0Sopenharmony_ci
592d5ac70f0Sopenharmony_ci	if (buf.dst == NULL) {
593d5ac70f0Sopenharmony_ci		err = -EINVAL;
594d5ac70f0Sopenharmony_ci		goto _err;
595d5ac70f0Sopenharmony_ci	}
596d5ac70f0Sopenharmony_ci
597d5ac70f0Sopenharmony_ci	if (flags & SND_TPLG_SAVE_NOCHECK) {
598d5ac70f0Sopenharmony_ci		*dst = tplg_buf_detach(&buf);
599d5ac70f0Sopenharmony_ci		return 0;
600d5ac70f0Sopenharmony_ci	}
601d5ac70f0Sopenharmony_ci
602d5ac70f0Sopenharmony_ci	/* always load configuration - check */
603d5ac70f0Sopenharmony_ci	err = snd_input_buffer_open(&in, buf.dst, strlen(buf.dst));
604d5ac70f0Sopenharmony_ci	if (err < 0) {
605d5ac70f0Sopenharmony_ci		SNDERR("could not create input buffer");
606d5ac70f0Sopenharmony_ci		goto _err;
607d5ac70f0Sopenharmony_ci	}
608d5ac70f0Sopenharmony_ci
609d5ac70f0Sopenharmony_ci	err = snd_config_top(&top);
610d5ac70f0Sopenharmony_ci	if (err < 0) {
611d5ac70f0Sopenharmony_ci		snd_input_close(in);
612d5ac70f0Sopenharmony_ci		goto _err;
613d5ac70f0Sopenharmony_ci	}
614d5ac70f0Sopenharmony_ci
615d5ac70f0Sopenharmony_ci	err = snd_config_load(top, in);
616d5ac70f0Sopenharmony_ci	snd_input_close(in);
617d5ac70f0Sopenharmony_ci	if (err < 0) {
618d5ac70f0Sopenharmony_ci		SNDERR("could not load configuration");
619d5ac70f0Sopenharmony_ci		snd_config_delete(top);
620d5ac70f0Sopenharmony_ci		goto _err;
621d5ac70f0Sopenharmony_ci	}
622d5ac70f0Sopenharmony_ci
623d5ac70f0Sopenharmony_ci	if (flags & SND_TPLG_SAVE_SORT) {
624d5ac70f0Sopenharmony_ci		top2 = sort_config(NULL, top);
625d5ac70f0Sopenharmony_ci		if (top2 == NULL) {
626d5ac70f0Sopenharmony_ci			SNDERR("could not sort configuration");
627d5ac70f0Sopenharmony_ci			snd_config_delete(top);
628d5ac70f0Sopenharmony_ci			err = -EINVAL;
629d5ac70f0Sopenharmony_ci			goto _err;
630d5ac70f0Sopenharmony_ci		}
631d5ac70f0Sopenharmony_ci		snd_config_delete(top);
632d5ac70f0Sopenharmony_ci		top = top2;
633d5ac70f0Sopenharmony_ci	}
634d5ac70f0Sopenharmony_ci
635d5ac70f0Sopenharmony_ci	tplg_buf_init(&buf2);
636d5ac70f0Sopenharmony_ci	err = save_config(&buf2, 0, NULL, top);
637d5ac70f0Sopenharmony_ci	snd_config_delete(top);
638d5ac70f0Sopenharmony_ci	if (err < 0) {
639d5ac70f0Sopenharmony_ci		SNDERR("could not save configuration");
640d5ac70f0Sopenharmony_ci		goto _err;
641d5ac70f0Sopenharmony_ci	}
642d5ac70f0Sopenharmony_ci
643d5ac70f0Sopenharmony_ci	tplg_buf_free(&buf);
644d5ac70f0Sopenharmony_ci	*dst = tplg_buf_detach(&buf2);
645d5ac70f0Sopenharmony_ci	return 0;
646d5ac70f0Sopenharmony_ci
647d5ac70f0Sopenharmony_ci_err:
648d5ac70f0Sopenharmony_ci	tplg_buf_free(&buf);
649d5ac70f0Sopenharmony_ci	*dst = NULL;
650d5ac70f0Sopenharmony_ci	return err;
651d5ac70f0Sopenharmony_ci}
652