+/* 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;
+}