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