spxdec: Use read_u16() from portable_io.h.
[paraslash.git] / lsu.c
diff --git a/lsu.c b/lsu.c
index de6f2cbcdf16f63149df685b3d7754a71ec6ab25..8ccf80d541929194f984759e38d7ffa7998d3c97 100644 (file)
--- a/lsu.c
+++ b/lsu.c
@@ -8,6 +8,8 @@
 #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)
 {
@@ -136,3 +138,98 @@ int lsu_com_help(bool long_help, const struct lls_parse_result *lpr,
                *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;
+}