mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 03:57:44 +00:00
mv: resolve merge conflict
This commit is contained in:
commit
85ded232bc
35 changed files with 550 additions and 209 deletions
40
.github/workflows/CICD.yml
vendored
40
.github/workflows/CICD.yml
vendored
|
@ -331,7 +331,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
RUSTDOCFLAGS="-Dwarnings" cargo doc ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-deps --workspace --document-private-items
|
RUSTDOCFLAGS="-Dwarnings" cargo doc ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-deps --workspace --document-private-items
|
||||||
- uses: DavidAnson/markdownlint-cli2-action@v9
|
- uses: DavidAnson/markdownlint-cli2-action@v10
|
||||||
with:
|
with:
|
||||||
command: fix
|
command: fix
|
||||||
globs: |
|
globs: |
|
||||||
|
@ -601,6 +601,44 @@ jobs:
|
||||||
--arg size "$SIZE" \
|
--arg size "$SIZE" \
|
||||||
--arg multisize "$SIZE_MULTI" \
|
--arg multisize "$SIZE_MULTI" \
|
||||||
'{($date): { sha: $sha, size: $size, multisize: $multisize, }}' > size-result.json
|
'{($date): { sha: $sha, size: $size, multisize: $multisize, }}' > size-result.json
|
||||||
|
- name: Download the previous individual size result
|
||||||
|
uses: dawidd6/action-download-artifact@v2
|
||||||
|
with:
|
||||||
|
workflow: CICD.yml
|
||||||
|
name: individual-size-result
|
||||||
|
repo: uutils/coreutils
|
||||||
|
path: dl
|
||||||
|
- name: Download the previous size result
|
||||||
|
uses: dawidd6/action-download-artifact@v2
|
||||||
|
with:
|
||||||
|
workflow: CICD.yml
|
||||||
|
name: size-result
|
||||||
|
repo: uutils/coreutils
|
||||||
|
path: dl
|
||||||
|
- name: Check uutil release sizes
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
check() {
|
||||||
|
# Warn if the size increases by more than 5%
|
||||||
|
threshold='1.05'
|
||||||
|
ratio=$(jq -n "$2 / $3")
|
||||||
|
echo "$1: size=$2, previous_size=$3, ratio=$ratio, threshold=$threshold"
|
||||||
|
if [[ "$(jq -n "$ratio > $threshold")" == 'true' ]]; then
|
||||||
|
echo "::warning file=$4::Size of $1 increases by more than 5%"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
## Check individual size result
|
||||||
|
while read -r name previous_size; do
|
||||||
|
size=$(cat individual-size-result.json | jq -r ".[] | .sizes | .\"$name\"")
|
||||||
|
check "\`$name\` binary" "$size" "$previous_size" 'individual-size-result.json'
|
||||||
|
done < <(cat dl/individual-size-result.json | jq -r '.[] | .sizes | to_entries[] | "\(.key) \(.value)"')
|
||||||
|
## Check size result
|
||||||
|
size=$(cat size-result.json | jq -r '.[] | .size')
|
||||||
|
previous_size=$(cat dl/size-result.json | jq -r '.[] | .size')
|
||||||
|
check 'multiple binaries' "$size" "$previous_size" 'size-result.json'
|
||||||
|
multisize=$(cat size-result.json | jq -r '.[] | .multisize')
|
||||||
|
previous_multisize=$(cat dl/size-result.json | jq -r '.[] | .multisize')
|
||||||
|
check 'multicall binary' "$multisize" "$previous_multisize" 'size-result.json'
|
||||||
- name: Upload the individual size result
|
- name: Upload the individual size result
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -217,6 +217,8 @@ To run uutils against the GNU test suite locally, run the following commands:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
bash util/build-gnu.sh
|
bash util/build-gnu.sh
|
||||||
|
# Build uutils without release optimizations
|
||||||
|
UU_MAKE_PROFILE=debug bash util/build-gnu.sh
|
||||||
bash util/run-gnu-test.sh
|
bash util/run-gnu-test.sh
|
||||||
# To run a single test:
|
# To run a single test:
|
||||||
bash util/run-gnu-test.sh tests/touch/not-owner.sh # for example
|
bash util/run-gnu-test.sh tests/touch/not-owner.sh # for example
|
||||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2538,7 +2538,9 @@ version = "0.0.18"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
|
"humantime_to_duration",
|
||||||
"libc",
|
"libc",
|
||||||
|
"time",
|
||||||
"uucore",
|
"uucore",
|
||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
|
@ -43,8 +43,8 @@ fn binary_path(args: &mut impl Iterator<Item = OsString>) -> PathBuf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(binary_path: &Path) -> &str {
|
fn name(binary_path: &Path) -> Option<&str> {
|
||||||
binary_path.file_stem().unwrap().to_str().unwrap()
|
binary_path.file_stem()?.to_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -54,7 +54,10 @@ fn main() {
|
||||||
let mut args = uucore::args_os();
|
let mut args = uucore::args_os();
|
||||||
|
|
||||||
let binary = binary_path(&mut args);
|
let binary = binary_path(&mut args);
|
||||||
let binary_as_util = name(&binary);
|
let binary_as_util = name(&binary).unwrap_or_else(|| {
|
||||||
|
usage(&utils, "<unknown binary name>");
|
||||||
|
process::exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
// binary name equals util name?
|
// binary name equals util name?
|
||||||
if let Some(&(uumain, _)) = utils.get(binary_as_util) {
|
if let Some(&(uumain, _)) = utils.get(binary_as_util) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# spell-checker:ignore humantime
|
||||||
[package]
|
[package]
|
||||||
name = "uu_date"
|
name = "uu_date"
|
||||||
version = "0.0.18"
|
version = "0.0.18"
|
||||||
|
@ -16,8 +17,11 @@ path = "src/date.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { workspace=true }
|
chrono = { workspace=true }
|
||||||
|
#/ TODO: check if we can avoid chrono+time
|
||||||
|
time = { workspace=true }
|
||||||
clap = { workspace=true }
|
clap = { workspace=true }
|
||||||
uucore = { workspace=true }
|
uucore = { workspace=true }
|
||||||
|
humantime_to_duration = { workspace=true }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = { workspace=true }
|
libc = { workspace=true }
|
||||||
|
|
87
src/uu/date/date-usage.md
Normal file
87
src/uu/date/date-usage.md
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
# `date` usage
|
||||||
|
|
||||||
|
<!-- spell-checker:ignore (format) hhmm -->
|
||||||
|
|
||||||
|
FORMAT controls the output. Interpreted sequences are:
|
||||||
|
|
||||||
|
| Sequence | Description | Example |
|
||||||
|
| -------- | -------------------------------------------------------------------- | ---------------------- |
|
||||||
|
| %% | a literal % | % |
|
||||||
|
| %a | locale's abbreviated weekday name | Sun |
|
||||||
|
| %A | locale's full weekday name | Sunday |
|
||||||
|
| %b | locale's abbreviated month name | Jan |
|
||||||
|
| %B | locale's full month name | January |
|
||||||
|
| %c | locale's date and time | Thu Mar 3 23:05:25 2005|
|
||||||
|
| %C | century; like %Y, except omit last two digits | 20 |
|
||||||
|
| %d | day of month | 01 |
|
||||||
|
| %D | date; same as %m/%d/%y | 12/31/99 |
|
||||||
|
| %e | day of month, space padded; same as %_d | 3 |
|
||||||
|
| %F | full date; same as %Y-%m-%d | 2005-03-03 |
|
||||||
|
| %g | last two digits of year of ISO week number (see %G) | 05 |
|
||||||
|
| %G | year of ISO week number (see %V); normally useful only with %V | 2005 |
|
||||||
|
| %h | same as %b | Jan |
|
||||||
|
| %H | hour (00..23) | 23 |
|
||||||
|
| %I | hour (01..12) | 11 |
|
||||||
|
| %j | day of year (001..366) | 062 |
|
||||||
|
| %k | hour, space padded ( 0..23); same as %_H | 3 |
|
||||||
|
| %l | hour, space padded ( 1..12); same as %_I | 9 |
|
||||||
|
| %m | month (01..12) | 03 |
|
||||||
|
| %M | minute (00..59) | 30 |
|
||||||
|
| %n | a newline | \n |
|
||||||
|
| %N | nanoseconds (000000000..999999999) | 123456789 |
|
||||||
|
| %p | locale's equivalent of either AM or PM; blank if not known | PM |
|
||||||
|
| %P | like %p, but lower case | pm |
|
||||||
|
| %q | quarter of year (1..4) | 1 |
|
||||||
|
| %r | locale's 12-hour clock time | 11:11:04 PM |
|
||||||
|
| %R | 24-hour hour and minute; same as %H:%M | 23:30 |
|
||||||
|
| %s | seconds since 1970-01-01 00:00:00 UTC | 1615432800 |
|
||||||
|
| %S | second (00..60) | 30 |
|
||||||
|
| %t | a tab | \t |
|
||||||
|
| %T | time; same as %H:%M:%S | 23:30:30 |
|
||||||
|
| %u | day of week (1..7); 1 is Monday | 4 |
|
||||||
|
| %U | week number of year, with Sunday as first day of week (00..53) | 10 |
|
||||||
|
| %V | ISO week number, with Monday as first day of week (01..53) | 12 |
|
||||||
|
| %w | day of week (0..6); 0 is Sunday | 4 |
|
||||||
|
| %W | week number of year, with Monday as first day of week (00..53) | 11 |
|
||||||
|
| %x | locale's date representation | 03/03/2005 |
|
||||||
|
| %X | locale's time representation | 23:30:30 |
|
||||||
|
| %y | last two digits of year (00..99) | 05 |
|
||||||
|
| %Y | year | 2005 |
|
||||||
|
| %z | +hhmm numeric time zone | -0400 |
|
||||||
|
| %:z | +hh:mm numeric time zone | -04:00 |
|
||||||
|
| %::z | +hh:mm:ss numeric time zone | -04:00:00 |
|
||||||
|
| %:::z | numeric time zone with : to necessary precision | -04, +05:30 |
|
||||||
|
| %Z | alphabetic time zone abbreviation | EDT |
|
||||||
|
|
||||||
|
By default, date pads numeric fields with zeroes.
|
||||||
|
The following optional flags may follow '%':
|
||||||
|
|
||||||
|
* `-` (hyphen) do not pad the field
|
||||||
|
* `_` (underscore) pad with spaces
|
||||||
|
* `0` (zero) pad with zeros
|
||||||
|
* `^` use upper case if possible
|
||||||
|
* `#` use opposite case if possible
|
||||||
|
|
||||||
|
After any flags comes an optional field width, as a decimal number;
|
||||||
|
then an optional modifier, which is either
|
||||||
|
E to use the locale's alternate representations if available, or
|
||||||
|
O to use the locale's alternate numeric symbols if available.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
Convert seconds since the epoch (1970-01-01 UTC) to a date
|
||||||
|
|
||||||
|
```
|
||||||
|
date --date='@2147483647'
|
||||||
|
```
|
||||||
|
|
||||||
|
Show the time on the west coast of the US (use tzselect(1) to find TZ)
|
||||||
|
|
||||||
|
```
|
||||||
|
TZ='America/Los_Angeles' date
|
||||||
|
```
|
||||||
|
|
||||||
|
Show the local time for 9AM next Friday on the west coast of the US
|
||||||
|
|
||||||
|
```
|
||||||
|
date --date='TZ="America/Los_Angeles" 09:00 next Fri'
|
||||||
|
```
|
|
@ -1,78 +0,0 @@
|
||||||
# `date` usage
|
|
||||||
|
|
||||||
<!-- spell-checker:ignore (format) hhmm -->
|
|
||||||
|
|
||||||
``` text
|
|
||||||
FORMAT controls the output. Interpreted sequences are:
|
|
||||||
|
|
||||||
%% a literal %
|
|
||||||
%a locale's abbreviated weekday name (e.g., Sun)
|
|
||||||
%A locale's full weekday name (e.g., Sunday)
|
|
||||||
%b locale's abbreviated month name (e.g., Jan)
|
|
||||||
%B locale's full month name (e.g., January)
|
|
||||||
%c locale's date and time (e.g., Thu Mar 3 23:05:25 2005)
|
|
||||||
%C century; like %Y, except omit last two digits (e.g., 20)
|
|
||||||
%d day of month (e.g., 01)
|
|
||||||
%D date; same as %m/%d/%y
|
|
||||||
%e day of month, space padded; same as %_d
|
|
||||||
%F full date; same as %Y-%m-%d
|
|
||||||
%g last two digits of year of ISO week number (see %G)
|
|
||||||
%G year of ISO week number (see %V); normally useful only with %V
|
|
||||||
%h same as %b
|
|
||||||
%H hour (00..23)
|
|
||||||
%I hour (01..12)
|
|
||||||
%j day of year (001..366)
|
|
||||||
%k hour, space padded ( 0..23); same as %_H
|
|
||||||
%l hour, space padded ( 1..12); same as %_I
|
|
||||||
%m month (01..12)
|
|
||||||
%M minute (00..59)
|
|
||||||
%n a newline
|
|
||||||
%N nanoseconds (000000000..999999999)
|
|
||||||
%p locale's equivalent of either AM or PM; blank if not known
|
|
||||||
%P like %p, but lower case
|
|
||||||
%q quarter of year (1..4)
|
|
||||||
%r locale's 12-hour clock time (e.g., 11:11:04 PM)
|
|
||||||
%R 24-hour hour and minute; same as %H:%M
|
|
||||||
%s seconds since 1970-01-01 00:00:00 UTC
|
|
||||||
%S second (00..60)
|
|
||||||
%t a tab
|
|
||||||
%T time; same as %H:%M:%S
|
|
||||||
%u day of week (1..7); 1 is Monday
|
|
||||||
%U week number of year, with Sunday as first day of week (00..53)
|
|
||||||
%V ISO week number, with Monday as first day of week (01..53)
|
|
||||||
%w day of week (0..6); 0 is Sunday
|
|
||||||
%W week number of year, with Monday as first day of week (00..53)
|
|
||||||
%x locale's date representation (e.g., 12/31/99)
|
|
||||||
%X locale's time representation (e.g., 23:13:48)
|
|
||||||
%y last two digits of year (00..99)
|
|
||||||
%Y year
|
|
||||||
%z +hhmm numeric time zone (e.g., -0400)
|
|
||||||
%:z +hh:mm numeric time zone (e.g., -04:00)
|
|
||||||
%::z +hh:mm:ss numeric time zone (e.g., -04:00:00)
|
|
||||||
%:::z numeric time zone with : to necessary precision (e.g., -04, +05:30)
|
|
||||||
%Z alphabetic time zone abbreviation (e.g., EDT)
|
|
||||||
|
|
||||||
By default, date pads numeric fields with zeroes.
|
|
||||||
The following optional flags may follow '%':
|
|
||||||
|
|
||||||
- (hyphen) do not pad the field
|
|
||||||
_ (underscore) pad with spaces
|
|
||||||
0 (zero) pad with zeros
|
|
||||||
^ use upper case if possible
|
|
||||||
# use opposite case if possible
|
|
||||||
|
|
||||||
After any flags comes an optional field width, as a decimal number;
|
|
||||||
then an optional modifier, which is either
|
|
||||||
E to use the locale's alternate representations if available, or
|
|
||||||
O to use the locale's alternate numeric symbols if available.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
Convert seconds since the epoch (1970-01-01 UTC) to a date
|
|
||||||
$ date --date='@2147483647'
|
|
||||||
|
|
||||||
Show the time on the west coast of the US (use tzselect(1) to find TZ)
|
|
||||||
$ TZ='America/Los_Angeles' date
|
|
||||||
|
|
||||||
Show the local time for 9AM next Friday on the west coast of the US
|
|
||||||
$ date --date='TZ="America/Los_Angeles" 09:00 next Fri'
|
|
||||||
```
|
|
|
@ -6,10 +6,10 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes
|
// spell-checker:ignore (chrono) Datelike Timelike ; (format) DATEFILE MMDDhhmm ; (vars) datetime datetimes humantime
|
||||||
|
|
||||||
use chrono::format::{Item, StrftimeItems};
|
use chrono::format::{Item, StrftimeItems};
|
||||||
use chrono::{DateTime, FixedOffset, Local, Offset, Utc};
|
use chrono::{DateTime, Duration as ChronoDuration, FixedOffset, Local, Offset, Utc};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use chrono::{Datelike, Timelike};
|
use chrono::{Datelike, Timelike};
|
||||||
use clap::{crate_version, Arg, ArgAction, Command};
|
use clap::{crate_version, Arg, ArgAction, Command};
|
||||||
|
@ -18,6 +18,7 @@ use libc::{clock_settime, timespec, CLOCK_REALTIME};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use time::Duration;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
#[cfg(not(any(target_os = "redox")))]
|
#[cfg(not(any(target_os = "redox")))]
|
||||||
use uucore::error::FromIo;
|
use uucore::error::FromIo;
|
||||||
|
@ -96,6 +97,7 @@ enum DateSource {
|
||||||
Now,
|
Now,
|
||||||
Custom(String),
|
Custom(String),
|
||||||
File(PathBuf),
|
File(PathBuf),
|
||||||
|
Human(Duration),
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Iso8601Format {
|
enum Iso8601Format {
|
||||||
|
@ -168,7 +170,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let date_source = if let Some(date) = matches.get_one::<String>(OPT_DATE) {
|
let date_source = if let Some(date) = matches.get_one::<String>(OPT_DATE) {
|
||||||
|
if let Ok(duration) = humantime_to_duration::from_str(date.as_str()) {
|
||||||
|
DateSource::Human(duration)
|
||||||
|
} else {
|
||||||
DateSource::Custom(date.into())
|
DateSource::Custom(date.into())
|
||||||
|
}
|
||||||
} else if let Some(file) = matches.get_one::<String>(OPT_FILE) {
|
} else if let Some(file) = matches.get_one::<String>(OPT_FILE) {
|
||||||
DateSource::File(file.into())
|
DateSource::File(file.into())
|
||||||
} else {
|
} else {
|
||||||
|
@ -219,6 +225,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let iter = std::iter::once(date);
|
let iter = std::iter::once(date);
|
||||||
Box::new(iter)
|
Box::new(iter)
|
||||||
}
|
}
|
||||||
|
DateSource::Human(ref input) => {
|
||||||
|
// Get the current DateTime<FixedOffset> and convert the input time::Duration to chrono::Duration
|
||||||
|
// for things like "1 year ago"
|
||||||
|
let current_time = DateTime::<FixedOffset>::from(Local::now());
|
||||||
|
let input_chrono = ChronoDuration::seconds(input.as_seconds_f32() as i64)
|
||||||
|
+ ChronoDuration::nanoseconds(input.subsec_nanoseconds() as i64);
|
||||||
|
let iter = std::iter::once(Ok(current_time + input_chrono));
|
||||||
|
Box::new(iter)
|
||||||
|
}
|
||||||
DateSource::File(ref path) => {
|
DateSource::File(ref path) => {
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
return Err(USimpleError::new(
|
return Err(USimpleError::new(
|
||||||
|
|
|
@ -281,7 +281,7 @@ impl Params {
|
||||||
.join(prefix_from_template)
|
.join(prefix_from_template)
|
||||||
.display()
|
.display()
|
||||||
.to_string();
|
.to_string();
|
||||||
if options.treat_as_template && prefix.contains(MAIN_SEPARATOR) {
|
if options.treat_as_template && prefix_from_template.contains(MAIN_SEPARATOR) {
|
||||||
return Err(MkTempError::PrefixContainsDirSeparator(options.template));
|
return Err(MkTempError::PrefixContainsDirSeparator(options.template));
|
||||||
}
|
}
|
||||||
if tmpdir.is_some() && Path::new(prefix_from_template).is_absolute() {
|
if tmpdir.is_some() && Path::new(prefix_from_template).is_absolute() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ pub enum MvError {
|
||||||
DirectoryToNonDirectory(String),
|
DirectoryToNonDirectory(String),
|
||||||
NonDirectoryToDirectory(String, String),
|
NonDirectoryToDirectory(String, String),
|
||||||
NotADirectory(String),
|
NotADirectory(String),
|
||||||
|
TargetNotADirectory(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for MvError {}
|
impl Error for MvError {}
|
||||||
|
@ -34,7 +35,8 @@ impl Display for MvError {
|
||||||
Self::NonDirectoryToDirectory(s, t) => {
|
Self::NonDirectoryToDirectory(s, t) => {
|
||||||
write!(f, "cannot overwrite non-directory {t} with directory {s}")
|
write!(f, "cannot overwrite non-directory {t} with directory {s}")
|
||||||
}
|
}
|
||||||
Self::NotADirectory(t) => write!(f, "target {t} is not a directory"),
|
Self::NotADirectory(t) => write!(f, "target {t}: Not a directory"),
|
||||||
|
Self::TargetNotADirectory(t) => write!(f, "target directory {t}: Not a directory"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,14 +108,22 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
|
||||||
let backup_suffix = backup_control::determine_backup_suffix(&matches);
|
let backup_suffix = backup_control::determine_backup_suffix(&matches);
|
||||||
|
|
||||||
|
let target_dir = matches
|
||||||
|
.get_one::<OsString>(OPT_TARGET_DIRECTORY)
|
||||||
|
.map(OsString::from);
|
||||||
|
|
||||||
|
if let Some(ref maybe_dir) = target_dir {
|
||||||
|
if !Path::new(&maybe_dir).is_dir() {
|
||||||
|
return Err(MvError::TargetNotADirectory(maybe_dir.quote().to_string()).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let behavior = Behavior {
|
let behavior = Behavior {
|
||||||
overwrite: overwrite_mode,
|
overwrite: overwrite_mode,
|
||||||
backup: backup_mode,
|
backup: backup_mode,
|
||||||
suffix: backup_suffix,
|
suffix: backup_suffix,
|
||||||
update: update_mode,
|
update: update_mode,
|
||||||
target_dir: matches
|
target_dir,
|
||||||
.get_one::<OsString>(OPT_TARGET_DIRECTORY)
|
|
||||||
.map(OsString::from),
|
|
||||||
no_target_dir: matches.get_flag(OPT_NO_TARGET_DIRECTORY),
|
no_target_dir: matches.get_flag(OPT_NO_TARGET_DIRECTORY),
|
||||||
verbose: matches.get_flag(OPT_VERBOSE),
|
verbose: matches.get_flag(OPT_VERBOSE),
|
||||||
strip_slashes: matches.get_flag(OPT_STRIP_TRAILING_SLASHES),
|
strip_slashes: matches.get_flag(OPT_STRIP_TRAILING_SLASHES),
|
||||||
|
@ -426,7 +434,12 @@ fn rename(
|
||||||
}
|
}
|
||||||
|
|
||||||
match b.overwrite {
|
match b.overwrite {
|
||||||
OverwriteMode::NoClobber => return Ok(()),
|
OverwriteMode::NoClobber => {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("not replacing {}", to.quote()),
|
||||||
|
));
|
||||||
|
}
|
||||||
OverwriteMode::Interactive => {
|
OverwriteMode::Interactive => {
|
||||||
if !prompt_yes!("overwrite {}?", to.quote()) {
|
if !prompt_yes!("overwrite {}?", to.quote()) {
|
||||||
return Err(io::Error::new(io::ErrorKind::Other, ""));
|
return Err(io::Error::new(io::ErrorKind::Other, ""));
|
||||||
|
@ -458,12 +471,12 @@ fn rename(
|
||||||
if b.verbose {
|
if b.verbose {
|
||||||
let message = match backup_path {
|
let message = match backup_path {
|
||||||
Some(path) => format!(
|
Some(path) => format!(
|
||||||
"{} -> {} (backup: {})",
|
"renamed {} -> {} (backup: {})",
|
||||||
from.quote(),
|
from.quote(),
|
||||||
to.quote(),
|
to.quote(),
|
||||||
path.quote()
|
path.quote()
|
||||||
),
|
),
|
||||||
None => format!("{} -> {}", from.quote(), to.quote()),
|
None => format!("renamed {} -> {}", from.quote(), to.quote()),
|
||||||
};
|
};
|
||||||
|
|
||||||
match multi_progress {
|
match multi_progress {
|
||||||
|
|
|
@ -12,7 +12,7 @@ use std::fmt::Display;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{stdin, stdout, BufRead, BufReader, Read, Write};
|
use std::io::{stdin, stdout, BufRead, BufReader, Read, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use uucore::error::{FromIo, UResult};
|
use uucore::error::{FromIo, UResult, USimpleError};
|
||||||
use uucore::{format_usage, help_about, help_usage};
|
use uucore::{format_usage, help_about, help_usage};
|
||||||
|
|
||||||
const ABOUT: &str = help_about!("paste.md");
|
const ABOUT: &str = help_about!("paste.md");
|
||||||
|
@ -129,6 +129,16 @@ fn paste(
|
||||||
files.push(file);
|
files.push(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if delimiters.ends_with('\\') && !delimiters.ends_with("\\\\") {
|
||||||
|
return Err(USimpleError::new(
|
||||||
|
1,
|
||||||
|
format!(
|
||||||
|
"delimiter list ends with an unescaped backslash: {}",
|
||||||
|
delimiters
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let delimiters: Vec<char> = unescape(delimiters).chars().collect();
|
let delimiters: Vec<char> = unescape(delimiters).chars().collect();
|
||||||
let mut delim_count = 0;
|
let mut delim_count = 0;
|
||||||
let mut delim_length = 1;
|
let mut delim_length = 1;
|
||||||
|
@ -222,10 +232,8 @@ fn paste(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unescape all special characters
|
// Unescape all special characters
|
||||||
// TODO: this will need work to conform to GNU implementation
|
|
||||||
fn unescape(s: &str) -> String {
|
fn unescape(s: &str) -> String {
|
||||||
s.replace("\\n", "\n")
|
s.replace("\\n", "\n")
|
||||||
.replace("\\t", "\t")
|
.replace("\\t", "\t")
|
||||||
.replace("\\\\", "\\")
|
.replace("\\\\", "\\")
|
||||||
.replace('\\', "")
|
|
||||||
}
|
}
|
||||||
|
|
21
src/uu/sort/sort.md
Normal file
21
src/uu/sort/sort.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<!-- spell-checker:ignore MbdfhnRrV -->
|
||||||
|
|
||||||
|
# sort
|
||||||
|
|
||||||
|
```
|
||||||
|
sort [OPTION]... [FILE]...
|
||||||
|
```
|
||||||
|
|
||||||
|
Display sorted concatenation of all FILE(s). With no FILE, or when FILE is -, read standard input.
|
||||||
|
|
||||||
|
## After help
|
||||||
|
|
||||||
|
The key format is `FIELD[.CHAR][OPTIONS][,FIELD[.CHAR]][OPTIONS]`.
|
||||||
|
|
||||||
|
Fields by default are separated by the first whitespace after a non-whitespace character. Use `-t` to specify a custom separator.
|
||||||
|
In the default case, whitespace is appended at the beginning of each field. Custom separators however are not included in fields.
|
||||||
|
|
||||||
|
`FIELD` and `CHAR` both start at 1 (i.e. they are 1-indexed). If there is no end specified after a comma, the end will be the end of the line.
|
||||||
|
If `CHAR` is set 0, it means the end of the field. `CHAR` defaults to 1 for the start position and to 0 for the end position.
|
||||||
|
|
||||||
|
Valid options are: `MbdfhnRrV`. They override the global options for this key.
|
|
@ -45,26 +45,15 @@ use std::str::Utf8Error;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{set_exit_code, strip_errno, UError, UResult, USimpleError, UUsageError};
|
use uucore::error::{set_exit_code, strip_errno, UError, UResult, USimpleError, UUsageError};
|
||||||
use uucore::format_usage;
|
|
||||||
use uucore::parse_size::{ParseSizeError, Parser};
|
use uucore::parse_size::{ParseSizeError, Parser};
|
||||||
use uucore::version_cmp::version_cmp;
|
use uucore::version_cmp::version_cmp;
|
||||||
|
use uucore::{format_usage, help_about, help_section, help_usage};
|
||||||
|
|
||||||
use crate::tmp_dir::TmpDirWrapper;
|
use crate::tmp_dir::TmpDirWrapper;
|
||||||
|
|
||||||
const ABOUT: &str = "\
|
const ABOUT: &str = help_about!("sort.md");
|
||||||
Display sorted concatenation of all FILE(s). \
|
const USAGE: &str = help_usage!("sort.md");
|
||||||
With no FILE, or when FILE is -, read standard input.";
|
const AFTER_HELP: &str = help_section!("after help", "sort.md");
|
||||||
const USAGE: &str = "{} [OPTION]... [FILE]...";
|
|
||||||
|
|
||||||
const LONG_HELP_KEYS: &str = "The key format is FIELD[.CHAR][OPTIONS][,FIELD[.CHAR]][OPTIONS].
|
|
||||||
|
|
||||||
Fields by default are separated by the first whitespace after a non-whitespace character. Use -t to specify a custom separator.
|
|
||||||
In the default case, whitespace is appended at the beginning of each field. Custom separators however are not included in fields.
|
|
||||||
|
|
||||||
FIELD and CHAR both start at 1 (i.e. they are 1-indexed). If there is no end specified after a comma, the end will be the end of the line.
|
|
||||||
If CHAR is set 0, it means the end of the field. CHAR defaults to 1 for the start position and to 0 for the end position.
|
|
||||||
|
|
||||||
Valid options are: MbdfhnRrV. They override the global options for this key.";
|
|
||||||
|
|
||||||
mod options {
|
mod options {
|
||||||
pub mod modes {
|
pub mod modes {
|
||||||
|
@ -1292,7 +1281,7 @@ pub fn uu_app() -> Command {
|
||||||
Command::new(uucore::util_name())
|
Command::new(uucore::util_name())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about(ABOUT)
|
.about(ABOUT)
|
||||||
.after_help(LONG_HELP_KEYS)
|
.after_help(AFTER_HELP)
|
||||||
.override_usage(format_usage(USAGE))
|
.override_usage(format_usage(USAGE))
|
||||||
.infer_long_args(true)
|
.infer_long_args(true)
|
||||||
.disable_help_flag(true)
|
.disable_help_flag(true)
|
||||||
|
|
13
src/uu/split/split.md
Normal file
13
src/uu/split/split.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!-- spell-checker:ignore PREFI -->
|
||||||
|
|
||||||
|
# split
|
||||||
|
|
||||||
|
```
|
||||||
|
split [OPTION]... [INPUT [PREFIX]]
|
||||||
|
```
|
||||||
|
|
||||||
|
Create output files containing consecutive or interleaved sections of input
|
||||||
|
|
||||||
|
## After Help
|
||||||
|
|
||||||
|
Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is -, read standard input.
|
|
@ -5,7 +5,7 @@
|
||||||
// * For the full copyright and license information, please view the LICENSE
|
// * For the full copyright and license information, please view the LICENSE
|
||||||
// * file that was distributed with this source code.
|
// * file that was distributed with this source code.
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) PREFIXaa PREFIXab nbbbb ncccc
|
// spell-checker:ignore nbbbb ncccc
|
||||||
|
|
||||||
mod filenames;
|
mod filenames;
|
||||||
mod number;
|
mod number;
|
||||||
|
@ -23,9 +23,9 @@ use std::io::{stdin, BufRead, BufReader, BufWriter, ErrorKind, Read, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{FromIo, UIoError, UResult, USimpleError, UUsageError};
|
use uucore::error::{FromIo, UIoError, UResult, USimpleError, UUsageError};
|
||||||
use uucore::format_usage;
|
|
||||||
use uucore::parse_size::{parse_size, ParseSizeError};
|
use uucore::parse_size::{parse_size, ParseSizeError};
|
||||||
use uucore::uio_error;
|
use uucore::uio_error;
|
||||||
|
use uucore::{format_usage, help_about, help_section, help_usage};
|
||||||
|
|
||||||
static OPT_BYTES: &str = "bytes";
|
static OPT_BYTES: &str = "bytes";
|
||||||
static OPT_LINE_BYTES: &str = "line-bytes";
|
static OPT_LINE_BYTES: &str = "line-bytes";
|
||||||
|
@ -47,11 +47,9 @@ static OPT_ELIDE_EMPTY_FILES: &str = "elide-empty-files";
|
||||||
static ARG_INPUT: &str = "input";
|
static ARG_INPUT: &str = "input";
|
||||||
static ARG_PREFIX: &str = "prefix";
|
static ARG_PREFIX: &str = "prefix";
|
||||||
|
|
||||||
const USAGE: &str = "{} [OPTION]... [INPUT [PREFIX]]";
|
const ABOUT: &str = help_about!("split.md");
|
||||||
const AFTER_HELP: &str = "\
|
const USAGE: &str = help_usage!("split.md");
|
||||||
Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default \
|
const AFTER_HELP: &str = help_section!("after help", "split.md");
|
||||||
size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is \
|
|
||||||
-, read standard input.";
|
|
||||||
|
|
||||||
#[uucore::main]
|
#[uucore::main]
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
@ -66,7 +64,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
pub fn uu_app() -> Command {
|
pub fn uu_app() -> Command {
|
||||||
Command::new(uucore::util_name())
|
Command::new(uucore::util_name())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about("Create output files containing consecutive or interleaved sections of input")
|
.about(ABOUT)
|
||||||
.after_help(AFTER_HELP)
|
.after_help(AFTER_HELP)
|
||||||
.override_usage(format_usage(USAGE))
|
.override_usage(format_usage(USAGE))
|
||||||
.infer_long_args(true)
|
.infer_long_args(true)
|
||||||
|
|
|
@ -17,23 +17,11 @@ use tempfile::tempdir;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
|
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
|
||||||
use uucore::parse_size::parse_size;
|
use uucore::parse_size::parse_size;
|
||||||
use uucore::{crash, format_usage};
|
use uucore::{crash, format_usage, help_about, help_section, help_usage};
|
||||||
|
|
||||||
static ABOUT: &str =
|
const ABOUT: &str = help_about!("stdbuf.md");
|
||||||
"Run COMMAND, with modified buffering operations for its standard streams.\n\n\
|
const USAGE: &str = help_usage!("stdbuf.md");
|
||||||
Mandatory arguments to long options are mandatory for short options too.";
|
const LONG_HELP: &str = help_section!("after help", "stdbuf.md");
|
||||||
const USAGE: &str = "{} OPTION... COMMAND";
|
|
||||||
static LONG_HELP: &str = "If MODE is 'L' the corresponding stream will be line buffered.\n\
|
|
||||||
This option is invalid with standard input.\n\n\
|
|
||||||
If MODE is '0' the corresponding stream will be unbuffered.\n\n\
|
|
||||||
Otherwise MODE is a number which may be followed by one of the following:\n\n\
|
|
||||||
KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
|
|
||||||
In this case the corresponding stream will be fully buffered with the buffer size set to \
|
|
||||||
MODE bytes.\n\n\
|
|
||||||
NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for e.g.) then \
|
|
||||||
that will override corresponding settings changed by 'stdbuf'.\n\
|
|
||||||
Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O, \
|
|
||||||
and are thus unaffected by 'stdbuf' settings.\n";
|
|
||||||
|
|
||||||
mod options {
|
mod options {
|
||||||
pub const INPUT: &str = "input";
|
pub const INPUT: &str = "input";
|
||||||
|
|
24
src/uu/stdbuf/stdbuf.md
Normal file
24
src/uu/stdbuf/stdbuf.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# stdbuf
|
||||||
|
|
||||||
|
```
|
||||||
|
stdbuf [OPTION]... COMMAND
|
||||||
|
```
|
||||||
|
|
||||||
|
Run `COMMAND`, with modified buffering operations for its standard streams.
|
||||||
|
|
||||||
|
Mandatory arguments to long options are mandatory for short options too.
|
||||||
|
|
||||||
|
## After Help
|
||||||
|
|
||||||
|
If `MODE` is 'L' the corresponding stream will be line buffered.
|
||||||
|
This option is invalid with standard input.
|
||||||
|
|
||||||
|
If `MODE` is '0' the corresponding stream will be unbuffered.
|
||||||
|
|
||||||
|
Otherwise, `MODE` is a number which may be followed by one of the following:
|
||||||
|
|
||||||
|
KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.
|
||||||
|
In this case the corresponding stream will be fully buffered with the buffer size set to `MODE` bytes.
|
||||||
|
|
||||||
|
NOTE: If `COMMAND` adjusts the buffering of its standard streams (`tee` does for e.g.) then that will override corresponding settings changed by `stdbuf`.
|
||||||
|
Also some filters (like `dd` and `cat` etc.) don't use streams for I/O, and are thus unaffected by `stdbuf` settings.
|
|
@ -18,7 +18,7 @@ use std::ops::ControlFlow;
|
||||||
use std::os::unix::fs::OpenOptionsExt;
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||||
use uucore::error::{UResult, USimpleError};
|
use uucore::error::{UResult, USimpleError};
|
||||||
use uucore::format_usage;
|
use uucore::{format_usage, help_about, help_usage};
|
||||||
|
|
||||||
#[cfg(not(any(
|
#[cfg(not(any(
|
||||||
target_os = "freebsd",
|
target_os = "freebsd",
|
||||||
|
@ -31,11 +31,8 @@ use uucore::format_usage;
|
||||||
use flags::BAUD_RATES;
|
use flags::BAUD_RATES;
|
||||||
use flags::{CONTROL_FLAGS, INPUT_FLAGS, LOCAL_FLAGS, OUTPUT_FLAGS};
|
use flags::{CONTROL_FLAGS, INPUT_FLAGS, LOCAL_FLAGS, OUTPUT_FLAGS};
|
||||||
|
|
||||||
const USAGE: &str = "\
|
const USAGE: &str = help_usage!("stty.md");
|
||||||
{} [-F DEVICE | --file=DEVICE] [SETTING]...
|
const SUMMARY: &str = help_about!("stty.md");
|
||||||
{} [-F DEVICE | --file=DEVICE] [-a|--all]
|
|
||||||
{} [-F DEVICE | --file=DEVICE] [-g|--save]";
|
|
||||||
const SUMMARY: &str = "Print or change terminal characteristics.";
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Flag<T> {
|
pub struct Flag<T> {
|
||||||
|
|
9
src/uu/stty/stty.md
Normal file
9
src/uu/stty/stty.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# stty
|
||||||
|
|
||||||
|
```
|
||||||
|
stty [-F DEVICE | --file=DEVICE] [SETTING]...
|
||||||
|
stty [-F DEVICE | --file=DEVICE] [-a|--all]
|
||||||
|
stty [-F DEVICE | --file=DEVICE] [-g|--save]
|
||||||
|
```
|
||||||
|
|
||||||
|
Print or change terminal characteristics.
|
|
@ -13,11 +13,10 @@ use std::io::{stdin, Read};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{FromIo, UResult, USimpleError};
|
use uucore::error::{FromIo, UResult, USimpleError};
|
||||||
use uucore::{format_usage, show};
|
use uucore::{format_usage, help_about, help_usage, show};
|
||||||
|
|
||||||
static USAGE: &str = "{} [OPTION]... [FILE]...";
|
const USAGE: &str = help_usage!("sum.md");
|
||||||
static ABOUT: &str = "Checksum and count the blocks in a file.\n\n\
|
const ABOUT: &str = help_about!("sum.md");
|
||||||
With no FILE, or when FILE is -, read standard input.";
|
|
||||||
|
|
||||||
// This can be replaced with usize::div_ceil once it is stabilized.
|
// This can be replaced with usize::div_ceil once it is stabilized.
|
||||||
// This implementation approach is optimized for when `b` is a constant,
|
// This implementation approach is optimized for when `b` is a constant,
|
||||||
|
|
9
src/uu/sum/sum.md
Normal file
9
src/uu/sum/sum.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# sum
|
||||||
|
|
||||||
|
```
|
||||||
|
sum [OPTION]... [FILE]..."
|
||||||
|
```
|
||||||
|
|
||||||
|
Checksum and count the blocks in a file.
|
||||||
|
|
||||||
|
With no FILE, or when FILE is -, read standard input.
|
|
@ -19,10 +19,11 @@ use uucore::display::Quotable;
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
use uucore::error::FromIo;
|
use uucore::error::FromIo;
|
||||||
use uucore::error::{UResult, USimpleError};
|
use uucore::error::{UResult, USimpleError};
|
||||||
use uucore::format_usage;
|
use uucore::{format_usage, help_about, help_usage};
|
||||||
|
|
||||||
|
const ABOUT: &str = help_about!("sync.md");
|
||||||
|
const USAGE: &str = help_usage!("sync.md");
|
||||||
|
|
||||||
static ABOUT: &str = "Synchronize cached writes to persistent storage";
|
|
||||||
const USAGE: &str = "{} [OPTION]... FILE...";
|
|
||||||
pub mod options {
|
pub mod options {
|
||||||
pub static FILE_SYSTEM: &str = "file-system";
|
pub static FILE_SYSTEM: &str = "file-system";
|
||||||
pub static DATA: &str = "data";
|
pub static DATA: &str = "data";
|
||||||
|
|
7
src/uu/sync/sync.md
Normal file
7
src/uu/sync/sync.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# sync
|
||||||
|
|
||||||
|
```
|
||||||
|
sync [OPTION]... FILE...
|
||||||
|
```
|
||||||
|
|
||||||
|
Synchronize cached writes to persistent storage
|
|
@ -22,12 +22,12 @@ use uucore::process::ChildExt;
|
||||||
use uucore::signals::enable_pipe_errors;
|
use uucore::signals::enable_pipe_errors;
|
||||||
|
|
||||||
use uucore::{
|
use uucore::{
|
||||||
format_usage, show_error,
|
format_usage, help_about, help_usage, show_error,
|
||||||
signals::{signal_by_name_or_value, signal_name_by_value},
|
signals::{signal_by_name_or_value, signal_name_by_value},
|
||||||
};
|
};
|
||||||
|
|
||||||
static ABOUT: &str = "Start COMMAND, and kill it if still running after DURATION.";
|
const ABOUT: &str = help_about!("timeout.md");
|
||||||
const USAGE: &str = "{} [OPTION] DURATION COMMAND...";
|
const USAGE: &str = help_usage!("timeout.md");
|
||||||
|
|
||||||
pub mod options {
|
pub mod options {
|
||||||
pub static FOREGROUND: &str = "foreground";
|
pub static FOREGROUND: &str = "foreground";
|
||||||
|
|
7
src/uu/timeout/timeout.md
Normal file
7
src/uu/timeout/timeout.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# timeout
|
||||||
|
|
||||||
|
```
|
||||||
|
timeout [OPTION] DURATION COMMAND...
|
||||||
|
```
|
||||||
|
|
||||||
|
Start `COMMAND`, and kill it if still running after `DURATION`.
|
|
@ -13,17 +13,15 @@ use clap::{crate_version, Arg, ArgAction, Command};
|
||||||
use nom::AsBytes;
|
use nom::AsBytes;
|
||||||
use operation::{translate_input, Sequence, SqueezeOperation, TranslateOperation};
|
use operation::{translate_input, Sequence, SqueezeOperation, TranslateOperation};
|
||||||
use std::io::{stdin, stdout, BufReader, BufWriter};
|
use std::io::{stdin, stdout, BufReader, BufWriter};
|
||||||
use uucore::{format_usage, show};
|
use uucore::{format_usage, help_about, help_section, help_usage, show};
|
||||||
|
|
||||||
use crate::operation::DeleteOperation;
|
use crate::operation::DeleteOperation;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{UResult, USimpleError, UUsageError};
|
use uucore::error::{UResult, USimpleError, UUsageError};
|
||||||
|
|
||||||
const ABOUT: &str = "Translate or delete characters";
|
const ABOUT: &str = help_about!("tr.md");
|
||||||
const USAGE: &str = "{} [OPTION]... SET1 [SET2]";
|
const AFTER_HELP: &str = help_section!("after help", "tr.md");
|
||||||
const LONG_USAGE: &str = "\
|
const USAGE: &str = help_usage!("tr.md");
|
||||||
Translate, squeeze, and/or delete characters from standard input, \
|
|
||||||
writing to standard output.";
|
|
||||||
|
|
||||||
mod options {
|
mod options {
|
||||||
pub const COMPLEMENT: &str = "complement";
|
pub const COMPLEMENT: &str = "complement";
|
||||||
|
@ -37,7 +35,7 @@ mod options {
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let args = args.collect_lossy();
|
let args = args.collect_lossy();
|
||||||
|
|
||||||
let matches = uu_app().after_help(LONG_USAGE).try_get_matches_from(args)?;
|
let matches = uu_app().after_help(AFTER_HELP).try_get_matches_from(args)?;
|
||||||
|
|
||||||
let delete_flag = matches.get_flag(options::DELETE);
|
let delete_flag = matches.get_flag(options::DELETE);
|
||||||
let complement_flag = matches.get_flag(options::COMPLEMENT);
|
let complement_flag = matches.get_flag(options::COMPLEMENT);
|
||||||
|
|
11
src/uu/tr/tr.md
Normal file
11
src/uu/tr/tr.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# tr
|
||||||
|
|
||||||
|
```
|
||||||
|
tr [OPTION]... SET1 [SET2]
|
||||||
|
```
|
||||||
|
|
||||||
|
Translate or delete characters
|
||||||
|
|
||||||
|
## After help
|
||||||
|
|
||||||
|
Translate, squeeze, and/or delete characters from standard input, writing to standard output.
|
|
@ -334,6 +334,29 @@ fn test_invalid_format_string() {
|
||||||
assert!(result.stderr_str().starts_with("date: invalid format "));
|
assert!(result.stderr_str().starts_with("date: invalid format "));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_date_string_human() {
|
||||||
|
let date_formats = vec![
|
||||||
|
"1 year ago",
|
||||||
|
"1 year",
|
||||||
|
"2 months ago",
|
||||||
|
"15 days ago",
|
||||||
|
"1 week ago",
|
||||||
|
"5 hours ago",
|
||||||
|
"30 minutes ago",
|
||||||
|
"10 seconds",
|
||||||
|
];
|
||||||
|
let re = Regex::new(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}\n$").unwrap();
|
||||||
|
for date_format in date_formats {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("-d")
|
||||||
|
.arg(date_format)
|
||||||
|
.arg("+%Y-%m-%d %S:%M")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_matches(&re);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_date_string() {
|
fn test_invalid_date_string() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc
|
// spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc
|
||||||
|
|
||||||
#[cfg(feature = "feat_selinux")]
|
#[cfg(any(unix, feature = "feat_selinux"))]
|
||||||
use crate::common::util::expected_result;
|
use crate::common::util::expected_result;
|
||||||
use crate::common::util::TestScenario;
|
use crate::common::util::TestScenario;
|
||||||
#[cfg(all(unix, feature = "chmod"))]
|
#[cfg(all(unix, feature = "chmod"))]
|
||||||
|
@ -1276,7 +1276,7 @@ fn test_ls_long_formats() {
|
||||||
// Zero or one "." for indicating a file with security context
|
// Zero or one "." for indicating a file with security context
|
||||||
|
|
||||||
// Regex for three names, so all of author, group and owner
|
// Regex for three names, so all of author, group and owner
|
||||||
let re_three = Regex::new(r"[xrw-]{9}\.? \d ([-0-9_a-z_A-Z]+ ){3}0").unwrap();
|
let re_three = Regex::new(r"[xrw-]{9}\.? \d ([-0-9_a-z.A-Z]+ ){3}0").unwrap();
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let re_three_num = Regex::new(r"[xrw-]{9}\.? \d (\d+ ){3}0").unwrap();
|
let re_three_num = Regex::new(r"[xrw-]{9}\.? \d (\d+ ){3}0").unwrap();
|
||||||
|
@ -1285,13 +1285,13 @@ fn test_ls_long_formats() {
|
||||||
// - group and owner
|
// - group and owner
|
||||||
// - author and owner
|
// - author and owner
|
||||||
// - author and group
|
// - author and group
|
||||||
let re_two = Regex::new(r"[xrw-]{9}\.? \d ([-0-9_a-z_A-Z]+ ){2}0").unwrap();
|
let re_two = Regex::new(r"[xrw-]{9}\.? \d ([-0-9_a-z.A-Z]+ ){2}0").unwrap();
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let re_two_num = Regex::new(r"[xrw-]{9}\.? \d (\d+ ){2}0").unwrap();
|
let re_two_num = Regex::new(r"[xrw-]{9}\.? \d (\d+ ){2}0").unwrap();
|
||||||
|
|
||||||
// Regex for one name: author, group or owner
|
// Regex for one name: author, group or owner
|
||||||
let re_one = Regex::new(r"[xrw-]{9}\.? \d [-0-9_a-z_A-Z]+ 0").unwrap();
|
let re_one = Regex::new(r"[xrw-]{9}\.? \d [-0-9_a-z.A-Z]+ 0").unwrap();
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let re_one_num = Regex::new(r"[xrw-]{9}\.? \d \d+ 0").unwrap();
|
let re_one_num = Regex::new(r"[xrw-]{9}\.? \d \d+ 0").unwrap();
|
||||||
|
@ -1640,88 +1640,103 @@ fn test_ls_styles() {
|
||||||
at.touch("test");
|
at.touch("test");
|
||||||
|
|
||||||
let re_full = Regex::new(
|
let re_full = Regex::new(
|
||||||
r"[a-z-]* \d* \w* \w* \d* \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d* (\+|\-)\d{4} test\n",
|
r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d* (\+|\-)\d{4} test\n",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let re_long =
|
let re_long =
|
||||||
Regex::new(r"[a-z-]* \d* \w* \w* \d* \d{4}-\d{2}-\d{2} \d{2}:\d{2} test\n").unwrap();
|
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}-\d{2}-\d{2} \d{2}:\d{2} test\n").unwrap();
|
||||||
let re_iso = Regex::new(r"[a-z-]* \d* \w* \w* \d* \d{2}-\d{2} \d{2}:\d{2} test\n").unwrap();
|
let re_iso =
|
||||||
|
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{2}-\d{2} \d{2}:\d{2} test\n").unwrap();
|
||||||
let re_locale =
|
let re_locale =
|
||||||
Regex::new(r"[a-z-]* \d* \w* \w* \d* [A-Z][a-z]{2} ( |\d)\d \d{2}:\d{2} test\n").unwrap();
|
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* [A-Z][a-z]{2} ( |\d)\d \d{2}:\d{2} test\n")
|
||||||
let re_custom_format = Regex::new(r"[a-z-]* \d* \w* \w* \d* \d{4}__\d{2} test\n").unwrap();
|
.unwrap();
|
||||||
|
let re_custom_format =
|
||||||
|
Regex::new(r"[a-z-]* \d* [\w.]* [\w.]* \d* \d{4}__\d{2} test\n").unwrap();
|
||||||
|
|
||||||
//full-iso
|
//full-iso
|
||||||
let result = scene
|
scene
|
||||||
.ucmd()
|
.ucmd()
|
||||||
.arg("-l")
|
.arg("-l")
|
||||||
.arg("--time-style=full-iso")
|
.arg("--time-style=full-iso")
|
||||||
.succeeds();
|
.succeeds()
|
||||||
assert!(re_full.is_match(result.stdout_str()));
|
.stdout_matches(&re_full);
|
||||||
//long-iso
|
//long-iso
|
||||||
let result = scene
|
scene
|
||||||
.ucmd()
|
.ucmd()
|
||||||
.arg("-l")
|
.arg("-l")
|
||||||
.arg("--time-style=long-iso")
|
.arg("--time-style=long-iso")
|
||||||
.succeeds();
|
.succeeds()
|
||||||
assert!(re_long.is_match(result.stdout_str()));
|
.stdout_matches(&re_long);
|
||||||
//iso
|
//iso
|
||||||
let result = scene.ucmd().arg("-l").arg("--time-style=iso").succeeds();
|
scene
|
||||||
assert!(re_iso.is_match(result.stdout_str()));
|
.ucmd()
|
||||||
|
.arg("-l")
|
||||||
|
.arg("--time-style=iso")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_matches(&re_iso);
|
||||||
//locale
|
//locale
|
||||||
let result = scene.ucmd().arg("-l").arg("--time-style=locale").succeeds();
|
scene
|
||||||
assert!(re_locale.is_match(result.stdout_str()));
|
.ucmd()
|
||||||
|
.arg("-l")
|
||||||
|
.arg("--time-style=locale")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_matches(&re_locale);
|
||||||
|
|
||||||
//+FORMAT
|
//+FORMAT
|
||||||
let result = scene
|
scene
|
||||||
.ucmd()
|
.ucmd()
|
||||||
.arg("-l")
|
.arg("-l")
|
||||||
.arg("--time-style=+%Y__%M")
|
.arg("--time-style=+%Y__%M")
|
||||||
.succeeds();
|
.succeeds()
|
||||||
assert!(re_custom_format.is_match(result.stdout_str()));
|
.stdout_matches(&re_custom_format);
|
||||||
|
|
||||||
// Also fails due to not having full clap support for time_styles
|
// Also fails due to not having full clap support for time_styles
|
||||||
scene.ucmd().arg("-l").arg("-time-style=invalid").fails();
|
scene.ucmd().arg("-l").arg("-time-style=invalid").fails();
|
||||||
|
|
||||||
//Overwrite options tests
|
//Overwrite options tests
|
||||||
let result = scene
|
scene
|
||||||
.ucmd()
|
.ucmd()
|
||||||
.arg("-l")
|
.arg("-l")
|
||||||
.arg("--time-style=long-iso")
|
.arg("--time-style=long-iso")
|
||||||
.arg("--time-style=iso")
|
.arg("--time-style=iso")
|
||||||
.succeeds();
|
.succeeds()
|
||||||
assert!(re_iso.is_match(result.stdout_str()));
|
.stdout_matches(&re_iso);
|
||||||
let result = scene
|
scene
|
||||||
.ucmd()
|
.ucmd()
|
||||||
.arg("--time-style=iso")
|
.arg("--time-style=iso")
|
||||||
.arg("--full-time")
|
.arg("--full-time")
|
||||||
.succeeds();
|
.succeeds()
|
||||||
assert!(re_full.is_match(result.stdout_str()));
|
.stdout_matches(&re_full);
|
||||||
let result = scene
|
scene
|
||||||
.ucmd()
|
.ucmd()
|
||||||
.arg("--full-time")
|
.arg("--full-time")
|
||||||
.arg("--time-style=iso")
|
.arg("--time-style=iso")
|
||||||
.succeeds();
|
.succeeds()
|
||||||
assert!(re_iso.is_match(result.stdout_str()));
|
.stdout_matches(&re_iso);
|
||||||
|
|
||||||
let result = scene
|
scene
|
||||||
.ucmd()
|
.ucmd()
|
||||||
.arg("--full-time")
|
.arg("--full-time")
|
||||||
.arg("--time-style=iso")
|
.arg("--time-style=iso")
|
||||||
.arg("--full-time")
|
.arg("--full-time")
|
||||||
.succeeds();
|
.succeeds()
|
||||||
assert!(re_full.is_match(result.stdout_str()));
|
.stdout_matches(&re_full);
|
||||||
|
|
||||||
let result = scene
|
scene
|
||||||
.ucmd()
|
.ucmd()
|
||||||
.arg("--full-time")
|
.arg("--full-time")
|
||||||
.arg("-x")
|
.arg("-x")
|
||||||
.arg("-l")
|
.arg("-l")
|
||||||
.succeeds();
|
.succeeds()
|
||||||
assert!(re_full.is_match(result.stdout_str()));
|
.stdout_matches(&re_full);
|
||||||
|
|
||||||
at.touch("test2");
|
at.touch("test2");
|
||||||
let result = scene.ucmd().arg("--full-time").arg("-x").succeeds();
|
scene
|
||||||
assert_eq!(result.stdout_str(), "test test2\n");
|
.ucmd()
|
||||||
|
.arg("--full-time")
|
||||||
|
.arg("-x")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is("test test2\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1776,9 +1791,15 @@ fn test_ls_order_time() {
|
||||||
at.open("test-4").metadata().unwrap().accessed().unwrap();
|
at.open("test-4").metadata().unwrap().accessed().unwrap();
|
||||||
|
|
||||||
// It seems to be dependent on the platform whether the access time is actually set
|
// It seems to be dependent on the platform whether the access time is actually set
|
||||||
#[cfg(all(unix, not(target_os = "android")))]
|
#[cfg(unix)]
|
||||||
result.stdout_only("test-3\ntest-4\ntest-2\ntest-1\n");
|
{
|
||||||
#[cfg(any(windows, target_os = "android"))]
|
let expected = unwrap_or_return!(expected_result(&scene, &["-t", arg]));
|
||||||
|
at.open("test-3").metadata().unwrap().accessed().unwrap();
|
||||||
|
at.open("test-4").metadata().unwrap().accessed().unwrap();
|
||||||
|
|
||||||
|
result.stdout_only(expected.stdout_str());
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
|
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ static TEST_TEMPLATE7: &str = "XXXtemplate"; // spell-checker:disable-line
|
||||||
static TEST_TEMPLATE8: &str = "tempXXXl/ate";
|
static TEST_TEMPLATE8: &str = "tempXXXl/ate";
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
static TEST_TEMPLATE8: &str = "tempXXXl\\ate";
|
static TEST_TEMPLATE8: &str = "tempXXXl\\ate";
|
||||||
|
static TEST_TEMPLATE9: &str = "a.XXXX";
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
const TMPDIR: &str = "TMPDIR";
|
const TMPDIR: &str = "TMPDIR";
|
||||||
|
@ -569,6 +570,34 @@ fn test_template_path_separator() {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test that a prefix with a point is valid.
|
||||||
|
#[test]
|
||||||
|
fn test_prefix_template_separator() {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-p", ".", "-t", TEST_TEMPLATE9])
|
||||||
|
.succeeds();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prefix_template_with_path_separator() {
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-t", "a/XXX"])
|
||||||
|
.fails()
|
||||||
|
.stderr_only(format!(
|
||||||
|
"mktemp: invalid template, {}, contains directory separator\n",
|
||||||
|
"a/XXX".quote()
|
||||||
|
));
|
||||||
|
#[cfg(windows)]
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-t", r"a\XXX"])
|
||||||
|
.fails()
|
||||||
|
.stderr_only(format!(
|
||||||
|
"mktemp: invalid template, {}, contains directory separator\n",
|
||||||
|
r"a\XXX".quote()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
/// Test that a suffix with a path separator is invalid.
|
/// Test that a suffix with a path separator is invalid.
|
||||||
#[test]
|
#[test]
|
||||||
fn test_suffix_path_separator() {
|
fn test_suffix_path_separator() {
|
||||||
|
|
|
@ -57,6 +57,63 @@ fn test_mv_move_file_into_dir() {
|
||||||
assert!(at.file_exists(format!("{dir}/{file}")));
|
assert!(at.file_exists(format!("{dir}/{file}")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mv_move_file_into_dir_with_target_arg() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
let dir = "test_mv_move_file_into_dir_with_target_arg_dir";
|
||||||
|
let file = "test_mv_move_file_into_dir_with_target_arg_file";
|
||||||
|
|
||||||
|
at.mkdir(dir);
|
||||||
|
at.touch(file);
|
||||||
|
|
||||||
|
ucmd.arg("--target")
|
||||||
|
.arg(dir)
|
||||||
|
.arg(file)
|
||||||
|
.succeeds()
|
||||||
|
.no_stderr();
|
||||||
|
|
||||||
|
assert!(at.file_exists(format!("{dir}/{file}")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mv_move_file_into_file_with_target_arg() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
let file1 = "test_mv_move_file_into_file_with_target_arg_file1";
|
||||||
|
let file2 = "test_mv_move_file_into_file_with_target_arg_file2";
|
||||||
|
|
||||||
|
at.touch(file1);
|
||||||
|
at.touch(file2);
|
||||||
|
|
||||||
|
ucmd.arg("--target")
|
||||||
|
.arg(file1)
|
||||||
|
.arg(file2)
|
||||||
|
.fails()
|
||||||
|
.stderr_is(format!("mv: target directory '{file1}': Not a directory\n"));
|
||||||
|
|
||||||
|
assert!(at.file_exists(file1))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mv_move_multiple_files_into_file() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
let file1 = "test_mv_move_multiple_files_into_file1";
|
||||||
|
let file2 = "test_mv_move_multiple_files_into_file2";
|
||||||
|
let file3 = "test_mv_move_multiple_files_into_file3";
|
||||||
|
|
||||||
|
at.touch(file1);
|
||||||
|
at.touch(file2);
|
||||||
|
at.touch(file3);
|
||||||
|
|
||||||
|
ucmd.arg(file1)
|
||||||
|
.arg(file2)
|
||||||
|
.arg(file3)
|
||||||
|
.fails()
|
||||||
|
.stderr_is(format!("mv: target '{file3}': Not a directory\n"));
|
||||||
|
|
||||||
|
assert!(at.file_exists(file1));
|
||||||
|
assert!(at.file_exists(file2));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mv_move_file_between_dirs() {
|
fn test_mv_move_file_between_dirs() {
|
||||||
let (at, mut ucmd) = at_and_ucmd!();
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
@ -234,8 +291,9 @@ fn test_mv_no_clobber() {
|
||||||
ucmd.arg("-n")
|
ucmd.arg("-n")
|
||||||
.arg(file_a)
|
.arg(file_a)
|
||||||
.arg(file_b)
|
.arg(file_b)
|
||||||
.succeeds()
|
.fails()
|
||||||
.no_stderr();
|
.code_is(1)
|
||||||
|
.stderr_only(format!("mv: not replacing '{file_b}'\n"));
|
||||||
|
|
||||||
assert!(at.file_exists(file_a));
|
assert!(at.file_exists(file_a));
|
||||||
assert!(at.file_exists(file_b));
|
assert!(at.file_exists(file_b));
|
||||||
|
@ -909,7 +967,9 @@ fn test_mv_backup_dir() {
|
||||||
.arg(dir_a)
|
.arg(dir_a)
|
||||||
.arg(dir_b)
|
.arg(dir_b)
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_only(format!("'{dir_a}' -> '{dir_b}' (backup: '{dir_b}~')\n"));
|
.stdout_only(format!(
|
||||||
|
"renamed '{dir_a}' -> '{dir_b}' (backup: '{dir_b}~')\n"
|
||||||
|
));
|
||||||
|
|
||||||
assert!(!at.dir_exists(dir_a));
|
assert!(!at.dir_exists(dir_a));
|
||||||
assert!(at.dir_exists(dir_b));
|
assert!(at.dir_exists(dir_b));
|
||||||
|
@ -981,7 +1041,7 @@ fn test_mv_verbose() {
|
||||||
.arg(file_a)
|
.arg(file_a)
|
||||||
.arg(file_b)
|
.arg(file_b)
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_only(format!("'{file_a}' -> '{file_b}'\n"));
|
.stdout_only(format!("renamed '{file_a}' -> '{file_b}'\n"));
|
||||||
|
|
||||||
at.touch(file_a);
|
at.touch(file_a);
|
||||||
scene
|
scene
|
||||||
|
@ -990,7 +1050,9 @@ fn test_mv_verbose() {
|
||||||
.arg(file_a)
|
.arg(file_a)
|
||||||
.arg(file_b)
|
.arg(file_b)
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_only(format!("'{file_a}' -> '{file_b}' (backup: '{file_b}~')\n"));
|
.stdout_only(format!(
|
||||||
|
"renamed '{file_a}' -> '{file_b}' (backup: '{file_b}~')\n"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
|
// spell-checker:ignore libc's
|
||||||
use crate::common::util::TestScenario;
|
use crate::common::util::TestScenario;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
fn test_get_current_niceness() {
|
fn test_get_current_niceness() {
|
||||||
// NOTE: this assumes the test suite is being run with a default niceness
|
// Test that the nice command with no arguments returns the default nice
|
||||||
// of 0, which may not necessarily be true
|
// value, which we determine by querying libc's `nice` in our own process.
|
||||||
new_ucmd!().run().stdout_is("0\n");
|
new_ucmd!()
|
||||||
|
.run()
|
||||||
|
.stdout_is(format!("{}\n", unsafe { libc::nice(0) }));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -156,6 +156,39 @@ fn test_multi_stdin() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// TODO: make this test work on Windows
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
fn test_delimiter_list_ending_with_escaped_backslash() {
|
||||||
|
for d in ["-d", "--delimiters"] {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
let mut ins = vec![];
|
||||||
|
for (i, _in) in ["a\n", "b\n"].iter().enumerate() {
|
||||||
|
let file = format!("in{}", i);
|
||||||
|
at.write(&file, _in);
|
||||||
|
ins.push(file);
|
||||||
|
}
|
||||||
|
ucmd.args(&[d, "\\\\"])
|
||||||
|
.args(&ins)
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is("a\\b\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delimiter_list_ending_with_unescaped_backslash() {
|
||||||
|
for d in ["-d", "--delimiters"] {
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&[d, "\\"])
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("delimiter list ends with an unescaped backslash: \\");
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&[d, "_\\"])
|
||||||
|
.fails()
|
||||||
|
.stderr_contains("delimiter list ends with an unescaped backslash: _\\");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_data() {
|
fn test_data() {
|
||||||
for example in EXAMPLE_DATA {
|
for example in EXAMPLE_DATA {
|
||||||
|
|
|
@ -63,6 +63,8 @@ for binary in $(./build-aux/gen-lists-of-programs.sh --list-progs); do
|
||||||
done
|
done
|
||||||
|
|
||||||
if test -f gnu-built; then
|
if test -f gnu-built; then
|
||||||
|
# Change the PATH in the Makefile to test the uutils coreutils instead of the GNU coreutils
|
||||||
|
sed -i "s/^[[:blank:]]*PATH=.*/ PATH='${UU_BUILD_DIR//\//\\/}\$(PATH_SEPARATOR)'\"\$\$PATH\" \\\/" Makefile
|
||||||
echo "GNU build already found. Skip"
|
echo "GNU build already found. Skip"
|
||||||
echo "'rm -f $(pwd)/gnu-built' to force the build"
|
echo "'rm -f $(pwd)/gnu-built' to force the build"
|
||||||
echo "Note: the customization of the tests will still happen"
|
echo "Note: the customization of the tests will still happen"
|
||||||
|
@ -220,6 +222,7 @@ sed -i -Ez "s/\n([^\n#]*pad-3\.2[^\n]*)\n([^\n]*)\n([^\n]*)/\n# uutils\/numfmt s
|
||||||
|
|
||||||
# Update the GNU error message to match the one generated by clap
|
# Update the GNU error message to match the one generated by clap
|
||||||
sed -i -e "s/\$prog: multiple field specifications/error: The argument '--field <FIELDS>' was provided more than once, but cannot be used multiple times\n\nUsage: numfmt [OPTION]... [NUMBER]...\n\n\nFor more information try '--help'/g" tests/misc/numfmt.pl
|
sed -i -e "s/\$prog: multiple field specifications/error: The argument '--field <FIELDS>' was provided more than once, but cannot be used multiple times\n\nUsage: numfmt [OPTION]... [NUMBER]...\n\n\nFor more information try '--help'/g" tests/misc/numfmt.pl
|
||||||
|
sed -i -e "s/Try 'mv --help' for more information/For more information, try '--help'/g" -e "s/mv: missing file operand/error: the following required arguments were not provided:\n <files>...\n\nUsage: mv [OPTION]... [-T] SOURCE DEST\n mv [OPTION]... SOURCE... DIRECTORY\n mv [OPTION]... -t DIRECTORY SOURCE...\n/g" -e "s/mv: missing destination file operand after 'no-file'/error: The argument '<files>...' requires at least 2 values, but only 1 was provided\n\nUsage: mv [OPTION]... [-T] SOURCE DEST\n mv [OPTION]... SOURCE... DIRECTORY\n mv [OPTION]... -t DIRECTORY SOURCE...\n/g" tests/mv/diag.sh
|
||||||
|
|
||||||
# GNU doesn't support width > INT_MAX
|
# GNU doesn't support width > INT_MAX
|
||||||
# disable these test cases
|
# disable these test cases
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue