Introduce lsu.{c,h}, implement help --long for para_server.
authorAndre Noll <maan@tuebingen.mpg.de>
Tue, 13 Mar 2018 21:37:39 +0000 (22:37 +0100)
committerAndre Noll <maan@tuebingen.mpg.de>
Sun, 22 Apr 2018 18:30:37 +0000 (20:30 +0200)
This adds the --long option to the server help subcommand. The former
help output becomes the long help while the short help text is shown
if --long is not given.

Although only the help command of para_server is converted in this
patch, the new functionality is implemented in a generic way so
that the help commands of para_audiod and para_play can use the same
implementation. Those will be converted in subsequent patches.

t0004 parses the help output and thus needs to be changed to include
--long.

client.c
command.c
configure.ac
lsu.c [new file with mode: 0644]
lsu.h [new file with mode: 0644]
m4/lls/include/long-help.m4 [new file with mode: 0644]
m4/lls/server_cmd.suite.m4
t/t0004-server.sh

index 3e9219f..c43ebe1 100644 (file)
--- a/client.c
+++ b/client.c
@@ -250,6 +250,12 @@ static struct i9e_completer completers[];
 static void help_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *result)
 {
+       char *opts[] = {LSG_SERVER_CMD_HELP_OPTS, NULL};
+
+       if (ci->word[0] == '-') {
+               i9e_complete_option(opts, ci, result);
+               return;
+       }
        result->matches = i9e_complete_commands(ci->word, completers);
 }
 
index 6568b78..bd3d17c 100644 (file)
--- a/command.c
+++ b/command.c
@@ -16,6 +16,7 @@
 #include "server.lsg.h"
 #include "para.h"
 #include "error.h"
+#include "lsu.h"
 #include "crypt.h"
 #include "sideband.h"
 #include "command.h"
@@ -528,63 +529,35 @@ out:
 }
 EXPORT_SERVER_CMD_HANDLER(stat);
 
-/* fixed-length, human readable permission string */
-const char *server_cmd_perms_str(unsigned int perms)
+const char *aux_info_cb(unsigned cmd_num, bool verbose)
 {
-       static char result[5];
+       static char result[80];
+       unsigned perms = server_command_perms[cmd_num];
 
-       result[0] = perms & AFS_READ? 'a' : '-';
-       result[1] = perms & AFS_WRITE? 'A' : '-';
-       result[2] = perms & VSS_READ? 'v' : '-';
-       result[3] = perms & VSS_WRITE? 'V' : '-';
-       result[4] = '\0';
-       return result;
-}
-
-static int send_list_of_commands(struct command_context *cc)
-{
-       int i;
-       const struct lls_command *cmd;
-       char *msg = para_strdup("");
-
-       for (i = 1; (cmd = lls_cmd(i, server_cmd_suite)); i++) {
-               const char *perms = server_cmd_perms_str(server_command_perms[i]);
-               char *tmp = make_message("%s%s\t%s\t%s\n", msg,
-                       lls_command_name(cmd), perms, lls_purpose(cmd));
-               free(msg);
-               msg = tmp;
+       if (verbose) {
+               /* permissions: VSS_READ | VSS_WRITE */
+               sprintf(result, "permissions: %s",
+                       server_command_perms_txt[cmd_num]);
+       } else {
+               result[0] = perms & AFS_READ? 'a' : '-';
+               result[1] = perms & AFS_WRITE? 'A' : '-';
+               result[2] = perms & VSS_READ? 'v' : '-';
+               result[3] = perms & VSS_WRITE? 'V' : '-';
+               result[4] = '\0';
        }
-       return send_sb(&cc->scc, msg, strlen(msg), SBD_OUTPUT, false);
+       return result;
 }
 
 static int com_help(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       const char *perms;
-       char *long_help, *buf, *errctx;
+       char *buf;
        int ret;
-       const struct lls_command *cmd;
+       unsigned n;
+       bool long_help = SERVER_CMD_OPT_GIVEN(HELP, LONG, lpr);
 
-       ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
-       if (ret < 0) {
-               send_errctx(cc, errctx);
-               return ret;
-       }
-       if (lls_num_inputs(lpr) == 0)
-               return send_list_of_commands(cc);
-       /* argument given for help */
-       ret = lls(lls_lookup_subcmd(lls_input(0, lpr), server_cmd_suite,
-               &errctx));
-       if (ret < 0) {
-               send_errctx(cc, errctx);
-               return ret;
-       }
-       cmd = lls_cmd(ret, server_cmd_suite);
-       perms = server_command_perms_txt[ret];
-       long_help = lls_long_help(cmd);
-       assert(long_help);
-       ret = xasprintf(&buf, "%spermissions: %s\n", long_help, perms);
-       free(long_help);
-       return send_sb(&cc->scc, buf, ret, SBD_OUTPUT, false);
+       lsu_com_help(long_help, lpr, server_cmd_suite, aux_info_cb, &buf, &n);
+       ret = send_sb(&cc->scc, buf, n, SBD_OUTPUT, false);
+       return ret;
 }
 EXPORT_SERVER_CMD_HANDLER(help);
 
index 499571a..060889f 100644 (file)
@@ -392,6 +392,7 @@ if test -n "$CRYPTOLIB" && test $HAVE_OSL = yes && test -n "$BISON" && \
                wma_common
                sideband
                version
+               lsu
        "
        if test "$CRYPTOLIB" = openssl; then
                server_errlist_objs="$server_errlist_objs crypt"
diff --git a/lsu.c b/lsu.c
new file mode 100644 (file)
index 0000000..de6f2cb
--- /dev/null
+++ b/lsu.c
@@ -0,0 +1,138 @@
+/* Copyright (C) 2018 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
+
+/** \file lsu.c Utilities related to the lopsub library. */
+
+#include <lopsub.h>
+#include <regex.h>
+
+#include "para.h"
+#include "error.h"
+#include "string.h"
+
+static int lsu_lopsub_error(int ret, char **errctx, char **result, unsigned *num_chars)
+{
+       const char *se = para_strerror(-ret);
+       unsigned n;
+
+       if (*errctx)
+               n = xasprintf(result, "%s: %s\n", *errctx, se);
+       else
+               n = xasprintf(result, "lopsub error: %s\n", se);
+       free(*errctx);
+       *errctx = NULL;
+       if (num_chars)
+               *num_chars = n;
+       return ret;
+}
+
+static void lsu_get_subcommand_summary(bool long_summary,
+               const struct lls_suite *suite,
+               const char *(*aux_info_cb)(unsigned cmd_num, bool verbose),
+               char **result, unsigned *num_chars)
+{
+       int i;
+       const struct lls_command *cmd;
+       const char *name, *aux_info = NULL;
+       struct para_buffer pb = {.max_size = 0 /* unlimited */};
+
+       para_printf(&pb, "Available subcommands:\n");
+       if (long_summary) {
+               int maxname = 0, max_aux_info = 0;
+               for (i = 1; (cmd = lls_cmd(i, suite)); i++) {
+                       maxname = PARA_MAX(maxname,
+                               (int)strlen(lls_command_name(cmd)));
+                       if (aux_info_cb) {
+                               aux_info = aux_info_cb(i, false);
+                               if (!aux_info)
+                                       continue;
+                               max_aux_info = PARA_MAX(max_aux_info,
+                                       (int)strlen(aux_info));
+                       }
+               }
+               for (i = 1; (cmd = lls_cmd(i, suite)); i++) {
+                       if (aux_info_cb)
+                               aux_info = aux_info_cb(i, false);
+                       para_printf(&pb, "%-*s %-*s %s\n", maxname,
+                               lls_command_name(cmd), max_aux_info, aux_info?
+                               aux_info : "", lls_purpose(cmd));
+               }
+       } else {
+               unsigned n = 8;
+               para_printf(&pb, "\t");
+               for (i = 1; (cmd = lls_cmd(i, suite)); i++) {
+                       name = lls_command_name(cmd);
+                       if (i > 1)
+                               n += para_printf(&pb, ", ");
+                       if (n > 70) {
+                               para_printf(&pb, "\n\t");
+                               n = 8;
+                       }
+                       n += para_printf(&pb, "%s", name);
+               }
+               para_printf(&pb, "\n");
+       }
+       *result = pb.buf;
+       if (num_chars)
+               *num_chars = pb.offset;
+}
+
+/**
+ * A generic implementation of the help subcommand.
+ *
+ * This function returns the help text for the given subcommand, or the list of
+ * all subcommands if no non-option argument is given. The function is generic
+ * in that it works for arbitrary lopsub suites.
+ *
+ * \param long_help Applies to both command list and command help.
+ * \param suite The supercommand, if any, is omitted.
+ * \param lpr Used to determine whether a non-option argument is given.
+ * \param aux_info_cb Optional callback, may return NULL, static memory.
+ * \param result Must be freed by the caller.
+ * \param num_chars Initialized to the length of the returned string, optional.
+ *
+ * If the optional aux_info_cb function pointer is not NULL, the callback
+ * function must return the string representation of the aux_info structure of
+ * the given command, or NULL to indicate that this command has no aux info
+ * structure.
+ *
+ * The function fails if lpr has more than one non-option argument, or if there
+ * is exactly one non-option argument, but this argument is not the name of a
+ * subcommand in the given lopsub suite.
+ *
+ * \return Standard. In the failure case a suitable error message is returned
+ * via the result pointer and num_chars is set accordingly.
+ */
+int lsu_com_help(bool long_help, const struct lls_parse_result *lpr,
+               const struct lls_suite *suite,
+               const char *(*aux_info_cb)(unsigned cmd_num, bool verbose),
+               char **result, unsigned *num_chars)
+{
+       int ret;
+       unsigned n;
+       char *errctx, *tmp;
+       const char *arg, *aux_info = NULL;
+       const struct lls_command *cmd;
+
+       ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+       if (ret < 0)
+               return lsu_lopsub_error(ret, &errctx, result, num_chars);
+       if (lls_num_inputs(lpr) == 0) {
+               lsu_get_subcommand_summary(long_help, suite,
+                       aux_info_cb, result, num_chars);
+               return 0;
+       }
+       arg = lls_input(0, lpr);
+       ret = lls(lls_lookup_subcmd(arg, suite, &errctx));
+       if (ret < 0)
+               return lsu_lopsub_error(ret, &errctx, result, num_chars);
+       cmd = lls_cmd(ret, suite);
+       tmp = long_help? lls_long_help(cmd) : lls_short_help(cmd);
+       if (aux_info_cb)
+               aux_info = aux_info_cb(ret, true);
+       n = xasprintf(result, "%s%s%s", tmp, aux_info? aux_info : "",
+               aux_info? "\n" : "");
+       free(tmp);
+       if (num_chars)
+               *num_chars = n;
+       return 1;
+}
diff --git a/lsu.h b/lsu.h
new file mode 100644 (file)
index 0000000..baba14f
--- /dev/null
+++ b/lsu.h
@@ -0,0 +1,7 @@
+/* Copyright (C) 2018 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
+
+/** \file lsu.h Lopsub Utilities, enums and public functions. */
+int lsu_com_help(bool long_help, const struct lls_parse_result *lpr,
+               const struct lls_suite *suite,
+               const char *(*aux_info_cb)(unsigned cmd_num, bool verbose),
+               char **result, unsigned *num_chars);
diff --git a/m4/lls/include/long-help.m4 b/m4/lls/include/long-help.m4
new file mode 100644 (file)
index 0000000..408f8e1
--- /dev/null
@@ -0,0 +1,15 @@
+[option long]
+       short_opt = l
+       summary = show the long help text
+[help]
+       If no non-option argument is supplied to the help subcommand and --long
+       is not given, only the names of all subcommands are shown. With --long
+       the purpose of each command is printed as well.
+
+       If the name of a subcommand is supplied and --long is given, the help
+       text for the given subcommand contains the synopsis, the purpose and
+       the description of the specified command, followed by the option list
+       including summary and help text of each option. Without --long the
+       description of the command and the option help are omitted.
+[/help]
+
index 9378b7c..1098b1e 100644 (file)
@@ -144,10 +144,11 @@ aux_info_prefix = Permissions:
        non-opts-name = [command]
        aux_info = NO_PERMISSION_REQUIRED
        [description]
-               Without any arguments, help prints a list of available commands. When
-               called with a command name as first argument, it prints the description
-               of this command.
+               When executed without any arguments, the available server commands
+               are listed. Otherwise, if the first argument is the name of a server
+               command, the description of this command is shown.
        [/description]
+       m4_include(`long-help.m4')
 
 [subcommand hup]
        purpose = reload config file, log file and user list
index 6a8e5bf..7e8ea82 100755 (executable)
@@ -27,8 +27,8 @@ declare -a oggs_base=(${oggs[@]##*/})
 declare -a commands=() cmdline=() required_objects=() good=() bad=()
 i=0
 commands[$i]="help"
-cmdline[$i]="help"
-good[$i]='help ----'
+cmdline[$i]="help -l"
+good[$i]='help \{1,\}----'
 
 let i++
 commands[$i]="init"