#include "para.h"
#include "error.h"
#include "string.h"
+#include "lsu.h"
+#include "fd.h"
static int lsu_lopsub_error(int ret, char **errctx, char **result, unsigned *num_chars)
{
*num_chars = n;
return 1;
}
+
+/**
+ * Merge command line options and config file options.
+ *
+ * This function parses the options stored in the configuration file and merges
+ * them with the currently effective options. If the application supports
+ * config files, it is supposed to call this after the command line options
+ * have been parsed. If the application also supports config file reloading,
+ * the function will be called for that purpose.
+ *
+ * \param path Config file path, usually the argument to --config-file.
+ * \param dflt Relative to ~/.paraslash, ignored if path is not NULL.
+ * \param lpr Value-result pointer.
+ * \param cmd Passed to lls_parse() and lls_merge().
+ * \param suite Needed to tell whether cmd is the supercommand.
+ * \param flags See enum \ref lsu_merge_cf_flags.
+ *
+ * The function does nothing if path is NULL and the default config file does
+ * not exist, or if path is an empty file. Otherwise, the options of the config
+ * file are parsed, the parse result is merged with lpr, and the merged parse
+ * result is returned via lpr.
+ *
+ * By default, lpr is freed if the merge was done, but this can be changed by
+ * including MCF_DONT_FREE flags.
+ *
+ * \return Zero if there was nothing to do, one if the config file options were
+ * merged successfully, negative error code on failure. It is considered an error
+ * if path is given, but the file does not exist.
+ */
+int lsu_merge_config_file_options(const char *path, const char *dflt,
+ struct lls_parse_result **lpr, const struct lls_command *cmd,
+ const struct lls_suite *suite, unsigned flags)
+{
+ int ret;
+ void *map;
+ size_t sz;
+ int cf_argc;
+ char *cf, **cf_argv, *errctx = NULL;
+ struct lls_parse_result *old_lpr = *lpr, *cf_lpr, *merged_lpr;
+ const char *subcmd_name;
+
+ if (path)
+ cf = para_strdup(path);
+ else {
+ char *home = para_homedir();
+ cf = make_message("%s/.paraslash/%s", home, dflt);
+ free(home);
+ }
+ ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ if (ret < 0) {
+ if (ret == -E_EMPTY)
+ ret = 0;
+ else if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && !path)
+ ret = 0;
+ else
+ PARA_ERROR_LOG("failed to mmap config file %s\n", cf);
+ goto free_cf;
+ }
+ subcmd_name = (lls_cmd(0, suite) == cmd)? NULL : lls_command_name(cmd);
+ ret = lls(lls_convert_config(map, sz, subcmd_name, &cf_argv, &errctx));
+ para_munmap(map, sz);
+ if (ret < 0) {
+ PARA_ERROR_LOG("failed to convert config file %s\n", cf);
+ goto lopsub_error;
+ }
+ cf_argc = ret;
+ ret = lls(lls_parse(cf_argc, cf_argv, cmd, &cf_lpr, &errctx));
+ lls_free_argv(cf_argv);
+ if (ret < 0) {
+ PARA_ERROR_LOG("failed to parse config file %s\n", cf);
+ goto lopsub_error;
+ }
+ if (flags & MCF_OVERRIDE)
+ ret = lls(lls_merge(cf_lpr, old_lpr, cmd, &merged_lpr, &errctx));
+ else
+ ret = lls(lls_merge(old_lpr, cf_lpr, cmd, &merged_lpr, &errctx));
+ lls_free_parse_result(cf_lpr, cmd);
+ if (ret < 0) {
+ PARA_ERROR_LOG("could not merge options in %s\n", cf);
+ goto lopsub_error;
+ }
+ if (!(flags & MCF_DONT_FREE))
+ lls_free_parse_result(old_lpr, cmd);
+ *lpr = merged_lpr;
+ ret = 1;
+ goto free_cf;
+lopsub_error:
+ assert(ret < 0);
+ if (errctx)
+ PARA_ERROR_LOG("lopsub error: %s\n", errctx);
+ free(errctx);
+free_cf:
+ free(cf);
+ return ret;
+}