1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 12:07:46 +00:00

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 5042 additions and 38041 deletions

View file

@ -13,7 +13,7 @@ env:
PROJECT_NAME: coreutils PROJECT_NAME: coreutils
PROJECT_DESC: "Core universal (cross-platform) utilities" PROJECT_DESC: "Core universal (cross-platform) utilities"
PROJECT_AUTH: "uutils" 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 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 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 ## Generate coverage data
COVERAGE_REPORT_DIR="target/debug" COVERAGE_REPORT_DIR="target/debug"
COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info" 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?) # 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}" mkdir -p "${COVERAGE_REPORT_DIR}"
# display coverage files # 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 # 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} echo ::set-output name=report::${COVERAGE_REPORT_FILE}
- name: Upload coverage results (to Codecov.io) - name: Upload coverage results (to Codecov.io)
uses: codecov/codecov-action@v1 uses: codecov/codecov-action@v1

View file

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

View file

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

513
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@
[package] [package]
name = "coreutils" name = "coreutils"
version = "0.0.8" version = "0.0.9"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "coreutils ~ GNU coreutils (updated); implemented as universal (cross-platform) utils, written in Rust" 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"] } clap = { version = "2.33", features = ["wrap_help"] }
lazy_static = { version="1.3" } lazy_static = { version="1.3" }
textwrap = { version="0.14", features=["terminal_size"] } 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 } selinux = { version="0.2.3", optional = true }
# * uutils # * 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" } arch = { optional=true, version="0.0.9", package="uu_arch", path="src/uu/arch" }
base32 = { optional=true, version="0.0.8", package="uu_base32", path="src/uu/base32" } base32 = { optional=true, version="0.0.9", package="uu_base32", path="src/uu/base32" }
base64 = { optional=true, version="0.0.8", package="uu_base64", path="src/uu/base64" } base64 = { optional=true, version="0.0.9", package="uu_base64", path="src/uu/base64" }
basename = { optional=true, version="0.0.8", package="uu_basename", path="src/uu/basename" } basename = { optional=true, version="0.0.9", package="uu_basename", path="src/uu/basename" }
basenc = { optional=true, version="0.0.8", package="uu_basenc", path="src/uu/basenc" } basenc = { optional=true, version="0.0.9", package="uu_basenc", path="src/uu/basenc" }
cat = { optional=true, version="0.0.8", package="uu_cat", path="src/uu/cat" } cat = { optional=true, version="0.0.9", package="uu_cat", path="src/uu/cat" }
chcon = { optional=true, version="0.0.8", package="uu_chcon", path="src/uu/chcon" } chcon = { optional=true, version="0.0.9", package="uu_chcon", path="src/uu/chcon" }
chgrp = { optional=true, version="0.0.8", package="uu_chgrp", path="src/uu/chgrp" } chgrp = { optional=true, version="0.0.9", package="uu_chgrp", path="src/uu/chgrp" }
chmod = { optional=true, version="0.0.8", package="uu_chmod", path="src/uu/chmod" } chmod = { optional=true, version="0.0.9", package="uu_chmod", path="src/uu/chmod" }
chown = { optional=true, version="0.0.8", package="uu_chown", path="src/uu/chown" } chown = { optional=true, version="0.0.9", package="uu_chown", path="src/uu/chown" }
chroot = { optional=true, version="0.0.8", package="uu_chroot", path="src/uu/chroot" } chroot = { optional=true, version="0.0.9", package="uu_chroot", path="src/uu/chroot" }
cksum = { optional=true, version="0.0.8", package="uu_cksum", path="src/uu/cksum" } cksum = { optional=true, version="0.0.9", package="uu_cksum", path="src/uu/cksum" }
comm = { optional=true, version="0.0.8", package="uu_comm", path="src/uu/comm" } comm = { optional=true, version="0.0.9", package="uu_comm", path="src/uu/comm" }
cp = { optional=true, version="0.0.8", package="uu_cp", path="src/uu/cp" } cp = { optional=true, version="0.0.9", package="uu_cp", path="src/uu/cp" }
csplit = { optional=true, version="0.0.8", package="uu_csplit", path="src/uu/csplit" } csplit = { optional=true, version="0.0.9", package="uu_csplit", path="src/uu/csplit" }
cut = { optional=true, version="0.0.8", package="uu_cut", path="src/uu/cut" } cut = { optional=true, version="0.0.9", package="uu_cut", path="src/uu/cut" }
date = { optional=true, version="0.0.8", package="uu_date", path="src/uu/date" } date = { optional=true, version="0.0.9", package="uu_date", path="src/uu/date" }
dd = { optional=true, version="0.0.8", package="uu_dd", path="src/uu/dd" } dd = { optional=true, version="0.0.9", package="uu_dd", path="src/uu/dd" }
df = { optional=true, version="0.0.8", package="uu_df", path="src/uu/df" } df = { optional=true, version="0.0.9", package="uu_df", path="src/uu/df" }
dircolors= { optional=true, version="0.0.8", package="uu_dircolors", path="src/uu/dircolors" } dircolors= { optional=true, version="0.0.9", package="uu_dircolors", path="src/uu/dircolors" }
dirname = { optional=true, version="0.0.8", package="uu_dirname", path="src/uu/dirname" } dirname = { optional=true, version="0.0.9", package="uu_dirname", path="src/uu/dirname" }
du = { optional=true, version="0.0.8", package="uu_du", path="src/uu/du" } du = { optional=true, version="0.0.9", package="uu_du", path="src/uu/du" }
echo = { optional=true, version="0.0.8", package="uu_echo", path="src/uu/echo" } echo = { optional=true, version="0.0.9", package="uu_echo", path="src/uu/echo" }
env = { optional=true, version="0.0.8", package="uu_env", path="src/uu/env" } env = { optional=true, version="0.0.9", package="uu_env", path="src/uu/env" }
expand = { optional=true, version="0.0.8", package="uu_expand", path="src/uu/expand" } expand = { optional=true, version="0.0.9", package="uu_expand", path="src/uu/expand" }
expr = { optional=true, version="0.0.8", package="uu_expr", path="src/uu/expr" } expr = { optional=true, version="0.0.9", package="uu_expr", path="src/uu/expr" }
factor = { optional=true, version="0.0.8", package="uu_factor", path="src/uu/factor" } factor = { optional=true, version="0.0.9", package="uu_factor", path="src/uu/factor" }
false = { optional=true, version="0.0.8", package="uu_false", path="src/uu/false" } false = { optional=true, version="0.0.9", package="uu_false", path="src/uu/false" }
fmt = { optional=true, version="0.0.8", package="uu_fmt", path="src/uu/fmt" } fmt = { optional=true, version="0.0.9", package="uu_fmt", path="src/uu/fmt" }
fold = { optional=true, version="0.0.8", package="uu_fold", path="src/uu/fold" } fold = { optional=true, version="0.0.9", package="uu_fold", path="src/uu/fold" }
groups = { optional=true, version="0.0.8", package="uu_groups", path="src/uu/groups" } groups = { optional=true, version="0.0.9", package="uu_groups", path="src/uu/groups" }
hashsum = { optional=true, version="0.0.8", package="uu_hashsum", path="src/uu/hashsum" } hashsum = { optional=true, version="0.0.9", package="uu_hashsum", path="src/uu/hashsum" }
head = { optional=true, version="0.0.8", package="uu_head", path="src/uu/head" } head = { optional=true, version="0.0.9", package="uu_head", path="src/uu/head" }
hostid = { optional=true, version="0.0.8", package="uu_hostid", path="src/uu/hostid" } hostid = { optional=true, version="0.0.9", package="uu_hostid", path="src/uu/hostid" }
hostname = { optional=true, version="0.0.8", package="uu_hostname", path="src/uu/hostname" } hostname = { optional=true, version="0.0.9", package="uu_hostname", path="src/uu/hostname" }
id = { optional=true, version="0.0.8", package="uu_id", path="src/uu/id" } id = { optional=true, version="0.0.9", package="uu_id", path="src/uu/id" }
install = { optional=true, version="0.0.8", package="uu_install", path="src/uu/install" } install = { optional=true, version="0.0.9", package="uu_install", path="src/uu/install" }
join = { optional=true, version="0.0.8", package="uu_join", path="src/uu/join" } join = { optional=true, version="0.0.9", package="uu_join", path="src/uu/join" }
kill = { optional=true, version="0.0.8", package="uu_kill", path="src/uu/kill" } kill = { optional=true, version="0.0.9", package="uu_kill", path="src/uu/kill" }
link = { optional=true, version="0.0.8", package="uu_link", path="src/uu/link" } link = { optional=true, version="0.0.9", package="uu_link", path="src/uu/link" }
ln = { optional=true, version="0.0.8", package="uu_ln", path="src/uu/ln" } ln = { optional=true, version="0.0.9", package="uu_ln", path="src/uu/ln" }
ls = { optional=true, version="0.0.8", package="uu_ls", path="src/uu/ls" } ls = { optional=true, version="0.0.9", package="uu_ls", path="src/uu/ls" }
logname = { optional=true, version="0.0.8", package="uu_logname", path="src/uu/logname" } logname = { optional=true, version="0.0.9", package="uu_logname", path="src/uu/logname" }
mkdir = { optional=true, version="0.0.8", package="uu_mkdir", path="src/uu/mkdir" } mkdir = { optional=true, version="0.0.9", package="uu_mkdir", path="src/uu/mkdir" }
mkfifo = { optional=true, version="0.0.8", package="uu_mkfifo", path="src/uu/mkfifo" } mkfifo = { optional=true, version="0.0.9", package="uu_mkfifo", path="src/uu/mkfifo" }
mknod = { optional=true, version="0.0.8", package="uu_mknod", path="src/uu/mknod" } mknod = { optional=true, version="0.0.9", package="uu_mknod", path="src/uu/mknod" }
mktemp = { optional=true, version="0.0.8", package="uu_mktemp", path="src/uu/mktemp" } mktemp = { optional=true, version="0.0.9", package="uu_mktemp", path="src/uu/mktemp" }
more = { optional=true, version="0.0.8", package="uu_more", path="src/uu/more" } more = { optional=true, version="0.0.9", package="uu_more", path="src/uu/more" }
mv = { optional=true, version="0.0.8", package="uu_mv", path="src/uu/mv" } mv = { optional=true, version="0.0.9", package="uu_mv", path="src/uu/mv" }
nice = { optional=true, version="0.0.8", package="uu_nice", path="src/uu/nice" } nice = { optional=true, version="0.0.9", package="uu_nice", path="src/uu/nice" }
nl = { optional=true, version="0.0.8", package="uu_nl", path="src/uu/nl" } nl = { optional=true, version="0.0.9", package="uu_nl", path="src/uu/nl" }
nohup = { optional=true, version="0.0.8", package="uu_nohup", path="src/uu/nohup" } nohup = { optional=true, version="0.0.9", package="uu_nohup", path="src/uu/nohup" }
nproc = { optional=true, version="0.0.8", package="uu_nproc", path="src/uu/nproc" } nproc = { optional=true, version="0.0.9", package="uu_nproc", path="src/uu/nproc" }
numfmt = { optional=true, version="0.0.8", package="uu_numfmt", path="src/uu/numfmt" } numfmt = { optional=true, version="0.0.9", package="uu_numfmt", path="src/uu/numfmt" }
od = { optional=true, version="0.0.8", package="uu_od", path="src/uu/od" } od = { optional=true, version="0.0.9", package="uu_od", path="src/uu/od" }
paste = { optional=true, version="0.0.8", package="uu_paste", path="src/uu/paste" } paste = { optional=true, version="0.0.9", package="uu_paste", path="src/uu/paste" }
pathchk = { optional=true, version="0.0.8", package="uu_pathchk", path="src/uu/pathchk" } pathchk = { optional=true, version="0.0.9", package="uu_pathchk", path="src/uu/pathchk" }
pinky = { optional=true, version="0.0.8", package="uu_pinky", path="src/uu/pinky" } pinky = { optional=true, version="0.0.9", package="uu_pinky", path="src/uu/pinky" }
pr = { optional=true, version="0.0.8", package="uu_pr", path="src/uu/pr" } pr = { optional=true, version="0.0.9", package="uu_pr", path="src/uu/pr" }
printenv = { optional=true, version="0.0.8", package="uu_printenv", path="src/uu/printenv" } printenv = { optional=true, version="0.0.9", package="uu_printenv", path="src/uu/printenv" }
printf = { optional=true, version="0.0.8", package="uu_printf", path="src/uu/printf" } printf = { optional=true, version="0.0.9", package="uu_printf", path="src/uu/printf" }
ptx = { optional=true, version="0.0.8", package="uu_ptx", path="src/uu/ptx" } ptx = { optional=true, version="0.0.9", package="uu_ptx", path="src/uu/ptx" }
pwd = { optional=true, version="0.0.8", package="uu_pwd", path="src/uu/pwd" } pwd = { optional=true, version="0.0.9", package="uu_pwd", path="src/uu/pwd" }
readlink = { optional=true, version="0.0.8", package="uu_readlink", path="src/uu/readlink" } readlink = { optional=true, version="0.0.9", package="uu_readlink", path="src/uu/readlink" }
realpath = { optional=true, version="0.0.8", package="uu_realpath", path="src/uu/realpath" } realpath = { optional=true, version="0.0.9", package="uu_realpath", path="src/uu/realpath" }
relpath = { optional=true, version="0.0.8", package="uu_relpath", path="src/uu/relpath" } relpath = { optional=true, version="0.0.9", package="uu_relpath", path="src/uu/relpath" }
rm = { optional=true, version="0.0.8", package="uu_rm", path="src/uu/rm" } rm = { optional=true, version="0.0.9", package="uu_rm", path="src/uu/rm" }
rmdir = { optional=true, version="0.0.8", package="uu_rmdir", path="src/uu/rmdir" } rmdir = { optional=true, version="0.0.9", package="uu_rmdir", path="src/uu/rmdir" }
runcon = { optional=true, version="0.0.8", package="uu_runcon", path="src/uu/runcon" } runcon = { optional=true, version="0.0.9", package="uu_runcon", path="src/uu/runcon" }
seq = { optional=true, version="0.0.8", package="uu_seq", path="src/uu/seq" } seq = { optional=true, version="0.0.9", package="uu_seq", path="src/uu/seq" }
shred = { optional=true, version="0.0.8", package="uu_shred", path="src/uu/shred" } shred = { optional=true, version="0.0.9", package="uu_shred", path="src/uu/shred" }
shuf = { optional=true, version="0.0.8", package="uu_shuf", path="src/uu/shuf" } shuf = { optional=true, version="0.0.9", package="uu_shuf", path="src/uu/shuf" }
sleep = { optional=true, version="0.0.8", package="uu_sleep", path="src/uu/sleep" } sleep = { optional=true, version="0.0.9", package="uu_sleep", path="src/uu/sleep" }
sort = { optional=true, version="0.0.8", package="uu_sort", path="src/uu/sort" } sort = { optional=true, version="0.0.9", package="uu_sort", path="src/uu/sort" }
split = { optional=true, version="0.0.8", package="uu_split", path="src/uu/split" } split = { optional=true, version="0.0.9", package="uu_split", path="src/uu/split" }
stat = { optional=true, version="0.0.8", package="uu_stat", path="src/uu/stat" } stat = { optional=true, version="0.0.9", package="uu_stat", path="src/uu/stat" }
stdbuf = { optional=true, version="0.0.8", package="uu_stdbuf", path="src/uu/stdbuf" } stdbuf = { optional=true, version="0.0.9", package="uu_stdbuf", path="src/uu/stdbuf" }
sum = { optional=true, version="0.0.8", package="uu_sum", path="src/uu/sum" } sum = { optional=true, version="0.0.9", package="uu_sum", path="src/uu/sum" }
sync = { optional=true, version="0.0.8", package="uu_sync", path="src/uu/sync" } sync = { optional=true, version="0.0.9", package="uu_sync", path="src/uu/sync" }
tac = { optional=true, version="0.0.8", package="uu_tac", path="src/uu/tac" } tac = { optional=true, version="0.0.9", package="uu_tac", path="src/uu/tac" }
tail = { optional=true, version="0.0.8", package="uu_tail", path="src/uu/tail" } tail = { optional=true, version="0.0.9", package="uu_tail", path="src/uu/tail" }
tee = { optional=true, version="0.0.8", package="uu_tee", path="src/uu/tee" } tee = { optional=true, version="0.0.9", package="uu_tee", path="src/uu/tee" }
timeout = { optional=true, version="0.0.8", package="uu_timeout", path="src/uu/timeout" } timeout = { optional=true, version="0.0.9", package="uu_timeout", path="src/uu/timeout" }
touch = { optional=true, version="0.0.8", package="uu_touch", path="src/uu/touch" } touch = { optional=true, version="0.0.9", package="uu_touch", path="src/uu/touch" }
tr = { optional=true, version="0.0.8", package="uu_tr", path="src/uu/tr" } tr = { optional=true, version="0.0.9", package="uu_tr", path="src/uu/tr" }
true = { optional=true, version="0.0.8", package="uu_true", path="src/uu/true" } true = { optional=true, version="0.0.9", package="uu_true", path="src/uu/true" }
truncate = { optional=true, version="0.0.8", package="uu_truncate", path="src/uu/truncate" } truncate = { optional=true, version="0.0.9", package="uu_truncate", path="src/uu/truncate" }
tsort = { optional=true, version="0.0.8", package="uu_tsort", path="src/uu/tsort" } tsort = { optional=true, version="0.0.9", package="uu_tsort", path="src/uu/tsort" }
tty = { optional=true, version="0.0.8", package="uu_tty", path="src/uu/tty" } tty = { optional=true, version="0.0.9", package="uu_tty", path="src/uu/tty" }
uname = { optional=true, version="0.0.8", package="uu_uname", path="src/uu/uname" } uname = { optional=true, version="0.0.9", package="uu_uname", path="src/uu/uname" }
unexpand = { optional=true, version="0.0.8", package="uu_unexpand", path="src/uu/unexpand" } unexpand = { optional=true, version="0.0.9", package="uu_unexpand", path="src/uu/unexpand" }
uniq = { optional=true, version="0.0.8", package="uu_uniq", path="src/uu/uniq" } uniq = { optional=true, version="0.0.9", package="uu_uniq", path="src/uu/uniq" }
unlink = { optional=true, version="0.0.8", package="uu_unlink", path="src/uu/unlink" } unlink = { optional=true, version="0.0.9", package="uu_unlink", path="src/uu/unlink" }
uptime = { optional=true, version="0.0.8", package="uu_uptime", path="src/uu/uptime" } uptime = { optional=true, version="0.0.9", package="uu_uptime", path="src/uu/uptime" }
users = { optional=true, version="0.0.8", package="uu_users", path="src/uu/users" } users = { optional=true, version="0.0.9", package="uu_users", path="src/uu/users" }
wc = { optional=true, version="0.0.8", package="uu_wc", path="src/uu/wc" } wc = { optional=true, version="0.0.9", package="uu_wc", path="src/uu/wc" }
who = { optional=true, version="0.0.8", package="uu_who", path="src/uu/who" } who = { optional=true, version="0.0.9", package="uu_who", path="src/uu/who" }
whoami = { optional=true, version="0.0.8", package="uu_whoami", path="src/uu/whoami" } whoami = { optional=true, version="0.0.9", package="uu_whoami", path="src/uu/whoami" }
yes = { optional=true, version="0.0.8", package="uu_yes", path="src/uu/yes" } 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)" # 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" } # 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()`) #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] [dev-dependencies]
chrono = "0.4.11" chrono = "^0.4.11"
conv = "0.3" conv = "0.3"
filetime = "0.2" filetime = "0.2"
glob = "0.3.0" glob = "0.3.0"
libc = "0.2" libc = "0.2"
pretty_assertions = "0.7.2" pretty_assertions = "1"
rand = "0.7" rand = "0.7"
regex = "1.0" regex = "1.0"
sha1 = { version="0.6", features=["std"] } sha1 = { version="0.6", features=["std"] }
tempfile = "3.2.0" tempfile = "3.2.0"
time = "0.1" time = "0.1"
unindent = "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" walkdir = "2.2"
atty = "0.2" atty = "0.2"
@ -381,15 +381,10 @@ atty = "0.2"
rlimit = "0.4.0" rlimit = "0.4.0"
[target.'cfg(unix)'.dev-dependencies] [target.'cfg(unix)'.dev-dependencies]
nix = "=0.23.1" nix = "0.23.1"
rust-users = { version="0.10", package="users" } rust-users = { version="0.10", package="users" }
unix_socket = "0.5.0" unix_socket = "0.5.0"
[[bin]] [[bin]]
name = "coreutils" name = "coreutils"
path = "src/bin/coreutils.rs" 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) BUSYBOX_SRC := $(BUSYBOX_ROOT)/busybox-$(BUSYBOX_VER)
ifeq ($(SELINUX_ENABLED),) ifeq ($(SELINUX_ENABLED),)
SELINUX_ENABLED := 0 SELINUX_ENABLED := 0
ifneq ($(OS),Windows_NT) ifneq ($(OS),Windows_NT)
ifeq ($(shell /sbin/selinuxenabled 2>/dev/null ; echo $$?),0) ifeq ($(shell /sbin/selinuxenabled 2>/dev/null ; echo $$?),0)
SELINUX_ENABLED := 1 SELINUX_ENABLED := 1
endif endif
endif endif
endif endif
# Possible programs # Possible programs
@ -161,11 +161,11 @@ SELINUX_PROGS := \
runcon runcon
ifneq ($(OS),Windows_NT) ifneq ($(OS),Windows_NT)
PROGS := $(PROGS) $(UNIX_PROGS) PROGS := $(PROGS) $(UNIX_PROGS)
endif endif
ifeq ($(SELINUX_ENABLED),1) ifeq ($(SELINUX_ENABLED),1)
PROGS := $(PROGS) $(SELINUX_PROGS) PROGS := $(PROGS) $(SELINUX_PROGS)
endif endif
UTILS ?= $(PROGS) 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 ### Rust Version
uutils follows Rust's release channels and is tested against stable, beta and nightly. 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. 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(); let out_dir = env::var("OUT_DIR").unwrap();
// println!("cargo:warning=out_dir={}", out_dir); // 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); // println!("cargo:warning=manifest_dir={}", manifest_dir);
let util_tests_dir = format!("{}/tests/by-util", manifest_dir); let util_tests_dir = format!("{}/tests/by-util", manifest_dir);
// println!("cargo:warning=util_tests_dir={}", util_tests_dir); // println!("cargo:warning=util_tests_dir={}", util_tests_dir);
@ -83,7 +83,7 @@ pub fn main() {
mf.write_all( mf.write_all(
format!( format!(
"\tmap.insert(\"{k}\", ({krate}::uumain, {krate}::uu_app));\n", "\tmap.insert(\"{k}\", ({krate}::uumain, {krate}::uu_app));\n",
k = krate[override_prefix.len()..].to_string(), k = &krate[override_prefix.len()..],
krate = krate krate = krate
) )
.as_bytes(), .as_bytes(),
@ -92,7 +92,7 @@ pub fn main() {
tf.write_all( tf.write_all(
format!( format!(
"#[path=\"{dir}/test_{k}.rs\"]\nmod test_{k};\n", "#[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, dir = util_tests_dir,
) )
.as_bytes(), .as_bytes(),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_chown" name = "uu_chown"
version = "0.0.8" version = "0.0.9"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "chown ~ (uutils) change the ownership of FILE" description = "chown ~ (uutils) change the ownership of FILE"
@ -16,8 +16,8 @@ path = "src/chown.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] } uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
[[bin]] [[bin]]
name = "chown" 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() { let uid = if !user.is_empty() {
Some(match Passwd::locate(user) { 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(_) => Err(_) =>
// we have NOT been able to find the uid // we have NOT been able to find the uid
// but we could be in the case where we have user.group // 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( Some(
Group::locate(group) Group::locate(group)
.map_err(|_| USimpleError::new(1, format!("invalid group: {}", spec.quote())))? .map_err(|_| USimpleError::new(1, format!("invalid group: {}", spec.quote())))?
.gid(), .gid,
) )
} else { } else {
None None

View file

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

View file

@ -7,15 +7,15 @@
// file that was distributed with this source code. // file that was distributed with this source code.
// spell-checker:ignore (ToDO) NEWROOT Userspec pstatus // spell-checker:ignore (ToDO) NEWROOT Userspec pstatus
mod error;
#[macro_use] use crate::error::ChrootError;
extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::ffi::CString; use std::ffi::CString;
use std::io::Error; use std::io::Error;
use std::path::Path; use std::path::Path;
use std::process::Command; 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::libc::{self, chroot, setgid, setgroups, setuid};
use uucore::{entries, InvalidEncodingHandling}; use uucore::{entries, InvalidEncodingHandling};
@ -31,7 +31,8 @@ mod options {
pub const COMMAND: &str = "command"; 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 let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy) .collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any(); .accept_any();
@ -44,19 +45,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let newroot: &Path = match matches.value_of(options::NEWROOT) { let newroot: &Path = match matches.value_of(options::NEWROOT) {
Some(v) => Path::new(v), Some(v) => Path::new(v),
None => crash!( None => return Err(ChrootError::MissingNewRoot.into()),
1,
"Missing operand: NEWROOT\nTry '{} --help' for more information.",
uucore::execution_phrase()
),
}; };
if !newroot.is_dir() { if !newroot.is_dir() {
crash!( return Err(ChrootError::NoSuchDirectory(format!("{}", newroot.display())).into());
1,
"cannot change root directory to {}: no such directory",
newroot.quote()
);
} }
let commands = match matches.values_of(options::COMMAND) { 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..]; let chroot_args = &command[1..];
// NOTE: Tests can only trigger code beyond this point if they're invoked with root permissions // 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) let pstatus = match Command::new(chroot_command).args(chroot_args).status() {
.args(chroot_args) Ok(status) => status,
.status() Err(e) => return Err(ChrootError::CommandFailed(command[0].to_string(), e).into()),
.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
)
});
if pstatus.success() { let code = if pstatus.success() {
0 0
} else { } else {
pstatus.code().unwrap_or(-1) pstatus.code().unwrap_or(-1)
} };
set_exit_code(code);
Ok(())
} }
pub fn uu_app() -> App<'static, 'static> { 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 userspec_str = options.value_of(options::USERSPEC);
let user_str = options.value_of(options::USER).unwrap_or_default(); let user_str = options.value_of(options::USER).unwrap_or_default();
let group_str = options.value_of(options::GROUP).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) => { Some(u) => {
let s: Vec<&str> = u.split(':').collect(); let s: Vec<&str> = u.split(':').collect();
if s.len() != 2 || s.iter().any(|&spec| spec.is_empty()) { 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 s
} }
@ -179,44 +163,40 @@ fn set_context(root: &Path, options: &clap::ArgMatches) {
(userspec[0], userspec[1]) (userspec[0], userspec[1])
}; };
enter_chroot(root); enter_chroot(root)?;
set_groups_from_str(groups_str); set_groups_from_str(groups_str)?;
set_main_group(group); set_main_group(group)?;
set_user(user); set_user(user)?;
Ok(())
} }
fn enter_chroot(root: &Path) { fn enter_chroot(root: &Path) -> UResult<()> {
std::env::set_current_dir(root).unwrap(); std::env::set_current_dir(root).unwrap();
let err = unsafe { let err = unsafe {
chroot(CString::new(".").unwrap().as_bytes_with_nul().as_ptr() as *const libc::c_char) chroot(CString::new(".").unwrap().as_bytes_with_nul().as_ptr() as *const libc::c_char)
}; };
if err != 0 { if err == 0 {
crash!( Ok(())
1, } else {
"cannot chroot to {}: {}", Err(ChrootError::CannotEnter(format!("{}", root.display()), Error::last_os_error()).into())
root.quote(), }
Error::last_os_error()
)
};
} }
fn set_main_group(group: &str) { fn set_main_group(group: &str) -> UResult<()> {
if !group.is_empty() { if !group.is_empty() {
let group_id = match entries::grp2gid(group) { let group_id = match entries::grp2gid(group) {
Ok(g) => g, 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) }; let err = unsafe { setgid(group_id) };
if err != 0 { if err != 0 {
crash!( return Err(
1, ChrootError::SetGidFailed(group_id.to_string(), Error::last_os_error()).into(),
"cannot set gid to {}: {}", );
group_id,
Error::last_os_error()
)
} }
} }
Ok(())
} }
#[cfg(any(target_vendor = "apple", target_os = "freebsd"))] #[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()) } 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() { if !groups.is_empty() {
let groups_vec: Vec<libc::gid_t> = groups let mut groups_vec = vec![];
.split(',') for group in groups.split(',') {
.map(|x| match entries::grp2gid(x) { let gid = match entries::grp2gid(group) {
Ok(g) => g, Ok(g) => g,
_ => crash!(1, "no such group: {}", x), Err(_) => return Err(ChrootError::NoSuchGroup(group.to_string()).into()),
}) };
.collect(); groups_vec.push(gid);
}
let err = set_groups(groups_vec); let err = set_groups(groups_vec);
if err != 0 { 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() { if !user.is_empty() {
let user_id = entries::usr2uid(user).unwrap(); let user_id = entries::usr2uid(user).unwrap();
let err = unsafe { setuid(user_id as libc::uid_t) }; let err = unsafe { setuid(user_id as libc::uid_t) };
if err != 0 { if err != 0 {
crash!( return Err(
1, ChrootError::SetUserFailed(user.to_string(), Error::last_os_error()).into(),
"cannot set user to {}: {}", );
user.maybe_quote(),
Error::last_os_error()
)
} }
} }
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] [package]
name = "uu_cksum" name = "uu_cksum"
version = "0.0.8" version = "0.0.9"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "cksum ~ (uutils) display CRC and size of input" description = "cksum ~ (uutils) display CRC and size of input"
@ -17,8 +17,8 @@ path = "src/cksum.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42" libc = "0.2.42"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } uucore = { version=">=0.0.11", 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" }
[[bin]] [[bin]]
name = "cksum" name = "cksum"

View file

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

View file

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

View file

@ -11,6 +11,8 @@ use std::cmp::Ordering;
use std::fs::File; use std::fs::File;
use std::io::{self, stdin, BufRead, BufReader, Stdin}; use std::io::{self, stdin, BufRead, BufReader, Stdin};
use std::path::Path; use std::path::Path;
use uucore::error::FromIo;
use uucore::error::UResult;
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
use clap::{crate_version, App, Arg, ArgMatches}; 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 usage = usage();
let args = args let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy) .collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any(); .accept_any();
let matches = uu_app().usage(&usage[..]).get_matches_from(args); let matches = uu_app().usage(&usage[..]).get_matches_from(args);
let filename1 = matches.value_of(options::FILE_1).unwrap();
let mut f1 = open_file(matches.value_of(options::FILE_1).unwrap()).unwrap(); let filename2 = matches.value_of(options::FILE_2).unwrap();
let mut f2 = open_file(matches.value_of(options::FILE_2).unwrap()).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); comm(&mut f1, &mut f2, &matches);
Ok(())
0
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {

View file

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

View file

@ -8,7 +8,7 @@
// For the full copyright and license information, please view the LICENSE file // For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code. // 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")] #[cfg(target_os = "linux")]
#[macro_use] #[macro_use]
@ -19,6 +19,7 @@ extern crate quick_error;
extern crate uucore; extern crate uucore;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::fs::FileInformation;
#[cfg(windows)] #[cfg(windows)]
use winapi::um::fileapi::CreateFileW; use winapi::um::fileapi::CreateFileW;
#[cfg(windows)] #[cfg(windows)]
@ -63,7 +64,7 @@ quick_error! {
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// Simple io::Error wrapper /// 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 /// Wrapper for io::Error with path context
IoErrContext(err: io::Error, path: String) { 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 non_fatal_errors = false;
let mut seen_sources = HashSet::with_capacity(sources.len()); let mut seen_sources = HashSet::with_capacity(sources.len());
let mut symlinked_files = HashSet::new();
for source in sources { for source in sources {
if seen_sources.contains(source) { 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()); show_warning!("source {} specified more than once", source.quote());
} else { } else {
let mut found_hard_link = false; 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(); preserve_hardlinks(&mut hard_links, source, dest, &mut found_hard_link).unwrap();
} }
if !found_hard_link { 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 { match error {
// When using --no-clobber, we don't want to show // When using --no-clobber, we don't want to show
// an error message // an error message
@ -909,15 +914,16 @@ fn copy_source(
target: &TargetSlice, target: &TargetSlice,
target_type: &TargetType, target_type: &TargetType,
options: &Options, options: &Options,
symlinked_files: &mut HashSet<FileInformation>,
) -> CopyResult<()> { ) -> CopyResult<()> {
let source_path = Path::new(&source); let source_path = Path::new(&source);
if source_path.is_dir() { if source_path.is_dir() {
// Copy as directory // Copy as directory
copy_directory(source, target, options) copy_directory(source, target, options, symlinked_files)
} else { } else {
// Copy as file // Copy as file
let dest = construct_dest_path(source_path, target, target_type, options)?; 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 /// Any errors encountered copying files in the tree will be logged but
/// will not cause a short-circuit. /// 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 { if !options.recursive {
return Err(format!("omitting directory {}", root.quote()).into()); return Err(format!("omitting directory {}", root.quote()).into());
} }
// if no-dereference is enabled and this is a symlink, copy it as a file // 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() { 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 = 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); let local_to_target = target.join(&local_to_root_parent);
if is_symlink && !options.dereference { 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() { } else if path.is_dir() && !local_to_target.exists() {
or_continue!(fs::create_dir_all(local_to_target)); or_continue!(fs::create_dir_all(local_to_target));
} else if !path.is_dir() { } 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(); let dest = local_to_target.as_path().to_path_buf();
preserve_hardlinks(&mut hard_links, &source, dest, &mut found_hard_link).unwrap(); preserve_hardlinks(&mut hard_links, &source, dest, &mut found_hard_link).unwrap();
if !found_hard_link { 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(()), Ok(_) => Ok(()),
Err(err) => { Err(err) => {
if fs::symlink_metadata(&source)?.file_type().is_symlink() { if fs::symlink_metadata(&source)?.file_type().is_symlink() {
@ -1036,7 +1052,12 @@ fn copy_directory(root: &Path, target: &TargetSlice, options: &Options) -> CopyR
}?; }?;
} }
} else { } 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(()) Ok(())
} }
#[cfg(not(windows))] fn symlink_file(
#[allow(clippy::unnecessary_wraps)] // needed for windows version source: &Path,
fn symlink_file(source: &Path, dest: &Path, context: &str) -> CopyResult<()> { dest: &Path,
match std::os::unix::fs::symlink(source, dest).context(context) { context: &str,
Ok(_) => Ok(()), symlinked_files: &mut HashSet<FileInformation>,
Err(_) => Ok(()), ) -> CopyResult<()> {
#[cfg(not(windows))]
{
std::os::unix::fs::symlink(source, dest).context(context)?;
} }
} #[cfg(windows)]
{
#[cfg(windows)] std::os::windows::fs::symlink_file(source, dest).context(context)?;
fn symlink_file(source: &Path, dest: &Path, context: &str) -> CopyResult<()> { }
Ok(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 { 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 { match options.overwrite {
// FIXME: print that the file was removed if --verbose is enabled
OverwriteMode::Clobber(ClobberMode::Force) => { OverwriteMode::Clobber(ClobberMode::Force) => {
if fs::metadata(dest)?.permissions().readonly() { if fs::metadata(dest)?.permissions().readonly() {
fs::remove_file(dest)?; 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` /// The original permissions of `source` will be copied to `dest`
/// after a successful copy. /// 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() { if dest.exists() {
handle_existing_dest(source, dest, options)?; 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 { if options.verbose {
println!("{}", context_for(source, dest)); 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)?; fs::hard_link(&source, &dest).context(context)?;
} }
CopyMode::Copy => { CopyMode::Copy => {
copy_helper(&source, &dest, options, context)?; copy_helper(&source, &dest, options, context, symlinked_files)?;
} }
CopyMode::SymLink => { 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::Sparse => return Err(Error::NotImplemented(options::SPARSE.to_string())),
CopyMode::Update => { CopyMode::Update => {
@ -1271,10 +1327,10 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
if src_time <= dest_time { if src_time <= dest_time {
return Ok(()); return Ok(());
} else { } else {
copy_helper(&source, &dest, options, context)?; copy_helper(&source, &dest, options, context, symlinked_files)?;
} }
} else { } else {
copy_helper(&source, &dest, options, context)?; copy_helper(&source, &dest, options, context, symlinked_files)?;
} }
} }
CopyMode::AttrOnly => { CopyMode::AttrOnly => {
@ -1292,7 +1348,13 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
.map(|meta| !meta.file_type().is_symlink()) .map(|meta| !meta.file_type().is_symlink())
.unwrap_or(false) .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 { for attribute in &options.preserve_attributes {
copy_attribute(&source, &dest, attribute)?; 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 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. /// 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 { if options.parents {
let parent = dest.parent().unwrap_or(dest); let parent = dest.parent().unwrap_or(dest);
fs::create_dir_all(parent)?; 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 /* workaround a limitation of fs::copy
* https://github.com/rust-lang/rust/issues/79390 * https://github.com/rust-lang/rust/issues/79390
*/ */
File::create(dest)?; File::create(dest).context(dest.display().to_string())?;
} else if is_symlink { } else if is_symlink {
copy_link(source, dest)?; copy_link(source, dest, symlinked_files)?;
} else if options.reflink_mode != ReflinkMode::Never { } else if options.reflink_mode != ReflinkMode::Never {
#[cfg(not(any(target_os = "linux", target_os = "macos")))] #[cfg(not(any(target_os = "linux", target_os = "macos")))]
return Err("--reflink is only supported on linux and 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(()) 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) // Here, we will copy the symlink itself (actually, just recreate it)
let link = fs::read_link(&source)?; let link = fs::read_link(&source)?;
let dest: Cow<'_, Path> = if dest.is_dir() { let dest: Cow<'_, Path> = if dest.is_dir() {
@ -1352,7 +1424,7 @@ fn copy_link(source: &Path, dest: &Path) -> CopyResult<()> {
} }
dest.into() 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. /// Copies `source` to `dest` using copy-on-write if possible.

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_csplit" name = "uu_csplit"
version = "0.0.8" version = "0.0.9"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" 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" 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"] } clap = { version = "2.33", features = ["wrap_help"] }
thiserror = "1.0" thiserror = "1.0"
regex = "1.0.0" regex = "1.0.0"
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "fs"] } uucore = { version=">=0.0.11", 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" }
[[bin]] [[bin]]
name = "csplit" name = "csplit"

View file

@ -2,15 +2,19 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use clap::{crate_version, App, Arg, ArgMatches};
use regex::Regex;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::io::{self, BufReader}; use std::io::{self, BufReader};
use std::{ use std::{
fs::{remove_file, File}, fs::{remove_file, File},
io::{BufRead, BufWriter, Write}, io::{BufRead, BufWriter, Write},
}; };
use clap::{crate_version, App, Arg, ArgMatches};
use regex::Regex;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};
use uucore::InvalidEncodingHandling;
mod csplit_error; mod csplit_error;
mod patterns; mod patterns;
@ -18,7 +22,6 @@ mod split_name;
use crate::csplit_error::CsplitError; use crate::csplit_error::CsplitError;
use crate::split_name::SplitName; use crate::split_name::SplitName;
use uucore::InvalidEncodingHandling;
static SUMMARY: &str = "split a file into sections determined by context lines"; 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."; 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 usage = usage();
let args = args let args = args
.collect_str(InvalidEncodingHandling::Ignore) .collect_str(InvalidEncodingHandling::Ignore)
@ -729,20 +733,22 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.unwrap() .unwrap()
.map(str::to_string) .map(str::to_string)
.collect(); .collect();
let patterns = crash_if_err!(1, patterns::get_patterns(&patterns[..])); let patterns = patterns::get_patterns(&patterns[..])?;
let options = CsplitOptions::new(&matches); let options = CsplitOptions::new(&matches);
if file_name == "-" { if file_name == "-" {
let stdin = io::stdin(); let stdin = io::stdin();
crash_if_err!(1, csplit(&options, patterns, stdin.lock())); Ok(csplit(&options, patterns, stdin.lock())?)
} else { } else {
let file = crash_if_err!(1, File::open(file_name)); let file = File::open(file_name)
let file_metadata = crash_if_err!(1, file.metadata()); .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() { 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))); Ok(csplit(&options, patterns, BufReader::new(file))?)
}; }
0
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {

View file

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

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_cut" name = "uu_cut"
version = "0.0.8" version = "0.0.9"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "cut ~ (uutils) display byte/field columns of input lines" description = "cut ~ (uutils) display byte/field columns of input lines"
@ -16,8 +16,8 @@ path = "src/cut.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } uucore = { version=">=0.0.11", 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" }
memchr = "2" memchr = "2"
bstr = "0.2" bstr = "0.2"
atty = "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::io::{stdin, stdout, BufReader, BufWriter, Read, Write};
use std::path::Path; use std::path::Path;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
use self::searcher::Searcher; use self::searcher::Searcher;
use uucore::ranges::Range; 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 newline_char = if opts.zero_terminated { b'\0' } else { b'\n' };
let buf_in = BufReader::new(reader); let buf_in = BufReader::new(reader);
let mut out = stdout_writer(); 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) .map_or("", String::as_str)
.as_bytes(); .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; let mut print_delim = false;
for &Range { low, high } in ranges { for &Range { low, high } in ranges {
if low > line.len() { 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])?; out.write_all(&[newline_char])?;
Ok(true) 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)] #[allow(clippy::cognitive_complexity)]
@ -183,7 +188,7 @@ fn cut_fields_delimiter<R: Read>(
only_delimited: bool, only_delimited: bool,
newline_char: u8, newline_char: u8,
out_delim: &str, out_delim: &str,
) -> i32 { ) -> UResult<()> {
let buf_in = BufReader::new(reader); let buf_in = BufReader::new(reader);
let mut out = stdout_writer(); let mut out = stdout_writer();
let input_delim_len = delim.len(); let input_delim_len = delim.len();
@ -246,12 +251,16 @@ fn cut_fields_delimiter<R: Read>(
out.write_all(&[newline_char])?; out.write_all(&[newline_char])?;
Ok(true) 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)] #[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' }; let newline_char = if opts.zero_terminated { b'\0' } else { b'\n' };
if let Some(ref o_delim) = opts.out_delimiter { if let Some(ref o_delim) = opts.out_delimiter {
return cut_fields_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])?; out.write_all(&[newline_char])?;
Ok(true) 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 stdin_read = false;
let mut exit_code = 0;
if filenames.is_empty() { if filenames.is_empty() {
filenames.push("-".to_owned()); filenames.push("-".to_owned());
@ -341,11 +353,11 @@ fn cut_files(mut filenames: Vec<String>, mode: Mode) -> i32 {
continue; continue;
} }
exit_code |= match mode { show_if_err!(match mode {
Mode::Bytes(ref ranges, ref opts) => cut_bytes(stdin(), ranges, opts), Mode::Bytes(ref ranges, ref opts) => cut_bytes(stdin(), ranges, opts),
Mode::Characters(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), Mode::Fields(ref ranges, ref opts) => cut_fields(stdin(), ranges, opts),
}; });
stdin_read = true; stdin_read = true;
} else { } else {
@ -356,28 +368,19 @@ fn cut_files(mut filenames: Vec<String>, mode: Mode) -> i32 {
continue; continue;
} }
if path.metadata().is_err() { show_if_err!(File::open(&path)
show_error!("{}: No such file or directory", filename.maybe_quote()); .map_err_context(|| filename.maybe_quote().to_string())
continue; .and_then(|file| {
} match &mode {
Mode::Bytes(ref ranges, ref opts) => cut_bytes(file, ranges, opts),
let file = match File::open(&path) { Mode::Characters(ref ranges, ref opts) => cut_bytes(file, ranges, opts),
Ok(f) => f, Mode::Fields(ref ranges, ref opts) => cut_fields(file, ranges, opts),
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),
};
} }
} }
exit_code Ok(())
} }
mod options { mod options {
@ -392,7 +395,8 @@ mod options {
pub const FILE: &str = "file"; 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 let args = args
.collect_str(InvalidEncodingHandling::Ignore) .collect_str(InvalidEncodingHandling::Ignore)
.accept_any(); .accept_any();
@ -462,12 +466,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
delim = "="; delim = "=";
} }
if delim.chars().count() > 1 { if delim.chars().count() > 1 {
Err(msg_opt_invalid_should_be!( Err("invalid input: The '--delimiter' ('-d') option expects empty or 1 character long, but was provided a value 2 characters or longer".into())
"empty or 1 character long",
"a value 2 characters or longer",
"--delimiter",
"-d"
))
} else { } else {
let delim = if delim.is_empty() { let delim = if delim.is_empty() {
"\0".to_owned() "\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( (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!( _ => Err("invalid usage: expects one of --fields (-f), --chars (-c) or --bytes (-b)".into()),
"--fields (-f)",
"--chars (-c)",
"--bytes (-b)"
)),
}; };
let mode_parse = match mode_parse { let mode_parse = match mode_parse {
@ -514,20 +509,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Mode::Bytes(_, _) | Mode::Characters(_, _) Mode::Bytes(_, _) | Mode::Characters(_, _)
if matches.is_present(options::DELIMITER) => if matches.is_present(options::DELIMITER) =>
{ {
Err(msg_opt_only_usable_if!( Err("invalid input: The '--delimiter' ('-d') option only usable if printing a sequence of fields".into())
"printing a sequence of fields",
"--delimiter",
"-d"
))
} }
Mode::Bytes(_, _) | Mode::Characters(_, _) Mode::Bytes(_, _) | Mode::Characters(_, _)
if matches.is_present(options::ONLY_DELIMITED) => if matches.is_present(options::ONLY_DELIMITED) =>
{ {
Err(msg_opt_only_usable_if!( Err("invalid input: The '--only-delimited' ('-s') option only usable if printing a sequence of fields".into())
"printing a sequence of fields",
"--only-delimited",
"-s"
))
} }
_ => Ok(mode), _ => Ok(mode),
}, },
@ -541,10 +528,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
match mode_parse { match mode_parse {
Ok(mode) => cut_files(files, mode), Ok(mode) => cut_files(files, mode),
Err(err_msg) => { Err(e) => Err(USimpleError::new(1, e)),
show_error!("{}", err_msg);
1
}
} }
} }

View file

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

View file

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

View file

@ -6,11 +6,13 @@
// file that was distributed with this source code. // file that was distributed with this source code.
// spell-checker:ignore ctable, outfile // spell-checker:ignore ctable, outfile
use crate::conversion_tables::*;
use std::error::Error; use std::error::Error;
use std::time; use std::time;
use uucore::error::UError;
use crate::conversion_tables::*;
pub struct ProgUpdate { pub struct ProgUpdate {
pub read_stat: ReadStat, pub read_stat: ReadStat,
pub write_stat: WriteStat, pub write_stat: WriteStat,
@ -154,6 +156,7 @@ impl std::fmt::Display for InternalError {
} }
impl Error for InternalError {} impl Error for InternalError {}
impl UError for InternalError {}
pub mod options { pub mod options {
pub const INFILE: &str = "if"; 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 // 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)] #[cfg(test)]
mod dd_unit_tests; mod dd_unit_tests;
@ -21,14 +19,10 @@ use parseargs::Matches;
mod conversion_tables; mod conversion_tables;
use 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::cmp;
use std::convert::TryInto; use std::convert::TryInto;
use std::env; use std::env;
#[cfg(target_os = "linux")]
use std::error::Error; use std::error::Error;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io::{self, Read, Seek, Write}; use std::io::{self, Read, Seek, Write};
@ -41,10 +35,17 @@ use std::sync::{atomic::AtomicUsize, atomic::Ordering, Arc};
use std::thread; use std::thread;
use std::time; 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 ABOUT: &str = "copy, and optionally convert, a file system resource";
const BUF_INIT_BYTE: u8 = 0xDD; const BUF_INIT_BYTE: u8 = 0xDD;
const RTN_SUCCESS: i32 = 0;
const RTN_FAILURE: i32 = 1;
const NEWLINE: u8 = b'\n'; const NEWLINE: u8 = b'\n';
const SPACE: u8 = b' '; const SPACE: u8 = b' ';
@ -59,7 +60,7 @@ struct Input<R: Read> {
} }
impl Input<io::Stdin> { 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 ibs = parseargs::parse_ibs(matches)?;
let non_ascii = parseargs::parse_input_non_ascii(matches)?; let non_ascii = parseargs::parse_input_non_ascii(matches)?;
let print_level = parseargs::parse_status_level(matches)?; let print_level = parseargs::parse_status_level(matches)?;
@ -80,8 +81,8 @@ impl Input<io::Stdin> {
if let Some(amt) = skip { if let Some(amt) = skip {
let mut buf = vec![BUF_INIT_BYTE; amt]; 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) Ok(i)
@ -125,7 +126,7 @@ fn make_linux_iflags(iflags: &IFlags) -> Option<libc::c_int> {
} }
impl Input<File> { 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 ibs = parseargs::parse_ibs(matches)?;
let non_ascii = parseargs::parse_input_non_ascii(matches)?; let non_ascii = parseargs::parse_input_non_ascii(matches)?;
let print_level = parseargs::parse_status_level(matches)?; let print_level = parseargs::parse_status_level(matches)?;
@ -144,12 +145,16 @@ impl Input<File> {
opts.custom_flags(libc_flags); 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 { if let Some(amt) = skip {
let amt: u64 = amt.try_into()?; let amt: u64 = amt
src.seek(io::SeekFrom::Start(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 { let i = Input {
@ -196,7 +201,7 @@ impl<R: Read> Input<R> {
/// Fills a given buffer. /// Fills a given buffer.
/// Reads in increments of 'self.ibs'. /// Reads in increments of 'self.ibs'.
/// The start of each ibs-sized read follows the previous one. /// 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_complete = 0;
let mut reads_partial = 0; let mut reads_partial = 0;
let mut bytes_total = 0; let mut bytes_total = 0;
@ -227,7 +232,7 @@ impl<R: Read> Input<R> {
/// Fills a given buffer. /// Fills a given buffer.
/// Reads in increments of 'self.ibs'. /// 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. /// 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_complete = 0;
let mut reads_partial = 0; let mut reads_partial = 0;
let mut base_idx = 0; let mut base_idx = 0;
@ -263,7 +268,7 @@ impl<R: Read> Input<R> {
/// interpreted as EOF. /// interpreted as EOF.
/// Note: This will not return unless the source (eventually) produces /// Note: This will not return unless the source (eventually) produces
/// enough bytes to meet target_len. /// 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; let mut base_idx = 0;
while base_idx < target_len { while base_idx < target_len {
base_idx += self.read(&mut buf[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 { 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 fsync(&mut self) -> io::Result<()>;
fn fdatasync(&mut self) -> io::Result<()>; fn fdatasync(&mut self) -> io::Result<()>;
} }
@ -286,7 +291,7 @@ struct Output<W: Write> {
} }
impl OutputTrait for Output<io::Stdout> { 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 obs = parseargs::parse_obs(matches)?;
let cflags = parseargs::parse_conv_flag_output(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 { let mut rstat = ReadStat {
reads_complete: 0, reads_complete: 0,
reads_partial: 0, reads_partial: 0,
@ -366,24 +371,30 @@ where
_, _,
) => break, ) => break,
(rstat_update, buf) => { (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; rstat += rstat_update;
wstat += wstat_update; wstat += wstat_update;
} }
}; };
// Update Prog // Update Prog
prog_tx.send(ProgUpdate { prog_tx
read_stat: rstat, .send(ProgUpdate {
write_stat: wstat, read_stat: rstat,
duration: start.elapsed(), write_stat: wstat,
})?; duration: start.elapsed(),
})
.map_err(|_| USimpleError::new(1, "failed to write output"))?;
} }
if self.cflags.fsync { if self.cflags.fsync {
self.fsync()?; self.fsync()
.map_err_context(|| "failed to write output".to_string())?;
} else if self.cflags.fdatasync { } else if self.cflags.fdatasync {
self.fdatasync()?; self.fdatasync()
.map_err_context(|| "failed to write output".to_string())?;
} }
match i.print_level { match i.print_level {
@ -439,7 +450,7 @@ fn make_linux_oflags(oflags: &OFlags) -> Option<libc::c_int> {
} }
impl OutputTrait for Output<File> { 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> { fn open_dst(path: &Path, cflags: &OConvFlags, oflags: &OFlags) -> Result<File, io::Error> {
let mut opts = OpenOptions::new(); let mut opts = OpenOptions::new();
opts.write(true) opts.write(true)
@ -461,11 +472,15 @@ impl OutputTrait for Output<File> {
let seek = parseargs::parse_seek_amt(&obs, &oflags, matches)?; let seek = parseargs::parse_seek_amt(&obs, &oflags, matches)?;
if let Some(fname) = matches.value_of(options::OUTFILE) { 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 { if let Some(amt) = seek {
let amt: u64 = amt.try_into()?; let amt: u64 = amt
dst.seek(io::SeekFrom::Start(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 }) Ok(Output { dst, obs, cflags })
@ -580,7 +595,7 @@ fn conv_block_unblock_helper<R: Read>(
mut buf: Vec<u8>, mut buf: Vec<u8>,
i: &mut Input<R>, i: &mut Input<R>,
rstat: &mut ReadStat, rstat: &mut ReadStat,
) -> Result<Vec<u8>, Box<dyn Error>> { ) -> Result<Vec<u8>, InternalError> {
// Local Predicate Fns ------------------------------------------------- // Local Predicate Fns -------------------------------------------------
fn should_block_then_conv<R: Read>(i: &Input<R>) -> bool { fn should_block_then_conv<R: Read>(i: &Input<R>) -> bool {
!i.non_ascii && i.cflags.block.is_some() !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. // by the parser before making it this far.
// Producing this error is an alternative to risking an unwrap call // Producing this error is an alternative to risking an unwrap call
// on 'cbs' if the required data is not provided. // 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. /// 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>( fn read_helper<R: Read>(i: &mut Input<R>, bsize: usize) -> UResult<(ReadStat, Vec<u8>)> {
i: &mut Input<R>,
bsize: usize,
) -> Result<(ReadStat, Vec<u8>), Box<dyn Error>> {
// Local Predicate Fns ----------------------------------------------- // Local Predicate Fns -----------------------------------------------
fn is_conv<R: Read>(i: &Input<R>) -> bool { fn is_conv<R: Read>(i: &Input<R>) -> bool {
i.cflags.ctable.is_some() i.cflags.ctable.is_some()
@ -693,8 +705,12 @@ fn read_helper<R: Read>(
// Read // Read
let mut buf = vec![BUF_INIT_BYTE; bsize]; let mut buf = vec![BUF_INIT_BYTE; bsize];
let mut rstat = match i.cflags.sync { let mut rstat = match i.cflags.sync {
Some(ch) => i.fill_blocks(&mut buf, ch)?, Some(ch) => i
_ => i.fill_consecutive(&mut buf)?, .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 // Return early if no data
if rstat.reads_complete == 0 && rstat.reads_partial == 0 { 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 acc
} }
macro_rules! unpack_or_rtn ( #[uucore_procs::gen_uumain]
($i:expr, $o:expr) => pub fn uumain(args: impl uucore::Args) -> UResult<()> {
{{
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 {
let dashed_args = args let dashed_args = args
.collect_str(InvalidEncodingHandling::Ignore) .collect_str(InvalidEncodingHandling::Ignore)
.accept_any() .accept_any()
@ -909,47 +905,30 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
//.after_help(TODO: Add note about multiplier strings here.) //.after_help(TODO: Add note about multiplier strings here.)
.get_matches_from(dashed_args); .get_matches_from(dashed_args);
let result = match ( match (
matches.is_present(options::INFILE), matches.is_present(options::INFILE),
matches.is_present(options::OUTFILE), matches.is_present(options::OUTFILE),
) { ) {
(true, true) => { (true, true) => {
let (i, o) = let i = Input::<File>::new(&matches)?;
unpack_or_rtn!(Input::<File>::new(&matches), Output::<File>::new(&matches)); let o = Output::<File>::new(&matches)?;
o.dd_out(i) o.dd_out(i)
} }
(false, true) => { (false, true) => {
let (i, o) = unpack_or_rtn!( let i = Input::<io::Stdin>::new(&matches)?;
Input::<io::Stdin>::new(&matches), let o = Output::<File>::new(&matches)?;
Output::<File>::new(&matches)
);
o.dd_out(i) o.dd_out(i)
} }
(true, false) => { (true, false) => {
let (i, o) = unpack_or_rtn!( let i = Input::<File>::new(&matches)?;
Input::<File>::new(&matches), let o = Output::<io::Stdout>::new(&matches)?;
Output::<io::Stdout>::new(&matches)
);
o.dd_out(i) o.dd_out(i)
} }
(false, false) => { (false, false) => {
let (i, o) = unpack_or_rtn!( let i = Input::<io::Stdin>::new(&matches)?;
Input::<io::Stdin>::new(&matches), let o = Output::<io::Stdout>::new(&matches)?;
Output::<io::Stdout>::new(&matches)
);
o.dd_out(i) 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 super::*;
use std::error::Error; use std::error::Error;
use uucore::error::UError;
pub type Matches = clap::ArgMatches<'static>; pub type Matches = clap::ArgMatches<'static>;
@ -79,6 +80,12 @@ impl std::fmt::Display for ParseError {
impl Error 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 /// Some flags specified as part of a conv=CONV[,CONV]... block
/// relate to the input file, others to the output file. /// relate to the input file, others to the output file.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -17,6 +17,7 @@ mod factor;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
pub use factor::*; pub use factor::*;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::UResult;
mod miller_rabin; mod miller_rabin;
pub mod numeric; 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 matches = uu_app().get_matches_from(args);
let stdout = stdout(); let stdout = stdout();
// We use a smaller buffer here to pass a gnu test. 4KiB appears to be the default pipe size for bash. // 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); show_error!("{}", e);
} }
0 Ok(())
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {

View file

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

View file

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

View file

@ -16,19 +16,11 @@ use std::fs::File;
use std::io::{stdin, stdout, Write}; use std::io::{stdin, stdout, Write};
use std::io::{BufReader, BufWriter, Read}; use std::io::{BufReader, BufWriter, Read};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
use self::linebreak::break_lines; use self::linebreak::break_lines;
use self::parasplit::ParagraphStream; use self::parasplit::ParagraphStream;
macro_rules! silent_unwrap(
($exp:expr) => (
match $exp {
Ok(_) => (),
Err(_) => ::std::process::exit(1),
}
)
);
mod linebreak; mod linebreak;
mod parasplit; mod parasplit;
@ -74,8 +66,9 @@ pub struct FmtOptions {
tabwidth: usize, tabwidth: usize,
} }
#[uucore_procs::gen_uumain]
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
pub fn uumain(args: impl uucore::Args) -> i32 { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage(); let usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args); 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>() { fmt_opts.width = match s.parse::<usize>() {
Ok(t) => t, Ok(t) => t,
Err(e) => { 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 { if fmt_opts.width > MAX_WIDTH {
crash!( return Err(USimpleError::new(
1, 1,
"invalid width: '{}': Numerical result out of range", format!(
fmt_opts.width "invalid width: '{}': Numerical result out of range",
); fmt_opts.width,
),
));
} }
fmt_opts.goal = cmp::min(fmt_opts.width * 94 / 100, fmt_opts.width - 3); 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>() { fmt_opts.goal = match s.parse::<usize>() {
Ok(t) => t, Ok(t) => t,
Err(e) => { 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) { if !matches.is_present(OPT_WIDTH) {
fmt_opts.width = cmp::max(fmt_opts.goal * 100 / 94, fmt_opts.goal + 3); fmt_opts.width = cmp::max(fmt_opts.goal * 100 / 94, fmt_opts.goal + 3);
} else if fmt_opts.goal > fmt_opts.width { } 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>() { fmt_opts.tabwidth = match s.parse::<usize>() {
Ok(t) => t, Ok(t) => t,
Err(e) => { 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 { for para_result in p_stream {
match para_result { match para_result {
Err(s) => { Err(s) => {
silent_unwrap!(ostream.write_all(s.as_bytes())); ostream
silent_unwrap!(ostream.write_all(b"\n")); .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 // 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> { 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 // indent
let p_indent = &para.indent_str[..]; let p_indent = &para.indent_str[..];
let p_indent_len = para.indent_len; 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() { let (w, w_len) = match p_words_words.next() {
Some(winfo) => (winfo.word, winfo.word_nchars), Some(winfo) => (winfo.word, winfo.word_nchars),
None => { None => {
silent_unwrap!(ostream.write_all(b"\n")); return ostream.write_all(b"\n");
return;
} }
}; };
// print the init, if it exists, and get its length // print the init, if it exists, and get its length
let p_init_len = w_len let p_init_len = w_len
+ if opts.crown || opts.tagged { + if opts.crown || opts.tagged {
// handle "init" portion // handle "init" portion
silent_unwrap!(ostream.write_all(para.init_str.as_bytes())); ostream.write_all(para.init_str.as_bytes())?;
para.init_len para.init_len
} else if !para.mail_header { } else if !para.mail_header {
// for non-(crown, tagged) that's the same as a normal indent // 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 p_indent_len
} else { } else {
// except that mail headers get no indent at all // except that mail headers get no indent at all
0 0
}; };
// write first word after writing init // 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? // does this paragraph require uniform spacing?
let uniform = para.mail_header || opts.uniform; 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 { if opts.quick || para.mail_header {
break_simple(p_words_words, &mut break_args); break_simple(p_words_words, &mut break_args)
} else { } 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 // break_simple implements a "greedy" breaking algorithm: print words until
// maxlength would be exceeded, then print a linebreak and indent and continue. // 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>) { fn break_simple<'a, T: Iterator<Item = &'a WordInfo<'a>>>(
iter.fold((args.init_len, false), |l, winfo| { 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) 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>( fn accum_words_simple<'a>(
args: &mut BreakArgs<'a>, args: &mut BreakArgs<'a>,
(l, prev_punct): (usize, bool), (l, prev_punct): (usize, bool),
winfo: &'a WordInfo<'a>, 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 // 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); 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 { if l + wlen + slen > args.opts.width {
write_newline(args.indent_str, args.ostream); write_newline(args.indent_str, args.ostream)?;
write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream); write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream)?;
(args.indent_len + winfo.word_nchars, winfo.ends_punct) Ok((args.indent_len + winfo.word_nchars, winfo.ends_punct))
} else { } else {
write_with_spaces(winfo.word, slen, args.ostream); write_with_spaces(winfo.word, slen, args.ostream)?;
(l + wlen + slen, winfo.ends_punct) 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>>>( fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>(
mut iter: T, mut iter: T,
args: &mut BreakArgs<'a>, args: &mut BreakArgs<'a>,
) { ) -> std::io::Result<()> {
// run the algorithm to get the breakpoints // run the algorithm to get the breakpoints
let breakpoints = find_kp_breakpoints(iter.clone(), args); let breakpoints = find_kp_breakpoints(iter.clone(), args);
// iterate through the breakpoints (note that breakpoints is in reverse break order, so we .rev() it // 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), (false, false),
|(mut prev_punct, mut fresh), &(next_break, break_before)| { |(mut prev_punct, mut fresh), &(next_break, break_before)| {
if fresh { 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 // at each breakpoint, keep emitting words until we find the word matching this breakpoint
for winfo in &mut iter { 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 { if winfo_ptr == next_break_ptr {
// OK, we found the matching word // OK, we found the matching word
if break_before { if break_before {
write_newline(args.indent_str, args.ostream); write_newline(args.indent_str, args.ostream)?;
write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream); write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream)?;
} else { } else {
// breaking after this word, so that means "fresh" is true for the next iteration // 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; fresh = true;
} }
break; break;
} else { } 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. // after the last linebreak, write out the rest of the final line.
for winfo in iter { for winfo in iter {
if fresh { if fresh {
write_newline(args.indent_str, args.ostream); write_newline(args.indent_str, args.ostream)?;
} }
let (slen, word) = slice_if_fresh( let (slen, word) = slice_if_fresh(
fresh, fresh,
@ -199,9 +206,9 @@ fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>(
); );
prev_punct = winfo.ends_punct; prev_punct = winfo.ends_punct;
fresh = false; 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> { struct LineBreak<'a> {
@ -494,17 +501,21 @@ fn slice_if_fresh(
} }
// Write a newline and add the indent. // Write a newline and add the indent.
fn write_newline(indent: &str, ostream: &mut BufWriter<Stdout>) { fn write_newline(indent: &str, ostream: &mut BufWriter<Stdout>) -> std::io::Result<()> {
silent_unwrap!(ostream.write_all(b"\n")); ostream.write_all(b"\n")?;
silent_unwrap!(ostream.write_all(indent.as_bytes())); ostream.write_all(indent.as_bytes())
} }
// Write the word, along with slen spaces. // 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 { if slen == 2 {
silent_unwrap!(ostream.write_all(b" ")); ostream.write_all(b" ")?;
} else if slen == 1 { } 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] [package]
name = "uu_fold" name = "uu_fold"
version = "0.0.8" version = "0.0.9"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "fold ~ (uutils) wrap each line of input" description = "fold ~ (uutils) wrap each line of input"
@ -16,8 +16,8 @@ path = "src/fold.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } uucore = { version=">=0.0.11", 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" }
[[bin]] [[bin]]
name = "fold" name = "fold"

View file

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

View file

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

View file

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

View file

@ -28,6 +28,7 @@ use sha1::Sha1;
use sha2::{Sha224, Sha256, Sha384, Sha512}; use sha2::{Sha224, Sha256, Sha384, Sha512};
use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256}; use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::error::Error;
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
use std::fs::File; use std::fs::File;
use std::io::{self, stdin, BufRead, BufReader, Read}; use std::io::{self, stdin, BufRead, BufReader, Read};
@ -35,6 +36,7 @@ use std::iter;
use std::num::ParseIntError; use std::num::ParseIntError;
use std::path::Path; use std::path::Path;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UError, UResult};
const NAME: &str = "hashsum"; const NAME: &str = "hashsum";
@ -274,7 +276,8 @@ fn is_valid_bit_num(arg: String) -> Result<(), String> {
.map_err(|e| format!("{}", e)) .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" // if there is no program name for some reason, default to "hashsum"
let program = args.next().unwrap_or_else(|| OsString::from(NAME)); let program = args.next().unwrap_or_else(|| OsString::from(NAME));
let binary_name = Path::new(&program) let binary_name = Path::new(&program)
@ -324,14 +327,9 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
warn, warn,
}; };
let res = match matches.values_of_os("FILE") { match matches.values_of_os("FILE") {
Some(files) => hashsum(opts, files), Some(files) => hashsum(opts, files),
None => hashsum(opts, iter::once(OsStr::new("-"))), 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)] #[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 where
I: Iterator<Item = &'a OsStr>, I: Iterator<Item = &'a OsStr>,
{ {
@ -470,7 +486,8 @@ where
stdin_buf = stdin(); stdin_buf = stdin();
Box::new(stdin_buf) as Box<dyn Read> Box::new(stdin_buf) as Box<dyn Read>
} else { } 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> Box::new(file_buf) as Box<dyn Read>
}); });
if options.check { if options.check {
@ -487,25 +504,24 @@ where
} else { } else {
"+".to_string() "+".to_string()
}; };
let gnu_re = crash_if_err!( let gnu_re = Regex::new(&format!(
1, r"^(?P<digest>[a-fA-F0-9]{}) (?P<binary>[ \*])(?P<fileName>.*)",
Regex::new(&format!( modifier,
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})",
let bsd_re = crash_if_err!( algorithm = options.algoname,
1, digest_size = modifier,
Regex::new(&format!( ))
r"^{algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{digest_size})", .map_err(|_| HashsumError::InvalidRegex)?;
algorithm = options.algoname,
digest_size = modifier,
))
);
let buffer = file; let buffer = file;
for (i, line) in buffer.lines().enumerate() { for (i, maybe_line) in buffer.lines().enumerate() {
let line = crash_if_err!(1, line); 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) { let (ck_filename, sum, binary_check) = match gnu_re.captures(&line) {
Some(caps) => ( Some(caps) => (
caps.name("fileName").unwrap().as_str(), caps.name("fileName").unwrap().as_str(),
@ -521,7 +537,7 @@ where
None => { None => {
bad_format += 1; bad_format += 1;
if options.strict { if options.strict {
return Err(1); return Err(HashsumError::InvalidFormat.into());
} }
if options.warn { if options.warn {
show_warning!( 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 mut ckf = BufReader::new(Box::new(f) as Box<dyn Read>);
let real_sum = crash_if_err!( let real_sum = digest_reader(
1, &mut options.digest,
digest_reader( &mut ckf,
&mut options.digest, binary_check,
&mut ckf, options.output_bits,
binary_check,
options.output_bits
)
) )
.map_err_context(|| "failed to read input".to_string())?
.to_ascii_lowercase(); .to_ascii_lowercase();
// FIXME: Filenames with newlines should be treated specially. // FIXME: Filenames with newlines should be treated specially.
// GNU appears to replace newlines by \n and backslashes by // GNU appears to replace newlines by \n and backslashes by
@ -568,15 +583,13 @@ where
} }
} }
} else { } else {
let sum = crash_if_err!( let sum = digest_reader(
1, &mut options.digest,
digest_reader( &mut file,
&mut options.digest, options.binary,
&mut file, options.output_bits,
options.binary, )
options.output_bits .map_err_context(|| "failed to read input".to_string())?;
)
);
if options.tag { if options.tag {
println!("{} ({}) = {}", options.algoname, filename.display(), sum); println!("{} ({}) = {}", options.algoname, filename.display(), sum);
} else { } 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 This particular file has about 170,000 lines, each of which is no longer
than 96 characters: than 96 characters:
$ wc -lL shakespeare.txt $ wc -lL shakespeare.txt
170592 96 shakespeare.txt 170592 96 shakespeare.txt
You could use files of different shapes and sizes to test the You could use files of different shapes and sizes to test the

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_id" name = "uu_id"
version = "0.0.8" version = "0.0.9"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "id ~ (uutils) display user and group information for USER" description = "id ~ (uutils) display user and group information for USER"
@ -16,8 +16,8 @@ path = "src/id.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore", features=["entries", "process"] } uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["entries", "process"] }
uucore_procs = { version=">=0.0.7", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.8", package="uucore_procs", path="../../uucore_procs" }
selinux = { version="0.2.1", optional = true } selinux = { version="0.2.1", optional = true }
[[bin]] [[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. // GNU's `id` does not support the flags: -p/-P/-A.
if matches.is_present(options::OPT_PASSWORD) { if matches.is_present(options::OPT_PASSWORD) {
// BSD's `id` ignores all but the first specified user // 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(()); return Ok(());
}; };
if matches.is_present(options::OPT_HUMAN_READABLE) { if matches.is_present(options::OPT_HUMAN_READABLE) {
@ -259,7 +259,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
return Ok(()); 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 { getuid() } else { geteuid() },
if state.rflag { getgid() } else { getegid() }, 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 = entries::get_groups_gnu(Some(gid)).unwrap();
let groups = if state.user_specified { 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 { } else {
groups.clone() groups.clone()
}; };
@ -453,7 +453,7 @@ pub fn uu_app() -> App<'static, 'static> {
fn pretty(possible_pw: Option<Passwd>) { fn pretty(possible_pw: Option<Passwd>) {
if let Some(p) = possible_pw { if let Some(p) = possible_pw {
print!("uid\t{}\ngroups\t", p.name()); print!("uid\t{}\ngroups\t", p.name);
println!( println!(
"{}", "{}",
p.belongs_to() p.belongs_to()
@ -466,10 +466,10 @@ fn pretty(possible_pw: Option<Passwd>) {
let login = cstr2cow!(getlogin() as *const _); let login = cstr2cow!(getlogin() as *const _);
let rid = getuid(); let rid = getuid();
if let Ok(p) = Passwd::locate(rid) { if let Ok(p) = Passwd::locate(rid) {
if login == p.name() { if login == p.name {
println!("login\t{}", login); println!("login\t{}", login);
} }
println!("uid\t{}", p.name()); println!("uid\t{}", p.name);
} else { } else {
println!("uid\t{}", rid); println!("uid\t{}", rid);
} }
@ -477,7 +477,7 @@ fn pretty(possible_pw: Option<Passwd>) {
let eid = getegid(); let eid = getegid();
if eid == rid { if eid == rid {
if let Ok(p) = Passwd::locate(eid) { if let Ok(p) = Passwd::locate(eid) {
println!("euid\t{}", p.name()); println!("euid\t{}", p.name);
} else { } else {
println!("euid\t{}", eid); println!("euid\t{}", eid);
} }
@ -486,7 +486,7 @@ fn pretty(possible_pw: Option<Passwd>) {
let rid = getgid(); let rid = getgid();
if rid != eid { if rid != eid {
if let Ok(g) = Group::locate(rid) { if let Ok(g) = Group::locate(rid) {
println!("euid\t{}", g.name()); println!("euid\t{}", g.name);
} else { } else {
println!("euid\t{}", rid); println!("euid\t{}", rid);
} }
@ -511,16 +511,16 @@ fn pline(possible_uid: Option<uid_t>) {
println!( println!(
"{}:{}:{}:{}:{}:{}:{}:{}:{}:{}", "{}:{}:{}:{}:{}:{}:{}:{}:{}:{}",
pw.name(), pw.name,
pw.user_passwd(), pw.user_passwd,
pw.uid(), pw.uid,
pw.gid(), pw.gid,
pw.user_access_class(), pw.user_access_class,
pw.passwd_change_time(), pw.passwd_change_time,
pw.expiration(), pw.expiration,
pw.user_info(), pw.user_info,
pw.user_dir(), pw.user_dir,
pw.user_shell() pw.user_shell
); );
} }
@ -531,13 +531,7 @@ fn pline(possible_uid: Option<uid_t>) {
println!( println!(
"{}:{}:{}:{}:{}:{}:{}", "{}:{}:{}:{}:{}:{}:{}",
pw.name(), pw.name, pw.user_passwd, pw.uid, pw.gid, pw.user_info, pw.user_dir, pw.user_shell
pw.user_passwd(),
pw.uid(),
pw.gid(),
pw.user_info(),
pw.user_dir(),
pw.user_shell()
); );
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -462,7 +462,7 @@ fn numbered_backup_path(path: &Path) -> PathBuf {
} }
fn existing_backup_path(path: &Path, suffix: &str) -> 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() { if test_path.exists() {
return numbered_backup_path(path); return numbered_backup_path(path);
} }

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -7,9 +7,6 @@
// spell-checker:ignore (methods) isnt // spell-checker:ignore (methods) isnt
#[macro_use]
extern crate uucore;
use std::{ use std::{
fs::File, fs::File,
io::{stdin, stdout, BufReader, Read, Stdout, Write}, io::{stdin, stdout, BufReader, Read, Stdout, Write},
@ -31,6 +28,7 @@ use crossterm::{
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError, UUsageError};
const BELL: &str = "\x07"; const BELL: &str = "\x07";
@ -51,7 +49,8 @@ pub mod options {
const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n"; 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 matches = uu_app().get_matches_from(args);
let mut buff = String::new(); let mut buff = String::new();
@ -65,32 +64,36 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let file = Path::new(file); let file = Path::new(file);
if file.is_dir() { if file.is_dir() {
terminal::disable_raw_mode().unwrap(); terminal::disable_raw_mode().unwrap();
show_usage_error!("{} is a directory.", file.quote()); return Err(UUsageError::new(
return 1; 1,
format!("{} is a directory.", file.quote()),
));
} }
if !file.exists() { if !file.exists() {
terminal::disable_raw_mode().unwrap(); terminal::disable_raw_mode().unwrap();
show_error!("cannot open {}: No such file or directory", file.quote()); return Err(USimpleError::new(
return 1; 1,
format!("cannot open {}: No such file or directory", file.quote()),
));
} }
if length > 1 { if length > 1 {
buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap())); buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap()));
} }
let mut reader = BufReader::new(File::open(file).unwrap()); let mut reader = BufReader::new(File::open(file).unwrap());
reader.read_to_string(&mut buff).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(); buff.clear();
} }
reset_term(&mut stdout); reset_term(&mut stdout);
} else if atty::isnt(atty::Stream::Stdin) { } else if atty::isnt(atty::Stream::Stdin) {
stdin().read_to_string(&mut buff).unwrap(); stdin().read_to_string(&mut buff).unwrap();
let mut stdout = setup_term(); let mut stdout = setup_term();
more(&buff, &mut stdout, None, silent); more(&buff, &mut stdout, None, silent)?;
reset_term(&mut stdout); reset_term(&mut stdout);
} else { } else {
show_usage_error!("bad usage"); return Err(UUsageError::new(1, "bad usage"));
} }
0 Ok(())
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {
@ -210,14 +213,14 @@ fn reset_term(stdout: &mut std::io::Stdout) {
#[inline(always)] #[inline(always)]
fn reset_term(_: &mut usize) {} 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 (cols, rows) = terminal::size().unwrap();
let lines = break_buff(buff, usize::from(cols)); let lines = break_buff(buff, usize::from(cols));
let mut pager = Pager::new(rows, lines, next_file, silent); let mut pager = Pager::new(rows, lines, next_file, silent);
pager.draw(stdout, None); pager.draw(stdout, None);
if pager.should_close() { if pager.should_close() {
return; return Ok(());
} }
loop { loop {
@ -244,7 +247,7 @@ fn more(buff: &str, stdout: &mut Stdout, next_file: Option<&str>, silent: bool)
modifiers: KeyModifiers::NONE, modifiers: KeyModifiers::NONE,
}) => { }) => {
if pager.should_close() { if pager.should_close() {
return; return Ok(());
} else { } else {
pager.page_down(); pager.page_down();
} }
@ -255,6 +258,22 @@ fn more(buff: &str, stdout: &mut Stdout, next_file: Option<&str>, silent: bool)
}) => { }) => {
pager.page_up(); 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) => { Event::Resize(col, row) => {
pager.page_resize(col, row); pager.page_resize(col, row);
} }
@ -301,6 +320,17 @@ impl<'a> Pager<'a> {
} }
fn page_down(&mut self) { 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()); 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()); 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. // TODO: Deal with column size changes.
fn page_resize(&mut self, _: u16, row: u16) { fn page_resize(&mut self, _: u16, row: u16) {
self.content_rows = row.saturating_sub(1); self.content_rows = row.saturating_sub(1);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
[package] [package]
name = "uu_numfmt" name = "uu_numfmt"
version = "0.0.8" version = "0.0.9"
authors = ["uutils developers"] authors = ["uutils developers"]
license = "MIT" license = "MIT"
description = "numfmt ~ (uutils) reformat NUMBER" description = "numfmt ~ (uutils) reformat NUMBER"
@ -16,12 +16,12 @@ path = "src/numfmt.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.10", package="uucore", path="../../uucore" } uucore = { version=">=0.0.11", 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" }
[[bin]] [[bin]]
name = "numfmt" name = "numfmt"
path = "src/main.rs" path = "src/main.rs"
[package.metadata.cargo-udeps.ignore] [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( let (prefix, field) = haystack.split_at(
haystack haystack
.find(|c: char| !c.is_whitespace()) .find(|c: char| !c.is_whitespace())
.unwrap_or_else(|| haystack.len()), .unwrap_or(haystack.len()),
); );
let (field, rest) = field.split_at( let (field, rest) = field.split_at(
field field
.find(|c: char| c.is_whitespace()) .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 }; self.s = if !rest.is_empty() { Some(rest) } else { None };
@ -220,16 +220,32 @@ fn format_string(
options: &NumfmtOptions, options: &NumfmtOptions,
implicit_padding: Option<isize>, implicit_padding: Option<isize>,
) -> Result<String> { ) -> 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( let number = transform_to(
transform_from(source, &options.transform.from)?, transform_from(source_without_suffix, &options.transform.from)?,
&options.transform.to, &options.transform.to,
options.round, 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) { Ok(match implicit_padding.unwrap_or(options.padding) {
0 => number, 0 => number_with_suffix,
p if p > 0 => format!("{:>padding$}", number, padding = p as usize), p if p > 0 => format!("{:>padding$}", number_with_suffix, padding = p as usize),
p => format!("{:<padding$}", number, padding = p.abs() 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!(" "); print!(" ");
&prefix[1..] &prefix[1..]
} else { } else {
&prefix prefix
}; };
let implicit_padding = if !empty_prefix && options.padding == 0 { let implicit_padding = if !empty_prefix && options.padding == 0 {

View file

@ -7,15 +7,13 @@
// spell-checker:ignore N'th M'th // spell-checker:ignore N'th M'th
#[macro_use]
extern crate uucore;
use crate::format::format_and_print; use crate::format::format_and_print;
use crate::options::*; use crate::options::*;
use crate::units::{Result, Unit}; use crate::units::{Result, Unit};
use clap::{crate_version, App, AppSettings, Arg, ArgMatches}; use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
use std::io::{BufRead, Write}; use std::io::{BufRead, Write};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError};
use uucore::ranges::Range; use uucore::ranges::Range;
pub mod format; pub mod format;
@ -144,6 +142,8 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
_ => unreachable!("Should be restricted by clap"), _ => unreachable!("Should be restricted by clap"),
}; };
let suffix = args.value_of(options::SUFFIX).map(|s| s.to_owned());
Ok(NumfmtOptions { Ok(NumfmtOptions {
transform, transform,
padding, padding,
@ -151,10 +151,12 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
fields, fields,
delimiter, delimiter,
round, 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 usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args); let matches = uu_app().usage(&usage[..]).get_matches_from(args);
@ -168,10 +170,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
match result { match result {
Err(e) => { Err(e) => {
std::io::stdout().flush().expect("error flushing stdout"); std::io::stdout().flush().expect("error flushing stdout");
show_error!("{}", e); // TODO Change `handle_args()` and `handle_stdin()` so that
1 // 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") .default_value("from-zero")
.possible_values(&["up", "down", "from-zero", "towards-zero", "nearest"]), .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)) .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