Blame axfer/xfer-options.c

Packit 229ac0
// SPDX-License-Identifier: GPL-2.0
Packit 229ac0
//
Packit 229ac0
// xfer-options.c - a parser of commandline options for xfer.
Packit 229ac0
//
Packit 229ac0
// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
Packit 229ac0
//
Packit 229ac0
// Licensed under the terms of the GNU General Public License, version 2.
Packit 229ac0
Packit 229ac0
#include "xfer.h"
Packit 229ac0
#include "misc.h"
Packit 229ac0
Packit 229ac0
#include <getopt.h>
Packit 229ac0
#include <math.h>
Packit 229ac0
#include <limits.h>
Packit 229ac0
Packit 229ac0
enum no_short_opts {
Packit 229ac0
	// 128 or later belong to non us-ascii character set.
Packit 229ac0
	OPT_XFER_TYPE = 128,
Packit 229ac0
	OPT_DUMP_HW_PARAMS,
Packit 229ac0
	OPT_PERIOD_SIZE,
Packit 229ac0
	OPT_BUFFER_SIZE,
Packit 229ac0
	// Obsoleted.
Packit 229ac0
	OPT_MAX_FILE_TIME,
Packit 229ac0
	OPT_USE_STRFTIME,
Packit 229ac0
	OPT_PROCESS_ID_FILE,
Packit 229ac0
};
Packit 229ac0
Packit 229ac0
static void print_help()
Packit 229ac0
{
Packit 229ac0
	printf(
Packit 229ac0
"Usage:\n"
Packit 229ac0
"  axfer transfer DIRECTION [ COMMON-OPTIONS ] [ BACKEND-OPTIONS ]\n"
Packit 229ac0
"\n"
Packit 229ac0
"  where:\n"
Packit 229ac0
"    DIRECTION = capture | playback\n"
Packit 229ac0
"    COMMON-OPTIONS =\n"
Packit 229ac0
"      -h, --help              help\n"
Packit 229ac0
"      -v, --verbose           verbose\n"
Packit 229ac0
"      -q, --quiet             quiet mode\n"
Packit 229ac0
"      -d, --duration=#        interrupt after # seconds\n"
Packit 229ac0
"      -s, --samples=#         interrupt after # frames\n"
Packit 229ac0
"      -f, --format=FORMAT     sample format (case-insensitive)\n"
Packit 229ac0
"      -c, --channels=#        channels\n"
Packit 229ac0
"      -r, --rate=#            numeric sample rate in unit of Hz or kHz\n"
Packit 229ac0
"      -t, --file-type=TYPE    file type (wav, au, sparc, voc or raw, case-insentive)\n"
Packit 229ac0
"      -I, --separate-channels one file for each channel\n"
Packit 229ac0
"      --dump-hw-params        dump hw_params of the device\n"
Packit 229ac0
"      --xfer-type=BACKEND     backend type (libasound, libffado)\n"
Packit 229ac0
	);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int allocate_paths(struct xfer_context *xfer, char *const *paths,
Packit 229ac0
			   unsigned int count)
Packit 229ac0
{
Packit 229ac0
	bool stdio = false;
Packit 229ac0
	int i;
Packit 229ac0
Packit 229ac0
	if (count == 0) {
Packit 229ac0
		stdio = true;
Packit 229ac0
		count = 1;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	xfer->paths = calloc(count, sizeof(xfer->paths[0]));
Packit 229ac0
	if (xfer->paths == NULL)
Packit 229ac0
		return -ENOMEM;
Packit 229ac0
	xfer->path_count = count;
Packit 229ac0
Packit 229ac0
	if (stdio) {
Packit 229ac0
		xfer->paths[0] = strndup("-", PATH_MAX);
Packit 229ac0
		if (xfer->paths[0] == NULL)
Packit 229ac0
			return -ENOMEM;
Packit 229ac0
	} else {
Packit 229ac0
		for (i = 0; i < count; ++i) {
Packit 229ac0
			xfer->paths[i] = strndup(paths[i], PATH_MAX);
Packit 229ac0
			if (xfer->paths[i] == NULL)
Packit 229ac0
				return -ENOMEM;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return 0;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int verify_cntr_format(struct xfer_context *xfer)
Packit 229ac0
{
Packit 229ac0
	static const struct {
Packit 229ac0
		const char *const literal;
Packit 229ac0
		enum container_format cntr_format;
Packit 229ac0
	} *entry, entries[] = {
Packit 229ac0
		{"raw",		CONTAINER_FORMAT_RAW},
Packit 229ac0
		{"voc",		CONTAINER_FORMAT_VOC},
Packit 229ac0
		{"wav",		CONTAINER_FORMAT_RIFF_WAVE},
Packit 229ac0
		{"au",		CONTAINER_FORMAT_AU},
Packit 229ac0
		{"sparc",	CONTAINER_FORMAT_AU},
Packit 229ac0
	};
Packit 229ac0
	int i;
Packit 229ac0
Packit 229ac0
	for (i = 0; i < ARRAY_SIZE(entries); ++i) {
Packit 229ac0
		entry = &entries[i];
Packit 229ac0
		if (strcasecmp(xfer->cntr_format_literal, entry->literal))
Packit 229ac0
			continue;
Packit 229ac0
Packit 229ac0
		xfer->cntr_format = entry->cntr_format;
Packit 229ac0
		return 0;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	fprintf(stderr, "unrecognized file format '%s'\n",
Packit 229ac0
		xfer->cntr_format_literal);
Packit 229ac0
Packit 229ac0
	return -EINVAL;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
// This should be called after 'verify_cntr_format()'.
Packit 229ac0
static int verify_sample_format(struct xfer_context *xfer)
Packit 229ac0
{
Packit 229ac0
	static const struct {
Packit 229ac0
		const char *const literal;
Packit 229ac0
		unsigned int frames_per_second;
Packit 229ac0
		unsigned int samples_per_frame;
Packit 229ac0
		snd_pcm_format_t le_format;
Packit 229ac0
		snd_pcm_format_t be_format;
Packit 229ac0
	} *entry, entries[] = {
Packit 229ac0
		{"cd",	44100, 2, SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE},
Packit 229ac0
		{"cdr",	44100, 2, SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE},
Packit 229ac0
		{"dat",	48000, 2, SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE},
Packit 229ac0
	};
Packit 229ac0
	int i;
Packit 229ac0
Packit 229ac0
	xfer->sample_format = snd_pcm_format_value(xfer->sample_format_literal);
Packit 229ac0
	if (xfer->sample_format != SND_PCM_FORMAT_UNKNOWN)
Packit 229ac0
		return 0;
Packit 229ac0
Packit 229ac0
	for (i = 0; i < ARRAY_SIZE(entries); ++i) {
Packit 229ac0
		entry = &entries[i];
Packit 229ac0
		if (strcmp(entry->literal, xfer->sample_format_literal))
Packit 229ac0
			continue;
Packit 229ac0
Packit 229ac0
		if (xfer->frames_per_second > 0 &&
Packit 229ac0
		    xfer->frames_per_second != entry->frames_per_second) {
Packit 229ac0
			fprintf(stderr,
Packit 229ac0
				"'%s' format can't be used with rate except "
Packit 229ac0
				"for %u.\n",
Packit 229ac0
				entry->literal, entry->frames_per_second);
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
Packit 229ac0
		if (xfer->samples_per_frame > 0 &&
Packit 229ac0
		    xfer->samples_per_frame != entry->samples_per_frame) {
Packit 229ac0
			fprintf(stderr,
Packit 229ac0
				"'%s' format can't be used with channel except "
Packit 229ac0
				"for %u.\n",
Packit 229ac0
				entry->literal, entry->samples_per_frame);
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
Packit 229ac0
		xfer->frames_per_second = entry->frames_per_second;
Packit 229ac0
		xfer->samples_per_frame = entry->samples_per_frame;
Packit 229ac0
		if (xfer->cntr_format == CONTAINER_FORMAT_AU)
Packit 229ac0
			xfer->sample_format = entry->be_format;
Packit 229ac0
		else
Packit 229ac0
			xfer->sample_format = entry->le_format;
Packit 229ac0
Packit 229ac0
		return 0;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	fprintf(stderr, "wrong extended format '%s'\n",
Packit 229ac0
		xfer->sample_format_literal);
Packit 229ac0
Packit 229ac0
	return -EINVAL;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int validate_options(struct xfer_context *xfer)
Packit 229ac0
{
Packit 229ac0
	unsigned int val;
Packit 229ac0
	int err = 0;
Packit 229ac0
Packit 229ac0
	if (xfer->cntr_format_literal == NULL) {
Packit 229ac0
		if (xfer->direction == SND_PCM_STREAM_CAPTURE) {
Packit 229ac0
			// To stdout.
Packit 229ac0
			if (xfer->path_count == 1 &&
Packit 229ac0
			    !strcmp(xfer->paths[0], "-")) {
Packit 229ac0
				xfer->cntr_format = CONTAINER_FORMAT_RAW;
Packit 229ac0
			} else {
Packit 229ac0
				// Use first path as a representative.
Packit 229ac0
				xfer->cntr_format = container_format_from_path(
Packit 229ac0
								xfer->paths[0]);
Packit 229ac0
			}
Packit 229ac0
		}
Packit 229ac0
		// For playback, perform auto-detection.
Packit 229ac0
	} else {
Packit 229ac0
		err = verify_cntr_format(xfer);
Packit 229ac0
	}
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	if (xfer->multiple_cntrs) {
Packit 229ac0
		if (!strcmp(xfer->paths[0], "-")) {
Packit 229ac0
			fprintf(stderr,
Packit 229ac0
				"An option for separated channels is not "
Packit 229ac0
				"available with stdin/stdout.\n");
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
Packit 229ac0
		// For captured PCM frames, even if one path is given for
Packit 229ac0
		// container files, it can be used to generate several paths.
Packit 229ac0
		// For this purpose, please see
Packit 229ac0
		// 'xfer_options_fixup_paths()'.
Packit 229ac0
		if (xfer->direction == SND_PCM_STREAM_PLAYBACK) {
Packit 229ac0
			// Require several paths for containers.
Packit 229ac0
			if (xfer->path_count == 1) {
Packit 229ac0
				fprintf(stderr,
Packit 229ac0
					"An option for separated channels "
Packit 229ac0
					"requires several files to playback "
Packit 229ac0
					"PCM frames.\n");
Packit 229ac0
				return -EINVAL;
Packit 229ac0
			}
Packit 229ac0
		}
Packit 229ac0
	} else {
Packit 229ac0
		// A single path is available only.
Packit 229ac0
		if (xfer->path_count > 1) {
Packit 229ac0
			fprintf(stderr,
Packit 229ac0
				"When using several files, an option for "
Packit 229ac0
				"sepatated channels is used with.\n");
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	xfer->sample_format = SND_PCM_FORMAT_UNKNOWN;
Packit 229ac0
	if (xfer->sample_format_literal) {
Packit 229ac0
		err = verify_sample_format(xfer);
Packit 229ac0
		if (err < 0)
Packit 229ac0
			return err;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	val = xfer->frames_per_second;
Packit 229ac0
	if (xfer->frames_per_second == 0)
Packit 229ac0
		xfer->frames_per_second = 8000;
Packit 229ac0
	if (xfer->frames_per_second < 1000)
Packit 229ac0
		xfer->frames_per_second *= 1000;
Packit 229ac0
	if (xfer->frames_per_second < 2000 ||
Packit 229ac0
	    xfer->frames_per_second > 192000) {
Packit 229ac0
		fprintf(stderr, "bad speed value '%i'\n", val);
Packit 229ac0
		return -EINVAL;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (xfer->samples_per_frame > 0) {
Packit 229ac0
		if (xfer->samples_per_frame < 1 ||
Packit 229ac0
		    xfer->samples_per_frame > 256) {
Packit 229ac0
			fprintf(stderr, "invalid channels argument '%u'\n",
Packit 229ac0
				xfer->samples_per_frame);
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return err;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
int xfer_options_parse_args(struct xfer_context *xfer,
Packit 229ac0
			    const struct xfer_data *data, int argc,
Packit 229ac0
			    char *const *argv)
Packit 229ac0
{
Packit 229ac0
	static const char *short_opts = "CPhvqd:s:f:c:r:t:IV:i";
Packit 229ac0
	static const struct option long_opts[] = {
Packit 229ac0
		// For generic purposes.
Packit 229ac0
		{"capture",		0, 0, 'C'},
Packit 229ac0
		{"playback",		0, 0, 'P'},
Packit 229ac0
		{"xfer-type",		1, 0, OPT_XFER_TYPE},
Packit 229ac0
		{"help",		0, 0, 'h'},
Packit 229ac0
		{"verbose",		0, 0, 'v'},
Packit 229ac0
		{"quiet",		0, 0, 'q'},
Packit 229ac0
		{"duration",		1, 0, 'd'},
Packit 229ac0
		{"samples",		1, 0, 's'},
Packit 229ac0
		// For transfer backend.
Packit 229ac0
		{"format",		1, 0, 'f'},
Packit 229ac0
		{"channels",		1, 0, 'c'},
Packit 229ac0
		{"rate",		1, 0, 'r'},
Packit 229ac0
		// For containers.
Packit 229ac0
		{"file-type",		1, 0, 't'},
Packit 229ac0
		// For mapper.
Packit 229ac0
		{"separate-channels",	0, 0, 'I'},
Packit 229ac0
		// For debugging.
Packit 229ac0
		{"dump-hw-params",	0, 0, OPT_DUMP_HW_PARAMS},
Packit 229ac0
		// Obsoleted.
Packit 229ac0
		{"max-file-time",	1, 0, OPT_MAX_FILE_TIME},
Packit 229ac0
		{"use-strftime",	0, 0, OPT_USE_STRFTIME},
Packit 229ac0
		{"process-id-file",	1, 0, OPT_PROCESS_ID_FILE},
Packit 229ac0
		{"vumeter",		1, 0, 'V'},
Packit 229ac0
		{"interactive",		0, 0, 'i'},
Packit 229ac0
	};
Packit 229ac0
	char *s_opts;
Packit 229ac0
	struct option *l_opts;
Packit 229ac0
	int l_index;
Packit 229ac0
	int key;
Packit 229ac0
	int err = 0;
Packit 229ac0
Packit 229ac0
	// Concatenate short options.
Packit 229ac0
	s_opts = malloc(strlen(data->s_opts) + strlen(short_opts) + 1);
Packit 229ac0
	if (s_opts == NULL)
Packit 229ac0
		return -ENOMEM;
Packit 229ac0
	strcpy(s_opts, data->s_opts);
Packit 229ac0
	strcpy(s_opts + strlen(s_opts), short_opts);
Packit 229ac0
	s_opts[strlen(data->s_opts) + strlen(short_opts)] = '\0';
Packit 229ac0
Packit 229ac0
	// Concatenate long options, including a sentinel.
Packit 229ac0
	l_opts = calloc(ARRAY_SIZE(long_opts) * data->l_opts_count + 1,
Packit 229ac0
			sizeof(*l_opts));
Packit 229ac0
	if (l_opts == NULL) {
Packit 229ac0
		free(s_opts);
Packit 229ac0
		return -ENOMEM;
Packit 229ac0
	}
Packit 229ac0
	memcpy(l_opts, long_opts, ARRAY_SIZE(long_opts) * sizeof(*l_opts));
Packit 229ac0
	memcpy(&l_opts[ARRAY_SIZE(long_opts)], data->l_opts,
Packit 229ac0
	       data->l_opts_count * sizeof(*l_opts));
Packit 229ac0
Packit 229ac0
	// Parse options.
Packit 229ac0
	l_index = 0;
Packit 229ac0
	optarg = NULL;
Packit 229ac0
	optind = 1;
Packit 229ac0
	opterr = 1;	// use error output.
Packit 229ac0
	optopt = 0;
Packit 229ac0
	while (1) {
Packit 229ac0
		key = getopt_long(argc, argv, s_opts, l_opts, &l_index);
Packit 229ac0
		if (key < 0)
Packit 229ac0
			break;
Packit 229ac0
		else if (key == 'C')
Packit 229ac0
			;	// already parsed.
Packit 229ac0
		else if (key == 'P')
Packit 229ac0
			;	// already parsed.
Packit 229ac0
		else if (key == OPT_XFER_TYPE)
Packit 229ac0
			;	// already parsed.
Packit 229ac0
		else if (key == 'h')
Packit 229ac0
			xfer->help = true;
Packit 229ac0
		else if (key == 'v')
Packit 229ac0
			++xfer->verbose;
Packit 229ac0
		else if (key == 'q')
Packit 229ac0
			xfer->quiet = true;
Packit 229ac0
		else if (key == 'd')
Packit 229ac0
			xfer->duration_seconds = arg_parse_decimal_num(optarg, &err;;
Packit 229ac0
		else if (key == 's')
Packit 229ac0
			xfer->duration_frames = arg_parse_decimal_num(optarg, &err;;
Packit 229ac0
		else if (key == 'f')
Packit 229ac0
			xfer->sample_format_literal = arg_duplicate_string(optarg, &err;;
Packit 229ac0
		else if (key == 'c')
Packit 229ac0
			xfer->samples_per_frame = arg_parse_decimal_num(optarg, &err;;
Packit 229ac0
		else if (key == 'r')
Packit 229ac0
			xfer->frames_per_second = arg_parse_decimal_num(optarg, &err;;
Packit 229ac0
		else if (key == 't')
Packit 229ac0
			xfer->cntr_format_literal = arg_duplicate_string(optarg, &err;;
Packit 229ac0
		else if (key == 'I')
Packit 229ac0
			xfer->multiple_cntrs = true;
Packit 229ac0
		else if (key == OPT_DUMP_HW_PARAMS)
Packit 229ac0
			xfer->dump_hw_params = true;
Packit 229ac0
		else if (key == '?') {
Packit 229ac0
			free(l_opts);
Packit 229ac0
			free(s_opts);
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		}
Packit 229ac0
		else if (key == OPT_MAX_FILE_TIME ||
Packit 229ac0
			 key == OPT_USE_STRFTIME ||
Packit 229ac0
			 key == OPT_PROCESS_ID_FILE ||
Packit 229ac0
			 key == 'V' ||
Packit 229ac0
			 key == 'i') {
Packit 229ac0
			fprintf(stderr,
Packit 229ac0
				"An option '--%s' is obsoleted and has no "
Packit 229ac0
				"effect.\n",
Packit 229ac0
				l_opts[l_index].name);
Packit 229ac0
			err = -EINVAL;
Packit 229ac0
		} else {
Packit 229ac0
			err = xfer->ops->parse_opt(xfer, key, optarg);
Packit 229ac0
			if (err < 0 && err != -ENXIO)
Packit 229ac0
				break;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	free(l_opts);
Packit 229ac0
	free(s_opts);
Packit 229ac0
Packit 229ac0
	if (xfer->help) {
Packit 229ac0
		print_help();
Packit 229ac0
		if (xfer->ops->help) {
Packit 229ac0
			printf("\n");
Packit 229ac0
			printf("    BACKEND-OPTIONS (%s) =\n",
Packit 229ac0
			       xfer_label_from_type(xfer->type));
Packit 229ac0
			xfer->ops->help(xfer);
Packit 229ac0
		}
Packit 229ac0
		return 0;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	err = allocate_paths(xfer, argv + optind, argc - optind);
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	return validate_options(xfer);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
void xfer_options_calculate_duration(struct xfer_context *xfer,
Packit 229ac0
				     uint64_t *total_frame_count)
Packit 229ac0
{
Packit 229ac0
	uint64_t frame_count;
Packit 229ac0
Packit 229ac0
	if (xfer->duration_seconds > 0) {
Packit 229ac0
		frame_count = (uint64_t)xfer->duration_seconds * (uint64_t)xfer->frames_per_second;
Packit 229ac0
		if (frame_count < *total_frame_count)
Packit 229ac0
			*total_frame_count = frame_count;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (xfer->duration_frames > 0) {
Packit 229ac0
		frame_count = xfer->duration_frames;
Packit 229ac0
		if (frame_count < *total_frame_count)
Packit 229ac0
			*total_frame_count = frame_count;
Packit 229ac0
	}
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static const char *const allowed_duplication[] = {
Packit 229ac0
	"/dev/null",
Packit 229ac0
	"/dev/zero",
Packit 229ac0
	"/dev/full",
Packit 229ac0
	"/dev/random",
Packit 229ac0
	"/dev/urandom",
Packit 229ac0
};
Packit 229ac0
Packit 229ac0
static int generate_path_with_suffix(struct xfer_context *xfer,
Packit 229ac0
				     const char *template, unsigned int index,
Packit 229ac0
				     const char *suffix)
Packit 229ac0
{
Packit 229ac0
	static const char *const single_format = "%s%s";
Packit 229ac0
	static const char *const multiple_format = "%s-%i%s";
Packit 229ac0
	unsigned int len;
Packit 229ac0
Packit 229ac0
	len = strlen(template) + strlen(suffix) + 1;
Packit 229ac0
	if (xfer->path_count > 1)
Packit 229ac0
		len += (unsigned int)log10(xfer->path_count) + 2;
Packit 229ac0
Packit 229ac0
	xfer->paths[index] = malloc(len);
Packit 229ac0
	if (xfer->paths[index] == NULL)
Packit 229ac0
		return -ENOMEM;
Packit 229ac0
Packit 229ac0
	if (xfer->path_count == 1) {
Packit 229ac0
		snprintf(xfer->paths[index], len, single_format, template,
Packit 229ac0
			 suffix);
Packit 229ac0
	} else {
Packit 229ac0
		snprintf(xfer->paths[index], len, multiple_format, template,
Packit 229ac0
			 index, suffix);
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return 0;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int generate_path_without_suffix(struct xfer_context *xfer,
Packit 229ac0
				        const char *template,
Packit 229ac0
					unsigned int index, const char *suffix)
Packit 229ac0
{
Packit 229ac0
	static const char *const single_format = "%s";
Packit 229ac0
	static const char *const multiple_format = "%s-%i";
Packit 229ac0
	unsigned int len;
Packit 229ac0
Packit 229ac0
	len = strlen(template) + 1;
Packit 229ac0
	if (xfer->path_count > 1)
Packit 229ac0
		len += (unsigned int)log10(xfer->path_count) + 2;
Packit 229ac0
Packit 229ac0
	xfer->paths[index] = malloc(len);
Packit 229ac0
	if (xfer->paths[index] == NULL)
Packit 229ac0
		return -ENOMEM;
Packit 229ac0
Packit 229ac0
	if (xfer->path_count == 1) {
Packit 229ac0
		snprintf(xfer->paths[index], len, single_format, template);
Packit 229ac0
	} else {
Packit 229ac0
		snprintf(xfer->paths[index], len, multiple_format, template,
Packit 229ac0
			index);
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return 0;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int generate_path(struct xfer_context *xfer, char *template,
Packit 229ac0
			 unsigned int index, const char *suffix)
Packit 229ac0
{
Packit 229ac0
	int (*generator)(struct xfer_context *xfer, const char *template,
Packit 229ac0
			 unsigned int index, const char *suffix);
Packit 229ac0
	char *pos;
Packit 229ac0
Packit 229ac0
	if (strlen(suffix) > 0) {
Packit 229ac0
		pos = template + strlen(template) - strlen(suffix);
Packit 229ac0
		// Separate filename and suffix.
Packit 229ac0
		if (!strcmp(pos, suffix))
Packit 229ac0
			*pos = '\0';
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	// Select handlers.
Packit 229ac0
	if (strlen(suffix) > 0)
Packit 229ac0
		generator = generate_path_with_suffix;
Packit 229ac0
	else
Packit 229ac0
		generator = generate_path_without_suffix;
Packit 229ac0
Packit 229ac0
	return generator(xfer, template, index, suffix);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int create_paths(struct xfer_context *xfer, unsigned int path_count)
Packit 229ac0
{
Packit 229ac0
	char *template;
Packit 229ac0
	const char *suffix;
Packit 229ac0
	int i, j;
Packit 229ac0
	int err = 0;
Packit 229ac0
Packit 229ac0
	// Can cause memory leak.
Packit 229ac0
	assert(xfer->path_count == 1);
Packit 229ac0
	assert(xfer->paths);
Packit 229ac0
	assert(xfer->paths[0]);
Packit 229ac0
	assert(xfer->paths[0][0] != '\0');
Packit 229ac0
Packit 229ac0
	// Release at first.
Packit 229ac0
	template = xfer->paths[0];
Packit 229ac0
	free(xfer->paths);
Packit 229ac0
	xfer->paths = NULL;
Packit 229ac0
Packit 229ac0
	// Allocate again.
Packit 229ac0
	xfer->paths = calloc(path_count, sizeof(*xfer->paths));
Packit 229ac0
	if (xfer->paths == NULL) {
Packit 229ac0
		err = -ENOMEM;
Packit 229ac0
		goto end;
Packit 229ac0
	}
Packit 229ac0
	xfer->path_count = path_count;
Packit 229ac0
Packit 229ac0
	suffix = container_suffix_from_format(xfer->cntr_format);
Packit 229ac0
Packit 229ac0
	for (i = 0; i < xfer->path_count; ++i) {
Packit 229ac0
		// Some file names are allowed to be duplicated.
Packit 229ac0
		for (j = 0; j < ARRAY_SIZE(allowed_duplication); ++j) {
Packit 229ac0
			if (!strcmp(template, allowed_duplication[j]))
Packit 229ac0
				break;
Packit 229ac0
		}
Packit 229ac0
		if (j < ARRAY_SIZE(allowed_duplication))
Packit 229ac0
			continue;
Packit 229ac0
Packit 229ac0
		err = generate_path(xfer, template, i, suffix);
Packit 229ac0
		if (err < 0)
Packit 229ac0
			break;
Packit 229ac0
	}
Packit 229ac0
end:
Packit 229ac0
	free(template);
Packit 229ac0
Packit 229ac0
	return err;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int fixup_paths(struct xfer_context *xfer)
Packit 229ac0
{
Packit 229ac0
	const char *suffix;
Packit 229ac0
	char *template;
Packit 229ac0
	int i, j;
Packit 229ac0
	int err = 0;
Packit 229ac0
Packit 229ac0
	suffix = container_suffix_from_format(xfer->cntr_format);
Packit 229ac0
Packit 229ac0
	for (i = 0; i < xfer->path_count; ++i) {
Packit 229ac0
		// Some file names are allowed to be duplicated.
Packit 229ac0
		for (j = 0; j < ARRAY_SIZE(allowed_duplication); ++j) {
Packit 229ac0
			if (!strcmp(xfer->paths[i], allowed_duplication[j]))
Packit 229ac0
				break;
Packit 229ac0
		}
Packit 229ac0
		if (j < ARRAY_SIZE(allowed_duplication))
Packit 229ac0
			continue;
Packit 229ac0
Packit 229ac0
		template = xfer->paths[i];
Packit 229ac0
		xfer->paths[i] = NULL;
Packit 229ac0
		err = generate_path(xfer, template, i, suffix);
Packit 229ac0
		free(template);
Packit 229ac0
		if (err < 0)
Packit 229ac0
			break;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return err;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
int xfer_options_fixup_paths(struct xfer_context *xfer)
Packit 229ac0
{
Packit 229ac0
	int i, j;
Packit 229ac0
	int err;
Packit 229ac0
Packit 229ac0
	if (xfer->path_count == 1) {
Packit 229ac0
		// Nothing to do for sign of stdin/stdout.
Packit 229ac0
		if (!strcmp(xfer->paths[0], "-"))
Packit 229ac0
			return 0;
Packit 229ac0
		if (!xfer->multiple_cntrs)
Packit 229ac0
			err = fixup_paths(xfer);
Packit 229ac0
		else
Packit 229ac0
			err = create_paths(xfer, xfer->samples_per_frame);
Packit 229ac0
	} else {
Packit 229ac0
		if (!xfer->multiple_cntrs)
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		if (xfer->path_count != xfer->samples_per_frame)
Packit 229ac0
			return -EINVAL;
Packit 229ac0
		else
Packit 229ac0
			err = fixup_paths(xfer);
Packit 229ac0
	}
Packit 229ac0
	if (err < 0)
Packit 229ac0
		return err;
Packit 229ac0
Packit 229ac0
	// Check duplication of the paths.
Packit 229ac0
	for (i = 0; i < xfer->path_count - 1; ++i) {
Packit 229ac0
		// Some file names are allowed to be duplicated.
Packit 229ac0
		for (j = 0; j < ARRAY_SIZE(allowed_duplication); ++j) {
Packit 229ac0
			if (!strcmp(xfer->paths[i], allowed_duplication[j]))
Packit 229ac0
				break;
Packit 229ac0
		}
Packit 229ac0
		if (j < ARRAY_SIZE(allowed_duplication))
Packit 229ac0
			continue;
Packit 229ac0
Packit 229ac0
		for (j = i + 1; j < xfer->path_count; ++j) {
Packit 229ac0
			if (!strcmp(xfer->paths[i], xfer->paths[j])) {
Packit 229ac0
				fprintf(stderr,
Packit 229ac0
					"Detect duplicated file names:\n");
Packit 229ac0
				err = -EINVAL;
Packit 229ac0
				break;
Packit 229ac0
			}
Packit 229ac0
		}
Packit 229ac0
		if (j < xfer->path_count)
Packit 229ac0
			break;
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	if (xfer->verbose > 1)
Packit 229ac0
		fprintf(stderr, "Handled file names:\n");
Packit 229ac0
	if (err < 0 || xfer->verbose > 1) {
Packit 229ac0
		for (i = 0; i < xfer->path_count; ++i)
Packit 229ac0
			fprintf(stderr, "    %d: %s\n", i, xfer->paths[i]);
Packit 229ac0
	}
Packit 229ac0
Packit 229ac0
	return err;
Packit 229ac0
}