diff --git a/xff/engine/evaluate.cc b/xff/engine/evaluate.cc index ce9a5eb..800e186 100644 --- a/xff/engine/evaluate.cc +++ b/xff/engine/evaluate.cc @@ -47,6 +47,11 @@ namespace xff::engine { namespace { +// The OS-native line terminator used by -println/-printfln (xff extensions). +// LF today; a Windows build would select "\r\n". Centralized so both actions +// agree and the platform choice lives in one place. +constexpr std::string_view kOsLineEnding = "\n"; + bool Fnmatch(std::string_view pattern, std::string_view text, int flags) { return ::fnmatch(std::string(pattern).c_str(), std::string(text).c_str(), flags) == 0; } @@ -441,6 +446,16 @@ bool EvaluatePredicate(const parser::Expr& expr, EvalContext& ctx) { } return true; } + if (name == "-println") { + emit(absl::StrCat(visit.path, kOsLineEnding)); // xff: -print with the OS line ending + return true; + } + if (name == "-printfln") { + if (has_arg) { // xff: -printf plus the OS line ending appended + emit(absl::StrCat(FormatPrintf(expr.args.front(), visit), kOsLineEnding)); + } + return true; + } if (name == "-delete") { static_cast(fs.Remove(visit.path)); // failures set a nonzero exit; wired in the exit-code work return true; diff --git a/xff/engine/evaluate_test.cc b/xff/engine/evaluate_test.cc index f57a29c..f4256f6 100644 --- a/xff/engine/evaluate_test.cc +++ b/xff/engine/evaluate_test.cc @@ -341,6 +341,19 @@ TEST_F(EvaluateTest, PrintfExpandsDirectivesAndEscapes) { EXPECT_THAT(emitted_, Eq("%\t3")); } +TEST_F(EvaluateTest, PrintlnAndPrintflnAppendOsLineEnding) { + vfs::Metadata md; + md.type = vfs::FileType::kRegular; + md.size = 42; + const Visit visit{.path = "a/b/c.txt", .name = "c.txt", .depth = 2, .metadata = md}; + // -println: -print with the OS line ending (LF on this platform). + EXPECT_TRUE(Match({"-println"}, visit)); + EXPECT_THAT(emitted_, Eq("a/b/c.txt\n")); + // -printfln: -printf plus a trailing OS line ending the format need not carry. + EXPECT_TRUE(Match({"-printfln", "%f|%s"}, visit)); + EXPECT_THAT(emitted_, Eq("c.txt|42\n")); +} + TEST_F(EvaluateTest, ExecFieldsGatesNamedPlaceholderSubstitution) { vfs::Metadata md; const Visit visit = MakeVisit("a/b/f.txt", "f.txt", vfs::FileType::kRegular, md); diff --git a/xff/registry/registry.cc b/xff/registry/registry.cc index c21b8a1..f4ff9a3 100644 --- a/xff/registry/registry.cc +++ b/xff/registry/registry.cc @@ -68,6 +68,8 @@ constexpr std::array kDescriptors = std::to_array({ {.name = "-print", .kind = Kind::kAction, .arity = 0}, {.name = "-print0", .kind = Kind::kAction, .arity = 0}, {.name = "-printf", .kind = Kind::kAction, .arity = 1}, + {.name = "-println", .kind = Kind::kAction, .arity = 0}, // xff: -print with the OS line ending + {.name = "-printfln", .kind = Kind::kAction, .arity = 1}, // xff: -printf + the OS line ending {.name = "-delete", .kind = Kind::kAction, .arity = 0}, {.name = "-prune", .kind = Kind::kAction, .arity = 0}, {.name = "-quit", .kind = Kind::kAction, .arity = 0},