// file : tests/test/script/runner/driver.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #ifndef _WIN32 # include <thread> // this_thread::sleep_for() # include <chrono> #else # include <libbutl/win32-utility.hxx> #endif #include <limits> // numeric_limits #include <string> #include <cstdlib> // abort() #include <ostream> // endl, *bit #include <istream> // istream::traits_type::eof() #include <iostream> #include <exception> #include <libbutl/path.hxx> #include <libbutl/optional.hxx> #include <libbutl/fdstream.hxx> #include <libbutl/filesystem.hxx> #undef NDEBUG #include <cassert> 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>)* (-v <name>)* [(-t (a|m|s|z)) | (-s <int>)] // // Execute actions specified by -i, -o, -e, -f, -d, -v, and -l 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. // // -v <name> // If the specified variable is set then print its value to stdout and // the string '<none>' otherwise. // // -l <sec> // Sleep the specified number of seconds. // // -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 == "-v") { optional<string> var (getenv (v)); cout << (var ? *var : "<none>") << endl; } else if (o == "-l") { size_t t (toi (v)); // MinGW GCC 4.9 doesn't implement this_thread so use Win32 Sleep(). // #ifndef _WIN32 this_thread::sleep_for (chrono::seconds (t)); #else Sleep (static_cast<DWORD> (t * 1000)); #endif } 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; }