1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 11:07:44 +00:00

Merge pull request #1449 from rivy/add.cicd

Improves build and maint; fixes all testing issues; adds GHA CICD; fixes a long list of lints
This commit is contained in:
Roy Ivy III 2020-04-13 10:12:24 -05:00 committed by GitHub
commit 08a2cd0fc8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
249 changed files with 3375 additions and 2478 deletions

View file

@ -1,5 +1,5 @@
# spell-checker:words POSIX repo SDK SDKs toolchain toolchains # spell-checker:words POSIX SDK SDKs repo toolchain toolchains
# spell-checker:ignore uutils ARCH ABI BACKTRACE BINDIR cl COMNTOOLS dllcrt findstr maint MINGW MINGWDIR mkdir MSVC MSYS rustc rustlib rustup USERPROFILE vcvarsall # spell-checker:ignore ABI ARCH BACKTRACE BINDIR COMNTOOLS MINGW MINGWDIR MSVC MSYS USERPROFILE cl dllcrt findstr maint mkdir rustc rustlib rustup targetting uutils vcvarsall
version: "{build} ~ {branch}" version: "{build} ~ {branch}"
@ -21,8 +21,8 @@ matrix:
environment: environment:
global: global:
FEATURES: "windows" FEATURES: "windows"
BUILD_OPTIONS: "--no-default-features" BUILD_OPTIONS: ""
TEST_OPTIONS: "--no-default-features --no-fail-fast" TEST_OPTIONS: "--no-fail-fast"
matrix: matrix:
# minimum version # minimum version
@ -69,6 +69,7 @@ environment:
# - CHANNEL: nightly # - CHANNEL: nightly
# ARCH: x86_64 # ARCH: x86_64
# ABI: gnu # ABI: gnu
# FEATURES: "windows nightly"
# * specific gnu compilers # * specific gnu compilers
- CHANNEL: stable - CHANNEL: stable
ARCH: i686 ARCH: i686
@ -83,8 +84,8 @@ environment:
install: install:
# force branch checkout (if knowable), then reset to the specific commit ## (can be needed for accurate code coverage info) # force branch checkout (if knowable), then reset to the specific commit ## (can be needed for accurate code coverage info)
# * this allows later apps to see the branch name using standard `git branch` operations, yet always builds the correct specific commit # * this allows later apps to see the branch name using standard `git branch` operations, yet always builds the correct specific commit
# * ref: <https://github.com/appveyor/ci/issues/1606>[`@`](https://archive.is/RVpnF) # * ref: <https://github.com/appveyor/ci/issues/1606>[`@`](https://archive.is/RVpnF) ; note: `git branch -a` may be helpful for debugging edge cases
- if DEFINED APPVEYOR_REPO_BRANCH if /I "%APPVEYOR_REPO_SCM%"=="git" ( git checkout "%APPVEYOR_REPO_BRANCH%" 2>NUL & git reset --hard "%APPVEYOR_REPO_COMMIT%" ) - if DEFINED APPVEYOR_REPO_BRANCH if /I "%APPVEYOR_REPO_SCM%"=="git" ( git checkout "%APPVEYOR_REPO_BRANCH%" 2>NUL & git reset --hard FETCH_HEAD 2>NUL || git reset --hard "%APPVEYOR_REPO_COMMIT%" )
# ensure CWD is project main directory # ensure CWD is project main directory
- cd "%APPVEYOR_BUILD_FOLDER%" - cd "%APPVEYOR_BUILD_FOLDER%"
# create a working area # create a working area

361
.github/workflows/CICD.yml vendored Normal file
View file

@ -0,0 +1,361 @@
name: CICD
# spell-checker:ignore (acronyms) CICD MSVC musl
# spell-checker:ignore (env/flags) Ccodegen Cinline Coverflow RUSTFLAGS
# spell-checker:ignore (jargon) SHAs deps softprops toolchain
# spell-checker:ignore (names) CodeCOV MacOS MinGW Peltoche rivy
# spell-checker:ignore (shell/tools) choco clippy dmake esac fakeroot gmake grcov halium libssl mkdir popd printf pushd rustc rustfmt rustup shopt
# spell-checker:ignore (misc) alnum gnueabihf issuecomment maint nullglob onexitbegin onexitend uutils
env:
PROJECT_NAME: uutils
PROJECT_DESC: "'Universal' (cross-platform) CLI utilities"
PROJECT_AUTH: "uutils"
RUST_MIN_SRV: "1.31.0"
on: [push, pull_request]
jobs:
style:
name: Style
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- { os: ubuntu-latest , features: unix }
- { os: macos-latest , features: unix }
- { os: windows-latest , features: windows }
steps:
- uses: actions/checkout@v1
- name: Initialize workflow variables
id: vars
shell: bash
run: |
## VARs setup
# #maint: [rivy; 2020-02-08] 'windows-latest' `cargo fmt` is bugged for this project (see reasons @ GH:rust-lang/rustfmt #3324, #3590, #3688 ; waiting for repair)
JOB_DO_FORMAT_TESTING="true"
case '${{ matrix.job.os }}' in windows-latest) unset JOB_DO_FORMAT_TESTING ;; esac;
echo set-output name=JOB_DO_FORMAT_TESTING::${JOB_DO_FORMAT_TESTING:-<empty>/false}
echo ::set-output name=JOB_DO_FORMAT_TESTING::${JOB_DO_FORMAT_TESTING}
# target-specific options
# * CARGO_FEATURES_OPTION
CARGO_FEATURES_OPTION='' ;
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
echo set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
echo ::set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
- name: Install `rust` toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
profile: minimal # minimal component installation (ie, no documentation)
components: rustfmt, clippy
- name: "`fmt` testing"
if: steps.vars.outputs.JOB_DO_FORMAT_TESTING
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: "`clippy` testing"
if: success() || failure() # run regardless of prior step ("`fmt` testing") success/failure
uses: actions-rs/cargo@v1
with:
command: clippy
args: ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -- -D warnings
min_version:
name: MinSRV # Minimum supported rust version
runs-on: ${{ matrix.job.os }}
strategy:
matrix:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v1
- name: Install `rust` toolchain (v${{ env.RUST_MIN_SRV }})
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_MIN_SRV }}
default: true
profile: minimal # minimal component installation (ie, no documentation)
- name: Install `cargo-tree` # for dependency information
uses: actions-rs/install@v0.1
with:
crate: cargo-tree
version: latest
use-tool-cache: true
env:
RUSTUP_TOOLCHAIN: stable
- name: Info
shell: bash
run: |
# Info
## tooling info display
echo "## tooling"
which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true
rustup -V
rustup show active-toolchain
cargo -V
rustc -V
## dependencies
echo "## dependency list"
cargo +stable fetch --quiet
cargo +stable tree --all --no-dev-dependencies --no-indent --features ${{ matrix.job.features }} | grep -vE "$PWD" | sort --unique
- name: Test
uses: actions-rs/cargo@v1
with:
command: test
args: --features "feat_os_unix"
build:
name: Build
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
# { os, target, cargo-options, features, use-cross, toolchain }
- { os: ubuntu-latest , target: arm-unknown-linux-gnueabihf , features: feat_os_unix_gnueabihf , use-cross: use-cross }
- { os: ubuntu-16.04 , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
- { os: ubuntu-18.04 , target: i686-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
- { os: ubuntu-18.04 , target: i686-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross }
- { os: ubuntu-18.04 , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
- { os: ubuntu-18.04 , target: x86_64-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross }
- { os: macos-latest , target: x86_64-apple-darwin , features: feat_os_unix }
- { os: windows-latest , target: i686-pc-windows-gnu , features: feat_os_windows }
- { os: windows-latest , target: i686-pc-windows-msvc , features: feat_os_windows }
- { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows , toolchain: nightly-x86_64-pc-windows-gnu } ## !maint: [rivy; due 2020-21-03] disable/remove when rust beta >= v1.43.0 is available (~mid-March)
# - { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows , toolchain: beta-x86_64-pc-windows-gnu } ## maint: [rivy; due 2020-21-03; due 2020-01-05] enable when rust beta >= v1.43.0 is available (~mid-March); disable when rust stable >= 1.43.0 is available (~early-May)
# - { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows } ## note: requires rust >= 1.43.0 to link correctly # ! maint: [rivy; due 2020-01-05] enable when rust stable >= 1.43.0 is available
- { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows }
steps:
- uses: actions/checkout@v1
- name: Install/setup prerequisites
shell: bash
run: |
## install/setup prerequisites
case '${{ matrix.job.target }}' in
arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
x86_64-pc-windows-gnu)
# hack: move interfering 'gcc' to head of PATH; fixes linking errors, but only works for rust >= v1.43.0; see GH:rust-lang/rust#68872 (after potential fix GH:rust-lang/rust#67429) for further discussion/repairs; follow issue/solutions? via PR GH:rust-lang/rust#67429 (from GH:rust-lang/rust#47048#issuecomment-437409270); refs: GH:rust-lang/cargo#6754, GH:rust-lang/rust#47048 , GH:rust-lang/rust#53454 , GH:bike-barn/hermit#172
echo "::add-path::C:\\ProgramData\\Chocolatey\\lib\\mingw\\tools\\install\\mingw64\\bin"
;;
esac
- name: Initialize workflow variables
id: vars
shell: bash
run: |
## VARs setup
# toolchain
TOOLCHAIN="stable" ## default to "stable" toolchain
# * specify alternate/non-default TOOLCHAIN for *-pc-windows-gnu targets; gnu targets on Windows are broken for the standard *-pc-windows-msvc toolchain (refs: GH:rust-lang/rust#47048, GH:rust-lang/rust#53454, GH:rust-lang/cargo#6754)
case ${{ matrix.job.target }} in *-pc-windows-gnu) TOOLCHAIN="stable-${{ matrix.job.target }}" ;; esac;
# * use requested TOOLCHAIN if specified
if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi
echo set-output name=TOOLCHAIN::${TOOLCHAIN:-<empty>/false}
echo ::set-output name=TOOLCHAIN::${TOOLCHAIN}
# staging directory
STAGING='_staging'
echo set-output name=STAGING::${STAGING}
echo ::set-output name=STAGING::${STAGING}
# determine EXE suffix
EXE_suffix="" ; case '${{ matrix.job.target }}' in *-pc-windows-*) EXE_suffix=".exe" ;; esac;
echo set-output name=EXE_suffix::${EXE_suffix}
echo ::set-output name=EXE_suffix::${EXE_suffix}
# parse commit reference info
echo GITHUB_REF=${GITHUB_REF}
echo GITHUB_SHA=${GITHUB_SHA}
REF_NAME=${GITHUB_REF#refs/*/}
unset REF_BRANCH ; case "${GITHUB_REF}" in refs/heads/*) REF_BRANCH=${GITHUB_REF#refs/heads/} ;; esac;
unset REF_TAG ; case "${GITHUB_REF}" in refs/tags/*) REF_TAG=${GITHUB_REF#refs/tags/} ;; esac;
REF_SHAS=${GITHUB_SHA:0:8}
echo set-output name=REF_NAME::${REF_NAME}
echo set-output name=REF_BRANCH::${REF_BRANCH}
echo set-output name=REF_TAG::${REF_TAG}
echo set-output name=REF_SHAS::${REF_SHAS}
echo ::set-output name=REF_NAME::${REF_NAME}
echo ::set-output name=REF_BRANCH::${REF_BRANCH}
echo ::set-output name=REF_TAG::${REF_TAG}
echo ::set-output name=REF_SHAS::${REF_SHAS}
# parse target
unset TARGET_ARCH ; case '${{ matrix.job.target }}' in arm-unknown-linux-gnueabihf) TARGET_ARCH=arm ;; i686-*) TARGET_ARCH=i686 ;; x86_64-*) TARGET_ARCH=x86_64 ;; esac;
echo set-output name=TARGET_ARCH::${TARGET_ARCH}
echo ::set-output name=TARGET_ARCH::${TARGET_ARCH}
unset TARGET_OS ; case '${{ matrix.job.target }}' in *-linux-*) TARGET_OS=linux ;; *-apple-*) TARGET_OS=macos ;; *-windows-*) TARGET_OS=windows ;; esac;
echo set-output name=TARGET_OS::${TARGET_OS}
echo ::set-output name=TARGET_OS::${TARGET_OS}
# package name
PKG_suffix=".tar.gz" ; case '${{ matrix.job.target }}' in *-pc-windows-*) PKG_suffix=".zip" ;; esac;
PKG_BASENAME=${PROJECT_NAME}-${REF_TAG:-$REF_SHAS}-${{ matrix.job.target }}
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
echo set-output name=PKG_suffix::${PKG_suffix}
echo set-output name=PKG_BASENAME::${PKG_BASENAME}
echo set-output name=PKG_NAME::${PKG_NAME}
echo ::set-output name=PKG_suffix::${PKG_suffix}
echo ::set-output name=PKG_BASENAME::${PKG_BASENAME}
echo ::set-output name=PKG_NAME::${PKG_NAME}
# deployable tag? (ie, leading "vM" or "M"; M == version number)
unset DEPLOYABLE ; if [[ $REF_TAG =~ ^[vV]?[0-9].* ]]; then DEPLOYABLE='true' ; fi
echo set-output name=DEPLOYABLE::${DEPLOYABLE:-<empty>/false}
echo ::set-output name=DEPLOYABLE::${DEPLOYABLE}
# target-specific options
# * CARGO_FEATURES_OPTION
CARGO_FEATURES_OPTION='' ;
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
echo set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
echo ::set-output name=CARGO_FEATURES_OPTION::${CARGO_FEATURES_OPTION}
# * CARGO_USE_CROSS (truthy)
CARGO_USE_CROSS='true' ; case '${{ matrix.job.use-cross }}' in ''|0|f|false|n|no) unset CARGO_USE_CROSS ;; esac;
echo set-output name=CARGO_USE_CROSS::${CARGO_USE_CROSS:-<empty>/false}
echo ::set-output name=CARGO_USE_CROSS::${CARGO_USE_CROSS}
# # * `arm` cannot be tested on ubuntu-* hosts (b/c testing is currently primarily done via comparison of target outputs with built-in outputs and the `arm` target is not executable on the host)
JOB_DO_TESTING="true"
case '${{ matrix.job.target }}' in arm-*) unset JOB_DO_TESTING ;; esac;
echo set-output name=JOB_DO_TESTING::${JOB_DO_TESTING:-<empty>/false}
echo ::set-output name=JOB_DO_TESTING::${JOB_DO_TESTING}
# # * test only binary for arm-type targets
unset CARGO_TEST_OPTIONS
unset CARGO_TEST_OPTIONS ; case '${{ matrix.job.target }}' in arm-*) CARGO_TEST_OPTIONS="--bin ${PROJECT_NAME}" ;; esac;
echo set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
# * strip executable?
STRIP="strip" ; case '${{ matrix.job.target }}' in arm-unknown-linux-gnueabihf) STRIP="arm-linux-gnueabihf-strip" ;; *-pc-windows-msvc) STRIP="" ;; esac;
echo set-output name=STRIP::${STRIP:-<empty>/false}
echo ::set-output name=STRIP::${STRIP}
- name: Create all needed build/work directories
shell: bash
run: |
## create build/work space
mkdir -p '${{ steps.vars.outputs.STAGING }}'
mkdir -p '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}'
- name: rust toolchain ~ install
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ steps.vars.outputs.TOOLCHAIN }}
target: ${{ matrix.job.target }}
default: true
profile: minimal # minimal component installation (ie, no documentation)
- name: Install `cargo-tree` # for dependency information
uses: actions-rs/install@v0.1
with:
crate: cargo-tree
version: latest
use-tool-cache: true
env:
RUSTUP_TOOLCHAIN: stable
- name: Info
shell: bash
run: |
# Info
## tooling info display
echo "## tooling"
which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true
rustup -V
rustup show active-toolchain
cargo -V
rustc -V
## dependencies
echo "## dependency list"
cargo fetch --quiet
cargo tree --target=${{ matrix.job.target }} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --all --no-dev-dependencies --no-indent | grep -vE "$PWD" | sort --unique
- name: Build
uses: actions-rs/cargo@v1
with:
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
command: build
args: --release --target=${{ matrix.job.target }} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
- name: Test
uses: actions-rs/cargo@v1
with:
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
command: test
args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
- name: Archive executable artifacts
uses: actions/upload-artifact@master
with:
name: ${{ env.PROJECT_NAME }}-${{ matrix.job.target }}
path: target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}
- name: Package
shell: bash
run: |
## package artifact(s)
# binary
cp 'target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/'
# `strip` binary (if needed)
if [ -n "${{ steps.vars.outputs.STRIP }}" ]; then "${{ steps.vars.outputs.STRIP }}" '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' ; fi
# README and LICENSE
# * spell-checker:ignore EADME ICENSE
(shopt -s nullglob; for f in [R]"EADME"{,.*}; do cp $f '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' ; done)
(shopt -s nullglob; for f in [L]"ICENSE"{-*,}{,.*}; do cp $f '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' ; done)
# core compressed package
pushd '${{ steps.vars.outputs.STAGING }}/' >/dev/null
case '${{ matrix.job.target }}' in
*-pc-windows-*) 7z -y a '${{ steps.vars.outputs.PKG_NAME }}' '${{ steps.vars.outputs.PKG_BASENAME }}'/* | tail -2 ;;
*) tar czf '${{ steps.vars.outputs.PKG_NAME }}' '${{ steps.vars.outputs.PKG_BASENAME }}'/* ;;
esac
popd >/dev/null
- name: Publish
uses: softprops/action-gh-release@v1
if: steps.vars.outputs.DEPLOYABLE
with:
files: |
${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_NAME }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
coverage:
name: Code Coverage
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: true
matrix:
# job: [ { os: ubuntu-latest }, { os: macos-latest }, { os: windows-latest } ]
job: [ { os: ubuntu-latest } ] ## cargo-tarpaulin is currently only available on linux
steps:
- uses: actions/checkout@v1
# - name: Reattach HEAD ## may be needed for accurate code coverage info
# run: git checkout ${{ github.head_ref }}
- name: Initialize workflow variables
id: vars
shell: bash
run: |
## VARs setup
# staging directory
STAGING='_staging'
echo set-output name=STAGING::${STAGING}
echo ::set-output name=STAGING::${STAGING}
# check for CODECOV_TOKEN availability (work-around for inaccessible 'secrets' object for 'if'; see <https://github.community/t5/GitHub-Actions/jobs-lt-job-id-gt-if-does-not-work-with-env-secrets/m-p/38549>)
unset HAS_CODECOV_TOKEN
if [ -n $CODECOV_TOKEN ]; then HAS_CODECOV_TOKEN='true' ; fi
echo set-output name=HAS_CODECOV_TOKEN::${HAS_CODECOV_TOKEN}
echo ::set-output name=HAS_CODECOV_TOKEN::${HAS_CODECOV_TOKEN}
env:
CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}"
- name: Create all needed build/work directories
shell: bash
run: |
## create build/work space
mkdir -p '${{ steps.vars.outputs.STAGING }}/work'
- name: Install required packages
run: |
sudo apt-get -y install libssl-dev
pushd '${{ steps.vars.outputs.STAGING }}/work' >/dev/null
wget --no-verbose https://github.com/xd009642/tarpaulin/releases/download/0.9.3/cargo-tarpaulin-0.9.3-travis.tar.gz
tar xf cargo-tarpaulin-0.9.3-travis.tar.gz
cp cargo-tarpaulin "$(dirname -- "$(which cargo)")"/
popd >/dev/null
- name: Generate coverage
run: |
cargo tarpaulin --out Xml
- name: Upload coverage results (CodeCov.io)
# CODECOV_TOKEN (aka, "Repository Upload Token" for REPO from CodeCov.io) ## set via REPO/Settings/Secrets
# if: secrets.CODECOV_TOKEN (not supported {yet?}; see <https://github.community/t5/GitHub-Actions/jobs-lt-job-id-gt-if-does-not-work-with-env-secrets/m-p/38549>)
if: steps.vars.outputs.HAS_CODECOV_TOKEN
run: |
# CodeCov.io
cargo tarpaulin --out Xml
bash <(curl -s https://codecov.io/bash)
env:
CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}"

View file

@ -19,21 +19,22 @@ matrix:
fast_finish: true fast_finish: true
include: include:
- rust: 1.31.0 - rust: 1.31.0
env: FEATURES=unix
- rust: stable - rust: stable
os: linux os: linux
env: TEST_INSTALL=true env: FEATURES=unix TEST_INSTALL=true
- rust: stable - rust: stable
os: osx os: osx
env: TEST_INSTALL=true env: FEATURES=macos TEST_INSTALL=true
- rust: nightly - rust: nightly
os: linux os: linux
env: FEATURES=nightly env: FEATURES=nightly,unix
- rust: nightly - rust: nightly
os: osx os: osx
env: FEATURES=nightly env: FEATURES=nightly,macos
- rust: nightly - rust: nightly
os: linux os: linux
env: FEATURES=nightly,redox CC=x86_64-unknown-redox-gcc CARGO_ARGS='--no-default-features --target=x86_64-unknown-redox' REDOX=1 env: FEATURES=nightly,feat_os_unix_redox CC=x86_64-unknown-redox-gcc CARGO_ARGS='--no-default-features --target=x86_64-unknown-redox' REDOX=1
cache: cache:
directories: directories:

8
Cargo.lock generated
View file

@ -57,7 +57,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "backtrace" name = "backtrace"
version = "0.3.15" version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -894,7 +894,7 @@ dependencies = [
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.13.0" version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1904,7 +1904,7 @@ dependencies = [
"getopts 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
"platform-info 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "platform-info 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2194,7 +2194,7 @@ dependencies = [
"checksum md5 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "79c56d6a0b07f9e19282511c83fc5b086364cbae4ba8c7d5f190c3d9b0425a48" "checksum md5 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "79c56d6a0b07f9e19282511c83fc5b086364cbae4ba8c7d5f190c3d9b0425a48"
"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
"checksum nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46f0f3210768d796e8fa79ec70ee6af172dacbe7147f5e69be5240a47778302b" "checksum nix 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4dbdc256eaac2e3bd236d93ad999d3479ef775c863dbda3068c4006a92eec51b"
"checksum nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47e49f6982987135c5e9620ab317623e723bd06738fd85377e8d55f57c8b6487" "checksum nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47e49f6982987135c5e9620ab317623e723bd06738fd85377e8d55f57c8b6487"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"

View file

@ -6,7 +6,54 @@ build = "build.rs"
autotests = false autotests = false
[features] [features]
unix = [ default = [ "feat_common_core" ]
## OS feature shortcodes
macos = [ "feat_os_unix" ]
unix = [ "feat_os_unix" ]
windows = [ "feat_os_windows" ]
## project-specific feature shortcodes
nightly = []
test_unimplemented = []
## feature sets
# "feat_os_unix" == set of utilities which can be built/run on modern/usual *nix platforms
feat_os_unix = [
"feat_common",
"feat_os_unix_musl",
"feat_os_unix_utmpx_required",
#
"stdbuf",
]
# "feat_os_unix_fuchsia" == set of utilities which can be built/run on the "Fuschia" OS (refs: <https://fuchsia.dev>; <https://en.wikipedia.org/wiki/Google_Fuchsia>)
feat_os_unix_fuchsia = [
"feat_common_core",
#
"chgrp",
"chmod",
"chown",
"du",
"groups",
"hostid",
"install",
"logname",
"mkfifo",
"mknod",
"nice",
"pathchk",
"stdbuf",
"tty",
"uname",
"unlink",
]
# "feat_os_unix_gnueabihf" == set of utilities which can be built/run on the "arm-unknown-linux-gnueabihf" target (ARMv6 Linux [hardfloat])
feat_os_unix_gnueabihf = [
"feat_common",
"feat_os_unix_musl",
"feat_os_unix_utmpx_required",
]
# "feat_os_unix_musl" == set of utilities which can be built/run on targets binding to the "musl" library (ref: <https://musl.libc.org/about.html>)
feat_os_unix_musl = [
"feat_common",
#
"chgrp", "chgrp",
"chmod", "chmod",
"chown", "chown",
@ -24,74 +71,57 @@ unix = [
"numfmt", "numfmt",
"nohup", "nohup",
"pathchk", "pathchk",
"pinky",
"stat", "stat",
"stdbuf",
"timeout", "timeout",
"touch", "touch",
"tty", "tty",
"uname", "uname",
"unlink", "unlink",
]
# "feat_os_unix_redox" == set of utilities which can be built/run on "Redox OS" (refs: <https://www.redox-os.org>; <https://en.wikipedia.org/wiki/Redox_(operating_system)>)
feat_os_unix_redox = [
"feat_common_core",
#
"uname",
"chmod",
"install",
]
# "feat_os_unix_utmpx_required" == set of utilites requiring utmp/utmpx support
# * ref: <https://wiki.musl-libc.org/faq.html#Q:-Why-is-the-utmp/wtmp-functionality-only-implemented-as-stubs?>
feat_os_unix_utmpx_required = [
"pinky",
"uptime", "uptime",
"users", "users",
"who", "who",
"generic"
] ]
windows = ["generic"] # "feat_os_windows" == set of utilities which can be built/run on modern/usual windows platforms
windows_legacy = [ feat_os_windows = [
"feat_common", ## == "feat_os_windows_legacy" + "hostname"
]
# "feat_os_windows_legacy" == slightly restricted set of utilities which can be built/run on early windows platforms (eg, "WinXP")
feat_os_windows_legacy = [
"feat_common_core",
#
"arch", "arch",
"nproc", "nproc",
"sync", "sync",
"touch", "touch",
"whoami", "whoami",
"redox_generic"
] ]
# Feature "fuchsia" contains the exclusive list of utilities ## (common/core) feature sets
# that can be compiled and run on Fuchsia. Should be built # "feat_common" == expanded set of utilities which can be built/run on the usual rust "tier 1" target platforms (ref: <https://forge.rust-lang.org/release/platform-support.html>)
# with --no-default-features when selecting this feature. feat_common = [
# TODO: merge with "unix" to avoid duplication once we support "feat_common_core",
# all utilities in that feature. #
fuchsia = [
# unix utilities
"chgrp",
"chmod",
"chown",
"du",
"groups",
"hostid",
"install",
"logname",
"mkfifo",
"mknod",
"nice",
"pathchk",
"stdbuf",
"tty",
"uname",
"unlink",
# All generic utilities
"generic"
]
generic = [
"arch", "arch",
"hostname", "hostname",
"nproc", "nproc",
"sync", "sync",
"touch", "touch",
"whoami", "whoami",
"redox_generic"
] ]
# Feature "redox"/"redox_generic" contains the exclusive list of utilities # "feat_common_core" == baseline core set of utilities which can be built/run on most targets
# that can be compiled and run on redox. Should be built feat_common_core = [
# with --no-default-features when selecting this feature.
# TODO: merge with "generic" to avoid duplication once we support
# all utilities in that feature.
redox_generic = [
# And maybe all generic utilities
"base32", "base32",
"base64", "base64",
"basename", "basename",
@ -153,20 +183,11 @@ redox_generic = [
"wc", "wc",
"yes", "yes",
] ]
redox = [
"uname",
"chmod",
"install",
"redox_generic"
]
test_unimplemented = []
nightly = []
default = ["unix"]
[workspace] [workspace]
[dependencies] [dependencies]
uucore = "0.0.1" uucore = "0.0.2"
arch = { optional=true, path="src/arch" } arch = { optional=true, path="src/arch" }
base32 = { optional=true, path="src/base32" } base32 = { optional=true, path="src/base32" }
base64 = { optional=true, path="src/base64" } base64 = { optional=true, path="src/base64" }
@ -260,16 +281,19 @@ wc = { optional=true, path="src/wc" }
who = { optional=true, path="src/who" } who = { optional=true, path="src/who" }
whoami = { optional=true, path="src/whoami" } whoami = { optional=true, path="src/whoami" }
yes = { optional=true, path="src/yes" } yes = { optional=true, path="src/yes" }
#
# * transitive dependency via 'failure'; pin to <= v0.3.30 to avoid increasing MinSRV to v1.33.0
backtrace = ">= 0.3.3, <= 0.3.30"
[dev-dependencies] [dev-dependencies]
time = "0.1.42"
filetime = "0.2.5" filetime = "0.2.5"
libc = "0.2.62"
regex = "1.0.3"
rand = "0.6.5"
tempdir = "0.3.7"
unindent = "0.1.3"
lazy_static = "1.3.0" lazy_static = "1.3.0"
libc = "0.2.62"
rand = "0.6.5"
regex = "1.0.3"
tempdir = "0.3.7"
time = "0.1.42"
unindent = "0.1.3"
[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 # FIXME: this should use the normal users crate, but it conflicts with the users utility
@ -282,3 +306,6 @@ path = "src/uutils/uutils.rs"
[[test]] [[test]]
name = "tests" name = "tests"
[patch.crates-io]
uucore = { git = "https://github.com/rivy/rust.uucore", tag = "0.0.2" }

View file

@ -1,17 +1,88 @@
# spell-checker:ignore (cargo-make) duckscript macos
# spell-checker:ignore (rust) clippy
[config] [config]
min_version = "0.26.2"
default_to_workspace = false default_to_workspace = false
init_task = "_init"
[config.modify_core_tasks] [config.modify_core_tasks]
namespace = "core" namespace = "core"
[env] ### initialization
CARGO_MAKE_CARGO_BUILD_TEST_FLAGS = { source = "${CARGO_MAKE_RUST_TARGET_OS}", default_value = "", mapping = { "linux" = "--no-default-features --features unix", "windows" = "--no-default-features --features windows" } }
[tasks._init]
private = true
run_task = "_init_"
[tasks._init_]
private = true
dependencies = [
"_init-vars",
]
[tasks._init-vars]
private = true
script_runner = "@duckscript"
script = [
'''
# reset build/test flags
set_env CARGO_MAKE_CARGO_BUILD_TEST_FLAGS ""
# determine features
env_features = get_env CARGO_FEATURES
if is_empty "${env_features}"
env_features = get_env FEATURES
end_if
if is_empty "${env_features}"
if eq "${CARGO_MAKE_RUST_TARGET_OS}" "macos"
features = set "unix"
else
if eq "${CARGO_MAKE_RUST_TARGET_OS}" "linux"
features = set "unix"
else
if eq "${CARGO_MAKE_RUST_TARGET_OS}" "windows"
features = set "windows"
end_if
end_if
end_if
end_if
if is_empty "${features}"
features = set "${env_features}"
else
if not is_empty "${env_features}"
features = set "${features},${env_features}"
end_if
end_if
# set build flags from features
if not is_empty "${features}"
set_env CARGO_MAKE_VAR_BUILD_TEST_FEATURES "${features}"
set_env CARGO_MAKE_CARGO_BUILD_TEST_FLAGS "--features ${features}"
end_if
# determine show-utils helper script
show_utils = set "util/show-utils.sh"
if eq "${CARGO_MAKE_RUST_TARGET_OS}" "windows"
show_utils = set "util/show-utils.BAT"
end_if
set_env CARGO_MAKE_VAR_SHOW_UTILS "${show_utils}"
# rebuild TASK_ARGS for "--features" and package-build compatibility (using "," instead of ";")
args = set ${CARGO_MAKE_TASK_ARGS}
args = replace ${args} ";" ","
set_env CARGO_MAKE_TASK_BUILD_FEATURES_ARGS "${args}"
args = replace ${args} "," " -p"
if not is_empty "${args}"
args = set "-p${args}"
end_if
set_env CARGO_MAKE_TASK_BUILD_UTILS_ARGS "${args}"
'''
]
### tasks
[tasks.default] [tasks.default]
description = "Build and Test" description = "Build and Test"
category = "[project]" category = "[project]"
dependencies = [ dependencies = [
"build", "action-build-debug",
"test-terse", "test-terse",
] ]
@ -20,15 +91,27 @@ description = "Build"
category = "[project]" category = "[project]"
dependencies = [ dependencies = [
"core::pre-build", "core::pre-build",
"core::build", "action-build",
"core::post-build", "core::post-build",
] ]
[tasks.build-features]
description = "Build (with features); usage: `cargo make (build-features | features) FEATURE..`"
category = "[project]"
dependencies = [
"core::pre-build",
"action-build-features",
"core::post-build",
]
[tasks.features]
alias = "build-features"
[tasks.format] [tasks.format]
description = "Format" description = "Format"
category = "[project]" category = "[project]"
dependencies = [ dependencies = [
"action.format", "action-format",
] ]
[tasks.help] [tasks.help]
@ -45,6 +128,11 @@ dependencies = [
"action-fmt_report", "action-fmt_report",
] ]
[tasks.release]
alias = "build"
description = "Build"
category = "[project]"
[tasks.test] [tasks.test]
description = "Test" description = "Test"
category = "[project]" category = "[project]"
@ -63,13 +151,66 @@ dependencies = [
"core::post-test", "core::post-test",
] ]
[tasks.util]
alias = "utils"
[tasks.utils]
description = "Build (individual) utilities; usage: `cargo make (util | utils) [UTIL_NAME..]`"
category = "[project]"
dependencies = [
"core::pre-build",
"action-determine-utils",
"action-build-utils",
"core::post-build",
]
### actions ### actions
[tasks.action-build]
description = "`cargo build --release`"
command = "cargo"
args = ["build", "--release", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )" ]
[tasks.action-build-debug]
description = "`cargo build`"
command = "cargo"
args = ["build", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )" ]
[tasks.action-build-features]
description = "`cargo build --release --features FEATURES`"
command = "cargo"
args = ["build", "--release", "--no-default-features", "--features", "${CARGO_MAKE_TASK_BUILD_FEATURES_ARGS}" ]
[tasks.action-build-utils]
description = "Build individual utilities"
command = "cargo"
# args = ["build", "@@remove-empty(CARGO_MAKE_TASK_BUILD_UTILS_ARGS)" ]
args = ["build", "--release", "@@split(CARGO_MAKE_TASK_BUILD_UTILS_ARGS, )" ]
[tasks.action-clippy] [tasks.action-clippy]
description = "`cargo clippy` lint report" description = "`cargo clippy` lint report"
command = "cargo" command = "cargo"
args = ["clippy", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )"] args = ["clippy", "@@split(CARGO_MAKE_CARGO_BUILD_TEST_FLAGS, )"]
[tasks.action-determine-utils]
script_runner = "@duckscript"
script = [
'''
package_options = get_env CARGO_MAKE_TASK_BUILD_UTILS_ARGS
if is_empty "${package_options}"
show_utils = get_env CARGO_MAKE_VAR_SHOW_UTILS
result = exec "${show_utils}"
set_env CARGO_MAKE_VAR_UTILS ${result.stdout}
utils = array %{result.stdout}
for util in ${utils}
package_options = set "${package_options} -p${util}"
end
package_options = trim "${package_options}"
end_if
set_env CARGO_MAKE_TASK_BUILD_UTILS_ARGS "${package_options}"
'''
]
[tasks.action-format] [tasks.action-format]
description = "`cargo fmt`" description = "`cargo fmt`"
command = "cargo" command = "cargo"

View file

@ -16,8 +16,9 @@ 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" | "windows" | "windows_legacy" "default" | "macos" | "unix" | "windows" => continue,
| "nightly" | "test_unimplemented" => continue, "nightly" | "test_unimplemented" => continue,
s if s.starts_with("feat_") => continue,
_ => {} _ => {}
} }
crates.push(krate.to_string()); crates.push(krate.to_string());
@ -35,7 +36,8 @@ pub fn main() {
fn util_map() -> UtilityMap { fn util_map() -> UtilityMap {
let mut map: UtilityMap = HashMap::new();\n" let mut map: UtilityMap = HashMap::new();\n"
.as_bytes(), .as_bytes(),
).unwrap(); )
.unwrap();
for krate in crates { for krate in crates {
cf.write_all(format!("extern crate uu_{krate};\n", krate = krate).as_bytes()) cf.write_all(format!("extern crate uu_{krate};\n", krate = krate).as_bytes())
@ -59,18 +61,22 @@ pub fn main() {
map.insert(\"shake128sum\", uu_hashsum::uumain); map.insert(\"shake128sum\", uu_hashsum::uumain);
map.insert(\"shake256sum\", uu_hashsum::uumain);\n" map.insert(\"shake256sum\", uu_hashsum::uumain);\n"
.as_bytes(), .as_bytes(),
).unwrap(); )
.unwrap();
} }
_ => mf.write_all( _ => mf
format!( .write_all(
"map.insert(\"{krate}\", uu_{krate}::uumain);\n", format!(
krate = krate "map.insert(\"{krate}\", uu_{krate}::uumain);\n",
).as_bytes(), krate = krate
).unwrap(), )
.as_bytes(),
)
.unwrap(),
} }
} }
mf.write_all("map\n}\n".as_bytes()).unwrap(); mf.write_all(b"map\n}\n").unwrap();
cf.flush().unwrap(); cf.flush().unwrap();
mf.flush().unwrap(); mf.flush().unwrap();

View file

@ -1,9 +1,9 @@
use std::env; use std::env;
use std::io::Write;
use std::fs::File; use std::fs::File;
use std::io::Write;
use std::path::Path; use std::path::Path;
static TEMPLATE: &'static str = "\ static TEMPLATE: &str = "\
extern crate uu_@UTIL_CRATE@; extern crate uu_@UTIL_CRATE@;
extern crate uucore; extern crate uucore;

View file

@ -2,7 +2,8 @@
name = "arch" name = "arch"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_arch" name = "uu_arch"
@ -10,8 +11,8 @@ path = "arch.rs"
[dependencies] [dependencies]
platform-info = "0.0.1" platform-info = "0.0.1"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "arch" name = "arch"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -2,20 +2,18 @@
name = "base32" name = "base32"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_base32" name = "uu_base32"
path = "base32.rs" path = "base32.rs"
[dependencies.uucore] [dependencies]
version = "0.0.1" uucore = { version = "0.0.2", features = ["encoding"] }
features = ["encoding"] ## optional
clippy = { version = "0.0.212", optional = true }
[dependencies.clippy]
version = "0.0.212"
optional = true
[[bin]] [[bin]]
name = "base32" name = "base32"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -16,8 +16,7 @@ use uucore::encoding::Format;
mod base_common; mod base_common;
static SYNTAX: &str = "[OPTION]... [FILE]"; static SYNTAX: &str = "[OPTION]... [FILE]";
static SUMMARY: &str = static SUMMARY: &str = "Base32 encode or decode FILE, or standard input, to standard output.";
"Base32 encode or decode FILE, or standard input, to standard output.";
static LONG_HELP: &str = " static LONG_HELP: &str = "
With no FILE, or when FILE is -, read standard input. With no FILE, or when FILE is -, read standard input.

View file

@ -2,16 +2,16 @@
name = "base64" name = "base64"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_base64" name = "uu_base64"
path = "base64.rs" path = "base64.rs"
[dependencies.uucore] [dependencies]
version = "0.0.1" uucore = { version = "0.0.2", features = ["encoding"] }
features = ["encoding"]
[[bin]] [[bin]]
name = "base64" name = "base64"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -16,8 +16,7 @@ use uucore::encoding::Format;
mod base_common; mod base_common;
static SYNTAX: &str = "[OPTION]... [FILE]"; static SYNTAX: &str = "[OPTION]... [FILE]";
static SUMMARY: &str = static SUMMARY: &str = "Base64 encode or decode FILE, or standard input, to standard output.";
"Base64 encode or decode FILE, or standard input, to standard output.";
static LONG_HELP: &str = " static LONG_HELP: &str = "
With no FILE, or when FILE is -, read standard input. With no FILE, or when FILE is -, read standard input.

View file

@ -36,13 +36,11 @@ pub fn execute(
"COLS", "COLS",
) )
.parse(args); .parse(args);
let line_wrap = matches.opt_str("wrap").map(|s| { let line_wrap = matches.opt_str("wrap").map(|s| match s.parse() {
match s.parse() { Ok(n) => n,
Ok(n) => n, Err(e) => {
Err(e) => { crash!(1, "invalid wrap size: {}: {}", s, e);
crash!(1, "invalid wrap size: {}: {}", s, e);
}
} }
}); });
let ignore_garbage = matches.opt_present("ignore-garbage"); let ignore_garbage = matches.opt_present("ignore-garbage");
@ -55,7 +53,13 @@ pub fn execute(
if matches.free.is_empty() || &matches.free[0][..] == "-" { if matches.free.is_empty() || &matches.free[0][..] == "-" {
let stdin_raw = stdin(); let stdin_raw = stdin();
handle_input(&mut stdin_raw.lock(), format, line_wrap, ignore_garbage, decode); handle_input(
&mut stdin_raw.lock(),
format,
line_wrap,
ignore_garbage,
decode,
);
} else { } else {
let path = Path::new(matches.free[0].as_str()); let path = Path::new(matches.free[0].as_str());
let file_buf = safe_unwrap!(File::open(&path)); let file_buf = safe_unwrap!(File::open(&path));
@ -73,8 +77,7 @@ fn handle_input<R: Read>(
ignore_garbage: bool, ignore_garbage: bool,
decode: bool, decode: bool,
) { ) {
let mut data = Data::new(input, format) let mut data = Data::new(input, format).ignore_garbage(ignore_garbage);
.ignore_garbage(ignore_garbage);
if let Some(wrap) = line_wrap { if let Some(wrap) = line_wrap {
data = data.line_wrap(wrap); data = data.line_wrap(wrap);
} }
@ -88,4 +91,4 @@ fn handle_input<R: Read>(
Err(_) => crash!(1, "invalid input"), Err(_) => crash!(1, "invalid input"),
} }
} }
} }

View file

@ -2,15 +2,16 @@
name = "basename" name = "basename"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_basename" name = "uu_basename"
path = "basename.rs" path = "basename.rs"
[dependencies] [dependencies]
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "basename" name = "basename"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -2,22 +2,20 @@
name = "cat" name = "cat"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_cat" name = "uu_cat"
path = "cat.rs" path = "cat.rs"
[dependencies] [dependencies]
quick-error = "1.2.2" quick-error = "1.2.3"
uucore = { version = "0.0.2", features = ["fs"] }
[dependencies.uucore]
version = "0.0.1"
features = ["fs"]
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
unix_socket = "0.5.0" unix_socket = "0.5.0"
[[bin]] [[bin]]
name = "cat" name = "cat"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -38,9 +38,9 @@ static LONG_HELP: &str = "";
#[derive(PartialEq)] #[derive(PartialEq)]
enum NumberingMode { enum NumberingMode {
NumberNone, None,
NumberNonEmpty, NonEmpty,
NumberAll, All,
} }
quick_error! { quick_error! {
@ -147,11 +147,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
.parse(args); .parse(args);
let number_mode = if matches.opt_present("b") { let number_mode = if matches.opt_present("b") {
NumberingMode::NumberNonEmpty NumberingMode::NonEmpty
} else if matches.opt_present("n") { } else if matches.opt_present("n") {
NumberingMode::NumberAll NumberingMode::All
} else { } else {
NumberingMode::NumberNone NumberingMode::None
}; };
let show_nonprint = matches.opts_present(&[ let show_nonprint = matches.opts_present(&[
@ -168,8 +168,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
files.push("-".to_owned()); files.push("-".to_owned());
} }
let can_write_fast = !(show_tabs || show_nonprint || show_ends || squeeze_blank let can_write_fast = !(show_tabs
|| number_mode != NumberingMode::NumberNone); || show_nonprint
|| show_ends
|| squeeze_blank
|| number_mode != NumberingMode::None);
let success = if can_write_fast { let success = if can_write_fast {
write_fast(files).is_ok() write_fast(files).is_ok()
@ -190,7 +193,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
write_lines(files, &options).is_ok() write_lines(files, &options).is_ok()
}; };
if success { 0 } else { 1 } if success {
0
} else {
1
}
} }
/// Classifies the `InputType` of file at `path` if possible /// Classifies the `InputType` of file at `path` if possible
@ -205,25 +212,13 @@ fn get_input_type(path: &str) -> CatResult<InputType> {
match metadata(path).context(path)?.file_type() { match metadata(path).context(path)?.file_type() {
#[cfg(unix)] #[cfg(unix)]
ft if ft.is_block_device() => ft if ft.is_block_device() => Ok(InputType::BlockDevice),
{
Ok(InputType::BlockDevice)
}
#[cfg(unix)] #[cfg(unix)]
ft if ft.is_char_device() => ft if ft.is_char_device() => Ok(InputType::CharacterDevice),
{
Ok(InputType::CharacterDevice)
}
#[cfg(unix)] #[cfg(unix)]
ft if ft.is_fifo() => ft if ft.is_fifo() => Ok(InputType::Fifo),
{
Ok(InputType::Fifo)
}
#[cfg(unix)] #[cfg(unix)]
ft if ft.is_socket() => ft if ft.is_socket() => Ok(InputType::Socket),
{
Ok(InputType::Socket)
}
ft if ft.is_dir() => Ok(InputType::Directory), ft if ft.is_dir() => Ok(InputType::Directory),
ft if ft.is_file() => Ok(InputType::File), ft if ft.is_file() => Ok(InputType::File),
ft if ft.is_symlink() => Ok(InputType::SymLink), ft if ft.is_symlink() => Ok(InputType::SymLink),
@ -282,12 +277,14 @@ fn write_fast(files: Vec<String>) -> CatResult<()> {
for file in files { for file in files {
match open(&file[..]) { match open(&file[..]) {
Ok(mut handle) => while let Ok(n) = handle.reader.read(&mut in_buf) { Ok(mut handle) => {
if n == 0 { while let Ok(n) = handle.reader.read(&mut in_buf) {
break; if n == 0 {
break;
}
writer.write_all(&in_buf[..n]).context(&file[..])?;
} }
writer.write_all(&in_buf[..n]).context(&file[..])?; }
},
Err(error) => { Err(error) => {
writeln!(&mut stderr(), "{}", error)?; writeln!(&mut stderr(), "{}", error)?;
error_count += 1; error_count += 1;
@ -357,7 +354,7 @@ fn write_file_lines(file: &str, options: &OutputOptions, state: &mut OutputState
if in_buf[pos] == b'\n' { if in_buf[pos] == b'\n' {
if !state.at_line_start || !options.squeeze_blank || !one_blank_kept { if !state.at_line_start || !options.squeeze_blank || !one_blank_kept {
one_blank_kept = true; one_blank_kept = true;
if state.at_line_start && options.number == NumberingMode::NumberAll { if state.at_line_start && options.number == NumberingMode::All {
write!(&mut writer, "{0:6}\t", state.line_number)?; write!(&mut writer, "{0:6}\t", state.line_number)?;
state.line_number += 1; state.line_number += 1;
} }
@ -371,7 +368,7 @@ fn write_file_lines(file: &str, options: &OutputOptions, state: &mut OutputState
continue; continue;
} }
one_blank_kept = false; one_blank_kept = false;
if state.at_line_start && options.number != NumberingMode::NumberNone { if state.at_line_start && options.number != NumberingMode::None {
write!(&mut writer, "{0:6}\t", state.line_number)?; write!(&mut writer, "{0:6}\t", state.line_number)?;
state.line_number += 1; state.line_number += 1;
} }
@ -421,10 +418,7 @@ fn write_to_end<W: Write>(in_buf: &[u8], writer: &mut W) -> usize {
fn write_tab_to_end<W: Write>(mut in_buf: &[u8], writer: &mut W) -> usize { fn write_tab_to_end<W: Write>(mut in_buf: &[u8], writer: &mut W) -> usize {
let mut count = 0; let mut count = 0;
loop { loop {
match in_buf match in_buf.iter().position(|c| *c == b'\n' || *c == b'\t') {
.iter()
.position(|c| *c == b'\n' || *c == b'\t')
{
Some(p) => { Some(p) => {
writer.write_all(&in_buf[..p]).unwrap(); writer.write_all(&in_buf[..p]).unwrap();
if in_buf[p] == b'\n' { if in_buf[p] == b'\n' {
@ -458,7 +452,8 @@ fn write_nonprint_to_end<W: Write>(in_buf: &[u8], writer: &mut W, tab: &[u8]) ->
128..=159 => writer.write_all(&[b'M', b'-', b'^', byte - 64]), 128..=159 => writer.write_all(&[b'M', b'-', b'^', byte - 64]),
160..=254 => writer.write_all(&[b'M', b'-', byte - 128]), 160..=254 => writer.write_all(&[b'M', b'-', byte - 128]),
_ => writer.write_all(&[b'M', b'-', b'^', 63]), _ => writer.write_all(&[b'M', b'-', b'^', 63]),
}.unwrap(); }
.unwrap();
count += 1; count += 1;
} }
if count != in_buf.len() { if count != in_buf.len() {

View file

@ -2,19 +2,17 @@
name = "chgrp" name = "chgrp"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_chgrp" name = "uu_chgrp"
path = "chgrp.rs" path = "chgrp.rs"
[dependencies] [dependencies]
uucore = { version = "0.0.2", features = ["entries", "fs"] }
walkdir = "2.2.8" walkdir = "2.2.8"
[dependencies.uucore]
version = "0.0.1"
features = ["entries", "fs"]
[[bin]] [[bin]]
name = "chgrp" name = "chgrp"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -10,15 +10,15 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use uucore::libc::{self, gid_t, lchown};
pub use uucore::entries; pub use uucore::entries;
use uucore::fs::resolve_relative_path; use uucore::fs::resolve_relative_path;
use uucore::libc::{self, gid_t, lchown};
extern crate walkdir; extern crate walkdir;
use walkdir::WalkDir; use walkdir::WalkDir;
use std::io::Result as IOResult;
use std::io::Error as IOError; use std::io::Error as IOError;
use std::io::Result as IOResult;
use std::fs; use std::fs;
use std::fs::Metadata; use std::fs::Metadata;
@ -183,12 +183,12 @@ struct Chgrper {
} }
macro_rules! unwrap { macro_rules! unwrap {
($m:expr, $e:ident, $err:block) => ( ($m:expr, $e:ident, $err:block) => {
match $m { match $m {
Ok(meta) => meta, Ok(meta) => meta,
Err($e) => $err, Err($e) => $err,
} }
) };
} }
impl Chgrper { impl Chgrper {

View file

@ -2,7 +2,8 @@
name = "chmod" name = "chmod"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_chmod" name = "uu_chmod"
@ -10,12 +11,9 @@ path = "chmod.rs"
[dependencies] [dependencies]
libc = "0.2.42" libc = "0.2.42"
uucore = { version = "0.0.2", features = ["mode"] }
walker = "1.0.0" walker = "1.0.0"
[dependencies.uucore]
version = "0.0.1"
features = ["mode"]
[[bin]] [[bin]]
name = "chmod" name = "chmod"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -19,10 +19,10 @@ extern crate uucore;
use std::fs; use std::fs;
use std::os::unix::fs::{MetadataExt, PermissionsExt}; use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::Path; use std::path::Path;
use walker::Walker; use uucore::fs::display_permissions_unix;
#[cfg(not(windows))] #[cfg(not(windows))]
use uucore::mode; use uucore::mode;
use uucore::fs::display_permissions_unix; use walker::Walker;
const NAME: &str = "chmod"; const NAME: &str = "chmod";
static SUMMARY: &str = "Change the mode of each FILE to MODE. static SUMMARY: &str = "Change the mode of each FILE to MODE.
@ -39,14 +39,31 @@ pub fn uumain(mut args: Vec<String>) -> i32 {
NAME NAME
); );
let mut opts = new_coreopts!(&syntax, SUMMARY, LONG_HELP); let mut opts = new_coreopts!(&syntax, SUMMARY, LONG_HELP);
opts.optflag("c", "changes", "like verbose but report only when a change is made") opts.optflag(
// TODO: support --silent (can be done using clap) "c",
.optflag("f", "quiet", "suppress most error messages") "changes",
.optflag("v", "verbose", "output a diagnostic for every file processed") "like verbose but report only when a change is made",
.optflag("", "no-preserve-root", "do not treat '/' specially (the default)") )
.optflag("", "preserve-root", "fail to operate recursively on '/'") // TODO: support --silent (can be done using clap)
.optopt("", "reference", "use RFILE's mode instead of MODE values", "RFILE") .optflag("f", "quiet", "suppress most error messages")
.optflag("R", "recursive", "change files and directories recursively"); .optflag(
"v",
"verbose",
"output a diagnostic for every file processed",
)
.optflag(
"",
"no-preserve-root",
"do not treat '/' specially (the default)",
)
.optflag("", "preserve-root", "fail to operate recursively on '/'")
.optopt(
"",
"reference",
"use RFILE's mode instead of MODE values",
"RFILE",
)
.optflag("R", "recursive", "change files and directories recursively");
// sanitize input for - at beginning (e.g. chmod -x testfile). Remove // sanitize input for - at beginning (e.g. chmod -x testfile). Remove
// the option and save it for later, after parsing is finished. // the option and save it for later, after parsing is finished.
@ -100,7 +117,7 @@ pub fn uumain(mut args: Vec<String>) -> i32 {
fn sanitize_input(args: &mut Vec<String>) -> Option<String> { fn sanitize_input(args: &mut Vec<String>) -> Option<String> {
for i in 0..args.len() { for i in 0..args.len() {
let first = args[i].chars().nth(0).unwrap(); let first = args[i].chars().next().unwrap();
if first != '-' { if first != '-' {
continue; continue;
} }
@ -148,17 +165,19 @@ impl Chmoder {
// on Windows OsStrings cannot be built out of non-UTF-8 chars. One // on Windows OsStrings cannot be built out of non-UTF-8 chars. One
// possible fix is to use CStrings rather than Strings in the args // possible fix is to use CStrings rather than Strings in the args
// to chmod() and chmod_file(). // to chmod() and chmod_file().
r = self.chmod( r = self
walk_dir .chmod(
.filter_map(|x| match x { walk_dir
Ok(o) => match o.path().into_os_string().to_str() { .filter_map(|x| match x {
Some(s) => Some(s.to_owned()), Ok(o) => match o.path().into_os_string().to_str() {
None => None, Some(s) => Some(s.to_owned()),
}, None => None,
Err(_) => None, },
}) Err(_) => None,
.collect(), })
).and(r); .collect(),
)
.and(r);
r = self.chmod_file(&file, filename).and(r); r = self.chmod_file(&file, filename).and(r);
} }
} else { } else {
@ -230,7 +249,12 @@ impl Chmoder {
fn change_file(&self, fperm: u32, mode: u32, file: &Path, path: &str) -> Result<(), i32> { fn change_file(&self, fperm: u32, mode: u32, file: &Path, path: &str) -> Result<(), i32> {
if fperm == mode { if fperm == mode {
if self.verbose && !self.changes { if self.verbose && !self.changes {
show_info!("mode of '{}' retained as {:o} ({})", file.display(), fperm, display_permissions_unix(fperm)); show_info!(
"mode of '{}' retained as {:o} ({})",
file.display(),
fperm,
display_permissions_unix(fperm)
);
} }
Ok(()) Ok(())
} else if let Err(err) = } else if let Err(err) =

View file

@ -2,7 +2,8 @@
name = "chown" name = "chown"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_chown" name = "uu_chown"
@ -10,16 +11,13 @@ path = "chown.rs"
[dependencies] [dependencies]
glob = "0.3.0" glob = "0.3.0"
uucore = { version = "0.0.2", features = ["entries", "fs"] }
walkdir = "2.2" walkdir = "2.2"
[dependencies.uucore]
version = "0.0.1"
features = ["entries", "fs"]
[dependencies.clippy] [dependencies.clippy]
version = "0.0.212" version = "0.0.212"
optional = true optional = true
[[bin]] [[bin]]
name = "chown" name = "chown"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -11,9 +11,9 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use uucore::libc::{self, gid_t, lchown, uid_t};
pub use uucore::entries::{self, Group, Locate, Passwd}; pub use uucore::entries::{self, Group, Locate, Passwd};
use uucore::fs::resolve_relative_path; use uucore::fs::resolve_relative_path;
use uucore::libc::{self, gid_t, lchown, uid_t};
extern crate walkdir; extern crate walkdir;
use walkdir::WalkDir; use walkdir::WalkDir;
@ -24,8 +24,8 @@ use std::os::unix::fs::MetadataExt;
use std::io; use std::io;
use std::io::Result as IOResult; use std::io::Result as IOResult;
use std::path::Path;
use std::convert::AsRef; use std::convert::AsRef;
use std::path::Path;
use std::ffi::CString; use std::ffi::CString;
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
@ -253,12 +253,12 @@ struct Chowner {
} }
macro_rules! unwrap { macro_rules! unwrap {
($m:expr, $e:ident, $err:block) => ( ($m:expr, $e:ident, $err:block) => {
match $m { match $m {
Ok(meta) => meta, Ok(meta) => meta,
Err($e) => $err, Err($e) => $err,
} }
) };
} }
impl Chowner { impl Chowner {
@ -395,8 +395,8 @@ impl Chowner {
fn wrap_chown<P: AsRef<Path>>(&self, path: P, meta: &Metadata, follow: bool) -> i32 { fn wrap_chown<P: AsRef<Path>>(&self, path: P, meta: &Metadata, follow: bool) -> i32 {
use self::Verbosity::*; use self::Verbosity::*;
let mut ret = 0; let mut ret = 0;
let dest_uid = self.dest_uid.unwrap_or(meta.uid()); let dest_uid = self.dest_uid.unwrap_or_else(|| meta.uid());
let dest_gid = self.dest_gid.unwrap_or(meta.gid()); let dest_gid = self.dest_gid.unwrap_or_else(|| meta.gid());
let path = path.as_ref(); let path = path.as_ref();
if let Err(e) = self.chown(path, dest_uid, dest_gid, follow) { if let Err(e) = self.chown(path, dest_uid, dest_gid, follow) {
match self.verbosity { match self.verbosity {

View file

@ -2,7 +2,8 @@
name = "chroot" name = "chroot"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_chroot" name = "uu_chroot"
@ -10,11 +11,8 @@ path = "chroot.rs"
[dependencies] [dependencies]
getopts = "0.2.18" getopts = "0.2.18"
uucore = { version = "0.0.2", features = ["entries"] }
[dependencies.uucore]
version = "0.0.1"
features = ["entries"]
[[bin]] [[bin]]
name = "chroot" name = "chroot"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -14,8 +14,8 @@ extern crate getopts;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use uucore::libc::{self, chroot, setgid, setgroups, setuid};
use uucore::entries; use uucore::entries;
use uucore::libc::{self, chroot, setgid, setgroups, setuid};
use std::ffi::CString; use std::ffi::CString;
use std::io::Error; use std::io::Error;
@ -140,10 +140,7 @@ fn enter_chroot(root: &Path) {
let root_str = root.display(); let root_str = root.display();
std::env::set_current_dir(root).unwrap(); std::env::set_current_dir(root).unwrap();
let err = unsafe { let err = unsafe {
chroot(CString::new(".") chroot(CString::new(".").unwrap().as_bytes_with_nul().as_ptr() as *const libc::c_char)
.unwrap()
.as_bytes_with_nul()
.as_ptr() as *const libc::c_char)
}; };
if err != 0 { if err != 0 {
crash!( crash!(

View file

@ -2,6 +2,7 @@
name = "cksum" name = "cksum"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
license = "MIT"
[lib] [lib]
name = "uu_cksum" name = "uu_cksum"
@ -9,8 +10,8 @@ path = "cksum.rs"
[dependencies] [dependencies]
libc = "0.2.42" libc = "0.2.42"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "cksum" name = "cksum"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -15,7 +15,7 @@ use std::path::Path;
const CRC_TABLE_LEN: usize = 256; const CRC_TABLE_LEN: usize = 256;
#[path = "../../mkmain.rs"] #[path = "../#common/mkmain.rs"]
mod mkmain; mod mkmain;
fn main() { fn main() {
@ -30,9 +30,10 @@ fn main() {
let file = File::create(&Path::new(&out_dir).join("crc_table.rs")).unwrap(); let file = File::create(&Path::new(&out_dir).join("crc_table.rs")).unwrap();
write!( write!(
&file, &file,
"const CRC_TABLE: [u32; {}] = {:?};", "#[allow(clippy::unreadable_literal)]\nconst CRC_TABLE: [u32; {}] = {:?};",
CRC_TABLE_LEN, table CRC_TABLE_LEN, table
).unwrap(); )
.unwrap();
} }
#[inline] #[inline]
@ -40,9 +41,9 @@ fn crc_entry(input: u8) -> u32 {
let mut crc = (input as u32) << 24; let mut crc = (input as u32) << 24;
for _ in 0..8 { for _ in 0..8 {
if crc & 0x80000000 != 0 { if crc & 0x8000_0000 != 0 {
crc <<= 1; crc <<= 1;
crc ^= 0x04c11db7; crc ^= 0x04c1_1db7;
} else { } else {
crc <<= 1; crc <<= 1;
} }

View file

@ -14,8 +14,6 @@ extern crate uucore;
use std::fs::File; use std::fs::File;
use std::io::{self, stdin, BufReader, Read}; use std::io::{self, stdin, BufReader, Read};
#[cfg(not(windows))]
use std::mem;
use std::path::Path; use std::path::Path;
include!(concat!(env!("OUT_DIR"), "/crc_table.rs")); include!(concat!(env!("OUT_DIR"), "/crc_table.rs"));
@ -39,16 +37,10 @@ fn crc_final(mut crc: u32, mut length: usize) -> u32 {
!crc !crc
} }
#[cfg(windows)]
fn init_byte_array() -> Vec<u8> { fn init_byte_array() -> Vec<u8> {
vec![0; 1024 * 1024] vec![0; 1024 * 1024]
} }
#[cfg(not(windows))]
fn init_byte_array() -> [u8; 1024 * 1024] {
unsafe { mem::uninitialized() }
}
#[inline] #[inline]
fn cksum(fname: &str) -> io::Result<(u32, usize)> { fn cksum(fname: &str) -> io::Result<(u32, usize)> {
let mut crc = 0u32; let mut crc = 0u32;

View file

@ -2,17 +2,18 @@
name = "comm" name = "comm"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_comm" name = "uu_comm"
path = "comm.rs" path = "comm.rs"
[dependencies] [dependencies]
libc = "0.2.42"
getopts = "0.2.18" getopts = "0.2.18"
uucore = "0.0.1" libc = "0.2.42"
uucore = "0.0.2"
[[bin]] [[bin]]
name = "comm" name = "comm"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -26,7 +26,7 @@ static LONG_HELP: &str = "";
fn mkdelim(col: usize, opts: &getopts::Matches) -> String { fn mkdelim(col: usize, opts: &getopts::Matches) -> String {
let mut s = String::new(); let mut s = String::new();
let delim = match opts.opt_str("output-delimiter") { let delim = match opts.opt_str("output-delimiter") {
Some(d) => d.clone(), Some(d) => d,
None => "\t".to_owned(), None => "\t".to_owned(),
}; };

View file

@ -5,23 +5,21 @@ 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>",
] ]
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_cp" name = "uu_cp"
path = "cp.rs" path = "cp.rs"
[dependencies] [dependencies]
clap = "2.32.0"
filetime = "0.2"
getopts = "0.2.18" getopts = "0.2.18"
libc = "0.2.42" libc = "0.2.42"
quick-error = "1.2.3"
uucore = { version = "0.0.2", features = ["fs"] }
walkdir = "2.2.8" walkdir = "2.2.8"
clap = "2.32.0"
quick-error = "1.2.2"
filetime = "0.2"
[dependencies.uucore]
version = "0.0.1"
features = ["fs"]
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
ioctl-sys = "0.5.2" ioctl-sys = "0.5.2"
@ -35,4 +33,4 @@ xattr="0.2.1"
[[bin]] [[bin]]
name = "cp" name = "cp"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -1,4 +1,5 @@
#![crate_name = "uu_cp"] #![crate_name = "uu_cp"]
#![allow(clippy::missing_safety_doc)]
/* /*
* This file is part of the uutils coreutils package. * This file is part of the uutils coreutils package.
@ -27,35 +28,42 @@ extern crate xattr;
#[cfg(windows)] #[cfg(windows)]
extern crate kernel32; extern crate kernel32;
#[cfg(windows)] #[cfg(windows)]
use kernel32::GetFileInformationByHandle;
#[cfg(windows)]
use kernel32::CreateFileW; use kernel32::CreateFileW;
#[cfg(windows)] #[cfg(windows)]
use kernel32::GetFileInformationByHandle;
#[cfg(windows)]
extern crate winapi; extern crate winapi;
use std::mem;
use std::ffi::CString;
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
use filetime::FileTime;
use quick_error::ResultExt; use quick_error::ResultExt;
use std::collections::HashSet; use std::collections::HashSet;
#[cfg(not(windows))]
use std::ffi::CString;
#[cfg(windows)]
use std::ffi::OsStr;
use std::fs; use std::fs;
use std::io::{stdin, stdout, Write};
use std::io;
use std::path::{Path, PathBuf, StripPrefixError};
use std::str::FromStr;
use uucore::fs::{canonicalize, CanonicalizeMode};
use walkdir::WalkDir;
#[cfg(target_os = "linux")]
use std::os::unix::io::IntoRawFd;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use std::fs::File; use std::fs::File;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use filetime::FileTime; use std::io;
use std::io::{stdin, stdout, Write};
use std::mem;
#[cfg(target_os = "linux")]
use std::os::unix::io::IntoRawFd;
#[cfg(windows)]
use std::os::windows::ffi::OsStrExt;
use std::path::{Path, PathBuf, StripPrefixError};
use std::str::FromStr;
use std::string::ToString;
use uucore::fs::{canonicalize, CanonicalizeMode};
use walkdir::WalkDir;
#[cfg(unix)] #[cfg(unix)]
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
#[allow(clippy::missing_safety_doc)]
ioctl!(write ficlone with 0x94, 9; std::os::raw::c_int); ioctl!(write ficlone with 0x94, 9; std::os::raw::c_int);
quick_error! { quick_error! {
@ -121,7 +129,7 @@ macro_rules! prompt_yes(
crash_if_err!(1, stdout().flush()); crash_if_err!(1, stdout().flush());
let mut s = String::new(); let mut s = String::new();
match stdin().read_line(&mut s) { match stdin().read_line(&mut s) {
Ok(_) => match s.char_indices().nth(0) { Ok(_) => match s.char_indices().next() {
Some((_, x)) => x == 'y' || x == 'Y', Some((_, x)) => x == 'y' || x == 'Y',
_ => false _ => false
}, },
@ -470,7 +478,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
let options = crash_if_err!(EXIT_ERR, Options::from_matches(&matches)); let options = crash_if_err!(EXIT_ERR, Options::from_matches(&matches));
let paths: Vec<String> = matches let paths: Vec<String> = matches
.values_of("paths") .values_of("paths")
.map(|v| v.map(|p| p.to_string()).collect()) .map(|v| v.map(ToString::to_string).collect())
.unwrap_or_default(); .unwrap_or_default();
let (sources, target) = crash_if_err!(EXIT_ERR, parse_path_args(&paths, &options)); let (sources, target) = crash_if_err!(EXIT_ERR, parse_path_args(&paths, &options));
@ -576,7 +584,8 @@ impl Options {
} }
} }
let recursive = matches.is_present(OPT_RECURSIVE) || matches.is_present(OPT_RECURSIVE_ALIAS) let recursive = matches.is_present(OPT_RECURSIVE)
|| matches.is_present(OPT_RECURSIVE_ALIAS)
|| matches.is_present(OPT_ARCHIVE); || matches.is_present(OPT_ARCHIVE);
let backup = matches.is_present(OPT_BACKUP) || (matches.occurrences_of(OPT_SUFFIX) > 0); let backup = matches.is_present(OPT_BACKUP) || (matches.occurrences_of(OPT_SUFFIX) > 0);
@ -585,7 +594,7 @@ impl Options {
let no_target_dir = matches.is_present(OPT_NO_TARGET_DIRECTORY); let no_target_dir = matches.is_present(OPT_NO_TARGET_DIRECTORY);
let target_dir = matches let target_dir = matches
.value_of(OPT_TARGET_DIRECTORY) .value_of(OPT_TARGET_DIRECTORY)
.map(|v| v.to_string()); .map(ToString::to_string);
// Parse attributes to preserve // Parse attributes to preserve
let preserve_attributes: Vec<Attribute> = if matches.is_present(OPT_PRESERVE) { let preserve_attributes: Vec<Attribute> = if matches.is_present(OPT_PRESERVE) {
@ -712,27 +721,30 @@ fn preserve_hardlinks(
{ {
if !source.is_dir() { if !source.is_dir() {
unsafe { unsafe {
let src_path = CString::new(source.as_os_str().to_str().unwrap()).unwrap();
let inode: u64; let inode: u64;
let nlinks: u64; let nlinks: u64;
#[cfg(unix)] #[cfg(unix)]
{ {
let src_path = CString::new(source.as_os_str().to_str().unwrap()).unwrap();
let mut stat = mem::zeroed(); let mut stat = mem::zeroed();
if libc::lstat(src_path.as_ptr(), &mut stat) < 0 { if libc::lstat(src_path.as_ptr(), &mut stat) < 0 {
return Err(format!( return Err(format!(
"cannot stat {:?}: {}", "cannot stat {:?}: {}",
src_path, src_path,
std::io::Error::last_os_error() std::io::Error::last_os_error()
).into()); )
.into());
} }
inode = stat.st_ino as u64; inode = stat.st_ino as u64;
nlinks = stat.st_nlink as u64; nlinks = stat.st_nlink as u64;
} }
#[cfg(windows)] #[cfg(windows)]
{ {
let src_path: Vec<u16> = OsStr::new(source).encode_wide().collect();
#[allow(deprecated)]
let stat = mem::uninitialized(); let stat = mem::uninitialized();
let handle = CreateFileW( let handle = CreateFileW(
src_path.as_ptr() as *const u16, src_path.as_ptr(),
winapi::um::winnt::GENERIC_READ, winapi::um::winnt::GENERIC_READ,
winapi::um::winnt::FILE_SHARE_READ, winapi::um::winnt::FILE_SHARE_READ,
std::ptr::null_mut(), std::ptr::null_mut(),
@ -745,7 +757,8 @@ fn preserve_hardlinks(
"cannot get file information {:?}: {}", "cannot get file information {:?}: {}",
source, source,
std::io::Error::last_os_error() std::io::Error::last_os_error()
).into()); )
.into());
} }
inode = ((*stat).nFileIndexHigh as u64) << 32 | (*stat).nFileIndexLow as u64; inode = ((*stat).nFileIndexHigh as u64) << 32 | (*stat).nFileIndexLow as u64;
nlinks = (*stat).nNumberOfLinks as u64; nlinks = (*stat).nNumberOfLinks as u64;
@ -758,7 +771,7 @@ fn preserve_hardlinks(
} }
} }
if !(*found_hard_link) && nlinks > 1 { if !(*found_hard_link) && nlinks > 1 {
hard_links.push((dest.clone().to_str().unwrap().to_string(), inode)); hard_links.push((dest.to_str().unwrap().to_string(), inode));
} }
} }
} }
@ -826,7 +839,8 @@ fn construct_dest_path(
return Err(format!( return Err(format!(
"cannot overwrite directory '{}' with non-directory", "cannot overwrite directory '{}' with non-directory",
target.display() target.display()
).into()); )
.into());
} }
Ok(match *target_type { Ok(match *target_type {
@ -940,7 +954,7 @@ impl OverwriteMode {
fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResult<()> { fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResult<()> {
let context = &*format!("'{}' -> '{}'", source.display().to_string(), dest.display()); let context = &*format!("'{}' -> '{}'", source.display().to_string(), dest.display());
Ok(match *attribute { match *attribute {
#[cfg(unix)] #[cfg(unix)]
Attribute::Mode => { Attribute::Mode => {
let mode = fs::metadata(source).context(context)?.permissions().mode(); let mode = fs::metadata(source).context(context)?.permissions().mode();
@ -973,10 +987,11 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu
} }
#[cfg(not(unix))] #[cfg(not(unix))]
{ {
return Err(format!("XAttrs are only supported on unix.").into()); return Err("XAttrs are only supported on unix.".to_string().into());
} }
} }
}) };
Ok(())
} }
#[cfg(not(windows))] #[cfg(not(windows))]
@ -1072,8 +1087,8 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
CopyMode::Sparse => return Err(Error::NotImplemented(OPT_SPARSE.to_string())), CopyMode::Sparse => return Err(Error::NotImplemented(OPT_SPARSE.to_string())),
CopyMode::Update => { CopyMode::Update => {
if dest.exists() { if dest.exists() {
let src_metadata = fs::metadata(source.clone())?; let src_metadata = fs::metadata(source)?;
let dest_metadata = fs::metadata(dest.clone())?; let dest_metadata = fs::metadata(dest)?;
let src_time = src_metadata.modified()?; let src_time = src_metadata.modified()?;
let dest_time = dest_metadata.modified()?; let dest_time = dest_metadata.modified()?;
@ -1108,7 +1123,7 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
if options.reflink { if options.reflink {
#[cfg(not(target_os = "linux"))] #[cfg(not(target_os = "linux"))]
return Err(format!("--reflink is only supported on linux").into()); return Err("--reflink is only supported on linux".to_string().into());
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
{ {
@ -1129,7 +1144,8 @@ fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()>
source, source,
dest, dest,
std::io::Error::last_os_error() std::io::Error::last_os_error()
).into()); )
.into());
} else { } else {
return Ok(()); return Ok(());
} }
@ -1158,7 +1174,8 @@ pub fn verify_target_type(target: &Path, target_type: &TargetType) -> CopyResult
(&TargetType::File, true) => Err(format!( (&TargetType::File, true) => Err(format!(
"cannot overwrite directory '{}' with non-directory", "cannot overwrite directory '{}' with non-directory",
target.display() target.display()
).into()), )
.into()),
_ => Ok(()), _ => Ok(()),
} }
} }
@ -1194,6 +1211,8 @@ fn test_cp_localize_to_target() {
&Path::new("a/source/"), &Path::new("a/source/"),
&Path::new("a/source/c.txt"), &Path::new("a/source/c.txt"),
&Path::new("target/") &Path::new("target/")
).unwrap() == Path::new("target/c.txt") )
.unwrap()
== Path::new("target/c.txt")
) )
} }

View file

@ -2,15 +2,16 @@
name = "cut" name = "cut"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_cut" name = "uu_cut"
path = "cut.rs" path = "cut.rs"
[dependencies] [dependencies]
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "cut" name = "cut"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -10,8 +10,8 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
use std::io::{BufRead, BufReader, Read, Write};
use std::io::Result as IoResult; use std::io::Result as IoResult;
use std::io::{BufRead, BufReader, Read, Write};
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub mod Bytes { pub mod Bytes {

View file

@ -203,6 +203,7 @@ fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> i32 {
0 0
} }
#[allow(clippy::cognitive_complexity)]
fn cut_fields_delimiter<R: Read>( fn cut_fields_delimiter<R: Read>(
reader: R, reader: R,
ranges: &[Range], ranges: &[Range],
@ -288,6 +289,7 @@ fn cut_fields_delimiter<R: Read>(
0 0
} }
#[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) -> i32 {
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_delimeter { if let Some(ref o_delim) = opts.out_delimeter {
@ -464,8 +466,8 @@ pub fn uumain(args: Vec<String>) -> i32 {
) )
}) })
} }
(None, None, Some(field_ranges)) => list_to_ranges(&field_ranges[..], complement) (None, None, Some(field_ranges)) => {
.and_then(|ranges| { list_to_ranges(&field_ranges[..], complement).and_then(|ranges| {
let out_delim = match matches.opt_str("output-delimiter") { let out_delim = match matches.opt_str("output-delimiter") {
Some(s) => { Some(s) => {
if s.is_empty() { if s.is_empty() {
@ -488,7 +490,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
"a value 2 characters or longer", "a value 2 characters or longer",
"--delimiter", "--delimiter",
"-d" "-d"
).to_owned()) ))
} else { } else {
let delim = if delim.is_empty() { let delim = if delim.is_empty() {
"\0".to_owned() "\0".to_owned()
@ -517,27 +519,30 @@ pub fn uumain(args: Vec<String>) -> 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)") msg_expects_no_more_than_one_of!("--fields (-f)", "--chars (-c)", "--bytes (-b)"),
.to_owned(),
), ),
_ => Err(msg_expects_one_of!("--fields (-f)", "--chars (-c)", "--bytes (-b)").to_owned()), _ => Err(msg_expects_one_of!(
"--fields (-f)",
"--chars (-c)",
"--bytes (-b)"
)),
}; };
let mode_parse = match mode_parse { let mode_parse = match mode_parse {
Err(_) => mode_parse, Err(_) => mode_parse,
Ok(mode) => match mode { Ok(mode) => match mode {
Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.opt_present("delimiter") => Err( Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.opt_present("delimiter") => Err(
msg_opt_only_usable_if!("printing a sequence of fields", "--delimiter", "-d") msg_opt_only_usable_if!("printing a sequence of fields", "--delimiter", "-d"),
.to_owned(),
), ),
Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.opt_present("only-delimited") => { Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.opt_present("only-delimited") => {
Err(msg_opt_only_usable_if!( Err(msg_opt_only_usable_if!(
"printing a sequence of fields", "printing a sequence of fields",
"--only-delimited", "--only-delimited",
"-s" "-s"
).to_owned()) ))
} }
_ => Ok(mode), _ => Ok(mode),
}, },

View file

@ -42,10 +42,7 @@ impl FromStr for Range {
(Some(n), Some(m)) if m.is_empty() => { (Some(n), Some(m)) if m.is_empty() => {
if let Ok(low) = n.parse::<usize>() { if let Ok(low) = n.parse::<usize>() {
if low > 0 { if low > 0 {
Ok(Range { Ok(Range { low, high: MAX - 1 })
low,
high: MAX - 1,
})
} else { } else {
Err(field) Err(field)
} }
@ -53,7 +50,7 @@ impl FromStr for Range {
Err(inval) Err(inval)
} }
} }
(Some(n), Some(m)) if n.len() == 0 => { (Some(n), Some(m)) if n.is_empty() => {
if let Ok(high) = m.parse::<usize>() { if let Ok(high) = m.parse::<usize>() {
if high > 0 { if high > 0 {
Ok(Range { low: 1, high }) Ok(Range { low: 1, high })
@ -67,10 +64,7 @@ impl FromStr for Range {
(Some(n), Some(m)) => match (n.parse::<usize>(), m.parse::<usize>()) { (Some(n), Some(m)) => match (n.parse::<usize>(), m.parse::<usize>()) {
(Ok(low), Ok(high)) => { (Ok(low), Ok(high)) => {
if low > 0 && low <= high { if low > 0 && low <= high {
Ok(Range { Ok(Range { low, high })
low,
high,
})
} else if low == 0 { } else if low == 0 {
Err(field) Err(field)
} else { } else {

View file

@ -2,7 +2,8 @@
name = "date" name = "date"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_date" name = "uu_date"
@ -11,8 +12,8 @@ path = "date.rs"
[dependencies] [dependencies]
chrono = "0.4.4" chrono = "0.4.4"
clap = "2.32.0" clap = "2.32.0"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "date" name = "date"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -14,8 +14,8 @@ extern crate chrono;
extern crate clap; extern crate clap;
extern crate uucore; extern crate uucore;
use chrono::{DateTime, FixedOffset, Local, Offset};
use chrono::offset::Utc; use chrono::offset::Utc;
use chrono::{DateTime, FixedOffset, Local, Offset};
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use std::path::PathBuf; use std::path::PathBuf;
@ -210,12 +210,11 @@ fn parse_cli(args: Vec<String>) -> Settings {
"set time described by STRING") "set time described by STRING")
(@arg utc: -u --utc --universal (@arg utc: -u --utc --universal
"print or set Coordinated Universal Time (UTC)")) "print or set Coordinated Universal Time (UTC)"))
// TODO: Decide whether this is appropriate. // TODO: Decide whether this is appropriate.
// The GNU date command has an explanation of all formatting options, // The GNU date command has an explanation of all formatting options,
// but the `chrono` crate has a few differences (most notably, the %Z option) // but the `chrono` crate has a few differences (most notably, the %Z option)
// (after_help: include_str!("usage.txt"))) // (after_help: include_str!("usage.txt")))
.get_matches_from(args); .get_matches_from(args);
let format = if let Some(form) = matches.value_of("custom_format") { let format = if let Some(form) = matches.value_of("custom_format") {
let form = form[1..].into(); let form = form[1..].into();

View file

@ -2,7 +2,8 @@
name = "dircolors" name = "dircolors"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_dircolors" name = "uu_dircolors"
@ -10,8 +11,8 @@ path = "dircolors.rs"
[dependencies] [dependencies]
glob = "0.3.0" glob = "0.3.0"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "dircolors" name = "dircolors"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -1,5 +1,4 @@
pub const INTERNAL_DB: &str = pub const INTERNAL_DB: &str = r#"# Configuration file for dircolors, a utility to help you set the
r#"# Configuration file for dircolors, a utility to help you set the
# LS_COLORS environment variable used by GNU ls with the --color option. # LS_COLORS environment variable used by GNU ls with the --color option.
# Copyright (C) 1996-2016 Free Software Foundation, Inc. # Copyright (C) 1996-2016 Free Software Foundation, Inc.
# Copying and distribution of this file, with or without modification, # Copying and distribution of this file, with or without modification,

View file

@ -13,10 +13,10 @@ extern crate glob;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::borrow::Borrow; use std::borrow::Borrow;
use std::env; use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
static SYNTAX: &str = "[OPTION]... [FILE]"; static SYNTAX: &str = "[OPTION]... [FILE]";
static SUMMARY: &str = "Output commands to set the LS_COLORS environment variable."; static SUMMARY: &str = "Output commands to set the LS_COLORS environment variable.";
@ -68,8 +68,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
.optflag("p", "print-database", "print the byte counts") .optflag("p", "print-database", "print the byte counts")
.parse(args); .parse(args);
if (matches.opt_present("csh") || matches.opt_present("c-shell") || matches.opt_present("sh") if (matches.opt_present("csh")
|| matches.opt_present("bourne-shell")) && matches.opt_present("print-database") || matches.opt_present("c-shell")
|| matches.opt_present("sh")
|| matches.opt_present("bourne-shell"))
&& matches.opt_present("print-database")
{ {
disp_err!( disp_err!(
"the options to output dircolors' internal database and\nto select a shell \ "the options to output dircolors' internal database and\nto select a shell \
@ -120,7 +123,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
Ok(f) => { Ok(f) => {
let fin = BufReader::new(f); let fin = BufReader::new(f);
result = parse( result = parse(
fin.lines().filter_map(|l| l.ok()), fin.lines().filter_map(Result::ok),
out_format, out_format,
matches.free[0].as_str(), matches.free[0].as_str(),
) )
@ -291,7 +294,7 @@ where
} else if key.starts_with('*') { } else if key.starts_with('*') {
result.push_str(format!("{}={}:", key, val).as_str()); result.push_str(format!("{}={}:", key, val).as_str());
} else if lower == "options" || lower == "color" || lower == "eightbit" { } else if lower == "options" || lower == "color" || lower == "eightbit" {
// Slackware only. Ignore // Slackware only. Ignore
} else if let Some(s) = table.get(lower.as_str()) { } else if let Some(s) = table.get(lower.as_str()) {
result.push_str(format!("{}={}:", s, val).as_str()); result.push_str(format!("{}={}:", s, val).as_str());
} else { } else {

View file

@ -2,7 +2,8 @@
name = "dirname" name = "dirname"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_dirname" name = "uu_dirname"
@ -10,8 +11,8 @@ path = "dirname.rs"
[dependencies] [dependencies]
libc = "0.2.42" libc = "0.2.42"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "dirname" name = "dirname"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -2,7 +2,8 @@
name = "du" name = "du"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_du" name = "uu_du"
@ -10,8 +11,8 @@ path = "du.rs"
[dependencies] [dependencies]
time = "0.1.40" time = "0.1.40"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "du" name = "du"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -163,44 +163,36 @@ fn du(
for f in read { for f in read {
match f { match f {
Ok(entry) => { Ok(entry) => match Stat::new(entry.path()) {
match Stat::new(entry.path()) { Ok(this_stat) => {
Ok(this_stat) => { if this_stat.is_dir {
if this_stat.is_dir { futures.push(du(this_stat, options, depth + 1, inodes));
futures.push(du(this_stat, options, depth + 1, inodes)); } else {
} else { if inodes.contains(&this_stat.inode) {
if inodes.contains(&this_stat.inode) { continue;
continue; }
} inodes.insert(this_stat.inode);
inodes.insert(this_stat.inode); my_stat.size += this_stat.size;
my_stat.size += this_stat.size; my_stat.blocks += this_stat.blocks;
my_stat.blocks += this_stat.blocks; if options.all {
if options.all { stats.push(this_stat);
stats.push(this_stat);
}
} }
} }
Err(error) => show_error!("{}", error),
} }
} Err(error) => show_error!("{}", error),
},
Err(error) => show_error!("{}", error), Err(error) => show_error!("{}", error),
} }
} }
} }
stats.extend(futures.into_iter().flat_map(|val| val).rev().filter_map( stats.extend(futures.into_iter().flatten().rev().filter(|stat| {
|stat| { if !options.separate_dirs && stat.path.parent().unwrap() == my_stat.path {
if !options.separate_dirs && stat.path.parent().unwrap() == my_stat.path { my_stat.size += stat.size;
my_stat.size += stat.size; my_stat.blocks += stat.blocks;
my_stat.blocks += stat.blocks; }
} options.max_depth == None || depth < options.max_depth.unwrap()
if options.max_depth == None || depth < options.max_depth.unwrap() { }));
Some(stat)
} else {
None
}
},
));
stats.push(my_stat); stats.push(my_stat);
Box::new(stats.into_iter()) Box::new(stats.into_iter())
} }
@ -216,7 +208,7 @@ fn convert_size_human(size: u64, multiplier: u64, _block_size: u64) -> String {
} }
fn convert_size_b(size: u64, _multiplier: u64, _block_size: u64) -> String { fn convert_size_b(size: u64, _multiplier: u64, _block_size: u64) -> String {
format!("{}", ((size as f64) / (1 as f64)).ceil()) format!("{}", ((size as f64) / (1_f64)).ceil())
} }
fn convert_size_k(size: u64, multiplier: u64, _block_size: u64) -> String { fn convert_size_k(size: u64, multiplier: u64, _block_size: u64) -> String {
@ -224,13 +216,17 @@ fn convert_size_k(size: u64, multiplier: u64, _block_size: u64) -> String {
} }
fn convert_size_m(size: u64, multiplier: u64, _block_size: u64) -> String { fn convert_size_m(size: u64, multiplier: u64, _block_size: u64) -> String {
format!("{}", ((size as f64) / ((multiplier * multiplier) as f64)).ceil()) format!(
"{}",
((size as f64) / ((multiplier * multiplier) as f64)).ceil()
)
} }
fn convert_size_other(size: u64, _multiplier: u64, block_size: u64) -> String { fn convert_size_other(size: u64, _multiplier: u64, block_size: u64) -> String {
format!("{}", ((size as f64) / (block_size as f64)).ceil()) format!("{}", ((size as f64) / (block_size as f64)).ceil())
} }
#[allow(clippy::cognitive_complexity)]
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let syntax = format!( let syntax = format!(
"[OPTION]... [FILE]... "[OPTION]... [FILE]...
@ -238,66 +234,109 @@ pub fn uumain(args: Vec<String>) -> i32 {
NAME NAME
); );
let matches = new_coreopts!(&syntax, SUMMARY, LONG_HELP) let matches = new_coreopts!(&syntax, SUMMARY, LONG_HELP)
// In task // In task
.optflag("a", "all", " write counts for all files, not just directories") .optflag(
// In main "a",
.optflag("", "apparent-size", "print apparent sizes, rather than disk usage "all",
" write counts for all files, not just directories",
)
// In main
.optflag(
"",
"apparent-size",
"print apparent sizes, rather than disk usage
although the apparent size is usually smaller, it may be larger due to holes although the apparent size is usually smaller, it may be larger due to holes
in ('sparse') files, internal fragmentation, indirect blocks, and the like") in ('sparse') files, internal fragmentation, indirect blocks, and the like",
// In main )
.optopt("B", "block-size", "scale sizes by SIZE before printing them. // In main
.optopt(
"B",
"block-size",
"scale sizes by SIZE before printing them.
E.g., '-BM' prints sizes in units of 1,048,576 bytes. See SIZE format below.", E.g., '-BM' prints sizes in units of 1,048,576 bytes. See SIZE format below.",
"SIZE") "SIZE",
// In main )
.optflag("b", "bytes", "equivalent to '--apparent-size --block-size=1'") // In main
// In main .optflag(
"b",
"bytes",
"equivalent to '--apparent-size --block-size=1'",
)
// In main
.optflag("c", "total", "produce a grand total") .optflag("c", "total", "produce a grand total")
// In task // In task
// opts.optflag("D", "dereference-args", "dereference only symlinks that are listed // opts.optflag("D", "dereference-args", "dereference only symlinks that are listed
// on the command line"), // on the command line"),
// In main // In main
// opts.optopt("", "files0-from", "summarize disk usage of the NUL-terminated file // opts.optopt("", "files0-from", "summarize disk usage of the NUL-terminated file
// names specified in file F; // names specified in file F;
// If F is - then read names from standard input", "F"), // If F is - then read names from standard input", "F"),
// // In task // // In task
// opts.optflag("H", "", "equivalent to --dereference-args (-D)"), // opts.optflag("H", "", "equivalent to --dereference-args (-D)"),
// In main // In main
.optflag("h", "human-readable", "print sizes in human readable format (e.g., 1K 234M 2G)") .optflag(
// In main "h",
"human-readable",
"print sizes in human readable format (e.g., 1K 234M 2G)",
)
// In main
.optflag("", "si", "like -h, but use powers of 1000 not 1024") .optflag("", "si", "like -h, but use powers of 1000 not 1024")
// In main // In main
.optflag("k", "", "like --block-size=1K") .optflag("k", "", "like --block-size=1K")
// In task // In task
.optflag("l", "count-links", "count sizes many times if hard linked") .optflag("l", "count-links", "count sizes many times if hard linked")
// // In main // // In main
.optflag("m", "", "like --block-size=1M") .optflag("m", "", "like --block-size=1M")
// // In task // // In task
// opts.optflag("L", "dereference", "dereference all symbolic links"), // opts.optflag("L", "dereference", "dereference all symbolic links"),
// // In task // // In task
// opts.optflag("P", "no-dereference", "don't follow any symbolic links (this is the default)"), // opts.optflag("P", "no-dereference", "don't follow any symbolic links (this is the default)"),
// // In main // // In main
.optflag("0", "null", "end each output line with 0 byte rather than newline") .optflag(
// In main "0",
.optflag("S", "separate-dirs", "do not include size of subdirectories") "null",
// In main "end each output line with 0 byte rather than newline",
)
// In main
.optflag(
"S",
"separate-dirs",
"do not include size of subdirectories",
)
// In main
.optflag("s", "summarize", "display only a total for each argument") .optflag("s", "summarize", "display only a total for each argument")
// // In task // // In task
// opts.optflag("x", "one-file-system", "skip directories on different file systems"), // opts.optflag("x", "one-file-system", "skip directories on different file systems"),
// // In task // // In task
// opts.optopt("X", "exclude-from", "exclude files that match any pattern in FILE", "FILE"), // opts.optopt("X", "exclude-from", "exclude files that match any pattern in FILE", "FILE"),
// // In task // // In task
// opts.optopt("", "exclude", "exclude files that match PATTERN", "PATTERN"), // opts.optopt("", "exclude", "exclude files that match PATTERN", "PATTERN"),
// In main // In main
.optopt("d", "max-depth", "print the total for a directory (or file, with --all) .optopt(
"d",
"max-depth",
"print the total for a directory (or file, with --all)
only if it is N or fewer levels below the command only if it is N or fewer levels below the command
line argument; --max-depth=0 is the same as --summarize", "N") line argument; --max-depth=0 is the same as --summarize",
// In main "N",
.optflagopt("", "time", "show time of the last modification of any file in the )
// In main
.optflagopt(
"",
"time",
"show time of the last modification of any file in the
directory, or any of its subdirectories. If WORD is given, show time as WORD instead directory, or any of its subdirectories. If WORD is given, show time as WORD instead
of modification time: atime, access, use, ctime or status", "WORD") of modification time: atime, access, use, ctime or status",
// In main "WORD",
.optopt("", "time-style", "show times using style STYLE: )
full-iso, long-iso, iso, +FORMAT FORMAT is interpreted like 'date'", "STYLE") // In main
.optopt(
"",
"time-style",
"show times using style STYLE:
full-iso, long-iso, iso, +FORMAT FORMAT is interpreted like 'date'",
"STYLE",
)
.parse(args); .parse(args);
let summarize = matches.opt_present("summarize"); let summarize = matches.opt_present("summarize");
@ -339,7 +378,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
}; };
let convert_size_fn = { let convert_size_fn = {
if matches.opt_present("human-readable") || matches.opt_present("si") { if matches.opt_present("human-readable") || matches.opt_present("si") {
convert_size_human convert_size_human
} else if matches.opt_present("b") { } else if matches.opt_present("b") {
convert_size_b convert_size_b
} else if matches.opt_present("k") { } else if matches.opt_present("k") {
@ -353,26 +392,24 @@ pub fn uumain(args: Vec<String>) -> i32 {
let convert_size = |size| convert_size_fn(size, multiplier, block_size); let convert_size = |size| convert_size_fn(size, multiplier, block_size);
let time_format_str = match matches.opt_str("time-style") { let time_format_str = match matches.opt_str("time-style") {
Some(s) => { Some(s) => match &s[..] {
match &s[..] { "full-iso" => "%Y-%m-%d %H:%M:%S.%f %z",
"full-iso" => "%Y-%m-%d %H:%M:%S.%f %z", "long-iso" => "%Y-%m-%d %H:%M",
"long-iso" => "%Y-%m-%d %H:%M", "iso" => "%Y-%m-%d",
"iso" => "%Y-%m-%d", _ => {
_ => { show_error!(
show_error!( "invalid argument '{}' for 'time style'
"invalid argument '{}' for 'time style'
Valid arguments are: Valid arguments are:
- 'full-iso' - 'full-iso'
- 'long-iso' - 'long-iso'
- 'iso' - 'iso'
Try '{} --help' for more information.", Try '{} --help' for more information.",
s, s,
NAME NAME
); );
return 1; return 1;
}
} }
} },
None => "%Y-%m-%d %H:%M", None => "%Y-%m-%d %H:%M",
}; };
@ -389,9 +426,7 @@ Try '{} --help' for more information.",
let (_, len) = iter.size_hint(); let (_, len) = iter.size_hint();
let len = len.unwrap(); let len = len.unwrap();
for (index, stat) in iter.enumerate() { for (index, stat) in iter.enumerate() {
let size = if matches.opt_present("apparent-size") { let size = if matches.opt_present("apparent-size") || matches.opt_present("b") {
stat.size
} else if matches.opt_present("b") {
stat.size stat.size
} else { } else {
// C's stat is such that each block is assume to be 512 bytes // C's stat is such that each block is assume to be 512 bytes
@ -402,23 +437,21 @@ Try '{} --help' for more information.",
let tm = { let tm = {
let (secs, nsecs) = { let (secs, nsecs) = {
let time = match matches.opt_str("time") { let time = match matches.opt_str("time") {
Some(s) => { Some(s) => match &s[..] {
match &s[..] { "accessed" => stat.accessed,
"accessed" => stat.accessed, "created" => stat.created,
"created" => stat.created, "modified" => stat.modified,
"modified" => stat.modified, _ => {
_ => { show_error!(
show_error!( "invalid argument 'modified' for '--time'
"invalid argument 'modified' for '--time'
Valid arguments are: Valid arguments are:
- 'accessed', 'created', 'modified' - 'accessed', 'created', 'modified'
Try '{} --help' for more information.", Try '{} --help' for more information.",
NAME NAME
); );
return 1; return 1;
}
} }
} },
None => stat.modified, None => stat.modified,
}; };
((time / 1000) as i64, (time % 1000 * 1_000_000) as i32) ((time / 1000) as i64, (time % 1000 * 1_000_000) as i32)

View file

@ -2,15 +2,16 @@
name = "echo" name = "echo"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_echo" name = "uu_echo"
path = "echo.rs" path = "echo.rs"
[dependencies] [dependencies]
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "echo" name = "echo"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -43,7 +43,7 @@ fn parse_code(
max_digits: u32, max_digits: u32,
bits_per_digit: u32, bits_per_digit: u32,
) -> Option<char> { ) -> Option<char> {
let mut ret = 0x80000000; let mut ret = 0x8000_0000;
for _ in 0..max_digits { for _ in 0..max_digits {
match input.peek().and_then(|c| c.to_digit(base)) { match input.peek().and_then(|c| c.to_digit(base)) {
Some(n) => ret = (ret << bits_per_digit) | n, Some(n) => ret = (ret << bits_per_digit) | n,
@ -71,8 +71,8 @@ fn print_escaped(input: &str, mut output: impl Write) -> io::Result<bool> {
'b' => '\x08', 'b' => '\x08',
'c' => { 'c' => {
should_stop = true; should_stop = true;
break break;
}, }
'e' => '\x1b', 'e' => '\x1b',
'f' => '\x0c', 'f' => '\x0c',
'n' => '\n', 'n' => '\n',
@ -90,7 +90,7 @@ fn print_escaped(input: &str, mut output: impl Write) -> io::Result<bool> {
_ => { _ => {
start = 0; start = 0;
next next
}, }
}; };
} }
} }
@ -110,7 +110,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
let matches = new_coreopts!(SYNTAX, SUMMARY, HELP) let matches = new_coreopts!(SYNTAX, SUMMARY, HELP)
.optflag("n", "", "do not output the trailing newline") .optflag("n", "", "do not output the trailing newline")
.optflag("e", "", "enable interpretation of backslash escapes") .optflag("e", "", "enable interpretation of backslash escapes")
.optflag("E", "", "disable interpretation of backslash escapes (default)") .optflag(
"E",
"",
"disable interpretation of backslash escapes (default)",
)
.parse(args); .parse(args);
let no_newline = matches.opt_present("n"); let no_newline = matches.opt_present("n");

7
src/env/Cargo.toml vendored
View file

@ -3,7 +3,8 @@ name = "env"
version = "0.0.1" version = "0.0.1"
authors = ["uutils developers"] authors = ["uutils developers"]
description = "Set each NAME to VALUE in the environment and run COMMAND" description = "Set each NAME to VALUE in the environment and run COMMAND"
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
edition = "2018" edition = "2018"
[lib] [lib]
@ -13,9 +14,9 @@ path = "env.rs"
[dependencies] [dependencies]
clap = "2.33" clap = "2.33"
libc = "0.2.42" libc = "0.2.42"
uucore = "0.0.1"
rust-ini = "0.13.0" rust-ini = "0.13.0"
uucore = "0.0.2"
[[bin]] [[bin]]
name = "env" name = "env"
path = "../../uumain.rs" path = "../#common/uumain.rs"

8
src/env/env.rs vendored
View file

@ -18,6 +18,7 @@ use ini::Ini;
use std::borrow::Cow; use std::borrow::Cow;
use std::env; use std::env;
use std::io::{self, Write}; use std::io::{self, Write};
use std::iter::Iterator;
use std::process::Command; use std::process::Command;
const USAGE: &str = "env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]"; const USAGE: &str = "env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]";
@ -89,7 +90,8 @@ fn load_config_file(opts: &mut Options) -> Result<(), i32> {
} }
}; };
for (_, prop) in &conf { // ignore all INI section lines (treat them as comments) for (_, prop) in &conf {
// ignore all INI section lines (treat them as comments)
for (key, value) in prop { for (key, value) in prop {
env::set_var(key, value); env::set_var(key, value);
} }
@ -159,11 +161,11 @@ fn run_env(args: Vec<String>) -> Result<(), i32> {
let null = matches.is_present("null"); let null = matches.is_present("null");
let files = matches let files = matches
.values_of("file") .values_of("file")
.map(|v| v.collect()) .map(Iterator::collect)
.unwrap_or_else(|| Vec::with_capacity(0)); .unwrap_or_else(|| Vec::with_capacity(0));
let unsets = matches let unsets = matches
.values_of("unset") .values_of("unset")
.map(|v| v.collect()) .map(Iterator::collect)
.unwrap_or_else(|| Vec::with_capacity(0)); .unwrap_or_else(|| Vec::with_capacity(0));
let mut opts = Options { let mut opts = Options {

View file

@ -2,17 +2,18 @@
name = "expand" name = "expand"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_expand" name = "uu_expand"
path = "expand.rs" path = "expand.rs"
[dependencies] [dependencies]
unicode-width = "0.1.5"
getopts = "0.2.18" getopts = "0.2.18"
uucore = "0.0.1" unicode-width = "0.1.5"
uucore = "0.0.2"
[[bin]] [[bin]]
name = "expand" name = "expand"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -45,7 +45,8 @@ fn tabstops_parse(s: String) -> Vec<usize> {
crash!(1, "{}\n", "tab size cannot be 0"); crash!(1, "{}\n", "tab size cannot be 0");
} }
if let (false, _) = nums.iter() if let (false, _) = nums
.iter()
.fold((true, 0), |(acc, last), &n| (acc && last <= n, n)) .fold((true, 0), |(acc, last), &n| (acc && last <= n, n))
{ {
crash!(1, "{}\n", "tab sizes must be ascending"); crash!(1, "{}\n", "tab sizes must be ascending");
@ -145,7 +146,7 @@ fn next_tabstop(tabstops: &[usize], col: usize) -> usize {
if tabstops.len() == 1 { if tabstops.len() == 1 {
tabstops[0] - col % tabstops[0] tabstops[0] - col % tabstops[0]
} else { } else {
match tabstops.iter().skip_while(|&&t| t <= col).next() { match tabstops.iter().find(|&&t| t > col) {
Some(t) => t - col, Some(t) => t - col,
None => 1, None => 1,
} }
@ -169,7 +170,7 @@ fn expand(options: Options) {
for file in options.files.into_iter() { for file in options.files.into_iter() {
let mut fh = open(file); let mut fh = open(file);
while match fh.read_until('\n' as u8, &mut buf) { while match fh.read_until(b'\n', &mut buf) {
Ok(s) => s > 0, Ok(s) => s > 0,
Err(_) => buf.is_empty(), Err(_) => buf.is_empty(),
} { } {

View file

@ -2,7 +2,8 @@
name = "expr" name = "expr"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_expr" name = "uu_expr"
@ -11,8 +12,8 @@ path = "expr.rs"
[dependencies] [dependencies]
libc = "0.2.42" libc = "0.2.42"
onig = "~4.3.2" onig = "~4.3.2"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "expr" name = "expr"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -13,8 +13,8 @@ extern crate onig;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
mod tokens;
mod syntax_tree; mod syntax_tree;
mod tokens;
static NAME: &str = "expr"; static NAME: &str = "expr";
static VERSION: &str = env!("CARGO_PKG_VERSION"); static VERSION: &str = env!("CARGO_PKG_VERSION");
@ -80,7 +80,7 @@ fn maybe_handle_help_or_version(args: &[String]) -> bool {
fn print_help() { fn print_help() {
//! The following is taken from GNU coreutils' "expr --help" output. //! The following is taken from GNU coreutils' "expr --help" output.
print!( println!(
r#"Usage: expr EXPRESSION r#"Usage: expr EXPRESSION
or: expr OPTION or: expr OPTION
@ -131,8 +131,7 @@ Environment variables:
* EXPR_DEBUG_TOKENS=1 dump expression's tokens * EXPR_DEBUG_TOKENS=1 dump expression's tokens
* EXPR_DEBUG_RPN=1 dump expression represented in reverse polish notation * EXPR_DEBUG_RPN=1 dump expression represented in reverse polish notation
* EXPR_DEBUG_SYA_STEP=1 dump each parser step * EXPR_DEBUG_SYA_STEP=1 dump each parser step
* EXPR_DEBUG_AST=1 dump expression represented abstract syntax tree * EXPR_DEBUG_AST=1 dump expression represented abstract syntax tree"#
"#
); );
} }

View file

@ -12,8 +12,8 @@
//! * https://en.wikipedia.org/wiki/Shunting-yard_algorithm //! * https://en.wikipedia.org/wiki/Shunting-yard_algorithm
//! //!
use tokens::Token;
use onig::{Regex, RegexOptions, Syntax}; use onig::{Regex, RegexOptions, Syntax};
use tokens::Token;
type TokenStack = Vec<(usize, Token)>; type TokenStack = Vec<(usize, Token)>;
pub type OperandsList = Vec<Box<ASTNode>>; pub type OperandsList = Vec<Box<ASTNode>>;
@ -68,9 +68,9 @@ impl ASTNode {
fn new_node(token_idx: usize, op_type: &str, operands: OperandsList) -> Box<ASTNode> { fn new_node(token_idx: usize, op_type: &str, operands: OperandsList) -> Box<ASTNode> {
Box::new(ASTNode::Node { Box::new(ASTNode::Node {
token_idx: token_idx, token_idx,
op_type: op_type.into(), op_type: op_type.into(),
operands: operands, operands,
}) })
} }
fn new_leaf(token_idx: usize, value: &str) -> Box<ASTNode> { fn new_leaf(token_idx: usize, value: &str) -> Box<ASTNode> {
@ -85,25 +85,27 @@ impl ASTNode {
ASTNode::Node { ref op_type, .. } => match self.operand_values() { ASTNode::Node { ref op_type, .. } => match self.operand_values() {
Err(reason) => Err(reason), Err(reason) => Err(reason),
Ok(operand_values) => match op_type.as_ref() { Ok(operand_values) => match op_type.as_ref() {
"+" => infix_operator_two_ints(|a: i64, b: i64| { "+" => infix_operator_two_ints(
checked_binop(|| a.checked_add(b), "+") |a: i64, b: i64| checked_binop(|| a.checked_add(b), "+"),
}, &operand_values &operand_values,
), ),
"-" => infix_operator_two_ints(|a: i64, b: i64| { "-" => infix_operator_two_ints(
checked_binop(|| a.checked_sub(b), "-") |a: i64, b: i64| checked_binop(|| a.checked_sub(b), "-"),
}, &operand_values &operand_values,
), ),
"*" => infix_operator_two_ints(|a: i64, b: i64| { "*" => infix_operator_two_ints(
checked_binop(|| a.checked_mul(b), "*") |a: i64, b: i64| checked_binop(|| a.checked_mul(b), "*"),
}, &operand_values &operand_values,
), ),
"/" => infix_operator_two_ints(|a: i64, b: i64| { "/" => infix_operator_two_ints(
|a: i64, b: i64| {
if b == 0 { if b == 0 {
Err("division by zero".to_owned()) Err("division by zero".to_owned())
} else { } else {
checked_binop(|| a.checked_div(b), "/") checked_binop(|| a.checked_div(b), "/")
} }
}, &operand_values },
&operand_values,
), ),
"%" => infix_operator_two_ints( "%" => infix_operator_two_ints(
|a: i64, b: i64| { |a: i64, b: i64| {
@ -158,7 +160,7 @@ impl ASTNode {
} }
} }
pub fn operand_values(&self) -> Result<Vec<String>, String> { pub fn operand_values(&self) -> Result<Vec<String>, String> {
if let &ASTNode::Node { ref operands, .. } = self { if let ASTNode::Node { ref operands, .. } = *self {
let mut out = Vec::with_capacity(operands.len()); let mut out = Vec::with_capacity(operands.len());
for operand in operands { for operand in operands {
match operand.evaluate() { match operand.evaluate() {
@ -219,6 +221,7 @@ fn maybe_dump_ast(result: &Result<Box<ASTNode>, String>) {
} }
} }
#[allow(clippy::ptr_arg)]
fn maybe_dump_rpn(rpn: &TokenStack) { fn maybe_dump_rpn(rpn: &TokenStack) {
use std::env; use std::env;
if let Ok(debug_var) = env::var("EXPR_DEBUG_RPN") { if let Ok(debug_var) = env::var("EXPR_DEBUG_RPN") {
@ -298,17 +301,29 @@ fn push_token_to_either_stack(
op_stack: &mut TokenStack, op_stack: &mut TokenStack,
) -> Result<(), String> { ) -> Result<(), String> {
let result = match *token { let result = match *token {
Token::Value { .. } => Ok(out_stack.push((token_idx, token.clone()))), Token::Value { .. } => {
out_stack.push((token_idx, token.clone()));
Ok(())
}
Token::InfixOp { .. } => if op_stack.is_empty() { Token::InfixOp { .. } => {
Ok(op_stack.push((token_idx, token.clone()))) if op_stack.is_empty() {
} else { op_stack.push((token_idx, token.clone()));
push_op_to_stack(token_idx, token, out_stack, op_stack) Ok(())
}, } else {
push_op_to_stack(token_idx, token, out_stack, op_stack)
}
}
Token::PrefixOp { .. } => Ok(op_stack.push((token_idx, token.clone()))), Token::PrefixOp { .. } => {
op_stack.push((token_idx, token.clone()));
Ok(())
}
Token::ParOpen => Ok(op_stack.push((token_idx, token.clone()))), Token::ParOpen => {
op_stack.push((token_idx, token.clone()));
Ok(())
}
Token::ParClose => move_till_match_paren(out_stack, op_stack), Token::ParClose => move_till_match_paren(out_stack, op_stack),
}; };
@ -316,6 +331,7 @@ fn push_token_to_either_stack(
result result
} }
#[allow(clippy::ptr_arg)]
fn maybe_dump_shunting_yard_step( fn maybe_dump_shunting_yard_step(
token_idx: usize, token_idx: usize,
token: &Token, token: &Token,
@ -341,15 +357,18 @@ fn push_op_to_stack(
out_stack: &mut TokenStack, out_stack: &mut TokenStack,
op_stack: &mut TokenStack, op_stack: &mut TokenStack,
) -> Result<(), String> { ) -> Result<(), String> {
if let &Token::InfixOp { if let Token::InfixOp {
precedence: prec, precedence: prec,
left_assoc: la, left_assoc: la,
.. ..
} = token } = *token
{ {
loop { loop {
match op_stack.last() { match op_stack.last() {
None => return Ok(op_stack.push((token_idx, token.clone()))), None => {
op_stack.push((token_idx, token.clone()));
return Ok(());
}
Some(&(_, Token::ParOpen)) => { Some(&(_, Token::ParOpen)) => {
op_stack.push((token_idx, token.clone())); op_stack.push((token_idx, token.clone()));
@ -362,12 +381,14 @@ fn push_op_to_stack(
precedence: prev_prec, precedence: prev_prec,
.. ..
}, },
)) => if la && prev_prec >= prec || !la && prev_prec > prec { )) => {
out_stack.push(op_stack.pop().unwrap()) if la && prev_prec >= prec || !la && prev_prec > prec {
} else { out_stack.push(op_stack.pop().unwrap())
op_stack.push((token_idx, token.clone())); } else {
return Ok(()); op_stack.push((token_idx, token.clone()));
}, return Ok(());
}
}
Some(&(_, Token::PrefixOp { .. })) => { Some(&(_, Token::PrefixOp { .. })) => {
op_stack.push((token_idx, token.clone())); op_stack.push((token_idx, token.clone()));
@ -487,10 +508,7 @@ fn prefix_operator_index(values: &[String]) -> Result<String, String> {
let haystack = &values[0]; let haystack = &values[0];
let needles = &values[1]; let needles = &values[1];
let mut current_idx = 0; for (current_idx, ch_h) in haystack.chars().enumerate() {
for ch_h in haystack.chars() {
current_idx += 1;
for ch_n in needles.chars() { for ch_n in needles.chars() {
if ch_n == ch_h { if ch_n == ch_h {
return Ok(current_idx.to_string()); return Ok(current_idx.to_string());

View file

@ -58,10 +58,7 @@ impl Token {
} }
fn is_a_number(&self) -> bool { fn is_a_number(&self) -> bool {
match *self { match *self {
Token::Value { ref value, .. } => match value.parse::<i64>() { Token::Value { ref value, .. } => value.parse::<i64>().is_ok(),
Ok(_) => true,
Err(_) => false,
},
_ => false, _ => false,
} }
} }
@ -144,12 +141,7 @@ fn maybe_dump_tokens_acc(tokens_acc: &[(usize, Token)]) {
} }
} }
fn push_token_if_not_escaped( fn push_token_if_not_escaped(acc: &mut Vec<(usize, Token)>, tok_idx: usize, token: Token, s: &str) {
acc: &mut Vec<(usize, Token)>,
tok_idx: usize,
token: Token,
s: &str,
) {
// Smells heuristics... :( // Smells heuristics... :(
let prev_is_plus = match acc.last() { let prev_is_plus = match acc.last() {
None => false, None => false,

View file

@ -2,6 +2,7 @@
name = "factor" name = "factor"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
license = "MIT"
[lib] [lib]
name = "uu_factor" name = "uu_factor"
@ -9,8 +10,8 @@ path = "factor.rs"
[dependencies] [dependencies]
rand = "0.5" rand = "0.5"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "factor" name = "factor"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -33,7 +33,7 @@ mod numeric;
mod sieve; mod sieve;
#[path = "../../mkmain.rs"] #[path = "../#common/mkmain.rs"]
mod mkmain; mod mkmain;
// extended Euclid algorithm // extended Euclid algorithm
@ -80,9 +80,8 @@ fn main() {
// By default, we print the multiplicative inverses mod 2^64 of the first 1k primes // By default, we print the multiplicative inverses mod 2^64 of the first 1k primes
let n = args() let n = args()
.skip(1) .nth(1)
.next() .unwrap_or_else(|| "1027".to_string())
.unwrap_or("1027".to_string())
.parse::<usize>() .parse::<usize>()
.ok() .ok()
.unwrap_or(1027); .unwrap_or(1027);
@ -115,7 +114,8 @@ fn main() {
file, file,
"\n];\n\n#[allow(dead_code)]\npub const NEXT_PRIME: u64 = {};\n", "\n];\n\n#[allow(dead_code)]\npub const NEXT_PRIME: u64 = {};\n",
x x
).unwrap(); )
.unwrap();
} }
#[test] #[test]
@ -131,12 +131,12 @@ fn test_inverter() {
#[test] #[test]
fn test_generator() { fn test_generator() {
let prime_10001 = Sieve::primes().skip(10000).next(); let prime_10001 = Sieve::primes().skip(10_000).next();
assert_eq!(prime_10001, Some(104743)); assert_eq!(prime_10001, Some(104_743));
} }
const MAX_WIDTH: usize = 102; const MAX_WIDTH: usize = 102;
const PREAMBLE: &'static str = r##"/* const PREAMBLE: &str = r##"/*
* This file is part of the uutils coreutils package. * This file is part of the uutils coreutils package.
* *
* (c) kwantam <kwantam@gmail.com> * (c) kwantam <kwantam@gmail.com>
@ -149,5 +149,6 @@ const PREAMBLE: &'static str = r##"/*
// Please do not edit by hand. Instead, modify and // Please do not edit by hand. Instead, modify and
// re-run src/factor/gen_tables.rs. // re-run src/factor/gen_tables.rs.
pub const P_INVS_U64: &'static [(u64, u64, u64)] = &[ #[allow(clippy::unreadable_literal)]
pub const P_INVS_U64: &[(u64, u64, u64)] = &[
"##; "##;

View file

@ -20,12 +20,12 @@ extern crate uucore;
use numeric::*; use numeric::*;
use rand::distributions::{Distribution, Uniform}; use rand::distributions::{Distribution, Uniform};
use rand::{SeedableRng, thread_rng};
use rand::rngs::SmallRng; use rand::rngs::SmallRng;
use rand::{thread_rng, SeedableRng};
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::io::{stdin, BufRead}; use std::io::{stdin, BufRead};
use std::num::Wrapping;
use std::mem::swap; use std::mem::swap;
use std::num::Wrapping;
mod numeric; mod numeric;
@ -53,6 +53,7 @@ fn gcd(mut a: u64, mut b: u64) -> u64 {
} }
fn rho_pollard_find_divisor(num: u64) -> u64 { fn rho_pollard_find_divisor(num: u64) -> u64 {
#![allow(clippy::many_single_char_names)]
let range = Uniform::new(1, num); let range = Uniform::new(1, num);
let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
let mut x = range.sample(&mut rng); let mut x = range.sample(&mut rng);
@ -154,7 +155,10 @@ fn print_factors(num: u64) {
} }
fn print_factors_str(num_str: &str) { fn print_factors_str(num_str: &str) {
if let Err(e) = num_str.parse::<u64>().and_then(|x| Ok(print_factors(x))) { if let Err(e) = num_str.parse::<u64>().and_then(|x| {
print_factors(x);
Ok(())
}) {
show_warning!("{}: {}", num_str, e); show_warning!("{}: {}", num_str, e);
} }
} }

View file

@ -9,8 +9,8 @@
* that was distributed with this source code. * that was distributed with this source code.
*/ */
use std::u64::MAX as MAX_U64;
use std::num::Wrapping; use std::num::Wrapping;
use std::u64::MAX as MAX_U64;
pub fn big_add(a: u64, b: u64, m: u64) -> u64 { pub fn big_add(a: u64, b: u64, m: u64) -> u64 {
let Wrapping(msb_mod_m) = Wrapping(MAX_U64) - Wrapping(m) + Wrapping(1); let Wrapping(msb_mod_m) = Wrapping(MAX_U64) - Wrapping(m) + Wrapping(1);
@ -125,7 +125,7 @@ pub fn is_prime(num: u64) -> bool {
// These witnesses detect all composites up to at least 2^64. // These witnesses detect all composites up to at least 2^64.
// Discovered by Jim Sinclair, according to http://miller-rabin.appspot.com // Discovered by Jim Sinclair, according to http://miller-rabin.appspot.com
let witnesses = [2, 325, 9375, 28178, 450775, 9780504, 1795265022]; let witnesses = [2, 325, 9_375, 28_178, 450_775, 9_780_504, 1_795_265_022];
!witnesses !witnesses
.iter() .iter()
.any(|&wit| witness(wit % num, exponent, num)) .any(|&wit| witness(wit % num, exponent, num))

View file

@ -68,6 +68,7 @@ impl Sieve {
#[allow(dead_code)] #[allow(dead_code)]
#[inline] #[inline]
pub fn primes() -> PrimeSieve { pub fn primes() -> PrimeSieve {
#[allow(clippy::trivially_copy_pass_by_ref)]
fn deref(x: &u64) -> u64 { fn deref(x: &u64) -> u64 {
*x *x
} }
@ -78,6 +79,7 @@ impl Sieve {
#[allow(dead_code)] #[allow(dead_code)]
#[inline] #[inline]
pub fn odd_primes() -> PrimeSieve { pub fn odd_primes() -> PrimeSieve {
#[allow(clippy::trivially_copy_pass_by_ref)]
fn deref(x: &u64) -> u64 { fn deref(x: &u64) -> u64 {
*x *x
} }
@ -124,11 +126,11 @@ impl Wheel {
/// The increments of a wheel of circumference 210 /// The increments of a wheel of circumference 210
/// (i.e., a wheel that skips all multiples of 2, 3, 5, 7) /// (i.e., a wheel that skips all multiples of 2, 3, 5, 7)
const WHEEL_INCS: &'static [u64] = &[ const WHEEL_INCS: &[u64] = &[
2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4, 2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4, 2, 4, 8, 6, 4, 6, 2, 4, 6,
2, 6, 6, 4, 2, 4, 6, 2, 6, 4, 2, 4, 2, 10, 2, 10, 2, 6, 6, 4, 2, 4, 6, 2, 6, 4, 2, 4, 2, 10, 2, 10,
]; ];
const INIT_PRIMES: &'static [u64] = &[2, 3, 5, 7]; const INIT_PRIMES: &[u64] = &[2, 3, 5, 7];
/// A min-heap of "infinite lists" of prime multiples, where a list is /// A min-heap of "infinite lists" of prime multiples, where a list is
/// represented as (head, increment). /// represented as (head, increment).

View file

@ -2,15 +2,16 @@
name = "false" name = "false"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_false" name = "uu_false"
path = "false.rs" path = "false.rs"
[dependencies] [dependencies]
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "false" name = "false"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -2,7 +2,8 @@
name = "fmt" name = "fmt"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_fmt" name = "uu_fmt"
@ -11,8 +12,8 @@ path = "fmt.rs"
[dependencies] [dependencies]
libc = "0.2.42" libc = "0.2.42"
unicode-width = "0.1.5" unicode-width = "0.1.5"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "fmt" name = "fmt"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -14,12 +14,12 @@ extern crate unicode_width;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::cmp;
use std::io::{BufReader, BufWriter, Read};
use std::fs::File;
use std::io::{stdin, stdout, Write};
use linebreak::break_lines; use linebreak::break_lines;
use parasplit::ParagraphStream; use parasplit::ParagraphStream;
use std::cmp;
use std::fs::File;
use std::io::{stdin, stdout, Write};
use std::io::{BufReader, BufWriter, Read};
macro_rules! silent_unwrap( macro_rules! silent_unwrap(
($exp:expr) => ( ($exp:expr) => (
@ -57,6 +57,7 @@ pub struct FmtOptions {
tabwidth: usize, tabwidth: usize,
} }
#[allow(clippy::cognitive_complexity)]
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP) let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optflag("c", "crown-margin", "First and second line of paragraph may have different indentations, in which case the first line's indentation is preserved, and each subsequent line's indentation matches the second line.") .optflag("c", "crown-margin", "First and second line of paragraph may have different indentations, in which case the first line's indentation is preserved, and each subsequent line's indentation matches the second line.")

View file

@ -7,12 +7,12 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
use FmtOptions;
use parasplit::{ParaWords, Paragraph, WordInfo}; use parasplit::{ParaWords, Paragraph, WordInfo};
use std::io::{BufWriter, Stdout, Write};
use std::i64;
use std::cmp; use std::cmp;
use std::i64;
use std::io::{BufWriter, Stdout, Write};
use std::mem; use std::mem;
use FmtOptions;
struct BreakArgs<'a> { struct BreakArgs<'a> {
opts: &'a FmtOptions, opts: &'a FmtOptions,
@ -58,18 +58,19 @@ pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<
} }
}; };
// 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 + if opts.crown || opts.tagged { let p_init_len = w_len
// handle "init" portion + if opts.crown || opts.tagged {
silent_unwrap!(ostream.write_all(para.init_str.as_bytes())); // handle "init" portion
para.init_len silent_unwrap!(ostream.write_all(para.init_str.as_bytes()));
} else if !para.mail_header { para.init_len
// for non-(crown, tagged) that's the same as a normal indent } else if !para.mail_header {
silent_unwrap!(ostream.write_all(p_indent.as_bytes())); // for non-(crown, tagged) that's the same as a normal indent
p_indent_len silent_unwrap!(ostream.write_all(p_indent.as_bytes()));
} else { p_indent_len
// except that mail headers get no indent at all } else {
0 // except that mail headers get no indent at all
}; 0
};
// write first word after writing init // write first word after writing init
silent_unwrap!(ostream.write_all(w.as_bytes())); silent_unwrap!(ostream.write_all(w.as_bytes()));
@ -218,17 +219,15 @@ fn find_kp_breakpoints<'a, T: Iterator<Item = &'a WordInfo<'a>>>(
) -> Vec<(&'a WordInfo<'a>, bool)> { ) -> Vec<(&'a WordInfo<'a>, bool)> {
let mut iter = iter.peekable(); let mut iter = iter.peekable();
// set up the initial null linebreak // set up the initial null linebreak
let mut linebreaks = vec![ let mut linebreaks = vec![LineBreak {
LineBreak { prev: 0,
prev: 0, linebreak: None,
linebreak: None, break_before: false,
break_before: false, demerits: 0,
demerits: 0, prev_rat: 0.0f32,
prev_rat: 0.0f32, length: args.init_len,
length: args.init_len, fresh: false,
fresh: false, }];
},
];
// this vec holds the current active linebreaks; next_ holds the breaks that will be active for // this vec holds the current active linebreaks; next_ holds the breaks that will be active for
// the next word // the next word
let active_breaks = &mut vec![0]; let active_breaks = &mut vec![0];
@ -275,7 +274,9 @@ fn find_kp_breakpoints<'a, T: Iterator<Item = &'a WordInfo<'a>>>(
} }
// get the new length // get the new length
let tlen = w.word_nchars + args.compute_width(w, active.length, active.fresh) + slen let tlen = w.word_nchars
+ args.compute_width(w, active.length, active.fresh)
+ slen
+ active.length; + active.length;
// if tlen is longer than args.opts.width, we drop this break from the active list // if tlen is longer than args.opts.width, we drop this break from the active list
@ -304,7 +305,8 @@ fn find_kp_breakpoints<'a, T: Iterator<Item = &'a WordInfo<'a>>>(
// do not even consider adding a line that has too many demerits // do not even consider adding a line that has too many demerits
// also, try to detect overflow by checking signum // also, try to detect overflow by checking signum
let total_demerits = new_demerits + active.demerits; let total_demerits = new_demerits + active.demerits;
if new_demerits < BAD_INFTY_SQ && total_demerits < ld_new if new_demerits < BAD_INFTY_SQ
&& total_demerits < ld_new
&& active.demerits.signum() <= new_demerits.signum() && active.demerits.signum() <= new_demerits.signum()
{ {
ld_new = total_demerits; ld_new = total_demerits;

View file

@ -7,8 +7,8 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
use std::iter::Peekable;
use std::io::{BufRead, Lines}; use std::io::{BufRead, Lines};
use std::iter::Peekable;
use std::slice::Iter; use std::slice::Iter;
use unicode_width::UnicodeWidthChar; use unicode_width::UnicodeWidthChar;
use FileOrStdReader; use FileOrStdReader;
@ -72,10 +72,7 @@ pub struct FileLines<'a> {
impl<'a> FileLines<'a> { impl<'a> FileLines<'a> {
fn new<'b>(opts: &'b FmtOptions, lines: Lines<&'b mut FileOrStdReader>) -> FileLines<'b> { fn new<'b>(opts: &'b FmtOptions, lines: Lines<&'b mut FileOrStdReader>) -> FileLines<'b> {
FileLines { FileLines { opts, lines }
opts,
lines,
}
} }
// returns true if this line should be formatted // returns true if this line should be formatted
@ -164,24 +161,28 @@ impl<'a> Iterator for FileLines<'a> {
// emit a blank line // emit a blank line
// Err(true) indicates that this was a linebreak, // Err(true) indicates that this was a linebreak,
// which is important to know when detecting mail headers // which is important to know when detecting mail headers
if n.chars().all(|c| c.is_whitespace()) { if n.chars().all(char::is_whitespace) {
return Some(Line::NoFormatLine("".to_owned(), true)); return Some(Line::NoFormatLine("".to_owned(), true));
} }
let (pmatch, poffset) = self.match_prefix(&n[..]);
// if this line does not match the prefix, // if this line does not match the prefix,
// emit the line unprocessed and iterate again // emit the line unprocessed and iterate again
let (pmatch, poffset) = self.match_prefix(&n[..]);
if !pmatch { if !pmatch {
return Some(Line::NoFormatLine(n, false)); return Some(Line::NoFormatLine(n, false));
} else if n[poffset + self.opts.prefix.len()..] }
.chars()
.all(|c| c.is_whitespace()) // if the line matches the prefix, but is blank after,
// don't allow lines to be combined through it (that is,
// treat it like a blank line, except that since it's
// not truly blank we will not allow mail headers on the
// following line)
if pmatch
&& n[poffset + self.opts.prefix.len()..]
.chars()
.all(char::is_whitespace)
{ {
// if the line matches the prefix, but is blank after,
// don't allow lines to be combined through it (that is,
// treat it like a blank line, except that since it's
// not truly blank we will not allow mail headers on the
// following line)
return Some(Line::NoFormatLine(n, false)); return Some(Line::NoFormatLine(n, false));
} }
@ -363,25 +364,31 @@ impl<'a> Iterator for ParagraphStream<'a> {
} }
} else if !second_done { } else if !second_done {
// now we have enough info to handle crown margin and tagged mode // now we have enough info to handle crown margin and tagged mode
// in both crown and tagged modes we require that prefix_len is the same
if prefix_len != fl.prefix_len || pfxind_end != fl.pfxind_end { if prefix_len != fl.prefix_len || pfxind_end != fl.pfxind_end {
// in both crown and tagged modes we require that prefix_len is the same
break; break;
} else if self.opts.tagged && indent_len - 4 == fl.indent_len }
// in tagged mode, indent has to be *different* on following lines
if self.opts.tagged
&& indent_len - 4 == fl.indent_len
&& indent_end == fl.indent_end && indent_end == fl.indent_end
{ {
// in tagged mode, indent has to be *different* on following lines
break; break;
} else {
// this is part of the same paragraph, get the indent info from this line
indent_str.clear();
indent_str.push_str(&fl.line[..fl.indent_end]);
indent_len = fl.indent_len;
indent_end = fl.indent_end;
} }
// this is part of the same paragraph, get the indent info from this line
indent_str.clear();
indent_str.push_str(&fl.line[..fl.indent_end]);
indent_len = fl.indent_len;
indent_end = fl.indent_end;
second_done = true; second_done = true;
} else { } else {
// detect mismatch // detect mismatch
if indent_end != fl.indent_end || pfxind_end != fl.pfxind_end if indent_end != fl.indent_end
|| pfxind_end != fl.pfxind_end
|| indent_len != fl.indent_len || indent_len != fl.indent_len
|| prefix_len != fl.prefix_len || prefix_len != fl.prefix_len
{ {

View file

@ -2,15 +2,16 @@
name = "fold" name = "fold"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_fold" name = "uu_fold"
path = "fold.rs" path = "fold.rs"
[dependencies] [dependencies]
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "fold" name = "fold"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -70,9 +70,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) { fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
for (i, arg) in args.iter().enumerate() { for (i, arg) in args.iter().enumerate() {
let slice = &arg; let slice = &arg;
if slice.starts_with('-') && slice.len() > 1 if slice.starts_with('-') && slice.len() > 1 && slice.chars().nth(1).unwrap().is_digit(10) {
&& slice.chars().nth(1).unwrap().is_digit(10)
{
let mut v = args.to_vec(); let mut v = args.to_vec();
v.remove(i); v.remove(i);
return (v, Some(slice[1..].to_owned())); return (v, Some(slice[1..].to_owned()));
@ -110,7 +108,7 @@ fn fold_file<T: Read>(mut file: BufReader<T>, bytes: bool, spaces: bool, width:
let slice = { let slice = {
let slice = &line[i..i + width]; let slice = &line[i..i + width];
if spaces && i + width < len { if spaces && i + width < len {
match slice.rfind(|ch: char| ch.is_whitespace()) { match slice.rfind(char::is_whitespace) {
Some(m) => &slice[..=m], Some(m) => &slice[..=m],
None => slice, None => slice,
} }
@ -145,11 +143,13 @@ fn fold_file<T: Read>(mut file: BufReader<T>, bytes: bool, spaces: bool, width:
let ncount = routput.chars().fold(0, |out, ch: char| { let ncount = routput.chars().fold(0, |out, ch: char| {
out + match ch { out + match ch {
'\t' => 8, '\t' => 8,
'\x08' => if out > 0 { '\x08' => {
!0 if out > 0 {
} else { !0
0 } else {
}, 0
}
}
'\r' => return 0, '\r' => return 0,
_ => 1, _ => 1,
} }

View file

@ -2,16 +2,16 @@
name = "groups" name = "groups"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_groups" name = "uu_groups"
path = "groups.rs" path = "groups.rs"
[dependencies.uucore] [dependencies]
version = "0.0.1" uucore = { version = "0.0.2", features = ["entries"] }
features = ["entries"]
[[bin]] [[bin]]
name = "groups" name = "groups"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -12,7 +12,7 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use uucore::entries::{get_groups, Locate, Passwd, gid2grp}; use uucore::entries::{get_groups, gid2grp, Locate, Passwd};
static SYNTAX: &str = "[user]"; static SYNTAX: &str = "[user]";
static SUMMARY: &str = "display current group names"; static SUMMARY: &str = "display current group names";

View file

@ -2,7 +2,8 @@
name = "hashsum" name = "hashsum"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_hashsum" name = "uu_hashsum"
@ -19,8 +20,8 @@ regex-syntax = "0.6.7"
sha1 = "0.6.0" sha1 = "0.6.0"
sha2 = "0.6.0" sha2 = "0.6.0"
sha3 = "0.6.0" sha3 = "0.6.0"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "hashsum" name = "hashsum"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -71,7 +71,7 @@ impl Digest for sha1::Sha1 {
// Implements the Digest trait for sha2 / sha3 algorithms with fixed ouput // Implements the Digest trait for sha2 / sha3 algorithms with fixed ouput
macro_rules! impl_digest_sha { macro_rules! impl_digest_sha {
($type: ty, $size: expr) => ( ($type: ty, $size: expr) => {
impl Digest for $type { impl Digest for $type {
fn new() -> Self { fn new() -> Self {
Self::default() Self::default()
@ -89,14 +89,16 @@ macro_rules! impl_digest_sha {
*self = Self::new(); *self = Self::new();
} }
fn output_bits(&self) -> usize { $size } fn output_bits(&self) -> usize {
$size
}
} }
) };
} }
// Implements the Digest trait for sha2 / sha3 algorithms with variable ouput // Implements the Digest trait for sha2 / sha3 algorithms with variable ouput
macro_rules! impl_digest_shake { macro_rules! impl_digest_shake {
($type: ty) => ( ($type: ty) => {
impl Digest for $type { impl Digest for $type {
fn new() -> Self { fn new() -> Self {
Self::default() Self::default()
@ -114,9 +116,11 @@ macro_rules! impl_digest_shake {
*self = Self::new(); *self = Self::new();
} }
fn output_bits(&self) -> usize { 0 } fn output_bits(&self) -> usize {
0
}
} }
) };
} }
impl_digest_sha!(sha2::Sha224, 224); impl_digest_sha!(sha2::Sha224, 224);

View file

@ -32,6 +32,7 @@ use regex::Regex;
use sha1::Sha1; 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::fs::File; use std::fs::File;
use std::io::{self, stdin, BufRead, BufReader, Read}; use std::io::{self, stdin, BufRead, BufReader, Read};
use std::path::Path; use std::path::Path;
@ -48,6 +49,7 @@ fn is_custom_binary(program: &str) -> bool {
} }
} }
#[allow(clippy::cognitive_complexity)]
fn detect_algo( fn detect_algo(
program: &str, program: &str,
matches: &getopts::Matches, matches: &getopts::Matches,
@ -64,10 +66,26 @@ fn detect_algo(
"sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box<dyn Digest>, 512), "sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box<dyn Digest>, 512),
"sha3sum" => match matches.opt_str("bits") { "sha3sum" => match matches.opt_str("bits") {
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) { Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
Ok(224) => ("SHA3-224", Box::new(Sha3_224::new()) as Box<dyn Digest>, 224), Ok(224) => (
Ok(256) => ("SHA3-256", Box::new(Sha3_256::new()) as Box<dyn Digest>, 256), "SHA3-224",
Ok(384) => ("SHA3-384", Box::new(Sha3_384::new()) as Box<dyn Digest>, 384), Box::new(Sha3_224::new()) as Box<dyn Digest>,
Ok(512) => ("SHA3-512", Box::new(Sha3_512::new()) as Box<dyn Digest>, 512), 224,
),
Ok(256) => (
"SHA3-256",
Box::new(Sha3_256::new()) as Box<dyn Digest>,
256,
),
Ok(384) => (
"SHA3-384",
Box::new(Sha3_384::new()) as Box<dyn Digest>,
384,
),
Ok(512) => (
"SHA3-512",
Box::new(Sha3_512::new()) as Box<dyn Digest>,
512,
),
Ok(_) => crash!( Ok(_) => crash!(
1, 1,
"Invalid output size for SHA3 (expected 224, 256, 384, or 512)" "Invalid output size for SHA3 (expected 224, 256, 384, or 512)"
@ -76,27 +94,51 @@ fn detect_algo(
}, },
None => crash!(1, "--bits required for SHA3"), None => crash!(1, "--bits required for SHA3"),
}, },
"sha3-224sum" => ("SHA3-224", Box::new(Sha3_224::new()) as Box<dyn Digest>, 224), "sha3-224sum" => (
"sha3-256sum" => ("SHA3-256", Box::new(Sha3_256::new()) as Box<dyn Digest>, 256), "SHA3-224",
"sha3-384sum" => ("SHA3-384", Box::new(Sha3_384::new()) as Box<dyn Digest>, 384), Box::new(Sha3_224::new()) as Box<dyn Digest>,
"sha3-512sum" => ("SHA3-512", Box::new(Sha3_512::new()) as Box<dyn Digest>, 512), 224,
),
"sha3-256sum" => (
"SHA3-256",
Box::new(Sha3_256::new()) as Box<dyn Digest>,
256,
),
"sha3-384sum" => (
"SHA3-384",
Box::new(Sha3_384::new()) as Box<dyn Digest>,
384,
),
"sha3-512sum" => (
"SHA3-512",
Box::new(Sha3_512::new()) as Box<dyn Digest>,
512,
),
"shake128sum" => match matches.opt_str("bits") { "shake128sum" => match matches.opt_str("bits") {
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) { Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
Ok(bits) => ("SHAKE128", Box::new(Shake128::new()) as Box<dyn Digest>, bits), Ok(bits) => (
"SHAKE128",
Box::new(Shake128::new()) as Box<dyn Digest>,
bits,
),
Err(err) => crash!(1, "{}", err), Err(err) => crash!(1, "{}", err),
}, },
None => crash!(1, "--bits required for SHAKE-128"), None => crash!(1, "--bits required for SHAKE-128"),
}, },
"shake256sum" => match matches.opt_str("bits") { "shake256sum" => match matches.opt_str("bits") {
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) { Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
Ok(bits) => ("SHAKE256", Box::new(Shake256::new()) as Box<dyn Digest>, bits), Ok(bits) => (
"SHAKE256",
Box::new(Shake256::new()) as Box<dyn Digest>,
bits,
),
Err(err) => crash!(1, "{}", err), Err(err) => crash!(1, "{}", err),
}, },
None => crash!(1, "--bits required for SHAKE-256"), None => crash!(1, "--bits required for SHAKE-256"),
}, },
_ => { _ => {
{ {
let mut set_or_crash = |n, val, bits| -> () { let mut set_or_crash = |n, val, bits| {
if alg.is_some() { if alg.is_some() {
crash!(1, "You cannot combine multiple hash algorithms!") crash!(1, "You cannot combine multiple hash algorithms!")
}; };
@ -318,17 +360,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
matches.free matches.free
}; };
match hashsum( match hashsum(
name, name, algo, files, binary, check, tag, status, quiet, strict, warn, bits,
algo,
files,
binary,
check,
tag,
status,
quiet,
strict,
warn,
bits,
) { ) {
Ok(()) => return 0, Ok(()) => return 0,
Err(e) => return e, Err(e) => return e,
@ -367,6 +399,8 @@ Compute and check message digests.",
print!("{}", opts.usage(&msg)); print!("{}", opts.usage(&msg));
} }
#[allow(clippy::cognitive_complexity)]
#[allow(clippy::too_many_arguments)]
fn hashsum( fn hashsum(
algoname: &str, algoname: &str,
mut digest: Box<dyn Digest>, mut digest: Box<dyn Digest>,
@ -442,11 +476,12 @@ fn hashsum(
let f = safe_unwrap!(File::open(ck_filename)); let f = safe_unwrap!(File::open(ck_filename));
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 = safe_unwrap!(digest_reader( let real_sum = safe_unwrap!(digest_reader(
&mut digest, &mut *digest,
&mut ckf, &mut ckf,
binary_check, binary_check,
output_bits output_bits
)).to_ascii_lowercase(); ))
.to_ascii_lowercase();
if sum == real_sum { if sum == real_sum {
if !quiet { if !quiet {
println!("{}: OK", ck_filename); println!("{}: OK", ck_filename);
@ -459,7 +494,7 @@ fn hashsum(
} }
} }
} else { } else {
let sum = safe_unwrap!(digest_reader(&mut digest, &mut file, binary, output_bits)); let sum = safe_unwrap!(digest_reader(&mut *digest, &mut file, binary, output_bits));
if tag { if tag {
println!("{} ({}) = {}", algoname, filename, sum); println!("{} ({}) = {}", algoname, filename, sum);
} else { } else {
@ -468,11 +503,11 @@ fn hashsum(
} }
} }
if !status { if !status {
if bad_format == 1 { match bad_format.cmp(&1) {
show_warning!("{} line is improperly formatted", bad_format); Ordering::Equal => show_warning!("{} line is improperly formatted", bad_format),
} else if bad_format > 1 { Ordering::Greater => show_warning!("{} lines are improperly formatted", bad_format),
show_warning!("{} lines are improperly formatted", bad_format); _ => {}
} };
if failed > 0 { if failed > 0 {
show_warning!("{} computed checksum did NOT match", failed); show_warning!("{} computed checksum did NOT match", failed);
} }
@ -482,7 +517,7 @@ fn hashsum(
} }
fn digest_reader<'a, T: Read>( fn digest_reader<'a, T: Read>(
digest: &mut Box<dyn Digest + 'a>, digest: &mut (dyn Digest + 'a),
reader: &mut BufReader<T>, reader: &mut BufReader<T>,
binary: bool, binary: bool,
output_bits: usize, output_bits: usize,

View file

@ -2,7 +2,8 @@
name = "head" name = "head"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_head" name = "uu_head"
@ -10,8 +11,8 @@ path = "head.rs"
[dependencies] [dependencies]
libc = "0.2.42" libc = "0.2.42"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "head" name = "head"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -14,8 +14,8 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::io::{stdin, BufRead, BufReader, Read};
use std::fs::File; use std::fs::File;
use std::io::{stdin, BufRead, BufReader, Read};
use std::path::Path; use std::path::Path;
use std::str::from_utf8; use std::str::from_utf8;
@ -90,12 +90,14 @@ pub fn uumain(args: Vec<String>) -> i32 {
} }
} }
} }
None => if let Some(count) = matches.opt_str("c") { None => {
match count.parse::<usize>() { if let Some(count) = matches.opt_str("c") {
Ok(m) => settings.mode = FilterMode::Bytes(m), match count.parse::<usize>() {
Err(e) => { Ok(m) => settings.mode = FilterMode::Bytes(m),
show_error!("invalid byte count '{}': {}", count, e); Err(e) => {
return 1; show_error!("invalid byte count '{}': {}", count, e);
return 1;
}
} }
} }
} }
@ -186,12 +188,16 @@ fn obsolete(options: &[String]) -> (Vec<String>, Option<usize>) {
// TODO: handle errors on read // TODO: handle errors on read
fn head<T: Read>(reader: &mut BufReader<T>, settings: &Settings) -> bool { fn head<T: Read>(reader: &mut BufReader<T>, settings: &Settings) -> bool {
match settings.mode { match settings.mode {
FilterMode::Bytes(count) => for byte in reader.bytes().take(count) { FilterMode::Bytes(count) => {
print!("{}", byte.unwrap() as char); for byte in reader.bytes().take(count) {
}, print!("{}", byte.unwrap() as char);
FilterMode::Lines(count) => for line in reader.lines().take(count) { }
println!("{}", line.unwrap()); }
}, FilterMode::Lines(count) => {
for line in reader.lines().take(count) {
println!("{}", line.unwrap());
}
}
} }
true true
} }

View file

@ -2,7 +2,8 @@
name = "hostid" name = "hostid"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_hostid" name = "uu_hostid"
@ -10,8 +11,8 @@ path = "hostid.rs"
[dependencies] [dependencies]
libc = "0.2.42" libc = "0.2.42"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "hostid" name = "hostid"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -43,6 +43,9 @@ fn hostid() {
result = gethostid(); result = gethostid();
} }
result &= 0xffff_ffff; #[allow(overflowing_literals)]
let mask = 0xffff_ffff;
result &= mask;
println!("{:0>8x}", result); println!("{:0>8x}", result);
} }

View file

@ -2,18 +2,19 @@
name = "hostname" name = "hostname"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_hostname" name = "uu_hostname"
path = "hostname.rs" path = "hostname.rs"
[dependencies] [dependencies]
libc = "0.2.42"
winapi = { version = "0.3", features = ["sysinfoapi", "winsock2"] }
getopts = "0.2" getopts = "0.2"
uucore = "0.0.1" libc = "0.2.42"
uucore = "0.0.2"
winapi = { version = "0.3", features = ["sysinfoapi", "winsock2"] }
[[bin]] [[bin]]
name = "hostname" name = "hostname"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -17,21 +17,21 @@ extern crate winapi;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::collections::hash_set::HashSet;
use std::iter::repeat;
use std::io;
use std::str;
use std::net::ToSocketAddrs;
use getopts::Matches; use getopts::Matches;
use std::collections::hash_set::HashSet;
use std::io;
use std::iter::repeat;
use std::net::ToSocketAddrs;
use std::str;
#[cfg(windows)] #[cfg(windows)]
use winapi::um::winsock2::{GetHostNameW, WSACleanup, WSAStartup}; use uucore::wide::*;
#[cfg(windows)]
use winapi::um::sysinfoapi::{ComputerNamePhysicalDnsHostname, SetComputerNameExW};
#[cfg(windows)] #[cfg(windows)]
use winapi::shared::minwindef::MAKEWORD; use winapi::shared::minwindef::MAKEWORD;
#[cfg(windows)] #[cfg(windows)]
use uucore::wide::*; use winapi::um::sysinfoapi::{ComputerNamePhysicalDnsHostname, SetComputerNameExW};
#[cfg(windows)]
use winapi::um::winsock2::{GetHostNameW, WSACleanup, WSAStartup};
#[cfg(not(windows))] #[cfg(not(windows))]
use libc::gethostname; use libc::gethostname;
@ -43,8 +43,10 @@ const SUMMARY: &str = "Print or set the system's host name.";
const LONG_HELP: &str = ""; const LONG_HELP: &str = "";
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
#![allow(clippy::let_and_return)]
#[cfg(windows)] #[cfg(windows)]
unsafe { unsafe {
#[allow(deprecated)]
let mut data = std::mem::uninitialized(); let mut data = std::mem::uninitialized();
if WSAStartup(MAKEWORD(2, 2), &mut data as *mut _) != 0 { if WSAStartup(MAKEWORD(2, 2), &mut data as *mut _) != 0 {
eprintln!("Failed to start Winsock 2.2"); eprintln!("Failed to start Winsock 2.2");
@ -61,12 +63,28 @@ pub fn uumain(args: Vec<String>) -> i32 {
fn execute(args: Vec<String>) -> i32 { fn execute(args: Vec<String>) -> i32 {
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP) let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optflag("d", "domain", "Display the name of the DNS domain if possible") .optflag(
.optflag("i", "ip-address", "Display the network address(es) of the host") "d",
"domain",
"Display the name of the DNS domain if possible",
)
.optflag(
"i",
"ip-address",
"Display the network address(es) of the host",
)
// TODO: support --long // TODO: support --long
.optflag("f", "fqdn", "Display the FQDN (Fully Qualified Domain Name) (default)") .optflag(
.optflag("s", "short", "Display the short hostname (the portion before the first dot) if \ "f",
possible") "fqdn",
"Display the FQDN (Fully Qualified Domain Name) (default)",
)
.optflag(
"s",
"short",
"Display the short hostname (the portion before the first dot) if \
possible",
)
.parse(args); .parse(args);
match matches.free.len() { match matches.free.len() {

View file

@ -2,16 +2,16 @@
name = "id" name = "id"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_id" name = "uu_id"
path = "id.rs" path = "id.rs"
[dependencies.uucore] [dependencies]
version = "0.0.1" uucore = { version = "0.0.2", features = ["entries", "process"] }
features = ["entries", "process"]
[[bin]] [[bin]]
name = "id" name = "id"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -16,28 +16,28 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::ffi::CStr;
use uucore::entries::{self, Group, Locate, Passwd};
pub use uucore::libc; pub use uucore::libc;
use uucore::libc::{getlogin, uid_t}; use uucore::libc::{getlogin, uid_t};
use uucore::entries::{self, Group, Locate, Passwd};
use uucore::process::{getegid, geteuid, getgid, getuid}; use uucore::process::{getegid, geteuid, getgid, getuid};
use std::ffi::CStr;
macro_rules! cstr2cow { macro_rules! cstr2cow {
($v:expr) => ( ($v:expr) => {
unsafe { CStr::from_ptr($v).to_string_lossy() } unsafe { CStr::from_ptr($v).to_string_lossy() }
) };
} }
#[cfg(not(target_os = "linux"))] #[cfg(not(target_os = "linux"))]
mod audit { mod audit {
pub use std::mem::uninitialized; use super::libc::{c_int, c_uint, dev_t, pid_t, uid_t};
use super::libc::{c_int, c_uint, dev_t, pid_t, uid_t, uint64_t};
pub type au_id_t = uid_t; pub type au_id_t = uid_t;
pub type au_asid_t = pid_t; pub type au_asid_t = pid_t;
pub type au_event_t = c_uint; pub type au_event_t = c_uint;
pub type au_emod_t = c_uint; pub type au_emod_t = c_uint;
pub type au_class_t = c_int; pub type au_class_t = c_int;
pub type au_flag_t = u64;
#[repr(C)] #[repr(C)]
pub struct au_mask { pub struct au_mask {
@ -58,7 +58,7 @@ mod audit {
pub ai_mask: au_mask_t, // Audit masks. pub ai_mask: au_mask_t, // Audit masks.
pub ai_termid: au_tid_addr_t, // Terminal ID. pub ai_termid: au_tid_addr_t, // Terminal ID.
pub ai_asid: au_asid_t, // Audit session ID. pub ai_asid: au_asid_t, // Audit session ID.
pub ai_flags: uint64_t, // Audit session flags pub ai_flags: au_flag_t, // Audit session flags
} }
pub type c_auditinfo_addr_t = c_auditinfo_addr; pub type c_auditinfo_addr_t = c_auditinfo_addr;
@ -67,8 +67,8 @@ mod audit {
} }
} }
static SYNTAX: &'static str = "[OPTION]... [USER]"; static SYNTAX: &str = "[OPTION]... [USER]";
static SUMMARY: &'static str = "Print user and group information for the specified USER,\n or (when USER omitted) for the current user."; static SUMMARY: &str = "Print user and group information for the specified USER,\n or (when USER omitted) for the current user.";
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = new_coreopts!(SYNTAX, SUMMARY, ""); let mut opts = new_coreopts!(SYNTAX, SUMMARY, "");
@ -117,7 +117,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
println!( println!(
"{}", "{}",
if nflag { if nflag {
entries::gid2grp(id).unwrap_or(id.to_string()) entries::gid2grp(id).unwrap_or_else(|_| id.to_string())
} else { } else {
id.to_string() id.to_string()
} }
@ -132,7 +132,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
println!( println!(
"{}", "{}",
if nflag { if nflag {
entries::uid2usr(id).unwrap_or(id.to_string()) entries::uid2usr(id).unwrap_or_else(|_| id.to_string())
} else { } else {
id.to_string() id.to_string()
} }
@ -146,7 +146,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
if nflag { if nflag {
possible_pw possible_pw
.map(|p| p.belongs_to()) .map(|p| p.belongs_to())
.unwrap_or(entries::get_groups().unwrap()) .unwrap_or_else(|| entries::get_groups().unwrap())
.iter() .iter()
.map(|&id| entries::gid2grp(id).unwrap()) .map(|&id| entries::gid2grp(id).unwrap())
.collect::<Vec<_>>() .collect::<Vec<_>>()
@ -154,7 +154,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
} else { } else {
possible_pw possible_pw
.map(|p| p.belongs_to()) .map(|p| p.belongs_to())
.unwrap_or(entries::get_groups().unwrap()) .unwrap_or_else(|| entries::get_groups().unwrap())
.iter() .iter()
.map(|&id| id.to_string()) .map(|&id| id.to_string())
.collect::<Vec<_>>() .collect::<Vec<_>>()
@ -238,7 +238,7 @@ fn pretty(possible_pw: Option<Passwd>) {
#[cfg(any(target_os = "macos", target_os = "freebsd"))] #[cfg(any(target_os = "macos", target_os = "freebsd"))]
fn pline(possible_uid: Option<uid_t>) { fn pline(possible_uid: Option<uid_t>) {
let uid = possible_uid.unwrap_or(getuid()); let uid = possible_uid.unwrap_or_else(getuid);
let pw = Passwd::locate(uid).unwrap(); let pw = Passwd::locate(uid).unwrap();
println!( println!(
@ -258,7 +258,7 @@ fn pline(possible_uid: Option<uid_t>) {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
fn pline(possible_uid: Option<uid_t>) { fn pline(possible_uid: Option<uid_t>) {
let uid = possible_uid.unwrap_or(getuid()); let uid = possible_uid.unwrap_or_else(getuid);
let pw = Passwd::locate(uid).unwrap(); let pw = Passwd::locate(uid).unwrap();
println!( println!(
@ -278,7 +278,8 @@ fn auditid() {}
#[cfg(not(target_os = "linux"))] #[cfg(not(target_os = "linux"))]
fn auditid() { fn auditid() {
let mut auditinfo: audit::c_auditinfo_addr_t = unsafe { audit::uninitialized() }; #[allow(deprecated)]
let mut auditinfo: audit::c_auditinfo_addr_t = unsafe { std::mem::uninitialized() };
let address = &mut auditinfo as *mut audit::c_auditinfo_addr_t; let address = &mut auditinfo as *mut audit::c_auditinfo_addr_t;
if unsafe { audit::getaudit(address) } < 0 { if unsafe { audit::getaudit(address) } < 0 {
println!("couldn't retrieve information"); println!("couldn't retrieve information");

View file

@ -2,7 +2,8 @@
name = "install" name = "install"
version = "0.0.1" version = "0.0.1"
authors = ["Ben Eills <ben@beneills.com>"] authors = ["Ben Eills <ben@beneills.com>"]
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_install" name = "uu_install"
@ -11,11 +12,11 @@ path = "install.rs"
[dependencies] [dependencies]
getopts = "0.2.18" getopts = "0.2.18"
libc = ">= 0.2" libc = ">= 0.2"
uucore = "0.0.1" uucore = "0.0.2"
[dev-dependencies] [dev-dependencies]
time = "0.1.40" time = "0.1.40"
[[bin]] [[bin]]
name = "install" name = "install"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -74,7 +74,8 @@ pub fn uumain(args: Vec<String>) -> i32 {
}; };
let paths: Vec<PathBuf> = { let paths: Vec<PathBuf> = {
fn string_to_path<'a>(s: &'a String) -> &'a Path { #[allow(clippy::ptr_arg)]
fn string_to_path(s: &String) -> &Path {
Path::new(s) Path::new(s)
}; };
let to_owned = |p: &Path| p.to_owned(); let to_owned = |p: &Path| p.to_owned();
@ -100,49 +101,115 @@ fn parse_opts(args: Vec<String>) -> getopts::Matches {
NAME NAME
); );
new_coreopts!(&syntax, SUMMARY, LONG_HELP) new_coreopts!(&syntax, SUMMARY, LONG_HELP)
// TODO implement flag // TODO implement flag
.optflagopt("", "backup", "(unimplemented) make a backup of each existing destination\n \ .optflagopt(
file", "CONTROL") "",
// TODO implement flag "backup",
.optflag("b", "", "(unimplemented) like --backup but does not accept an argument") "(unimplemented) make a backup of each existing destination\n \
file",
"CONTROL",
)
// TODO implement flag
.optflag(
"b",
"",
"(unimplemented) like --backup but does not accept an argument",
)
.optflag("c", "", "ignored") .optflag("c", "", "ignored")
// TODO implement flag // TODO implement flag
.optflag("C", "compare", "(unimplemented) compare each pair of source and destination\n \ .optflag(
files, and in some cases, do not modify the destination at all") "C",
.optflag("d", "directory", "treat all arguments as directory names.\n \ "compare",
create all components of the specified directories") "(unimplemented) compare each pair of source and destination\n \
// TODO implement flag files, and in some cases, do not modify the destination at all",
.optflag("D", "", "(unimplemented) create all leading components of DEST except the\n \ )
last, then copy SOURCE to DEST") .optflag(
// TODO implement flag "d",
.optflagopt("g", "group", "(unimplemented) set group ownership, instead of process'\n \ "directory",
current group", "GROUP") "treat all arguments as directory names.\n \
.optflagopt("m", "mode", "set permission mode (as in chmod), instead\n \ create all components of the specified directories",
of rwxr-xr-x", "MODE") )
// TODO implement flag // TODO implement flag
.optflagopt("o", "owner", "(unimplemented) set ownership (super-user only)", .optflag(
"OWNER") "D",
// TODO implement flag "",
.optflag("p", "preserve-timestamps", "(unimplemented) apply access/modification times\n \ "(unimplemented) create all leading components of DEST except the\n \
of SOURCE files to corresponding destination files") last, then copy SOURCE to DEST",
// TODO implement flag )
// TODO implement flag
.optflagopt(
"g",
"group",
"(unimplemented) set group ownership, instead of process'\n \
current group",
"GROUP",
)
.optflagopt(
"m",
"mode",
"set permission mode (as in chmod), instead\n \
of rwxr-xr-x",
"MODE",
)
// TODO implement flag
.optflagopt(
"o",
"owner",
"(unimplemented) set ownership (super-user only)",
"OWNER",
)
// TODO implement flag
.optflag(
"p",
"preserve-timestamps",
"(unimplemented) apply access/modification times\n \
of SOURCE files to corresponding destination files",
)
// TODO implement flag
.optflag("s", "strip", "(unimplemented) strip symbol tables") .optflag("s", "strip", "(unimplemented) strip symbol tables")
// TODO implement flag // TODO implement flag
.optflagopt("", "strip-program", "(unimplemented) program used to strip binaries", .optflagopt(
"PROGRAM") "",
// TODO implement flag "strip-program",
.optopt("S", "suffix", "(unimplemented) override the usual backup suffix", "SUFFIX") "(unimplemented) program used to strip binaries",
// TODO implement flag "PROGRAM",
.optopt("t", "target-directory", "(unimplemented) move all SOURCE arguments into\n \ )
DIRECTORY", "DIRECTORY") // TODO implement flag
// TODO implement flag .optopt(
.optflag("T", "no-target-directory", "(unimplemented) treat DEST as a normal file") "S",
"suffix",
"(unimplemented) override the usual backup suffix",
"SUFFIX",
)
// TODO implement flag
.optopt(
"t",
"target-directory",
"(unimplemented) move all SOURCE arguments into\n \
DIRECTORY",
"DIRECTORY",
)
// TODO implement flag
.optflag(
"T",
"no-target-directory",
"(unimplemented) treat DEST as a normal file",
)
.optflag("v", "verbose", "explain what is being done") .optflag("v", "verbose", "explain what is being done")
// TODO implement flag // TODO implement flag
.optflag("P", "preserve-context", "(unimplemented) preserve security context") .optflag(
// TODO implement flag "P",
.optflagopt("Z", "context", "(unimplemented) set security context of files and\n \ "preserve-context",
directories", "CONTEXT") "(unimplemented) preserve security context",
)
// TODO implement flag
.optflagopt(
"Z",
"context",
"(unimplemented) set security context of files and\n \
directories",
"CONTEXT",
)
.parse(args) .parse(args)
} }
@ -244,8 +311,8 @@ fn behaviour(matches: &getopts::Matches) -> Result<Behaviour, i32> {
}; };
Ok(Behaviour { Ok(Behaviour {
main_function: main_function, main_function,
specified_mode: specified_mode, specified_mode,
suffix: backup_suffix, suffix: backup_suffix,
verbose: matches.opt_present("v"), verbose: matches.opt_present("v"),
}) })
@ -259,7 +326,7 @@ fn behaviour(matches: &getopts::Matches) -> Result<Behaviour, i32> {
/// Returns an integer intended as a program return code. /// Returns an integer intended as a program return code.
/// ///
fn directory(paths: &[PathBuf], b: Behaviour) -> i32 { fn directory(paths: &[PathBuf], b: Behaviour) -> i32 {
if paths.len() < 1 { if paths.is_empty() {
println!("{} with -d requires at least one argument.", NAME); println!("{} with -d requires at least one argument.", NAME);
1 1
} else { } else {
@ -297,7 +364,7 @@ fn directory(paths: &[PathBuf], b: Behaviour) -> i32 {
/// Test if the path is a a new file path that can be /// Test if the path is a a new file path that can be
/// created immediately /// created immediately
fn is_new_file_path(path: &Path) -> bool { fn is_new_file_path(path: &Path) -> bool {
path.is_file() || !path.exists() && path.parent().map(|p| p.is_dir()).unwrap_or(true) path.is_file() || !path.exists() && path.parent().map(Path::is_dir).unwrap_or(true)
} }
/// Perform an install, given a list of paths and behaviour. /// Perform an install, given a list of paths and behaviour.

View file

@ -1,7 +1,7 @@
extern crate libc; extern crate libc;
use std::path::Path;
use std::fs; use std::fs;
use std::path::Path;
#[cfg(not(windows))] #[cfg(not(windows))]
use uucore::mode; use uucore::mode;

View file

@ -2,7 +2,8 @@
name = "join" name = "join"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_join" name = "uu_join"
@ -10,8 +11,8 @@ path = "join.rs"
[dependencies] [dependencies]
clap = "2.32.0" clap = "2.32.0"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "join" name = "join"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -14,10 +14,10 @@ extern crate clap;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use clap::{App, Arg};
use std::cmp::{min, Ordering};
use std::fs::File; use std::fs::File;
use std::io::{stdin, BufRead, BufReader, Lines, Stdin}; use std::io::{stdin, BufRead, BufReader, Lines, Stdin};
use std::cmp::{min, Ordering};
use clap::{App, Arg};
static NAME: &str = "join"; static NAME: &str = "join";
static VERSION: &str = env!("CARGO_PKG_VERSION"); static VERSION: &str = env!("CARGO_PKG_VERSION");
@ -186,7 +186,7 @@ impl Spec {
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 let None = chars.next() { if chars.next().is_none() {
return Spec::Key; return Spec::Key;
} }
@ -260,9 +260,9 @@ impl<'a> State<'a> {
}; };
State { State {
key: key, key,
file_name: name, file_name: name,
file_num: file_num, file_num,
print_unpaired: print_unpaired == file_num, print_unpaired: print_unpaired == file_num,
lines: f.lines(), lines: f.lines(),
seq: Vec::new(), seq: Vec::new(),
@ -294,7 +294,7 @@ impl<'a> State<'a> {
} }
} }
return None; None
} }
/// Print lines in the buffers as headers. /// Print lines in the buffers as headers.
@ -317,9 +317,9 @@ impl<'a> State<'a> {
for line1 in &self.seq { for line1 in &self.seq {
for line2 in &other.seq { for line2 in &other.seq {
if repr.uses_format() { if repr.uses_format() {
repr.print_format(|spec| match spec { repr.print_format(|spec| match *spec {
&Spec::Key => key, Spec::Key => key,
&Spec::Field(file_num, field_num) => { Spec::Field(file_num, field_num) => {
if file_num == self.file_num { if file_num == self.file_num {
return line1.get_field(field_num); return line1.get_field(field_num);
} }
@ -423,13 +423,15 @@ impl<'a> State<'a> {
fn print_line(&self, line: &Line, repr: &Repr) { fn print_line(&self, line: &Line, repr: &Repr) {
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),
&Spec::Field(file_num, field_num) => if file_num == self.file_num { Spec::Field(file_num, field_num) => {
line.get_field(field_num) if file_num == self.file_num {
} else { line.get_field(field_num)
None } else {
}, None
}
}
}); });
} else { } else {
repr.print_field(line.get_field(self.key)); repr.print_field(line.get_field(self.key));
@ -567,7 +569,7 @@ FILENUM is 1 or 2, corresponding to FILE1 or FILE2",
if let Some(value) = matches.value_of("t") { if let Some(value) = matches.value_of("t") {
settings.separator = match value.len() { settings.separator = match value.len() {
0 => Sep::Line, 0 => Sep::Line,
1 => Sep::Char(value.chars().nth(0).unwrap()), 1 => Sep::Char(value.chars().next().unwrap()),
_ => crash!(1, "multi-character tab {}", value), _ => crash!(1, "multi-character tab {}", value),
}; };
} }

View file

@ -2,7 +2,8 @@
name = "kill" name = "kill"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_kill" name = "uu_kill"
@ -10,11 +11,8 @@ path = "kill.rs"
[dependencies] [dependencies]
libc = "0.2.42" libc = "0.2.42"
uucore = { version = "0.0.2", features = ["signals"] }
[dependencies.uucore]
version = "0.0.1"
features = ["signals"]
[[bin]] [[bin]]
name = "kill" name = "kill"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -58,7 +58,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
return kill( return kill(
&matches &matches
.opt_str("signal") .opt_str("signal")
.unwrap_or(obs_signal.unwrap_or("9".to_owned())), .unwrap_or_else(|| obs_signal.unwrap_or_else(|| "9".to_owned())),
matches.free, matches.free,
) )
} }
@ -74,9 +74,7 @@ fn handle_obsolete(mut args: Vec<String>) -> (Vec<String>, Option<String>) {
while i < args.len() { while i < args.len() {
// this is safe because slice is valid when it is referenced // this is safe because slice is valid when it is referenced
let slice = &args[i].clone(); let slice = &args[i].clone();
if slice.chars().next().unwrap() == '-' && slice.len() > 1 if slice.starts_with('-') && slice.len() > 1 && slice.chars().nth(1).unwrap().is_digit(10) {
&& slice.chars().nth(1).unwrap().is_digit(10)
{
let val = &slice[1..]; let val = &slice[1..];
match val.parse() { match val.parse() {
Ok(num) => { Ok(num) => {
@ -107,7 +105,7 @@ fn table() {
//TODO: obtain max signal width here //TODO: obtain max signal width here
if (idx + 1) % 7 == 0 { if (idx + 1) % 7 == 0 {
println!(""); println!();
} }
} }
} }
@ -133,7 +131,7 @@ fn print_signals() {
pos += signal.name.len(); pos += signal.name.len();
print!("{}", signal.name); print!("{}", signal.name);
if idx > 0 && pos > 73 { if idx > 0 && pos > 73 {
println!(""); println!();
pos = 0; pos = 0;
} else { } else {
pos += 1; pos += 1;

View file

@ -2,7 +2,8 @@
name = "link" name = "link"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_link" name = "uu_link"
@ -10,8 +11,8 @@ path = "link.rs"
[dependencies] [dependencies]
libc = "0.2.42" libc = "0.2.42"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "link" name = "link"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -13,8 +13,8 @@
extern crate uucore; extern crate uucore;
use std::fs::hard_link; use std::fs::hard_link;
use std::path::Path;
use std::io::Error; use std::io::Error;
use std::path::Path;
static SYNTAX: &str = "[OPTIONS] FILE1 FILE2"; static SYNTAX: &str = "[OPTIONS] FILE1 FILE2";
static SUMMARY: &str = "Create a link named FILE2 to FILE1"; static SUMMARY: &str = "Create a link named FILE2 to FILE1";

View file

@ -2,7 +2,8 @@
name = "ln" name = "ln"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_ln" name = "uu_ln"
@ -10,8 +11,8 @@ path = "ln.rs"
[dependencies] [dependencies]
libc = "0.2.42" libc = "0.2.42"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "ln" name = "ln"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -67,24 +67,45 @@ pub fn uumain(args: Vec<String>) -> i32 {
NAME NAME
); );
let matches = new_coreopts!(&syntax, SUMMARY, LONG_HELP) let matches = new_coreopts!(&syntax, SUMMARY, LONG_HELP)
.optflag("b", "", "make a backup of each file that would otherwise be overwritten or \ .optflag(
removed") "b",
.optflagopt("", "backup", "make a backup of each file that would otherwise be overwritten \ "",
or removed", "METHOD") "make a backup of each file that would otherwise be overwritten or \
// TODO: opts.optflag("d", "directory", "allow users with appropriate privileges to attempt \ removed",
// to make hard links to directories"); )
.optflagopt(
"",
"backup",
"make a backup of each file that would otherwise be overwritten \
or removed",
"METHOD",
)
// TODO: opts.optflag("d", "directory", "allow users with appropriate privileges to attempt \
// to make hard links to directories");
.optflag("f", "force", "remove existing destination files") .optflag("f", "force", "remove existing destination files")
.optflag("i", "interactive", "prompt whether to remove existing destination files") .optflag(
// TODO: opts.optflag("L", "logical", "dereference TARGETs that are symbolic links"); "i",
// TODO: opts.optflag("n", "no-dereference", "treat LINK_NAME as a normal file if it is a \ "interactive",
// symbolic link to a directory"); "prompt whether to remove existing destination files",
// TODO: opts.optflag("P", "physical", "make hard links directly to symbolic links"); )
// TODO: opts.optflag("r", "relative", "create symbolic links relative to link location"); // TODO: opts.optflag("L", "logical", "dereference TARGETs that are symbolic links");
// TODO: opts.optflag("n", "no-dereference", "treat LINK_NAME as a normal file if it is a \
// symbolic link to a directory");
// TODO: opts.optflag("P", "physical", "make hard links directly to symbolic links");
// TODO: opts.optflag("r", "relative", "create symbolic links relative to link location");
.optflag("s", "symbolic", "make symbolic links instead of hard links") .optflag("s", "symbolic", "make symbolic links instead of hard links")
.optopt("S", "suffix", "override the usual backup suffix", "SUFFIX") .optopt("S", "suffix", "override the usual backup suffix", "SUFFIX")
.optopt("t", "target-directory", "specify the DIRECTORY in which to create the links", .optopt(
"DIRECTORY") "t",
.optflag("T", "no-target-directory", "treat LINK_NAME as a normal file always") "target-directory",
"specify the DIRECTORY in which to create the links",
"DIRECTORY",
)
.optflag(
"T",
"no-target-directory",
"treat LINK_NAME as a normal file always",
)
.optflag("v", "verbose", "print name of each linked file") .optflag("v", "verbose", "print name of each linked file")
.parse(args); .parse(args);
@ -159,7 +180,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
} }
fn exec(files: &[PathBuf], settings: &Settings) -> i32 { fn exec(files: &[PathBuf], settings: &Settings) -> i32 {
if files.len() == 0 { if files.is_empty() {
show_error!( show_error!(
"missing file operand\nTry '{} --help' for more information.", "missing file operand\nTry '{} --help' for more information.",
NAME NAME
@ -201,7 +222,7 @@ fn exec(files: &[PathBuf], settings: &Settings) -> i32 {
); );
return 1; return 1;
} }
assert!(files.len() != 0); assert!(!files.is_empty());
match link(&files[0], &files[1], settings) { match link(&files[0], &files[1], settings) {
Ok(_) => 0, Ok(_) => 0,
@ -295,7 +316,7 @@ fn link(src: &PathBuf, dst: &PathBuf, settings: &Settings) -> Result<()> {
print!("'{}' -> '{}'", dst.display(), src.display()); print!("'{}' -> '{}'", dst.display(), src.display());
match backup_path { match backup_path {
Some(path) => println!(" (backup: '{}')", path.display()), Some(path) => println!(" (backup: '{}')", path.display()),
None => println!(""), None => println!(),
} }
} }
Ok(()) Ok(())
@ -304,7 +325,7 @@ fn link(src: &PathBuf, dst: &PathBuf, settings: &Settings) -> Result<()> {
fn read_yes() -> bool { fn read_yes() -> bool {
let mut s = String::new(); let mut s = String::new();
match stdin().read_line(&mut s) { match stdin().read_line(&mut s) {
Ok(_) => match s.char_indices().nth(0) { Ok(_) => match s.char_indices().next() {
Some((_, x)) => x == 'y' || x == 'Y', Some((_, x)) => x == 'y' || x == 'Y',
_ => false, _ => false,
}, },

View file

@ -2,7 +2,8 @@
name = "logname" name = "logname"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_logname" name = "uu_logname"
@ -10,8 +11,8 @@ path = "logname.rs"
[dependencies] [dependencies]
libc = "0.2.42" libc = "0.2.42"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "logname" name = "logname"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -2,7 +2,8 @@
name = "ls" name = "ls"
version = "0.0.1" version = "0.0.1"
authors = ["Jeremiah Peschka <jeremiah.peschka@gmail.com>"] authors = ["Jeremiah Peschka <jeremiah.peschka@gmail.com>"]
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_ls" name = "uu_ls"
@ -11,17 +12,14 @@ path = "ls.rs"
[dependencies] [dependencies]
getopts = "0.2.18" getopts = "0.2.18"
isatty = "0.1" isatty = "0.1"
lazy_static = "1.0.1"
number_prefix = "0.2.8" number_prefix = "0.2.8"
term_grid = "0.1.5" term_grid = "0.1.5"
termsize = "0.1.6" termsize = "0.1.6"
time = "0.1.40" time = "0.1.40"
lazy_static = "1.0.1"
unicode-width = "0.1.5" unicode-width = "0.1.5"
uucore = { version = "0.0.2", features = ["entries", "fs"] }
[dependencies.uucore]
version = "0.0.1"
features = ["entries", "fs"]
[[bin]] [[bin]]
name = "ls" name = "ls"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -9,43 +9,41 @@
// //
extern crate getopts; extern crate getopts;
#[cfg(unix)]
extern crate isatty;
extern crate number_prefix;
extern crate term_grid; extern crate term_grid;
extern crate termsize; extern crate termsize;
extern crate time; extern crate time;
extern crate unicode_width; extern crate unicode_width;
extern crate number_prefix;
extern crate isatty;
use isatty::stdout_isatty;
use number_prefix::{Standalone, Prefixed, decimal_prefix};
use term_grid::{Cell, Direction, Filling, Grid, GridOptions};
use time::{strftime, Timespec};
#[cfg(unix)] #[cfg(unix)]
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
#[cfg(unix)]
use uucore::libc::{mode_t, S_ISGID, S_ISUID, S_ISVTX, S_IWOTH,
S_IXGRP, S_IXOTH, S_IXUSR};
use std::fs; #[cfg(unix)]
use std::fs::{DirEntry, FileType, Metadata}; use isatty::stdout_isatty;
use std::path::{Path, PathBuf}; use number_prefix::{decimal_prefix, Prefixed, Standalone};
use std::cmp::Reverse; use std::cmp::Reverse;
#[cfg(unix)] #[cfg(unix)]
use std::collections::HashMap; use std::collections::HashMap;
use std::fs;
#[cfg(any(unix, target_os = "redox"))] use std::fs::{DirEntry, FileType, Metadata};
use std::os::unix::fs::MetadataExt;
#[cfg(unix)] #[cfg(unix)]
use std::os::unix::fs::FileTypeExt; use std::os::unix::fs::FileTypeExt;
#[cfg(unix)] #[cfg(any(unix, target_os = "redox"))]
use unicode_width::UnicodeWidthStr; use std::os::unix::fs::MetadataExt;
#[cfg(windows)] #[cfg(windows)]
use std::os::windows::fs::MetadataExt; use std::os::windows::fs::MetadataExt;
use std::path::{Path, PathBuf};
use term_grid::{Cell, Direction, Filling, Grid, GridOptions};
use time::{strftime, Timespec};
#[cfg(unix)]
use unicode_width::UnicodeWidthStr;
#[cfg(unix)]
use uucore::libc::{mode_t, S_ISGID, S_ISUID, S_ISVTX, S_IWOTH, S_IXGRP, S_IXOTH, S_IXUSR};
static NAME: &str = "ls"; static NAME: &str = "ls";
static SUMMARY: &str = ""; static SUMMARY: &str = "";
@ -60,12 +58,13 @@ static DEFAULT_COLORS: &str = "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do
#[cfg(unix)] #[cfg(unix)]
lazy_static! { lazy_static! {
static ref LS_COLORS: String = std::env::var("LS_COLORS").unwrap_or(DEFAULT_COLORS.to_string()); static ref LS_COLORS: String =
std::env::var("LS_COLORS").unwrap_or_else(|_| DEFAULT_COLORS.to_string());
static ref COLOR_MAP: HashMap<&'static str, &'static str> = { static ref COLOR_MAP: HashMap<&'static str, &'static str> = {
let codes = LS_COLORS.split(":"); let codes = LS_COLORS.split(':');
let mut map = HashMap::new(); let mut map = HashMap::new();
for c in codes { for c in codes {
let p: Vec<_> = c.split("=").collect(); let p: Vec<_> = c.split('=').collect();
if p.len() == 2 { if p.len() == 2 {
map.insert(p[0], p[1]); map.insert(p[0], p[1]);
} }
@ -160,7 +159,12 @@ pub fn uumain(args: Vec<String>) -> i32 {
directory. This is especially useful when listing very large directories, \ directory. This is especially useful when listing very large directories, \
since not doing any sorting can be noticeably faster.", since not doing any sorting can be noticeably faster.",
) )
.optflagopt("", "color", "Color output based on file type.", "always|auto|never") .optflagopt(
"",
"color",
"Color output based on file type.",
"always|auto|never",
)
.parse(args); .parse(args);
list(matches); list(matches);
@ -171,7 +175,7 @@ fn list(options: getopts::Matches) {
let locs: Vec<String> = if options.free.is_empty() { let locs: Vec<String> = if options.free.is_empty() {
vec![String::from(".")] vec![String::from(".")]
} else { } else {
options.free.iter().cloned().collect() options.free.to_vec()
}; };
let mut files = Vec::<PathBuf>::new(); let mut files = Vec::<PathBuf>::new();
@ -277,20 +281,17 @@ fn max(lhs: usize, rhs: usize) -> usize {
fn should_display(entry: &DirEntry, options: &getopts::Matches) -> bool { fn should_display(entry: &DirEntry, options: &getopts::Matches) -> bool {
let ffi_name = entry.file_name(); let ffi_name = entry.file_name();
let name = ffi_name.to_string_lossy(); let name = ffi_name.to_string_lossy();
if !options.opt_present("a") && !options.opt_present("A") { if !options.opt_present("a") && !options.opt_present("A") && name.starts_with('.') {
if name.starts_with('.') { return false;
return false;
}
} }
if options.opt_present("B") && name.ends_with('~') { if options.opt_present("B") && name.ends_with('~') {
return false; return false;
} }
return true; true
} }
fn enter_directory(dir: &PathBuf, options: &getopts::Matches) { fn enter_directory(dir: &PathBuf, options: &getopts::Matches) {
let mut entries = let mut entries: Vec<_> = safe_unwrap!(fs::read_dir(dir).and_then(Iterator::collect));
safe_unwrap!(fs::read_dir(dir).and_then(|e| e.collect::<Result<Vec<_>, _>>()));
entries.retain(|e| should_display(e, options)); entries.retain(|e| should_display(e, options));
@ -316,7 +317,7 @@ fn enter_directory(dir: &PathBuf, options: &getopts::Matches) {
fn get_metadata(entry: &PathBuf, options: &getopts::Matches) -> std::io::Result<Metadata> { fn get_metadata(entry: &PathBuf, options: &getopts::Matches) -> std::io::Result<Metadata> {
if options.opt_present("L") { if options.opt_present("L") {
entry.metadata().or(entry.symlink_metadata()) entry.metadata().or_else(|_| entry.symlink_metadata())
} else { } else {
entry.symlink_metadata() entry.symlink_metadata()
} }
@ -336,14 +337,14 @@ fn display_dir_entry_size(entry: &PathBuf, options: &getopts::Matches) -> (usize
fn pad_left(string: String, count: usize) -> String { fn pad_left(string: String, count: usize) -> String {
if count > string.len() { if count > string.len() {
let pad = count - string.len(); let pad = count - string.len();
let pad = String::from_utf8(vec![' ' as u8; pad]).unwrap(); let pad = String::from_utf8(vec![b' '; pad]).unwrap();
format!("{}{}", pad, string) format!("{}{}", pad, string)
} else { } else {
string string
} }
} }
fn display_items(items: &Vec<PathBuf>, strip: Option<&Path>, options: &getopts::Matches) { fn display_items(items: &[PathBuf], strip: Option<&Path>, options: &getopts::Matches) {
if options.opt_present("long") || options.opt_present("numeric-uid-gid") { if options.opt_present("long") || options.opt_present("numeric-uid-gid") {
let (mut max_links, mut max_size) = (1, 1); let (mut max_links, mut max_size) = (1, 1);
for item in items { for item in items {
@ -356,19 +357,17 @@ fn display_items(items: &Vec<PathBuf>, strip: Option<&Path>, options: &getopts::
} }
} else { } else {
if !options.opt_present("1") { if !options.opt_present("1") {
let names = items let names = items.iter().filter_map(|i| {
.iter() let md = get_metadata(i, options);
.filter_map(|i| { match md {
let md = get_metadata(i, options); Err(e) => {
match md { let filename = get_file_name(i, strip);
Err(e) => { show_error!("{}: {}", filename, e);
let filename = get_file_name(i, strip); None
show_error!("{}: {}", filename, e);
None
}
Ok(md) => Some(display_file_name(&i, strip, &md, options)),
} }
}); Ok(md) => Some(display_file_name(&i, strip, &md, options)),
}
});
if let Some(size) = termsize::get() { if let Some(size) = termsize::get() {
let mut grid = Grid::new(GridOptions { let mut grid = Grid::new(GridOptions {
@ -454,7 +453,7 @@ fn display_uname(metadata: &Metadata, options: &getopts::Matches) -> String {
if options.opt_present("numeric-uid-gid") { if options.opt_present("numeric-uid-gid") {
metadata.uid().to_string() metadata.uid().to_string()
} else { } else {
entries::uid2usr(metadata.uid()).unwrap_or(metadata.uid().to_string()) entries::uid2usr(metadata.uid()).unwrap_or_else(|_| metadata.uid().to_string())
} }
} }
@ -463,7 +462,7 @@ fn display_group(metadata: &Metadata, options: &getopts::Matches) -> String {
if options.opt_present("numeric-uid-gid") { if options.opt_present("numeric-uid-gid") {
metadata.gid().to_string() metadata.gid().to_string()
} else { } else {
entries::gid2grp(metadata.gid()).unwrap_or(metadata.gid().to_string()) entries::gid2grp(metadata.gid()).unwrap_or_else(|_| metadata.gid().to_string())
} }
} }
@ -511,7 +510,7 @@ fn display_file_size(metadata: &Metadata, options: &getopts::Matches) -> String
if options.opt_present("human-readable") { if options.opt_present("human-readable") {
match decimal_prefix(metadata.len() as f64) { match decimal_prefix(metadata.len() as f64) {
Standalone(bytes) => bytes.to_string(), Standalone(bytes) => bytes.to_string(),
Prefixed(prefix, bytes) => format!("{:.2}{}", bytes, prefix).to_uppercase() Prefixed(prefix, bytes) => format!("{:.2}{}", bytes, prefix).to_uppercase(),
} }
} else { } else {
metadata.len().to_string() metadata.len().to_string()
@ -533,7 +532,7 @@ fn get_file_name(name: &Path, strip: Option<&Path>) -> String {
Some(prefix) => name.strip_prefix(prefix).unwrap_or(name), Some(prefix) => name.strip_prefix(prefix).unwrap_or(name),
None => name, None => name,
}; };
if name.as_os_str().len() == 0 { if name.as_os_str().is_empty() {
name = Path::new("."); name = Path::new(".");
} }
name.to_string_lossy().into_owned() name.to_string_lossy().into_owned()
@ -595,12 +594,13 @@ fn color_name(name: String, typ: &str) -> String {
#[cfg(unix)] #[cfg(unix)]
macro_rules! has { macro_rules! has {
($mode:expr, $perm:expr) => ( ($mode:expr, $perm:expr) => {
$mode & ($perm as mode_t) != 0 $mode & ($perm as mode_t) != 0
) };
} }
#[cfg(unix)] #[cfg(unix)]
#[allow(clippy::cognitive_complexity)]
fn display_file_name( fn display_file_name(
path: &Path, path: &Path,
strip: Option<&Path>, strip: Option<&Path>,
@ -618,7 +618,7 @@ fn display_file_name(
Some(val) => match val.as_ref() { Some(val) => match val.as_ref() {
"always" | "yes" | "force" => true, "always" | "yes" | "force" => true,
"auto" | "tty" | "if-tty" => stdout_isatty(), "auto" | "tty" | "if-tty" => stdout_isatty(),
"never" | "no" | "none" | _ => false, /* "never" | "no" | "none" | */ _ => false,
}, },
}; };
let classify = options.opt_present("classify"); let classify = options.opt_present("classify");
@ -697,7 +697,7 @@ fn display_file_name(
Cell { Cell {
contents: name, contents: name,
width: width, width,
} }
} }

View file

@ -2,7 +2,8 @@
name = "mkdir" name = "mkdir"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_mkdir" name = "uu_mkdir"
@ -11,8 +12,8 @@ path = "mkdir.rs"
[dependencies] [dependencies]
getopts = "0.2.18" getopts = "0.2.18"
libc = "0.2.42" libc = "0.2.42"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "mkdir" name = "mkdir"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -55,16 +55,15 @@ pub fn uumain(args: Vec<String>) -> i32 {
// Translate a ~str in octal form to u16, default to 755 // Translate a ~str in octal form to u16, default to 755
// Not tested on Windows // Not tested on Windows
let mode_match = matches.opts_str(&["mode".to_owned()]); let mode_match = matches.opts_str(&["mode".to_owned()]);
let mode: u16 = if mode_match.is_some() { let mode: u16 = match mode_match {
let m = mode_match.unwrap(); Some(m) => {
let res: Option<u16> = u16::from_str_radix(&m, 8).ok(); let res: Option<u16> = u16::from_str_radix(&m, 8).ok();
if res.is_some() { match res {
res.unwrap() Some(r) => r,
} else { _ => crash!(1, "no mode given"),
crash!(1, "no mode given"); }
} }
} else { _ => 0o755 as u16,
0o755 as u16
}; };
let dirs = matches.free; let dirs = matches.free;
@ -76,7 +75,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
fn print_help(opts: &getopts::Options) { fn print_help(opts: &getopts::Options) {
println!("{} {}", NAME, VERSION); println!("{} {}", NAME, VERSION);
println!(""); println!();
println!("Usage:"); println!("Usage:");
print!( print!(
"{}", "{}",
@ -113,7 +112,11 @@ fn exec(dirs: Vec<String>, recursive: bool, mode: u16, verbose: bool) -> i32 {
* Wrapper to catch errors, return 1 if failed * Wrapper to catch errors, return 1 if failed
*/ */
fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> i32 { fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> i32 {
let create_dir = if recursive { fs::create_dir_all } else { fs::create_dir }; let create_dir = if recursive {
fs::create_dir_all
} else {
fs::create_dir
};
if let Err(e) = create_dir(path) { if let Err(e) = create_dir(path) {
show_info!("{}: {}", path.display(), e.to_string()); show_info!("{}: {}", path.display(), e.to_string());
return 1; return 1;
@ -125,17 +128,13 @@ fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> i32 {
#[cfg(any(unix, target_os = "redox"))] #[cfg(any(unix, target_os = "redox"))]
fn chmod(path: &Path, mode: u16) -> i32 { fn chmod(path: &Path, mode: u16) -> i32 {
use fs::{Permissions, set_permissions}; use fs::{set_permissions, Permissions};
use std::os::unix::fs::{PermissionsExt}; use std::os::unix::fs::PermissionsExt;
let mode = Permissions::from_mode(mode as u32); let mode = Permissions::from_mode(u32::from(mode));
if let Err(err) = set_permissions(path, mode) { if let Err(err) = set_permissions(path, mode) {
show_error!( show_error!("{}: {}", path.display(), err);
"{}: {}",
path.display(),
err
);
return 1; return 1;
} }
0 0

View file

@ -2,7 +2,8 @@
name = "mkfifo" name = "mkfifo"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_mkfifo" name = "uu_mkfifo"
@ -11,8 +12,8 @@ path = "mkfifo.rs"
[dependencies] [dependencies]
getopts = "0.2.18" getopts = "0.2.18"
libc = "0.2.42" libc = "0.2.42"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "mkfifo" name = "mkfifo"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -76,10 +76,8 @@ Create a FIFO with the given name.",
let mut exit_status = 0; let mut exit_status = 0;
for f in &matches.free { for f in &matches.free {
let err = unsafe { let err = unsafe {
mkfifo( let name = CString::new(f.as_bytes()).unwrap();
CString::new(f.as_bytes()).unwrap().as_ptr(), mkfifo(name.as_ptr(), mode as libc::mode_t)
mode as libc::mode_t,
)
}; };
if err == -1 { if err == -1 {
show_error!( show_error!(

View file

@ -2,7 +2,8 @@
name = "mknod" name = "mknod"
version = "0.0.1" version = "0.0.1"
authors = [] authors = []
build = "../../mkmain.rs" license = "MIT"
build = "../#common/mkmain.rs"
[lib] [lib]
name = "uu_mknod" name = "uu_mknod"
@ -11,8 +12,8 @@ path = "mknod.rs"
[dependencies] [dependencies]
getopts = "0.2.18" getopts = "0.2.18"
libc = "^0.2.42" libc = "^0.2.42"
uucore = "0.0.1" uucore = "0.0.2"
[[bin]] [[bin]]
name = "mknod" name = "mknod"
path = "../../uumain.rs" path = "../#common/uumain.rs"

View file

@ -31,8 +31,7 @@ const MODE_RW_UGO: mode_t = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_
#[inline(always)] #[inline(always)]
fn makedev(maj: u64, min: u64) -> dev_t { fn makedev(maj: u64, min: u64) -> dev_t {
// pick up from <sys/sysmacros.h> // pick up from <sys/sysmacros.h>
((min & 0xff) | ((maj & 0xfff) << 8) | (((min & !0xff)) << 12) | (((maj & !0xfff)) << 32)) ((min & 0xff) | ((maj & 0xfff) << 8) | ((min & !0xff) << 12) | ((maj & !0xfff) << 32)) as dev_t
as dev_t
} }
#[cfg(windows)] #[cfg(windows)]
@ -45,6 +44,7 @@ fn _makenod(path: CString, mode: mode_t, dev: dev_t) -> i32 {
unsafe { libc::mknod(path.as_ptr(), mode, dev) } unsafe { libc::mknod(path.as_ptr(), mode, dev) }
} }
#[allow(clippy::cognitive_complexity)]
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = Options::new(); let mut opts = Options::new();
@ -130,7 +130,7 @@ for details about the options it supports.",
// 'mknod /dev/rst0 character 18 0'. // 'mknod /dev/rst0 character 18 0'.
let ch = args[1] let ch = args[1]
.chars() .chars()
.nth(0) .next()
.expect("Failed to get the first char"); .expect("Failed to get the first char");
if ch == 'p' { if ch == 'p' {

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