Validate regular expressions before reading input
This commit is contained in:
+8
-1
@@ -1,7 +1,7 @@
|
|||||||
project(
|
project(
|
||||||
'sedpp',
|
'sedpp',
|
||||||
'cpp',
|
'cpp',
|
||||||
version: '1.0.0',
|
version: '1.0.1',
|
||||||
default_options: [
|
default_options: [
|
||||||
'cpp_std=c++20',
|
'cpp_std=c++20',
|
||||||
'warning_level=3',
|
'warning_level=3',
|
||||||
@@ -24,3 +24,10 @@ test(
|
|||||||
depends: sed_exe,
|
depends: sed_exe,
|
||||||
timeout: 240,
|
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/Compiler.h"
|
||||||
|
#include "sedpp/Regex.h"
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -103,6 +104,28 @@ Program Compiler::compile(const std::vector<std::string> &scripts) {
|
|||||||
if (braceDepth_ > 0) {
|
if (braceDepth_ > 0) {
|
||||||
throw std::runtime_error("-e expression #1, char 0: unmatched '{'");
|
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_;
|
return program_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
namespace sedpp {
|
namespace sedpp {
|
||||||
int SEDPP_VERSION = 1;
|
constexpr const char *SEDPP_VERSION = "1.0.1";
|
||||||
|
|
||||||
void OptionParser::printVersion() {
|
void OptionParser::printVersion() {
|
||||||
// Version output intentionally names this implementation, not GNU sed.
|
// Version output intentionally names this implementation, not GNU sed.
|
||||||
|
|||||||
Reference in New Issue
Block a user