Validate regular expressions before reading input
This commit is contained in:
+8
-1
@@ -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,
|
||||
)
|
||||
|
||||
Executable
+22
@@ -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
|
||||
@@ -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
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user