Validate regular expressions before reading input

This commit is contained in:
2026-07-10 18:13:27 -05:00
parent 572cbc9387
commit 5709f54081
4 changed files with 54 additions and 2 deletions
+8 -1
View File
@@ -1,7 +1,7 @@
project(
'sedpp',
'cpp',
version: '1.0.0',
version: '1.0.1',
default_options: [
'cpp_std=c++20',
'warning_level=3',
@@ -24,3 +24,10 @@ test(
depends: sed_exe,
timeout: 240,
)
test(
'pipe-regex-fallback',
find_program('scripts/test-pipe-regex-fallback.sh'),
args: [sed_exe.full_path()],
depends: sed_exe,
)
+22
View File
@@ -0,0 +1,22 @@
#!/bin/sh
set -eu
sed_bin=$1
# Limine uses this portability probe in gensyms.sh. The first sed must reject
# the unsupported word-boundary character classes without consuming the pipe,
# leaving the input available to the GNU-compatible fallback expression.
actual="$({
printf '%s\n' \
'00000000 l d .text discarded' \
'00000010 g F .text retained'
} | ("$sed_bin" '/[[:<:]]d[[:>:]]/d' 2>/dev/null ||
"$sed_bin" '/\bd\b/d'))"
expected='00000010 g F .text retained'
if [ "$actual" != "$expected" ]; then
printf 'expected: %s\nactual: %s\n' "$expected" "$actual" >&2
exit 1
fi
+23
View File
@@ -1,4 +1,5 @@
#include "sedpp/Compiler.h"
#include "sedpp/Regex.h"
#include <cctype>
#include <iostream>
@@ -103,6 +104,28 @@ Program Compiler::compile(const std::vector<std::string> &scripts) {
if (braceDepth_ > 0) {
throw std::runtime_error("-e expression #1, char 0: unmatched '{'");
}
// Validate every literal regular expression before Runner opens or reads an
// input stream. Besides matching GNU sed's diagnostic timing, this matters
// for portability probes such as `(sed non_gnu_re || sed fallback_re)`: the
// rejected first expression must leave the shared pipe untouched so the
// fallback process can consume it. Empty expressions are runtime references
// to the previously used regex and therefore cannot be compiled here.
auto validateAddress = [this](const std::optional<Address> &address) {
if (address && address->kind == AddressKind::Regex &&
!address->text.empty()) {
Regex regex(address->text, options_);
}
};
for (const Command &command : program_.commands) {
validateAddress(command.firstAddress);
validateAddress(command.secondAddress);
if (command.substitute && !command.substitute->pattern.empty()) {
Regex regex(command.substitute->pattern, options_,
command.substitute->ignoreCase,
command.substitute->multiline);
}
}
return program_;
}
+1 -1
View File
@@ -7,7 +7,7 @@
#include <cstdlib>
namespace sedpp {
int SEDPP_VERSION = 1;
constexpr const char *SEDPP_VERSION = "1.0.1";
void OptionParser::printVersion() {
// Version output intentionally names this implementation, not GNU sed.