find: Add the -newer, -anewer and -cnewer options

These return true if the last modification time, last access time or
creation time of a file is greater than the given reference file.

If the `-L` option is in use and the given reference file is a
symbolic link then the timestamp of the file pointed to by the
symbolic link will be used.
This commit is contained in:
Tim Ledbetter 2023-08-29 14:31:34 +01:00 committed by Jelle Raaijmakers
parent 57a1d99cf4
commit 64bb5652a1
2 changed files with 71 additions and 0 deletions

View file

@ -52,6 +52,18 @@ space rounded up to the nearest whole unit.
* `-writable`: Checks if the file is writable by the current user.
* `-executable`: Checks if the file is executable, or directory is searchable,
by the current user.
* `-newer file`: Checks if the file last modification time is greater than that
of the specified reference file. If `file` is a symbolic link and the `-L`
option is in use, then the last modification time of the file pointed to by
the symbolic link is used.
* `-anewer file`: Checks if the file last access time is greater than that of
the specified reference file. If `file` is a symbolic link and the `-L`
option is in use, then the last access time of the file pointed to by the
symbolic link is used.
* `-cnewer file`: Checks if the file creation time is greater than that of
the specified reference file. If `file` is a symbolic link and the `-L`
option is in use, then the creation time of the file pointed to by the
symbolic link is used.
* `-print`: Outputs the file path, followed by a newline. Always evaluates to
true.
* `-print0`: Outputs the file path, followed by a zero byte. Always evaluates to

View file

@ -9,6 +9,7 @@
#include <AK/LexicalPath.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/OwnPtr.h>
#include <AK/Time.h>
#include <AK/Vector.h>
#include <LibCore/System.h>
#include <LibMain/Main.h>
@ -326,6 +327,52 @@ private:
mode_t m_mode { 0 };
};
class NewerCommand final : public StatCommand {
public:
enum class TimestampType {
LastAccess,
Creation,
LastModification
};
NewerCommand(char const* arg, TimestampType timestamp_type)
{
auto stat_function = g_follow_symlinks ? Core::System::stat : Core::System::lstat;
auto stat_or_error = stat_function({ arg, strlen(arg) });
if (stat_or_error.is_error())
fatal_error("find: '{}': {}", arg, strerror(stat_or_error.error().code()));
m_reference_file_stat = stat_or_error.release_value();
m_timestamp_type = timestamp_type;
}
private:
virtual bool evaluate(struct stat const& stat) const override
{
struct timespec current_file_timestamp;
struct timespec reference_file_timestamp;
switch (m_timestamp_type) {
case TimestampType::LastAccess:
current_file_timestamp = stat.st_atim;
reference_file_timestamp = m_reference_file_stat.st_atim;
break;
case TimestampType::Creation:
current_file_timestamp = stat.st_ctim;
reference_file_timestamp = m_reference_file_stat.st_ctim;
break;
case TimestampType::LastModification:
current_file_timestamp = stat.st_mtim;
reference_file_timestamp = m_reference_file_stat.st_mtim;
break;
}
return Duration::from_timespec(current_file_timestamp) > Duration::from_timespec(reference_file_timestamp);
}
struct stat m_reference_file_stat;
TimestampType m_timestamp_type { TimestampType::LastModification };
};
class PrintCommand final : public Command {
public:
PrintCommand(char terminator = '\n')
@ -488,6 +535,18 @@ static OwnPtr<Command> parse_simple_command(Vector<char*>& args)
return make<AccessCommand>(W_OK);
} else if (arg == "-executable") {
return make<AccessCommand>(X_OK);
} else if (arg == "-newer") {
if (args.is_empty())
fatal_error("-newer: requires additional arguments");
return make<NewerCommand>(args.take_first(), NewerCommand::TimestampType::LastModification);
} else if (arg == "-anewer") {
if (args.is_empty())
fatal_error("-anewer: requires additional arguments");
return make<NewerCommand>(args.take_first(), NewerCommand::TimestampType::LastAccess);
} else if (arg == "-cnewer") {
if (args.is_empty())
fatal_error("-cnewer: requires additional arguments");
return make<NewerCommand>(args.take_first(), NewerCommand::TimestampType::Creation);
} else if (arg == "-print") {
g_have_seen_action_command = true;
return make<PrintCommand>();