From: Andre Noll Date: Tue, 13 Mar 2018 21:37:39 +0000 (+0100) Subject: Introduce lsu.{c,h}, implement help --long for para_server. X-Git-Tag: v0.6.2~2^2~11 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=e072e2a4feb9879f66bc4847a5007cec07b5f5f7 Introduce lsu.{c,h}, implement help --long for para_server. 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. --- diff --git a/client.c b/client.c index 3e9219fd..c43ebe16 100644 --- 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); } diff --git a/command.c b/command.c index 6568b789..bd3d17c8 100644 --- 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); diff --git a/configure.ac b/configure.ac index 499571a7..060889f4 100644 --- a/configure.ac +++ b/configure.ac @@ -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 index 00000000..de6f2cbc --- /dev/null +++ b/lsu.c @@ -0,0 +1,138 @@ +/* Copyright (C) 2018 Andre Noll , see file COPYING. */ + +/** \file lsu.c Utilities related to the lopsub library. */ + +#include +#include + +#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 index 00000000..baba14fd --- /dev/null +++ b/lsu.h @@ -0,0 +1,7 @@ +/* Copyright (C) 2018 Andre Noll , 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 index 00000000..408f8e14 --- /dev/null +++ b/m4/lls/include/long-help.m4 @@ -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] + diff --git a/m4/lls/server_cmd.suite.m4 b/m4/lls/server_cmd.suite.m4 index 9378b7c3..1098b1e1 100644 --- a/m4/lls/server_cmd.suite.m4 +++ b/m4/lls/server_cmd.suite.m4 @@ -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 diff --git a/t/t0004-server.sh b/t/t0004-server.sh index 6a8e5bf1..7e8ea820 100755 --- a/t/t0004-server.sh +++ b/t/t0004-server.sh @@ -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"