mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
Merge branch 'main' into ls-version-cmp
This commit is contained in:
@@ -31,12 +31,12 @@ jobs:
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
## VARs setup
|
||||
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
||||
# surface MSRV from CICD workflow
|
||||
RUST_MIN_SRV=$(grep -P "^\s+RUST_MIN_SRV:" .github/workflows/CICD.yml | grep -Po "(?<=\x22)\d+[.]\d+(?:[.]\d+)?(?=\x22)" )
|
||||
outputs RUST_MIN_SRV
|
||||
- uses: dtolnay/rust-toolchain@${{ steps.vars.outputs.RUST_MIN_SRV }}
|
||||
echo "RUST_MIN_SRV=${RUST_MIN_SRV}" >> $GITHUB_OUTPUT
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ steps.vars.outputs.RUST_MIN_SRV }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Ensure updated 'Cargo.lock'
|
||||
shell: bash
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
||||
uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
branch: ${{ env.BRANCH_TARGET }}
|
||||
new_branch: ${{ env.BRANCH_TARGET }}
|
||||
default_author: github_actions
|
||||
message: "maint ~ refresh 'Cargo.lock'"
|
||||
add: Cargo.lock
|
||||
@@ -90,13 +90,11 @@ jobs:
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
## VARs setup
|
||||
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
||||
# target-specific options
|
||||
# * CARGO_FEATURES_OPTION
|
||||
CARGO_FEATURES_OPTION='' ;
|
||||
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
||||
outputs CARGO_FEATURES_OPTION
|
||||
echo "CARGO_FEATURES_OPTION=${CARGO_FEATURES_OPTION}" >> $GITHUB_OUTPUT
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -114,7 +112,7 @@ jobs:
|
||||
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
||||
uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
branch: ${{ env.BRANCH_TARGET }}
|
||||
new_branch: ${{ env.BRANCH_TARGET }}
|
||||
default_author: github_actions
|
||||
message: "maint ~ rustfmt (`cargo fmt`)"
|
||||
env:
|
||||
|
||||
@@ -205,7 +205,7 @@ jobs:
|
||||
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
|
||||
# https://github.com/uutils/coreutils/issues/4294
|
||||
# https://github.com/uutils/coreutils/issues/4295
|
||||
IGNORE_INTERMITTENT='${path_UUTILS}/.github/workflows/ignore-intermittent.txt'
|
||||
IGNORE_INTERMITTENT="${path_UUTILS}/.github/workflows/ignore-intermittent.txt"
|
||||
|
||||
mkdir -p ${{ steps.vars.outputs.path_reference }}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.3
|
||||
- name: Prepare, build and test
|
||||
uses: vmactions/freebsd-vm@v0.3.0
|
||||
uses: vmactions/freebsd-vm@v0.3.1
|
||||
with:
|
||||
usesh: true
|
||||
# We need jq to run show-utils.sh and bash to use inline shell string replacement
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.3
|
||||
- name: Prepare, build and test
|
||||
uses: vmactions/freebsd-vm@v0.3.0
|
||||
uses: vmactions/freebsd-vm@v0.3.1
|
||||
with:
|
||||
usesh: true
|
||||
# sync: sshfs
|
||||
|
||||
@@ -5,6 +5,7 @@ target/
|
||||
/busybox/
|
||||
/.vscode/
|
||||
/.vs/
|
||||
/public/
|
||||
*~
|
||||
.*.swp
|
||||
.*.swo
|
||||
|
||||
Vendored
+1
@@ -19,6 +19,7 @@
|
||||
// files to ignore (globs supported)
|
||||
"ignorePaths": [
|
||||
"Cargo.lock",
|
||||
"oranda.json",
|
||||
"target/**",
|
||||
"tests/**/fixtures/**",
|
||||
"src/uu/dd/test-resources/**",
|
||||
|
||||
Generated
+153
-217
File diff suppressed because it is too large
Load Diff
+16
-15
@@ -1,7 +1,7 @@
|
||||
# coreutils (uutils)
|
||||
# * see the repository LICENSE, README, and CONTRIBUTING files for more information
|
||||
|
||||
# spell-checker:ignore (libs) libselinux gethostid procfs bigdecimal kqueue fundu mangen datetime uuhelp
|
||||
# spell-checker:ignore (libs) bigdecimal datetime fundu gethostid kqueue libselinux mangen memmap procfs uuhelp
|
||||
|
||||
[package]
|
||||
name = "coreutils"
|
||||
@@ -257,9 +257,9 @@ feat_os_windows_legacy = [
|
||||
test = ["uu_test"]
|
||||
|
||||
[workspace.dependencies]
|
||||
bigdecimal = "0.3"
|
||||
bigdecimal = "0.4"
|
||||
binary-heap-plus = "0.5.0"
|
||||
bstr = "1.5"
|
||||
bstr = "1.6"
|
||||
bytecount = "0.6.3"
|
||||
byteorder = "1.4.3"
|
||||
chrono = { version = "^0.4.26", default-features = false, features = [
|
||||
@@ -280,18 +280,19 @@ filetime = "0.2"
|
||||
fnv = "1.0.7"
|
||||
fs_extra = "1.3.0"
|
||||
fts-sys = "0.2"
|
||||
fundu = "1.0.0"
|
||||
fundu = "1.1.0"
|
||||
gcd = "2.3"
|
||||
glob = "0.3.1"
|
||||
half = "2.2"
|
||||
indicatif = "0.17"
|
||||
is-terminal = "0.4.7"
|
||||
itertools = "0.10.5"
|
||||
libc = "0.2.146"
|
||||
itertools = "0.11.0"
|
||||
libc = "0.2.147"
|
||||
lscolors = { version = "0.14.0", default-features = false, features = [
|
||||
"nu-ansi-term",
|
||||
] }
|
||||
memchr = "2"
|
||||
memmap2 = "0.7"
|
||||
nix = { version = "0.26", default-features = false }
|
||||
nom = "7.1.3"
|
||||
notify = { version = "=6.0.1", features = ["macos_kqueue"] }
|
||||
@@ -300,23 +301,23 @@ num-traits = "0.2.15"
|
||||
number_prefix = "0.4"
|
||||
once_cell = "1.18.0"
|
||||
onig = { version = "~6.4", default-features = false }
|
||||
ouroboros = "0.15.6"
|
||||
parse_datetime = "0.4.0"
|
||||
phf = "0.11.1"
|
||||
phf_codegen = "0.11.1"
|
||||
phf = "0.11.2"
|
||||
phf_codegen = "0.11.2"
|
||||
platform-info = "2.0.1"
|
||||
quick-error = "2.0.1"
|
||||
rand = { version = "0.8", features = ["small_rng"] }
|
||||
rand_core = "0.6"
|
||||
rayon = "1.7"
|
||||
redox_syscall = "0.3"
|
||||
regex = "1.8.4"
|
||||
rstest = "0.17.0"
|
||||
rust-ini = "0.18.0"
|
||||
regex = "1.9.1"
|
||||
rstest = "0.18.1"
|
||||
rust-ini = "0.19.0"
|
||||
same-file = "1.0.6"
|
||||
self_cell = "1.0.1"
|
||||
selinux = "0.4"
|
||||
signal-hook = "0.3.15"
|
||||
smallvec = { version = "1.10", features = ["union"] }
|
||||
smallvec = { version = "1.11", features = ["union"] }
|
||||
tempfile = "3.6.0"
|
||||
term_grid = "0.1.5"
|
||||
terminal_size = "0.2.6"
|
||||
@@ -491,11 +492,11 @@ uucore = { workspace = true, features = ["entries", "process", "signals"] }
|
||||
walkdir = { workspace = true }
|
||||
is-terminal = { workspace = true }
|
||||
hex-literal = "0.4.1"
|
||||
rstest = "0.17.0"
|
||||
rstest = { workspace = true }
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies]
|
||||
procfs = { version = "0.15", default-features = false }
|
||||
rlimit = "0.9.1"
|
||||
rlimit = "0.10.0"
|
||||
|
||||
[target.'cfg(unix)'.dev-dependencies]
|
||||
nix = { workspace = true, features = ["process", "signal", "user"] }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<!-- markdownlint-disable MD033 MD041 MD002 -->
|
||||
<!-- markdownlint-disable commands-show-output no-duplicate-heading -->
|
||||
<!-- spell-checker:ignore markdownlint ; (options) DESTDIR UTILNAME manpages reimplementation -->
|
||||
<!-- spell-checker:ignore markdownlint ; (options) DESTDIR UTILNAME manpages reimplementation oranda -->
|
||||
<div class="oranda-hide">
|
||||
<div align="center">
|
||||
|
||||

|
||||
@@ -19,11 +20,14 @@
|
||||
|
||||
---
|
||||
|
||||
</div>
|
||||
|
||||
uutils coreutils is a cross-platform reimplementation of the GNU coreutils in
|
||||
[Rust](http://www.rust-lang.org). While all programs have been implemented, some
|
||||
options might be missing or different behavior might be experienced.
|
||||
|
||||
<div class="oranda-hide">
|
||||
|
||||
To install it:
|
||||
|
||||
```shell
|
||||
@@ -31,6 +35,8 @@ cargo install coreutils
|
||||
~/.cargo/bin/coreutils
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
<!-- markdownlint-disable-next-line MD026 -->
|
||||
|
||||
## Goals
|
||||
@@ -42,6 +48,8 @@ uutils aims to work on as many platforms as possible, to be able to use the same
|
||||
utils on Linux, Mac, Windows and other platforms. This ensures, for example,
|
||||
that scripts can be easily transferred between platforms.
|
||||
|
||||
<div class="oranda-hide">
|
||||
|
||||
## Documentation
|
||||
|
||||
uutils has both user and developer documentation available:
|
||||
@@ -52,6 +60,7 @@ uutils has both user and developer documentation available:
|
||||
Both can also be generated locally, the instructions for that can be found in
|
||||
the [coreutils docs](https://github.com/uutils/uutils.github.io) repository.
|
||||
|
||||
|
||||
<!-- ANCHOR: build (this mark is needed for mdbook) -->
|
||||
|
||||
## Requirements
|
||||
@@ -301,6 +310,8 @@ See <https://github.com/uutils/coreutils/issues/3336> for the main meta bugs
|
||||
|
||||

|
||||
|
||||
</div> <!-- close oranda-hide div -->
|
||||
|
||||
## Contributing
|
||||
|
||||
To contribute to uutils, please see [CONTRIBUTING](CONTRIBUTING.md).
|
||||
|
||||
@@ -59,8 +59,6 @@ highlight = "all"
|
||||
# introduces it.
|
||||
# spell-checker: disable
|
||||
skip = [
|
||||
# is-terminal
|
||||
{ name = "hermit-abi", version = "0.3.1" },
|
||||
# procfs
|
||||
{ name = "rustix", version = "0.36.14" },
|
||||
# rustix
|
||||
@@ -87,6 +85,10 @@ skip = [
|
||||
{ name = "redox_syscall", version = "0.3.5" },
|
||||
# cpp_macros
|
||||
{ name = "aho-corasick", version = "0.7.19" },
|
||||
# ordered-multimap (via rust-ini)
|
||||
{ name = "hashbrown", version = "0.13.2" },
|
||||
# various crates
|
||||
{ name = "syn", version = "1.0.109" },
|
||||
]
|
||||
# spell-checker: enable
|
||||
|
||||
|
||||
+16
-3
@@ -61,7 +61,20 @@ feature is adopted from [FreeBSD](https://www.freebsd.org/cgi/man.cgi?cut).
|
||||
|
||||
## `fmt`
|
||||
|
||||
`fmt` has additional flags for prefixes: `-P/--skip-prefix`, `-x/--exact-prefix`, and
|
||||
`-X/--exact-skip-prefix`. With `-m/--preserve-headers`, an attempt is made to detect and preserve
|
||||
mail headers in the input. `-q/--quick` breaks lines more quickly. And `-T/--tab-width` defines the
|
||||
`fmt` has additional flags for prefixes: `-P`/`--skip-prefix`, `-x`/`--exact-prefix`, and
|
||||
`-X`/`--exact-skip-prefix`. With `-m`/`--preserve-headers`, an attempt is made to detect and preserve
|
||||
mail headers in the input. `-q`/`--quick` breaks lines more quickly. And `-T`/`--tab-width` defines the
|
||||
number of spaces representing a tab when determining the line length.
|
||||
|
||||
## `seq`
|
||||
|
||||
`seq` provides `-t`/`--terminator` to set the terminator character.
|
||||
|
||||
## `ls`
|
||||
|
||||
GNU `ls` provides two ways to use a long listing format: `-l` and `--format=long`. We support a
|
||||
third way: `--long`.
|
||||
|
||||
## `du`
|
||||
|
||||
`du` allows `birth` and `creation` as values for the `--time` argument to show the creation time.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
.logo {
|
||||
display: block;
|
||||
height: 170px;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"project": {
|
||||
"name": "uutils coreutils"
|
||||
},
|
||||
"components": {
|
||||
"changelog": true
|
||||
},
|
||||
"styles": {
|
||||
"theme": "light",
|
||||
"logo": "docs/src/logo.svg",
|
||||
"additional_css": ["docs/src/oranda.css"]
|
||||
}
|
||||
}
|
||||
@@ -383,6 +383,7 @@ pub fn uu_app() -> Command {
|
||||
backup_control::BACKUP_CONTROL_LONG_HELP
|
||||
))
|
||||
.infer_long_args(true)
|
||||
.args_override_self(true)
|
||||
.arg(
|
||||
Arg::new(options::TARGET_DIRECTORY)
|
||||
.short('t')
|
||||
|
||||
+10
-9
@@ -26,14 +26,13 @@ use uucore::{format_usage, help_about, help_usage, show};
|
||||
#[cfg(windows)]
|
||||
use windows_sys::Win32::{Foundation::SYSTEMTIME, System::SystemInformation::SetSystemTime};
|
||||
|
||||
use uucore::shortcut_value_parser::ShortcutValueParser;
|
||||
|
||||
// Options
|
||||
const DATE: &str = "date";
|
||||
const HOURS: &str = "hours";
|
||||
const MINUTES: &str = "minutes";
|
||||
const SECONDS: &str = "seconds";
|
||||
const HOUR: &str = "hour";
|
||||
const MINUTE: &str = "minute";
|
||||
const SECOND: &str = "second";
|
||||
const NS: &str = "ns";
|
||||
|
||||
const ABOUT: &str = help_about!("date.md");
|
||||
@@ -110,9 +109,9 @@ enum Iso8601Format {
|
||||
impl<'a> From<&'a str> for Iso8601Format {
|
||||
fn from(s: &str) -> Self {
|
||||
match s {
|
||||
HOURS | HOUR => Self::Hours,
|
||||
MINUTES | MINUTE => Self::Minutes,
|
||||
SECONDS | SECOND => Self::Seconds,
|
||||
HOURS => Self::Hours,
|
||||
MINUTES => Self::Minutes,
|
||||
SECONDS => Self::Seconds,
|
||||
NS => Self::Ns,
|
||||
DATE => Self::Date,
|
||||
// Note: This is caught by clap via `possible_values`
|
||||
@@ -131,7 +130,7 @@ impl<'a> From<&'a str> for Rfc3339Format {
|
||||
fn from(s: &str) -> Self {
|
||||
match s {
|
||||
DATE => Self::Date,
|
||||
SECONDS | SECOND => Self::Seconds,
|
||||
SECONDS => Self::Seconds,
|
||||
NS => Self::Ns,
|
||||
// Should be caught by clap
|
||||
_ => panic!("Invalid format: {s}"),
|
||||
@@ -317,7 +316,9 @@ pub fn uu_app() -> Command {
|
||||
.short('I')
|
||||
.long(OPT_ISO_8601)
|
||||
.value_name("FMT")
|
||||
.value_parser([DATE, HOUR, HOURS, MINUTE, MINUTES, SECOND, SECONDS, NS])
|
||||
.value_parser(ShortcutValueParser::new([
|
||||
DATE, HOURS, MINUTES, SECONDS, NS,
|
||||
]))
|
||||
.num_args(0..=1)
|
||||
.default_missing_value(OPT_DATE)
|
||||
.help(ISO_8601_HELP_STRING),
|
||||
@@ -333,7 +334,7 @@ pub fn uu_app() -> Command {
|
||||
Arg::new(OPT_RFC_3339)
|
||||
.long(OPT_RFC_3339)
|
||||
.value_name("FMT")
|
||||
.value_parser([DATE, SECOND, SECONDS, NS])
|
||||
.value_parser(ShortcutValueParser::new([DATE, SECONDS, NS]))
|
||||
.help(RFC_3339_HELP_STRING),
|
||||
)
|
||||
.arg(
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@ Copy, and optionally convert, a file system resource
|
||||
|
||||
### Operands
|
||||
|
||||
- `Bs=BYTES` : read and write up to BYTES bytes at a time (default: 512);
|
||||
- `bs=BYTES` : read and write up to BYTES bytes at a time (default: 512);
|
||||
overwrites `ibs` and `obs`.
|
||||
- `cbs=BYTES` : the 'conversion block size' in bytes. Applies to the
|
||||
`conv=block`, and `conv=unblock` operations.
|
||||
@@ -114,7 +114,7 @@ Copy, and optionally convert, a file system resource
|
||||
|
||||
### General Flags
|
||||
|
||||
- `Direct` : use direct I/O for data.
|
||||
- `direct` : use direct I/O for data.
|
||||
- `directory` : fail unless the given input (if used as an iflag) or
|
||||
output (if used as an oflag) is a directory.
|
||||
- `dsync` : use synchronized I/O for data.
|
||||
|
||||
+54
-8
@@ -36,9 +36,12 @@ use std::os::unix::{
|
||||
io::{AsRawFd, FromRawFd},
|
||||
};
|
||||
use std::path::Path;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering::Relaxed},
|
||||
mpsc, Arc,
|
||||
};
|
||||
use std::thread;
|
||||
use std::time;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use clap::{crate_version, Arg, Command};
|
||||
use gcd::Gcd;
|
||||
@@ -75,6 +78,45 @@ struct Settings {
|
||||
status: Option<StatusLevel>,
|
||||
}
|
||||
|
||||
/// A timer which triggers on a given interval
|
||||
///
|
||||
/// After being constructed with [`Alarm::with_interval`], [`Alarm::is_triggered`]
|
||||
/// will return true once per the given [`Duration`].
|
||||
///
|
||||
/// Can be cloned, but the trigger status is shared across all instances so only
|
||||
/// the first caller each interval will yield true.
|
||||
///
|
||||
/// When all instances are dropped the background thread will exit on the next interval.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Alarm {
|
||||
interval: Duration,
|
||||
trigger: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl Alarm {
|
||||
pub fn with_interval(interval: Duration) -> Self {
|
||||
let trigger = Arc::new(AtomicBool::default());
|
||||
|
||||
let weak_trigger = Arc::downgrade(&trigger);
|
||||
thread::spawn(move || {
|
||||
while let Some(trigger) = weak_trigger.upgrade() {
|
||||
thread::sleep(interval);
|
||||
trigger.store(true, Relaxed);
|
||||
}
|
||||
});
|
||||
|
||||
Self { interval, trigger }
|
||||
}
|
||||
|
||||
pub fn is_triggered(&self) -> bool {
|
||||
self.trigger.swap(false, Relaxed)
|
||||
}
|
||||
|
||||
pub fn get_interval(&self) -> Duration {
|
||||
self.interval
|
||||
}
|
||||
}
|
||||
|
||||
/// A number in blocks or bytes
|
||||
///
|
||||
/// Some values (seek, skip, iseek, oseek) can have values either in blocks or in bytes.
|
||||
@@ -760,7 +802,7 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
||||
// of its report includes the throughput in bytes per second,
|
||||
// which requires knowing how long the process has been
|
||||
// running.
|
||||
let start = time::Instant::now();
|
||||
let start = Instant::now();
|
||||
|
||||
// A good buffer size for reading.
|
||||
//
|
||||
@@ -780,7 +822,6 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
||||
// information.
|
||||
let (prog_tx, rx) = mpsc::channel();
|
||||
let output_thread = thread::spawn(gen_prog_updater(rx, i.settings.status));
|
||||
let mut progress_as_secs = 0;
|
||||
|
||||
// Optimization: if no blocks are to be written, then don't
|
||||
// bother allocating any buffers.
|
||||
@@ -813,6 +854,12 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
||||
// This is the max size needed.
|
||||
let mut buf = vec![BUF_INIT_BYTE; bsize];
|
||||
|
||||
// Spawn a timer thread to provide a scheduled signal indicating when we
|
||||
// should send an update of our progress to the reporting thread.
|
||||
//
|
||||
// This avoids the need to query the OS monotonic clock for every block.
|
||||
let alarm = Alarm::with_interval(Duration::from_secs(1));
|
||||
|
||||
// Index in the input file where we are reading bytes and in
|
||||
// the output file where we are writing bytes.
|
||||
//
|
||||
@@ -871,9 +918,8 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
||||
// error.
|
||||
rstat += rstat_update;
|
||||
wstat += wstat_update;
|
||||
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
|
||||
if prog_update.duration.as_secs() >= progress_as_secs {
|
||||
progress_as_secs = prog_update.duration.as_secs() + 1;
|
||||
if alarm.is_triggered() {
|
||||
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
|
||||
prog_tx.send(prog_update).unwrap_or(());
|
||||
}
|
||||
}
|
||||
@@ -885,7 +931,7 @@ fn finalize<T>(
|
||||
output: &mut Output,
|
||||
rstat: ReadStat,
|
||||
wstat: WriteStat,
|
||||
start: time::Instant,
|
||||
start: Instant,
|
||||
prog_tx: &mpsc::Sender<ProgUpdate>,
|
||||
output_thread: thread::JoinHandle<T>,
|
||||
) -> std::io::Result<()> {
|
||||
|
||||
+2
-2
@@ -145,7 +145,7 @@ impl Stat {
|
||||
return Ok(Self {
|
||||
path: path.to_path_buf(),
|
||||
is_dir: metadata.is_dir(),
|
||||
size: metadata.len(),
|
||||
size: if path.is_dir() { 0 } else { metadata.len() },
|
||||
blocks: metadata.blocks(),
|
||||
inodes: 1,
|
||||
inode: Some(file_info),
|
||||
@@ -162,7 +162,7 @@ impl Stat {
|
||||
Ok(Self {
|
||||
path: path.to_path_buf(),
|
||||
is_dir: metadata.is_dir(),
|
||||
size: metadata.len(),
|
||||
size: if path.is_dir() { 0 } else { metadata.len() },
|
||||
blocks: size_on_disk / 1024 * 2,
|
||||
inode: file_info,
|
||||
inodes: 1,
|
||||
|
||||
@@ -306,7 +306,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
|
||||
// if there is no program name for some reason, default to "hashsum"
|
||||
let program = args.next().unwrap_or_else(|| OsString::from(NAME));
|
||||
let binary_name = Path::new(&program)
|
||||
.file_name()
|
||||
.file_stem()
|
||||
.unwrap_or_else(|| OsStr::new(NAME))
|
||||
.to_string_lossy();
|
||||
|
||||
|
||||
@@ -3010,6 +3010,20 @@ fn display_inode(metadata: &Metadata) -> String {
|
||||
#[allow(unused_variables)]
|
||||
fn get_security_context(config: &Config, p_buf: &Path, must_dereference: bool) -> String {
|
||||
let substitute_string = "?".to_string();
|
||||
// If we must dereference, ensure that the symlink is actually valid even if the system
|
||||
// does not support SELinux.
|
||||
// Conforms to the GNU coreutils where a dangling symlink results in exit code 1.
|
||||
if must_dereference {
|
||||
match get_metadata(p_buf, must_dereference) {
|
||||
Err(err) => {
|
||||
// The Path couldn't be dereferenced, so return early and set exit code 1
|
||||
// to indicate a minor error
|
||||
show!(LsError::IOErrorContext(err, p_buf.to_path_buf(), false));
|
||||
return substitute_string;
|
||||
}
|
||||
Ok(md) => (),
|
||||
}
|
||||
}
|
||||
if config.selinux_supported {
|
||||
#[cfg(feature = "selinux")]
|
||||
{
|
||||
|
||||
+54
-28
@@ -50,10 +50,11 @@ pub mod options {
|
||||
pub const FILES: &str = "files";
|
||||
}
|
||||
|
||||
const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";
|
||||
const MULTI_FILE_TOP_PROMPT: &str = "\r::::::::::::::\n\r{}\n\r::::::::::::::\n";
|
||||
|
||||
struct Options {
|
||||
clean_print: bool,
|
||||
from_line: usize,
|
||||
lines: Option<u16>,
|
||||
print_over: bool,
|
||||
silent: bool,
|
||||
@@ -72,8 +73,13 @@ impl Options {
|
||||
(None, Some(number)) if number > 0 => Some(number + 1),
|
||||
(_, _) => None,
|
||||
};
|
||||
let from_line = match matches.get_one::<usize>(options::FROM_LINE).copied() {
|
||||
Some(number) if number > 1 => number - 1,
|
||||
_ => 0,
|
||||
};
|
||||
Self {
|
||||
clean_print: matches.get_flag(options::CLEAN_PRINT),
|
||||
from_line,
|
||||
lines,
|
||||
print_over: matches.get_flag(options::PRINT_OVER),
|
||||
silent: matches.get_flag(options::SILENT),
|
||||
@@ -90,7 +96,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
|
||||
let options = Options::from(&matches);
|
||||
let mut options = Options::from(&matches);
|
||||
|
||||
let mut buff = String::new();
|
||||
|
||||
@@ -115,9 +121,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
format!("cannot open {}: No such file or directory", file.quote()),
|
||||
));
|
||||
}
|
||||
if length > 1 {
|
||||
buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap()));
|
||||
}
|
||||
let opened_file = match File::open(file) {
|
||||
Err(why) => {
|
||||
terminal::disable_raw_mode().unwrap();
|
||||
@@ -130,14 +133,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
};
|
||||
let mut reader = BufReader::new(opened_file);
|
||||
reader.read_to_string(&mut buff).unwrap();
|
||||
more(&buff, &mut stdout, next_file.copied(), &options)?;
|
||||
more(
|
||||
&buff,
|
||||
&mut stdout,
|
||||
length > 1,
|
||||
file.to_str(),
|
||||
next_file.copied(),
|
||||
&mut options,
|
||||
)?;
|
||||
buff.clear();
|
||||
}
|
||||
reset_term(&mut stdout);
|
||||
} else if !std::io::stdin().is_terminal() {
|
||||
stdin().read_to_string(&mut buff).unwrap();
|
||||
let mut stdout = setup_term();
|
||||
more(&buff, &mut stdout, None, &options)?;
|
||||
more(&buff, &mut stdout, false, None, None, &mut options)?;
|
||||
reset_term(&mut stdout);
|
||||
} else {
|
||||
return Err(UUsageError::new(1, "bad usage"));
|
||||
@@ -179,6 +189,22 @@ pub fn uu_app() -> Command {
|
||||
.help("Squeeze multiple blank lines into one")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::PLAIN)
|
||||
.short('u')
|
||||
.long(options::PLAIN)
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::FROM_LINE)
|
||||
.short('F')
|
||||
.long(options::FROM_LINE)
|
||||
.num_args(1)
|
||||
.value_name("number")
|
||||
.value_parser(value_parser!(usize))
|
||||
.help("Display file beginning from line number"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::LINES)
|
||||
.short('n')
|
||||
@@ -191,7 +217,6 @@ pub fn uu_app() -> Command {
|
||||
.arg(
|
||||
Arg::new(options::NUMBER)
|
||||
.long(options::NUMBER)
|
||||
.required(false)
|
||||
.num_args(1)
|
||||
.value_parser(value_parser!(u16).range(0..))
|
||||
.help("Same as --lines"),
|
||||
@@ -210,21 +235,6 @@ pub fn uu_app() -> Command {
|
||||
.long(options::NO_PAUSE)
|
||||
.help("Suppress pause after form feed"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::PLAIN)
|
||||
.short('u')
|
||||
.long(options::PLAIN)
|
||||
.help("Suppress underlining and bold"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::FROM_LINE)
|
||||
.short('F')
|
||||
.allow_hyphen_values(true)
|
||||
.required(false)
|
||||
.takes_value(true)
|
||||
.value_name("number")
|
||||
.help("Display file beginning from line number"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::PATTERN)
|
||||
.short('P')
|
||||
@@ -273,8 +283,10 @@ fn reset_term(_: &mut usize) {}
|
||||
fn more(
|
||||
buff: &str,
|
||||
stdout: &mut Stdout,
|
||||
multiple_file: bool,
|
||||
file: Option<&str>,
|
||||
next_file: Option<&str>,
|
||||
options: &Options,
|
||||
options: &mut Options,
|
||||
) -> UResult<()> {
|
||||
let (cols, mut rows) = terminal::size().unwrap();
|
||||
if let Some(number) = options.lines {
|
||||
@@ -284,8 +296,23 @@ fn more(
|
||||
let lines = break_buff(buff, usize::from(cols));
|
||||
|
||||
let mut pager = Pager::new(rows, lines, next_file, options);
|
||||
|
||||
if multiple_file {
|
||||
execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
|
||||
stdout.write_all(
|
||||
MULTI_FILE_TOP_PROMPT
|
||||
.replace("{}", file.unwrap_or_default())
|
||||
.as_bytes(),
|
||||
)?;
|
||||
pager.content_rows -= 3;
|
||||
}
|
||||
pager.draw(stdout, None);
|
||||
if pager.should_close() {
|
||||
if multiple_file {
|
||||
options.from_line = 0;
|
||||
pager.content_rows += 3;
|
||||
}
|
||||
|
||||
if pager.should_close() && next_file.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -406,7 +433,7 @@ impl<'a> Pager<'a> {
|
||||
fn new(rows: u16, lines: Vec<String>, next_file: Option<&'a str>, options: &Options) -> Self {
|
||||
let line_count = lines.len();
|
||||
Self {
|
||||
upper_mark: 0,
|
||||
upper_mark: options.from_line,
|
||||
content_rows: rows.saturating_sub(1),
|
||||
lines,
|
||||
next_file,
|
||||
@@ -472,10 +499,10 @@ impl<'a> Pager<'a> {
|
||||
}
|
||||
|
||||
fn draw(&mut self, stdout: &mut std::io::Stdout, wrong_key: Option<char>) {
|
||||
self.draw_lines(stdout);
|
||||
let lower_mark = self
|
||||
.line_count
|
||||
.min(self.upper_mark.saturating_add(self.content_rows.into()));
|
||||
self.draw_lines(stdout);
|
||||
self.draw_prompt(stdout, lower_mark, wrong_key);
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
@@ -535,7 +562,6 @@ impl<'a> Pager<'a> {
|
||||
};
|
||||
|
||||
let status = format!("--More--({status_inner})");
|
||||
|
||||
let banner = match (self.silent, wrong_key) {
|
||||
(true, Some(key)) => format!(
|
||||
"{status} [Unknown key: '{key}'. Press 'h' for instructions. (unimplemented)]"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user