Blame axfer/main.c

Packit Service a9274b
// SPDX-License-Identifier: GPL-2.0
Packit Service a9274b
// main.c - an entry point for this program.
Packit Service a9274b
//
Packit Service a9274b
// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
Packit Service a9274b
//
Packit Service a9274b
// Originally written as 'aplay', by Michael Beck and Jaroslav Kysela.
Packit Service a9274b
//
Packit Service a9274b
// Licensed under the terms of the GNU General Public License, version 2.
Packit Service a9274b
Packit Service a9274b
#include "subcmd.h"
Packit Service a9274b
#include "misc.h"
Packit Service a9274b
Packit Service a9274b
#include "version.h"
Packit Service a9274b
Packit Service a9274b
#include <stdio.h>
Packit Service a9274b
#include <stdlib.h>
Packit Service a9274b
#include <stdbool.h>
Packit Service a9274b
#include <string.h>
Packit Service a9274b
Packit Service a9274b
enum subcmds {
Packit Service a9274b
	SUBCMD_TRANSFER = 0,
Packit Service a9274b
	SUBCMD_LIST,
Packit Service a9274b
	SUBCMD_HELP,
Packit Service a9274b
	SUBCMD_VERSION,
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
char *arg_duplicate_string(const char *str, int *err)
Packit Service a9274b
{
Packit Service a9274b
	char *ptr;
Packit Service a9274b
Packit Service a9274b
	// For safe.
Packit Service a9274b
	if (strlen(str) > 1024) {
Packit Service a9274b
		*err = -EINVAL;
Packit Service a9274b
		return NULL;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	ptr = strdup(str);
Packit Service a9274b
	if (ptr == NULL)
Packit Service a9274b
		*err = -ENOMEM;
Packit Service a9274b
Packit Service a9274b
	return ptr;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
long arg_parse_decimal_num(const char *str, int *err)
Packit Service a9274b
{
Packit Service a9274b
	long val;
Packit Service a9274b
	char *endptr;
Packit Service a9274b
Packit Service a9274b
	errno = 0;
Packit Service a9274b
	val = strtol(str, &endptr, 0);
Packit Service a9274b
	if (errno > 0) {
Packit Service a9274b
		*err = -errno;
Packit Service a9274b
		return 0;
Packit Service a9274b
	}
Packit Service a9274b
	if (*endptr != '\0') {
Packit Service a9274b
		*err = -EINVAL;
Packit Service a9274b
		return 0;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return val;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void print_version(const char *const cmdname)
Packit Service a9274b
{
Packit Service a9274b
	printf("%s: version %s\n", cmdname, SND_UTIL_VERSION_STR);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void print_help(void)
Packit Service a9274b
{
Packit Service a9274b
	printf(
Packit Service a9274b
"Usage:\n"
Packit Service a9274b
"  axfer transfer DIRECTION OPTIONS\n"
Packit Service a9274b
"  axfer list DIRECTION OPTIONS\n"
Packit Service a9274b
"  axfer version\n"
Packit Service a9274b
"  axfer help\n"
Packit Service a9274b
"\n"
Packit Service a9274b
"  where:\n"
Packit Service a9274b
"    DIRECTION = capture | playback\n"
Packit Service a9274b
"    OPTIONS = -h | --help | (subcommand specific)\n"
Packit Service a9274b
	);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
// Backward compatibility to aplay(1).
Packit Service a9274b
static bool decide_subcmd(int argc, char *const *argv, enum subcmds *subcmd)
Packit Service a9274b
{
Packit Service a9274b
	static const struct {
Packit Service a9274b
		const char *const name;
Packit Service a9274b
		enum subcmds subcmd;
Packit Service a9274b
	} long_opts[] = {
Packit Service a9274b
		{"--list-devices",	SUBCMD_LIST},
Packit Service a9274b
		{"--list-pcms",		SUBCMD_LIST},
Packit Service a9274b
		{"--help",  		SUBCMD_HELP},
Packit Service a9274b
		{"--version",  		SUBCMD_VERSION},
Packit Service a9274b
	};
Packit Service a9274b
	static const struct {
Packit Service a9274b
		unsigned char c;
Packit Service a9274b
		enum subcmds subcmd;
Packit Service a9274b
	} short_opts[] = {
Packit Service a9274b
		{'l', SUBCMD_LIST},
Packit Service a9274b
		{'L', SUBCMD_LIST},
Packit Service a9274b
		{'h', SUBCMD_HELP},
Packit Service a9274b
	};
Packit Service a9274b
	char *pos;
Packit Service a9274b
	int i, j;
Packit Service a9274b
Packit Service a9274b
	if (argc == 1)
Packit Service a9274b
		return false;
Packit Service a9274b
Packit Service a9274b
	// Original command system. For long options.
Packit Service a9274b
	for (i = 0; i < ARRAY_SIZE(long_opts); ++i) {
Packit Service a9274b
		for (j = 0; j < argc; ++j) {
Packit Service a9274b
			if (!strcmp(long_opts[i].name, argv[j])) {
Packit Service a9274b
				*subcmd = long_opts[i].subcmd;
Packit Service a9274b
				return true;
Packit Service a9274b
			}
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// Original command system. For short options.
Packit Service a9274b
	for (i = 1; i < argc; ++i) {
Packit Service a9274b
		// Pick up short options only.
Packit Service a9274b
		if (argv[i][0] != '-' || argv[i][0] == '\0' ||
Packit Service a9274b
		    argv[i][1] == '-' || argv[i][1] == '\0')
Packit Service a9274b
			continue;
Packit Service a9274b
		for (pos = argv[i]; *pos != '\0'; ++pos) {
Packit Service a9274b
			for (j = 0; j < ARRAY_SIZE(short_opts); ++j) {
Packit Service a9274b
				if (*pos == short_opts[j].c) {
Packit Service a9274b
					*subcmd = short_opts[j].subcmd;
Packit Service a9274b
					return true;
Packit Service a9274b
				}
Packit Service a9274b
			}
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return false;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
// Backward compatibility to aplay(1).
Packit Service a9274b
static bool decide_direction(int argc, char *const *argv,
Packit Service a9274b
			     snd_pcm_stream_t *direction)
Packit Service a9274b
{
Packit Service a9274b
	static const struct {
Packit Service a9274b
		const char *const name;
Packit Service a9274b
		snd_pcm_stream_t direction;
Packit Service a9274b
	} long_opts[] = {
Packit Service a9274b
		{"--capture",	SND_PCM_STREAM_CAPTURE},
Packit Service a9274b
		{"--playback",	SND_PCM_STREAM_PLAYBACK},
Packit Service a9274b
	};
Packit Service a9274b
	static const struct {
Packit Service a9274b
		unsigned char c;
Packit Service a9274b
		snd_pcm_stream_t direction;
Packit Service a9274b
	} short_opts[] = {
Packit Service a9274b
		{'C',		SND_PCM_STREAM_CAPTURE},
Packit Service a9274b
		{'P',		SND_PCM_STREAM_PLAYBACK},
Packit Service a9274b
	};
Packit Service a9274b
	static const char *const aliases[] = {
Packit Service a9274b
		[SND_PCM_STREAM_CAPTURE] = "arecord",
Packit Service a9274b
		[SND_PCM_STREAM_PLAYBACK] = "aplay",
Packit Service a9274b
	};
Packit Service a9274b
	int i, j;
Packit Service a9274b
	char *pos;
Packit Service a9274b
Packit Service a9274b
	// Original command system. For long options.
Packit Service a9274b
	for (i = 0; i < ARRAY_SIZE(long_opts); ++i) {
Packit Service a9274b
		for (j = 0; j < argc; ++j) {
Packit Service a9274b
			if (!strcmp(long_opts[i].name, argv[j])) {
Packit Service a9274b
				*direction = long_opts[i].direction;
Packit Service a9274b
				return true;
Packit Service a9274b
			}
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// Original command system. For short options.
Packit Service a9274b
	for (i = 1; i < argc; ++i) {
Packit Service a9274b
		// Pick up short options only.
Packit Service a9274b
		if (argv[i][0] != '-' || argv[i][0] == '\0' ||
Packit Service a9274b
		    argv[i][1] == '-' || argv[i][1] == '\0')
Packit Service a9274b
			continue;
Packit Service a9274b
		for (pos = argv[i]; *pos != '\0'; ++pos) {
Packit Service a9274b
			for (j = 0; j < ARRAY_SIZE(short_opts); ++j) {
Packit Service a9274b
				if (*pos == short_opts[j].c) {
Packit Service a9274b
					*direction = short_opts[j].direction;
Packit Service a9274b
					return true;
Packit Service a9274b
				}
Packit Service a9274b
			}
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// If not decided yet, judge according to command name.
Packit Service a9274b
	for (i = 0; i < ARRAY_SIZE(aliases); ++i) {
Packit Service a9274b
		for (pos = argv[0] + strlen(argv[0]); pos != argv[0]; --pos) {
Packit Service a9274b
			if (strstr(pos, aliases[i]) != NULL) {
Packit Service a9274b
				*direction = i;
Packit Service a9274b
				return true;
Packit Service a9274b
			}
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return false;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static bool detect_subcmd(int argc, char *const *argv, enum subcmds *subcmd)
Packit Service a9274b
{
Packit Service a9274b
	static const char *const subcmds[] = {
Packit Service a9274b
		[SUBCMD_TRANSFER] = "transfer",
Packit Service a9274b
		[SUBCMD_LIST] = "list",
Packit Service a9274b
		[SUBCMD_HELP] = "help",
Packit Service a9274b
		[SUBCMD_VERSION] = "version",
Packit Service a9274b
	};
Packit Service a9274b
	int i;
Packit Service a9274b
Packit Service a9274b
	if (argc < 2)
Packit Service a9274b
		return false;
Packit Service a9274b
Packit Service a9274b
	for (i = 0; i < ARRAY_SIZE(subcmds); ++i) {
Packit Service a9274b
		if (!strcmp(argv[1], subcmds[i])) {
Packit Service a9274b
			*subcmd = i;
Packit Service a9274b
			return true;
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return false;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static bool detect_direction(int argc, char *const *argv,
Packit Service a9274b
			     snd_pcm_stream_t *direction)
Packit Service a9274b
{
Packit Service a9274b
	if (argc < 3)
Packit Service a9274b
		return false;
Packit Service a9274b
Packit Service a9274b
	if (!strcmp(argv[2], "capture")) {
Packit Service a9274b
		*direction = SND_PCM_STREAM_CAPTURE;
Packit Service a9274b
		return true;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	if (!strcmp(argv[2], "playback")) {
Packit Service a9274b
		*direction = SND_PCM_STREAM_PLAYBACK;
Packit Service a9274b
		return true;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return false;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int main(int argc, char *const *argv)
Packit Service a9274b
{
Packit Service a9274b
	snd_pcm_stream_t direction;
Packit Service a9274b
	enum subcmds subcmd;
Packit Service a9274b
	int err = 0;
Packit Service a9274b
Packit Service a9274b
	// For compatibility to aplay(1) implementation.
Packit Service a9274b
	if (strstr(argv[0], "arecord") == argv[0] + strlen(argv[0]) - 7 ||
Packit Service a9274b
	    strstr(argv[0], "aplay") == argv[0] + strlen(argv[0]) - 5) {
Packit Service a9274b
		if (!decide_direction(argc, argv, &direction))
Packit Service a9274b
			direction = SND_PCM_STREAM_PLAYBACK;
Packit Service a9274b
		if (!decide_subcmd(argc, argv, &subcmd))
Packit Service a9274b
			subcmd = SUBCMD_TRANSFER;
Packit Service a9274b
	} else {
Packit Service a9274b
		// The first option should be one of subcommands.
Packit Service a9274b
		if (!detect_subcmd(argc, argv, &subcmd))
Packit Service a9274b
			subcmd = SUBCMD_HELP;
Packit Service a9274b
		// The second option should be either 'capture' or 'direction'
Packit Service a9274b
		// if subcommand is neither 'version' nor 'help'.
Packit Service a9274b
		if (subcmd != SUBCMD_VERSION && subcmd != SUBCMD_HELP) {
Packit Service a9274b
			if (!detect_direction(argc, argv, &direction)) {
Packit Service a9274b
				subcmd = SUBCMD_HELP;
Packit Service a9274b
			} else {
Packit Service a9274b
				// argv[0] is needed for unparsed option to use
Packit Service a9274b
				// getopt_long(3).
Packit Service a9274b
				argc -= 2;
Packit Service a9274b
				argv += 2;
Packit Service a9274b
			}
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	if (subcmd == SUBCMD_TRANSFER)
Packit Service a9274b
		err = subcmd_transfer(argc, argv, direction);
Packit Service a9274b
	else if (subcmd == SUBCMD_LIST)
Packit Service a9274b
		err = subcmd_list(argc, argv, direction);
Packit Service a9274b
	else if (subcmd == SUBCMD_VERSION)
Packit Service a9274b
		print_version(argv[0]);
Packit Service a9274b
	else
Packit Service a9274b
		print_help();
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return EXIT_FAILURE;
Packit Service a9274b
Packit Service a9274b
	return EXIT_SUCCESS;
Packit Service a9274b
}