#include #include #include #include #include #include "JLang/JNullType.hh" #include "JLang/JEquation.hh" #include "JLang/JEquationFacet.hh" #include "JLang/JException.hh" #include "JLang/JRedirectStream.hh" #include "JLang/JLangToolkit.hh" #include "JSystem/JSystemToolkit.hh" #include "Jeep/JParser.hh" /** * \author mdejong */ namespace { using namespace JPP; const char OPTIONS_SEPARATOR = ','; //!< options separator /** * Auxiliary class for environment variable. */ template struct JParameter { /** * Constructor. * * \param variable environment variable */ JParameter(const std::string& variable) : name (variable), value() {} /** * Equals operator. * * \param first first parameter * \param second second parameter * \return true if first parameter equals second parameter; else false */ friend bool operator==(const JParameter& first, const JParameter& second) { return (first.name == second.name && first.value == second.value); } /** * Stream input. * * Note that only the value of the parameter is read. * * \param in input stream * \return input stream */ inline std::istream& read(std::istream& in) { return in >> this->value; } /** * Stream output. * * Note that only the value of the parameter is written. * * \param out output stream * \return output stream */ inline std::ostream& write(std::ostream& out) const { return out << this->value; } std::string name; //!< environment variable name T value; //!< environment variable value }; /** * Template specialisation of JParameter::read. * * Note that the complete text until the end-of-line is read, including possible white spaces. * * \param in input stream * \return input stream */ template<> inline std::istream& JParameter::read(std::istream& in) { return std::getline(in, this->value); } /** * Template specialisation of JParameter::read. * * Note that no data are read; only the value is set to true * * \param in input stream * \return input stream */ template<> inline std::istream& JParameter::read(std::istream& in) { this->value = true; return in; } /** * Template specialisation of JParameter::write. * * \param out output stream * \return output stream */ template<> inline std::ostream& JParameter::write(std::ostream& out) const { return out << (this->value ? "1" : "0"); } /** * Stream input of parameter. * * \param in input stream * \param parameter parameter * \return input stream */ template inline std::istream& operator>>(std::istream& in, JParameter& parameter) { return parameter.read(in); } /** * Stream output of parameter. * * \param out output stream * \param parameter parameter * \return output stream */ template inline std::ostream& operator<<(std::ostream& out, const JParameter& parameter) { return parameter.write(out); } /** * Base class for environment variables. */ template struct JShellParser; /** * Base class for environment variables. * * This class is used to redirect the standard output and error so that * - output is parsed via JSYSTEM::gprint; and * - termination is handled via JSYSTEM::gexit. */ template<> struct JShellParser : public JParser<> { typedef JParser<>::key_type key_type; /** * Default constructor. */ JShellParser() : __terminate__(false), __exit_code__(0) {} /** * Terminate. * * \param status exit status */ virtual void terminate(const int status) { __terminate__ = true; __exit_code__ = status; } /** * Parse the program's command line options. * * \param args argument list * \return argument list */ JArgs operator()(const JArgs& args) { using namespace std; using namespace JPP; ostringstream os; { JRedirectStream rs_out(cout, os); JRedirectStream rs_err(cerr, os); if (rs_out && rs_err) { static_cast&>(*this)(args); } } gprint(os.str()); if (__terminate__) { gexit(__exit_code__); } return JArgs(); } private: bool __terminate__; int __exit_code__; }; /** * Base class for environment variables. * * This class includes the data to be evaluated to shell variables in the end. */ template struct JShellParser : public JShellParser<> { typedef JShellParser<>::key_type key_type; typedef JParameter parameter_type; typedef std::list data_type; /** * Evaluate environment to shell commands. */ void evaluate() const { for (typename data_type::const_iterator i = this->data.begin(); i != this->data.end(); ++i) { std::ostringstream os; os << *i; set_variable(i->name, os.str()); } } protected: data_type data; //!< internal memory }; /** * Parser for environment variables. */ template struct JEnvironment; /** * Template specialisation of JEnvironment for regular environment variables. */ template<> struct JEnvironment : public JShellParser { typedef JShellParser::key_type key_type; typedef JShellParser::parameter_type parameter_type; /** * Default constructor. */ JEnvironment() {} /** * Stream input. * * \param in input stream * \param environment environment * \return input stream */ friend inline std::istream& operator>>(std::istream& in, JEnvironment& environment) { using namespace std; using namespace JPP; static const JEquationFacet facet(JEquationParameters("=", ";\n", "./", "#")); char c; key_type key; JEquation equation; if (!has_facet(in.getloc())) { in.imbue(locale(in.getloc(), facet.clone())); } if (in >> c >> key >> equation) { if (c == '-' && !equation.getKey().empty()) { environment.data.push_back(parameter_type(equation.getKey())); parameter_type& parameter = environment.data.back(); if (facet.isSeparator(equation.getSeparator())) { // read possible values istringstream is(equation.getValue()); vector possible_values; for (parameter_type buffer(parameter.name); getline(is, buffer.value, OPTIONS_SEPARATOR); ) { buffer.value = trim(buffer.value); possible_values.push_back(buffer); } environment[key] = JPARSER::getOption(parameter, equation.getKey(), possible_values.begin(), possible_values.end()); } else { environment[key] = JPARSER::getOption(parameter, equation.getKey()); } } else { THROW(JParseError, "JEnvironment: error parsing " << c << " <" << equation.getKey() << ">"); } } return in; } }; /** * Template specialisation of JEnvironment for boolean environment variables. */ template<> struct JEnvironment : public JShellParser { typedef JShellParser::key_type key_type; typedef JShellParser::parameter_type parameter_type; /** * Default constructor. */ JEnvironment() {} /** * Stream input. * * \param in input stream * \param environment environment * \return input stream */ friend inline std::istream& operator>>(std::istream& in, JEnvironment& environment) { using namespace std; using namespace JPP; static const JEquationFacet facet(JEquationParameters("=", ";\n", "./", "#")); char c; key_type key; JEquation equation; if (!has_facet(in.getloc())) { in.imbue(locale(in.getloc(), facet.clone())); } if (in >> c >> key >> equation) { if (c == '-' && !equation.getKey().empty()) { environment.data.push_back(parameter_type(equation.getKey())); parameter_type& parameter = environment.data.back(); if (facet.isSeparator(equation.getSeparator()) || equation.getValue() != "") { THROW(JParseError, "JEnvironment: invalid operation " << c << " <" << equation.getKey() << ">"); } else { environment[key] = JPARSER::getOption(parameter.value, equation.getKey()); } } else { THROW(JParseError, "JEnvironment: error parsing " << c << " <" << equation.getKey() << ">"); } } return in; } }; } /** * \file * * Auxiliary program to interface JPARSER::JParser to command line of shell scripts. * Syntax: *
 *   eval `JShellParser -o "-a  [= [, ]]" -- $*`
 * 
*/ int main(int argc, char **argv) { using namespace std; using namespace JPP; try { JEnvironment boolean; JEnvironment regular; JParser<> zap; zap['b'] = make_field(boolean) = JPARSER::initialised(); zap['o'] = make_field(regular) = JPARSER::initialised(); const JArgs options = zap(argc, argv); JShellParser<> buffer; buffer.join(boolean); buffer.join(regular); buffer(options); boolean.evaluate(); regular.evaluate(); } catch(const exception& error) { return gexit(2, error.what()); } }