diff options
author | Mu Qiao <qiaomuf@gentoo.org> | 2011-06-08 21:51:41 +0800 |
---|---|---|
committer | Petteri Räty <petsku@petteriraty.eu> | 2011-06-11 11:48:20 +0300 |
commit | 261f32a51ff1e487fb41cecbd77b0da6ef729ce2 (patch) | |
tree | 9c43233c38edd7145161c4dc1c65b871160bbcaf /src/builtins | |
parent | Merge remote-tracking branch 'mu/array_expansion' (diff) | |
download | libbash-261f32a51ff1e487fb41cecbd77b0da6ef729ce2.tar.gz libbash-261f32a51ff1e487fb41cecbd77b0da6ef729ce2.tar.bz2 libbash-261f32a51ff1e487fb41cecbd77b0da6ef729ce2.zip |
Builtin: support printf built-in
Note that we use boost::format to implement printf so it's not
completely the same as bash printf.
Diffstat (limited to 'src/builtins')
-rw-r--r-- | src/builtins/builtin_exceptions.h | 4 | ||||
-rw-r--r-- | src/builtins/echo_builtin.cpp | 38 | ||||
-rw-r--r-- | src/builtins/echo_builtin.h | 4 | ||||
-rw-r--r-- | src/builtins/printf_builtin.cpp | 63 | ||||
-rw-r--r-- | src/builtins/printf_builtin.h | 35 | ||||
-rw-r--r-- | src/builtins/tests/printf_tests.cpp | 67 |
6 files changed, 171 insertions, 40 deletions
diff --git a/src/builtins/builtin_exceptions.h b/src/builtins/builtin_exceptions.h index d6eded4..3da6de8 100644 --- a/src/builtins/builtin_exceptions.h +++ b/src/builtins/builtin_exceptions.h @@ -58,4 +58,8 @@ public: } } }; + +class suppress_output: public std::exception +{ +}; #endif diff --git a/src/builtins/echo_builtin.cpp b/src/builtins/echo_builtin.cpp index 8d703ff..8caecef 100644 --- a/src/builtins/echo_builtin.cpp +++ b/src/builtins/echo_builtin.cpp @@ -26,15 +26,12 @@ #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> +#include "builtins/builtin_exceptions.h" namespace qi = boost::spirit::qi; namespace karma = boost::spirit::karma; namespace phoenix = boost::phoenix; -class suppress_output -{ -}; - int echo_builtin::exec(const std::vector<std::string>& bash_args) { bool suppress_nl = false; @@ -64,7 +61,7 @@ int echo_builtin::exec(const std::vector<std::string>& bash_args) { try { - transform_escapes(*i); + transform_escapes(*i, out_buffer()); } catch(suppress_output) { @@ -121,34 +118,3 @@ bool echo_builtin::determine_options(const std::string &string, bool &suppress_n return false; } } - -void echo_builtin::transform_escapes(const std::string &string) -{ - using phoenix::val; - using qi::lit; - - auto escape_parser = - +( - lit('\\') >> - ( - lit('a')[this->out_buffer() << val("\a")] | - lit('b')[this->out_buffer() << val("\b")] | - // \e is a GNU extension - lit('e')[this->out_buffer() << val("\033")] | - lit('f')[this->out_buffer() << val("\f")] | - lit('n')[this->out_buffer() << val("\n")] | - lit('r')[this->out_buffer() << val("\r")] | - lit('t')[this->out_buffer() << val("\t")] | - lit('v')[this->out_buffer() << val("\v")] | - lit('c')[phoenix::throw_(suppress_output())] | - lit('\\')[this->out_buffer() << val('\\')] | - lit("0") >> qi::uint_parser<unsigned, 8, 1, 3>()[ this->out_buffer() << phoenix::static_cast_<char>(qi::_1)] | - lit("x") >> qi::uint_parser<unsigned, 16, 1, 2>()[ this->out_buffer() << phoenix::static_cast_<char>(qi::_1)] - - ) | - qi::char_[this->out_buffer() << qi::_1] - ); - - auto begin = string.begin(); - qi::parse(begin, string.end(), escape_parser); -} diff --git a/src/builtins/echo_builtin.h b/src/builtins/echo_builtin.h index 6de6475..3f5da62 100644 --- a/src/builtins/echo_builtin.h +++ b/src/builtins/echo_builtin.h @@ -51,10 +51,6 @@ class echo_builtin: public virtual cppbash_builtin /// \param enable_escapes returns back whether to enable escapes /// \return false if all options have been processed bool determine_options(const std::string &string, bool &suppress_nl, bool &enable_escapes); - - /// \brief transforms escapes in echo input - /// \return false when further output should be suppressed - void transform_escapes(const std::string &string); }; #endif diff --git a/src/builtins/printf_builtin.cpp b/src/builtins/printf_builtin.cpp new file mode 100644 index 0000000..c365863 --- /dev/null +++ b/src/builtins/printf_builtin.cpp @@ -0,0 +1,63 @@ +/* + Please use git log for copyright holder and year information + + This file is part of libbash. + + libbash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + libbash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libbash. If not, see <http://www.gnu.org/licenses/>. +*/ +/// +/// \file printf_builtin.h +/// \brief implementation for the printf builtin +/// + +#include "builtins/printf_builtin.h" + +#include <boost/format.hpp> + +#include "core/interpreter.h" +#include "cppbash_builtin.h" + +int printf_builtin::exec(const std::vector<std::string>& bash_args) +{ + std::vector<std::string>::const_iterator begin; + if(!(bash_args[0] == "-v")) + begin = bash_args.begin(); + else if(bash_args.size() < 3) + throw interpreter_exception("printf: illegal number of arguments"); + else + begin = bash_args.begin() + 2; + + std::stringstream format_string; + transform_escapes(*begin, format_string); + boost::format formatter(format_string.str()); + for(auto iter = begin + 1; iter != bash_args.end(); ++iter) + formatter = formatter % *iter; + + if(!(bash_args[0][0] == '-')) + { + *_out_stream << formatter; + } + else if(bash_args[0] == "-v") + { + std::stringstream output; + output << formatter; + _walker.set_value(bash_args[1], output.str()); + } + else + { + throw interpreter_exception("printf: invalid option: " + bash_args[0]); + } + + return 0; +} diff --git a/src/builtins/printf_builtin.h b/src/builtins/printf_builtin.h new file mode 100644 index 0000000..07d5ac7 --- /dev/null +++ b/src/builtins/printf_builtin.h @@ -0,0 +1,35 @@ +/* + Please use git log for copyright holder and year information + + This file is part of libbash. + + libbash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + libbash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libbash. If not, see <http://www.gnu.org/licenses/>. +*/ +/// +/// \file printf_builtin.h +/// \brief implementation for the printf builtin +/// +#ifndef LIBBASH_BUILTINS_printf_BUILTIN_H_ +#define LIBBASH_BUILTINS_printf_BUILTIN_H_ + +#include "cppbash_builtin.h" + +class printf_builtin : public virtual cppbash_builtin +{ +public: + BUILTIN_CONSTRUCTOR(printf) + virtual int exec(const std::vector<std::string>& ); +}; + +#endif diff --git a/src/builtins/tests/printf_tests.cpp b/src/builtins/tests/printf_tests.cpp new file mode 100644 index 0000000..a0c463c --- /dev/null +++ b/src/builtins/tests/printf_tests.cpp @@ -0,0 +1,67 @@ +/* + Please use git log for copyright holder and year information + + This file is part of libbash. + + libbash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + libbash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libbash. If not, see <http://www.gnu.org/licenses/>. +*/ +/// +/// \file printf_tests.cpp +/// \brief series of unit tests for printf builtin +/// +#include <boost/lexical_cast.hpp> +#include <gtest/gtest.h> + +#include "builtins/builtin_exceptions.h" +#include "core/interpreter.h" +#include "cppbash_builtin.h" + +namespace +{ + void verify_error(const std::vector<std::string>& arguments, const std::string& expected, interpreter& walker) + { + try + { + cppbash_builtin::exec("printf", arguments, std::cout, std::cerr, std::cin, walker); + FAIL(); + } + catch(interpreter_exception& e) + { + EXPECT_STREQ(expected.c_str(), e.what()); + } + } + + void verify_output(const std::vector<std::string>& arguments, const std::string& expected, interpreter& walker) + { + std::stringstream output; + EXPECT_EQ(0, cppbash_builtin::exec("printf", arguments, output, std::cerr, std::cin, walker)); + EXPECT_STREQ(expected.c_str(), output.str().c_str()); + } +} + +TEST(printf_builtin_test, bad_argument) +{ + interpreter walker; + verify_error({"-v"}, "printf: illegal number of arguments", walker); + verify_error({"-p"}, "printf: invalid option: -p", walker); +} + +TEST(printf_builtin_test, normal) +{ + interpreter walker; + verify_output({"-v", "foo", "%s\n", "bar"}, "", walker); + EXPECT_STREQ(walker.resolve<std::string>("foo").c_str(), "bar\n"); + + verify_output({"%s %s\n", "foo", "bar"}, "foo bar\n", walker); +} |