diff --git a/cmake.h.in b/cmake.h.in index 27dfdd908..73d03b535 100644 --- a/cmake.h.in +++ b/cmake.h.in @@ -61,6 +61,9 @@ /* Found timegm */ #cmakedefine HAVE_TIMEGM +/* Found st.st_birthtime struct member */ +#cmakedefine HAVE_ST_BIRTHTIME + /* Found get_current_dir_name */ #cmakedefine HAVE_GET_CURRENT_DIR_NAME diff --git a/src/A3.cpp b/src/A3.cpp index 625af770b..4b9e73f8e 100644 --- a/src/A3.cpp +++ b/src/A3.cpp @@ -1575,6 +1575,7 @@ bool A3::is_dom (Nibbler& n, Arg& arg) //////////////////////////////////////////////////////////////////////////////// bool A3::is_date (Nibbler& n, std::string& result) { +#ifdef NIBBLER_FEATURE_DATE std::string date_format = context.config.get ("dateformat"); std::string::size_type start = n.save (); time_t t; @@ -1586,6 +1587,7 @@ bool A3::is_date (Nibbler& n, std::string& result) } n.restore (); +#endif return false; } diff --git a/src/Color.h b/src/Color.h index 7d4e90b87..1f7a0bcb3 100644 --- a/src/Color.h +++ b/src/Color.h @@ -30,6 +30,8 @@ #include +#define FEATURE_COLOR 1 + //////////////////////////////////////////////////////////////////////////////// #define _COLOR_INVERSE 0x00400000 // Inverse attribute. #define _COLOR_256 0x00200000 // 256-color mode. diff --git a/src/Date.cpp b/src/Date.cpp index 29aaff2eb..114662f24 100644 --- a/src/Date.cpp +++ b/src/Date.cpp @@ -133,8 +133,10 @@ Date::Date (const std::string& input, const std::string& format /* = "m/d/Y" */) // Parse a formatted date. Nibbler n (input); +#ifdef NIBBLER_FEATURE_DATE if (n.getDate (format, _t) && n.depleted ()) return; +#endif // Parse an ISO date. if (n.getDateISO (_t) && n.depleted ()) diff --git a/src/Date.h b/src/Date.h index 4bd0342f8..dfd28186c 100644 --- a/src/Date.h +++ b/src/Date.h @@ -29,9 +29,9 @@ #define INCLUDED_DATE #include +#include #include - class Date; class Date diff --git a/src/Directory.cpp b/src/Directory.cpp index b666d11b3..481a6427c 100644 --- a/src/Directory.cpp +++ b/src/Directory.cpp @@ -91,13 +91,13 @@ bool Directory::create () } //////////////////////////////////////////////////////////////////////////////// -bool Directory::remove () +bool Directory::remove () const { return remove_directory (_data); } //////////////////////////////////////////////////////////////////////////////// -bool Directory::remove_directory (const std::string& dir) +bool Directory::remove_directory (const std::string& dir) const { DIR* dp = opendir (dir.c_str ()); if (dp != NULL) @@ -191,6 +191,12 @@ bool Directory::up () return false; } +//////////////////////////////////////////////////////////////////////////////// +bool Directory::cd () const +{ + return chdir (_data.c_str ()) == 0 ? true : false; +} + //////////////////////////////////////////////////////////////////////////////// void Directory::list ( const std::string& base, diff --git a/src/Directory.h b/src/Directory.h index dd03c21ad..a0dd33acf 100644 --- a/src/Directory.h +++ b/src/Directory.h @@ -43,17 +43,18 @@ public: Directory& operator= (const Directory&); virtual bool create (); - virtual bool remove (); + virtual bool remove () const; std::vector list (); std::vector listRecursive (); static std::string cwd (); bool up (); + bool cd () const; private: void list (const std::string&, std::vector &, bool); - bool remove_directory (const std::string&); + bool remove_directory (const std::string&) const; }; #endif diff --git a/src/E9.cpp b/src/E9.cpp index fe77b682c..1119a0383 100644 --- a/src/E9.cpp +++ b/src/E9.cpp @@ -25,6 +25,7 @@ // //////////////////////////////////////////////////////////////////////////////// +#include #include #include #include diff --git a/src/File.cpp b/src/File.cpp index e44a8d730..e7b5abea7 100644 --- a/src/File.cpp +++ b/src/File.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -101,7 +102,7 @@ bool File::create () } //////////////////////////////////////////////////////////////////////////////// -bool File::remove () +bool File::remove () const { return unlink (_data.c_str ()) == 0 ? true : false; } @@ -335,6 +336,30 @@ time_t File::mtime () const return 0; } +//////////////////////////////////////////////////////////////////////////////// +time_t File::ctime () const +{ + struct stat s; + if (!stat (_data.c_str (), &s)) + return s.st_ctime; + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +time_t File::btime () const +{ + struct stat s; + if (!stat (_data.c_str (), &s)) +#ifdef HAVE_ST_BIRTHTIME + return s.st_birthtime; +#else + return s.st_ctime; +#endif + + return 0; +} + //////////////////////////////////////////////////////////////////////////////// bool File::create (const std::string& name) { diff --git a/src/File.h b/src/File.h index d6d7b4c49..ffbec1ab9 100644 --- a/src/File.h +++ b/src/File.h @@ -46,7 +46,7 @@ public: File& operator= (const File&); virtual bool create (); - virtual bool remove (); + virtual bool remove () const; bool open (); bool openAndLock (); @@ -69,6 +69,8 @@ public: virtual mode_t mode (); virtual size_t size () const; virtual time_t mtime () const; + virtual time_t ctime () const; + virtual time_t btime () const; static bool create (const std::string&); static std::string read (const std::string&); diff --git a/src/Nibbler.cpp b/src/Nibbler.cpp index bc0c5d8af..33b32c5ed 100644 --- a/src/Nibbler.cpp +++ b/src/Nibbler.cpp @@ -294,6 +294,69 @@ bool Nibbler::getDigit (int& result) return false; } +//////////////////////////////////////////////////////////////////////////////// +bool Nibbler::getDigit6 (int& result) +{ + std::string::size_type i = _cursor; + if (i < _length && + _length - i >= 6) + { + if (isdigit (_input[i + 0]) && + isdigit (_input[i + 1]) && + isdigit (_input[i + 2]) && + isdigit (_input[i + 3]) && + isdigit (_input[i + 4]) && + isdigit (_input[i + 5])) + { + result = strtoimax (_input.substr (_cursor, 6).c_str (), NULL, 10); + _cursor += 6; + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +bool Nibbler::getDigit4 (int& result) +{ + std::string::size_type i = _cursor; + if (i < _length && + _length - i >= 4) + { + if (isdigit (_input[i + 0]) && + isdigit (_input[i + 1]) && + isdigit (_input[i + 2]) && + isdigit (_input[i + 3])) + { + result = strtoimax (_input.substr (_cursor, 4).c_str (), NULL, 10); + _cursor += 4; + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +bool Nibbler::getDigit2 (int& result) +{ + std::string::size_type i = _cursor; + if (i < _length && + _length - i >= 2) + { + if (isdigit (_input[i + 0]) && + isdigit (_input[i + 1])) + { + result = strtoimax (_input.substr (_cursor, 2).c_str (), NULL, 10); + _cursor += 2; + return true; + } + } + + return false; +} + //////////////////////////////////////////////////////////////////////////////// bool Nibbler::getInt (int& result) { @@ -716,7 +779,6 @@ bool Nibbler::getDateISO (time_t& t) } //////////////////////////////////////////////////////////////////////////////// -#ifdef NIBBLER_FEATURE_DATE // Parse the longest integer using the next 'limit' characters of 'result' // following position 'i' (when strict is true, the number of digits must be // equal to limit). @@ -758,6 +820,7 @@ bool Nibbler::parseDigits(std::string::size_type& i, } //////////////////////////////////////////////////////////////////////////////// +#ifdef NIBBLER_FEATURE_DATE bool Nibbler::getDate (const std::string& format, time_t& t) { std::string::size_type i = _cursor; diff --git a/src/Nibbler.h b/src/Nibbler.h index eeb6bf276..dbf2304a0 100644 --- a/src/Nibbler.h +++ b/src/Nibbler.h @@ -28,6 +28,8 @@ #ifndef INCLUDED_NIBBLER #define INCLUDED_NIBBLER +#include + #define NIBBLER_FEATURE_DATE //#undef NIBBLER_FEATURE_DATE @@ -63,6 +65,9 @@ public: bool getN (const int, std::string&); bool getQuoted (char, std::string&, bool quote = false); bool getDigit (int&); + bool getDigit6 (int&); + bool getDigit4 (int&); + bool getDigit2 (int&); bool getInt (int&); bool getHex (int&); bool getUnsignedInt (int&); @@ -76,8 +81,8 @@ public: bool getUUID (std::string&); bool getPartialUUID (std::string&); bool getDateISO (time_t&); -#ifdef NIBBLER_FEATURE_DATE bool parseDigits(std::string::size_type&, int&, unsigned int, bool strict = true); +#ifdef NIBBLER_FEATURE_DATE bool getDate (const std::string&, time_t&); #endif bool getOneOf (const std::vector &, std::string&); diff --git a/src/Path.cpp b/src/Path.cpp index a934cbed8..80786bb3b 100644 --- a/src/Path.cpp +++ b/src/Path.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -88,6 +89,13 @@ bool Path::operator== (const Path& other) return _data == other._data; } +//////////////////////////////////////////////////////////////////////////////// +Path& Path::operator+= (const std::string& dir) +{ + _data += "/" + dir; + return *this; +} + //////////////////////////////////////////////////////////////////////////////// Path::operator std::string () const { @@ -153,7 +161,7 @@ bool Path::is_directory () const //////////////////////////////////////////////////////////////////////////////// bool Path::is_absolute () const { - if (_data.length () && _data.substr (0, 1) == "/") + if (_data.length () && _data[0] == '/') return true; return false; @@ -197,6 +205,8 @@ bool Path::rename (const std::string& new_name) // ~ --> /home/user // ~foo/x --> /home/foo/s // ~/x --> /home/foo/x +// ./x --> $PWD/x +// x --> $PWD/x std::string Path::expand (const std::string& in) { std::string copy = in; @@ -234,6 +244,23 @@ std::string Path::expand (const std::string& in) } } + // Relative paths + else if (in.length () > 2 && + in.substr (0, 2) == "./") + { + char buf[PATH_MAX]; + getcwd (buf, PATH_MAX - 1); + copy = std::string (buf) + "/" + in.substr (2); + } + else if (in.length () > 1 && + in[0] != '.' && + in[0] != '/') + { + char buf[PATH_MAX]; + getcwd (buf, PATH_MAX - 1); + copy = std::string (buf) + "/" + in; + } + return copy; } diff --git a/src/Path.h b/src/Path.h index 4dabb1266..6436ab3d3 100644 --- a/src/Path.h +++ b/src/Path.h @@ -28,7 +28,6 @@ #ifndef INCLUDED_PATH #define INCLUDED_PATH -#include #include #include @@ -42,6 +41,7 @@ public: Path& operator= (const Path&); bool operator== (const Path&); + Path& operator+= (const std::string&); operator std::string () const; std::string name () const; diff --git a/src/Timer.cpp b/src/Timer.cpp index c106a7408..ad974f36c 100644 --- a/src/Timer.cpp +++ b/src/Timer.cpp @@ -112,3 +112,40 @@ void Timer::subtract (unsigned long value) } //////////////////////////////////////////////////////////////////////////////// +HighResTimer::HighResTimer () +{ + _start.tv_sec = 0; + _start.tv_usec = 0; + + _stop.tv_sec = 0; + _stop.tv_usec = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +HighResTimer::~HighResTimer () +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void HighResTimer::start () +{ + gettimeofday (&_start, NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +void HighResTimer::stop () +{ + gettimeofday (&_stop, NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +double HighResTimer::total () const +{ + if (_stop.tv_sec > 0 || _stop.tv_usec > 0) + return (_stop.tv_sec - _start.tv_sec) + + (_stop.tv_usec - _start.tv_usec) / 1000000.0; + + return 0.0; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/Timer.h b/src/Timer.h index 5218441a6..ce8843c1d 100644 --- a/src/Timer.h +++ b/src/Timer.h @@ -31,6 +31,7 @@ #include #include +// Timer is a scope-activated timer that dumps to std::cout at end of scope. class Timer { public: @@ -52,6 +53,25 @@ private: unsigned long _total; }; +// HighResTimer is a stop watch with microsecond resolution. +class HighResTimer +{ +public: + HighResTimer (); + ~HighResTimer (); + HighResTimer (const HighResTimer&); + HighResTimer& operator= (const HighResTimer&); + + void start (); + void stop (); + double total () const; + +private: + struct timeval _start; + struct timeval _stop; +}; + + #endif //////////////////////////////////////////////////////////////////////////////// diff --git a/test/color.err.t b/test/color.err.t index f1224c9ec..83390565f 100755 --- a/test/color.err.t +++ b/test/color.err.t @@ -46,8 +46,8 @@ if (open my $fh, '>', 'color.rc') # Test the errors colors my $output = qx{../src/task rc:color.rc rc.debug:on add due:__ 2>&1 >/dev/null}; like ($output, qr/^\033\[33mThe\ duration\ '__'\ was\ not\ recognized\ as\ valid,\ with\ correct\ units\ like\ '3days'\.\033\[0m$/xms, 'color.error'); -like ($output, qr/^\033\[32mTimer\ Config::load\ \(color.rc\) .* \033\[0m$/xms, 'color.debug'); -like ($output, qr/^\033\[34mUsing\ alternate\ .taskrc\ file\ color.rc\033\[0m$/xms, 'color.header'); +like ($output, qr/^\033\[32mTimer\ Config::load\ \(.+color.rc\) .* \033\[0m$/xms, 'color.debug'); +like ($output, qr/^\033\[34mUsing\ alternate\ .taskrc\ file\ /xms, 'color.header'); like ($output, qr/^\033\[31mConfiguration\ override\ rc.debug:on\033\[0m$/xms, 'color.footnote'); # Cleanup. diff --git a/test/directory.t.cpp b/test/directory.t.cpp index 444272268..6408dcad2 100644 --- a/test/directory.t.cpp +++ b/test/directory.t.cpp @@ -51,17 +51,17 @@ int main (int argc, char** argv) // Directory (const Directory&); Directory d3 (d2); - t.is (d3._data, "tmp", "Directory (Directory&)"); + t.is (d3._data, Directory::cwd () + "/tmp", "Directory (Directory&)"); // Directory (const std::string&); Directory d4 ("tmp/test_directory"); // Directory& operator= (const Directory&); Directory d5 = d4; - t.is (d5._data, "tmp/test_directory", "Directory::operator="); + t.is (d5._data, Directory::cwd () + "/tmp/test_directory", "Directory::operator="); // operator (std::string) const; - t.is ((std::string) d3, "tmp", "Directory::operator (std::string) const"); + t.is ((std::string) d3, Directory::cwd () + "/tmp", "Directory::operator (std::string) const"); // virtual bool create (); t.ok (d5.create (), "Directory::create tmp/test_directory"); @@ -77,15 +77,15 @@ int main (int argc, char** argv) std::vector files = d5.list (); std::sort (files.begin (), files.end ()); t.is ((int)files.size (), 2, "Directory::list 1 file"); - t.is (files[0], "tmp/test_directory/dir", "file[0] is tmp/test_directory/dir"); - t.is (files[1], "tmp/test_directory/f0", "file[1] is tmp/test_directory/f0"); + t.is (files[0], Directory::cwd () + "/tmp/test_directory/dir", "file[0] is tmp/test_directory/dir"); + t.is (files[1], Directory::cwd () + "/tmp/test_directory/f0", "file[1] is tmp/test_directory/f0"); // std::vector listRecursive (); files = d5.listRecursive (); std::sort (files.begin (), files.end ()); t.is ((int)files.size (), 2, "Directory::list 1 file"); - t.is (files[0], "tmp/test_directory/dir/f1", "file is tmp/test_directory/dir/f1"); - t.is (files[1], "tmp/test_directory/f0", "file is tmp/test_directory/f0"); + t.is (files[0], Directory::cwd () + "/tmp/test_directory/dir/f1", "file is tmp/test_directory/dir/f1"); + t.is (files[1], Directory::cwd () + "/tmp/test_directory/f0", "file is tmp/test_directory/f0"); // virtual bool remove (); t.ok (File::remove (d5._data + "/f0"), "File::remove tmp/test_directory/f0"); diff --git a/test/feature.1013.t b/test/feature.1013.t index 178368ade..6c95e4516 100755 --- a/test/feature.1013.t +++ b/test/feature.1013.t @@ -49,9 +49,9 @@ like ($stderr, qr/^The duration '__' was not recognized as valid, with correct u # Check that headers are sent to standard error $stdout = qx{../src/task rc:outerr.rc list 2> /dev/null}; -unlike ($stdout, qr/^Using alternate .taskrc file outerr.rc$/ms, 'Headers are not sent to stdout'); +unlike ($stdout, qr/^Using alternate .taskrc file .+outerr.rc$/ms, 'Headers are not sent to stdout'); $stderr = qx{../src/task rc:outerr.rc list 2>&1 >/dev/null}; -like ($stderr, qr/^Using alternate .taskrc file outerr.rc$/ms, 'Headers are sent to stderr'); +like ($stderr, qr/^Using alternate .taskrc file .+outerr.rc$/ms, 'Headers are sent to stderr'); # Check that footnotes are sent to standard error $stdout = qx{../src/task rc:outerr.rc rc.debug:on list 2> /dev/null}; @@ -61,9 +61,9 @@ like ($stderr, qr/^Configuration override rc.debug:on$/ms, 'Footnotes are sent t # Check that debugs are sent to standard error $stdout = qx{../src/task rc:outerr.rc rc.debug:on list 2> /dev/null}; -unlike ($stdout, qr/^Timer Config::load \(outerr.rc\) /ms, 'Debugs are not sent to stdout'); +unlike ($stdout, qr/^Timer Config::load \(.+outerr.rc\) /ms, 'Debugs are not sent to stdout'); $stderr = qx{../src/task rc:outerr.rc rc.debug:on list 2>&1 >/dev/null}; -like ($stderr, qr/^Timer Config::load \(outerr.rc\) /ms, 'Debugs are sent to stderr'); +like ($stderr, qr/^Timer Config::load \(.+outerr.rc\) /ms, 'Debugs are sent to stderr'); # Cleanup. unlink qw(pending.data completed.data undo.data backlog.data outerr.rc); diff --git a/test/file.t.cpp b/test/file.t.cpp index 4bddc2df4..e6684658b 100644 --- a/test/file.t.cpp +++ b/test/file.t.cpp @@ -47,7 +47,7 @@ int main (int argc, char** argv) t.ok (File::remove ("tmp/file.t.txt"), "File::remove tmp/file.t.txt good"); // operator (std::string) const; - t.is ((std::string) f6, "tmp/file.t.txt", "File::operator (std::string) const"); + t.is ((std::string) f6, Directory::cwd () + "/tmp/file.t.txt", "File::operator (std::string) const"); t.ok (File::create ("tmp/file.t.create"), "File::create tmp/file.t.create good"); t.ok (File::remove ("tmp/file.t.create"), "File::remove tmp/file.t.create good"); @@ -56,7 +56,7 @@ int main (int argc, char** argv) t.is (f6.name (), "file.t.txt", "File::basename tmp/file.t.txt --> file.t.txt"); // dirname (std::string) const; - t.is (f6.parent (), "tmp", "File::dirname tmp/file.t.txt --> /tmp"); + t.is (f6.parent (), Directory::cwd () + "/tmp", "File::dirname tmp/file.t.txt --> tmp"); // bool rename (const std::string&); File f7 ("tmp/file.t.2.txt"); @@ -64,7 +64,7 @@ int main (int argc, char** argv) f7.close (); t.ok (f7.rename ("tmp/file.t.3.txt"), "File::rename did not fail"); - t.is (f7._data, "tmp/file.t.3.txt", "File::rename stored new name"); + t.is (f7._data, Directory::cwd () + "/tmp/file.t.3.txt", "File::rename stored new name"); t.ok (f7.exists (), "File::rename new file exists"); t.ok (f7.remove (), "File::remove tmp/file.t.3.txt good"); t.notok (f7.exists (), "File::remove new file no longer exists"); diff --git a/test/nibbler.t.cpp b/test/nibbler.t.cpp index 68bb9431f..9f4326a82 100644 --- a/test/nibbler.t.cpp +++ b/test/nibbler.t.cpp @@ -39,15 +39,15 @@ int main (int argc, char** argv) { #ifdef NIBBLER_FEATURE_DATE #ifdef NIBBLER_FEATURE_REGEX - UnitTest t (396); + UnitTest t (402); #else - UnitTest t (372); + UnitTest t (378); #endif #else #ifdef NIBBLER_FEATURE_REGEX - UnitTest t (346); + UnitTest t (338); #else - UnitTest t (322); + UnitTest t (314); #endif #endif @@ -258,6 +258,24 @@ int main (int argc, char** argv) t.is (i, 2, " '2x' : getDigit () -> 2"); t.notok (n.getDigit (i), " 'x' : getDigit () -> false"); + // bool getDigit6 (int&); + t.diag ("Nibbler::getDigit6"); + n = Nibbler ("654321"); + t.ok (n.getDigit6 (i), " 654321 : getDigit6 () -> true"); + t.is (i, 654321, " 654321 : getDigit6 () -> 654321"); + + // bool getDigit4 (int&); + t.diag ("Nibbler::getDigit4"); + n = Nibbler ("4321"); + t.ok (n.getDigit4 (i), " 4321 : getDigit4 () -> true"); + t.is (i, 4321, " 4321 : getDigit4 () -> 4321"); + + // bool getDigit2 (int&); + t.diag ("Nibbler::getDigit2"); + n = Nibbler ("21"); + t.ok (n.getDigit2 (i), " 21 : getDigit2 () -> true"); + t.is (i, 21, " 21 : getDigit2 () -> 21"); + // bool getInt (int&); t.diag ("Nibbler::getInt"); n = Nibbler ("123 -4"); diff --git a/test/path.t.cpp b/test/path.t.cpp index 026938f8a..08e9a71a8 100644 --- a/test/path.t.cpp +++ b/test/path.t.cpp @@ -27,6 +27,7 @@ #include #include +#include #include Context context; @@ -37,11 +38,11 @@ int main (int argc, char** argv) // Path (); Path p0; - t.ok (p0._data == "", "Path::Path"); + t.is (p0._data, "", "Path::Path"); // Path (const Path&); Path p1 = Path ("foo"); - t.ok (p1._data == "foo", "Path::operator="); + t.is (p1._data, Directory::cwd () + "/foo", "Path::operator="); // Path (const std::string&); Path p2 ("~"); @@ -106,7 +107,7 @@ int main (int argc, char** argv) // bool is_absolute () const; t.notok (p0.is_absolute (), "'' !is_absolute"); - t.notok (p1.is_absolute (), "foo !is_absolute"); + t.ok (p1.is_absolute (), "foo is_absolute"); t.ok (p2.is_absolute (), "~ is_absolute (after expansion)"); t.ok (p3.is_absolute (), "/tmp is_absolute"); t.ok (p4.is_absolute (), "/a/b/c/file.ext is_absolute"); diff --git a/test/problems b/test/problems index 7dfada892..8f8586901 100755 --- a/test/problems +++ b/test/problems @@ -1,4 +1,4 @@ -#! /usr/bin/env perl +#!/usr/bin/env perl use strict; use warnings; diff --git a/test/shadow.t b/test/shadow.t index 602b4db01..4c13de4e2 100755 --- a/test/shadow.t +++ b/test/shadow.t @@ -43,16 +43,16 @@ if (open my $fh, '>', 'shadow.rc') } my $output = qx{../src/task rc:shadow.rc add one 2>&1 >/dev/null}; -like ($output, qr/\[Shadow file '\.\/shadow\.txt' updated\.\]/, 'shadow file updated on add'); +like ($output, qr/\[Shadow file '.+\/shadow\.txt' updated\.\]/, 'shadow file updated on add'); $output = qx{../src/task rc:shadow.rc list 2>&1 >/dev/null}; -unlike ($output, qr/\[Shadow file '\.\/shadow\.txt' updated\.\]/, 'shadow file not updated on list'); +unlike ($output, qr/\[Shadow file '.+\/shadow\.txt' updated\.\]/, 'shadow file not updated on list'); $output = qx{../src/task rc:shadow.rc 1 delete 2>&1 >/dev/null}; -like ($output, qr/\[Shadow file '\.\/shadow\.txt' updated\.\]/, 'shadow file updated on delete'); +like ($output, qr/\[Shadow file '.+\/shadow\.txt' updated\.\]/, 'shadow file updated on delete'); $output = qx{../src/task rc:shadow.rc list 2>&1 >/dev/null}; -unlike ($output, qr/\[Shadow file '\.\/shadow\.txt' updated\.\]/, 'shadow file not updated on list'); +unlike ($output, qr/\[Shadow file '.+\/shadow\.txt' updated\.\]/, 'shadow file not updated on list'); # Inspect the shadow file. my $file = slurp ('./shadow.txt');