aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/builtins/set_builtin.cpp2
-rw-r--r--src/builtins/tests/set_tests.cpp19
-rw-r--r--src/core/interpreter.cpp22
-rw-r--r--src/core/interpreter.h49
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<std::string>& 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<std::string>("*", 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<std::string>("VAR1").c_str(), libbash::unsupported_exception);
+
+ walker.set_value("ARRAY", "foo");
+ EXPECT_NO_THROW(walker.resolve<std::string>("ARRAY").c_str());
+ EXPECT_THROW(walker.resolve<std::string>("ARRAY", 2).c_str(), libbash::unsupported_exception);
+
+ walker.set_value("ARRAY", "foo", 3);
+ EXPECT_NO_THROW(walker.resolve<std::string>("ARRAY", 3).c_str());
+
+ EXPECT_EQ(0, cppbash_builtin::exec("set", {"+u"}, std::cout, std::cerr, std::cin, walker));
+ EXPECT_NO_THROW(walker.resolve<std::string>("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<variable>& 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<std::string>(index) + ": unbound variable");
+ else if(index == 0)
+ throw libbash::unsupported_exception(name + ": unbound variable");
+ else
+ throw libbash::unsupported_exception(name + "[" + boost::lexical_cast<std::string>(index) + "]: unbound variable");
+ }
return var->get_value<T>(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<T>& 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