X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=play.c;h=4dab1cad58b3a989e96072c95e153b08cd18e24f;hp=6eed58ab53779dd22afb9442d002b60c71165525;hb=fc8dfbb416ff07cca08fbf4e13efcaa25e17cc54;hpb=79d6515d49cdb0a91ff7c4a599f2d63cb5678032 diff --git a/play.c b/play.c index 6eed58ab..4dab1cad 100644 --- a/play.c +++ b/play.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Andre Noll + * Copyright (C) 2012 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -7,13 +7,15 @@ /** \file play.c Paraslash's standalone player. */ #include -#include #include +#include +#include +#include "recv_cmd.lsg.h" #include "para.h" #include "list.h" #include "play.cmdline.h" -#include "filter.cmdline.h" +#include "play_cmd.lsg.h" #include "error.h" #include "ggo.h" #include "buffer_tree.h" @@ -37,6 +39,9 @@ * Playlist handling is done exclusively in play context. */ +/** Array of error strings. */ +DEFINE_PARA_ERRLIST; + /** * Describes a request to change the state of para_play. * @@ -57,7 +62,7 @@ enum state_change_request_type { }; struct play_task { - struct task task; + struct task *task; /* A bit array of invalid files (those will be skipped). */ bool *invalid; /* The file which is currently open. */ @@ -96,16 +101,15 @@ struct play_task { 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; @@ -120,22 +124,9 @@ 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; - - 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); -} +#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) { @@ -184,10 +175,10 @@ static void parse_config_or_die(int argc, char *argv[]) loglevel = get_loglevel_by_name(conf.loglevel_arg); } for (i = 0; i < conf.key_map_given; i++) { - char *s = strchr(conf.key_map_arg[i] + 1, ':'); - if (s) + char *kma = conf.key_map_arg[i]; + if (*kma && strchr(kma + 1, ':')) continue; - PARA_EMERG_LOG("invalid key map arg: %s\n", conf.key_map_arg[i]); + PARA_EMERG_LOG("invalid key map arg: %s\n", kma); goto err; } free(config_file); @@ -231,25 +222,29 @@ static long unsigned get_play_time(struct play_task *pt) 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)); } /* returns: 0 not eof, 1: eof, < 0: fatal error. */ static int get_playback_error(struct play_task *pt) { - int err = pt->wn.task.error; + int err; + if (!pt->wn.task) + return 0; + err = task_status(pt->wn.task); if (err >= 0) return 0; - if (pt->fn.task.error >= 0) + if (task_status(pt->fn.task) >= 0) return 0; - if (pt->rn.task.error >= 0) + if (task_status(pt->rn.task) >= 0) return 0; if (err == -E_BTR_EOF || err == -E_RECV_EOF || err == -E_EOF || err == -E_WRITE_COMMON_EOF) @@ -260,23 +255,27 @@ static int get_playback_error(struct play_task *pt) 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 = filter_get(pt->fn.filter_num); int ret; ret = get_playback_error(pt); if (ret == 0) return ret; PARA_NOTICE_LOG("cleaning up wn/fn nodes\n"); + task_reap(&pt->wn.task); w->close(&pt->wn); btr_remove_node(&pt->wn.btrn); w->free_config(pt->wn.conf); memset(&pt->wn, 0, sizeof(struct writer_node)); - decoder->close(&pt->fn); + task_reap(&pt->fn.task); + 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)); + task_reap(&pt->rn.task); btr_remove_node(&pt->rn.btrn); /* * On eof (ret > 0), we do not wipe the receiver node struct until a @@ -295,35 +294,35 @@ static int shuffle_compare(__a_unused const void *a, __a_unused const void *b) static void shuffle(char **base, size_t num) { - srandom(now->tv_sec); + srandom(time(NULL)); qsort(base, num, sizeof(char *), 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}; + char *tmp, *path = conf.inputs[pt->next_file], *errctx = NULL, + *argv[] = {"play", "-f", path, "-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; @@ -351,9 +350,6 @@ static int open_new_file(struct play_task *pt) free(tmp); tmp = NULL; } - pt->rn.task.pre_select = afh_recv->pre_select; - pt->rn.task.post_select = afh_recv->post_select; - sprintf(pt->rn.task.status, "%s receiver node", afh_recv->name); return 1; fail: wipe_receiver_node(pt); @@ -363,9 +359,9 @@ fail: static int load_file(struct play_task *pt) { const char *af; - char *tmp; + char *tmp, buf[20]; int ret; - struct filter *decoder; + const struct filter *decoder; btr_remove_node(&pt->rn.btrn); if (!pt->rn.receiver || pt->next_file != pt->current_file) { @@ -373,7 +369,6 @@ static int load_file(struct play_task *pt) if (ret < 0) return ret; } else { - char buf[20]; pt->rn.btrn = new_recv_btrn(&pt->rn); sprintf(buf, "repos %lu", pt->start_chunk); ret = btr_exec_up(pt->rn.btrn, buf, &tmp); @@ -386,27 +381,40 @@ static int load_file(struct play_task *pt) /* set up decoding filter */ af = audio_format_name(pt->audio_format_num); tmp = make_message("%sdec", af); + PARA_INFO_LOG("decoder: %s\n", tmp); ret = check_filter_arg(tmp, &pt->fn.conf); freep(&tmp); if (ret < 0) goto fail; pt->fn.filter_num = ret; - decoder = filters + ret; - pt->fn.task.pre_select = decoder->pre_select; - pt->fn.task.post_select = decoder->post_select; - sprintf(pt->fn.task.status, "%s decoder", af); + 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); + 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.task.error = 0; /* success, register tasks */ - register_task(&sched, &pt->rn.task); - register_task(&sched, &pt->fn.task); + pt->rn.task = task_register( + &(struct task_info) { + .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); + pt->fn.task = task_register( + &(struct task_info) { + .name = buf, + .pre_select = decoder->pre_select, + .post_select = decoder->post_select, + .context = &pt->fn + }, &sched); register_writer_node(&pt->wn, pt->fn.btrn, &sched); return 1; fail: @@ -431,7 +439,7 @@ static int load_next_file(struct play_task *pt) int ret; again: - if (pt->rq == CRT_NONE || pt->rq == CRT_FILE_CHANGE) { + if (pt->rq == CRT_NONE) { pt->start_chunk = 0; ret = next_valid_file(pt); if (ret < 0) @@ -441,6 +449,8 @@ again: 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; @@ -452,7 +462,8 @@ again: static void kill_stream(struct play_task *pt) { - task_notify(&pt->wn.task, E_EOF); + if (pt->wn.task) + task_notify(pt->wn.task, E_EOF); } #ifdef HAVE_READLINE @@ -579,6 +590,28 @@ static char *get_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)]); @@ -607,37 +640,13 @@ static char **get_mapped_keyseqs(void) 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; } -#include "play_completion.h" - - -/* 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); @@ -660,7 +669,14 @@ static void help_completer(struct i9e_completion_info *ci, 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) { @@ -676,72 +692,74 @@ static void detach_stdout(struct play_task *pt) 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]); btr_add_output(buf, sz, pt->btrn); @@ -749,26 +767,25 @@ static int com_info(struct play_task *pt, int argc, __a_unused char **argv) 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? + sz = xasprintf(&buf, "%s %4d %s\n", num == pt->current_file? "*" : " ", num, conf.inputs[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); @@ -776,36 +793,34 @@ static int com_tasks(struct play_task *pt, int argc, __a_unused char **argv) 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; - 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_EACH_PLAYLIST_FILE(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; @@ -813,7 +828,7 @@ static int com_play(struct play_task *pt, int argc, char **argv) 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) @@ -823,14 +838,14 @@ static int com_play(struct play_task *pt, int argc, char **argv) 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') @@ -846,66 +861,76 @@ static int com_pause(struct play_task *pt, int argc, __a_unused char **argv) 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; kill_stream(pt); pt->next_file = ret; pt->rq = CRT_FILE_CHANGE; + 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; kill_stream(pt); pt->next_file = ret; pt->rq = CRT_FILE_CHANGE; + 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; @@ -915,15 +940,22 @@ static int com_jmp(struct play_task *pt, int argc, char **argv) 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) @@ -940,34 +972,38 @@ static int com_ff(struct play_task *pt, int argc, char **argv) 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; } @@ -1014,7 +1050,7 @@ static void sigint_handler(int sig) * 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; @@ -1083,9 +1119,8 @@ static void session_update_time_string(struct play_task *pt, char *str, unsigned * terminates. Subsequent calls to i9e_get_error() then return negative and we * are allowed to call i9e_close() and terminate as well. */ -static int session_post_select(__a_unused struct sched *s, struct task *t) +static int session_post_select(__a_unused struct sched *s, struct play_task *pt) { - struct play_task *pt = container_of(t, struct play_task, task); int ret; if (pt->background) @@ -1107,9 +1142,8 @@ static int session_post_select(__a_unused struct sched *s, struct task *t) #else /* HAVE_READLINE */ -static int session_post_select(struct sched *s, struct task *t) +static int session_post_select(struct sched *s, struct play_task *pt) { - struct play_task *pt = container_of(t, struct play_task, task); char c; if (!FD_ISSET(STDIN_FILENO, &s->rfds)) @@ -1132,9 +1166,9 @@ static void session_update_time_string(__a_unused struct play_task *pt, } #endif /* HAVE_READLINE */ -static void play_pre_select(struct sched *s, struct task *t) +static void play_pre_select(struct sched *s, void *context) { - struct play_task *pt = container_of(t, struct play_task, task); + struct play_task *pt = context; char state; para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno); @@ -1171,9 +1205,9 @@ static unsigned get_time_string(struct play_task *pt, char **result) ); } -static int play_post_select(struct sched *s, struct task *t) +static int play_post_select(struct sched *s, void *context) { - struct play_task *pt = container_of(t, struct play_task, task); + struct play_task *pt = context; int ret; ret = eof_cleanup(pt); @@ -1181,7 +1215,7 @@ static int play_post_select(struct sched *s, struct task *t) pt->rq = CRT_TERM_RQ; return 0; } - ret = session_post_select(s, t); + ret = session_post_select(s, pt); if (ret < 0) goto out; if (!pt->wn.btrn && !pt->fn.btrn) { @@ -1230,14 +1264,12 @@ int main(int argc, char *argv[]) filter_init(); writer_init(); - clock_get_realtime(now); 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); @@ -1245,11 +1277,14 @@ int main(int argc, char *argv[]) pt->rq = CRT_FILE_CHANGE; pt->current_file = conf.inputs_num - 1; pt->playing = true; - pt->task.pre_select = play_pre_select; - pt->task.post_select = play_post_select; - sprintf(pt->task.status, "play task"); - register_task(&sched, &pt->task); + pt->task = task_register(&(struct task_info){ + .name = "play", + .pre_select = play_pre_select, + .post_select = play_post_select, + .context = pt, + }, &sched); ret = schedule(&sched); + sched_shutdown(&sched); if (ret < 0) PARA_ERROR_LOG("%s\n", para_strerror(-ret)); return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;