diff --git a/src/builtins/test.cpp b/src/builtins/test.cpp index 4f3d2f0ea..06c4b87a8 100644 --- a/src/builtins/test.cpp +++ b/src/builtins/test.cpp @@ -65,6 +65,10 @@ enum token_t { test_string_equal, // "=", true if strings are identical test_string_not_equal, // "!=", true if strings are not identical + test_file_newer, // f1 -nt f2, true if f1 exists and is newer than f2, or there is no f2 + test_file_older, // f1 -ot f2, true if f2 exists and f1 does not, or f1 is older than f2 + test_file_same, // f1 -ef f2, true if f1 and f2 exist and refer to same file + test_number_equal, // "-eq", true if numbers are equal test_number_not_equal, // "-ne", true if numbers are not equal test_number_greater, // "-gt", true if first number is larger than second @@ -152,6 +156,9 @@ static const token_info_t *token_for_string(const wcstring &str) { {L"-z", {test_string_z, UNARY_PRIMARY}}, {L"=", {test_string_equal, BINARY_PRIMARY}}, {L"!=", {test_string_not_equal, BINARY_PRIMARY}}, + {L"-nt", {test_file_newer, BINARY_PRIMARY}}, + {L"-ot", {test_file_older, BINARY_PRIMARY}}, + {L"-ef", {test_file_same, BINARY_PRIMARY}}, {L"-eq", {test_number_equal, BINARY_PRIMARY}}, {L"-ne", {test_number_not_equal, BINARY_PRIMARY}}, {L"-gt", {test_number_greater, BINARY_PRIMARY}}, @@ -742,6 +749,15 @@ static bool binary_primary_evaluate(test_expressions::token_t token, const wcstr case test_string_not_equal: { return left != right; } + case test_file_newer: { + return file_id_for_path(right).older_than(file_id_for_path(left)); + } + case test_file_older: { + return file_id_for_path(left).older_than(file_id_for_path(right)); + } + case test_file_same: { + return file_id_for_path(left) == file_id_for_path(right); + } case test_number_equal: { return parse_number(left, &ln, errors) && parse_number(right, &rn, errors) && ln.compare(rn) == 0; diff --git a/src/wutil.cpp b/src/wutil.cpp index 677e77cb3..ccfa05fbf 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -868,6 +868,22 @@ static int compare(T a, T b) { return 0; } +/// \return true if \param rhs has higher mtime seconds than this file_id_t. +/// If identical, nanoseconds are compared. +bool file_id_t::older_than(const file_id_t &rhs) const { + int ret = compare(mod_seconds, rhs.mod_seconds); + if (!ret) ret = compare(mod_nanoseconds, rhs.mod_nanoseconds); + switch (ret) { + case -1: + return true; + case 1: + case 0: + return false; + default: + DIE("unreachable"); + } +} + int file_id_t::compare_file_id(const file_id_t &rhs) const { // Compare each field, stopping when we get to a non-equal field. int ret = 0; diff --git a/src/wutil.h b/src/wutil.h index accb4e189..80e6c258c 100644 --- a/src/wutil.h +++ b/src/wutil.h @@ -145,9 +145,14 @@ struct file_id_t { dev_t device{static_cast(-1LL)}; ino_t inode{static_cast(-1LL)}; uint64_t size{static_cast(-1LL)}; - time_t change_seconds{-1}; + /* some platforms handle negative time_t + values to represent WW1-era dates, initialize ancient + for the sake of comparisons. + tv_nsec's meaningful values are REALLY [0, 999999999] + this nanosecond component we'll initialize at 0. */ + time_t change_seconds{std::numeric_limits::min()}; long change_nanoseconds{-1}; - time_t mod_seconds{-1}; + time_t mod_seconds{std::numeric_limits::min()}; long mod_nanoseconds{-1}; constexpr file_id_t() = default; @@ -159,7 +164,7 @@ struct file_id_t { bool operator<(const file_id_t &rhs) const; static file_id_t from_stat(const struct stat &buf); - + bool older_than(const file_id_t &rhs) const; wcstring dump() const; private: