aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am13
-rw-r--r--configure.ac3
-rw-r--r--m4/doxample.m4 (renamed from acinclude.m4)0
-rw-r--r--m4/gtest.m474
-rw-r--r--src/builtins/builtins.h28
-rw-r--r--src/builtins/echo_builtin.cpp171
-rw-r--r--src/builtins/echo_builtin.h83
-rw-r--r--src/builtins/tests/echo_tests.cpp173
-rw-r--r--src/builtins/tests/run_tests.cpp32
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();
+}