From 74c74571311431924285032588e9a3b99a048a9b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 26 Apr 2011 17:46:54 +0200 Subject: [PATCH] Add GetOpts module to std --- src/lib/GetOpts.rs | 249 +++++++++++++++++++++++++++++++++++++++++++++ src/lib/std.rc | 1 + 2 files changed, 250 insertions(+) create mode 100644 src/lib/GetOpts.rs diff --git a/src/lib/GetOpts.rs b/src/lib/GetOpts.rs new file mode 100644 index 00000000000..f85ce85c136 --- /dev/null +++ b/src/lib/GetOpts.rs @@ -0,0 +1,249 @@ +/* Simple getopt alternative. Construct a vector of options, either by using + * reqopt, optopt, and optflag or by building them from components yourself, + * and pass them to getopts, along with a vector of actual arguments (not + * including argv[0]). You'll either get a failure code back, or a match. + * You'll have to verify whether the amount of 'free' arguments in the match + * is what you expect. Use opt_* accessors (bottom of the file) to get + * argument values out of the match object. + */ + +import option.some; +import option.none; + +tag name { long(str); short(char); } +tag hasarg { yes; no; maybe; } +tag occur { req; optional; multi; } + +type opt = rec(name name, hasarg hasarg, occur occur); + +fn mkname(str nm) -> name { + if (_str.char_len(nm) == 1u) { ret short(_str.char_at(nm, 0u)); } + else { ret long(nm); } +} +fn reqopt(str name) -> opt { + ret rec(name=mkname(name), hasarg=yes, occur=req); +} +fn optopt(str name) -> opt { + ret rec(name=mkname(name), hasarg=yes, occur=optional); +} +fn optflag(str name) -> opt { + ret rec(name=mkname(name), hasarg=no, occur=optional); +} +fn optmulti(str name) -> opt { + ret rec(name=mkname(name), hasarg=yes, occur=multi); +} + +tag optval { + val(str); + given; +} + +type match = rec(vec[opt] opts, vec[mutable vec[optval]] vals, vec[str] free); + +fn is_arg(str arg) -> bool { + ret _str.byte_len(arg) > 1u && arg.(0) == '-' as u8; +} +fn name_str(name nm) -> str { + alt (nm) { + case (short(?ch)) {ret _str.from_char(ch);} + case (long(?s)) {ret s;} + } +} + +// FIXME rustboot workaround +fn name_eq(name a, name b) -> bool { + alt (a) { + case (long(?a)) { + alt (b) { + case (long(?b)) { ret _str.eq(a, b); } + case (_) { ret false; } + } + } + case (_) { if (a == b) { ret true; } else {ret false; } } + } +} +fn find_opt(vec[opt] opts, name nm) -> option.t[uint] { + auto i = 0u; + auto l = _vec.len[opt](opts); + while (i < l) { + if (name_eq(opts.(i).name, nm)) { ret some[uint](i); } + i += 1u; + } + ret none[uint]; +} + +tag fail_ { + argument_missing(str); + unrecognized_option(str); + option_missing(str); + option_duplicated(str); +} + +fn fail_str(fail_ f) -> str { + alt (f) { + case (argument_missing(?nm)) { + ret "Argument to option '" + nm + "' missing."; + } + case (unrecognized_option(?nm)) { + ret "Unrecognized option: '" + nm + "'."; + } + case (option_missing(?nm)) { + ret "Required option '" + nm + "' missing."; + } + case (option_duplicated(?nm)) { + ret "Option '" + nm + "' given more than once."; + } + } +} + +tag result { + success(match); + failure(fail_); +} + +fn getopts(vec[str] args, vec[opt] opts) -> result { + auto n_opts = _vec.len[opt](opts); + fn empty_(uint x) -> vec[optval]{ret _vec.empty[optval]();} + auto f = empty_; + auto vals = _vec.init_fn_mut[vec[optval]](f, n_opts); + let vec[str] free = vec(); + + auto l = _vec.len[str](args); + auto i = 0u; + while (i < l) { + auto cur = args.(i); + auto curlen = _str.byte_len(cur); + if (!is_arg(cur)) { + _vec.push[str](free, cur); + } else if (_str.eq(cur, "--")) { + free += _vec.slice[str](args, i + 1u, l); + break; + } else { + auto names; + auto i_arg = option.none[str]; + if (cur.(1) == '-' as u8) { + auto tail = _str.slice(cur, 2u, curlen); + auto eq = _str.index(tail, '=' as u8); + if (eq == -1) { + names = vec(long(tail)); + } else { + names = vec(long(_str.slice(tail, 0u, eq as uint))); + i_arg = option.some[str] + (_str.slice(tail, (eq as uint) + 1u, curlen - 2u)); + } + } else { + auto j = 1u; + names = vec(); + while (j < curlen) { + auto range = _str.char_range_at(cur, j); + _vec.push[name](names, short(range._0)); + j = range._1; + } + } + auto name_pos = 0u; + for (name nm in names) { + name_pos += 1u; + auto optid; + alt (find_opt(opts, nm)) { + case (some[uint](?id)) {optid = id;} + case (none[uint]) { + ret failure(unrecognized_option(name_str(nm))); + } + } + alt (opts.(optid).hasarg) { + case (no) { + _vec.push[optval](vals.(optid), given); + } + case (maybe) { + if (!option.is_none[str](i_arg)) { + _vec.push[optval](vals.(optid), + val(option.get[str](i_arg))); + } else if (name_pos < _vec.len[name](names) || + i + 1u == l || is_arg(args.(i + 1u))) { + _vec.push[optval](vals.(optid), given); + } else { + i += 1u; + _vec.push[optval](vals.(optid), val(args.(i))); + } + } + case (yes) { + if (!option.is_none[str](i_arg)) { + _vec.push[optval](vals.(optid), + val(option.get[str](i_arg))); + } else if (i + 1u == l) { + ret failure(argument_missing(name_str(nm))); + } else { + i += 1u; + _vec.push[optval](vals.(optid), val(args.(i))); + } + } + } + } + } + i += 1u; + } + + i = 0u; + while (i < n_opts) { + auto n = _vec.len[optval](vals.(i)); + auto occ = opts.(i).occur; + if (occ == req) {if (n == 0u) { + ret failure(option_missing(name_str(opts.(i).name))); + }} + if (occ != multi) {if (n > 1u) { + ret failure(option_duplicated(name_str(opts.(i).name))); + }} + i += 1u; + } + + ret success(rec(opts=opts, vals=vals, free=free)); +} + +fn opt_vals(match m, str nm) -> vec[optval] { + alt (find_opt(m.opts, mkname(nm))) { + case (some[uint](?id)) { ret m.vals.(id); } + case (none[uint]) { + log_err "No option '" + nm + "' defined."; + fail; + } + } +} +fn opt_val(match m, str nm) -> optval { + ret opt_vals(m, nm).(0); +} +fn opt_present(match m, str nm) -> bool { + ret _vec.len[optval](opt_vals(m, nm)) > 0u; +} +fn opt_str(match m, str nm) -> str { + alt (opt_val(m, nm)) { + case (val(?s)) { ret s; } + case (_) { fail; } + } +} +fn opt_strs(match m, str nm) -> vec[str] { + let vec[str] acc = vec(); + for (optval v in opt_vals(m, nm)) { + alt (v) { + case (val(?s)) { _vec.push[str](acc, s); } + case (_) {} + } + } + ret acc; +} +fn opt_maybe_str(match m, str nm) -> option.t[str] { + auto vals = opt_vals(m, nm); + if (_vec.len[optval](vals) == 0u) { ret none[str]; } + alt (vals.(0)) { + case (val(?s)) { ret some[str](s); } + case (_) { ret none[str]; } + } +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: diff --git a/src/lib/std.rc b/src/lib/std.rc index 1056f375138..9fc1b7b8cbf 100644 --- a/src/lib/std.rc +++ b/src/lib/std.rc @@ -70,6 +70,7 @@ mod ebml; mod UFind; mod ExtFmt; mod Box; +mod GetOpts; // Local Variables: // mode: rust;