t0004: Always create PEM keys.
[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 #include "lsu.h"
12 #include "fd.h"
13
14 static int lsu_lopsub_error(int ret, char **errctx, char **result, unsigned *num_chars)
15 {
16 const char *se = para_strerror(-ret);
17 unsigned n;
18
19 if (*errctx)
20 n = xasprintf(result, "%s: %s\n", *errctx, se);
21 else
22 n = xasprintf(result, "lopsub error: %s\n", se);
23 free(*errctx);
24 *errctx = NULL;
25 if (num_chars)
26 *num_chars = n;
27 return ret;
28 }
29
30 static void lsu_get_subcommand_summary(bool long_summary,
31 const struct lls_suite *suite,
32 const char *(*aux_info_cb)(unsigned cmd_num, bool verbose),
33 char **result, unsigned *num_chars)
34 {
35 int i;
36 const struct lls_command *cmd;
37 const char *name, *aux_info = NULL;
38 struct para_buffer pb = {.max_size = 0 /* unlimited */};
39
40 para_printf(&pb, "Available subcommands:\n");
41 if (long_summary) {
42 int maxname = 0, max_aux_info = 0;
43 for (i = 1; (cmd = lls_cmd(i, suite)); i++) {
44 maxname = PARA_MAX(maxname,
45 (int)strlen(lls_command_name(cmd)));
46 if (aux_info_cb) {
47 aux_info = aux_info_cb(i, false);
48 if (!aux_info)
49 continue;
50 max_aux_info = PARA_MAX(max_aux_info,
51 (int)strlen(aux_info));
52 }
53 }
54 for (i = 1; (cmd = lls_cmd(i, suite)); i++) {
55 if (aux_info_cb)
56 aux_info = aux_info_cb(i, false);
57 para_printf(&pb, "%-*s %-*s %s\n", maxname,
58 lls_command_name(cmd), max_aux_info, aux_info?
59 aux_info : "", lls_purpose(cmd));
60 }
61 } else {
62 unsigned n = 8;
63 para_printf(&pb, "\t");
64 for (i = 1; (cmd = lls_cmd(i, suite)); i++) {
65 name = lls_command_name(cmd);
66 if (i > 1)
67 n += para_printf(&pb, ", ");
68 if (n > 70) {
69 para_printf(&pb, "\n\t");
70 n = 8;
71 }
72 n += para_printf(&pb, "%s", name);
73 }
74 para_printf(&pb, "\n");
75 }
76 *result = pb.buf;
77 if (num_chars)
78 *num_chars = pb.offset;
79 }
80
81 /**
82 * A generic implementation of the help subcommand.
83 *
84 * This function returns the help text for the given subcommand, or the list of
85 * all subcommands if no non-option argument is given. The function is generic
86 * in that it works for arbitrary lopsub suites.
87 *
88 * \param long_help Applies to both command list and command help.
89 * \param suite The supercommand, if any, is omitted.
90 * \param lpr Used to determine whether a non-option argument is given.
91 * \param aux_info_cb Optional callback, may return NULL, static memory.
92 * \param result Must be freed by the caller.
93 * \param num_chars Initialized to the length of the returned string, optional.
94 *
95 * If the optional aux_info_cb function pointer is not NULL, the callback
96 * function must return the string representation of the aux_info structure of
97 * the given command, or NULL to indicate that this command has no aux info
98 * structure.
99 *
100 * The function fails if lpr has more than one non-option argument, or if there
101 * is exactly one non-option argument, but this argument is not the name of a
102 * subcommand in the given lopsub suite.
103 *
104 * \return Standard. In the failure case a suitable error message is returned
105 * via the result pointer and num_chars is set accordingly.
106 */
107 int lsu_com_help(bool long_help, const struct lls_parse_result *lpr,
108 const struct lls_suite *suite,
109 const char *(*aux_info_cb)(unsigned cmd_num, bool verbose),
110 char **result, unsigned *num_chars)
111 {
112 int ret;
113 unsigned n;
114 char *errctx, *tmp;
115 const char *arg, *aux_info = NULL;
116 const struct lls_command *cmd;
117
118 ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
119 if (ret < 0)
120 return lsu_lopsub_error(ret, &errctx, result, num_chars);
121 if (lls_num_inputs(lpr) == 0) {
122 lsu_get_subcommand_summary(long_help, suite,
123 aux_info_cb, result, num_chars);
124 return 0;
125 }
126 arg = lls_input(0, lpr);
127 ret = lls(lls_lookup_subcmd(arg, suite, &errctx));
128 if (ret < 0)
129 return lsu_lopsub_error(ret, &errctx, result, num_chars);
130 cmd = lls_cmd(ret, suite);
131 tmp = long_help? lls_long_help(cmd) : lls_short_help(cmd);
132 if (aux_info_cb)
133 aux_info = aux_info_cb(ret, true);
134 n = xasprintf(result, "%s%s%s", tmp, aux_info? aux_info : "",
135 aux_info? "\n" : "");
136 free(tmp);
137 if (num_chars)
138 *num_chars = n;
139 return 1;
140 }
141
142 /**
143 * Merge command line options and config file options.
144 *
145 * This function parses the options stored in the configuration file and merges
146 * them with the currently effective options. If the application supports
147 * config files, it is supposed to call this after the command line options
148 * have been parsed. If the application also supports config file reloading,
149 * the function will be called for that purpose.
150 *
151 * \param path Config file path, usually the argument to --config-file.
152 * \param dflt Relative to ~/.paraslash, ignored if path is not NULL.
153 * \param lpr Value-result pointer.
154 * \param cmd Passed to lls_parse() and lls_merge().
155 * \param suite Needed to tell whether cmd is the supercommand.
156 * \param flags See enum \ref lsu_merge_cf_flags.
157 *
158 * The function does nothing if path is NULL and the default config file does
159 * not exist, or if path is an empty file. Otherwise, the options of the config
160 * file are parsed, the parse result is merged with lpr, and the merged parse
161 * result is returned via lpr.
162 *
163 * By default, lpr is freed if the merge was done, but this can be changed by
164 * including MCF_DONT_FREE flags.
165 *
166 * \return Zero if there was nothing to do, one if the config file options were
167 * merged successfully, negative error code on failure. It is considered an error
168 * if path is given, but the file does not exist.
169 */
170 int lsu_merge_config_file_options(const char *path, const char *dflt,
171 struct lls_parse_result **lpr, const struct lls_command *cmd,
172 const struct lls_suite *suite, unsigned flags)
173 {
174 int ret;
175 void *map;
176 size_t sz;
177 int cf_argc;
178 char *cf, **cf_argv, *errctx = NULL;
179 struct lls_parse_result *old_lpr = *lpr, *cf_lpr, *merged_lpr;
180 const char *subcmd_name;
181
182 if (path)
183 cf = para_strdup(path);
184 else {
185 char *home = para_homedir();
186 cf = make_message("%s/.paraslash/%s", home, dflt);
187 free(home);
188 }
189 ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
190 if (ret < 0) {
191 if (ret == -E_EMPTY)
192 ret = 0;
193 else if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && !path)
194 ret = 0;
195 else
196 PARA_ERROR_LOG("failed to mmap config file %s\n", cf);
197 goto free_cf;
198 }
199 subcmd_name = (lls_cmd(0, suite) == cmd)? NULL : lls_command_name(cmd);
200 ret = lls(lls_convert_config(map, sz, subcmd_name, &cf_argv, &errctx));
201 para_munmap(map, sz);
202 if (ret < 0) {
203 PARA_ERROR_LOG("failed to convert config file %s\n", cf);
204 goto lopsub_error;
205 }
206 cf_argc = ret;
207 ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx));
208 lls_free_argv(cf_argv);
209 if (ret < 0) {
210 PARA_ERROR_LOG("failed to parse config file %s\n", cf);
211 goto lopsub_error;
212 }
213 if (flags & MCF_OVERRIDE)
214 ret = lls(lls_merge(cf_lpr, old_lpr, cmd, &merged_lpr, &errctx));
215 else
216 ret = lls(lls_merge(old_lpr, cf_lpr, cmd, &merged_lpr, &errctx));
217 lls_free_parse_result(cf_lpr, cmd);
218 if (ret < 0) {
219 PARA_ERROR_LOG("could not merge options in %s\n", cf);
220 goto lopsub_error;
221 }
222 if (!(flags & MCF_DONT_FREE))
223 lls_free_parse_result(old_lpr, cmd);
224 *lpr = merged_lpr;
225 ret = 1;
226 goto free_cf;
227 lopsub_error:
228 assert(ret < 0);
229 if (errctx)
230 PARA_ERROR_LOG("lopsub error: %s\n", errctx);
231 free(errctx);
232 free_cf:
233 free(cf);
234 return ret;
235 }