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
);