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

Merge pull request #1362 from Arcterus/cirrus

Test on FreeBSD using Cirrus CI and link to the Discord in the README
This commit is contained in:
Alex Lyon 2019-05-03 11:51:06 -07:00 committed by GitHub
commit 036dd81295
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 1246 additions and 836 deletions

View file

@ -20,13 +20,13 @@ matrix:
environment: environment:
global: global:
FEATURES: "generic" FEATURES: "windows"
BUILD_OPTIONS: "--no-default-features" BUILD_OPTIONS: "--no-default-features"
TEST_OPTIONS: "--no-default-features --no-fail-fast" TEST_OPTIONS: "--no-default-features --no-fail-fast"
matrix: matrix:
# minimum version # minimum version
- CHANNEL: 1.27.0 - CHANNEL: 1.31.0
ARCH: i686 ARCH: i686
ABI: msvc ABI: msvc
# "msvc" ABI # "msvc" ABI
@ -45,11 +45,11 @@ environment:
- CHANNEL: nightly - CHANNEL: nightly
ARCH: i686 ARCH: i686
ABI: msvc ABI: msvc
FEATURES: "generic nightly" FEATURES: "windows nightly"
- CHANNEL: nightly - CHANNEL: nightly
ARCH: x86_64 ARCH: x86_64
ABI: msvc ABI: msvc
FEATURES: "generic nightly" FEATURES: "windows nightly"
# "gnu" ABI # "gnu" ABI
- CHANNEL: stable - CHANNEL: stable
ARCH: i686 ARCH: i686

15
.cirrus.yml Normal file
View file

@ -0,0 +1,15 @@
freebsd_instance:
image: freebsd-12-0-release-amd64
task:
name: stable x86_64-unknown-freebsd
setup_script:
- pkg install -y curl gmake
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y
build_script:
- . $HOME/.cargo/env
- cargo build
test_script:
- . $HOME/.cargo/env
- cargo test

View file

@ -17,7 +17,7 @@ matrix:
- rust: nightly - rust: nightly
fast_finish: true fast_finish: true
include: include:
- rust: 1.27.0 - rust: 1.31.0
- rust: stable - rust: stable
os: linux os: linux
env: TEST_INSTALL=true env: TEST_INSTALL=true

745
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -35,7 +35,10 @@ unix = [
"uptime", "uptime",
"users", "users",
"who", "who",
"generic"
] ]
windows = ["generic"]
# Feature "fuchsia" contains the exclusive list of utilities # Feature "fuchsia" contains the exclusive list of utilities
# that can be compiled and run on Fuchsia. Should be built # that can be compiled and run on Fuchsia. Should be built
# with --no-default-features when selecting this feature. # with --no-default-features when selecting this feature.
@ -149,7 +152,7 @@ redox = [
] ]
test_unimplemented = [] test_unimplemented = []
nightly = [] nightly = []
default = ["generic", "unix"] default = ["unix"]
[workspace] [workspace]
@ -260,6 +263,7 @@ unindent = "0.1.3"
lazy_static = "1.3.0" lazy_static = "1.3.0"
[target.'cfg(unix)'.dev-dependencies] [target.'cfg(unix)'.dev-dependencies]
# FIXME: this should use the normal users crate, but it conflicts with the users utility
rust-users = { git = "https://github.com/uutils/rust-users" } rust-users = { git = "https://github.com/uutils/rust-users" }
unix_socket = "0.5.0" unix_socket = "0.5.0"

324
GNUmakefile Normal file
View file

@ -0,0 +1,324 @@
# Config options
PROFILE ?= debug
MULTICALL ?= n
INSTALL ?= install
ifneq (,$(filter install, $(MAKECMDGOALS)))
override PROFILE:=release
endif
PROFILE_CMD :=
ifeq ($(PROFILE),release)
PROFILE_CMD = --release
endif
RM := rm -rf
# Binaries
CARGO ?= cargo
CARGOFLAGS ?=
# Install directories
PREFIX ?= /usr/local
DESTDIR ?=
BINDIR ?= /bin
MANDIR ?= /man/man1
INSTALLDIR_BIN=$(DESTDIR)$(PREFIX)$(BINDIR)
INSTALLDIR_MAN=$(DESTDIR)$(PREFIX)/share/$(MANDIR)
$(shell test -d $(INSTALLDIR_MAN))
ifneq ($(.SHELLSTATUS),0)
override INSTALLDIR_MAN=$(DESTDIR)$(PREFIX)$(MANDIR)
endif
#prefix to apply to uutils binary and all tool binaries
PROG_PREFIX ?=
# This won't support any directory with spaces in its name, but you can just
# make a symlink without spaces that points to the directory.
BASEDIR ?= $(shell pwd)
BUILDDIR := $(BASEDIR)/target/${PROFILE}
PKG_BUILDDIR := $(BUILDDIR)/deps
DOCSDIR := $(BASEDIR)/docs
BUSYBOX_ROOT := $(BASEDIR)/tmp
BUSYBOX_VER := 1.24.1
BUSYBOX_SRC := $(BUSYBOX_ROOT)/busybox-$(BUSYBOX_VER)
# Possible programs
PROGS := \
base32 \
base64 \
basename \
cat \
cksum \
comm \
cp \
cut \
dircolors \
dirname \
echo \
env \
expand \
expr \
factor \
false \
fmt \
fold \
hashsum \
head \
join \
link \
ln \
ls \
mkdir \
mktemp \
more \
mv \
nl \
numfmt \
nproc \
od \
paste \
printenv \
printf \
ptx \
pwd \
readlink \
realpath \
relpath \
rm \
rmdir \
seq \
shred \
shuf \
sleep \
sort \
split \
sum \
sync \
tac \
tail \
tee \
test \
tr \
true \
truncate \
tsort \
unexpand \
uniq \
wc \
whoami \
yes
UNIX_PROGS := \
arch \
chgrp \
chmod \
chown \
chroot \
du \
groups \
hostid \
hostname \
id \
install \
kill \
logname \
mkfifo \
mknod \
nice \
nohup \
pathchk \
pinky \
stat \
stdbuf \
timeout \
touch \
tty \
uname \
unlink \
uptime \
users \
who
ifneq ($(OS),Windows_NT)
PROGS := $(PROGS) $(UNIX_PROGS)
endif
UTILS ?= $(PROGS)
# Programs with usable tests
TEST_PROGS := \
base32 \
base64 \
basename \
cat \
chgrp \
chmod \
chown \
cksum \
comm \
cp \
cut \
dircolors \
dirname \
echo \
env \
expr \
factor \
false \
fold \
hashsum \
head \
install \
link \
ln \
ls \
mkdir \
mktemp \
mv \
nl \
numfmt \
od \
paste \
pathchk \
pinky \
printf \
ptx \
pwd \
readlink \
realpath \
rm \
rmdir \
seq \
sort \
split \
stat \
stdbuf \
sum \
tac \
tail \
test \
touch \
tr \
true \
truncate \
tsort \
unexpand \
uniq \
unlink \
wc \
who
TESTS := \
$(sort $(filter $(UTILS),$(filter-out $(SKIP_UTILS),$(TEST_PROGS))))
TEST_NO_FAIL_FAST :=
TEST_SPEC_FEATURE :=
ifneq ($(SPEC),)
TEST_NO_FAIL_FAST :=--no-fail-fast
TEST_SPEC_FEATURE := test_unimplemented
endif
define TEST_BUSYBOX
test_busybox_$(1):
(cd $(BUSYBOX_SRC)/testsuite && bindir=$(BUILDDIR) ./runtest $(RUNTEST_ARGS) $(1) )
endef
# Output names
EXES := \
$(sort $(filter $(UTILS),$(filter-out $(SKIP_UTILS),$(PROGS))))
INSTALLEES := ${EXES}
ifeq (${MULTICALL}, y)
INSTALLEES := ${INSTALLEES} uutils
endif
# Shared library extension
SYSTEM := $(shell uname)
DYLIB_EXT :=
ifeq ($(SYSTEM),Linux)
DYLIB_EXT := so
DYLIB_FLAGS := -shared
endif
ifeq ($(SYSTEM),Darwin)
DYLIB_EXT := dylib
DYLIB_FLAGS := -dynamiclib -undefined dynamic_lookup
endif
all: build
do_install = $(INSTALL) ${1}
use_default := 1
build-pkgs:
ifneq (${MULTICALL}, y)
${CARGO} build ${CARGOFLAGS} ${PROFILE_CMD} $(foreach pkg,$(EXES),-p $(pkg))
endif
build-uutils:
${CARGO} build ${CARGOFLAGS} --features "${EXES}" ${PROFILE_CMD} --no-default-features
build-manpages:
cd $(DOCSDIR) && make man
build: build-uutils build-pkgs build-manpages
$(foreach test,$(filter-out $(SKIP_UTILS),$(PROGS)),$(eval $(call TEST_BUSYBOX,$(test))))
test:
${CARGO} test ${CARGOFLAGS} --features "$(TESTS) $(TEST_SPEC_FEATURE)" --no-default-features $(TEST_NO_FAIL_FAST)
busybox-src:
if [ ! -e $(BUSYBOX_SRC) ]; then \
mkdir -p $(BUSYBOX_ROOT); \
wget https://busybox.net/downloads/busybox-$(BUSYBOX_VER).tar.bz2 -P $(BUSYBOX_ROOT); \
tar -C $(BUSYBOX_ROOT) -xf $(BUSYBOX_ROOT)/busybox-$(BUSYBOX_VER).tar.bz2; \
fi; \
# This is a busybox-specific config file their test suite wants to parse.
$(BUILDDIR)/.config: $(BASEDIR)/.busybox-config
cp $< $@
# Test under the busybox testsuite
$(BUILDDIR)/busybox: busybox-src build-uutils $(BUILDDIR)/.config
cp $(BUILDDIR)/uutils $(BUILDDIR)/busybox; \
chmod +x $@;
ifeq ($(EXES),)
busytest:
else
busytest: $(BUILDDIR)/busybox $(addprefix test_busybox_,$(filter-out $(SKIP_UTILS),$(EXES)))
endif
clean:
$(RM) $(BUILDDIR)
cd $(DOCSDIR) && make clean
distclean: clean
$(CARGO) clean $(CARGOFLAGS) && $(CARGO) update $(CARGOFLAGS)
install: build
mkdir -p $(INSTALLDIR_BIN)
mkdir -p $(INSTALLDIR_MAN)
ifeq (${MULTICALL}, y)
$(INSTALL) $(BUILDDIR)/uutils $(INSTALLDIR_BIN)/$(PROG_PREFIX)uutils
cd $(INSTALLDIR_BIN) && $(foreach prog, $(filter-out uutils, $(INSTALLEES)), \
ln -fs $(PROG_PREFIX)uutils $(PROG_PREFIX)$(prog) &&) :
cat $(DOCSDIR)/_build/man/uutils.1 | gzip > $(INSTALLDIR_MAN)/$(PROG_PREFIX)uutils.1.gz
else
$(foreach prog, $(INSTALLEES), \
$(INSTALL) $(BUILDDIR)/$(prog) $(INSTALLDIR_BIN)/$(PROG_PREFIX)$(prog);)
endif
$(foreach man, $(filter $(INSTALLEES), $(basename $(notdir $(wildcard $(DOCSDIR)/_build/man/*)))), \
cat $(DOCSDIR)/_build/man/$(man).1 | gzip > $(INSTALLDIR_MAN)/$(PROG_PREFIX)$(man).1.gz &&) :
uninstall:
ifeq (${MULTICALL}, y)
rm -f $(addprefix $(INSTALLDIR_BIN)/,$(PROG_PREFIX)uutils)
endif
rm -f $(addprefix $(INSTALLDIR_MAN)/,$(PROG_PREFIX)uutils.1.gz)
rm -f $(addprefix $(INSTALLDIR_BIN)/$(PROG_PREFIX),$(PROGS))
rm -f $(addprefix $(INSTALLDIR_MAN)/$(PROG_PREFIX),$(addsuffix .1.gz,$(PROGS)))
.PHONY: all build build-uutils build-pkgs build-docs test distclean clean busytest install uninstall

329
Makefile
View file

@ -1,324 +1,5 @@
# Config options USEGNU=gmake $*
PROFILE ?= debug all:
MULTICALL ?= n @$(USEGNU)
INSTALL ?= install .DEFAULT:
ifneq (,$(filter install, $(MAKECMDGOALS))) @$(USEGNU)
override PROFILE:=release
endif
PROFILE_CMD :=
ifeq ($(PROFILE),release)
PROFILE_CMD = --release
endif
RM := rm -rf
# Binaries
CARGO ?= cargo
CARGOFLAGS ?=
# Install directories
PREFIX ?= /usr/local
DESTDIR ?=
BINDIR ?= /bin
MANDIR ?= /man/man1
INSTALLDIR_BIN=$(DESTDIR)$(PREFIX)$(BINDIR)
INSTALLDIR_MAN=$(DESTDIR)$(PREFIX)/share/$(MANDIR)
$(shell test -d $(INSTALLDIR_MAN))
ifneq ($(.SHELLSTATUS),0)
override INSTALLDIR_MAN=$(DESTDIR)$(PREFIX)$(MANDIR)
endif
#prefix to apply to uutils binary and all tool binaries
PROG_PREFIX ?=
# This won't support any directory with spaces in its name, but you can just
# make a symlink without spaces that points to the directory.
BASEDIR ?= $(shell pwd)
BUILDDIR := $(BASEDIR)/target/${PROFILE}
PKG_BUILDDIR := $(BUILDDIR)/deps
DOCSDIR := $(BASEDIR)/docs
BUSYBOX_ROOT := $(BASEDIR)/tmp
BUSYBOX_VER := 1.24.1
BUSYBOX_SRC := $(BUSYBOX_ROOT)/busybox-$(BUSYBOX_VER)
# Possible programs
PROGS := \
base32 \
base64 \
basename \
cat \
cksum \
comm \
cp \
cut \
dircolors \
dirname \
echo \
env \
expand \
expr \
factor \
false \
fmt \
fold \
hashsum \
head \
join \
link \
ln \
ls \
mkdir \
mktemp \
more \
mv \
nl \
numfmt \
nproc \
od \
paste \
printenv \
printf \
ptx \
pwd \
readlink \
realpath \
relpath \
rm \
rmdir \
seq \
shred \
shuf \
sleep \
sort \
split \
sum \
sync \
tac \
tail \
tee \
test \
tr \
true \
truncate \
tsort \
unexpand \
uniq \
wc \
whoami \
yes
UNIX_PROGS := \
arch \
chgrp \
chmod \
chown \
chroot \
du \
groups \
hostid \
hostname \
id \
install \
kill \
logname \
mkfifo \
mknod \
nice \
nohup \
pathchk \
pinky \
stat \
stdbuf \
timeout \
touch \
tty \
uname \
unlink \
uptime \
users \
who
ifneq ($(OS),Windows_NT)
PROGS := $(PROGS) $(UNIX_PROGS)
endif
UTILS ?= $(PROGS)
# Programs with usable tests
TEST_PROGS := \
base32 \
base64 \
basename \
cat \
chgrp \
chmod \
chown \
cksum \
comm \
cp \
cut \
dircolors \
dirname \
echo \
env \
expr \
factor \
false \
fold \
hashsum \
head \
install \
link \
ln \
ls \
mkdir \
mktemp \
mv \
nl \
numfmt \
od \
paste \
pathchk \
pinky \
printf \
ptx \
pwd \
readlink \
realpath \
rm \
rmdir \
seq \
sort \
split \
stat \
stdbuf \
sum \
tac \
tail \
test \
touch \
tr \
true \
truncate \
tsort \
unexpand \
uniq \
unlink \
wc \
who
TESTS := \
$(sort $(filter $(UTILS),$(filter-out $(SKIP_UTILS),$(TEST_PROGS))))
TEST_NO_FAIL_FAST :=
TEST_SPEC_FEATURE :=
ifneq ($(SPEC),)
TEST_NO_FAIL_FAST :=--no-fail-fast
TEST_SPEC_FEATURE := test_unimplemented
endif
define TEST_BUSYBOX
test_busybox_$(1):
(cd $(BUSYBOX_SRC)/testsuite && bindir=$(BUILDDIR) ./runtest $(RUNTEST_ARGS) $(1) )
endef
# Output names
EXES := \
$(sort $(filter $(UTILS),$(filter-out $(SKIP_UTILS),$(PROGS))))
INSTALLEES := ${EXES}
ifeq (${MULTICALL}, y)
INSTALLEES := ${INSTALLEES} uutils
endif
# Shared library extension
SYSTEM := $(shell uname)
DYLIB_EXT :=
ifeq ($(SYSTEM),Linux)
DYLIB_EXT := so
DYLIB_FLAGS := -shared
endif
ifeq ($(SYSTEM),Darwin)
DYLIB_EXT := dylib
DYLIB_FLAGS := -dynamiclib -undefined dynamic_lookup
endif
all: build
do_install = $(INSTALL) ${1}
use_default := 1
build-pkgs:
ifneq (${MULTICALL}, y)
${CARGO} build ${CARGOFLAGS} ${PROFILE_CMD} $(foreach pkg,$(EXES),-p $(pkg))
endif
build-uutils:
${CARGO} build ${CARGOFLAGS} --features "${EXES}" ${PROFILE_CMD} --no-default-features
build-manpages:
cd $(DOCSDIR) && make man
build: build-uutils build-pkgs build-manpages
$(foreach test,$(filter-out $(SKIP_UTILS),$(PROGS)),$(eval $(call TEST_BUSYBOX,$(test))))
test:
${CARGO} test ${CARGOFLAGS} --features "$(TESTS) $(TEST_SPEC_FEATURE)" --no-default-features $(TEST_NO_FAIL_FAST)
busybox-src:
if [ ! -e $(BUSYBOX_SRC) ]; then \
mkdir -p $(BUSYBOX_ROOT); \
wget https://busybox.net/downloads/busybox-$(BUSYBOX_VER).tar.bz2 -P $(BUSYBOX_ROOT); \
tar -C $(BUSYBOX_ROOT) -xf $(BUSYBOX_ROOT)/busybox-$(BUSYBOX_VER).tar.bz2; \
fi; \
# This is a busybox-specific config file their test suite wants to parse.
$(BUILDDIR)/.config: $(BASEDIR)/.busybox-config
cp $< $@
# Test under the busybox testsuite
$(BUILDDIR)/busybox: busybox-src build-uutils $(BUILDDIR)/.config
cp $(BUILDDIR)/uutils $(BUILDDIR)/busybox; \
chmod +x $@;
ifeq ($(EXES),)
busytest:
else
busytest: $(BUILDDIR)/busybox $(addprefix test_busybox_,$(filter-out $(SKIP_UTILS),$(EXES)))
endif
clean:
$(RM) $(BUILDDIR)
cd $(DOCSDIR) && make clean
distclean: clean
$(CARGO) clean $(CARGOFLAGS) && $(CARGO) update $(CARGOFLAGS)
install: build
mkdir -p $(INSTALLDIR_BIN)
mkdir -p $(INSTALLDIR_MAN)
ifeq (${MULTICALL}, y)
$(INSTALL) $(BUILDDIR)/uutils $(INSTALLDIR_BIN)/$(PROG_PREFIX)uutils
cd $(INSTALLDIR_BIN) && $(foreach prog, $(filter-out uutils, $(INSTALLEES)), \
ln -fs $(PROG_PREFIX)uutils $(PROG_PREFIX)$(prog) &&) :
cat $(DOCSDIR)/_build/man/uutils.1 | gzip > $(INSTALLDIR_MAN)/$(PROG_PREFIX)uutils.1.gz
else
$(foreach prog, $(INSTALLEES), \
$(INSTALL) $(BUILDDIR)/$(prog) $(INSTALLDIR_BIN)/$(PROG_PREFIX)$(prog);)
endif
$(foreach man, $(filter $(INSTALLEES), $(basename $(notdir $(wildcard $(DOCSDIR)/_build/man/*)))), \
cat $(DOCSDIR)/_build/man/$(man).1 | gzip > $(INSTALLDIR_MAN)/$(PROG_PREFIX)$(man).1.gz &&) :
uninstall:
ifeq (${MULTICALL}, y)
rm -f $(addprefix $(INSTALLDIR_BIN)/,$(PROG_PREFIX)uutils)
endif
rm -f $(addprefix $(INSTALLDIR_MAN)/,$(PROG_PREFIX)uutils.1.gz)
rm -f $(addprefix $(INSTALLDIR_BIN)/$(PROG_PREFIX),$(PROGS))
rm -f $(addprefix $(INSTALLDIR_MAN)/$(PROG_PREFIX),$(addsuffix .1.gz,$(PROGS)))
.PHONY: all build build-uutils build-pkgs build-docs test distclean clean busytest install uninstall

View file

@ -1,12 +1,17 @@
uutils coreutils uutils coreutils
================ ================
[![Discord](https://img.shields.io/badge/discord-join-7289DA.svg?logo=discord&longCache=true&style=flat)](https://discord.gg/wQVJbvJ)
[![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/uutils/coreutils/blob/master/LICENSE) [![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/uutils/coreutils/blob/master/LICENSE)
[![Build Status](https://api.travis-ci.org/uutils/coreutils.svg?branch=master)](https://travis-ci.org/uutils/coreutils) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fuutils%2Fcoreutils.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fuutils%2Fcoreutils?ref=badge_shield)
[![Build status](https://ci.appveyor.com/api/projects/status/787ltcxgy86r20le?svg=true)](https://ci.appveyor.com/project/Arcterus/coreutils)
[![LOC](https://tokei.rs/b1/github/uutils/coreutils?category=code)](https://github.com/Aaronepower/tokei) [![LOC](https://tokei.rs/b1/github/uutils/coreutils?category=code)](https://github.com/Aaronepower/tokei)
[![dependency status](https://deps.rs/repo/github/uutils/coreutils/status.svg)](https://deps.rs/repo/github/uutils/coreutils) [![dependency status](https://deps.rs/repo/github/uutils/coreutils/status.svg)](https://deps.rs/repo/github/uutils/coreutils)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fuutils%2Fcoreutils.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fuutils%2Fcoreutils?ref=badge_shield)
[![Build Status](https://api.travis-ci.org/uutils/coreutils.svg?branch=master)](https://travis-ci.org/uutils/coreutils)
[![Build Status (Windows)](https://ci.appveyor.com/api/projects/status/787ltcxgy86r20le?svg=true)](https://ci.appveyor.com/project/Arcterus/coreutils)
[![Build Status (FreeBSD)](https://api.cirrus-ci.com/github/uutils/coreutils.svg)](https://cirrus-ci.com/github/uutils/coreutils/master)
-----------------------------------------------
uutils is an attempt at writing universal (as in cross-platform) CLI uutils is an attempt at writing universal (as in cross-platform) CLI
utils in [Rust](http://www.rust-lang.org). This repo is to aggregate the GNU utils in [Rust](http://www.rust-lang.org). This repo is to aggregate the GNU
@ -28,15 +33,13 @@ Requirements
* Rust (`cargo`, `rustc`) * Rust (`cargo`, `rustc`)
* GNU Make (required to build documentation) * GNU Make (required to build documentation)
* CMake (Unix; used by Oniguruma, which is required for `expr`)
* NMake (Windows; used by Oniguruma, which is required for `expr`)
* [Sphinx](http://www.sphinx-doc.org/) (for documentation) * [Sphinx](http://www.sphinx-doc.org/) (for documentation)
* gzip (for installing documentation) * gzip (for installing documentation)
### 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.27.0`. The current oldest supported version of the Rust compiler is `1.31.0`.
On both Windows and Redox, only the nightly version is tested currently. On both Windows and Redox, only the nightly version is tested currently.
@ -67,7 +70,7 @@ Unix-like platforms at the moment, to build on Windows, you must do the
following: following:
```bash ```bash
# to keep debug information, compile without --release # to keep debug information, compile without --release
$ cargo build --release --no-default-features --features generic $ cargo build --release --no-default-features --features windows
``` ```
If you don't want to build every utility available on your platform into the If you don't want to build every utility available on your platform into the

View file

@ -16,7 +16,7 @@ pub fn main() {
if val == "1" && key.starts_with(feature_prefix) { if val == "1" && key.starts_with(feature_prefix) {
let krate = key[feature_prefix.len()..].to_lowercase(); let krate = key[feature_prefix.len()..].to_lowercase();
match krate.as_ref() { match krate.as_ref() {
"default" | "unix" | "redox" | "redox_generic" | "fuchsia" | "generic" "default" | "unix" | "redox" | "redox_generic" | "fuchsia" | "generic" | "windows"
| "nightly" | "test_unimplemented" => continue, | "nightly" | "test_unimplemented" => continue,
_ => {} _ => {}
} }

20
docs/GNUmakefile Normal file
View file

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = uutils
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help GNUmakefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: GNUmakefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View file

@ -1,20 +1,5 @@
# Minimal makefile for Sphinx documentation USEGNU=gmake $*
# all:
@$(USEGNU)
# You can set these variables from the command line. .DEFAULT:
SPHINXOPTS = @$(USEGNU)
SPHINXBUILD = sphinx-build
SPHINXPROJ = uutils
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View file

@ -9,7 +9,7 @@ name = "uu_arch"
path = "arch.rs" path = "arch.rs"
[dependencies] [dependencies]
platform-info = { git = "https://github.com/uutils/platform-info" } platform-info = "0.0.1"
uucore = { path = "../uucore" } uucore = { path = "../uucore" }
[[bin]] [[bin]]

View file

@ -196,7 +196,7 @@ impl Chmoder {
} }
}; };
match self.fmode { match self.fmode {
Some(mode) => try!(self.change_file(fperm, mode, file, name)), Some(mode) => self.change_file(fperm, mode, file, name)?,
None => { None => {
let cmode_unwrapped = self.cmode.clone().unwrap(); let cmode_unwrapped = self.cmode.clone().unwrap();
for mode in cmode_unwrapped.split(',') { for mode in cmode_unwrapped.split(',') {
@ -209,7 +209,7 @@ impl Chmoder {
}; };
match result { match result {
Ok(mode) => { Ok(mode) => {
try!(self.change_file(fperm, mode, file, name)); self.change_file(fperm, mode, file, name)?;
fperm = mode; fperm = mode;
} }
Err(f) => { Err(f) => {

View file

@ -58,7 +58,7 @@ fn cksum(fname: &str) -> io::Result<(u32, usize)> {
let mut rd: Box<Read> = match fname { let mut rd: Box<Read> = match fname {
"-" => Box::new(stdin()), "-" => Box::new(stdin()),
_ => { _ => {
file = try!(File::open(&Path::new(fname))); file = File::open(&Path::new(fname))?;
Box::new(BufReader::new(file)) Box::new(BufReader::new(file))
} }
}; };

View file

@ -117,7 +117,7 @@ fn open_file(name: &str) -> io::Result<LineReader> {
match name { match name {
"-" => Ok(LineReader::Stdin(stdin())), "-" => Ok(LineReader::Stdin(stdin())),
_ => { _ => {
let f = try!(File::open(&Path::new(name))); let f = File::open(&Path::new(name))?;
Ok(LineReader::FileIn(BufReader::new(f))) Ok(LineReader::FileIn(BufReader::new(f)))
} }
} }

View file

@ -1179,8 +1179,8 @@ pub fn localize_to_target(root: &Path, source: &Path, target: &Path) -> CopyResu
pub fn paths_refer_to_same_file(p1: &Path, p2: &Path) -> io::Result<bool> { pub fn paths_refer_to_same_file(p1: &Path, p2: &Path) -> io::Result<bool> {
// We have to take symlinks and relative paths into account. // We have to take symlinks and relative paths into account.
let pathbuf1 = try!(canonicalize(p1, CanonicalizeMode::Normal)); let pathbuf1 = canonicalize(p1, CanonicalizeMode::Normal)?;
let pathbuf2 = try!(canonicalize(p2, CanonicalizeMode::Normal)); let pathbuf2 = canonicalize(p2, CanonicalizeMode::Normal)?;
Ok(pathbuf1 == pathbuf2) Ok(pathbuf1 == pathbuf2)
} }

View file

@ -520,7 +520,7 @@ impl<'a> WordSplit<'a> {
impl<'a> WordSplit<'a> { impl<'a> WordSplit<'a> {
fn new<'b>(opts: &'b FmtOptions, string: &'b str) -> WordSplit<'b> { fn new<'b>(opts: &'b FmtOptions, string: &'b str) -> WordSplit<'b> {
// wordsplits *must* start at a non-whitespace character // wordsplits *must* start at a non-whitespace character
let trim_string = string.trim_left(); let trim_string = string.trim_start();
WordSplit { WordSplit {
opts, opts,
string: trim_string, string: trim_string,

View file

@ -269,9 +269,9 @@ fn link(src: &PathBuf, dst: &PathBuf, settings: &Settings) -> Result<()> {
if !read_yes() { if !read_yes() {
return Ok(()); return Ok(());
} }
try!(fs::remove_file(dst)) fs::remove_file(dst)?
} }
OverwriteMode::Force => try!(fs::remove_file(dst)), OverwriteMode::Force => fs::remove_file(dst)?,
}; };
backup_path = match settings.backup { backup_path = match settings.backup {
@ -281,14 +281,14 @@ fn link(src: &PathBuf, dst: &PathBuf, settings: &Settings) -> Result<()> {
BackupMode::ExistingBackup => Some(existing_backup_path(dst, &settings.suffix)), BackupMode::ExistingBackup => Some(existing_backup_path(dst, &settings.suffix)),
}; };
if let Some(ref p) = backup_path { if let Some(ref p) = backup_path {
try!(fs::rename(dst, p)); fs::rename(dst, p)?;
} }
} }
if settings.symbolic { if settings.symbolic {
try!(symlink(src, dst)); symlink(src, dst)?;
} else { } else {
try!(fs::hard_link(src, dst)); fs::hard_link(src, dst)?;
} }
if settings.verbose { if settings.verbose {

View file

@ -12,7 +12,7 @@ path = "ls.rs"
getopts = "0.2.18" getopts = "0.2.18"
number_prefix = "0.2.8" number_prefix = "0.2.8"
term_grid = "0.1.5" term_grid = "0.1.5"
termsize = "0.1.4" termsize = "0.1.6"
time = "0.1.40" time = "0.1.40"
lazy_static = "1.0.1" lazy_static = "1.0.1"
unicode-width = "0.1.5" unicode-width = "0.1.5"

View file

@ -33,15 +33,15 @@ impl fmt::Debug for FormatWriter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
&FormatWriter::IntWriter(ref p) => { &FormatWriter::IntWriter(ref p) => {
try!(f.write_str("IntWriter:")); f.write_str("IntWriter:")?;
fmt::Pointer::fmt(p, f) fmt::Pointer::fmt(p, f)
} }
&FormatWriter::FloatWriter(ref p) => { &FormatWriter::FloatWriter(ref p) => {
try!(f.write_str("FloatWriter:")); f.write_str("FloatWriter:")?;
fmt::Pointer::fmt(p, f) fmt::Pointer::fmt(p, f)
} }
&FormatWriter::MultibyteWriter(ref p) => { &FormatWriter::MultibyteWriter(ref p) => {
try!(f.write_str("MultibyteWriter:")); f.write_str("MultibyteWriter:")?;
fmt::Pointer::fmt(&(*p as *const ()), f) fmt::Pointer::fmt(&(*p as *const ()), f)
} }
} }

View file

@ -94,7 +94,7 @@ fn paste(filenames: Vec<String>, serial: bool, delimiters: String) {
match file.read_line(&mut line) { match file.read_line(&mut line) {
Ok(0) => break, Ok(0) => break,
Ok(_) => { Ok(_) => {
output.push_str(line.trim_right()); output.push_str(line.trim_end());
output.push_str(&delimiters[delim_count % delimiters.len()]); output.push_str(&delimiters[delim_count % delimiters.len()]);
} }
Err(e) => crash!(1, "{}", e.to_string()), Err(e) => crash!(1, "{}", e.to_string()),
@ -118,7 +118,7 @@ fn paste(filenames: Vec<String>, serial: bool, delimiters: String) {
eof[i] = true; eof[i] = true;
eof_count += 1; eof_count += 1;
} }
Ok(_) => output.push_str(line.trim_right()), Ok(_) => output.push_str(line.trim_end()),
Err(e) => crash!(1, "{}", e.to_string()), Err(e) => crash!(1, "{}", e.to_string()),
} }
} }

View file

@ -416,7 +416,7 @@ fn format_tex_line(config: &Config, word_ref: &WordRef, line: &str, reference: &
output.push_str(&format!("\\{} ", config.macro_name)); output.push_str(&format!("\\{} ", config.macro_name));
let all_before = if config.input_ref { let all_before = if config.input_ref {
let before = &line[0..word_ref.position]; let before = &line[0..word_ref.position];
adjust_tex_str(before.trim().trim_left_matches(reference)) adjust_tex_str(before.trim().trim_start_matches(reference))
} else { } else {
adjust_tex_str(&line[0..word_ref.position]) adjust_tex_str(&line[0..word_ref.position])
}; };
@ -447,7 +447,7 @@ fn format_roff_line(config: &Config, word_ref: &WordRef, line: &str, reference:
output.push_str(&format!(".{}", config.macro_name)); output.push_str(&format!(".{}", config.macro_name));
let all_before = if config.input_ref { let all_before = if config.input_ref {
let before = &line[0..word_ref.position]; let before = &line[0..word_ref.position];
adjust_roff_str(before.trim().trim_left_matches(reference)) adjust_roff_str(before.trim().trim_start_matches(reference))
} else { } else {
adjust_roff_str(&line[0..word_ref.position]) adjust_roff_str(&line[0..word_ref.position])
}; };

View file

@ -491,24 +491,24 @@ fn do_pass(
given_file_size: Option<u64>, given_file_size: Option<u64>,
exact: bool, exact: bool,
) -> Result<(), io::Error> { ) -> Result<(), io::Error> {
try!(file.seek(SeekFrom::Start(0))); file.seek(SeekFrom::Start(0))?;
// Use the given size or the whole file if not specified // Use the given size or the whole file if not specified
let size: u64 = given_file_size.unwrap_or(try!(get_file_size(path))); let size: u64 = given_file_size.unwrap_or(get_file_size(path)?);
let generator = BytesGenerator::new(size, generator_type, exact); let generator = BytesGenerator::new(size, generator_type, exact);
for block in generator { for block in generator {
try!(file.write_all(&*block)); file.write_all(&*block)?;
} }
try!(file.sync_data()); file.sync_data()?;
Ok(()) Ok(())
} }
fn get_file_size(path: &Path) -> Result<u64, io::Error> { fn get_file_size(path: &Path) -> Result<u64, io::Error> {
let size: u64 = try!(fs::metadata(path)).len(); let size: u64 = fs::metadata(path)?.len();
Ok(size) Ok(size)
} }
@ -574,7 +574,7 @@ fn do_remove(path: &Path, orig_filename: &str, verbose: bool) -> Result<(), io::
let renamed_path: Option<PathBuf> = wipe_name(&path, verbose); let renamed_path: Option<PathBuf> = wipe_name(&path, verbose);
match renamed_path { match renamed_path {
Some(rp) => { Some(rp) => {
try!(fs::remove_file(rp)); fs::remove_file(rp)?;
} }
None => (), None => (),
} }

View file

@ -21,28 +21,21 @@ pub trait BirthTime {
use std::fs::Metadata; use std::fs::Metadata;
impl BirthTime for Metadata { impl BirthTime for Metadata {
#[cfg(feature = "nightly")]
fn pretty_birth(&self) -> String { fn pretty_birth(&self) -> String {
self.created() self.created()
.map(|t| t.elapsed().unwrap()) .ok()
.and_then(|t| t.elapsed().ok())
.map(|e| pretty_time(e.as_secs() as i64, e.subsec_nanos() as i64)) .map(|e| pretty_time(e.as_secs() as i64, e.subsec_nanos() as i64))
.unwrap_or("-".to_owned()) .unwrap_or("-".to_owned())
} }
#[cfg(not(feature = "nightly"))]
fn pretty_birth(&self) -> String {
"-".to_owned()
}
#[cfg(feature = "nightly")]
fn birth(&self) -> String { fn birth(&self) -> String {
self.created() self.created()
.map(|t| t.elapsed().unwrap()) .ok()
.and_then(|t| t.elapsed().ok())
.map(|e| format!("{}", e.as_secs())) .map(|e| format!("{}", e.as_secs()))
.unwrap_or("0".to_owned()) .unwrap_or("0".to_owned())
} }
#[cfg(not(feature = "nightly"))]
fn birth(&self) -> String {
"0".to_owned()
}
} }
#[macro_export] #[macro_export]
@ -94,7 +87,6 @@ pub fn pretty_access(mode: mode_t) -> String {
S_IFLNK => 'l', S_IFLNK => 'l',
S_IFSOCK => 's', S_IFSOCK => 's',
// TODO: Other file types // TODO: Other file types
// See coreutils/gnulib/lib/filemode.c
_ => '?', _ => '?',
}); });
@ -151,19 +143,19 @@ use std::convert::{AsRef, From};
use std::error::Error; use std::error::Error;
use std::io::Error as IOError; use std::io::Error as IOError;
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "macos", target_os = "android", target_os = "freebsd"))]
use libc::statfs as Sstatfs; use libc::statfs as Sstatfs;
// #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "openbsd", target_os = "bitrig", target_os = "dragonfly"))] #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "openbsd", target_os = "bitrig", target_os = "dragonfly"))]
// use self::libc::statvfs as Sstatfs; use libc::statvfs as Sstatfs;
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "macos", target_os = "android", target_os = "freebsd"))]
use libc::statfs as statfs_fn; use libc::statfs as statfs_fn;
// #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "openbsd", target_os = "bitrig", target_os = "dragonfly"))] #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "openbsd", target_os = "bitrig", target_os = "dragonfly"))]
// use self::libc::statvfs as statfs_fn; use libc::statvfs as statfs_fn;
pub trait FsMeta { pub trait FsMeta {
fn fs_type(&self) -> i64; fn fs_type(&self) -> i64;
fn iosize(&self) -> i64; fn iosize(&self) -> u64;
fn blksize(&self) -> i64; fn blksize(&self) -> i64;
fn total_blocks(&self) -> u64; fn total_blocks(&self) -> u64;
fn free_blocks(&self) -> u64; fn free_blocks(&self) -> u64;
@ -171,7 +163,7 @@ pub trait FsMeta {
fn total_fnodes(&self) -> u64; fn total_fnodes(&self) -> u64;
fn free_fnodes(&self) -> u64; fn free_fnodes(&self) -> u64;
fn fsid(&self) -> u64; fn fsid(&self) -> u64;
fn namelen(&self) -> i64; fn namelen(&self) -> u64;
} }
impl FsMeta for Sstatfs { impl FsMeta for Sstatfs {
@ -193,22 +185,28 @@ impl FsMeta for Sstatfs {
fn free_fnodes(&self) -> u64 { fn free_fnodes(&self) -> u64 {
self.f_ffree as u64 self.f_ffree as u64
} }
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))]
fn fs_type(&self) -> i64 { fn fs_type(&self) -> i64 {
self.f_type as i64 self.f_type as i64
} }
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "freebsd")))]
fn fs_type(&self) -> i64 {
// FIXME: statvfs doesn't have an equivalent, so we need to do something else
unimplemented!()
}
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
fn iosize(&self) -> i64 { fn iosize(&self) -> u64 {
self.f_frsize as i64 self.f_frsize as u64
} }
#[cfg(target_os = "macos")] #[cfg(any(target_os = "macos", target_os = "freebsd"))]
fn iosize(&self) -> i64 { fn iosize(&self) -> u64 {
self.f_iosize as i64 self.f_iosize as u64
} }
// FIXME: // XXX: dunno if this is right
#[cfg(not(any(target_os = "macos", target_os = "linux")))] #[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "linux")))]
fn iosize(&self) -> i64 { fn iosize(&self) -> u64 {
0 self.f_bsize as u64
} }
// Linux, SunOS, HP-UX, 4.4BSD, FreeBSD have a system call statfs() that returns // Linux, SunOS, HP-UX, 4.4BSD, FreeBSD have a system call statfs() that returns
@ -217,29 +215,32 @@ impl FsMeta for Sstatfs {
// //
// Solaris, Irix and POSIX have a system call statvfs(2) that returns a // Solaris, Irix and POSIX have a system call statvfs(2) that returns a
// struct statvfs, containing an unsigned long f_fsid // struct statvfs, containing an unsigned long f_fsid
#[cfg(any(target_os = "macos", target_os = "linux"))] #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "linux"))]
fn fsid(&self) -> u64 { fn fsid(&self) -> u64 {
let f_fsid: &[u32; 2] = unsafe { transmute(&self.f_fsid) }; let f_fsid: &[u32; 2] = unsafe { transmute(&self.f_fsid) };
(f_fsid[0] as u64) << 32 | f_fsid[1] as u64 (f_fsid[0] as u64) << 32 | f_fsid[1] as u64
} }
// FIXME: #[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "linux")))]
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
fn fsid(&self) -> u64 { fn fsid(&self) -> u64 {
0 self.f_fsid as u64
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
fn namelen(&self) -> i64 { fn namelen(&self) -> u64 {
self.f_namelen as i64 self.f_namelen as u64
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
fn namelen(&self) -> i64 { fn namelen(&self) -> u64 {
1024 1024
} }
// FIXME: #[cfg(target_os = "freebsd")]
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
fn namelen(&self) -> u64 { fn namelen(&self) -> u64 {
0 self.f_namemax as u64
}
// XXX: should everything just use statvfs?
#[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "linux")))]
fn namelen(&self) -> u64 {
self.f_namemax as u64
} }
} }

View file

@ -468,7 +468,7 @@ impl Stater {
let default_tokens = if fmtstr.is_empty() { let default_tokens = if fmtstr.is_empty() {
Stater::generate_tokens(&Stater::default_fmt(showfs, terse, false), use_printf).unwrap() Stater::generate_tokens(&Stater::default_fmt(showfs, terse, false), use_printf).unwrap()
} else { } else {
try!(Stater::generate_tokens(&fmtstr, use_printf)) Stater::generate_tokens(&fmtstr, use_printf)?
}; };
let default_dev_tokens = let default_dev_tokens =
Stater::generate_tokens(&Stater::default_fmt(showfs, terse, true), use_printf).unwrap(); Stater::generate_tokens(&Stater::default_fmt(showfs, terse, true), use_printf).unwrap();

View file

@ -5,16 +5,21 @@ use std::path::Path;
#[path = "../../mkmain.rs"] #[path = "../../mkmain.rs"]
mod mkmain; mod mkmain;
#[cfg(target_os = "linux")] #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows")))]
mod platform { mod platform {
pub const DYLIB_EXT: &str = ".so"; pub const DYLIB_EXT: &str = ".so";
} }
#[cfg(target_os = "macos")] #[cfg(any(target_os = "macos", target_os = "ios"))]
mod platform { mod platform {
pub const DYLIB_EXT: &str = ".dylib"; pub const DYLIB_EXT: &str = ".dylib";
} }
#[cfg(target_os = "windows")]
mod platform {
pub const DYLIB_EXT: &str = ".dll";
}
fn main() { fn main() {
mkmain::main(); mkmain::main();

View file

@ -51,7 +51,7 @@ enum OkMsg {
Version, Version,
} }
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd", target_os = "dragonflybsd"))]
fn preload_strings() -> (&'static str, &'static str) { fn preload_strings() -> (&'static str, &'static str) {
("LD_PRELOAD", "so") ("LD_PRELOAD", "so")
} }
@ -61,7 +61,7 @@ fn preload_strings() -> (&'static str, &'static str) {
("DYLD_LIBRARY_PATH", "dylib") ("DYLD_LIBRARY_PATH", "dylib")
} }
#[cfg(not(any(target_os = "linux", target_os = "macos")))] #[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd", target_os = "dragonflybsd", target_os = "macos")))]
fn preload_strings() -> (&'static str, &'static str) { fn preload_strings() -> (&'static str, &'static str) {
crash!(1, "Command not supported for this operating system!") crash!(1, "Command not supported for this operating system!")
} }
@ -93,8 +93,8 @@ fn print_usage(opts: &Options) {
} }
fn parse_size(size: &str) -> Option<u64> { fn parse_size(size: &str) -> Option<u64> {
let ext = size.trim_left_matches(|c: char| c.is_digit(10)); let ext = size.trim_start_matches(|c: char| c.is_digit(10));
let num = size.trim_right_matches(|c: char| c.is_alphabetic()); let num = size.trim_end_matches(|c: char| c.is_alphabetic());
let mut recovered = num.to_owned(); let mut recovered = num.to_owned();
recovered.push_str(ext); recovered.push_str(ext);
if recovered != size { if recovered != size {
@ -172,9 +172,9 @@ fn parse_options(
return Ok(OkMsg::Version); return Ok(OkMsg::Version);
} }
let mut modified = false; let mut modified = false;
options.stdin = try!(check_option(&matches, "input", &mut modified).ok_or(ErrMsg::Fatal)); options.stdin = check_option(&matches, "input", &mut modified).ok_or(ErrMsg::Fatal)?;
options.stdout = try!(check_option(&matches, "output", &mut modified).ok_or(ErrMsg::Fatal)); options.stdout = check_option(&matches, "output", &mut modified).ok_or(ErrMsg::Fatal)?;
options.stderr = try!(check_option(&matches, "error", &mut modified).ok_or(ErrMsg::Fatal)); options.stderr = check_option(&matches, "error", &mut modified).ok_or(ErrMsg::Fatal)?;
if matches.free.len() != 1 { if matches.free.len() != 1 {
return Err(ErrMsg::Retry); return Err(ErrMsg::Retry);

View file

@ -68,7 +68,7 @@ fn open(name: &str) -> Result<Box<Read>> {
match name { match name {
"-" => Ok(Box::new(stdin()) as Box<Read>), "-" => Ok(Box::new(stdin()) as Box<Read>),
_ => { _ => {
let f = try!(File::open(&Path::new(name))); let f = File::open(&Path::new(name))?;
Ok(Box::new(f) as Box<Read>) Ok(Box::new(f) as Box<Read>)
} }
} }

4
src/tee/tee.rs Executable file → Normal file
View file

@ -131,14 +131,14 @@ struct MultiWriter {
impl Write for MultiWriter { impl Write for MultiWriter {
fn write(&mut self, buf: &[u8]) -> Result<usize> { fn write(&mut self, buf: &[u8]) -> Result<usize> {
for writer in &mut self.writers { for writer in &mut self.writers {
try!(writer.write_all(buf)); writer.write_all(buf)?;
} }
Ok(buf.len()) Ok(buf.len())
} }
fn flush(&mut self) -> Result<()> { fn flush(&mut self) -> Result<()> {
for writer in &mut self.writers { for writer in &mut self.writers {
try!(writer.flush()); writer.flush()?;
} }
Ok(()) Ok(())
} }

2
src/tsort/tsort.rs Executable file → Normal file
View file

@ -79,7 +79,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
let mut line = String::new(); let mut line = String::new();
match reader.read_line(&mut line) { match reader.read_line(&mut line) {
Ok(_) => { Ok(_) => {
let tokens: Vec<String> = line.trim_right() let tokens: Vec<String> = line.trim_end()
.split_whitespace() .split_whitespace()
.map(|s| s.to_owned()) .map(|s| s.to_owned())
.collect(); .collect();

View file

@ -10,7 +10,7 @@ path = "uname.rs"
[dependencies] [dependencies]
clap = "2.32.0" clap = "2.32.0"
platform-info = { git = "https://github.com/uutils/platform-info" } platform-info = "0.0.1"
uucore = { path = "../uucore" } uucore = { path = "../uucore" }
[[bin]] [[bin]]

View file

@ -122,7 +122,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
output.push_str(HOST_OS); output.push_str(HOST_OS);
output.push_str(" "); output.push_str(" ");
} }
println!("{}", output.trim_right()); println!("{}", output.trim_end());
0 0
} }

View file

@ -11,6 +11,9 @@ time = { version = "0.1.40", optional = true }
data-encoding = { version = "^2.1", optional = true } data-encoding = { version = "^2.1", optional = true }
libc = { version = "0.2.42", optional = true } libc = { version = "0.2.42", optional = true }
wild = "2.0.1" wild = "2.0.1"
nix = { version = "0.13", optional = true }
lazy_static = { version = "1.3", optional = true }
platform-info = { version = "0.0.1", optional = true }
[target.'cfg(target_os = "redox")'.dependencies] [target.'cfg(target_os = "redox")'.dependencies]
termion = "1.5" termion = "1.5"
@ -25,6 +28,7 @@ utmpx = ["time", "libc"]
process = ["libc"] process = ["libc"]
signals = [] signals = []
entries = ["libc"] entries = ["libc"]
zero-copy = ["nix", "libc", "lazy_static", "platform-info"]
wide = [] wide = []
default = [] default = []

View file

@ -13,6 +13,13 @@ extern crate failure;
#[cfg(feature = "failure_derive")] #[cfg(feature = "failure_derive")]
#[macro_use] #[macro_use]
extern crate failure_derive; extern crate failure_derive;
#[cfg(feature = "nix")]
extern crate nix;
#[cfg(all(feature = "lazy_static", target_os = "linux"))]
#[macro_use]
extern crate lazy_static;
#[cfg(feature = "platform-info")]
extern crate platform_info;
#[macro_use] #[macro_use]
mod macros; mod macros;
@ -40,5 +47,8 @@ pub mod process;
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))] #[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
pub mod signals; pub mod signals;
#[cfg(feature = "zero-copy")]
pub mod zero_copy;
#[cfg(all(windows, feature = "wide"))] #[cfg(all(windows, feature = "wide"))]
pub mod wide; pub mod wide;

View file

@ -10,7 +10,7 @@ use std::error::Error;
pub fn parse_numeric(fperm: u32, mut mode: &str) -> Result<u32, String> { pub fn parse_numeric(fperm: u32, mut mode: &str) -> Result<u32, String> {
let (op, pos) = parse_op(mode, Some('='))?; let (op, pos) = parse_op(mode, Some('='))?;
mode = mode[pos..].trim_left_matches('0'); mode = mode[pos..].trim_start_matches('0');
if mode.len() > 4 { if mode.len() > 4 {
Err(format!("mode is too large ({} > 7777)", mode)) Err(format!("mode is too large ({} > 7777)", mode))
} else { } else {

139
src/uucore/zero_copy/mod.rs Normal file
View file

@ -0,0 +1,139 @@
use self::platform::*;
use std::io::{self, Write};
mod platform;
pub trait AsRawObject {
fn as_raw_object(&self) -> RawObject;
}
pub trait FromRawObject : Sized {
unsafe fn from_raw_object(obj: RawObject) -> Option<Self>;
}
// TODO: also make a SpliceWriter that takes an input fd and and output fd and uses splice() to
// transfer data
// TODO: make a TeeWriter or something that takes an input fd and two output fds and uses tee() to
// transfer to both output fds
enum InnerZeroCopyWriter<T: Write + Sized> {
Platform(PlatformZeroCopyWriter),
Standard(T),
}
impl<T: Write + Sized> Write for InnerZeroCopyWriter<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
InnerZeroCopyWriter::Platform(ref mut writer) => writer.write(buf),
InnerZeroCopyWriter::Standard(ref mut writer) => writer.write(buf),
}
}
fn flush(&mut self) -> io::Result<()> {
match self {
InnerZeroCopyWriter::Platform(ref mut writer) => writer.flush(),
InnerZeroCopyWriter::Standard(ref mut writer) => writer.flush(),
}
}
}
pub struct ZeroCopyWriter<T: Write + AsRawObject + Sized> {
/// This field is never used, but we need it to drop file descriptors
#[allow(dead_code)]
raw_obj_owner: Option<T>,
inner: InnerZeroCopyWriter<T>,
}
struct TransformContainer<'a, A: Write + AsRawObject + Sized, B: Write + Sized> {
/// This field is never used and probably could be converted into PhantomData, but might be
/// useful for restructuring later (at the moment it's basically left over from an earlier
/// design)
#[allow(dead_code)]
original: Option<&'a mut A>,
transformed: Option<B>,
}
impl<'a, A: Write + AsRawObject + Sized, B: Write + Sized> Write for TransformContainer<'a, A, B> {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
self.transformed.as_mut().unwrap().write(bytes)
}
fn flush(&mut self) -> io::Result<()> {
self.transformed.as_mut().unwrap().flush()
}
}
impl<'a, A: Write + AsRawObject + Sized, B: Write + Sized> AsRawObject for TransformContainer<'a, A, B> {
fn as_raw_object(&self) -> RawObject {
panic!("Test should never be used")
}
}
impl<T: Write + AsRawObject + Sized> ZeroCopyWriter<T> {
pub fn new(writer: T) -> Self {
let raw_obj = writer.as_raw_object();
match unsafe { PlatformZeroCopyWriter::new(raw_obj) } {
Ok(inner) => {
ZeroCopyWriter {
raw_obj_owner: Some(writer),
inner: InnerZeroCopyWriter::Platform(inner),
}
}
_ => {
// creating the splice writer failed for whatever reason, so just make a default
// writer
ZeroCopyWriter {
raw_obj_owner: None,
inner: InnerZeroCopyWriter::Standard(writer),
}
}
}
}
pub fn with_default<'a: 'b, 'b, F, W>(writer: &'a mut T, func: F) -> ZeroCopyWriter<impl Write + AsRawObject + Sized + 'b>
where
F: Fn(&'a mut T) -> W,
W: Write + Sized + 'b,
{
let raw_obj = writer.as_raw_object();
match unsafe { PlatformZeroCopyWriter::new(raw_obj) } {
Ok(inner) => {
ZeroCopyWriter {
raw_obj_owner: Some(TransformContainer { original: Some(writer), transformed: None, }),
inner: InnerZeroCopyWriter::Platform(inner),
}
}
_ => {
// XXX: should func actually consume writer and leave it up to the user to save the value?
// maybe provide a default stdin method then? in some cases it would make more sense for the
// value to be consumed
let real_writer = func(writer);
ZeroCopyWriter {
raw_obj_owner: None,
inner: InnerZeroCopyWriter::Standard(TransformContainer { original: None, transformed: Some(real_writer) }),
}
}
}
}
// XXX: unsure how to get something like this working without allocating, so not providing it
/*pub fn stdout() -> ZeroCopyWriter<impl Write + AsRawObject + Sized> {
let mut stdout = io::stdout();
ZeroCopyWriter::with_default(&mut stdout, |stdout| {
stdout.lock()
})
}*/
}
impl<T: Write + AsRawObject + Sized> Write for ZeroCopyWriter<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}

View file

@ -0,0 +1,21 @@
use crate::zero_copy::RawObject;
use std::io::{self, Write};
pub struct PlatformZeroCopyWriter;
impl PlatformZeroCopyWriter {
pub unsafe fn new(_obj: RawObject) -> Result<Self, ()> {
Err(())
}
}
impl Write for PlatformZeroCopyWriter {
fn write(&mut self, _bytes: &[u8]) -> io::Result<usize> {
panic!("should never occur")
}
fn flush(&mut self) -> io::Result<()> {
panic!("should never occur")
}
}

View file

@ -0,0 +1,105 @@
use std::io::{self, Write};
use std::os::unix::io::RawFd;
use libc::{O_APPEND, S_IFIFO, S_IFREG};
use nix::errno::Errno;
use nix::fcntl::{fcntl, splice, vmsplice, FcntlArg, SpliceFFlags};
use nix::sys::uio::IoVec;
use nix::sys::stat::{fstat, FileStat};
use nix::unistd::pipe;
use platform_info::{Uname, PlatformInfo};
use crate::zero_copy::{FromRawObject, RawObject};
lazy_static! {
static ref IN_WSL: bool = {
let info = PlatformInfo::new().unwrap();
info.release().contains("Microsoft")
};
}
pub struct PlatformZeroCopyWriter {
raw_obj: RawObject,
read_pipe: RawFd,
write_pipe: RawFd,
write_fn: fn(&mut PlatformZeroCopyWriter, &[IoVec<&[u8]>], usize) -> io::Result<usize>,
}
impl PlatformZeroCopyWriter {
pub unsafe fn new(raw_obj: RawObject) -> nix::Result<Self> {
if *IN_WSL {
// apparently WSL hasn't implemented vmsplice(), causing writes to fail
// thus, we will just say zero-copy doesn't work there rather than working
// around it
return Err(nix::Error::from(Errno::EOPNOTSUPP))
}
let stat_info: FileStat = fstat(raw_obj)?;
let access_mode: libc::c_int = fcntl(raw_obj, FcntlArg::F_GETFL)?;
let is_regular = (stat_info.st_mode & S_IFREG) != 0;
let is_append = (access_mode & O_APPEND) != 0;
let is_fifo = (stat_info.st_mode & S_IFIFO) != 0;
if is_regular && !is_append {
let (read_pipe, write_pipe) = pipe()?;
Ok(PlatformZeroCopyWriter {
raw_obj,
read_pipe,
write_pipe,
write_fn: write_regular,
})
} else if is_fifo {
Ok(PlatformZeroCopyWriter {
raw_obj,
read_pipe: Default::default(),
write_pipe: Default::default(),
write_fn: write_fifo,
})
} else {
// FIXME: how to error?
Err(nix::Error::from(Errno::UnknownErrno))
}
}
}
impl FromRawObject for PlatformZeroCopyWriter {
unsafe fn from_raw_object(obj: RawObject) -> Option<Self> {
PlatformZeroCopyWriter::new(obj).ok()
}
}
impl Write for PlatformZeroCopyWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let iovec = &[IoVec::from_slice(buf)];
let func = self.write_fn;
func(self, iovec, buf.len())
}
fn flush(&mut self) -> io::Result<()> {
// XXX: not sure if we need anything else
Ok(())
}
}
fn write_regular(writer: &mut PlatformZeroCopyWriter, iovec: &[IoVec<&[u8]>], len: usize) -> io::Result<usize> {
vmsplice(writer.write_pipe, iovec, SpliceFFlags::empty())
.and_then(|_|
splice(
writer.read_pipe,
None,
writer.raw_obj,
None,
len,
SpliceFFlags::empty()
)
)
.map_err(|_| io::Error::last_os_error())
}
fn write_fifo(writer: &mut PlatformZeroCopyWriter, iovec: &[IoVec<&[u8]>], _len: usize) -> io::Result<usize> {
vmsplice(writer.raw_obj, iovec, SpliceFFlags::empty())
.map_err(|_| io::Error::last_os_error())
}

View file

@ -0,0 +1,21 @@
#[cfg(unix)]
pub use self::unix::*;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use self::linux::*;
#[cfg(windows)]
pub use self::windows::*;
// Add any operating systems we support here
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub use self::default::*;
#[cfg(unix)]
mod unix;
#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux;
#[cfg(windows)]
mod windows;
// Add any operating systems we support here
#[cfg(not(any(target_os = "linux", target_os = "android")))]
mod default;

View file

@ -0,0 +1,18 @@
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use crate::zero_copy::{AsRawObject, FromRawObject};
pub type RawObject = RawFd;
impl<T: AsRawFd> AsRawObject for T {
fn as_raw_object(&self) -> RawObject {
self.as_raw_fd()
}
}
// FIXME: check if this works right
impl<T: FromRawFd> FromRawObject for T {
unsafe fn from_raw_object(obj: RawObject) -> Option<Self> {
Some(T::from_raw_fd(obj))
}
}

View file

@ -0,0 +1,19 @@
use std::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle};
use crate::zero_copy::{AsRawObject, FromRawObject};
pub type RawObject = RawHandle;
impl<T: AsRawHandle> AsRawObject for T {
fn as_raw_object(&self) -> RawObject {
self.as_raw_handle()
}
}
impl<T: FromRawHandle> FromRawObject for T {
unsafe fn from_raw_object(obj: RawObject) -> Option<Self> {
Some(T::from_raw_handle(obj))
}
}
// TODO: see if there's some zero-copy stuff in Windows

View file

@ -144,7 +144,7 @@ fn wc(files: Vec<String>, settings: &Settings) -> StdResult<(), i32> {
let mut max_width: usize = 0; let mut max_width: usize = 0;
for path in &files { for path in &files {
let mut reader = try!(open(&path[..])); let mut reader = open(&path[..])?;
let mut line_count: usize = 0; let mut line_count: usize = 0;
let mut word_count: usize = 0; let mut word_count: usize = 0;

View file

@ -31,7 +31,7 @@ static LONG_HELP: &str = "
-m only hostname and user associated with stdin -m only hostname and user associated with stdin
-p, --process print active processes spawned by init -p, --process print active processes spawned by init
-q, --count all login names and number of users logged on -q, --count all login names and number of users logged on
-r, --runlevel print current runlevel -r, --runlevel print current runlevel (not available on BSDs)
-s, --short print only name, line, and time (default) -s, --short print only name, line, and time (default)
-t, --time print last system clock change -t, --time print last system clock change
-T, -w, --mesg add user's message status as +, - or ? -T, -w, --mesg add user's message status as +, - or ?
@ -60,6 +60,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
"count", "count",
"all login names and number of users logged on", "all login names and number of users logged on",
); );
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "linux", target_os = "android"))]
opts.optflag("r", "runlevel", "print current runlevel"); opts.optflag("r", "runlevel", "print current runlevel");
opts.optflag("s", "short", "print only name, line, and time (default)"); opts.optflag("s", "short", "print only name, line, and time (default)");
opts.optflag("t", "time", "print last system clock change"); opts.optflag("t", "time", "print last system clock change");
@ -281,7 +282,7 @@ fn current_tty() -> String {
if !res.is_null() { if !res.is_null() {
CStr::from_ptr(res as *const _) CStr::from_ptr(res as *const _)
.to_string_lossy() .to_string_lossy()
.trim_left_matches("/dev/") .trim_start_matches("/dev/")
.to_owned() .to_owned()
} else { } else {
"".to_owned() "".to_owned()
@ -291,6 +292,17 @@ fn current_tty() -> String {
impl Who { impl Who {
fn exec(&mut self) { fn exec(&mut self) {
let run_level_chk = |record: i16| {
#[allow(unused_assignments)]
let mut res = false;
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "linux", target_os = "android"))]
{
res = record == utmpx::RUN_LVL;
}
res
};
let f = if self.args.len() == 1 { let f = if self.args.len() == 1 {
self.args[0].as_ref() self.args[0].as_ref()
} else { } else {
@ -321,7 +333,7 @@ impl Who {
if !self.my_line_only || cur_tty == ut.tty_device() { if !self.my_line_only || cur_tty == ut.tty_device() {
if self.need_users && ut.is_user_process() { if self.need_users && ut.is_user_process() {
self.print_user(&ut); self.print_user(&ut);
} else if self.need_runlevel && ut.record_type() == utmpx::RUN_LVL { } else if self.need_runlevel && run_level_chk(ut.record_type()) {
self.print_runlevel(&ut); self.print_runlevel(&ut);
} else if self.need_boottime && ut.record_type() == utmpx::BOOT_TIME { } else if self.need_boottime && ut.record_type() == utmpx::BOOT_TIME {
self.print_boottime(&ut); self.print_boottime(&ut);
@ -510,7 +522,7 @@ impl Who {
if self.include_exit { if self.include_exit {
buf.push_str(&format!(" {:<12}", exit)); buf.push_str(&format!(" {:<12}", exit));
} }
println!("{}", buf.trim_right()); println!("{}", buf.trim_end());
} }
#[inline] #[inline]

View file

@ -11,7 +11,7 @@ path = "yes.rs"
[dependencies] [dependencies]
clap = "2.32" clap = "2.32"
uucore = { path = "../uucore" } uucore = { path = "../uucore", features = ["zero-copy"] }
[features] [features]
latency = [] latency = []

View file

@ -17,6 +17,7 @@ extern crate clap;
extern crate uucore; extern crate uucore;
use clap::Arg; use clap::Arg;
use uucore::zero_copy::ZeroCopyWriter;
use std::borrow::Cow; use std::borrow::Cow;
use std::io::{self, Write}; use std::io::{self, Write};
@ -83,9 +84,10 @@ fn prepare_buffer<'a>(input: &'a str, _buffer: &'a mut [u8; BUF_SIZE]) -> &'a [u
} }
pub fn exec(bytes: &[u8]) { pub fn exec(bytes: &[u8]) {
let stdout_raw = io::stdout(); let mut stdin_raw = io::stdout();
let mut stdout = stdout_raw.lock(); let mut writer = ZeroCopyWriter::with_default(&mut stdin_raw, |stdin| stdin.lock());
loop { loop {
stdout.write_all(bytes).unwrap(); // TODO: needs to check if pipe fails
writer.write_all(bytes).unwrap();
} }
} }

16
tests/common/util.rs Executable file → Normal file
View file

@ -110,8 +110,8 @@ impl CmdResult {
/// stderr_only is a better choice unless stdout may or will be non-empty /// stderr_only is a better choice unless stdout may or will be non-empty
pub fn stderr_is<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> { pub fn stderr_is<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> {
assert_eq!( assert_eq!(
String::from(msg.as_ref()).trim_right(), String::from(msg.as_ref()).trim_end(),
self.stderr.trim_right() self.stderr.trim_end()
); );
Box::new(self) Box::new(self)
} }
@ -162,16 +162,16 @@ pub fn log_info<T: AsRef<str>, U: AsRef<str>>(msg: T, par: U) {
} }
pub fn recursive_copy(src: &Path, dest: &Path) -> Result<()> { pub fn recursive_copy(src: &Path, dest: &Path) -> Result<()> {
if try!(fs::metadata(src)).is_dir() { if fs::metadata(src)?.is_dir() {
for entry in try!(fs::read_dir(src)) { for entry in try!(fs::read_dir(src)) {
let entry = try!(entry); let entry = entry?;
let mut new_dest = PathBuf::from(dest); let mut new_dest = PathBuf::from(dest);
new_dest.push(entry.file_name()); new_dest.push(entry.file_name());
if try!(fs::metadata(entry.path())).is_dir() { if fs::metadata(entry.path())?.is_dir() {
try!(fs::create_dir(&new_dest)); fs::create_dir(&new_dest)?;
try!(recursive_copy(&entry.path(), &new_dest)); recursive_copy(&entry.path(), &new_dest)?;
} else { } else {
try!(fs::copy(&entry.path(), new_dest)); fs::copy(&entry.path(), new_dest)?;
} }
} }
} }

View file

@ -17,10 +17,10 @@ mod test_passgrp {
#[test] #[test]
fn test_grp2gid() { fn test_grp2gid() {
if cfg!(target_os = "macos") { if cfg!(target_os = "linux") || cfg!(target_os = "android") || cfg!(target_os = "windows") {
assert_eq!(0, grp2gid("wheel").unwrap()); assert_eq!(0, grp2gid("root").unwrap())
} else { } else {
assert_eq!(0, grp2gid("root").unwrap()); assert_eq!(0, grp2gid("wheel").unwrap());
} }
assert!(grp2gid("88888888").is_err()); assert!(grp2gid("88888888").is_err());
assert!(grp2gid("agroupthatdoesntexist").is_err()); assert!(grp2gid("agroupthatdoesntexist").is_err());
@ -34,10 +34,10 @@ mod test_passgrp {
#[test] #[test]
fn test_gid2grp() { fn test_gid2grp() {
if cfg!(target_os = "macos") { if cfg!(target_os = "linux") || cfg!(target_os = "android") || cfg!(target_os = "windows") {
assert_eq!("wheel", gid2grp(0).unwrap());
} else {
assert_eq!("root", gid2grp(0).unwrap()); assert_eq!("root", gid2grp(0).unwrap());
} else {
assert_eq!("wheel", gid2grp(0).unwrap());
} }
assert!(gid2grp(88888888).is_err()); assert!(gid2grp(88888888).is_err());
} }

18
util/rewrite_rules.rs Normal file
View file

@ -0,0 +1,18 @@
//! Rules to update the codebase using Rerast
/// Converts try!() to ?
fn try_to_question_mark<T, E, X: From<E>>(r: Result<T, E>) -> Result<T, X> {
replace!(try!(r) => r?);
unreachable!()
}
fn trim_left_to_start(s: &str) {
replace!(s.trim_left() => s.trim_start());
replace!(s.trim_right() => s.trim_end());
}
fn trim_left_matches_to_start<P: FnMut(char) -> bool>(s: &str, inner: P) {
replace!(s.trim_left_matches(inner) => s.trim_start_matches(inner));
replace!(s.trim_right_matches(inner) => s.trim_end_matches(inner));
}