// file : tests/test/script/runner/driver.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include <limits> // numeric_limits #include <string> #include <cstdlib> // abort() #include <cassert> #include <ostream> // endl, *bit #include <istream> // istream::traits_type::eof() #include <iostream> #include <exception> #include <libbutl/path.mxx> #include <libbutl/optional.mxx> #include <libbutl/fdstream.mxx> #include <libbutl/filesystem.mxx> using namespace std; using namespace butl; // Call itself recursively causing stack overflow. Parameterized to avoid // "stack overflow" warning. // static void stack_overflow (bool overflow) { if (overflow) stack_overflow (true); } int main (int argc, char* argv[]) { using butl::optional; // Usage: driver [-i <int>] (-o <string>)* (-e <string>)* (-f <file>)* // (-d <dir>)* [(-t (a|m|s|z)) | (-s <int>)] // // Execute actions specified by -i, -o, -e, -f, -d options in the order as // they appear on the command line. After that terminate abnormally if -t // option is provided, otherwise exit normally with the status specified by // -s option (0 by default). // // -i <fd> // Forward STDIN data to the standard stream denoted by the file // descriptor. Read and discard if 0. // // -o <string> // Print the line to STDOUT. // // -e <string> // Print the line to STDERR. // // -f <path> // Create an empty file with the path specified. // // -d <path> // Create a directory with the path specified. Create parent directories // if required. // // -t <method> // Abnormally terminate itself using one of the following methods: // // a - call abort() // m - dereference null-pointer // s - cause stack overflow using infinite function call recursion // z - divide integer by zero // // -s <int> // Exit normally with the status specified. The default status is 0. // int ifd (3); optional<int> status; char aterm ('\0'); // Abnormal termination method. cin.exceptions (ostream::failbit | ostream::badbit); cout.exceptions (ostream::failbit | ostream::badbit); cerr.exceptions (ostream::failbit | ostream::badbit); for (int i (1); i < argc; ++i) { string o (argv[i++]); assert (i < argc); string v (argv[i]); auto toi = [] (const string& s) -> int { int r (-1); try { size_t n; r = stoi (s, &n); assert (n == s.size ()); } catch (const exception&) { assert (false); } return r; }; if (o == "-i") { assert (ifd == 3); // Make sure is not set yet. ifd = toi (v); assert (ifd >= 0 && ifd < 3); if (ifd == 0) cin.ignore (numeric_limits<streamsize>::max ()); else if (cin.peek () != istream::traits_type::eof ()) { ostream& o (ifd == 1 ? cout : cerr); o << cin.rdbuf (); o.flush (); } } else if (o == "-o") { cout << v << endl; } else if (o == "-e") { cerr << v << endl; } else if (o == "-f") { ofdstream os (v); os.close (); } else if (o == "-d") { try_mkdir_p (dir_path (v)); } else if (o == "-t") { assert (aterm == '\0' && !status); // Make sure exit method is not set. assert (v.size () == 1 && v.find_first_of ("amsz") != string::npos); aterm = v[0]; } else if (o == "-s") { assert (!status && aterm == '\0'); // Make sure exit method is not set. status = toi (v); } else assert (false); } switch (aterm) { case 'a': abort (); break; case 'm': { int* p (nullptr); *p = 0; break; } case 's': stack_overflow (true); break; case 'z': { int z (0); z /= z; break; } } return status ? *status : 0; }