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