1 /* Copyright (C) 2018 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
3 /** \file lsu.c Utilities related to the lopsub library. */
14 static int lsu_lopsub_error(int ret, char **errctx, char **result, unsigned *num_chars)
16 const char *se = para_strerror(-ret);
20 n = xasprintf(result, "%s: %s\n", *errctx, se);
22 n = xasprintf(result, "lopsub error: %s\n", se);
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)
36 const struct lls_command *cmd;
37 const char *name, *aux_info = NULL;
38 struct para_buffer pb = {.max_size = 0 /* unlimited */};
40 para_printf(&pb, "Available subcommands:\n");
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)));
47 aux_info = aux_info_cb(i, false);
50 max_aux_info = PARA_MAX(max_aux_info,
51 (int)strlen(aux_info));
54 for (i = 1; (cmd = lls_cmd(i, suite)); i++) {
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));
63 para_printf(&pb, "\t");
64 for (i = 1; (cmd = lls_cmd(i, suite)); i++) {
65 name = lls_command_name(cmd);
67 n += para_printf(&pb, ", ");
69 para_printf(&pb, "\n\t");
72 n += para_printf(&pb, "%s", name);
74 para_printf(&pb, "\n");
78 *num_chars = pb.offset;
82 * A generic implementation of the help subcommand.
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.
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.
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
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.
104 * \return Standard. In the failure case a suitable error message is returned
105 * via the result pointer and num_chars is set accordingly.
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)
115 const char *arg, *aux_info = NULL;
116 const struct lls_command *cmd;
118 ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
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);
126 arg = lls_input(0, lpr);
127 ret = lls(lls_lookup_subcmd(arg, suite, &errctx));
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);
133 aux_info = aux_info_cb(ret, true);
134 n = xasprintf(result, "%s%s%s", tmp, aux_info? aux_info : "",
135 aux_info? "\n" : "");
143 * Merge command line options and config file options.
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.
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.
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.
163 * By default, lpr is freed if the merge was done, but this can be changed by
164 * including MCF_DONT_FREE flags.
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.
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)
178 char *cf, **cf_argv, *errctx = NULL;
179 struct lls_parse_result *old_lpr = *lpr, *cf_lpr, *merged_lpr;
180 const char *subcmd_name;
183 cf = para_strdup(path);
185 char *home = para_homedir();
186 cf = make_message("%s/.paraslash/%s", home, dflt);
189 ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
193 else if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && !path)
196 PARA_ERROR_LOG("failed to mmap config file %s\n", cf);
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);
203 PARA_ERROR_LOG("failed to convert config file %s\n", cf);
207 ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx));
208 lls_free_argv(cf_argv);
210 PARA_ERROR_LOG("failed to parse config file %s\n", cf);
213 if (flags & MCF_OVERRIDE)
214 ret = lls(lls_merge(cf_lpr, old_lpr, cmd, &merged_lpr, &errctx));
216 ret = lls(lls_merge(old_lpr, cf_lpr, cmd, &merged_lpr, &errctx));
217 lls_free_parse_result(cf_lpr, cmd);
219 PARA_ERROR_LOG("could not merge options in %s\n", cf);
222 if (!(flags & MCF_DONT_FREE))
223 lls_free_parse_result(old_lpr, cmd);
230 PARA_ERROR_LOG("lopsub error: %s\n", errctx);