Merge branch 'main' into hbina-tr-reimplement-expansion

This commit is contained in:
Terts Diepraam 2022-01-19 19:31:54 +01:00
commit da728dd2b6
338 changed files with 5047 additions and 38041 deletions

View file

@ -13,7 +13,7 @@ env:
PROJECT_NAME: coreutils
PROJECT_DESC: "Core universal (cross-platform) utilities"
PROJECT_AUTH: "uutils"
RUST_MIN_SRV: "1.47.0" ## MSRV v1.47.0
RUST_MIN_SRV: "1.54.0" ## MSRV v1.54.0
# * style job configuration
STYLE_FAIL_ON_FAULT: true ## (bool) fail the build if a style job contains a fault (error or warning); may be overridden on a per-job basis
@ -844,13 +844,13 @@ jobs:
## Generate coverage data
COVERAGE_REPORT_DIR="target/debug"
COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info"
# GRCOV_IGNORE_OPTION='--ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*"' ## `grcov` ignores these params when passed as an environment variable (why?)
# GRCOV_IGNORE_OPTION='--ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*"' ## `grcov` ignores these params when passed as an environment variable (why?)
# GRCOV_EXCLUDE_OPTION='--excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"' ## `grcov` ignores these params when passed as an environment variable (why?)
mkdir -p "${COVERAGE_REPORT_DIR}"
# display coverage files
grcov . --output-type files --ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique
grcov . --output-type files --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique
# generate coverage report
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"
echo ::set-output name=report::${COVERAGE_REPORT_FILE}
- name: Upload coverage results (to Codecov.io)
uses: codecov/codecov-action@v1

View file

@ -59,6 +59,7 @@ kibibytes
libacl
lcase
lossily
lstat
mebi
mebibytes
mergeable

View file

@ -182,6 +182,7 @@ getgrgid
getgrnam
getgrouplist
getgroups
getpwent
getpwnam
getpwuid
getuid

513
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@
[package]
name = "coreutils"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "coreutils ~ GNU coreutils (updated); implemented as universal (cross-platform) utils, written in Rust"
@ -247,110 +247,110 @@ test = [ "uu_test" ]
clap = { version = "2.33", features = ["wrap_help"] }
lazy_static = { version="1.3" }
textwrap = { version="0.14", features=["terminal_size"] }
uucore = { version=">=0.0.10", package="uucore", path="src/uucore" }
uucore = { version=">=0.0.11", package="uucore", path="src/uucore" }
selinux = { version="0.2.3", optional = true }
# * uutils
uu_test = { optional=true, version="0.0.8", package="uu_test", path="src/uu/test" }
uu_test = { optional=true, version="0.0.9", package="uu_test", path="src/uu/test" }
#
arch = { optional=true, version="0.0.8", package="uu_arch", path="src/uu/arch" }
base32 = { optional=true, version="0.0.8", package="uu_base32", path="src/uu/base32" }
base64 = { optional=true, version="0.0.8", package="uu_base64", path="src/uu/base64" }
basename = { optional=true, version="0.0.8", package="uu_basename", path="src/uu/basename" }
basenc = { optional=true, version="0.0.8", package="uu_basenc", path="src/uu/basenc" }
cat = { optional=true, version="0.0.8", package="uu_cat", path="src/uu/cat" }
chcon = { optional=true, version="0.0.8", package="uu_chcon", path="src/uu/chcon" }
chgrp = { optional=true, version="0.0.8", package="uu_chgrp", path="src/uu/chgrp" }
chmod = { optional=true, version="0.0.8", package="uu_chmod", path="src/uu/chmod" }
chown = { optional=true, version="0.0.8", package="uu_chown", path="src/uu/chown" }
chroot = { optional=true, version="0.0.8", package="uu_chroot", path="src/uu/chroot" }
cksum = { optional=true, version="0.0.8", package="uu_cksum", path="src/uu/cksum" }
comm = { optional=true, version="0.0.8", package="uu_comm", path="src/uu/comm" }
cp = { optional=true, version="0.0.8", package="uu_cp", path="src/uu/cp" }
csplit = { optional=true, version="0.0.8", package="uu_csplit", path="src/uu/csplit" }
cut = { optional=true, version="0.0.8", package="uu_cut", path="src/uu/cut" }
date = { optional=true, version="0.0.8", package="uu_date", path="src/uu/date" }
dd = { optional=true, version="0.0.8", package="uu_dd", path="src/uu/dd" }
df = { optional=true, version="0.0.8", package="uu_df", path="src/uu/df" }
dircolors= { optional=true, version="0.0.8", package="uu_dircolors", path="src/uu/dircolors" }
dirname = { optional=true, version="0.0.8", package="uu_dirname", path="src/uu/dirname" }
du = { optional=true, version="0.0.8", package="uu_du", path="src/uu/du" }
echo = { optional=true, version="0.0.8", package="uu_echo", path="src/uu/echo" }
env = { optional=true, version="0.0.8", package="uu_env", path="src/uu/env" }
expand = { optional=true, version="0.0.8", package="uu_expand", path="src/uu/expand" }
expr = { optional=true, version="0.0.8", package="uu_expr", path="src/uu/expr" }
factor = { optional=true, version="0.0.8", package="uu_factor", path="src/uu/factor" }
false = { optional=true, version="0.0.8", package="uu_false", path="src/uu/false" }
fmt = { optional=true, version="0.0.8", package="uu_fmt", path="src/uu/fmt" }
fold = { optional=true, version="0.0.8", package="uu_fold", path="src/uu/fold" }
groups = { optional=true, version="0.0.8", package="uu_groups", path="src/uu/groups" }
hashsum = { optional=true, version="0.0.8", package="uu_hashsum", path="src/uu/hashsum" }
head = { optional=true, version="0.0.8", package="uu_head", path="src/uu/head" }
hostid = { optional=true, version="0.0.8", package="uu_hostid", path="src/uu/hostid" }
hostname = { optional=true, version="0.0.8", package="uu_hostname", path="src/uu/hostname" }
id = { optional=true, version="0.0.8", package="uu_id", path="src/uu/id" }
install = { optional=true, version="0.0.8", package="uu_install", path="src/uu/install" }
join = { optional=true, version="0.0.8", package="uu_join", path="src/uu/join" }
kill = { optional=true, version="0.0.8", package="uu_kill", path="src/uu/kill" }
link = { optional=true, version="0.0.8", package="uu_link", path="src/uu/link" }
ln = { optional=true, version="0.0.8", package="uu_ln", path="src/uu/ln" }
ls = { optional=true, version="0.0.8", package="uu_ls", path="src/uu/ls" }
logname = { optional=true, version="0.0.8", package="uu_logname", path="src/uu/logname" }
mkdir = { optional=true, version="0.0.8", package="uu_mkdir", path="src/uu/mkdir" }
mkfifo = { optional=true, version="0.0.8", package="uu_mkfifo", path="src/uu/mkfifo" }
mknod = { optional=true, version="0.0.8", package="uu_mknod", path="src/uu/mknod" }
mktemp = { optional=true, version="0.0.8", package="uu_mktemp", path="src/uu/mktemp" }
more = { optional=true, version="0.0.8", package="uu_more", path="src/uu/more" }
mv = { optional=true, version="0.0.8", package="uu_mv", path="src/uu/mv" }
nice = { optional=true, version="0.0.8", package="uu_nice", path="src/uu/nice" }
nl = { optional=true, version="0.0.8", package="uu_nl", path="src/uu/nl" }
nohup = { optional=true, version="0.0.8", package="uu_nohup", path="src/uu/nohup" }
nproc = { optional=true, version="0.0.8", package="uu_nproc", path="src/uu/nproc" }
numfmt = { optional=true, version="0.0.8", package="uu_numfmt", path="src/uu/numfmt" }
od = { optional=true, version="0.0.8", package="uu_od", path="src/uu/od" }
paste = { optional=true, version="0.0.8", package="uu_paste", path="src/uu/paste" }
pathchk = { optional=true, version="0.0.8", package="uu_pathchk", path="src/uu/pathchk" }
pinky = { optional=true, version="0.0.8", package="uu_pinky", path="src/uu/pinky" }
pr = { optional=true, version="0.0.8", package="uu_pr", path="src/uu/pr" }
printenv = { optional=true, version="0.0.8", package="uu_printenv", path="src/uu/printenv" }
printf = { optional=true, version="0.0.8", package="uu_printf", path="src/uu/printf" }
ptx = { optional=true, version="0.0.8", package="uu_ptx", path="src/uu/ptx" }
pwd = { optional=true, version="0.0.8", package="uu_pwd", path="src/uu/pwd" }
readlink = { optional=true, version="0.0.8", package="uu_readlink", path="src/uu/readlink" }
realpath = { optional=true, version="0.0.8", package="uu_realpath", path="src/uu/realpath" }
relpath = { optional=true, version="0.0.8", package="uu_relpath", path="src/uu/relpath" }
rm = { optional=true, version="0.0.8", package="uu_rm", path="src/uu/rm" }
rmdir = { optional=true, version="0.0.8", package="uu_rmdir", path="src/uu/rmdir" }
runcon = { optional=true, version="0.0.8", package="uu_runcon", path="src/uu/runcon" }
seq = { optional=true, version="0.0.8", package="uu_seq", path="src/uu/seq" }
shred = { optional=true, version="0.0.8", package="uu_shred", path="src/uu/shred" }
shuf = { optional=true, version="0.0.8", package="uu_shuf", path="src/uu/shuf" }
sleep = { optional=true, version="0.0.8", package="uu_sleep", path="src/uu/sleep" }
sort = { optional=true, version="0.0.8", package="uu_sort", path="src/uu/sort" }
split = { optional=true, version="0.0.8", package="uu_split", path="src/uu/split" }
stat = { optional=true, version="0.0.8", package="uu_stat", path="src/uu/stat" }
stdbuf = { optional=true, version="0.0.8", package="uu_stdbuf", path="src/uu/stdbuf" }
sum = { optional=true, version="0.0.8", package="uu_sum", path="src/uu/sum" }
sync = { optional=true, version="0.0.8", package="uu_sync", path="src/uu/sync" }
tac = { optional=true, version="0.0.8", package="uu_tac", path="src/uu/tac" }
tail = { optional=true, version="0.0.8", package="uu_tail", path="src/uu/tail" }
tee = { optional=true, version="0.0.8", package="uu_tee", path="src/uu/tee" }
timeout = { optional=true, version="0.0.8", package="uu_timeout", path="src/uu/timeout" }
touch = { optional=true, version="0.0.8", package="uu_touch", path="src/uu/touch" }
tr = { optional=true, version="0.0.8", package="uu_tr", path="src/uu/tr" }
true = { optional=true, version="0.0.8", package="uu_true", path="src/uu/true" }
truncate = { optional=true, version="0.0.8", package="uu_truncate", path="src/uu/truncate" }
tsort = { optional=true, version="0.0.8", package="uu_tsort", path="src/uu/tsort" }
tty = { optional=true, version="0.0.8", package="uu_tty", path="src/uu/tty" }
uname = { optional=true, version="0.0.8", package="uu_uname", path="src/uu/uname" }
unexpand = { optional=true, version="0.0.8", package="uu_unexpand", path="src/uu/unexpand" }
uniq = { optional=true, version="0.0.8", package="uu_uniq", path="src/uu/uniq" }
unlink = { optional=true, version="0.0.8", package="uu_unlink", path="src/uu/unlink" }
uptime = { optional=true, version="0.0.8", package="uu_uptime", path="src/uu/uptime" }
users = { optional=true, version="0.0.8", package="uu_users", path="src/uu/users" }
wc = { optional=true, version="0.0.8", package="uu_wc", path="src/uu/wc" }
who = { optional=true, version="0.0.8", package="uu_who", path="src/uu/who" }
whoami = { optional=true, version="0.0.8", package="uu_whoami", path="src/uu/whoami" }
yes = { optional=true, version="0.0.8", package="uu_yes", path="src/uu/yes" }
arch = { optional=true, version="0.0.9", package="uu_arch", path="src/uu/arch" }
base32 = { optional=true, version="0.0.9", package="uu_base32", path="src/uu/base32" }
base64 = { optional=true, version="0.0.9", package="uu_base64", path="src/uu/base64" }
basename = { optional=true, version="0.0.9", package="uu_basename", path="src/uu/basename" }
basenc = { optional=true, version="0.0.9", package="uu_basenc", path="src/uu/basenc" }
cat = { optional=true, version="0.0.9", package="uu_cat", path="src/uu/cat" }
chcon = { optional=true, version="0.0.9", package="uu_chcon", path="src/uu/chcon" }
chgrp = { optional=true, version="0.0.9", package="uu_chgrp", path="src/uu/chgrp" }
chmod = { optional=true, version="0.0.9", package="uu_chmod", path="src/uu/chmod" }
chown = { optional=true, version="0.0.9", package="uu_chown", path="src/uu/chown" }
chroot = { optional=true, version="0.0.9", package="uu_chroot", path="src/uu/chroot" }
cksum = { optional=true, version="0.0.9", package="uu_cksum", path="src/uu/cksum" }
comm = { optional=true, version="0.0.9", package="uu_comm", path="src/uu/comm" }
cp = { optional=true, version="0.0.9", package="uu_cp", path="src/uu/cp" }
csplit = { optional=true, version="0.0.9", package="uu_csplit", path="src/uu/csplit" }
cut = { optional=true, version="0.0.9", package="uu_cut", path="src/uu/cut" }
date = { optional=true, version="0.0.9", package="uu_date", path="src/uu/date" }
dd = { optional=true, version="0.0.9", package="uu_dd", path="src/uu/dd" }
df = { optional=true, version="0.0.9", package="uu_df", path="src/uu/df" }
dircolors= { optional=true, version="0.0.9", package="uu_dircolors", path="src/uu/dircolors" }
dirname = { optional=true, version="0.0.9", package="uu_dirname", path="src/uu/dirname" }
du = { optional=true, version="0.0.9", package="uu_du", path="src/uu/du" }
echo = { optional=true, version="0.0.9", package="uu_echo", path="src/uu/echo" }
env = { optional=true, version="0.0.9", package="uu_env", path="src/uu/env" }
expand = { optional=true, version="0.0.9", package="uu_expand", path="src/uu/expand" }
expr = { optional=true, version="0.0.9", package="uu_expr", path="src/uu/expr" }
factor = { optional=true, version="0.0.9", package="uu_factor", path="src/uu/factor" }
false = { optional=true, version="0.0.9", package="uu_false", path="src/uu/false" }
fmt = { optional=true, version="0.0.9", package="uu_fmt", path="src/uu/fmt" }
fold = { optional=true, version="0.0.9", package="uu_fold", path="src/uu/fold" }
groups = { optional=true, version="0.0.9", package="uu_groups", path="src/uu/groups" }
hashsum = { optional=true, version="0.0.9", package="uu_hashsum", path="src/uu/hashsum" }
head = { optional=true, version="0.0.9", package="uu_head", path="src/uu/head" }
hostid = { optional=true, version="0.0.9", package="uu_hostid", path="src/uu/hostid" }
hostname = { optional=true, version="0.0.9", package="uu_hostname", path="src/uu/hostname" }
id = { optional=true, version="0.0.9", package="uu_id", path="src/uu/id" }
install = { optional=true, version="0.0.9", package="uu_install", path="src/uu/install" }
join = { optional=true, version="0.0.9", package="uu_join", path="src/uu/join" }
kill = { optional=true, version="0.0.9", package="uu_kill", path="src/uu/kill" }
link = { optional=true, version="0.0.9", package="uu_link", path="src/uu/link" }
ln = { optional=true, version="0.0.9", package="uu_ln", path="src/uu/ln" }
ls = { optional=true, version="0.0.9", package="uu_ls", path="src/uu/ls" }
logname = { optional=true, version="0.0.9", package="uu_logname", path="src/uu/logname" }
mkdir = { optional=true, version="0.0.9", package="uu_mkdir", path="src/uu/mkdir" }
mkfifo = { optional=true, version="0.0.9", package="uu_mkfifo", path="src/uu/mkfifo" }
mknod = { optional=true, version="0.0.9", package="uu_mknod", path="src/uu/mknod" }
mktemp = { optional=true, version="0.0.9", package="uu_mktemp", path="src/uu/mktemp" }
more = { optional=true, version="0.0.9", package="uu_more", path="src/uu/more" }
mv = { optional=true, version="0.0.9", package="uu_mv", path="src/uu/mv" }
nice = { optional=true, version="0.0.9", package="uu_nice", path="src/uu/nice" }
nl = { optional=true, version="0.0.9", package="uu_nl", path="src/uu/nl" }
nohup = { optional=true, version="0.0.9", package="uu_nohup", path="src/uu/nohup" }
nproc = { optional=true, version="0.0.9", package="uu_nproc", path="src/uu/nproc" }
numfmt = { optional=true, version="0.0.9", package="uu_numfmt", path="src/uu/numfmt" }
od = { optional=true, version="0.0.9", package="uu_od", path="src/uu/od" }
paste = { optional=true, version="0.0.9", package="uu_paste", path="src/uu/paste" }
pathchk = { optional=true, version="0.0.9", package="uu_pathchk", path="src/uu/pathchk" }
pinky = { optional=true, version="0.0.9", package="uu_pinky", path="src/uu/pinky" }
pr = { optional=true, version="0.0.9", package="uu_pr", path="src/uu/pr" }
printenv = { optional=true, version="0.0.9", package="uu_printenv", path="src/uu/printenv" }
printf = { optional=true, version="0.0.9", package="uu_printf", path="src/uu/printf" }
ptx = { optional=true, version="0.0.9", package="uu_ptx", path="src/uu/ptx" }
pwd = { optional=true, version="0.0.9", package="uu_pwd", path="src/uu/pwd" }
readlink = { optional=true, version="0.0.9", package="uu_readlink", path="src/uu/readlink" }
realpath = { optional=true, version="0.0.9", package="uu_realpath", path="src/uu/realpath" }
relpath = { optional=true, version="0.0.9", package="uu_relpath", path="src/uu/relpath" }
rm = { optional=true, version="0.0.9", package="uu_rm", path="src/uu/rm" }
rmdir = { optional=true, version="0.0.9", package="uu_rmdir", path="src/uu/rmdir" }
runcon = { optional=true, version="0.0.9", package="uu_runcon", path="src/uu/runcon" }
seq = { optional=true, version="0.0.9", package="uu_seq", path="src/uu/seq" }
shred = { optional=true, version="0.0.9", package="uu_shred", path="src/uu/shred" }
shuf = { optional=true, version="0.0.9", package="uu_shuf", path="src/uu/shuf" }
sleep = { optional=true, version="0.0.9", package="uu_sleep", path="src/uu/sleep" }
sort = { optional=true, version="0.0.9", package="uu_sort", path="src/uu/sort" }
split = { optional=true, version="0.0.9", package="uu_split", path="src/uu/split" }
stat = { optional=true, version="0.0.9", package="uu_stat", path="src/uu/stat" }
stdbuf = { optional=true, version="0.0.9", package="uu_stdbuf", path="src/uu/stdbuf" }
sum = { optional=true, version="0.0.9", package="uu_sum", path="src/uu/sum" }
sync = { optional=true, version="0.0.9", package="uu_sync", path="src/uu/sync" }
tac = { optional=true, version="0.0.9", package="uu_tac", path="src/uu/tac" }
tail = { optional=true, version="0.0.9", package="uu_tail", path="src/uu/tail" }
tee = { optional=true, version="0.0.9", package="uu_tee", path="src/uu/tee" }
timeout = { optional=true, version="0.0.9", package="uu_timeout", path="src/uu/timeout" }
touch = { optional=true, version="0.0.9", package="uu_touch", path="src/uu/touch" }
tr = { optional=true, version="0.0.9", package="uu_tr", path="src/uu/tr" }
true = { optional=true, version="0.0.9", package="uu_true", path="src/uu/true" }
truncate = { optional=true, version="0.0.9", package="uu_truncate", path="src/uu/truncate" }
tsort = { optional=true, version="0.0.9", package="uu_tsort", path="src/uu/tsort" }
tty = { optional=true, version="0.0.9", package="uu_tty", path="src/uu/tty" }
uname = { optional=true, version="0.0.9", package="uu_uname", path="src/uu/uname" }
unexpand = { optional=true, version="0.0.9", package="uu_unexpand", path="src/uu/unexpand" }
uniq = { optional=true, version="0.0.9", package="uu_uniq", path="src/uu/uniq" }
unlink = { optional=true, version="0.0.9", package="uu_unlink", path="src/uu/unlink" }
uptime = { optional=true, version="0.0.9", package="uu_uptime", path="src/uu/uptime" }
users = { optional=true, version="0.0.9", package="uu_users", path="src/uu/users" }
wc = { optional=true, version="0.0.9", package="uu_wc", path="src/uu/wc" }
who = { optional=true, version="0.0.9", package="uu_who", path="src/uu/who" }
whoami = { optional=true, version="0.0.9", package="uu_whoami", path="src/uu/whoami" }
yes = { optional=true, version="0.0.9", package="uu_yes", path="src/uu/yes" }
# this breaks clippy linting with: "tests/by-util/test_factor_benches.rs: No such file or directory (os error 2)"
# factor_benches = { optional = true, version = "0.0.0", package = "uu_factor_benches", path = "tests/benches/factor" }
@ -361,19 +361,19 @@ yes = { optional=true, version="0.0.8", package="uu_yes", path="src/uu/yes"
#pin_cc = { version="1.0.61, < 1.0.62", package="cc" } ## cc v1.0.62 has compiler errors for MinRustV v1.32.0, requires 1.34 (for `std::str::split_ascii_whitespace()`)
[dev-dependencies]
chrono = "0.4.11"
chrono = "^0.4.11"
conv = "0.3"
filetime = "0.2"
glob = "0.3.0"
libc = "0.2"
pretty_assertions = "0.7.2"
pretty_assertions = "1"
rand = "0.7"
regex = "1.0"
sha1 = { version="0.6", features=["std"] }
tempfile = "3.2.0"
time = "0.1"
unindent = "0.1"
uucore = { version=">=0.0.10", package="uucore", path="src/uucore", features=["entries", "process"] }
uucore = { version=">=0.0.11", package="uucore", path="src/uucore", features=["entries", "process"] }
walkdir = "2.2"
atty = "0.2"
@ -381,15 +381,10 @@ atty = "0.2"
rlimit = "0.4.0"
[target.'cfg(unix)'.dev-dependencies]
nix = "=0.23.1"
nix = "0.23.1"
rust-users = { version="0.10", package="users" }
unix_socket = "0.5.0"
[[bin]]
name = "coreutils"
path = "src/bin/coreutils.rs"
[patch.crates-io]
# FixME: [2021-11-16; rivy] remove 'nix' patch when MacOS compatibility is restored; ref: <https://github.com/nix-rust/nix/pull/1590>
# nix = { git = "https://github.com/rivy-t/nix" }
nix = { path = "vendor/nix-v0.23.1-patched" }

View file

@ -47,12 +47,12 @@ BUSYBOX_VER := 1.32.1
BUSYBOX_SRC := $(BUSYBOX_ROOT)/busybox-$(BUSYBOX_VER)
ifeq ($(SELINUX_ENABLED),)
SELINUX_ENABLED := 0
ifneq ($(OS),Windows_NT)
ifeq ($(shell /sbin/selinuxenabled 2>/dev/null ; echo $$?),0)
SELINUX_ENABLED := 1
endif
endif
SELINUX_ENABLED := 0
ifneq ($(OS),Windows_NT)
ifeq ($(shell /sbin/selinuxenabled 2>/dev/null ; echo $$?),0)
SELINUX_ENABLED := 1
endif
endif
endif
# Possible programs
@ -161,11 +161,11 @@ SELINUX_PROGS := \
runcon
ifneq ($(OS),Windows_NT)
PROGS := $(PROGS) $(UNIX_PROGS)
PROGS := $(PROGS) $(UNIX_PROGS)
endif
ifeq ($(SELINUX_ENABLED),1)
PROGS := $(PROGS) $(SELINUX_PROGS)
PROGS := $(PROGS) $(SELINUX_PROGS)
endif
UTILS ?= $(PROGS)

View file

@ -39,7 +39,7 @@ to compile anywhere, and this is as good a way as any to try and learn it.
### Rust Version
uutils follows Rust's release channels and is tested against stable, beta and nightly.
The current oldest supported version of the Rust compiler is `1.47`.
The current oldest supported version of the Rust compiler is `1.54`.
On both Windows and Redox, only the nightly version is tested currently.

View file

@ -18,7 +18,7 @@ pub fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
// println!("cargo:warning=out_dir={}", out_dir);
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap().replace("\\", "/");
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap().replace('\\', "/");
// println!("cargo:warning=manifest_dir={}", manifest_dir);
let util_tests_dir = format!("{}/tests/by-util", manifest_dir);
// println!("cargo:warning=util_tests_dir={}", util_tests_dir);
@ -83,7 +83,7 @@ pub fn main() {
mf.write_all(
format!(
"\tmap.insert(\"{k}\", ({krate}::uumain, {krate}::uu_app));\n",
k = krate[override_prefix.len()..].to_string(),
k = &krate[override_prefix.len()..],
krate = krate
)
.as_bytes(),
@ -92,7 +92,7 @@ pub fn main() {
tf.write_all(
format!(
"#[path=\"{dir}/test_{k}.rs\"]\nmod test_{k};\n",
k = krate[override_prefix.len()..].to_string(),
k = &krate[override_prefix.len()..],
dir = util_tests_dir,
)
.as_bytes(),

View file

@ -1,6 +1,6 @@
[package]
name = "uu_arch"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "arch ~ (uutils) display machine architecture"
@ -15,10 +15,10 @@ edition = "2018"
path = "src/arch.rs"
[dependencies]
platform-info = "0.1"
platform-info = "0.2"
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "arch"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_base32"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "base32 ~ (uutils) decode/encode input (base32-encoding)"
@ -16,8 +16,8 @@ path = "src/base32.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features = ["encoding"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features = ["encoding"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "base32"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_base64"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "base64 ~ (uutils) decode/encode input (base64-encoding)"
@ -16,8 +16,8 @@ path = "src/base64.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features = ["encoding"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features = ["encoding"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
uu_base32 = { version=">=0.0.8", package="uu_base32", path="../base32"}
[[bin]]

View file

@ -1,6 +1,6 @@
[package]
name = "uu_basename"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "basename ~ (uutils) display PATHNAME with leading directory components removed"
@ -16,8 +16,8 @@ path = "src/basename.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "basename"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_basenc"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "basenc ~ (uutils) decode/encode input"
@ -16,8 +16,8 @@ path = "src/basenc.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features = ["encoding"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features = ["encoding"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
uu_base32 = { version=">=0.0.8", package="uu_base32", path="../base32"}
[[bin]]

View file

@ -1,6 +1,6 @@
[package]
name = "uu_cat"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "cat ~ (uutils) concatenate and display input"
@ -18,12 +18,12 @@ path = "src/cat.rs"
clap = { version = "2.33", features = ["wrap_help"] }
thiserror = "1.0"
atty = "0.2"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "pipes"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["fs", "pipes"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[target.'cfg(unix)'.dependencies]
unix_socket = "0.5.0"
nix = "=0.23.1"
nix = "0.23.1"
[target.'cfg(windows)'.dependencies]
winapi-util = "0.1.5"

View file

@ -20,6 +20,7 @@ use std::io::{self, Read, Write};
use thiserror::Error;
use uucore::display::Quotable;
use uucore::error::UResult;
use uucore::fs::FileInformation;
#[cfg(unix)]
use std::os::unix::io::AsRawFd;
@ -317,8 +318,7 @@ fn cat_path(
path: &str,
options: &OutputOptions,
state: &mut OutputState,
#[cfg(unix)] out_info: &nix::sys::stat::FileStat,
#[cfg(windows)] out_info: &winapi_util::file::Information,
out_info: Option<&FileInformation>,
) -> CatResult<()> {
if path == "-" {
let stdin = io::stdin();
@ -342,10 +342,15 @@ fn cat_path(
}
_ => {
let file = File::open(path)?;
#[cfg(any(windows, unix))]
if same_file(out_info, &file) {
return Err(CatError::OutputIsInput);
if let Some(out_info) = out_info {
if out_info.file_size() != 0
&& FileInformation::from_file(&file).as_ref() == Some(out_info)
{
return Err(CatError::OutputIsInput);
}
}
let mut handle = InputHandle {
reader: file,
is_interactive: false,
@ -355,25 +360,8 @@ fn cat_path(
}
}
#[cfg(unix)]
fn same_file(a_info: &nix::sys::stat::FileStat, b: &File) -> bool {
let b_info = nix::sys::stat::fstat(b.as_raw_fd()).unwrap();
b_info.st_size != 0 && b_info.st_dev == a_info.st_dev && b_info.st_ino == a_info.st_ino
}
#[cfg(windows)]
fn same_file(a_info: &winapi_util::file::Information, b: &File) -> bool {
let b_info = winapi_util::file::information(b).unwrap();
b_info.file_size() != 0
&& b_info.volume_serial_number() == a_info.volume_serial_number()
&& b_info.file_index() == a_info.file_index()
}
fn cat_files(files: Vec<String>, options: &OutputOptions) -> UResult<()> {
#[cfg(windows)]
let out_info = winapi_util::file::information(&std::io::stdout()).unwrap();
#[cfg(unix)]
let out_info = nix::sys::stat::fstat(std::io::stdout().as_raw_fd()).unwrap();
let out_info = FileInformation::from_file(&std::io::stdout());
let mut state = OutputState {
line_number: 1,
@ -384,7 +372,7 @@ fn cat_files(files: Vec<String>, options: &OutputOptions) -> UResult<()> {
let mut error_messages: Vec<String> = Vec::new();
for path in &files {
if let Err(err) = cat_path(path, options, &mut state, &out_info) {
if let Err(err) = cat_path(path, options, &mut state, out_info.as_ref()) {
error_messages.push(format!("{}: {}", path.maybe_quote(), err));
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "uu_chcon"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "chcon ~ (uutils) change file security context"

View file

@ -2,7 +2,8 @@
#![allow(clippy::upper_case_acronyms)]
use uucore::{display::Quotable, show_error, show_usage_error, show_warning};
use uucore::error::{UResult, USimpleError, UUsageError};
use uucore::{display::Quotable, show_error, show_warning};
use clap::{App, Arg};
use selinux::{OpaqueSecurityContext, SecurityContext};
@ -60,7 +61,8 @@ fn get_usage() -> String {
)
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = get_usage();
let config = uu_app().usage(usage.as_ref());
@ -72,14 +74,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
match r.kind {
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
println!("{}", r);
return libc::EXIT_SUCCESS;
return Ok(());
}
_ => {}
}
}
show_usage_error!("{}.\n", r);
return libc::EXIT_FAILURE;
return Err(UUsageError::new(libc::EXIT_FAILURE, format!("{}.\n", r)));
}
};
@ -98,8 +99,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
match result {
Err(r) => {
show_error!("{}.", report_full_error(&r));
return libc::EXIT_FAILURE;
return Err(USimpleError::new(
libc::EXIT_FAILURE,
format!("{}.", report_full_error(&r)),
));
}
Ok(file_context) => SELinuxSecurityContext::File(file_context),
@ -111,14 +114,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Ok(context) => context,
Err(_r) => {
show_error!("Invalid security context {}.", context.quote());
return libc::EXIT_FAILURE;
return Err(USimpleError::new(
libc::EXIT_FAILURE,
format!("Invalid security context {}.", context.quote()),
));
}
};
if SecurityContext::from_c_str(&c_context, false).check() == Some(false) {
show_error!("Invalid security context {}.", context.quote());
return libc::EXIT_FAILURE;
return Err(USimpleError::new(
libc::EXIT_FAILURE,
format!("Invalid security context {}.", context.quote()),
));
}
SELinuxSecurityContext::String(Some(c_context))
@ -132,8 +139,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Ok(r) => Some(r),
Err(r) => {
show_error!("{}.", report_full_error(&r));
return libc::EXIT_FAILURE;
return Err(USimpleError::new(
libc::EXIT_FAILURE,
format!("{}.", report_full_error(&r)),
));
}
}
} else {
@ -142,13 +151,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let results = process_files(&options, &context, root_dev_ino);
if results.is_empty() {
return libc::EXIT_SUCCESS;
return Ok(());
}
for result in &results {
show_error!("{}.", report_full_error(result));
}
libc::EXIT_FAILURE
Err(libc::EXIT_FAILURE.into())
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -1,6 +1,6 @@
[package]
name = "uu_chgrp"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "chgrp ~ (uutils) change the group ownership of FILE"
@ -16,8 +16,8 @@ path = "src/chgrp.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "chgrp"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_chmod"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "chmod ~ (uutils) change mode of FILE"
@ -17,8 +17,8 @@ path = "src/chmod.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "mode"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["fs", "mode"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
walkdir = "2.2"
[[bin]]

View file

@ -1,6 +1,6 @@
[package]
name = "uu_chown"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "chown ~ (uutils) change the ownership of FILE"
@ -16,8 +16,8 @@ path = "src/chown.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "chown"

View file

@ -183,7 +183,7 @@ fn parse_spec(spec: &str, sep: char) -> UResult<(Option<u32>, Option<u32>)> {
let uid = if !user.is_empty() {
Some(match Passwd::locate(user) {
Ok(u) => u.uid(), // We have been able to get the uid
Ok(u) => u.uid, // We have been able to get the uid
Err(_) =>
// we have NOT been able to find the uid
// but we could be in the case where we have user.group
@ -208,7 +208,7 @@ fn parse_spec(spec: &str, sep: char) -> UResult<(Option<u32>, Option<u32>)> {
Some(
Group::locate(group)
.map_err(|_| USimpleError::new(1, format!("invalid group: {}", spec.quote())))?
.gid(),
.gid,
)
} else {
None

View file

@ -1,6 +1,6 @@
[package]
name = "uu_chroot"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "chroot ~ (uutils) run COMMAND under a new root directory"
@ -16,8 +16,8 @@ path = "src/chroot.rs"
[dependencies]
clap= "2.33"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["entries"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "chroot"

View file

@ -7,15 +7,15 @@
// file that was distributed with this source code.
// spell-checker:ignore (ToDO) NEWROOT Userspec pstatus
mod error;
#[macro_use]
extern crate uucore;
use crate::error::ChrootError;
use clap::{crate_version, App, Arg};
use std::ffi::CString;
use std::io::Error;
use std::path::Path;
use std::process::Command;
use uucore::display::Quotable;
use uucore::error::{set_exit_code, UResult};
use uucore::libc::{self, chroot, setgid, setgroups, setuid};
use uucore::{entries, InvalidEncodingHandling};
@ -31,7 +31,8 @@ mod options {
pub const COMMAND: &str = "command";
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
@ -44,19 +45,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let newroot: &Path = match matches.value_of(options::NEWROOT) {
Some(v) => Path::new(v),
None => crash!(
1,
"Missing operand: NEWROOT\nTry '{} --help' for more information.",
uucore::execution_phrase()
),
None => return Err(ChrootError::MissingNewRoot.into()),
};
if !newroot.is_dir() {
crash!(
1,
"cannot change root directory to {}: no such directory",
newroot.quote()
);
return Err(ChrootError::NoSuchDirectory(format!("{}", newroot.display())).into());
}
let commands = match matches.values_of(options::COMMAND) {
@ -82,29 +75,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let chroot_args = &command[1..];
// NOTE: Tests can only trigger code beyond this point if they're invoked with root permissions
set_context(newroot, &matches);
set_context(newroot, &matches)?;
let pstatus = Command::new(chroot_command)
.args(chroot_args)
.status()
.unwrap_or_else(|e| {
// TODO: Exit status:
// 125 if chroot itself fails
// 126 if command is found but cannot be invoked
// 127 if command cannot be found
crash!(
1,
"failed to run command {}: {}",
command[0].to_string().quote(),
e
)
});
let pstatus = match Command::new(chroot_command).args(chroot_args).status() {
Ok(status) => status,
Err(e) => return Err(ChrootError::CommandFailed(command[0].to_string(), e).into()),
};
if pstatus.success() {
let code = if pstatus.success() {
0
} else {
pstatus.code().unwrap_or(-1)
}
};
set_exit_code(code);
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {
@ -157,7 +141,7 @@ pub fn uu_app() -> App<'static, 'static> {
)
}
fn set_context(root: &Path, options: &clap::ArgMatches) {
fn set_context(root: &Path, options: &clap::ArgMatches) -> UResult<()> {
let userspec_str = options.value_of(options::USERSPEC);
let user_str = options.value_of(options::USER).unwrap_or_default();
let group_str = options.value_of(options::GROUP).unwrap_or_default();
@ -166,7 +150,7 @@ fn set_context(root: &Path, options: &clap::ArgMatches) {
Some(u) => {
let s: Vec<&str> = u.split(':').collect();
if s.len() != 2 || s.iter().any(|&spec| spec.is_empty()) {
crash!(1, "invalid userspec: {}", u.quote())
return Err(ChrootError::InvalidUserspec(u.to_string()).into());
};
s
}
@ -179,44 +163,40 @@ fn set_context(root: &Path, options: &clap::ArgMatches) {
(userspec[0], userspec[1])
};
enter_chroot(root);
enter_chroot(root)?;
set_groups_from_str(groups_str);
set_main_group(group);
set_user(user);
set_groups_from_str(groups_str)?;
set_main_group(group)?;
set_user(user)?;
Ok(())
}
fn enter_chroot(root: &Path) {
fn enter_chroot(root: &Path) -> UResult<()> {
std::env::set_current_dir(root).unwrap();
let err = unsafe {
chroot(CString::new(".").unwrap().as_bytes_with_nul().as_ptr() as *const libc::c_char)
};
if err != 0 {
crash!(
1,
"cannot chroot to {}: {}",
root.quote(),
Error::last_os_error()
)
};
if err == 0 {
Ok(())
} else {
Err(ChrootError::CannotEnter(format!("{}", root.display()), Error::last_os_error()).into())
}
}
fn set_main_group(group: &str) {
fn set_main_group(group: &str) -> UResult<()> {
if !group.is_empty() {
let group_id = match entries::grp2gid(group) {
Ok(g) => g,
_ => crash!(1, "no such group: {}", group.maybe_quote()),
_ => return Err(ChrootError::NoSuchGroup(group.to_string()).into()),
};
let err = unsafe { setgid(group_id) };
if err != 0 {
crash!(
1,
"cannot set gid to {}: {}",
group_id,
Error::last_os_error()
)
return Err(
ChrootError::SetGidFailed(group_id.to_string(), Error::last_os_error()).into(),
);
}
}
Ok(())
}
#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
@ -229,33 +209,33 @@ fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
unsafe { setgroups(groups.len() as libc::size_t, groups.as_ptr()) }
}
fn set_groups_from_str(groups: &str) {
fn set_groups_from_str(groups: &str) -> UResult<()> {
if !groups.is_empty() {
let groups_vec: Vec<libc::gid_t> = groups
.split(',')
.map(|x| match entries::grp2gid(x) {
let mut groups_vec = vec![];
for group in groups.split(',') {
let gid = match entries::grp2gid(group) {
Ok(g) => g,
_ => crash!(1, "no such group: {}", x),
})
.collect();
Err(_) => return Err(ChrootError::NoSuchGroup(group.to_string()).into()),
};
groups_vec.push(gid);
}
let err = set_groups(groups_vec);
if err != 0 {
crash!(1, "cannot set groups: {}", Error::last_os_error())
return Err(ChrootError::SetGroupsFailed(Error::last_os_error()).into());
}
}
Ok(())
}
fn set_user(user: &str) {
fn set_user(user: &str) -> UResult<()> {
if !user.is_empty() {
let user_id = entries::usr2uid(user).unwrap();
let err = unsafe { setuid(user_id as libc::uid_t) };
if err != 0 {
crash!(
1,
"cannot set user to {}: {}",
user.maybe_quote(),
Error::last_os_error()
)
return Err(
ChrootError::SetUserFailed(user.to_string(), Error::last_os_error()).into(),
);
}
}
Ok(())
}

View file

@ -0,0 +1,81 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore NEWROOT Userspec userspec
//! Errors returned by chroot.
use std::fmt::Display;
use std::io::Error;
use uucore::display::Quotable;
use uucore::error::UError;
/// Errors that can happen while executing chroot.
#[derive(Debug)]
pub enum ChrootError {
/// Failed to enter the specified directory.
CannotEnter(String, Error),
/// Failed to execute the specified command.
CommandFailed(String, Error),
/// The given user and group specification was invalid.
InvalidUserspec(String),
/// The new root directory was not given.
MissingNewRoot,
/// Failed to find the specified group.
NoSuchGroup(String),
/// The given directory does not exist.
NoSuchDirectory(String),
/// The call to `setgid()` failed.
SetGidFailed(String, Error),
/// The call to `setgroups()` failed.
SetGroupsFailed(Error),
/// The call to `setuid()` failed.
SetUserFailed(String, Error),
}
impl std::error::Error for ChrootError {}
impl UError for ChrootError {
// TODO: Exit status:
// 125 if chroot itself fails
// 126 if command is found but cannot be invoked
// 127 if command cannot be found
fn code(&self) -> i32 {
1
}
}
impl Display for ChrootError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
ChrootError::CannotEnter(s, e) => write!(f, "cannot chroot to {}: {}", s.quote(), e,),
ChrootError::CommandFailed(s, e) => {
write!(f, "failed to run command {}: {}", s.to_string().quote(), e,)
}
ChrootError::InvalidUserspec(s) => write!(f, "invalid userspec: {}", s.quote(),),
ChrootError::MissingNewRoot => write!(
f,
"Missing operand: NEWROOT\nTry '{} --help' for more information.",
uucore::execution_phrase(),
),
ChrootError::NoSuchGroup(s) => write!(f, "no such group: {}", s.maybe_quote(),),
ChrootError::NoSuchDirectory(s) => write!(
f,
"cannot change root directory to {}: no such directory",
s.quote(),
),
ChrootError::SetGidFailed(s, e) => write!(f, "cannot set gid to {}: {}", s, e),
ChrootError::SetGroupsFailed(e) => write!(f, "cannot set groups: {}", e),
ChrootError::SetUserFailed(s, e) => {
write!(f, "cannot set user to {}: {}", s.maybe_quote(), e)
}
}
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "uu_cksum"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "cksum ~ (uutils) display CRC and size of input"
@ -17,8 +17,8 @@ path = "src/cksum.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "cksum"

View file

@ -6,15 +6,13 @@
// file that was distributed with this source code.
// spell-checker:ignore (ToDO) fname
#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg};
use std::fs::File;
use std::io::{self, stdin, BufReader, Read};
use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};
use uucore::show;
use uucore::InvalidEncodingHandling;
// NOTE: CRC_TABLE_LEN *must* be <= 256 as we cast 0..CRC_TABLE_LEN to u8
@ -82,27 +80,18 @@ fn cksum(fname: &str) -> io::Result<(u32, usize)> {
let mut crc = 0u32;
let mut size = 0usize;
let file;
let mut rd: Box<dyn Read> = match fname {
"-" => Box::new(stdin()),
_ => {
let path = &Path::new(fname);
if path.is_dir() {
return Err(std::io::Error::new(
io::ErrorKind::InvalidInput,
"Is a directory",
));
};
// Silent the warning as we want to the error message
#[allow(clippy::question_mark)]
if path.metadata().is_err() {
return Err(std::io::Error::new(
io::ErrorKind::NotFound,
"No such file or directory",
));
};
file = File::open(&path)?;
Box::new(BufReader::new(file))
let p = Path::new(fname);
// Directories should not give an error, but should be interpreted
// as empty files to match GNU semantics.
if p.is_dir() {
Box::new(BufReader::new(io::empty())) as Box<dyn Read>
} else {
Box::new(BufReader::new(File::open(p)?)) as Box<dyn Read>
}
}
};
@ -123,7 +112,8 @@ mod options {
pub static FILE: &str = "file";
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args
.collect_str(InvalidEncodingHandling::Ignore)
.accept_any();
@ -136,28 +126,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
};
if files.is_empty() {
match cksum("-") {
Ok((crc, size)) => println!("{} {}", crc, size),
Err(err) => {
show_error!("-: {}", err);
return 2;
}
}
return 0;
let (crc, size) = cksum("-")?;
println!("{} {}", crc, size);
return Ok(());
}
let mut exit_code = 0;
for fname in &files {
match cksum(fname.as_ref()) {
match cksum(fname.as_ref()).map_err_context(|| format!("{}", fname.maybe_quote())) {
Ok((crc, size)) => println!("{} {} {}", crc, size, fname),
Err(err) => {
show_error!("{}: {}", fname.maybe_quote(), err);
exit_code = 2;
}
}
Err(err) => show!(err),
};
}
exit_code
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -1,6 +1,6 @@
[package]
name = "uu_comm"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "comm ~ (uutils) compare sorted inputs"
@ -17,8 +17,8 @@ path = "src/comm.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "comm"

View file

@ -11,6 +11,8 @@ use std::cmp::Ordering;
use std::fs::File;
use std::io::{self, stdin, BufRead, BufReader, Stdin};
use std::path::Path;
use uucore::error::FromIo;
use uucore::error::UResult;
use uucore::InvalidEncodingHandling;
use clap::{crate_version, App, Arg, ArgMatches};
@ -128,20 +130,21 @@ fn open_file(name: &str) -> io::Result<LineReader> {
}
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage();
let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
let mut f1 = open_file(matches.value_of(options::FILE_1).unwrap()).unwrap();
let mut f2 = open_file(matches.value_of(options::FILE_2).unwrap()).unwrap();
let filename1 = matches.value_of(options::FILE_1).unwrap();
let filename2 = matches.value_of(options::FILE_2).unwrap();
let mut f1 = open_file(filename1).map_err_context(|| filename1.to_string())?;
let mut f2 = open_file(filename2).map_err_context(|| filename2.to_string())?;
comm(&mut f1, &mut f2, &matches);
0
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -1,6 +1,6 @@
[package]
name = "uu_cp"
version = "0.0.8"
version = "0.0.9"
authors = [
"Jordy Dickinson <jordy.dickinson@gmail.com>",
"Joshua S. Miller <jsmiller@uchicago.edu>",
@ -24,8 +24,8 @@ filetime = "0.2"
libc = "0.2.85"
quick-error = "1.2.3"
selinux = { version="0.2.3", optional=true }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs", "perms", "mode"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["entries", "fs", "perms", "mode"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
walkdir = "2.2"
[target.'cfg(target_os = "linux")'.dependencies]

View file

@ -8,7 +8,7 @@
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
// spell-checker:ignore (ToDO) ficlone linkgs lstat nlink nlinks pathbuf reflink strs xattrs
// spell-checker:ignore (ToDO) ficlone linkgs lstat nlink nlinks pathbuf reflink strs xattrs symlinked
#[cfg(target_os = "linux")]
#[macro_use]
@ -19,6 +19,7 @@ extern crate quick_error;
extern crate uucore;
use uucore::display::Quotable;
use uucore::fs::FileInformation;
#[cfg(windows)]
use winapi::um::fileapi::CreateFileW;
#[cfg(windows)]
@ -63,7 +64,7 @@ quick_error! {
#[derive(Debug)]
pub enum Error {
/// Simple io::Error wrapper
IoErr(err: io::Error) { from() cause(err) display("{}", err) }
IoErr(err: io::Error) { from() cause(err) display("{}", err)}
/// Wrapper for io::Error with path context
IoErrContext(err: io::Error, path: String) {
@ -838,8 +839,10 @@ fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResu
let mut non_fatal_errors = false;
let mut seen_sources = HashSet::with_capacity(sources.len());
let mut symlinked_files = HashSet::new();
for source in sources {
if seen_sources.contains(source) {
// FIXME: compare sources by the actual file they point to, not their path. (e.g. dir/file == dir/../dir/file in most cases)
show_warning!("source {} specified more than once", source.quote());
} else {
let mut found_hard_link = false;
@ -848,7 +851,9 @@ fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResu
preserve_hardlinks(&mut hard_links, source, dest, &mut found_hard_link).unwrap();
}
if !found_hard_link {
if let Err(error) = copy_source(source, target, &target_type, options) {
if let Err(error) =
copy_source(source, target, &target_type, options, &mut symlinked_files)
{
match error {
// When using --no-clobber, we don't want to show
// an error message
@ -909,15 +914,16 @@ fn copy_source(
target: &TargetSlice,
target_type: &TargetType,
options: &Options,
symlinked_files: &mut HashSet<FileInformation>,
) -> CopyResult<()> {
let source_path = Path::new(&source);
if source_path.is_dir() {
// Copy as directory
copy_directory(source, target, options)
copy_directory(source, target, options, symlinked_files)
} else {
// Copy as file
let dest = construct_dest_path(source_path, target, target_type, options)?;
copy_file(source_path, dest.as_path(), options)
copy_file(source_path, dest.as_path(), options, symlinked_files)
}
}
@ -947,14 +953,19 @@ fn adjust_canonicalization(p: &Path) -> Cow<Path> {
///
/// Any errors encountered copying files in the tree will be logged but
/// will not cause a short-circuit.
fn copy_directory(root: &Path, target: &TargetSlice, options: &Options) -> CopyResult<()> {
fn copy_directory(
root: &Path,
target: &TargetSlice,
options: &Options,
symlinked_files: &mut HashSet<FileInformation>,
) -> CopyResult<()> {
if !options.recursive {
return Err(format!("omitting directory {}", root.quote()).into());
}
// if no-dereference is enabled and this is a symlink, copy it as a file
if !options.dereference && fs::symlink_metadata(root).unwrap().file_type().is_symlink() {
return copy_file(root, target, options);
return copy_file(root, target, options, symlinked_files);
}
let current_dir =
@ -1011,7 +1022,7 @@ fn copy_directory(root: &Path, target: &TargetSlice, options: &Options) -> CopyR
let local_to_target = target.join(&local_to_root_parent);
if is_symlink && !options.dereference {
copy_link(&path, &local_to_target)?;
copy_link(&path, &local_to_target, symlinked_files)?;
} else if path.is_dir() && !local_to_target.exists() {
or_continue!(fs::create_dir_all(local_to_target));
} else if !path.is_dir() {
@ -1021,7 +1032,12 @@ fn copy_directory(root: &Path, target: &TargetSlice, options: &Options) -> CopyR
let dest = local_to_target.as_path().to_path_buf();
preserve_hardlinks(&mut hard_links, &source, dest, &mut found_hard_link).unwrap();
if !found_hard_link {
match copy_file(path.as_path(), local_to_target.as_path(), options) {
match copy_file(
path.as_path(),
local_to_target.as_path(),
options,
symlinked_files,
) {
Ok(_) => Ok(()),
Err(err) => {
if fs::symlink_metadata(&source)?.file_type().is_symlink() {
@ -1036,7 +1052,12 @@ fn copy_directory(root: &Path, target: &TargetSlice, options: &Options) -> CopyR
}?;
}
} else {
copy_file(path.as_path(), local_to_target.as_path(), options)?;
copy_file(
path.as_path(),
local_to_target.as_path(),
options,
symlinked_files,
)?;
}
}
}
@ -1145,18 +1166,24 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu
Ok(())
}
#[cfg(not(windows))]
#[allow(clippy::unnecessary_wraps)] // needed for windows version
fn symlink_file(source: &Path, dest: &Path, context: &str) -> CopyResult<()> {
match std::os::unix::fs::symlink(source, dest).context(context) {
Ok(_) => Ok(()),
Err(_) => Ok(()),
fn symlink_file(
source: &Path,
dest: &Path,
context: &str,
symlinked_files: &mut HashSet<FileInformation>,
) -> CopyResult<()> {
#[cfg(not(windows))]
{
std::os::unix::fs::symlink(source, dest).context(context)?;
}
}
#[cfg(windows)]
fn symlink_file(source: &Path, dest: &Path, context: &str) -> CopyResult<()> {
Ok(std::os::windows::fs::symlink_file(source, dest).context(context)?)
#[cfg(windows)]
{
std::os::windows::fs::symlink_file(source, dest).context(context)?;
}
if let Some(file_info) = FileInformation::from_path(dest, false) {
symlinked_files.insert(file_info);
}
Ok(())
}
fn context_for(src: &Path, dest: &Path) -> String {
@ -1183,6 +1210,7 @@ fn handle_existing_dest(source: &Path, dest: &Path, options: &Options) -> CopyRe
}
match options.overwrite {
// FIXME: print that the file was removed if --verbose is enabled
OverwriteMode::Clobber(ClobberMode::Force) => {
if fs::metadata(dest)?.permissions().readonly() {
fs::remove_file(dest)?;
@ -1206,11 +1234,39 @@ fn handle_existing_dest(source: &Path, dest: &Path, options: &Options) -> CopyRe
///
/// The original permissions of `source` will be copied to `dest`
/// after a successful copy.
fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
fn copy_file(
source: &Path,
dest: &Path,
options: &Options,
symlinked_files: &mut HashSet<FileInformation>,
) -> CopyResult<()> {
if dest.exists() {
handle_existing_dest(source, dest, options)?;
}
// Fail if dest is a dangling symlink or a symlink this program created previously
if fs::symlink_metadata(dest)
.map(|m| m.file_type().is_symlink())
.unwrap_or(false)
{
if FileInformation::from_path(dest, false)
.map(|info| symlinked_files.contains(&info))
.unwrap_or(false)
{
return Err(Error::Error(format!(
"will not copy '{}' through just-created symlink '{}'",
source.display(),
dest.display()
)));
}
if !dest.exists() {
return Err(Error::Error(format!(
"not writing through dangling symlink '{}'",
dest.display()
)));
}
}
if options.verbose {
println!("{}", context_for(source, dest));
}
@ -1255,10 +1311,10 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
fs::hard_link(&source, &dest).context(context)?;
}
CopyMode::Copy => {
copy_helper(&source, &dest, options, context)?;
copy_helper(&source, &dest, options, context, symlinked_files)?;
}
CopyMode::SymLink => {
symlink_file(&source, &dest, context)?;
symlink_file(&source, &dest, context, symlinked_files)?;
}
CopyMode::Sparse => return Err(Error::NotImplemented(options::SPARSE.to_string())),
CopyMode::Update => {
@ -1271,10 +1327,10 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
if src_time <= dest_time {
return Ok(());
} else {
copy_helper(&source, &dest, options, context)?;
copy_helper(&source, &dest, options, context, symlinked_files)?;
}
} else {
copy_helper(&source, &dest, options, context)?;
copy_helper(&source, &dest, options, context, symlinked_files)?;
}
}
CopyMode::AttrOnly => {
@ -1292,7 +1348,13 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
.map(|meta| !meta.file_type().is_symlink())
.unwrap_or(false)
{
fs::set_permissions(&dest, dest_permissions).unwrap();
// Here, to match GNU semantics, we quietly ignore an error
// if a user does not have the correct ownership to modify
// the permissions of a file.
//
// FWIW, the OS will throw an error later, on the write op, if
// the user does not have permission to write to the file.
fs::set_permissions(&dest, dest_permissions).ok();
}
for attribute in &options.preserve_attributes {
copy_attribute(&source, &dest, attribute)?;
@ -1302,7 +1364,13 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
/// Copy the file from `source` to `dest` either using the normal `fs::copy` or a
/// copy-on-write scheme if --reflink is specified and the filesystem supports it.
fn copy_helper(source: &Path, dest: &Path, options: &Options, context: &str) -> CopyResult<()> {
fn copy_helper(
source: &Path,
dest: &Path,
options: &Options,
context: &str,
symlinked_files: &mut HashSet<FileInformation>,
) -> CopyResult<()> {
if options.parents {
let parent = dest.parent().unwrap_or(dest);
fs::create_dir_all(parent)?;
@ -1312,9 +1380,9 @@ fn copy_helper(source: &Path, dest: &Path, options: &Options, context: &str) ->
/* workaround a limitation of fs::copy
* https://github.com/rust-lang/rust/issues/79390
*/
File::create(dest)?;
File::create(dest).context(dest.display().to_string())?;
} else if is_symlink {
copy_link(source, dest)?;
copy_link(source, dest, symlinked_files)?;
} else if options.reflink_mode != ReflinkMode::Never {
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
return Err("--reflink is only supported on linux and macOS"
@ -1332,7 +1400,11 @@ fn copy_helper(source: &Path, dest: &Path, options: &Options, context: &str) ->
Ok(())
}
fn copy_link(source: &Path, dest: &Path) -> CopyResult<()> {
fn copy_link(
source: &Path,
dest: &Path,
symlinked_files: &mut HashSet<FileInformation>,
) -> CopyResult<()> {
// Here, we will copy the symlink itself (actually, just recreate it)
let link = fs::read_link(&source)?;
let dest: Cow<'_, Path> = if dest.is_dir() {
@ -1352,7 +1424,7 @@ fn copy_link(source: &Path, dest: &Path) -> CopyResult<()> {
}
dest.into()
};
symlink_file(&link, &dest, &*context_for(&link, &dest))
symlink_file(&link, &dest, &*context_for(&link, &dest), symlinked_files)
}
/// Copies `source` to `dest` using copy-on-write if possible.

View file

@ -1,6 +1,6 @@
[package]
name = "uu_csplit"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "csplit ~ (uutils) Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ..., and output byte counts of each piece to standard output"
@ -18,8 +18,8 @@ path = "src/csplit.rs"
clap = { version = "2.33", features = ["wrap_help"] }
thiserror = "1.0"
regex = "1.0.0"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["entries", "fs"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "csplit"

View file

@ -2,15 +2,19 @@
#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg, ArgMatches};
use regex::Regex;
use std::cmp::Ordering;
use std::io::{self, BufReader};
use std::{
fs::{remove_file, File},
io::{BufRead, BufWriter, Write},
};
use clap::{crate_version, App, Arg, ArgMatches};
use regex::Regex;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};
use uucore::InvalidEncodingHandling;
mod csplit_error;
mod patterns;
@ -18,7 +22,6 @@ mod split_name;
use crate::csplit_error::CsplitError;
use crate::split_name::SplitName;
use uucore::InvalidEncodingHandling;
static SUMMARY: &str = "split a file into sections determined by context lines";
static LONG_HELP: &str = "Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ..., and output byte counts of each piece to standard output.";
@ -712,7 +715,8 @@ mod tests {
}
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage();
let args = args
.collect_str(InvalidEncodingHandling::Ignore)
@ -729,20 +733,22 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.unwrap()
.map(str::to_string)
.collect();
let patterns = crash_if_err!(1, patterns::get_patterns(&patterns[..]));
let patterns = patterns::get_patterns(&patterns[..])?;
let options = CsplitOptions::new(&matches);
if file_name == "-" {
let stdin = io::stdin();
crash_if_err!(1, csplit(&options, patterns, stdin.lock()));
Ok(csplit(&options, patterns, stdin.lock())?)
} else {
let file = crash_if_err!(1, File::open(file_name));
let file_metadata = crash_if_err!(1, file.metadata());
let file = File::open(file_name)
.map_err_context(|| format!("cannot access {}", file_name.quote()))?;
let file_metadata = file
.metadata()
.map_err_context(|| format!("cannot access {}", file_name.quote()))?;
if !file_metadata.is_file() {
crash!(1, "{} is not a regular file", file_name.quote());
return Err(CsplitError::NotRegularFile(file_name.to_string()).into());
}
crash_if_err!(1, csplit(&options, patterns, BufReader::new(file)));
};
0
Ok(csplit(&options, patterns, BufReader::new(file))?)
}
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -2,6 +2,7 @@ use std::io;
use thiserror::Error;
use uucore::display::Quotable;
use uucore::error::UError;
/// Errors thrown by the csplit command
#[derive(Debug, Error)]
@ -28,6 +29,8 @@ pub enum CsplitError {
SuffixFormatIncorrect,
#[error("too many % conversion specifications in suffix")]
SuffixFormatTooManyPercents,
#[error("{} is not a regular file", ._0.quote())]
NotRegularFile(String),
}
impl From<io::Error> for CsplitError {
@ -35,3 +38,9 @@ impl From<io::Error> for CsplitError {
CsplitError::IoError(error)
}
}
impl UError for CsplitError {
fn code(&self) -> i32 {
1
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "uu_cut"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "cut ~ (uutils) display byte/field columns of input lines"
@ -16,8 +16,8 @@ path = "src/cut.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
memchr = "2"
bstr = "0.2"
atty = "0.2"

View file

@ -16,6 +16,7 @@ use std::fs::File;
use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write};
use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
use self::searcher::Searcher;
use uucore::ranges::Range;
@ -142,7 +143,7 @@ fn list_to_ranges(list: &str, complement: bool) -> Result<Vec<Range>, String> {
}
}
fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> i32 {
fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> UResult<()> {
let newline_char = if opts.zero_terminated { b'\0' } else { b'\n' };
let buf_in = BufReader::new(reader);
let mut out = stdout_writer();
@ -152,7 +153,7 @@ fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> i32 {
.map_or("", String::as_str)
.as_bytes();
let res = buf_in.for_byte_record(newline_char, |line| {
let result = buf_in.for_byte_record(newline_char, |line| {
let mut print_delim = false;
for &Range { low, high } in ranges {
if low > line.len() {
@ -171,8 +172,12 @@ fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> i32 {
out.write_all(&[newline_char])?;
Ok(true)
});
crash_if_err!(1, res);
0
if let Err(e) = result {
return Err(USimpleError::new(1, e.to_string()));
}
Ok(())
}
#[allow(clippy::cognitive_complexity)]
@ -183,7 +188,7 @@ fn cut_fields_delimiter<R: Read>(
only_delimited: bool,
newline_char: u8,
out_delim: &str,
) -> i32 {
) -> UResult<()> {
let buf_in = BufReader::new(reader);
let mut out = stdout_writer();
let input_delim_len = delim.len();
@ -246,12 +251,16 @@ fn cut_fields_delimiter<R: Read>(
out.write_all(&[newline_char])?;
Ok(true)
});
crash_if_err!(1, result);
0
if let Err(e) = result {
return Err(USimpleError::new(1, e.to_string()));
}
Ok(())
}
#[allow(clippy::cognitive_complexity)]
fn cut_fields<R: Read>(reader: R, ranges: &[Range], opts: &FieldOptions) -> i32 {
fn cut_fields<R: Read>(reader: R, ranges: &[Range], opts: &FieldOptions) -> UResult<()> {
let newline_char = if opts.zero_terminated { b'\0' } else { b'\n' };
if let Some(ref o_delim) = opts.out_delimiter {
return cut_fields_delimiter(
@ -323,13 +332,16 @@ fn cut_fields<R: Read>(reader: R, ranges: &[Range], opts: &FieldOptions) -> i32
out.write_all(&[newline_char])?;
Ok(true)
});
crash_if_err!(1, result);
0
if let Err(e) = result {
return Err(USimpleError::new(1, e.to_string()));
}
Ok(())
}
fn cut_files(mut filenames: Vec<String>, mode: Mode) -> i32 {
fn cut_files(mut filenames: Vec<String>, mode: Mode) -> UResult<()> {
let mut stdin_read = false;
let mut exit_code = 0;
if filenames.is_empty() {
filenames.push("-".to_owned());
@ -341,11 +353,11 @@ fn cut_files(mut filenames: Vec<String>, mode: Mode) -> i32 {
continue;
}
exit_code |= match mode {
show_if_err!(match mode {
Mode::Bytes(ref ranges, ref opts) => cut_bytes(stdin(), ranges, opts),
Mode::Characters(ref ranges, ref opts) => cut_bytes(stdin(), ranges, opts),
Mode::Fields(ref ranges, ref opts) => cut_fields(stdin(), ranges, opts),
};
});
stdin_read = true;
} else {
@ -356,28 +368,19 @@ fn cut_files(mut filenames: Vec<String>, mode: Mode) -> i32 {
continue;
}
if path.metadata().is_err() {
show_error!("{}: No such file or directory", filename.maybe_quote());
continue;
}
let file = match File::open(&path) {
Ok(f) => f,
Err(e) => {
show_error!("opening {}: {}", filename.quote(), e);
continue;
}
};
exit_code |= match mode {
Mode::Bytes(ref ranges, ref opts) => cut_bytes(file, ranges, opts),
Mode::Characters(ref ranges, ref opts) => cut_bytes(file, ranges, opts),
Mode::Fields(ref ranges, ref opts) => cut_fields(file, ranges, opts),
};
show_if_err!(File::open(&path)
.map_err_context(|| filename.maybe_quote().to_string())
.and_then(|file| {
match &mode {
Mode::Bytes(ref ranges, ref opts) => cut_bytes(file, ranges, opts),
Mode::Characters(ref ranges, ref opts) => cut_bytes(file, ranges, opts),
Mode::Fields(ref ranges, ref opts) => cut_fields(file, ranges, opts),
}
}));
}
}
exit_code
Ok(())
}
mod options {
@ -392,7 +395,8 @@ mod options {
pub const FILE: &str = "file";
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args
.collect_str(InvalidEncodingHandling::Ignore)
.accept_any();
@ -462,12 +466,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
delim = "=";
}
if delim.chars().count() > 1 {
Err(msg_opt_invalid_should_be!(
"empty or 1 character long",
"a value 2 characters or longer",
"--delimiter",
"-d"
))
Err("invalid input: The '--delimiter' ('-d') option expects empty or 1 character long, but was provided a value 2 characters or longer".into())
} else {
let delim = if delim.is_empty() {
"\0".to_owned()
@ -499,13 +498,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
})
}
(ref b, ref c, ref f) if b.is_some() || c.is_some() || f.is_some() => Err(
msg_expects_no_more_than_one_of!("--fields (-f)", "--chars (-c)", "--bytes (-b)"),
"invalid usage: expects no more than one of --fields (-f), --chars (-c) or --bytes (-b)".into()
),
_ => Err(msg_expects_one_of!(
"--fields (-f)",
"--chars (-c)",
"--bytes (-b)"
)),
_ => Err("invalid usage: expects one of --fields (-f), --chars (-c) or --bytes (-b)".into()),
};
let mode_parse = match mode_parse {
@ -514,20 +509,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Mode::Bytes(_, _) | Mode::Characters(_, _)
if matches.is_present(options::DELIMITER) =>
{
Err(msg_opt_only_usable_if!(
"printing a sequence of fields",
"--delimiter",
"-d"
))
Err("invalid input: The '--delimiter' ('-d') option only usable if printing a sequence of fields".into())
}
Mode::Bytes(_, _) | Mode::Characters(_, _)
if matches.is_present(options::ONLY_DELIMITED) =>
{
Err(msg_opt_only_usable_if!(
"printing a sequence of fields",
"--only-delimited",
"-s"
))
Err("invalid input: The '--only-delimited' ('-s') option only usable if printing a sequence of fields".into())
}
_ => Ok(mode),
},
@ -541,10 +528,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
match mode_parse {
Ok(mode) => cut_files(files, mode),
Err(err_msg) => {
show_error!("{}", err_msg);
1
}
Err(e) => Err(USimpleError::new(1, e)),
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "uu_date"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "date ~ (uutils) display or set the current time"
@ -15,10 +15,10 @@ edition = "2018"
path = "src/date.rs"
[dependencies]
chrono = "0.4.4"
chrono = "^0.4.11"
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[target.'cfg(unix)'.dependencies]
libc = "0.2"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_dd"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "dd ~ (uutils) copy and convert files"

View file

@ -6,11 +6,13 @@
// file that was distributed with this source code.
// spell-checker:ignore ctable, outfile
use crate::conversion_tables::*;
use std::error::Error;
use std::time;
use uucore::error::UError;
use crate::conversion_tables::*;
pub struct ProgUpdate {
pub read_stat: ReadStat,
pub write_stat: WriteStat,
@ -154,6 +156,7 @@ impl std::fmt::Display for InternalError {
}
impl Error for InternalError {}
impl UError for InternalError {}
pub mod options {
pub const INFILE: &str = "if";

View file

@ -7,8 +7,6 @@
// spell-checker:ignore fname, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, behaviour, bmax, bremain, btotal, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, outfile, parseargs, rlen, rmax, rposition, rremain, rsofar, rstat, sigusr, sigval, wlen, wstat
use uucore::InvalidEncodingHandling;
#[cfg(test)]
mod dd_unit_tests;
@ -21,14 +19,10 @@ use parseargs::Matches;
mod conversion_tables;
use conversion_tables::*;
use byte_unit::Byte;
use clap::{self, crate_version};
use gcd::Gcd;
#[cfg(target_os = "linux")]
use signal_hook::consts::signal;
use std::cmp;
use std::convert::TryInto;
use std::env;
#[cfg(target_os = "linux")]
use std::error::Error;
use std::fs::{File, OpenOptions};
use std::io::{self, Read, Seek, Write};
@ -41,10 +35,17 @@ use std::sync::{atomic::AtomicUsize, atomic::Ordering, Arc};
use std::thread;
use std::time;
use byte_unit::Byte;
use clap::{self, crate_version};
use gcd::Gcd;
#[cfg(target_os = "linux")]
use signal_hook::consts::signal;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
use uucore::InvalidEncodingHandling;
const ABOUT: &str = "copy, and optionally convert, a file system resource";
const BUF_INIT_BYTE: u8 = 0xDD;
const RTN_SUCCESS: i32 = 0;
const RTN_FAILURE: i32 = 1;
const NEWLINE: u8 = b'\n';
const SPACE: u8 = b' ';
@ -59,7 +60,7 @@ struct Input<R: Read> {
}
impl Input<io::Stdin> {
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
fn new(matches: &Matches) -> UResult<Self> {
let ibs = parseargs::parse_ibs(matches)?;
let non_ascii = parseargs::parse_input_non_ascii(matches)?;
let print_level = parseargs::parse_status_level(matches)?;
@ -80,8 +81,8 @@ impl Input<io::Stdin> {
if let Some(amt) = skip {
let mut buf = vec![BUF_INIT_BYTE; amt];
i.force_fill(&mut buf, amt)?;
i.force_fill(&mut buf, amt)
.map_err_context(|| "failed to read input".to_string())?;
}
Ok(i)
@ -125,7 +126,7 @@ fn make_linux_iflags(iflags: &IFlags) -> Option<libc::c_int> {
}
impl Input<File> {
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
fn new(matches: &Matches) -> UResult<Self> {
let ibs = parseargs::parse_ibs(matches)?;
let non_ascii = parseargs::parse_input_non_ascii(matches)?;
let print_level = parseargs::parse_status_level(matches)?;
@ -144,12 +145,16 @@ impl Input<File> {
opts.custom_flags(libc_flags);
}
opts.open(fname)?
opts.open(fname)
.map_err_context(|| "failed to open input file".to_string())?
};
if let Some(amt) = skip {
let amt: u64 = amt.try_into()?;
src.seek(io::SeekFrom::Start(amt))?;
let amt: u64 = amt
.try_into()
.map_err(|_| USimpleError::new(1, "failed to parse seek amount"))?;
src.seek(io::SeekFrom::Start(amt))
.map_err_context(|| "failed to seek in input file".to_string())?;
}
let i = Input {
@ -196,7 +201,7 @@ impl<R: Read> Input<R> {
/// Fills a given buffer.
/// Reads in increments of 'self.ibs'.
/// The start of each ibs-sized read follows the previous one.
fn fill_consecutive(&mut self, buf: &mut Vec<u8>) -> Result<ReadStat, Box<dyn Error>> {
fn fill_consecutive(&mut self, buf: &mut Vec<u8>) -> std::io::Result<ReadStat> {
let mut reads_complete = 0;
let mut reads_partial = 0;
let mut bytes_total = 0;
@ -227,7 +232,7 @@ impl<R: Read> Input<R> {
/// Fills a given buffer.
/// Reads in increments of 'self.ibs'.
/// The start of each ibs-sized read is aligned to multiples of ibs; remaining space is filled with the 'pad' byte.
fn fill_blocks(&mut self, buf: &mut Vec<u8>, pad: u8) -> Result<ReadStat, Box<dyn Error>> {
fn fill_blocks(&mut self, buf: &mut Vec<u8>, pad: u8) -> std::io::Result<ReadStat> {
let mut reads_complete = 0;
let mut reads_partial = 0;
let mut base_idx = 0;
@ -263,7 +268,7 @@ impl<R: Read> Input<R> {
/// interpreted as EOF.
/// Note: This will not return unless the source (eventually) produces
/// enough bytes to meet target_len.
fn force_fill(&mut self, buf: &mut [u8], target_len: usize) -> Result<usize, Box<dyn Error>> {
fn force_fill(&mut self, buf: &mut [u8], target_len: usize) -> std::io::Result<usize> {
let mut base_idx = 0;
while base_idx < target_len {
base_idx += self.read(&mut buf[base_idx..target_len])?;
@ -274,7 +279,7 @@ impl<R: Read> Input<R> {
}
trait OutputTrait: Sized + Write {
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>>;
fn new(matches: &Matches) -> UResult<Self>;
fn fsync(&mut self) -> io::Result<()>;
fn fdatasync(&mut self) -> io::Result<()>;
}
@ -286,7 +291,7 @@ struct Output<W: Write> {
}
impl OutputTrait for Output<io::Stdout> {
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
fn new(matches: &Matches) -> UResult<Self> {
let obs = parseargs::parse_obs(matches)?;
let cflags = parseargs::parse_conv_flag_output(matches)?;
@ -333,7 +338,7 @@ where
})
}
fn dd_out<R: Read>(mut self, mut i: Input<R>) -> Result<(), Box<dyn Error>> {
fn dd_out<R: Read>(mut self, mut i: Input<R>) -> UResult<()> {
let mut rstat = ReadStat {
reads_complete: 0,
reads_partial: 0,
@ -366,24 +371,30 @@ where
_,
) => break,
(rstat_update, buf) => {
let wstat_update = self.write_blocks(buf)?;
let wstat_update = self
.write_blocks(buf)
.map_err_context(|| "failed to write output".to_string())?;
rstat += rstat_update;
wstat += wstat_update;
}
};
// Update Prog
prog_tx.send(ProgUpdate {
read_stat: rstat,
write_stat: wstat,
duration: start.elapsed(),
})?;
prog_tx
.send(ProgUpdate {
read_stat: rstat,
write_stat: wstat,
duration: start.elapsed(),
})
.map_err(|_| USimpleError::new(1, "failed to write output"))?;
}
if self.cflags.fsync {
self.fsync()?;
self.fsync()
.map_err_context(|| "failed to write output".to_string())?;
} else if self.cflags.fdatasync {
self.fdatasync()?;
self.fdatasync()
.map_err_context(|| "failed to write output".to_string())?;
}
match i.print_level {
@ -439,7 +450,7 @@ fn make_linux_oflags(oflags: &OFlags) -> Option<libc::c_int> {
}
impl OutputTrait for Output<File> {
fn new(matches: &Matches) -> Result<Self, Box<dyn Error>> {
fn new(matches: &Matches) -> UResult<Self> {
fn open_dst(path: &Path, cflags: &OConvFlags, oflags: &OFlags) -> Result<File, io::Error> {
let mut opts = OpenOptions::new();
opts.write(true)
@ -461,11 +472,15 @@ impl OutputTrait for Output<File> {
let seek = parseargs::parse_seek_amt(&obs, &oflags, matches)?;
if let Some(fname) = matches.value_of(options::OUTFILE) {
let mut dst = open_dst(Path::new(&fname), &cflags, &oflags)?;
let mut dst = open_dst(Path::new(&fname), &cflags, &oflags)
.map_err_context(|| format!("failed to open {}", fname.quote()))?;
if let Some(amt) = seek {
let amt: u64 = amt.try_into()?;
dst.seek(io::SeekFrom::Start(amt))?;
let amt: u64 = amt
.try_into()
.map_err(|_| USimpleError::new(1, "failed to parse seek amount"))?;
dst.seek(io::SeekFrom::Start(amt))
.map_err_context(|| "failed to seek in output file".to_string())?;
}
Ok(Output { dst, obs, cflags })
@ -580,7 +595,7 @@ fn conv_block_unblock_helper<R: Read>(
mut buf: Vec<u8>,
i: &mut Input<R>,
rstat: &mut ReadStat,
) -> Result<Vec<u8>, Box<dyn Error>> {
) -> Result<Vec<u8>, InternalError> {
// Local Predicate Fns -------------------------------------------------
fn should_block_then_conv<R: Read>(i: &Input<R>) -> bool {
!i.non_ascii && i.cflags.block.is_some()
@ -664,15 +679,12 @@ fn conv_block_unblock_helper<R: Read>(
// by the parser before making it this far.
// Producing this error is an alternative to risking an unwrap call
// on 'cbs' if the required data is not provided.
Err(Box::new(InternalError::InvalidConvBlockUnblockCase))
Err(InternalError::InvalidConvBlockUnblockCase)
}
}
/// Read helper performs read operations common to all dd reads, and dispatches the buffer to relevant helper functions as dictated by the operations requested by the user.
fn read_helper<R: Read>(
i: &mut Input<R>,
bsize: usize,
) -> Result<(ReadStat, Vec<u8>), Box<dyn Error>> {
fn read_helper<R: Read>(i: &mut Input<R>, bsize: usize) -> UResult<(ReadStat, Vec<u8>)> {
// Local Predicate Fns -----------------------------------------------
fn is_conv<R: Read>(i: &Input<R>) -> bool {
i.cflags.ctable.is_some()
@ -693,8 +705,12 @@ fn read_helper<R: Read>(
// Read
let mut buf = vec![BUF_INIT_BYTE; bsize];
let mut rstat = match i.cflags.sync {
Some(ch) => i.fill_blocks(&mut buf, ch)?,
_ => i.fill_consecutive(&mut buf)?,
Some(ch) => i
.fill_blocks(&mut buf, ch)
.map_err_context(|| "failed to write output".to_string())?,
_ => i
.fill_consecutive(&mut buf)
.map_err_context(|| "failed to write output".to_string())?,
};
// Return early if no data
if rstat.reads_complete == 0 && rstat.reads_partial == 0 {
@ -877,28 +893,8 @@ fn append_dashes_if_not_present(mut acc: Vec<String>, mut s: String) -> Vec<Stri
acc
}
macro_rules! unpack_or_rtn (
($i:expr, $o:expr) =>
{{
match ($i, $o)
{
(Ok(i), Ok(o)) =>
(i,o),
(Err(e), _) =>
{
eprintln!("dd Error: {}", e);
return RTN_FAILURE;
},
(_, Err(e)) =>
{
eprintln!("dd Error: {}", e);
return RTN_FAILURE;
},
}
}};
);
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let dashed_args = args
.collect_str(InvalidEncodingHandling::Ignore)
.accept_any()
@ -909,47 +905,30 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
//.after_help(TODO: Add note about multiplier strings here.)
.get_matches_from(dashed_args);
let result = match (
match (
matches.is_present(options::INFILE),
matches.is_present(options::OUTFILE),
) {
(true, true) => {
let (i, o) =
unpack_or_rtn!(Input::<File>::new(&matches), Output::<File>::new(&matches));
let i = Input::<File>::new(&matches)?;
let o = Output::<File>::new(&matches)?;
o.dd_out(i)
}
(false, true) => {
let (i, o) = unpack_or_rtn!(
Input::<io::Stdin>::new(&matches),
Output::<File>::new(&matches)
);
let i = Input::<io::Stdin>::new(&matches)?;
let o = Output::<File>::new(&matches)?;
o.dd_out(i)
}
(true, false) => {
let (i, o) = unpack_or_rtn!(
Input::<File>::new(&matches),
Output::<io::Stdout>::new(&matches)
);
let i = Input::<File>::new(&matches)?;
let o = Output::<io::Stdout>::new(&matches)?;
o.dd_out(i)
}
(false, false) => {
let (i, o) = unpack_or_rtn!(
Input::<io::Stdin>::new(&matches),
Output::<io::Stdout>::new(&matches)
);
let i = Input::<io::Stdin>::new(&matches)?;
let o = Output::<io::Stdout>::new(&matches)?;
o.dd_out(i)
}
};
match result {
Ok(_) => RTN_SUCCESS,
Err(e) => {
eprintln!("dd exiting with error:\n\t{}", e);
RTN_FAILURE
}
}
}

View file

@ -11,6 +11,7 @@ mod unit_tests;
use super::*;
use std::error::Error;
use uucore::error::UError;
pub type Matches = clap::ArgMatches<'static>;
@ -79,6 +80,12 @@ impl std::fmt::Display for ParseError {
impl Error for ParseError {}
impl UError for ParseError {
fn code(&self) -> i32 {
1
}
}
/// Some flags specified as part of a conv=CONV[,CONV]... block
/// relate to the input file, others to the output file.
#[derive(Debug, PartialEq)]

View file

@ -1,6 +1,6 @@
[package]
name = "uu_df"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "df ~ (uutils) display file system information"
@ -17,8 +17,8 @@ path = "src/df.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
number_prefix = "0.4"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["libc", "fsext"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["libc", "fsext"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "df"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_dircolors"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "dircolors ~ (uutils) display commands to set LS_COLORS"
@ -17,8 +17,8 @@ path = "src/dircolors.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
glob = "0.3.0"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "dircolors"

View file

@ -8,9 +8,6 @@
// spell-checker:ignore (ToDO) clrtoeol dircolors eightbit endcode fnmatch leftcode multihardlink rightcode setenv sgid suid
#[macro_use]
extern crate uucore;
use std::borrow::Borrow;
use std::env;
use std::fs::File;
@ -18,6 +15,7 @@ use std::io::{BufRead, BufReader};
use clap::{crate_version, App, Arg};
use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError, UUsageError};
mod options {
pub const BOURNE_SHELL: &str = "bourne-shell";
@ -67,7 +65,8 @@ fn usage() -> String {
format!("{0} {1}", uucore::execution_phrase(), SYNTAX)
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args
.collect_str(InvalidEncodingHandling::Ignore)
.accept_any();
@ -85,24 +84,26 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if (matches.is_present(options::C_SHELL) || matches.is_present(options::BOURNE_SHELL))
&& matches.is_present(options::PRINT_DATABASE)
{
show_usage_error!(
return Err(UUsageError::new(
1,
"the options to output dircolors' internal database and\nto select a shell \
syntax are mutually exclusive"
);
return 1;
syntax are mutually exclusive",
));
}
if matches.is_present(options::PRINT_DATABASE) {
if !files.is_empty() {
show_usage_error!(
"extra operand {}\nfile operands cannot be combined with \
--print-database (-p)",
files[0].quote()
);
return 1;
return Err(UUsageError::new(
1,
format!(
"extra operand {}\nfile operands cannot be combined with \
--print-database (-p)",
files[0].quote()
),
));
}
println!("{}", INTERNAL_DB);
return 0;
return Ok(());
}
let mut out_format = OutputFmt::Unknown;
@ -115,8 +116,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if out_format == OutputFmt::Unknown {
match guess_syntax() {
OutputFmt::Unknown => {
show_error!("no SHELL environment variable, and no shell type option given");
return 1;
return Err(USimpleError::new(
1,
"no SHELL environment variable, and no shell type option given",
));
}
fmt => out_format = fmt,
}
@ -127,8 +130,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
result = parse(INTERNAL_DB.lines(), out_format, "")
} else {
if files.len() > 1 {
show_usage_error!("extra operand {}", files[1].quote());
return 1;
return Err(UUsageError::new(
1,
format!("extra operand {}", files[1].quote()),
));
}
match File::open(files[0]) {
Ok(f) => {
@ -136,19 +141,21 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
result = parse(fin.lines().filter_map(Result::ok), out_format, files[0])
}
Err(e) => {
show_error!("{}: {}", files[0].maybe_quote(), e);
return 1;
return Err(USimpleError::new(
1,
format!("{}: {}", files[0].maybe_quote(), e),
));
}
}
}
match result {
Ok(s) => {
println!("{}", s);
0
Ok(())
}
Err(s) => {
show_error!("{}", s);
1
return Err(USimpleError::new(1, s));
}
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "uu_dirname"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "dirname ~ (uutils) display parent directory of PATHNAME"
@ -17,8 +17,8 @@ path = "src/dirname.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "dirname"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_du"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "du ~ (uutils) display disk usage"
@ -16,9 +16,9 @@ path = "src/du.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
chrono = "0.4"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
chrono = "^0.4.11"
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version="0.3", features=[] }

View file

@ -18,7 +18,7 @@ use std::env;
use std::fs;
#[cfg(not(windows))]
use std::fs::Metadata;
use std::io::{stderr, ErrorKind, Result, Write};
use std::io::{ErrorKind, Result};
use std::iter;
#[cfg(not(windows))]
use std::os::unix::fs::MetadataExt;
@ -292,8 +292,7 @@ fn du(
let read = match fs::read_dir(&my_stat.path) {
Ok(read) => read,
Err(e) => {
safe_writeln!(
stderr(),
eprintln!(
"{}: cannot read directory {}: {}",
options.util_name,
my_stat.path.quote(),

View file

@ -1,6 +1,6 @@
[package]
name = "uu_echo"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "echo ~ (uutils) display TEXT"
@ -16,8 +16,8 @@ path = "src/echo.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "echo"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_env"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "env ~ (uutils) set each NAME to VALUE in the environment and run COMMAND"
@ -18,8 +18,8 @@ path = "src/env.rs"
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
rust-ini = "0.17.0"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "env"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_expand"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "expand ~ (uutils) convert input tabs to spaces"
@ -17,8 +17,8 @@ path = "src/expand.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
unicode-width = "0.1.5"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "expand"

View file

@ -18,6 +18,7 @@ use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write};
use std::str::from_utf8;
use unicode_width::UnicodeWidthChar;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};
static ABOUT: &str = "Convert tabs in each FILE to spaces, writing to standard output.
With no FILE, or when FILE is -, read standard input.";
@ -170,12 +171,12 @@ impl Options {
}
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
expand(Options::new(&matches));
0
expand(Options::new(&matches)).map_err_context(|| "failed to write output".to_string())
}
pub fn uu_app() -> App<'static, 'static> {
@ -269,7 +270,7 @@ enum CharType {
Other,
}
fn expand(options: Options) {
fn expand(options: Options) -> std::io::Result<()> {
use self::CharType::*;
let mut output = BufWriter::new(stdout());
@ -330,15 +331,12 @@ fn expand(options: Options) {
// now dump out either spaces if we're expanding, or a literal tab if we're not
if init || !options.iflag {
if nts <= options.tspaces.len() {
crash_if_err!(
1,
output.write_all(options.tspaces[..nts].as_bytes())
);
output.write_all(options.tspaces[..nts].as_bytes())?
} else {
crash_if_err!(1, output.write_all(" ".repeat(nts).as_bytes()));
output.write_all(" ".repeat(nts).as_bytes())?;
};
} else {
crash_if_err!(1, output.write_all(&buf[byte..byte + nbytes]));
output.write_all(&buf[byte..byte + nbytes])?;
}
}
_ => {
@ -356,17 +354,18 @@ fn expand(options: Options) {
init = false;
}
crash_if_err!(1, output.write_all(&buf[byte..byte + nbytes]));
output.write_all(&buf[byte..byte + nbytes])?;
}
}
byte += nbytes; // advance the pointer
}
crash_if_err!(1, output.flush());
output.flush()?;
buf.truncate(0); // clear the buffer
}
}
Ok(())
}
#[cfg(test)]

View file

@ -1,6 +1,6 @@
[package]
name = "uu_expr"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "expr ~ (uutils) display the value of EXPRESSION"
@ -20,8 +20,8 @@ libc = "0.2.42"
num-bigint = "0.4.0"
num-traits = "0.2.14"
onig = "~4.3.2"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "expr"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_factor"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "factor ~ (uutils) display the prime factors of each NUMBER"
@ -21,7 +21,7 @@ num-traits = "0.2.13" # Needs at least version 0.2.13 for "OverflowingAdd"
rand = { version = "0.7", features = ["small_rng"] }
smallvec = "1.7" # TODO(nicoo): Use `union` feature, requires Rust 1.49 or later.
uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" }
uucore_procs = { version=">=0.0.7", package = "uucore_procs", path = "../../uucore_procs" }
uucore_procs = { version=">=0.0.8", package = "uucore_procs", path = "../../uucore_procs" }
[dev-dependencies]
paste = "0.1.18"

View file

@ -17,6 +17,7 @@ mod factor;
use clap::{crate_version, App, Arg};
pub use factor::*;
use uucore::display::Quotable;
use uucore::error::UResult;
mod miller_rabin;
pub mod numeric;
@ -43,7 +44,8 @@ fn print_factors_str(
})
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().get_matches_from(args);
let stdout = stdout();
// We use a smaller buffer here to pass a gnu test. 4KiB appears to be the default pipe size for bash.
@ -72,7 +74,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
show_error!("{}", e);
}
0
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -1,6 +1,6 @@
[package]
name = "uu_false"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "false ~ (uutils) do nothing and fail"
@ -16,8 +16,8 @@ path = "src/false.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "false"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_fmt"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "fmt ~ (uutils) reformat each paragraph of input"
@ -18,8 +18,8 @@ path = "src/fmt.rs"
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
unicode-width = "0.1.5"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "fmt"

View file

@ -16,19 +16,11 @@ use std::fs::File;
use std::io::{stdin, stdout, Write};
use std::io::{BufReader, BufWriter, Read};
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
use self::linebreak::break_lines;
use self::parasplit::ParagraphStream;
macro_rules! silent_unwrap(
($exp:expr) => (
match $exp {
Ok(_) => (),
Err(_) => ::std::process::exit(1),
}
)
);
mod linebreak;
mod parasplit;
@ -74,8 +66,9 @@ pub struct FmtOptions {
tabwidth: usize,
}
#[uucore_procs::gen_uumain]
#[allow(clippy::cognitive_complexity)]
pub fn uumain(args: impl uucore::Args) -> i32 {
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
@ -133,15 +126,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
fmt_opts.width = match s.parse::<usize>() {
Ok(t) => t,
Err(e) => {
crash!(1, "Invalid WIDTH specification: {}: {}", s.quote(), e);
return Err(USimpleError::new(
1,
format!("Invalid WIDTH specification: {}: {}", s.quote(), e),
));
}
};
if fmt_opts.width > MAX_WIDTH {
crash!(
return Err(USimpleError::new(
1,
"invalid width: '{}': Numerical result out of range",
fmt_opts.width
);
format!(
"invalid width: '{}': Numerical result out of range",
fmt_opts.width,
),
));
}
fmt_opts.goal = cmp::min(fmt_opts.width * 94 / 100, fmt_opts.width - 3);
};
@ -150,13 +148,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
fmt_opts.goal = match s.parse::<usize>() {
Ok(t) => t,
Err(e) => {
crash!(1, "Invalid GOAL specification: {}: {}", s.quote(), e);
return Err(USimpleError::new(
1,
format!("Invalid GOAL specification: {}: {}", s.quote(), e),
));
}
};
if !matches.is_present(OPT_WIDTH) {
fmt_opts.width = cmp::max(fmt_opts.goal * 100 / 94, fmt_opts.goal + 3);
} else if fmt_opts.goal > fmt_opts.width {
crash!(1, "GOAL cannot be greater than WIDTH.");
return Err(USimpleError::new(1, "GOAL cannot be greater than WIDTH."));
}
};
@ -164,7 +165,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
fmt_opts.tabwidth = match s.parse::<usize>() {
Ok(t) => t,
Err(e) => {
crash!(1, "Invalid TABWIDTH specification: {}: {}", s.quote(), e);
return Err(USimpleError::new(
1,
format!("Invalid TABWIDTH specification: {}: {}", s.quote(), e),
));
}
};
};
@ -197,18 +201,25 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
for para_result in p_stream {
match para_result {
Err(s) => {
silent_unwrap!(ostream.write_all(s.as_bytes()));
silent_unwrap!(ostream.write_all(b"\n"));
ostream
.write_all(s.as_bytes())
.map_err_context(|| "failed to write output".to_string())?;
ostream
.write_all(b"\n")
.map_err_context(|| "failed to write output".to_string())?;
}
Ok(para) => break_lines(&para, &fmt_opts, &mut ostream),
Ok(para) => break_lines(&para, &fmt_opts, &mut ostream)
.map_err_context(|| "failed to write output".to_string())?,
}
}
// flush the output after each file
silent_unwrap!(ostream.flush());
ostream
.flush()
.map_err_context(|| "failed to write output".to_string())?;
}
0
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -40,7 +40,11 @@ impl<'a> BreakArgs<'a> {
}
}
pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<Stdout>) {
pub fn break_lines(
para: &Paragraph,
opts: &FmtOptions,
ostream: &mut BufWriter<Stdout>,
) -> std::io::Result<()> {
// indent
let p_indent = &para.indent_str[..];
let p_indent_len = para.indent_len;
@ -54,26 +58,25 @@ pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<
let (w, w_len) = match p_words_words.next() {
Some(winfo) => (winfo.word, winfo.word_nchars),
None => {
silent_unwrap!(ostream.write_all(b"\n"));
return;
return ostream.write_all(b"\n");
}
};
// print the init, if it exists, and get its length
let p_init_len = w_len
+ if opts.crown || opts.tagged {
// handle "init" portion
silent_unwrap!(ostream.write_all(para.init_str.as_bytes()));
ostream.write_all(para.init_str.as_bytes())?;
para.init_len
} else if !para.mail_header {
// for non-(crown, tagged) that's the same as a normal indent
silent_unwrap!(ostream.write_all(p_indent.as_bytes()));
ostream.write_all(p_indent.as_bytes())?;
p_indent_len
} else {
// except that mail headers get no indent at all
0
};
// write first word after writing init
silent_unwrap!(ostream.write_all(w.as_bytes()));
ostream.write_all(w.as_bytes())?;
// does this paragraph require uniform spacing?
let uniform = para.mail_header || opts.uniform;
@ -88,26 +91,29 @@ pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<
};
if opts.quick || para.mail_header {
break_simple(p_words_words, &mut break_args);
break_simple(p_words_words, &mut break_args)
} else {
break_knuth_plass(p_words_words, &mut break_args);
break_knuth_plass(p_words_words, &mut break_args)
}
}
// break_simple implements a "greedy" breaking algorithm: print words until
// maxlength would be exceeded, then print a linebreak and indent and continue.
fn break_simple<'a, T: Iterator<Item = &'a WordInfo<'a>>>(iter: T, args: &mut BreakArgs<'a>) {
iter.fold((args.init_len, false), |l, winfo| {
fn break_simple<'a, T: Iterator<Item = &'a WordInfo<'a>>>(
mut iter: T,
args: &mut BreakArgs<'a>,
) -> std::io::Result<()> {
iter.try_fold((args.init_len, false), |l, winfo| {
accum_words_simple(args, l, winfo)
});
silent_unwrap!(args.ostream.write_all(b"\n"));
})?;
args.ostream.write_all(b"\n")
}
fn accum_words_simple<'a>(
args: &mut BreakArgs<'a>,
(l, prev_punct): (usize, bool),
winfo: &'a WordInfo<'a>,
) -> (usize, bool) {
) -> std::io::Result<(usize, bool)> {
// compute the length of this word, considering how tabs will expand at this position on the line
let wlen = winfo.word_nchars + args.compute_width(winfo, l, false);
@ -119,12 +125,12 @@ fn accum_words_simple<'a>(
);
if l + wlen + slen > args.opts.width {
write_newline(args.indent_str, args.ostream);
write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream);
(args.indent_len + winfo.word_nchars, winfo.ends_punct)
write_newline(args.indent_str, args.ostream)?;
write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream)?;
Ok((args.indent_len + winfo.word_nchars, winfo.ends_punct))
} else {
write_with_spaces(winfo.word, slen, args.ostream);
(l + wlen + slen, winfo.ends_punct)
write_with_spaces(winfo.word, slen, args.ostream)?;
Ok((l + wlen + slen, winfo.ends_punct))
}
}
@ -135,16 +141,16 @@ fn accum_words_simple<'a>(
fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>(
mut iter: T,
args: &mut BreakArgs<'a>,
) {
) -> std::io::Result<()> {
// run the algorithm to get the breakpoints
let breakpoints = find_kp_breakpoints(iter.clone(), args);
// iterate through the breakpoints (note that breakpoints is in reverse break order, so we .rev() it
let (mut prev_punct, mut fresh) = breakpoints.iter().rev().fold(
let result: std::io::Result<(bool, bool)> = breakpoints.iter().rev().try_fold(
(false, false),
|(mut prev_punct, mut fresh), &(next_break, break_before)| {
if fresh {
write_newline(args.indent_str, args.ostream);
write_newline(args.indent_str, args.ostream)?;
}
// at each breakpoint, keep emitting words until we find the word matching this breakpoint
for winfo in &mut iter {
@ -167,26 +173,27 @@ fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>(
if winfo_ptr == next_break_ptr {
// OK, we found the matching word
if break_before {
write_newline(args.indent_str, args.ostream);
write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream);
write_newline(args.indent_str, args.ostream)?;
write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream)?;
} else {
// breaking after this word, so that means "fresh" is true for the next iteration
write_with_spaces(word, slen, args.ostream);
write_with_spaces(word, slen, args.ostream)?;
fresh = true;
}
break;
} else {
write_with_spaces(word, slen, args.ostream);
write_with_spaces(word, slen, args.ostream)?;
}
}
(prev_punct, fresh)
Ok((prev_punct, fresh))
},
);
let (mut prev_punct, mut fresh) = result?;
// after the last linebreak, write out the rest of the final line.
for winfo in iter {
if fresh {
write_newline(args.indent_str, args.ostream);
write_newline(args.indent_str, args.ostream)?;
}
let (slen, word) = slice_if_fresh(
fresh,
@ -199,9 +206,9 @@ fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>(
);
prev_punct = winfo.ends_punct;
fresh = false;
write_with_spaces(word, slen, args.ostream);
write_with_spaces(word, slen, args.ostream)?;
}
silent_unwrap!(args.ostream.write_all(b"\n"));
args.ostream.write_all(b"\n")
}
struct LineBreak<'a> {
@ -494,17 +501,21 @@ fn slice_if_fresh(
}
// Write a newline and add the indent.
fn write_newline(indent: &str, ostream: &mut BufWriter<Stdout>) {
silent_unwrap!(ostream.write_all(b"\n"));
silent_unwrap!(ostream.write_all(indent.as_bytes()));
fn write_newline(indent: &str, ostream: &mut BufWriter<Stdout>) -> std::io::Result<()> {
ostream.write_all(b"\n")?;
ostream.write_all(indent.as_bytes())
}
// Write the word, along with slen spaces.
fn write_with_spaces(word: &str, slen: usize, ostream: &mut BufWriter<Stdout>) {
fn write_with_spaces(
word: &str,
slen: usize,
ostream: &mut BufWriter<Stdout>,
) -> std::io::Result<()> {
if slen == 2 {
silent_unwrap!(ostream.write_all(b" "));
ostream.write_all(b" ")?;
} else if slen == 1 {
silent_unwrap!(ostream.write_all(b" "));
ostream.write_all(b" ")?;
}
silent_unwrap!(ostream.write_all(word.as_bytes()));
ostream.write_all(word.as_bytes())
}

View file

@ -1,6 +1,6 @@
[package]
name = "uu_fold"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "fold ~ (uutils) wrap each line of input"
@ -16,8 +16,8 @@ path = "src/fold.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "fold"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_groups"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "groups ~ (uutils) display group memberships for USERNAME"
@ -15,8 +15,8 @@ edition = "2018"
path = "src/groups.rs"
[dependencies]
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "process"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["entries", "process"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
clap = { version = "2.33", features = ["wrap_help"] }
[[bin]]

View file

@ -17,9 +17,12 @@
#[macro_use]
extern crate uucore;
use std::error::Error;
use std::fmt::Display;
use uucore::{
display::Quotable,
entries::{get_groups_gnu, gid2grp, Locate, Passwd},
error::{UError, UResult},
};
use clap::{crate_version, App, Arg};
@ -35,7 +38,39 @@ fn usage() -> String {
format!("{0} [OPTION]... [USERNAME]...", uucore::execution_phrase())
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[derive(Debug)]
enum GroupsError {
GetGroupsFailed,
GroupNotFound(u32),
UserNotFound(String),
}
impl Error for GroupsError {}
impl UError for GroupsError {}
impl Display for GroupsError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
GroupsError::GetGroupsFailed => write!(f, "failed to fetch groups"),
GroupsError::GroupNotFound(gid) => write!(f, "cannot find name for group ID {}", gid),
GroupsError::UserNotFound(user) => write!(f, "{}: no such user", user.quote()),
}
}
}
fn infallible_gid2grp(gid: &u32) -> String {
match gid2grp(*gid) {
Ok(grp) => grp,
Err(_) => {
// The `show!()` macro sets the global exit code for the program.
show!(GroupsError::GroupNotFound(*gid));
gid.to_string()
}
}
}
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
@ -45,46 +80,29 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.map(|v| v.map(ToString::to_string).collect())
.unwrap_or_default();
let mut exit_code = 0;
if users.is_empty() {
println!(
"{}",
get_groups_gnu(None)
.unwrap()
.iter()
.map(|&gid| gid2grp(gid).unwrap_or_else(|_| {
show_error!("cannot find name for group ID {}", gid);
exit_code = 1;
gid.to_string()
}))
.collect::<Vec<_>>()
.join(" ")
);
return exit_code;
let gids = match get_groups_gnu(None) {
Ok(v) => v,
Err(_) => return Err(GroupsError::GetGroupsFailed.into()),
};
let groups: Vec<String> = gids.iter().map(infallible_gid2grp).collect();
println!("{}", groups.join(" "));
return Ok(());
}
for user in users {
if let Ok(p) = Passwd::locate(user.as_str()) {
println!(
"{} : {}",
user,
p.belongs_to()
.iter()
.map(|&gid| gid2grp(gid).unwrap_or_else(|_| {
show_error!("cannot find name for group ID {}", gid);
exit_code = 1;
gid.to_string()
}))
.collect::<Vec<_>>()
.join(" ")
);
} else {
show_error!("{}: no such user", user.quote());
exit_code = 1;
match Passwd::locate(user.as_str()) {
Ok(p) => {
let groups: Vec<String> = p.belongs_to().iter().map(infallible_gid2grp).collect();
println!("{} : {}", user, groups.join(" "));
}
Err(_) => {
// The `show!()` macro sets the global exit code for the program.
show!(GroupsError::UserNotFound(user));
}
}
}
exit_code
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -1,6 +1,6 @@
[package]
name = "uu_hashsum"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "hashsum ~ (uutils) display or check input digests"
@ -27,8 +27,8 @@ sha1 = "0.6.0"
sha2 = "0.6.0"
sha3 = "0.6.0"
blake2b_simd = "0.5.11"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "hashsum"

View file

@ -28,6 +28,7 @@ use sha1::Sha1;
use sha2::{Sha224, Sha256, Sha384, Sha512};
use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
use std::cmp::Ordering;
use std::error::Error;
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io::{self, stdin, BufRead, BufReader, Read};
@ -35,6 +36,7 @@ use std::iter;
use std::num::ParseIntError;
use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{FromIo, UError, UResult};
const NAME: &str = "hashsum";
@ -274,7 +276,8 @@ fn is_valid_bit_num(arg: String) -> Result<(), String> {
.map_err(|e| format!("{}", e))
}
pub fn uumain(mut args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
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)
@ -324,14 +327,9 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
warn,
};
let res = match matches.values_of_os("FILE") {
match matches.values_of_os("FILE") {
Some(files) => hashsum(opts, files),
None => hashsum(opts, iter::once(OsStr::new("-"))),
};
match res {
Ok(()) => 0,
Err(e) => e,
}
}
@ -453,8 +451,26 @@ fn uu_app(binary_name: &str) -> App<'static, 'static> {
}
}
#[derive(Debug)]
enum HashsumError {
InvalidRegex,
InvalidFormat,
}
impl Error for HashsumError {}
impl UError for HashsumError {}
impl std::fmt::Display for HashsumError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
HashsumError::InvalidRegex => write!(f, "invalid regular expression"),
HashsumError::InvalidFormat => Ok(()),
}
}
}
#[allow(clippy::cognitive_complexity)]
fn hashsum<'a, I>(mut options: Options, files: I) -> Result<(), i32>
fn hashsum<'a, I>(mut options: Options, files: I) -> UResult<()>
where
I: Iterator<Item = &'a OsStr>,
{
@ -470,7 +486,8 @@ where
stdin_buf = stdin();
Box::new(stdin_buf) as Box<dyn Read>
} else {
file_buf = crash_if_err!(1, File::open(filename));
file_buf =
File::open(filename).map_err_context(|| "failed to open file".to_string())?;
Box::new(file_buf) as Box<dyn Read>
});
if options.check {
@ -487,25 +504,24 @@ where
} else {
"+".to_string()
};
let gnu_re = crash_if_err!(
1,
Regex::new(&format!(
r"^(?P<digest>[a-fA-F0-9]{}) (?P<binary>[ \*])(?P<fileName>.*)",
modifier,
))
);
let bsd_re = crash_if_err!(
1,
Regex::new(&format!(
r"^{algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{digest_size})",
algorithm = options.algoname,
digest_size = modifier,
))
);
let gnu_re = Regex::new(&format!(
r"^(?P<digest>[a-fA-F0-9]{}) (?P<binary>[ \*])(?P<fileName>.*)",
modifier,
))
.map_err(|_| HashsumError::InvalidRegex)?;
let bsd_re = Regex::new(&format!(
r"^{algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{digest_size})",
algorithm = options.algoname,
digest_size = modifier,
))
.map_err(|_| HashsumError::InvalidRegex)?;
let buffer = file;
for (i, line) in buffer.lines().enumerate() {
let line = crash_if_err!(1, line);
for (i, maybe_line) in buffer.lines().enumerate() {
let line = match maybe_line {
Ok(l) => l,
Err(e) => return Err(e.map_err_context(|| "failed to read file".to_string())),
};
let (ck_filename, sum, binary_check) = match gnu_re.captures(&line) {
Some(caps) => (
caps.name("fileName").unwrap().as_str(),
@ -521,7 +537,7 @@ where
None => {
bad_format += 1;
if options.strict {
return Err(1);
return Err(HashsumError::InvalidFormat.into());
}
if options.warn {
show_warning!(
@ -535,17 +551,16 @@ where
}
},
};
let f = crash_if_err!(1, File::open(ck_filename));
let f = File::open(ck_filename)
.map_err_context(|| "failed to open file".to_string())?;
let mut ckf = BufReader::new(Box::new(f) as Box<dyn Read>);
let real_sum = crash_if_err!(
1,
digest_reader(
&mut options.digest,
&mut ckf,
binary_check,
options.output_bits
)
let real_sum = digest_reader(
&mut options.digest,
&mut ckf,
binary_check,
options.output_bits,
)
.map_err_context(|| "failed to read input".to_string())?
.to_ascii_lowercase();
// FIXME: Filenames with newlines should be treated specially.
// GNU appears to replace newlines by \n and backslashes by
@ -568,15 +583,13 @@ where
}
}
} else {
let sum = crash_if_err!(
1,
digest_reader(
&mut options.digest,
&mut file,
options.binary,
options.output_bits
)
);
let sum = digest_reader(
&mut options.digest,
&mut file,
options.binary,
options.output_bits,
)
.map_err_context(|| "failed to read input".to_string())?;
if options.tag {
println!("{} ({}) = {}", options.algoname, filename.display(), sum);
} else {

View file

@ -20,7 +20,7 @@ and most other parts of the world.
This particular file has about 170,000 lines, each of which is no longer
than 96 characters:
$ wc -lL shakespeare.txt
$ wc -lL shakespeare.txt
170592 96 shakespeare.txt
You could use files of different shapes and sizes to test the

View file

@ -1,6 +1,6 @@
[package]
name = "uu_head"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "head ~ (uutils) display the first lines of input"
@ -17,8 +17,8 @@ path = "src/head.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
memchr = "2"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["ringbuffer"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["ringbuffer"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "head"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_hostid"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "hostid ~ (uutils) display the numeric identifier of the current host"
@ -17,8 +17,8 @@ path = "src/hostid.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "hostid"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_hostname"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "hostname ~ (uutils) display or set the host name of the current host"
@ -18,8 +18,8 @@ path = "src/hostname.rs"
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
hostname = { version = "0.3", features = ["set"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["wide"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["wide"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
winapi = { version="0.3", features=["sysinfoapi", "winsock2"] }
[[bin]]

View file

@ -1,6 +1,6 @@
[package]
name = "uu_id"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "id ~ (uutils) display user and group information for USER"
@ -16,8 +16,8 @@ path = "src/id.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "process"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["entries", "process"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
selinux = { version="0.2.1", optional = true }
[[bin]]

View file

@ -245,7 +245,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// GNU's `id` does not support the flags: -p/-P/-A.
if matches.is_present(options::OPT_PASSWORD) {
// BSD's `id` ignores all but the first specified user
pline(possible_pw.map(|v| v.uid()));
pline(possible_pw.as_ref().map(|v| v.uid));
return Ok(());
};
if matches.is_present(options::OPT_HUMAN_READABLE) {
@ -259,7 +259,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
return Ok(());
}
let (uid, gid) = possible_pw.map(|p| (p.uid(), p.gid())).unwrap_or((
let (uid, gid) = possible_pw.as_ref().map(|p| (p.uid, p.gid)).unwrap_or((
if state.rflag { getuid() } else { geteuid() },
if state.rflag { getgid() } else { getegid() },
));
@ -302,7 +302,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let groups = entries::get_groups_gnu(Some(gid)).unwrap();
let groups = if state.user_specified {
possible_pw.map(|p| p.belongs_to()).unwrap()
possible_pw.as_ref().map(|p| p.belongs_to()).unwrap()
} else {
groups.clone()
};
@ -453,7 +453,7 @@ pub fn uu_app() -> App<'static, 'static> {
fn pretty(possible_pw: Option<Passwd>) {
if let Some(p) = possible_pw {
print!("uid\t{}\ngroups\t", p.name());
print!("uid\t{}\ngroups\t", p.name);
println!(
"{}",
p.belongs_to()
@ -466,10 +466,10 @@ fn pretty(possible_pw: Option<Passwd>) {
let login = cstr2cow!(getlogin() as *const _);
let rid = getuid();
if let Ok(p) = Passwd::locate(rid) {
if login == p.name() {
if login == p.name {
println!("login\t{}", login);
}
println!("uid\t{}", p.name());
println!("uid\t{}", p.name);
} else {
println!("uid\t{}", rid);
}
@ -477,7 +477,7 @@ fn pretty(possible_pw: Option<Passwd>) {
let eid = getegid();
if eid == rid {
if let Ok(p) = Passwd::locate(eid) {
println!("euid\t{}", p.name());
println!("euid\t{}", p.name);
} else {
println!("euid\t{}", eid);
}
@ -486,7 +486,7 @@ fn pretty(possible_pw: Option<Passwd>) {
let rid = getgid();
if rid != eid {
if let Ok(g) = Group::locate(rid) {
println!("euid\t{}", g.name());
println!("euid\t{}", g.name);
} else {
println!("euid\t{}", rid);
}
@ -511,16 +511,16 @@ fn pline(possible_uid: Option<uid_t>) {
println!(
"{}:{}:{}:{}:{}:{}:{}:{}:{}:{}",
pw.name(),
pw.user_passwd(),
pw.uid(),
pw.gid(),
pw.user_access_class(),
pw.passwd_change_time(),
pw.expiration(),
pw.user_info(),
pw.user_dir(),
pw.user_shell()
pw.name,
pw.user_passwd,
pw.uid,
pw.gid,
pw.user_access_class,
pw.passwd_change_time,
pw.expiration,
pw.user_info,
pw.user_dir,
pw.user_shell
);
}
@ -531,13 +531,7 @@ fn pline(possible_uid: Option<uid_t>) {
println!(
"{}:{}:{}:{}:{}:{}:{}",
pw.name(),
pw.user_passwd(),
pw.uid(),
pw.gid(),
pw.user_info(),
pw.user_dir(),
pw.user_shell()
pw.name, pw.user_passwd, pw.uid, pw.gid, pw.user_info, pw.user_dir, pw.user_shell
);
}

View file

@ -1,6 +1,6 @@
[package]
name = "uu_install"
version = "0.0.8"
version = "0.0.9"
authors = [
"Ben Eills <ben@beneills.com>",
"uutils developers",
@ -22,8 +22,8 @@ clap = { version = "2.33", features = ["wrap_help"] }
filetime = "0.2"
file_diff = "1.0.0"
libc = ">= 0.2"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "mode", "perms", "entries"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["fs", "mode", "perms", "entries"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[dev-dependencies]
time = "0.1.40"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_join"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "join ~ (uutils) merge lines from inputs with matching join fields"
@ -16,8 +16,8 @@ path = "src/join.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "join"

View file

@ -13,8 +13,9 @@ extern crate uucore;
use clap::{crate_version, App, Arg};
use std::cmp::Ordering;
use std::fs::File;
use std::io::{stdin, BufRead, BufReader, Lines, Stdin};
use std::io::{stdin, stdout, BufRead, BufReader, Split, Stdin, Write};
use uucore::display::Quotable;
use uucore::error::{set_exit_code, UResult, USimpleError};
static NAME: &str = "join";
@ -24,9 +25,16 @@ enum FileNum {
File2,
}
#[repr(u8)]
#[derive(Copy, Clone)]
enum LineEnding {
Nul = 0,
Newline = b'\n',
}
#[derive(Copy, Clone)]
enum Sep {
Char(char),
Char(u8),
Line,
Whitespaces,
}
@ -45,10 +53,11 @@ struct Settings {
print_unpaired2: bool,
print_joined: bool,
ignore_case: bool,
line_ending: LineEnding,
separator: Sep,
autoformat: bool,
format: Vec<Spec>,
empty: String,
empty: Vec<u8>,
check_order: CheckOrder,
headers: bool,
}
@ -62,10 +71,11 @@ impl Default for Settings {
print_unpaired2: false,
print_joined: true,
ignore_case: false,
line_ending: LineEnding::Newline,
separator: Sep::Whitespaces,
autoformat: false,
format: vec![],
empty: String::new(),
empty: vec![],
check_order: CheckOrder::Default,
headers: false,
}
@ -74,14 +84,21 @@ impl Default for Settings {
/// Output representation.
struct Repr<'a> {
separator: char,
line_ending: LineEnding,
separator: u8,
format: &'a [Spec],
empty: &'a str,
empty: &'a [u8],
}
impl<'a> Repr<'a> {
fn new(separator: char, format: &'a [Spec], empty: &'a str) -> Repr<'a> {
fn new(
line_ending: LineEnding,
separator: u8,
format: &'a [Spec],
empty: &'a [u8],
) -> Repr<'a> {
Repr {
line_ending,
separator,
format,
empty,
@ -93,32 +110,34 @@ impl<'a> Repr<'a> {
}
/// Print the field or empty filler if the field is not set.
fn print_field(&self, field: Option<&str>) {
fn print_field(&self, field: Option<&Vec<u8>>) -> Result<(), std::io::Error> {
let value = match field {
Some(field) => field,
None => self.empty,
};
print!("{}", value);
stdout().write_all(value)
}
/// Print each field except the one at the index.
fn print_fields(&self, line: &Line, index: usize) {
fn print_fields(&self, line: &Line, index: usize) -> Result<(), std::io::Error> {
for i in 0..line.fields.len() {
if i != index {
print!("{}{}", self.separator, line.fields[i]);
stdout().write_all(&[self.separator])?;
stdout().write_all(&line.fields[i])?;
}
}
Ok(())
}
/// Print each field or the empty filler if the field is not set.
fn print_format<F>(&self, f: F)
fn print_format<F>(&self, f: F) -> Result<(), std::io::Error>
where
F: Fn(&Spec) -> Option<&'a str>,
F: Fn(&Spec) -> Option<&'a Vec<u8>>,
{
for i in 0..self.format.len() {
if i > 0 {
print!("{}", self.separator);
stdout().write_all(&[self.separator])?;
}
let field = match f(&self.format[i]) {
@ -126,8 +145,13 @@ impl<'a> Repr<'a> {
None => self.empty,
};
print!("{}", field);
stdout().write_all(field)?;
}
Ok(())
}
fn print_line_ending(&self) -> Result<(), std::io::Error> {
stdout().write_all(&[self.line_ending as u8])
}
}
@ -147,10 +171,12 @@ impl Input {
}
}
fn compare(&self, field1: Option<&str>, field2: Option<&str>) -> Ordering {
fn compare(&self, field1: Option<&Vec<u8>>, field2: Option<&Vec<u8>>) -> Ordering {
if let (Some(field1), Some(field2)) = (field1, field2) {
if self.ignore_case {
field1.to_lowercase().cmp(&field2.to_lowercase())
field1
.to_ascii_lowercase()
.cmp(&field2.to_ascii_lowercase())
} else {
field1.cmp(field2)
}
@ -172,40 +198,55 @@ enum Spec {
}
impl Spec {
fn parse(format: &str) -> Spec {
fn parse(format: &str) -> UResult<Spec> {
let mut chars = format.chars();
let file_num = match chars.next() {
Some('0') => {
// Must be all alone without a field specifier.
if chars.next().is_none() {
return Spec::Key;
return Ok(Spec::Key);
}
crash!(1, "invalid field specifier: {}", format.quote());
return Err(USimpleError::new(
1,
format!("invalid field specifier: {}", format.quote()),
));
}
Some('1') => FileNum::File1,
Some('2') => FileNum::File2,
_ => crash!(1, "invalid file number in field spec: {}", format.quote()),
_ => {
return Err(USimpleError::new(
1,
format!("invalid file number in field spec: {}", format.quote()),
));
}
};
if let Some('.') = chars.next() {
return Spec::Field(file_num, parse_field_number(chars.as_str()));
return Ok(Spec::Field(file_num, parse_field_number(chars.as_str())?));
}
crash!(1, "invalid field specifier: {}", format.quote());
Err(USimpleError::new(
1,
format!("invalid field specifier: {}", format.quote()),
))
}
}
struct Line {
fields: Vec<String>,
fields: Vec<Vec<u8>>,
}
impl Line {
fn new(string: String, separator: Sep) -> Line {
fn new(string: Vec<u8>, separator: Sep) -> Line {
let fields = match separator {
Sep::Whitespaces => string.split_whitespace().map(String::from).collect(),
Sep::Char(sep) => string.split(sep).map(String::from).collect(),
Sep::Whitespaces => string
// GNU join uses Bourne shell field splitters by default
.split(|c| matches!(*c, b' ' | b'\t' | b'\n'))
.filter(|f| !f.is_empty())
.map(Vec::from)
.collect(),
Sep::Char(sep) => string.split(|c| *c == sep).map(Vec::from).collect(),
Sep::Line => vec![string],
};
@ -213,7 +254,7 @@ impl Line {
}
/// Get field at index.
fn get_field(&self, index: usize) -> Option<&str> {
fn get_field(&self, index: usize) -> Option<&Vec<u8>> {
if index < self.fields.len() {
Some(&self.fields[index])
} else {
@ -227,7 +268,7 @@ struct State<'a> {
file_name: &'a str,
file_num: FileNum,
print_unpaired: bool,
lines: Lines<Box<dyn BufRead + 'a>>,
lines: Split<Box<dyn BufRead + 'a>>,
seq: Vec<Line>,
line_num: usize,
has_failed: bool,
@ -239,6 +280,7 @@ impl<'a> State<'a> {
name: &'a str,
stdin: &'a Stdin,
key: usize,
line_ending: LineEnding,
print_unpaired: bool,
) -> State<'a> {
let f = if name == "-" {
@ -255,7 +297,7 @@ impl<'a> State<'a> {
file_name: name,
file_num,
print_unpaired,
lines: f.lines(),
lines: f.split(line_ending as u8),
seq: Vec::new(),
line_num: 0,
has_failed: false,
@ -263,12 +305,13 @@ impl<'a> State<'a> {
}
/// Skip the current unpaired line.
fn skip_line(&mut self, input: &Input, repr: &Repr) {
fn skip_line(&mut self, input: &Input, repr: &Repr) -> Result<(), std::io::Error> {
if self.print_unpaired {
self.print_first_line(repr);
self.print_first_line(repr)?;
}
self.reset_next_line(input);
Ok(())
}
/// Keep reading line sequence until the key does not change, return
@ -288,20 +331,22 @@ impl<'a> State<'a> {
}
/// Print lines in the buffers as headers.
fn print_headers(&self, other: &State, repr: &Repr) {
fn print_headers(&self, other: &State, repr: &Repr) -> Result<(), std::io::Error> {
if self.has_line() {
if other.has_line() {
self.combine(other, repr);
self.combine(other, repr)?;
} else {
self.print_first_line(repr);
self.print_first_line(repr)?;
}
} else if other.has_line() {
other.print_first_line(repr);
other.print_first_line(repr)?;
}
Ok(())
}
/// Combine two line sequences.
fn combine(&self, other: &State, repr: &Repr) {
fn combine(&self, other: &State, repr: &Repr) -> Result<(), std::io::Error> {
let key = self.get_current_key();
for line1 in &self.seq {
@ -320,16 +365,18 @@ impl<'a> State<'a> {
None
}
});
})?;
} else {
repr.print_field(key);
repr.print_fields(line1, self.key);
repr.print_fields(line2, other.key);
repr.print_field(key)?;
repr.print_fields(line1, self.key)?;
repr.print_fields(line2, other.key)?;
}
println!();
repr.print_line_ending()?;
}
}
Ok(())
}
/// Reset with the next line.
@ -366,14 +413,16 @@ impl<'a> State<'a> {
0
}
fn finalize(&mut self, input: &Input, repr: &Repr) {
fn finalize(&mut self, input: &Input, repr: &Repr) -> Result<(), std::io::Error> {
if self.has_line() && self.print_unpaired {
self.print_first_line(repr);
self.print_first_line(repr)?;
while let Some(line) = self.next_line(input) {
self.print_line(&line, repr);
self.print_line(&line, repr)?;
}
}
Ok(())
}
/// Get the next line without the order check.
@ -402,7 +451,7 @@ impl<'a> State<'a> {
// This is fatal if the check is enabled.
if input.check_order == CheckOrder::Enabled {
exit!(1);
std::process::exit(1);
}
self.has_failed = true;
@ -412,11 +461,11 @@ impl<'a> State<'a> {
}
/// Gets the key value of the lines stored in seq.
fn get_current_key(&self) -> Option<&str> {
fn get_current_key(&self) -> Option<&Vec<u8>> {
self.seq[0].get_field(self.key)
}
fn print_line(&self, line: &Line, repr: &Repr) {
fn print_line(&self, line: &Line, repr: &Repr) -> Result<(), std::io::Error> {
if repr.uses_format() {
repr.print_format(|spec| match *spec {
Spec::Key => line.get_field(self.key),
@ -427,26 +476,27 @@ impl<'a> State<'a> {
None
}
}
});
})?;
} else {
repr.print_field(line.get_field(self.key));
repr.print_fields(line, self.key);
repr.print_field(line.get_field(self.key))?;
repr.print_fields(line, self.key)?;
}
println!();
repr.print_line_ending()
}
fn print_first_line(&self, repr: &Repr) {
self.print_line(&self.seq[0], repr);
fn print_first_line(&self, repr: &Repr) -> Result<(), std::io::Error> {
self.print_line(&self.seq[0], repr)
}
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().get_matches_from(args);
let keys = parse_field_number_option(matches.value_of("j"));
let key1 = parse_field_number_option(matches.value_of("1"));
let key2 = parse_field_number_option(matches.value_of("2"));
let keys = parse_field_number_option(matches.value_of("j"))?;
let key1 = parse_field_number_option(matches.value_of("1"))?;
let key2 = parse_field_number_option(matches.value_of("2"))?;
let mut settings: Settings = Default::default();
@ -459,21 +509,27 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.unwrap_or_default()
.chain(matches.values_of("a").unwrap_or_default());
for file_num in unpaired {
match parse_file_number(file_num) {
match parse_file_number(file_num)? {
FileNum::File1 => settings.print_unpaired1 = true,
FileNum::File2 => settings.print_unpaired2 = true,
}
}
settings.ignore_case = matches.is_present("i");
settings.key1 = get_field_number(keys, key1);
settings.key2 = get_field_number(keys, key2);
settings.key1 = get_field_number(keys, key1)?;
settings.key2 = get_field_number(keys, key2)?;
if let Some(value) = matches.value_of("t") {
if let Some(value_str) = matches.value_of("t") {
let value = value_str.as_bytes();
settings.separator = match value.len() {
0 => Sep::Line,
1 => Sep::Char(value.chars().next().unwrap()),
_ => crash!(1, "multi-character tab {}", value),
1 => Sep::Char(value[0]),
_ => {
return Err(USimpleError::new(
1,
format!("multi-character tab {}", value_str),
))
}
};
}
@ -481,15 +537,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if format == "auto" {
settings.autoformat = true;
} else {
settings.format = format
.split(|c| c == ' ' || c == ',' || c == '\t')
.map(Spec::parse)
.collect();
let mut specs = vec![];
for part in format.split(|c| c == ' ' || c == ',' || c == '\t') {
specs.push(Spec::parse(part)?);
}
settings.format = specs;
}
}
if let Some(empty) = matches.value_of("e") {
settings.empty = empty.to_string();
settings.empty = empty.as_bytes().to_vec();
}
if matches.is_present("nocheck-order") {
@ -504,14 +561,21 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
settings.headers = true;
}
if matches.is_present("z") {
settings.line_ending = LineEnding::Nul;
}
let file1 = matches.value_of("file1").unwrap();
let file2 = matches.value_of("file2").unwrap();
if file1 == "-" && file2 == "-" {
crash!(1, "both files cannot be standard input");
return Err(USimpleError::new(1, "both files cannot be standard input"));
}
exec(file1, file2, settings)
match exec(file1, file2, settings) {
Ok(_) => Ok(()),
Err(e) => Err(USimpleError::new(1, format!("{}", e))),
}
}
pub fn uu_app() -> App<'static, 'static> {
@ -607,6 +671,12 @@ FILENUM is 1 or 2, corresponding to FILE1 or FILE2",
"treat the first line in each file as field headers, \
print them without trying to pair them",
))
.arg(
Arg::with_name("z")
.short("z")
.long("zero-terminated")
.help("line delimiter is NUL, not newline"),
)
.arg(
Arg::with_name("file1")
.required(true)
@ -621,7 +691,7 @@ FILENUM is 1 or 2, corresponding to FILE1 or FILE2",
)
}
fn exec(file1: &str, file2: &str, settings: Settings) -> i32 {
fn exec(file1: &str, file2: &str, settings: Settings) -> Result<(), std::io::Error> {
let stdin = stdin();
let mut state1 = State::new(
@ -629,6 +699,7 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> i32 {
file1,
&stdin,
settings.key1,
settings.line_ending,
settings.print_unpaired1,
);
@ -637,6 +708,7 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> i32 {
file2,
&stdin,
settings.key2,
settings.line_ending,
settings.print_unpaired2,
);
@ -666,16 +738,17 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> i32 {
};
let repr = Repr::new(
settings.line_ending,
match settings.separator {
Sep::Char(sep) => sep,
_ => ' ',
_ => b' ',
},
&format,
&settings.empty,
);
if settings.headers {
state1.print_headers(&state2, &repr);
state1.print_headers(&state2, &repr)?;
state1.reset_read_line(&input);
state2.reset_read_line(&input);
}
@ -685,17 +758,17 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> i32 {
match diff {
Ordering::Less => {
state1.skip_line(&input, &repr);
state1.skip_line(&input, &repr)?;
}
Ordering::Greater => {
state2.skip_line(&input, &repr);
state2.skip_line(&input, &repr)?;
}
Ordering::Equal => {
let next_line1 = state1.extend(&input);
let next_line2 = state2.extend(&input);
if settings.print_joined {
state1.combine(&state2, &repr);
state1.combine(&state2, &repr)?;
}
state1.reset(next_line1);
@ -704,46 +777,61 @@ fn exec(file1: &str, file2: &str, settings: Settings) -> i32 {
}
}
state1.finalize(&input, &repr);
state2.finalize(&input, &repr);
state1.finalize(&input, &repr)?;
state2.finalize(&input, &repr)?;
(state1.has_failed || state2.has_failed) as i32
if state1.has_failed || state2.has_failed {
set_exit_code(1);
}
Ok(())
}
/// Check that keys for both files and for a particular file are not
/// contradictory and return the key index.
fn get_field_number(keys: Option<usize>, key: Option<usize>) -> usize {
fn get_field_number(keys: Option<usize>, key: Option<usize>) -> UResult<usize> {
if let Some(keys) = keys {
if let Some(key) = key {
if keys != key {
// Show zero-based field numbers as one-based.
crash!(1, "incompatible join fields {}, {}", keys + 1, key + 1);
return Err(USimpleError::new(
1,
format!("incompatible join fields {}, {}", keys + 1, key + 1),
));
}
}
return keys;
return Ok(keys);
}
key.unwrap_or(0)
Ok(key.unwrap_or(0))
}
/// Parse the specified field string as a natural number and return
/// the zero-based field number.
fn parse_field_number(value: &str) -> usize {
fn parse_field_number(value: &str) -> UResult<usize> {
match value.parse::<usize>() {
Ok(result) if result > 0 => result - 1,
_ => crash!(1, "invalid field number: {}", value.quote()),
Ok(result) if result > 0 => Ok(result - 1),
_ => Err(USimpleError::new(
1,
format!("invalid field number: {}", value.quote()),
)),
}
}
fn parse_file_number(value: &str) -> FileNum {
fn parse_file_number(value: &str) -> UResult<FileNum> {
match value {
"1" => FileNum::File1,
"2" => FileNum::File2,
value => crash!(1, "invalid file number: {}", value.quote()),
"1" => Ok(FileNum::File1),
"2" => Ok(FileNum::File2),
value => Err(USimpleError::new(
1,
format!("invalid file number: {}", value.quote()),
)),
}
}
fn parse_field_number_option(value: Option<&str>) -> Option<usize> {
Some(parse_field_number(value?))
fn parse_field_number_option(value: Option<&str>) -> UResult<Option<usize>> {
match value {
None => Ok(None),
Some(val) => Ok(Some(parse_field_number(val)?)),
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "uu_kill"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "kill ~ (uutils) send a signal to a process"
@ -17,8 +17,8 @@ path = "src/kill.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["signals"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["signals"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "kill"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_link"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "link ~ (uutils) create a hard (file system) link to FILE"
@ -16,8 +16,8 @@ path = "src/link.rs"
[dependencies]
libc = "0.2.42"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
clap = { version = "2.33", features = ["wrap_help"] }
[[bin]]

View file

@ -4,14 +4,11 @@
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg};
use std::fs::hard_link;
use std::io::Error;
use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};
static ABOUT: &str = "Call the link function to create a link named FILE2 to an existing FILE1.";
@ -23,14 +20,8 @@ fn usage() -> String {
format!("{0} FILE1 FILE2", uucore::execution_phrase())
}
pub fn normalize_error_message(e: Error) -> String {
match e.raw_os_error() {
Some(2) => String::from("No such file or directory (os error 2)"),
_ => format!("{}", e),
}
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
@ -41,13 +32,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let old = Path::new(files[0]);
let new = Path::new(files[1]);
match hard_link(old, new) {
Ok(_) => 0,
Err(err) => {
show_error!("{}", normalize_error_message(err));
1
}
}
hard_link(old, new)
.map_err_context(|| format!("cannot create link {} to {}", new.quote(), old.quote()))
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -1,6 +1,6 @@
[package]
name = "uu_ln"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "ln ~ (uutils) create a (file system) link to TARGET"
@ -17,8 +17,8 @@ path = "src/ln.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["fs"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "ln"

View file

@ -462,7 +462,7 @@ fn numbered_backup_path(path: &Path) -> PathBuf {
}
fn existing_backup_path(path: &Path, suffix: &str) -> PathBuf {
let test_path = simple_backup_path(path, &".~1~".to_owned());
let test_path = simple_backup_path(path, ".~1~");
if test_path.exists() {
return numbered_backup_path(path);
}

View file

@ -1,6 +1,6 @@
[package]
name = "uu_logname"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "logname ~ (uutils) display the login name of the current user"
@ -17,8 +17,8 @@ path = "src/logname.rs"
[dependencies]
libc = "0.2.42"
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "logname"

View file

@ -12,10 +12,10 @@
#[macro_use]
extern crate uucore;
use std::ffi::CStr;
use uucore::InvalidEncodingHandling;
use clap::{crate_version, App};
use std::ffi::CStr;
use uucore::error::UResult;
use uucore::InvalidEncodingHandling;
extern "C" {
// POSIX requires using getlogin (or equivalent code)
@ -39,7 +39,8 @@ fn usage() -> &'static str {
uucore::execution_phrase()
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args
.collect_str(InvalidEncodingHandling::Ignore)
.accept_any();
@ -51,7 +52,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
None => show_error!("no login name"),
}
0
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -1,6 +1,6 @@
[package]
name = "uu_ls"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "ls ~ (uutils) display directory contents"
@ -21,10 +21,10 @@ unicode-width = "0.1.8"
number_prefix = "0.4"
term_grid = "0.1.5"
termsize = "0.1.6"
globset = "0.4.6"
glob = "0.3.0"
lscolors = { version = "0.7.1", features = ["ansi_term"] }
uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore", features = ["entries", "fs"] }
uucore_procs = { version=">=0.0.7", package = "uucore_procs", path = "../../uucore_procs" }
uucore_procs = { version=">=0.0.8", package = "uucore_procs", path = "../../uucore_procs" }
once_cell = "1.7.2"
atty = "0.2"
selinux = { version="0.2.1", optional = true }

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package]
name = "uu_mkdir"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "mkdir ~ (uutils) create DIRECTORY"
@ -17,8 +17,8 @@ path = "src/mkdir.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs", "mode"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["fs", "mode"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "mkdir"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_mkfifo"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "mkfifo ~ (uutils) create FIFOs (named pipes)"
@ -17,8 +17,8 @@ path = "src/mkfifo.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "mkfifo"

View file

@ -11,6 +11,7 @@ extern crate uucore;
use clap::{crate_version, App, Arg};
use libc::mkfifo;
use std::ffi::CString;
use uucore::error::{UResult, USimpleError};
use uucore::{display::Quotable, InvalidEncodingHandling};
static NAME: &str = "mkfifo";
@ -24,7 +25,8 @@ mod options {
pub static FIFO: &str = "fifo";
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args
.collect_str(InvalidEncodingHandling::Ignore)
.accept_any();
@ -32,41 +34,39 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let matches = uu_app().get_matches_from(args);
if matches.is_present(options::CONTEXT) {
crash!(1, "--context is not implemented");
return Err(USimpleError::new(1, "--context is not implemented"));
}
if matches.is_present(options::SE_LINUX_SECURITY_CONTEXT) {
crash!(1, "-Z is not implemented");
return Err(USimpleError::new(1, "-Z is not implemented"));
}
let mode = match matches.value_of(options::MODE) {
Some(m) => match usize::from_str_radix(m, 8) {
Ok(m) => m,
Err(e) => {
show_error!("invalid mode: {}", e);
return 1;
}
Err(e) => return Err(USimpleError::new(1, format!("invalid mode: {}", e))),
},
None => 0o666,
};
let fifos: Vec<String> = match matches.values_of(options::FIFO) {
Some(v) => v.clone().map(|s| s.to_owned()).collect(),
None => crash!(1, "missing operand"),
None => return Err(USimpleError::new(1, "missing operand")),
};
let mut exit_code = 0;
for f in fifos {
let err = unsafe {
let name = CString::new(f.as_bytes()).unwrap();
mkfifo(name.as_ptr(), mode as libc::mode_t)
};
if err == -1 {
show_error!("cannot create fifo {}: File exists", f.quote());
exit_code = 1;
show!(USimpleError::new(
1,
format!("cannot create fifo {}: File exists", f.quote())
));
}
}
exit_code
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -1,6 +1,6 @@
[package]
name = "uu_mknod"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "mknod ~ (uutils) create special file NAME of TYPE"
@ -18,8 +18,8 @@ path = "src/mknod.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
libc = "^0.2.42"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["mode"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["mode"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "mknod"

View file

@ -7,9 +7,6 @@
// spell-checker:ignore (ToDO) parsemode makedev sysmacros perror IFBLK IFCHR IFIFO
#[macro_use]
extern crate uucore;
use std::ffi::CString;
use clap::{crate_version, App, Arg, ArgMatches};
@ -17,6 +14,7 @@ use libc::{dev_t, mode_t};
use libc::{S_IFBLK, S_IFCHR, S_IFIFO, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR};
use uucore::display::Quotable;
use uucore::error::{set_exit_code, UResult, USimpleError, UUsageError};
use uucore::InvalidEncodingHandling;
static ABOUT: &str = "Create the special file NAME of the given TYPE.";
@ -81,8 +79,8 @@ fn _mknod(file_name: &str, mode: mode_t, dev: dev_t) -> i32 {
}
}
#[allow(clippy::cognitive_complexity)]
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args
.collect_str(InvalidEncodingHandling::Ignore)
.accept_any();
@ -92,13 +90,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let matches = uu_app().get_matches_from(args);
let mode = match get_mode(&matches) {
Ok(mode) => mode,
Err(err) => {
show_error!("{}", err);
return 1;
}
};
let mode = get_mode(&matches).map_err(|e| USimpleError::new(1, e))?;
let file_name = matches.value_of("name").expect("Missing argument 'NAME'");
@ -113,31 +105,29 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if ch == 'p' {
if matches.is_present("major") || matches.is_present("minor") {
eprintln!("Fifos do not have major and minor device numbers.");
eprintln!(
"Try '{} --help' for more information.",
uucore::execution_phrase()
);
1
Err(UUsageError::new(
1,
"Fifos do not have major and minor device numbers.",
))
} else {
_mknod(file_name, S_IFIFO | mode, 0)
let exit_code = _mknod(file_name, S_IFIFO | mode, 0);
set_exit_code(exit_code);
Ok(())
}
} else {
match (matches.value_of("major"), matches.value_of("minor")) {
(None, None) | (_, None) | (None, _) => {
eprintln!("Special files require major and minor device numbers.");
eprintln!(
"Try '{} --help' for more information.",
uucore::execution_phrase()
);
1
return Err(UUsageError::new(
1,
"Special files require major and minor device numbers.",
));
}
(Some(major), Some(minor)) => {
let major = major.parse::<u64>().expect("validated by clap");
let minor = minor.parse::<u64>().expect("validated by clap");
let dev = makedev(major, minor);
if ch == 'b' {
let exit_code = if ch == 'b' {
// block special file
_mknod(file_name, S_IFBLK | mode, dev)
} else if ch == 'c' || ch == 'u' {
@ -145,7 +135,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
_mknod(file_name, S_IFCHR | mode, dev)
} else {
unreachable!("{} was validated to be only b, c or u", ch);
}
};
set_exit_code(exit_code);
Ok(())
}
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "uu_mktemp"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "mktemp ~ (uutils) create and display a temporary file or directory from TEMPLATE"
@ -18,8 +18,8 @@ path = "src/mktemp.rs"
clap = { version = "2.33", features = ["wrap_help"] }
rand = "0.5"
tempfile = "3.1"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "mktemp"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_more"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "more ~ (uutils) input perusal filter"
@ -17,7 +17,7 @@ path = "src/more.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version = ">=0.0.7", package = "uucore", path = "../../uucore" }
uucore_procs = { version=">=0.0.7", package = "uucore_procs", path = "../../uucore_procs" }
uucore_procs = { version=">=0.0.8", package = "uucore_procs", path = "../../uucore_procs" }
crossterm = ">=0.19"
atty = "0.2"
unicode-width = "0.1.7"
@ -28,7 +28,7 @@ redox_termios = "0.1"
redox_syscall = "0.2"
[target.'cfg(all(unix, not(target_os = "fuchsia")))'.dependencies]
nix = "=0.23.1"
nix = "0.23.1"
[[bin]]
name = "more"

View file

@ -7,9 +7,6 @@
// spell-checker:ignore (methods) isnt
#[macro_use]
extern crate uucore;
use std::{
fs::File,
io::{stdin, stdout, BufReader, Read, Stdout, Write},
@ -31,6 +28,7 @@ use crossterm::{
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError, UUsageError};
const BELL: &str = "\x07";
@ -51,7 +49,8 @@ pub mod options {
const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().get_matches_from(args);
let mut buff = String::new();
@ -65,32 +64,36 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let file = Path::new(file);
if file.is_dir() {
terminal::disable_raw_mode().unwrap();
show_usage_error!("{} is a directory.", file.quote());
return 1;
return Err(UUsageError::new(
1,
format!("{} is a directory.", file.quote()),
));
}
if !file.exists() {
terminal::disable_raw_mode().unwrap();
show_error!("cannot open {}: No such file or directory", file.quote());
return 1;
return Err(USimpleError::new(
1,
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 mut reader = BufReader::new(File::open(file).unwrap());
reader.read_to_string(&mut buff).unwrap();
more(&buff, &mut stdout, next_file.copied(), silent);
more(&buff, &mut stdout, next_file.copied(), silent)?;
buff.clear();
}
reset_term(&mut stdout);
} else if atty::isnt(atty::Stream::Stdin) {
stdin().read_to_string(&mut buff).unwrap();
let mut stdout = setup_term();
more(&buff, &mut stdout, None, silent);
more(&buff, &mut stdout, None, silent)?;
reset_term(&mut stdout);
} else {
show_usage_error!("bad usage");
return Err(UUsageError::new(1, "bad usage"));
}
0
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {
@ -210,14 +213,14 @@ fn reset_term(stdout: &mut std::io::Stdout) {
#[inline(always)]
fn reset_term(_: &mut usize) {}
fn more(buff: &str, stdout: &mut Stdout, next_file: Option<&str>, silent: bool) {
fn more(buff: &str, stdout: &mut Stdout, next_file: Option<&str>, silent: bool) -> UResult<()> {
let (cols, rows) = terminal::size().unwrap();
let lines = break_buff(buff, usize::from(cols));
let mut pager = Pager::new(rows, lines, next_file, silent);
pager.draw(stdout, None);
if pager.should_close() {
return;
return Ok(());
}
loop {
@ -244,7 +247,7 @@ fn more(buff: &str, stdout: &mut Stdout, next_file: Option<&str>, silent: bool)
modifiers: KeyModifiers::NONE,
}) => {
if pager.should_close() {
return;
return Ok(());
} else {
pager.page_down();
}
@ -255,6 +258,22 @@ fn more(buff: &str, stdout: &mut Stdout, next_file: Option<&str>, silent: bool)
}) => {
pager.page_up();
}
Event::Key(KeyEvent {
code: KeyCode::Char('j'),
modifiers: KeyModifiers::NONE,
}) => {
if pager.should_close() {
return Ok(());
} else {
pager.next_line();
}
}
Event::Key(KeyEvent {
code: KeyCode::Char('k'),
modifiers: KeyModifiers::NONE,
}) => {
pager.prev_line();
}
Event::Resize(col, row) => {
pager.page_resize(col, row);
}
@ -301,6 +320,17 @@ impl<'a> Pager<'a> {
}
fn page_down(&mut self) {
// If the next page down position __after redraw__ is greater than the total line count,
// the upper mark must not grow past top of the screen at the end of the open file.
if self
.upper_mark
.saturating_add(self.content_rows as usize * 2)
.ge(&self.line_count)
{
self.upper_mark = self.line_count - self.content_rows as usize;
return;
}
self.upper_mark = self.upper_mark.saturating_add(self.content_rows.into());
}
@ -308,6 +338,14 @@ impl<'a> Pager<'a> {
self.upper_mark = self.upper_mark.saturating_sub(self.content_rows.into());
}
fn next_line(&mut self) {
self.upper_mark = self.upper_mark.saturating_add(1);
}
fn prev_line(&mut self) {
self.upper_mark = self.upper_mark.saturating_sub(1);
}
// TODO: Deal with column size changes.
fn page_resize(&mut self, _: u16, row: u16) {
self.content_rows = row.saturating_sub(1);

View file

@ -1,6 +1,6 @@
[package]
name = "uu_mv"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "mv ~ (uutils) move (rename) SOURCE to DESTINATION"
@ -17,8 +17,8 @@ path = "src/mv.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
fs_extra = "1.1.0"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "mv"

43
src/uu/mv/src/error.rs Normal file
View file

@ -0,0 +1,43 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
use std::error::Error;
use std::fmt::{Display, Formatter, Result};
use uucore::error::UError;
#[derive(Debug)]
pub enum MvError {
NoSuchFile(String),
SameFile(String, String),
SelfSubdirectory(String),
DirectoryToNonDirectory(String),
NonDirectoryToDirectory(String, String),
NotADirectory(String),
}
impl Error for MvError {}
impl UError for MvError {}
impl Display for MvError {
fn fmt(&self, f: &mut Formatter) -> Result {
match self {
MvError::NoSuchFile(s) => write!(f, "cannot stat {}: No such file or directory", s),
MvError::SameFile(s, t) => write!(f, "{} and {} are the same file", s, t),
MvError::SelfSubdirectory(s) => write!(
f,
"cannot move '{s}' to a subdirectory of itself, '{s}/{s}'",
s = s
),
MvError::DirectoryToNonDirectory(t) => {
write!(f, "cannot overwrite directory {} with non-directory", t)
}
MvError::NonDirectoryToDirectory(s, t) => write!(
f,
"cannot overwrite non-directory {} with directory {}",
t, s
),
MvError::NotADirectory(t) => write!(f, "target {} is not a directory", t),
}
}
}

View file

@ -8,11 +8,14 @@
// spell-checker:ignore (ToDO) sourcepath targetpath
mod error;
#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg, ArgMatches};
use std::env;
use std::ffi::OsString;
use std::fs;
use std::io::{self, stdin};
#[cfg(unix)]
@ -22,17 +25,21 @@ use std::os::windows;
use std::path::{Path, PathBuf};
use uucore::backup_control::{self, BackupMode};
use uucore::display::Quotable;
use uucore::error::{FromIo, UError, UResult, USimpleError, UUsageError};
use fs_extra::dir::{move_dir, CopyOptions as DirCopyOptions};
use crate::error::MvError;
pub struct Behavior {
overwrite: OverwriteMode,
backup: BackupMode,
suffix: String,
update: bool,
target_dir: Option<String>,
target_dir: Option<OsString>,
no_target_dir: bool,
verbose: bool,
strip_slashes: bool,
}
#[derive(Clone, Eq, PartialEq)]
@ -65,7 +72,8 @@ fn usage() -> String {
)
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage();
let matches = uu_app()
@ -77,23 +85,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.usage(&usage[..])
.get_matches_from(args);
let files: Vec<String> = matches
.values_of(ARG_FILES)
.map(|v| v.map(ToString::to_string).collect())
.unwrap_or_default();
let files: Vec<OsString> = matches
.values_of_os(ARG_FILES)
.unwrap_or_default()
.map(|v| v.to_os_string())
.collect();
let overwrite_mode = determine_overwrite_mode(&matches);
let backup_mode = match backup_control::determine_backup_mode(&matches) {
Err(e) => {
show!(e);
return 1;
}
Ok(mode) => mode,
};
let backup_mode = backup_control::determine_backup_mode(&matches)?;
if overwrite_mode == OverwriteMode::NoClobber && backup_mode != BackupMode::NoBackup {
show_usage_error!("options --backup and --no-clobber are mutually exclusive");
return 1;
return Err(UUsageError::new(
1,
"options --backup and --no-clobber are mutually exclusive",
));
}
let backup_suffix = backup_control::determine_backup_suffix(&matches);
@ -103,26 +108,15 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
backup: backup_mode,
suffix: backup_suffix,
update: matches.is_present(OPT_UPDATE),
target_dir: matches.value_of(OPT_TARGET_DIRECTORY).map(String::from),
target_dir: matches
.value_of_os(OPT_TARGET_DIRECTORY)
.map(OsString::from),
no_target_dir: matches.is_present(OPT_NO_TARGET_DIRECTORY),
verbose: matches.is_present(OPT_VERBOSE),
strip_slashes: matches.is_present(OPT_STRIP_TRAILING_SLASHES),
};
let paths: Vec<PathBuf> = {
fn strip_slashes(p: &Path) -> &Path {
p.components().as_path()
}
let to_owned = |p: &Path| p.to_owned();
let paths = files.iter().map(Path::new);
if matches.is_present(OPT_STRIP_TRAILING_SLASHES) {
paths.map(strip_slashes).map(to_owned).collect()
} else {
paths.map(to_owned).collect()
}
};
exec(&paths[..], behavior)
exec(&files[..], behavior)
}
pub fn uu_app() -> App<'static, 'static> {
@ -210,117 +204,107 @@ fn determine_overwrite_mode(matches: &ArgMatches) -> OverwriteMode {
}
}
fn exec(files: &[PathBuf], b: Behavior) -> i32 {
fn exec(files: &[OsString], b: Behavior) -> UResult<()> {
let paths: Vec<PathBuf> = {
let paths = files.iter().map(Path::new);
// Strip slashes from path, if strip opt present
if b.strip_slashes {
paths
.map(|p| p.components().as_path().to_owned())
.collect::<Vec<PathBuf>>()
} else {
paths.map(|p| p.to_owned()).collect::<Vec<PathBuf>>()
}
};
if let Some(ref name) = b.target_dir {
return move_files_into_dir(files, &PathBuf::from(name), &b);
return move_files_into_dir(&paths, &PathBuf::from(name), &b);
}
match files.len() {
match paths.len() {
/* case 0/1 are not possible thanks to clap */
2 => {
let source = &files[0];
let target = &files[1];
let source = &paths[0];
let target = &paths[1];
// Here we use the `symlink_metadata()` method instead of `exists()`,
// since it handles dangling symlinks correctly. The method gives an
// `Ok()` results unless the source does not exist, or the user
// lacks permission to access metadata.
if source.symlink_metadata().is_err() {
show_error!("cannot stat {}: No such file or directory", source.quote());
return 1;
return Err(MvError::NoSuchFile(source.quote().to_string()).into());
}
// GNU semantics are: if the source and target are the same, no move occurs and we print an error
if source.eq(target) {
// Done to match GNU semantics for the dot file
if source.eq(Path::new(".")) || source.ends_with("/.") || source.is_file() {
return Err(MvError::SameFile(
source.quote().to_string(),
target.quote().to_string(),
)
.into());
} else {
return Err(MvError::SelfSubdirectory(source.display().to_string()).into());
}
}
if target.is_dir() {
if b.no_target_dir {
if !source.is_dir() {
show_error!(
"cannot overwrite directory {} with non-directory",
target.quote()
);
return 1;
Err(MvError::DirectoryToNonDirectory(target.quote().to_string()).into())
} else {
rename(source, target, &b).map_err_context(|| {
format!("cannot move {} to {}", source.quote(), target.quote())
})
}
return match rename(source, target, &b) {
Err(e) => {
show_error!(
"cannot move {} to {}: {}",
source.quote(),
target.quote(),
e.to_string()
);
1
}
_ => 0,
};
} else {
move_files_into_dir(&[source.clone()], target, &b)
}
return move_files_into_dir(&[source.clone()], target, &b);
} else if target.exists() && source.is_dir() {
show_error!(
"cannot overwrite non-directory {} with directory {}",
target.quote(),
source.quote()
);
return 1;
}
if let Err(e) = rename(source, target, &b) {
show_error!("{}", e);
return 1;
Err(MvError::NonDirectoryToDirectory(
source.quote().to_string(),
target.quote().to_string(),
)
.into())
} else {
rename(source, target, &b).map_err(|e| USimpleError::new(1, format!("{}", e)))
}
}
_ => {
if b.no_target_dir {
show_error!(
"mv: extra operand {}\n\
Try '{} --help' for more information.",
files[2].quote(),
uucore::execution_phrase()
);
return 1;
return Err(UUsageError::new(
1,
format!("mv: extra operand {}", files[2].quote()),
));
}
let target_dir = files.last().unwrap();
move_files_into_dir(&files[..files.len() - 1], target_dir, &b);
let target_dir = paths.last().unwrap();
move_files_into_dir(&paths[..paths.len() - 1], target_dir, &b)
}
}
0
}
fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> i32 {
fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> UResult<()> {
if !target_dir.is_dir() {
show_error!("target {} is not a directory", target_dir.quote());
return 1;
return Err(MvError::NotADirectory(target_dir.quote().to_string()).into());
}
let mut all_successful = true;
for sourcepath in files.iter() {
let targetpath = match sourcepath.file_name() {
Some(name) => target_dir.join(name),
None => {
show_error!(
"cannot stat {}: No such file or directory",
sourcepath.quote()
);
all_successful = false;
show!(MvError::NoSuchFile(sourcepath.quote().to_string()));
continue;
}
};
if let Err(e) = rename(sourcepath, &targetpath, b) {
show_error!(
"cannot move {} to {}: {}",
show_if_err!(
rename(sourcepath, &targetpath, b).map_err_context(|| format!(
"cannot move {} to {}",
sourcepath.quote(),
targetpath.quote(),
e.to_string()
);
all_successful = false;
}
}
if all_successful {
0
} else {
1
targetpath.quote()
))
)
}
Ok(())
}
fn rename(from: &Path, to: &Path, b: &Behavior) -> io::Result<()> {

View file

@ -1,6 +1,6 @@
[package]
name = "uu_nice"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "nice ~ (uutils) run PROGRAM with modified scheduling priority"
@ -17,9 +17,9 @@ path = "src/nice.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
nix = "=0.23.1"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
nix = "0.23.1"
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "nice"

View file

@ -16,6 +16,7 @@ use std::io::Error;
use std::ptr;
use clap::{crate_version, App, AppSettings, Arg};
use uucore::error::{set_exit_code, UResult, USimpleError, UUsageError};
pub mod options {
pub static ADJUSTMENT: &str = "adjustment";
@ -35,7 +36,8 @@ process).",
)
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
@ -45,31 +47,34 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
libc::getpriority(PRIO_PROCESS, 0)
};
if Error::last_os_error().raw_os_error().unwrap() != 0 {
show_error!("getpriority: {}", Error::last_os_error());
return 125;
return Err(USimpleError::new(
125,
format!("getpriority: {}", Error::last_os_error()),
));
}
let adjustment = match matches.value_of(options::ADJUSTMENT) {
Some(nstr) => {
if !matches.is_present(options::COMMAND) {
show_error!(
"A command must be given with an adjustment.\nTry '{} --help' for more information.",
uucore::execution_phrase()
);
return 125;
return Err(UUsageError::new(
125,
"A command must be given with an adjustment.",
));
}
match nstr.parse() {
Ok(num) => num,
Err(e) => {
show_error!("\"{}\" is not a valid number: {}", nstr, e);
return 125;
return Err(USimpleError::new(
125,
format!("\"{}\" is not a valid number: {}", nstr, e),
))
}
}
}
None => {
if !matches.is_present(options::COMMAND) {
println!("{}", niceness);
return 0;
return Ok(());
}
10_i32
}
@ -93,11 +98,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}
show_error!("execvp: {}", Error::last_os_error());
if Error::last_os_error().raw_os_error().unwrap() as c_int == libc::ENOENT {
let exit_code = if Error::last_os_error().raw_os_error().unwrap() as c_int == libc::ENOENT {
127
} else {
126
}
};
set_exit_code(exit_code);
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -1,6 +1,6 @@
[package]
name = "uu_nl"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "nl ~ (uutils) display input with added line numbers"
@ -21,12 +21,12 @@ libc = "0.2.42"
memchr = "2.2.0"
regex = "1.0.1"
regex-syntax = "0.6.7"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "nl"
path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]
normal = ["uucore_procs"]

View file

@ -1,6 +1,6 @@
[package]
name = "uu_nohup"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "nohup ~ (uutils) run COMMAND, ignoring hangup signals"
@ -18,8 +18,8 @@ path = "src/nohup.rs"
clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
atty = "0.2"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["fs"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "nohup"

View file

@ -15,11 +15,13 @@ use libc::{c_char, dup2, execvp, signal};
use libc::{SIGHUP, SIG_IGN};
use std::env;
use std::ffi::CString;
use std::fmt::{Display, Formatter};
use std::fs::{File, OpenOptions};
use std::io::Error;
use std::os::unix::prelude::*;
use std::path::{Path, PathBuf};
use uucore::display::Quotable;
use uucore::error::{set_exit_code, UError, UResult};
use uucore::InvalidEncodingHandling;
static ABOUT: &str = "Run COMMAND ignoring hangup signals.";
@ -40,7 +42,47 @@ mod options {
pub const CMD: &str = "cmd";
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[derive(Debug)]
enum NohupError {
CannotDetach,
CannotReplace(&'static str, std::io::Error),
OpenFailed(i32, std::io::Error),
OpenFailed2(i32, std::io::Error, String, std::io::Error),
}
impl std::error::Error for NohupError {}
impl UError for NohupError {
fn code(&self) -> i32 {
match self {
NohupError::OpenFailed(code, _) | NohupError::OpenFailed2(code, _, _, _) => *code,
_ => 2,
}
}
}
impl Display for NohupError {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
NohupError::CannotDetach => write!(f, "Cannot detach from console"),
NohupError::CannotReplace(s, e) => write!(f, "Cannot replace {}: {}", s, e),
NohupError::OpenFailed(_, e) => {
write!(f, "failed to open {}: {}", NOHUP_OUT.quote(), e)
}
NohupError::OpenFailed2(_, e1, s, e2) => write!(
f,
"failed to open {}: {}\nfailed to open {}: {}",
NOHUP_OUT.quote(),
e1,
s.quote(),
e2
),
}
}
}
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage();
let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy)
@ -48,12 +90,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
replace_fds();
replace_fds()?;
unsafe { signal(SIGHUP, SIG_IGN) };
if unsafe { !_vprocmgr_detach_from_console(0).is_null() } {
crash!(2, "Cannot detach from console")
return Err(NohupError::CannotDetach.into());
};
let cstrs: Vec<CString> = matches
@ -66,9 +108,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let ret = unsafe { execvp(args[0], args.as_mut_ptr()) };
match ret {
libc::ENOENT => EXIT_ENOENT,
_ => EXIT_CANNOT_INVOKE,
libc::ENOENT => set_exit_code(EXIT_ENOENT),
_ => set_exit_code(EXIT_CANNOT_INVOKE),
}
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {
@ -85,32 +128,31 @@ pub fn uu_app() -> App<'static, 'static> {
.setting(AppSettings::TrailingVarArg)
}
fn replace_fds() {
fn replace_fds() -> UResult<()> {
if atty::is(atty::Stream::Stdin) {
let new_stdin = match File::open(Path::new("/dev/null")) {
Ok(t) => t,
Err(e) => crash!(2, "Cannot replace STDIN: {}", e),
};
let new_stdin = File::open(Path::new("/dev/null"))
.map_err(|e| NohupError::CannotReplace("STDIN", e))?;
if unsafe { dup2(new_stdin.as_raw_fd(), 0) } != 0 {
crash!(2, "Cannot replace STDIN: {}", Error::last_os_error())
return Err(NohupError::CannotReplace("STDIN", Error::last_os_error()).into());
}
}
if atty::is(atty::Stream::Stdout) {
let new_stdout = find_stdout();
let new_stdout = find_stdout()?;
let fd = new_stdout.as_raw_fd();
if unsafe { dup2(fd, 1) } != 1 {
crash!(2, "Cannot replace STDOUT: {}", Error::last_os_error())
return Err(NohupError::CannotReplace("STDOUT", Error::last_os_error()).into());
}
}
if atty::is(atty::Stream::Stderr) && unsafe { dup2(1, 2) } != 2 {
crash!(2, "Cannot replace STDERR: {}", Error::last_os_error())
return Err(NohupError::CannotReplace("STDERR", Error::last_os_error()).into());
}
Ok(())
}
fn find_stdout() -> File {
fn find_stdout() -> UResult<File> {
let internal_failure_code = match std::env::var("POSIXLY_CORRECT") {
Ok(_) => POSIX_NOHUP_FAILURE,
Err(_) => EXIT_CANCELED,
@ -127,14 +169,11 @@ fn find_stdout() -> File {
"ignoring input and appending output to {}",
NOHUP_OUT.quote()
);
t
Ok(t)
}
Err(e1) => {
let home = match env::var("HOME") {
Err(_) => {
show_error!("failed to open {}: {}", NOHUP_OUT.quote(), e1);
exit!(internal_failure_code)
}
Err(_) => return Err(NohupError::OpenFailed(internal_failure_code, e1).into()),
Ok(h) => h,
};
let mut homeout = PathBuf::from(home);
@ -151,13 +190,15 @@ fn find_stdout() -> File {
"ignoring input and appending output to {}",
homeout_str.quote()
);
t
}
Err(e2) => {
show_error!("failed to open {}: {}", NOHUP_OUT.quote(), e1);
show_error!("failed to open {}: {}", homeout_str.quote(), e2);
exit!(internal_failure_code)
Ok(t)
}
Err(e2) => Err(NohupError::OpenFailed2(
internal_failure_code,
e1,
homeout_str.to_string(),
e2,
)
.into()),
}
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "uu_nproc"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "nproc ~ (uutils) display the number of processing units available"
@ -18,8 +18,8 @@ path = "src/nproc.rs"
libc = "0.2.42"
num_cpus = "1.10"
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["fs"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["fs"] }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "nproc"

View file

@ -1,6 +1,6 @@
[package]
name = "uu_numfmt"
version = "0.0.8"
version = "0.0.9"
authors = ["uutils developers"]
license = "MIT"
description = "numfmt ~ (uutils) reformat NUMBER"
@ -16,12 +16,12 @@ path = "src/numfmt.rs"
[dependencies]
clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" }
uucore = { version=">=0.0.11", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]]
name = "numfmt"
path = "src/main.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["uucore_procs"]
normal = ["uucore_procs"]

View file

@ -45,13 +45,13 @@ impl<'a> Iterator for WhitespaceSplitter<'a> {
let (prefix, field) = haystack.split_at(
haystack
.find(|c: char| !c.is_whitespace())
.unwrap_or_else(|| haystack.len()),
.unwrap_or(haystack.len()),
);
let (field, rest) = field.split_at(
field
.find(|c: char| c.is_whitespace())
.unwrap_or_else(|| field.len()),
.unwrap_or(field.len()),
);
self.s = if !rest.is_empty() { Some(rest) } else { None };
@ -220,16 +220,32 @@ fn format_string(
options: &NumfmtOptions,
implicit_padding: Option<isize>,
) -> Result<String> {
// strip the (optional) suffix before applying any transformation
let source_without_suffix = match &options.suffix {
Some(suffix) => source.strip_suffix(suffix).unwrap_or(source),
None => source,
};
let number = transform_to(
transform_from(source, &options.transform.from)?,
transform_from(source_without_suffix, &options.transform.from)?,
&options.transform.to,
options.round,
)?;
// bring back the suffix before applying padding
let number_with_suffix = match &options.suffix {
Some(suffix) => format!("{}{}", number, suffix),
None => number,
};
Ok(match implicit_padding.unwrap_or(options.padding) {
0 => number,
p if p > 0 => format!("{:>padding$}", number, padding = p as usize),
p => format!("{:<padding$}", number, padding = p.abs() as usize),
0 => number_with_suffix,
p if p > 0 => format!("{:>padding$}", number_with_suffix, padding = p as usize),
p => format!(
"{:<padding$}",
number_with_suffix,
padding = p.abs() as usize
),
})
}
@ -269,7 +285,7 @@ fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<()> {
print!(" ");
&prefix[1..]
} else {
&prefix
prefix
};
let implicit_padding = if !empty_prefix && options.padding == 0 {

View file

@ -7,15 +7,13 @@
// spell-checker:ignore N'th M'th
#[macro_use]
extern crate uucore;
use crate::format::format_and_print;
use crate::options::*;
use crate::units::{Result, Unit};
use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
use std::io::{BufRead, Write};
use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError};
use uucore::ranges::Range;
pub mod format;
@ -144,6 +142,8 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
_ => unreachable!("Should be restricted by clap"),
};
let suffix = args.value_of(options::SUFFIX).map(|s| s.to_owned());
Ok(NumfmtOptions {
transform,
padding,
@ -151,10 +151,12 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
fields,
delimiter,
round,
suffix,
})
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
@ -168,10 +170,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
match result {
Err(e) => {
std::io::stdout().flush().expect("error flushing stdout");
show_error!("{}", e);
1
// TODO Change `handle_args()` and `handle_stdin()` so that
// they return `UResult`.
return Err(USimpleError::new(1, e));
}
_ => 0,
_ => Ok(()),
}
}
@ -242,5 +245,14 @@ pub fn uu_app() -> App<'static, 'static> {
.default_value("from-zero")
.possible_values(&["up", "down", "from-zero", "towards-zero", "nearest"]),
)
.arg(
Arg::with_name(options::SUFFIX)
.long(options::SUFFIX)
.help(
"print SUFFIX after each formatted number, and accept \
inputs optionally ending with SUFFIX",
)
.value_name("SUFFIX"),
)
.arg(Arg::with_name(options::NUMBER).hidden(true).multiple(true))
}

Some files were not shown because too many files have changed in this diff Show more