mirror of
https://github.com/uutils/coreutils
synced 2024-12-04 18:39:52 +00:00
Add dir and vdir utils (based on ls)
Fix issue #3163 They are basically ls with some different options.
This commit is contained in:
parent
02bd97a00d
commit
c2e214bd99
18 changed files with 950 additions and 619 deletions
|
@ -179,3 +179,4 @@ Smigle00
|
|||
anonymousknight
|
||||
kwantam
|
||||
nicoo
|
||||
gmnsii
|
||||
|
|
|
@ -93,6 +93,7 @@ rollup
|
|||
sed
|
||||
selinuxenabled
|
||||
sestatus
|
||||
vdir
|
||||
wslpath
|
||||
xargs
|
||||
|
||||
|
|
22
Cargo.lock
generated
22
Cargo.lock
generated
|
@ -368,6 +368,7 @@ dependencies = [
|
|||
"uu_date",
|
||||
"uu_dd",
|
||||
"uu_df",
|
||||
"uu_dir",
|
||||
"uu_dircolors",
|
||||
"uu_dirname",
|
||||
"uu_du",
|
||||
|
@ -445,6 +446,7 @@ dependencies = [
|
|||
"uu_unlink",
|
||||
"uu_uptime",
|
||||
"uu_users",
|
||||
"uu_vdir",
|
||||
"uu_wc",
|
||||
"uu_who",
|
||||
"uu_whoami",
|
||||
|
@ -2376,6 +2378,16 @@ dependencies = [
|
|||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_dir"
|
||||
version = "0.0.13"
|
||||
dependencies = [
|
||||
"clap 3.1.8",
|
||||
"selinux",
|
||||
"uu_ls",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_dircolors"
|
||||
version = "0.0.13"
|
||||
|
@ -3127,6 +3139,16 @@ dependencies = [
|
|||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_vdir"
|
||||
version = "0.0.13"
|
||||
dependencies = [
|
||||
"clap 3.1.8",
|
||||
"selinux",
|
||||
"uu_ls",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_wc"
|
||||
version = "0.0.13"
|
||||
|
|
|
@ -45,6 +45,7 @@ feat_common_core = [
|
|||
"cut",
|
||||
"date",
|
||||
"df",
|
||||
"dir",
|
||||
"dircolors",
|
||||
"dirname",
|
||||
"dd",
|
||||
|
@ -100,6 +101,7 @@ feat_common_core = [
|
|||
"unexpand",
|
||||
"uniq",
|
||||
"unlink",
|
||||
"vdir",
|
||||
"wc",
|
||||
"yes",
|
||||
]
|
||||
|
@ -276,6 +278,7 @@ cut = { optional=true, version="0.0.13", package="uu_cut", path="src/uu/cut
|
|||
date = { optional=true, version="0.0.13", package="uu_date", path="src/uu/date" }
|
||||
dd = { optional=true, version="0.0.13", package="uu_dd", path="src/uu/dd" }
|
||||
df = { optional=true, version="0.0.13", package="uu_df", path="src/uu/df" }
|
||||
dir = { optional=true, version="0.0.13", package="uu_dir", path="src/uu/dir" }
|
||||
dircolors= { optional=true, version="0.0.13", package="uu_dircolors", path="src/uu/dircolors" }
|
||||
dirname = { optional=true, version="0.0.13", package="uu_dirname", path="src/uu/dirname" }
|
||||
du = { optional=true, version="0.0.13", package="uu_du", path="src/uu/du" }
|
||||
|
@ -352,6 +355,7 @@ uniq = { optional=true, version="0.0.13", package="uu_uniq", path="src/uu/un
|
|||
unlink = { optional=true, version="0.0.13", package="uu_unlink", path="src/uu/unlink" }
|
||||
uptime = { optional=true, version="0.0.13", package="uu_uptime", path="src/uu/uptime" }
|
||||
users = { optional=true, version="0.0.13", package="uu_users", path="src/uu/users" }
|
||||
vdir = { optional=true, version="0.0.13", package="uu_vdir", path="src/uu/vdir" }
|
||||
wc = { optional=true, version="0.0.13", package="uu_wc", path="src/uu/wc" }
|
||||
who = { optional=true, version="0.0.13", package="uu_who", path="src/uu/who" }
|
||||
whoami = { optional=true, version="0.0.13", package="uu_whoami", path="src/uu/whoami" }
|
||||
|
|
|
@ -65,6 +65,7 @@ PROGS := \
|
|||
date \
|
||||
dd \
|
||||
df \
|
||||
dir \
|
||||
dircolors \
|
||||
dirname \
|
||||
echo \
|
||||
|
@ -118,6 +119,7 @@ PROGS := \
|
|||
tsort \
|
||||
unexpand \
|
||||
uniq \
|
||||
vdir \
|
||||
wc \
|
||||
whoami \
|
||||
yes
|
||||
|
|
25
src/uu/dir/Cargo.toml
Normal file
25
src/uu/dir/Cargo.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "uu_dir"
|
||||
version = "0.0.13"
|
||||
authors = ["uutils developers"]
|
||||
license = "MIT"
|
||||
description = "shortcut to ls -C -b"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ls"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
path = "src/dir.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.1", features = ["wrap_help", "cargo", "env"] }
|
||||
uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] }
|
||||
selinux = { version="0.2", optional = true }
|
||||
uu_ls = {path="../ls"}
|
||||
|
||||
[[bin]]
|
||||
name = "dir"
|
||||
path = "src/main.rs"
|
1
src/uu/dir/LICENSE
Symbolic link
1
src/uu/dir/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
70
src/uu/dir/src/dir.rs
Normal file
70
src/uu/dir/src/dir.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
// * This file is part of the uutils coreutils package.
|
||||
// *
|
||||
// * (c) gmnsii <gmnsii@protonmail.com>
|
||||
// *
|
||||
// * For the full copyright and license information, please view the LICENSE file
|
||||
// * that was distributed with this source code.
|
||||
|
||||
use clap::Command;
|
||||
use std::path::Path;
|
||||
use uu_ls::quoting_style::{Quotes, QuotingStyle};
|
||||
use uu_ls::{options, Config, Format};
|
||||
use uucore::error::UResult;
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let command = uu_ls::uu_app();
|
||||
|
||||
let matches = command.get_matches_from(args);
|
||||
|
||||
let mut default_quoting_style = false;
|
||||
let mut default_format_style = false;
|
||||
|
||||
// We check if any options on formatting or quoting style have been given.
|
||||
// If not, we will use dir default formatting and quoting style options
|
||||
|
||||
if !matches.is_present(options::QUOTING_STYLE)
|
||||
&& !matches.is_present(options::quoting::C)
|
||||
&& !matches.is_present(options::quoting::ESCAPE)
|
||||
&& !matches.is_present(options::quoting::LITERAL)
|
||||
{
|
||||
default_quoting_style = true;
|
||||
}
|
||||
if !matches.is_present(options::FORMAT)
|
||||
&& !matches.is_present(options::format::ACROSS)
|
||||
&& !matches.is_present(options::format::COLUMNS)
|
||||
&& !matches.is_present(options::format::COMMAS)
|
||||
&& !matches.is_present(options::format::LONG)
|
||||
&& !matches.is_present(options::format::LONG_NO_GROUP)
|
||||
&& !matches.is_present(options::format::LONG_NO_OWNER)
|
||||
&& !matches.is_present(options::format::LONG_NUMERIC_UID_GID)
|
||||
&& !matches.is_present(options::format::ONE_LINE)
|
||||
{
|
||||
default_format_style = true;
|
||||
}
|
||||
|
||||
let mut config = Config::from(&matches)?;
|
||||
|
||||
if default_quoting_style {
|
||||
config.quoting_style = QuotingStyle::C {
|
||||
quotes: Quotes::None,
|
||||
};
|
||||
}
|
||||
if default_format_style {
|
||||
config.format = Format::Columns;
|
||||
}
|
||||
|
||||
let locs = matches
|
||||
.values_of_os(options::PATHS)
|
||||
.map(|v| v.map(Path::new).collect())
|
||||
.unwrap_or_else(|| vec![Path::new(".")]);
|
||||
|
||||
uu_ls::list(locs, &config)
|
||||
}
|
||||
|
||||
// To avoid code duplication, we reuse ls uu_app function which has the same
|
||||
// arguments. However, coreutils won't compile if one of the utils is missing
|
||||
// an uu_app function, so we need this dummy one.
|
||||
pub fn uu_app<'a>() -> Command<'a> {
|
||||
Command::new(uucore::util_name())
|
||||
}
|
1
src/uu/dir/src/main.rs
Normal file
1
src/uu/dir/src/main.rs
Normal file
|
@ -0,0 +1 @@
|
|||
uucore::bin!(uu_dir);
|
1231
src/uu/ls/src/ls.rs
1231
src/uu/ls/src/ls.rs
File diff suppressed because it is too large
Load diff
|
@ -6,7 +6,7 @@ use std::ffi::OsStr;
|
|||
const SPECIAL_SHELL_CHARS_START: &[char] = &['~', '#'];
|
||||
const SPECIAL_SHELL_CHARS: &str = "`$&*()|[]{};\\'\"<>?! ";
|
||||
|
||||
pub(super) enum QuotingStyle {
|
||||
pub enum QuotingStyle {
|
||||
Shell {
|
||||
escape: bool,
|
||||
always_quote: bool,
|
||||
|
@ -21,7 +21,7 @@ pub(super) enum QuotingStyle {
|
|||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) enum Quotes {
|
||||
pub enum Quotes {
|
||||
None,
|
||||
Single,
|
||||
Double,
|
||||
|
|
25
src/uu/vdir/Cargo.toml
Normal file
25
src/uu/vdir/Cargo.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "uu_vdir"
|
||||
version = "0.0.13"
|
||||
authors = ["uutils developers"]
|
||||
license = "MIT"
|
||||
description = "shortcut to ls -l -b"
|
||||
|
||||
homepage = "https://github.com/uutils/coreutils"
|
||||
repository = "https://github.com/uutils/coreutils/tree/main/src/uu/ls"
|
||||
keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
||||
categories = ["command-line-utilities"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
path = "src/vdir.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.1", features = ["wrap_help", "cargo", "env"] }
|
||||
uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] }
|
||||
selinux = { version="0.2", optional = true }
|
||||
uu_ls = {path="../ls"}
|
||||
|
||||
[[bin]]
|
||||
name = "vdir"
|
||||
path = "src/main.rs"
|
1
src/uu/vdir/LICENSE
Symbolic link
1
src/uu/vdir/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../LICENSE
|
1
src/uu/vdir/src/main.rs
Normal file
1
src/uu/vdir/src/main.rs
Normal file
|
@ -0,0 +1 @@
|
|||
uucore::bin!(uu_vdir);
|
68
src/uu/vdir/src/vdir.rs
Normal file
68
src/uu/vdir/src/vdir.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
// * This file is part of the uutils coreutils package.
|
||||
// *
|
||||
// * (c) gmnsii <gmnsii@protonmail.com>
|
||||
// *
|
||||
// * For the full copyright and license information, please view the LICENSE file
|
||||
// * that was distributed with this source code.
|
||||
|
||||
use clap::Command;
|
||||
use std::path::Path;
|
||||
use uu_ls::quoting_style::{Quotes, QuotingStyle};
|
||||
use uu_ls::{options, Config, Format};
|
||||
use uucore::error::UResult;
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let command = uu_ls::uu_app();
|
||||
let matches = command.get_matches_from(args);
|
||||
|
||||
let mut default_quoting_style = false;
|
||||
let mut default_format_style = false;
|
||||
|
||||
// We check if any options on formatting or quoting style have been given.
|
||||
// If not, we will use dir default formatting and quoting style options
|
||||
|
||||
if !matches.is_present(options::QUOTING_STYLE)
|
||||
&& !matches.is_present(options::quoting::C)
|
||||
&& !matches.is_present(options::quoting::ESCAPE)
|
||||
&& !matches.is_present(options::quoting::LITERAL)
|
||||
{
|
||||
default_quoting_style = true;
|
||||
}
|
||||
if !matches.is_present(options::FORMAT)
|
||||
&& !matches.is_present(options::format::ACROSS)
|
||||
&& !matches.is_present(options::format::COLUMNS)
|
||||
&& !matches.is_present(options::format::COMMAS)
|
||||
&& !matches.is_present(options::format::LONG)
|
||||
&& !matches.is_present(options::format::LONG_NO_GROUP)
|
||||
&& !matches.is_present(options::format::LONG_NO_OWNER)
|
||||
&& !matches.is_present(options::format::LONG_NUMERIC_UID_GID)
|
||||
&& !matches.is_present(options::format::ONE_LINE)
|
||||
{
|
||||
default_format_style = true;
|
||||
}
|
||||
|
||||
let mut config = Config::from(&matches)?;
|
||||
|
||||
if default_quoting_style {
|
||||
config.quoting_style = QuotingStyle::C {
|
||||
quotes: Quotes::None,
|
||||
};
|
||||
}
|
||||
if default_format_style {
|
||||
config.format = Format::Long;
|
||||
}
|
||||
|
||||
let locs = matches
|
||||
.values_of_os(options::PATHS)
|
||||
.map(|v| v.map(Path::new).collect())
|
||||
.unwrap_or_else(|| vec![Path::new(".")]);
|
||||
uu_ls::list(locs, &config)
|
||||
}
|
||||
|
||||
// To avoid code duplication, we reuse ls uu_app function which has the same
|
||||
// arguments. However, coreutils won't compile if one of the utils is missing
|
||||
// an uu_app function, so we need this dummy one.
|
||||
pub fn uu_app<'a>() -> Command<'a> {
|
||||
Command::new(uucore::util_name())
|
||||
}
|
55
tests/by-util/test_dir.rs
Normal file
55
tests/by-util/test_dir.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
#[cfg(not(windows))]
|
||||
extern crate libc;
|
||||
extern crate regex;
|
||||
#[cfg(not(windows))]
|
||||
extern crate tempfile;
|
||||
#[cfg(unix)]
|
||||
extern crate unix_socket;
|
||||
|
||||
use self::regex::Regex;
|
||||
use crate::common::util::*;
|
||||
|
||||
/*
|
||||
* As dir use the same functions than ls, we don't have to retest them here.
|
||||
* We just test the default and the long output
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn test_dir() {
|
||||
new_ucmd!().succeeds();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_output() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
at.mkdir("some-dir1");
|
||||
at.touch("some-file1");
|
||||
|
||||
scene.ucmd().succeeds().stdout_contains("some-file1");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.succeeds()
|
||||
.stdout_does_not_match(&Regex::new("[rwx][^some-file1]").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_long_output() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
at.mkdir("some-dir1");
|
||||
at.touch("some-file1");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-l")
|
||||
.succeeds()
|
||||
.stdout_contains("some-file1");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-l")
|
||||
.succeeds()
|
||||
.stdout_matches(&Regex::new("[rwx][^some-file1]").unwrap());
|
||||
}
|
55
tests/by-util/test_vdir.rs
Normal file
55
tests/by-util/test_vdir.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
#[cfg(not(windows))]
|
||||
extern crate libc;
|
||||
extern crate regex;
|
||||
#[cfg(not(windows))]
|
||||
extern crate tempfile;
|
||||
#[cfg(unix)]
|
||||
extern crate unix_socket;
|
||||
|
||||
use self::regex::Regex;
|
||||
use crate::common::util::*;
|
||||
|
||||
/*
|
||||
* As vdir use the same functions than ls, we don't have to retest them here.
|
||||
* We just test the default and the column output
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn test_vdir() {
|
||||
new_ucmd!().succeeds();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_output() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
at.mkdir("some-dir1");
|
||||
at.touch("some-file1");
|
||||
|
||||
scene.ucmd().succeeds().stdout_contains("some-file1");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.succeeds()
|
||||
.stdout_matches(&Regex::new("[rwx][^some-file1]").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_column_output() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
at.mkdir("some-dir1");
|
||||
at.touch("some-file1");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-C")
|
||||
.succeeds()
|
||||
.stdout_contains("some-file1");
|
||||
|
||||
scene
|
||||
.ucmd()
|
||||
.arg("-C")
|
||||
.succeeds()
|
||||
.stdout_does_not_match(&Regex::new("[rwx][^some-file1]").unwrap());
|
||||
}
|
|
@ -180,7 +180,7 @@ sed -i -e "s~ sed -n \"1s/'\\\/'/'OPT'/p\" < err >> pat || framework_failure_~
|
|||
# see issue #3331 (clap limitation).
|
||||
# Upstream returns 1 for most of the program. We do for cp, truncate & pr
|
||||
# So, keep it as it
|
||||
sed -i -e "s/rcexp=1$/rcexp=2\n case \"\$prg\" in chcon|dir|runcon|vdir) return;; esac/" -e "s/rcexp=125 ;;/rcexp=2 ;;\ncp|truncate|pr) rcexp=1;;/" tests/misc/usage_vs_getopt.sh
|
||||
sed -i -e "s/rcexp=1$/rcexp=2\n case \"\$prg\" in chcon|runcon) return;; esac/" -e "s/rcexp=125 ;;/rcexp=2 ;;\ncp|truncate|pr) rcexp=1;;/" tests/misc/usage_vs_getopt.sh
|
||||
# GNU has option=[SUFFIX], clap is <SUFFIX>
|
||||
sed -i -e "s/cat opts/sed -i -e \"s| <.\*>$||g\" opts/" tests/misc/usage_vs_getopt.sh
|
||||
# Strip empty lines for the diff - see https://github.com/uutils/coreutils/issues/3370
|
||||
|
|
Loading…
Reference in a new issue