Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c98e4d68f |
No files matched your search
Generated
+1
-1
@@ -1103,7 +1103,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sed"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"assert_fs",
|
||||
"chrono",
|
||||
|
||||
+2
-3
@@ -1,12 +1,11 @@
|
||||
# sed (uutils)
|
||||
# * see the repository LICENSE, README, and CONTRIBUTING files for more information
|
||||
|
||||
# spell-checker:ignore (libs) mangen sed binstall chrono codspeed libc memchr memmap rlimit rustix textwrap uucore uutests xattr
|
||||
# spell-checker:ignore (misc) clippy codegen msvc startswith uninlined uutils
|
||||
# spell-checker:ignore (libs) mangen sed
|
||||
|
||||
[package]
|
||||
name = "sed"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
license = "MIT"
|
||||
description = "sed ~ implemented as universal (cross-platform) utils, written in Rust"
|
||||
default-run = "sed"
|
||||
|
||||
Generated
+1
-1
@@ -897,7 +897,7 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "sed"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"clap_complete",
|
||||
|
||||
+31
-38
@@ -359,7 +359,7 @@ fn compile_address_range(
|
||||
let is_step_match = line.current() == '~'; // E.g. 0~2: Pick even-numbered lines
|
||||
line.advance();
|
||||
line.eat_spaces();
|
||||
let is_step_end = if !line.eol() && line.current() == '~' {
|
||||
let is_step_end = if line.current() == '~' {
|
||||
// E.g. /foo/,~10: Start at foo, include all lines until multiple of 10 is reached.
|
||||
line.advance();
|
||||
line.eat_spaces();
|
||||
@@ -373,46 +373,39 @@ fn compile_address_range(
|
||||
return compilation_error(lines, line, "~step is invalid in POSIX mode");
|
||||
}
|
||||
|
||||
// Look for second address; a missing ~step counts as 0.
|
||||
let has_addr2 = !line.eol() && (is_address_char(line.current()) || line.current() == '+');
|
||||
if !(has_addr2 || is_step_match || is_step_end) {
|
||||
return compilation_error(lines, line, "unexpected `,'");
|
||||
}
|
||||
let addr2 = if has_addr2 {
|
||||
compile_address(lines, line, context)?
|
||||
} else {
|
||||
Address::Line(0)
|
||||
};
|
||||
// Set step_n to the number specified in the (required numeric) address.
|
||||
let step_n = if is_step_match || is_step_end {
|
||||
match addr2 {
|
||||
Address::Line(n) => n,
|
||||
_ => {
|
||||
return compilation_error(
|
||||
lines,
|
||||
line,
|
||||
"~step can only be specified through numeric values",
|
||||
);
|
||||
// Look for second address.
|
||||
if !line.eol() {
|
||||
let addr2 = compile_address(lines, line, context)?;
|
||||
// Set step_n to the number specified in the (required numeric) address.
|
||||
let step_n = if is_step_match || is_step_end {
|
||||
match addr2 {
|
||||
Address::Line(n) => n,
|
||||
_ => {
|
||||
return compilation_error(
|
||||
lines,
|
||||
line,
|
||||
"~step can only be specified through numeric values",
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
0 // dummy, not used
|
||||
};
|
||||
|
||||
if is_line0 && !matches!(addr2, Address::Re(_)) && !is_step_match {
|
||||
return compilation_error(lines, line, ERR_ADDRESS_0_USAGE);
|
||||
}
|
||||
} else {
|
||||
0 // dummy, not used
|
||||
};
|
||||
|
||||
// Address 0 needs a regex end or a positive ~step (0~0 selects no line).
|
||||
if is_line0 && !matches!(addr2, Address::Re(_)) && !(is_step_match && step_n > 0) {
|
||||
return compilation_error(lines, line, ERR_ADDRESS_0_USAGE);
|
||||
// If needed, transform Address::Line into Address::Step*.
|
||||
cmd.addr2 = if is_step_match {
|
||||
Some(Address::StepMatch(step_n))
|
||||
} else if is_step_end {
|
||||
Some(Address::StepEnd(step_n))
|
||||
} else {
|
||||
Some(addr2)
|
||||
};
|
||||
n_addr += 1;
|
||||
}
|
||||
|
||||
// If needed, transform Address::Line into Address::Step*.
|
||||
cmd.addr2 = if is_step_match {
|
||||
Some(Address::StepMatch(step_n))
|
||||
} else if is_step_end {
|
||||
Some(Address::StepEnd(step_n))
|
||||
} else {
|
||||
Some(addr2)
|
||||
};
|
||||
n_addr += 1;
|
||||
}
|
||||
|
||||
// Zero-address read command check
|
||||
@@ -503,7 +496,7 @@ fn compile_address(
|
||||
let number = parse_number(lines, line, true)?.unwrap();
|
||||
Ok(Address::Line(number))
|
||||
}
|
||||
_ => compilation_error(lines, line, "expected context address"),
|
||||
_ => panic!("invalid context address"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ fn applies(
|
||||
Address::Line(n) if linenum >= *n => {
|
||||
context.last_address = true;
|
||||
}
|
||||
Address::RelLine(0) | Address::StepMatch(0) | Address::StepEnd(0) => {
|
||||
Address::RelLine(n) if *n == 0 => {
|
||||
context.last_address = true;
|
||||
}
|
||||
_ => {
|
||||
|
||||
@@ -40,12 +40,7 @@ enum State {
|
||||
input_name: String, // Input description (path or script string)
|
||||
line_number: usize, // Current line number
|
||||
},
|
||||
// All scripts have been processed; keeps the last position for errors
|
||||
// raised at the end of the script.
|
||||
Done {
|
||||
input_name: String,
|
||||
line_number: usize,
|
||||
},
|
||||
Done, // All scripts have been processed
|
||||
}
|
||||
|
||||
impl ScriptLineProvider {
|
||||
@@ -60,18 +55,16 @@ impl ScriptLineProvider {
|
||||
/// Return the currently processed script line number.
|
||||
pub fn get_line_number(&self) -> usize {
|
||||
match &self.state {
|
||||
State::Active { line_number, .. } | State::Done { line_number, .. } => *line_number,
|
||||
State::NotStarted => 0,
|
||||
State::Active { line_number, .. } => *line_number,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the currently processed script descriptive name.
|
||||
pub fn get_input_name(&self) -> &str {
|
||||
match &self.state {
|
||||
State::Active { input_name, .. } | State::Done { input_name, .. } => {
|
||||
input_name.as_str()
|
||||
}
|
||||
State::NotStarted => "",
|
||||
State::Active { input_name, .. } => input_name.as_str(),
|
||||
_ => "",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +94,7 @@ impl ScriptLineProvider {
|
||||
return Ok(Some(line));
|
||||
}
|
||||
}
|
||||
State::Done { .. } => {
|
||||
State::Done => {
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
@@ -115,23 +108,7 @@ impl ScriptLineProvider {
|
||||
// Move to the next available script source.
|
||||
fn advance_source(&mut self, next_index: usize) -> UResult<()> {
|
||||
if next_index >= self.sources.len() {
|
||||
let (input_name, line_number) =
|
||||
match std::mem::replace(&mut self.state, State::NotStarted) {
|
||||
State::Active {
|
||||
input_name,
|
||||
line_number,
|
||||
..
|
||||
}
|
||||
| State::Done {
|
||||
input_name,
|
||||
line_number,
|
||||
} => (input_name, line_number),
|
||||
State::NotStarted => (String::new(), 0),
|
||||
};
|
||||
self.state = State::Done {
|
||||
input_name,
|
||||
line_number,
|
||||
};
|
||||
self.state = State::Done;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -174,14 +151,7 @@ impl fmt::Debug for State {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
State::NotStarted => f.debug_struct("NotStarted").finish(),
|
||||
State::Done {
|
||||
input_name,
|
||||
line_number,
|
||||
} => f
|
||||
.debug_struct("Done")
|
||||
.field("input_name", input_name)
|
||||
.field("line_number", line_number)
|
||||
.finish(),
|
||||
State::Done => f.debug_struct("Done").finish(),
|
||||
State::Active {
|
||||
index,
|
||||
input_name,
|
||||
|
||||
@@ -2487,20 +2487,7 @@ fn test_incomplete_test_command_posix() {
|
||||
.args(&["--posix", "i\\"])
|
||||
.fails()
|
||||
.code_is(1)
|
||||
.stderr_is("sed: <script argument 1>:1:3: error: incomplete command\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_at_end_of_script_keeps_its_position() {
|
||||
// The script is exhausted when the error is found: report its last line,
|
||||
// as GNU sed does ("-e expression #1").
|
||||
new_ucmd!()
|
||||
.args(&["s/a/b\\"])
|
||||
.fails()
|
||||
.code_is(1)
|
||||
.stderr_is(
|
||||
"sed: <script argument 1>:1:7: error: unterminated substitute replacement (unexpected EOF)\n",
|
||||
);
|
||||
.stderr_is("sed: :0:3: error: incomplete command\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2559,74 +2546,6 @@ fn test_step_end_non_posix() {
|
||||
.stderr_is("sed: <script argument 1>:1:4: error: ~step is invalid in POSIX mode\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_step_without_number_is_zero() {
|
||||
// `N~` and `N,~` select line N alone, like a step of 0.
|
||||
for script in ["3~=", "3,~=", "3,~0="] {
|
||||
new_ucmd!()
|
||||
.args(&["-n", script])
|
||||
.pipe_in("q\nr\ns\nt\n")
|
||||
.succeeds()
|
||||
.stdout_is("3\n");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_step_match_zero_closes_range() {
|
||||
// `3~0` selects line 3 alone, so `c` prints its text there.
|
||||
new_ucmd!()
|
||||
.args(&["3~0c\\X"])
|
||||
.pipe_in("1\n2\n3\n4\n5\n")
|
||||
.succeeds()
|
||||
.stdout_is("1\n2\nX\n4\n5\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_step_non_numeric() {
|
||||
for (script, column) in [("1~/x/p", 6), ("1,~/x/p", 7)] {
|
||||
new_ucmd!()
|
||||
.args(&[script])
|
||||
.fails()
|
||||
.code_is(1)
|
||||
.stderr_is(format!(
|
||||
"sed: <script argument 1>:1:{column}: error: ~step can only be specified through numeric values\n"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_step_without_number_at_end_of_line() {
|
||||
new_ucmd!()
|
||||
.args(&["7~"])
|
||||
.fails()
|
||||
.code_is(1)
|
||||
.stderr_is("sed: <script argument 1>:1:3: error: command expected\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_addr0_step_zero() {
|
||||
for script in ["0~0p", "0~p"] {
|
||||
new_ucmd!()
|
||||
.args(&[script])
|
||||
.fails()
|
||||
.code_is(1)
|
||||
.stderr_contains("address 0 can only be used with ~step");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_comma_without_second_address() {
|
||||
for (script, column) in [("5,", 3), ("5,=", 3), ("5, x", 4)] {
|
||||
new_ucmd!()
|
||||
.args(&[script])
|
||||
.fails()
|
||||
.code_is(1)
|
||||
.stderr_is(format!(
|
||||
"sed: <script argument 1>:1:{column}: error: unexpected `,'\n"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// The following test diverse ways in which regexes are matched.
|
||||
// Search for 'regex\.' to find them in the code.
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user