diff options
-rw-r--r-- | Makefile.am | 13 | ||||
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | m4/doxample.m4 (renamed from acinclude.m4) | 0 | ||||
-rw-r--r-- | m4/gtest.m4 | 74 | ||||
-rw-r--r-- | src/builtins/builtins.h | 28 | ||||
-rw-r--r-- | src/builtins/echo_builtin.cpp | 171 | ||||
-rw-r--r-- | src/builtins/echo_builtin.h | 83 | ||||
-rw-r--r-- | src/builtins/tests/echo_tests.cpp | 173 | ||||
-rw-r--r-- | src/builtins/tests/run_tests.cpp | 32 |
9 files changed, 574 insertions, 3 deletions
diff --git a/Makefile.am b/Makefile.am index 9565d35..6257025 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -############################################# +############################################### #Copyright 2010 Nathan Eloe # #This file is part of libbash. @@ -23,10 +23,20 @@ include doxygen.am ACLOCAL_AMFLAGS = -I m4 TESTS = bashast/gunit/runtests.sh +TEST_EXTENSIONS= .sh SH_LOG_COMPILER = /bin/bash +if HAVE_GTEST +TESTS += builtin_unittests +check_PROGRAMS = builtin_unittests +builtin_unittests_SOURCES = src/builtins/tests/run_tests.cpp +builtin_unittests_SOURCES += src/builtins/tests/echo_tests.cpp +builtin_unittests_LDADD = ${GTEST_LIBS} libcppbash.la +endif + lib_LTLIBRARIES = libcppbash.la libcppbash_la_SOURCES = src/cppbash_builtin.cpp +libcppbash_la_SOURCES += src/builtins/echo_builtin.cpp coding_standard.pdf: coding_standard/coding_standard.tex @PDFLATEX@ coding_standard/coding_standard.tex 2&>1 > /dev/null @@ -38,4 +48,3 @@ grammar.run: bashast.g bashast.g: bashast/bashast.g sed -e 's/CommonTree/pANTLR3_BASE_TREE/g' -e 's/Java/C/g' $< > $@ - diff --git a/configure.ac b/configure.ac index 87b0b20..659cc38 100644 --- a/configure.ac +++ b/configure.ac @@ -18,7 +18,7 @@ AC_CONFIG_MACRO_DIR([m4]) AC_INIT([libbash], [0.1],[powerofazure@gmail.com]) -AM_INIT_AUTOMAKE([parallel-tests]) +AM_INIT_AUTOMAKE([parallel-tests subdir-objects]) AC_PREREQ([2.65]) AC_PROG_CXX LT_INIT @@ -28,6 +28,7 @@ AC_PATH_PROG([JAVA],[java],"no") if test "$JAVA" = "no"; then AC_MSG_ERROR([No java executable found]) fi +GTEST_LIB_CHECK(,[:],[:]) AC_ARG_WITH(antlr, [],with_antlr=$withval,with_antlr=jc) if test "$with_antlr" = "jc"; then AC_PATH_PROG(JAVA_CONFIG, java-config, "no") diff --git a/acinclude.m4 b/m4/doxample.m4 index d9a951c..d9a951c 100644 --- a/acinclude.m4 +++ b/m4/doxample.m4 diff --git a/m4/gtest.m4 b/m4/gtest.m4 new file mode 100644 index 0000000..6598ba7 --- /dev/null +++ b/m4/gtest.m4 @@ -0,0 +1,74 @@ +dnl GTEST_LIB_CHECK([minimum version [, +dnl action if found [,action if not found]]]) +dnl +dnl Check for the presence of the Google Test library, optionally at a minimum +dnl version, and indicate a viable version with the HAVE_GTEST flag. It defines +dnl standard variables for substitution including GTEST_CPPFLAGS, +dnl GTEST_CXXFLAGS, GTEST_LDFLAGS, and GTEST_LIBS. It also defines +dnl GTEST_VERSION as the version of Google Test found. Finally, it provides +dnl optional custom action slots in the event GTEST is found or not. +AC_DEFUN([GTEST_LIB_CHECK], +[ +dnl Provide a flag to enable or disable Google Test usage. +AC_ARG_ENABLE([gtest], + [AS_HELP_STRING([--enable-gtest], + [Enable tests using the Google C++ Testing Framework. + (Default is enabled.)])], + [], + [enable_gtest=]) +AC_ARG_VAR([GTEST_CONFIG], + [The exact path of Google Test's 'gtest-config' script.]) +AC_ARG_VAR([GTEST_CPPFLAGS], + [C-like preprocessor flags for Google Test.]) +AC_ARG_VAR([GTEST_CXXFLAGS], + [C++ compile flags for Google Test.]) +AC_ARG_VAR([GTEST_LDFLAGS], + [Linker path and option flags for Google Test.]) +AC_ARG_VAR([GTEST_LIBS], + [Library linking flags for Google Test.]) +AC_ARG_VAR([GTEST_VERSION], + [The version of Google Test available.]) +HAVE_GTEST="no" +AS_IF([test "x${enable_gtest}" != "xno"], + [AC_MSG_CHECKING([for 'gtest-config']) + AS_IF([test "x${enable_gtest}" != "xyes"], + [AS_IF([test -x "${enable_gtest}/scripts/gtest-config"], + [GTEST_CONFIG="${enable_gtest}/scripts/gtest-config"], + [GTEST_CONFIG="${enable_gtest}/bin/gtest-config"]) + AS_IF([test -x "${GTEST_CONFIG}"], [], + [AC_MSG_RESULT([no]) + AC_MSG_ERROR([dnl +Unable to locate either a built or installed Google Test. +The specific location '${enable_gtest}' was provided for a built or installed +Google Test, but no 'gtest-config' script could be found at this location.]) + ])], + [AC_PATH_PROG([GTEST_CONFIG], [gtest-config])]) + AS_IF([test -x "${GTEST_CONFIG}"], + [AC_MSG_RESULT([${GTEST_CONFIG}]) + m4_ifval([$1], + [_gtest_min_version="--min-version=$1" + AC_MSG_CHECKING([for Google Test at least version >= $1])], + [_gtest_min_version="--min-version=0" + AC_MSG_CHECKING([for Google Test])]) + AS_IF([${GTEST_CONFIG} ${_gtest_min_version}], + [AC_MSG_RESULT([yes]) + HAVE_GTEST='yes'], + [AC_MSG_RESULT([no])])], + [AC_MSG_RESULT([no])]) + AS_IF([test "x${HAVE_GTEST}" = "xyes"], + [GTEST_CPPFLAGS=`${GTEST_CONFIG} --cppflags` + GTEST_CXXFLAGS=`${GTEST_CONFIG} --cxxflags` + GTEST_LDFLAGS=`${GTEST_CONFIG} --ldflags` + GTEST_LIBS=`${GTEST_CONFIG} --libs` + GTEST_VERSION=`${GTEST_CONFIG} --version` + AC_DEFINE([HAVE_GTEST],[1],[Defined when Google Test is available.])], + [AS_IF([test "x${enable_gtest}" = "xyes"], + [AC_MSG_ERROR([dnl +Google Test was enabled, but no viable version could be found.]) + ])])]) +AC_SUBST([HAVE_GTEST]) +AM_CONDITIONAL([HAVE_GTEST],[test "x$HAVE_GTEST" = "xyes"]) +AS_IF([test "x$HAVE_GTEST" = "xyes"], + [m4_ifval([$2], [$2])], + [m4_ifval([$3], [$3])]) +]) diff --git a/src/builtins/builtins.h b/src/builtins/builtins.h new file mode 100644 index 0000000..b954b8d --- /dev/null +++ b/src/builtins/builtins.h @@ -0,0 +1,28 @@ +/* +Copyright 2010 Nathan Eloe + +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 builtins.h +/// \author Nathan Eloe +/// \brief An include file for all of the builtin implementations +/// +#ifndef BUILTINS_H +#define BUILTINS_H + +#include "echo_builtin.h" + +#endif diff --git a/src/builtins/echo_builtin.cpp b/src/builtins/echo_builtin.cpp new file mode 100644 index 0000000..909458b --- /dev/null +++ b/src/builtins/echo_builtin.cpp @@ -0,0 +1,171 @@ +/* +Copyright 2010 Nathan Eloe + +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 echo_builtin.cpp +/// \author Nathan Eloe +/// \brief class that implements the echo builtin +/// + +#include "echo_builtin.h" + +echo_builtin::echo_builtin(std::ostream &outstream, std::ostream &errstream, std::istream&instream) : cppbash_builtin(outstream, errstream, instream) +{ +} + +int echo_builtin::exec(std::vector<std::string> bash_args) +{ + //figure out just what the options are + bool suppress_nl; + bool escapes_enabled; + determine_options(bash_args, suppress_nl, escapes_enabled); + if (escapes_enabled) + { + suppress_nl = (suppress_nl || newline_suppressed(bash_args)); + replace_escapes(bash_args); + } + std::copy(bash_args.begin(), bash_args.end()-1, std::ostream_iterator<std::string>(this->out_buffer()," ")); + this->out_buffer() << *(bash_args.end()-1); + if (!suppress_nl) + { + this->out_buffer() << std::endl; + } + return 0; +} + +void echo_builtin::determine_options(std::vector<std::string> &args, bool &suppress_nl, bool &enable_escapes) +{ + enable_escapes=false; + suppress_nl=false; + bool sup_nl=false; + bool en_esc=false; + bool real_opts; + for (int i=0; i<args.size(); i++) + { + if (*(args[i].begin()) == '-') + { + real_opts=true; + for (std::string::iterator j = args[i].begin()+1; j != args[i].end(); j++) + { + if (*j=='n') + { + sup_nl=true; + } + else if (*j=='e') + { + en_esc=true; + } + else if (*j=='E') + { + en_esc=false; + } + else + { + real_opts=false; + } + } + if (real_opts) + { + args.erase(args.begin()+i); + i--; + suppress_nl=sup_nl; + enable_escapes=en_esc; + } + } + else + { + i=args.size(); + } + } +} + +bool echo_builtin::newline_suppressed(std::vector<std::string> &args) +{ + bool suppressed = false; + for (int i = 0; i < args.size(); i++) + { + while (args[i].find("\\c")!=std::string::npos) + { + suppressed = true; + replace_all(args[i], "\\c", ""); + } + } + return suppressed; +} + +void echo_builtin::replace_escapes(std::vector<std::string> &args) +{ + for (int i = 0; i<args.size(); i++) + { + replace_all(args[i],"\\a","\a"); + replace_all(args[i],"\\b","\b"); + replace_all(args[i],"\\e","\e"); + replace_all(args[i],"\\f","\f"); + replace_all(args[i],"\\n","\n"); + replace_all(args[i],"\\r","\r"); + replace_all(args[i],"\\t","\t"); + replace_all(args[i],"\\v","\v"); + replace_all(args[i],"\\\\","\\"); + replace_numeric_escapes(args[i]); + } +} + +void echo_builtin::replace_all(std::string &word, const std::string &to_rep, const std::string &rep) +{ + while (word.find(to_rep) != std::string::npos) + { + word.replace(word.find(to_rep),to_rep.size(),rep); + } +} + +void echo_builtin::replace_numeric_escapes(std::string &word) +{ + //start with octals + std::string octal_dig = "12345670"; + std::string hex_dig = "123456789aAbBcCdDeEfF0"; + while (word.find("\\0")!=std::string::npos) + { + std::string octal_num; + for (int i = 2; i <= 4; i++) + { + if(octal_dig.find(word[word.find("\\0") + i]) != std::string::npos) + { + octal_num += word[word.find("\\0")+i]; + } + } + int a=std::strtol(octal_num.c_str(),NULL,8); + std::string replace_str; + replace_str += (char)a; + word.replace(word.find("\\0"),2 + octal_num.size(), replace_str); + } + //move on up to the hex numbers + while (word.find("\\x")!=std::string::npos) + { + std::string hex_num; + for (int i = 2; i <= 3; i++) + { + if(hex_dig.find(word[word.find("\\x")+i]) != std::string::npos) + { + hex_num += word[word.find("\\x")+i]; + } + } + int a=std::strtol(hex_num.c_str(),NULL,16); + std::string replace_str; + replace_str += (char)a; + word.replace(word.find("\\x"),2+hex_num.size(), replace_str); + } +} diff --git a/src/builtins/echo_builtin.h b/src/builtins/echo_builtin.h new file mode 100644 index 0000000..2de7230 --- /dev/null +++ b/src/builtins/echo_builtin.h @@ -0,0 +1,83 @@ +/* +Copyright 2010 Nathan Eloe + +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 echo_builtin.h +/// \author Nathan Eloe +/// \brief class that implements the echo builtin +/// + +#ifndef ECHO_BUILTIN_H +#define ECHO_BUILTIN_H + +#include <cstdlib> +#include <iterator> +#include "../cppbash_builtin.h" + +/// +/// \class echo_builtin +/// \brief the echo builtin for bash +/// +class echo_builtin: public virtual cppbash_builtin +{ + public: + /// + /// \brief default constructor, sets default streams + /// \param outstream where to send standard output. Default: cout + /// \param errstream where to send standard error. Default: cerr + /// \param instream where to get standard input from. Default cin + /// + echo_builtin(std::ostream &outstream=std::cout, std::ostream &errstream=std::cerr, std::istream &instream=std::cin); + /// + /// \brief runs the echo plugin on the supplied arguments + /// \param bash_args the arguments to the echo builtin + /// \return exit status of echo + /// + virtual int exec(std::vector<std::string> bash_args); + private: + /// + /// \brief determines the options passed as arguments + /// \param args list of arguments passed to echo + /// \param suppress_nl returns back whether to suppress newlines + /// \param enable_escapes returns back whether to enable escapes + void determine_options(std::vector<std::string> &args, bool &suppress_nl, bool &enable_escapes); + /// + /// \brief checks to see if the trailing newline is suppressed + /// \param args arguments to check, removes suppressing escape sequences + /// \return true if newlines are suppressed, false otherwise + /// + bool newline_suppressed(std::vector<std::string> &args); + /// + /// \brief replaces all escape seqs in std::string with actual escape seq. + /// \param args list of arguments to replace escape sequences in + /// + void replace_escapes(std::vector<std::string> &args); + /// + /// \brief replaces all instances of to_rep with rep + /// \param word word to replace instances in + /// \param to_rep the pattern to replace in the std::string + /// \param rep what to replace to_rep with + /// + void replace_all(std::string &word, const std::string &to_rep, const std::string &rep); + /// + /// \brief replaces octal and hex escapes with escape sequences + /// \param word std::string to replace numeric escapes in + /// + void replace_numeric_escapes(std::string &word); +}; + +#endif diff --git a/src/builtins/tests/echo_tests.cpp b/src/builtins/tests/echo_tests.cpp new file mode 100644 index 0000000..26deff2 --- /dev/null +++ b/src/builtins/tests/echo_tests.cpp @@ -0,0 +1,173 @@ +/* +Copyright 2010 Nathan Eloe + +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 echo_tests.cpp +/// \brief series of unit tests for echo built in +/// \author Nathan Eloe +/// +#include <iostream> +#include "../builtins.h" +#include <sstream> +#include <vector> +#include <gtest/gtest.h> + +using namespace std; +TEST(echo_builtin_test, simple_output) +{ + vector<string> args; + args.push_back("hello"); + args.push_back("world"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("hello world\n", test_output.str()); +} +TEST(echo_builtin_test, suppress_newline) +{ + vector<string> args; + args.push_back("-n"); + args.push_back("foo"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("foo",test_output.str()); +} +TEST(echo_builtin_test, enable_escape) +{ + vector<string> args; + args.push_back("-e"); + args.push_back("foo\\t"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("foo\t\n",test_output.str()); +} +TEST(echo_builtin_test, no_escape) +{ + vector<string> args; + args.push_back("foo\\t"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("foo\\t\n",test_output.str()); +} +TEST(echo_builtin_test, only_options) +{ + vector<string> args; + args.push_back("-n"); + args.push_back("foo"); + args.push_back("-e"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("foo -e",test_output.str()); +} +TEST(echo_builtin_test, only_options2) +{ + vector<string> args; + args.push_back("foo"); + args.push_back("-n"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("foo -n\n",test_output.str()); +} +TEST(echo_builtin_test, combined_options) +{ + vector<string> args; + args.push_back("-ne"); + args.push_back("fo\\to"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("fo\to",test_output.str()); +} +TEST(echo_builtin_test, combined_options2) +{ + vector<string> args; + args.push_back("-enE"); + args.push_back("fo\\to"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("fo\\to",test_output.str()); +} +TEST(echo_builtin_test, fake_options) +{ + vector<string> args; + args.push_back("-nea"); + args.push_back("fo\\to"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("-nea fo\\to\n",test_output.str()); +} +TEST(echo_builtin_test, combined_options_alternating_e) +{ + vector<string> args; + args.push_back("-enEeEe"); + args.push_back("fo\\to"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("fo\to",test_output.str()); +} +TEST(echo_builtin_test, coflicting_options) +{ + vector<string> args; + args.push_back("-e"); + args.push_back("-E"); + args.push_back("fo\\to"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("fo\\to\n",test_output.str()); +} +TEST(echo_builtin_test, coflicting_options2) +{ + vector<string> args; + args.push_back("-e"); + args.push_back("-E"); + args.push_back("-e"); + args.push_back("fo\\to"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("fo\to\n",test_output.str()); +} +TEST(echo_builtin_test, oct_escape) +{ + vector<string>args; + args.push_back("-e"); + args.push_back("\\0135"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("]\n", test_output.str()); +} +TEST(echo_builtin_test, hex_escape) +{ + vector<string>args; + args.push_back("-e"); + args.push_back("\\x57"); + stringstream test_output; + echo_builtin my_echo(test_output,cerr,cin); + my_echo.exec(args); + ASSERT_EQ("W\n", test_output.str()); +} + diff --git a/src/builtins/tests/run_tests.cpp b/src/builtins/tests/run_tests.cpp new file mode 100644 index 0000000..0f1fea3 --- /dev/null +++ b/src/builtins/tests/run_tests.cpp @@ -0,0 +1,32 @@ +/* +Copyright 2010 Nathan Eloe + +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 run_tests.cpp +/// \brief runs unit tests for builtin bash functionality +/// + +#include<gtest/gtest.h> + +/// +/// \brief runs all the unit tests linked to this file +/// +int main(int argc, char* argv[]) +{ + ::testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS(); +} |