diff options
Diffstat (limited to 'libbutl/default-options.txx')
-rw-r--r-- | libbutl/default-options.txx | 151 |
1 files changed, 138 insertions, 13 deletions
diff --git a/libbutl/default-options.txx b/libbutl/default-options.txx index 730d588..aa254b2 100644 --- a/libbutl/default-options.txx +++ b/libbutl/default-options.txx @@ -1,7 +1,15 @@ // file : libbutl/default-options.txx -*- C++ -*- // license : MIT; see accompanying LICENSE file -LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. +#include <utility> // move(), forward(), make_pair() +#include <algorithm> // reverse() +#include <stdexcept> // invalid_argument +#include <system_error> + +#include <libbutl/git.hxx> +#include <libbutl/filesystem.hxx> + +namespace butl { inline bool options_dir_exists (const dir_path& d) @@ -14,10 +22,11 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. throw std::make_pair (path_cast<path> (d), std::move (e)); } - // Search for and parse the options files in the specified directory and - // its local/ subdirectory, if exists, in the reverse order and append the - // options to the resulting list. Return false if --no-default-options is - // encountered. + // Search for and parse the options files in the specified directory and its + // local/ subdirectory, if exists, in the reverse order and append the + // options to the resulting list. Verify that the number of arguments + // doesn't exceed the limits and decrement arg_max by arg_max_file after + // parsing each file. Return false if --no-default-options is encountered. // // Note that by default we check for the local/ subdirectory even if we // don't think it belongs to the remote directory; the user may move things @@ -31,9 +40,13 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. template <typename O, typename S, typename U, typename F> bool load_default_options_files (const dir_path& d, + const std::string& opt, + bool args, bool remote, const small_vector<path, 2>& fs, F&& fn, + std::size_t& arg_max, + std::size_t arg_max_file, default_options<O>& def_ops, bool load_sub = true, bool load_dir = true) @@ -42,7 +55,8 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. bool r (true); - auto load = [&fs, &fn, &def_ops, &r] (const dir_path& d, bool remote) + auto load = [&opt, args, &fs, &fn, &def_ops, &arg_max, arg_max_file, &r] + (const dir_path& d, bool rem) { using namespace std; @@ -54,9 +68,14 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. { if (file_exists (p)) // Follows symlinks. { - fn (p, remote, false /* overwrite */); + if (arg_max < arg_max_file) + throw invalid_argument ("too many options files"); + + size_t start_pos (arg_max - arg_max_file); - S s (p.string ()); + fn (p, rem, false /* overwrite */); + + S s (p.string (), opt, start_pos); // @@ Note that the potentially thrown exceptions (unknown option, // unexpected argument, etc) will not contain any location @@ -65,19 +84,40 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. // this in CLI. // O o; - o.parse (s, U::fail, U::fail); + small_vector<string, 1> as; + + if (args) + { + while (s.more ()) + { + if (!o.parse (s, U::fail, U::stop)) + as.push_back (s.next ()); + } + } + else + o.parse (s, U::fail, U::fail); + + if (s.position () > arg_max) + throw invalid_argument ("too many options in file " + + p.string ()); + + // Don't decrement arg_max for the empty option files. + // + if (s.position () != start_pos) + arg_max = start_pos; if (o.no_default_options ()) r = false; def_ops.push_back (default_options_entry<O> {move (p), move (o), - remote}); + move (as), + rem}); } } - catch (std::system_error& e) + catch (system_error& e) { - throw std::make_pair (move (p), std::move (e)); + throw make_pair (move (p), move (e)); } } }; @@ -102,7 +142,11 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. const optional<dir_path>& home_dir, const optional<dir_path>& extra_dir, const default_options_files& ofs, - F&& fn) + F&& fn, + const std::string& opt, + std::size_t arg_max, + std::size_t arg_max_file, + bool args) { if (sys_dir) assert (sys_dir->absolute () && sys_dir->normalized ()); @@ -192,9 +236,13 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. if (load_extra && extra_dir->sub (d)) { load = load_default_options_files<O, S, U> (*extra_dir, + opt, + args, false /* remote */, ofs.files, std::forward<F> (fn), + arg_max, + arg_max_file, r); load_extra = false; @@ -204,9 +252,13 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. if (load && options_dir_exists (od)) load = load_default_options_files<O, S, U> (od, + opt, + args, remote, ofs.files, std::forward<F> (fn), + arg_max, + arg_max_file, r, load_build2_local, load_build2); @@ -219,9 +271,13 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. if (load && load_extra) load = load_default_options_files<O, S, U> (*extra_dir, + opt, + args, false /* remote */, ofs.files, std::forward<F> (fn), + arg_max, + arg_max_file, r); if (load && home_dir) @@ -230,17 +286,25 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. if (options_dir_exists (d)) load = load_default_options_files<O, S, U> (d, + opt, + args, false /* remote */, ofs.files, std::forward<F> (fn), + arg_max, + arg_max_file, r); } if (load && sys_dir && options_dir_exists (*sys_dir)) load_default_options_files<O, S, U> (*sys_dir, + opt, + args, false /* remote */, ofs.files, std::forward<F> (fn), + arg_max, + arg_max_file, r); std::reverse (r.begin (), r.end ()); @@ -269,4 +333,65 @@ LIBBUTL_MODEXPORT namespace butl //@@ MOD Clang needs this for some reason. r.merge (cmd_ops); return r; } + + template <typename O, typename AS, typename F> + AS + merge_default_arguments (const default_options<O>& def_ops, + const AS& cmd_args, + F&& fn) + { + AS r; + for (const default_options_entry<O>& e: def_ops) + { + fn (e, cmd_args); + r.insert (r.end (), e.arguments.begin (), e.arguments.end ()); + } + + // Optimize for the common case. + // + if (r.empty ()) + return cmd_args; + + r.insert (r.end (), cmd_args.begin (), cmd_args.end ()); + return r; + } + + template <typename I, typename F> + optional<dir_path> + default_options_start (const optional<dir_path>& home, I b, I e, F&& f) + { + if (home) + assert (home->absolute () && home->normalized ()); + + if (b == e) + return nullopt; + + // Use the first directory as a start. + // + I i (b); + dir_path d (f (i)); + + // Try to find a common prefix for each subsequent directory. + // + for (++i; i != e; ++i) + { + bool p (false); + + for (; + !(d.root () || (home && d == *home)); + d = d.directory ()) + { + if (f (i).sub (d)) + { + p = true; + break; + } + } + + if (!p) + return nullopt; + } + + return d; + } } |