#include <lopsub.h>
#include "recv_cmd.lsg.h"
+#include "play_cmd.lsg.h"
+#include "play.lsg.h"
#include "para.h"
#include "list.h"
-#include "play.cmdline.h"
-#include "play_cmd.lsg.h"
#include "error.h"
#include "ggo.h"
#include "buffer_tree.h"
/** 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 *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;
#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))
-__noreturn static void print_help_and_die(void)
+static unsigned *shuffle_map;
+
+static const char *get_playlist_file(unsigned idx)
+{
+ return lls_input(shuffle_map[idx], play_lpr);
+}
+
+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 *kma = conf.key_map_arg[i];
+ 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", kma);
- goto err;
+ 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 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)
static int open_new_file(struct play_task *pt)
{
int ret;
- char *tmp, *path = conf.inputs[pt->next_file], *errctx = NULL,
- *argv[] = {"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);
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;
}
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)
static char dflt[] = "[no information available]";
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);
size_t sz;
sz = xasprintf(&buf, "%s %4d %s\n", num == pt->current_file?
- "*" : " ", num, conf.inputs[num]);
+ "*" : " ", num, get_playlist_file(num));
btr_add_output(buf, sz, pt->btrn);
}
__a_unused struct lls_parse_result *lpr)
{
int i;
+ unsigned num_inputs = lls_num_inputs(play_lpr);
- FOR_EACH_PLAYLIST_FILE(i)
+ for (i = 0; i < num_inputs; i++)
list_file(pt, i);
return 0;
}
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;
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();
writer_init();
sched.default_timeout.tv_sec = 5;
-
parse_config_or_die(argc, argv);
- if (conf.inputs_num == 0)
- print_help_and_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",