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 8aa10a6e3aa73766cba1d65e97d6dcd85c7594e2..53fa22fe6502001f9fe84ac19d48b79c4d2dc345 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
 
 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))
 
 # 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
 
 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)
 
 $(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_audioc \
 para_audiod \
+para_play \
 para_server \
 : LDFLAGS += $(lopsub_ldflags)
 
 para_server \
 : LDFLAGS += $(lopsub_ldflags)
 
diff --git a/error.h b/error.h
index 346ef50f76e73773ed30edd6bd27ab6e49291af0..a4038d5c127c177cd2a7a3622e4b84cd942f4da3 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_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"), \
        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 fac551aa161d14f258e1495a4530462ca1146357..6fe66678cc1cb2ef49681cf16355e55cc79b4d5c 100644 (file)
--- a/play.c
+++ b/play.c
@@ -9,10 +9,13 @@
 #include <regex.h>
 #include <fnmatch.h>
 #include <signal.h>
 #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 "para.h"
 #include "list.h"
 #include "play.cmdline.h"
+#include "play_cmd.lsg.h"
 #include "error.h"
 #include "ggo.h"
 #include "buffer_tree.h"
 #include "error.h"
 #include "ggo.h"
 #include "buffer_tree.h"
@@ -98,6 +101,16 @@ struct play_task {
        char *afhi_txt;
 };
 
        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
 /* 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++)
 
 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);
 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);
 }
 
        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)
 {
 
 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;
 }
 
        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;
        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);
                        }
                                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);
                                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;
        }
                }
                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, 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;
 {
        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);
 
        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;
        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)
                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:
 out:
+       if (errctx)
+               PARA_ERROR_LOG("%s\n", errctx);
+       free(errctx);
        free_argv(argv);
        return ret;
 }
        free_argv(argv);
        return ret;
 }
index 459ad8cfe3091264f17913d2915861dde7f8b02f..85b1421100c51ce3b0d4b64d4a598c5f298b6656 100644 (file)
--- a/play.cmd
+++ b/play.cmd
@@ -2,14 +2,6 @@ BN: play
 SF: play.c
 SN: list of commands
 ---
 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
 N: next
 D: Load next file.
 U: next