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