1c72fcc34Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
2c72fcc34Sopenharmony_ci//
3c72fcc34Sopenharmony_ci// xfer-options.c - a parser of commandline options for xfer.
4c72fcc34Sopenharmony_ci//
5c72fcc34Sopenharmony_ci// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
6c72fcc34Sopenharmony_ci//
7c72fcc34Sopenharmony_ci// Licensed under the terms of the GNU General Public License, version 2.
8c72fcc34Sopenharmony_ci
9c72fcc34Sopenharmony_ci#include "xfer.h"
10c72fcc34Sopenharmony_ci#include "misc.h"
11c72fcc34Sopenharmony_ci
12c72fcc34Sopenharmony_ci#include <getopt.h>
13c72fcc34Sopenharmony_ci#include <math.h>
14c72fcc34Sopenharmony_ci#include <limits.h>
15c72fcc34Sopenharmony_ci
16c72fcc34Sopenharmony_cienum no_short_opts {
17c72fcc34Sopenharmony_ci	// 128 or later belong to non us-ascii character set.
18c72fcc34Sopenharmony_ci	OPT_XFER_TYPE = 128,
19c72fcc34Sopenharmony_ci	OPT_DUMP_HW_PARAMS,
20c72fcc34Sopenharmony_ci	OPT_PERIOD_SIZE,
21c72fcc34Sopenharmony_ci	OPT_BUFFER_SIZE,
22c72fcc34Sopenharmony_ci	// Obsoleted.
23c72fcc34Sopenharmony_ci	OPT_MAX_FILE_TIME,
24c72fcc34Sopenharmony_ci	OPT_USE_STRFTIME,
25c72fcc34Sopenharmony_ci	OPT_PROCESS_ID_FILE,
26c72fcc34Sopenharmony_ci};
27c72fcc34Sopenharmony_ci
28c72fcc34Sopenharmony_cistatic void print_help()
29c72fcc34Sopenharmony_ci{
30c72fcc34Sopenharmony_ci	printf(
31c72fcc34Sopenharmony_ci"Usage:\n"
32c72fcc34Sopenharmony_ci"  axfer transfer DIRECTION [ COMMON-OPTIONS ] [ BACKEND-OPTIONS ]\n"
33c72fcc34Sopenharmony_ci"\n"
34c72fcc34Sopenharmony_ci"  where:\n"
35c72fcc34Sopenharmony_ci"    DIRECTION = capture | playback\n"
36c72fcc34Sopenharmony_ci"    COMMON-OPTIONS =\n"
37c72fcc34Sopenharmony_ci"      -h, --help              help\n"
38c72fcc34Sopenharmony_ci"      -v, --verbose           verbose\n"
39c72fcc34Sopenharmony_ci"      -q, --quiet             quiet mode\n"
40c72fcc34Sopenharmony_ci"      -d, --duration=#        interrupt after # seconds\n"
41c72fcc34Sopenharmony_ci"      -s, --samples=#         interrupt after # frames\n"
42c72fcc34Sopenharmony_ci"      -f, --format=FORMAT     sample format (case-insensitive)\n"
43c72fcc34Sopenharmony_ci"      -c, --channels=#        channels\n"
44c72fcc34Sopenharmony_ci"      -r, --rate=#            numeric sample rate in unit of Hz or kHz\n"
45c72fcc34Sopenharmony_ci"      -t, --file-type=TYPE    file type (wav, au, sparc, voc or raw, case-insentive)\n"
46c72fcc34Sopenharmony_ci"      -I, --separate-channels one file for each channel\n"
47c72fcc34Sopenharmony_ci"      --dump-hw-params        dump hw_params of the device\n"
48c72fcc34Sopenharmony_ci"      --xfer-type=BACKEND     backend type (libasound, libffado)\n"
49c72fcc34Sopenharmony_ci	);
50c72fcc34Sopenharmony_ci}
51c72fcc34Sopenharmony_ci
52c72fcc34Sopenharmony_cistatic int allocate_paths(struct xfer_context *xfer, char *const *paths,
53c72fcc34Sopenharmony_ci			   unsigned int count)
54c72fcc34Sopenharmony_ci{
55c72fcc34Sopenharmony_ci	bool stdio = false;
56c72fcc34Sopenharmony_ci	unsigned int i;
57c72fcc34Sopenharmony_ci
58c72fcc34Sopenharmony_ci	if (count == 0) {
59c72fcc34Sopenharmony_ci		stdio = true;
60c72fcc34Sopenharmony_ci		count = 1;
61c72fcc34Sopenharmony_ci	}
62c72fcc34Sopenharmony_ci
63c72fcc34Sopenharmony_ci	xfer->paths = calloc(count, sizeof(xfer->paths[0]));
64c72fcc34Sopenharmony_ci	if (xfer->paths == NULL)
65c72fcc34Sopenharmony_ci		return -ENOMEM;
66c72fcc34Sopenharmony_ci	xfer->path_count = count;
67c72fcc34Sopenharmony_ci
68c72fcc34Sopenharmony_ci	if (stdio) {
69c72fcc34Sopenharmony_ci		xfer->paths[0] = strdup("-");
70c72fcc34Sopenharmony_ci		if (xfer->paths[0] == NULL)
71c72fcc34Sopenharmony_ci			return -ENOMEM;
72c72fcc34Sopenharmony_ci	} else {
73c72fcc34Sopenharmony_ci		for (i = 0; i < count; ++i) {
74c72fcc34Sopenharmony_ci			xfer->paths[i] = strndup(paths[i], PATH_MAX);
75c72fcc34Sopenharmony_ci			if (xfer->paths[i] == NULL)
76c72fcc34Sopenharmony_ci				return -ENOMEM;
77c72fcc34Sopenharmony_ci		}
78c72fcc34Sopenharmony_ci	}
79c72fcc34Sopenharmony_ci
80c72fcc34Sopenharmony_ci	return 0;
81c72fcc34Sopenharmony_ci}
82c72fcc34Sopenharmony_ci
83c72fcc34Sopenharmony_cistatic int verify_cntr_format(struct xfer_context *xfer)
84c72fcc34Sopenharmony_ci{
85c72fcc34Sopenharmony_ci	static const struct {
86c72fcc34Sopenharmony_ci		const char *const literal;
87c72fcc34Sopenharmony_ci		enum container_format cntr_format;
88c72fcc34Sopenharmony_ci	} *entry, entries[] = {
89c72fcc34Sopenharmony_ci		{"raw",		CONTAINER_FORMAT_RAW},
90c72fcc34Sopenharmony_ci		{"voc",		CONTAINER_FORMAT_VOC},
91c72fcc34Sopenharmony_ci		{"wav",		CONTAINER_FORMAT_RIFF_WAVE},
92c72fcc34Sopenharmony_ci		{"au",		CONTAINER_FORMAT_AU},
93c72fcc34Sopenharmony_ci		{"sparc",	CONTAINER_FORMAT_AU},
94c72fcc34Sopenharmony_ci	};
95c72fcc34Sopenharmony_ci	int i;
96c72fcc34Sopenharmony_ci
97c72fcc34Sopenharmony_ci	for (i = 0; i < (int)ARRAY_SIZE(entries); ++i) {
98c72fcc34Sopenharmony_ci		entry = &entries[i];
99c72fcc34Sopenharmony_ci		if (strcasecmp(xfer->cntr_format_literal, entry->literal))
100c72fcc34Sopenharmony_ci			continue;
101c72fcc34Sopenharmony_ci
102c72fcc34Sopenharmony_ci		xfer->cntr_format = entry->cntr_format;
103c72fcc34Sopenharmony_ci		return 0;
104c72fcc34Sopenharmony_ci	}
105c72fcc34Sopenharmony_ci
106c72fcc34Sopenharmony_ci	fprintf(stderr, "unrecognized file format '%s'\n",
107c72fcc34Sopenharmony_ci		xfer->cntr_format_literal);
108c72fcc34Sopenharmony_ci
109c72fcc34Sopenharmony_ci	return -EINVAL;
110c72fcc34Sopenharmony_ci}
111c72fcc34Sopenharmony_ci
112c72fcc34Sopenharmony_ci// This should be called after 'verify_cntr_format()'.
113c72fcc34Sopenharmony_cistatic int verify_sample_format(struct xfer_context *xfer)
114c72fcc34Sopenharmony_ci{
115c72fcc34Sopenharmony_ci	static const struct {
116c72fcc34Sopenharmony_ci		const char *const literal;
117c72fcc34Sopenharmony_ci		unsigned int frames_per_second;
118c72fcc34Sopenharmony_ci		unsigned int samples_per_frame;
119c72fcc34Sopenharmony_ci		snd_pcm_format_t le_format;
120c72fcc34Sopenharmony_ci		snd_pcm_format_t be_format;
121c72fcc34Sopenharmony_ci	} *entry, entries[] = {
122c72fcc34Sopenharmony_ci		{"cd",	44100, 2, SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE},
123c72fcc34Sopenharmony_ci		{"cdr",	44100, 2, SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE},
124c72fcc34Sopenharmony_ci		{"dat",	48000, 2, SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE},
125c72fcc34Sopenharmony_ci	};
126c72fcc34Sopenharmony_ci	unsigned int i;
127c72fcc34Sopenharmony_ci
128c72fcc34Sopenharmony_ci	xfer->sample_format = snd_pcm_format_value(xfer->sample_format_literal);
129c72fcc34Sopenharmony_ci	if (xfer->sample_format != SND_PCM_FORMAT_UNKNOWN)
130c72fcc34Sopenharmony_ci		return 0;
131c72fcc34Sopenharmony_ci
132c72fcc34Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(entries); ++i) {
133c72fcc34Sopenharmony_ci		entry = &entries[i];
134c72fcc34Sopenharmony_ci		if (strcmp(entry->literal, xfer->sample_format_literal))
135c72fcc34Sopenharmony_ci			continue;
136c72fcc34Sopenharmony_ci
137c72fcc34Sopenharmony_ci		if (xfer->frames_per_second > 0 &&
138c72fcc34Sopenharmony_ci		    xfer->frames_per_second != entry->frames_per_second) {
139c72fcc34Sopenharmony_ci			fprintf(stderr,
140c72fcc34Sopenharmony_ci				"'%s' format can't be used with rate except "
141c72fcc34Sopenharmony_ci				"for %u.\n",
142c72fcc34Sopenharmony_ci				entry->literal, entry->frames_per_second);
143c72fcc34Sopenharmony_ci			return -EINVAL;
144c72fcc34Sopenharmony_ci		}
145c72fcc34Sopenharmony_ci
146c72fcc34Sopenharmony_ci		if (xfer->samples_per_frame > 0 &&
147c72fcc34Sopenharmony_ci		    xfer->samples_per_frame != entry->samples_per_frame) {
148c72fcc34Sopenharmony_ci			fprintf(stderr,
149c72fcc34Sopenharmony_ci				"'%s' format can't be used with channel except "
150c72fcc34Sopenharmony_ci				"for %u.\n",
151c72fcc34Sopenharmony_ci				entry->literal, entry->samples_per_frame);
152c72fcc34Sopenharmony_ci			return -EINVAL;
153c72fcc34Sopenharmony_ci		}
154c72fcc34Sopenharmony_ci
155c72fcc34Sopenharmony_ci		xfer->frames_per_second = entry->frames_per_second;
156c72fcc34Sopenharmony_ci		xfer->samples_per_frame = entry->samples_per_frame;
157c72fcc34Sopenharmony_ci		if (xfer->cntr_format == CONTAINER_FORMAT_AU)
158c72fcc34Sopenharmony_ci			xfer->sample_format = entry->be_format;
159c72fcc34Sopenharmony_ci		else
160c72fcc34Sopenharmony_ci			xfer->sample_format = entry->le_format;
161c72fcc34Sopenharmony_ci
162c72fcc34Sopenharmony_ci		return 0;
163c72fcc34Sopenharmony_ci	}
164c72fcc34Sopenharmony_ci
165c72fcc34Sopenharmony_ci	fprintf(stderr, "wrong extended format '%s'\n",
166c72fcc34Sopenharmony_ci		xfer->sample_format_literal);
167c72fcc34Sopenharmony_ci
168c72fcc34Sopenharmony_ci	return -EINVAL;
169c72fcc34Sopenharmony_ci}
170c72fcc34Sopenharmony_ci
171c72fcc34Sopenharmony_cistatic int validate_options(struct xfer_context *xfer)
172c72fcc34Sopenharmony_ci{
173c72fcc34Sopenharmony_ci	unsigned int val;
174c72fcc34Sopenharmony_ci	int err = 0;
175c72fcc34Sopenharmony_ci
176c72fcc34Sopenharmony_ci	if (xfer->cntr_format_literal == NULL) {
177c72fcc34Sopenharmony_ci		if (xfer->direction == SND_PCM_STREAM_CAPTURE) {
178c72fcc34Sopenharmony_ci			// To stdout.
179c72fcc34Sopenharmony_ci			if (xfer->path_count == 1 &&
180c72fcc34Sopenharmony_ci			    !strcmp(xfer->paths[0], "-")) {
181c72fcc34Sopenharmony_ci				xfer->cntr_format = CONTAINER_FORMAT_RAW;
182c72fcc34Sopenharmony_ci			} else {
183c72fcc34Sopenharmony_ci				// Use first path as a representative.
184c72fcc34Sopenharmony_ci				xfer->cntr_format = container_format_from_path(
185c72fcc34Sopenharmony_ci								xfer->paths[0]);
186c72fcc34Sopenharmony_ci			}
187c72fcc34Sopenharmony_ci		}
188c72fcc34Sopenharmony_ci		// For playback, perform auto-detection.
189c72fcc34Sopenharmony_ci	} else {
190c72fcc34Sopenharmony_ci		err = verify_cntr_format(xfer);
191c72fcc34Sopenharmony_ci	}
192c72fcc34Sopenharmony_ci	if (err < 0)
193c72fcc34Sopenharmony_ci		return err;
194c72fcc34Sopenharmony_ci
195c72fcc34Sopenharmony_ci	if (xfer->multiple_cntrs) {
196c72fcc34Sopenharmony_ci		if (!strcmp(xfer->paths[0], "-")) {
197c72fcc34Sopenharmony_ci			fprintf(stderr,
198c72fcc34Sopenharmony_ci				"An option for separated channels is not "
199c72fcc34Sopenharmony_ci				"available with stdin/stdout.\n");
200c72fcc34Sopenharmony_ci			return -EINVAL;
201c72fcc34Sopenharmony_ci		}
202c72fcc34Sopenharmony_ci
203c72fcc34Sopenharmony_ci		// For captured PCM frames, even if one path is given for
204c72fcc34Sopenharmony_ci		// container files, it can be used to generate several paths.
205c72fcc34Sopenharmony_ci		// For this purpose, please see
206c72fcc34Sopenharmony_ci		// 'xfer_options_fixup_paths()'.
207c72fcc34Sopenharmony_ci		if (xfer->direction == SND_PCM_STREAM_PLAYBACK) {
208c72fcc34Sopenharmony_ci			// Require several paths for containers.
209c72fcc34Sopenharmony_ci			if (xfer->path_count == 1) {
210c72fcc34Sopenharmony_ci				fprintf(stderr,
211c72fcc34Sopenharmony_ci					"An option for separated channels "
212c72fcc34Sopenharmony_ci					"requires several files to playback "
213c72fcc34Sopenharmony_ci					"PCM frames.\n");
214c72fcc34Sopenharmony_ci				return -EINVAL;
215c72fcc34Sopenharmony_ci			}
216c72fcc34Sopenharmony_ci		}
217c72fcc34Sopenharmony_ci	} else {
218c72fcc34Sopenharmony_ci		// A single path is available only.
219c72fcc34Sopenharmony_ci		if (xfer->path_count > 1) {
220c72fcc34Sopenharmony_ci			fprintf(stderr,
221c72fcc34Sopenharmony_ci				"When using several files, an option for "
222c72fcc34Sopenharmony_ci				"sepatated channels is used with.\n");
223c72fcc34Sopenharmony_ci			return -EINVAL;
224c72fcc34Sopenharmony_ci		}
225c72fcc34Sopenharmony_ci	}
226c72fcc34Sopenharmony_ci
227c72fcc34Sopenharmony_ci	xfer->sample_format = SND_PCM_FORMAT_UNKNOWN;
228c72fcc34Sopenharmony_ci	if (xfer->sample_format_literal) {
229c72fcc34Sopenharmony_ci		err = verify_sample_format(xfer);
230c72fcc34Sopenharmony_ci		if (err < 0)
231c72fcc34Sopenharmony_ci			return err;
232c72fcc34Sopenharmony_ci	}
233c72fcc34Sopenharmony_ci
234c72fcc34Sopenharmony_ci	val = xfer->frames_per_second;
235c72fcc34Sopenharmony_ci	if (xfer->frames_per_second == 0)
236c72fcc34Sopenharmony_ci		xfer->frames_per_second = 8000;
237c72fcc34Sopenharmony_ci	if (xfer->frames_per_second < 1000)
238c72fcc34Sopenharmony_ci		xfer->frames_per_second *= 1000;
239c72fcc34Sopenharmony_ci	if (xfer->frames_per_second < 2000 ||
240c72fcc34Sopenharmony_ci	    xfer->frames_per_second > 192000) {
241c72fcc34Sopenharmony_ci		fprintf(stderr, "bad speed value '%u'\n", val);
242c72fcc34Sopenharmony_ci		return -EINVAL;
243c72fcc34Sopenharmony_ci	}
244c72fcc34Sopenharmony_ci
245c72fcc34Sopenharmony_ci	if (xfer->samples_per_frame > 0) {
246c72fcc34Sopenharmony_ci		if (xfer->samples_per_frame < 1 ||
247c72fcc34Sopenharmony_ci		    xfer->samples_per_frame > 256) {
248c72fcc34Sopenharmony_ci			fprintf(stderr, "invalid channels argument '%u'\n",
249c72fcc34Sopenharmony_ci				xfer->samples_per_frame);
250c72fcc34Sopenharmony_ci			return -EINVAL;
251c72fcc34Sopenharmony_ci		}
252c72fcc34Sopenharmony_ci	}
253c72fcc34Sopenharmony_ci
254c72fcc34Sopenharmony_ci	return err;
255c72fcc34Sopenharmony_ci}
256c72fcc34Sopenharmony_ci
257c72fcc34Sopenharmony_ciint xfer_options_parse_args(struct xfer_context *xfer,
258c72fcc34Sopenharmony_ci			    const struct xfer_data *data, int argc,
259c72fcc34Sopenharmony_ci			    char *const *argv)
260c72fcc34Sopenharmony_ci{
261c72fcc34Sopenharmony_ci	static const char *short_opts = "CPhvqd:s:f:c:r:t:IV:i";
262c72fcc34Sopenharmony_ci	static const struct option long_opts[] = {
263c72fcc34Sopenharmony_ci		// For generic purposes.
264c72fcc34Sopenharmony_ci		{"capture",		0, 0, 'C'},
265c72fcc34Sopenharmony_ci		{"playback",		0, 0, 'P'},
266c72fcc34Sopenharmony_ci		{"xfer-type",		1, 0, OPT_XFER_TYPE},
267c72fcc34Sopenharmony_ci		{"help",		0, 0, 'h'},
268c72fcc34Sopenharmony_ci		{"verbose",		0, 0, 'v'},
269c72fcc34Sopenharmony_ci		{"quiet",		0, 0, 'q'},
270c72fcc34Sopenharmony_ci		{"duration",		1, 0, 'd'},
271c72fcc34Sopenharmony_ci		{"samples",		1, 0, 's'},
272c72fcc34Sopenharmony_ci		// For transfer backend.
273c72fcc34Sopenharmony_ci		{"format",		1, 0, 'f'},
274c72fcc34Sopenharmony_ci		{"channels",		1, 0, 'c'},
275c72fcc34Sopenharmony_ci		{"rate",		1, 0, 'r'},
276c72fcc34Sopenharmony_ci		// For containers.
277c72fcc34Sopenharmony_ci		{"file-type",		1, 0, 't'},
278c72fcc34Sopenharmony_ci		// For mapper.
279c72fcc34Sopenharmony_ci		{"separate-channels",	0, 0, 'I'},
280c72fcc34Sopenharmony_ci		// For debugging.
281c72fcc34Sopenharmony_ci		{"dump-hw-params",	0, 0, OPT_DUMP_HW_PARAMS},
282c72fcc34Sopenharmony_ci		// Obsoleted.
283c72fcc34Sopenharmony_ci		{"max-file-time",	1, 0, OPT_MAX_FILE_TIME},
284c72fcc34Sopenharmony_ci		{"use-strftime",	0, 0, OPT_USE_STRFTIME},
285c72fcc34Sopenharmony_ci		{"process-id-file",	1, 0, OPT_PROCESS_ID_FILE},
286c72fcc34Sopenharmony_ci		{"vumeter",		1, 0, 'V'},
287c72fcc34Sopenharmony_ci		{"interactive",		0, 0, 'i'},
288c72fcc34Sopenharmony_ci	};
289c72fcc34Sopenharmony_ci	char *s_opts;
290c72fcc34Sopenharmony_ci	struct option *l_opts;
291c72fcc34Sopenharmony_ci	int l_index;
292c72fcc34Sopenharmony_ci	int key;
293c72fcc34Sopenharmony_ci	int err = 0;
294c72fcc34Sopenharmony_ci
295c72fcc34Sopenharmony_ci	// Concatenate short options.
296c72fcc34Sopenharmony_ci	s_opts = malloc(strlen(data->s_opts) + strlen(short_opts) + 1);
297c72fcc34Sopenharmony_ci	if (s_opts == NULL)
298c72fcc34Sopenharmony_ci		return -ENOMEM;
299c72fcc34Sopenharmony_ci	strcpy(s_opts, data->s_opts);
300c72fcc34Sopenharmony_ci	strcpy(s_opts + strlen(s_opts), short_opts);
301c72fcc34Sopenharmony_ci	s_opts[strlen(data->s_opts) + strlen(short_opts)] = '\0';
302c72fcc34Sopenharmony_ci
303c72fcc34Sopenharmony_ci	// Concatenate long options, including a sentinel.
304c72fcc34Sopenharmony_ci	l_opts = calloc(ARRAY_SIZE(long_opts) * data->l_opts_count + 1,
305c72fcc34Sopenharmony_ci			sizeof(*l_opts));
306c72fcc34Sopenharmony_ci	if (l_opts == NULL) {
307c72fcc34Sopenharmony_ci		free(s_opts);
308c72fcc34Sopenharmony_ci		return -ENOMEM;
309c72fcc34Sopenharmony_ci	}
310c72fcc34Sopenharmony_ci	memcpy(l_opts, long_opts, ARRAY_SIZE(long_opts) * sizeof(*l_opts));
311c72fcc34Sopenharmony_ci	memcpy(&l_opts[ARRAY_SIZE(long_opts)], data->l_opts,
312c72fcc34Sopenharmony_ci	       data->l_opts_count * sizeof(*l_opts));
313c72fcc34Sopenharmony_ci
314c72fcc34Sopenharmony_ci	// Parse options.
315c72fcc34Sopenharmony_ci	l_index = 0;
316c72fcc34Sopenharmony_ci	optarg = NULL;
317c72fcc34Sopenharmony_ci	optind = 1;
318c72fcc34Sopenharmony_ci	opterr = 1;	// use error output.
319c72fcc34Sopenharmony_ci	optopt = 0;
320c72fcc34Sopenharmony_ci	while (1) {
321c72fcc34Sopenharmony_ci		key = getopt_long(argc, argv, s_opts, l_opts, &l_index);
322c72fcc34Sopenharmony_ci		if (key < 0)
323c72fcc34Sopenharmony_ci			break;
324c72fcc34Sopenharmony_ci		else if (key == 'C')
325c72fcc34Sopenharmony_ci			;	// already parsed.
326c72fcc34Sopenharmony_ci		else if (key == 'P')
327c72fcc34Sopenharmony_ci			;	// already parsed.
328c72fcc34Sopenharmony_ci		else if (key == OPT_XFER_TYPE)
329c72fcc34Sopenharmony_ci			;	// already parsed.
330c72fcc34Sopenharmony_ci		else if (key == 'h')
331c72fcc34Sopenharmony_ci			xfer->help = true;
332c72fcc34Sopenharmony_ci		else if (key == 'v')
333c72fcc34Sopenharmony_ci			++xfer->verbose;
334c72fcc34Sopenharmony_ci		else if (key == 'q')
335c72fcc34Sopenharmony_ci			xfer->quiet = true;
336c72fcc34Sopenharmony_ci		else if (key == 'd')
337c72fcc34Sopenharmony_ci			xfer->duration_seconds = arg_parse_decimal_num(optarg, &err);
338c72fcc34Sopenharmony_ci		else if (key == 's')
339c72fcc34Sopenharmony_ci			xfer->duration_frames = arg_parse_decimal_num(optarg, &err);
340c72fcc34Sopenharmony_ci		else if (key == 'f')
341c72fcc34Sopenharmony_ci			xfer->sample_format_literal = arg_duplicate_string(optarg, &err);
342c72fcc34Sopenharmony_ci		else if (key == 'c')
343c72fcc34Sopenharmony_ci			xfer->samples_per_frame = arg_parse_decimal_num(optarg, &err);
344c72fcc34Sopenharmony_ci		else if (key == 'r')
345c72fcc34Sopenharmony_ci			xfer->frames_per_second = arg_parse_decimal_num(optarg, &err);
346c72fcc34Sopenharmony_ci		else if (key == 't')
347c72fcc34Sopenharmony_ci			xfer->cntr_format_literal = arg_duplicate_string(optarg, &err);
348c72fcc34Sopenharmony_ci		else if (key == 'I')
349c72fcc34Sopenharmony_ci			xfer->multiple_cntrs = true;
350c72fcc34Sopenharmony_ci		else if (key == OPT_DUMP_HW_PARAMS)
351c72fcc34Sopenharmony_ci			xfer->dump_hw_params = true;
352c72fcc34Sopenharmony_ci		else if (key == '?') {
353c72fcc34Sopenharmony_ci			free(l_opts);
354c72fcc34Sopenharmony_ci			free(s_opts);
355c72fcc34Sopenharmony_ci			return -EINVAL;
356c72fcc34Sopenharmony_ci		}
357c72fcc34Sopenharmony_ci		else if (key == OPT_MAX_FILE_TIME ||
358c72fcc34Sopenharmony_ci			 key == OPT_USE_STRFTIME ||
359c72fcc34Sopenharmony_ci			 key == OPT_PROCESS_ID_FILE ||
360c72fcc34Sopenharmony_ci			 key == 'V' ||
361c72fcc34Sopenharmony_ci			 key == 'i') {
362c72fcc34Sopenharmony_ci			fprintf(stderr,
363c72fcc34Sopenharmony_ci				"An option '--%s' is obsoleted and has no "
364c72fcc34Sopenharmony_ci				"effect.\n",
365c72fcc34Sopenharmony_ci				l_opts[l_index].name);
366c72fcc34Sopenharmony_ci			err = -EINVAL;
367c72fcc34Sopenharmony_ci		} else {
368c72fcc34Sopenharmony_ci			err = xfer->ops->parse_opt(xfer, key, optarg);
369c72fcc34Sopenharmony_ci			if (err < 0 && err != -ENXIO)
370c72fcc34Sopenharmony_ci				break;
371c72fcc34Sopenharmony_ci		}
372c72fcc34Sopenharmony_ci	}
373c72fcc34Sopenharmony_ci
374c72fcc34Sopenharmony_ci	free(l_opts);
375c72fcc34Sopenharmony_ci	free(s_opts);
376c72fcc34Sopenharmony_ci
377c72fcc34Sopenharmony_ci	if (xfer->help) {
378c72fcc34Sopenharmony_ci		print_help();
379c72fcc34Sopenharmony_ci		if (xfer->ops->help) {
380c72fcc34Sopenharmony_ci			printf("\n");
381c72fcc34Sopenharmony_ci			printf("    BACKEND-OPTIONS (%s) =\n",
382c72fcc34Sopenharmony_ci			       xfer_label_from_type(xfer->type));
383c72fcc34Sopenharmony_ci			xfer->ops->help(xfer);
384c72fcc34Sopenharmony_ci		}
385c72fcc34Sopenharmony_ci		return 0;
386c72fcc34Sopenharmony_ci	}
387c72fcc34Sopenharmony_ci
388c72fcc34Sopenharmony_ci	err = allocate_paths(xfer, argv + optind, argc - optind);
389c72fcc34Sopenharmony_ci	if (err < 0)
390c72fcc34Sopenharmony_ci		return err;
391c72fcc34Sopenharmony_ci
392c72fcc34Sopenharmony_ci	return validate_options(xfer);
393c72fcc34Sopenharmony_ci}
394c72fcc34Sopenharmony_ci
395c72fcc34Sopenharmony_civoid xfer_options_calculate_duration(struct xfer_context *xfer,
396c72fcc34Sopenharmony_ci				     uint64_t *total_frame_count)
397c72fcc34Sopenharmony_ci{
398c72fcc34Sopenharmony_ci	uint64_t frame_count;
399c72fcc34Sopenharmony_ci
400c72fcc34Sopenharmony_ci	if (xfer->duration_seconds > 0) {
401c72fcc34Sopenharmony_ci		frame_count = (uint64_t)xfer->duration_seconds * (uint64_t)xfer->frames_per_second;
402c72fcc34Sopenharmony_ci		if (frame_count < *total_frame_count)
403c72fcc34Sopenharmony_ci			*total_frame_count = frame_count;
404c72fcc34Sopenharmony_ci	}
405c72fcc34Sopenharmony_ci
406c72fcc34Sopenharmony_ci	if (xfer->duration_frames > 0) {
407c72fcc34Sopenharmony_ci		frame_count = xfer->duration_frames;
408c72fcc34Sopenharmony_ci		if (frame_count < *total_frame_count)
409c72fcc34Sopenharmony_ci			*total_frame_count = frame_count;
410c72fcc34Sopenharmony_ci	}
411c72fcc34Sopenharmony_ci}
412c72fcc34Sopenharmony_ci
413c72fcc34Sopenharmony_cistatic const char *const allowed_duplication[] = {
414c72fcc34Sopenharmony_ci	"/dev/null",
415c72fcc34Sopenharmony_ci	"/dev/zero",
416c72fcc34Sopenharmony_ci	"/dev/full",
417c72fcc34Sopenharmony_ci	"/dev/random",
418c72fcc34Sopenharmony_ci	"/dev/urandom",
419c72fcc34Sopenharmony_ci};
420c72fcc34Sopenharmony_ci
421c72fcc34Sopenharmony_cistatic int generate_path_with_suffix(struct xfer_context *xfer,
422c72fcc34Sopenharmony_ci				     const char *template, unsigned int index,
423c72fcc34Sopenharmony_ci				     const char *suffix)
424c72fcc34Sopenharmony_ci{
425c72fcc34Sopenharmony_ci	static const char *const single_format = "%s%s";
426c72fcc34Sopenharmony_ci	static const char *const multiple_format = "%s-%i%s";
427c72fcc34Sopenharmony_ci	unsigned int len;
428c72fcc34Sopenharmony_ci
429c72fcc34Sopenharmony_ci	len = strlen(template) + strlen(suffix) + 1;
430c72fcc34Sopenharmony_ci	if (xfer->path_count > 1)
431c72fcc34Sopenharmony_ci		len += (unsigned int)log10(xfer->path_count) + 2;
432c72fcc34Sopenharmony_ci
433c72fcc34Sopenharmony_ci	xfer->paths[index] = malloc(len);
434c72fcc34Sopenharmony_ci	if (xfer->paths[index] == NULL)
435c72fcc34Sopenharmony_ci		return -ENOMEM;
436c72fcc34Sopenharmony_ci
437c72fcc34Sopenharmony_ci	if (xfer->path_count == 1) {
438c72fcc34Sopenharmony_ci		snprintf(xfer->paths[index], len, single_format, template,
439c72fcc34Sopenharmony_ci			 suffix);
440c72fcc34Sopenharmony_ci	} else {
441c72fcc34Sopenharmony_ci		snprintf(xfer->paths[index], len, multiple_format, template,
442c72fcc34Sopenharmony_ci			 index, suffix);
443c72fcc34Sopenharmony_ci	}
444c72fcc34Sopenharmony_ci
445c72fcc34Sopenharmony_ci	return 0;
446c72fcc34Sopenharmony_ci}
447c72fcc34Sopenharmony_ci
448c72fcc34Sopenharmony_cistatic int generate_path_without_suffix(struct xfer_context *xfer,
449c72fcc34Sopenharmony_ci				        const char *template,
450c72fcc34Sopenharmony_ci					unsigned int index,
451c72fcc34Sopenharmony_ci					const char *suffix ATTRIBUTE_UNUSED)
452c72fcc34Sopenharmony_ci{
453c72fcc34Sopenharmony_ci	static const char *const single_format = "%s";
454c72fcc34Sopenharmony_ci	static const char *const multiple_format = "%s-%i";
455c72fcc34Sopenharmony_ci	unsigned int len;
456c72fcc34Sopenharmony_ci
457c72fcc34Sopenharmony_ci	len = strlen(template) + 1;
458c72fcc34Sopenharmony_ci	if (xfer->path_count > 1)
459c72fcc34Sopenharmony_ci		len += (unsigned int)log10(xfer->path_count) + 2;
460c72fcc34Sopenharmony_ci
461c72fcc34Sopenharmony_ci	xfer->paths[index] = malloc(len);
462c72fcc34Sopenharmony_ci	if (xfer->paths[index] == NULL)
463c72fcc34Sopenharmony_ci		return -ENOMEM;
464c72fcc34Sopenharmony_ci
465c72fcc34Sopenharmony_ci	if (xfer->path_count == 1) {
466c72fcc34Sopenharmony_ci		snprintf(xfer->paths[index], len, single_format, template);
467c72fcc34Sopenharmony_ci	} else {
468c72fcc34Sopenharmony_ci		snprintf(xfer->paths[index], len, multiple_format, template,
469c72fcc34Sopenharmony_ci			index);
470c72fcc34Sopenharmony_ci	}
471c72fcc34Sopenharmony_ci
472c72fcc34Sopenharmony_ci	return 0;
473c72fcc34Sopenharmony_ci}
474c72fcc34Sopenharmony_ci
475c72fcc34Sopenharmony_cistatic int generate_path(struct xfer_context *xfer, char *template,
476c72fcc34Sopenharmony_ci			 unsigned int index, const char *suffix)
477c72fcc34Sopenharmony_ci{
478c72fcc34Sopenharmony_ci	int (*generator)(struct xfer_context *xfer, const char *template,
479c72fcc34Sopenharmony_ci			 unsigned int index, const char *suffix);
480c72fcc34Sopenharmony_ci	char *pos;
481c72fcc34Sopenharmony_ci
482c72fcc34Sopenharmony_ci	if (strlen(suffix) > 0) {
483c72fcc34Sopenharmony_ci		pos = template + strlen(template) - strlen(suffix);
484c72fcc34Sopenharmony_ci		// Separate filename and suffix.
485c72fcc34Sopenharmony_ci		if (!strcmp(pos, suffix))
486c72fcc34Sopenharmony_ci			*pos = '\0';
487c72fcc34Sopenharmony_ci	}
488c72fcc34Sopenharmony_ci
489c72fcc34Sopenharmony_ci	// Select handlers.
490c72fcc34Sopenharmony_ci	if (strlen(suffix) > 0)
491c72fcc34Sopenharmony_ci		generator = generate_path_with_suffix;
492c72fcc34Sopenharmony_ci	else
493c72fcc34Sopenharmony_ci		generator = generate_path_without_suffix;
494c72fcc34Sopenharmony_ci
495c72fcc34Sopenharmony_ci	return generator(xfer, template, index, suffix);
496c72fcc34Sopenharmony_ci}
497c72fcc34Sopenharmony_ci
498c72fcc34Sopenharmony_cistatic int create_paths(struct xfer_context *xfer, unsigned int path_count)
499c72fcc34Sopenharmony_ci{
500c72fcc34Sopenharmony_ci	char *template;
501c72fcc34Sopenharmony_ci	const char *suffix;
502c72fcc34Sopenharmony_ci	unsigned int i, j;
503c72fcc34Sopenharmony_ci	int err = 0;
504c72fcc34Sopenharmony_ci
505c72fcc34Sopenharmony_ci	// Can cause memory leak.
506c72fcc34Sopenharmony_ci	assert(xfer->path_count == 1);
507c72fcc34Sopenharmony_ci	assert(xfer->paths);
508c72fcc34Sopenharmony_ci	assert(xfer->paths[0]);
509c72fcc34Sopenharmony_ci	assert(xfer->paths[0][0] != '\0');
510c72fcc34Sopenharmony_ci
511c72fcc34Sopenharmony_ci	// Release at first.
512c72fcc34Sopenharmony_ci	template = xfer->paths[0];
513c72fcc34Sopenharmony_ci	free(xfer->paths);
514c72fcc34Sopenharmony_ci	xfer->paths = NULL;
515c72fcc34Sopenharmony_ci
516c72fcc34Sopenharmony_ci	// Allocate again.
517c72fcc34Sopenharmony_ci	xfer->paths = calloc(path_count, sizeof(*xfer->paths));
518c72fcc34Sopenharmony_ci	if (xfer->paths == NULL) {
519c72fcc34Sopenharmony_ci		err = -ENOMEM;
520c72fcc34Sopenharmony_ci		goto end;
521c72fcc34Sopenharmony_ci	}
522c72fcc34Sopenharmony_ci	xfer->path_count = path_count;
523c72fcc34Sopenharmony_ci
524c72fcc34Sopenharmony_ci	suffix = container_suffix_from_format(xfer->cntr_format);
525c72fcc34Sopenharmony_ci
526c72fcc34Sopenharmony_ci	for (i = 0; i < xfer->path_count; ++i) {
527c72fcc34Sopenharmony_ci		// Some file names are allowed to be duplicated.
528c72fcc34Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(allowed_duplication); ++j) {
529c72fcc34Sopenharmony_ci			if (!strcmp(template, allowed_duplication[j]))
530c72fcc34Sopenharmony_ci				break;
531c72fcc34Sopenharmony_ci		}
532c72fcc34Sopenharmony_ci		if (j < ARRAY_SIZE(allowed_duplication))
533c72fcc34Sopenharmony_ci			continue;
534c72fcc34Sopenharmony_ci
535c72fcc34Sopenharmony_ci		err = generate_path(xfer, template, i, suffix);
536c72fcc34Sopenharmony_ci		if (err < 0)
537c72fcc34Sopenharmony_ci			break;
538c72fcc34Sopenharmony_ci	}
539c72fcc34Sopenharmony_ciend:
540c72fcc34Sopenharmony_ci	free(template);
541c72fcc34Sopenharmony_ci
542c72fcc34Sopenharmony_ci	return err;
543c72fcc34Sopenharmony_ci}
544c72fcc34Sopenharmony_ci
545c72fcc34Sopenharmony_cistatic int fixup_paths(struct xfer_context *xfer)
546c72fcc34Sopenharmony_ci{
547c72fcc34Sopenharmony_ci	const char *suffix;
548c72fcc34Sopenharmony_ci	char *template;
549c72fcc34Sopenharmony_ci	unsigned int i, j;
550c72fcc34Sopenharmony_ci	int err = 0;
551c72fcc34Sopenharmony_ci
552c72fcc34Sopenharmony_ci	suffix = container_suffix_from_format(xfer->cntr_format);
553c72fcc34Sopenharmony_ci
554c72fcc34Sopenharmony_ci	for (i = 0; i < xfer->path_count; ++i) {
555c72fcc34Sopenharmony_ci		// Some file names are allowed to be duplicated.
556c72fcc34Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(allowed_duplication); ++j) {
557c72fcc34Sopenharmony_ci			if (!strcmp(xfer->paths[i], allowed_duplication[j]))
558c72fcc34Sopenharmony_ci				break;
559c72fcc34Sopenharmony_ci		}
560c72fcc34Sopenharmony_ci		if (j < ARRAY_SIZE(allowed_duplication))
561c72fcc34Sopenharmony_ci			continue;
562c72fcc34Sopenharmony_ci
563c72fcc34Sopenharmony_ci		template = xfer->paths[i];
564c72fcc34Sopenharmony_ci		xfer->paths[i] = NULL;
565c72fcc34Sopenharmony_ci		err = generate_path(xfer, template, i, suffix);
566c72fcc34Sopenharmony_ci		free(template);
567c72fcc34Sopenharmony_ci		if (err < 0)
568c72fcc34Sopenharmony_ci			break;
569c72fcc34Sopenharmony_ci	}
570c72fcc34Sopenharmony_ci
571c72fcc34Sopenharmony_ci	return err;
572c72fcc34Sopenharmony_ci}
573c72fcc34Sopenharmony_ci
574c72fcc34Sopenharmony_ciint xfer_options_fixup_paths(struct xfer_context *xfer)
575c72fcc34Sopenharmony_ci{
576c72fcc34Sopenharmony_ci	unsigned int i, j;
577c72fcc34Sopenharmony_ci	int err;
578c72fcc34Sopenharmony_ci
579c72fcc34Sopenharmony_ci	if (xfer->path_count == 1) {
580c72fcc34Sopenharmony_ci		// Nothing to do for sign of stdin/stdout.
581c72fcc34Sopenharmony_ci		if (!strcmp(xfer->paths[0], "-"))
582c72fcc34Sopenharmony_ci			return 0;
583c72fcc34Sopenharmony_ci		if (!xfer->multiple_cntrs)
584c72fcc34Sopenharmony_ci			err = fixup_paths(xfer);
585c72fcc34Sopenharmony_ci		else
586c72fcc34Sopenharmony_ci			err = create_paths(xfer, xfer->samples_per_frame);
587c72fcc34Sopenharmony_ci	} else {
588c72fcc34Sopenharmony_ci		if (!xfer->multiple_cntrs)
589c72fcc34Sopenharmony_ci			return -EINVAL;
590c72fcc34Sopenharmony_ci		if (xfer->path_count != xfer->samples_per_frame)
591c72fcc34Sopenharmony_ci			return -EINVAL;
592c72fcc34Sopenharmony_ci		else
593c72fcc34Sopenharmony_ci			err = fixup_paths(xfer);
594c72fcc34Sopenharmony_ci	}
595c72fcc34Sopenharmony_ci	if (err < 0)
596c72fcc34Sopenharmony_ci		return err;
597c72fcc34Sopenharmony_ci
598c72fcc34Sopenharmony_ci	// Check duplication of the paths.
599c72fcc34Sopenharmony_ci	for (i = 0; i < xfer->path_count - 1; ++i) {
600c72fcc34Sopenharmony_ci		// Some file names are allowed to be duplicated.
601c72fcc34Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(allowed_duplication); ++j) {
602c72fcc34Sopenharmony_ci			if (!strcmp(xfer->paths[i], allowed_duplication[j]))
603c72fcc34Sopenharmony_ci				break;
604c72fcc34Sopenharmony_ci		}
605c72fcc34Sopenharmony_ci		if (j < ARRAY_SIZE(allowed_duplication))
606c72fcc34Sopenharmony_ci			continue;
607c72fcc34Sopenharmony_ci
608c72fcc34Sopenharmony_ci		for (j = i + 1; j < xfer->path_count; ++j) {
609c72fcc34Sopenharmony_ci			if (!strcmp(xfer->paths[i], xfer->paths[j])) {
610c72fcc34Sopenharmony_ci				fprintf(stderr,
611c72fcc34Sopenharmony_ci					"Detect duplicated file names:\n");
612c72fcc34Sopenharmony_ci				err = -EINVAL;
613c72fcc34Sopenharmony_ci				break;
614c72fcc34Sopenharmony_ci			}
615c72fcc34Sopenharmony_ci		}
616c72fcc34Sopenharmony_ci		if (j < xfer->path_count)
617c72fcc34Sopenharmony_ci			break;
618c72fcc34Sopenharmony_ci	}
619c72fcc34Sopenharmony_ci
620c72fcc34Sopenharmony_ci	if (xfer->verbose > 1)
621c72fcc34Sopenharmony_ci		fprintf(stderr, "Handled file names:\n");
622c72fcc34Sopenharmony_ci	if (err < 0 || xfer->verbose > 1) {
623c72fcc34Sopenharmony_ci		for (i = 0; i < xfer->path_count; ++i)
624c72fcc34Sopenharmony_ci			fprintf(stderr, "    %d: %s\n", i, xfer->paths[i]);
625c72fcc34Sopenharmony_ci	}
626c72fcc34Sopenharmony_ci
627c72fcc34Sopenharmony_ci	return err;
628c72fcc34Sopenharmony_ci}
629