From 31f6bbe25a23e7e0d2b1411b3952ba39066a195b Mon Sep 17 00:00:00 2001 From: André Aparício Date: Mon, 28 May 2012 23:48:16 +0100 Subject: Builtin: Implement set -u option --- src/builtins/set_builtin.cpp | 2 ++ src/builtins/tests/set_tests.cpp | 19 ++++++++++++++++ src/core/interpreter.cpp | 22 ++++++++++++++++-- src/core/interpreter.h | 49 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 87 insertions(+), 5 deletions(-) diff --git a/src/builtins/set_builtin.cpp b/src/builtins/set_builtin.cpp index fca0f82..6becb76 100644 --- a/src/builtins/set_builtin.cpp +++ b/src/builtins/set_builtin.cpp @@ -62,6 +62,8 @@ int set_builtin::exec(const std::vector& bash_args) case 'p': case 't': case 'u': + _walker.set_option('u', bash_args[0][0] == '-'); + return 0; case 'v': case 'x': case 'B': diff --git a/src/builtins/tests/set_tests.cpp b/src/builtins/tests/set_tests.cpp index c4807c9..2b724bb 100644 --- a/src/builtins/tests/set_tests.cpp +++ b/src/builtins/tests/set_tests.cpp @@ -41,3 +41,22 @@ TEST(set_builtin_test, positional) EXPECT_EQ(0, walker.get_array_length("*")); EXPECT_STREQ("", walker.resolve("*", 1).c_str()); } + +TEST(set_builtin_test, u_option) +{ + interpreter walker; + + EXPECT_EQ(0, cppbash_builtin::exec("set", {"-u"}, std::cout, std::cerr, std::cin, walker)); + EXPECT_THROW(walker.resolve("VAR1").c_str(), libbash::unsupported_exception); + + walker.set_value("ARRAY", "foo"); + EXPECT_NO_THROW(walker.resolve("ARRAY").c_str()); + EXPECT_THROW(walker.resolve("ARRAY", 2).c_str(), libbash::unsupported_exception); + + walker.set_value("ARRAY", "foo", 3); + EXPECT_NO_THROW(walker.resolve("ARRAY", 3).c_str()); + + EXPECT_EQ(0, cppbash_builtin::exec("set", {"+u"}, std::cout, std::cerr, std::cin, walker)); + EXPECT_NO_THROW(walker.resolve("VAR2", 1).c_str()); + +} diff --git a/src/core/interpreter.cpp b/src/core/interpreter.cpp index 405023d..1b8ef0a 100644 --- a/src/core/interpreter.cpp +++ b/src/core/interpreter.cpp @@ -248,7 +248,7 @@ std::string::size_type interpreter::get_length(const std::string& name, const unsigned index) const { auto var = resolve_variable(name); - if(!var) + if(!is_valid(var, name)) return 0; return var->get_length(index); } @@ -256,7 +256,7 @@ std::string::size_type interpreter::get_length(const std::string& name, variable::size_type interpreter::get_array_length(const std::string& name) const { auto var = resolve_variable(name); - if(!var) + if(!is_valid(var, name)) return 0; return var->get_array_length(); } @@ -471,6 +471,24 @@ void interpreter::set_additional_option(const std::string& name, bool value) iter->second = value; } +bool interpreter::get_option(const char name) const +{ + auto iter = options.find(name); + if(iter == options.end()) + throw libbash::illegal_argument_exception("Invalid bash option"); + + return iter->second; +} + +void interpreter::set_option(const char name, bool value) +{ + auto iter = options.find(name); + if(iter == options.end()) + throw libbash::illegal_argument_exception(name + " is not a valid bash option"); + + iter->second = value; +} + long interpreter::eval_arithmetic(const std::string& expression) { bash_ast ast(std::stringstream(expression), &bash_ast::parser_arithmetics); diff --git a/src/core/interpreter.h b/src/core/interpreter.h index dd72377..3f77f5d 100644 --- a/src/core/interpreter.h +++ b/src/core/interpreter.h @@ -189,6 +189,20 @@ public: _out = &std::cout; } + /// \brief check whether a variable is valid and can be used + /// \param var variable + /// \return whether the variable is valid + bool is_valid(const std::shared_ptr& var, const std::string& name) const + { + if(var) + return true; + + if(get_option('u')) + throw libbash::unsupported_exception(name + ": unbound variable"); + + return false; + } + /// \brief resolve string/long variable, local scope will be /// checked first, then global scope /// \param name variable name @@ -199,10 +213,25 @@ public: T resolve(const std::string& name, const unsigned index=0) const { auto var = resolve_variable(name); - if(var) + if(is_valid(var, name)) + { + if(get_option('u') && var->is_unset(index)) + { + if(name == "*") + throw libbash::unsupported_exception("$" + boost::lexical_cast(index) + ": unbound variable"); + else if(index == 0) + throw libbash::unsupported_exception(name + ": unbound variable"); + else + throw libbash::unsupported_exception(name + "[" + boost::lexical_cast(index) + "]: unbound variable"); + } return var->get_value(index); + } else + { + if(get_option('u')) + throw libbash::unsupported_exception(name + ": unbound variable"); return T{}; + } } /// \brief resolve array variable @@ -212,7 +241,7 @@ public: bool resolve_array(const std::string& name, std::vector& values) const { auto var = resolve_variable(name); - if(!var) + if(!is_valid(var, name)) return false; var->get_all_values(values); @@ -467,7 +496,10 @@ public: variable::size_type get_max_index(const std::string& name) const { auto var = resolve_variable(name); - return var ? var->get_max_index() : 0; + if(is_valid(var, name)) + return var->get_max_index(); + else + return 0; } /// \brief get all array elements concatenated by space @@ -496,6 +528,17 @@ public: /// \return zero unless the name is not a valid shell option void set_additional_option(const std::string& name, bool value); + /// \brief get the status of shell optional behavior + /// \param name the option name + /// \return zero unless the name is not a valid shell option + bool get_option(const char name) const; + + /// \brief set the status of shell optional behavior + /// \param name the option name + /// \param[in] value true if option is enabled, false otherwise + /// \return zero unless the name is not a valid shell option + void set_option(const char name, bool value); + /// \brief return an iterator referring to the first variable /// \return iterator referring to the first variable option_iterator additional_options_begin() const -- cgit v1.2.3-65-gdbad