Introduce lsu.{c,h}, implement help --long for para_server.
[paraslash.git] / lsu.c
1 /* Copyright (C) 2018 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
2
3 /** \file lsu.c Utilities related to the lopsub library. */
4
5 #include <lopsub.h>
6 #include <regex.h>
7
8 #include "para.h"
9 #include "error.h"
10 #include "string.h"
11
12 static int lsu_lopsub_error(int ret, char **errctx, char **result, unsigned *num_chars)
13 {
14 const char *se = para_strerror(-ret);
15 unsigned n;
16
17 if (*errctx)
18 n = xasprintf(result, "%s: %s\n", *errctx, se);
19 else
20 n = xasprintf(result, "lopsub error: %s\n", se);
21 free(*errctx);
22 *errctx = NULL;
23 if (num_chars)
24 *num_chars = n;
25 return ret;
26 }
27
28 static void lsu_get_subcommand_summary(bool long_summary,
29 const struct lls_suite *suite,
30 const char *(*aux_info_cb)(unsigned cmd_num, bool verbose),
31 char **result, unsigned *num_chars)
32 {
33 int i;
34 const struct lls_command *cmd;
35 const char *name, *aux_info = NULL;
36 struct para_buffer pb = {.max_size = 0 /* unlimited */};
37
38 para_printf(&pb, "Available subcommands:\n");
39 if (long_summary) {
40 int maxname = 0, max_aux_info = 0;
41 for (i = 1; (cmd = lls_cmd(i, suite)); i++) {
42 maxname = PARA_MAX(maxname,
43 (int)strlen(lls_command_name(cmd)));
44 if (aux_info_cb) {
45 aux_info = aux_info_cb(i, false);
46 if (!aux_info)
47 continue;
48 max_aux_info = PARA_MAX(max_aux_info,
49 (int)strlen(aux_info));
50 }
51 }
52 for (i = 1; (cmd = lls_cmd(i, suite)); i++) {
53 if (aux_info_cb)
54 aux_info = aux_info_cb(i, false);
55 para_printf(&pb, "%-*s %-*s %s\n", maxname,
56 lls_command_name(cmd), max_aux_info, aux_info?
57 aux_info : "", lls_purpose(cmd));
58 }
59 } else {
60 unsigned n = 8;
61 para_printf(&pb, "\t");
62 for (i = 1; (cmd = lls_cmd(i, suite)); i++) {
63 name = lls_command_name(cmd);
64 if (i > 1)
65 n += para_printf(&pb, ", ");
66 if (n > 70) {
67 para_printf(&pb, "\n\t");
68 n = 8;
69 }
70 n += para_printf(&pb, "%s", name);
71 }
72 para_printf(&pb, "\n");
73 }
74 *result = pb.buf;
75 if (num_chars)
76 *num_chars = pb.offset;
77 }
78
79 /**
80 * A generic implementation of the help subcommand.
81 *
82 * This function returns the help text for the given subcommand, or the list of
83 * all subcommands if no non-option argument is given. The function is generic
84 * in that it works for arbitrary lopsub suites.
85 *
86 * \param long_help Applies to both command list and command help.
87 * \param suite The supercommand, if any, is omitted.
88 * \param lpr Used to determine whether a non-option argument is given.
89 * \param aux_info_cb Optional callback, may return NULL, static memory.
90 * \param result Must be freed by the caller.
91 * \param num_chars Initialized to the length of the returned string, optional.
92 *
93 * If the optional aux_info_cb function pointer is not NULL, the callback
94 * function must return the string representation of the aux_info structure of
95 * the given command, or NULL to indicate that this command has no aux info
96 * structure.
97 *
98 * The function fails if lpr has more than one non-option argument, or if there
99 * is exactly one non-option argument, but this argument is not the name of a
100 * subcommand in the given lopsub suite.
101 *
102 * \return Standard. In the failure case a suitable error message is returned
103 * via the result pointer and num_chars is set accordingly.
104 */
105 int lsu_com_help(bool long_help, const struct lls_parse_result *lpr,
106 const struct lls_suite *suite,
107 const char *(*aux_info_cb)(unsigned cmd_num, bool verbose),
108 char **result, unsigned *num_chars)
109 {
110 int ret;
111 unsigned n;
112 char *errctx, *tmp;
113 const char *arg, *aux_info = NULL;
114 const struct lls_command *cmd;
115
116 ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
117 if (ret < 0)
118 return lsu_lopsub_error(ret, &errctx, result, num_chars);
119 if (lls_num_inputs(lpr) == 0) {
120 lsu_get_subcommand_summary(long_help, suite,
121 aux_info_cb, result, num_chars);
122 return 0;
123 }
124 arg = lls_input(0, lpr);
125 ret = lls(lls_lookup_subcmd(arg, suite, &errctx));
126 if (ret < 0)
127 return lsu_lopsub_error(ret, &errctx, result, num_chars);
128 cmd = lls_cmd(ret, suite);
129 tmp = long_help? lls_long_help(cmd) : lls_short_help(cmd);
130 if (aux_info_cb)
131 aux_info = aux_info_cb(ret, true);
132 n = xasprintf(result, "%s%s%s", tmp, aux_info? aux_info : "",
133 aux_info? "\n" : "");
134 free(tmp);
135 if (num_chars)
136 *num_chars = n;
137 return 1;
138 }