1 |
commit: 31f6bbe25a23e7e0d2b1411b3952ba39066a195b |
2 |
Author: André Aparício <aparicio99 <AT> gmail <DOT> com> |
3 |
AuthorDate: Mon May 28 22:48:16 2012 +0000 |
4 |
Commit: Petteri Räty <betelgeuse <AT> gentoo <DOT> org> |
5 |
CommitDate: Tue Jul 3 01:16:19 2012 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/libbash.git;a=commit;h=31f6bbe2 |
7 |
|
8 |
Builtin: Implement set -u option |
9 |
|
10 |
--- |
11 |
src/builtins/set_builtin.cpp | 2 + |
12 |
src/builtins/tests/set_tests.cpp | 19 ++++++++++++++ |
13 |
src/core/interpreter.cpp | 22 +++++++++++++++- |
14 |
src/core/interpreter.h | 49 +++++++++++++++++++++++++++++++++++-- |
15 |
4 files changed, 87 insertions(+), 5 deletions(-) |
16 |
|
17 |
diff --git a/src/builtins/set_builtin.cpp b/src/builtins/set_builtin.cpp |
18 |
index fca0f82..6becb76 100644 |
19 |
--- a/src/builtins/set_builtin.cpp |
20 |
+++ b/src/builtins/set_builtin.cpp |
21 |
@@ -62,6 +62,8 @@ int set_builtin::exec(const std::vector<std::string>& bash_args) |
22 |
case 'p': |
23 |
case 't': |
24 |
case 'u': |
25 |
+ _walker.set_option('u', bash_args[0][0] == '-'); |
26 |
+ return 0; |
27 |
case 'v': |
28 |
case 'x': |
29 |
case 'B': |
30 |
|
31 |
diff --git a/src/builtins/tests/set_tests.cpp b/src/builtins/tests/set_tests.cpp |
32 |
index c4807c9..2b724bb 100644 |
33 |
--- a/src/builtins/tests/set_tests.cpp |
34 |
+++ b/src/builtins/tests/set_tests.cpp |
35 |
@@ -41,3 +41,22 @@ TEST(set_builtin_test, positional) |
36 |
EXPECT_EQ(0, walker.get_array_length("*")); |
37 |
EXPECT_STREQ("", walker.resolve<std::string>("*", 1).c_str()); |
38 |
} |
39 |
+ |
40 |
+TEST(set_builtin_test, u_option) |
41 |
+{ |
42 |
+ interpreter walker; |
43 |
+ |
44 |
+ EXPECT_EQ(0, cppbash_builtin::exec("set", {"-u"}, std::cout, std::cerr, std::cin, walker)); |
45 |
+ EXPECT_THROW(walker.resolve<std::string>("VAR1").c_str(), libbash::unsupported_exception); |
46 |
+ |
47 |
+ walker.set_value("ARRAY", "foo"); |
48 |
+ EXPECT_NO_THROW(walker.resolve<std::string>("ARRAY").c_str()); |
49 |
+ EXPECT_THROW(walker.resolve<std::string>("ARRAY", 2).c_str(), libbash::unsupported_exception); |
50 |
+ |
51 |
+ walker.set_value("ARRAY", "foo", 3); |
52 |
+ EXPECT_NO_THROW(walker.resolve<std::string>("ARRAY", 3).c_str()); |
53 |
+ |
54 |
+ EXPECT_EQ(0, cppbash_builtin::exec("set", {"+u"}, std::cout, std::cerr, std::cin, walker)); |
55 |
+ EXPECT_NO_THROW(walker.resolve<std::string>("VAR2", 1).c_str()); |
56 |
+ |
57 |
+} |
58 |
|
59 |
diff --git a/src/core/interpreter.cpp b/src/core/interpreter.cpp |
60 |
index 405023d..1b8ef0a 100644 |
61 |
--- a/src/core/interpreter.cpp |
62 |
+++ b/src/core/interpreter.cpp |
63 |
@@ -248,7 +248,7 @@ std::string::size_type interpreter::get_length(const std::string& name, |
64 |
const unsigned index) const |
65 |
{ |
66 |
auto var = resolve_variable(name); |
67 |
- if(!var) |
68 |
+ if(!is_valid(var, name)) |
69 |
return 0; |
70 |
return var->get_length(index); |
71 |
} |
72 |
@@ -256,7 +256,7 @@ std::string::size_type interpreter::get_length(const std::string& name, |
73 |
variable::size_type interpreter::get_array_length(const std::string& name) const |
74 |
{ |
75 |
auto var = resolve_variable(name); |
76 |
- if(!var) |
77 |
+ if(!is_valid(var, name)) |
78 |
return 0; |
79 |
return var->get_array_length(); |
80 |
} |
81 |
@@ -471,6 +471,24 @@ void interpreter::set_additional_option(const std::string& name, bool value) |
82 |
iter->second = value; |
83 |
} |
84 |
|
85 |
+bool interpreter::get_option(const char name) const |
86 |
+{ |
87 |
+ auto iter = options.find(name); |
88 |
+ if(iter == options.end()) |
89 |
+ throw libbash::illegal_argument_exception("Invalid bash option"); |
90 |
+ |
91 |
+ return iter->second; |
92 |
+} |
93 |
+ |
94 |
+void interpreter::set_option(const char name, bool value) |
95 |
+{ |
96 |
+ auto iter = options.find(name); |
97 |
+ if(iter == options.end()) |
98 |
+ throw libbash::illegal_argument_exception(name + " is not a valid bash option"); |
99 |
+ |
100 |
+ iter->second = value; |
101 |
+} |
102 |
+ |
103 |
long interpreter::eval_arithmetic(const std::string& expression) |
104 |
{ |
105 |
bash_ast ast(std::stringstream(expression), &bash_ast::parser_arithmetics); |
106 |
|
107 |
diff --git a/src/core/interpreter.h b/src/core/interpreter.h |
108 |
index dd72377..3f77f5d 100644 |
109 |
--- a/src/core/interpreter.h |
110 |
+++ b/src/core/interpreter.h |
111 |
@@ -189,6 +189,20 @@ public: |
112 |
_out = &std::cout; |
113 |
} |
114 |
|
115 |
+ /// \brief check whether a variable is valid and can be used |
116 |
+ /// \param var variable |
117 |
+ /// \return whether the variable is valid |
118 |
+ bool is_valid(const std::shared_ptr<variable>& var, const std::string& name) const |
119 |
+ { |
120 |
+ if(var) |
121 |
+ return true; |
122 |
+ |
123 |
+ if(get_option('u')) |
124 |
+ throw libbash::unsupported_exception(name + ": unbound variable"); |
125 |
+ |
126 |
+ return false; |
127 |
+ } |
128 |
+ |
129 |
/// \brief resolve string/long variable, local scope will be |
130 |
/// checked first, then global scope |
131 |
/// \param name variable name |
132 |
@@ -199,10 +213,25 @@ public: |
133 |
T resolve(const std::string& name, const unsigned index=0) const |
134 |
{ |
135 |
auto var = resolve_variable(name); |
136 |
- if(var) |
137 |
+ if(is_valid(var, name)) |
138 |
+ { |
139 |
+ if(get_option('u') && var->is_unset(index)) |
140 |
+ { |
141 |
+ if(name == "*") |
142 |
+ throw libbash::unsupported_exception("$" + boost::lexical_cast<std::string>(index) + ": unbound variable"); |
143 |
+ else if(index == 0) |
144 |
+ throw libbash::unsupported_exception(name + ": unbound variable"); |
145 |
+ else |
146 |
+ throw libbash::unsupported_exception(name + "[" + boost::lexical_cast<std::string>(index) + "]: unbound variable"); |
147 |
+ } |
148 |
return var->get_value<T>(index); |
149 |
+ } |
150 |
else |
151 |
+ { |
152 |
+ if(get_option('u')) |
153 |
+ throw libbash::unsupported_exception(name + ": unbound variable"); |
154 |
return T{}; |
155 |
+ } |
156 |
} |
157 |
|
158 |
/// \brief resolve array variable |
159 |
@@ -212,7 +241,7 @@ public: |
160 |
bool resolve_array(const std::string& name, std::vector<T>& values) const |
161 |
{ |
162 |
auto var = resolve_variable(name); |
163 |
- if(!var) |
164 |
+ if(!is_valid(var, name)) |
165 |
return false; |
166 |
|
167 |
var->get_all_values(values); |
168 |
@@ -467,7 +496,10 @@ public: |
169 |
variable::size_type get_max_index(const std::string& name) const |
170 |
{ |
171 |
auto var = resolve_variable(name); |
172 |
- return var ? var->get_max_index() : 0; |
173 |
+ if(is_valid(var, name)) |
174 |
+ return var->get_max_index(); |
175 |
+ else |
176 |
+ return 0; |
177 |
} |
178 |
|
179 |
/// \brief get all array elements concatenated by space |
180 |
@@ -496,6 +528,17 @@ public: |
181 |
/// \return zero unless the name is not a valid shell option |
182 |
void set_additional_option(const std::string& name, bool value); |
183 |
|
184 |
+ /// \brief get the status of shell optional behavior |
185 |
+ /// \param name the option name |
186 |
+ /// \return zero unless the name is not a valid shell option |
187 |
+ bool get_option(const char name) const; |
188 |
+ |
189 |
+ /// \brief set the status of shell optional behavior |
190 |
+ /// \param name the option name |
191 |
+ /// \param[in] value true if option is enabled, false otherwise |
192 |
+ /// \return zero unless the name is not a valid shell option |
193 |
+ void set_option(const char name, bool value); |
194 |
+ |
195 |
/// \brief return an iterator referring to the first variable |
196 |
/// \return iterator referring to the first variable |
197 |
option_iterator additional_options_begin() const |