play: Convert com_ls() to lopsub.
[paraslash.git] / play.c
diff --git a/play.c b/play.c
index d2539ee134773614a40bca9941f35dd161007946..1c1d809a8cd292798fbb138dc677c6467293d39d 100644 (file)
--- a/play.c
+++ b/play.c
@@ -7,12 +7,14 @@
 /** \file play.c Paraslash's standalone player. */
 
 #include <regex.h>
-#include <fnmatch.h>
 #include <signal.h>
+#include <inttypes.h>
+#include <lopsub.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"
@@ -36,6 +38,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.
  *
@@ -95,8 +100,15 @@ struct play_task {
        char *afhi_txt;
 };
 
-/** Initialize the array of errors for para_play. */
-INIT_PLAY_ERRLISTS;
+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 \
+       };
 
 /* Activate the afh receiver. */
 extern void afh_recv_init(struct receiver *r);
@@ -328,8 +340,7 @@ static int open_new_file(struct play_task *pt)
        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;
@@ -388,6 +399,7 @@ 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)
@@ -399,6 +411,8 @@ static int load_file(struct play_task *pt)
                        .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);
@@ -453,6 +467,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;
@@ -666,7 +682,6 @@ struct pp_command {
 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);
@@ -689,7 +704,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)
 {
@@ -713,55 +735,63 @@ static int com_quit(struct play_task *pt, int argc, __a_unused char **argv)
        return 0;
 }
 
-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);
+       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);
                        }
-               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_safe(i);
-                               char *cmd = get_key_map_cmd(i);
-                               sz = xasprintf(&buf,
-                                       "%s key #%d: %s -> %s\n",
-                                       internal? "internal" : "user-defined",
-                                       idx, seq, cmd);
+                       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);
-                               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)
 {
@@ -784,7 +814,7 @@ 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);
 }
@@ -806,24 +836,16 @@ static int com_tasks(struct play_task *pt, int argc, __a_unused char **argv)
        return 0;
 }
 
-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)
 {
@@ -876,13 +898,11 @@ static int com_pause(struct play_task *pt, int argc, __a_unused char **argv)
        return 0;
 }
 
-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;
@@ -892,13 +912,13 @@ static int com_prev(struct play_task *pt, int argc, __a_unused char **argv)
        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;
@@ -908,37 +928,44 @@ static int com_next(struct play_task *pt, int argc, __a_unused char **argv)
        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, 1, (char *[]){"next", NULL});
+               return com_next(pt, NULL);
        if (pt->playing && !pt->fn.btrn)
                return 0;
        pt->start_chunk = percent * pt->num_chunks / 100;
@@ -948,15 +975,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)
@@ -973,34 +1007,48 @@ 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;
        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);
+
+       ret = lls(lls_lookup_subcmd(argv[0], play_cmd_suite, &errctx));
+       if (ret >= 0) {
+               cmd = lls_cmd(ret, play_cmd_suite);
+               ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
                if (ret < 0)
-                       PARA_WARNING_LOG("%s: %s\n", pt->background?
-                               "" : argv[0], para_strerror(-ret));
-               ret = 1;
-               goto out;
+                       goto out;
+               pci = lls_user_data(cmd);
+               ret = pci->handler(pt, lpr);
+               lls_free_parse_result(lpr, cmd);
+       } else {
+               FOR_EACH_COMMAND(i) {
+                       if (strcmp(pp_cmds[i].name, argv[0]))
+                               continue;
+                       free(errctx);
+                       errctx = NULL;
+                       ret = pp_cmds[i].handler(pt, argc, argv);
+                       break;
+               }
        }
-       PARA_WARNING_LOG("invalid command: %s\n", argv[0]);
-       ret = 0;
 out:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       free(errctx);
        free_argv(argv);
        return ret;
 }