play: Link against lopsub and convert com_help() to lopsub.
authorAndre Noll <maan@tuebingen.mpg.de>
Sun, 10 Apr 2016 20:02:10 +0000 (22:02 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Sun, 26 Mar 2017 09:02:28 +0000 (11:02 +0200)
This introduces the play_cmd suite for the subcommands of para_play,
like play, pause, fg, bg, etc. The new suite contains only the
"help" subcommand so far. Other commands will be converted one by one
in subsequent patches.

We make use of the lopsub user_data feature to store a pointer to the
command handler in the lls_command structure generated by lopsubgen.
This pointer is initialized by the new EXPORT_PLAY_CMD_HANDLER macro
in play.c.

The suite has no supercommand, therefore lopsubgen adds a special
"unavailable" identifier to play.lsg.h. We must therefore provide a
dummy completer for the fake supercommand.

Until all subcommands are converted, the run_command() and com_help()
subcommand need to iterate over two command lists. Afterwards, this
compatibility code can be removed.

Makefile.real
error.h
m4/lls/play_cmd.suite.m4 [new file with mode: 0644]
play.c
play.cmd

index 8aa10a6..53fa22f 100644 (file)
@@ -47,6 +47,7 @@ m4_deps := $(addprefix $(m4depdir)/, $(addsuffix .m4d, $(executables)))
 
 audiod_objs += audiod_cmd.lsg.o
 server_objs += server_cmd.lsg.o
+play_objs += play_cmd.lsg.o
 
 # now prefix all objects with object dir
 recv_objs := $(addprefix $(object_dir)/, $(recv_objs))
@@ -176,7 +177,7 @@ $(cmdlist_dir)/audiod.completion.h \
 
 server_command_lists := $(lls_suite_dir)/server_cmd.lsg.man
 audiod_command_lists := $(lls_suite_dir)/audiod_cmd.lsg.man
-play_command_lists := $(cmdlist_dir)/play.command_list.man
+play_command_lists := $(lls_suite_dir)/play_cmd.lsg.man
 
 $(man_dir)/para_server.1: $(server_command_lists)
 $(man_dir)/para_audiod.1: $(audiod_command_lists)
@@ -320,6 +321,7 @@ para_fade \
 
 para_audioc \
 para_audiod \
+para_play \
 para_server \
 : LDFLAGS += $(lopsub_ldflags)
 
diff --git a/error.h b/error.h
index 346ef50..a4038d5 100644 (file)
--- a/error.h
+++ b/error.h
@@ -63,7 +63,6 @@
        PARA_ERROR(BAD_FILTER_OPTIONS, "invalid filter option given"), \
        PARA_ERROR(BAD_LL, "invalid loglevel"), \
        PARA_ERROR(BAD_PATH, "invalid path"), \
-       PARA_ERROR(BAD_PLAY_CMD, "invalid command"), \
        PARA_ERROR(BAD_PRIVATE_KEY, "invalid private key"), \
        PARA_ERROR(BAD_SAMPLE_FORMAT, "sample format not supported"), \
        PARA_ERROR(BAD_SAMPLERATE, "sample rate not supported"), \
diff --git a/m4/lls/play_cmd.suite.m4 b/m4/lls/play_cmd.suite.m4
new file mode 100644 (file)
index 0000000..50727c9
--- /dev/null
@@ -0,0 +1,16 @@
+[suite play_cmd]
+caption = list of commands
+[subcommand help]
+       purpose = list commands or keybindings, or print command-specific help
+       non-opts-name = [command]
+       [description]
+
+               This command acts differently depending on whether it is executed in
+               command mode or in insert mode.
+
+               In command mode, the help command prints the list of defined
+               keybindings. In insert mode, if no argument is given, it prints the
+               list of available commands. When called with the name of a command
+               as first argument, it prints the description of this command.
+       [/description]
+
diff --git a/play.c b/play.c
index fac551a..6fe6667 100644 (file)
--- a/play.c
+++ b/play.c
@@ -9,10 +9,13 @@
 #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"
@@ -98,6 +101,16 @@ struct play_task {
        char *afhi_txt;
 };
 
+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);
 #undef AFH_RECEIVER
@@ -670,7 +683,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);
@@ -693,7 +705,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)
 {
@@ -717,55 +736,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)
 {
@@ -982,29 +1009,42 @@ 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;
 }
index 459ad8c..85b1421 100644 (file)
--- a/play.cmd
+++ b/play.cmd
@@ -2,14 +2,6 @@ BN: play
 SF: play.c
 SN: list of commands
 ---
-N: help
-D: Display command list or help for given command.
-U: help [command]
-H: This command acts differently depending on whether it is executed in command
-H: mode or in insert mode.  In command mode, the list of keybindings is printed.
-H: In insert mode, if no command is given, the list of commands is shown.
-H: Otherwise, the help for the given command is printed.
----
 N: next
 D: Load next file.
 U: next