1 /*
2   Copyright(c) 2019 Red Hat Inc.
3   Copyright(c) 2014-2015 Intel Corporation
4   Copyright(c) 2010-2011 Texas Instruments Incorporated,
5   All rights reserved.
6 
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of version 2 of the GNU General Public License as
9   published by the Free Software Foundation.
10 
11   This program is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   General Public License for more details.
15 
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
19   The full GNU General Public License is included in this distribution
20   in the file called LICENSE.GPL.
21 */
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stdint.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #include <getopt.h>
32 #include <assert.h>
33 
34 #include <alsa/asoundlib.h>
35 #include <alsa/topology.h>
36 #include "gettext.h"
37 #include "version.h"
38 
39 static snd_output_t *log;
40 
usage(const char * name)41 static void usage(const char *name)
42 {
43 	printf(
44 _("Usage: %s [OPTIONS]...\n"
45 "\n"
46 "-h, --help              help\n"
47 "-c, --compile=FILE      compile configuration file\n"
48 "-d, --decode=FILE       decode binary topology file\n"
49 "-n, --normalize=FILE    normalize configuration file\n"
50 "-u, --dump=FILE         dump (reparse) configuration file\n"
51 "-v, --verbose=LEVEL     set verbosity level (0...1)\n"
52 "-o, --output=FILE       set output file\n"
53 "-s, --sort              sort the identifiers in the normalized output\n"
54 "-g, --group             save configuration by group indexes\n"
55 "-x, --nocheck           save configuration without additional integrity checks\n"
56 "-z, --dapm-nosort       do not sort the DAPM widgets\n"
57 "-V, --version           print version\n"
58 ), name);
59 }
60 
version(const char * name)61 static void version(const char *name)
62 {
63 	printf(
64 _("%s version %s\n"
65 "libasound version %s\n"
66 "libatopology version %s\n"
67 ), name, SND_UTIL_VERSION_STR,
68    snd_asoundlib_version(), snd_tplg_version());
69 }
70 
load(const char * source_file,void ** dst,size_t * dst_size)71 static int load(const char *source_file, void **dst, size_t *dst_size)
72 {
73 	int fd;
74 	void *buf, *buf2;
75 	size_t size, pos;
76 	ssize_t r;
77 
78 	if (strcmp(source_file, "-") == 0) {
79 		fd = fileno(stdin);
80 	} else {
81 		fd = open(source_file, O_RDONLY);
82 		if (fd < 0) {
83 			fprintf(stderr, _("Unable to open input file '%s': %s\n"),
84 				source_file, strerror(-errno));
85 			return 1;
86 		}
87 	}
88 
89 	size = 16*1024;
90 	pos = 0;
91 	buf = malloc(size);
92 	if (buf == NULL)
93 		goto _nomem;
94 	while (1) {
95 		r = read(fd, buf + pos, size - pos);
96 		if (r < 0 && (errno == EAGAIN || errno == EINTR))
97 			continue;
98 		if (r <= 0)
99 			break;
100 		pos += r;
101 		size += 8*1024;
102 		buf2 = realloc(buf, size);
103 		if (buf2 == NULL) {
104 			free(buf);
105 			goto _nomem;
106 		}
107 		buf = buf2;
108 	}
109 	if (fd != fileno(stdin))
110 		close(fd);
111 	if (r < 0) {
112 		fprintf(stderr, _("Read error: %s\n"), strerror(-errno));
113 		free(buf);
114 		goto _err;
115 	}
116 
117 	*dst = buf;
118 	*dst_size = pos;
119 	return 0;
120 
121 _nomem:
122 	fprintf(stderr, _("No enough memory\n"));
123 _err:
124 	if (fd != fileno(stdin))
125 		close(fd);
126 	free(buf);
127 	return 1;
128 }
129 
load_topology(snd_tplg_t ** tplg,char * config,size_t config_size,int cflags)130 static int load_topology(snd_tplg_t **tplg, char *config,
131 			 size_t config_size, int cflags)
132 {
133 	int err;
134 
135 	*tplg = snd_tplg_create(cflags);
136 	if (*tplg == NULL) {
137 		fprintf(stderr, _("failed to create new topology context\n"));
138 		return 1;
139 	}
140 
141 	err = snd_tplg_load(*tplg, config, config_size);
142 	if (err < 0) {
143 		fprintf(stderr, _("Unable to load configuration: %s\n"),
144 			snd_strerror(-err));
145 		snd_tplg_free(*tplg);
146 		return 1;
147 	}
148 
149 	return 0;
150 }
151 
save(const char * output_file,void * buf,size_t size)152 static int save(const char *output_file, void *buf, size_t size)
153 {
154 	char *fname = NULL;
155 	int fd;
156 	ssize_t r;
157 
158 	if (strcmp(output_file, "-") == 0) {
159 		fd = fileno(stdout);
160 	} else {
161 		fname = alloca(strlen(output_file) + 5);
162 		strcpy(fname, output_file);
163 		strcat(fname, ".new");
164 		fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
165 		if (fd < 0) {
166 			fprintf(stderr, _("Unable to open output file '%s': %s\n"),
167 				fname, strerror(-errno));
168 			return 1;
169 		}
170 	}
171 
172 	r = 0;
173 	while (size > 0) {
174 		r = write(fd, buf, size);
175 		if (r < 0 && (errno == EAGAIN || errno == EINTR))
176 			continue;
177 		if (r < 0)
178 			break;
179 		size -= r;
180 		buf += r;
181 	}
182 
183 	if (r < 0) {
184 		fprintf(stderr, _("Write error: %s\n"), strerror(-errno));
185 		if (fd != fileno(stdout)) {
186 			remove(fname);
187 			close(fd);
188 		}
189 		return 1;
190 	}
191 
192 	if (fd != fileno(stdout))
193 		close(fd);
194 
195 	if (fname && rename(fname, output_file)) {
196 		fprintf(stderr, _("Unable to rename file '%s' to '%s': %s\n"),
197 			fname, output_file, strerror(-errno));
198 		return 1;
199 	}
200 
201 	return 0;
202 }
203 
dump(const char * source_file,const char * output_file,int cflags,int sflags)204 static int dump(const char *source_file, const char *output_file, int cflags, int sflags)
205 {
206 	snd_tplg_t *tplg;
207 	char *config, *text;
208 	size_t size;
209 	int err;
210 
211 	err = load(source_file, (void **)&config, &size);
212 	if (err)
213 		return err;
214 	err = load_topology(&tplg, config, size, cflags);
215 	free(config);
216 	if (err)
217 		return err;
218 	err = snd_tplg_save(tplg, &text, sflags);
219 	snd_tplg_free(tplg);
220 	if (err < 0) {
221 		fprintf(stderr, _("Unable to save parsed configuration: %s\n"),
222 			snd_strerror(-err));
223 		return 1;
224 	}
225 	err = save(output_file, text, strlen(text));
226 	free(text);
227 	return err;
228 }
229 
compile(const char * source_file,const char * output_file,int cflags)230 static int compile(const char *source_file, const char *output_file, int cflags)
231 {
232 	snd_tplg_t *tplg;
233 	char *config;
234 	void *bin;
235 	size_t config_size, size;
236 	int err;
237 
238 	err = load(source_file, (void **)&config, &config_size);
239 	if (err)
240 		return err;
241 	err = load_topology(&tplg, config, config_size, cflags);
242 	free(config);
243 	if (err)
244 		return err;
245 	err = snd_tplg_build_bin(tplg, &bin, &size);
246 	snd_tplg_free(tplg);
247 	if (err < 0 || size == 0) {
248 		fprintf(stderr, _("failed to compile context %s: %s\n"),
249 			source_file, snd_strerror(-err));
250 		return 1;
251 	}
252 	err = save(output_file, bin, size);
253 	free(bin);
254 	return err;
255 }
256 
decode(const char * source_file,const char * output_file,int cflags,int dflags,int sflags)257 static int decode(const char *source_file, const char *output_file,
258 		  int cflags, int dflags, int sflags)
259 {
260 	snd_tplg_t *tplg;
261 	void *bin;
262 	char *text;
263 	size_t size;
264 	int err;
265 
266 	if (load(source_file, &bin, &size))
267 		return 1;
268 	tplg = snd_tplg_create(cflags);
269 	if (tplg == NULL) {
270 		fprintf(stderr, _("failed to create new topology context\n"));
271 		return 1;
272 	}
273 	err = snd_tplg_decode(tplg, bin, size, dflags);
274 	free(bin);
275 	if (err < 0) {
276 		snd_tplg_free(tplg);
277 		fprintf(stderr, _("failed to decode context %s: %s\n"),
278 			source_file, snd_strerror(-err));
279 		return 1;
280 	}
281 	err = snd_tplg_save(tplg, &text, sflags);
282 	snd_tplg_free(tplg);
283 	if (err < 0) {
284 		fprintf(stderr, _("Unable to save parsed configuration: %s\n"),
285 			snd_strerror(-err));
286 		return 1;
287 	}
288 	err = save(output_file, text, strlen(text));
289 	free(text);
290 	return err;
291 }
292 
main(int argc,char * argv[])293 int main(int argc, char *argv[])
294 {
295 	static const char short_options[] = "hc:d:n:u:v:o:sgxzV";
296 	static const struct option long_options[] = {
297 		{"help", 0, NULL, 'h'},
298 		{"verbose", 1, NULL, 'v'},
299 		{"compile", 1, NULL, 'c'},
300 		{"decode", 1, NULL, 'd'},
301 		{"normalize", 1, NULL, 'n'},
302 		{"dump", 1, NULL, 'u'},
303 		{"output", 1, NULL, 'o'},
304 		{"sort", 0, NULL, 's'},
305 		{"group", 0, NULL, 'g'},
306 		{"nocheck", 0, NULL, 'x'},
307 		{"dapm-nosort", 0, NULL, 'z'},
308 		{"version", 0, NULL, 'V'},
309 		{0, 0, 0, 0},
310 	};
311 	char *source_file = NULL;
312 	char *output_file = NULL;
313 	int c, err, op = 'c', cflags = 0, dflags = 0, sflags = 0, option_index;
314 
315 #ifdef ENABLE_NLS
316 	setlocale(LC_ALL, "");
317 	textdomain(PACKAGE);
318 #endif
319 
320 	err = snd_output_stdio_attach(&log, stderr, 0);
321 	assert(err >= 0);
322 
323 	while ((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) {
324 		switch (c) {
325 		case 'h':
326 			usage(argv[0]);
327 			return 0;
328 		case 'v':
329 			cflags |= SND_TPLG_CREATE_VERBOSE;
330 			break;
331 		case 'z':
332 			cflags |= SND_TPLG_CREATE_DAPM_NOSORT;
333 			break;
334 		case 'c':
335 		case 'd':
336 		case 'n':
337 		case 'u':
338 			if (source_file) {
339 				fprintf(stderr, _("Cannot combine operations (compile, normalize, dump)\n"));
340 				return 1;
341 			}
342 			source_file = optarg;
343 			op = c;
344 			break;
345 		case 'o':
346 			output_file = optarg;
347 			break;
348 		case 's':
349 			sflags |= SND_TPLG_SAVE_SORT;
350 			break;
351 		case 'g':
352 			sflags |= SND_TPLG_SAVE_GROUPS;
353 			break;
354 		case 'x':
355 			sflags |= SND_TPLG_SAVE_NOCHECK;
356 			break;
357 		case 'V':
358 			version(argv[0]);
359 			return 0;
360 		default:
361 			fprintf(stderr, _("Try `%s --help' for more information.\n"), argv[0]);
362 			return 1;
363 		}
364 	}
365 
366 	if (source_file == NULL || output_file == NULL) {
367 		usage(argv[0]);
368 		return 1;
369 	}
370 
371 	if (op == 'n') {
372 		if (sflags != 0 && sflags != SND_TPLG_SAVE_SORT) {
373 			fprintf(stderr, _("Wrong parameters for the normalize operation!\n"));
374 			return 1;
375 		}
376 		/* normalize has predefined output */
377 		sflags = SND_TPLG_SAVE_SORT;
378 	}
379 
380 	switch (op) {
381 	case 'c':
382 		err = compile(source_file, output_file, cflags);
383 		break;
384 	case 'd':
385 		err = decode(source_file, output_file, cflags, dflags, sflags);
386 		break;
387 	default:
388 		err = dump(source_file, output_file, cflags, sflags);
389 		break;
390 	}
391 
392 	snd_output_close(log);
393 	return err ? 1 : 0;
394 }
395