/** \file play.c Paraslash's standalone player. */
#include <regex.h>
-#include <fnmatch.h>
#include <signal.h>
+#include <inttypes.h>
+#include <lopsub.h>
+#include "recv_cmd.lsg.h"
+#include "play_cmd.lsg.h"
+#include "write_cmd.lsg.h"
+#include "play.lsg.h"
#include "para.h"
#include "list.h"
-#include "play.cmdline.h"
-#include "filter.cmdline.h"
#include "error.h"
-#include "ggo.h"
#include "buffer_tree.h"
#include "version.h"
#include "string.h"
#include "afh.h"
#include "recv.h"
#include "write.h"
-#include "write_common.h"
#include "fd.h"
/**
* Playlist handling is done exclusively in play context.
*/
+/** Array of error strings. */
+DEFINE_PARA_ERRLIST;
+
+static struct lls_parse_result *play_lpr;
+
+#define CMD_PTR (lls_cmd(0, play_suite))
+#define OPT_RESULT(_name) \
+ (lls_opt_result(LSG_PLAY_PARA_PLAY_OPT_ ## _name, play_lpr))
+#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
+#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
+#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
+
/**
* Describes a request to change the state of para_play.
*
char *afhi_txt;
};
-/** Initialize the array of errors for para_play. */
-INIT_PLAY_ERRLISTS;
-
-/* Activate the afh receiver. */
-extern void afh_recv_init(struct receiver *r);
-#undef AFH_RECEIVER
-/** Initialization code for a receiver struct. */
-#define AFH_RECEIVER {.name = "afh", .init = afh_recv_init},
-/** This expands to the array of all receivers. */
-DEFINE_RECEIVER_ARRAY;
+typedef int (*play_cmd_handler_t)(struct play_task *pt,
+ struct lls_parse_result *lpr);
+struct play_command_info {
+ play_cmd_handler_t handler;
+};
+#define EXPORT_PLAY_CMD_HANDLER(_cmd) \
+ const struct play_command_info lsg_play_cmd_com_ ## _cmd ## _user_data = { \
+ .handler = com_ ## _cmd \
+ };
static int loglevel = LL_WARNING;
char *stat_item_values[NUM_STAT_ITEMS] = {NULL};
-/** Iterate over all files in the playlist. */
-#define FOR_EACH_PLAYLIST_FILE(i) for (i = 0; i < conf.inputs_num; i++)
-static struct play_args_info conf;
-
static struct sched sched = {.max_fileno = 0};
static struct play_task play_task;
-static struct receiver *afh_recv;
-static void check_afh_receiver_or_die(void)
-{
- int i;
+#define AFH_RECV_CMD (lls_cmd(LSG_RECV_CMD_CMD_AFH, recv_cmd_suite))
+#define AFH_RECV ((struct receiver *)lls_user_data(AFH_RECV_CMD))
- FOR_EACH_RECEIVER(i) {
- struct receiver *r = receivers + i;
- if (strcmp(r->name, "afh"))
- continue;
- afh_recv = r;
- return;
- }
- PARA_EMERG_LOG("fatal: afh receiver not found\n");
- exit(EXIT_FAILURE);
+static unsigned *shuffle_map;
+
+static const char *get_playlist_file(unsigned idx)
+{
+ return lls_input(shuffle_map[idx], play_lpr);
}
-__noreturn static void print_help_and_die(void)
+static void handle_help_flags(void)
{
- struct ggo_help help = DEFINE_GGO_HELP(play);
- unsigned flags = conf.detailed_help_given?
- GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
+ char *help;
- ggo_print_help(&help, flags);
- printf("supported audio formats: %s\n", AUDIO_FORMAT_HANDLERS);
- exit(0);
+ if (OPT_GIVEN(DETAILED_HELP))
+ help = lls_long_help(CMD_PTR);
+ else if (OPT_GIVEN(HELP))
+ help = lls_short_help(CMD_PTR);
+ else
+ return;
+ printf("%s\n", help);
+ free(help);
+ exit(EXIT_SUCCESS);
}
static void parse_config_or_die(int argc, char *argv[])
{
- int i, ret;
- char *config_file;
- struct play_cmdline_parser_params params = {
- .override = 0,
- .initialize = 1,
- .check_required = 0,
- .check_ambiguity = 0,
- .print_errors = 1
- };
+ const struct lls_command *cmd = CMD_PTR;
+ int i, ret, cf_argc;
+ char *cf, *errctx, **cf_argv;
+ struct lls_parse_result *cf_lpr, *merged_lpr;
+ unsigned num_kmas;
+ void *map;
+ size_t sz;
- play_cmdline_parser_ext(argc, argv, &conf, ¶ms);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
- version_handle_flag("play", conf.version_given);
- if (conf.help_given || conf.detailed_help_given)
- print_help_and_die();
- if (conf.config_file_given)
- config_file = para_strdup(conf.config_file_arg);
+ ret = lls(lls_parse(argc, argv, cmd, &play_lpr, &errctx));
+ if (ret < 0)
+ goto fail;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+ version_handle_flag("play", OPT_GIVEN(VERSION));
+ handle_help_flags();
+ if (OPT_GIVEN(CONFIG_FILE))
+ cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
else {
char *home = para_homedir();
- config_file = make_message("%s/.paraslash/play.conf", home);
+ cf = make_message("%s/.paraslash/play.conf", home);
free(home);
}
- ret = file_exists(config_file);
- if (conf.config_file_given && !ret) {
- PARA_EMERG_LOG("can not read config file %s\n", config_file);
- goto err;
- }
- if (ret) {
- params.initialize = 0;
- params.check_required = 1;
- play_cmdline_parser_config_file(config_file, &conf, ¶ms);
- loglevel = get_loglevel_by_name(conf.loglevel_arg);
+ ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ if (ret < 0) {
+ if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
+ goto free_cf;
+ if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE))
+ goto free_cf;
+ ret = 0;
+ goto free_cf;
}
- for (i = 0; i < conf.key_map_given; i++) {
- char *s = strchr(conf.key_map_arg[i] + 1, ':');
- if (s)
+ ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
+ para_munmap(map, sz);
+ if (ret < 0)
+ goto free_cf;
+ cf_argc = ret;
+ ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx));
+ lls_free_argv(cf_argv);
+ if (ret < 0)
+ goto free_cf;
+ ret = lls(lls_merge(play_lpr, cf_lpr, cmd, &merged_lpr, &errctx));
+ lls_free_parse_result(cf_lpr, cmd);
+ if (ret < 0)
+ goto free_cf;
+ lls_free_parse_result(play_lpr, cmd);
+ play_lpr = merged_lpr;
+ loglevel = OPT_UINT32_VAL(LOGLEVEL);
+
+ ret = lls(lls_check_arg_count(play_lpr, 1, INT_MAX, &errctx));
+ if (ret < 0)
+ goto free_cf;
+ num_kmas = OPT_GIVEN(KEY_MAP);
+ for (i = 0; i < num_kmas; i++) {
+ const char *kma = lls_string_val(i, OPT_RESULT(KEY_MAP));
+ if (*kma && strchr(kma + 1, ':'))
continue;
- PARA_EMERG_LOG("invalid key map arg: %s\n", conf.key_map_arg[i]);
- goto err;
+ PARA_EMERG_LOG("invalid key map arg: %s\n", kma);
+ goto free_cf;
}
- free(config_file);
- return;
-err:
- free(config_file);
+ ret = 1;
+free_cf:
+ free(cf);
+ if (ret >= 0)
+ return;
+ lls_free_parse_result(play_lpr, cmd);
+fail:
+ if (errctx)
+ PARA_EMERG_LOG("%s\n", errctx);
+ free(errctx);
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
exit(EXIT_FAILURE);
}
return result;
}
+
static void wipe_receiver_node(struct play_task *pt)
{
PARA_NOTICE_LOG("cleaning up receiver node\n");
btr_remove_node(&pt->rn.btrn);
- afh_recv->close(&pt->rn);
- afh_recv->free_config(pt->rn.conf);
+ AFH_RECV->close(&pt->rn);
+ lls_free_parse_result(pt->rn.lpr, AFH_RECV_CMD);
memset(&pt->rn, 0, sizeof(struct receiver_node));
}
static int eof_cleanup(struct play_task *pt)
{
- struct writer *w = writers + DEFAULT_WRITER;
- struct filter *decoder = filters + pt->fn.filter_num;
+ const struct filter *decoder;
+ const struct writer *w = writer_get(-1); /* default writer */
int ret;
ret = get_playback_error(pt);
task_reap(&pt->wn.task);
w->close(&pt->wn);
btr_remove_node(&pt->wn.btrn);
- w->free_config(pt->wn.conf);
+ lls_free_parse_result(pt->wn.lpr, WRITE_CMD(pt->wn.wid));
memset(&pt->wn, 0, sizeof(struct writer_node));
+ decoder = filter_get(pt->fn.filter_num);
task_reap(&pt->fn.task);
- decoder->close(&pt->fn);
+ if (decoder->close)
+ decoder->close(&pt->fn);
btr_remove_node(&pt->fn.btrn);
free(pt->fn.conf);
memset(&pt->fn, 0, sizeof(struct filter_node));
return para_random(100) - 50;
}
-static void shuffle(char **base, size_t num)
+static void init_shuffle_map(void)
{
+ unsigned n, num_inputs = lls_num_inputs(play_lpr);
+ shuffle_map = para_malloc(num_inputs * sizeof(unsigned));
+ for (n = 0; n < num_inputs; n++)
+ shuffle_map[n] = n;
+ if (!OPT_GIVEN(RANDOMIZE))
+ return;
srandom(time(NULL));
- qsort(base, num, sizeof(char *), shuffle_compare);
+ qsort(shuffle_map, num_inputs, sizeof(unsigned), shuffle_compare);
}
static struct btr_node *new_recv_btrn(struct receiver_node *rn)
{
return btr_new_node(&(struct btr_node_description)
- EMBRACE(.name = afh_recv->name, .context = rn,
- .handler = afh_recv->execute));
+ EMBRACE(.name = lls_command_name(AFH_RECV_CMD), .context = rn,
+ .handler = AFH_RECV->execute));
}
static int open_new_file(struct play_task *pt)
{
int ret;
- char *tmp, *path = conf.inputs[pt->next_file], *afh_recv_conf[] =
- {"play", "-f", path, "-b", "0", NULL};
+ const char *path = get_playlist_file(pt->next_file);
+ char *tmp = para_strdup(path), *errctx;
+ char *argv[] = {"play", "-f", tmp, "-b", "0", NULL};
PARA_NOTICE_LOG("next file: %s\n", path);
wipe_receiver_node(pt);
pt->start_chunk = 0;
pt->rn.btrn = new_recv_btrn(&pt->rn);
- pt->rn.conf = afh_recv->parse_config(ARRAY_SIZE(afh_recv_conf) - 1,
- afh_recv_conf);
- assert(pt->rn.conf);
- pt->rn.receiver = afh_recv;
- ret = afh_recv->open(&pt->rn);
+ ret = lls(lls_parse(ARRAY_SIZE(argv) - 1, argv, AFH_RECV_CMD,
+ &pt->rn.lpr, &errctx));
+ free(tmp);
+ assert(ret >= 0);
+ pt->rn.receiver = AFH_RECV;
+ ret = AFH_RECV->open(&pt->rn);
if (ret < 0) {
- PARA_ERROR_LOG("could not open %s: %s\n", path,
- para_strerror(-ret));
+ PARA_ERROR_LOG("could not open %s\n", path);
goto fail;
}
pt->audio_format_num = ret;
const char *af;
char *tmp, buf[20];
int ret;
- struct filter *decoder;
+ const struct filter *decoder;
+ static struct lls_parse_result *filter_lpr, *writer_lpr;
btr_remove_node(&pt->rn.btrn);
if (!pt->rn.receiver || pt->next_file != pt->current_file) {
/* set up decoding filter */
af = audio_format_name(pt->audio_format_num);
tmp = make_message("%sdec", af);
- ret = check_filter_arg(tmp, &pt->fn.conf);
+ ret = filter_setup(tmp, &pt->fn.conf, &filter_lpr);
freep(&tmp);
if (ret < 0)
goto fail;
pt->fn.filter_num = ret;
- decoder = filters + ret;
+ pt->fn.lpr = filter_lpr;
+ decoder = filter_get(ret);
pt->fn.btrn = btr_new_node(&(struct btr_node_description)
- EMBRACE(.name = decoder->name, .parent = pt->rn.btrn,
- .handler = decoder->execute, .context = &pt->fn));
- decoder->open(&pt->fn);
+ EMBRACE(.name = filter_name(pt->fn.filter_num),
+ .parent = pt->rn.btrn, .handler = decoder->execute,
+ .context = &pt->fn));
+ if (decoder->open)
+ decoder->open(&pt->fn);
+ PARA_INFO_LOG("buffer tree:\n");
+ btr_log_tree(pt->rn.btrn, LL_INFO);
/* setup default writer */
- pt->wn.conf = check_writer_arg_or_die(NULL, &pt->wn.writer_num);
-
+ pt->wn.wid = check_writer_arg_or_die(NULL, &writer_lpr);
+ pt->wn.lpr = writer_lpr;
/* success, register tasks */
pt->rn.task = task_register(
&(struct task_info) {
- .name = afh_recv->name,
- .pre_select = afh_recv->pre_select,
- .post_select = afh_recv->post_select,
+ .name = lls_command_name(AFH_RECV_CMD),
+ .pre_select = AFH_RECV->pre_select,
+ .post_select = AFH_RECV->post_select,
.context = &pt->rn
}, &sched);
sprintf(buf, "%s decoder", af);
static int next_valid_file(struct play_task *pt)
{
int i, j = pt->current_file;
+ unsigned num_inputs = lls_num_inputs(play_lpr);
- FOR_EACH_PLAYLIST_FILE(i) {
- j = (j + 1) % conf.inputs_num;
+ for (i = 0; i < num_inputs; i++) {
+ j = (j + 1) % num_inputs;
if (!pt->invalid[j])
return j;
}
pt->next_file = pt->current_file;
ret = load_file(pt);
if (ret < 0) {
+ PARA_ERROR_LOG("%s: marking file as invalid\n",
+ para_strerror(-ret));
pt->invalid[pt->next_file] = true;
pt->rq = CRT_NONE;
goto again;
static int previous_valid_file(struct play_task *pt)
{
int i, j = pt->current_file;
+ unsigned num_inputs = lls_num_inputs(play_lpr);
- FOR_EACH_PLAYLIST_FILE(i) {
+ for (i = 0; i < num_inputs; i++) {
j--;
if (j < 0)
- j = conf.inputs_num - 1;
+ j = num_inputs - 1;
if (!pt->invalid[j])
return j;
}
static const char *default_commands[] = {INTERNAL_KEYMAP_ENTRIES};
#undef KEYMAP_ENTRY
#define NUM_INTERNALLY_MAPPED_KEYS ARRAY_SIZE(default_commands)
-#define NUM_MAPPED_KEYS (NUM_INTERNALLY_MAPPED_KEYS + conf.key_map_given)
+#define NUM_MAPPED_KEYS (NUM_INTERNALLY_MAPPED_KEYS + OPT_GIVEN(KEY_MAP))
#define FOR_EACH_MAPPED_KEY(i) for (i = 0; i < NUM_MAPPED_KEYS; i++)
static inline bool is_internal_key(int key)
get_internal_key_map_idx(key) : get_user_key_map_idx(key);
}
-static inline char *get_user_key_map_arg(int key)
+static inline const char *get_user_key_map_arg(int key)
{
- return conf.key_map_arg[get_user_key_map_idx(key)];
+ return lls_string_val(get_user_key_map_idx(key), OPT_RESULT(KEY_MAP));
}
static inline char *get_internal_key_map_seq(int key)
get_internal_key_map_seq(key) : get_user_key_map_seq(key);
}
+static char *get_key_map_seq_safe(int key)
+{
+ const char hex[] = "0123456789abcdef";
+ char *seq = get_key_map_seq(key), *sseq;
+ size_t n, len = strlen(seq);
+
+ if (len == 1 && isprint(*seq))
+ return seq;
+ sseq = para_malloc(2 + 2 * len + 1);
+ sseq[0] = '0';
+ sseq[1] = 'x';
+ for (n = 0; n < len; n++) {
+ uint8_t val = (seq[n] & 0xf0) >> 4;
+ sseq[2 + 2 * n] = hex[val];
+ val = seq[n] & 0xf;
+ sseq[2 + 2 * n + 1] = hex[val];
+ }
+ free(seq);
+ sseq[2 + 2 * n] = '\0';
+ return sseq;
+}
+
static inline char *get_internal_key_map_cmd(int key)
{
return para_strdup(default_commands[get_internal_key_map_idx(key)]);
result = para_malloc((NUM_MAPPED_KEYS + 1) * sizeof(char *));
FOR_EACH_MAPPED_KEY(i) {
- int idx = get_key_map_idx(i);
char *seq = get_key_map_seq(i);
- char *cmd = get_key_map_cmd(i);
- bool internal = is_internal_key(i);
- PARA_DEBUG_LOG("%s key sequence #%d: %s -> %s\n",
- internal? "internal" : "user-defined",
- idx, seq, cmd);
result[i] = seq;
- free(cmd);
}
result[i] = NULL;
return result;
}
-/* defines one command of para_play */
-struct pp_command {
- const char *name;
- int (*handler)(struct play_task *, int, char**);
- const char *description;
- const char *usage;
- const char *help;
-};
-
-#include "play.command_list.h"
-static struct pp_command pp_cmds[] = {DEFINE_PLAY_CMD_ARRAY};
-#define FOR_EACH_COMMAND(c) for (c = 0; pp_cmds[c].name; c++)
-
-#include "play.completion.h"
static struct i9e_completer pp_completers[];
I9E_DUMMY_COMPLETER(jmp);
I9E_DUMMY_COMPLETER(info);
I9E_DUMMY_COMPLETER(play);
I9E_DUMMY_COMPLETER(pause);
-I9E_DUMMY_COMPLETER(stop);
I9E_DUMMY_COMPLETER(tasks);
I9E_DUMMY_COMPLETER(quit);
I9E_DUMMY_COMPLETER(ff);
result->matches = i9e_complete_commands(ci->word, pp_completers);
}
-static struct i9e_completer pp_completers[] = {PLAY_COMPLETERS {.name = NULL}};
+I9E_DUMMY_COMPLETER(SUPERCOMMAND_UNAVAILABLE);
+static struct i9e_completer pp_completers[] = {
+#define LSG_PLAY_CMD_CMD(_name) {.name = #_name, \
+ .completer = _name ## _completer}
+ LSG_PLAY_CMD_SUBCOMMANDS
+#undef LSG_PLAY_CMD_CMD
+ {.name = NULL}
+};
static void attach_stdout(struct play_task *pt, const char *name)
{
btr_remove_node(&pt->btrn);
}
-static int com_quit(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_quit(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
- if (argc != 1)
- return -E_PLAY_SYNTAX;
pt->rq = CRT_TERM_RQ;
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(quit);
-static int com_help(struct play_task *pt, int argc, char **argv)
+static int com_help(struct play_task *pt, struct lls_parse_result *lpr)
{
- int i;
- char *buf;
+ int i, ret;
+ char *buf, *errctx;
size_t sz;
+ const struct lls_command *cmd;
- if (argc > 2)
- return -E_PLAY_SYNTAX;
- if (argc < 2) {
- if (pt->background)
- FOR_EACH_COMMAND(i) {
- sz = xasprintf(&buf, "%s\t%s\n", pp_cmds[i].name,
- pp_cmds[i].description);
- btr_add_output(buf, sz, pt->btrn);
- }
- else {
- FOR_EACH_MAPPED_KEY(i) {
- bool internal = is_internal_key(i);
- int idx = get_key_map_idx(i);
- char *seq = get_key_map_seq(i);
- char *cmd = get_key_map_cmd(i);
- sz = xasprintf(&buf,
- "%s key #%d: %s -> %s\n",
- internal? "internal" : "user-defined",
- idx, seq, cmd);
+ ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ return ret;
+ }
+ if (lls_num_inputs(lpr) == 0) {
+ if (pt->background) {
+ for (i = 1; (cmd = lls_cmd(i, play_cmd_suite)); i++) {
+ sz = xasprintf(&buf, "%s\t%s\n",
+ lls_command_name(cmd), lls_purpose(cmd));
btr_add_output(buf, sz, pt->btrn);
- free(seq);
- free(cmd);
}
+ return 0;
+ }
+ FOR_EACH_MAPPED_KEY(i) {
+ bool internal = is_internal_key(i);
+ int idx = get_key_map_idx(i);
+ char *seq = get_key_map_seq_safe(i);
+ char *kmc = get_key_map_cmd(i);
+ sz = xasprintf(&buf, "%s key #%d: %s -> %s\n",
+ internal? "internal" : "user-defined",
+ idx, seq, kmc);
+ btr_add_output(buf, sz, pt->btrn);
+ free(seq);
+ free(kmc);
}
return 0;
}
- FOR_EACH_COMMAND(i) {
- if (strcmp(pp_cmds[i].name, argv[1]))
- continue;
- sz = xasprintf(&buf,
- "NAME\n\t%s -- %s\n"
- "SYNOPSIS\n\t%s\n"
- "DESCRIPTION\n%s\n",
- argv[1],
- pp_cmds[i].description,
- pp_cmds[i].usage,
- pp_cmds[i].help
- );
- btr_add_output(buf, sz, pt->btrn);
- return 0;
+ ret = lls(lls_lookup_subcmd(lls_input(0, lpr), play_cmd_suite,
+ &errctx));
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ return ret;
}
- return -E_BAD_PLAY_CMD;
+ cmd = lls_cmd(ret, play_cmd_suite);
+ buf = lls_long_help(cmd);
+ assert(buf);
+ btr_add_output(buf, strlen(buf), pt->btrn);
+ return 0;
}
+EXPORT_PLAY_CMD_HANDLER(help);
-static int com_info(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_info(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
char *buf;
size_t sz;
static char dflt[] = "[no information available]";
- if (argc != 1)
- return -E_PLAY_SYNTAX;
sz = xasprintf(&buf, "playlist_pos: %u\npath: %s\n",
- pt->current_file, conf.inputs[pt->current_file]);
+ pt->current_file, get_playlist_file(pt->current_file));
btr_add_output(buf, sz, pt->btrn);
buf = pt->afhi_txt? pt->afhi_txt : dflt;
btr_add_output_dont_free(buf, strlen(buf), pt->btrn);
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(info);
static void list_file(struct play_task *pt, int num)
{
char *buf;
size_t sz;
- sz = xasprintf(&buf, "%s %4u %s\n", num == pt->current_file?
- "*" : " ", num, conf.inputs[num]);
+ sz = xasprintf(&buf, "%s %4d %s\n", num == pt->current_file?
+ "*" : " ", num, get_playlist_file(num));
btr_add_output(buf, sz, pt->btrn);
}
-static int com_tasks(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_tasks(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
static char state;
char *buf;
size_t sz;
- if (argc != 1)
- return -E_PLAY_SYNTAX;
-
buf = get_task_list(&sched);
btr_add_output(buf, strlen(buf), pt->btrn);
state = get_playback_state(pt);
btr_add_output(buf, sz, pt->btrn);
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(tasks);
-static int com_ls(struct play_task *pt, int argc, char **argv)
+static int com_ls(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
- int i, j, ret;
+ int i;
+ unsigned num_inputs = lls_num_inputs(play_lpr);
- if (argc == 1) {
- FOR_EACH_PLAYLIST_FILE(i)
- list_file(pt, i);
- return 0;
- }
- for (j = 1; j < argc; j++) {
- FOR_EACH_PLAYLIST_FILE(i) {
- ret = fnmatch(argv[j], conf.inputs[i], 0);
- if (ret == 0) /* match */
- list_file(pt, i);
- }
- }
+ for (i = 0; i < num_inputs; i++)
+ list_file(pt, i);
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(ls);
-static int com_play(struct play_task *pt, int argc, char **argv)
+static int com_play(struct play_task *pt, struct lls_parse_result *lpr)
{
int32_t x;
int ret;
- char state;
+ char state, *errctx;
- if (argc > 2)
- return -E_PLAY_SYNTAX;
+ ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ return ret;
+ }
state = get_playback_state(pt);
- if (argc == 1) {
+ if (lls_num_inputs(lpr) == 0) {
if (state == 'P')
return 0;
pt->next_file = pt->current_file;
pt->playing = true;
return 0;
}
- ret = para_atoi32(argv[1], &x);
+ ret = para_atoi32(lls_input(0, lpr), &x);
if (ret < 0)
return ret;
- if (x < 0 || x >= conf.inputs_num)
+ if (x < 0 || x >= lls_num_inputs(play_lpr))
return -ERRNO_TO_PARA_ERROR(EINVAL);
kill_stream(pt);
pt->next_file = x;
pt->rq = CRT_FILE_CHANGE;
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(play);
-static int com_pause(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_pause(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
char state;
long unsigned seconds, ss;
- if (argc != 1)
- return -E_PLAY_SYNTAX;
state = get_playback_state(pt);
pt->playing = false;
if (state != 'P')
kill_stream(pt);
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(pause);
-static int com_prev(struct play_task *pt, int argc, __a_unused char **argv)
-
+static int com_prev(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
int ret;
- if (argc != 1)
- return -E_PLAY_SYNTAX;
ret = previous_valid_file(pt);
if (ret < 0)
return ret;
pt->start_chunk = 0;
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(prev);
-static int com_next(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_next(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
int ret;
- if (argc != 1)
- return -E_PLAY_SYNTAX;
ret = next_valid_file(pt);
if (ret < 0)
return ret;
pt->start_chunk = 0;
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(next);
-static int com_fg(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_fg(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
- if (argc != 1)
- return -E_PLAY_SYNTAX;
pt->background = false;
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(fg);
-static int com_bg(struct play_task *pt, int argc, __a_unused char **argv)
+static int com_bg(struct play_task *pt,
+ __a_unused struct lls_parse_result *lpr)
{
- if (argc != 1)
- return -E_PLAY_SYNTAX;
pt->background = true;
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(bg);
-static int com_jmp(struct play_task *pt, int argc, char **argv)
+static int com_jmp(struct play_task *pt, struct lls_parse_result *lpr)
{
int32_t percent;
int ret;
+ char *errctx;
- if (argc != 2)
- return -E_PLAY_SYNTAX;
- ret = para_atoi32(argv[1], &percent);
+ ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ return ret;
+ }
+ ret = para_atoi32(lls_input(0, lpr), &percent);
if (ret < 0)
return ret;
if (percent < 0 || percent > 100)
return -ERRNO_TO_PARA_ERROR(EINVAL);
+ if (percent == 100)
+ return com_next(pt, NULL);
if (pt->playing && !pt->fn.btrn)
return 0;
pt->start_chunk = percent * pt->num_chunks / 100;
kill_stream(pt);
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(jmp);
-static int com_ff(struct play_task *pt, int argc, char **argv)
+static int com_ff(struct play_task *pt, struct lls_parse_result *lpr)
{
int32_t seconds;
+ char *errctx;
int ret;
- if (argc != 2)
- return -E_PLAY_SYNTAX;
- ret = para_atoi32(argv[1], &seconds);
+ ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
+ if (ret < 0) {
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
+ return ret;
+ }
+ ret = para_atoi32(lls_input(0, lpr), &seconds);
if (ret < 0)
return ret;
if (pt->playing && !pt->fn.btrn)
kill_stream(pt);
return 0;
}
+EXPORT_PLAY_CMD_HANDLER(ff);
static int run_command(char *line, struct play_task *pt)
{
- int i, ret, argc;
+ int ret, argc;
char **argv = NULL;
+ char *errctx = NULL;
+ const struct play_command_info *pci;
+ struct lls_parse_result *lpr;
+ const struct lls_command *cmd;
attach_stdout(pt, __FUNCTION__);
ret = create_argv(line, " ", &argv);
- if (ret < 0) {
- PARA_ERROR_LOG("parse error: %s\n", para_strerror(-ret));
- return 0;
- }
+ if (ret < 0)
+ goto out;
if (ret == 0)
goto out;
argc = ret;
- FOR_EACH_COMMAND(i) {
- if (strcmp(pp_cmds[i].name, argv[0]))
- continue;
- ret = pp_cmds[i].handler(pt, argc, argv);
- if (ret < 0)
- PARA_WARNING_LOG("%s: %s\n", pt->background?
- "" : argv[0], para_strerror(-ret));
- ret = 1;
+ ret = lls(lls_lookup_subcmd(argv[0], play_cmd_suite, &errctx));
+ if (ret < 0)
goto out;
- }
- PARA_WARNING_LOG("invalid command: %s\n", argv[0]);
- ret = 0;
+ cmd = lls_cmd(ret, play_cmd_suite);
+ ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+ if (ret < 0)
+ goto out;
+ pci = lls_user_data(cmd);
+ ret = pci->handler(pt, lpr);
+ lls_free_parse_result(lpr, cmd);
out:
+ if (errctx)
+ PARA_ERROR_LOG("%s\n", errctx);
+ free(errctx);
free_argv(argv);
return ret;
}
* stderr. Once the i9e subsystem has been initialized, we switch to the i9e
* log facility.
*/
-static void session_open(__a_unused struct play_task *pt)
+static void session_open(struct play_task *pt)
{
int ret;
char *history_file;
struct sigaction act;
PARA_NOTICE_LOG("\n%s\n", version_text("play"));
- if (conf.history_file_given)
- history_file = para_strdup(conf.history_file_arg);
+ if (OPT_GIVEN(HISTORY_FILE))
+ history_file = para_strdup(OPT_STRING_VAL(HISTORY_FILE));
else {
char *home = para_homedir();
history_file = make_message("%s/.paraslash/play.history",
length? (seconds * 100 + length / 2) / length : 0,
length / 60,
length % 60,
- conf.inputs[pt->current_file]
+ get_playlist_file(pt->current_file)
);
}
{
int ret;
struct play_task *pt = &play_task;
+ unsigned num_inputs;
/* needed this early to make help work */
recv_init();
- filter_init();
- writer_init();
sched.default_timeout.tv_sec = 5;
-
parse_config_or_die(argc, argv);
- if (conf.inputs_num == 0)
- print_help_and_die();
- check_afh_receiver_or_die();
-
+ AFH_RECV->init();
session_open(pt);
- if (conf.randomize_given)
- shuffle(conf.inputs, conf.inputs_num);
- pt->invalid = para_calloc(sizeof(*pt->invalid) * conf.inputs_num);
+ num_inputs = lls_num_inputs(play_lpr);
+ init_shuffle_map();
+ pt->invalid = para_calloc(sizeof(*pt->invalid) * num_inputs);
pt->rq = CRT_FILE_CHANGE;
- pt->current_file = conf.inputs_num - 1;
+ pt->current_file = num_inputs - 1;
pt->playing = true;
pt->task = task_register(&(struct task_info){
.name = "play",