/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "option.h" namespace ledger { namespace { typedef std::pair op_bool_tuple; op_bool_tuple find_option(scope_t& scope, const string& name) { char buf[128]; char * p = buf; if (name.length() > 127) { throw_(option_error, _f("Illegal option --%1%") % name); } foreach (char ch, name) { if (ch == '-') *p++ = '_'; else *p++ = ch; } *p++ = '_'; *p = '\0'; if (expr_t::ptr_op_t op = scope.lookup(symbol_t::OPTION, buf)) return op_bool_tuple(op, true); *--p = '\0'; return op_bool_tuple(scope.lookup(symbol_t::OPTION, buf), false); } op_bool_tuple find_option(scope_t& scope, const char letter) { char buf[4]; buf[0] = letter; buf[1] = '_'; buf[2] = '\0'; if (expr_t::ptr_op_t op = scope.lookup(symbol_t::OPTION, buf)) return op_bool_tuple(op, true); buf[1] = '\0'; return op_bool_tuple(scope.lookup(symbol_t::OPTION, buf), false); } void process_option(const string& whence, const expr_t::func_t& opt, scope_t& scope, const char * arg, const string& name) { try { call_scope_t args(scope); args.push_back(string_value(whence)); if (arg) args.push_back(string_value(arg)); opt(args); } catch (const std::exception&) { if (name[0] == '-') add_error_context(_f("While parsing option '%1%'") % name); else add_error_context(_f("While parsing environment variable '%1%'") % name); throw; } } } bool process_option(const string& whence, const string& name, scope_t& scope, const char * arg, const string& varname) { op_bool_tuple opt(find_option(scope, name)); if (opt.first) { process_option(whence, opt.first->as_function(), scope, arg, varname); return true; } return false; } void process_environment(const char ** envp, const string& tag, scope_t& scope) { const char * tag_p = tag.c_str(); string::size_type tag_len = tag.length(); assert(tag_p); assert(tag_len > 0); for (const char ** p = envp; *p; p++) { if (std::strlen(*p) >= tag_len && std::strncmp(*p, tag_p, tag_len) == 0) { char buf[8192]; char * r = buf; const char * q; for (q = *p + tag_len; *q && *q != '=' && r - buf < 8191; q++) if (*q == '_') *r++ = '-'; else *r++ = static_cast(std::tolower(*q)); *r = '\0'; if (*q == '=') { try { string value = string(*p, static_cast(q - *p)); if (! value.empty()) process_option(string("$") + buf, string(buf), scope, q + 1, value); } catch (const std::exception&) { add_error_context(_f("While parsing environment variable option '%1%':") % *p); throw; } } } } } namespace { struct op_bool_char_tuple { expr_t::ptr_op_t op; bool truth; char ch; op_bool_char_tuple(expr_t::ptr_op_t _op, bool _truth, char _ch) : op(_op), truth(_truth), ch(_ch) {} }; } strings_list process_arguments(strings_list args, scope_t& scope) { bool anywhere = true; strings_list remaining; for (strings_list::iterator i = args.begin(); i != args.end(); i++) { DEBUG("option.args", "Examining argument '" << *i << "'"); if (! anywhere || (*i)[0] != '-') { DEBUG("option.args", " adding to list of real args"); remaining.push_back(*i); continue; } // --long-option or -s if ((*i)[1] == '-') { if ((*i)[2] == '\0') { DEBUG("option.args", " it's a --, ending options processing"); anywhere = false; continue; } DEBUG("option.args", " it's an option string"); string opt_name; const char * name = (*i).c_str() + 2; const char * value = NULL; if (const char * p = std::strchr(name, '=')) { opt_name = string(name, static_cast(p - name)); value = ++p; DEBUG("option.args", " read option value from option: " << value); } else { opt_name = name; } op_bool_tuple opt(find_option(scope, opt_name)); if (! opt.first) throw_(option_error, _f("Illegal option --%1%") % name); if (opt.second && ! value && ++i != args.end() && value == NULL) { value = (*i).c_str(); DEBUG("option.args", " read option value from arg: " << value); if (value == NULL) throw_(option_error, _f("Missing option argument for --%1%") % name); } process_option(string("--") + name, opt.first->as_function(), scope, value, string("--") + name); } else if ((*i)[1] == '\0') { throw_(option_error, _f("illegal option -%1%") % (*i)[0]); } else { DEBUG("option.args", " single-char option"); std::list option_queue; std::string::size_type x = 1; for (char c = (*i)[x]; c != '\0'; x++, c = (*i)[x]) { op_bool_tuple opt(find_option(scope, c)); if (! opt.first) throw_(option_error, _f("Illegal option -%1%") % c); option_queue.push_back(op_bool_char_tuple(opt.first, opt.second, c)); } foreach (op_bool_char_tuple& o, option_queue) { const char * value = NULL; if (o.truth && ++i != args.end()) { value = (*i).c_str(); DEBUG("option.args", " read option value from arg: " << value); if (value == NULL) throw_(option_error, _f("Missing option argument for -%1%") % o.ch); } process_option(string("-") + o.ch, o.op->as_function(), scope, value, string("-") + o.ch); } } } return remaining; } } // namespace ledger