mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-28 13:53:10 +00:00
string repeat: allow omission of -n
(#10282)
This commit is contained in:
parent
662fde7b71
commit
4e6e897781
2 changed files with 59 additions and 16 deletions
|
@ -3,8 +3,8 @@ use crate::wutil::fish_wcstol;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Repeat {
|
pub struct Repeat {
|
||||||
count: usize,
|
count: Option<usize>,
|
||||||
max: usize,
|
max: Option<usize>,
|
||||||
quiet: bool,
|
quiet: bool,
|
||||||
no_newline: bool,
|
no_newline: bool,
|
||||||
}
|
}
|
||||||
|
@ -21,14 +21,17 @@ impl StringSubCommand<'_> for Repeat {
|
||||||
fn parse_opt(&mut self, name: &wstr, c: char, arg: Option<&wstr>) -> Result<(), StringError> {
|
fn parse_opt(&mut self, name: &wstr, c: char, arg: Option<&wstr>) -> Result<(), StringError> {
|
||||||
match c {
|
match c {
|
||||||
'n' => {
|
'n' => {
|
||||||
self.count = fish_wcstol(arg.unwrap())?
|
self.count =
|
||||||
.try_into()
|
Some(fish_wcstol(arg.unwrap())?.try_into().map_err(|_| {
|
||||||
.map_err(|_| invalid_args!("%ls: Invalid count value '%ls'\n", name, arg))?
|
invalid_args!("%ls: Invalid count value '%ls'\n", name, arg)
|
||||||
|
})?)
|
||||||
}
|
}
|
||||||
'm' => {
|
'm' => {
|
||||||
self.max = fish_wcstol(arg.unwrap())?
|
self.max = Some(
|
||||||
.try_into()
|
fish_wcstol(arg.unwrap())?
|
||||||
.map_err(|_| invalid_args!("%ls: Invalid max value '%ls'\n", name, arg))?
|
.try_into()
|
||||||
|
.map_err(|_| invalid_args!("%ls: Invalid max value '%ls'\n", name, arg))?,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
'q' => self.quiet = true,
|
'q' => self.quiet = true,
|
||||||
'N' => self.no_newline = true,
|
'N' => self.no_newline = true,
|
||||||
|
@ -37,6 +40,33 @@ impl StringSubCommand<'_> for Repeat {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_args(
|
||||||
|
&mut self,
|
||||||
|
optind: &mut usize,
|
||||||
|
args: &[&'_ wstr],
|
||||||
|
streams: &mut IoStreams,
|
||||||
|
) -> Option<c_int> {
|
||||||
|
if self.count.is_some() || self.max.is_some() {
|
||||||
|
return STATUS_CMD_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = args[0];
|
||||||
|
|
||||||
|
let Some(arg) = args.get(*optind) else {
|
||||||
|
string_error!(streams, BUILTIN_ERR_ARG_COUNT0, name);
|
||||||
|
return STATUS_INVALID_ARGS;
|
||||||
|
};
|
||||||
|
*optind += 1;
|
||||||
|
|
||||||
|
let Ok(Ok(count)) = fish_wcstol(arg).map(|count| count.try_into()) else {
|
||||||
|
string_error!(streams, "%ls: Invalid count value '%ls'\n", name, arg);
|
||||||
|
return STATUS_INVALID_ARGS;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.count = Some(count);
|
||||||
|
return STATUS_CMD_OK;
|
||||||
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle(
|
||||||
&mut self,
|
&mut self,
|
||||||
_parser: &Parser,
|
_parser: &Parser,
|
||||||
|
@ -44,7 +74,10 @@ impl StringSubCommand<'_> for Repeat {
|
||||||
optind: &mut usize,
|
optind: &mut usize,
|
||||||
args: &[&wstr],
|
args: &[&wstr],
|
||||||
) -> Option<libc::c_int> {
|
) -> Option<libc::c_int> {
|
||||||
if self.max == 0 && self.count == 0 {
|
let max = self.max.unwrap_or_default();
|
||||||
|
let count = self.count.unwrap_or_default();
|
||||||
|
|
||||||
|
if max == 0 && count == 0 {
|
||||||
// XXX: This used to be allowed, but returned 1.
|
// XXX: This used to be allowed, but returned 1.
|
||||||
// Keep it that way for now instead of adding an error.
|
// Keep it that way for now instead of adding an error.
|
||||||
// streams.err.append(L"Count or max must be greater than zero");
|
// streams.err.append(L"Count or max must be greater than zero");
|
||||||
|
@ -75,13 +108,12 @@ impl StringSubCommand<'_> for Repeat {
|
||||||
|
|
||||||
// The maximum size of the string is either the "max" characters,
|
// The maximum size of the string is either the "max" characters,
|
||||||
// or it's the "count" repetitions, whichever ends up lower.
|
// or it's the "count" repetitions, whichever ends up lower.
|
||||||
let max = if self.max == 0
|
let max_repeat_len = w.len().wrapping_mul(count);
|
||||||
|| (self.count > 0 && w.len().wrapping_mul(self.count) < self.max)
|
let max = if max == 0 || (count > 0 && max_repeat_len < max) {
|
||||||
{
|
|
||||||
// TODO: we should disallow overflowing unless max <= w.len().checked_mul(self.count).unwrap_or(usize::MAX)
|
// TODO: we should disallow overflowing unless max <= w.len().checked_mul(self.count).unwrap_or(usize::MAX)
|
||||||
w.len().wrapping_mul(self.count)
|
max_repeat_len
|
||||||
} else {
|
} else {
|
||||||
self.max
|
max
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reserve a string to avoid writing constantly.
|
// Reserve a string to avoid writing constantly.
|
||||||
|
|
|
@ -468,9 +468,21 @@ string repeat -n 2 foo
|
||||||
string repeat --count 2 foo
|
string repeat --count 2 foo
|
||||||
# CHECK: foofoo
|
# CHECK: foofoo
|
||||||
|
|
||||||
|
string repeat 2 foo
|
||||||
|
# CHECK: foofoo
|
||||||
|
|
||||||
|
string repeat 2 -n 3
|
||||||
|
# CHECK: 222
|
||||||
|
|
||||||
echo foo | string repeat -n 2
|
echo foo | string repeat -n 2
|
||||||
# CHECK: foofoo
|
# CHECK: foofoo
|
||||||
|
|
||||||
|
echo foo | string repeat 2
|
||||||
|
# CHECK: foofoo
|
||||||
|
|
||||||
|
string repeat foo
|
||||||
|
# CHECKERR: string repeat: Invalid count value 'foo'
|
||||||
|
|
||||||
string repeat -n2 -q foo; and echo "exit 0"
|
string repeat -n2 -q foo; and echo "exit 0"
|
||||||
# CHECK: exit 0
|
# CHECK: exit 0
|
||||||
|
|
||||||
|
@ -566,8 +578,7 @@ string repeat -n; and echo "exit 0"
|
||||||
# DONTCHECKERR: string repeat: Unknown option '-l'
|
# DONTCHECKERR: string repeat: Unknown option '-l'
|
||||||
|
|
||||||
string repeat ""
|
string repeat ""
|
||||||
or echo string repeat empty string failed
|
# CHECKERR: string repeat: Invalid count value ''
|
||||||
# CHECK: string repeat empty string failed
|
|
||||||
|
|
||||||
string repeat -n3 ""
|
string repeat -n3 ""
|
||||||
or echo string repeat empty string failed
|
or echo string repeat empty string failed
|
||||||
|
|
Loading…
Reference in a new issue