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

Format everything using rustfmt

This commit is contained in:
Alex Lyon 2018-03-12 01:20:58 -07:00
parent 00a8b0b0f1
commit 880a4973c1
162 changed files with 7895 additions and 5056 deletions

166
Cargo.lock generated
View file

@ -45,6 +45,7 @@ dependencies = [
name = "base32" name = "base32"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"clippy 0.0.143 (registry+https://github.com/rust-lang/crates.io-index)",
"uucore 0.0.1", "uucore 0.0.1",
] ]
@ -109,6 +110,16 @@ name = "byteorder"
version = "1.2.1" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cargo_metadata"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "cat" name = "cat"
version = "0.0.1" version = "0.0.1"
@ -149,6 +160,7 @@ dependencies = [
name = "chown" name = "chown"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"clippy 0.0.143 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"uucore 0.0.1", "uucore 0.0.1",
"walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -193,6 +205,32 @@ dependencies = [
"vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "clippy"
version = "0.0.143"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy_lints 0.0.143 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "clippy_lints"
version = "0.0.143"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itertools 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "cmake" name = "cmake"
version = "0.1.29" version = "0.1.29"
@ -345,6 +383,11 @@ dependencies = [
"uucore 0.0.1", "uucore 0.0.1",
] ]
[[package]]
name = "dtoa"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "du" name = "du"
version = "0.0.1" version = "0.0.1"
@ -564,6 +607,7 @@ version = "0.0.1"
dependencies = [ dependencies = [
"getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"uucore 0.0.1", "uucore 0.0.1",
] ]
@ -580,6 +624,11 @@ dependencies = [
"either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "itoa"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "join" name = "join"
version = "0.0.1" version = "0.0.1"
@ -673,6 +722,11 @@ dependencies = [
"uucore 0.0.1", "uucore 0.0.1",
] ]
[[package]]
name = "matches"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "md5" name = "md5"
version = "0.3.7" version = "0.3.7"
@ -970,6 +1024,14 @@ dependencies = [
"uucore 0.0.1", "uucore 0.0.1",
] ]
[[package]]
name = "proc-macro2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "ptx" name = "ptx"
version = "0.0.1" version = "0.0.1"
@ -996,11 +1058,24 @@ name = "quick-error"
version = "1.2.1" version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quine-mc_cluskey"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "quote" name = "quote"
version = "0.3.15" version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.3.22" version = "0.3.22"
@ -1137,6 +1212,14 @@ dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "semver"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "semver" name = "semver"
version = "0.7.0" version = "0.7.0"
@ -1158,6 +1241,42 @@ dependencies = [
"uucore 0.0.1", "uucore 0.0.1",
] ]
[[package]]
name = "serde"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_derive"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive_internals 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive_internals"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_json"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "sha1" name = "sha1"
version = "0.2.0" version = "0.2.0"
@ -1275,6 +1394,16 @@ dependencies = [
"uucore 0.0.1", "uucore 0.0.1",
] ]
[[package]]
name = "syn"
version = "0.12.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "sync" name = "sync"
version = "0.0.1" version = "0.0.1"
@ -1410,6 +1539,14 @@ dependencies = [
"uucore 0.0.1", "uucore 0.0.1",
] ]
[[package]]
name = "toml"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "touch" name = "touch"
version = "0.0.1" version = "0.0.1"
@ -1490,6 +1627,11 @@ dependencies = [
"uucore 0.0.1", "uucore 0.0.1",
] ]
[[package]]
name = "unicode-normalization"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.4" version = "0.1.4"
@ -1500,6 +1642,11 @@ name = "unicode-xid"
version = "0.0.4" version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "unindent" name = "unindent"
version = "0.1.2" version = "0.1.2"
@ -1726,6 +1873,7 @@ dependencies = [
name = "who" name = "who"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"clippy 0.0.143 (registry+https://github.com/rust-lang/crates.io-index)",
"uucore 0.0.1", "uucore 0.0.1",
] ]
@ -1797,10 +1945,13 @@ dependencies = [
"checksum block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1339a1042f5d9f295737ad4d9a6ab6bf81c84a933dba110b9200cd6d1448b814" "checksum block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1339a1042f5d9f295737ad4d9a6ab6bf81c84a933dba110b9200cd6d1448b814"
"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
"checksum cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "be1057b8462184f634c3a208ee35b0f935cfd94b694b26deadccd98732088d7b"
"checksum cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fedf677519ac9e865c4ff43ef8f930773b37ed6e6ea61b6b83b400a7b5787f49" "checksum cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fedf677519ac9e865c4ff43ef8f930773b37ed6e6ea61b6b83b400a7b5787f49"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9" "checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9"
"checksum clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dc18f6f4005132120d9711636b32c46a233fad94df6217fa1d81c5e97a9f200" "checksum clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dc18f6f4005132120d9711636b32c46a233fad94df6217fa1d81c5e97a9f200"
"checksum clippy 0.0.143 (registry+https://github.com/rust-lang/crates.io-index)" = "0ba494d48c1022ff0ded833291488c399a24cf4b3ca42c90870a4e354f73482b"
"checksum clippy_lints 0.0.143 (registry+https://github.com/rust-lang/crates.io-index)" = "305d52f00df613fc9078dddbd820f03242a2682d57d9f7e7628891bdc041dbf5"
"checksum cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "56d741ea7a69e577f6d06b36b7dff4738f680593dc27a701ffa8506b73ce28bb" "checksum cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "56d741ea7a69e577f6d06b36b7dff4738f680593dc27a701ffa8506b73ce28bb"
"checksum cpp 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b244cf36c028e27227d6e7f9963c768dff5a1c06d5e01ff97f12ef4e05afd57c" "checksum cpp 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b244cf36c028e27227d6e7f9963c768dff5a1c06d5e01ff97f12ef4e05afd57c"
"checksum cpp_build 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9aebc7c97550a8c6a1f48dbaf078b033dc546e1b5badde300767099d4cace8f" "checksum cpp_build 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e9aebc7c97550a8c6a1f48dbaf078b033dc546e1b5badde300767099d4cace8f"
@ -1811,6 +1962,7 @@ dependencies = [
"checksum cpp_synom 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc8da5694233b646150c785118f77835ad0a49680c7f312a10ef30957c67b6d" "checksum cpp_synom 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc8da5694233b646150c785118f77835ad0a49680c7f312a10ef30957c67b6d"
"checksum data-encoding 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d867ddbf09de0b73e09ec798972fb7f870495a0893f6f736c1855448c5a56789" "checksum data-encoding 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d867ddbf09de0b73e09ec798972fb7f870495a0893f6f736c1855448c5a56789"
"checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" "checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
"checksum duct 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8c553d79f40e74f7f611e49bf3429b6760cff79596b61818291c27cc0b18549d" "checksum duct 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8c553d79f40e74f7f611e49bf3429b6760cff79596b61818291c27cc0b18549d"
"checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3" "checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
@ -1826,11 +1978,13 @@ dependencies = [
"checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" "checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa"
"checksum ioctl-sys 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5e2c4b26352496eaaa8ca7cfa9bd99e93419d3f7983dc6e99c2a35fe9e33504a" "checksum ioctl-sys 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5e2c4b26352496eaaa8ca7cfa9bd99e93419d3f7983dc6e99c2a35fe9e33504a"
"checksum itertools 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3f2be4da1690a039e9ae5fd575f706a63ad5a2120f161b1d653c9da3930dd21" "checksum itertools 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3f2be4da1690a039e9ae5fd575f706a63ad5a2120f161b1d653c9da3930dd21"
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
"checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b" "checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b"
"checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff" "checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff"
"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
"checksum md5 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "daa1004633f76cdcd5a9d83ffcfe615e30ca7a2a638fcc8b8039a2dac21289d7" "checksum md5 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "daa1004633f76cdcd5a9d83ffcfe615e30ca7a2a638fcc8b8039a2dac21289d7"
"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.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
@ -1848,8 +2002,11 @@ dependencies = [
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
"checksum platform-info 0.1.0 (git+https://github.com/uutils/platform-info)" = "<none>" "checksum platform-info 0.1.0 (git+https://github.com/uutils/platform-info)" = "<none>"
"checksum pretty-bytes 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "009d6edd2c1dbf2e1c0cd48a2f7766e03498d49ada7109a01c6911815c685316" "checksum pretty-bytes 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "009d6edd2c1dbf2e1c0cd48a2f7766e03498d49ada7109a01c6911815c685316"
"checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0"
"checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4"
"checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408"
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd"
@ -1861,13 +2018,19 @@ dependencies = [
"checksum remove_dir_all 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5d2f806b0fcdabd98acd380dc8daef485e22bcb7cddc811d1337967f2528cf5" "checksum remove_dir_all 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5d2f806b0fcdabd98acd380dc8daef485e22bcb7cddc811d1337967f2528cf5"
"checksum rust-users 0.6.0 (git+https://github.com/uutils/rust-users)" = "<none>" "checksum rust-users 0.6.0 (git+https://github.com/uutils/rust-users)" = "<none>"
"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7" "checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7"
"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
"checksum semver 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd61b85a0fa777f7fb7c454b9189b2941b110d1385ce84d7f76efdf1606a85" "checksum semver 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd61b85a0fa777f7fb7c454b9189b2941b110d1385ce84d7f76efdf1606a85"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "4763b773978e495252615e814d2ad04773b2c1f85421c7913869a537f35cb406"
"checksum serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "8ab31f00ae5574bb643c196d5e302961c122da1c768604c6d16a35c5d551948a"
"checksum serde_derive_internals 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc848d073be32cd982380c06587ea1d433bc1a4c4a111de07ec2286a3ddade8"
"checksum serde_json 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "fab6c4d75bedcf880711c85e39ebf8ccc70d0eba259899047ec5d7436643ee17"
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" "checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
"checksum sha2 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d963c78ce367df26d7ea8b8cc655c651b42e8a1e584e869c1e17dae3ccb116a" "checksum sha2 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d963c78ce367df26d7ea8b8cc655c651b42e8a1e584e869c1e17dae3ccb116a"
"checksum sha3 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26405905b6a56a94c60109cfda62610507ac14a65be531f5767dec5c5a8dd6a0" "checksum sha3 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26405905b6a56a94c60109cfda62610507ac14a65be531f5767dec5c5a8dd6a0"
"checksum shared_child 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bcd5e483b3475af9bc2a35311c2f3bbf0bd98fde91410ab15a0d4ba3c3127b4e" "checksum shared_child 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bcd5e483b3475af9bc2a35311c2f3bbf0bd98fde91410ab15a0d4ba3c3127b4e"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8c5bc2d6ff27891209efa5f63e9de78648d7801f085e4653701a692ce938d6fd"
"checksum tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f73eebdb68c14bcb24aef74ea96079830e7fa7b31a6106e42ea7ee887c1e134e" "checksum tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f73eebdb68c14bcb24aef74ea96079830e7fa7b31a6106e42ea7ee887c1e134e"
"checksum tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ce2fe9db64b842314052e2421ac61a73ce41b898dc8e3750398b219c5fc1e0" "checksum tempfile 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ce2fe9db64b842314052e2421ac61a73ce41b898dc8e3750398b219c5fc1e0"
"checksum term_grid 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "230d3e804faaed5a39b08319efb797783df2fd9671b39b7596490cb486d702cf" "checksum term_grid 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "230d3e804faaed5a39b08319efb797783df2fd9671b39b7596490cb486d702cf"
@ -1876,10 +2039,13 @@ dependencies = [
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" "checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098"
"checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e"
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
"checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f"
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unindent 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "10743f62eafa8863b3dbb35cd6abbb609c963dd98940142a85bc530b753e33a4" "checksum unindent 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "10743f62eafa8863b3dbb35cd6abbb609c963dd98940142a85bc530b753e33a4"
"checksum unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564" "checksum unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"

View file

@ -148,6 +148,8 @@ test_unimplemented = []
nightly = [] nightly = []
default = ["generic", "unix"] default = ["generic", "unix"]
[workspace]
[dependencies] [dependencies]
uucore = { path="src/uucore" } uucore = { path="src/uucore" }
arch = { optional=true, path="src/arch" } arch = { optional=true, path="src/arch" }

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" | "nightly" | "test_unimplemented" => continue, "default" | "unix" | "redox" | "redox_generic" | "fuchsia" | "generic"
_ => {}, | "nightly" | "test_unimplemented" => continue,
_ => {}
} }
crates.push(krate.to_string()); crates.push(krate.to_string());
} }
@ -27,18 +28,23 @@ pub fn main() {
let mut cf = File::create(Path::new(&out_dir).join("uutils_crates.rs")).unwrap(); let mut cf = File::create(Path::new(&out_dir).join("uutils_crates.rs")).unwrap();
let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap(); let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap();
mf.write_all(" mf.write_all(
"
type UtilityMap = HashMap<&'static str, fn(Vec<String>) -> i32>; type UtilityMap = HashMap<&'static str, fn(Vec<String>) -> i32>;
fn util_map() -> UtilityMap { fn util_map() -> UtilityMap {
let mut map: UtilityMap = HashMap::new();\n".as_bytes()).unwrap(); let mut map: UtilityMap = HashMap::new();\n"
.as_bytes(),
).unwrap();
for krate in crates { for krate in crates {
cf.write_all(format!("extern crate uu_{krate};\n", krate=krate).as_bytes()).unwrap(); cf.write_all(format!("extern crate uu_{krate};\n", krate = krate).as_bytes())
.unwrap();
match krate.as_ref() { match krate.as_ref() {
"hashsum" => { "hashsum" => {
mf.write_all("map.insert(\"hashsum\", uu_hashsum::uumain); mf.write_all(
"map.insert(\"hashsum\", uu_hashsum::uumain);
map.insert(\"md5sum\", uu_hashsum::uumain); map.insert(\"md5sum\", uu_hashsum::uumain);
map.insert(\"sha1sum\", uu_hashsum::uumain); map.insert(\"sha1sum\", uu_hashsum::uumain);
map.insert(\"sha224sum\", uu_hashsum::uumain); map.insert(\"sha224sum\", uu_hashsum::uumain);
@ -51,10 +57,16 @@ pub fn main() {
map.insert(\"sha3-384sum\", uu_hashsum::uumain); map.insert(\"sha3-384sum\", uu_hashsum::uumain);
map.insert(\"sha3-512sum\", uu_hashsum::uumain); map.insert(\"sha3-512sum\", uu_hashsum::uumain);
map.insert(\"shake128sum\", uu_hashsum::uumain); map.insert(\"shake128sum\", uu_hashsum::uumain);
map.insert(\"shake256sum\", uu_hashsum::uumain);\n".as_bytes()).unwrap(); map.insert(\"shake256sum\", uu_hashsum::uumain);\n"
}, .as_bytes(),
_ => ).unwrap();
mf.write_all(format!("map.insert(\"{krate}\", uu_{krate}::uumain);\n", krate=krate).as_bytes()).unwrap(), }
_ => mf.write_all(
format!(
"map.insert(\"{krate}\", uu_{krate}::uumain);\n",
krate = krate
).as_bytes(),
).unwrap(),
} }
} }

View file

@ -9,9 +9,9 @@
// file that was distributed with this source code. // file that was distributed with this source code.
// //
extern crate platform_info;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
extern crate platform_info;
use platform_info::*; use platform_info::*;

View file

@ -10,14 +10,15 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use uucore::encoding::{Data, Format, wrap_print}; use uucore::encoding::{wrap_print, Data, Format};
use std::fs::File; use std::fs::File;
use std::io::{BufReader, Read, stdin}; use std::io::{stdin, BufReader, Read};
use std::path::Path; use std::path::Path;
static SYNTAX: &'static str = "[OPTION]... [FILE]"; static SYNTAX: &'static str = "[OPTION]... [FILE]";
static SUMMARY: &'static str = "Base32 encode or decode FILE, or standard input, to standard output."; static SUMMARY: &'static str =
"Base32 encode or decode FILE, or standard input, to standard output.";
static LONG_HELP: &'static str = " static LONG_HELP: &'static str = "
With no FILE, or when FILE is -, read standard input. With no FILE, or when FILE is -, read standard input.
@ -31,24 +32,26 @@ static LONG_HELP: &'static str = "
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("d", "decode", "decode data") .optflag("d", "decode", "decode data")
.optflag("i", .optflag(
"i",
"ignore-garbage", "ignore-garbage",
"when decoding, ignore non-alphabetic characters") "when decoding, ignore non-alphabetic characters",
.optopt("w", )
.optopt(
"w",
"wrap", "wrap",
"wrap encoded lines after COLS character (default 76, 0 to disable wrapping)", "wrap encoded lines after COLS character (default 76, 0 to disable wrapping)",
"COLS") "COLS",
)
.parse(args); .parse(args);
let line_wrap = match matches.opt_str("wrap") { let line_wrap = match matches.opt_str("wrap") {
Some(s) => { Some(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);
} }
} },
}
None => 76, None => 76,
}; };

View file

@ -9,17 +9,17 @@
// that was distributed with this source code. // that was distributed with this source code.
// //
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use uucore::encoding::{Data, Format, wrap_print}; use uucore::encoding::{wrap_print, Data, Format};
use std::fs::File; use std::fs::File;
use std::io::{BufReader, Read, stdin}; use std::io::{stdin, BufReader, Read};
use std::path::Path; use std::path::Path;
static SYNTAX: &'static str = "[OPTION]... [FILE]"; static SYNTAX: &'static str = "[OPTION]... [FILE]";
static SUMMARY: &'static str = "Base64 encode or decode FILE, or standard input, to standard output."; static SUMMARY: &'static str =
"Base64 encode or decode FILE, or standard input, to standard output.";
static LONG_HELP: &'static str = " static LONG_HELP: &'static str = "
With no FILE, or when FILE is -, read standard input. With no FILE, or when FILE is -, read standard input.
@ -33,24 +33,26 @@ static LONG_HELP: &'static str = "
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("d", "decode", "decode data") .optflag("d", "decode", "decode data")
.optflag("i", .optflag(
"i",
"ignore-garbage", "ignore-garbage",
"when decoding, ignore non-alphabetic characters") "when decoding, ignore non-alphabetic characters",
.optopt("w", )
.optopt(
"w",
"wrap", "wrap",
"wrap encoded lines after COLS character (default 76, 0 to disable wrapping)", "wrap encoded lines after COLS character (default 76, 0 to disable wrapping)",
"COLS") "COLS",
)
.parse(args); .parse(args);
let line_wrap = match matches.opt_str("wrap") { let line_wrap = match matches.opt_str("wrap") {
Some(s) => { Some(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);
} }
} },
}
None => 76, None => 76,
}; };

View file

@ -25,9 +25,22 @@ pub fn uumain(args: Vec<String>) -> i32 {
// Argument parsing // Argument parsing
// //
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP) let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optflag("a", "multiple", "Support more than one argument. Treat every argument as a name.") .optflag(
.optopt("s", "suffix", "Remove a trailing suffix. This option implies the -a option.", "SUFFIX") "a",
.optflag("z", "zero", "Output a zero byte (ASCII NUL) at the end of each line, rather than a newline.") "multiple",
"Support more than one argument. Treat every argument as a name.",
)
.optopt(
"s",
"suffix",
"Remove a trailing suffix. This option implies the -a option.",
"SUFFIX",
)
.optflag(
"z",
"zero",
"Output a zero byte (ASCII NUL) at the end of each line, rather than a newline.",
)
.parse(args); .parse(args);
// too few arguments // too few arguments
@ -81,7 +94,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
fn basename(fullname: &str, suffix: &str) -> String { fn basename(fullname: &str, suffix: &str) -> String {
// Remove all platform-specific path separators from the end // Remove all platform-specific path separators from the end
let mut path: String = fullname.chars().rev().skip_while(|&ch| is_separator(ch)).collect(); let mut path: String = fullname
.chars()
.rev()
.skip_while(|&ch| is_separator(ch))
.collect();
// Undo reverse // Undo reverse
path = path.chars().rev().collect(); path = path.chars().rev().collect();
@ -90,7 +107,7 @@ fn basename(fullname: &str, suffix: &str) -> String {
let pb = PathBuf::from(path); let pb = PathBuf::from(path);
match pb.components().last() { match pb.components().last() {
Some(c) => strip_suffix(c.as_os_str().to_str().unwrap(), suffix), Some(c) => strip_suffix(c.as_os_str().to_str().unwrap(), suffix),
None => "".to_owned() None => "".to_owned(),
} }
} }

View file

@ -20,20 +20,22 @@ extern crate uucore;
// last synced with: cat (GNU coreutils) 8.13 // last synced with: cat (GNU coreutils) 8.13
use quick_error::ResultExt; use quick_error::ResultExt;
use std::fs::{metadata, File}; use std::fs::{metadata, File};
use std::io::{self, stdout, stdin, stderr, Write, Read, BufWriter}; use std::io::{self, stderr, stdin, stdout, BufWriter, Read, Write};
use uucore::fs::is_stdin_interactive; use uucore::fs::is_stdin_interactive;
/// Unix domain socket support /// Unix domain socket support
#[cfg(unix)] use std::net::Shutdown; #[cfg(unix)]
#[cfg(unix)] use std::os::unix::fs::FileTypeExt; use std::net::Shutdown;
#[cfg(unix)] use unix_socket::UnixStream; #[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
#[cfg(unix)]
use unix_socket::UnixStream;
static SYNTAX: &'static str = "[OPTION]... [FILE]..."; static SYNTAX: &'static str = "[OPTION]... [FILE]...";
static SUMMARY: &'static str = "Concatenate FILE(s), or standard input, to standard output static SUMMARY: &'static str = "Concatenate FILE(s), or standard input, to standard output
With no FILE, or when FILE is -, read standard input."; With no FILE, or when FILE is -, read standard input.";
static LONG_HELP: &'static str = ""; static LONG_HELP: &'static str = "";
#[derive(PartialEq)] #[derive(PartialEq)]
enum NumberingMode { enum NumberingMode {
NumberNone, NumberNone,
@ -41,7 +43,6 @@ enum NumberingMode {
NumberAll, NumberAll,
} }
quick_error! { quick_error! {
#[derive(Debug)] #[derive(Debug)]
enum CatError { enum CatError {
@ -75,7 +76,6 @@ quick_error! {
} }
} }
struct OutputOptions { struct OutputOptions {
/// Line numbering mode /// Line numbering mode
number: NumberingMode, number: NumberingMode,
@ -98,14 +98,12 @@ struct OutputOptions {
show_nonprint: bool, show_nonprint: bool,
} }
/// Represents an open file handle, stream, or other device /// Represents an open file handle, stream, or other device
struct InputHandle { struct InputHandle {
reader: Box<Read>, reader: Box<Read>,
is_interactive: bool, is_interactive: bool,
} }
/// Concrete enum of recognized file types. /// Concrete enum of recognized file types.
/// ///
/// *Note*: `cat`-ing a directory should result in an /// *Note*: `cat`-ing a directory should result in an
@ -115,31 +113,37 @@ enum InputType {
File, File,
StdIn, StdIn,
SymLink, SymLink,
#[cfg(unix)] BlockDevice, #[cfg(unix)]
#[cfg(unix)] CharacterDevice, BlockDevice,
#[cfg(unix)] Fifo, #[cfg(unix)]
#[cfg(unix)] Socket, CharacterDevice,
} #[cfg(unix)]
Fifo,
#[cfg(unix)]
Socket,
}
type CatResult<T> = Result<T, CatError>; type CatResult<T> = Result<T, CatError>;
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("A", "show-all", "equivalent to -vET") .optflag("A", "show-all", "equivalent to -vET")
.optflag("b", .optflag(
"b",
"number-nonblank", "number-nonblank",
"number nonempty output lines, overrides -n") "number nonempty output lines, overrides -n",
)
.optflag("e", "", "equivalent to -vE") .optflag("e", "", "equivalent to -vE")
.optflag("E", "show-ends", "display $ at end of each line") .optflag("E", "show-ends", "display $ at end of each line")
.optflag("n", "number", "number all output lines") .optflag("n", "number", "number all output lines")
.optflag("s", "squeeze-blank", "suppress repeated empty output lines") .optflag("s", "squeeze-blank", "suppress repeated empty output lines")
.optflag("t", "", "equivalent to -vT") .optflag("t", "", "equivalent to -vT")
.optflag("T", "show-tabs", "display TAB characters as ^I") .optflag("T", "show-tabs", "display TAB characters as ^I")
.optflag("v", .optflag(
"v",
"show-nonprinting", "show-nonprinting",
"use ^ and M- notation, except for LF (\\n) and TAB (\\t)") "use ^ and M- notation, except for LF (\\n) and TAB (\\t)",
)
.parse(args); .parse(args);
let number_mode = if matches.opt_present("b") { let number_mode = if matches.opt_present("b") {
@ -150,8 +154,12 @@ pub fn uumain(args: Vec<String>) -> i32 {
NumberingMode::NumberNone NumberingMode::NumberNone
}; };
let show_nonprint = let show_nonprint = matches.opts_present(&[
matches.opts_present(&["A".to_owned(), "e".to_owned(), "t".to_owned(), "v".to_owned()]); "A".to_owned(),
"e".to_owned(),
"t".to_owned(),
"v".to_owned(),
]);
let show_ends = matches.opts_present(&["E".to_owned(), "A".to_owned(), "e".to_owned()]); let show_ends = matches.opts_present(&["E".to_owned(), "A".to_owned(), "e".to_owned()]);
let show_tabs = matches.opts_present(&["A".to_owned(), "T".to_owned(), "t".to_owned()]); let show_tabs = matches.opts_present(&["A".to_owned(), "T".to_owned(), "t".to_owned()]);
let squeeze_blank = matches.opt_present("s"); let squeeze_blank = matches.opt_present("s");
@ -160,15 +168,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
files.push("-".to_owned()); files.push("-".to_owned());
} }
let can_write_fast = !(show_tabs let can_write_fast = !(show_tabs || show_nonprint || show_ends || squeeze_blank
|| show_nonprint
|| show_ends
|| squeeze_blank
|| number_mode != NumberingMode::NumberNone); || number_mode != NumberingMode::NumberNone);
let success = if can_write_fast { let success = if can_write_fast {
write_fast(files).is_ok() write_fast(files).is_ok()
} else { } else {
let tab = match show_tabs { let tab = match show_tabs {
true => "^I", true => "^I",
@ -198,7 +202,6 @@ pub fn uumain(args: Vec<String>) -> i32 {
} }
} }
/// Classifies the `InputType` of file at `path` if possible /// Classifies the `InputType` of file at `path` if possible
/// ///
/// # Arguments /// # Arguments
@ -206,18 +209,34 @@ pub fn uumain(args: Vec<String>) -> i32 {
/// * `path` - Path on a file system to classify metadata /// * `path` - Path on a file system to classify metadata
fn get_input_type(path: &str) -> CatResult<InputType> { fn get_input_type(path: &str) -> CatResult<InputType> {
if path == "-" { if path == "-" {
return Ok(InputType::StdIn) return Ok(InputType::StdIn);
} }
match metadata(path).context(path)?.file_type() { match metadata(path).context(path)?.file_type() {
#[cfg(unix)] ft if ft.is_block_device() => Ok(InputType::BlockDevice), #[cfg(unix)]
#[cfg(unix)] ft if ft.is_char_device() => Ok(InputType::CharacterDevice), ft if ft.is_block_device() =>
#[cfg(unix)] ft if ft.is_fifo() => Ok(InputType::Fifo), {
#[cfg(unix)] ft if ft.is_socket() => Ok(InputType::Socket), Ok(InputType::BlockDevice)
}
#[cfg(unix)]
ft if ft.is_char_device() =>
{
Ok(InputType::CharacterDevice)
}
#[cfg(unix)]
ft if ft.is_fifo() =>
{
Ok(InputType::Fifo)
}
#[cfg(unix)]
ft if ft.is_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),
_ => Err(CatError::UnknownFiletype(path.to_owned())) _ => Err(CatError::UnknownFiletype(path.to_owned())),
} }
} }
@ -238,21 +257,22 @@ fn open(path: &str) -> CatResult<InputHandle> {
match get_input_type(path)? { match get_input_type(path)? {
InputType::Directory => Err(CatError::IsDirectory(path.to_owned())), InputType::Directory => Err(CatError::IsDirectory(path.to_owned())),
#[cfg(unix)] InputType::Socket => { #[cfg(unix)]
InputType::Socket => {
let socket = UnixStream::connect(path).context(path)?; let socket = UnixStream::connect(path).context(path)?;
socket.shutdown(Shutdown::Write).context(path)?; socket.shutdown(Shutdown::Write).context(path)?;
Ok(InputHandle { Ok(InputHandle {
reader: Box::new(socket) as Box<Read>, reader: Box::new(socket) as Box<Read>,
is_interactive: false, is_interactive: false,
}) })
}, }
_ => { _ => {
let file = File::open(path).context(path)?; let file = File::open(path).context(path)?;
Ok(InputHandle { Ok(InputHandle {
reader: Box::new(file) as Box<Read>, reader: Box::new(file) as Box<Read>,
is_interactive: false, is_interactive: false,
}) })
}, }
} }
} }
@ -271,18 +291,16 @@ fn write_fast(files: Vec<String>) -> CatResult<()> {
for file in files { for file in files {
match open(&file[..]) { match open(&file[..]) {
Ok(mut handle) => { Ok(mut handle) => while let Ok(n) = handle.reader.read(&mut in_buf) {
while let Ok(n) = handle.reader.read(&mut in_buf) {
if n == 0 { if n == 0 {
break; 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;
}, }
} }
} }
@ -331,9 +349,7 @@ fn write_lines(files: Vec<String>, options: &OutputOptions) -> CatResult<()> {
/// Outputs file contents to stdout in a linewise fashion, /// Outputs file contents to stdout in a linewise fashion,
/// propagating any errors that might occur. /// propagating any errors that might occur.
fn write_file_lines(file: &str, fn write_file_lines(file: &str, options: &OutputOptions, state: &mut OutputState) -> CatResult<()> {
options: &OutputOptions,
state: &mut OutputState) -> CatResult<()> {
let mut handle = open(file)?; let mut handle = open(file)?;
let mut in_buf = [0; 1024 * 31]; let mut in_buf = [0; 1024 * 31];
let mut writer = BufWriter::with_capacity(1024 * 64, stdout()); let mut writer = BufWriter::with_capacity(1024 * 64, stdout());
@ -413,7 +429,10 @@ 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 {
loop { loop {
match in_buf.iter().position(|c| *c == '\n' as u8 || *c == '\t' as u8) { match in_buf
.iter()
.position(|c| *c == '\n' as u8 || *c == '\t' as u8)
{
Some(p) => { Some(p) => {
writer.write_all(&in_buf[..p]).unwrap(); writer.write_all(&in_buf[..p]).unwrap();
if in_buf[p] == '\n' as u8 { if in_buf[p] == '\n' as u8 {
@ -449,5 +468,9 @@ fn write_nonprint_to_end<W: Write>(in_buf: &[u8], writer: &mut W, tab: &[u8]) ->
}.unwrap(); }.unwrap();
count += 1; count += 1;
} }
if count != in_buf.len() { count + 1 } else { 0 } if count != in_buf.len() {
count + 1
} else {
0
}
} }

View file

@ -29,7 +29,8 @@ use std::path::Path;
use std::ffi::CString; use std::ffi::CString;
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
static SYNTAX: &'static str = "chgrp [OPTION]... GROUP FILE...\n or : chgrp [OPTION]... --reference=RFILE FILE..."; static SYNTAX: &'static str =
"chgrp [OPTION]... GROUP FILE...\n or : chgrp [OPTION]... --reference=RFILE FILE...";
static SUMMARY: &'static str = "Change the group of each FILE to GROUP."; static SUMMARY: &'static str = "Change the group of each FILE to GROUP.";
const FTS_COMFOLLOW: u8 = 1; const FTS_COMFOLLOW: u8 = 1;
@ -334,10 +335,12 @@ impl Chgrper {
_ => { _ => {
show_info!("changing group of '{}': {}", path.display(), e); show_info!("changing group of '{}': {}", path.display(), e);
if self.verbosity == Verbose { if self.verbosity == Verbose {
println!("failed to change group of {} from {} to {}", println!(
"failed to change group of {} from {} to {}",
path.display(), path.display(),
entries::gid2grp(meta.gid()).unwrap(), entries::gid2grp(meta.gid()).unwrap(),
entries::gid2grp(dest_gid).unwrap()); entries::gid2grp(dest_gid).unwrap()
);
}; };
} }
} }
@ -347,17 +350,21 @@ impl Chgrper {
if changed { if changed {
match self.verbosity { match self.verbosity {
Changes | Verbose => { Changes | Verbose => {
println!("changed group of {} from {} to {}", println!(
"changed group of {} from {} to {}",
path.display(), path.display(),
entries::gid2grp(meta.gid()).unwrap(), entries::gid2grp(meta.gid()).unwrap(),
entries::gid2grp(dest_gid).unwrap()); entries::gid2grp(dest_gid).unwrap()
);
} }
_ => (), _ => (),
}; };
} else if self.verbosity == Verbose { } else if self.verbosity == Verbose {
println!("group of {} retained as {}", println!(
"group of {} retained as {}",
path.display(), path.display(),
entries::gid2grp(dest_gid).unwrap()); entries::gid2grp(dest_gid).unwrap()
);
} }
} }
ret ret

View file

@ -31,12 +31,17 @@ static LONG_HELP: &'static str = "
"; ";
pub fn uumain(mut args: Vec<String>) -> i32 { pub fn uumain(mut args: Vec<String>) -> i32 {
let syntax = format!("[OPTION]... MODE[,MODE]... FILE... let syntax = format!(
"[OPTION]... MODE[,MODE]... FILE...
{0} [OPTION]... OCTAL-MODE FILE... {0} [OPTION]... OCTAL-MODE FILE...
{0} [OPTION]... --reference=RFILE FILE...", NAME); {0} [OPTION]... --reference=RFILE FILE...",
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 (unimplemented)") opts.optflag("c", "changes", "like verbose but report only when a change is made \
.optflag("f", "quiet", "suppress most error messages (unimplemented)") // TODO: support --silent (unimplemented)")
// TODO: support --silent (can be done using clap)
.optflag("f", "quiet", "suppress most error messages (unimplemented)")
.optflag("v", "verbose", "output a diagnostic for every file processed (unimplemented)") .optflag("v", "verbose", "output a diagnostic for every file processed (unimplemented)")
.optflag("", "no-preserve-root", "do not treat '/' specially (the default)") .optflag("", "no-preserve-root", "do not treat '/' specially (the default)")
.optflag("", "preserve-root", "fail to operate recursively on '/'") .optflag("", "preserve-root", "fail to operate recursively on '/'")
@ -58,14 +63,13 @@ pub fn uumain(mut args: Vec<String>) -> i32 {
let verbose = matches.opt_present("verbose"); let verbose = matches.opt_present("verbose");
let preserve_root = matches.opt_present("preserve-root"); let preserve_root = matches.opt_present("preserve-root");
let recursive = matches.opt_present("recursive"); let recursive = matches.opt_present("recursive");
let fmode = matches.opt_str("reference").and_then(|ref fref| { let fmode = matches
match fs::metadata(fref) { .opt_str("reference")
.and_then(|ref fref| match fs::metadata(fref) {
Ok(meta) => Some(meta.mode()), Ok(meta) => Some(meta.mode()),
Err(err) => crash!(1, "cannot stat attribues of '{}': {}", fref, err) Err(err) => crash!(1, "cannot stat attribues of '{}': {}", fref, err),
}
}); });
let cmode = let cmode = if fmode.is_none() {
if fmode.is_none() {
// If there was a negative option, now it's a good time to // If there was a negative option, now it's a good time to
// use it. // use it.
if negative_option.is_some() { if negative_option.is_some() {
@ -76,7 +80,7 @@ pub fn uumain(mut args: Vec<String>) -> i32 {
} else { } else {
None None
}; };
let chmoder = Chmoder{ let chmoder = Chmoder {
changes: changes, changes: changes,
quiet: quiet, quiet: quiet,
verbose: verbose, verbose: verbose,
@ -87,7 +91,7 @@ pub fn uumain(mut args: Vec<String>) -> i32 {
}; };
match chmoder.chmod(matches.free) { match chmoder.chmod(matches.free) {
Ok(()) => {} Ok(()) => {}
Err(e) => return e Err(e) => return e,
} }
} }
@ -102,9 +106,9 @@ fn sanitize_input(args: &mut Vec<String>) -> Option<String> {
} }
if let Some(second) = args[i].chars().nth(1) { if let Some(second) = args[i].chars().nth(1) {
match second { match second {
'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0' ... '7' => { 'r' | 'w' | 'x' | 'X' | 's' | 't' | 'u' | 'g' | 'o' | '0'...'7' => {
return Some(args.remove(i)); return Some(args.remove(i));
}, }
_ => {} _ => {}
} }
} }
@ -123,7 +127,6 @@ struct Chmoder {
} }
impl Chmoder { impl Chmoder {
fn chmod(&self, files: Vec<String>) -> Result<(), i32> { fn chmod(&self, files: Vec<String>) -> Result<(), i32> {
let mut r = Ok(()); let mut r = Ok(());
@ -145,18 +148,21 @@ 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(walk_dir.filter_map(|x| match x { r = self.chmod(
walk_dir
.filter_map(|x| match x {
Ok(o) => match o.path().into_os_string().to_str() { Ok(o) => match o.path().into_os_string().to_str() {
Some(s) => Some(s.to_owned()), Some(s) => Some(s.to_owned()),
None => None, 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 {
show_error!("could not change permissions of directory '{}'", show_error!("could not change permissions of directory '{}'", filename);
filename);
r = Err(1); r = Err(1);
} }
} else { } else {
@ -193,10 +199,10 @@ impl Chmoder {
Some(mode) => try!(self.change_file(fperm, mode, file, name)), Some(mode) => try!(self.change_file(fperm, mode, file, name)),
None => { None => {
let cmode_unwrapped = self.cmode.clone().unwrap(); let cmode_unwrapped = self.cmode.clone().unwrap();
for mode in cmode_unwrapped.split(',') { // cmode is guaranteed to be Some in this case for mode in cmode_unwrapped.split(',') {
// cmode is guaranteed to be Some in this case
let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
let result = let result = if mode.contains(arr) {
if mode.contains(arr) {
mode::parse_numeric(fperm, mode) mode::parse_numeric(fperm, mode)
} else { } else {
mode::parse_symbolic(fperm, mode, file.is_dir()) mode::parse_symbolic(fperm, mode, file.is_dir())
@ -226,17 +232,29 @@ impl Chmoder {
show_info!("mode of '{}' retained as {:o}", file.display(), fperm); show_info!("mode of '{}' retained as {:o}", file.display(), fperm);
} }
Ok(()) Ok(())
} else if let Err(err) = fs::set_permissions(Path::new(path), fs::Permissions::from_mode(mode)) { } else if let Err(err) =
fs::set_permissions(Path::new(path), fs::Permissions::from_mode(mode))
{
if !self.quiet { if !self.quiet {
show_error!("{}", err); show_error!("{}", err);
} }
if self.verbose { if self.verbose {
show_info!("failed to change mode of file '{}' from {:o} to {:o}", file.display(), fperm, mode); show_info!(
"failed to change mode of file '{}' from {:o} to {:o}",
file.display(),
fperm,
mode
);
} }
Err(1) Err(1)
} else { } else {
if self.verbose || self.changes { if self.verbose || self.changes {
show_info!("mode of '{}' changed from {:o} to {:o}", file.display(), fperm, mode); show_info!(
"mode of '{}' changed from {:o} to {:o}",
file.display(),
fperm,
mode
);
} }
Ok(()) Ok(())
} }

View file

@ -1,5 +1,4 @@
#![crate_name = "uu_chown"] #![crate_name = "uu_chown"]
// This file is part of the uutils coreutils package. // This file is part of the uutils coreutils package.
// //
// (c) Jian Zeng <anonymousknight96@gmail.com> // (c) Jian Zeng <anonymousknight96@gmail.com>
@ -7,14 +6,13 @@
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
// //
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature="clippy", feature(plugin))] #![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use uucore::libc::{self, uid_t, gid_t, lchown}; use uucore::libc::{self, gid_t, lchown, uid_t};
pub use uucore::entries::{self, Locate, Passwd, Group}; pub use uucore::entries::{self, Group, Locate, Passwd};
use uucore::fs::resolve_relative_path; use uucore::fs::resolve_relative_path;
extern crate walkdir; extern crate walkdir;
@ -32,7 +30,8 @@ use std::convert::AsRef;
use std::ffi::CString; use std::ffi::CString;
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
static SYNTAX: &'static str = "[OPTION]... [OWNER][:[GROUP]] FILE...\n chown [OPTION]... --reference=RFILE FILE..."; static SYNTAX: &'static str =
"[OPTION]... [OWNER][:[GROUP]] FILE...\n chown [OPTION]... --reference=RFILE FILE...";
static SUMMARY: &'static str = "change file owner and group"; static SUMMARY: &'static str = "change file owner and group";
const FTS_COMFOLLOW: u8 = 1; const FTS_COMFOLLOW: u8 = 1;
@ -193,26 +192,32 @@ fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
let usr_grp = args.len() == 2 && !args[0].is_empty() && !args[1].is_empty(); let usr_grp = args.len() == 2 && !args[0].is_empty() && !args[1].is_empty();
if usr_only { if usr_only {
Ok((Some(match Passwd::locate(args[0]) { Ok((
Some(match Passwd::locate(args[0]) {
Ok(v) => v.uid(), Ok(v) => v.uid(),
_ => return Err(format!("invalid user: {}", spec)), _ => return Err(format!("invalid user: {}", spec)),
}), }),
None)) None,
))
} else if grp_only { } else if grp_only {
Ok((None, Ok((
None,
Some(match Group::locate(args[1]) { Some(match Group::locate(args[1]) {
Ok(v) => v.gid(), Ok(v) => v.gid(),
_ => return Err(format!("invalid group: {}", spec)), _ => return Err(format!("invalid group: {}", spec)),
}))) }),
))
} else if usr_grp { } else if usr_grp {
Ok((Some(match Passwd::locate(args[0]) { Ok((
Some(match Passwd::locate(args[0]) {
Ok(v) => v.uid(), Ok(v) => v.uid(),
_ => return Err(format!("invalid user: {}", spec)), _ => return Err(format!("invalid user: {}", spec)),
}), }),
Some(match Group::locate(args[1]) { Some(match Group::locate(args[1]) {
Ok(v) => v.gid(), Ok(v) => v.gid(),
_ => return Err(format!("invalid group: {}", spec)), _ => return Err(format!("invalid group: {}", spec)),
}))) }),
))
} else { } else {
Ok((None, None)) Ok((None, None))
} }
@ -263,7 +268,13 @@ impl Chowner {
ret ret
} }
fn chown<P: AsRef<Path>>(&self, path: P, duid: uid_t, dgid: gid_t, follow: bool) -> IOResult<()> { fn chown<P: AsRef<Path>>(
&self,
path: P,
duid: uid_t,
dgid: gid_t,
follow: bool,
) -> IOResult<()> {
let path = path.as_ref(); let path = path.as_ref();
let s = CString::new(path.as_os_str().as_bytes()).unwrap(); let s = CString::new(path.as_os_str().as_bytes()).unwrap();
let ret = unsafe { let ret = unsafe {
@ -391,12 +402,14 @@ impl Chowner {
_ => { _ => {
show_info!("changing ownership of '{}': {}", path.display(), e); show_info!("changing ownership of '{}': {}", path.display(), e);
if self.verbosity == Verbose { if self.verbosity == Verbose {
println!("failed to change ownership of {} from {}:{} to {}:{}", println!(
"failed to change ownership of {} from {}:{} to {}:{}",
path.display(), path.display(),
entries::uid2usr(meta.uid()).unwrap(), entries::uid2usr(meta.uid()).unwrap(),
entries::gid2grp(meta.gid()).unwrap(), entries::gid2grp(meta.gid()).unwrap(),
entries::uid2usr(dest_uid).unwrap(), entries::uid2usr(dest_uid).unwrap(),
entries::gid2grp(dest_gid).unwrap()); entries::gid2grp(dest_gid).unwrap()
);
}; };
} }
} }
@ -406,20 +419,24 @@ impl Chowner {
if changed { if changed {
match self.verbosity { match self.verbosity {
Changes | Verbose => { Changes | Verbose => {
println!("changed ownership of {} from {}:{} to {}:{}", println!(
"changed ownership of {} from {}:{} to {}:{}",
path.display(), path.display(),
entries::uid2usr(meta.uid()).unwrap(), entries::uid2usr(meta.uid()).unwrap(),
entries::gid2grp(meta.gid()).unwrap(), entries::gid2grp(meta.gid()).unwrap(),
entries::uid2usr(dest_uid).unwrap(), entries::uid2usr(dest_uid).unwrap(),
entries::gid2grp(dest_gid).unwrap()); entries::gid2grp(dest_gid).unwrap()
);
} }
_ => (), _ => (),
}; };
} else if self.verbosity == Verbose { } else if self.verbosity == Verbose {
println!("ownership of {} retained as {}:{}", println!(
"ownership of {} retained as {}:{}",
path.display(), path.display(),
entries::uid2usr(dest_uid).unwrap(), entries::uid2usr(dest_uid).unwrap(),
entries::gid2grp(dest_gid).unwrap()); entries::gid2grp(dest_gid).unwrap()
);
} }
} }
ret ret

View file

@ -14,7 +14,7 @@ extern crate getopts;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use uucore::libc::{self, setgid, setuid, chroot, setgroups}; use uucore::libc::{self, chroot, setgid, setgroups, setuid};
use uucore::entries; use uucore::entries;
use std::ffi::CString; use std::ffi::CString;
@ -33,18 +33,33 @@ static LONG_HELP: &'static str = "
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)
.optopt("u", "user", "User (ID or name) to switch before running the program", "USER") .optopt(
"u",
"user",
"User (ID or name) to switch before running the program",
"USER",
)
.optopt("g", "group", "Group (ID or name) to switch to", "GROUP") .optopt("g", "group", "Group (ID or name) to switch to", "GROUP")
.optopt("G", "groups", "Comma-separated list of groups to switch to", "GROUP1,GROUP2...") .optopt(
.optopt("", "userspec", "Colon-separated user and group to switch to. \ "G",
"groups",
"Comma-separated list of groups to switch to",
"GROUP1,GROUP2...",
)
.optopt(
"",
"userspec",
"Colon-separated user and group to switch to. \
Same as -u USER -g GROUP. \ Same as -u USER -g GROUP. \
Userspec has higher preference than -u and/or -g", "USER:GROUP") Userspec has higher preference than -u and/or -g",
"USER:GROUP",
)
.parse(args); .parse(args);
if matches.free.is_empty() { if matches.free.is_empty() {
println!("Missing operand: NEWROOT"); println!("Missing operand: NEWROOT");
println!("Try `{} --help` for more information.", NAME); println!("Try `{} --help` for more information.", NAME);
return 1 return 1;
} }
let default_shell: &'static str = "/bin/sh"; let default_shell: &'static str = "/bin/sh";
@ -53,7 +68,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
let newroot = Path::new(&matches.free[0][..]); let newroot = Path::new(&matches.free[0][..]);
if !newroot.is_dir() { if !newroot.is_dir() {
crash!(1, "cannot change root directory to `{}`: no such directory", newroot.display()); crash!(
1,
"cannot change root directory to `{}`: no such directory",
newroot.display()
);
} }
let command: Vec<&str> = match matches.free.len() { let command: Vec<&str> = match matches.free.len() {
@ -62,9 +81,9 @@ pub fn uumain(args: Vec<String>) -> i32 {
Err(_) => default_shell, Err(_) => default_shell,
Ok(ref s) => s.as_ref(), Ok(ref s) => s.as_ref(),
}; };
vec!(shell, default_option) vec![shell, default_option]
}, }
_ => matches.free[1..].iter().map(|x| &x[..]).collect() _ => matches.free[1..].iter().map(|x| &x[..]).collect(),
}; };
set_context(&newroot, &matches); set_context(&newroot, &matches);
@ -97,10 +116,18 @@ fn set_context(root: &Path, options: &getopts::Matches) {
}; };
s s
} }
None => Vec::new() None => Vec::new(),
};
let user = if userspec.is_empty() {
&user_str[..]
} else {
&userspec[0][..]
};
let group = if userspec.is_empty() {
&group_str[..]
} else {
&userspec[1][..]
}; };
let user = if userspec.is_empty() { &user_str[..] } else { &userspec[0][..] };
let group = if userspec.is_empty() { &group_str[..] } else { &userspec[1][..] };
enter_chroot(root); enter_chroot(root);
@ -113,10 +140,18 @@ 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(".".as_bytes()).unwrap().as_bytes_with_nul().as_ptr() as *const libc::c_char) chroot(CString::new(".".as_bytes())
.unwrap()
.as_bytes_with_nul()
.as_ptr() as *const libc::c_char)
}; };
if err != 0 { if err != 0 {
crash!(1, "cannot chroot to {}: {}", root_str, Error::last_os_error()) crash!(
1,
"cannot chroot to {}: {}",
root_str,
Error::last_os_error()
)
}; };
} }
@ -128,36 +163,33 @@ fn set_main_group(group: &str) {
}; };
let err = unsafe { setgid(group_id) }; let err = unsafe { setgid(group_id) };
if err != 0 { if err != 0 {
crash!(1, "cannot set gid to {}: {}", group_id, Error::last_os_error()) crash!(
1,
"cannot set gid to {}: {}",
group_id,
Error::last_os_error()
)
} }
} }
} }
#[cfg(any(target_os = "macos", target_os = "freebsd"))] #[cfg(any(target_os = "macos", target_os = "freebsd"))]
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int { fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
unsafe { unsafe { setgroups(groups.len() as libc::c_int, groups.as_ptr()) }
setgroups(groups.len() as libc::c_int,
groups.as_ptr())
}
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int { fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
unsafe { unsafe { setgroups(groups.len() as libc::size_t, groups.as_ptr()) }
setgroups(groups.len() as libc::size_t,
groups.as_ptr())
}
} }
fn set_groups_from_str(groups: &str) { fn set_groups_from_str(groups: &str) {
if !groups.is_empty() { if !groups.is_empty() {
let groups_vec: Vec<libc::gid_t> = FromIterator::from_iter( let groups_vec: Vec<libc::gid_t> =
groups.split(',').map( FromIterator::from_iter(groups.split(',').map(|x| match entries::grp2gid(x) {
|x| match entries::grp2gid(x) {
Ok(g) => g, Ok(g) => g,
_ => crash!(1, "no such group: {}", x), _ => crash!(1, "no such group: {}", x),
}) }));
);
let err = set_groups(groups_vec); let err = set_groups(groups_vec);
if err != 0 { if err != 0 {
crash!(1, "cannot set groups: {}", Error::last_os_error()) crash!(1, "cannot set groups: {}", Error::last_os_error())

View file

@ -28,7 +28,11 @@ fn main() {
table.push(crc_entry(num as u8) as u32); table.push(crc_entry(num as u8) as u32);
} }
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!(&file, "const CRC_TABLE: [u32; {}] = {:?};", CRC_TABLE_LEN, table).unwrap(); write!(
&file,
"const CRC_TABLE: [u32; {}] = {:?};",
CRC_TABLE_LEN, table
).unwrap();
} }
#[inline] #[inline]

View file

@ -9,12 +9,11 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::fs::File; use std::fs::File;
use std::io::{self, stdin, Read, BufReader}; use std::io::{self, stdin, BufReader, Read};
#[cfg(not(windows))] #[cfg(not(windows))]
use std::mem; use std::mem;
use std::path::Path; use std::path::Path;
@ -46,7 +45,7 @@ fn init_byte_array() -> Vec<u8> {
} }
#[cfg(not(windows))] #[cfg(not(windows))]
fn init_byte_array() -> [u8; 1024*1024] { fn init_byte_array() -> [u8; 1024 * 1024] {
unsafe { mem::uninitialized() } unsafe { mem::uninitialized() }
} }
@ -56,10 +55,8 @@ fn cksum(fname: &str) -> io::Result<(u32, usize)> {
let mut size = 0usize; let mut size = 0usize;
let file; let file;
let mut rd : Box<Read> = match fname { let mut rd: Box<Read> = match fname {
"-" => { "-" => Box::new(stdin()),
Box::new(stdin())
}
_ => { _ => {
file = try!(File::open(&Path::new(fname))); file = try!(File::open(&Path::new(fname)));
Box::new(BufReader::new(file)) Box::new(BufReader::new(file))
@ -78,15 +75,14 @@ fn cksum(fname: &str) -> io::Result<(u32, usize)> {
} }
size += num_bytes; size += num_bytes;
} }
Err(err) => return Err(err) Err(err) => return Err(err),
} }
} }
//Ok((0 as u32,0 as usize)) //Ok((0 as u32,0 as usize))
} }
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).parse(args);
.parse(args);
let files = matches.free; let files = matches.free;

View file

@ -16,7 +16,7 @@ extern crate uucore;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fs::File; use std::fs::File;
use std::io::{self, BufRead, BufReader, stdin, Stdin}; use std::io::{self, stdin, BufRead, BufReader, Stdin};
use std::path::Path; use std::path::Path;
static SYNTAX: &'static str = "[OPTIONS] FILE1 FILE2"; static SYNTAX: &'static str = "[OPTIONS] FILE1 FILE2";
@ -43,13 +43,13 @@ fn mkdelim(col: usize, opts: &getopts::Matches) -> String {
fn ensure_nl(line: &mut String) { fn ensure_nl(line: &mut String) {
match line.chars().last() { match line.chars().last() {
Some('\n') => (), Some('\n') => (),
_ => line.push_str("\n") _ => line.push_str("\n"),
} }
} }
enum LineReader { enum LineReader {
Stdin(Stdin), Stdin(Stdin),
FileIn(BufReader<File>) FileIn(BufReader<File>),
} }
impl LineReader { impl LineReader {
@ -62,8 +62,7 @@ impl LineReader {
} }
fn comm(a: &mut LineReader, b: &mut LineReader, opts: &getopts::Matches) { fn comm(a: &mut LineReader, b: &mut LineReader, opts: &getopts::Matches) {
let delim: Vec<String> = (0..4).map(|col| mkdelim(col, opts)).collect();
let delim : Vec<String> = (0 .. 4).map(|col| mkdelim(col, opts)).collect();
let ra = &mut String::new(); let ra = &mut String::new();
let mut na = a.read_line(ra); let mut na = a.read_line(ra);
@ -73,8 +72,8 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &getopts::Matches) {
while na.is_ok() || nb.is_ok() { while na.is_ok() || nb.is_ok() {
let ord = match (na.is_ok(), nb.is_ok()) { let ord = match (na.is_ok(), nb.is_ok()) {
(false, true) => Ordering::Greater, (false, true) => Ordering::Greater,
(true , false) => Ordering::Less, (true, false) => Ordering::Less,
(true , true) => match(&na, &nb) { (true, true) => match (&na, &nb) {
(&Ok(0), &Ok(0)) => break, (&Ok(0), &Ok(0)) => break,
(&Ok(0), _) => Ordering::Greater, (&Ok(0), _) => Ordering::Greater,
(_, &Ok(0)) => Ordering::Less, (_, &Ok(0)) => Ordering::Less,
@ -91,7 +90,7 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &getopts::Matches) {
} }
ra.clear(); ra.clear();
na = a.read_line(ra); na = a.read_line(ra);
}, }
Ordering::Greater => { Ordering::Greater => {
if !opts.opt_present("2") { if !opts.opt_present("2") {
ensure_nl(rb); ensure_nl(rb);
@ -99,7 +98,7 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &getopts::Matches) {
} }
rb.clear(); rb.clear();
nb = b.read_line(rb); nb = b.read_line(rb);
}, }
Ordering::Equal => { Ordering::Equal => {
if !opts.opt_present("3") { if !opts.opt_present("3") {
ensure_nl(ra); ensure_nl(ra);
@ -128,7 +127,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP) let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optflag("1", "", "suppress column 1 (lines uniq to FILE1)") .optflag("1", "", "suppress column 1 (lines uniq to FILE1)")
.optflag("2", "", "suppress column 2 (lines uniq to FILE2)") .optflag("2", "", "suppress column 2 (lines uniq to FILE2)")
.optflag("3", "", "suppress column 3 (lines that appear in both files)") .optflag(
"3",
"",
"suppress column 3 (lines that appear in both files)",
)
.optopt("", "output-delimiter", "separate columns with STR", "STR") .optopt("", "output-delimiter", "separate columns with STR", "STR")
.parse(args); .parse(args);

View file

@ -10,14 +10,17 @@
* that was distributed with this source code. * that was distributed with this source code.
*/ */
extern crate libc;
extern crate clap; extern crate clap;
extern crate walkdir;
extern crate filetime; extern crate filetime;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
#[macro_use] extern crate ioctl_sys; #[macro_use]
#[macro_use] extern crate uucore; extern crate ioctl_sys;
#[macro_use] extern crate quick_error; extern crate libc;
#[macro_use]
extern crate quick_error;
#[macro_use]
extern crate uucore;
extern crate walkdir;
#[cfg(unix)] #[cfg(unix)]
extern crate xattr; extern crate xattr;
@ -35,24 +38,28 @@ extern crate winapi;
use std::mem; use std::mem;
use std::ffi::CString; use std::ffi::CString;
use clap::{Arg, App, ArgMatches}; use clap::{App, Arg, ArgMatches};
use quick_error::ResultExt; use quick_error::ResultExt;
use std::collections::HashSet; use std::collections::HashSet;
use std::fs; use std::fs;
use std::io::{BufReader, BufRead, stdin, stdout, Write}; use std::io::{stdin, stdout, BufRead, BufReader, Write};
use std::io; use std::io;
use std::path::{Path, PathBuf, StripPrefixError}; use std::path::{Path, PathBuf, StripPrefixError};
use std::str::FromStr; use std::str::FromStr;
use uucore::fs::{canonicalize, CanonicalizeMode}; use uucore::fs::{canonicalize, CanonicalizeMode};
use walkdir::WalkDir; 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::os::unix::io::IntoRawFd;
#[cfg(target_os = "linux")]
use std::fs::File;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use filetime::FileTime; use filetime::FileTime;
#[cfg(unix)] use std::os::unix::fs::PermissionsExt; #[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
#[cfg(target_os = "linux")] ioctl!(write ficlone with 0x94, 9; std::os::raw::c_int); #[cfg(target_os = "linux")]
ioctl!(write ficlone with 0x94, 9; std::os::raw::c_int);
quick_error! { quick_error! {
#[derive(Debug)] #[derive(Debug)]
@ -97,7 +104,6 @@ quick_error! {
} }
} }
/// Continue next iteration of loop if result of expression is error /// Continue next iteration of loop if result of expression is error
macro_rules! or_continue( macro_rules! or_continue(
($expr:expr) => (match $expr { ($expr:expr) => (match $expr {
@ -109,7 +115,6 @@ macro_rules! or_continue(
}) })
); );
/// Prompts the user yes/no and returns `true` they if successfully /// Prompts the user yes/no and returns `true` they if successfully
/// answered yes. /// answered yes.
macro_rules! prompt_yes( macro_rules! prompt_yes(
@ -133,7 +138,7 @@ pub type Source = PathBuf;
pub type Target = PathBuf; pub type Target = PathBuf;
/// Specifies whether when overwrite files /// Specifies whether when overwrite files
#[derive (Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
pub enum ClobberMode { pub enum ClobberMode {
Force, Force,
RemoveDestination, RemoveDestination,
@ -141,7 +146,7 @@ pub enum ClobberMode {
} }
/// Specifies whether when overwrite files /// Specifies whether when overwrite files
#[derive (Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
pub enum OverwriteMode { pub enum OverwriteMode {
/// [Default] Always overwrite existing files /// [Default] Always overwrite existing files
Clobber(ClobberMode), Clobber(ClobberMode),
@ -151,9 +156,11 @@ pub enum OverwriteMode {
NoClobber, NoClobber,
} }
#[derive (Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
pub enum ReflinkMode { pub enum ReflinkMode {
Always, Auto, Never Always,
Auto,
Never,
} }
/// Specifies the expected file type of copy target /// Specifies the expected file type of copy target
@ -176,12 +183,13 @@ pub enum CopyMode {
Sparse, Sparse,
Copy, Copy,
Update, Update,
AttrOnly AttrOnly,
} }
#[derive(Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
pub enum Attribute { pub enum Attribute {
#[cfg(unix)] Mode, #[cfg(unix)]
Mode,
Ownership, Ownership,
Timestamps, Timestamps,
Context, Context,
@ -222,12 +230,14 @@ fn print_version() {
} }
fn get_usage() -> String { fn get_usage() -> String {
format!("{0} [OPTION]... [-T] SOURCE DEST format!(
"{0} [OPTION]... [-T] SOURCE DEST
{0} [OPTION]... SOURCE... DIRECTORY {0} [OPTION]... SOURCE... DIRECTORY
{0} [OPTION]... -t DIRECTORY SOURCE...", executable!()) {0} [OPTION]... -t DIRECTORY SOURCE...",
executable!()
)
} }
// Argument constants // Argument constants
static OPT_ARCHIVE: &str = "archive"; static OPT_ARCHIVE: &str = "archive";
static OPT_ATTRIBUTES_ONLY: &str = "attributes-only"; static OPT_ATTRIBUTES_ONLY: &str = "attributes-only";
@ -263,18 +273,33 @@ static OPT_VERBOSE: &str = "verbose";
static OPT_VERSION: &str = "version"; static OPT_VERSION: &str = "version";
#[cfg(unix)] #[cfg(unix)]
static PRESERVABLE_ATTRIBUTES: &[&str] = &["mode", "ownership", "timestamps", "context", "links", "xattr", "all"]; static PRESERVABLE_ATTRIBUTES: &[&str] = &[
"mode",
"ownership",
"timestamps",
"context",
"links",
"xattr",
"all",
];
#[cfg(not(unix))] #[cfg(not(unix))]
static PRESERVABLE_ATTRIBUTES: &[&str] = &["ownership", "timestamps", "context", "links", "xattr", "all"]; static PRESERVABLE_ATTRIBUTES: &[&str] = &[
"ownership",
"timestamps",
"context",
"links",
"xattr",
"all",
];
static DEFAULT_ATTRIBUTES: &[Attribute] = &[ static DEFAULT_ATTRIBUTES: &[Attribute] = &[
#[cfg(unix)] Attribute::Mode, #[cfg(unix)]
Attribute::Mode,
Attribute::Ownership, Attribute::Ownership,
Attribute::Timestamps, Attribute::Timestamps,
]; ];
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let usage = get_usage(); let usage = get_usage();
let matches = App::new(executable!()) let matches = App::new(executable!())
@ -446,7 +471,8 @@ 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.values_of("paths") let paths: Vec<String> = matches
.values_of("paths")
.map(|v| v.map(|p| p.to_string()).collect()) .map(|v| v.map(|p| p.to_string()).collect())
.unwrap_or_default(); .unwrap_or_default();
@ -513,13 +539,19 @@ impl FromStr for Attribute {
fn from_str(value: &str) -> CopyResult<Attribute> { fn from_str(value: &str) -> CopyResult<Attribute> {
Ok(match &*value.to_lowercase() { Ok(match &*value.to_lowercase() {
#[cfg(unix)] "mode" => Attribute::Mode, #[cfg(unix)]
"mode" => Attribute::Mode,
"ownership" => Attribute::Ownership, "ownership" => Attribute::Ownership,
"timestamps" => Attribute::Timestamps, "timestamps" => Attribute::Timestamps,
"context" => Attribute::Context, "context" => Attribute::Context,
"links" => Attribute::Links, "links" => Attribute::Links,
"xattr" => Attribute::Xattr, "xattr" => Attribute::Xattr,
_ => return Err(Error::InvalidArgument(format!("invalid attribute '{}'", value))) _ => {
return Err(Error::InvalidArgument(format!(
"invalid attribute '{}'",
value
)))
}
}) })
} }
} }
@ -537,25 +569,26 @@ impl Options {
OPT_STRIP_TRAILING_SLASHES, OPT_STRIP_TRAILING_SLASHES,
OPT_ONE_FILE_SYSTEM, OPT_ONE_FILE_SYSTEM,
OPT_CONTEXT, OPT_CONTEXT,
#[cfg(windows)] OPT_FORCE, #[cfg(windows)]
OPT_FORCE,
]; ];
for not_implemented_opt in not_implemented_opts { for not_implemented_opt in not_implemented_opts {
if matches.is_present(not_implemented_opt) { if matches.is_present(not_implemented_opt) {
return Err(Error::NotImplemented(not_implemented_opt.to_string())) return Err(Error::NotImplemented(not_implemented_opt.to_string()));
} }
} }
let recursive = matches.is_present(OPT_RECURSIVE) let recursive = matches.is_present(OPT_RECURSIVE) || matches.is_present(OPT_RECURSIVE_ALIAS)
|| matches.is_present(OPT_RECURSIVE_ALIAS)
|| matches.is_present(OPT_ARCHIVE); || matches.is_present(OPT_ARCHIVE);
let backup = matches.is_present(OPT_BACKUP) let backup = matches.is_present(OPT_BACKUP) || matches.is_present(OPT_SUFFIX);
|| matches.is_present(OPT_SUFFIX);
// Parse target directory options // Parse target directory 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.value_of(OPT_TARGET_DIRECTORY).map(|v| v.to_string()); let target_dir = matches
.value_of(OPT_TARGET_DIRECTORY)
.map(|v| v.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) {
@ -601,14 +634,13 @@ impl Options {
reflink_mode: { reflink_mode: {
if let Some(reflink) = matches.value_of(OPT_REFLINK) { if let Some(reflink) = matches.value_of(OPT_REFLINK) {
match reflink { match reflink {
"always" => { "always" => ReflinkMode::Always,
ReflinkMode::Always "auto" => ReflinkMode::Auto,
},
"auto" => {
ReflinkMode::Auto
},
value => { value => {
return Err(Error::InvalidArgument(format!("invalid argument '{}' for \'reflink\'", value))) return Err(Error::InvalidArgument(format!(
"invalid argument '{}' for \'reflink\'",
value
)))
} }
} }
} else { } else {
@ -640,7 +672,6 @@ impl TargetType {
} }
} }
/// Returns tuple of (Source paths, Target) /// Returns tuple of (Source paths, Target)
fn parse_path_args(path_args: &[String], options: &Options) -> CopyResult<(Vec<Source>, Target)> { fn parse_path_args(path_args: &[String], options: &Options) -> CopyResult<(Vec<Source>, Target)> {
let mut paths = path_args.iter().map(PathBuf::from).collect::<Vec<_>>(); let mut paths = path_args.iter().map(PathBuf::from).collect::<Vec<_>>();
@ -673,7 +704,12 @@ fn parse_path_args(path_args: &[String], options: &Options) -> CopyResult<(Vec<S
Ok((sources, target)) Ok((sources, target))
} }
fn preserve_hardlinks(hard_links: &mut Vec<(String, u64)>, source: &std::path::PathBuf, dest: std::path::PathBuf, found_hard_link: &mut bool) -> CopyResult<()> { fn preserve_hardlinks(
hard_links: &mut Vec<(String, u64)>,
source: &std::path::PathBuf,
dest: std::path::PathBuf,
found_hard_link: &mut bool,
) -> CopyResult<()> {
// Redox does not currently support hard links // Redox does not currently support hard links
#[cfg(not(target_os = "redox"))] #[cfg(not(target_os = "redox"))]
{ {
@ -686,7 +722,11 @@ fn preserve_hardlinks(hard_links: &mut Vec<(String, u64)>, source: &std::path::P
{ {
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!("cannot stat {:?}: {}", src_path, std::io::Error::last_os_error()).into()); return Err(format!(
"cannot stat {:?}: {}",
src_path,
std::io::Error::last_os_error()
).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;
@ -694,13 +734,19 @@ fn preserve_hardlinks(hard_links: &mut Vec<(String, u64)>, source: &std::path::P
#[cfg(windows)] #[cfg(windows)]
{ {
let mut stat = mem::uninitialized(); let mut stat = mem::uninitialized();
let handle = CreateFile2(src_path.as_ptr() as *const u16, let handle = CreateFile2(
src_path.as_ptr() as *const u16,
winapi::um::winnt::GENERIC_READ, winapi::um::winnt::GENERIC_READ,
winapi::um::winnt::FILE_SHARE_READ, winapi::um::winnt::FILE_SHARE_READ,
0, 0,
std::ptr::null_mut()); std::ptr::null_mut(),
);
if GetFileInformationByHandle(handle, stat) != 0 { if GetFileInformationByHandle(handle, stat) != 0 {
return Err(format!("cannot get file information {:?}: {}", source, std::io::Error::last_os_error()).into()); return Err(format!(
"cannot get file information {:?}: {}",
source,
std::io::Error::last_os_error()
).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;
@ -771,26 +817,34 @@ fn copy(sources: &[Source], target: &Target, options: &Options) -> CopyResult<()
} }
} }
fn construct_dest_path(
fn construct_dest_path(source_path: &Path, target: &Target, target_type: &TargetType, options: &Options) source_path: &Path,
-> CopyResult<PathBuf> target: &Target,
{ target_type: &TargetType,
options: &Options,
) -> CopyResult<PathBuf> {
if options.no_target_dir && target.is_dir() { if options.no_target_dir && target.is_dir() {
return Err(format!("cannot overwrite directory '{}' with non-directory", target.display()).into()) return Err(format!(
"cannot overwrite directory '{}' with non-directory",
target.display()
).into());
} }
Ok(match *target_type { Ok(match *target_type {
TargetType::Directory => { TargetType::Directory => {
let root = source_path.parent().unwrap_or(source_path); let root = source_path.parent().unwrap_or(source_path);
localize_to_target(root, source_path, target)? localize_to_target(root, source_path, target)?
}, }
TargetType::File => target.to_path_buf(), TargetType::File => target.to_path_buf(),
}) })
} }
fn copy_source(source: &Source, target: &Target, target_type: &TargetType, options: &Options) fn copy_source(
-> CopyResult<()> source: &Source,
{ target: &Target,
target_type: &TargetType,
options: &Options,
) -> CopyResult<()> {
let source_path = Path::new(&source); let source_path = Path::new(&source);
if source_path.is_dir() { if source_path.is_dir() {
// Copy as directory // Copy as directory
@ -802,7 +856,6 @@ fn copy_source(source: &Source, target: &Target, target_type: &TargetType, optio
} }
} }
/// Read the contents of the directory `root` and recursively copy the /// Read the contents of the directory `root` and recursively copy the
/// contents to `target`. /// contents to `target`.
/// ///
@ -866,22 +919,26 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult
impl OverwriteMode { impl OverwriteMode {
fn verify(&self, path: &Path) -> CopyResult<()> { fn verify(&self, path: &Path) -> CopyResult<()> {
match *self { match *self {
OverwriteMode::NoClobber => { OverwriteMode::NoClobber => Err(Error::Skipped(format!(
Err(Error::Skipped(format!("Not overwriting {} because of option '{}'", path.display(), OPT_NO_CLOBBER))) "Not overwriting {} because of option '{}'",
}, path.display(),
OPT_NO_CLOBBER
))),
OverwriteMode::Interactive(_) => { OverwriteMode::Interactive(_) => {
if prompt_yes!("{}: overwrite {}? ", executable!(), path.display()) { if prompt_yes!("{}: overwrite {}? ", executable!(), path.display()) {
Ok(()) Ok(())
} else { } else {
Err(Error::Skipped(format!("Not overwriting {} at user request", path.display()))) Err(Error::Skipped(format!(
"Not overwriting {} at user request",
path.display()
)))
}
} }
},
OverwriteMode::Clobber(_) => Ok(()), OverwriteMode::Clobber(_) => Ok(()),
} }
} }
} }
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 { Ok(match *attribute {
@ -890,17 +947,21 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu
let mode = fs::metadata(source).context(context)?.permissions().mode(); let mode = fs::metadata(source).context(context)?.permissions().mode();
let mut dest_metadata = fs::metadata(source).context(context)?.permissions(); let mut dest_metadata = fs::metadata(source).context(context)?.permissions();
dest_metadata.set_mode(mode); dest_metadata.set_mode(mode);
}, }
Attribute::Ownership => { Attribute::Ownership => {
let metadata = fs::metadata(source).context(context)?; let metadata = fs::metadata(source).context(context)?;
fs::set_permissions(dest, metadata.permissions()).context(context)?; fs::set_permissions(dest, metadata.permissions()).context(context)?;
}, }
Attribute::Timestamps => { Attribute::Timestamps => {
let metadata = fs::metadata(source)?; let metadata = fs::metadata(source)?;
filetime::set_file_times(Path::new(dest), FileTime::from_last_access_time(&metadata), FileTime::from_last_modification_time(&metadata))?; filetime::set_file_times(
}, Path::new(dest),
Attribute::Context => {}, FileTime::from_last_access_time(&metadata),
Attribute::Links => {}, FileTime::from_last_modification_time(&metadata),
)?;
}
Attribute::Context => {}
Attribute::Links => {}
Attribute::Xattr => { Attribute::Xattr => {
#[cfg(unix)] #[cfg(unix)]
{ {
@ -915,7 +976,7 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu
{ {
return Err(format!("XAttrs are only supported on unix.").into()); return Err(format!("XAttrs are only supported on unix.").into());
} }
}, }
}) })
} }
@ -960,10 +1021,10 @@ fn handle_existing_dest(source: &Path, dest: &Path, options: &Options) -> CopyRe
if fs::metadata(dest)?.permissions().readonly() { if fs::metadata(dest)?.permissions().readonly() {
fs::remove_file(dest)?; fs::remove_file(dest)?;
} }
}, }
OverwriteMode::Clobber(ClobberMode::RemoveDestination) => { OverwriteMode::Clobber(ClobberMode::RemoveDestination) => {
fs::remove_file(dest)?; fs::remove_file(dest)?;
}, }
_ => (), _ => (),
}; };
@ -1025,7 +1086,7 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
} else { } else {
copy_helper(source, dest, options)?; copy_helper(source, dest, options)?;
} }
}, }
CopyMode::AttrOnly => { CopyMode::AttrOnly => {
OpenOptions::new() OpenOptions::new()
.write(true) .write(true)
@ -1064,9 +1125,14 @@ fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()>
ReflinkMode::Always => unsafe { ReflinkMode::Always => unsafe {
let result = ficlone(dst_file, src_file as *const i32); let result = ficlone(dst_file, src_file as *const i32);
if result != 0 { if result != 0 {
return Err(format!("failed to clone {:?} from {:?}: {}", source, dest, std::io::Error::last_os_error()).into()); return Err(format!(
"failed to clone {:?} from {:?}: {}",
source,
dest,
std::io::Error::last_os_error()
).into());
} else { } else {
return Ok(()) return Ok(());
} }
}, },
ReflinkMode::Auto => unsafe { ReflinkMode::Auto => unsafe {
@ -1090,14 +1156,14 @@ pub fn verify_target_type(target: &Path, target_type: &TargetType) -> CopyResult
(&TargetType::Directory, false) => { (&TargetType::Directory, false) => {
Err(format!("target: '{}' is not a directory", target.display()).into()) Err(format!("target: '{}' is not a directory", target.display()).into())
} }
(&TargetType::File, true) => { (&TargetType::File, true) => Err(format!(
Err(format!("cannot overwrite directory '{}' with non-directory", target.display()).into()) "cannot overwrite directory '{}' with non-directory",
} target.display()
).into()),
_ => Ok(()), _ => Ok(()),
} }
} }
/// Remove the `root` prefix from `source` and prefix it with `target` /// Remove the `root` prefix from `source` and prefix it with `target`
/// to create a file that is local to `target` /// to create a file that is local to `target`
/// # Examples /// # Examples
@ -1114,7 +1180,6 @@ pub fn localize_to_target(root: &Path, source: &Path, target: &Path) -> CopyResu
Ok(target.join(&local_to_root)) Ok(target.join(&local_to_root))
} }
pub fn paths_refer_to_same_file(p1: &Path, p2: &Path) -> io::Result<bool> { pub fn paths_refer_to_same_file(p1: &Path, p2: &Path) -> io::Result<bool> {
// We have to take symlinks and relative paths into account. // We have to take symlinks and relative paths into account.
let pathbuf1 = try!(canonicalize(p1, CanonicalizeMode::Normal)); let pathbuf1 = try!(canonicalize(p1, CanonicalizeMode::Normal));
@ -1123,12 +1188,13 @@ pub fn paths_refer_to_same_file(p1: &Path, p2: &Path) -> io::Result<bool> {
Ok(pathbuf1 == pathbuf2) Ok(pathbuf1 == pathbuf2)
} }
#[test] #[test]
fn test_cp_localize_to_target() { fn test_cp_localize_to_target() {
assert!(localize_to_target( assert!(
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

@ -31,7 +31,10 @@ pub mod Bytes {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ByteReader<R> where R: Read { pub struct ByteReader<R>
where
R: Read,
{
inner: BufReader<R>, inner: BufReader<R>,
newline_char: u8, newline_char: u8,
} }
@ -40,7 +43,7 @@ impl<R: Read> ByteReader<R> {
pub fn new(read: R, newline_char: u8) -> ByteReader<R> { pub fn new(read: R, newline_char: u8) -> ByteReader<R> {
ByteReader { ByteReader {
inner: BufReader::with_capacity(4096, read), inner: BufReader::with_capacity(4096, read),
newline_char: newline_char newline_char: newline_char,
} }
} }
} }
@ -68,15 +71,16 @@ impl<R: Read> ByteReader<R> {
let newline_char = self.newline_char; let newline_char = self.newline_char;
loop { loop {
{ // need filled_buf to go out of scope {
// need filled_buf to go out of scope
let filled_buf = match self.fill_buf() { let filled_buf = match self.fill_buf() {
Ok(b) => { Ok(b) => {
if b.len() == 0 { if b.len() == 0 {
return bytes_consumed return bytes_consumed;
} else { } else {
b b
} }
}, }
Err(e) => crash!(1, "read error: {}", e), Err(e) => crash!(1, "read error: {}", e),
}; };
@ -123,15 +127,13 @@ impl<R: Read> self::Bytes::Select for ByteReader<R> {
let buf_slice = &buffer[0..bytes + 1]; let buf_slice = &buffer[0..bytes + 1];
match buf_slice.iter().position(|byte| *byte == newline_char) { match buf_slice.iter().position(|byte| *byte == newline_char) {
Some(idx) => (SRes::Newl, idx+1), Some(idx) => (SRes::Newl, idx + 1),
None => (SRes::Comp, bytes), None => (SRes::Comp, bytes),
} }
},
_ => {
match buffer.iter().position(|byte| *byte == newline_char) {
Some(idx) => (SRes::Newl, idx+1),
None => (SRes::Part, buffer.len()),
} }
_ => match buffer.iter().position(|byte| *byte == newline_char) {
Some(idx) => (SRes::Newl, idx + 1),
None => (SRes::Part, buffer.len()),
}, },
}; };

View file

@ -13,7 +13,7 @@
extern crate uucore; extern crate uucore;
use std::fs::File; use std::fs::File;
use std::io::{stdout, stdin, BufRead, BufReader, Read, Stdout, Write}; use std::io::{stdin, stdout, BufRead, BufReader, Read, Stdout, Write};
use std::path::Path; use std::path::Path;
use ranges::Range; use ranges::Range;
@ -23,8 +23,10 @@ mod buffer;
mod ranges; mod ranges;
mod searcher; mod searcher;
static SYNTAX: &'static str = "[-d] [-s] [-z] [--output-delimiter] ((-f|-b|-c) {{sequence}}) {{sourcefile}}+"; static SYNTAX: &'static str =
static SUMMARY: &'static str = "Prints specified byte or field columns from each line of stdin or the input files"; "[-d] [-s] [-z] [--output-delimiter] ((-f|-b|-c) {{sequence}}) {{sourcefile}}+";
static SUMMARY: &'static str =
"Prints specified byte or field columns from each line of stdin or the input files";
static LONG_HELP: &'static str = " static LONG_HELP: &'static str = "
Each call must specify a mode (what to use for columns), Each call must specify a mode (what to use for columns),
a sequence (which columns to print), and provide a data source a sequence (which columns to print), and provide a data source
@ -135,8 +137,7 @@ fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> i32 {
use buffer::Bytes::Select; use buffer::Bytes::Select;
use buffer::Bytes::Selected::*; use buffer::Bytes::Selected::*;
let newline_char = let newline_char = if opts.zero_terminated { b'\0' } else { b'\n' };
if opts.zero_terminated { b'\0' } else { b'\n' };
let mut buf_read = buffer::ByteReader::new(reader, newline_char); let mut buf_read = buffer::ByteReader::new(reader, newline_char);
let mut out = stdout(); let mut out = stdout();
@ -151,11 +152,11 @@ fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> i32 {
match buf_read.select(low - cur_pos, None::<&mut Stdout>) { match buf_read.select(low - cur_pos, None::<&mut Stdout>) {
NewlineFound => { NewlineFound => {
crash_if_err!(1, out.write_all(&[newline_char])); crash_if_err!(1, out.write_all(&[newline_char]));
continue 'newline continue 'newline;
} }
Complete(len) => { Complete(len) => {
cur_pos += len; cur_pos += len;
break break;
} }
Partial(len) => cur_pos += len, Partial(len) => cur_pos += len,
EndOfFile => { EndOfFile => {
@ -163,7 +164,7 @@ fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> i32 {
crash_if_err!(1, out.write_all(&[newline_char])); crash_if_err!(1, out.write_all(&[newline_char]));
} }
break 'newline break 'newline;
} }
} }
} }
@ -175,7 +176,7 @@ fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> i32 {
} }
print_delim = true; print_delim = true;
} }
None => () None => (),
} }
// write out from low to high // write out from low to high
@ -185,14 +186,14 @@ fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> i32 {
Partial(len) => cur_pos += len, Partial(len) => cur_pos += len,
Complete(_) => { Complete(_) => {
cur_pos = high + 1; cur_pos = high + 1;
break break;
} }
EndOfFile => { EndOfFile => {
if cur_pos != low || low == high { if cur_pos != low || low == high {
crash_if_err!(1, out.write_all(&[newline_char])); crash_if_err!(1, out.write_all(&[newline_char]));
} }
break 'newline break 'newline;
} }
} }
} }
@ -205,7 +206,14 @@ fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> i32 {
0 0
} }
fn cut_fields_delimiter<R: Read>(reader: R, ranges: &[Range], delim: &str, only_delimited: bool, newline_char: u8, out_delim: &str) -> i32 { fn cut_fields_delimiter<R: Read>(
reader: R,
ranges: &[Range],
delim: &str,
only_delimited: bool,
newline_char: u8,
out_delim: &str,
) -> i32 {
let mut buf_in = BufReader::new(reader); let mut buf_in = BufReader::new(reader);
let mut out = stdout(); let mut out = stdout();
let mut buffer = Vec::new(); let mut buffer = Vec::new();
@ -218,7 +226,7 @@ fn cut_fields_delimiter<R: Read>(reader: R, ranges: &[Range], delim: &str, only_
if buffer.is_empty() { if buffer.is_empty() {
crash!(1, "read error: {}", e); crash!(1, "read error: {}", e);
} }
}, }
_ => (), _ => (),
} }
@ -229,21 +237,21 @@ fn cut_fields_delimiter<R: Read>(reader: R, ranges: &[Range], delim: &str, only_
let mut print_delim = false; let mut print_delim = false;
if delim_search.peek().is_none() { if delim_search.peek().is_none() {
if ! only_delimited { if !only_delimited {
crash_if_err!(1, out.write_all(line)); crash_if_err!(1, out.write_all(line));
if line[line.len() - 1] != newline_char { if line[line.len() - 1] != newline_char {
crash_if_err!(1, out.write_all(&[newline_char])); crash_if_err!(1, out.write_all(&[newline_char]));
} }
} }
continue continue;
} }
for &Range { low, high } in ranges.iter() { for &Range { low, high } in ranges.iter() {
if low - fields_pos > 0 { if low - fields_pos > 0 {
low_idx = match delim_search.nth(low - fields_pos - 1) { low_idx = match delim_search.nth(low - fields_pos - 1) {
Some((_, beyond_delim)) => beyond_delim, Some((_, beyond_delim)) => beyond_delim,
None => break None => break,
}; };
} }
@ -269,9 +277,9 @@ fn cut_fields_delimiter<R: Read>(reader: R, ranges: &[Range], delim: &str, only_
crash_if_err!(1, out.write_all(segment)); crash_if_err!(1, out.write_all(segment));
if line[line.len() - 1] == newline_char { if line[line.len() - 1] == newline_char {
continue 'newline continue 'newline;
} }
break break;
} }
} }
} }
@ -284,14 +292,19 @@ fn cut_fields_delimiter<R: Read>(reader: R, ranges: &[Range], delim: &str, only_
} }
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 = let newline_char = if opts.zero_terminated { b'\0' } else { b'\n' };
if opts.zero_terminated { b'\0' } else { b'\n' };
match opts.out_delimeter { match opts.out_delimeter {
Some(ref o_delim) => { Some(ref o_delim) => {
return cut_fields_delimiter(reader, ranges, &opts.delimiter, return cut_fields_delimiter(
opts.only_delimited, newline_char, o_delim) reader,
ranges,
&opts.delimiter,
opts.only_delimited,
newline_char,
o_delim,
)
} }
None => () None => (),
} }
let mut buf_in = BufReader::new(reader); let mut buf_in = BufReader::new(reader);
@ -306,7 +319,7 @@ fn cut_fields<R: Read>(reader: R, ranges: &[Range], opts: &FieldOptions) -> i32
if buffer.is_empty() { if buffer.is_empty() {
crash!(1, "read error: {}", e); crash!(1, "read error: {}", e);
} }
}, }
_ => (), _ => (),
} }
@ -317,21 +330,21 @@ fn cut_fields<R: Read>(reader: R, ranges: &[Range], opts: &FieldOptions) -> i32
let mut print_delim = false; let mut print_delim = false;
if delim_search.peek().is_none() { if delim_search.peek().is_none() {
if ! opts.only_delimited { if !opts.only_delimited {
crash_if_err!(1, out.write_all(line)); crash_if_err!(1, out.write_all(line));
if line[line.len() - 1] != newline_char { if line[line.len() - 1] != newline_char {
crash_if_err!(1, out.write_all(&[newline_char])); crash_if_err!(1, out.write_all(&[newline_char]));
} }
} }
continue continue;
} }
for &Range { low, high } in ranges.iter() { for &Range { low, high } in ranges.iter() {
if low - fields_pos > 0 { if low - fields_pos > 0 {
low_idx = match delim_search.nth(low - fields_pos - 1) { low_idx = match delim_search.nth(low - fields_pos - 1) {
Some((_, beyond_delim)) => beyond_delim, Some((_, beyond_delim)) => beyond_delim,
None => break None => break,
}; };
} }
@ -357,9 +370,9 @@ fn cut_fields<R: Read>(reader: R, ranges: &[Range], opts: &FieldOptions) -> i32
crash_if_err!(1, out.write_all(segment)); crash_if_err!(1, out.write_all(segment));
if line[line.len() - 1] == newline_char { if line[line.len() - 1] == newline_char {
continue 'newline continue 'newline;
} }
break break;
} }
} }
} }
@ -374,11 +387,15 @@ fn cut_files(mut filenames: Vec<String>, mode: Mode) -> i32 {
let mut stdin_read = false; let mut stdin_read = false;
let mut exit_code = 0; let mut exit_code = 0;
if filenames.is_empty() { filenames.push("-".to_owned()); } if filenames.is_empty() {
filenames.push("-".to_owned());
}
for filename in &filenames { for filename in &filenames {
if filename == "-" { if filename == "-" {
if stdin_read { continue } if stdin_read {
continue;
}
exit_code |= match mode { exit_code |= match mode {
Mode::Bytes(ref ranges, ref opts) => cut_bytes(stdin(), ranges, opts), Mode::Bytes(ref ranges, ref opts) => cut_bytes(stdin(), ranges, opts),
@ -392,14 +409,14 @@ fn cut_files(mut filenames: Vec<String>, mode: Mode) -> i32 {
if !path.exists() { if !path.exists() {
show_error!("{}", msg_args_nonexistent_file!(filename)); show_error!("{}", msg_args_nonexistent_file!(filename));
continue continue;
} }
let file = match File::open(&path) { let file = match File::open(&path) {
Ok(f) => f, Ok(f) => f,
Err(e) => { Err(e) => {
show_error!("opening '{}': {}", &filename[..], e); show_error!("opening '{}': {}", &filename[..], e);
continue continue;
} }
}; };
@ -428,20 +445,35 @@ pub fn uumain(args: Vec<String>) -> i32 {
.parse(args); .parse(args);
let complement = matches.opt_present("complement"); let complement = matches.opt_present("complement");
let mode_parse = match (matches.opt_str("bytes"), let mode_parse = match (
matches.opt_str("bytes"),
matches.opt_str("characters"), matches.opt_str("characters"),
matches.opt_str("fields")) { matches.opt_str("fields"),
) {
(Some(byte_ranges), None, None) => { (Some(byte_ranges), None, None) => {
list_to_ranges(&byte_ranges[..], complement) list_to_ranges(&byte_ranges[..], complement).map(|ranges| {
.map(|ranges| Mode::Bytes(ranges, Options { out_delim: matches.opt_str("output-delimiter"), zero_terminated : matches.opt_present("zero-terminated") })) Mode::Bytes(
ranges,
Options {
out_delim: matches.opt_str("output-delimiter"),
zero_terminated: matches.opt_present("zero-terminated"),
},
)
})
} }
(None, Some(char_ranges), None) => { (None, Some(char_ranges), None) => {
list_to_ranges(&char_ranges[..], complement) list_to_ranges(&char_ranges[..], complement).map(|ranges| {
.map(|ranges| Mode::Characters(ranges, Options { out_delim: matches.opt_str("output-delimiter"), zero_terminated : matches.opt_present("zero-terminated") })) Mode::Characters(
ranges,
Options {
out_delim: matches.opt_str("output-delimiter"),
zero_terminated: matches.opt_present("zero-terminated"),
},
)
})
} }
(None, None, Some(field_ranges)) => { (None, None, Some(field_ranges)) => list_to_ranges(&field_ranges[..], complement)
list_to_ranges(&field_ranges[..], complement).and_then(|ranges| .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() {
@ -449,7 +481,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
} else { } else {
Some(s) Some(s)
} }
}, }
None => None, None => None,
}; };
@ -459,7 +491,12 @@ pub fn uumain(args: Vec<String>) -> i32 {
match matches.opt_str("delimiter") { match matches.opt_str("delimiter") {
Some(delim) => { Some(delim) => {
if delim.chars().count() > 1 { if delim.chars().count() > 1 {
Err(msg_opt_invalid_should_be!("empty or 1 character long", "a value 2 characters or longer", "--delimiter", "-d").to_owned()) Err(msg_opt_invalid_should_be!(
"empty or 1 character long",
"a value 2 characters or longer",
"--delimiter",
"-d"
).to_owned())
} else { } else {
let delim = if delim.is_empty() { let delim = if delim.is_empty() {
"\0".to_owned() "\0".to_owned()
@ -467,43 +504,51 @@ pub fn uumain(args: Vec<String>) -> i32 {
delim delim
}; };
Ok(Mode::Fields(ranges, Ok(Mode::Fields(
ranges,
FieldOptions { FieldOptions {
delimiter: delim, delimiter: delim,
out_delimeter: out_delim, out_delimeter: out_delim,
only_delimited: only_delimited, only_delimited: only_delimited,
zero_terminated: zero_terminated zero_terminated: zero_terminated,
})) },
))
} }
} }
None => Ok(Mode::Fields(ranges, None => Ok(Mode::Fields(
ranges,
FieldOptions { FieldOptions {
delimiter: "\t".to_owned(), delimiter: "\t".to_owned(),
out_delimeter: out_delim, out_delimeter: out_delim,
only_delimited: only_delimited, only_delimited: only_delimited,
zero_terminated: zero_terminated zero_terminated: zero_terminated,
})) },
)),
} }
} }),
) (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)")
(ref b, ref c, ref f) if b.is_some() || c.is_some() || f.is_some() => { .to_owned(),
Err(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)").to_owned())
}; };
let mode_parse = match mode_parse { let mode_parse = match mode_parse {
Err(_) => mode_parse, Err(_) => mode_parse,
Ok(mode) => { Ok(mode) => match mode {
match mode { Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.opt_present("delimiter") => Err(
Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.opt_present("delimiter") => msg_opt_only_usable_if!("printing a sequence of fields", "--delimiter", "-d")
Err(msg_opt_only_usable_if!("printing a sequence of fields", "--delimiter", "-d").to_owned()), .to_owned(),
Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.opt_present("only-delimited") => ),
Err(msg_opt_only_usable_if!("printing a sequence of fields", "--only-delimited", "-s").to_owned()), Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.opt_present("only-delimited") => {
Err(msg_opt_only_usable_if!(
"printing a sequence of fields",
"--only-delimited",
"-s"
).to_owned())
}
_ => Ok(mode), _ => Ok(mode),
} },
}
}; };
match mode_parse { match mode_parse {

View file

@ -9,7 +9,7 @@
use std::str::FromStr; use std::str::FromStr;
#[derive(PartialEq,Eq,PartialOrd,Ord,Debug)] #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Range { pub struct Range {
pub low: usize, pub low: usize,
pub high: usize, pub high: usize,
@ -30,40 +30,56 @@ impl FromStr for Range {
match (parts.next(), parts.next()) { match (parts.next(), parts.next()) {
(Some(nm), None) => { (Some(nm), None) => {
if let Ok(nm) = nm.parse::<usize>() { if let Ok(nm) = nm.parse::<usize>() {
if nm > 0 { Ok(Range{ low: nm, high: nm}) } else { Err(field) } if nm > 0 {
Ok(Range { low: nm, high: nm })
} else {
Err(field)
}
} else { } else {
Err(inval) Err(inval)
} }
} }
(Some(n), Some(m)) if m.len() == 0 => { (Some(n), Some(m)) if m.len() == 0 => {
if let Ok(low) = n.parse::<usize>() { if let Ok(low) = n.parse::<usize>() {
if low > 0 { Ok(Range{ low: low, high: MAX - 1}) } else { Err(field) } if low > 0 {
Ok(Range {
low: low,
high: MAX - 1,
})
} else {
Err(field)
}
} else { } else {
Err(inval) Err(inval)
} }
} }
(Some(n), Some(m)) if n.len() == 0 => { (Some(n), Some(m)) if n.len() == 0 => {
if let Ok(high) = m.parse::<usize>() { if let Ok(high) = m.parse::<usize>() {
if high > 0 { Ok(Range{ low: 1, high: high}) } else { Err(field) } if high > 0 {
Ok(Range { low: 1, high: high })
} else {
Err(field)
}
} else { } else {
Err(inval) Err(inval)
} }
} }
(Some(n), Some(m)) => { (Some(n), Some(m)) => match (n.parse::<usize>(), m.parse::<usize>()) {
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 { low: low, high: high }) Ok(Range {
low: low,
high: high,
})
} else if low == 0 { } else if low == 0 {
Err(field) Err(field)
} else { } else {
Err(order) Err(order)
} }
}, }
_ => Err(inval), _ => Err(inval),
} },
} _ => unreachable!(),
_ => unreachable!()
} }
} }
} }
@ -72,12 +88,12 @@ impl Range {
pub fn from_list(list: &str) -> Result<Vec<Range>, String> { pub fn from_list(list: &str) -> Result<Vec<Range>, String> {
use std::cmp::max; use std::cmp::max;
let mut ranges : Vec<Range> = vec!(); let mut ranges: Vec<Range> = vec![];
for item in list.split(',') { for item in list.split(',') {
match FromStr::from_str(item) { match FromStr::from_str(item) {
Ok(range_item) => ranges.push(range_item), Ok(range_item) => ranges.push(range_item),
Err(e)=> return Err(format!("range '{}' was invalid: {}", item, e)) Err(e) => return Err(format!("range '{}' was invalid: {}", item, e)),
} }
} }
@ -103,7 +119,10 @@ pub fn complement(ranges: &[Range]) -> Vec<Range> {
let mut complements = Vec::with_capacity(ranges.len() + 1); let mut complements = Vec::with_capacity(ranges.len() + 1);
if ranges.len() > 0 && ranges[0].low > 1 { if ranges.len() > 0 && ranges[0].low > 1 {
complements.push(Range { low: 1, high: ranges[0].low - 1 }); complements.push(Range {
low: 1,
high: ranges[0].low - 1,
});
} }
let mut ranges_iter = ranges.iter().peekable(); let mut ranges_iter = ranges.iter().peekable();
@ -113,7 +132,7 @@ pub fn complement(ranges: &[Range]) -> Vec<Range> {
if left.high + 1 != right.low { if left.high + 1 != right.low {
complements.push(Range { complements.push(Range {
low: left.high + 1, low: left.high + 1,
high: right.low - 1 high: right.low - 1,
}); });
} }
} }
@ -121,11 +140,11 @@ pub fn complement(ranges: &[Range]) -> Vec<Range> {
if last.high < usize::MAX - 1 { if last.high < usize::MAX - 1 {
complements.push(Range { complements.push(Range {
low: last.high + 1, low: last.high + 1,
high: usize::MAX - 1 high: usize::MAX - 1,
}); });
} }
} }
_ => break _ => break,
} }
} }

View file

@ -11,7 +11,7 @@
pub struct Searcher<'a> { pub struct Searcher<'a> {
haystack: &'a [u8], haystack: &'a [u8],
needle: &'a [u8], needle: &'a [u8],
position: usize position: usize,
} }
impl<'a> Searcher<'a> { impl<'a> Searcher<'a> {
@ -19,7 +19,7 @@ impl<'a> Searcher<'a> {
Searcher { Searcher {
haystack: haystack, haystack: haystack,
needle: needle, needle: needle,
position: 0 position: 0,
} }
} }
} }

View file

@ -14,7 +14,7 @@ extern crate chrono;
extern crate clap; extern crate clap;
extern crate uucore; extern crate uucore;
use chrono::{DateTime, FixedOffset, Offset, Local}; use chrono::{DateTime, FixedOffset, Local, Offset};
use chrono::offset::Utc; use chrono::offset::Utc;
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
@ -108,14 +108,12 @@ impl<'a> From<&'a str> for Rfc3339Format {
} }
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let settings = parse_cli(args); let settings = parse_cli(args);
if let Some(_time) = settings.set_to { if let Some(_time) = settings.set_to {
unimplemented!(); unimplemented!();
// Probably need to use this syscall: // Probably need to use this syscall:
// https://doc.rust-lang.org/libc/i686-unknown-linux-gnu/libc/fn.clock_settime.html // https://doc.rust-lang.org/libc/i686-unknown-linux-gnu/libc/fn.clock_settime.html
} else { } else {
// Declare a file here because it needs to outlive the `dates` iterator. // Declare a file here because it needs to outlive the `dates` iterator.
let file: File; let file: File;
@ -134,8 +132,9 @@ pub fn uumain(args: Vec<String>) -> i32 {
/// Parse a `String` into a `DateTime`. /// Parse a `String` into a `DateTime`.
/// If it fails, return a tuple of the `String` along with its `ParseError`. /// If it fails, return a tuple of the `String` along with its `ParseError`.
fn parse_date(s: String) fn parse_date(
-> Result<DateTime<FixedOffset>, (String, chrono::format::ParseError)> { s: String,
) -> Result<DateTime<FixedOffset>, (String, chrono::format::ParseError)> {
// TODO: The GNU date command can parse a wide variety of inputs. // TODO: The GNU date command can parse a wide variety of inputs.
s.parse().map_err(|e| (s, e)) s.parse().map_err(|e| (s, e))
} }
@ -178,7 +177,6 @@ pub fn uumain(args: Vec<String>) -> i32 {
0 0
} }
/// Handle command line arguments. /// Handle command line arguments.
fn parse_cli(args: Vec<String>) -> Settings { fn parse_cli(args: Vec<String>) -> Settings {
let matches = clap_app!( let matches = clap_app!(
@ -221,15 +219,13 @@ fn parse_cli(args: Vec<String>) -> Settings {
// (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();
Format::Custom(form) Format::Custom(form)
} else if let Some(fmt) = matches.values_of("iso_8601").map(|mut iter| { } else if let Some(fmt) = matches
iter.next() .values_of("iso_8601")
.unwrap_or(DATE) .map(|mut iter| iter.next().unwrap_or(DATE).into())
.into() {
}) {
Format::Iso8601(fmt) Format::Iso8601(fmt)
} else if matches.is_present("rfc_2822") { } else if matches.is_present("rfc_2822") {
Format::Rfc2822 Format::Rfc2822
@ -256,27 +252,22 @@ fn parse_cli(args: Vec<String>) -> Settings {
} }
} }
/// Return the appropriate format string for the given settings. /// Return the appropriate format string for the given settings.
fn make_format_string(settings: &Settings) -> &str { fn make_format_string(settings: &Settings) -> &str {
match settings.format { match settings.format {
Format::Iso8601(ref fmt) => { Format::Iso8601(ref fmt) => match fmt {
match fmt {
&Iso8601Format::Date => "%F", &Iso8601Format::Date => "%F",
&Iso8601Format::Hours => "%FT%H%:z", &Iso8601Format::Hours => "%FT%H%:z",
&Iso8601Format::Minutes => "%FT%H:%M%:z", &Iso8601Format::Minutes => "%FT%H:%M%:z",
&Iso8601Format::Seconds => "%FT%T%:z", &Iso8601Format::Seconds => "%FT%T%:z",
&Iso8601Format::Ns => "%FT%T,%f%:z", &Iso8601Format::Ns => "%FT%T,%f%:z",
} },
}
Format::Rfc2822 => "%a, %d %h %Y %T %z", Format::Rfc2822 => "%a, %d %h %Y %T %z",
Format::Rfc3339(ref fmt) => { Format::Rfc3339(ref fmt) => match fmt {
match fmt {
&Rfc3339Format::Date => "%F", &Rfc3339Format::Date => "%F",
&Rfc3339Format::Seconds => "%F %T%:z", &Rfc3339Format::Seconds => "%F %T%:z",
&Rfc3339Format::Ns => "%F %T.%f%:z", &Rfc3339Format::Ns => "%F %T.%f%:z",
} },
}
Format::Custom(ref fmt) => fmt, Format::Custom(ref fmt) => fmt,
Format::Default => "%c", Format::Default => "%c",
} }

View file

@ -58,27 +58,33 @@ pub fn guess_syntax() -> OutputFmt {
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("b", "sh", "output Bourne shell code to set LS_COLORS") .optflag("b", "sh", "output Bourne shell code to set LS_COLORS")
.optflag("", .optflag(
"",
"bourne-shell", "bourne-shell",
"output Bourne shell code to set LS_COLORS") "output Bourne shell code to set LS_COLORS",
)
.optflag("c", "csh", "output C shell code to set LS_COLORS") .optflag("c", "csh", "output C shell code to set LS_COLORS")
.optflag("", "c-shell", "output C shell code to set LS_COLORS") .optflag("", "c-shell", "output C shell code to set LS_COLORS")
.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") || if (matches.opt_present("csh") || matches.opt_present("c-shell") || matches.opt_present("sh")
matches.opt_present("sh") || matches.opt_present("bourne-shell")) && || matches.opt_present("bourne-shell")) && matches.opt_present("print-database")
matches.opt_present("print-database") { {
disp_err!("the options to output dircolors' internal database and\nto select a shell \ disp_err!(
syntax are mutually exclusive"); "the options to output dircolors' internal database and\nto select a shell \
syntax are mutually exclusive"
);
return 1; return 1;
} }
if matches.opt_present("print-database") { if matches.opt_present("print-database") {
if !matches.free.is_empty() { if !matches.free.is_empty() {
disp_err!("extra operand {}\nfile operands cannot be combined with \ disp_err!(
"extra operand {}\nfile operands cannot be combined with \
--print-database (-p)", --print-database (-p)",
matches.free[0]); matches.free[0]
);
return 1; return 1;
} }
println!("{}", INTERNAL_DB); println!("{}", INTERNAL_DB);
@ -113,9 +119,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
match File::open(matches.free[0].as_str()) { match File::open(matches.free[0].as_str()) {
Ok(f) => { Ok(f) => {
let fin = BufReader::new(f); let fin = BufReader::new(f);
result = parse(fin.lines().filter_map(|l| l.ok()), result = parse(
fin.lines().filter_map(|l| l.ok()),
out_format, out_format,
matches.free[0].as_str()) matches.free[0].as_str(),
)
} }
Err(e) => { Err(e) => {
show_info!("{}: {}", matches.free[0], e); show_info!("{}: {}", matches.free[0], e);
@ -193,8 +201,9 @@ enum ParseState {
} }
use std::collections::HashMap; use std::collections::HashMap;
fn parse<T>(lines: T, fmt: OutputFmt, fp: &str) -> Result<String, String> fn parse<T>(lines: T, fmt: OutputFmt, fp: &str) -> Result<String, String>
where T: IntoIterator, where
T::Item: Borrow<str> T: IntoIterator,
T::Item: Borrow<str>,
{ {
// 1440 > $(dircolors | wc -m) // 1440 > $(dircolors | wc -m)
let mut result = String::with_capacity(1440); let mut result = String::with_capacity(1440);
@ -257,7 +266,10 @@ fn parse<T>(lines: T, fmt: OutputFmt, fp: &str) -> Result<String, String>
let (key, val) = line.split_two(); let (key, val) = line.split_two();
if val.is_empty() { if val.is_empty() {
return Err(format!("{}:{}: invalid line; missing second token", fp, num)); return Err(format!(
"{}:{}: invalid line; missing second token",
fp, num
));
} }
let lower = key.to_lowercase(); let lower = key.to_lowercase();

View file

@ -28,7 +28,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
.optflag("z", "zero", "separate output with NUL rather than newline") .optflag("z", "zero", "separate output with NUL rather than newline")
.parse(args); .parse(args);
let separator = if matches.opt_present("zero") {"\0"} else {"\n"}; let separator = if matches.opt_present("zero") {
"\0"
} else {
"\n"
};
if !matches.free.is_empty() { if !matches.free.is_empty() {
for path in &matches.free { for path in &matches.free {

View file

@ -64,26 +64,29 @@ impl Stat {
nlink: metadata.nlink() as u64, nlink: metadata.nlink() as u64,
created: metadata.mtime() as u64, created: metadata.mtime() as u64,
accessed: metadata.atime() as u64, accessed: metadata.atime() as u64,
modified: metadata.mtime() as u64 modified: metadata.mtime() as u64,
} }
} }
} }
// this takes `my_stat` to avoid having to stat files multiple times. // this takes `my_stat` to avoid having to stat files multiple times.
// XXX: this should use the impl Trait return type when it is stabilized // XXX: this should use the impl Trait return type when it is stabilized
fn du(mut my_stat: Stat, options: &Options, depth: usize) fn du(mut my_stat: Stat, options: &Options, depth: usize) -> Box<DoubleEndedIterator<Item = Stat>> {
-> Box<DoubleEndedIterator<Item = Stat>> let mut stats = vec![];
{ let mut futures = vec![];
let mut stats = vec!();
let mut futures = vec!();
if my_stat.is_dir { if my_stat.is_dir {
let read = match fs::read_dir(&my_stat.path) { let read = match fs::read_dir(&my_stat.path) {
Ok(read) => read, Ok(read) => read,
Err(e) => { Err(e) => {
safe_writeln!(stderr(), "{}: cannot read directory {}: {}", safe_writeln!(
options.program_name, my_stat.path.display(), e); stderr(),
return Box::new(iter::once(my_stat)) "{}: cannot read directory {}: {}",
options.program_name,
my_stat.path.display(),
e
);
return Box::new(iter::once(my_stat));
} }
}; };
@ -102,7 +105,12 @@ fn du(mut my_stat: Stat, options: &Options, depth: usize)
} }
} }
stats.extend(futures.into_iter().flat_map(|val| val).rev().filter_map(|stat| { stats.extend(
futures
.into_iter()
.flat_map(|val| val)
.rev()
.filter_map(|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;
@ -112,14 +120,18 @@ fn du(mut my_stat: Stat, options: &Options, depth: usize)
} else { } else {
None None
} }
})); }),
);
stats.push(my_stat); stats.push(my_stat);
Box::new(stats.into_iter()) Box::new(stats.into_iter())
} }
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let syntax = format!("[OPTION]... [FILE]... let syntax = format!(
{0} [OPTION]... --files0-from=F", NAME); "[OPTION]... [FILE]...
{0} [OPTION]... --files0-from=F",
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("a", "all", " write counts for all files, not just directories")
@ -176,8 +188,8 @@ pub fn uumain(args: Vec<String>) -> i32 {
line argument; --max-depth=0 is the same as --summarize", "N") line argument; --max-depth=0 is the same as --summarize", "N")
// In main // In main
.optflagopt("", "time", "show time of the last modification of any file in the .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 of modification time: directory, or any of its subdirectories. If WORD is given, show time as WORD instead
atime, access, use, ctime or status", "WORD") of modification time: atime, access, use, ctime or status", "WORD")
// In main // In main
.optopt("", "time-style", "show times using style STYLE: .optopt("", "time-style", "show times using style STYLE:
full-iso, long-iso, iso, +FORMAT FORMAT is interpreted like 'date'", "STYLE") full-iso, long-iso, iso, +FORMAT FORMAT is interpreted like 'date'", "STYLE")
@ -207,10 +219,22 @@ pub fn uumain(args: Vec<String>) -> i32 {
separate_dirs: matches.opt_present("S"), separate_dirs: matches.opt_present("S"),
}; };
let strs = if matches.free.is_empty() {vec!("./".to_owned())} else {matches.free.clone()}; let strs = if matches.free.is_empty() {
vec!["./".to_owned()]
} else {
matches.free.clone()
};
let mb = if matches.opt_present("si") {1000 * 1000} else {1024 * 1024}; let mb = if matches.opt_present("si") {
let kb = if matches.opt_present("si") {1000} else {1024}; 1000 * 1000
} else {
1024 * 1024
};
let kb = if matches.opt_present("si") {
1000
} else {
1024
};
let block_size = match matches.opt_str("block-size") { let block_size = match matches.opt_str("block-size") {
Some(s) => { Some(s) => {
@ -232,22 +256,30 @@ pub fn uumain(args: Vec<String>) -> i32 {
} }
let number = numbers.parse::<u64>().unwrap(); let number = numbers.parse::<u64>().unwrap();
let multiple = match &letters[..] { let multiple = match &letters[..] {
"K" => 1024u64.pow(1), "M" => 1024u64.pow(2), "K" => 1024u64.pow(1),
"G" => 1024u64.pow(3), "T" => 1024u64.pow(4), "M" => 1024u64.pow(2),
"P" => 1024u64.pow(5), "E" => 1024u64.pow(6), "G" => 1024u64.pow(3),
"Z" => 1024u64.pow(7), "Y" => 1024u64.pow(8), "T" => 1024u64.pow(4),
"KB" => 1000u64.pow(1), "MB" => 1000u64.pow(2), "P" => 1024u64.pow(5),
"GB" => 1000u64.pow(3), "TB" => 1000u64.pow(4), "E" => 1024u64.pow(6),
"PB" => 1000u64.pow(5), "EB" => 1000u64.pow(6), "Z" => 1024u64.pow(7),
"ZB" => 1000u64.pow(7), "YB" => 1000u64.pow(8), "Y" => 1024u64.pow(8),
"KB" => 1000u64.pow(1),
"MB" => 1000u64.pow(2),
"GB" => 1000u64.pow(3),
"TB" => 1000u64.pow(4),
"PB" => 1000u64.pow(5),
"EB" => 1000u64.pow(6),
"ZB" => 1000u64.pow(7),
"YB" => 1000u64.pow(8),
_ => { _ => {
show_error!("invalid --block-size argument '{}'", s); show_error!("invalid --block-size argument '{}'", s);
return 1; return 1;
} }
}; };
number * multiple number * multiple
}, }
None => 1024 None => 1024,
}; };
let convert_size = |size: u64| -> String { let convert_size = |size: u64| -> String {
@ -269,26 +301,28 @@ pub fn uumain(args: Vec<String>) -> i32 {
}; };
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!("invalid argument '{}' for 'time style' show_error!(
"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.", s, NAME); Try '{} --help' for more information.",
s,
NAME
);
return 1; return 1;
} }
}
}, },
None => "%Y-%m-%d %H:%M" None => "%Y-%m-%d %H:%M",
}; };
let line_separator = if matches.opt_present("0") {"\0"} else {"\n"}; let line_separator = if matches.opt_present("0") { "\0" } else { "\n" };
let mut grand_total = 0; let mut grand_total = 0;
for path_str in strs.into_iter() { for path_str in strs.into_iter() {
@ -313,26 +347,40 @@ Try '{} --help' for more information.", s, NAME);
"created" => stat.created, "created" => stat.created,
"modified" => stat.modified, "modified" => stat.modified,
_ => { _ => {
show_error!("invalid argument 'modified' for '--time' show_error!(
"invalid argument 'modified' for '--time'
Valid arguments are: Valid arguments are:
- 'accessed', 'created', 'modified' - 'accessed', 'created', 'modified'
Try '{} --help' for more information.", NAME); Try '{} --help' for more information.",
NAME
);
return 1; return 1;
} }
}, },
None => stat.modified None => stat.modified,
}; };
((time / 1000) as i64, (time % 1000 * 1000000) as i32) ((time / 1000) as i64, (time % 1000 * 1000000) as i32)
}; };
time::at(Timespec::new(secs, nsecs)) time::at(Timespec::new(secs, nsecs))
}; };
if !summarize || (summarize && index == len-1) { if !summarize || (summarize && index == len - 1) {
let time_str = tm.strftime(time_format_str).unwrap(); let time_str = tm.strftime(time_format_str).unwrap();
print!("{}\t{}\t{}{}", convert_size(size), time_str, stat.path.display(), line_separator); print!(
"{}\t{}\t{}{}",
convert_size(size),
time_str,
stat.path.display(),
line_separator
);
} }
} else { } else {
if !summarize || (summarize && index == len-1) { if !summarize || (summarize && index == len - 1) {
print!("{}\t{}{}", convert_size(size), stat.path.display(), line_separator); print!(
"{}\t{}{}",
convert_size(size),
stat.path.display(),
line_separator
);
} }
} }
if options.total && index == (len - 1) { if options.total && index == (len - 1) {

View file

@ -12,7 +12,7 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::io::{Write, stdout}; use std::io::{stdout, Write};
use std::str::from_utf8; use std::str::from_utf8;
#[allow(dead_code)] #[allow(dead_code)]
@ -43,13 +43,13 @@ enum Base {
struct Opts { struct Opts {
newline: bool, newline: bool,
escape: bool escape: bool,
} }
fn convert_str(string: &[u8], index: usize, base: Base) -> (char, usize) { fn convert_str(string: &[u8], index: usize, base: Base) -> (char, usize) {
let (max_digits, is_legal_digit): (usize, fn(u8) -> bool) = match base { let (max_digits, is_legal_digit): (usize, fn(u8) -> bool) = match base {
Base::B8 => (3, |c| { (c as char).is_digit(8) }), Base::B8 => (3, |c| (c as char).is_digit(8)),
Base::B16 => (2, |c| { (c as char).is_digit(16) }), Base::B16 => (2, |c| (c as char).is_digit(16)),
}; };
let mut bytes = vec![]; let mut bytes = vec![];
@ -68,10 +68,11 @@ fn convert_str(string: &[u8], index: usize, base: Base) -> (char, usize) {
if bytes.is_empty() { if bytes.is_empty() {
(' ', 0) (' ', 0)
} else { } else {
(usize::from_str_radix( (
from_utf8(bytes.as_ref()).unwrap(), usize::from_str_radix(from_utf8(bytes.as_ref()).unwrap(), base as u32).unwrap() as u8
base as u32 as char,
).unwrap() as u8 as char, bytes.len()) bytes.len(),
)
} }
} }
@ -79,7 +80,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 options = Opts { let options = Opts {
@ -112,20 +117,15 @@ pub fn uumain(args: Vec<String>) -> i32 {
'c' => break, 'c' => break,
'e' => print!("\x1B"), 'e' => print!("\x1B"),
'f' => print!("\x0C"), 'f' => print!("\x0C"),
ch => { // 'x' or '0' or _ ch => {
idx = if ch == 'x' || ch == '0' { // 'x' or '0' or _
idx + 1 idx = if ch == 'x' || ch == '0' { idx + 1 } else { idx };
} else {
idx
};
let base = if ch == 'x' { Base::B16 } else { Base::B8 }; let base = if ch == 'x' { Base::B16 } else { Base::B8 };
match convert_str(string.as_bytes(), idx, base) { match convert_str(string.as_bytes(), idx, base) {
(_, 0) => { (_, 0) => match ch {
match ch {
'x' => print!("\\x"), 'x' => print!("\\x"),
'0' => print!("\0"), '0' => print!("\0"),
_ => print!("\\{}", c), _ => print!("\\{}", c),
}
}, },
(c, num_char_used) => { (c, num_char_used) => {
print!("{}", c); print!("{}", c);

45
src/env/env.rs vendored
View file

@ -1,5 +1,4 @@
#![crate_name = "uu_env"] #![crate_name = "uu_env"]
/* /*
* This file is part of the uutils coreutils package. * This file is part of the uutils coreutils package.
* *
@ -10,14 +9,13 @@
*/ */
/* last synced with: env (GNU coreutils) 8.13 */ /* last synced with: env (GNU coreutils) 8.13 */
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::env; use std::env;
use std::io::{Write, stdout}; use std::io::{stdout, Write};
use std::process::Command; use std::process::Command;
static NAME: &'static str = "env"; static NAME: &'static str = "env";
@ -32,7 +30,7 @@ struct options {
null: bool, null: bool,
unsets: Vec<String>, unsets: Vec<String>,
sets: Vec<(String, String)>, sets: Vec<(String, String)>,
program: Vec<String> program: Vec<String>,
} }
// print name=value env pairs on screen // print name=value env pairs on screen
@ -45,16 +43,21 @@ fn print_env(null: bool) {
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut core_opts = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP); let mut core_opts = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP);
core_opts.optflag("i", "ignore-environment", "start with an empty environment") core_opts
.optflag("0", "null", "end each output line with a 0 byte rather than newline") .optflag("i", "ignore-environment", "start with an empty environment")
.optflag(
"0",
"null",
"end each output line with a 0 byte rather than newline",
)
.optopt("u", "unset", "remove variable from the environment", "NAME"); .optopt("u", "unset", "remove variable from the environment", "NAME");
let mut opts = Box::new(options { let mut opts = Box::new(options {
ignore_env: false, ignore_env: false,
null: false, null: false,
unsets: vec!(), unsets: vec![],
sets: vec!(), sets: vec![],
program: vec!() program: vec![],
}); });
let mut wait_cmd = false; let mut wait_cmd = false;
@ -86,8 +89,14 @@ pub fn uumain(args: Vec<String>) -> i32 {
} }
} else if opt.starts_with("--") { } else if opt.starts_with("--") {
match opt.as_ref() { match opt.as_ref() {
"--help" => { core_opts.parse(vec![String::new(), String::from("--help")]); return 0; } "--help" => {
"--version" => { core_opts.parse(vec![String::new(), String::from("--version")]); return 0; } core_opts.parse(vec![String::new(), String::from("--help")]);
return 0;
}
"--version" => {
core_opts.parse(vec![String::new(), String::from("--version")]);
return 0;
}
"--ignore-environment" => opts.ignore_env = true, "--ignore-environment" => opts.ignore_env = true,
"--null" => opts.null = true, "--null" => opts.null = true,
@ -96,7 +105,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
match var { match var {
None => println!("{}: this option requires an argument: {}", NAME, opt), None => println!("{}: this option requires an argument: {}", NAME, opt),
Some(s) => opts.unsets.push(s.to_owned()) Some(s) => opts.unsets.push(s.to_owned()),
} }
} }
@ -127,7 +136,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
match var { match var {
None => println!("{}: this option requires an argument: {}", NAME, opt), None => println!("{}: this option requires an argument: {}", NAME, opt),
Some(s) => opts.unsets.push(s.to_owned()) Some(s) => opts.unsets.push(s.to_owned()),
} }
} }
_ => { _ => {
@ -183,8 +192,14 @@ pub fn uumain(args: Vec<String>) -> i32 {
let prog = opts.program[0].clone(); let prog = opts.program[0].clone();
let args = &opts.program[1..]; let args = &opts.program[1..];
match Command::new(prog).args(args).status() { match Command::new(prog).args(args).status() {
Ok(exit) => return if exit.success() { 0 } else { exit.code().unwrap() }, Ok(exit) => {
Err(_) => return 1 return if exit.success() {
0
} else {
exit.code().unwrap()
}
}
Err(_) => return 1,
} }
} else { } else {
// no program provided // no program provided

View file

@ -11,8 +11,8 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
extern crate unicode_width;
extern crate getopts; extern crate getopts;
extern crate unicode_width;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
@ -33,18 +33,21 @@ static DEFAULT_TABSTOP: usize = 8;
fn tabstops_parse(s: String) -> Vec<usize> { fn tabstops_parse(s: String) -> Vec<usize> {
let words = s.split(',').collect::<Vec<&str>>(); let words = s.split(',').collect::<Vec<&str>>();
let nums = words.into_iter() let nums = words
.map(|sn| sn.parse::<usize>() .into_iter()
.unwrap_or_else( .map(|sn| {
|_| crash!(1, "{}\n", "tab size contains invalid character(s)")) sn.parse::<usize>()
) .unwrap_or_else(|_| crash!(1, "{}\n", "tab size contains invalid character(s)"))
})
.collect::<Vec<usize>>(); .collect::<Vec<usize>>();
if nums.iter().any(|&n| n == 0) { if nums.iter().any(|&n| n == 0) {
crash!(1, "{}\n", "tab size cannot be 0"); crash!(1, "{}\n", "tab size cannot be 0");
} }
if let (false, _) = nums.iter().fold((true, 0), |(acc, last), &n| (acc && last <= n, n)) { if let (false, _) = nums.iter()
.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");
} }
@ -62,8 +65,8 @@ struct Options {
impl Options { impl Options {
fn new(matches: getopts::Matches) -> Options { fn new(matches: getopts::Matches) -> Options {
let tabstops = match matches.opt_str("t") { let tabstops = match matches.opt_str("t") {
None => vec!(DEFAULT_TABSTOP), None => vec![DEFAULT_TABSTOP],
Some(s) => tabstops_parse(s) Some(s) => tabstops_parse(s),
}; };
let iflag = matches.opt_present("i"); let iflag = matches.opt_present("i");
@ -71,30 +74,53 @@ impl Options {
// avoid allocations when dumping out long sequences of spaces // avoid allocations when dumping out long sequences of spaces
// by precomputing the longest string of spaces we will ever need // by precomputing the longest string of spaces we will ever need
let nspaces = tabstops.iter().scan(0, |pr,&it| { let nspaces = tabstops
.iter()
.scan(0, |pr, &it| {
let ret = Some(it - *pr); let ret = Some(it - *pr);
*pr = it; *pr = it;
ret ret
}).max().unwrap(); // length of tabstops is guaranteed >= 1 })
.max()
.unwrap(); // length of tabstops is guaranteed >= 1
let tspaces = repeat(' ').take(nspaces).collect(); let tspaces = repeat(' ').take(nspaces).collect();
let files = let files = if matches.free.is_empty() {
if matches.free.is_empty() { vec!["-".to_owned()]
vec!("-".to_owned())
} else { } else {
matches.free matches.free
}; };
Options { files: files, tabstops: tabstops, tspaces: tspaces, iflag: iflag, uflag: uflag } Options {
files: files,
tabstops: tabstops,
tspaces: tspaces,
iflag: iflag,
uflag: uflag,
}
} }
} }
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("i", "initial", "do not convert tabs after non blanks") .optflag("i", "initial", "do not convert tabs after non blanks")
.optopt("t", "tabs", "have tabs NUMBER characters apart, not 8", "NUMBER") .optopt(
.optopt("t", "tabs", "use comma separated list of explicit tab positions", "LIST") "t",
.optflag("U", "no-utf8", "interpret input file as 8-bit ASCII rather than UTF-8") "tabs",
"have tabs NUMBER characters apart, not 8",
"NUMBER",
)
.optopt(
"t",
"tabs",
"use comma separated list of explicit tab positions",
"LIST",
)
.optflag(
"U",
"no-utf8",
"interpret input file as 8-bit ASCII rather than UTF-8",
)
.parse(args); .parse(args);
expand(Options::new(matches)); expand(Options::new(matches));
@ -102,7 +128,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
0 0
} }
fn open(path: String) -> BufReader<Box<Read+'static>> { fn open(path: String) -> BufReader<Box<Read + 'static>> {
let file_buf; let file_buf;
if path == "-" { if path == "-" {
BufReader::new(Box::new(stdin()) as Box<Read>) BufReader::new(Box::new(stdin()) as Box<Read>)
@ -158,24 +184,30 @@ fn expand(options: Options) {
if byte + nbytes > buf.len() { if byte + nbytes > buf.len() {
// don't overrun buffer because of invalid UTF-8 // don't overrun buffer because of invalid UTF-8
(Other, 1, 1) (Other, 1, 1)
} else if let Ok(t) = from_utf8(&buf[byte..byte+nbytes]) { } else if let Ok(t) = from_utf8(&buf[byte..byte + nbytes]) {
match t.chars().next() { match t.chars().next() {
Some('\t') => (Tab, 0, nbytes), Some('\t') => (Tab, 0, nbytes),
Some('\x08') => (Backspace, 0, nbytes), Some('\x08') => (Backspace, 0, nbytes),
Some(c) => (Other, UnicodeWidthChar::width(c).unwrap_or(0), nbytes), Some(c) => (Other, UnicodeWidthChar::width(c).unwrap_or(0), nbytes),
None => { // no valid char at start of t, so take 1 byte None => {
// no valid char at start of t, so take 1 byte
(Other, 1, 1) (Other, 1, 1)
}, }
} }
} else { } else {
(Other, 1, 1) // implicit assumption: non-UTF-8 char is 1 col wide (Other, 1, 1) // implicit assumption: non-UTF-8 char is 1 col wide
} }
} else { } else {
(match buf[byte] { // always take exactly 1 byte in strict ASCII mode (
match buf[byte] {
// always take exactly 1 byte in strict ASCII mode
0x09 => Tab, 0x09 => Tab,
0x08 => Backspace, 0x08 => Backspace,
_ => Other, _ => Other,
}, 1, 1) },
1,
1,
)
}; };
// figure out how many columns this char takes up // figure out how many columns this char takes up
@ -189,9 +221,9 @@ fn expand(options: Options) {
if init || !options.iflag { if init || !options.iflag {
safe_unwrap!(output.write_all(&options.tspaces[..nts].as_bytes())); safe_unwrap!(output.write_all(&options.tspaces[..nts].as_bytes()));
} else { } else {
safe_unwrap!(output.write_all(&buf[byte..byte+nbytes])); safe_unwrap!(output.write_all(&buf[byte..byte + nbytes]));
}
} }
},
_ => { _ => {
col = if ctype == Other { col = if ctype == Other {
col + cwidth col + cwidth
@ -207,8 +239,8 @@ fn expand(options: Options) {
init = false; init = false;
} }
safe_unwrap!(output.write_all(&buf[byte..byte+nbytes])); safe_unwrap!(output.write_all(&buf[byte..byte + nbytes]));
}, }
} }
byte += nbytes; // advance the pointer byte += nbytes; // advance the pointer

View file

@ -9,9 +9,9 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
extern crate onig;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
extern crate onig;
mod tokens; mod tokens;
mod syntax_tree; mod syntax_tree;
@ -23,51 +23,65 @@ pub fn uumain(args: Vec<String>) -> i32 {
// For expr utility we do not want getopts. // For expr utility we do not want getopts.
// The following usage should work without escaping hyphens: `expr -15 = 1 + 2 \* \( 3 - -4 \)` // The following usage should work without escaping hyphens: `expr -15 = 1 + 2 \* \( 3 - -4 \)`
if maybe_handle_help_or_version( &args ) { 0 } if maybe_handle_help_or_version(&args) {
else { 0
} else {
let token_strings = args[1..].to_vec(); let token_strings = args[1..].to_vec();
match process_expr( &token_strings ) { match process_expr(&token_strings) {
Ok( expr_result ) => print_expr_ok( &expr_result ), Ok(expr_result) => print_expr_ok(&expr_result),
Err( expr_error ) => print_expr_error( &expr_error ) Err(expr_error) => print_expr_error(&expr_error),
} }
} }
} }
fn process_expr( token_strings: &Vec<String> ) -> Result< String, String > { fn process_expr(token_strings: &Vec<String>) -> Result<String, String> {
let maybe_tokens = tokens::strings_to_tokens( &token_strings ); let maybe_tokens = tokens::strings_to_tokens(&token_strings);
let maybe_ast = syntax_tree::tokens_to_ast( maybe_tokens ); let maybe_ast = syntax_tree::tokens_to_ast(maybe_tokens);
evaluate_ast( maybe_ast ) evaluate_ast(maybe_ast)
} }
fn print_expr_ok( expr_result: &String ) -> i32 { fn print_expr_ok(expr_result: &String) -> i32 {
println!("{}", expr_result); println!("{}", expr_result);
if expr_result == "0" || expr_result == "" { 1 } if expr_result == "0" || expr_result == "" {
else { 0 } 1
} else {
0
}
} }
fn print_expr_error( expr_error: &String ) -> ! { fn print_expr_error(expr_error: &String) -> ! {
crash!(2, "{}", expr_error) crash!(2, "{}", expr_error)
} }
fn evaluate_ast( maybe_ast: Result<Box<syntax_tree::ASTNode>, String> ) -> Result<String, String> { fn evaluate_ast(maybe_ast: Result<Box<syntax_tree::ASTNode>, String>) -> Result<String, String> {
if maybe_ast.is_err() { Err( maybe_ast.err().unwrap() ) } if maybe_ast.is_err() {
else { maybe_ast.ok().unwrap().evaluate() } Err(maybe_ast.err().unwrap())
} else {
maybe_ast.ok().unwrap().evaluate()
}
} }
fn maybe_handle_help_or_version( args: &Vec<String> ) -> bool { fn maybe_handle_help_or_version(args: &Vec<String>) -> bool {
if args.len() == 2 { if args.len() == 2 {
if args[1] == "--help" { print_help(); true } if args[1] == "--help" {
else if args[1] == "--version" { print_version(); true } print_help();
else { false } true
} else if args[1] == "--version" {
print_version();
true
} else {
false
}
} else {
false
} }
else { false }
} }
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!( print!(
r#"Usage: expr EXPRESSION r#"Usage: expr EXPRESSION
or: expr OPTION or: expr OPTION
--help display this help and exit --help display this help and exit

View file

@ -12,170 +12,210 @@
//! * https://en.wikipedia.org/wiki/Shunting-yard_algorithm //! * https://en.wikipedia.org/wiki/Shunting-yard_algorithm
//! //!
use tokens::{Token}; use tokens::Token;
use onig::{Regex, RegexOptions, Syntax}; use onig::{Regex, RegexOptions, Syntax};
type TokenStack = Vec<(usize, Token)>; type TokenStack = Vec<(usize, Token)>;
pub type OperandsList = Vec< Box<ASTNode> >; pub type OperandsList = Vec<Box<ASTNode>>;
#[derive(Debug)] #[derive(Debug)]
pub enum ASTNode { pub enum ASTNode {
Leaf { token_idx: usize, value: String }, Leaf {
Node { token_idx: usize, op_type: String, operands: OperandsList } token_idx: usize,
value: String,
},
Node {
token_idx: usize,
op_type: String,
operands: OperandsList,
},
} }
impl ASTNode { impl ASTNode {
fn debug_dump( &self ) { fn debug_dump(&self) {
self.debug_dump_impl( 1 ); self.debug_dump_impl(1);
} }
fn debug_dump_impl( &self, depth: usize ) { fn debug_dump_impl(&self, depth: usize) {
for _ in 0..depth { for _ in 0..depth {
print!("\t", ); print!("\t",);
} }
match *self { match *self {
ASTNode::Leaf{ ref token_idx, ref value } => println!("Leaf( {} ) at #{} ( evaluate -> {:?} )", value, token_idx, self.evaluate()), ASTNode::Leaf {
ASTNode::Node{ ref token_idx, ref op_type, ref operands } => { ref token_idx,
println!("Node( {} ) at #{} (evaluate -> {:?})", op_type, token_idx, self.evaluate()); ref value,
} => println!(
"Leaf( {} ) at #{} ( evaluate -> {:?} )",
value,
token_idx,
self.evaluate()
),
ASTNode::Node {
ref token_idx,
ref op_type,
ref operands,
} => {
println!(
"Node( {} ) at #{} (evaluate -> {:?})",
op_type,
token_idx,
self.evaluate()
);
for operand in operands { for operand in operands {
operand.debug_dump_impl( depth + 1 ); operand.debug_dump_impl(depth + 1);
} }
} }
} }
} }
fn new_node( token_idx: usize, op_type: &String, operands: OperandsList ) -> Box<ASTNode> { fn new_node(token_idx: usize, op_type: &String, operands: OperandsList) -> Box<ASTNode> {
Box::new( ASTNode::Node{ Box::new(ASTNode::Node {
token_idx: token_idx, token_idx: token_idx,
op_type: op_type.clone(), op_type: op_type.clone(),
operands: operands operands: operands,
} ) })
} }
fn new_leaf( token_idx: usize, value: &String ) -> Box<ASTNode> { fn new_leaf(token_idx: usize, value: &String) -> Box<ASTNode> {
Box::new( ASTNode::Leaf{ token_idx: token_idx, value: value.clone() } ) Box::new(ASTNode::Leaf {
token_idx: token_idx,
value: value.clone(),
})
} }
pub fn evaluate( &self ) -> Result<String, String> { pub fn evaluate(&self) -> Result<String, String> {
match *self { match *self {
ASTNode::Leaf{ ref value, .. } => Ok( value.clone() ), ASTNode::Leaf { ref value, .. } => Ok(value.clone()),
ASTNode::Node{ ref op_type, .. } => ASTNode::Node { ref op_type, .. } => match self.operand_values() {
match self.operand_values() { Err(reason) => Err(reason),
Err( reason ) => Err( reason ), Ok(operand_values) => match op_type.as_ref() {
Ok( operand_values ) => "+" => infix_operator_two_ints(|a: i64, b: i64| Ok(a + b), &operand_values),
match op_type.as_ref() { "-" => infix_operator_two_ints(|a: i64, b: i64| Ok(a - b), &operand_values),
"+" => infix_operator_two_ints( |a: i64, b: i64| Ok( a + b ), &operand_values ), "*" => infix_operator_two_ints(|a: i64, b: i64| Ok(a * b), &operand_values),
"-" => infix_operator_two_ints( |a: i64, b: i64| Ok( a - b ), &operand_values ),
"*" => infix_operator_two_ints( |a: i64, b: i64| Ok( a * b ), &operand_values ),
"/" => infix_operator_two_ints( "/" => infix_operator_two_ints(
|a: i64, b: i64| |a: i64, b: i64| {
if b == 0 { Err("division by zero".to_owned()) } if b == 0 {
else { Ok( a / b ) }, Err("division by zero".to_owned())
&operand_values ), } else {
Ok(a / b)
}
},
&operand_values,
),
"%" => infix_operator_two_ints( "%" => infix_operator_two_ints(
|a: i64, b: i64| |a: i64, b: i64| {
if b == 0 { Err("division by zero".to_owned()) } if b == 0 {
else { Ok( a % b ) }, Err("division by zero".to_owned())
&operand_values ), } else {
Ok(a % b)
}
},
&operand_values,
),
"=" => infix_operator_two_ints_or_two_strings( "=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok( bool_as_int(a == b) ), |a: i64, b: i64| Ok(bool_as_int(a == b)),
|a: &String, b: &String| Ok( bool_as_string(a == b) ), |a: &String, b: &String| Ok(bool_as_string(a == b)),
&operand_values &operand_values,
), ),
"!=" => infix_operator_two_ints_or_two_strings( "!=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok( bool_as_int(a != b) ), |a: i64, b: i64| Ok(bool_as_int(a != b)),
|a: &String, b: &String| Ok( bool_as_string(a != b) ), |a: &String, b: &String| Ok(bool_as_string(a != b)),
&operand_values &operand_values,
), ),
"<" => infix_operator_two_ints_or_two_strings( "<" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok( bool_as_int(a < b) ), |a: i64, b: i64| Ok(bool_as_int(a < b)),
|a: &String, b: &String| Ok( bool_as_string(a < b) ), |a: &String, b: &String| Ok(bool_as_string(a < b)),
&operand_values &operand_values,
), ),
">" => infix_operator_two_ints_or_two_strings( ">" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok( bool_as_int(a > b) ), |a: i64, b: i64| Ok(bool_as_int(a > b)),
|a: &String, b: &String| Ok( bool_as_string(a > b) ), |a: &String, b: &String| Ok(bool_as_string(a > b)),
&operand_values &operand_values,
), ),
"<=" => infix_operator_two_ints_or_two_strings( "<=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok( bool_as_int(a <= b) ), |a: i64, b: i64| Ok(bool_as_int(a <= b)),
|a: &String, b: &String| Ok( bool_as_string(a <= b) ), |a: &String, b: &String| Ok(bool_as_string(a <= b)),
&operand_values &operand_values,
), ),
">=" => infix_operator_two_ints_or_two_strings( ">=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok( bool_as_int(a >= b) ), |a: i64, b: i64| Ok(bool_as_int(a >= b)),
|a: &String, b: &String| Ok( bool_as_string(a >= b) ), |a: &String, b: &String| Ok(bool_as_string(a >= b)),
&operand_values &operand_values,
), ),
"|" => infix_operator_or(&operand_values), "|" => infix_operator_or(&operand_values),
"&" => infix_operator_and(&operand_values), "&" => infix_operator_and(&operand_values),
":" | "match" => operator_match(&operand_values), ":" | "match" => operator_match(&operand_values),
"length" => prefix_operator_length( &operand_values ), "length" => prefix_operator_length(&operand_values),
"index" => prefix_operator_index( &operand_values ), "index" => prefix_operator_index(&operand_values),
"substr" => prefix_operator_substr( &operand_values ), "substr" => prefix_operator_substr(&operand_values),
_ => Err(format!("operation not implemented: {}", op_type)) _ => Err(format!("operation not implemented: {}", op_type)),
},
},
} }
} }
} pub fn operand_values(&self) -> Result<Vec<String>, String> {
} if let &ASTNode::Node { ref operands, .. } = self {
pub fn operand_values( &self ) -> Result<Vec<String>, String> { let mut out = Vec::with_capacity(operands.len());
if let &ASTNode::Node{ ref operands, .. } = self {
let mut out = Vec::with_capacity( operands.len() );
for operand in operands { for operand in operands {
match operand.evaluate() { match operand.evaluate() {
Ok( value ) => out.push( value ), Ok(value) => out.push(value),
Err( reason ) => return Err( reason ), Err(reason) => return Err(reason),
} }
} }
Ok( out ) Ok(out)
} else {
panic!("Invoked .operand_values(&self) not with ASTNode::Node")
} }
else { panic!("Invoked .operand_values(&self) not with ASTNode::Node") }
} }
} }
pub fn tokens_to_ast( maybe_tokens: Result< Vec<(usize, Token)>, String > ) -> Result<Box<ASTNode>, String> { pub fn tokens_to_ast(
if maybe_tokens.is_err() { Err( maybe_tokens.err().unwrap() ) } maybe_tokens: Result<Vec<(usize, Token)>, String>,
else { ) -> Result<Box<ASTNode>, String> {
if maybe_tokens.is_err() {
Err(maybe_tokens.err().unwrap())
} else {
let tokens = maybe_tokens.ok().unwrap(); let tokens = maybe_tokens.ok().unwrap();
let mut out_stack: TokenStack = Vec::new(); let mut out_stack: TokenStack = Vec::new();
let mut op_stack: TokenStack = Vec::new(); let mut op_stack: TokenStack = Vec::new();
for (token_idx, token) in tokens { for (token_idx, token) in tokens {
if let Err( reason ) = push_token_to_either_stack( token_idx, &token, &mut out_stack, &mut op_stack ) { if let Err(reason) =
return Err( reason ) push_token_to_either_stack(token_idx, &token, &mut out_stack, &mut op_stack)
{
return Err(reason);
} }
} }
if let Err( reason ) = move_rest_of_ops_to_out( &mut out_stack, &mut op_stack ) { if let Err(reason) = move_rest_of_ops_to_out(&mut out_stack, &mut op_stack) {
return Err( reason ) return Err(reason);
} }
assert!( op_stack.is_empty() ); assert!(op_stack.is_empty());
maybe_dump_rpn( &out_stack ); maybe_dump_rpn(&out_stack);
let result = ast_from_rpn( &mut out_stack ); let result = ast_from_rpn(&mut out_stack);
if !out_stack.is_empty() { if !out_stack.is_empty() {
Err( "syntax error (fist RPN token does not represent expression AST's root)".to_owned() ) Err("syntax error (fist RPN token does not represent expression AST's root)".to_owned())
} } else {
else { maybe_dump_ast(&result);
maybe_dump_ast( &result );
result result
} }
} }
} }
fn maybe_dump_ast( result: &Result< Box<ASTNode>, String > ) { fn maybe_dump_ast(result: &Result<Box<ASTNode>, String>) {
use std::env; use std::env;
if let Ok( debug_var ) = env::var( "EXPR_DEBUG_AST" ) { if let Ok(debug_var) = env::var("EXPR_DEBUG_AST") {
if debug_var == "1" { if debug_var == "1" {
println!("EXPR_DEBUG_AST"); println!("EXPR_DEBUG_AST");
match *result { match *result {
Ok( ref ast ) => ast.debug_dump(), Ok(ref ast) => ast.debug_dump(),
Err( ref reason ) => println!("\terr: {:?}", reason), Err(ref reason) => println!("\terr: {:?}", reason),
} }
} }
} }
} }
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") {
if debug_var == "1" { if debug_var == "1" {
println!("EXPR_DEBUG_RPN"); println!("EXPR_DEBUG_RPN");
for token in rpn { for token in rpn {
@ -185,68 +225,100 @@ fn maybe_dump_rpn( rpn: &TokenStack ) {
} }
} }
fn ast_from_rpn( rpn: &mut TokenStack ) -> Result<Box<ASTNode>, String> { fn ast_from_rpn(rpn: &mut TokenStack) -> Result<Box<ASTNode>, String> {
match rpn.pop() { match rpn.pop() {
None => Err( "syntax error (premature end of expression)".to_owned() ), None => Err("syntax error (premature end of expression)".to_owned()),
Some( (token_idx, Token::Value{ value }) ) => Some((token_idx, Token::Value { value })) => Ok(ASTNode::new_leaf(token_idx, &value)),
Ok( ASTNode::new_leaf( token_idx, &value ) ),
Some( (token_idx, Token::InfixOp{ value, .. }) ) => Some((token_idx, Token::InfixOp { value, .. })) => {
maybe_ast_node( token_idx, &value, 2, rpn ), maybe_ast_node(token_idx, &value, 2, rpn)
}
Some( (token_idx, Token::PrefixOp{ value, arity }) ) => Some((token_idx, Token::PrefixOp { value, arity })) => {
maybe_ast_node( token_idx, &value, arity, rpn ), maybe_ast_node(token_idx, &value, arity, rpn)
}
Some( (token_idx, unexpected_token) ) => Some((token_idx, unexpected_token)) => {
panic!("unexpected token at #{} {:?}", token_idx, unexpected_token), panic!("unexpected token at #{} {:?}", token_idx, unexpected_token)
}
} }
} }
fn maybe_ast_node( token_idx: usize, op_type: &String, arity: usize, rpn: &mut TokenStack ) -> Result< Box<ASTNode>, String > { fn maybe_ast_node(
let mut operands = Vec::with_capacity( arity ); token_idx: usize,
op_type: &String,
arity: usize,
rpn: &mut TokenStack,
) -> Result<Box<ASTNode>, String> {
let mut operands = Vec::with_capacity(arity);
for _ in 0..arity { for _ in 0..arity {
match ast_from_rpn( rpn ) { match ast_from_rpn(rpn) {
Err( reason ) => return Err( reason ), Err(reason) => return Err(reason),
Ok( operand ) => operands.push( operand ), Ok(operand) => operands.push(operand),
} }
} }
operands.reverse(); operands.reverse();
Ok( ASTNode::new_node( token_idx, op_type, operands ) ) Ok(ASTNode::new_node(token_idx, op_type, operands))
} }
fn move_rest_of_ops_to_out( out_stack: &mut TokenStack, op_stack: &mut TokenStack ) -> Result<(), String> { fn move_rest_of_ops_to_out(
out_stack: &mut TokenStack,
op_stack: &mut TokenStack,
) -> Result<(), String> {
loop { loop {
match op_stack.pop() { match op_stack.pop() {
None => return Ok( () ), None => return Ok(()),
Some( (token_idx, Token::ParOpen) ) => return Err( format!( "syntax error (Mismatched open-parenthesis at #{})", token_idx ) ), Some((token_idx, Token::ParOpen)) => {
Some( (token_idx, Token::ParClose) ) => return Err( format!( "syntax error (Mismatched close-parenthesis at #{})", token_idx ) ), return Err(format!(
Some( other ) => out_stack.push( other ) "syntax error (Mismatched open-parenthesis at #{})",
token_idx
))
}
Some((token_idx, Token::ParClose)) => {
return Err(format!(
"syntax error (Mismatched close-parenthesis at #{})",
token_idx
))
}
Some(other) => out_stack.push(other),
} }
} }
} }
fn push_token_to_either_stack( token_idx: usize, token: &Token, out_stack: &mut TokenStack, op_stack: &mut TokenStack ) -> Result<(), String> { fn push_token_to_either_stack(
let result = token_idx: usize,
match *token { token: &Token,
Token::Value{ .. } => Ok( out_stack.push( (token_idx, token.clone()) ) ), out_stack: &mut TokenStack,
op_stack: &mut TokenStack,
) -> Result<(), String> {
let result = match *token {
Token::Value { .. } => Ok(out_stack.push((token_idx, token.clone()))),
Token::InfixOp{ .. } => Token::InfixOp { .. } => if op_stack.is_empty() {
if op_stack.is_empty() { Ok( op_stack.push( (token_idx, token.clone()) ) ) } Ok(op_stack.push((token_idx, token.clone())))
else { push_op_to_stack( token_idx, token, out_stack, op_stack ) }, } else {
push_op_to_stack(token_idx, token, out_stack, op_stack)
},
Token::PrefixOp{ .. } => Ok( op_stack.push( (token_idx, token.clone()) ) ), Token::PrefixOp { .. } => Ok(op_stack.push((token_idx, token.clone()))),
Token::ParOpen => Ok( op_stack.push( (token_idx, token.clone()) ) ), Token::ParOpen => Ok(op_stack.push((token_idx, token.clone()))),
Token::ParClose => move_till_match_paren( out_stack, op_stack ) Token::ParClose => move_till_match_paren(out_stack, op_stack),
}; };
maybe_dump_shunting_yard_step( token_idx, token, out_stack, op_stack, &result ); maybe_dump_shunting_yard_step(token_idx, token, out_stack, op_stack, &result);
result result
} }
fn maybe_dump_shunting_yard_step( token_idx: usize, token: &Token, out_stack: &TokenStack, op_stack: &TokenStack, result: &Result<(), String> ) { fn maybe_dump_shunting_yard_step(
token_idx: usize,
token: &Token,
out_stack: &TokenStack,
op_stack: &TokenStack,
result: &Result<(), String>,
) {
use std::env; use std::env;
if let Ok( debug_var ) = env::var( "EXPR_DEBUG_SYA_STEP" ) { if let Ok(debug_var) = env::var("EXPR_DEBUG_SYA_STEP") {
if debug_var == "1" { if debug_var == "1" {
println!("EXPR_DEBUG_SYA_STEP"); println!("EXPR_DEBUG_SYA_STEP");
println!("\t{} => {:?}", token_idx, token); println!("\t{} => {:?}", token_idx, token);
@ -257,85 +329,100 @@ fn maybe_dump_shunting_yard_step( token_idx: usize, token: &Token, out_stack: &T
} }
} }
fn push_op_to_stack( token_idx: usize, token: &Token, out_stack: &mut TokenStack, op_stack: &mut TokenStack ) -> Result<(), String> { fn push_op_to_stack(
if let &Token::InfixOp{ precedence: prec, left_assoc: la, .. } = token { token_idx: usize,
token: &Token,
out_stack: &mut TokenStack,
op_stack: &mut TokenStack,
) -> Result<(), String> {
if let &Token::InfixOp {
precedence: prec,
left_assoc: la,
..
} = token
{
loop { loop {
match op_stack.last() { match op_stack.last() {
None => None => return Ok(op_stack.push((token_idx, token.clone()))),
return Ok( op_stack.push( (token_idx, token.clone()) ) ),
Some( &(_, Token::ParOpen) ) => Some(&(_, Token::ParOpen)) => return Ok(op_stack.push((token_idx, token.clone()))),
return Ok( op_stack.push( (token_idx, token.clone()) ) ),
Some( &(_, Token::InfixOp{ precedence: prev_prec, .. }) ) => Some(&(
if la && prev_prec >= prec _,
|| !la && prev_prec > prec { Token::InfixOp {
out_stack.push( op_stack.pop().unwrap() ) precedence: prev_prec,
} ..
else { },
return Ok( op_stack.push( (token_idx, token.clone()) ) ) )) => if la && prev_prec >= prec || !la && prev_prec > prec {
out_stack.push(op_stack.pop().unwrap())
} else {
return Ok(op_stack.push((token_idx, token.clone())));
}, },
Some( &(_, Token::PrefixOp{ .. }) ) => Some(&(_, Token::PrefixOp { .. })) => {
return Ok( op_stack.push( (token_idx, token.clone()) ) ), return Ok(op_stack.push((token_idx, token.clone())))
}
Some( _ ) => panic!("Non-operator on op_stack") Some(_) => panic!("Non-operator on op_stack"),
} }
} }
} } else {
else {
panic!("Expected infix-op") panic!("Expected infix-op")
} }
} }
fn move_till_match_paren( out_stack: &mut TokenStack, op_stack: &mut TokenStack ) -> Result<(), String> { fn move_till_match_paren(
out_stack: &mut TokenStack,
op_stack: &mut TokenStack,
) -> Result<(), String> {
loop { loop {
match op_stack.pop() { match op_stack.pop() {
None => return Err( "syntax error (Mismatched close-parenthesis)".to_string() ), None => return Err("syntax error (Mismatched close-parenthesis)".to_string()),
Some( (_, Token::ParOpen) ) => return Ok( () ), Some((_, Token::ParOpen)) => return Ok(()),
Some( other ) => out_stack.push( other ) Some(other) => out_stack.push(other),
} }
} }
} }
fn infix_operator_two_ints<F>(f: F, values: &Vec<String>) -> Result<String, String>
fn infix_operator_two_ints<F>( f: F, values: &Vec<String> ) -> Result<String, String> where
where F : Fn( i64, i64 ) -> Result<i64, String> F: Fn(i64, i64) -> Result<i64, String>,
{ {
assert!( values.len() == 2 ); assert!(values.len() == 2);
if let Some( left ) = values[0].parse::<i64>().ok() { if let Some(left) = values[0].parse::<i64>().ok() {
if let Some( right ) = values[1].parse::<i64>().ok() { if let Some(right) = values[1].parse::<i64>().ok() {
return match f( left, right ) { return match f(left, right) {
Ok(result) => Ok(result.to_string()),
Err(reason) => Err(reason),
};
}
}
Err("Expected an integer operand".to_string())
}
fn infix_operator_two_ints_or_two_strings<FI, FS>(
fi: FI,
fs: FS,
values: &Vec<String>,
) -> Result<String, String>
where
FI: Fn(i64, i64) -> Result<i64, String>,
FS: Fn(&String, &String) -> Result<String, String>,
{
assert!(values.len() == 2);
if let (Some(a_int), Some(b_int)) =
(values[0].parse::<i64>().ok(), values[1].parse::<i64>().ok())
{
match fi(a_int, b_int) {
Ok(result) => Ok(result.to_string()), Ok(result) => Ok(result.to_string()),
Err(reason) => Err(reason), Err(reason) => Err(reason),
} }
} } else {
} fs(&values[0], &values[1])
Err( "Expected an integer operand".to_string() )
}
fn infix_operator_two_ints_or_two_strings<FI, FS>( fi: FI, fs: FS, values: &Vec<String> ) -> Result<String, String>
where FI : Fn( i64, i64 ) -> Result<i64, String>,
FS : Fn( &String, &String ) -> Result<String, String>
{
assert!( values.len() == 2 );
if let ( Some( a_int ), Some( b_int ) ) =
(
values[0].parse::<i64>().ok(),
values[1].parse::<i64>().ok()
) {
match fi( a_int, b_int ) {
Ok( result ) => Ok(result.to_string()),
Err( reason ) => Err(reason)
}
}
else {
fs( &values[0], &values[1] )
} }
} }
fn infix_operator_or( values: &Vec<String> ) -> Result<String, String> { fn infix_operator_or(values: &Vec<String>) -> Result<String, String> {
assert!(values.len() == 2); assert!(values.len() == 2);
if value_as_bool(&values[0]) { if value_as_bool(&values[0]) {
Ok(values[0].clone()) Ok(values[0].clone())
@ -344,7 +431,7 @@ fn infix_operator_or( values: &Vec<String> ) -> Result<String, String> {
} }
} }
fn infix_operator_and( values: &Vec<String> ) -> Result<String, String> { fn infix_operator_and(values: &Vec<String>) -> Result<String, String> {
if value_as_bool(&values[0]) && value_as_bool(&values[1]) { if value_as_bool(&values[0]) && value_as_bool(&values[1]) {
Ok(values[0].clone()) Ok(values[0].clone())
} else { } else {
@ -354,30 +441,31 @@ fn infix_operator_and( values: &Vec<String> ) -> Result<String, String> {
fn operator_match(values: &Vec<String>) -> Result<String, String> { fn operator_match(values: &Vec<String>) -> Result<String, String> {
assert!(values.len() == 2); assert!(values.len() == 2);
let re = match Regex::with_options(&values[1], RegexOptions::REGEX_OPTION_NONE, Syntax::grep()) { let re = match Regex::with_options(&values[1], RegexOptions::REGEX_OPTION_NONE, Syntax::grep())
{
Ok(m) => m, Ok(m) => m,
Err(err) => return Err(err.description().to_string()) Err(err) => return Err(err.description().to_string()),
}; };
if re.captures_len() > 0 { if re.captures_len() > 0 {
Ok(match re.captures(&values[0]) { Ok(match re.captures(&values[0]) {
Some(captures) => captures.at(1).unwrap().to_string(), Some(captures) => captures.at(1).unwrap().to_string(),
None => "".to_string() None => "".to_string(),
}) })
} else { } else {
Ok(match re.find(&values[0]) { Ok(match re.find(&values[0]) {
Some((start, end)) => (end - start).to_string(), Some((start, end)) => (end - start).to_string(),
None => "0".to_string() None => "0".to_string(),
}) })
} }
} }
fn prefix_operator_length( values: &Vec<String> ) -> Result<String, String> { fn prefix_operator_length(values: &Vec<String>) -> Result<String, String> {
assert!( values.len() == 1 ); assert!(values.len() == 1);
Ok( values[0].len().to_string() ) Ok(values[0].len().to_string())
} }
fn prefix_operator_index( values: &Vec<String> ) -> Result<String, String> { fn prefix_operator_index(values: &Vec<String>) -> Result<String, String> {
assert!( values.len() == 2 ); assert!(values.len() == 2);
let haystack = &values[0]; let haystack = &values[0];
let needles = &values[1]; let needles = &values[1];
@ -387,45 +475,61 @@ fn prefix_operator_index( values: &Vec<String> ) -> Result<String, String> {
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());
} }
} }
} }
Ok( "0".to_string() ) Ok("0".to_string())
} }
fn prefix_operator_substr( values: &Vec<String> ) -> Result<String, String> { fn prefix_operator_substr(values: &Vec<String>) -> Result<String, String> {
assert!( values.len() == 3 ); assert!(values.len() == 3);
let subj = &values[0]; let subj = &values[0];
let mut idx = match values[1].parse::<i64>() { let mut idx = match values[1].parse::<i64>() {
Ok( i ) => i, Ok(i) => i,
Err( _ ) => return Err( "expected integer as POS arg to 'substr'".to_string() ), Err(_) => return Err("expected integer as POS arg to 'substr'".to_string()),
}; };
let mut len = match values[2].parse::<i64>() { let mut len = match values[2].parse::<i64>() {
Ok( i ) => i, Ok(i) => i,
Err( _ ) => return Err( "expected integer as LENGTH arg to 'substr'".to_string() ), Err(_) => return Err("expected integer as LENGTH arg to 'substr'".to_string()),
}; };
if idx <= 0 || len <= 0 { return Ok( "".to_string() ) } if idx <= 0 || len <= 0 {
return Ok("".to_string());
}
let mut out_str = String::new(); let mut out_str = String::new();
for ch in subj.chars() { for ch in subj.chars() {
idx -= 1; idx -= 1;
if idx <= 0 { if idx <= 0 {
if len <= 0 { break; } if len <= 0 {
break;
}
len -= 1; len -= 1;
out_str.push( ch ); out_str.push(ch);
} }
} }
Ok( out_str ) Ok(out_str)
} }
fn bool_as_int( b: bool ) -> i64 { if b { 1 } else { 0 } } fn bool_as_int(b: bool) -> i64 {
fn bool_as_string( b: bool ) -> String { if b { "1".to_string() } else { "0".to_string() } } if b {
fn value_as_bool( s: &str ) -> bool { 1
} else {
0
}
}
fn bool_as_string(b: bool) -> String {
if b {
"1".to_string()
} else {
"0".to_string()
}
}
fn value_as_bool(s: &str) -> bool {
if s.len() == 0 { if s.len() == 0 {
return false return false;
} }
match s.parse::<i64>() { match s.parse::<i64>() {
Ok(n) => n != 0, Ok(n) => n != 0,

View file

@ -18,10 +18,11 @@
//! Hence all we need is to map the strings into the Token structures, except for some ugly fiddling with +-escaping. //! Hence all we need is to map the strings into the Token structures, except for some ugly fiddling with +-escaping.
//! //!
#[derive(Debug)] #[derive(Debug, Clone)]
#[derive(Clone)]
pub enum Token { pub enum Token {
Value{ value: String }, Value {
value: String,
},
ParOpen, ParOpen,
ParClose, ParClose,
@ -29,45 +30,42 @@ pub enum Token {
InfixOp { InfixOp {
precedence: u8, precedence: u8,
left_assoc: bool, left_assoc: bool,
value: String value: String,
}, },
PrefixOp { PrefixOp {
arity: usize, arity: usize,
value: String value: String,
}, },
} }
impl Token { impl Token {
fn new_infix_op( v: &String, left_assoc: bool, precedence: u8 ) -> Self { fn new_infix_op(v: &String, left_assoc: bool, precedence: u8) -> Self {
Token::InfixOp{ Token::InfixOp {
left_assoc: left_assoc, left_assoc: left_assoc,
precedence: precedence, precedence: precedence,
value: v.clone() value: v.clone(),
} }
} }
fn new_value( v: &String ) -> Self { fn new_value(v: &String) -> Self {
Token::Value{ Token::Value { value: v.clone() }
value: v.clone()
}
} }
fn is_infix_plus( &self ) -> bool { fn is_infix_plus(&self) -> bool {
match *self { match *self {
Token::InfixOp{ ref value, .. } => value == "+", Token::InfixOp { ref value, .. } => value == "+",
_ => false _ => false,
} }
} }
fn is_a_number( &self ) -> bool { fn is_a_number(&self) -> bool {
match *self { match *self {
Token::Value{ ref value, .. } => Token::Value { ref value, .. } => match value.parse::<i64>() {
match value.parse::<i64>() { Ok(_) => true,
Ok( _ ) => true, Err(_) => false,
Err( _ ) => false
}, },
_ => false, _ => false,
} }
} }
fn is_a_close_paren( &self ) -> bool { fn is_a_close_paren(&self) -> bool {
match *self { match *self {
Token::ParClose => true, Token::ParClose => true,
_ => false, _ => false,
@ -75,57 +73,68 @@ impl Token {
} }
} }
pub fn strings_to_tokens( strings: &[String] ) -> Result< Vec<(usize, Token)>, String > { pub fn strings_to_tokens(strings: &[String]) -> Result<Vec<(usize, Token)>, String> {
let mut tokens_acc = Vec::with_capacity( strings.len() ); let mut tokens_acc = Vec::with_capacity(strings.len());
let mut tok_idx = 1; let mut tok_idx = 1;
for s in strings { for s in strings {
let token_if_not_escaped = let token_if_not_escaped = match s.as_ref() {
match s.as_ref() {
"(" => Token::ParOpen, "(" => Token::ParOpen,
")" => Token::ParClose, ")" => Token::ParClose,
"^" => Token::new_infix_op( &s, false, 7 ), "^" => Token::new_infix_op(&s, false, 7),
":" => Token::new_infix_op( &s, true, 6 ), ":" => Token::new_infix_op(&s, true, 6),
"*" => Token::new_infix_op( &s, true, 5 ), "*" => Token::new_infix_op(&s, true, 5),
"/" => Token::new_infix_op( &s, true, 5 ), "/" => Token::new_infix_op(&s, true, 5),
"%" => Token::new_infix_op( &s, true, 5 ), "%" => Token::new_infix_op(&s, true, 5),
"+" => Token::new_infix_op( &s, true, 4 ), "+" => Token::new_infix_op(&s, true, 4),
"-" => Token::new_infix_op( &s, true, 4 ), "-" => Token::new_infix_op(&s, true, 4),
"=" => Token::new_infix_op( &s, true, 3 ), "=" => Token::new_infix_op(&s, true, 3),
"!=" => Token::new_infix_op( &s, true, 3 ), "!=" => Token::new_infix_op(&s, true, 3),
"<" => Token::new_infix_op( &s, true, 3 ), "<" => Token::new_infix_op(&s, true, 3),
">" => Token::new_infix_op( &s, true, 3 ), ">" => Token::new_infix_op(&s, true, 3),
"<=" => Token::new_infix_op( &s, true, 3 ), "<=" => Token::new_infix_op(&s, true, 3),
">=" => Token::new_infix_op( &s, true, 3 ), ">=" => Token::new_infix_op(&s, true, 3),
"&" => Token::new_infix_op( &s, true, 2 ), "&" => Token::new_infix_op(&s, true, 2),
"|" => Token::new_infix_op( &s, true, 1 ), "|" => Token::new_infix_op(&s, true, 1),
"match" => Token::PrefixOp{ arity: 2, value: s.clone() }, "match" => Token::PrefixOp {
"substr" => Token::PrefixOp{ arity: 3, value: s.clone() }, arity: 2,
"index" => Token::PrefixOp{ arity: 2, value: s.clone() }, value: s.clone(),
"length" => Token::PrefixOp{ arity: 1, value: s.clone() }, },
"substr" => Token::PrefixOp {
arity: 3,
value: s.clone(),
},
"index" => Token::PrefixOp {
arity: 2,
value: s.clone(),
},
"length" => Token::PrefixOp {
arity: 1,
value: s.clone(),
},
_ => Token::new_value( &s ), _ => Token::new_value(&s),
}; };
push_token_if_not_escaped( &mut tokens_acc, tok_idx, token_if_not_escaped, &s ); push_token_if_not_escaped(&mut tokens_acc, tok_idx, token_if_not_escaped, &s);
tok_idx += 1; tok_idx += 1;
} }
maybe_dump_tokens_acc( &tokens_acc ); maybe_dump_tokens_acc(&tokens_acc);
Ok( tokens_acc ) Ok(tokens_acc)
} }
fn maybe_dump_tokens_acc( tokens_acc: &[(usize, Token)] ) { fn maybe_dump_tokens_acc(tokens_acc: &[(usize, Token)]) {
use std::env; use std::env;
if let Ok(debug_var) = env::var( "EXPR_DEBUG_TOKENS" ) { if let Ok(debug_var) = env::var("EXPR_DEBUG_TOKENS") {
if debug_var == "1" { if debug_var == "1" {
println!("EXPR_DEBUG_TOKENS"); println!("EXPR_DEBUG_TOKENS");
for token in tokens_acc { for token in tokens_acc {
@ -135,27 +144,28 @@ fn maybe_dump_tokens_acc( tokens_acc: &[(usize, Token)] ) {
} }
} }
fn push_token_if_not_escaped( acc: &mut Vec<(usize, Token)>, tok_idx: usize, token: Token, s: &String ) { fn push_token_if_not_escaped(
acc: &mut Vec<(usize, Token)>,
tok_idx: usize,
token: Token,
s: &String,
) {
// Smells heuristics... :( // Smells heuristics... :(
let prev_is_plus = let prev_is_plus = match acc.last() {
match acc.last() {
None => false, None => false,
Some( ref t ) => t.1.is_infix_plus(), Some(ref t) => t.1.is_infix_plus(),
}; };
let should_use_as_escaped = let should_use_as_escaped = if prev_is_plus && acc.len() >= 2 {
if prev_is_plus && acc.len() >= 2 {
let pre_prev = &acc[acc.len() - 2]; let pre_prev = &acc[acc.len() - 2];
! ( pre_prev.1.is_a_number() || pre_prev.1.is_a_close_paren() ) !(pre_prev.1.is_a_number() || pre_prev.1.is_a_close_paren())
} } else {
else {
prev_is_plus prev_is_plus
}; };
if should_use_as_escaped { if should_use_as_escaped {
acc.pop(); acc.pop();
acc.push( (tok_idx, Token::new_value( s )) ) acc.push((tok_idx, Token::new_value(s)))
} } else {
else { acc.push((tok_idx, token))
acc.push( (tok_idx, token) )
} }
} }

View file

@ -54,18 +54,17 @@ fn inv_mod_u64(a: u64) -> Option<u64> {
r r
} / newr; } / newr;
let (tp, Wrapping(newtp)) = let (tp, Wrapping(newtp)) = (newt, Wrapping(t) - (Wrapping(quot) * Wrapping(newt)));
(newt, Wrapping(t) - (Wrapping(quot) * Wrapping(newt)));
t = tp; t = tp;
newt = newtp; newt = newtp;
let (rp, Wrapping(newrp)) = let (rp, Wrapping(newrp)) = (newr, Wrapping(r) - (Wrapping(quot) * Wrapping(newr)));
(newr, Wrapping(r) - (Wrapping(quot) * Wrapping(newr)));
r = rp; r = rp;
newr = newrp; newr = newrp;
} }
if r > 1 { // not invertible if r > 1 {
// not invertible
return None; return None;
} }
@ -80,7 +79,13 @@ fn main() {
let mut file = File::create(&Path::new(&out_dir).join("prime_table.rs")).unwrap(); let mut file = File::create(&Path::new(&out_dir).join("prime_table.rs")).unwrap();
// 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().skip(1).next().unwrap_or("1027".to_string()).parse::<usize>().ok().unwrap_or(1027); let n = args()
.skip(1)
.next()
.unwrap_or("1027".to_string())
.parse::<usize>()
.ok()
.unwrap_or(1027);
write!(file, "{}", PREAMBLE).unwrap(); write!(file, "{}", PREAMBLE).unwrap();
let mut cols = 3; let mut cols = 3;
@ -106,7 +111,11 @@ fn main() {
x = next; x = next;
} }
write!(file, "\n];\n\n#[allow(dead_code)]\npub const NEXT_PRIME: u64 = {};\n", x).unwrap(); write!(
file,
"\n];\n\n#[allow(dead_code)]\npub const NEXT_PRIME: u64 = {};\n",
x
).unwrap();
} }
#[test] #[test]
@ -127,8 +136,7 @@ fn test_generator() {
} }
const MAX_WIDTH: usize = 102; const MAX_WIDTH: usize = 102;
const PREAMBLE: &'static str = const PREAMBLE: &'static str = r##"/*
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>

View file

@ -19,7 +19,7 @@ extern crate rand;
extern crate uucore; extern crate uucore;
use numeric::*; use numeric::*;
use rand::distributions::{Range, IndependentSample}; use rand::distributions::{IndependentSample, Range};
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::io::{stdin, BufRead, BufReader}; use std::io::{stdin, BufRead, BufReader};
use std::num::Wrapping; use std::num::Wrapping;
@ -158,8 +158,7 @@ fn print_factors_str(num_str: &str) {
} }
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).parse(args);
.parse(args);
if matches.free.is_empty() { if matches.free.is_empty() {
for line in BufReader::new(stdin()).lines() { for line in BufReader::new(stdin()).lines() {

View file

@ -92,7 +92,7 @@ fn witness(mut a: u64, exponent: u64, m: u64) -> bool {
big_mul as fn(u64, u64, u64) -> u64 big_mul as fn(u64, u64, u64) -> u64
}; };
if pow(a, m-1, m, mul) != 1 { if pow(a, m - 1, m, mul) != 1 {
return true; return true;
} }
a = pow(a, exponent, m, mul); a = pow(a, exponent, m, mul);
@ -103,7 +103,7 @@ fn witness(mut a: u64, exponent: u64, m: u64) -> bool {
if a == 1 { if a == 1 {
return true; return true;
} }
if a == m-1 { if a == m - 1 {
return false; return false;
} }
a = mul(a, a, m); a = mul(a, a, m);
@ -126,5 +126,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, 9375, 28178, 450775, 9780504, 1795265022];
! witnesses.iter().any(|&wit| witness(wit % num, exponent, num)) !witnesses
.iter()
.any(|&wit| witness(wit % num, exponent, num))
} }

View file

@ -59,13 +59,18 @@ impl Iterator for Sieve {
impl Sieve { impl Sieve {
fn new() -> Sieve { fn new() -> Sieve {
Sieve { inner: Wheel::new(), filts: PrimeHeap::new() } Sieve {
inner: Wheel::new(),
filts: PrimeHeap::new(),
}
} }
#[allow(dead_code)] #[allow(dead_code)]
#[inline] #[inline]
pub fn primes() -> PrimeSieve { pub fn primes() -> PrimeSieve {
fn deref(x: &u64) -> u64 { *x } fn deref(x: &u64) -> u64 {
*x
}
let deref = deref as fn(&u64) -> u64; let deref = deref as fn(&u64) -> u64;
INIT_PRIMES.iter().map(deref).chain(Sieve::new()) INIT_PRIMES.iter().map(deref).chain(Sieve::new())
} }
@ -73,7 +78,9 @@ impl Sieve {
#[allow(dead_code)] #[allow(dead_code)]
#[inline] #[inline]
pub fn odd_primes() -> PrimeSieve { pub fn odd_primes() -> PrimeSieve {
fn deref(x: &u64) -> u64 { *x } fn deref(x: &u64) -> u64 {
*x
}
let deref = deref as fn(&u64) -> u64; let deref = deref as fn(&u64) -> u64;
(&INIT_PRIMES[1..]).iter().map(deref).chain(Sieve::new()) (&INIT_PRIMES[1..]).iter().map(deref).chain(Sieve::new())
} }
@ -97,7 +104,7 @@ impl Iterator for Wheel {
} }
#[inline] #[inline]
fn next (&mut self) -> Option<u64> { fn next(&mut self) -> Option<u64> {
let increment = self.increment.next().unwrap(); // infinite iterator, no check necessary let increment = self.increment.next().unwrap(); // infinite iterator, no check necessary
let ret = self.next; let ret = self.next;
self.next = ret + increment; self.next = ret + increment;
@ -108,13 +115,19 @@ impl Iterator for Wheel {
impl Wheel { impl Wheel {
#[inline] #[inline]
fn new() -> Wheel { fn new() -> Wheel {
Wheel { next: 11u64, increment: WHEEL_INCS.iter().cycle() } Wheel {
next: 11u64,
increment: WHEEL_INCS.iter().cycle(),
}
} }
} }
/// 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] = &[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]; const WHEEL_INCS: &'static [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, 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: &'static [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
@ -169,8 +182,8 @@ impl PrimeHeap {
let len = self.data.len(); let len = self.data.len();
let (key, _) = self.data[0]; let (key, _) = self.data[0];
loop { loop {
let child1 = 2*idx + 1; let child1 = 2 * idx + 1;
let child2 = 2*idx + 2; let child2 = 2 * idx + 2;
// no more children // no more children
if child1 >= len { if child1 >= len {

View file

@ -15,7 +15,7 @@ extern crate unicode_width;
extern crate uucore; extern crate uucore;
use std::cmp; use std::cmp;
use std::io::{Read, BufReader, BufWriter}; use std::io::{BufReader, BufWriter, Read};
use std::fs::File; use std::fs::File;
use std::io::{stdin, stdout, Write}; use std::io::{stdin, stdout, Write};
use linebreak::break_lines; use linebreak::break_lines;
@ -38,23 +38,23 @@ static SYNTAX: &'static str = "[OPTION]... [FILE]...";
static SUMMARY: &'static str = "Reformat paragraphs from input files (or stdin) to stdout."; static SUMMARY: &'static str = "Reformat paragraphs from input files (or stdin) to stdout.";
static LONG_HELP: &'static str = ""; static LONG_HELP: &'static str = "";
pub type FileOrStdReader = BufReader<Box<Read+'static>>; pub type FileOrStdReader = BufReader<Box<Read + 'static>>;
pub struct FmtOptions { pub struct FmtOptions {
crown : bool, crown: bool,
tagged : bool, tagged: bool,
mail : bool, mail: bool,
split_only : bool, split_only: bool,
use_prefix : bool, use_prefix: bool,
prefix : String, prefix: String,
xprefix : bool, xprefix: bool,
use_anti_prefix : bool, use_anti_prefix: bool,
anti_prefix : String, anti_prefix: String,
xanti_prefix : bool, xanti_prefix: bool,
uniform : bool, uniform: bool,
quick : bool, quick: bool,
width : usize, width: usize,
goal : usize, goal: usize,
tabwidth : usize, tabwidth: usize,
} }
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
@ -75,38 +75,57 @@ pub fn uumain(args: Vec<String>) -> i32 {
.parse(args); .parse(args);
let mut fmt_opts = FmtOptions { let mut fmt_opts = FmtOptions {
crown : false, crown: false,
tagged : false, tagged: false,
mail : false, mail: false,
uniform : false, uniform: false,
quick : false, quick: false,
split_only : false, split_only: false,
use_prefix : false, use_prefix: false,
prefix : String::new(), prefix: String::new(),
xprefix : false, xprefix: false,
use_anti_prefix : false, use_anti_prefix: false,
anti_prefix : String::new(), anti_prefix: String::new(),
xanti_prefix : false, xanti_prefix: false,
width : 79, width: 79,
goal : 74, goal: 74,
tabwidth : 8, tabwidth: 8,
}; };
if matches.opt_present("t") { fmt_opts.tagged = true; } if matches.opt_present("t") {
if matches.opt_present("c") { fmt_opts.crown = true; fmt_opts.tagged = false; } fmt_opts.tagged = true;
if matches.opt_present("m") { fmt_opts.mail = true; } }
if matches.opt_present("u") { fmt_opts.uniform = true; } if matches.opt_present("c") {
if matches.opt_present("q") { fmt_opts.quick = true; } fmt_opts.crown = true;
if matches.opt_present("s") { fmt_opts.split_only = true; fmt_opts.crown = false; fmt_opts.tagged = false; } fmt_opts.tagged = false;
if matches.opt_present("x") { fmt_opts.xprefix = true; } }
if matches.opt_present("X") { fmt_opts.xanti_prefix = true; } if matches.opt_present("m") {
fmt_opts.mail = true;
}
if matches.opt_present("u") {
fmt_opts.uniform = true;
}
if matches.opt_present("q") {
fmt_opts.quick = true;
}
if matches.opt_present("s") {
fmt_opts.split_only = true;
fmt_opts.crown = false;
fmt_opts.tagged = false;
}
if matches.opt_present("x") {
fmt_opts.xprefix = true;
}
if matches.opt_present("X") {
fmt_opts.xanti_prefix = true;
}
match matches.opt_str("p") { match matches.opt_str("p") {
Some(s) => { Some(s) => {
fmt_opts.prefix = s; fmt_opts.prefix = s;
fmt_opts.use_prefix = true; fmt_opts.use_prefix = true;
} }
None => () None => (),
}; };
match matches.opt_str("P") { match matches.opt_str("P") {
@ -114,27 +133,29 @@ pub fn uumain(args: Vec<String>) -> i32 {
fmt_opts.anti_prefix = s; fmt_opts.anti_prefix = s;
fmt_opts.use_anti_prefix = true; fmt_opts.use_anti_prefix = true;
} }
None => () None => (),
}; };
match matches.opt_str("w") { match matches.opt_str("w") {
Some(s) => { Some(s) => {
fmt_opts.width = fmt_opts.width = match s.parse::<usize>() {
match s.parse::<usize>() {
Ok(t) => t, Ok(t) => t,
Err(e) => { crash!(1, "Invalid WIDTH specification: `{}': {}", s, e); } Err(e) => {
crash!(1, "Invalid WIDTH specification: `{}': {}", s, e);
}
}; };
fmt_opts.goal = cmp::min(fmt_opts.width * 94 / 100, fmt_opts.width - 3); fmt_opts.goal = cmp::min(fmt_opts.width * 94 / 100, fmt_opts.width - 3);
} }
None => () None => (),
}; };
match matches.opt_str("g") { match matches.opt_str("g") {
Some(s) => { Some(s) => {
fmt_opts.goal = fmt_opts.goal = match s.parse::<usize>() {
match s.parse::<usize>() {
Ok(t) => t, Ok(t) => t,
Err(e) => { crash!(1, "Invalid GOAL specification: `{}': {}", s, e); } Err(e) => {
crash!(1, "Invalid GOAL specification: `{}': {}", s, e);
}
}; };
if !matches.opt_present("w") { if !matches.opt_present("w") {
fmt_opts.width = cmp::max(fmt_opts.goal * 100 / 94, fmt_opts.goal + 3); fmt_opts.width = cmp::max(fmt_opts.goal * 100 / 94, fmt_opts.goal + 3);
@ -142,18 +163,19 @@ pub fn uumain(args: Vec<String>) -> i32 {
crash!(1, "GOAL cannot be greater than WIDTH."); crash!(1, "GOAL cannot be greater than WIDTH.");
} }
} }
None => () None => (),
}; };
match matches.opt_str("T") { match matches.opt_str("T") {
Some(s) => { Some(s) => {
fmt_opts.tabwidth = fmt_opts.tabwidth = match s.parse::<usize>() {
match s.parse::<usize>() {
Ok(t) => t, Ok(t) => t,
Err(e) => { crash!(1, "Invalid TABWIDTH specification: `{}': {}", s, e); } Err(e) => {
crash!(1, "Invalid TABWIDTH specification: `{}': {}", s, e);
}
}; };
} }
None => () None => (),
}; };
if fmt_opts.tabwidth < 1 { if fmt_opts.tabwidth < 1 {
@ -172,13 +194,13 @@ pub fn uumain(args: Vec<String>) -> i32 {
for i in files.iter().map(|x| &x[..]) { for i in files.iter().map(|x| &x[..]) {
let mut fp = match i { let mut fp = match i {
"-" => BufReader::new(Box::new(stdin()) as Box<Read+'static>), "-" => BufReader::new(Box::new(stdin()) as Box<Read + 'static>),
_ => match File::open(i) { _ => match File::open(i) {
Ok(f) => BufReader::new(Box::new(f) as Box<Read+'static>), Ok(f) => BufReader::new(Box::new(f) as Box<Read + 'static>),
Err(e) => { Err(e) => {
show_warning!("{}: {}", i, e); show_warning!("{}: {}", i, e);
continue; continue;
}, }
}, },
}; };
let p_stream = ParagraphStream::new(&fmt_opts, &mut fp); let p_stream = ParagraphStream::new(&fmt_opts, &mut fp);
@ -187,8 +209,8 @@ pub fn uumain(args: Vec<String>) -> i32 {
Err(s) => { Err(s) => {
silent_unwrap!(ostream.write_all(s.as_bytes())); silent_unwrap!(ostream.write_all(s.as_bytes()));
silent_unwrap!(ostream.write_all("\n".as_bytes())); silent_unwrap!(ostream.write_all("\n".as_bytes()));
}, }
Ok(para) => break_lines(&para, &fmt_opts, &mut ostream) Ok(para) => break_lines(&para, &fmt_opts, &mut ostream),
} }
} }

View file

@ -8,19 +8,19 @@
*/ */
use FmtOptions; use FmtOptions;
use parasplit::{Paragraph, ParaWords, WordInfo}; use parasplit::{ParaWords, Paragraph, WordInfo};
use std::io::{Write, BufWriter, Stdout}; use std::io::{BufWriter, Stdout, Write};
use std::i64; use std::i64;
use std::cmp; use std::cmp;
use std::mem; use std::mem;
struct BreakArgs<'a> { struct BreakArgs<'a> {
opts : &'a FmtOptions, opts: &'a FmtOptions,
init_len : usize, init_len: usize,
indent_str : &'a str, indent_str: &'a str,
indent_len : usize, indent_len: usize,
uniform : bool, uniform: bool,
ostream : &'a mut BufWriter<Stdout> ostream: &'a mut BufWriter<Stdout>,
} }
impl<'a> BreakArgs<'a> { impl<'a> BreakArgs<'a> {
@ -31,7 +31,9 @@ impl<'a> BreakArgs<'a> {
let post = winfo.after_tab; let post = winfo.after_tab;
match winfo.before_tab { match winfo.before_tab {
None => post, None => post,
Some(pre) => post + ((pre + posn) / self.opts.tabwidth + 1) * self.opts.tabwidth - posn Some(pre) => {
post + ((pre + posn) / self.opts.tabwidth + 1) * self.opts.tabwidth - posn
}
} }
} }
} }
@ -56,8 +58,7 @@ 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 + let p_init_len = w_len + if opts.crown || opts.tagged {
if opts.crown || opts.tagged {
// handle "init" portion // handle "init" portion
silent_unwrap!(ostream.write_all(para.init_str.as_bytes())); silent_unwrap!(ostream.write_all(para.init_str.as_bytes()));
para.init_len para.init_len
@ -76,12 +77,12 @@ pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<
let uniform = para.mail_header || opts.uniform; let uniform = para.mail_header || opts.uniform;
let mut break_args = BreakArgs { let mut break_args = BreakArgs {
opts : opts, opts: opts,
init_len : p_init_len, init_len: p_init_len,
indent_str : &p_indent[..], indent_str: &p_indent[..],
indent_len : p_indent_len, indent_len: p_indent_len,
uniform : uniform, uniform: uniform,
ostream : ostream ostream: ostream,
}; };
if opts.quick || para.mail_header { if opts.quick || para.mail_header {
@ -93,16 +94,27 @@ pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<
// break_simple implements a "greedy" breaking algorithm: print words until // break_simple implements a "greedy" breaking algorithm: print words until
// maxlength would be exceeded, then print a linebreak and indent and continue. // maxlength would be exceeded, then print a linebreak and indent and continue.
fn break_simple<'a, T: Iterator<Item=&'a WordInfo<'a>>>(iter: T, args: &mut BreakArgs<'a>) { fn break_simple<'a, T: Iterator<Item = &'a WordInfo<'a>>>(iter: T, args: &mut BreakArgs<'a>) {
iter.fold((args.init_len, false), |l, winfo| accum_words_simple(args, l, winfo)); iter.fold((args.init_len, false), |l, winfo| {
accum_words_simple(args, l, winfo)
});
silent_unwrap!(args.ostream.write_all("\n".as_bytes())); silent_unwrap!(args.ostream.write_all("\n".as_bytes()));
} }
fn accum_words_simple<'a>(args: &mut BreakArgs<'a>, (l, prev_punct): (usize, bool), winfo: &'a WordInfo<'a>) -> (usize, bool) { fn accum_words_simple<'a>(
args: &mut BreakArgs<'a>,
(l, prev_punct): (usize, bool),
winfo: &'a WordInfo<'a>,
) -> (usize, bool) {
// compute the length of this word, considering how tabs will expand at this position on the line // compute the length of this word, considering how tabs will expand at this position on the line
let wlen = winfo.word_nchars + args.compute_width(winfo, l, false); let wlen = winfo.word_nchars + args.compute_width(winfo, l, false);
let slen = compute_slen(args.uniform, winfo.new_line, winfo.sentence_start, prev_punct); let slen = compute_slen(
args.uniform,
winfo.new_line,
winfo.sentence_start,
prev_punct,
);
if l + wlen + slen > args.opts.width { if l + wlen + slen > args.opts.width {
write_newline(args.indent_str, args.ostream); write_newline(args.indent_str, args.ostream);
@ -118,20 +130,31 @@ fn accum_words_simple<'a>(args: &mut BreakArgs<'a>, (l, prev_punct): (usize, boo
// Knuth, D.E., and Plass, M.F. "Breaking Paragraphs into Lines." in Software, // Knuth, D.E., and Plass, M.F. "Breaking Paragraphs into Lines." in Software,
// Practice and Experience. Vol. 11, No. 11, November 1981. // Practice and Experience. Vol. 11, No. 11, November 1981.
// http://onlinelibrary.wiley.com/doi/10.1002/spe.4380111102/pdf // http://onlinelibrary.wiley.com/doi/10.1002/spe.4380111102/pdf
fn break_knuth_plass<'a, T: Clone + Iterator<Item=&'a WordInfo<'a>>>(mut iter: T, args: &mut BreakArgs<'a>) { fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>(
mut iter: T,
args: &mut BreakArgs<'a>,
) {
// run the algorithm to get the breakpoints // run the algorithm to get the breakpoints
let breakpoints = find_kp_breakpoints(iter.clone(), args); let breakpoints = find_kp_breakpoints(iter.clone(), args);
// iterate through the breakpoints (note that breakpoints is in reverse break order, so we .rev() it // iterate through the breakpoints (note that breakpoints is in reverse break order, so we .rev() it
let (mut prev_punct, mut fresh) = let (mut prev_punct, mut fresh) = breakpoints.iter().rev().fold(
breakpoints.iter().rev().fold((false, false), |(mut prev_punct, mut fresh), &(next_break, break_before)| { (false, false),
|(mut prev_punct, mut fresh), &(next_break, break_before)| {
if fresh { if fresh {
write_newline(args.indent_str, args.ostream); write_newline(args.indent_str, args.ostream);
} }
// at each breakpoint, keep emitting words until we find the word matching this breakpoint // at each breakpoint, keep emitting words until we find the word matching this breakpoint
for winfo in &mut iter { for winfo in &mut iter {
let (slen, word) = slice_if_fresh(fresh, winfo.word, winfo.word_start, args.uniform, let (slen, word) = slice_if_fresh(
winfo.new_line, winfo.sentence_start, prev_punct); fresh,
winfo.word,
winfo.word_start,
args.uniform,
winfo.new_line,
winfo.sentence_start,
prev_punct,
);
fresh = false; fresh = false;
prev_punct = winfo.ends_punct; prev_punct = winfo.ends_punct;
@ -155,15 +178,23 @@ fn break_knuth_plass<'a, T: Clone + Iterator<Item=&'a WordInfo<'a>>>(mut iter: T
} }
} }
(prev_punct, fresh) (prev_punct, fresh)
}); },
);
// after the last linebreak, write out the rest of the final line. // after the last linebreak, write out the rest of the final line.
for winfo in iter { for winfo in iter {
if fresh { if fresh {
write_newline(args.indent_str, args.ostream); write_newline(args.indent_str, args.ostream);
} }
let (slen, word) = slice_if_fresh(fresh, winfo.word, winfo.word_start, args.uniform, let (slen, word) = slice_if_fresh(
winfo.new_line, winfo.sentence_start, prev_punct); fresh,
winfo.word,
winfo.word_start,
args.uniform,
winfo.new_line,
winfo.sentence_start,
prev_punct,
);
prev_punct = winfo.ends_punct; prev_punct = winfo.ends_punct;
fresh = false; fresh = false;
write_with_spaces(word, slen, args.ostream); write_with_spaces(word, slen, args.ostream);
@ -172,48 +203,56 @@ fn break_knuth_plass<'a, T: Clone + Iterator<Item=&'a WordInfo<'a>>>(mut iter: T
} }
struct LineBreak<'a> { struct LineBreak<'a> {
prev : usize, prev: usize,
linebreak : Option<&'a WordInfo<'a>>, linebreak: Option<&'a WordInfo<'a>>,
break_before : bool, break_before: bool,
demerits : i64, demerits: i64,
prev_rat : f32, prev_rat: f32,
length : usize, length: usize,
fresh : bool fresh: bool,
} }
fn find_kp_breakpoints<'a, T: Iterator<Item=&'a WordInfo<'a>>>(iter: T, args: &BreakArgs<'a>) -> Vec<(&'a WordInfo<'a>, bool)> { fn find_kp_breakpoints<'a, T: Iterator<Item = &'a WordInfo<'a>>>(
iter: T,
args: &BreakArgs<'a>,
) -> 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!(LineBreak { let mut linebreaks = vec![
prev : 0, LineBreak {
linebreak : None, prev: 0,
break_before : false, linebreak: None,
demerits : 0, break_before: false,
prev_rat : 0.0f32, demerits: 0,
length : args.init_len, prev_rat: 0.0f32,
fresh : false length: args.init_len,
}); fresh: false,
// this vec holds the current active linebreaks; next_ holds the breaks that will be active for the next word },
let active_breaks = &mut vec!(0); ];
let next_active_breaks = &mut vec!(); // this vec holds the current active linebreaks; next_ holds the breaks that will be active for
// the next word
let active_breaks = &mut vec![0];
let next_active_breaks = &mut vec![];
let stretch = (args.opts.width - args.opts.goal) as isize; let stretch = (args.opts.width - args.opts.goal) as isize;
let minlength = args.opts.goal - stretch as usize; let minlength = args.opts.goal - stretch as usize;
let mut new_linebreaks = vec!(); let mut new_linebreaks = vec![];
let mut is_sentence_start = false; let mut is_sentence_start = false;
let mut least_demerits = 0; let mut least_demerits = 0;
loop { loop {
let w = let w = match iter.next() {
match iter.next() {
None => break, None => break,
Some(w) => w Some(w) => w,
}; };
// if this is the last word, we don't add additional demerits for this break // if this is the last word, we don't add additional demerits for this break
let (is_last_word, is_sentence_end) = let (is_last_word, is_sentence_end) = match iter.peek() {
match iter.peek() {
None => (true, true), None => (true, true),
Some(&&WordInfo { sentence_start: st, new_line: nl, .. }) => (false, st || (nl && w.ends_punct)) Some(&&WordInfo {
sentence_start: st,
new_line: nl,
..
}) => (false, st || (nl && w.ends_punct)),
}; };
// should we be adding extra space at the beginning of the next sentence? // should we be adding extra space at the beginning of the next sentence?
@ -236,7 +275,8 @@ fn find_kp_breakpoints<'a, T: Iterator<Item=&'a WordInfo<'a>>>(iter: T, args: &B
} }
// get the new length // get the new length
let tlen = w.word_nchars + args.compute_width(w, active.length, active.fresh) + slen + active.length; let tlen = w.word_nchars + args.compute_width(w, active.length, active.fresh) + slen
+ 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
// otherwise, we extend the break, and possibly add a new break at this point // otherwise, we extend the break, and possibly add a new break at this point
@ -249,27 +289,33 @@ fn find_kp_breakpoints<'a, T: Iterator<Item=&'a WordInfo<'a>>>(iter: T, args: &B
// if we're above the minlength, we can also consider breaking here // if we're above the minlength, we can also consider breaking here
if tlen >= minlength { if tlen >= minlength {
let (new_demerits, new_ratio) = let (new_demerits, new_ratio) = if is_last_word {
if is_last_word {
// there is no penalty for the final line's length // there is no penalty for the final line's length
(0, 0.0) (0, 0.0)
} else { } else {
compute_demerits((args.opts.goal - tlen) as isize, stretch, w.word_nchars as isize, active.prev_rat) compute_demerits(
(args.opts.goal - tlen) as isize,
stretch,
w.word_nchars as isize,
active.prev_rat,
)
}; };
// 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 && active.demerits.signum() <= new_demerits.signum() { if new_demerits < BAD_INFTY_SQ && total_demerits < ld_new
&& active.demerits.signum() <= new_demerits.signum()
{
ld_new = total_demerits; ld_new = total_demerits;
new_linebreaks.push(LineBreak { new_linebreaks.push(LineBreak {
prev : i, prev: i,
linebreak : Some(w), linebreak: Some(w),
break_before : false, break_before: false,
demerits : total_demerits, demerits: total_demerits,
prev_rat : new_ratio, prev_rat: new_ratio,
length : args.indent_len, length: args.indent_len,
fresh : true fresh: true,
}); });
} }
} }
@ -289,7 +335,8 @@ fn find_kp_breakpoints<'a, T: Iterator<Item=&'a WordInfo<'a>>>(iter: T, args: &B
if next_active_breaks.is_empty() { if next_active_breaks.is_empty() {
// every potential linebreak is too long! choose the linebreak with the least demerits, ld_idx // every potential linebreak is too long! choose the linebreak with the least demerits, ld_idx
let new_break = restart_active_breaks(args, &linebreaks[ld_idx], ld_idx, w, slen, minlength); let new_break =
restart_active_breaks(args, &linebreaks[ld_idx], ld_idx, w, slen, minlength);
next_active_breaks.push(linebreaks.len()); next_active_breaks.push(linebreaks.len());
linebreaks.push(new_break); linebreaks.push(new_break);
least_demerits = 0; least_demerits = 0;
@ -309,11 +356,14 @@ fn find_kp_breakpoints<'a, T: Iterator<Item=&'a WordInfo<'a>>>(iter: T, args: &B
} }
fn build_best_path<'a>(paths: &[LineBreak<'a>], active: &[usize]) -> Vec<(&'a WordInfo<'a>, bool)> { fn build_best_path<'a>(paths: &[LineBreak<'a>], active: &[usize]) -> Vec<(&'a WordInfo<'a>, bool)> {
let mut breakwords = vec!(); let mut breakwords = vec![];
// of the active paths, we select the one with the fewest demerits // of the active paths, we select the one with the fewest demerits
let mut best_idx = match active.iter().min_by_key(|&&a| paths[a].demerits) { let mut best_idx = match active.iter().min_by_key(|&&a| paths[a].demerits) {
None => crash!(1, "Failed to find a k-p linebreak solution. This should never happen."), None => crash!(
Some(&s) => s 1,
"Failed to find a k-p linebreak solution. This should never happen."
),
Some(&s) => s,
}; };
// now, chase the pointers back through the break list, recording // now, chase the pointers back through the break list, recording
@ -342,27 +392,27 @@ const DL_MULT: f32 = 300.0;
fn compute_demerits(delta_len: isize, stretch: isize, wlen: isize, prev_rat: f32) -> (i64, f32) { fn compute_demerits(delta_len: isize, stretch: isize, wlen: isize, prev_rat: f32) -> (i64, f32) {
// how much stretch are we using? // how much stretch are we using?
let ratio = let ratio = if delta_len == 0 {
if delta_len == 0 {
0.0f32 0.0f32
} else { } else {
delta_len as f32 / stretch as f32 delta_len as f32 / stretch as f32
}; };
// compute badness given the stretch ratio // compute badness given the stretch ratio
let bad_linelen = let bad_linelen = if ratio.abs() > 1.0f32 {
if ratio.abs() > 1.0f32 {
BAD_INFTY BAD_INFTY
} else { } else {
(BAD_MULT * ratio.powf(3f32).abs()) as i64 (BAD_MULT * ratio.powf(3f32).abs()) as i64
}; };
// we penalize lines ending in really short words // we penalize lines ending in really short words
let bad_wordlen = let bad_wordlen = if wlen >= stretch {
if wlen >= stretch {
0 0
} else { } else {
(DL_MULT * ((stretch - wlen) as f32 / (stretch - 1) as f32).powf(3f32).abs()) as i64 (DL_MULT
* ((stretch - wlen) as f32 / (stretch - 1) as f32)
.powf(3f32)
.abs()) as i64
}; };
// we penalize lines that have very different ratios from previous lines // we penalize lines that have very different ratios from previous lines
@ -373,9 +423,15 @@ fn compute_demerits(delta_len: isize, stretch: isize, wlen: isize, prev_rat: f32
(demerits, ratio) (demerits, ratio)
} }
fn restart_active_breaks<'a>(args: &BreakArgs<'a>, active: &LineBreak<'a>, act_idx: usize, w: &'a WordInfo<'a>, slen: usize, min: usize) -> LineBreak<'a> { fn restart_active_breaks<'a>(
let (break_before, line_length) = args: &BreakArgs<'a>,
if active.fresh { active: &LineBreak<'a>,
act_idx: usize,
w: &'a WordInfo<'a>,
slen: usize,
min: usize,
) -> LineBreak<'a> {
let (break_before, line_length) = if active.fresh {
// never break before a word if that word would be the first on a line // never break before a word if that word would be the first on a line
(false, args.indent_len) (false, args.indent_len)
} else { } else {
@ -393,13 +449,13 @@ fn restart_active_breaks<'a>(args: &BreakArgs<'a>, active: &LineBreak<'a>, act_i
// restart the linebreak. This will be our only active path. // restart the linebreak. This will be our only active path.
LineBreak { LineBreak {
prev : act_idx, prev: act_idx,
linebreak : Some(w), linebreak: Some(w),
break_before : break_before, break_before: break_before,
demerits : 0, // this is the only active break, so we can reset the demerit count demerits: 0, // this is the only active break, so we can reset the demerit count
prev_rat : if break_before { 1.0 } else { -1.0 }, prev_rat: if break_before { 1.0 } else { -1.0 },
length : line_length, length: line_length,
fresh : !break_before fresh: !break_before,
} }
} }
@ -418,7 +474,15 @@ fn compute_slen(uniform: bool, newline: bool, start: bool, punct: bool) -> usize
// If we're on a fresh line, slen=0 and we slice off leading whitespace. // If we're on a fresh line, slen=0 and we slice off leading whitespace.
// Otherwise, compute slen and leave whitespace alone. // Otherwise, compute slen and leave whitespace alone.
fn slice_if_fresh(fresh: bool, word: &str, start: usize, uniform: bool, newline: bool, sstart: bool, punct: bool) -> (usize, &str) { fn slice_if_fresh(
fresh: bool,
word: &str,
start: usize,
uniform: bool,
newline: bool,
sstart: bool,
punct: bool,
) -> (usize, &str) {
if fresh { if fresh {
(0, &word[start..]) (0, &word[start..])
} else { } else {

View file

@ -32,7 +32,7 @@ fn char_width(c: char) -> usize {
#[derive(Debug)] #[derive(Debug)]
pub enum Line { pub enum Line {
FormatLine(FileLine), FormatLine(FileLine),
NoFormatLine(String, bool) NoFormatLine(String, bool),
} }
impl Line { impl Line {
@ -40,7 +40,7 @@ impl Line {
fn get_formatline(self) -> FileLine { fn get_formatline(self) -> FileLine {
match self { match self {
Line::FormatLine(fl) => fl, Line::FormatLine(fl) => fl,
Line::NoFormatLine(..) => panic!("Found NoFormatLine when expecting FormatLine") Line::NoFormatLine(..) => panic!("Found NoFormatLine when expecting FormatLine"),
} }
} }
@ -48,7 +48,7 @@ impl Line {
fn get_noformatline(self) -> (String, bool) { fn get_noformatline(self) -> (String, bool) {
match self { match self {
Line::NoFormatLine(s, b) => (s, b), Line::NoFormatLine(s, b) => (s, b),
Line::FormatLine(..) => panic!("Found FormatLine when expecting NoFormatLine") Line::FormatLine(..) => panic!("Found FormatLine when expecting NoFormatLine"),
} }
} }
} }
@ -57,38 +57,49 @@ impl Line {
// the next line or not // the next line or not
#[derive(Debug)] #[derive(Debug)]
pub struct FileLine { pub struct FileLine {
line : String, line: String,
indent_end : usize, // the end of the indent, always the start of the text indent_end: usize, // the end of the indent, always the start of the text
pfxind_end : usize, // the end of the PREFIX's indent, that is, the spaces before the prefix pfxind_end: usize, // the end of the PREFIX's indent, that is, the spaces before the prefix
indent_len : usize, // display length of indent taking into account tabs indent_len: usize, // display length of indent taking into account tabs
prefix_len : usize, // PREFIX indent length taking into account tabs prefix_len: usize, // PREFIX indent length taking into account tabs
} }
// iterator that produces a stream of Lines from a file // iterator that produces a stream of Lines from a file
pub struct FileLines<'a> { pub struct FileLines<'a> {
opts : &'a FmtOptions, opts: &'a FmtOptions,
lines : Lines<&'a mut FileOrStdReader>, lines: Lines<&'a mut FileOrStdReader>,
} }
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 { opts: opts, lines: lines } FileLines {
opts: opts,
lines: lines,
}
} }
// returns true if this line should be formatted // returns true if this line should be formatted
fn match_prefix(&self, line: &str) -> (bool, usize) { fn match_prefix(&self, line: &str) -> (bool, usize) {
if !self.opts.use_prefix { return (true, 0); } if !self.opts.use_prefix {
return (true, 0);
}
FileLines::match_prefix_generic(&self.opts.prefix[..], line, self.opts.xprefix) FileLines::match_prefix_generic(&self.opts.prefix[..], line, self.opts.xprefix)
} }
// returns true if this line should be formatted // returns true if this line should be formatted
fn match_anti_prefix(&self, line: &str) -> bool { fn match_anti_prefix(&self, line: &str) -> bool {
if !self.opts.use_anti_prefix { return true; } if !self.opts.use_anti_prefix {
return true;
}
match FileLines::match_prefix_generic(&self.opts.anti_prefix[..], line, self.opts.xanti_prefix) { match FileLines::match_prefix_generic(
&self.opts.anti_prefix[..],
line,
self.opts.xanti_prefix,
) {
(true, _) => false, (true, _) => false,
(_ , _) => true (_, _) => true,
} }
} }
@ -141,13 +152,12 @@ impl<'a> Iterator for FileLines<'a> {
type Item = Line; type Item = Line;
fn next(&mut self) -> Option<Line> { fn next(&mut self) -> Option<Line> {
let n = let n = match self.lines.next() {
match self.lines.next() {
Some(t) => match t { Some(t) => match t {
Ok(tt) => tt, Ok(tt) => tt,
Err(_) => return None Err(_) => return None,
}, },
None => return None None => return None,
}; };
// if this line is entirely whitespace, // if this line is entirely whitespace,
@ -163,7 +173,10 @@ impl<'a> Iterator for FileLines<'a> {
let (pmatch, poffset) = self.match_prefix(&n[..]); 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()) { } else if n[poffset + self.opts.prefix.len()..]
.chars()
.all(|c| c.is_whitespace())
{
// if the line matches the prefix, but is blank after, // if the line matches the prefix, but is blank after,
// don't allow lines to be combined through it (that is, // don't allow lines to be combined through it (that is,
// treat it like a blank line, except that since it's // treat it like a blank line, except that since it's
@ -183,11 +196,11 @@ impl<'a> Iterator for FileLines<'a> {
let (indent_end, prefix_len, indent_len) = self.compute_indent(&n[..], prefix_end); let (indent_end, prefix_len, indent_len) = self.compute_indent(&n[..], prefix_end);
Some(Line::FormatLine(FileLine { Some(Line::FormatLine(FileLine {
line : n, line: n,
indent_end : indent_end, indent_end: indent_end,
pfxind_end : poffset, pfxind_end: poffset,
indent_len : indent_len, indent_len: indent_len,
prefix_len : prefix_len prefix_len: prefix_len,
})) }))
} }
} }
@ -198,29 +211,33 @@ impl<'a> Iterator for FileLines<'a> {
// is only there to help us in deciding how to merge lines into Paragraphs // is only there to help us in deciding how to merge lines into Paragraphs
#[derive(Debug)] #[derive(Debug)]
pub struct Paragraph { pub struct Paragraph {
lines : Vec<String>, // the lines of the file lines: Vec<String>, // the lines of the file
pub init_str : String, // string representing the init, that is, the first line's indent pub init_str: String, // string representing the init, that is, the first line's indent
pub init_len : usize, // printable length of the init string considering TABWIDTH pub init_len: usize, // printable length of the init string considering TABWIDTH
init_end : usize, // byte location of end of init in first line String init_end: usize, // byte location of end of init in first line String
pub indent_str : String, // string representing indent pub indent_str: String, // string representing indent
pub indent_len : usize, // length of above pub indent_len: usize, // length of above
indent_end : usize, // byte location of end of indent (in crown and tagged mode, only applies to 2nd line and onward) indent_end: usize, // byte location of end of indent (in crown and tagged mode, only applies to 2nd line and onward)
pub mail_header : bool // we need to know if this is a mail header because we do word splitting differently in that case pub mail_header: bool, // we need to know if this is a mail header because we do word splitting differently in that case
} }
// an iterator producing a stream of paragraphs from a stream of lines // an iterator producing a stream of paragraphs from a stream of lines
// given a set of options. // given a set of options.
pub struct ParagraphStream<'a> { pub struct ParagraphStream<'a> {
lines : Peekable<FileLines<'a>>, lines: Peekable<FileLines<'a>>,
next_mail : bool, next_mail: bool,
opts : &'a FmtOptions, opts: &'a FmtOptions,
} }
impl<'a> ParagraphStream<'a> { impl<'a> ParagraphStream<'a> {
pub fn new<'b>(opts: &'b FmtOptions, reader: &'b mut FileOrStdReader) -> ParagraphStream<'b> { pub fn new<'b>(opts: &'b FmtOptions, reader: &'b mut FileOrStdReader) -> ParagraphStream<'b> {
let lines = FileLines::new(opts, reader.lines()).peekable(); let lines = FileLines::new(opts, reader.lines()).peekable();
// at the beginning of the file, we might find mail headers // at the beginning of the file, we might find mail headers
ParagraphStream { lines: lines, next_mail: true, opts: opts } ParagraphStream {
lines: lines,
next_mail: true,
opts: opts,
}
} }
// detect RFC822 mail header // detect RFC822 mail header
@ -235,18 +252,19 @@ impl<'a> ParagraphStream<'a> {
if l_slice.starts_with("From ") { if l_slice.starts_with("From ") {
true true
} else { } else {
let colon_posn = let colon_posn = match l_slice.find(':') {
match l_slice.find(':') {
Some(n) => n, Some(n) => n,
None => return false None => return false,
}; };
// header field must be nonzero length // header field must be nonzero length
if colon_posn == 0 { return false; } if colon_posn == 0 {
return false;
}
l_slice[..colon_posn].chars().all(|x| match x as usize { l_slice[..colon_posn].chars().all(|x| match x as usize {
y if y < 33 || y > 126 => false, y if y < 33 || y > 126 => false,
_ => true _ => true,
}) })
} }
} }
@ -258,13 +276,12 @@ impl<'a> Iterator for ParagraphStream<'a> {
fn next(&mut self) -> Option<Result<Paragraph, String>> { fn next(&mut self) -> Option<Result<Paragraph, String>> {
// return a NoFormatLine in an Err; it should immediately be output // return a NoFormatLine in an Err; it should immediately be output
let noformat = let noformat = match self.lines.peek() {
match self.lines.peek() {
None => return None, None => return None,
Some(l) => match *l { Some(l) => match *l {
Line::FormatLine(_) => false, Line::FormatLine(_) => false,
Line::NoFormatLine(_, _) => true Line::NoFormatLine(_, _) => true,
} },
}; };
// found a NoFormatLine, immediately dump it out // found a NoFormatLine, immediately dump it out
@ -288,17 +305,15 @@ impl<'a> Iterator for ParagraphStream<'a> {
let mut in_mail = false; let mut in_mail = false;
let mut second_done = false; // for when we use crown or tagged mode let mut second_done = false; // for when we use crown or tagged mode
loop { loop {
{ // peek ahead {
// peek ahead
// need to explicitly force fl out of scope before we can call self.lines.next() // need to explicitly force fl out of scope before we can call self.lines.next()
let fl = let fl = match self.lines.peek() {
match self.lines.peek() {
None => break, None => break,
Some(l) => { Some(l) => match *l {
match *l {
Line::FormatLine(ref x) => x, Line::FormatLine(ref x) => x,
Line::NoFormatLine(..) => break Line::NoFormatLine(..) => break,
} },
}
}; };
if p_lines.is_empty() { if p_lines.is_empty() {
@ -306,8 +321,9 @@ impl<'a> Iterator for ParagraphStream<'a> {
// detect mail header // detect mail header
if self.opts.mail && self.next_mail && ParagraphStream::is_mail_header(fl) { if self.opts.mail && self.next_mail && ParagraphStream::is_mail_header(fl) {
in_mail = true; in_mail = true;
// there can't be any indent or pfxind because otherwise is_mail_header would fail // there can't be any indent or pfxind because otherwise is_mail_header
// since there cannot be any whitespace before the colon in a valid header field // would fail since there cannot be any whitespace before the colon in a
// valid header field
indent_str.push_str(" "); indent_str.push_str(" ");
indent_len = 2; indent_len = 2;
} else { } else {
@ -350,7 +366,9 @@ impl<'a> Iterator for ParagraphStream<'a> {
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 // 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 && indent_end == fl.indent_end { } else if self.opts.tagged && indent_len - 4 == fl.indent_len
&& indent_end == fl.indent_end
{
// in tagged mode, indent has to be *different* on following lines // in tagged mode, indent has to be *different* on following lines
break; break;
} else { } else {
@ -363,7 +381,10 @@ impl<'a> Iterator for ParagraphStream<'a> {
second_done = true; second_done = true;
} else { } else {
// detect mismatch // detect mismatch
if indent_end != fl.indent_end || pfxind_end != fl.pfxind_end || indent_len != fl.indent_len || prefix_len != fl.prefix_len { if indent_end != fl.indent_end || pfxind_end != fl.pfxind_end
|| indent_len != fl.indent_len
|| prefix_len != fl.prefix_len
{
break; break;
} }
} }
@ -383,50 +404,59 @@ impl<'a> Iterator for ParagraphStream<'a> {
self.next_mail = in_mail; self.next_mail = in_mail;
Some(Ok(Paragraph { Some(Ok(Paragraph {
lines : p_lines, lines: p_lines,
init_str : init_str, init_str: init_str,
init_len : init_len, init_len: init_len,
init_end : init_end, init_end: init_end,
indent_str : indent_str, indent_str: indent_str,
indent_len : indent_len, indent_len: indent_len,
indent_end : indent_end, indent_end: indent_end,
mail_header : in_mail mail_header: in_mail,
})) }))
} }
} }
pub struct ParaWords<'a> { pub struct ParaWords<'a> {
opts : &'a FmtOptions, opts: &'a FmtOptions,
para : &'a Paragraph, para: &'a Paragraph,
words : Vec<WordInfo<'a>> words: Vec<WordInfo<'a>>,
} }
impl<'a> ParaWords<'a> { impl<'a> ParaWords<'a> {
pub fn new<'b>(opts: &'b FmtOptions, para: &'b Paragraph) -> ParaWords<'b> { pub fn new<'b>(opts: &'b FmtOptions, para: &'b Paragraph) -> ParaWords<'b> {
let mut pw = ParaWords { opts: opts, para: para, words: Vec::new() }; let mut pw = ParaWords {
opts: opts,
para: para,
words: Vec::new(),
};
pw.create_words(); pw.create_words();
pw pw
} }
fn create_words(& mut self) { fn create_words(&mut self) {
if self.para.mail_header { if self.para.mail_header {
// no extra spacing for mail headers; always exactly 1 space // no extra spacing for mail headers; always exactly 1 space
// safe to trim_left on every line of a mail header, since the // safe to trim_left on every line of a mail header, since the
// first line is guaranteed not to have any spaces // first line is guaranteed not to have any spaces
self.words.extend(self.para.lines.iter().flat_map(|x| x.split_whitespace()).map(|x| WordInfo { self.words.extend(
word : x, self.para
word_start : 0, .lines
word_nchars : x.len(), // OK for mail headers; only ASCII allowed (unicode is escaped) .iter()
before_tab : None, .flat_map(|x| x.split_whitespace())
after_tab : 0, .map(|x| WordInfo {
sentence_start : false, word: x,
ends_punct : false, word_start: 0,
new_line : false word_nchars: x.len(), // OK for mail headers; only ASCII allowed (unicode is escaped)
})); before_tab: None,
after_tab: 0,
sentence_start: false,
ends_punct: false,
new_line: false,
}),
);
} else { } else {
// first line // first line
self.words.extend( self.words.extend(if self.opts.crown || self.opts.tagged {
if self.opts.crown || self.opts.tagged {
// crown and tagged mode has the "init" in the first line, so slice from there // crown and tagged mode has the "init" in the first line, so slice from there
WordSplit::new(self.opts, &self.para.lines[0][self.para.init_end..]) WordSplit::new(self.opts, &self.para.lines[0][self.para.init_end..])
} else { } else {
@ -438,20 +468,27 @@ impl<'a> ParaWords<'a> {
let indent_end = self.para.indent_end; let indent_end = self.para.indent_end;
let opts = self.opts; let opts = self.opts;
self.words.extend( self.words.extend(
self.para.lines.iter().skip(1).flat_map(|x| WordSplit::new(opts, &x[indent_end..]))); self.para
.lines
.iter()
.skip(1)
.flat_map(|x| WordSplit::new(opts, &x[indent_end..])),
);
} }
} }
} }
pub fn words(&'a self) -> Iter<'a, WordInfo<'a>> { self.words.iter() } pub fn words(&'a self) -> Iter<'a, WordInfo<'a>> {
self.words.iter()
}
} }
struct WordSplit<'a> { struct WordSplit<'a> {
opts : &'a FmtOptions, opts: &'a FmtOptions,
string : &'a str, string: &'a str,
length : usize, length: usize,
position : usize, position: usize,
prev_punct : bool prev_punct: bool,
} }
impl<'a> WordSplit<'a> { impl<'a> WordSplit<'a> {
@ -484,26 +521,32 @@ impl<'a> WordSplit<'a> {
fn new<'b>(opts: &'b FmtOptions, string: &'b str) -> WordSplit<'b> { fn new<'b>(opts: &'b FmtOptions, string: &'b str) -> WordSplit<'b> {
// wordsplits *must* start at a non-whitespace character // wordsplits *must* start at a non-whitespace character
let trim_string = string.trim_left(); let trim_string = string.trim_left();
WordSplit { opts: opts, string: trim_string, length: string.len(), position: 0, prev_punct: false } WordSplit {
opts: opts,
string: trim_string,
length: string.len(),
position: 0,
prev_punct: false,
}
} }
fn is_punctuation(c: char) -> bool { fn is_punctuation(c: char) -> bool {
match c { match c {
'!' | '.' | '?' => true, '!' | '.' | '?' => true,
_ => false _ => false,
} }
} }
} }
pub struct WordInfo<'a> { pub struct WordInfo<'a> {
pub word : &'a str, pub word: &'a str,
pub word_start : usize, pub word_start: usize,
pub word_nchars : usize, pub word_nchars: usize,
pub before_tab : Option<usize>, pub before_tab: Option<usize>,
pub after_tab : usize, pub after_tab: usize,
pub sentence_start : bool, pub sentence_start: bool,
pub ends_punct : bool, pub ends_punct: bool,
pub new_line : bool pub new_line: bool,
} }
// returns (&str, is_start_of_sentence) // returns (&str, is_start_of_sentence)
@ -512,14 +555,15 @@ impl<'a> Iterator for WordSplit<'a> {
fn next(&mut self) -> Option<WordInfo<'a>> { fn next(&mut self) -> Option<WordInfo<'a>> {
if self.position >= self.length { if self.position >= self.length {
return None return None;
} }
let old_position = self.position; let old_position = self.position;
let new_line = old_position == 0; let new_line = old_position == 0;
// find the start of the next word, and record if we find a tab character // find the start of the next word, and record if we find a tab character
let (before_tab, after_tab, word_start) = match self.analyze_tabs(&self.string[old_position..]) { let (before_tab, after_tab, word_start) =
match self.analyze_tabs(&self.string[old_position..]) {
(b, a, Some(s)) => (b, a, s + old_position), (b, a, Some(s)) => (b, a, s + old_position),
(_, _, None) => { (_, _, None) => {
self.position = self.length; self.position = self.length;
@ -531,39 +575,49 @@ impl<'a> Iterator for WordSplit<'a> {
// note that this preserves the invariant that self.position // note that this preserves the invariant that self.position
// points to whitespace character OR end of string // points to whitespace character OR end of string
let mut word_nchars = 0; let mut word_nchars = 0;
self.position = self.position = match self.string[word_start..].find(|x: char| {
match self.string[word_start..] if !x.is_whitespace() {
.find(|x: char| if !x.is_whitespace() { word_nchars += char_width(x); false } else { true }) { word_nchars += char_width(x);
false
} else {
true
}
}) {
None => self.length, None => self.length,
Some(s) => s + word_start Some(s) => s + word_start,
}; };
let word_start_relative = word_start - old_position; let word_start_relative = word_start - old_position;
// if the previous sentence was punctuation and this sentence has >2 whitespace or one tab, is a new sentence. // if the previous sentence was punctuation and this sentence has >2 whitespace or one tab, is a new sentence.
let is_start_of_sentence = self.prev_punct && (before_tab.is_some() || word_start_relative > 1); let is_start_of_sentence =
self.prev_punct && (before_tab.is_some() || word_start_relative > 1);
// now record whether this word ends in punctuation // now record whether this word ends in punctuation
self.prev_punct = match self.string[..self.position].chars().rev().next() { self.prev_punct = match self.string[..self.position].chars().rev().next() {
Some(ch) => WordSplit::is_punctuation(ch), Some(ch) => WordSplit::is_punctuation(ch),
_ => panic!("fatal: expected word not to be empty") _ => panic!("fatal: expected word not to be empty"),
}; };
let (word, word_start_relative, before_tab, after_tab) = let (word, word_start_relative, before_tab, after_tab) = if self.opts.uniform {
if self.opts.uniform {
(&self.string[word_start..self.position], 0, None, 0) (&self.string[word_start..self.position], 0, None, 0)
} else { } else {
(&self.string[old_position..self.position], word_start_relative, before_tab, after_tab) (
&self.string[old_position..self.position],
word_start_relative,
before_tab,
after_tab,
)
}; };
Some(WordInfo { Some(WordInfo {
word : word, word: word,
word_start : word_start_relative, word_start: word_start_relative,
word_nchars : word_nchars, word_nchars: word_nchars,
before_tab : before_tab, before_tab: before_tab,
after_tab : after_tab, after_tab: after_tab,
sentence_start : is_start_of_sentence, sentence_start: is_start_of_sentence,
ends_punct : self.prev_punct, ends_punct: self.prev_punct,
new_line : new_line new_line: new_line,
}) })
} }
} }

View file

@ -13,7 +13,7 @@
extern crate uucore; extern crate uucore;
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader, Read, stdin}; use std::io::{stdin, BufRead, BufReader, Read};
use std::path::Path; use std::path::Path;
static SYNTAX: &'static str = "[OPTION]... [FILE]..."; static SYNTAX: &'static str = "[OPTION]... [FILE]...";
@ -24,15 +24,28 @@ static LONG_HELP: &'static str = "";
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let (args, obs_width) = handle_obsolete(&args[..]); let (args, obs_width) = handle_obsolete(&args[..]);
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP) let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optflag("b", "bytes", "count using bytes rather than columns (meaning control characters such as newline are not treated specially)") .optflag(
.optflag("s", "spaces", "break lines at word boundaries rather than a hard cut-off") "b",
.optopt("w", "width", "set WIDTH as the maximum line width rather than 80", "WIDTH") "bytes",
"count using bytes rather than columns (meaning control characters \
such as newline are not treated specially)",
)
.optflag(
"s",
"spaces",
"break lines at word boundaries rather than a hard cut-off",
)
.optopt(
"w",
"width",
"set WIDTH as the maximum line width rather than 80",
"WIDTH",
)
.parse(args); .parse(args);
let bytes = matches.opt_present("b"); let bytes = matches.opt_present("b");
let spaces = matches.opt_present("s"); let spaces = matches.opt_present("s");
let poss_width = let poss_width = if matches.opt_present("w") {
if matches.opt_present("w") {
matches.opt_str("w") matches.opt_str("w")
} else { } else {
obs_width obs_width
@ -40,12 +53,12 @@ pub fn uumain(args: Vec<String>) -> i32 {
let width = match poss_width { let width = match poss_width {
Some(inp_width) => match inp_width.parse::<usize>() { Some(inp_width) => match inp_width.parse::<usize>() {
Ok(width) => width, Ok(width) => width,
Err(e) => crash!(1, "illegal width value (\"{}\"): {}", inp_width, e) Err(e) => crash!(1, "illegal width value (\"{}\"): {}", inp_width, e),
}, },
None => 80 None => 80,
}; };
let files = if matches.free.is_empty() { let files = if matches.free.is_empty() {
vec!("-".to_owned()) vec!["-".to_owned()]
} else { } else {
matches.free matches.free
}; };
@ -57,7 +70,9 @@ 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.chars().next().unwrap() == '-' && slice.len() > 1 && slice.chars().nth(1).unwrap().is_digit(10) { if slice.chars().next().unwrap() == '-' && slice.len() > 1
&& 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()));
@ -72,15 +87,13 @@ fn fold(filenames: Vec<String>, bytes: bool, spaces: bool, width: usize) {
let filename: &str = &filename; let filename: &str = &filename;
let mut stdin_buf; let mut stdin_buf;
let mut file_buf; let mut file_buf;
let buffer = BufReader::new( let buffer = BufReader::new(if filename == "-" {
if filename == "-" {
stdin_buf = stdin(); stdin_buf = stdin();
&mut stdin_buf as &mut Read &mut stdin_buf as &mut Read
} else { } else {
file_buf = safe_unwrap!(File::open(Path::new(filename))); file_buf = safe_unwrap!(File::open(Path::new(filename)));
&mut file_buf as &mut Read &mut file_buf as &mut Read
} });
);
fold_file(buffer, bytes, spaces, width); fold_file(buffer, bytes, spaces, width);
} }
} }
@ -99,7 +112,7 @@ fn fold_file<T: Read>(mut file: BufReader<T>, bytes: bool, spaces: bool, width:
if spaces && i + width < len { if spaces && i + width < len {
match slice.rfind(|ch: char| ch.is_whitespace()) { match slice.rfind(|ch: char| ch.is_whitespace()) {
Some(m) => &slice[..m + 1], Some(m) => &slice[..m + 1],
None => slice None => slice,
} }
} else { } else {
slice slice
@ -125,22 +138,25 @@ fn fold_file<T: Read>(mut file: BufReader<T>, bytes: bool, spaces: bool, width:
if count >= width { if count >= width {
let (val, ncount) = { let (val, ncount) = {
let slice = &output[..]; let slice = &output[..];
let (out, val, ncount) = let (out, val, ncount) = if spaces && i + 1 < len {
if spaces && i + 1 < len {
match rfind_whitespace(slice) { match rfind_whitespace(slice) {
Some(m) => { Some(m) => {
let routput = &slice[m + 1 .. slice.chars().count()]; let routput = &slice[m + 1..slice.chars().count()];
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 { !0 } else { 0 }, '\x08' => if out > 0 {
!0
} else {
0
},
'\r' => return 0, '\r' => return 0,
_ => 1 _ => 1,
} }
}); });
(&slice[0 .. m + 1], routput, ncount) (&slice[0..m + 1], routput, ncount)
}, }
None => (slice, "", 0) None => (slice, "", 0),
} }
} else { } else {
(slice, "", 0) (slice, "", 0)
@ -173,7 +189,7 @@ fn fold_file<T: Read>(mut file: BufReader<T>, bytes: bool, spaces: bool, width:
count = 0; count = 0;
continue; continue;
} }
_ => count += 1 _ => count += 1,
}; };
output.push(ch); output.push(ch);
} }

View file

@ -12,20 +12,34 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use uucore::entries::{Passwd, Locate, get_groups, gid2grp}; use uucore::entries::{get_groups, Locate, Passwd, gid2grp};
static SYNTAX: &'static str = "[user]"; static SYNTAX: &'static str = "[user]";
static SUMMARY: &'static str = "display current group names"; static SUMMARY: &'static str = "display current group names";
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let matches = new_coreopts!(SYNTAX, SUMMARY, "") let matches = new_coreopts!(SYNTAX, SUMMARY, "").parse(args);
.parse(args);
if matches.free.is_empty() { if matches.free.is_empty() {
println!("{}", get_groups().unwrap().iter().map(|&g| gid2grp(g).unwrap()).collect::<Vec<_>>().join(" ")); println!(
"{}",
get_groups()
.unwrap()
.iter()
.map(|&g| gid2grp(g).unwrap())
.collect::<Vec<_>>()
.join(" ")
);
} else { } else {
if let Ok(p) = Passwd::locate(matches.free[0].as_str()) { if let Ok(p) = Passwd::locate(matches.free[0].as_str()) {
println!("{}", p.belongs_to().iter().map(|&g| gid2grp(g).unwrap()).collect::<Vec<_>>().join(" ")); println!(
"{}",
p.belongs_to()
.iter()
.map(|&g| gid2grp(g).unwrap())
.collect::<Vec<_>>()
.join(" ")
);
} else { } else {
crash!(1, "unknown user {}", matches.free[0]); crash!(1, "unknown user {}", matches.free[0]);
} }

View file

@ -4,11 +4,13 @@ extern crate sha1;
extern crate sha2; extern crate sha2;
extern crate sha3; extern crate sha3;
use digest::digest::{Input, ExtendableOutput, XofReader}; use digest::digest::{ExtendableOutput, Input, XofReader};
use hex::ToHex; use hex::ToHex;
pub trait Digest { pub trait Digest {
fn new() -> Self where Self: Sized; fn new() -> Self
where
Self: Sized;
fn input(&mut self, input: &[u8]); fn input(&mut self, input: &[u8]);
fn result(&mut self, out: &mut [u8]); fn result(&mut self, out: &mut [u8]);
fn reset(&mut self); fn reset(&mut self);
@ -40,7 +42,9 @@ impl Digest for md5::Context {
*self = md5::Context::new(); *self = md5::Context::new();
} }
fn output_bits(&self) -> usize { 128 } fn output_bits(&self) -> usize {
128
}
} }
impl Digest for sha1::Sha1 { impl Digest for sha1::Sha1 {
@ -60,7 +64,9 @@ impl Digest for sha1::Sha1 {
self.reset(); self.reset();
} }
fn output_bits(&self) -> usize { 160 } fn output_bits(&self) -> usize {
160
}
} }
// Implements the Digest trait for sha2 / sha3 algorithms with fixed ouput // Implements the Digest trait for sha2 / sha3 algorithms with fixed ouput

View file

@ -14,8 +14,8 @@
extern crate getopts; extern crate getopts;
extern crate hex; extern crate hex;
extern crate md5; extern crate md5;
extern crate regex_syntax;
extern crate regex; extern crate regex;
extern crate regex_syntax;
extern crate sha1; extern crate sha1;
extern crate sha2; extern crate sha2;
extern crate sha3; extern crate sha3;
@ -35,7 +35,7 @@ use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
#[allow(unused_imports)] #[allow(unused_imports)]
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::fs::File; use std::fs::File;
use std::io::{self, BufRead, BufReader, Read, stdin}; use std::io::{self, stdin, BufRead, BufReader, Read};
use std::path::Path; use std::path::Path;
static NAME: &'static str = "hashsum"; static NAME: &'static str = "hashsum";
@ -43,18 +43,17 @@ static VERSION: &'static str = env!("CARGO_PKG_VERSION");
fn is_custom_binary(program: &str) -> bool { fn is_custom_binary(program: &str) -> bool {
match program { match program {
"md5sum" | "sha1sum" "md5sum" | "sha1sum" | "sha224sum" | "sha256sum" | "sha384sum" | "sha512sum"
| "sha224sum" | "sha256sum" | "sha3sum" | "sha3-224sum" | "sha3-256sum" | "sha3-384sum" | "sha3-512sum"
| "sha384sum" | "sha512sum" | "shake128sum" | "shake256sum" => true,
| "sha3sum" | "sha3-224sum" _ => false,
| "sha3-256sum" | "sha3-384sum"
| "sha3-512sum" | "shake128sum"
| "shake256sum" => true,
_ => false
} }
} }
fn detect_algo(program: &str, matches: &getopts::Matches) -> (&'static str, Box<Digest+'static>, usize) { fn detect_algo(
program: &str,
matches: &getopts::Matches,
) -> (&'static str, Box<Digest + 'static>, usize) {
let mut alg: Option<Box<Digest>> = None; let mut alg: Option<Box<Digest>> = None;
let mut name: &'static str = ""; let mut name: &'static str = "";
let mut output_bits = 0; let mut output_bits = 0;
@ -65,92 +64,132 @@ fn detect_algo(program: &str, matches: &getopts::Matches) -> (&'static str, Box<
"sha256sum" => ("SHA256", Box::new(Sha256::new()) as Box<Digest>, 256), "sha256sum" => ("SHA256", Box::new(Sha256::new()) as Box<Digest>, 256),
"sha384sum" => ("SHA384", Box::new(Sha384::new()) as Box<Digest>, 384), "sha384sum" => ("SHA384", Box::new(Sha384::new()) as Box<Digest>, 384),
"sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box<Digest>, 512), "sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box<Digest>, 512),
"sha3sum" => { "sha3sum" => match matches.opt_str("bits") {
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<Digest>, 224), Ok(224) => ("SHA3-224", Box::new(Sha3_224::new()) as Box<Digest>, 224),
Ok(256) => ("SHA3-256", Box::new(Sha3_256::new()) as Box<Digest>, 256), Ok(256) => ("SHA3-256", Box::new(Sha3_256::new()) as Box<Digest>, 256),
Ok(384) => ("SHA3-384", Box::new(Sha3_384::new()) as Box<Digest>, 384), Ok(384) => ("SHA3-384", Box::new(Sha3_384::new()) as Box<Digest>, 384),
Ok(512) => ("SHA3-512", Box::new(Sha3_512::new()) as Box<Digest>, 512), Ok(512) => ("SHA3-512", Box::new(Sha3_512::new()) as Box<Digest>, 512),
Ok(_) => crash!(1, "Invalid output size for SHA3 (expected 224, 256, 384, or 512)"), Ok(_) => crash!(
Err(err) => crash!(1, "{}", err) 1,
"Invalid output size for SHA3 (expected 224, 256, 384, or 512)"
),
Err(err) => crash!(1, "{}", err),
},
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<Digest>, 224), "sha3-224sum" => ("SHA3-224", Box::new(Sha3_224::new()) as Box<Digest>, 224),
"sha3-256sum" => ("SHA3-256", Box::new(Sha3_256::new()) as Box<Digest>, 256), "sha3-256sum" => ("SHA3-256", Box::new(Sha3_256::new()) as Box<Digest>, 256),
"sha3-384sum" => ("SHA3-384", Box::new(Sha3_384::new()) as Box<Digest>, 384), "sha3-384sum" => ("SHA3-384", Box::new(Sha3_384::new()) as Box<Digest>, 384),
"sha3-512sum" => ("SHA3-512", Box::new(Sha3_512::new()) as Box<Digest>, 512), "sha3-512sum" => ("SHA3-512", Box::new(Sha3_512::new()) as Box<Digest>, 512),
"shake128sum" => { "shake128sum" => match matches.opt_str("bits") {
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<Digest>, bits), Ok(bits) => ("SHAKE128", Box::new(Shake128::new()) as Box<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<Digest>, bits), Ok(bits) => ("SHAKE256", Box::new(Shake256::new()) as Box<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() { crash!(1, "You cannot combine multiple hash algorithms!") }; if alg.is_some() {
crash!(1, "You cannot combine multiple hash algorithms!")
};
name = n; name = n;
alg = Some(val); alg = Some(val);
output_bits = bits output_bits = bits
}; };
if matches.opt_present("md5") { set_or_crash("MD5", Box::new(Md5::new()), 128) } if matches.opt_present("md5") {
if matches.opt_present("sha1") { set_or_crash("SHA1", Box::new(Sha1::new()), 160) } set_or_crash("MD5", Box::new(Md5::new()), 128)
if matches.opt_present("sha224") { set_or_crash("SHA224", Box::new(Sha224::new()), 224) } }
if matches.opt_present("sha256") { set_or_crash("SHA256", Box::new(Sha256::new()), 256) } if matches.opt_present("sha1") {
if matches.opt_present("sha384") { set_or_crash("SHA384", Box::new(Sha384::new()), 384) } set_or_crash("SHA1", Box::new(Sha1::new()), 160)
if matches.opt_present("sha512") { set_or_crash("SHA512", Box::new(Sha512::new()), 512) } }
if matches.opt_present("sha224") {
set_or_crash("SHA224", Box::new(Sha224::new()), 224)
}
if matches.opt_present("sha256") {
set_or_crash("SHA256", Box::new(Sha256::new()), 256)
}
if matches.opt_present("sha384") {
set_or_crash("SHA384", Box::new(Sha384::new()), 384)
}
if matches.opt_present("sha512") {
set_or_crash("SHA512", Box::new(Sha512::new()), 512)
}
if matches.opt_present("sha3") { if matches.opt_present("sha3") {
match matches.opt_str("bits") { 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) => set_or_crash("SHA3-224", Box::new(Sha3_224::new()) as Box<Digest>, 224), Ok(224) => set_or_crash(
Ok(256) => set_or_crash("SHA3-256", Box::new(Sha3_256::new()) as Box<Digest>, 256), "SHA3-224",
Ok(384) => set_or_crash("SHA3-384", Box::new(Sha3_384::new()) as Box<Digest>, 384), Box::new(Sha3_224::new()) as Box<Digest>,
Ok(512) => set_or_crash("SHA3-512", Box::new(Sha3_512::new()) as Box<Digest>, 512), 224,
Ok(_) => crash!(1, "Invalid output size for SHA3 (expected 224, 256, 384, or 512)"), ),
Err(err) => crash!(1, "{}", err) Ok(256) => set_or_crash(
"SHA3-256",
Box::new(Sha3_256::new()) as Box<Digest>,
256,
),
Ok(384) => set_or_crash(
"SHA3-384",
Box::new(Sha3_384::new()) as Box<Digest>,
384,
),
Ok(512) => set_or_crash(
"SHA3-512",
Box::new(Sha3_512::new()) as Box<Digest>,
512,
),
Ok(_) => crash!(
1,
"Invalid output size for SHA3 (expected 224, 256, 384, or 512)"
),
Err(err) => crash!(1, "{}", err),
}, },
None => crash!(1, "--bits required for SHA3") None => crash!(1, "--bits required for SHA3"),
} }
} }
if matches.opt_present("sha3-224") { set_or_crash("SHA3-224", Box::new(Sha3_224::new()), 224) } if matches.opt_present("sha3-224") {
if matches.opt_present("sha3-256") { set_or_crash("SHA3-256", Box::new(Sha3_256::new()), 256) } set_or_crash("SHA3-224", Box::new(Sha3_224::new()), 224)
if matches.opt_present("sha3-384") { set_or_crash("SHA3-384", Box::new(Sha3_384::new()), 384) } }
if matches.opt_present("sha3-512") { set_or_crash("SHA3-512", Box::new(Sha3_512::new()), 512) } if matches.opt_present("sha3-256") {
set_or_crash("SHA3-256", Box::new(Sha3_256::new()), 256)
}
if matches.opt_present("sha3-384") {
set_or_crash("SHA3-384", Box::new(Sha3_384::new()), 384)
}
if matches.opt_present("sha3-512") {
set_or_crash("SHA3-512", Box::new(Sha3_512::new()), 512)
}
if matches.opt_present("shake128") { if matches.opt_present("shake128") {
match matches.opt_str("bits") { 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) => set_or_crash("SHAKE128", Box::new(Shake128::new()), bits), Ok(bits) => set_or_crash("SHAKE128", Box::new(Shake128::new()), 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"),
} }
} }
if matches.opt_present("shake256") { if matches.opt_present("shake256") {
match matches.opt_str("bits") { 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) => set_or_crash("SHAKE256", Box::new(Shake256::new()), bits), Ok(bits) => set_or_crash("SHAKE256", Box::new(Shake256::new()), 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"),
} }
} }
} }
if alg.is_none() { crash!(1, "You must specify hash algorithm!") }; if alg.is_none() {
crash!(1, "You must specify hash algorithm!")
};
(name, alg.unwrap(), output_bits) (name, alg.unwrap(), output_bits)
} }
} }
@ -164,14 +203,52 @@ pub fn uumain(args: Vec<String>) -> i32 {
let binary_flag_default = cfg!(windows); let binary_flag_default = cfg!(windows);
let mut opts = getopts::Options::new(); let mut opts = getopts::Options::new();
opts.optflag("b", "binary", &format!("read in binary mode{}", if binary_flag_default { " (default)" } else { "" })); opts.optflag(
"b",
"binary",
&format!(
"read in binary mode{}",
if binary_flag_default {
" (default)"
} else {
""
}
),
);
opts.optflag("c", "check", "read hashsums from the FILEs and check them"); opts.optflag("c", "check", "read hashsums from the FILEs and check them");
opts.optflag("", "tag", "create a BSD-style checksum"); opts.optflag("", "tag", "create a BSD-style checksum");
opts.optflag("t", "text", &format!("read in text mode{}", if binary_flag_default { "" } else { " (default)" })); opts.optflag(
opts.optflag("q", "quiet", "don't print OK for each successfully verified file"); "t",
opts.optflag("s", "status", "don't output anything, status code shows success"); "text",
opts.optflag("", "strict", "exit non-zero for improperly formatted checksum lines"); &format!(
opts.optflag("w", "warn", "warn about improperly formatted checksum lines"); "read in text mode{}",
if binary_flag_default {
""
} else {
" (default)"
}
),
);
opts.optflag(
"q",
"quiet",
"don't print OK for each successfully verified file",
);
opts.optflag(
"s",
"status",
"don't output anything, status code shows success",
);
opts.optflag(
"",
"strict",
"exit non-zero for improperly formatted checksum lines",
);
opts.optflag(
"w",
"warn",
"warn about improperly formatted checksum lines",
);
opts.optflag("h", "help", "display this help and exit"); opts.optflag("h", "help", "display this help and exit");
opts.optflag("V", "version", "output version information and exit"); opts.optflag("V", "version", "output version information and exit");
@ -187,16 +264,29 @@ pub fn uumain(args: Vec<String>) -> i32 {
opts.optflag("", "sha3-256", "work with SHA3-256"); opts.optflag("", "sha3-256", "work with SHA3-256");
opts.optflag("", "sha3-384", "work with SHA3-384"); opts.optflag("", "sha3-384", "work with SHA3-384");
opts.optflag("", "sha3-512", "work with SHA3-512"); opts.optflag("", "sha3-512", "work with SHA3-512");
opts.optflag("", "shake128", "work with SHAKE128 using BITS for the output size"); opts.optflag(
opts.optflag("", "shake256", "work with SHAKE256 using BITS for the output size"); "",
"shake128",
"work with SHAKE128 using BITS for the output size",
);
opts.optflag(
"",
"shake256",
"work with SHAKE256 using BITS for the output size",
);
} }
// Needed for variable-length output sums (e.g. SHAKE) // Needed for variable-length output sums (e.g. SHAKE)
opts.optopt("", "bits", "set the size of the output (only for SHAKE)", "BITS"); opts.optopt(
"",
"bits",
"set the size of the output (only for SHAKE)",
"BITS",
);
let matches = match opts.parse(&args[1..]) { let matches = match opts.parse(&args[1..]) {
Ok(m) => m, Ok(m) => m,
Err(f) => crash!(1, "{}", f) Err(f) => crash!(1, "{}", f),
}; };
if matches.opt_present("help") { if matches.opt_present("help") {
@ -211,7 +301,13 @@ pub fn uumain(args: Vec<String>) -> i32 {
if binary_flag && text_flag { if binary_flag && text_flag {
crash!(1, "cannot set binary and text mode at the same time"); crash!(1, "cannot set binary and text mode at the same time");
} }
let binary = if binary_flag { true } else if text_flag { false } else { binary_flag_default }; let binary = if binary_flag {
true
} else if text_flag {
false
} else {
binary_flag_default
};
let check = matches.opt_present("check"); let check = matches.opt_present("check");
let tag = matches.opt_present("tag"); let tag = matches.opt_present("tag");
let status = matches.opt_present("status"); let status = matches.opt_present("status");
@ -219,13 +315,25 @@ pub fn uumain(args: Vec<String>) -> i32 {
let strict = matches.opt_present("strict"); let strict = matches.opt_present("strict");
let warn = matches.opt_present("warn") && !status; let warn = matches.opt_present("warn") && !status;
let files = if matches.free.is_empty() { let files = if matches.free.is_empty() {
vec!("-".to_owned()) vec!["-".to_owned()]
} else { } else {
matches.free matches.free
}; };
match hashsum(name, algo, files, binary, check, tag, status, quiet, strict, warn, bits) { match hashsum(
name,
algo,
files,
binary,
check,
tag,
status,
quiet,
strict,
warn,
bits,
) {
Ok(()) => return 0, Ok(()) => return 0,
Err(e) => return e Err(e) => return e,
} }
} }
@ -240,90 +348,107 @@ fn usage(program: &str, binary_name: &str, opts: &getopts::Options) {
let spec = if is_custom_binary(binary_name) { let spec = if is_custom_binary(binary_name) {
format!(" {} [OPTION]... [FILE]...", program) format!(" {} [OPTION]... [FILE]...", program)
} else { } else {
format!(" {} {{--md5|--sha1|--sha224|--sha256|--sha384|--sha512|\ format!(
" {} {{--md5|--sha1|--sha224|--sha256|--sha384|--sha512|\
--sha3|--sha3-224|--sha3-256|--sha3-384|--sha3-512|\ --sha3|--sha3-224|--sha3-256|--sha3-384|--sha3-512|\
--shake128|--shake256}} [OPTION]... [FILE]...", program) --shake128|--shake256}} [OPTION]... [FILE]...",
program
)
}; };
let msg = format!("{} {} let msg = format!(
"{} {}
Usage: Usage:
{} {}
Compute and check message digests.", NAME, VERSION, spec); Compute and check message digests.",
NAME, VERSION, spec
);
print!("{}", opts.usage(&msg)); print!("{}", opts.usage(&msg));
} }
fn hashsum(algoname: &str, mut digest: Box<Digest>, files: Vec<String>, binary: bool, check: bool, tag: bool, status: bool, quiet: bool, strict: bool, warn: bool, output_bits: usize) -> Result<(), i32> { fn hashsum(
algoname: &str,
mut digest: Box<Digest>,
files: Vec<String>,
binary: bool,
check: bool,
tag: bool,
status: bool,
quiet: bool,
strict: bool,
warn: bool,
output_bits: usize,
) -> Result<(), i32> {
let mut bad_format = 0; let mut bad_format = 0;
let mut failed = 0; let mut failed = 0;
let binary_marker = if binary { let binary_marker = if binary { "*" } else { " " };
"*"
} else {
" "
};
for filename in &files { for filename in &files {
let filename: &str = filename; let filename: &str = filename;
let stdin_buf; let stdin_buf;
let file_buf; let file_buf;
let mut file = BufReader::new( let mut file = BufReader::new(if filename == "-" {
if filename == "-" {
stdin_buf = stdin(); stdin_buf = stdin();
Box::new(stdin_buf) as Box<Read> Box::new(stdin_buf) as Box<Read>
} else { } else {
file_buf = safe_unwrap!(File::open(filename)); file_buf = safe_unwrap!(File::open(filename));
Box::new(file_buf) as Box<Read> Box::new(file_buf) as Box<Read>
} });
);
if check { if check {
// Set up Regexes for line validation and parsing // Set up Regexes for line validation and parsing
let bytes = digest.output_bits() / 4; let bytes = digest.output_bits() / 4;
let gnu_re = safe_unwrap!( let gnu_re = safe_unwrap!(Regex::new(&format!(
Regex::new(
&format!(
r"^(?P<digest>[a-fA-F0-9]{{{}}}) (?P<binary>[ \*])(?P<fileName>.*)", r"^(?P<digest>[a-fA-F0-9]{{{}}}) (?P<binary>[ \*])(?P<fileName>.*)",
bytes bytes
) )));
) let bsd_re = safe_unwrap!(Regex::new(&format!(
);
let bsd_re = safe_unwrap!(
Regex::new(
&format!(
r"^{algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{{{digest_size}}})", r"^{algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{{{digest_size}}})",
algorithm = algoname, algorithm = algoname,
digest_size = bytes digest_size = bytes
) )));
)
);
let buffer = file; let buffer = file;
for (i, line) in buffer.lines().enumerate() { for (i, line) in buffer.lines().enumerate() {
let line = safe_unwrap!(line); let line = safe_unwrap!(line);
let (ck_filename, sum, binary_check) = match gnu_re.captures(&line) { let (ck_filename, sum, binary_check) = match gnu_re.captures(&line) {
Some(caps) => (caps.name("fileName").unwrap().as_str(), Some(caps) => (
caps.name("fileName").unwrap().as_str(),
caps.name("digest").unwrap().as_str().to_ascii_lowercase(), caps.name("digest").unwrap().as_str().to_ascii_lowercase(),
caps.name("binary").unwrap().as_str() == "*"), caps.name("binary").unwrap().as_str() == "*",
),
None => match bsd_re.captures(&line) { None => match bsd_re.captures(&line) {
Some(caps) => (caps.name("fileName").unwrap().as_str(), Some(caps) => (
caps.name("fileName").unwrap().as_str(),
caps.name("digest").unwrap().as_str().to_ascii_lowercase(), caps.name("digest").unwrap().as_str().to_ascii_lowercase(),
true), true,
),
None => { None => {
bad_format += 1; bad_format += 1;
if strict { if strict {
return Err(1); return Err(1);
} }
if warn { if warn {
show_warning!("{}: {}: improperly formatted {} checksum line", filename, i + 1, algoname); show_warning!(
"{}: {}: improperly formatted {} checksum line",
filename,
i + 1,
algoname
);
} }
continue; continue;
} }
} },
}; };
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<Read>); let mut ckf = BufReader::new(Box::new(f) as Box<Read>);
let real_sum = safe_unwrap!(digest_reader(&mut digest, &mut ckf, binary_check, output_bits)) let real_sum = safe_unwrap!(digest_reader(
.to_ascii_lowercase(); &mut digest,
&mut ckf,
binary_check,
output_bits
)).to_ascii_lowercase();
if sum == real_sum { if sum == real_sum {
if !quiet { if !quiet {
println!("{}: OK", ck_filename); println!("{}: OK", ck_filename);
@ -358,7 +483,12 @@ fn hashsum(algoname: &str, mut digest: Box<Digest>, files: Vec<String>, binary:
Ok(()) Ok(())
} }
fn digest_reader<'a, T: Read>(digest: &mut Box<Digest+'a>, reader: &mut BufReader<T>, binary: bool, output_bits: usize) -> io::Result<String> { fn digest_reader<'a, T: Read>(
digest: &mut Box<Digest + 'a>,
reader: &mut BufReader<T>,
binary: bool,
output_bits: usize,
) -> io::Result<String> {
digest.reset(); digest.reset();
// Digest file, do not hold too much in memory at any given moment // Digest file, do not hold too much in memory at any given moment
@ -368,11 +498,13 @@ fn digest_reader<'a, T: Read>(digest: &mut Box<Digest+'a>, reader: &mut BufReade
let mut looking_for_newline = false; let mut looking_for_newline = false;
loop { loop {
match reader.read_to_end(&mut buffer) { match reader.read_to_end(&mut buffer) {
Ok(0) => { break; }, Ok(0) => {
break;
}
Ok(nread) => { Ok(nread) => {
if windows && !binary { if windows && !binary {
// Windows text mode returns '\n' when reading '\r\n' // Windows text mode returns '\n' when reading '\r\n'
for i in 0 .. nread { for i in 0..nread {
if looking_for_newline { if looking_for_newline {
if buffer[i] != ('\n' as u8) { if buffer[i] != ('\n' as u8) {
vec.push('\r' as u8); vec.push('\r' as u8);
@ -392,8 +524,8 @@ fn digest_reader<'a, T: Read>(digest: &mut Box<Digest+'a>, reader: &mut BufReade
} else { } else {
digest.input(&buffer[..nread]); digest.input(&buffer[..nread]);
} }
}, }
Err(e) => return Err(e) Err(e) => return Err(e),
} }
} }
if windows && looking_for_newline { if windows && looking_for_newline {

View file

@ -14,7 +14,7 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::io::{BufRead, BufReader, Read, stdin}; use std::io::{stdin, BufRead, BufReader, Read};
use std::fs::File; use std::fs::File;
use std::path::Path; use std::path::Path;
use std::str::from_utf8; use std::str::from_utf8;
@ -47,13 +47,26 @@ pub fn uumain(args: Vec<String>) -> i32 {
// handle obsolete -number syntax // handle obsolete -number syntax
let new_args = match obsolete(&args[0..]) { let new_args = match obsolete(&args[0..]) {
(args, Some(n)) => { settings.mode = FilterMode::Lines(n); args }, (args, Some(n)) => {
(args, None) => args settings.mode = FilterMode::Lines(n);
args
}
(args, None) => args,
}; };
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP) let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optopt("c", "bytes", "Print the first K bytes. With the leading '-', print all but the last K bytes", "[-]K") .optopt(
.optopt("n", "lines", "Print the first K lines. With the leading '-', print all but the last K lines", "[-]K") "c",
"bytes",
"Print the first K bytes. With the leading '-', print all but the last K bytes",
"[-]K",
)
.optopt(
"n",
"lines",
"Print the first K lines. With the leading '-', print all but the last K lines",
"[-]K",
)
.optflag("q", "quiet", "never print headers giving file names") .optflag("q", "quiet", "never print headers giving file names")
.optflag("v", "verbose", "always print headers giving file names") .optflag("v", "verbose", "always print headers giving file names")
.optflag("h", "help", "display this help and exit") .optflag("h", "help", "display this help and exit")
@ -70,7 +83,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
return 1; return 1;
} }
match n.parse::<usize>() { match n.parse::<usize>() {
Ok(m) => { settings.mode = FilterMode::Lines(m) } Ok(m) => settings.mode = FilterMode::Lines(m),
Err(e) => { Err(e) => {
show_error!("invalid line count '{}': {}", n, e); show_error!("invalid line count '{}': {}", n, e);
return 1; return 1;
@ -80,13 +93,13 @@ pub fn uumain(args: Vec<String>) -> i32 {
None => match matches.opt_str("c") { None => match matches.opt_str("c") {
Some(count) => match count.parse::<usize>() { Some(count) => match count.parse::<usize>() {
Ok(m) => settings.mode = FilterMode::Bytes(m), Ok(m) => settings.mode = FilterMode::Bytes(m),
Err(e)=> { Err(e) => {
show_error!("invalid byte count '{}': {}", count, e); show_error!("invalid byte count '{}': {}", count, e);
return 1; return 1;
} }
}, },
None => {} None => {}
} },
}; };
let quiet = matches.opt_present("q"); let quiet = matches.opt_present("q");
@ -115,7 +128,9 @@ pub fn uumain(args: Vec<String>) -> i32 {
for file in &files { for file in &files {
if settings.verbose { if settings.verbose {
if !firstime { println!(""); } if !firstime {
println!("");
}
println!("==> {} <==", file); println!("==> {} <==", file);
} }
firstime = false; firstime = false;
@ -147,21 +162,24 @@ fn obsolete(options: &[String]) -> (Vec<String>, Option<usize>) {
if current.len() > 1 && current[0] == '-' as u8 { if current.len() > 1 && current[0] == '-' as u8 {
let len = current.len(); let len = current.len();
for pos in 1 .. len { for pos in 1..len {
// Ensure that the argument is only made out of digits // Ensure that the argument is only made out of digits
if !(current[pos] as char).is_numeric() { break; } if !(current[pos] as char).is_numeric() {
break;
}
// If this is the last number // If this is the last number
if pos == len - 1 { if pos == len - 1 {
options.remove(a); options.remove(a);
let number: Option<usize> = from_utf8(&current[1..len]).unwrap().parse::<usize>().ok(); let number: Option<usize> =
from_utf8(&current[1..len]).unwrap().parse::<usize>().ok();
return (options, Some(number.unwrap())); return (options, Some(number.unwrap()));
} }
} }
} }
a += 1; a += 1;
}; }
(options, None) (options, None)
} }
@ -169,16 +187,12 @@ 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) => { FilterMode::Bytes(count) => for byte in reader.bytes().take(count) {
for byte in reader.bytes().take(count) {
print!("{}", byte.unwrap() as char); print!("{}", byte.unwrap() as char);
}
}, },
FilterMode::Lines(count) => { FilterMode::Lines(count) => for line in reader.lines().take(count) {
for line in reader.lines().take(count) {
println!("{}", line.unwrap()); println!("{}", line.unwrap());
} },
}
} }
true true
} }

View file

@ -27,13 +27,12 @@ pub enum Mode {
} }
// currently rust libc interface doesn't include gethostid // currently rust libc interface doesn't include gethostid
extern { extern "C" {
pub fn gethostid() -> c_long; pub fn gethostid() -> c_long;
} }
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
new_coreopts!(SYNTAX, SUMMARY, LONG_HELP) new_coreopts!(SYNTAX, SUMMARY, LONG_HELP).parse(args);
.parse(args);
hostid(); hostid();
0 0
} }
@ -45,7 +44,7 @@ fn hostid() {
* is a no-op unless unsigned int is wider than 32 bits. * is a no-op unless unsigned int is wider than 32 bits.
*/ */
let mut result:c_long; let mut result: c_long;
unsafe { unsafe {
result = gethostid(); result = gethostid();
} }

View file

@ -9,10 +9,10 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
extern crate getopts;
extern crate libc; extern crate libc;
#[cfg(windows)] #[cfg(windows)]
extern crate winapi; extern crate winapi;
extern crate getopts;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
@ -25,7 +25,7 @@ use std::net::ToSocketAddrs;
use getopts::Matches; use getopts::Matches;
#[cfg(windows)] #[cfg(windows)]
use winapi::um::winsock2::{GetHostNameW, WSAStartup, WSACleanup}; use winapi::um::winsock2::{GetHostNameW, WSACleanup, WSAStartup};
#[cfg(windows)] #[cfg(windows)]
use winapi::um::sysinfoapi::{ComputerNamePhysicalDnsHostname, SetComputerNameExW}; use winapi::um::sysinfoapi::{ComputerNamePhysicalDnsHostname, SetComputerNameExW};
#[cfg(windows)] #[cfg(windows)]
@ -63,8 +63,10 @@ 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("d", "domain", "Display the name of the DNS domain if possible")
.optflag("i", "ip-address", "Display the network address(es) of the host") .optflag("i", "ip-address", "Display the network address(es) of the host")
.optflag("f", "fqdn", "Display the FQDN (Fully Qualified Domain Name) (default)") // TODO: support --long // TODO: support --long
.optflag("s", "short", "Display the short hostname (the portion before the first dot) if possible") .optflag("f", "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() {
@ -148,7 +150,10 @@ fn xgethostname() -> io::Result<String> {
let namelen = 256; let namelen = 256;
let mut name: Vec<u8> = repeat(0).take(namelen).collect(); let mut name: Vec<u8> = repeat(0).take(namelen).collect();
let err = unsafe { let err = unsafe {
gethostname(name.as_mut_ptr() as *mut libc::c_char, namelen as libc::size_t) gethostname(
name.as_mut_ptr() as *mut libc::c_char,
namelen as libc::size_t,
)
}; };
if err == 0 { if err == 0 {
@ -158,7 +163,10 @@ fn xgethostname() -> io::Result<String> {
last_char += 1; last_char += 1;
} }
Ok(CStr::from_bytes_with_nul(&name[..last_char]).unwrap().to_string_lossy().into_owned()) Ok(CStr::from_bytes_with_nul(&name[..last_char])
.unwrap()
.to_string_lossy()
.into_owned())
} else { } else {
Err(io::Error::last_os_error()) Err(io::Error::last_os_error())
} }
@ -168,9 +176,7 @@ fn xgethostname() -> io::Result<String> {
fn xgethostname() -> io::Result<String> { fn xgethostname() -> io::Result<String> {
let namelen = 256; let namelen = 256;
let mut name: Vec<u16> = repeat(0).take(namelen).collect(); let mut name: Vec<u16> = repeat(0).take(namelen).collect();
let err = unsafe { let err = unsafe { GetHostNameW(name.as_mut_ptr(), namelen as libc::c_int) };
GetHostNameW(name.as_mut_ptr(), namelen as libc::c_int)
};
if err == 0 { if err == 0 {
Ok(String::from_wide_null(&name)) Ok(String::from_wide_null(&name))
@ -183,9 +189,7 @@ fn xgethostname() -> io::Result<String> {
fn xsethostname(name: &str) -> io::Result<()> { fn xsethostname(name: &str) -> io::Result<()> {
let vec_name: Vec<libc::c_char> = name.bytes().map(|c| c as libc::c_char).collect(); let vec_name: Vec<libc::c_char> = name.bytes().map(|c| c as libc::c_char).collect();
let err = unsafe { let err = unsafe { sethostname(vec_name.as_ptr(), vec_name.len() as _) };
sethostname(vec_name.as_ptr(), vec_name.len() as _)
};
if err != 0 { if err != 0 {
Err(io::Error::last_os_error()) Err(io::Error::last_os_error())
@ -200,9 +204,7 @@ fn xsethostname(name: &str) -> io::Result<()> {
let wide_name = OsStr::new(name).to_wide_null(); let wide_name = OsStr::new(name).to_wide_null();
let err = unsafe { let err = unsafe { SetComputerNameExW(ComputerNamePhysicalDnsHostname, wide_name.as_ptr()) };
SetComputerNameExW(ComputerNamePhysicalDnsHostname, wide_name.as_ptr())
};
if err == 0 { if err == 0 {
// NOTE: the above is correct, failure is when the function returns 0 apparently // NOTE: the above is correct, failure is when the function returns 0 apparently

View file

@ -1,5 +1,4 @@
#![crate_name = "uu_id"] #![crate_name = "uu_id"]
// This file is part of the uutils coreutils package. // This file is part of the uutils coreutils package.
// //
// (c) Alan Andrade <alan.andradec@gmail.com> // (c) Alan Andrade <alan.andradec@gmail.com>
@ -12,7 +11,6 @@
// http://ftp-archive.freebsd.org/mirror/FreeBSD-Archive/old-releases/i386/1.0-RELEASE/ports/shellutils/src/id.c // http://ftp-archive.freebsd.org/mirror/FreeBSD-Archive/old-releases/i386/1.0-RELEASE/ports/shellutils/src/id.c
// http://www.opensource.apple.com/source/shell_cmds/shell_cmds-118/id/id.c // http://www.opensource.apple.com/source/shell_cmds/shell_cmds-118/id/id.c
// //
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(dead_code)] #![allow(dead_code)]
@ -20,8 +18,8 @@
extern crate uucore; extern crate uucore;
pub use uucore::libc; pub use uucore::libc;
use uucore::libc::{getlogin, uid_t}; use uucore::libc::{getlogin, uid_t};
use uucore::entries::{self, Passwd, Group, Locate}; use uucore::entries::{self, Group, Locate, Passwd};
use uucore::process::{getgid, getuid, getegid, geteuid}; use uucore::process::{getegid, geteuid, getgid, getuid};
use std::ffi::CStr; use std::ffi::CStr;
macro_rules! cstr2cow { macro_rules! cstr2cow {
@ -33,7 +31,7 @@ macro_rules! cstr2cow {
#[cfg(not(target_os = "linux"))] #[cfg(not(target_os = "linux"))]
mod audit { mod audit {
pub use std::mem::uninitialized; pub use std::mem::uninitialized;
use super::libc::{uid_t, pid_t, c_int, c_uint, uint64_t, dev_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;
@ -74,14 +72,18 @@ static SUMMARY: &'static str = "Print user and group information for the specifi
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, "");
opts.optflag("A", opts.optflag(
"A",
"", "",
"Display the process audit (not available on Linux)"); "Display the process audit (not available on Linux)",
);
opts.optflag("G", "", "Display the different group IDs"); opts.optflag("G", "", "Display the different group IDs");
opts.optflag("g", "", "Display the effective group ID as a number"); opts.optflag("g", "", "Display the effective group ID as a number");
opts.optflag("n", opts.optflag(
"n",
"", "",
"Display the name of the user or group ID for the -G, -g and -u options"); "Display the name of the user or group ID for the -G, -g and -u options",
);
opts.optflag("P", "", "Display the id as a password file entry"); opts.optflag("P", "", "Display the id as a password file entry");
opts.optflag("p", "", "Make the output human-readable"); opts.optflag("p", "", "Make the output human-readable");
opts.optflag("r", "", "Display the real ID for the -g and -u options"); opts.optflag("r", "", "Display the real ID for the -g and -u options");
@ -109,52 +111,56 @@ pub fn uumain(args: Vec<String>) -> i32 {
let rflag = matches.opt_present("r"); let rflag = matches.opt_present("r");
if gflag { if gflag {
let id = possible_pw.map(|p| p.gid()).unwrap_or(if rflag { let id = possible_pw
getgid() .map(|p| p.gid())
} else { .unwrap_or(if rflag { getgid() } else { getegid() });
getegid() println!(
}); "{}",
println!("{}",
if nflag { if nflag {
entries::gid2grp(id).unwrap_or(id.to_string()) entries::gid2grp(id).unwrap_or(id.to_string())
} else { } else {
id.to_string() id.to_string()
}); }
);
return 0; return 0;
} }
if uflag { if uflag {
let id = possible_pw.map(|p| p.uid()).unwrap_or(if rflag { let id = possible_pw
getuid() .map(|p| p.uid())
} else { .unwrap_or(if rflag { getuid() } else { geteuid() });
geteuid() println!(
}); "{}",
println!("{}",
if nflag { if nflag {
entries::uid2usr(id).unwrap_or(id.to_string()) entries::uid2usr(id).unwrap_or(id.to_string())
} else { } else {
id.to_string() id.to_string()
}); }
);
return 0; return 0;
} }
if matches.opt_present("G") { if matches.opt_present("G") {
println!("{}", println!(
"{}",
if nflag { if nflag {
possible_pw.map(|p| p.belongs_to()) possible_pw
.map(|p| p.belongs_to())
.unwrap_or(entries::get_groups().unwrap()) .unwrap_or(entries::get_groups().unwrap())
.iter() .iter()
.map(|&id| entries::gid2grp(id).unwrap()) .map(|&id| entries::gid2grp(id).unwrap())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(" ") .join(" ")
} else { } else {
possible_pw.map(|p| p.belongs_to()) possible_pw
.map(|p| p.belongs_to())
.unwrap_or(entries::get_groups().unwrap()) .unwrap_or(entries::get_groups().unwrap())
.iter() .iter()
.map(|&id| id.to_string()) .map(|&id| id.to_string())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(" ") .join(" ")
}); }
);
return 0; return 0;
} }
@ -180,8 +186,14 @@ pub fn uumain(args: Vec<String>) -> i32 {
fn pretty(possible_pw: Option<Passwd>) { fn pretty(possible_pw: Option<Passwd>) {
if let Some(p) = possible_pw { if let Some(p) = possible_pw {
print!("uid\t{}\ngroups\t", p.name()); print!("uid\t{}\ngroups\t", p.name());
println!("{}", println!(
p.belongs_to().iter().map(|&gr| entries::gid2grp(gr).unwrap()).collect::<Vec<_>>().join(" ")); "{}",
p.belongs_to()
.iter()
.map(|&gr| entries::gid2grp(gr).unwrap())
.collect::<Vec<_>>()
.join(" ")
);
} else { } else {
let login = cstr2cow!(getlogin() as *const _); let login = cstr2cow!(getlogin() as *const _);
let rid = getuid(); let rid = getuid();
@ -212,13 +224,15 @@ fn pretty(possible_pw: Option<Passwd>) {
} }
} }
println!("groups\t{}", println!(
"groups\t{}",
entries::get_groups() entries::get_groups()
.unwrap() .unwrap()
.iter() .iter()
.map(|&gr| entries::gid2grp(gr).unwrap()) .map(|&gr| entries::gid2grp(gr).unwrap())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(" ")); .join(" ")
);
} }
} }
@ -227,7 +241,8 @@ fn pline(possible_uid: Option<uid_t>) {
let uid = possible_uid.unwrap_or(getuid()); let uid = possible_uid.unwrap_or(getuid());
let pw = Passwd::locate(uid).unwrap(); let pw = Passwd::locate(uid).unwrap();
println!("{}:{}:{}:{}:{}:{}:{}:{}:{}:{}", println!(
"{}:{}:{}:{}:{}:{}:{}:{}:{}:{}",
pw.name(), pw.name(),
pw.user_passwd(), pw.user_passwd(),
pw.uid(), pw.uid(),
@ -237,7 +252,8 @@ fn pline(possible_uid: Option<uid_t>) {
pw.expiration(), pw.expiration(),
pw.user_info(), pw.user_info(),
pw.user_dir(), pw.user_dir(),
pw.user_shell()); pw.user_shell()
);
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -245,14 +261,16 @@ fn pline(possible_uid: Option<uid_t>) {
let uid = possible_uid.unwrap_or(getuid()); let uid = possible_uid.unwrap_or(getuid());
let pw = Passwd::locate(uid).unwrap(); let pw = Passwd::locate(uid).unwrap();
println!("{}:{}:{}:{}:{}:{}:{}", println!(
"{}:{}:{}:{}:{}:{}:{}",
pw.name(), pw.name(),
pw.user_passwd(), pw.user_passwd(),
pw.uid(), pw.uid(),
pw.gid(), pw.gid(),
pw.user_info(), pw.user_info(),
pw.user_dir(), pw.user_dir(),
pw.user_shell()); pw.user_shell()
);
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -275,7 +293,9 @@ fn auditid() {
} }
fn id_print(possible_pw: Option<Passwd>, p_euid: bool, p_egid: bool) { fn id_print(possible_pw: Option<Passwd>, p_euid: bool, p_egid: bool) {
let (uid, gid) = possible_pw.map(|p| (p.uid(), p.gid())).unwrap_or((getuid(), getgid()));; let (uid, gid) = possible_pw
.map(|p| (p.uid(), p.gid()))
.unwrap_or((getuid(), getgid()));
let groups = Passwd::locate(uid).unwrap().belongs_to(); let groups = Passwd::locate(uid).unwrap().belongs_to();
@ -292,9 +312,12 @@ fn id_print(possible_pw: Option<Passwd>, p_euid: bool, p_egid: bool) {
print!(" egid={}({})", euid, entries::gid2grp(egid).unwrap()); print!(" egid={}({})", euid, entries::gid2grp(egid).unwrap());
} }
println!(" groups={}", println!(
groups.iter() " groups={}",
groups
.iter()
.map(|&gr| format!("{}({})", gr, entries::gid2grp(gr).unwrap())) .map(|&gr| format!("{}({})", gr, entries::gid2grp(gr).unwrap()))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(",")); .join(",")
);
} }

View file

@ -33,7 +33,7 @@ pub struct Behaviour {
main_function: MainFunction, main_function: MainFunction,
specified_mode: Option<u32>, specified_mode: Option<u32>,
suffix: String, suffix: String,
verbose: bool verbose: bool,
} }
#[derive(Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
@ -41,7 +41,7 @@ pub enum MainFunction {
/// Create directories /// Create directories
Directory, Directory,
/// Install files to locations (primary functionality) /// Install files to locations (primary functionality)
Standard Standard,
} }
impl Behaviour { impl Behaviour {
@ -49,7 +49,7 @@ impl Behaviour {
pub fn mode(&self) -> u32 { pub fn mode(&self) -> u32 {
match self.specified_mode { match self.specified_mode {
Some(x) => x, Some(x) => x,
None => DEFAULT_MODE None => DEFAULT_MODE,
} }
} }
} }
@ -84,12 +84,8 @@ pub fn uumain(args: Vec<String>) -> i32 {
}; };
match behaviour.main_function { match behaviour.main_function {
MainFunction::Directory => { MainFunction::Directory => directory(&paths[..], behaviour),
directory(&paths[..], behaviour) MainFunction::Standard => standard(&paths[..], behaviour),
},
MainFunction::Standard => {
standard(&paths[..], behaviour)
}
} }
} }
@ -98,8 +94,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
/// Returns a getopts::Options struct. /// Returns a getopts::Options struct.
/// ///
fn parse_opts(args: Vec<String>) -> getopts::Matches { fn parse_opts(args: Vec<String>) -> getopts::Matches {
let syntax = format!("SOURCE DEST let syntax = format!(
{} SOURCE... DIRECTORY", NAME); "SOURCE DEST
{} SOURCE... DIRECTORY",
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("", "backup", "(unimplemented) make a backup of each existing destination\n \
@ -208,18 +207,19 @@ fn behaviour(matches: &getopts::Matches) -> Result<Behaviour, i32> {
let specified_mode: Option<u32> = if matches.opt_present("mode") { let specified_mode: Option<u32> = if matches.opt_present("mode") {
match matches.opt_str("mode") { match matches.opt_str("mode") {
Some(x) => { Some(x) => match mode::parse(&x[..], considering_dir) {
match mode::parse(&x[..], considering_dir) {
Ok(y) => Some(y), Ok(y) => Some(y),
Err(err) => { Err(err) => {
show_error!("Invalid mode string: {}", err); show_error!("Invalid mode string: {}", err);
return Err(1); return Err(1);
} }
}
}, },
None => { None => {
show_error!("option '--mode' requires an argument\n \ show_error!(
Try '{} --help' for more information.", NAME); "option '--mode' requires an argument\n \
Try '{} --help' for more information.",
NAME
);
return Err(1); return Err(1);
} }
} }
@ -231,8 +231,11 @@ fn behaviour(matches: &getopts::Matches) -> Result<Behaviour, i32> {
match matches.opt_str("suffix") { match matches.opt_str("suffix") {
Some(x) => x, Some(x) => x,
None => { None => {
show_error!("option '--suffix' requires an argument\n\ show_error!(
Try '{} --help' for more information.", NAME); "option '--suffix' requires an argument\n\
Try '{} --help' for more information.",
NAME
);
return Err(1); return Err(1);
} }
} }
@ -283,15 +286,18 @@ fn directory(paths: &[PathBuf], b: Behaviour) -> i32 {
show_info!("created directory '{}'", path.display()); show_info!("created directory '{}'", path.display());
} }
} }
if all_successful { 0 } else { 1 } if all_successful {
0
} else {
1
}
} }
} }
/// 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.is_file() || !path.exists() && path.parent().map(|p| p.is_dir()).unwrap_or(true)
! path.exists() && path.parent().map(|p| p.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.
@ -335,8 +341,10 @@ fn copy_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behaviour) -
let targetpath = match sourcepath.as_os_str().to_str() { let targetpath = match sourcepath.as_os_str().to_str() {
Some(name) => target_dir.join(name), Some(name) => target_dir.join(name),
None => { None => {
show_error!("cannot stat {}: No such file or directory", show_error!(
sourcepath.display()); "cannot stat {}: No such file or directory",
sourcepath.display()
);
all_successful = false; all_successful = false;
continue; continue;
@ -346,8 +354,12 @@ fn copy_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behaviour) -
if copy(sourcepath, &targetpath, b).is_err() { if copy(sourcepath, &targetpath, b).is_err() {
all_successful = false; all_successful = false;
} }
}; }
if all_successful { 0 } else { 1 } if all_successful {
0
} else {
1
}
} }
/// Copy a file to another file. /// Copy a file to another file.
@ -361,7 +373,11 @@ fn copy_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behaviour) -
/// _target_ must be a non-directory /// _target_ must be a non-directory
/// ///
fn copy_file_to_file(file: &PathBuf, target: &PathBuf, b: &Behaviour) -> i32 { fn copy_file_to_file(file: &PathBuf, target: &PathBuf, b: &Behaviour) -> i32 {
if copy(file, &target, b).is_err() { 1 } else { 0 } if copy(file, &target, b).is_err() {
1
} else {
0
}
} }
/// Copy one file to a new location, changing metadata. /// Copy one file to a new location, changing metadata.
@ -379,8 +395,12 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behaviour) -> Result<(), ()> {
let io_result = fs::copy(from, to); let io_result = fs::copy(from, to);
if let Err(err) = io_result { if let Err(err) = io_result {
show_error!("install: cannot install {} to {}: {}", show_error!(
from.display(), to.display(), err); "install: cannot install {} to {}: {}",
from.display(),
to.display(),
err
);
return Err(()); return Err(());
} }

View file

@ -410,65 +410,90 @@ pub fn uumain(args: Vec<String>) -> i32 {
"For each pair of input lines with identical join fields, write a line to "For each pair of input lines with identical join fields, write a line to
standard output. The default join field is the first, delimited by blanks. standard output. The default join field is the first, delimited by blanks.
When FILE1 or FILE2 (not both) is -, read standard input.") When FILE1 or FILE2 (not both) is -, read standard input.",
)
.help_message("display this help and exit") .help_message("display this help and exit")
.version_message("display version and exit") .version_message("display version and exit")
.arg(Arg::with_name("a") .arg(
Arg::with_name("a")
.short("a") .short("a")
.takes_value(true) .takes_value(true)
.possible_values(&["1", "2"]) .possible_values(&["1", "2"])
.value_name("FILENUM") .value_name("FILENUM")
.help("also print unpairable lines from file FILENUM, where .help(
FILENUM is 1 or 2, corresponding to FILE1 or FILE2")) "also print unpairable lines from file FILENUM, where
.arg(Arg::with_name("e") FILENUM is 1 or 2, corresponding to FILE1 or FILE2",
),
)
.arg(
Arg::with_name("e")
.short("e") .short("e")
.takes_value(true) .takes_value(true)
.value_name("EMPTY") .value_name("EMPTY")
.help("replace missing input fields with EMPTY")) .help("replace missing input fields with EMPTY"),
.arg(Arg::with_name("i") )
.arg(
Arg::with_name("i")
.short("i") .short("i")
.long("ignore-case") .long("ignore-case")
.help("ignore differences in case when comparing fields")) .help("ignore differences in case when comparing fields"),
.arg(Arg::with_name("j") )
.arg(
Arg::with_name("j")
.short("j") .short("j")
.takes_value(true) .takes_value(true)
.value_name("FIELD") .value_name("FIELD")
.help("equivalent to '-1 FIELD -2 FIELD'")) .help("equivalent to '-1 FIELD -2 FIELD'"),
.arg(Arg::with_name("o") )
.arg(
Arg::with_name("o")
.short("o") .short("o")
.takes_value(true) .takes_value(true)
.value_name("FORMAT") .value_name("FORMAT")
.help("obey FORMAT while constructing output line")) .help("obey FORMAT while constructing output line"),
.arg(Arg::with_name("t") )
.arg(
Arg::with_name("t")
.short("t") .short("t")
.takes_value(true) .takes_value(true)
.value_name("CHAR") .value_name("CHAR")
.help("use CHAR as input and output field separator")) .help("use CHAR as input and output field separator"),
.arg(Arg::with_name("1") )
.arg(
Arg::with_name("1")
.short("1") .short("1")
.takes_value(true) .takes_value(true)
.value_name("FIELD") .value_name("FIELD")
.help("join on this FIELD of file 1")) .help("join on this FIELD of file 1"),
.arg(Arg::with_name("2") )
.arg(
Arg::with_name("2")
.short("2") .short("2")
.takes_value(true) .takes_value(true)
.value_name("FIELD") .value_name("FIELD")
.help("join on this FIELD of file 2")) .help("join on this FIELD of file 2"),
.arg(Arg::with_name("check-order") )
.long("check-order") .arg(Arg::with_name("check-order").long("check-order").help(
.help("check that the input is correctly sorted, \ "check that the input is correctly sorted, \
even if all input lines are pairable")) even if all input lines are pairable",
.arg(Arg::with_name("nocheck-order") ))
.arg(
Arg::with_name("nocheck-order")
.long("nocheck-order") .long("nocheck-order")
.help("do not check that the input is correctly sorted")) .help("do not check that the input is correctly sorted"),
.arg(Arg::with_name("file1") )
.arg(
Arg::with_name("file1")
.required(true) .required(true)
.value_name("FILE1") .value_name("FILE1")
.hidden(true)) .hidden(true),
.arg(Arg::with_name("file2") )
.arg(
Arg::with_name("file2")
.required(true) .required(true)
.value_name("FILE2") .value_name("FILE2")
.hidden(true)) .hidden(true),
)
.get_matches_from(args); .get_matches_from(args);
let keys = parse_field_number_option(matches.value_of("j")); let keys = parse_field_number_option(matches.value_of("j"));

View file

@ -36,7 +36,12 @@ pub fn uumain(args: Vec<String>) -> i32 {
let (args, obs_signal) = handle_obsolete(args); let (args, obs_signal) = handle_obsolete(args);
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP) let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optopt("s", "signal", "specify the <signal> to be sent", "SIGNAL") .optopt("s", "signal", "specify the <signal> to be sent", "SIGNAL")
.optflagopt("l", "list", "list all signal names, or convert one to a name", "LIST") .optflagopt(
"l",
"list",
"list all signal names, or convert one to a name",
"LIST",
)
.optflag("L", "table", "list all signal names in a nice table") .optflag("L", "table", "list all signal names in a nice table")
.parse(args); .parse(args);
@ -49,7 +54,14 @@ pub fn uumain(args: Vec<String>) -> i32 {
}; };
match mode { match mode {
Mode::Kill => return kill(&matches.opt_str("signal").unwrap_or(obs_signal.unwrap_or("9".to_owned())), matches.free), Mode::Kill => {
return kill(
&matches
.opt_str("signal")
.unwrap_or(obs_signal.unwrap_or("9".to_owned())),
matches.free,
)
}
Mode::Table => table(), Mode::Table => table(),
Mode::List => list(matches.opt_str("list")), Mode::List => list(matches.opt_str("list")),
} }
@ -62,7 +74,9 @@ 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 && slice.chars().nth(1).unwrap().is_digit(10) { if slice.chars().next().unwrap() == '-' && slice.len() > 1
&& 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) => {
@ -71,7 +85,7 @@ fn handle_obsolete(mut args: Vec<String>) -> (Vec<String>, Option<String>) {
return (args, Some(val.to_owned())); return (args, Some(val.to_owned()));
} }
} }
Err(_)=> break /* getopts will error out for us */ Err(_) => break, /* getopts will error out for us */
} }
} }
i += 1; i += 1;
@ -89,10 +103,10 @@ fn table() {
} }
for (idx, signal) in ALL_SIGNALS.iter().enumerate() { for (idx, signal) in ALL_SIGNALS.iter().enumerate() {
print!("{0: >#2} {1: <#8}", idx+1, signal.name); print!("{0: >#2} {1: <#8}", idx + 1, signal.name);
//TODO: obtain max signal width here //TODO: obtain max signal width here
if (idx+1) % 7 == 0 { if (idx + 1) % 7 == 0 {
println!(""); println!("");
} }
} }
@ -100,7 +114,9 @@ fn table() {
fn print_signal(signal_name_or_value: &str) { fn print_signal(signal_name_or_value: &str) {
for signal in &ALL_SIGNALS { for signal in &ALL_SIGNALS {
if signal.name == signal_name_or_value || (format!("SIG{}", signal.name)) == signal_name_or_value { if signal.name == signal_name_or_value
|| (format!("SIG{}", signal.name)) == signal_name_or_value
{
println!("{}", signal.value); println!("{}", signal.value);
exit!(EXIT_OK as i32) exit!(EXIT_OK as i32)
} else if signal_name_or_value == signal.value.to_string() { } else if signal_name_or_value == signal.value.to_string() {
@ -138,7 +154,7 @@ fn kill(signalname: &str, pids: std::vec::Vec<String>) -> i32 {
let optional_signal_value = uucore::signals::signal_by_name_or_value(signalname); let optional_signal_value = uucore::signals::signal_by_name_or_value(signalname);
let signal_value = match optional_signal_value { let signal_value = match optional_signal_value {
Some(x) => x, Some(x) => x,
None => crash!(EXIT_ERR, "unknown signal name {}", signalname) None => crash!(EXIT_ERR, "unknown signal name {}", signalname),
}; };
for pid in &pids { for pid in &pids {
match pid.parse::<usize>() { match pid.parse::<usize>() {
@ -147,8 +163,8 @@ fn kill(signalname: &str, pids: std::vec::Vec<String>) -> i32 {
show_error!("{}", Error::last_os_error()); show_error!("{}", Error::last_os_error());
status = 1; status = 1;
} }
}, }
Err(e) => crash!(EXIT_ERR, "failed to parse argument {}: {}", pid, e) Err(e) => crash!(EXIT_ERR, "failed to parse argument {}: {}", pid, e),
}; };
} }
status status

View file

@ -22,14 +22,13 @@ static LONG_HELP: &'static str = "";
pub fn normalize_error_message(e: Error) -> String { pub fn normalize_error_message(e: Error) -> String {
match e.raw_os_error() { match e.raw_os_error() {
Some(2) => { String::from("No such file or directory (os error 2)") } Some(2) => String::from("No such file or directory (os error 2)"),
_ => { format!("{}", e) } _ => format!("{}", e),
} }
} }
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).parse(args);
.parse(args);
if matches.free.len() != 2 { if matches.free.len() != 2 {
crash!(1, "{}", msg_wrong_number_of_arguments!(2)); crash!(1, "{}", msg_wrong_number_of_arguments!(2));
} }
@ -40,7 +39,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
match hard_link(old, new) { match hard_link(old, new) {
Ok(_) => 0, Ok(_) => 0,
Err(err) => { Err(err) => {
show_error!("{}",normalize_error_message(err)); show_error!("{}", normalize_error_message(err));
1 1
} }
} }

View file

@ -9,14 +9,15 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::fs; use std::fs;
use std::io::{BufRead, BufReader, Result, stdin}; use std::io::{stdin, BufRead, BufReader, Result};
#[cfg(unix)] use std::os::unix::fs::symlink; #[cfg(unix)]
#[cfg(windows)] use std::os::windows::fs::{symlink_file,symlink_dir}; use std::os::unix::fs::symlink;
#[cfg(windows)]
use std::os::windows::fs::{symlink_dir, symlink_file};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
static NAME: &'static str = "ln"; static NAME: &'static str = "ln";
@ -58,23 +59,31 @@ pub enum BackupMode {
} }
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let syntax = format!("[OPTION]... [-T] TARGET LINK_NAME (1st form) let syntax = format!(
"[OPTION]... [-T] TARGET LINK_NAME (1st form)
{0} [OPTION]... TARGET (2nd form) {0} [OPTION]... TARGET (2nd form)
{0} [OPTION]... TARGET... DIRECTORY (3rd form) {0} [OPTION]... TARGET... DIRECTORY (3rd form)
{0} [OPTION]... -t DIRECTORY TARGET... (4th form)", NAME); {0} [OPTION]... -t DIRECTORY TARGET... (4th form)",
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 removed") .optflag("b", "", "make a backup of each file that would otherwise be overwritten or \
.optflagopt("", "backup", "make a backup of each file that would otherwise be overwritten or removed", "METHOD") removed")
// TODO: opts.optflag("d", "directory", "allow users with appropriate privileges to attempt 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("i", "interactive", "prompt whether to remove existing destination files")
// TODO: opts.optflag("L", "logical", "dereference TARGETs that are symbolic links"); // 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("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("P", "physical", "make hard links directly to symbolic links");
// TODO: opts.optflag("r", "relative", "create symbolic links relative to link location"); // 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", "DIRECTORY") .optopt("t", "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("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);
@ -98,11 +107,15 @@ pub fn uumain(args: Vec<String>) -> i32 {
"existing" | "nil" => BackupMode::ExistingBackup, "existing" | "nil" => BackupMode::ExistingBackup,
"none" | "off" => BackupMode::NoBackup, "none" | "off" => BackupMode::NoBackup,
x => { x => {
show_error!("invalid argument '{}' for 'backup method'\n\ show_error!(
Try '{} --help' for more information.", x, NAME); "invalid argument '{}' for 'backup method'\n\
Try '{} --help' for more information.",
x,
NAME
);
return 1; return 1;
} }
} },
} }
} else { } else {
BackupMode::NoBackup BackupMode::NoBackup
@ -112,8 +125,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
match matches.opt_str("suffix") { match matches.opt_str("suffix") {
Some(x) => x, Some(x) => x,
None => { None => {
show_error!("option '--suffix' requires an argument\n\ show_error!(
Try '{} --help' for more information.", NAME); "option '--suffix' requires an argument\n\
Try '{} --help' for more information.",
NAME
);
return 1; return 1;
} }
} }
@ -136,7 +152,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
verbose: matches.opt_present("v"), verbose: matches.opt_present("v"),
}; };
let string_to_path = |s: &String| { PathBuf::from(s) }; let string_to_path = |s: &String| PathBuf::from(s);
let paths: Vec<PathBuf> = matches.free.iter().map(string_to_path).collect(); let paths: Vec<PathBuf> = matches.free.iter().map(string_to_path).collect();
exec(&paths[..], &settings) exec(&paths[..], &settings)
@ -144,7 +160,10 @@ 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.len() == 0 {
show_error!("missing file operand\nTry '{} --help' for more information.", NAME); show_error!(
"missing file operand\nTry '{} --help' for more information.",
NAME
);
return 1; return 1;
} }
@ -161,18 +180,25 @@ fn exec(files: &[PathBuf], settings: &Settings) -> i32 {
let last_file = &PathBuf::from(files.last().unwrap()); let last_file = &PathBuf::from(files.last().unwrap());
if files.len() > 2 || last_file.is_dir() { if files.len() > 2 || last_file.is_dir() {
// 3rd form: create links in the last argument. // 3rd form: create links in the last argument.
return link_files_in_dir(&files[0..files.len()-1], last_file, &settings); return link_files_in_dir(&files[0..files.len() - 1], last_file, &settings);
} }
} }
// 1st form. Now there should be only two operands, but if -T is // 1st form. Now there should be only two operands, but if -T is
// specified we may have a wrong number of operands. // specified we may have a wrong number of operands.
if files.len() == 1 { if files.len() == 1 {
show_error!("missing destination file operand after '{}'", files[0].to_string_lossy()); show_error!(
"missing destination file operand after '{}'",
files[0].to_string_lossy()
);
return 1; return 1;
} }
if files.len() > 2 { if files.len() > 2 {
show_error!("extra operand '{}'\nTry '{} --help' for more information.", files[2].display(), NAME); show_error!(
"extra operand '{}'\nTry '{} --help' for more information.",
files[2].display(),
NAME
);
return 1; return 1;
} }
assert!(files.len() != 0); assert!(files.len() != 0);
@ -206,20 +232,30 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &PathBuf, settings: &Setting
} }
} }
None => { None => {
show_error!("cannot stat '{}': No such file or directory", show_error!(
srcpath.display()); "cannot stat '{}': No such file or directory",
srcpath.display()
);
all_successful = false; all_successful = false;
continue; continue;
} }
}; };
if let Err(e) = link(srcpath, &targetpath, settings) { if let Err(e) = link(srcpath, &targetpath, settings) {
show_error!("cannot link '{}' to '{}': {}", show_error!(
targetpath.display(), srcpath.display(), e); "cannot link '{}' to '{}': {}",
targetpath.display(),
srcpath.display(),
e
);
all_successful = false; all_successful = false;
} }
} }
if all_successful { 0 } else { 1 } if all_successful {
0
} else {
1
}
} }
fn link(src: &PathBuf, dst: &PathBuf, settings: &Settings) -> Result<()> { fn link(src: &PathBuf, dst: &PathBuf, settings: &Settings) -> Result<()> {
@ -227,24 +263,22 @@ fn link(src: &PathBuf, dst: &PathBuf, settings: &Settings) -> Result<()> {
if is_symlink(dst) || dst.exists() { if is_symlink(dst) || dst.exists() {
match settings.overwrite { match settings.overwrite {
OverwriteMode::NoClobber => {}, OverwriteMode::NoClobber => {}
OverwriteMode::Interactive => { OverwriteMode::Interactive => {
print!("{}: overwrite '{}'? ", NAME, dst.display()); print!("{}: overwrite '{}'? ", NAME, dst.display());
if !read_yes() { if !read_yes() {
return Ok(()); return Ok(());
} }
try!(fs::remove_file(dst)) try!(fs::remove_file(dst))
},
OverwriteMode::Force => {
try!(fs::remove_file(dst))
} }
OverwriteMode::Force => try!(fs::remove_file(dst)),
}; };
backup_path = match settings.backup { backup_path = match settings.backup {
BackupMode::NoBackup => None, BackupMode::NoBackup => None,
BackupMode::SimpleBackup => Some(simple_backup_path(dst, &settings.suffix)), BackupMode::SimpleBackup => Some(simple_backup_path(dst, &settings.suffix)),
BackupMode::NumberedBackup => Some(numbered_backup_path(dst)), BackupMode::NumberedBackup => Some(numbered_backup_path(dst)),
BackupMode::ExistingBackup => Some(existing_backup_path(dst, &settings.suffix)) BackupMode::ExistingBackup => Some(existing_backup_path(dst, &settings.suffix)),
}; };
if let Some(ref p) = backup_path { if let Some(ref p) = backup_path {
try!(fs::rename(dst, p)); try!(fs::rename(dst, p));
@ -261,7 +295,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(())
@ -272,9 +306,9 @@ fn read_yes() -> bool {
match BufReader::new(stdin()).read_line(&mut s) { match BufReader::new(stdin()).read_line(&mut s) {
Ok(_) => match s.char_indices().nth(0) { Ok(_) => match s.char_indices().nth(0) {
Some((_, x)) => x == 'y' || x == 'Y', Some((_, x)) => x == 'y' || x == 'Y',
_ => false _ => false,
}, },
_ => false _ => false,
} }
} }
@ -305,19 +339,16 @@ fn existing_backup_path(path: &PathBuf, suffix: &str) -> PathBuf {
#[cfg(windows)] #[cfg(windows)]
pub fn symlink<P: AsRef<Path>>(src: P, dst: P) -> Result<()> { pub fn symlink<P: AsRef<Path>>(src: P, dst: P) -> Result<()> {
if src.as_ref().is_dir() if src.as_ref().is_dir() {
{ symlink_dir(src, dst)
symlink_dir(src,dst) } else {
} symlink_file(src, dst)
else
{
symlink_file(src,dst)
} }
} }
pub fn is_symlink<P: AsRef<Path>>(path: P) -> bool { pub fn is_symlink<P: AsRef<Path>>(path: P) -> bool {
match fs::symlink_metadata(path) { match fs::symlink_metadata(path) {
Ok(m) => m.file_type().is_symlink(), Ok(m) => m.file_type().is_symlink(),
Err(_) => false Err(_) => false,
} }
} }

View file

@ -18,7 +18,7 @@ extern crate uucore;
use std::ffi::CStr; use std::ffi::CStr;
extern { extern "C" {
// POSIX requires using getlogin (or equivalent code) // POSIX requires using getlogin (or equivalent code)
pub fn getlogin() -> *const libc::c_char; pub fn getlogin() -> *const libc::c_char;
} }
@ -49,6 +49,6 @@ pub fn uumain(args: Vec<String>) -> i32 {
fn exec() { fn exec() {
match get_userlogin() { match get_userlogin() {
Some(userlogin) => println!("{}", userlogin), Some(userlogin) => println!("{}", userlogin),
None => show_error!("no login name") None => show_error!("no login name"),
} }
} }

View file

@ -10,13 +10,13 @@
extern crate getopts; extern crate getopts;
extern crate pretty_bytes; extern crate pretty_bytes;
extern crate termsize;
extern crate term_grid; extern crate term_grid;
extern crate termsize;
extern crate time; extern crate time;
extern crate unicode_width; extern crate unicode_width;
use pretty_bytes::converter::convert; use pretty_bytes::converter::convert;
use term_grid::{Grid, GridOptions, Direction, Filling, Cell}; use term_grid::{Cell, Direction, Filling, Grid, GridOptions};
use time::{Timespec, strftime}; use time::{strftime, Timespec};
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
@ -24,8 +24,8 @@ extern crate lazy_static;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
#[cfg(unix)] #[cfg(unix)]
use uucore::libc::{S_ISUID, S_ISGID, S_ISVTX, S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, use uucore::libc::{mode_t, S_IRGRP, S_IROTH, S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH,
S_IROTH, S_IWOTH, S_IXOTH, mode_t}; S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR};
use std::fs; use std::fs;
use std::fs::{DirEntry, FileType, Metadata}; use std::fs::{DirEntry, FileType, Metadata};
@ -76,65 +76,86 @@ lazy_static! {
} }
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let syntax = format!("[OPTION]... DIRECTORY let syntax = format!(
{0} [OPTION]... [FILE]...", NAME); "[OPTION]... DIRECTORY
{0} [OPTION]... [FILE]...",
NAME
);
let matches = new_coreopts!(&syntax, SUMMARY, LONG_HELP) let matches = new_coreopts!(&syntax, SUMMARY, LONG_HELP)
.optflag("a", .optflag(
"a",
"all", "all",
"Do not ignore hidden files (files with names that start with '.').") "Do not ignore hidden files (files with names that start with '.').",
.optflag("A", )
.optflag(
"A",
"almost-all", "almost-all",
"In a directory, do not ignore all file names that start with '.', only ignore \ "In a directory, do not ignore all file names that start with '.', only ignore \
'.' and '..'.") '.' and '..'.",
.optflag("B", )
"ignore-backups", .optflag("B", "ignore-backups", "Ignore entries which end with ~.")
"Ignore entries which end with ~.") .optflag(
.optflag("c", "c",
"", "",
"If the long listing format (e.g., -l, -o) is being used, print the status \ "If the long listing format (e.g., -l, -o) is being used, print the status \
change time (the ctime in the inode) instead of the modification time. When \ change time (the ctime in the inode) instead of the modification time. When \
explicitly sorting by time (--sort=time or -t) or when not using a long listing \ explicitly sorting by time (--sort=time or -t) or when not using a long listing \
format, sort according to the status change time.") format, sort according to the status change time.",
.optflag("d", )
.optflag(
"d",
"directory", "directory",
"Only list the names of directories, rather than listing directory contents. \ "Only list the names of directories, rather than listing directory contents. \
This will not follow symbolic links unless one of `--dereference-command-line \ This will not follow symbolic links unless one of `--dereference-command-line \
(-H)`, `--dereference (-L)`, or `--dereference-command-line-symlink-to-dir` is \ (-H)`, `--dereference (-L)`, or `--dereference-command-line-symlink-to-dir` is \
specified.") specified.",
.optflag("F", )
.optflag(
"F",
"classify", "classify",
"Append a character to each file name indicating the file type. Also, for \ "Append a character to each file name indicating the file type. Also, for \
regular files that are executable, append '*'. The file type indicators are \ regular files that are executable, append '*'. The file type indicators are \
'/' for directories, '@' for symbolic links, '|' for FIFOs, '=' for sockets, \ '/' for directories, '@' for symbolic links, '|' for FIFOs, '=' for sockets, \
'>' for doors, and nothing for regular files.") '>' for doors, and nothing for regular files.",
.optflag("h", )
.optflag(
"h",
"human-readable", "human-readable",
"Print human readable file sizes (e.g. 1K 234M 56G).") "Print human readable file sizes (e.g. 1K 234M 56G).",
.optflag("i", )
"inode", .optflag("i", "inode", "print the index number of each file")
"print the index number of each file") .optflag(
.optflag("L", "L",
"dereference", "dereference",
"When showing file information for a symbolic link, show information for the \ "When showing file information for a symbolic link, show information for the \
file the link references rather than the link itself.") file the link references rather than the link itself.",
)
.optflag("l", "long", "Display detailed information.") .optflag("l", "long", "Display detailed information.")
.optflag("n", "numeric-uid-gid", "-l with numeric UIDs and GIDs.") .optflag("n", "numeric-uid-gid", "-l with numeric UIDs and GIDs.")
.optflag("r", .optflag(
"r",
"reverse", "reverse",
"Reverse whatever the sorting method is--e.g., list files in reverse \ "Reverse whatever the sorting method is--e.g., list files in reverse \
alphabetical order, youngest first, smallest first, or whatever.") alphabetical order, youngest first, smallest first, or whatever.",
.optflag("R", )
.optflag(
"R",
"recursive", "recursive",
"List the contents of all directories recursively.") "List the contents of all directories recursively.",
)
.optflag("S", "", "Sort by file size, largest first.") .optflag("S", "", "Sort by file size, largest first.")
.optflag("t", .optflag(
"t",
"", "",
"Sort by modification time (the 'mtime' in the inode), newest first.") "Sort by modification time (the 'mtime' in the inode), newest first.",
.optflag("U", )
.optflag(
"U",
"", "",
"Do not sort; list the files in whatever order they are stored in the \ "Do not sort; list the files in whatever order they are stored in the \
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.",
)
.optflag("", "color", "Color output based on file type.") .optflag("", "color", "Color output based on file type.")
.parse(args); .parse(args);
@ -159,7 +180,7 @@ fn list(options: getopts::Matches) {
dir = true; dir = true;
if options.opt_present("l") && !(options.opt_present("L")) { if options.opt_present("l") && !(options.opt_present("L")) {
if let Ok(md) = p.symlink_metadata() { if let Ok(md) = p.symlink_metadata() {
if md.file_type().is_symlink() && !p.ends_with( "/" ) { if md.file_type().is_symlink() && !p.ends_with("/") {
dir = false; dir = false;
} }
} }
@ -189,16 +210,16 @@ fn sort_entries(entries: &mut Vec<PathBuf>, options: &getopts::Matches) {
if options.opt_present("t") { if options.opt_present("t") {
if options.opt_present("c") { if options.opt_present("c") {
entries.sort_by_key(|k| { entries.sort_by_key(|k| {
Reverse(get_metadata(k, options) Reverse(get_metadata(k, options).map(|md| md.ctime()).unwrap_or(0))
.map(|md| md.ctime())
.unwrap_or(0))
}); });
} else { } else {
entries.sort_by_key(|k| { entries.sort_by_key(|k| {
// Newest first // Newest first
Reverse(get_metadata(k, options) Reverse(
get_metadata(k, options)
.and_then(|md| md.modified()) .and_then(|md| md.modified())
.unwrap_or(std::time::UNIX_EPOCH)) .unwrap_or(std::time::UNIX_EPOCH),
)
}); });
} }
} else if options.opt_present("S") { } else if options.opt_present("S") {
@ -219,12 +240,18 @@ fn sort_entries(entries: &mut Vec<PathBuf>, options: &getopts::Matches) {
if options.opt_present("t") { if options.opt_present("t") {
entries.sort_by_key(|k| { entries.sort_by_key(|k| {
// Newest first // Newest first
Reverse(get_metadata(k, options) Reverse(
get_metadata(k, options)
.and_then(|md| md.modified()) .and_then(|md| md.modified())
.unwrap_or(std::time::UNIX_EPOCH)) .unwrap_or(std::time::UNIX_EPOCH),
)
}); });
} else if options.opt_present("S") { } else if options.opt_present("S") {
entries.sort_by_key(|k| get_metadata(k, options).map(|md| md.file_size()).unwrap_or(0)); entries.sort_by_key(|k| {
get_metadata(k, options)
.map(|md| md.file_size())
.unwrap_or(0)
});
reverse = !reverse; reverse = !reverse;
} else if !options.opt_present("U") { } else if !options.opt_present("U") {
entries.sort(); entries.sort();
@ -258,28 +285,23 @@ fn should_display(entry: &DirEntry, options: &getopts::Matches) -> bool {
} }
fn enter_directory(dir: &PathBuf, options: &getopts::Matches) { fn enter_directory(dir: &PathBuf, options: &getopts::Matches) {
let mut entries = safe_unwrap!(fs::read_dir(dir) let mut entries =
.and_then(|e| e.collect::<Result<Vec<_>, _>>())); 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));
let mut entries: Vec<_> = entries.iter().map(DirEntry::path).collect(); let mut entries: Vec<_> = entries.iter().map(DirEntry::path).collect();
sort_entries(&mut entries, options); sort_entries(&mut entries, options);
if options.opt_present("a") { if options.opt_present("a") {
let mut display_entries = entries.clone(); let mut display_entries = entries.clone();
display_entries.insert(0, dir.join("..")); display_entries.insert(0, dir.join(".."));
display_entries.insert(0, dir.join(".")); display_entries.insert(0, dir.join("."));
display_items(&display_entries, Some(dir), options); display_items(&display_entries, Some(dir), options);
} } else {
else
{
display_items(&entries, Some(dir), options); display_items(&entries, Some(dir), options);
} }
if options.opt_present("R") { if options.opt_present("R") {
for e in entries.iter().filter(|p| p.is_dir()) { for e in entries.iter().filter(|p| p.is_dir()) {
println!("\n{}:", e.to_string_lossy()); println!("\n{}:", e.to_string_lossy());
@ -298,7 +320,10 @@ fn get_metadata(entry: &PathBuf, options: &getopts::Matches) -> std::io::Result<
fn display_dir_entry_size(entry: &PathBuf, options: &getopts::Matches) -> (usize, usize) { fn display_dir_entry_size(entry: &PathBuf, options: &getopts::Matches) -> (usize, usize) {
if let Ok(md) = get_metadata(entry, options) { if let Ok(md) = get_metadata(entry, options) {
(display_symlink_count(&md).len(), display_file_size(&md, options).len()) (
display_symlink_count(&md).len(),
display_file_size(&md, options).len(),
)
} else { } else {
(0, 0) (0, 0)
} }
@ -326,7 +351,8 @@ fn display_items(items: &Vec<PathBuf>, strip: Option<&Path>, options: &getopts::
display_item_long(item, strip, max_links, max_size, options); display_item_long(item, strip, max_links, max_size, options);
} }
} else { } else {
let names: Vec<_> = items.iter() let names: Vec<_> = items
.iter()
.filter_map(|i| { .filter_map(|i| {
let md = get_metadata(i, options); let md = get_metadata(i, options);
match md { match md {
@ -364,11 +390,13 @@ fn display_items(items: &Vec<PathBuf>, strip: Option<&Path>, options: &getopts::
} }
} }
fn display_item_long(item: &PathBuf, fn display_item_long(
item: &PathBuf,
strip: Option<&Path>, strip: Option<&Path>,
max_links: usize, max_links: usize,
max_size: usize, max_size: usize,
options: &getopts::Matches) { options: &getopts::Matches,
) {
let md = match get_metadata(item, options) { let md = match get_metadata(item, options) {
Err(e) => { Err(e) => {
let filename = get_file_name(&item, strip); let filename = get_file_name(&item, strip);
@ -378,7 +406,8 @@ fn display_item_long(item: &PathBuf,
Ok(md) => md, Ok(md) => md,
}; };
println!("{}{}{} {} {} {} {} {} {}", println!(
"{}{}{} {} {} {} {} {} {}",
get_inode(&md, options), get_inode(&md, options),
display_file_type(md.file_type()), display_file_type(md.file_type()),
display_permissions(&md), display_permissions(&md),
@ -387,7 +416,8 @@ fn display_item_long(item: &PathBuf,
display_group(&md, options), display_group(&md, options),
pad_left(display_file_size(&md, options), max_size), pad_left(display_file_size(&md, options), max_size),
display_date(&md, options), display_date(&md, options),
display_file_name(&item, strip, &md, options).contents); display_file_name(&item, strip, &md, options).contents
);
} }
#[cfg(unix)] #[cfg(unix)]
@ -404,7 +434,6 @@ fn get_inode(_metadata: &Metadata, _options: &getopts::Matches) -> String {
"".to_string() "".to_string()
} }
// Currently getpwuid is `linux` target only. If it's broken out into // Currently getpwuid is `linux` target only. If it's broken out into
// a posix-compliant attribute this can be updated... // a posix-compliant attribute this can be updated...
#[cfg(unix)] #[cfg(unix)]
@ -455,11 +484,13 @@ fn display_date(metadata: &Metadata, options: &getopts::Matches) -> String {
#[allow(unused_variables)] #[allow(unused_variables)]
fn display_date(metadata: &Metadata, options: &getopts::Matches) -> String { fn display_date(metadata: &Metadata, options: &getopts::Matches) -> String {
if let Ok(mtime) = metadata.modified() { if let Ok(mtime) = metadata.modified() {
let time = let time = time::at(Timespec::new(
time::at(Timespec::new(mtime.duration_since(std::time::UNIX_EPOCH) mtime
.duration_since(std::time::UNIX_EPOCH)
.unwrap() .unwrap()
.as_secs() as i64, .as_secs() as i64,
0)); 0,
));
strftime("%F %R", &time).unwrap() strftime("%F %R", &time).unwrap()
} else { } else {
"???".to_string() "???".to_string()
@ -496,11 +527,12 @@ fn get_file_name(name: &Path, strip: Option<&Path>) -> String {
} }
#[cfg(not(unix))] #[cfg(not(unix))]
fn display_file_name(path: &Path, fn display_file_name(
path: &Path,
strip: Option<&Path>, strip: Option<&Path>,
metadata: &Metadata, metadata: &Metadata,
options: &getopts::Matches) options: &getopts::Matches,
-> Cell { ) -> Cell {
let mut name = get_file_name(path, strip); let mut name = get_file_name(path, strip);
if !options.opt_present("long") { if !options.opt_present("long") {
@ -539,15 +571,9 @@ fn color_name(name: String, typ: &str) -> String {
} }
}; };
if let Some(code) = COLOR_MAP.get(typ) { if let Some(code) = COLOR_MAP.get(typ) {
format!("{}{}{}{}{}{}{}{}", format!(
*LEFT_CODE, "{}{}{}{}{}{}{}{}",
code, *LEFT_CODE, code, *RIGHT_CODE, name, *END_CODE, *LEFT_CODE, *RESET_CODE, *RIGHT_CODE,
*RIGHT_CODE,
name,
*END_CODE,
*LEFT_CODE,
*RESET_CODE,
*RIGHT_CODE,
) )
} else { } else {
name name
@ -560,11 +586,12 @@ macro_rules! has {
) )
} }
#[cfg(unix)] #[cfg(unix)]
fn display_file_name(path: &Path, fn display_file_name(
path: &Path,
strip: Option<&Path>, strip: Option<&Path>,
metadata: &Metadata, metadata: &Metadata,
options: &getopts::Matches) options: &getopts::Matches,
-> Cell { ) -> Cell {
let mut name = get_file_name(path, strip); let mut name = get_file_name(path, strip);
if !options.opt_present("long") { if !options.opt_present("long") {
name = get_inode(metadata, options) + &name; name = get_inode(metadata, options) + &name;
@ -639,11 +666,7 @@ fn display_file_name(path: &Path,
if options.opt_present("long") && metadata.file_type().is_symlink() { if options.opt_present("long") && metadata.file_type().is_symlink() {
if let Ok(target) = path.read_link() { if let Ok(target) = path.read_link() {
// We don't bother updating width here because it's not used for long listings // We don't bother updating width here because it's not used for long listings
let code = if target.exists() { let code = if target.exists() { "fi" } else { "mi" };
"fi"
} else {
"mi"
};
let target_name = color_name(target.to_string_lossy().to_string(), code); let target_name = color_name(target.to_string_lossy().to_string(), code);
name.push_str(" -> "); name.push_str(" -> ");
name.push_str(&target_name); name.push_str(&target_name);
@ -679,16 +702,8 @@ fn display_permissions(metadata: &Metadata) -> String {
fn display_permissions(metadata: &Metadata) -> String { fn display_permissions(metadata: &Metadata) -> String {
let mode = metadata.mode() as mode_t; let mode = metadata.mode() as mode_t;
let mut result = String::with_capacity(9); let mut result = String::with_capacity(9);
result.push(if has!(mode, S_IRUSR) { result.push(if has!(mode, S_IRUSR) { 'r' } else { '-' });
'r' result.push(if has!(mode, S_IWUSR) { 'w' } else { '-' });
} else {
'-'
});
result.push(if has!(mode, S_IWUSR) {
'w'
} else {
'-'
});
result.push(if has!(mode, S_ISUID) { result.push(if has!(mode, S_ISUID) {
if has!(mode, S_IXUSR) { if has!(mode, S_IXUSR) {
's' 's'
@ -701,16 +716,8 @@ fn display_permissions(metadata: &Metadata) -> String {
'-' '-'
}); });
result.push(if has!(mode, S_IRGRP) { result.push(if has!(mode, S_IRGRP) { 'r' } else { '-' });
'r' result.push(if has!(mode, S_IWGRP) { 'w' } else { '-' });
} else {
'-'
});
result.push(if has!(mode, S_IWGRP) {
'w'
} else {
'-'
});
result.push(if has!(mode, S_ISGID) { result.push(if has!(mode, S_ISGID) {
if has!(mode, S_IXGRP) { if has!(mode, S_IXGRP) {
's' 's'
@ -723,16 +730,8 @@ fn display_permissions(metadata: &Metadata) -> String {
'-' '-'
}); });
result.push(if has!(mode, S_IROTH) { result.push(if has!(mode, S_IROTH) { 'r' } else { '-' });
'r' result.push(if has!(mode, S_IWOTH) { 'w' } else { '-' });
} else {
'-'
});
result.push(if has!(mode, S_IWOTH) {
'w'
} else {
'-'
});
result.push(if has!(mode, S_ISVTX) { result.push(if has!(mode, S_ISVTX) {
if has!(mode, S_IXOTH) { if has!(mode, S_IXOTH) {
't' 't'

View file

@ -38,7 +38,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
let matches = match opts.parse(&args[1..]) { let matches = match opts.parse(&args[1..]) {
Ok(m) => m, Ok(m) => m,
Err(f) => crash!(1, "Invalid options\n{}", f) Err(f) => crash!(1, "Invalid options\n{}", f),
}; };
if args.len() == 1 || matches.opt_present("help") { if args.len() == 1 || matches.opt_present("help") {
@ -78,7 +78,10 @@ fn print_help(opts: &getopts::Options) {
println!("{} {}", NAME, VERSION); println!("{} {}", NAME, VERSION);
println!(""); println!("");
println!("Usage:"); println!("Usage:");
print!("{}", opts.usage("Create the given DIRECTORY(ies) if they do not exist")); print!(
"{}",
opts.usage("Create the given DIRECTORY(ies) if they do not exist")
);
} }
/** /**
@ -101,12 +104,15 @@ fn exec(dirs: Vec<String>, recursive: bool, mode: u16, verbose: bool) -> i32 {
match path.parent() { match path.parent() {
Some(parent) => { Some(parent) => {
if parent != empty && !parent.exists() { if parent != empty && !parent.exists() {
show_info!("cannot create directory '{}': No such file or directory", path.display()); show_info!(
"cannot create directory '{}': No such file or directory",
path.display()
);
status = 1; status = 1;
} else { } else {
status |= mkdir(path, mode, verbose); status |= mkdir(path, mode, verbose);
} }
}, }
None => { None => {
status |= mkdir(path, mode, verbose); status |= mkdir(path, mode, verbose);
} }
@ -134,11 +140,16 @@ fn mkdir(path: &Path, mode: u16, verbose: bool) -> i32 {
use std::ffi::CString; use std::ffi::CString;
use std::io::Error; use std::io::Error;
let directory = CString::new(path.as_os_str().to_str().unwrap()).unwrap_or_else(|e| crash!(1, "{}", e)); let directory =
CString::new(path.as_os_str().to_str().unwrap()).unwrap_or_else(|e| crash!(1, "{}", e));
let mode = mode as libc::mode_t; let mode = mode as libc::mode_t;
if unsafe { libc::chmod(directory.as_ptr(), mode) } != 0 { if unsafe { libc::chmod(directory.as_ptr(), mode) } != 0 {
show_info!("{}: errno {}", path.display(), Error::last_os_error().raw_os_error().unwrap()); show_info!(
"{}: errno {}",
path.display(),
Error::last_os_error().raw_os_error().unwrap()
);
return 1; return 1;
} }
0 0

View file

@ -25,7 +25,12 @@ static VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = getopts::Options::new(); let mut opts = getopts::Options::new();
opts.optopt("m", "mode", "file permissions for the fifo", "(default 0666)"); opts.optopt(
"m",
"mode",
"file permissions for the fifo",
"(default 0666)",
);
opts.optflag("h", "help", "display this help and exit"); opts.optflag("h", "help", "display this help and exit");
opts.optflag("V", "version", "output version information and exit"); opts.optflag("V", "version", "output version information and exit");
@ -40,12 +45,15 @@ pub fn uumain(args: Vec<String>) -> i32 {
} }
if matches.opt_present("help") || matches.free.is_empty() { if matches.opt_present("help") || matches.free.is_empty() {
let msg = format!("{0} {1} let msg = format!(
"{0} {1}
Usage: Usage:
{0} [OPTIONS] NAME... {0} [OPTIONS] NAME...
Create a FIFO with the given name.", NAME, VERSION); Create a FIFO with the given name.",
NAME, VERSION
);
print!("{}", opts.usage(&msg)); print!("{}", opts.usage(&msg));
if matches.free.is_empty() { if matches.free.is_empty() {
@ -57,7 +65,7 @@ Create a FIFO with the given name.", NAME, VERSION);
let mode = match matches.opt_str("m") { let mode = match matches.opt_str("m") {
Some(m) => match usize::from_str_radix(&m, 8) { Some(m) => match usize::from_str_radix(&m, 8) {
Ok(m) => m, Ok(m) => m,
Err(e)=> { Err(e) => {
show_error!("invalid mode: {}", e); show_error!("invalid mode: {}", e);
return 1; return 1;
} }
@ -67,9 +75,18 @@ Create a FIFO with the given name.", NAME, VERSION);
let mut exit_status = 0; let mut exit_status = 0;
for f in &matches.free { for f in &matches.free {
let err = unsafe { mkfifo(CString::new(f.as_bytes()).unwrap().as_ptr(), mode as libc::mode_t) }; let err = unsafe {
mkfifo(
CString::new(f.as_bytes()).unwrap().as_ptr(),
mode as libc::mode_t,
)
};
if err == -1 { if err == -1 {
show_error!("creating '{}': {}", f, Error::last_os_error().raw_os_error().unwrap()); show_error!(
"creating '{}': {}",
f,
Error::last_os_error().raw_os_error().unwrap()
);
exit_status = 1; exit_status = 1;
} }
} }

View file

@ -16,8 +16,8 @@ mod parsemode;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use libc::{mode_t, dev_t}; use libc::{dev_t, mode_t};
use libc::{S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP, S_IROTH, S_IWOTH, S_IFIFO, S_IFBLK, S_IFCHR}; use libc::{S_IFBLK, S_IFCHR, S_IFIFO, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR};
use getopts::Options; use getopts::Options;
@ -31,8 +31,8 @@ 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) | ((min & 0xff) | ((maj & 0xfff) << 8) | (((min & !0xff)) << 12) | (((maj & !0xfff)) << 32))
(((maj & !0xfff)) << 32)) as dev_t as dev_t
} }
#[cfg(windows)] #[cfg(windows)]
@ -51,10 +51,12 @@ pub fn uumain(args: Vec<String>) -> i32 {
// Linux-specific options, not implemented // Linux-specific options, not implemented
// opts.optflag("Z", "", "set the SELinux security context to default type"); // opts.optflag("Z", "", "set the SELinux security context to default type");
// opts.optopt("", "context", "like -Z, or if CTX is specified then set the SELinux or SMACK security context to CTX"); // opts.optopt("", "context", "like -Z, or if CTX is specified then set the SELinux or SMACK security context to CTX");
opts.optopt("m", opts.optopt(
"m",
"mode", "mode",
"set file permission bits to MODE, not a=rw - umask", "set file permission bits to MODE, not a=rw - umask",
"MODE"); "MODE",
);
opts.optflag("", "help", "display this help and exit"); opts.optflag("", "help", "display this help and exit");
opts.optflag("", "version", "output version information and exit"); opts.optflag("", "version", "output version information and exit");
@ -66,7 +68,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
if matches.opt_present("help") { if matches.opt_present("help") {
println!( println!(
"Usage: {0} [OPTION]... NAME TYPE [MAJOR MINOR] "Usage: {0} [OPTION]... NAME TYPE [MAJOR MINOR]
Mandatory arguments to long options are mandatory for short options too. Mandatory arguments to long options are mandatory for short options too.
-m, --mode=MODE set file permission bits to MODE, not a=rw - umask -m, --mode=MODE set file permission bits to MODE, not a=rw - umask
@ -84,7 +86,9 @@ otherwise, as decimal. TYPE may be:
NOTE: your shell may have its own version of mknod, which usually supersedes NOTE: your shell may have its own version of mknod, which usually supersedes
the version described here. Please refer to your shell's documentation the version described here. Please refer to your shell's documentation
for details about the options it supports.", NAME); for details about the options it supports.",
NAME
);
return 0; return 0;
} }
@ -124,7 +128,10 @@ for details about the options it supports.", NAME);
// Only check the first character, to allow mnemonic usage like // Only check the first character, to allow mnemonic usage like
// 'mknod /dev/rst0 character 18 0'. // 'mknod /dev/rst0 character 18 0'.
let ch = args[1].chars().nth(0).expect("Failed to get the first char"); let ch = args[1]
.chars()
.nth(0)
.expect("Failed to get the first char");
if ch == 'p' { if ch == 'p' {
if args.len() > 2 { if args.len() > 2 {

View file

@ -1,5 +1,5 @@
extern crate libc; extern crate libc;
use libc::{mode_t, S_IRGRP, S_IWGRP, S_IROTH, S_IWOTH, S_IRUSR, S_IWUSR}; use libc::{mode_t, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR};
use uucore::mode; use uucore::mode;

View file

@ -10,14 +10,14 @@
// //
extern crate getopts; extern crate getopts;
extern crate tempfile;
extern crate rand; extern crate rand;
extern crate tempfile;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::env; use std::env;
use std::path::{PathBuf, is_separator}; use std::path::{is_separator, PathBuf};
use std::mem::forget; use std::mem::forget;
use std::iter; use std::iter;
@ -30,22 +30,37 @@ static NAME: &'static str = "mktemp";
static VERSION: &'static str = env!("CARGO_PKG_VERSION"); static VERSION: &'static str = env!("CARGO_PKG_VERSION");
static DEFAULT_TEMPLATE: &'static str = "tmp.XXXXXXXXXX"; static DEFAULT_TEMPLATE: &'static str = "tmp.XXXXXXXXXX";
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = getopts::Options::new(); let mut opts = getopts::Options::new();
opts.optflag("d", "directory", "Make a directory instead of a file"); opts.optflag("d", "directory", "Make a directory instead of a file");
opts.optflag("u", opts.optflag(
"u",
"dry-run", "dry-run",
"do not create anything; merely print a name (unsafe)"); "do not create anything; merely print a name (unsafe)",
);
opts.optflag("q", "quiet", "Fail silently if an error occurs."); opts.optflag("q", "quiet", "Fail silently if an error occurs.");
opts.optopt("", "suffix", "append SUFF to TEMPLATE; SUFF must not contain a path separator. This option is implied if TEMPLATE does not end with X.", "SUFF"); opts.optopt(
opts.optopt("p", "tmpdir", "interpret TEMPLATE relative to DIR; if DIR is not specified, use $TMPDIR if set, else /tmp. With this option, TEMPLATE must not be an absolute name; unlike with -t, TEMPLATE may contain slashes, but mktemp creates only the final component", "DIR"); "",
"suffix",
"append SUFF to TEMPLATE; SUFF must not contain a path separator. \
This option is implied if TEMPLATE does not end with X.",
"SUFF",
);
opts.optopt(
"p",
"tmpdir",
"interpret TEMPLATE relative to DIR; if DIR is not specified, use \
$TMPDIR if set, else /tmp. With this option, TEMPLATE must not \
be an absolute name; unlike with -t, TEMPLATE may contain \
slashes, but mktemp creates only the final component",
"DIR",
);
// deprecated option of GNU coreutils // deprecated option of GNU coreutils
// opts.optflag("t", "", "Generate a template (using the supplied prefix and TMPDIR if set) to create a filename template"); // opts.optflag("t", "", "Generate a template (using the supplied prefix and TMPDIR if set) \
// to create a filename template");
opts.optflag("", "help", "Print this help and exit"); opts.optflag("", "help", "Print this help and exit");
opts.optflag("", "version", "print the version and exit"); opts.optflag("", "version", "print the version and exit");
// >> early return options // >> early return options
let matches = match opts.parse(&args[1..]) { let matches = match opts.parse(&args[1..]) {
Ok(m) => m, Ok(m) => m,
@ -71,7 +86,6 @@ pub fn uumain(args: Vec<String>) -> i32 {
let suffix_opt = matches.opt_str("suffix"); let suffix_opt = matches.opt_str("suffix");
let suppress_file_err = matches.opt_present("quiet"); let suppress_file_err = matches.opt_present("quiet");
let template = if matches.free.is_empty() { let template = if matches.free.is_empty() {
DEFAULT_TEMPLATE DEFAULT_TEMPLATE
} else { } else {
@ -79,19 +93,19 @@ pub fn uumain(args: Vec<String>) -> i32 {
}; };
let (prefix, rand, suffix) = match parse_template(template) { let (prefix, rand, suffix) = match parse_template(template) {
Some((p, r, s)) => { Some((p, r, s)) => match suffix_opt {
match suffix_opt {
Some(suf) => { Some(suf) => {
if s == "" { if s == "" {
(p, r, suf) (p, r, suf)
} else { } else {
crash!(1, crash!(
"Template should end with 'X' when you specify suffix option.") 1,
"Template should end with 'X' when you specify suffix option."
)
} }
} }
None => (p, r, s.to_owned()), None => (p, r, s.to_owned()),
} },
}
None => ("", 0, "".to_owned()), None => ("", 0, "".to_owned()),
}; };
@ -103,11 +117,13 @@ pub fn uumain(args: Vec<String>) -> i32 {
crash!(1, "suffix cannot contain any path separators"); crash!(1, "suffix cannot contain any path separators");
} }
let tmpdir = match matches.opt_str("tmpdir") { let tmpdir = match matches.opt_str("tmpdir") {
Some(s) => { Some(s) => {
if PathBuf::from(prefix).is_absolute() { if PathBuf::from(prefix).is_absolute() {
show_info!("invalid template, {}; with --tmpdir, it may not be absolute", template); show_info!(
"invalid template, {}; with --tmpdir, it may not be absolute",
template
);
return 1; return 1;
} }
PathBuf::from(s) PathBuf::from(s)
@ -120,14 +136,15 @@ pub fn uumain(args: Vec<String>) -> i32 {
} else { } else {
exec(tmpdir, prefix, rand, &suffix, make_dir, suppress_file_err) exec(tmpdir, prefix, rand, &suffix, make_dir, suppress_file_err)
} }
} }
fn print_help(opts: &getopts::Options) { fn print_help(opts: &getopts::Options) {
let usage = format!(" Create a temporary file or directory, safely, and print its name. let usage = format!(
" Create a temporary file or directory, safely, and print its name.
TEMPLATE must contain at least 3 consecutive 'X's in last component. TEMPLATE must contain at least 3 consecutive 'X's in last component.
If TEMPLATE is not specified, use {}, and --tmpdir is implied", If TEMPLATE is not specified, use {}, and --tmpdir is implied",
DEFAULT_TEMPLATE); DEFAULT_TEMPLATE
);
println!("{} {}", NAME, VERSION); println!("{} {}", NAME, VERSION);
println!("SYNOPSIS"); println!("SYNOPSIS");
@ -174,7 +191,14 @@ pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) ->
0 0
} }
fn exec(tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool, quiet: bool) -> i32 { fn exec(
tmpdir: PathBuf,
prefix: &str,
rand: usize,
suffix: &str,
make_dir: bool,
quiet: bool,
) -> i32 {
if make_dir { if make_dir {
match tempdir::new_in(&tmpdir, prefix, rand, suffix) { match tempdir::new_in(&tmpdir, prefix, rand, suffix) {
Ok(ref f) => { Ok(ref f) => {
@ -206,9 +230,7 @@ fn exec(tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool
} }
}; };
let tmpname = tmpfile.path() let tmpname = tmpfile.path().to_string_lossy().to_string();
.to_string_lossy()
.to_string();
println!("{}", tmpname); println!("{}", tmpname);

View file

@ -1,7 +1,7 @@
// Mainly taken from crate `tempdir` // Mainly taken from crate `tempdir`
extern crate rand; extern crate rand;
use rand::{Rng, thread_rng}; use rand::{thread_rng, Rng};
use std::io::Result as IOResult; use std::io::Result as IOResult;
use std::io::{Error, ErrorKind}; use std::io::{Error, ErrorKind};
@ -24,8 +24,12 @@ fn create_dir<P: AsRef<Path>>(path: P) -> IOResult<()> {
::std::fs::create_dir(path) ::std::fs::create_dir(path)
} }
pub fn new_in<P: AsRef<Path>>(tmpdir: P, prefix: &str, rand: usize, suffix: &str) -> IOResult<String> { pub fn new_in<P: AsRef<Path>>(
tmpdir: P,
prefix: &str,
rand: usize,
suffix: &str,
) -> IOResult<String> {
let mut rng = thread_rng(); let mut rng = thread_rng();
for _ in 0..NUM_RETRIES { for _ in 0..NUM_RETRIES {
let rand_chars: String = rng.gen_ascii_chars().take(rand).collect(); let rand_chars: String = rng.gen_ascii_chars().take(rand).collect();
@ -38,6 +42,8 @@ pub fn new_in<P: AsRef<Path>>(tmpdir: P, prefix: &str, rand: usize, suffix: &str
} }
} }
Err(Error::new(ErrorKind::AlreadyExists, Err(Error::new(
"too many temporary directories already exist")) ErrorKind::AlreadyExists,
"too many temporary directories already exist",
))
} }

View file

@ -15,7 +15,7 @@ extern crate getopts;
extern crate uucore; extern crate uucore;
use getopts::Options; use getopts::Options;
use std::io::{stdout, Write, Read}; use std::io::{stdout, Read, Write};
use std::fs::File; use std::fs::File;
#[cfg(all(unix, not(target_os = "fuchsia")))] #[cfg(all(unix, not(target_os = "fuchsia")))]
@ -44,7 +44,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
Err(e) => { Err(e) => {
show_error!("{}", e); show_error!("{}", e);
panic!() panic!()
}, }
}; };
let usage = opts.usage("more TARGET."); let usage = opts.usage("more TARGET.");
let mode = if matches.opt_present("version") { let mode = if matches.opt_present("version") {
@ -69,10 +69,13 @@ fn version() {
} }
fn help(usage: &str) { fn help(usage: &str) {
let msg = format!("{0} {1}\n\n\ let msg = format!(
"{0} {1}\n\n\
Usage: {0} TARGET\n \ Usage: {0} TARGET\n \
\n\ \n\
{2}", NAME, VERSION, usage); {2}",
NAME, VERSION, usage
);
println!("{}", msg); println!("{}", msg);
} }
@ -102,8 +105,7 @@ fn reset_term(term: &mut termios::Termios) {
#[cfg(any(windows, target_os = "fuchsia"))] #[cfg(any(windows, target_os = "fuchsia"))]
#[inline(always)] #[inline(always)]
fn reset_term(_: &mut usize) { fn reset_term(_: &mut usize) {}
}
fn more(matches: getopts::Matches) { fn more(matches: getopts::Matches) {
let files = matches.free; let files = matches.free;
@ -114,7 +116,9 @@ fn more(matches: getopts::Matches) {
let mut end = false; let mut end = false;
while let Ok(sz) = f.read(&mut buffer) { while let Ok(sz) = f.read(&mut buffer) {
if sz == 0 { break } if sz == 0 {
break;
}
stdout().write(&buffer[0..sz]).unwrap(); stdout().write(&buffer[0..sz]).unwrap();
for byte in std::io::stdin().bytes() { for byte in std::io::stdin().bytes() {
match byte.unwrap() { match byte.unwrap() {
@ -122,12 +126,14 @@ fn more(matches: getopts::Matches) {
b'q' | 27 => { b'q' | 27 => {
end = true; end = true;
break; break;
}, }
_ => () _ => (),
} }
} }
if end { break } if end {
break;
}
} }
reset_term(&mut term); reset_term(&mut term);

View file

@ -16,7 +16,7 @@ extern crate uucore;
use std::fs; use std::fs;
use std::env; use std::env;
use std::io::{BufRead, BufReader, Result, stdin}; use std::io::{stdin, BufRead, BufReader, Result};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
static NAME: &'static str = "mv"; static NAME: &'static str = "mv";
@ -50,29 +50,37 @@ pub enum BackupMode {
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = getopts::Options::new(); let mut opts = getopts::Options::new();
opts.optflagopt("", opts.optflagopt(
"",
"backup", "backup",
"make a backup of each existing destination file", "make a backup of each existing destination file",
"CONTROL"); "CONTROL",
);
opts.optflag("b", "", "like --backup but does not accept an argument"); opts.optflag("b", "", "like --backup but does not accept an argument");
opts.optflag("f", "force", "do not prompt before overwriting"); opts.optflag("f", "force", "do not prompt before overwriting");
opts.optflag("i", "interactive", "prompt before override"); opts.optflag("i", "interactive", "prompt before override");
opts.optflag("n", "no-clobber", "do not overwrite an existing file"); opts.optflag("n", "no-clobber", "do not overwrite an existing file");
opts.optflag("", opts.optflag(
"",
"strip-trailing-slashes", "strip-trailing-slashes",
"remove any trailing slashes from each SOURCE\n \ "remove any trailing slashes from each SOURCE\n \
argument"); argument",
);
opts.optopt("S", "suffix", "override the usual backup suffix", "SUFFIX"); opts.optopt("S", "suffix", "override the usual backup suffix", "SUFFIX");
opts.optopt("t", opts.optopt(
"t",
"target-directory", "target-directory",
"move all SOURCE arguments into DIRECTORY", "move all SOURCE arguments into DIRECTORY",
"DIRECTORY"); "DIRECTORY",
);
opts.optflag("T", "no-target-directory", "treat DEST as a normal file"); opts.optflag("T", "no-target-directory", "treat DEST as a normal file");
opts.optflag("u", opts.optflag(
"u",
"update", "update",
"move only when the SOURCE file is newer\n \ "move only when the SOURCE file is newer\n \
than the destination file or when the\n \ than the destination file or when the\n \
destination file is missing"); destination file is missing",
);
opts.optflag("v", "verbose", "explain what is being done"); opts.optflag("v", "verbose", "explain what is being done");
opts.optflag("h", "help", "display this help and exit"); opts.optflag("h", "help", "display this help and exit");
opts.optflag("V", "version", "output version information and exit"); opts.optflag("V", "version", "output version information and exit");
@ -90,9 +98,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
let backup_mode = determine_backup_mode(&matches); let backup_mode = determine_backup_mode(&matches);
if overwrite_mode == OverwriteMode::NoClobber && backup_mode != BackupMode::NoBackup { if overwrite_mode == OverwriteMode::NoClobber && backup_mode != BackupMode::NoBackup {
show_error!("options --backup and --no-clobber are mutually exclusive\n\ show_error!(
"options --backup and --no-clobber are mutually exclusive\n\
Try '{} --help' for more information.", Try '{} --help' for more information.",
NAME); NAME
);
return 1; return 1;
} }
@ -158,20 +168,21 @@ fn determine_backup_mode(matches: &getopts::Matches) -> BackupMode {
} else if matches.opt_present("backup") { } else if matches.opt_present("backup") {
match matches.opt_str("backup") { match matches.opt_str("backup") {
None => BackupMode::SimpleBackup, None => BackupMode::SimpleBackup,
Some(mode) => { Some(mode) => match &mode[..] {
match &mode[..] {
"simple" | "never" => BackupMode::SimpleBackup, "simple" | "never" => BackupMode::SimpleBackup,
"numbered" | "t" => BackupMode::NumberedBackup, "numbered" | "t" => BackupMode::NumberedBackup,
"existing" | "nil" => BackupMode::ExistingBackup, "existing" | "nil" => BackupMode::ExistingBackup,
"none" | "off" => BackupMode::NoBackup, "none" | "off" => BackupMode::NoBackup,
x => { x => {
crash!(1, "invalid argument {} for backup type\n\ crash!(
1,
"invalid argument {} for backup type\n\
Try '{} --help' for more information.", Try '{} --help' for more information.",
x, x,
NAME); NAME
} );
}
} }
},
} }
} else { } else {
BackupMode::NoBackup BackupMode::NoBackup
@ -183,9 +194,12 @@ fn determine_backup_suffix(backup_mode: BackupMode, matches: &getopts::Matches)
match matches.opt_str("suffix") { match matches.opt_str("suffix") {
Some(x) => x, Some(x) => x,
None => { None => {
crash!(1, "option '--suffix' requires an argument\n\ crash!(
1,
"option '--suffix' requires an argument\n\
Try '{} --help' for more information.", Try '{} --help' for more information.",
NAME); NAME
);
} }
} }
} else { } else {
@ -198,13 +212,13 @@ fn determine_backup_suffix(backup_mode: BackupMode, matches: &getopts::Matches)
} }
fn help(usage: &str) { fn help(usage: &str) {
println!("{0} {1}\n\n\ println!(
"{0} {1}\n\n\
Usage: {0} SOURCE DEST\n \ Usage: {0} SOURCE DEST\n \
or: {0} SOURCE... DIRECTORY\n\n\ or: {0} SOURCE... DIRECTORY\n\n\
{2}", {2}",
NAME, NAME, VERSION, usage
VERSION, );
usage);
} }
fn exec(files: &[PathBuf], b: Behaviour) -> i32 { fn exec(files: &[PathBuf], b: Behaviour) -> i32 {
@ -213,25 +227,31 @@ fn exec(files: &[PathBuf], b: Behaviour) -> i32 {
} }
match files.len() { match files.len() {
0 | 1 => { 0 | 1 => {
show_error!("missing file operand\n\ show_error!(
"missing file operand\n\
Try '{} --help' for more information.", Try '{} --help' for more information.",
NAME); NAME
);
return 1; return 1;
} }
2 => { 2 => {
let source = &files[0]; let source = &files[0];
let target = &files[1]; let target = &files[1];
if !source.exists() { if !source.exists() {
show_error!("cannot stat {}: No such file or directory", show_error!(
source.display()); "cannot stat {}: No such file or directory",
source.display()
);
return 1; return 1;
} }
if target.is_dir() { if target.is_dir() {
if b.no_target_dir { if b.no_target_dir {
if !source.is_dir() { if !source.is_dir() {
show_error!("cannot overwrite directory {} with non-directory", show_error!(
target.display()); "cannot overwrite directory {} with non-directory",
target.display()
);
return 1; return 1;
} }
@ -254,10 +274,12 @@ fn exec(files: &[PathBuf], b: Behaviour) -> i32 {
} }
_ => { _ => {
if b.no_target_dir { if b.no_target_dir {
show_error!("mv: extra operand {}\n\ show_error!(
"mv: extra operand {}\n\
Try '{} --help' for more information.", Try '{} --help' for more information.",
files[2].display(), files[2].display(),
NAME); NAME
);
return 1; return 1;
} }
let target_dir = files.last().unwrap(); let target_dir = files.last().unwrap();
@ -278,8 +300,10 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behaviour) -
let targetpath = match sourcepath.as_os_str().to_str() { let targetpath = match sourcepath.as_os_str().to_str() {
Some(name) => target_dir.join(name), Some(name) => target_dir.join(name),
None => { None => {
show_error!("cannot stat {}: No such file or directory", show_error!(
sourcepath.display()); "cannot stat {}: No such file or directory",
sourcepath.display()
);
all_successful = false; all_successful = false;
continue; continue;
@ -287,10 +311,12 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behaviour) -
}; };
if let Err(e) = rename(sourcepath, &targetpath, b) { if let Err(e) = rename(sourcepath, &targetpath, b) {
show_error!("mv: cannot move {} to {}: {}", show_error!(
"mv: cannot move {} to {}: {}",
sourcepath.display(), sourcepath.display(),
targetpath.display(), targetpath.display(),
e); e
);
all_successful = false; all_successful = false;
} }
} }
@ -327,7 +353,8 @@ fn rename(from: &PathBuf, to: &PathBuf, b: &Behaviour) -> Result<()> {
} }
if b.update { if b.update {
if try!(try!(fs::metadata(from)).modified()) <= try!(try!(fs::metadata(to)).modified()) { if try!(try!(fs::metadata(from)).modified()) <= try!(try!(fs::metadata(to)).modified())
{
return Ok(()); return Ok(());
} }
} }
@ -348,12 +375,10 @@ fn rename(from: &PathBuf, to: &PathBuf, b: &Behaviour) -> Result<()> {
fn read_yes() -> bool { fn read_yes() -> bool {
let mut s = String::new(); let mut s = String::new();
match BufReader::new(stdin()).read_line(&mut s) { match BufReader::new(stdin()).read_line(&mut s) {
Ok(_) => { Ok(_) => match s.chars().nth(0) {
match s.chars().nth(0) {
Some(x) => x == 'y' || x == 'Y', Some(x) => x == 'y' || x == 'Y',
_ => false, _ => false,
} },
}
_ => false, _ => false,
} }
} }

View file

@ -25,7 +25,7 @@ const VERSION: &'static str = env!("CARGO_PKG_VERSION");
// XXX: PRIO_PROCESS is 0 on at least FreeBSD and Linux. Don't know about Mac OS X. // XXX: PRIO_PROCESS is 0 on at least FreeBSD and Linux. Don't know about Mac OS X.
const PRIO_PROCESS: c_int = 0; const PRIO_PROCESS: c_int = 0;
extern { extern "C" {
fn getpriority(which: c_int, who: c_int) -> c_int; fn getpriority(which: c_int, who: c_int) -> c_int;
fn setpriority(which: c_int, who: c_int, prio: c_int) -> c_int; fn setpriority(which: c_int, who: c_int, prio: c_int) -> c_int;
} }
@ -33,7 +33,12 @@ extern {
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = getopts::Options::new(); let mut opts = getopts::Options::new();
opts.optopt("n", "adjustment", "add N to the niceness (default is 10)", "N"); opts.optopt(
"n",
"adjustment",
"add N to the niceness (default is 10)",
"N",
);
opts.optflag("h", "help", "display this help and exit"); opts.optflag("h", "help", "display this help and exit");
opts.optflag("V", "version", "output version information and exit"); opts.optflag("V", "version", "output version information and exit");
@ -51,7 +56,8 @@ pub fn uumain(args: Vec<String>) -> i32 {
} }
if matches.opt_present("help") { if matches.opt_present("help") {
let msg = format!("{0} {1} let msg = format!(
"{0} {1}
Usage: Usage:
{0} [OPTIONS] [COMMAND [ARGS]] {0} [OPTIONS] [COMMAND [ARGS]]
@ -59,7 +65,9 @@ Usage:
Run COMMAND with an adjusted niceness, which affects process scheduling. Run COMMAND with an adjusted niceness, which affects process scheduling.
With no COMMAND, print the current niceness. Niceness values range from at With no COMMAND, print the current niceness. Niceness values range from at
least -20 (most favorable to the process) to 19 (least favorable to the least -20 (most favorable to the process) to 19 (least favorable to the
process).", NAME, VERSION); process).",
NAME, VERSION
);
print!("{}", opts.usage(&msg)); print!("{}", opts.usage(&msg));
return 0; return 0;
@ -74,18 +82,21 @@ process).", NAME, VERSION);
let adjustment = match matches.opt_str("adjustment") { let adjustment = match matches.opt_str("adjustment") {
Some(nstr) => { Some(nstr) => {
if matches.free.is_empty() { if matches.free.is_empty() {
show_error!("A command must be given with an adjustment. show_error!(
Try \"{} --help\" for more information.", args[0]); "A command must be given with an adjustment.
Try \"{} --help\" for more information.",
args[0]
);
return 125; return 125;
} }
match nstr.parse() { match nstr.parse() {
Ok(num) => num, Ok(num) => num,
Err(e)=> { Err(e) => {
show_error!("\"{}\" is not a valid number: {}", nstr, e); show_error!("\"{}\" is not a valid number: {}", nstr, e);
return 125; return 125;
} }
} }
}, }
None => { None => {
if matches.free.is_empty() { if matches.free.is_empty() {
println!("{}", niceness); println!("{}", niceness);
@ -96,16 +107,28 @@ process).", NAME, VERSION);
}; };
niceness += adjustment; niceness += adjustment;
unsafe { setpriority(PRIO_PROCESS, 0, niceness); } unsafe {
setpriority(PRIO_PROCESS, 0, niceness);
}
if Error::last_os_error().raw_os_error().unwrap() != 0 { if Error::last_os_error().raw_os_error().unwrap() != 0 {
show_warning!("{}", Error::last_os_error()); show_warning!("{}", Error::last_os_error());
} }
let cstrs: Vec<CString> = matches.free.iter().map(|x| CString::new(x.as_bytes()).unwrap()).collect(); let cstrs: Vec<CString> = matches
.free
.iter()
.map(|x| CString::new(x.as_bytes()).unwrap())
.collect();
let mut args: Vec<*const c_char> = cstrs.iter().map(|s| s.as_ptr()).collect(); let mut args: Vec<*const c_char> = cstrs.iter().map(|s| s.as_ptr()).collect();
args.push(0 as *const c_char); args.push(0 as *const c_char);
unsafe { execvp(args[0], args.as_mut_ptr()); } unsafe {
execvp(args[0], args.as_mut_ptr());
}
show_error!("{}", Error::last_os_error()); show_error!("{}", Error::last_os_error());
if Error::last_os_error().raw_os_error().unwrap() as c_int == libc::ENOENT { 127 } else { 126 } if Error::last_os_error().raw_os_error().unwrap() as c_int == libc::ENOENT {
127
} else {
126
}
} }

View file

@ -27,45 +27,67 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
let mut errs: Vec<String> = vec![]; let mut errs: Vec<String> = vec![];
settings.renumber = !opts.opt_present("p"); settings.renumber = !opts.opt_present("p");
match opts.opt_str("s") { match opts.opt_str("s") {
None => {}, None => {}
Some(val) => { settings.number_separator = val; } Some(val) => {
settings.number_separator = val;
}
} }
match opts.opt_str("n") { match opts.opt_str("n") {
None => {}, None => {}
Some(val) => match val.as_ref() { Some(val) => match val.as_ref() {
"ln" => { settings.number_format = ::NumberFormat::Left; }, "ln" => {
"rn" => { settings.number_format = ::NumberFormat::Right; }, settings.number_format = ::NumberFormat::Left;
"rz" => { settings.number_format = ::NumberFormat::RightZero; },
_ => { errs.push(String::from("Illegal value for -n")); },
} }
"rn" => {
settings.number_format = ::NumberFormat::Right;
}
"rz" => {
settings.number_format = ::NumberFormat::RightZero;
}
_ => {
errs.push(String::from("Illegal value for -n"));
}
},
} }
match opts.opt_str("b") { match opts.opt_str("b") {
None => {}, None => {}
Some(val) => { Some(val) => {
let chars: Vec<char> = val.chars().collect(); let chars: Vec<char> = val.chars().collect();
match parse_style(&chars) { match parse_style(&chars) {
Ok(s) => { settings.body_numbering = s; } Ok(s) => {
Err(message) => { errs.push(message); } settings.body_numbering = s;
}
Err(message) => {
errs.push(message);
}
} }
} }
} }
match opts.opt_str("f") { match opts.opt_str("f") {
None => {}, None => {}
Some(val) => { Some(val) => {
let chars: Vec<char> = val.chars().collect(); let chars: Vec<char> = val.chars().collect();
match parse_style(&chars) { match parse_style(&chars) {
Ok(s) => { settings.footer_numbering = s; } Ok(s) => {
Err(message) => { errs.push(message); } settings.footer_numbering = s;
}
Err(message) => {
errs.push(message);
}
} }
} }
} }
match opts.opt_str("h") { match opts.opt_str("h") {
None => {}, None => {}
Some(val) => { Some(val) => {
let chars: Vec<char> = val.chars().collect(); let chars: Vec<char> = val.chars().collect();
match parse_style(&chars) { match parse_style(&chars) {
Ok(s) => { settings.header_numbering = s; } Ok(s) => {
Err(message) => { errs.push(message); } settings.header_numbering = s;
}
Err(message) => {
errs.push(message);
}
} }
} }
} }
@ -77,7 +99,7 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
None => { None => {
errs.push(String::from("Illegal value for -i")); errs.push(String::from("Illegal value for -i"));
} }
Some(num) => { settings.line_increment = num } Some(num) => settings.line_increment = num,
} }
} }
} }
@ -89,7 +111,7 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
None => { None => {
errs.push(String::from("Illegal value for -w")); errs.push(String::from("Illegal value for -w"));
} }
Some(num) => { settings.number_width = num } Some(num) => settings.number_width = num,
} }
} }
} }
@ -101,7 +123,7 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
None => { None => {
errs.push(String::from("Illegal value for -v")); errs.push(String::from("Illegal value for -v"));
} }
Some(num) => { settings.starting_line_number = num } Some(num) => settings.starting_line_number = num,
} }
} }
} }
@ -113,7 +135,7 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
None => { None => {
errs.push(String::from("Illegal value for -l")); errs.push(String::from("Illegal value for -l"));
} }
Some(num) => { settings.join_blank_lines = num } Some(num) => settings.join_blank_lines = num,
} }
} }
} }

View file

@ -13,14 +13,14 @@
extern crate aho_corasick; extern crate aho_corasick;
extern crate getopts; extern crate getopts;
extern crate memchr; extern crate memchr;
extern crate regex_syntax;
extern crate regex; extern crate regex;
extern crate regex_syntax;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader, Read, stdin}; use std::io::{stdin, BufRead, BufReader, Read};
use std::iter::repeat; use std::iter::repeat;
use std::path::Path; use std::path::Path;
@ -49,7 +49,7 @@ pub struct Settings {
number_format: NumberFormat, number_format: NumberFormat,
renumber: bool, renumber: bool,
// The string appended to each line number output. // The string appended to each line number output.
number_separator: String number_separator: String,
} }
// NumberingStyle stores which lines are to be numbered. // NumberingStyle stores which lines are to be numbered.
@ -62,7 +62,7 @@ enum NumberingStyle {
NumberForAll, NumberForAll,
NumberForNonEmpty, NumberForNonEmpty,
NumberForNone, NumberForNone,
NumberForRegularExpression(regex::Regex) NumberForRegularExpression(regex::Regex),
} }
// NumberFormat specifies how line numbers are output within their allocated // NumberFormat specifies how line numbers are output within their allocated
@ -77,17 +77,71 @@ enum NumberFormat {
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = getopts::Options::new(); let mut opts = getopts::Options::new();
opts.optopt("b", "body-numbering", "use STYLE for numbering body lines", "STYLE"); opts.optopt(
opts.optopt("d", "section-delimiter", "use CC for separating logical pages", "CC"); "b",
opts.optopt("f", "footer-numbering", "use STYLE for numbering footer lines", "STYLE"); "body-numbering",
opts.optopt("h", "header-numbering", "use STYLE for numbering header lines", "STYLE"); "use STYLE for numbering body lines",
opts.optopt("i", "line-increment", "line number increment at each line", ""); "STYLE",
opts.optopt("l", "join-blank-lines", "group of NUMBER empty lines counted as one", "NUMBER"); );
opts.optopt("n", "number-format", "insert line numbers according to FORMAT", "FORMAT"); opts.optopt(
opts.optflag("p", "no-renumber", "do not reset line numbers at logical pages"); "d",
opts.optopt("s", "number-separator", "add STRING after (possible) line number", "STRING"); "section-delimiter",
opts.optopt("v", "starting-line-number", "first line number on each logical page", "NUMBER"); "use CC for separating logical pages",
opts.optopt("w", "number-width", "use NUMBER columns for line numbers", "NUMBER"); "CC",
);
opts.optopt(
"f",
"footer-numbering",
"use STYLE for numbering footer lines",
"STYLE",
);
opts.optopt(
"h",
"header-numbering",
"use STYLE for numbering header lines",
"STYLE",
);
opts.optopt(
"i",
"line-increment",
"line number increment at each line",
"",
);
opts.optopt(
"l",
"join-blank-lines",
"group of NUMBER empty lines counted as one",
"NUMBER",
);
opts.optopt(
"n",
"number-format",
"insert line numbers according to FORMAT",
"FORMAT",
);
opts.optflag(
"p",
"no-renumber",
"do not reset line numbers at logical pages",
);
opts.optopt(
"s",
"number-separator",
"add STRING after (possible) line number",
"STRING",
);
opts.optopt(
"v",
"starting-line-number",
"first line number on each logical page",
"NUMBER",
);
opts.optopt(
"w",
"number-width",
"use NUMBER columns for line numbers",
"NUMBER",
);
opts.optflag("", "help", "display this help and exit"); opts.optflag("", "help", "display this help and exit");
opts.optflag("V", "version", "version"); opts.optflag("V", "version", "version");
@ -107,11 +161,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
}; };
let given_options = match opts.parse(&args[1..]) { let given_options = match opts.parse(&args[1..]) {
Ok (m) => { m } Ok(m) => m,
Err(f) => { Err(f) => {
show_error!("{}", f); show_error!("{}", f);
print_usage(&opts); print_usage(&opts);
return 1 return 1;
} }
}; };
@ -119,7 +173,10 @@ pub fn uumain(args: Vec<String>) -> i32 {
print_usage(&opts); print_usage(&opts);
return 0; return 0;
} }
if given_options.opt_present("version") { version(); return 0; } if given_options.opt_present("version") {
version();
return 0;
}
// Update the settings from the command line options, and terminate the // Update the settings from the command line options, and terminate the
// program if some options could not successfully be parsed. // program if some options could not successfully be parsed.
@ -140,7 +197,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
// If both file names and '-' are specified, we choose to treat first all // If both file names and '-' are specified, we choose to treat first all
// regular files, and then read from stdin last. // regular files, and then read from stdin last.
read_stdin = true; read_stdin = true;
continue continue;
} }
let path = Path::new(file); let path = Path::new(file);
let reader = File::open(path).unwrap(); let reader = File::open(path).unwrap();
@ -156,7 +213,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
} }
// nl implements the main functionality for an individual buffer. // nl implements the main functionality for an individual buffer.
fn nl<T: Read> (reader: &mut BufReader<T>, settings: &Settings) { fn nl<T: Read>(reader: &mut BufReader<T>, settings: &Settings) {
let regexp: regex::Regex = regex::Regex::new(r".?").unwrap(); let regexp: regex::Regex = regex::Regex::new(r".?").unwrap();
let mut line_no = settings.starting_line_number; let mut line_no = settings.starting_line_number;
// The current line number's width as a string. Using to_string is inefficient // The current line number's width as a string. Using to_string is inefficient
@ -169,14 +226,14 @@ fn nl<T: Read> (reader: &mut BufReader<T>, settings: &Settings) {
let mut empty_line_count: u64 = 0; let mut empty_line_count: u64 = 0;
let fill_char = match settings.number_format { let fill_char = match settings.number_format {
NumberFormat::RightZero => '0', NumberFormat::RightZero => '0',
_ => ' ' _ => ' ',
}; };
// Initially, we use the body's line counting settings // Initially, we use the body's line counting settings
let mut regex_filter = match settings.body_numbering { let mut regex_filter = match settings.body_numbering {
NumberingStyle::NumberForRegularExpression(ref re) => re, NumberingStyle::NumberForRegularExpression(ref re) => re,
_ => &regexp, _ => &regexp,
}; };
let mut line_filter : fn(&str, &regex::Regex) -> bool = pass_regex; let mut line_filter: fn(&str, &regex::Regex) -> bool = pass_regex;
for mut l in reader.lines().map(|r| r.unwrap()) { for mut l in reader.lines().map(|r| r.unwrap()) {
// Sanitize the string. We want to print the newline ourselves. // Sanitize the string. We want to print the newline ourselves.
if !l.is_empty() && l.chars().rev().next().unwrap() == '\n' { if !l.is_empty() && l.chars().rev().next().unwrap() == '\n' {
@ -198,8 +255,7 @@ fn nl<T: Read> (reader: &mut BufReader<T>, settings: &Settings) {
// If we have already seen three groups (corresponding to // If we have already seen three groups (corresponding to
// a header) or the current char does not form part of // a header) or the current char does not form part of
// a new group, then this line is not a segment indicator. // a new group, then this line is not a segment indicator.
if matched_groups >= 3 if matched_groups >= 3 || settings.section_delimiter[if odd { 1 } else { 0 }] != c {
|| settings.section_delimiter[if odd { 1 } else { 0 }] != c {
matched_groups = 0; matched_groups = 0;
break; break;
} }
@ -230,22 +286,18 @@ fn nl<T: Read> (reader: &mut BufReader<T>, settings: &Settings) {
line_no_threshold = 10u64.pow(line_no_width as u32); line_no_threshold = 10u64.pow(line_no_width as u32);
} }
&settings.header_numbering &settings.header_numbering
}, }
1 => { 1 => &settings.footer_numbering,
&settings.footer_numbering
},
// The only option left is 2, but rust wants // The only option left is 2, but rust wants
// a catch-all here. // a catch-all here.
_ => { _ => &settings.body_numbering,
&settings.body_numbering
}
} { } {
NumberingStyle::NumberForAll => { NumberingStyle::NumberForAll => {
line_filter = pass_all; line_filter = pass_all;
}, }
NumberingStyle::NumberForNonEmpty => { NumberingStyle::NumberForNonEmpty => {
line_filter = pass_nonempty; line_filter = pass_nonempty;
}, }
NumberingStyle::NumberForNone => { NumberingStyle::NumberForNone => {
line_filter = pass_none; line_filter = pass_none;
} }
@ -267,7 +319,8 @@ fn nl<T: Read> (reader: &mut BufReader<T>, settings: &Settings) {
empty_line_count = 0; empty_line_count = 0;
} }
if !line_filter(&line, regex_filter) if !line_filter(&line, regex_filter)
|| ( empty_line_count > 0 && empty_line_count < settings.join_blank_lines) { || (empty_line_count > 0 && empty_line_count < settings.join_blank_lines)
{
// No number is printed for this line. Either we did not // No number is printed for this line. Either we did not
// want to print one in the first place, or it is a blank // want to print one in the first place, or it is a blank
// line but we are still collecting more blank lines via // line but we are still collecting more blank lines via
@ -286,12 +339,14 @@ fn nl<T: Read> (reader: &mut BufReader<T>, settings: &Settings) {
} }
let fill: String = repeat(fill_char).take(w).collect(); let fill: String = repeat(fill_char).take(w).collect();
match settings.number_format { match settings.number_format {
NumberFormat::Left => { NumberFormat::Left => println!(
println!("{1}{0}{2}{3}", fill, line_no, settings.number_separator, line) "{1}{0}{2}{3}",
}, fill, line_no, settings.number_separator, line
_ => { ),
println!("{0}{1}{2}{3}", fill, line_no, settings.number_separator, line) _ => println!(
} "{0}{1}{2}{3}",
fill, line_no, settings.number_separator, line
),
} }
// Now update the variables for the (potential) next // Now update the variables for the (potential) next
// line. // line.
@ -301,7 +356,6 @@ fn nl<T: Read> (reader: &mut BufReader<T>, settings: &Settings) {
line_no_threshold *= 10; line_no_threshold *= 10;
line_no_width += 1; line_no_width += 1;
} }
} }
} }

View file

@ -15,8 +15,8 @@ extern crate libc;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use libc::{c_char, signal, dup2, execvp}; use libc::{c_char, execvp, signal, dup2};
use libc::{SIG_IGN, SIGHUP}; use libc::{SIGHUP, SIG_IGN};
use std::ffi::CString; use std::ffi::CString;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io::Error; use std::io::Error;
@ -29,12 +29,14 @@ static NAME: &'static str = "nohup";
static VERSION: &'static str = env!("CARGO_PKG_VERSION"); static VERSION: &'static str = env!("CARGO_PKG_VERSION");
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
extern { extern "C" {
fn _vprocmgr_detach_from_console(flags: u32) -> *const libc::c_int; fn _vprocmgr_detach_from_console(flags: u32) -> *const libc::c_int;
} }
#[cfg(any(target_os = "linux", target_os = "freebsd"))] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
unsafe fn _vprocmgr_detach_from_console(_: u32) -> *const libc::c_int { std::ptr::null() } unsafe fn _vprocmgr_detach_from_console(_: u32) -> *const libc::c_int {
std::ptr::null()
}
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = getopts::Options::new(); let mut opts = getopts::Options::new();
@ -47,37 +49,47 @@ pub fn uumain(args: Vec<String>) -> i32 {
Err(f) => { Err(f) => {
show_error!("{}", f); show_error!("{}", f);
show_usage(&opts); show_usage(&opts);
return 1 return 1;
} }
}; };
if matches.opt_present("V") { println!("{} {}", NAME, VERSION); return 0 } if matches.opt_present("V") {
if matches.opt_present("h") { show_usage(&opts); return 0 } println!("{} {}", NAME, VERSION);
return 0;
}
if matches.opt_present("h") {
show_usage(&opts);
return 0;
}
if matches.free.is_empty() { if matches.free.is_empty() {
show_error!("Missing operand: COMMAND"); show_error!("Missing operand: COMMAND");
println!("Try `{} --help` for more information.", NAME); println!("Try `{} --help` for more information.", NAME);
return 1 return 1;
} }
replace_fds(); replace_fds();
unsafe { signal(SIGHUP, SIG_IGN) }; unsafe { signal(SIGHUP, SIG_IGN) };
if unsafe { _vprocmgr_detach_from_console(0) } != std::ptr::null() { crash!(2, "Cannot detach from console")}; if unsafe { _vprocmgr_detach_from_console(0) } != std::ptr::null() {
crash!(2, "Cannot detach from console")
};
let cstrs: Vec<CString> = matches.free.iter().map(|x| CString::new(x.as_bytes()).unwrap()).collect(); let cstrs: Vec<CString> = matches
.free
.iter()
.map(|x| CString::new(x.as_bytes()).unwrap())
.collect();
let mut args: Vec<*const c_char> = cstrs.iter().map(|s| s.as_ptr()).collect(); let mut args: Vec<*const c_char> = cstrs.iter().map(|s| s.as_ptr()).collect();
args.push(std::ptr::null()); args.push(std::ptr::null());
unsafe { execvp(args[0], args.as_mut_ptr())} unsafe { execvp(args[0], args.as_mut_ptr()) }
} }
fn replace_fds() { fn replace_fds() {
if is_stdin_interactive() { if is_stdin_interactive() {
let new_stdin = match File::open(Path::new("/dev/null")) { let new_stdin = match File::open(Path::new("/dev/null")) {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => crash!(2, "Cannot replace STDIN: {}", e),
crash!(2, "Cannot replace STDIN: {}", e)
}
}; };
if unsafe { dup2(new_stdin.as_raw_fd(), 0) } != 0 { if unsafe { dup2(new_stdin.as_raw_fd(), 0) } != 0 {
crash!(2, "Cannot replace STDIN: {}", Error::last_os_error()) crash!(2, "Cannot replace STDIN: {}", Error::last_os_error())
@ -101,33 +113,42 @@ fn replace_fds() {
} }
fn find_stdout() -> File { fn find_stdout() -> File {
match OpenOptions::new().write(true).create(true).append(true).open(Path::new("nohup.out")) { match OpenOptions::new()
.write(true)
.create(true)
.append(true)
.open(Path::new("nohup.out"))
{
Ok(t) => { Ok(t) => {
show_warning!("Output is redirected to: nohup.out"); show_warning!("Output is redirected to: nohup.out");
t t
}, }
Err(e) => { Err(e) => {
let home = match env::var("HOME") { let home = match env::var("HOME") {
Err(_) => crash!(2, "Cannot replace STDOUT: {}", e), Err(_) => crash!(2, "Cannot replace STDOUT: {}", e),
Ok(h) => h Ok(h) => h,
}; };
let mut homeout = PathBuf::from(home); let mut homeout = PathBuf::from(home);
homeout.push("nohup.out"); homeout.push("nohup.out");
match OpenOptions::new().write(true).create(true).append(true).open(&homeout) { match OpenOptions::new()
.write(true)
.create(true)
.append(true)
.open(&homeout)
{
Ok(t) => { Ok(t) => {
show_warning!("Output is redirected to: {:?}", homeout); show_warning!("Output is redirected to: {:?}", homeout);
t t
},
Err(e) => {
crash!(2, "Cannot replace STDOUT: {}", e)
} }
Err(e) => crash!(2, "Cannot replace STDOUT: {}", e),
} }
} }
} }
} }
fn show_usage(opts: &getopts::Options) { fn show_usage(opts: &getopts::Options) {
let msg = format!("{0} {1} let msg = format!(
"{0} {1}
Usage: Usage:
{0} COMMAND [ARG]... {0} COMMAND [ARG]...
@ -137,7 +158,9 @@ Run COMMAND ignoring hangup signals.
If standard input is terminal, it'll be replaced with /dev/null. If standard input is terminal, it'll be replaced with /dev/null.
If standard output is terminal, it'll be appended to nohup.out instead, If standard output is terminal, it'll be appended to nohup.out instead,
or $HOME/nohup.out, if nohup.out open failed. or $HOME/nohup.out, if nohup.out open failed.
If standard error is terminal, it'll be redirected to stdout.", NAME, VERSION); If standard error is terminal, it'll be redirected to stdout.",
NAME, VERSION
);
print!("{}", opts.usage(&msg)); print!("{}", opts.usage(&msg));
} }

View file

@ -29,14 +29,17 @@ pub const _SC_NPROCESSORS_CONF: libc::c_int = 57;
#[cfg(target_os = "netbsd")] #[cfg(target_os = "netbsd")]
pub const _SC_NPROCESSORS_CONF: libc::c_int = 1001; pub const _SC_NPROCESSORS_CONF: libc::c_int = 1001;
static NAME: &'static str = "nproc"; static NAME: &'static str = "nproc";
static VERSION: &'static str = env!("CARGO_PKG_VERSION"); static VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = getopts::Options::new(); let mut opts = getopts::Options::new();
opts.optflag("", "all", "print the number of cores available to the system"); opts.optflag(
"",
"all",
"print the number of cores available to the system",
);
opts.optopt("", "ignore", "ignore up to N cores", "N"); opts.optopt("", "ignore", "ignore up to N cores", "N");
opts.optflag("h", "help", "display this help and exit"); opts.optflag("h", "help", "display this help and exit");
opts.optflag("V", "version", "output version information and exit"); opts.optflag("V", "version", "output version information and exit");
@ -55,12 +58,15 @@ pub fn uumain(args: Vec<String>) -> i32 {
} }
if matches.opt_present("help") { if matches.opt_present("help") {
let msg = format!("{0} {1} let msg = format!(
"{0} {1}
Usage: Usage:
{0} [OPTIONS]... {0} [OPTIONS]...
Print the number of cores available to the current process.", NAME, VERSION); Print the number of cores available to the current process.",
NAME, VERSION
);
print!("{}", opts.usage(&msg)); print!("{}", opts.usage(&msg));
return 0; return 0;
@ -74,16 +80,16 @@ Print the number of cores available to the current process.", NAME, VERSION);
return 1; return 1;
} }
}, },
None => 0 None => 0,
}; };
if !matches.opt_present("all") { if !matches.opt_present("all") {
ignore += match env::var("OMP_NUM_THREADS") { ignore += match env::var("OMP_NUM_THREADS") {
Ok(threadstr) => match threadstr.parse() { Ok(threadstr) => match threadstr.parse() {
Ok(num) => num, Ok(num) => num,
Err(_)=> 0 Err(_) => 0,
}, },
Err(_) => 0 Err(_) => 0,
}; };
} }
@ -102,10 +108,7 @@ Print the number of cores available to the current process.", NAME, VERSION);
0 0
} }
#[cfg(any(target_os = "linux", #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd", target_os = "netbsd"))]
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd"))]
fn num_cpus_all() -> usize { fn num_cpus_all() -> usize {
let nprocs = unsafe { libc::sysconf(_SC_NPROCESSORS_CONF) }; let nprocs = unsafe { libc::sysconf(_SC_NPROCESSORS_CONF) };
if nprocs == 1 { if nprocs == 1 {
@ -113,14 +116,16 @@ fn num_cpus_all() -> usize {
// However, we want to guarantee that `nproc --all` >= `nproc`. // However, we want to guarantee that `nproc --all` >= `nproc`.
num_cpus::get() num_cpus::get()
} else { } else {
if nprocs > 0 { nprocs as usize } else { 1 } if nprocs > 0 {
nprocs as usize
} else {
1
}
} }
} }
// Other platform(e.g., windows), num_cpus::get() directly. // Other platform(e.g., windows), num_cpus::get() directly.
#[cfg(not(any(target_os = "linux", #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "freebsd",
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd")))] target_os = "netbsd")))]
fn num_cpus_all() -> usize { fn num_cpus_all() -> usize {
num_cpus::get() num_cpus::get()

View file

@ -75,8 +75,7 @@ fn parse_suffix(s: String) -> Result<(f64, Option<Suffix>)> {
Some('T') => Ok((Some(Suffix::T), 1)), Some('T') => Ok((Some(Suffix::T), 1)),
Some('P') => Ok((Some(Suffix::P), 1)), Some('P') => Ok((Some(Suffix::P), 1)),
Some('E') => Ok((Some(Suffix::E), 1)), Some('E') => Ok((Some(Suffix::E), 1)),
Some('i') => { Some('i') => match iter.next_back() {
match iter.next_back() {
Some('K') => Ok((Some(Suffix::Ki), 2)), Some('K') => Ok((Some(Suffix::Ki), 2)),
Some('M') => Ok((Some(Suffix::Mi), 2)), Some('M') => Ok((Some(Suffix::Mi), 2)),
Some('G') => Ok((Some(Suffix::Gi), 2)), Some('G') => Ok((Some(Suffix::Gi), 2)),
@ -84,14 +83,13 @@ fn parse_suffix(s: String) -> Result<(f64, Option<Suffix>)> {
Some('P') => Ok((Some(Suffix::Pi), 2)), Some('P') => Ok((Some(Suffix::Pi), 2)),
Some('E') => Ok((Some(Suffix::Ei), 2)), Some('E') => Ok((Some(Suffix::Ei), 2)),
_ => Err("Failed to parse suffix"), _ => Err("Failed to parse suffix"),
} },
}
_ => Ok((None, 0)), _ => Ok((None, 0)),
}?; }?;
let number = s[..s.len() - suffix_len].parse::<f64>().map_err(|err| { let number = s[..s.len() - suffix_len]
err.to_string() .parse::<f64>()
})?; .map_err(|err| err.to_string())?;
Ok((number, suffix)) Ok((number, suffix))
} }
@ -120,37 +118,35 @@ struct NumfmtOptions {
fn remove_suffix(i: f64, s: Option<Suffix>, u: &Unit) -> Result<f64> { fn remove_suffix(i: f64, s: Option<Suffix>, u: &Unit) -> Result<f64> {
match (s, u) { match (s, u) {
(None, _) => Ok(i), (None, _) => Ok(i),
(Some(Suffix::K), &Unit::Auto) | (Some(Suffix::K), &Unit::Auto) | (Some(Suffix::K), &Unit::Si) => Ok(i * 1000.),
(Some(Suffix::K), &Unit::Si) => Ok(i * 1000.), (Some(Suffix::M), &Unit::Auto) | (Some(Suffix::M), &Unit::Si) => Ok(i * 1000_000.),
(Some(Suffix::M), &Unit::Auto) | (Some(Suffix::G), &Unit::Auto) | (Some(Suffix::G), &Unit::Si) => Ok(i * 1000_000_000.),
(Some(Suffix::M), &Unit::Si) => Ok(i * 1000_000.), (Some(Suffix::T), &Unit::Auto) | (Some(Suffix::T), &Unit::Si) => Ok(i * 1000_000_000_000.),
(Some(Suffix::G), &Unit::Auto) | (Some(Suffix::P), &Unit::Auto) | (Some(Suffix::P), &Unit::Si) => {
(Some(Suffix::G), &Unit::Si) => Ok(i * 1000_000_000.), Ok(i * 1000_000_000_000_000.)
(Some(Suffix::T), &Unit::Auto) | }
(Some(Suffix::T), &Unit::Si) => Ok(i * 1000_000_000_000.), (Some(Suffix::E), &Unit::Auto) | (Some(Suffix::E), &Unit::Si) => {
(Some(Suffix::P), &Unit::Auto) | Ok(i * 1000_000_000_000_000_000.)
(Some(Suffix::P), &Unit::Si) => Ok(i * 1000_000_000_000_000.), }
(Some(Suffix::E), &Unit::Auto) |
(Some(Suffix::E), &Unit::Si) => Ok(i * 1000_000_000_000_000_000.),
(Some(Suffix::Ki), &Unit::Auto) | (Some(Suffix::Ki), &Unit::Auto)
(Some(Suffix::Ki), &Unit::IecI) | | (Some(Suffix::Ki), &Unit::IecI)
(Some(Suffix::K), &Unit::Iec) => Ok(i * 1024.), | (Some(Suffix::K), &Unit::Iec) => Ok(i * 1024.),
(Some(Suffix::Mi), &Unit::Auto) | (Some(Suffix::Mi), &Unit::Auto)
(Some(Suffix::Mi), &Unit::IecI) | | (Some(Suffix::Mi), &Unit::IecI)
(Some(Suffix::M), &Unit::Iec) => Ok(i * 1048576.), | (Some(Suffix::M), &Unit::Iec) => Ok(i * 1048576.),
(Some(Suffix::Gi), &Unit::Auto) | (Some(Suffix::Gi), &Unit::Auto)
(Some(Suffix::Gi), &Unit::IecI) | | (Some(Suffix::Gi), &Unit::IecI)
(Some(Suffix::G), &Unit::Iec) => Ok(i * 1073741824.), | (Some(Suffix::G), &Unit::Iec) => Ok(i * 1073741824.),
(Some(Suffix::Ti), &Unit::Auto) | (Some(Suffix::Ti), &Unit::Auto)
(Some(Suffix::Ti), &Unit::IecI) | | (Some(Suffix::Ti), &Unit::IecI)
(Some(Suffix::T), &Unit::Iec) => Ok(i * 1099511627776.), | (Some(Suffix::T), &Unit::Iec) => Ok(i * 1099511627776.),
(Some(Suffix::Pi), &Unit::Auto) | (Some(Suffix::Pi), &Unit::Auto)
(Some(Suffix::Pi), &Unit::IecI) | | (Some(Suffix::Pi), &Unit::IecI)
(Some(Suffix::P), &Unit::Iec) => Ok(i * 1125899906842624.), | (Some(Suffix::P), &Unit::Iec) => Ok(i * 1125899906842624.),
(Some(Suffix::Ei), &Unit::Auto) | (Some(Suffix::Ei), &Unit::Auto)
(Some(Suffix::Ei), &Unit::IecI) | | (Some(Suffix::Ei), &Unit::IecI)
(Some(Suffix::E), &Unit::Iec) => Ok(i * 1152921504606846976.), | (Some(Suffix::E), &Unit::Iec) => Ok(i * 1152921504606846976.),
(_, _) => Err("This suffix is unsupported for specified unit".to_owned()), (_, _) => Err("This suffix is unsupported for specified unit".to_owned()),
} }
@ -163,25 +159,19 @@ fn transform_from(s: String, unit: &Unit) -> Result<String> {
fn consider_suffix(i: f64, u: &Unit) -> Result<(f64, Option<Suffix>)> { fn consider_suffix(i: f64, u: &Unit) -> Result<(f64, Option<Suffix>)> {
match *u { match *u {
Unit::Si => { Unit::Si => match i {
match i {
_ if i < 1000. => Ok((i, None)), _ if i < 1000. => Ok((i, None)),
_ if i < 1000_000. => Ok((i / 1000., Some(Suffix::K))), _ if i < 1000_000. => Ok((i / 1000., Some(Suffix::K))),
_ if i < 1000_000_000. => Ok((i / 1000_000., Some(Suffix::M))), _ if i < 1000_000_000. => Ok((i / 1000_000., Some(Suffix::M))),
_ if i < 1000_000_000_000. => Ok((i / 1000_000_000., Some(Suffix::G))), _ if i < 1000_000_000_000. => Ok((i / 1000_000_000., Some(Suffix::G))),
_ if i < 1000_000_000_000_000. => Ok((i / 1000_000_000_000., Some(Suffix::T))), _ if i < 1000_000_000_000_000. => Ok((i / 1000_000_000_000., Some(Suffix::T))),
_ if i < 1000_000_000_000_000_000. => Ok( _ if i < 1000_000_000_000_000_000. => Ok((i / 1000_000_000_000_000., Some(Suffix::P))),
(i / 1000_000_000_000_000., Some(Suffix::P)), _ if i < 1000_000_000_000_000_000_000. => {
), Ok((i / 1000_000_000_000_000_000., Some(Suffix::E)))
_ if i < 1000_000_000_000_000_000_000. => Ok(( }
i / 1000_000_000_000_000_000.,
Some(Suffix::E),
)),
_ => Err("Number is too big and unsupported".to_owned()), _ => Err("Number is too big and unsupported".to_owned()),
} },
} Unit::Iec => match i {
Unit::Iec => {
match i {
_ if i < 1024. => Ok((i, None)), _ if i < 1024. => Ok((i, None)),
_ if i < 1048576. => Ok((i / 1024., Some(Suffix::K))), _ if i < 1048576. => Ok((i / 1024., Some(Suffix::K))),
_ if i < 1073741824. => Ok((i / 1048576., Some(Suffix::M))), _ if i < 1073741824. => Ok((i / 1048576., Some(Suffix::M))),
@ -190,22 +180,17 @@ fn consider_suffix(i: f64, u: &Unit) -> Result<(f64, Option<Suffix>)> {
_ if i < 1152921504606846976. => Ok((i / 1125899906842624., Some(Suffix::P))), _ if i < 1152921504606846976. => Ok((i / 1125899906842624., Some(Suffix::P))),
_ if i < 1180591620717411303424. => Ok((i / 1152921504606846976., Some(Suffix::E))), _ if i < 1180591620717411303424. => Ok((i / 1152921504606846976., Some(Suffix::E))),
_ => Err("Number is too big and unsupported".to_owned()), _ => Err("Number is too big and unsupported".to_owned()),
} },
} Unit::IecI => match i {
Unit::IecI => {
match i {
_ if i < 1024. => Ok((i, None)), _ if i < 1024. => Ok((i, None)),
_ if i < 1048576. => Ok((i / 1024., Some(Suffix::Ki))), _ if i < 1048576. => Ok((i / 1024., Some(Suffix::Ki))),
_ if i < 1073741824. => Ok((i / 1048576., Some(Suffix::Mi))), _ if i < 1073741824. => Ok((i / 1048576., Some(Suffix::Mi))),
_ if i < 1099511627776. => Ok((i / 1073741824., Some(Suffix::Gi))), _ if i < 1099511627776. => Ok((i / 1073741824., Some(Suffix::Gi))),
_ if i < 1125899906842624. => Ok((i / 1099511627776., Some(Suffix::Ti))), _ if i < 1125899906842624. => Ok((i / 1099511627776., Some(Suffix::Ti))),
_ if i < 1152921504606846976. => Ok((i / 1125899906842624., Some(Suffix::Pi))), _ if i < 1152921504606846976. => Ok((i / 1125899906842624., Some(Suffix::Pi))),
_ if i < 1180591620717411303424. => Ok( _ if i < 1180591620717411303424. => Ok((i / 1152921504606846976., Some(Suffix::Ei))),
(i / 1152921504606846976., Some(Suffix::Ei)),
),
_ => Err("Number is too big and unsupported".to_owned()), _ => Err("Number is too big and unsupported".to_owned()),
} },
}
Unit::Auto => Err("Unit 'auto' isn't supported with --to options".to_owned()), Unit::Auto => Err("Unit 'auto' isn't supported with --to options".to_owned()),
} }
} }

View file

@ -1,7 +1,7 @@
// workaround until https://github.com/BurntSushi/byteorder/issues/41 has been fixed // workaround until https://github.com/BurntSushi/byteorder/issues/41 has been fixed
// based on: https://github.com/netvl/immeta/blob/4460ee/src/utils.rs#L76 // based on: https://github.com/netvl/immeta/blob/4460ee/src/utils.rs#L76
use byteorder::{NativeEndian, LittleEndian, BigEndian}; use byteorder::{BigEndian, LittleEndian, NativeEndian};
use byteorder::ByteOrder as ByteOrderTrait; use byteorder::ByteOrder as ByteOrderTrait;
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]

View file

@ -35,18 +35,18 @@ impl fmt::Debug for FormatWriter {
&FormatWriter::IntWriter(ref p) => { &FormatWriter::IntWriter(ref p) => {
try!(f.write_str("IntWriter:")); try!(f.write_str("IntWriter:"));
fmt::Pointer::fmt(p, f) fmt::Pointer::fmt(p, f)
}, }
&FormatWriter::FloatWriter(ref p) => { &FormatWriter::FloatWriter(ref p) => {
try!(f.write_str("FloatWriter:")); try!(f.write_str("FloatWriter:"));
fmt::Pointer::fmt(p, f) fmt::Pointer::fmt(p, f)
}, }
&FormatWriter::MultibyteWriter(ref p) => { &FormatWriter::MultibyteWriter(ref p) => {
try!(f.write_str("MultibyteWriter:")); try!(f.write_str("MultibyteWriter:"));
fmt::Pointer::fmt(&(*p as *const ()), f) fmt::Pointer::fmt(&(*p as *const ()), f)
},
} }
} }
} }
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct FormatterItemInfo { pub struct FormatterItemInfo {

View file

@ -7,7 +7,10 @@ use half::f16;
/// Processes an input and provides access to the data read in various formats /// Processes an input and provides access to the data read in various formats
/// ///
/// Currently only useful if the input implements `PeekRead`. /// Currently only useful if the input implements `PeekRead`.
pub struct InputDecoder<'a, I> where I: 'a { pub struct InputDecoder<'a, I>
where
I: 'a,
{
/// The input from which data is read /// The input from which data is read
input: &'a mut I, input: &'a mut I,
@ -28,9 +31,16 @@ pub struct InputDecoder<'a, I> where I: 'a {
impl<'a, I> InputDecoder<'a, I> { impl<'a, I> InputDecoder<'a, I> {
/// Creates a new `InputDecoder` with an allocated buffer of `normal_length` + `peek_length` bytes. /// Creates a new `InputDecoder` with an allocated buffer of `normal_length` + `peek_length` bytes.
/// `byte_order` determines how to read multibyte formats from the buffer. /// `byte_order` determines how to read multibyte formats from the buffer.
pub fn new(input: &mut I, normal_length: usize, peek_length: usize, byte_order: ByteOrder) -> InputDecoder<I> { pub fn new(
input: &mut I,
normal_length: usize,
peek_length: usize,
byte_order: ByteOrder,
) -> InputDecoder<I> {
let mut bytes: Vec<u8> = Vec::with_capacity(normal_length + peek_length); let mut bytes: Vec<u8> = Vec::with_capacity(normal_length + peek_length);
unsafe { bytes.set_len(normal_length + peek_length); } // fast but uninitialized unsafe {
bytes.set_len(normal_length + peek_length);
} // fast but uninitialized
InputDecoder { InputDecoder {
input: input, input: input,
@ -43,12 +53,16 @@ impl<'a, I> InputDecoder<'a, I> {
} }
} }
impl<'a, I> InputDecoder<'a, I>
impl<'a, I> InputDecoder<'a, I> where I: PeekRead { where
I: PeekRead,
{
/// calls `peek_read` on the internal stream to (re)fill the buffer. Returns a /// calls `peek_read` on the internal stream to (re)fill the buffer. Returns a
/// MemoryDecoder providing access to the result or returns an i/o error. /// MemoryDecoder providing access to the result or returns an i/o error.
pub fn peek_read(&mut self) -> io::Result<MemoryDecoder> { pub fn peek_read(&mut self) -> io::Result<MemoryDecoder> {
match self.input.peek_read(self.data.as_mut_slice(), self.reserved_peek_length) { match self.input
.peek_read(self.data.as_mut_slice(), self.reserved_peek_length)
{
Ok((n, p)) => { Ok((n, p)) => {
self.used_normal_length = n; self.used_normal_length = n;
self.used_peek_length = p; self.used_peek_length = p;
@ -58,14 +72,16 @@ impl<'a, I> InputDecoder<'a, I> where I: PeekRead {
used_peek_length: self.used_peek_length, used_peek_length: self.used_peek_length,
byte_order: self.byte_order, byte_order: self.byte_order,
}) })
}, }
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
} }
impl<'a, I> HasError for InputDecoder<'a, I> where I: HasError { impl<'a, I> HasError for InputDecoder<'a, I>
where
I: HasError,
{
/// calls has_error on the internal stream. /// calls has_error on the internal stream.
fn has_error(&self) -> bool { fn has_error(&self) -> bool {
self.input.has_error() self.input.has_error()
@ -87,7 +103,7 @@ pub struct MemoryDecoder<'a> {
impl<'a> MemoryDecoder<'a> { impl<'a> MemoryDecoder<'a> {
/// Set a part of the internal buffer to zero. /// Set a part of the internal buffer to zero.
/// access to the whole buffer is possible, not just to the valid data. /// access to the whole buffer is possible, not just to the valid data.
pub fn zero_out_buffer(&mut self, start:usize, end:usize) { pub fn zero_out_buffer(&mut self, start: usize, end: usize) {
for i in start..end { for i in start..end {
self.data[i] = 0; self.data[i] = 0;
} }
@ -128,7 +144,9 @@ impl<'a> MemoryDecoder<'a> {
/// Returns a f32/f64 from the internal buffer at position `start`. /// Returns a f32/f64 from the internal buffer at position `start`.
pub fn read_float(&self, start: usize, byte_size: usize) -> f64 { pub fn read_float(&self, start: usize, byte_size: usize) -> f64 {
match byte_size { match byte_size {
2 => f64::from(f16::from_bits(self.byte_order.read_u16(&self.data[start..start + 2]))), 2 => f64::from(f16::from_bits(
self.byte_order.read_u16(&self.data[start..start + 2]),
)),
4 => self.byte_order.read_f32(&self.data[start..start + 4]) as f64, 4 => self.byte_order.read_f32(&self.data[start..start + 4]) as f64,
8 => self.byte_order.read_f64(&self.data[start..start + 8]), 8 => self.byte_order.read_f64(&self.data[start..start + 8]),
_ => panic!("Invalid byte_size: {}", byte_size), _ => panic!("Invalid byte_size: {}", byte_size),
@ -169,7 +187,9 @@ mod tests {
mem.zero_out_buffer(7, 8); mem.zero_out_buffer(7, 8);
assert_eq!(&[0, 0, 0xff, 0xff], mem.get_full_buffer(6)); assert_eq!(&[0, 0, 0xff, 0xff], mem.get_full_buffer(6));
} }
Err(e) => { assert!(false, e); } Err(e) => {
assert!(false, e);
}
} }
match sut.peek_read() { match sut.peek_read() {
@ -177,7 +197,9 @@ mod tests {
assert_eq!(2, mem.length()); assert_eq!(2, mem.length());
assert_eq!(0xffff, mem.read_uint(0, 2)); assert_eq!(0xffff, mem.read_uint(0, 2));
} }
Err(e) => { assert!(false, e); } Err(e) => {
assert!(false, e);
}
} }
} }
} }

View file

@ -1,6 +1,10 @@
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Radix { Decimal, Hexadecimal, Octal, NoPrefix } pub enum Radix {
Decimal,
Hexadecimal,
Octal,
NoPrefix,
}
/// provides the byte offset printed at the left margin /// provides the byte offset printed at the left margin
pub struct InputOffset { pub struct InputOffset {

View file

@ -1,6 +1,6 @@
// https://github.com/lazy-bitfield/rust-mockstream/pull/2 // https://github.com/lazy-bitfield/rust-mockstream/pull/2
use std::io::{Cursor, Read, Result, Error, ErrorKind}; use std::io::{Cursor, Error, ErrorKind, Read, Result};
use std::error::Error as errorError; use std::error::Error as errorError;
/// `FailingMockStream` mocks a stream which will fail upon read or write /// `FailingMockStream` mocks a stream which will fail upon read or write
@ -56,12 +56,16 @@ impl FailingMockStream {
/// When `read` or `write` is called, it will return an error `repeat_count` times. /// When `read` or `write` is called, it will return an error `repeat_count` times.
/// `kind` and `message` can be specified to define the exact error. /// `kind` and `message` can be specified to define the exact error.
pub fn new(kind: ErrorKind, message: &'static str, repeat_count: i32) -> FailingMockStream { pub fn new(kind: ErrorKind, message: &'static str, repeat_count: i32) -> FailingMockStream {
FailingMockStream { kind: kind, message: message, repeat_count: repeat_count, } FailingMockStream {
kind: kind,
message: message,
repeat_count: repeat_count,
}
} }
fn error(&mut self) -> Result<usize> { fn error(&mut self) -> Result<usize> {
if self.repeat_count == 0 { if self.repeat_count == 0 {
return Ok(0) return Ok(0);
} else { } else {
if self.repeat_count > 0 { if self.repeat_count > 0 {
self.repeat_count -= 1; self.repeat_count -= 1;
@ -91,7 +95,11 @@ fn test_failing_mock_stream_read() {
#[test] #[test]
fn test_failing_mock_stream_chain_interrupted() { fn test_failing_mock_stream_chain_interrupted() {
let mut c = Cursor::new(&b"abcd"[..]) let mut c = Cursor::new(&b"abcd"[..])
.chain(FailingMockStream::new(ErrorKind::Interrupted, "Interrupted", 5)) .chain(FailingMockStream::new(
ErrorKind::Interrupted,
"Interrupted",
5,
))
.chain(Cursor::new(&b"ABCD"[..])); .chain(Cursor::new(&b"ABCD"[..]));
let mut v = [0; 8]; let mut v = [0; 8];

View file

@ -56,9 +56,12 @@ impl<'b> MultifileReader<'b> {
// print an error at the time that the file is needed, // print an error at the time that the file is needed,
// then move on the the next file. // then move on the the next file.
// This matches the behavior of the original `od` // This matches the behavior of the original `od`
eprintln!("{}: '{}': {}", eprintln!(
"{}: '{}': {}",
executable!().split("::").next().unwrap(), // remove module executable!().split("::").next().unwrap(), // remove module
fname, e); fname,
e
);
self.any_err = true self.any_err = true
} }
} }
@ -91,12 +94,14 @@ impl<'b> io::Read for MultifileReader<'b> {
Ok(0) => break, Ok(0) => break,
Ok(n) => n, Ok(n) => n,
Err(e) => { Err(e) => {
eprintln!("{}: I/O: {}", eprintln!(
"{}: I/O: {}",
executable!().split("::").next().unwrap(), // remove module executable!().split("::").next().unwrap(), // remove module
e); e
);
self.any_err = true; self.any_err = true;
break; break;
}, }
}; };
if xfrd == buf.len() { if xfrd == buf.len() {
// transferred all that was asked for. // transferred all that was asked for.
@ -117,11 +122,10 @@ impl<'b> HasError for MultifileReader<'b> {
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::io::{Cursor, Read, ErrorKind}; use std::io::{Cursor, ErrorKind, Read};
use mockstream::*; use mockstream::*;
#[test] #[test]
@ -176,11 +180,23 @@ mod tests {
#[test] #[test]
fn test_multi_file_reader_read_error_at_start() { fn test_multi_file_reader_read_error_at_start() {
let mut inputs = Vec::new(); let mut inputs = Vec::new();
inputs.push(InputSource::Stream(Box::new(FailingMockStream::new(ErrorKind::Other, "Failing", 1)))); inputs.push(InputSource::Stream(Box::new(FailingMockStream::new(
ErrorKind::Other,
"Failing",
1,
))));
inputs.push(InputSource::Stream(Box::new(Cursor::new(&b"abcd"[..])))); inputs.push(InputSource::Stream(Box::new(Cursor::new(&b"abcd"[..]))));
inputs.push(InputSource::Stream(Box::new(FailingMockStream::new(ErrorKind::Other, "Failing", 1)))); inputs.push(InputSource::Stream(Box::new(FailingMockStream::new(
ErrorKind::Other,
"Failing",
1,
))));
inputs.push(InputSource::Stream(Box::new(Cursor::new(&b"ABCD"[..])))); inputs.push(InputSource::Stream(Box::new(Cursor::new(&b"ABCD"[..]))));
inputs.push(InputSource::Stream(Box::new(FailingMockStream::new(ErrorKind::Other, "Failing", 1)))); inputs.push(InputSource::Stream(Box::new(FailingMockStream::new(
ErrorKind::Other,
"Failing",
1,
))));
let mut v = [0; 5]; let mut v = [0; 5];
let mut sut = MultifileReader::new(inputs); let mut sut = MultifileReader::new(inputs);

View file

@ -9,8 +9,8 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
extern crate getopts;
extern crate byteorder; extern crate byteorder;
extern crate getopts;
extern crate half; extern crate half;
#[macro_use] #[macro_use]
@ -44,14 +44,13 @@ use parse_formats::{parse_format_flags, ParsedFormatterItemInfo};
use prn_char::format_ascii_dump; use prn_char::format_ascii_dump;
use parse_inputs::{parse_inputs, CommandLineInputs}; use parse_inputs::{parse_inputs, CommandLineInputs};
use inputoffset::{InputOffset, Radix}; use inputoffset::{InputOffset, Radix};
use inputdecoder::{InputDecoder,MemoryDecoder}; use inputdecoder::{InputDecoder, MemoryDecoder};
use output_info::OutputInfo; use output_info::OutputInfo;
static VERSION: &'static str = env!("CARGO_PKG_VERSION"); static VERSION: &'static str = env!("CARGO_PKG_VERSION");
const PEEK_BUFFER_SIZE: usize = 4; // utf-8 can be 4 bytes const PEEK_BUFFER_SIZE: usize = 4; // utf-8 can be 4 bytes
static USAGE: &'static str = static USAGE: &'static str = r#"Usage:
r#"Usage:
od [OPTION]... [--] [FILENAME]... od [OPTION]... [--] [FILENAME]...
od [-abcdDefFhHiIlLoOsxX] [FILENAME] [[+][0x]OFFSET[.][b]] od [-abcdDefFhHiIlLoOsxX] [FILENAME] [[+][0x]OFFSET[.][b]]
od --traditional [OPTION]... [FILENAME] [[+][0x]OFFSET[.][b] [[+][0x]LABEL[.][b]]] od --traditional [OPTION]... [FILENAME] [[+][0x]OFFSET[.][b] [[+][0x]LABEL[.][b]]]
@ -97,17 +96,37 @@ exitcode will be non-zero."#;
fn create_getopts_options() -> getopts::Options { fn create_getopts_options() -> getopts::Options {
let mut opts = getopts::Options::new(); let mut opts = getopts::Options::new();
opts.optopt("A", "address-radix", opts.optopt(
"Select the base in which file offsets are printed.", "RADIX"); "A",
opts.optopt("j", "skip-bytes", "address-radix",
"Skip bytes input bytes before formatting and writing.", "BYTES"); "Select the base in which file offsets are printed.",
opts.optopt("N", "read-bytes", "RADIX",
"limit dump to BYTES input bytes", "BYTES"); );
opts.optopt("", "endian", "byte order to use for multi-byte formats", "big|little"); opts.optopt(
opts.optopt("S", "strings", "j",
"skip-bytes",
"Skip bytes input bytes before formatting and writing.",
"BYTES",
);
opts.optopt(
"N",
"read-bytes",
"limit dump to BYTES input bytes",
"BYTES",
);
opts.optopt(
"",
"endian",
"byte order to use for multi-byte formats",
"big|little",
);
opts.optopt(
"S",
"strings",
"output strings of at least BYTES graphic chars. 3 is assumed when \ "output strings of at least BYTES graphic chars. 3 is assumed when \
BYTES is not specified.", BYTES is not specified.",
"BYTES"); "BYTES",
);
opts.optflagmulti("a", "", "named characters, ignoring high-order bit"); opts.optflagmulti("a", "", "named characters, ignoring high-order bit");
opts.optflagmulti("b", "", "octal bytes"); opts.optflagmulti("b", "", "octal bytes");
opts.optflagmulti("c", "", "ASCII characters or backslash escapes"); opts.optflagmulti("c", "", "ASCII characters or backslash escapes");
@ -132,14 +151,25 @@ fn create_getopts_options() -> getopts::Options {
opts.optflagmulti("F", "", "floating point double precision (64-bit) units"); opts.optflagmulti("F", "", "floating point double precision (64-bit) units");
opts.optmulti("t", "format", "select output format or formats", "TYPE"); opts.optmulti("t", "format", "select output format or formats", "TYPE");
opts.optflag("v", "output-duplicates", "do not use * to mark line suppression"); opts.optflag(
opts.optflagopt("w", "width", "v",
"output-duplicates",
"do not use * to mark line suppression",
);
opts.optflagopt(
"w",
"width",
"output BYTES bytes per output line. 32 is implied when BYTES is not \ "output BYTES bytes per output line. 32 is implied when BYTES is not \
specified.", specified.",
"BYTES"); "BYTES",
);
opts.optflag("", "help", "display this help and exit."); opts.optflag("", "help", "display this help and exit.");
opts.optflag("", "version", "output version information and exit."); opts.optflag("", "version", "output version information and exit.");
opts.optflag("", "traditional", "compatibility mode with one input, offset and label."); opts.optflag(
"",
"traditional",
"compatibility mode with one input, offset and label.",
);
opts opts
} }
@ -159,9 +189,9 @@ struct OdOptions {
impl OdOptions { impl OdOptions {
fn new(matches: getopts::Matches, args: Vec<String>) -> Result<OdOptions, String> { fn new(matches: getopts::Matches, args: Vec<String>) -> Result<OdOptions, String> {
let byte_order = match matches.opt_str("endian").as_ref().map(String::as_ref) { let byte_order = match matches.opt_str("endian").as_ref().map(String::as_ref) {
None => { ByteOrder::Native }, None => ByteOrder::Native,
Some("little") => { ByteOrder::Little }, Some("little") => ByteOrder::Little,
Some("big") => { ByteOrder::Big }, Some("big") => ByteOrder::Big,
Some(s) => { Some(s) => {
return Err(format!("Invalid argument --endian={}", s)); return Err(format!("Invalid argument --endian={}", s));
} }
@ -169,14 +199,12 @@ impl OdOptions {
let mut skip_bytes = match matches.opt_default("skip-bytes", "0") { let mut skip_bytes = match matches.opt_default("skip-bytes", "0") {
None => 0, None => 0,
Some(s) => { Some(s) => match parse_number_of_bytes(&s) {
match parse_number_of_bytes(&s) { Ok(i) => i,
Ok(i) => { i }
Err(_) => { Err(_) => {
return Err(format!("Invalid argument --skip-bytes={}", s)); return Err(format!("Invalid argument --skip-bytes={}", s));
} }
} },
}
}; };
let mut label: Option<usize> = None; let mut label: Option<usize> = None;
@ -187,7 +215,7 @@ impl OdOptions {
skip_bytes = s; skip_bytes = s;
label = l; label = l;
vec![f] vec![f]
}, }
Err(e) => { Err(e) => {
return Err(format!("Invalid inputs: {}", e)); return Err(format!("Invalid inputs: {}", e));
} }
@ -202,14 +230,14 @@ impl OdOptions {
let mut line_bytes = match matches.opt_default("w", "32") { let mut line_bytes = match matches.opt_default("w", "32") {
None => 16, None => 16,
Some(s) => { Some(s) => match s.parse::<usize>() {
match s.parse::<usize>() { Ok(i) => i,
Ok(i) => { i } Err(_) => 0,
Err(_) => { 0 } },
}
}
}; };
let min_bytes = formats.iter().fold(1, |max, next| cmp::max(max, next.formatter_item_info.byte_size)); let min_bytes = formats.iter().fold(1, |max, next| {
cmp::max(max, next.formatter_item_info.byte_size)
});
if line_bytes == 0 || line_bytes % min_bytes != 0 { if line_bytes == 0 || line_bytes % min_bytes != 0 {
show_warning!("invalid width {}; using {} instead", line_bytes, min_bytes); show_warning!("invalid width {}; using {} instead", line_bytes, min_bytes);
line_bytes = min_bytes; line_bytes = min_bytes;
@ -219,14 +247,12 @@ impl OdOptions {
let read_bytes = match matches.opt_str("read-bytes") { let read_bytes = match matches.opt_str("read-bytes") {
None => None, None => None,
Some(s) => { Some(s) => match parse_number_of_bytes(&s) {
match parse_number_of_bytes(&s) { Ok(i) => Some(i),
Ok(i) => { Some(i) }
Err(_) => { Err(_) => {
return Err(format!("Invalid argument --read-bytes={}", s)); return Err(format!("Invalid argument --read-bytes={}", s));
} }
} },
}
}; };
let radix = match matches.opt_str("A") { let radix = match matches.opt_str("A") {
@ -234,16 +260,16 @@ impl OdOptions {
Some(s) => { Some(s) => {
let st = s.into_bytes(); let st = s.into_bytes();
if st.len() != 1 { if st.len() != 1 {
return Err(format!("Radix must be one of [d, o, n, x]")) return Err(format!("Radix must be one of [d, o, n, x]"));
} else { } else {
let radix: char = *(st.get(0) let radix: char =
.expect("byte string of length 1 lacks a 0th elem")) as char; *(st.get(0).expect("byte string of length 1 lacks a 0th elem")) as char;
match radix { match radix {
'd' => Radix::Decimal, 'd' => Radix::Decimal,
'x' => Radix::Hexadecimal, 'x' => Radix::Hexadecimal,
'o' => Radix::Octal, 'o' => Radix::Octal,
'n' => Radix::NoPrefix, 'n' => Radix::NoPrefix,
_ => return Err(format!("Radix must be one of [d, o, n, x]")) _ => return Err(format!("Radix must be one of [d, o, n, x]")),
} }
} }
} }
@ -289,28 +315,43 @@ pub fn uumain(args: Vec<String>) -> i32 {
Err(s) => { Err(s) => {
disp_err!("{}", s); disp_err!("{}", s);
return 1; return 1;
}, }
Ok(o) => o, Ok(o) => o,
}; };
let mut input_offset = InputOffset::new(od_options.radix, od_options.skip_bytes, let mut input_offset =
od_options.label); InputOffset::new(od_options.radix, od_options.skip_bytes, od_options.label);
let mut input = open_input_peek_reader(&od_options.input_strings, let mut input = open_input_peek_reader(
od_options.skip_bytes, od_options.read_bytes); &od_options.input_strings,
let mut input_decoder = InputDecoder::new(&mut input, od_options.line_bytes, od_options.skip_bytes,
PEEK_BUFFER_SIZE, od_options.byte_order); od_options.read_bytes,
);
let mut input_decoder = InputDecoder::new(
&mut input,
od_options.line_bytes,
PEEK_BUFFER_SIZE,
od_options.byte_order,
);
let output_info = OutputInfo::new(od_options.line_bytes, &od_options.formats[..], let output_info = OutputInfo::new(
od_options.output_duplicates); od_options.line_bytes,
&od_options.formats[..],
od_options.output_duplicates,
);
odfunc(&mut input_offset, &mut input_decoder, &output_info) odfunc(&mut input_offset, &mut input_decoder, &output_info)
} }
/// Loops through the input line by line, calling print_bytes to take care of the output. /// Loops through the input line by line, calling print_bytes to take care of the output.
fn odfunc<I>(input_offset: &mut InputOffset, input_decoder: &mut InputDecoder<I>, fn odfunc<I>(
output_info: &OutputInfo) -> i32 input_offset: &mut InputOffset,
where I: PeekRead + HasError { input_decoder: &mut InputDecoder<I>,
output_info: &OutputInfo,
) -> i32
where
I: PeekRead + HasError,
{
let mut duplicate_line = false; let mut duplicate_line = false;
let mut previous_bytes: Vec<u8> = Vec::new(); let mut previous_bytes: Vec<u8> = Vec::new();
let line_bytes = output_info.byte_size_line; let line_bytes = output_info.byte_size_line;
@ -338,9 +379,9 @@ fn odfunc<I>(input_offset: &mut InputOffset, input_decoder: &mut InputDecoder<I>
memory_decoder.zero_out_buffer(length, max_used); memory_decoder.zero_out_buffer(length, max_used);
} }
if !output_info.output_duplicates if !output_info.output_duplicates && length == line_bytes
&& length == line_bytes && memory_decoder.get_buffer(0) == &previous_bytes[..]
&& memory_decoder.get_buffer(0) == &previous_bytes[..] { {
if !duplicate_line { if !duplicate_line {
duplicate_line = true; duplicate_line = true;
println!("*"); println!("*");
@ -352,8 +393,11 @@ fn odfunc<I>(input_offset: &mut InputOffset, input_decoder: &mut InputDecoder<I>
memory_decoder.clone_buffer(&mut previous_bytes); memory_decoder.clone_buffer(&mut previous_bytes);
} }
print_bytes(&input_offset.format_byte_offset(), &memory_decoder, print_bytes(
&output_info); &input_offset.format_byte_offset(),
&memory_decoder,
&output_info,
);
} }
input_offset.increase_position(length); input_offset.increase_position(length);
@ -381,9 +425,11 @@ fn print_bytes(prefix: &str, input_decoder: &MemoryDecoder, output_info: &Output
let mut b = 0; let mut b = 0;
while b < input_decoder.length() { while b < input_decoder.length() {
output_text.push_str(&format!("{:>width$}", output_text.push_str(&format!(
"{:>width$}",
"", "",
width = f.spacing[b % output_info.byte_size_block])); width = f.spacing[b % output_info.byte_size_block]
));
match f.formatter_item_info.formatter { match f.formatter_item_info.formatter {
FormatWriter::IntWriter(func) => { FormatWriter::IntWriter(func) => {
@ -403,11 +449,15 @@ fn print_bytes(prefix: &str, input_decoder: &MemoryDecoder, output_info: &Output
} }
if f.add_ascii_dump { if f.add_ascii_dump {
let missing_spacing = output_info.print_width_line.saturating_sub(output_text.chars().count()); let missing_spacing = output_info
output_text.push_str(&format!("{:>width$} {}", .print_width_line
.saturating_sub(output_text.chars().count());
output_text.push_str(&format!(
"{:>width$} {}",
"", "",
format_ascii_dump(input_decoder.get_buffer(0)), format_ascii_dump(input_decoder.get_buffer(0)),
width = missing_spacing)); width = missing_spacing
));
} }
if first { if first {
@ -417,7 +467,7 @@ fn print_bytes(prefix: &str, input_decoder: &MemoryDecoder, output_info: &Output
} else { } else {
// this takes the space of the file offset on subsequent // this takes the space of the file offset on subsequent
// lines of multi-format rasters. // lines of multi-format rasters.
print!("{:>width$}", "", width=prefix.chars().count()); print!("{:>width$}", "", width = prefix.chars().count());
} }
print!("{}\n", output_text); print!("{}\n", output_text);
} }
@ -427,8 +477,11 @@ fn print_bytes(prefix: &str, input_decoder: &MemoryDecoder, output_info: &Output
/// ///
/// `skip_bytes` is the number of bytes skipped from the input /// `skip_bytes` is the number of bytes skipped from the input
/// `read_bytes` is an optional limit to the number of bytes to read /// `read_bytes` is an optional limit to the number of bytes to read
fn open_input_peek_reader<'a>(input_strings: &'a Vec<String>, skip_bytes: usize, fn open_input_peek_reader<'a>(
read_bytes: Option<usize>) -> PeekReader<PartialReader<MultifileReader<'a>>> { input_strings: &'a Vec<String>,
skip_bytes: usize,
read_bytes: Option<usize>,
) -> PeekReader<PartialReader<MultifileReader<'a>>> {
// should return "impl PeekRead + Read + HasError" when supported in (stable) rust // should return "impl PeekRead + Read + HasError" when supported in (stable) rust
let inputs = input_strings let inputs = input_strings
.iter() .iter()

View file

@ -40,7 +40,6 @@ pub struct OutputInfo {
pub output_duplicates: bool, pub output_duplicates: bool,
} }
impl OutputInfo { impl OutputInfo {
/// Returns an iterator over the `SpacedFormatterItemInfo` vector. /// Returns an iterator over the `SpacedFormatterItemInfo` vector.
pub fn spaced_formatters_iter(&self) -> Iter<SpacedFormatterItemInfo> { pub fn spaced_formatters_iter(&self) -> Iter<SpacedFormatterItemInfo> {
@ -48,16 +47,25 @@ impl OutputInfo {
} }
/// Creates a new `OutputInfo` based on the parameters /// Creates a new `OutputInfo` based on the parameters
pub fn new(line_bytes: usize, formats: &[ParsedFormatterItemInfo], output_duplicates: bool) -> OutputInfo { pub fn new(
let byte_size_block = formats.iter().fold(1, |max, next| cmp::max(max, next.formatter_item_info.byte_size)); line_bytes: usize,
let print_width_block = formats formats: &[ParsedFormatterItemInfo],
.iter() output_duplicates: bool,
.fold(1, |max, next| { ) -> OutputInfo {
cmp::max(max, next.formatter_item_info.print_width * (byte_size_block / next.formatter_item_info.byte_size)) let byte_size_block = formats.iter().fold(1, |max, next| {
cmp::max(max, next.formatter_item_info.byte_size)
});
let print_width_block = formats.iter().fold(1, |max, next| {
cmp::max(
max,
next.formatter_item_info.print_width
* (byte_size_block / next.formatter_item_info.byte_size),
)
}); });
let print_width_line = print_width_block * (line_bytes / byte_size_block); let print_width_line = print_width_block * (line_bytes / byte_size_block);
let spaced_formatters = OutputInfo::create_spaced_formatter_info(&formats, byte_size_block, print_width_block); let spaced_formatters =
OutputInfo::create_spaced_formatter_info(&formats, byte_size_block, print_width_block);
OutputInfo { OutputInfo {
byte_size_line: line_bytes, byte_size_line: line_bytes,
@ -69,14 +77,17 @@ impl OutputInfo {
} }
} }
fn create_spaced_formatter_info(formats: &[ParsedFormatterItemInfo], fn create_spaced_formatter_info(
byte_size_block: usize, print_width_block: usize) -> Vec<SpacedFormatterItemInfo> { formats: &[ParsedFormatterItemInfo],
byte_size_block: usize,
print_width_block: usize,
) -> Vec<SpacedFormatterItemInfo> {
formats formats
.iter() .iter()
.map(|f| SpacedFormatterItemInfo { .map(|f| SpacedFormatterItemInfo {
formatter_item_info: f.formatter_item_info, formatter_item_info: f.formatter_item_info,
add_ascii_dump: f.add_ascii_dump, add_ascii_dump: f.add_ascii_dump,
spacing: OutputInfo::calculate_alignment(f, byte_size_block, print_width_block) spacing: OutputInfo::calculate_alignment(f, byte_size_block, print_width_block),
}) })
.collect() .collect()
} }
@ -126,12 +137,17 @@ impl OutputInfo {
/// ///
/// This algorithm assumes the size of all types is a power of 2 (1, 2, 4, 8, 16, ...) /// This algorithm assumes the size of all types is a power of 2 (1, 2, 4, 8, 16, ...)
/// Increase MAX_BYTES_PER_UNIT to allow larger types. /// Increase MAX_BYTES_PER_UNIT to allow larger types.
fn calculate_alignment(sf: &TypeSizeInfo, byte_size_block: usize, fn calculate_alignment(
print_width_block: usize) -> [usize; MAX_BYTES_PER_UNIT] { sf: &TypeSizeInfo,
byte_size_block: usize,
print_width_block: usize,
) -> [usize; MAX_BYTES_PER_UNIT] {
if byte_size_block > MAX_BYTES_PER_UNIT { if byte_size_block > MAX_BYTES_PER_UNIT {
panic!("{}-bits types are unsupported. Current max={}-bits.", panic!(
"{}-bits types are unsupported. Current max={}-bits.",
8 * byte_size_block, 8 * byte_size_block,
8 * MAX_BYTES_PER_UNIT); 8 * MAX_BYTES_PER_UNIT
);
} }
let mut spacing = [0; MAX_BYTES_PER_UNIT]; let mut spacing = [0; MAX_BYTES_PER_UNIT];
@ -161,8 +177,12 @@ trait TypeSizeInfo {
} }
impl TypeSizeInfo for ParsedFormatterItemInfo { impl TypeSizeInfo for ParsedFormatterItemInfo {
fn byte_size(&self) -> usize { self.formatter_item_info.byte_size } fn byte_size(&self) -> usize {
fn print_width(&self) -> usize { self.formatter_item_info.print_width } self.formatter_item_info.byte_size
}
fn print_width(&self) -> usize {
self.formatter_item_info.print_width
}
} }
#[cfg(test)] #[cfg(test)]
@ -173,8 +193,12 @@ struct TypeInfo {
#[cfg(test)] #[cfg(test)]
impl TypeSizeInfo for TypeInfo { impl TypeSizeInfo for TypeInfo {
fn byte_size(&self) -> usize { self.byte_size } fn byte_size(&self) -> usize {
fn print_width(&self) -> usize { self.print_width } self.byte_size
}
fn print_width(&self) -> usize {
self.print_width
}
} }
#[test] #[test]
@ -185,14 +209,41 @@ fn test_calculate_alignment() {
// ffff ffff ffff ffff ffff ffff ffff ffff // ffff ffff ffff ffff ffff ffff ffff ffff
// the first line has no additional spacing: // the first line has no additional spacing:
assert_eq!([0, 0, 0, 0, 0, 0, 0, 0], assert_eq!(
OutputInfo::calculate_alignment(&TypeInfo{byte_size:8, print_width:23}, 8, 23)); [0, 0, 0, 0, 0, 0, 0, 0],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 8,
print_width: 23,
},
8,
23
)
);
// the second line a single space at the start of the block: // the second line a single space at the start of the block:
assert_eq!([1, 0, 0, 0, 0, 0, 0, 0], assert_eq!(
OutputInfo::calculate_alignment(&TypeInfo{byte_size:4, print_width:11}, 8, 23)); [1, 0, 0, 0, 0, 0, 0, 0],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 4,
print_width: 11,
},
8,
23
)
);
// the third line two spaces at pos 0, and 1 space at pos 4: // the third line two spaces at pos 0, and 1 space at pos 4:
assert_eq!([2, 0, 0, 0, 1, 0, 0, 0], assert_eq!(
OutputInfo::calculate_alignment(&TypeInfo{byte_size:2, print_width:5}, 8, 23)); [2, 0, 0, 0, 1, 0, 0, 0],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 2,
print_width: 5,
},
8,
23
)
);
// For this example `byte_size_block` is 8 and 'print_width_block' is 28: // For this example `byte_size_block` is 8 and 'print_width_block' is 28:
// 18446744073709551615 18446744073709551615 // 18446744073709551615 18446744073709551615
@ -200,42 +251,195 @@ fn test_calculate_alignment() {
// 177777 177777 177777 177777 177777 177777 177777 177777 // 177777 177777 177777 177777 177777 177777 177777 177777
// ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff // ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
assert_eq!([7, 0, 0, 0, 0, 0, 0, 0], assert_eq!(
OutputInfo::calculate_alignment(&TypeInfo{byte_size:8, print_width:21}, 8, 28)); [7, 0, 0, 0, 0, 0, 0, 0],
assert_eq!([5, 0, 0, 0, 5, 0, 0, 0], OutputInfo::calculate_alignment(
OutputInfo::calculate_alignment(&TypeInfo{byte_size:4, print_width:9}, 8, 28)); &TypeInfo {
assert_eq!([0, 0, 0, 0, 0, 0, 0, 0], byte_size: 8,
OutputInfo::calculate_alignment(&TypeInfo{byte_size:2, print_width:7}, 8, 28)); print_width: 21,
assert_eq!([1, 0, 1, 0, 1, 0, 1, 0], },
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:3}, 8, 28)); 8,
28
)
);
assert_eq!(
[5, 0, 0, 0, 5, 0, 0, 0],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 4,
print_width: 9,
},
8,
28
)
);
assert_eq!(
[0, 0, 0, 0, 0, 0, 0, 0],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 2,
print_width: 7,
},
8,
28
)
);
assert_eq!(
[1, 0, 1, 0, 1, 0, 1, 0],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 1,
print_width: 3,
},
8,
28
)
);
// 9 tests where 8 .. 16 spaces are spread across 8 positions // 9 tests where 8 .. 16 spaces are spread across 8 positions
assert_eq!([1, 1, 1, 1, 1, 1, 1, 1], assert_eq!(
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 8)); [1, 1, 1, 1, 1, 1, 1, 1],
assert_eq!([2, 1, 1, 1, 1, 1, 1, 1], OutputInfo::calculate_alignment(
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 9)); &TypeInfo {
assert_eq!([2, 1, 1, 1, 2, 1, 1, 1], byte_size: 1,
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 10)); print_width: 2,
assert_eq!([3, 1, 1, 1, 2, 1, 1, 1], },
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 11)); 8,
assert_eq!([2, 1, 2, 1, 2, 1, 2, 1], 16 + 8
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 12)); )
assert_eq!([3, 1, 2, 1, 2, 1, 2, 1], );
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 13)); assert_eq!(
assert_eq!([3, 1, 2, 1, 3, 1, 2, 1], [2, 1, 1, 1, 1, 1, 1, 1],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 14)); OutputInfo::calculate_alignment(
assert_eq!([4, 1, 2, 1, 3, 1, 2, 1], &TypeInfo {
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 15)); byte_size: 1,
assert_eq!([2, 2, 2, 2, 2, 2, 2, 2], print_width: 2,
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 16)); },
8,
16 + 9
)
);
assert_eq!(
[2, 1, 1, 1, 2, 1, 1, 1],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 1,
print_width: 2,
},
8,
16 + 10
)
);
assert_eq!(
[3, 1, 1, 1, 2, 1, 1, 1],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 1,
print_width: 2,
},
8,
16 + 11
)
);
assert_eq!(
[2, 1, 2, 1, 2, 1, 2, 1],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 1,
print_width: 2,
},
8,
16 + 12
)
);
assert_eq!(
[3, 1, 2, 1, 2, 1, 2, 1],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 1,
print_width: 2,
},
8,
16 + 13
)
);
assert_eq!(
[3, 1, 2, 1, 3, 1, 2, 1],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 1,
print_width: 2,
},
8,
16 + 14
)
);
assert_eq!(
[4, 1, 2, 1, 3, 1, 2, 1],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 1,
print_width: 2,
},
8,
16 + 15
)
);
assert_eq!(
[2, 2, 2, 2, 2, 2, 2, 2],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 1,
print_width: 2,
},
8,
16 + 16
)
);
// 4 tests where 15 spaces are spread across 8, 4, 2 or 1 position(s) // 4 tests where 15 spaces are spread across 8, 4, 2 or 1 position(s)
assert_eq!([4, 1, 2, 1, 3, 1, 2, 1], assert_eq!(
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 15)); [4, 1, 2, 1, 3, 1, 2, 1],
assert_eq!([5, 0, 3, 0, 4, 0, 3, 0], OutputInfo::calculate_alignment(
OutputInfo::calculate_alignment(&TypeInfo{byte_size:2, print_width:4}, 8, 16 + 15)); &TypeInfo {
assert_eq!([8, 0, 0, 0, 7, 0, 0, 0], byte_size: 1,
OutputInfo::calculate_alignment(&TypeInfo{byte_size:4, print_width:8}, 8, 16 + 15)); print_width: 2,
assert_eq!([15, 0, 0, 0, 0, 0, 0, 0], },
OutputInfo::calculate_alignment(&TypeInfo{byte_size:8, print_width:16}, 8, 16 + 15)); 8,
16 + 15
)
);
assert_eq!(
[5, 0, 3, 0, 4, 0, 3, 0],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 2,
print_width: 4,
},
8,
16 + 15
)
);
assert_eq!(
[8, 0, 0, 0, 7, 0, 0, 0],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 4,
print_width: 8,
},
8,
16 + 15
)
);
assert_eq!(
[15, 0, 0, 0, 0, 0, 0, 0],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 8,
print_width: 16,
},
8,
16 + 15
)
);
} }

View file

@ -10,7 +10,10 @@ pub struct ParsedFormatterItemInfo {
} }
impl ParsedFormatterItemInfo { impl ParsedFormatterItemInfo {
pub fn new(formatter_item_info: FormatterItemInfo, add_ascii_dump: bool) -> ParsedFormatterItemInfo { pub fn new(
formatter_item_info: FormatterItemInfo,
add_ascii_dump: bool,
) -> ParsedFormatterItemInfo {
ParsedFormatterItemInfo { ParsedFormatterItemInfo {
formatter_item_info: formatter_item_info, formatter_item_info: formatter_item_info,
add_ascii_dump: add_ascii_dump, add_ascii_dump: add_ascii_dump,
@ -51,45 +54,41 @@ fn od_format_type(type_char: FormatType, byte_size: u8) -> Option<FormatterItemI
(FormatType::DecimalInt, 1) => Some(FORMAT_ITEM_DEC8S), (FormatType::DecimalInt, 1) => Some(FORMAT_ITEM_DEC8S),
(FormatType::DecimalInt, 2) => Some(FORMAT_ITEM_DEC16S), (FormatType::DecimalInt, 2) => Some(FORMAT_ITEM_DEC16S),
(FormatType::DecimalInt, 0) | (FormatType::DecimalInt, 0) | (FormatType::DecimalInt, 4) => Some(FORMAT_ITEM_DEC32S),
(FormatType::DecimalInt, 4) => Some(FORMAT_ITEM_DEC32S),
(FormatType::DecimalInt, 8) => Some(FORMAT_ITEM_DEC64S), (FormatType::DecimalInt, 8) => Some(FORMAT_ITEM_DEC64S),
(FormatType::OctalInt, 1) => Some(FORMAT_ITEM_OCT8), (FormatType::OctalInt, 1) => Some(FORMAT_ITEM_OCT8),
(FormatType::OctalInt, 2) => Some(FORMAT_ITEM_OCT16), (FormatType::OctalInt, 2) => Some(FORMAT_ITEM_OCT16),
(FormatType::OctalInt, 0) | (FormatType::OctalInt, 0) | (FormatType::OctalInt, 4) => Some(FORMAT_ITEM_OCT32),
(FormatType::OctalInt, 4) => Some(FORMAT_ITEM_OCT32),
(FormatType::OctalInt, 8) => Some(FORMAT_ITEM_OCT64), (FormatType::OctalInt, 8) => Some(FORMAT_ITEM_OCT64),
(FormatType::UnsignedInt, 1) => Some(FORMAT_ITEM_DEC8U), (FormatType::UnsignedInt, 1) => Some(FORMAT_ITEM_DEC8U),
(FormatType::UnsignedInt, 2) => Some(FORMAT_ITEM_DEC16U), (FormatType::UnsignedInt, 2) => Some(FORMAT_ITEM_DEC16U),
(FormatType::UnsignedInt, 0) | (FormatType::UnsignedInt, 0) | (FormatType::UnsignedInt, 4) => Some(FORMAT_ITEM_DEC32U),
(FormatType::UnsignedInt, 4) => Some(FORMAT_ITEM_DEC32U),
(FormatType::UnsignedInt, 8) => Some(FORMAT_ITEM_DEC64U), (FormatType::UnsignedInt, 8) => Some(FORMAT_ITEM_DEC64U),
(FormatType::HexadecimalInt, 1) => Some(FORMAT_ITEM_HEX8), (FormatType::HexadecimalInt, 1) => Some(FORMAT_ITEM_HEX8),
(FormatType::HexadecimalInt, 2) => Some(FORMAT_ITEM_HEX16), (FormatType::HexadecimalInt, 2) => Some(FORMAT_ITEM_HEX16),
(FormatType::HexadecimalInt, 0) | (FormatType::HexadecimalInt, 0) | (FormatType::HexadecimalInt, 4) => {
(FormatType::HexadecimalInt, 4) => Some(FORMAT_ITEM_HEX32), Some(FORMAT_ITEM_HEX32)
}
(FormatType::HexadecimalInt, 8) => Some(FORMAT_ITEM_HEX64), (FormatType::HexadecimalInt, 8) => Some(FORMAT_ITEM_HEX64),
(FormatType::Float, 2) => Some(FORMAT_ITEM_F16), (FormatType::Float, 2) => Some(FORMAT_ITEM_F16),
(FormatType::Float, 0) | (FormatType::Float, 0) | (FormatType::Float, 4) => Some(FORMAT_ITEM_F32),
(FormatType::Float, 4) => Some(FORMAT_ITEM_F32),
(FormatType::Float, 8) => Some(FORMAT_ITEM_F64), (FormatType::Float, 8) => Some(FORMAT_ITEM_F64),
_ => None, _ => None,
} }
} }
fn od_argument_with_option(ch:char) -> bool { fn od_argument_with_option(ch: char) -> bool {
match ch { match ch {
'A' | 'j' | 'N' | 'S' | 'w' => true, 'A' | 'j' | 'N' | 'S' | 'w' => true,
_ => false, _ => false,
} }
} }
/// Parses format flags from commandline /// Parses format flags from commandline
/// ///
/// getopts, docopt, clap don't seem suitable to parse the commandline /// getopts, docopt, clap don't seem suitable to parse the commandline
@ -156,7 +155,9 @@ pub fn parse_format_flags(args: &Vec<String>) -> Result<Vec<ParsedFormatterItemI
} }
} }
if expect_type_string { if expect_type_string {
return Err(format!("missing format specification after '--format' / '-t'")); return Err(format!(
"missing format specification after '--format' / '-t'"
));
} }
if formats.is_empty() { if formats.is_empty() {
@ -197,53 +198,61 @@ fn format_type(ch: char) -> Option<FormatType> {
} }
} }
fn format_type_category(t: FormatType) -> FormatTypeCategory { fn format_type_category(t: FormatType) -> FormatTypeCategory {
match t { match t {
FormatType::Ascii | FormatType::Char FormatType::Ascii | FormatType::Char => FormatTypeCategory::Char,
=> FormatTypeCategory::Char, FormatType::DecimalInt
FormatType::DecimalInt | FormatType::OctalInt | FormatType::UnsignedInt | FormatType::HexadecimalInt | FormatType::OctalInt
=> FormatTypeCategory::Integer, | FormatType::UnsignedInt
FormatType::Float | FormatType::HexadecimalInt => FormatTypeCategory::Integer,
=> FormatTypeCategory::Float, FormatType::Float => FormatTypeCategory::Float,
} }
} }
fn is_format_size_char(ch: Option<char>, format_type: FormatTypeCategory, byte_size: &mut u8) -> bool { fn is_format_size_char(
ch: Option<char>,
format_type: FormatTypeCategory,
byte_size: &mut u8,
) -> bool {
match (format_type, ch) { match (format_type, ch) {
(FormatTypeCategory::Integer, Some('C')) => { (FormatTypeCategory::Integer, Some('C')) => {
*byte_size = 1; *byte_size = 1;
true true
}, }
(FormatTypeCategory::Integer, Some('S')) => { (FormatTypeCategory::Integer, Some('S')) => {
*byte_size = 2; *byte_size = 2;
true true
}, }
(FormatTypeCategory::Integer, Some('I')) => { (FormatTypeCategory::Integer, Some('I')) => {
*byte_size = 4; *byte_size = 4;
true true
}, }
(FormatTypeCategory::Integer, Some('L')) => { (FormatTypeCategory::Integer, Some('L')) => {
*byte_size = 8; *byte_size = 8;
true true
}, }
(FormatTypeCategory::Float, Some('F')) => { (FormatTypeCategory::Float, Some('F')) => {
*byte_size = 4; *byte_size = 4;
true true
}, }
(FormatTypeCategory::Float, Some('D')) => { (FormatTypeCategory::Float, Some('D')) => {
*byte_size = 8; *byte_size = 8;
true true
}, }
// FormatTypeCategory::Float, 'L' => *byte_size = 16, // TODO support f128 // FormatTypeCategory::Float, 'L' => *byte_size = 16, // TODO support f128
_ => false, _ => false,
} }
} }
fn is_format_size_decimal(ch: Option<char>, format_type: FormatTypeCategory, decimal_size: &mut String) -> bool { fn is_format_size_decimal(
if format_type == FormatTypeCategory::Char { return false; } ch: Option<char>,
format_type: FormatTypeCategory,
decimal_size: &mut String,
) -> bool {
if format_type == FormatTypeCategory::Char {
return false;
}
match ch { match ch {
Some(d) if d.is_digit(10) => { Some(d) if d.is_digit(10) => {
decimal_size.push(d); decimal_size.push(d);
@ -274,7 +283,10 @@ fn parse_type_string(params: &String) -> Result<Vec<ParsedFormatterItemInfo>, St
let type_char = match format_type(type_char) { let type_char = match format_type(type_char) {
Some(t) => t, Some(t) => t,
None => { None => {
return Err(format!("unexpected char '{}' in format specification '{}'", type_char, params)); return Err(format!(
"unexpected char '{}' in format specification '{}'",
type_char, params
));
} }
}; };
@ -293,7 +305,12 @@ fn parse_type_string(params: &String) -> Result<Vec<ParsedFormatterItemInfo>, St
} }
if !decimal_size.is_empty() { if !decimal_size.is_empty() {
byte_size = match decimal_size.parse() { byte_size = match decimal_size.parse() {
Err(_) => return Err(format!("invalid number '{}' in format specification '{}'", decimal_size, params)), Err(_) => {
return Err(format!(
"invalid number '{}' in format specification '{}'",
decimal_size, params
))
}
Ok(n) => n, Ok(n) => n,
} }
} }
@ -304,7 +321,12 @@ fn parse_type_string(params: &String) -> Result<Vec<ParsedFormatterItemInfo>, St
match od_format_type(type_char, byte_size) { match od_format_type(type_char, byte_size) {
Some(ft) => formats.push(ParsedFormatterItemInfo::new(ft, show_ascii_dump)), Some(ft) => formats.push(ParsedFormatterItemInfo::new(ft, show_ascii_dump)),
None => return Err(format!("invalid size '{}' in format specification '{}'", byte_size, params)), None => {
return Err(format!(
"invalid size '{}' in format specification '{}'",
byte_size, params
))
}
} }
} }
@ -312,7 +334,9 @@ fn parse_type_string(params: &String) -> Result<Vec<ParsedFormatterItemInfo>, St
} }
#[cfg(test)] #[cfg(test)]
pub fn parse_format_flags_str(args_str: &Vec<&'static str>) -> Result<Vec<FormatterItemInfo>, String> { pub fn parse_format_flags_str(
args_str: &Vec<&'static str>,
) -> Result<Vec<FormatterItemInfo>, String> {
let args = args_str.iter().map(|s| s.to_string()).collect(); let args = args_str.iter().map(|s| s.to_string()).collect();
match parse_format_flags(&args) { match parse_format_flags(&args) {
Err(e) => Err(e), Err(e) => Err(e),
@ -322,170 +346,187 @@ pub fn parse_format_flags_str(args_str: &Vec<&'static str>) -> Result<Vec<Format
.inspect(|f| assert!(!f.add_ascii_dump)) .inspect(|f| assert!(!f.add_ascii_dump))
.map(|f| f.formatter_item_info) .map(|f| f.formatter_item_info)
.collect()) .collect())
}, }
} }
} }
#[test] #[test]
fn test_no_options() { fn test_no_options() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od")).unwrap(), parse_format_flags_str(&vec!["od"]).unwrap(),
vec!(FORMAT_ITEM_OCT16)); vec![FORMAT_ITEM_OCT16]
);
} }
#[test] #[test]
fn test_one_option() { fn test_one_option() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "-F")).unwrap(), parse_format_flags_str(&vec!["od", "-F"]).unwrap(),
vec!(FORMAT_ITEM_F64)); vec![FORMAT_ITEM_F64]
);
} }
#[test] #[test]
fn test_two_separate_options() { fn test_two_separate_options() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "-F", "-x")).unwrap(), parse_format_flags_str(&vec!["od", "-F", "-x"]).unwrap(),
vec!(FORMAT_ITEM_F64, FORMAT_ITEM_HEX16)); vec![FORMAT_ITEM_F64, FORMAT_ITEM_HEX16]
);
} }
#[test] #[test]
fn test_two_combined_options() { fn test_two_combined_options() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "-Fx")).unwrap(), parse_format_flags_str(&vec!["od", "-Fx"]).unwrap(),
vec!(FORMAT_ITEM_F64, FORMAT_ITEM_HEX16)); vec![FORMAT_ITEM_F64, FORMAT_ITEM_HEX16]
);
} }
#[test] #[test]
fn test_ignore_non_format_parameters() { fn test_ignore_non_format_parameters() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "-d", "-Ax")).unwrap(), parse_format_flags_str(&vec!["od", "-d", "-Ax"]).unwrap(),
vec!(FORMAT_ITEM_DEC16U)); vec![FORMAT_ITEM_DEC16U]
);
} }
#[test] #[test]
fn test_ignore_separate_parameters() { fn test_ignore_separate_parameters() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "-I", "-A", "x")).unwrap(), parse_format_flags_str(&vec!["od", "-I", "-A", "x"]).unwrap(),
vec!(FORMAT_ITEM_DEC64S)); vec![FORMAT_ITEM_DEC64S]
);
} }
#[test] #[test]
fn test_ignore_trailing_vals() { fn test_ignore_trailing_vals() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "-D", "--", "-x")).unwrap(), parse_format_flags_str(&vec!["od", "-D", "--", "-x"]).unwrap(),
vec!(FORMAT_ITEM_DEC32U)); vec![FORMAT_ITEM_DEC32U]
);
} }
#[test] #[test]
fn test_invalid_long_format() { fn test_invalid_long_format() {
parse_format_flags_str(&vec!("od", "--format=X")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=X"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xX")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=xX"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=aC")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=aC"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=fI")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=fI"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xD")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=xD"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xC1")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=xC1"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=x1C")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=x1C"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xz1")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=xz1"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xzC")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=xzC"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xzz")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=xzz"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xCC")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=xCC"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=c1")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=c1"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=x256")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=x256"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=d5")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=d5"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=f1")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format=f1"]).unwrap_err();
} }
#[test] #[test]
fn test_long_format_a() { fn test_long_format_a() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "--format=a")).unwrap(), parse_format_flags_str(&vec!["od", "--format=a"]).unwrap(),
vec!(FORMAT_ITEM_A)); vec![FORMAT_ITEM_A]
);
} }
#[test] #[test]
fn test_long_format_cz() { fn test_long_format_cz() {
assert_eq!(parse_format_flags( assert_eq!(
&vec!("od".to_string(), "--format=cz".to_string())).unwrap(), parse_format_flags(&vec!["od".to_string(), "--format=cz".to_string()]).unwrap(),
vec!(ParsedFormatterItemInfo::new(FORMAT_ITEM_C, true))); vec![ParsedFormatterItemInfo::new(FORMAT_ITEM_C, true)]
);
} }
#[test] #[test]
fn test_long_format_d() { fn test_long_format_d() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "--format=d8")).unwrap(), parse_format_flags_str(&vec!["od", "--format=d8"]).unwrap(),
vec!(FORMAT_ITEM_DEC64S)); vec![FORMAT_ITEM_DEC64S]
);
} }
#[test] #[test]
fn test_long_format_d_default() { fn test_long_format_d_default() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "--format=d")).unwrap(), parse_format_flags_str(&vec!["od", "--format=d"]).unwrap(),
vec!(FORMAT_ITEM_DEC32S)); vec![FORMAT_ITEM_DEC32S]
);
} }
#[test] #[test]
fn test_long_format_o_default() { fn test_long_format_o_default() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "--format=o")).unwrap(), parse_format_flags_str(&vec!["od", "--format=o"]).unwrap(),
vec!(FORMAT_ITEM_OCT32)); vec![FORMAT_ITEM_OCT32]
);
} }
#[test] #[test]
fn test_long_format_u_default() { fn test_long_format_u_default() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "--format=u")).unwrap(), parse_format_flags_str(&vec!["od", "--format=u"]).unwrap(),
vec!(FORMAT_ITEM_DEC32U)); vec![FORMAT_ITEM_DEC32U]
);
} }
#[test] #[test]
fn test_long_format_x_default() { fn test_long_format_x_default() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "--format=x")).unwrap(), parse_format_flags_str(&vec!["od", "--format=x"]).unwrap(),
vec!(FORMAT_ITEM_HEX32)); vec![FORMAT_ITEM_HEX32]
);
} }
#[test] #[test]
fn test_long_format_f_default() { fn test_long_format_f_default() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "--format=f")).unwrap(), parse_format_flags_str(&vec!["od", "--format=f"]).unwrap(),
vec!(FORMAT_ITEM_F32)); vec![FORMAT_ITEM_F32]
);
} }
#[test] #[test]
fn test_long_format_next_arg() { fn test_long_format_next_arg() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "--format", "f8")).unwrap(), parse_format_flags_str(&vec!["od", "--format", "f8"]).unwrap(),
vec!(FORMAT_ITEM_F64)); vec![FORMAT_ITEM_F64]
);
} }
#[test] #[test]
fn test_short_format_next_arg() { fn test_short_format_next_arg() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "-t", "x8")).unwrap(), parse_format_flags_str(&vec!["od", "-t", "x8"]).unwrap(),
vec!(FORMAT_ITEM_HEX64)); vec![FORMAT_ITEM_HEX64]
);
} }
#[test] #[test]
fn test_short_format_combined_arg() { fn test_short_format_combined_arg() {
assert_eq!(parse_format_flags_str( assert_eq!(
&vec!("od", "-tu8")).unwrap(), parse_format_flags_str(&vec!["od", "-tu8"]).unwrap(),
vec!(FORMAT_ITEM_DEC64U)); vec![FORMAT_ITEM_DEC64U]
);
} }
#[test] #[test]
fn test_format_next_arg_invalid() { fn test_format_next_arg_invalid() {
parse_format_flags_str(&vec!("od", "--format", "-v")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format", "-v"]).unwrap_err();
parse_format_flags_str(&vec!("od", "--format")).unwrap_err(); parse_format_flags_str(&vec!["od", "--format"]).unwrap_err();
parse_format_flags_str(&vec!("od", "-t", "-v")).unwrap_err(); parse_format_flags_str(&vec!["od", "-t", "-v"]).unwrap_err();
parse_format_flags_str(&vec!("od", "-t")).unwrap_err(); parse_format_flags_str(&vec!["od", "-t"]).unwrap_err();
} }
#[test] #[test]
fn test_mixed_formats() { fn test_mixed_formats() {
assert_eq!(parse_format_flags( assert_eq!(
&vec!( parse_format_flags(&vec![
"od".to_string(), "od".to_string(),
"--skip-bytes=2".to_string(), "--skip-bytes=2".to_string(),
"-vItu1z".to_string(), "-vItu1z".to_string(),
@ -499,8 +540,9 @@ fn test_mixed_formats() {
"-xAx".to_string(), "-xAx".to_string(),
"--".to_string(), "--".to_string(),
"-h".to_string(), "-h".to_string(),
"--format=f8".to_string())).unwrap(), "--format=f8".to_string(),
vec!( ]).unwrap(),
vec![
ParsedFormatterItemInfo::new(FORMAT_ITEM_DEC64S, false), // I ParsedFormatterItemInfo::new(FORMAT_ITEM_DEC64S, false), // I
ParsedFormatterItemInfo::new(FORMAT_ITEM_DEC8U, true), // tu1z ParsedFormatterItemInfo::new(FORMAT_ITEM_DEC8U, true), // tu1z
ParsedFormatterItemInfo::new(FORMAT_ITEM_HEX16, false), // x ParsedFormatterItemInfo::new(FORMAT_ITEM_HEX16, false), // x
@ -512,5 +554,6 @@ fn test_mixed_formats() {
ParsedFormatterItemInfo::new(FORMAT_ITEM_C, false), // tc ParsedFormatterItemInfo::new(FORMAT_ITEM_C, false), // tc
ParsedFormatterItemInfo::new(FORMAT_ITEM_F32, false), // tf ParsedFormatterItemInfo::new(FORMAT_ITEM_F32, false), // tf
ParsedFormatterItemInfo::new(FORMAT_ITEM_HEX16, false), // x ParsedFormatterItemInfo::new(FORMAT_ITEM_HEX16, false), // x
)); ]
);
} }

View file

@ -31,7 +31,6 @@ pub enum CommandLineInputs {
FileAndOffset((String, usize, Option<usize>)), FileAndOffset((String, usize, Option<usize>)),
} }
/// Interprets the commandline inputs of od. /// Interprets the commandline inputs of od.
/// ///
/// Returns either an unspecified number of filenames. /// Returns either an unspecified number of filenames.
@ -52,7 +51,7 @@ pub fn parse_inputs(matches: &CommandLineOpts) -> Result<CommandLineInputs, Stri
// if any of the options -A, -j, -N, -t, -v or -w are present there is no offset // if any of the options -A, -j, -N, -t, -v or -w are present there is no offset
if !matches.opts_present(&["A", "j", "N", "t", "v", "w"]) { if !matches.opts_present(&["A", "j", "N", "t", "v", "w"]) {
// test if the last input can be parsed as an offset. // test if the last input can be parsed as an offset.
let offset = parse_offset_operand(&input_strings[input_strings.len()-1]); let offset = parse_offset_operand(&input_strings[input_strings.len() - 1]);
match offset { match offset {
Ok(n) => { Ok(n) => {
// if there is just 1 input (stdin), an offset must start with '+' // if there is just 1 input (stdin), an offset must start with '+'
@ -60,7 +59,11 @@ pub fn parse_inputs(matches: &CommandLineOpts) -> Result<CommandLineInputs, Stri
return Ok(CommandLineInputs::FileAndOffset(("-".to_string(), n, None))); return Ok(CommandLineInputs::FileAndOffset(("-".to_string(), n, None)));
} }
if input_strings.len() == 2 { if input_strings.len() == 2 {
return Ok(CommandLineInputs::FileAndOffset((input_strings[0].clone(), n, None))); return Ok(CommandLineInputs::FileAndOffset((
input_strings[0].clone(),
n,
None,
)));
} }
} }
_ => { _ => {
@ -82,9 +85,7 @@ pub fn parse_inputs(matches: &CommandLineOpts) -> Result<CommandLineInputs, Stri
/// it returns CommandLineInputs::FileNames (also to differentiate from the offset == 0) /// it returns CommandLineInputs::FileNames (also to differentiate from the offset == 0)
pub fn parse_inputs_traditional(input_strings: Vec<String>) -> Result<CommandLineInputs, String> { pub fn parse_inputs_traditional(input_strings: Vec<String>) -> Result<CommandLineInputs, String> {
match input_strings.len() { match input_strings.len() {
0 => { 0 => Ok(CommandLineInputs::FileNames(vec!["-".to_string()])),
Ok(CommandLineInputs::FileNames(vec!["-".to_string()]))
}
1 => { 1 => {
let offset0 = parse_offset_operand(&input_strings[0]); let offset0 = parse_offset_operand(&input_strings[0]);
Ok(match offset0 { Ok(match offset0 {
@ -96,8 +97,16 @@ pub fn parse_inputs_traditional(input_strings: Vec<String>) -> Result<CommandLin
let offset0 = parse_offset_operand(&input_strings[0]); let offset0 = parse_offset_operand(&input_strings[0]);
let offset1 = parse_offset_operand(&input_strings[1]); let offset1 = parse_offset_operand(&input_strings[1]);
match (offset0, offset1) { match (offset0, offset1) {
(Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset(("-".to_string(), n, Some(m)))), (Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
(_, Ok(m)) => Ok(CommandLineInputs::FileAndOffset((input_strings[0].clone(), m, None))), "-".to_string(),
n,
Some(m),
))),
(_, Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
input_strings[0].clone(),
m,
None,
))),
_ => Err(format!("invalid offset: {}", input_strings[1])), _ => Err(format!("invalid offset: {}", input_strings[1])),
} }
} }
@ -105,14 +114,19 @@ pub fn parse_inputs_traditional(input_strings: Vec<String>) -> Result<CommandLin
let offset = parse_offset_operand(&input_strings[1]); let offset = parse_offset_operand(&input_strings[1]);
let label = parse_offset_operand(&input_strings[2]); let label = parse_offset_operand(&input_strings[2]);
match (offset, label) { match (offset, label) {
(Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((input_strings[0].clone(), n, Some(m)))), (Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
input_strings[0].clone(),
n,
Some(m),
))),
(Err(_), _) => Err(format!("invalid offset: {}", input_strings[1])), (Err(_), _) => Err(format!("invalid offset: {}", input_strings[1])),
(_, Err(_)) => Err(format!("invalid label: {}", input_strings[2])), (_, Err(_)) => Err(format!("invalid label: {}", input_strings[2])),
} }
} }
_ => { _ => Err(format!(
Err(format!("too many inputs after --traditional: {}", input_strings[3])) "too many inputs after --traditional: {}",
} input_strings[3]
)),
} }
} }
@ -146,7 +160,6 @@ pub fn parse_offset_operand(s: &String) -> Result<usize, &'static str> {
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -187,143 +200,151 @@ mod tests {
#[test] #[test]
fn test_parse_inputs_normal() { fn test_parse_inputs_normal() {
assert_eq!(CommandLineInputs::FileNames(vec!["-".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec!["-".to_string()]),
vec![], parse_inputs(&MockOptions::new(vec![], vec![])).unwrap()
vec![])).unwrap()); );
assert_eq!(CommandLineInputs::FileNames(vec!["-".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec!["-".to_string()]),
vec!["-"], parse_inputs(&MockOptions::new(vec!["-"], vec![])).unwrap()
vec![])).unwrap()); );
assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec!["file1".to_string()]),
vec!["file1"], parse_inputs(&MockOptions::new(vec!["file1"], vec![])).unwrap()
vec![])).unwrap()); );
assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string(), "file2".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec!["file1".to_string(), "file2".to_string()]),
vec!["file1", "file2"], parse_inputs(&MockOptions::new(vec!["file1", "file2"], vec![])).unwrap()
vec![])).unwrap()); );
assert_eq!(CommandLineInputs::FileNames(vec!["-".to_string(), "file1".to_string(), "file2".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec![
vec!["-", "file1", "file2"], "-".to_string(),
vec![])).unwrap()); "file1".to_string(),
"file2".to_string(),
]),
parse_inputs(&MockOptions::new(vec!["-", "file1", "file2"], vec![])).unwrap()
);
} }
#[test] #[test]
fn test_parse_inputs_with_offset() { fn test_parse_inputs_with_offset() {
// offset is found without filename, so stdin will be used. // offset is found without filename, so stdin will be used.
assert_eq!(CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)),
vec!["+10"], parse_inputs(&MockOptions::new(vec!["+10"], vec![])).unwrap()
vec![])).unwrap()); );
// offset must start with "+" if no input is specified. // offset must start with "+" if no input is specified.
assert_eq!(CommandLineInputs::FileNames(vec!["10".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec!["10".to_string()]),
vec!["10"], parse_inputs(&MockOptions::new(vec!["10"], vec![""])).unwrap()
vec![""])).unwrap()); );
// offset is not valid, so it is considered a filename. // offset is not valid, so it is considered a filename.
assert_eq!(CommandLineInputs::FileNames(vec!["+10a".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec!["+10a".to_string()]),
vec!["+10a"], parse_inputs(&MockOptions::new(vec!["+10a"], vec![""])).unwrap()
vec![""])).unwrap()); );
// if -j is included in the commandline, there cannot be an offset. // if -j is included in the commandline, there cannot be an offset.
assert_eq!(CommandLineInputs::FileNames(vec!["+10".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec!["+10".to_string()]),
vec!["+10"], parse_inputs(&MockOptions::new(vec!["+10"], vec!["j"])).unwrap()
vec!["j"])).unwrap()); );
// if -v is included in the commandline, there cannot be an offset. // if -v is included in the commandline, there cannot be an offset.
assert_eq!(CommandLineInputs::FileNames(vec!["+10".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec!["+10".to_string()]),
vec!["+10"], parse_inputs(&MockOptions::new(vec!["+10"], vec!["o", "v"])).unwrap()
vec!["o", "v"])).unwrap()); );
assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
vec!["file1", "+10"], parse_inputs(&MockOptions::new(vec!["file1", "+10"], vec![])).unwrap()
vec![])).unwrap()); );
// offset does not need to start with "+" if a filename is included. // offset does not need to start with "+" if a filename is included.
assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
vec!["file1", "10"], parse_inputs(&MockOptions::new(vec!["file1", "10"], vec![])).unwrap()
vec![])).unwrap()); );
assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string(), "+10a".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec!["file1".to_string(), "+10a".to_string()]),
vec!["file1", "+10a"], parse_inputs(&MockOptions::new(vec!["file1", "+10a"], vec![""])).unwrap()
vec![""])).unwrap()); );
assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string(), "+10".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec!["file1".to_string(), "+10".to_string()]),
vec!["file1", "+10"], parse_inputs(&MockOptions::new(vec!["file1", "+10"], vec!["j"])).unwrap()
vec!["j"])).unwrap()); );
// offset must be last on the commandline // offset must be last on the commandline
assert_eq!(CommandLineInputs::FileNames(vec!["+10".to_string(), "file1".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec!["+10".to_string(), "file1".to_string()]),
vec!["+10", "file1"], parse_inputs(&MockOptions::new(vec!["+10", "file1"], vec![""])).unwrap()
vec![""])).unwrap()); );
} }
#[test] #[test]
fn test_parse_inputs_traditional() { fn test_parse_inputs_traditional() {
// it should not return FileAndOffset to signal no offset was entered on the commandline. // it should not return FileAndOffset to signal no offset was entered on the commandline.
assert_eq!(CommandLineInputs::FileNames(vec!["-".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec!["-".to_string()]),
vec![], parse_inputs(&MockOptions::new(vec![], vec!["traditional"])).unwrap()
vec!["traditional"])).unwrap()); );
assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string()]), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileNames(vec!["file1".to_string()]),
vec!["file1"], parse_inputs(&MockOptions::new(vec!["file1"], vec!["traditional"])).unwrap()
vec!["traditional"])).unwrap()); );
// offset does not need to start with a + // offset does not need to start with a +
assert_eq!(CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)),
vec!["10"], parse_inputs(&MockOptions::new(vec!["10"], vec!["traditional"])).unwrap()
vec!["traditional"])).unwrap()); );
// valid offset and valid label // valid offset and valid label
assert_eq!(CommandLineInputs::FileAndOffset(("-".to_string(), 8, Some(8))), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileAndOffset(("-".to_string(), 8, Some(8))),
vec!["10", "10"], parse_inputs(&MockOptions::new(vec!["10", "10"], vec!["traditional"])).unwrap()
vec!["traditional"])).unwrap()); );
assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)), assert_eq!(
parse_inputs(&MockOptions::new( CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
vec!["file1", "10"], parse_inputs(&MockOptions::new(vec!["file1", "10"], vec!["traditional"])).unwrap()
vec!["traditional"])).unwrap()); );
// only one file is allowed, it must be the first // only one file is allowed, it must be the first
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(vec!["10", "file1"], vec!["traditional"])).unwrap_err();
vec!["10", "file1"],
vec!["traditional"])).unwrap_err();
assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, Some(8))), assert_eq!(
CommandLineInputs::FileAndOffset(("file1".to_string(), 8, Some(8))),
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!["file1", "10", "10"], vec!["file1", "10", "10"],
vec!["traditional"])).unwrap()); vec!["traditional"]
)).unwrap()
);
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!["10", "file1", "10"], vec!["10", "file1", "10"],
vec!["traditional"])).unwrap_err(); vec!["traditional"],
)).unwrap_err();
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!["10", "10", "file1"], vec!["10", "10", "file1"],
vec!["traditional"])).unwrap_err(); vec!["traditional"],
)).unwrap_err();
parse_inputs(&MockOptions::new( parse_inputs(&MockOptions::new(
vec!["10", "10", "10", "10"], vec!["10", "10", "10", "10"],
vec!["traditional"])).unwrap_err(); vec!["traditional"],
)).unwrap_err();
} }
fn parse_offset_operand_str(s: &str) -> Result<usize, &'static str> { fn parse_offset_operand_str(s: &str) -> Result<usize, &'static str> {

View file

@ -1,4 +1,3 @@
pub fn parse_number_of_bytes(s: &String) -> Result<usize, &'static str> { pub fn parse_number_of_bytes(s: &String) -> Result<usize, &'static str> {
let mut start = 0; let mut start = 0;
let mut len = s.len(); let mut len = s.len();
@ -17,7 +16,7 @@ pub fn parse_number_of_bytes(s: &String) -> Result<usize, &'static str> {
Some('b') if radix != 16 => { Some('b') if radix != 16 => {
multiply = 512; multiply = 512;
len -= 1; len -= 1;
}, }
Some('k') | Some('K') => { Some('k') | Some('K') => {
multiply = 1024; multiply = 1024;
len -= 1; len -= 1;
@ -59,8 +58,8 @@ pub fn parse_number_of_bytes(s: &String) -> Result<usize, &'static str> {
Some('E') => 1000 * 1000 * 1000 * 1000 * 1000 * 1000, Some('E') => 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
_ => return Err("parse failed"), _ => return Err("parse failed"),
} }
}, }
_ => {}, _ => {}
} }
match usize::from_str_radix(&s[start..len], radix) { match usize::from_str_radix(&s[start..len], radix) {
@ -120,9 +119,15 @@ fn test_parse_number_of_bytes() {
fn test_parse_number_of_bytes_64bits() { fn test_parse_number_of_bytes_64bits() {
assert_eq!(1099511627776, parse_number_of_bytes_str("1T").unwrap()); assert_eq!(1099511627776, parse_number_of_bytes_str("1T").unwrap());
assert_eq!(1125899906842624, parse_number_of_bytes_str("1P").unwrap()); assert_eq!(1125899906842624, parse_number_of_bytes_str("1P").unwrap());
assert_eq!(1152921504606846976, parse_number_of_bytes_str("1E").unwrap()); assert_eq!(
1152921504606846976,
parse_number_of_bytes_str("1E").unwrap()
);
assert_eq!(2000000000000, parse_number_of_bytes_str("2TB").unwrap()); assert_eq!(2000000000000, parse_number_of_bytes_str("2TB").unwrap());
assert_eq!(2000000000000000, parse_number_of_bytes_str("2PB").unwrap()); assert_eq!(2000000000000000, parse_number_of_bytes_str("2PB").unwrap());
assert_eq!(2000000000000000000, parse_number_of_bytes_str("2EB").unwrap()); assert_eq!(
2000000000000000000,
parse_number_of_bytes_str("2EB").unwrap()
);
} }

View file

@ -34,7 +34,9 @@ impl<R: Read> Read for PartialReader<R> {
if self.skip > 0 { if self.skip > 0 {
let buf_size = cmp::min(self.skip, MAX_SKIP_BUFFER); let buf_size = cmp::min(self.skip, MAX_SKIP_BUFFER);
let mut bytes: Vec<u8> = Vec::with_capacity(buf_size); let mut bytes: Vec<u8> = Vec::with_capacity(buf_size);
unsafe { bytes.set_len(buf_size); } unsafe {
bytes.set_len(buf_size);
}
while self.skip > 0 { while self.skip > 0 {
let skip_count = cmp::min(self.skip, buf_size); let skip_count = cmp::min(self.skip, buf_size);
@ -49,15 +51,19 @@ impl<R: Read> Read for PartialReader<R> {
None => self.inner.read(out), None => self.inner.read(out),
Some(0) => Ok(0), Some(0) => Ok(0),
Some(ref mut limit) => { Some(ref mut limit) => {
let slice = if *limit > out.len() { out } else { &mut out[0..*limit] }; let slice = if *limit > out.len() {
out
} else {
&mut out[0..*limit]
};
match self.inner.read(slice) { match self.inner.read(slice) {
Err(e) => Err(e), Err(e) => Err(e),
Ok(r) => { Ok(r) => {
*limit -= r; *limit -= r;
Ok(r) Ok(r)
},
} }
}, }
}
} }
} }
} }
@ -71,7 +77,7 @@ impl<R: HasError> HasError for PartialReader<R> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::io::{Cursor, Read, ErrorKind}; use std::io::{Cursor, ErrorKind, Read};
use std::error::Error; use std::error::Error;
use mockstream::*; use mockstream::*;
@ -127,8 +133,7 @@ mod tests {
#[test] #[test]
fn test_read_skipping_with_two_reads_during_skip() { fn test_read_skipping_with_two_reads_during_skip() {
let mut v = [0; 10]; let mut v = [0; 10];
let c = Cursor::new(&b"a"[..]) let c = Cursor::new(&b"a"[..]).chain(Cursor::new(&b"bcdefgh"[..]));
.chain(Cursor::new(&b"bcdefgh"[..]));
let mut sut = PartialReader::new(c, 2, None); let mut sut = PartialReader::new(c, 2, None);
assert_eq!(sut.read(v.as_mut()).unwrap(), 6); assert_eq!(sut.read(v.as_mut()).unwrap(), 6);

View file

@ -28,7 +28,7 @@ pub trait PeekRead {
/// ///
/// # Panics /// # Panics
/// Might panic if `peek_size` is larger then the size of `out` /// Might panic if `peek_size` is larger then the size of `out`
fn peek_read(&mut self, out: &mut [u8], peek_size: usize) -> io::Result<(usize,usize)>; fn peek_read(&mut self, out: &mut [u8], peek_size: usize) -> io::Result<(usize, usize)>;
} }
/// Wrapper for `std::io::Read` allowing to peek into the data to be read. /// Wrapper for `std::io::Read` allowing to peek into the data to be read.
@ -53,7 +53,7 @@ impl<R: Read> PeekReader<R> {
Ok(n) => { Ok(n) => {
self.temp_buffer.drain(..n); self.temp_buffer.drain(..n);
n n
}, }
Err(_) => 0, Err(_) => 0,
} }
} }
@ -83,7 +83,7 @@ impl<R: Read> PeekRead for PeekReader<R> {
/// ///
/// # Panics /// # Panics
/// If `peek_size` is larger then the size of `out` /// If `peek_size` is larger then the size of `out`
fn peek_read(&mut self, out: &mut [u8], peek_size: usize) -> io::Result<(usize,usize)> { fn peek_read(&mut self, out: &mut [u8], peek_size: usize) -> io::Result<(usize, usize)> {
assert!(out.len() >= peek_size); assert!(out.len() >= peek_size);
match self.read(out) { match self.read(out) {
Err(e) => Err(e), Err(e) => Err(e),
@ -97,7 +97,7 @@ impl<R: Read> PeekRead for PeekReader<R> {
self.write_to_tempbuffer(&out[real_size..bytes_in_buffer]); self.write_to_tempbuffer(&out[real_size..bytes_in_buffer]);
Ok((real_size, actual_peek_size)) Ok((real_size, actual_peek_size))
} }
}, }
} }
} }
} }
@ -127,7 +127,7 @@ mod tests {
let mut sut = PeekReader::new(Cursor::new(&b"abcdefgh"[..])); let mut sut = PeekReader::new(Cursor::new(&b"abcdefgh"[..]));
let mut v = [0; 10]; let mut v = [0; 10];
assert_eq!(sut.peek_read(v.as_mut(), 0).unwrap(), (8,0)); assert_eq!(sut.peek_read(v.as_mut(), 0).unwrap(), (8, 0));
assert_eq!(v, [0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0, 0]); assert_eq!(v, [0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0, 0]);
} }

View file

@ -13,51 +13,33 @@ pub static FORMAT_ITEM_C: FormatterItemInfo = FormatterItemInfo {
formatter: FormatWriter::MultibyteWriter(format_item_c), formatter: FormatWriter::MultibyteWriter(format_item_c),
}; };
static A_CHRS: [&'static str; 128] = [
static A_CHRS: [&'static str; 128] = "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", "bs", "ht", "nl", "vt", "ff", "cr",
["nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", "so", "si", "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", "can", "em", "sub", "esc",
"bs", "ht", "nl", "vt", "ff", "cr", "so", "si", "fs", "gs", "rs", "us", "sp", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-",
"dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@",
"can", "em", "sub", "esc", "fs", "gs", "rs", "us", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
"sp", "!", "\"", "#", "$", "%", "&", "'", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f",
"(", ")", "*", "+", ",", "-", ".", "/", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y",
"0", "1", "2", "3", "4", "5", "6", "7", "z", "{", "|", "}", "~", "del",
"8", "9", ":", ";", "<", "=", ">", "?", ];
"@", "A", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "O",
"P", "Q", "R", "S", "T", "U", "V", "W",
"X", "Y", "Z", "[", "\\", "]", "^", "_",
"`", "a", "b", "c", "d", "e", "f", "g",
"h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w",
"x", "y", "z", "{", "|", "}", "~", "del"];
fn format_item_a(p: u64) -> String { fn format_item_a(p: u64) -> String {
// itembytes == 1 // itembytes == 1
let b = (p & 0x7f) as u8; let b = (p & 0x7f) as u8;
format!("{:>4}", A_CHRS.get(b as usize).unwrap_or(&"??") format!("{:>4}", A_CHRS.get(b as usize).unwrap_or(&"??"))
)
} }
static C_CHRS: [&'static str; 128] = [ static C_CHRS: [&'static str; 128] = [
"\\0", "001", "002", "003", "004", "005", "006", "\\a", "\\0", "001", "002", "003", "004", "005", "006", "\\a", "\\b", "\\t", "\\n", "\\v", "\\f",
"\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "016", "017", "\\r", "016", "017", "020", "021", "022", "023", "024", "025", "026", "027", "030", "031",
"020", "021", "022", "023", "024", "025", "026", "027", "032", "033", "034", "035", "036", "037", " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")",
"030", "031", "032", "033", "034", "035", "036", "037", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<",
" ", "!", "\"", "#", "$", "%", "&", "'", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
"(", ")", "*", "+", ",", "-", ".", "/", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b",
"0", "1", "2", "3", "4", "5", "6", "7", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u",
"8", "9", ":", ";", "<", "=", ">", "?", "v", "w", "x", "y", "z", "{", "|", "}", "~", "177",
"@", "A", "B", "C", "D", "E", "F", "G", ];
"H", "I", "J", "K", "L", "M", "N", "O",
"P", "Q", "R", "S", "T", "U", "V", "W",
"X", "Y", "Z", "[", "\\", "]", "^", "_",
"`", "a", "b", "c", "d", "e", "f", "g",
"h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w",
"x", "y", "z", "{", "|", "}", "~", "177"];
fn format_item_c(bytes: &[u8]) -> String { fn format_item_c(bytes: &[u8]) -> String {
// itembytes == 1 // itembytes == 1
@ -74,20 +56,20 @@ fn format_item_c(bytes: &[u8]) -> String {
} else if ((b & 0xe0) == 0xc0) && (bytes.len() >= 2) { } else if ((b & 0xe0) == 0xc0) && (bytes.len() >= 2) {
// start of a 2 octet utf-8 sequence // start of a 2 octet utf-8 sequence
match from_utf8(&bytes[0..2]) { match from_utf8(&bytes[0..2]) {
Ok(s) => { format!("{:>4}", s) }, Ok(s) => format!("{:>4}", s),
Err(_) => { format!(" {:03o}", b) }, Err(_) => format!(" {:03o}", b),
} }
} else if ((b & 0xf0) == 0xe0) && (bytes.len() >= 3) { } else if ((b & 0xf0) == 0xe0) && (bytes.len() >= 3) {
// start of a 3 octet utf-8 sequence // start of a 3 octet utf-8 sequence
match from_utf8(&bytes[0..3]) { match from_utf8(&bytes[0..3]) {
Ok(s) => { format!("{:>4}", s) }, Ok(s) => format!("{:>4}", s),
Err(_) => { format!(" {:03o}", b) }, Err(_) => format!(" {:03o}", b),
} }
} else if ((b & 0xf8) == 0xf0) && (bytes.len() >= 4) { } else if ((b & 0xf8) == 0xf0) && (bytes.len() >= 4) {
// start of a 4 octet utf-8 sequence // start of a 4 octet utf-8 sequence
match from_utf8(&bytes[0..4]) { match from_utf8(&bytes[0..4]) {
Ok(s) => { format!("{:>4}", s) }, Ok(s) => format!("{:>4}", s),
Err(_) => { format!(" {:03o}", b) }, Err(_) => format!(" {:03o}", b),
} }
} else { } else {
// invalid utf-8 // invalid utf-8
@ -146,7 +128,10 @@ fn test_format_item_c() {
assert_eq!(" \u{1000}", format_item_c(&[0xe1, 0x80, 0x80, 0x21])); assert_eq!(" \u{1000}", format_item_c(&[0xe1, 0x80, 0x80, 0x21]));
assert_eq!(" \u{1f496}", format_item_c(&[0xf0, 0x9f, 0x92, 0x96])); assert_eq!(" \u{1f496}", format_item_c(&[0xf0, 0x9f, 0x92, 0x96]));
assert_eq!(" \u{1f496}", format_item_c(&[0xf0, 0x9f, 0x92, 0x96, 0x21])); assert_eq!(
" \u{1f496}",
format_item_c(&[0xf0, 0x9f, 0x92, 0x96, 0x21])
);
assert_eq!(" 300", format_item_c(&[0xc0, 0x80])); // invalid utf-8 (MUTF-8 null) assert_eq!(" 300", format_item_c(&[0xc0, 0x80])); // invalid utf-8 (MUTF-8 null)
assert_eq!(" 301", format_item_c(&[0xc1, 0xa1])); // invalid utf-8 assert_eq!(" 301", format_item_c(&[0xc1, 0xa1])); // invalid utf-8
@ -162,5 +147,8 @@ fn test_format_item_c() {
#[test] #[test]
fn test_format_ascii_dump() { fn test_format_ascii_dump() {
assert_eq!(">.<", format_ascii_dump(&[0x00])); assert_eq!(">.<", format_ascii_dump(&[0x00]));
assert_eq!(">. A~.<", format_ascii_dump(&[0x1f, 0x20, 0x41, 0x7e, 0x7f])); assert_eq!(
">. A~.<",
format_ascii_dump(&[0x1f, 0x20, 0x41, 0x7e, 0x7f])
);
} }

View file

@ -58,9 +58,13 @@ fn format_flo64(f: f64) -> String {
fn format_float(f: f64, width: usize, precision: usize) -> String { fn format_float(f: f64, width: usize, precision: usize) -> String {
if !f.is_normal() { if !f.is_normal() {
if f == -0.0 && f.is_sign_negative() { return format!("{:>width$}", "-0", width = width) } if f == -0.0 && f.is_sign_negative() {
if f == 0.0 || !f.is_finite() { return format!("{:width$}", f, width = width) } return format!("{:>width$}", "-0", width = width);
return format!("{:width$e}", f, width = width) // subnormal numbers }
if f == 0.0 || !f.is_finite() {
return format!("{:width$}", f, width = width);
}
return format!("{:width$e}", f, width = width); // subnormal numbers
} }
let mut l = f.abs().log10().floor() as i32; let mut l = f.abs().log10().floor() as i32;
@ -72,17 +76,16 @@ fn format_float(f: f64, width: usize, precision: usize) -> String {
} }
if l >= 0 && l <= (precision as i32 - 1) { if l >= 0 && l <= (precision as i32 - 1) {
format!("{:width$.dec$}", f, format!(
"{:width$.dec$}",
f,
width = width, width = width,
dec = (precision-1) - l as usize) dec = (precision - 1) - l as usize
)
} else if l == -1 { } else if l == -1 {
format!("{:width$.dec$}", f, format!("{:width$.dec$}", f, width = width, dec = precision)
width = width,
dec = precision)
} else { } else {
format!("{:width$.dec$e}", f, format!("{:width$.dec$e}", f, width = width, dec = precision - 1)
width = width,
dec = precision - 1)
} }
} }
@ -150,8 +153,8 @@ fn test_format_flo32() {
assert_eq!(format_flo32(3.4028233e38), " 3.4028233e38"); assert_eq!(format_flo32(3.4028233e38), " 3.4028233e38");
assert_eq!(format_flo32(-3.4028233e38), " -3.4028233e38"); assert_eq!(format_flo32(-3.4028233e38), " -3.4028233e38");
assert_eq!(format_flo32(-1.1663108e-38),"-1.1663108e-38"); assert_eq!(format_flo32(-1.1663108e-38), "-1.1663108e-38");
assert_eq!(format_flo32(-4.7019771e-38),"-4.7019771e-38"); assert_eq!(format_flo32(-4.7019771e-38), "-4.7019771e-38");
assert_eq!(format_flo32(1e-45), " 1e-45"); assert_eq!(format_flo32(1e-45), " 1e-45");
assert_eq!(format_flo32(-3.402823466e+38), " -3.4028235e38"); assert_eq!(format_flo32(-3.402823466e+38), " -3.4028235e38");
@ -167,13 +170,22 @@ fn test_format_flo64() {
assert_eq!(format_flo64(1.0), " 1.0000000000000000"); assert_eq!(format_flo64(1.0), " 1.0000000000000000");
assert_eq!(format_flo64(10.0), " 10.000000000000000"); assert_eq!(format_flo64(10.0), " 10.000000000000000");
assert_eq!(format_flo64(1000000000000000.0), " 1000000000000000.0"); assert_eq!(format_flo64(1000000000000000.0), " 1000000000000000.0");
assert_eq!(format_flo64(10000000000000000.0), " 10000000000000000"); assert_eq!(
assert_eq!(format_flo64(100000000000000000.0), " 1.0000000000000000e17"); format_flo64(10000000000000000.0),
" 10000000000000000"
);
assert_eq!(
format_flo64(100000000000000000.0),
" 1.0000000000000000e17"
);
assert_eq!(format_flo64(-0.1), " -0.10000000000000001"); assert_eq!(format_flo64(-0.1), " -0.10000000000000001");
assert_eq!(format_flo64(-0.01), " -1.0000000000000000e-2"); assert_eq!(format_flo64(-0.01), " -1.0000000000000000e-2");
assert_eq!(format_flo64(-2.2250738585072014e-308),"-2.2250738585072014e-308"); assert_eq!(
format_flo64(-2.2250738585072014e-308),
"-2.2250738585072014e-308"
);
assert_eq!(format_flo64(4e-320), " 4e-320"); assert_eq!(format_flo64(4e-320), " 4e-320");
assert_eq!(format_flo64(f64::NAN), " NaN"); assert_eq!(format_flo64(f64::NAN), " NaN");
assert_eq!(format_flo64(f64::INFINITY), " inf"); assert_eq!(format_flo64(f64::INFINITY), " inf");

View file

@ -51,12 +51,11 @@ macro_rules! int_writer_signed {
} }
/// Extends a signed number in `item` of `itembytes` bytes into a (signed) i64 /// Extends a signed number in `item` of `itembytes` bytes into a (signed) i64
fn sign_extend(item: u64, itembytes: usize) -> i64{ fn sign_extend(item: u64, itembytes: usize) -> i64 {
let shift = 64 - itembytes * 8; let shift = 64 - itembytes * 8;
(item << shift) as i64 >> shift (item << shift) as i64 >> shift
} }
int_writer_unsigned!(FORMAT_ITEM_OCT8, 1, 4, format_item_oct8, OCT!()); // max: 377 int_writer_unsigned!(FORMAT_ITEM_OCT8, 1, 4, format_item_oct8, OCT!()); // max: 377
int_writer_unsigned!(FORMAT_ITEM_OCT16, 2, 7, format_item_oct16, OCT!()); // max: 177777 int_writer_unsigned!(FORMAT_ITEM_OCT16, 2, 7, format_item_oct16, OCT!()); // max: 177777
int_writer_unsigned!(FORMAT_ITEM_OCT32, 4, 12, format_item_oct32, OCT!()); // max: 37777777777 int_writer_unsigned!(FORMAT_ITEM_OCT32, 4, 12, format_item_oct32, OCT!()); // max: 37777777777
@ -79,14 +78,38 @@ int_writer_signed!(FORMAT_ITEM_DEC64S, 8, 21, format_item_dec_s64, DEC!()); // m
#[test] #[test]
fn test_sign_extend() { fn test_sign_extend() {
assert_eq!(0xffffffffffffff80u64 as i64, sign_extend(0x0000000000000080, 1)); assert_eq!(
assert_eq!(0xffffffffffff8000u64 as i64, sign_extend(0x0000000000008000, 2)); 0xffffffffffffff80u64 as i64,
assert_eq!(0xffffffffff800000u64 as i64, sign_extend(0x0000000000800000, 3)); sign_extend(0x0000000000000080, 1)
assert_eq!(0xffffffff80000000u64 as i64, sign_extend(0x0000000080000000, 4)); );
assert_eq!(0xffffff8000000000u64 as i64, sign_extend(0x0000008000000000, 5)); assert_eq!(
assert_eq!(0xffff800000000000u64 as i64, sign_extend(0x0000800000000000, 6)); 0xffffffffffff8000u64 as i64,
assert_eq!(0xff80000000000000u64 as i64, sign_extend(0x0080000000000000, 7)); sign_extend(0x0000000000008000, 2)
assert_eq!(0x8000000000000000u64 as i64, sign_extend(0x8000000000000000, 8)); );
assert_eq!(
0xffffffffff800000u64 as i64,
sign_extend(0x0000000000800000, 3)
);
assert_eq!(
0xffffffff80000000u64 as i64,
sign_extend(0x0000000080000000, 4)
);
assert_eq!(
0xffffff8000000000u64 as i64,
sign_extend(0x0000008000000000, 5)
);
assert_eq!(
0xffff800000000000u64 as i64,
sign_extend(0x0000800000000000, 6)
);
assert_eq!(
0xff80000000000000u64 as i64,
sign_extend(0x0080000000000000, 7)
);
assert_eq!(
0x8000000000000000u64 as i64,
sign_extend(0x8000000000000000, 8)
);
assert_eq!(0x000000000000007f, sign_extend(0x000000000000007f, 1)); assert_eq!(0x000000000000007f, sign_extend(0x000000000000007f, 1));
assert_eq!(0x0000000000007fff, sign_extend(0x0000000000007fff, 2)); assert_eq!(0x0000000000007fff, sign_extend(0x0000000000007fff, 2));
@ -107,7 +130,10 @@ fn test_format_item_oct() {
assert_eq!(" 00000000000", format_item_oct32(0)); assert_eq!(" 00000000000", format_item_oct32(0));
assert_eq!(" 37777777777", format_item_oct32(0xffffffff)); assert_eq!(" 37777777777", format_item_oct32(0xffffffff));
assert_eq!(" 0000000000000000000000", format_item_oct64(0)); assert_eq!(" 0000000000000000000000", format_item_oct64(0));
assert_eq!(" 1777777777777777777777", format_item_oct64(0xffffffffffffffff)); assert_eq!(
" 1777777777777777777777",
format_item_oct64(0xffffffffffffffff)
);
} }
#[test] #[test]
@ -131,7 +157,10 @@ fn test_format_item_dec_u() {
assert_eq!(" 0", format_item_dec_u32(0)); assert_eq!(" 0", format_item_dec_u32(0));
assert_eq!(" 4294967295", format_item_dec_u32(0xffffffff)); assert_eq!(" 4294967295", format_item_dec_u32(0xffffffff));
assert_eq!(" 0", format_item_dec_u64(0)); assert_eq!(" 0", format_item_dec_u64(0));
assert_eq!(" 18446744073709551615", format_item_dec_u64(0xffffffffffffffff)); assert_eq!(
" 18446744073709551615",
format_item_dec_u64(0xffffffffffffffff)
);
} }
#[test] #[test]
@ -146,6 +175,12 @@ fn test_format_item_dec_s() {
assert_eq!(" 2147483647", format_item_dec_s32(0x7fffffff)); assert_eq!(" 2147483647", format_item_dec_s32(0x7fffffff));
assert_eq!(" -2147483648", format_item_dec_s32(0x80000000)); assert_eq!(" -2147483648", format_item_dec_s32(0x80000000));
assert_eq!(" 0", format_item_dec_s64(0)); assert_eq!(" 0", format_item_dec_s64(0));
assert_eq!(" 9223372036854775807", format_item_dec_s64(0x7fffffffffffffff)); assert_eq!(
assert_eq!(" -9223372036854775808", format_item_dec_s64(0x8000000000000000)); " 9223372036854775807",
format_item_dec_s64(0x7fffffffffffffff)
);
assert_eq!(
" -9223372036854775808",
format_item_dec_s64(0x8000000000000000)
);
} }

View file

@ -14,7 +14,7 @@ extern crate getopts;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use std::io::{BufRead, BufReader, Read, stdin}; use std::io::{stdin, BufRead, BufReader, Read};
use std::iter::repeat; use std::iter::repeat;
use std::fs::File; use std::fs::File;
use std::path::Path; use std::path::Path;
@ -25,24 +25,36 @@ static VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = getopts::Options::new(); let mut opts = getopts::Options::new();
opts.optflag("s", "serial", "paste one file at a time instead of in parallel"); opts.optflag(
opts.optopt("d", "delimiters", "reuse characters from LIST instead of TABs", "LIST"); "s",
"serial",
"paste one file at a time instead of in parallel",
);
opts.optopt(
"d",
"delimiters",
"reuse characters from LIST instead of TABs",
"LIST",
);
opts.optflag("h", "help", "display this help and exit"); opts.optflag("h", "help", "display this help and exit");
opts.optflag("V", "version", "output version information and exit"); opts.optflag("V", "version", "output version information and exit");
let matches = match opts.parse(&args[1..]) { let matches = match opts.parse(&args[1..]) {
Ok(m) => m, Ok(m) => m,
Err(e) => crash!(1, "{}", e) Err(e) => crash!(1, "{}", e),
}; };
if matches.opt_present("help") { if matches.opt_present("help") {
let msg = format!("{0} {1} let msg = format!(
"{0} {1}
Usage: Usage:
{0} [OPTION]... [FILE]... {0} [OPTION]... [FILE]...
Write lines consisting of the sequentially corresponding lines from each Write lines consisting of the sequentially corresponding lines from each
FILE, separated by TABs, to standard output.", NAME, VERSION); FILE, separated by TABs, to standard output.",
NAME, VERSION
);
print!("{}", opts.usage(&msg)); print!("{}", opts.usage(&msg));
} else if matches.opt_present("version") { } else if matches.opt_present("version") {
println!("{} {}", NAME, VERSION); println!("{} {}", NAME, VERSION);
@ -56,18 +68,22 @@ FILE, separated by TABs, to standard output.", NAME, VERSION);
} }
fn paste(filenames: Vec<String>, serial: bool, delimiters: String) { fn paste(filenames: Vec<String>, serial: bool, delimiters: String) {
let mut files: Vec<BufReader<Box<Read>>> = filenames.into_iter().map(|name| let mut files: Vec<BufReader<Box<Read>>> = filenames
BufReader::new( .into_iter()
if name == "-" { .map(|name| {
BufReader::new(if name == "-" {
Box::new(stdin()) as Box<Read> Box::new(stdin()) as Box<Read>
} else { } else {
let r = crash_if_err!(1, File::open(Path::new(&name))); let r = crash_if_err!(1, File::open(Path::new(&name)));
Box::new(r) as Box<Read> Box::new(r) as Box<Read>
} })
) })
).collect(); .collect();
let delimiters: Vec<String> = unescape(delimiters).chars().map(|x| x.to_string()).collect(); let delimiters: Vec<String> = unescape(delimiters)
.chars()
.map(|x| x.to_string())
.collect();
let mut delim_count = 0; let mut delim_count = 0;
if serial { if serial {
@ -81,11 +97,11 @@ fn paste(filenames: Vec<String>, serial: bool, delimiters: String) {
output.push_str(line.trim_right()); output.push_str(line.trim_right());
output.push_str(&delimiters[delim_count % delimiters.len()]); output.push_str(&delimiters[delim_count % delimiters.len()]);
} }
Err(e) => crash!(1, "{}", e.to_string()) Err(e) => crash!(1, "{}", e.to_string()),
} }
delim_count += 1; delim_count += 1;
} }
println!("{}", &output[..output.len()-1]); println!("{}", &output[..output.len() - 1]);
} }
} else { } else {
let mut eof: Vec<bool> = repeat(false).take(files.len()).collect(); let mut eof: Vec<bool> = repeat(false).take(files.len()).collect();
@ -103,7 +119,7 @@ fn paste(filenames: Vec<String>, serial: bool, delimiters: String) {
eof_count += 1; eof_count += 1;
} }
Ok(_) => output.push_str(line.trim_right()), Ok(_) => output.push_str(line.trim_right()),
Err(e) => crash!(1, "{}", e.to_string()) Err(e) => crash!(1, "{}", e.to_string()),
} }
} }
output.push_str(&delimiters[delim_count % delimiters.len()]); output.push_str(&delimiters[delim_count % delimiters.len()]);
@ -112,7 +128,7 @@ fn paste(filenames: Vec<String>, serial: bool, delimiters: String) {
if files.len() == eof_count { if files.len() == eof_count {
break; break;
} }
println!("{}", &output[..output.len()-1]); println!("{}", &output[..output.len() - 1]);
delim_count = 0; delim_count = 0;
} }
} }

View file

@ -18,7 +18,7 @@ extern crate uucore;
use getopts::Options; use getopts::Options;
use std::fs; use std::fs;
use std::io::{Write, ErrorKind}; use std::io::{ErrorKind, Write};
// operating mode // operating mode
enum Mode { enum Mode {
@ -27,7 +27,7 @@ enum Mode {
Extra, // check for leading dashes and empty names Extra, // check for leading dashes and empty names
Both, // a combination of `Basic` and `Extra` Both, // a combination of `Basic` and `Extra`
Help, // show help Help, // show help
Version // show version information Version, // show version information
} }
static NAME: &'static str = "pathchk"; static NAME: &'static str = "pathchk";
@ -41,15 +41,21 @@ pub fn uumain(args: Vec<String>) -> i32 {
// add options // add options
let mut opts = Options::new(); let mut opts = Options::new();
opts.optflag("p", "posix", "check for (most) POSIX systems"); opts.optflag("p", "posix", "check for (most) POSIX systems");
opts.optflag("P", opts.optflag(
"posix-special", "check for empty names and leading \"-\""); "P",
opts.optflag("", "posix-special",
"portability", "check for all POSIX systems (equivalent to -p -P)"); "check for empty names and leading \"-\"",
);
opts.optflag(
"",
"portability",
"check for all POSIX systems (equivalent to -p -P)",
);
opts.optflag("h", "help", "display this help text and exit"); opts.optflag("h", "help", "display this help text and exit");
opts.optflag("V", "version", "output version information and exit"); opts.optflag("V", "version", "output version information and exit");
let matches = match opts.parse(&args[1..]) { let matches = match opts.parse(&args[1..]) {
Ok(m) => m, Ok(m) => m,
Err(e) => { crash!(1, "{}", e) } Err(e) => crash!(1, "{}", e),
}; };
// set working mode // set working mode
@ -57,9 +63,9 @@ pub fn uumain(args: Vec<String>) -> i32 {
Mode::Version Mode::Version
} else if matches.opt_present("help") { } else if matches.opt_present("help") {
Mode::Help Mode::Help
} else if (matches.opt_present("posix") && } else if (matches.opt_present("posix") && matches.opt_present("posix-special"))
matches.opt_present("posix-special")) || || matches.opt_present("portability")
matches.opt_present("portability") { {
Mode::Both Mode::Both
} else if matches.opt_present("posix") { } else if matches.opt_present("posix") {
Mode::Basic Mode::Basic
@ -71,14 +77,18 @@ pub fn uumain(args: Vec<String>) -> i32 {
// take necessary actions // take necessary actions
match mode { match mode {
Mode::Help => { help(opts); 0 } Mode::Help => {
Mode::Version => { version(); 0 } help(opts);
0
}
Mode::Version => {
version();
0
}
_ => { _ => {
let mut res = true; let mut res = true;
if matches.free.len() == 0 { if matches.free.len() == 0 {
show_error!( show_error!("missing operand\nTry {} --help for more information", NAME);
"missing operand\nTry {} --help for more information", NAME
);
res = false; res = false;
} }
// free strings are path operands // free strings are path operands
@ -91,15 +101,22 @@ pub fn uumain(args: Vec<String>) -> i32 {
res &= check_path(&mode, &path); res &= check_path(&mode, &path);
} }
// determine error code // determine error code
if res { 0 } else { 1 } if res {
0
} else {
1
}
} }
} }
} }
// print help // print help
fn help(opts: Options) { fn help(opts: Options) {
let msg = format!("Usage: {} [OPTION]... NAME...\n\n\ let msg = format!(
Diagnose invalid or unportable file names.", NAME); "Usage: {} [OPTION]... NAME...\n\n\
Diagnose invalid or unportable file names.",
NAME
);
print!("{}", opts.usage(&msg)); print!("{}", opts.usage(&msg));
} }
@ -115,7 +132,7 @@ fn check_path(mode: &Mode, path: &[String]) -> bool {
Mode::Basic => check_basic(&path), Mode::Basic => check_basic(&path),
Mode::Extra => check_default(&path) && check_extra(&path), Mode::Extra => check_default(&path) && check_extra(&path),
Mode::Both => check_basic(&path) && check_extra(&path), Mode::Both => check_basic(&path) && check_extra(&path),
_ => check_default(&path) _ => check_default(&path),
} }
} }
@ -125,9 +142,13 @@ fn check_basic(path: &[String]) -> bool {
let total_len = joined_path.len(); let total_len = joined_path.len();
// path length // path length
if total_len > POSIX_PATH_MAX { if total_len > POSIX_PATH_MAX {
writeln!(&mut std::io::stderr(), writeln!(
&mut std::io::stderr(),
"limit {} exceeded by length {} of file name {}", "limit {} exceeded by length {} of file name {}",
POSIX_PATH_MAX, total_len, joined_path); POSIX_PATH_MAX,
total_len,
joined_path
);
return false; return false;
} else if total_len == 0 { } else if total_len == 0 {
writeln!(&mut std::io::stderr(), "empty file name"); writeln!(&mut std::io::stderr(), "empty file name");
@ -137,9 +158,13 @@ fn check_basic(path: &[String]) -> bool {
for p in path { for p in path {
let component_len = p.len(); let component_len = p.len();
if component_len > POSIX_NAME_MAX { if component_len > POSIX_NAME_MAX {
writeln!(&mut std::io::stderr(), writeln!(
&mut std::io::stderr(),
"limit {} exceeded by length {} of file name component '{}'", "limit {} exceeded by length {} of file name component '{}'",
POSIX_NAME_MAX, component_len, p); POSIX_NAME_MAX,
component_len,
p
);
return false; return false;
} }
if !check_portable_chars(&p) { if !check_portable_chars(&p) {
@ -155,8 +180,11 @@ fn check_extra(path: &[String]) -> bool {
// components: leading hyphens // components: leading hyphens
for p in path { for p in path {
if !no_leading_hyphen(&p) { if !no_leading_hyphen(&p) {
writeln!(&mut std::io::stderr(), writeln!(
"leading hyphen in file name component '{}'", p); &mut std::io::stderr(),
"leading hyphen in file name component '{}'",
p
);
return false; return false;
} }
} }
@ -174,18 +202,26 @@ fn check_default(path: &[String]) -> bool {
let total_len = joined_path.len(); let total_len = joined_path.len();
// path length // path length
if total_len > libc::PATH_MAX as usize { if total_len > libc::PATH_MAX as usize {
writeln!(&mut std::io::stderr(), writeln!(
&mut std::io::stderr(),
"limit {} exceeded by length {} of file name '{}'", "limit {} exceeded by length {} of file name '{}'",
libc::PATH_MAX, total_len, joined_path); libc::PATH_MAX,
total_len,
joined_path
);
return false; return false;
} }
// components: length // components: length
for p in path { for p in path {
let component_len = p.len(); let component_len = p.len();
if component_len > libc::FILENAME_MAX as usize { if component_len > libc::FILENAME_MAX as usize {
writeln!(&mut std::io::stderr(), writeln!(
&mut std::io::stderr(),
"limit {} exceeded by length {} of file name component '{}'", "limit {} exceeded by length {} of file name component '{}'",
libc::FILENAME_MAX, component_len, p); libc::FILENAME_MAX,
component_len,
p
);
return false; return false;
} }
} }
@ -203,7 +239,7 @@ fn check_searchable(path: &String) -> bool {
} else { } else {
writeln!(&mut std::io::stderr(), "{}", e); writeln!(&mut std::io::stderr(), "{}", e);
false false
} },
} }
} }
@ -214,14 +250,15 @@ fn no_leading_hyphen(path_segment: &String) -> bool {
// check whether a path segment contains only valid (read: portable) characters // check whether a path segment contains only valid (read: portable) characters
fn check_portable_chars(path_segment: &String) -> bool { fn check_portable_chars(path_segment: &String) -> bool {
let valid_str = let valid_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-".to_string();
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-"
.to_string();
for ch in path_segment.chars() { for ch in path_segment.chars() {
if !valid_str.contains(ch) { if !valid_str.contains(ch) {
writeln!(&mut std::io::stderr(), writeln!(
&mut std::io::stderr(),
"nonportable character '{}' in file name component '{}'", "nonportable character '{}' in file name component '{}'",
ch, path_segment); ch,
path_segment
);
return false; return false;
} }
} }

View file

@ -1,5 +1,4 @@
#![crate_name = "uu_pinky"] #![crate_name = "uu_pinky"]
// This file is part of the uutils coreutils package. // This file is part of the uutils coreutils package.
// //
// (c) Jian Zeng <anonymousknight96@gmail.com> // (c) Jian Zeng <anonymousknight96@gmail.com>
@ -7,8 +6,8 @@
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
// //
#![cfg_attr(feature="clippy", feature(plugin))] #![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))] #![cfg_attr(feature = "clippy", plugin(clippy))]
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
@ -31,7 +30,8 @@ static SUMMARY: &'static str = "A lightweight 'finger' program; print user info
const BUFSIZE: usize = 1024; const BUFSIZE: usize = 1024;
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let long_help = &format!(" let long_help = &format!(
"
-l produce long format output for the specified USERs -l produce long format output for the specified USERs
-b omit the user's home directory and shell in long format -b omit the user's home directory and shell in long format
-h omit the user's project file in long format -h omit the user's project file in long format
@ -45,25 +45,35 @@ pub fn uumain(args: Vec<String>) -> i32 {
--help display this help and exit --help display this help and exit
--version output version information and exit --version output version information and exit
The utmp file will be {}", utmpx::DEFAULT_FILE); The utmp file will be {}",
utmpx::DEFAULT_FILE
);
let mut opts = new_coreopts!(SYNTAX, SUMMARY, &long_help); let mut opts = new_coreopts!(SYNTAX, SUMMARY, &long_help);
opts.optflag("l", opts.optflag(
"l",
"", "",
"produce long format output for the specified USERs"); "produce long format output for the specified USERs",
opts.optflag("b", );
opts.optflag(
"b",
"", "",
"omit the user's home directory and shell in long format"); "omit the user's home directory and shell in long format",
);
opts.optflag("h", "", "omit the user's project file in long format"); opts.optflag("h", "", "omit the user's project file in long format");
opts.optflag("p", "", "omit the user's plan file in long format"); opts.optflag("p", "", "omit the user's plan file in long format");
opts.optflag("s", "", "do short format output, this is the default"); opts.optflag("s", "", "do short format output, this is the default");
opts.optflag("f", "", "omit the line of column headings in short format"); opts.optflag("f", "", "omit the line of column headings in short format");
opts.optflag("w", "", "omit the user's full name in short format"); opts.optflag("w", "", "omit the user's full name in short format");
opts.optflag("i", opts.optflag(
"i",
"", "",
"omit the user's full name and remote host in short format"); "omit the user's full name and remote host in short format",
opts.optflag("q", );
opts.optflag(
"q",
"", "",
"omit the user's full name, remote host and idle time in short format"); "omit the user's full name, remote host and idle time in short format",
);
opts.optflag("", "help", "display this help and exit"); opts.optflag("", "help", "display this help and exit");
opts.optflag("", "version", "output version information and exit"); opts.optflag("", "version", "output version information and exit");
@ -135,7 +145,6 @@ The utmp file will be {}", utmpx::DEFAULT_FILE);
} else { } else {
pk.long_pinky() pk.long_pinky()
} }
} }
struct Pinky { struct Pinky {
@ -157,7 +166,8 @@ impl Capitalize for str {
fn capitalize(&self) -> String { fn capitalize(&self) -> String {
#[allow(unused_imports)] #[allow(unused_imports)]
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
self.char_indices().fold(String::with_capacity(self.len()), |mut acc, x| { self.char_indices()
.fold(String::with_capacity(self.len()), |mut acc, x| {
if x.0 != 0 { if x.0 != 0 {
acc.push(x.1) acc.push(x.1)
} else { } else {
@ -228,7 +238,6 @@ impl Pinky {
} else { } else {
print!(" {:19}", " ???"); print!(" {:19}", " ???");
} }
} }
print!(" {}{:<8.*}", mesg, utmpx::UT_LINESIZE, ut.tty_device()); print!(" {}{:<8.*}", mesg, utmpx::UT_LINESIZE, ut.tty_device());

View file

@ -23,22 +23,27 @@ static VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = getopts::Options::new(); let mut opts = getopts::Options::new();
opts.optflag("0", "null", "end each output line with 0 byte rather than newline"); opts.optflag(
"0",
"null",
"end each output line with 0 byte rather than newline",
);
opts.optflag("h", "help", "display this help and exit"); opts.optflag("h", "help", "display this help and exit");
opts.optflag("V", "version", "output version information and exit"); opts.optflag("V", "version", "output version information and exit");
let matches = match opts.parse(&args[1..]) { let matches = match opts.parse(&args[1..]) {
Ok(m) => m, Ok(m) => m,
Err(f) => { Err(f) => crash!(1, "Invalid options\n{}", f),
crash!(1, "Invalid options\n{}", f)
}
}; };
if matches.opt_present("help") { if matches.opt_present("help") {
let msg = format!("{0} {1} let msg = format!(
"{0} {1}
Usage: Usage:
{0} [VARIABLE]... [OPTION]... {0} [VARIABLE]... [OPTION]...
Prints the given environment VARIABLE(s), otherwise prints them all.", NAME, VERSION); Prints the given environment VARIABLE(s), otherwise prints them all.",
NAME, VERSION
);
print!("{}", opts.usage(&msg)); print!("{}", opts.usage(&msg));
return 0; return 0;
} }

View file

@ -18,8 +18,10 @@ pub struct Memo {
} }
fn warn_excess_args(first_arg: &str) { fn warn_excess_args(first_arg: &str) {
cli::err_msg(&format!("warning: ignoring excess arguments, starting with '{}'", cli::err_msg(&format!(
first_arg)); "warning: ignoring excess arguments, starting with '{}'",
first_arg
));
} }
impl Memo { impl Memo {

View file

@ -1,5 +1,4 @@
#![crate_name = "uu_printf"] #![crate_name = "uu_printf"]
#![allow(dead_code)] #![allow(dead_code)]
extern crate itertools; extern crate itertools;
@ -9,7 +8,6 @@ mod cli;
mod memo; mod memo;
mod tokenize; mod tokenize;
static NAME: &'static str = "printf"; static NAME: &'static str = "printf";
static VERSION: &'static str = "0.0.1"; static VERSION: &'static str = "0.0.1";
static SHORT_USAGE: &'static str = "printf: usage: printf [-v var] format [arguments]"; static SHORT_USAGE: &'static str = "printf: usage: printf [-v var] format [arguments]";
@ -179,8 +177,10 @@ static LONGHELP_BODY: &'static str = "
e.g. \\n will be transformed into a newline character. e.g. \\n will be transformed into a newline character.
One special rule about %b mode is that octal literals are intepreted differently One special rule about %b mode is that octal literals are intepreted differently
In arguments passed by %b, pass octal-interpreted literals must be in the form of \\0NNN instead of \\NNN In arguments passed by %b, pass octal-interpreted literals must be in the form of \\0NNN
(Although, for legacy reasons, octal literals in the form of \\NNN will still be interpreted and not throw a warning, you will have problems if you use this for a literal whose code begins with zero, as it will be viewed as in \\0NNN form.) instead of \\NNN. (Although, for legacy reasons, octal literals in the form of \\NNN will
still be interpreted and not throw a warning, you will have problems if you use this for a
literal whose code begins with zero, as it will be viewed as in \\0NNN form.)
CHAR SUBSTITUTIONS CHAR SUBSTITUTIONS
The character field does not have a secondary parameter. The character field does not have a secondary parameter.
@ -206,12 +206,18 @@ static LONGHELP_BODY: &'static str = "
All floating point fields have a 'max decimal places / max significant digits' parameter All floating point fields have a 'max decimal places / max significant digits' parameter
%.10f means a decimal floating point with 7 decimal places past 0 %.10f means a decimal floating point with 7 decimal places past 0
%.10e means a scientific notation number with 10 significant digits %.10e means a scientific notation number with 10 significant digits
%.10g means the same behavior for decimal and Sci. Note, respectively, and provides the shorter of each's output. %.10g means the same behavior for decimal and Sci. Note, respectively, and provides the shorter
of each's output.
Like with GNU coreutils, the value after the decimal point is these outputs is parsed as a double first before being rendered to text. For both implementations do not expect meaningful precision past the 18th decimal place. When using a number of decimal places that is 18 or higher, you can expect variation in output between GNU coreutils printf and this printf at the 18th decimal place of +/- 1 Like with GNU coreutils, the value after the decimal point is these outputs is parsed as a
double first before being rendered to text. For both implementations do not expect meaningful
precision past the 18th decimal place. When using a number of decimal places that is 18 or
higher, you can expect variation in output between GNU coreutils printf and this printf at the
18th decimal place of +/- 1
%f - floating point value presented in decimal, truncated and displayed to 6 decimal places by default. %f - floating point value presented in decimal, truncated and displayed to 6 decimal places by
There is not past-double behavior parity with Coreutils printf, values are not estimated or adjusted beyond input values. default. There is not past-double behavior parity with Coreutils printf, values are not
estimated or adjusted beyond input values.
%e or %E - floating point value presented in scientific notation %e or %E - floating point value presented in scientific notation
7 significant digits by default 7 significant digits by default
@ -252,7 +258,8 @@ static LONGHELP_BODY: &'static str = "
Character Constant: if the argument begins with a single quote character, the first byte Character Constant: if the argument begins with a single quote character, the first byte
of the next character will be interpreted as an 8-bit unsigned integer. If there are of the next character will be interpreted as an 8-bit unsigned integer. If there are
additional bytes, they will throw an error (unless the environment variable POSIXLY_CORRECt is set) additional bytes, they will throw an error (unless the environment variable POSIXLY_CORRECT
is set)
WRITTEN BY : WRITTEN BY :
Nathan E. Ross, et al. for the uutils project Nathan E. Ross, et al. for the uutils project
@ -269,8 +276,10 @@ COPYRIGHT :
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let location = &args[0]; let location = &args[0];
if args.len() <= 1 { if args.len() <= 1 {
println!("{0}: missing operand\nTry '{0} --help' for more information.", println!(
location); "{0}: missing operand\nTry '{0} --help' for more information.",
location
);
return 1; return 1;
} }
let ref formatstr = args[1]; let ref formatstr = args[1];

View file

@ -2,7 +2,7 @@
//! never dealt with above (e.g. Sub Tokenizer never uses these) //! never dealt with above (e.g. Sub Tokenizer never uses these)
use std::str::Chars; use std::str::Chars;
use itertools::{PutBackN, put_back_n}; use itertools::{put_back_n, PutBackN};
use cli; use cli;
use super::format_field::FormatField; use super::format_field::FormatField;
@ -28,8 +28,7 @@ impl Default for FormatPrimitive {
} }
} }
#[derive(Clone)] #[derive(Clone, PartialEq)]
#[derive(PartialEq)]
pub enum Base { pub enum Base {
Ten = 10, Ten = 10,
Hex = 16, Hex = 16,
@ -48,11 +47,12 @@ pub trait Formatter {
// return a FormatPrimitive for // return a FormatPrimitive for
// particular field char(s), given the argument // particular field char(s), given the argument
// string and prefix information (sign, radix) // string and prefix information (sign, radix)
fn get_primitive(&self, fn get_primitive(
&self,
field: &FormatField, field: &FormatField,
inprefix: &InPrefix, inprefix: &InPrefix,
str_in: &str) str_in: &str,
-> Option<FormatPrimitive>; ) -> Option<FormatPrimitive>;
// return a string from a formatprimitive, // return a string from a formatprimitive,
// given information about the field // given information about the field
fn primitive_to_str(&self, prim: &FormatPrimitive, field: FormatField) -> String; fn primitive_to_str(&self, prim: &FormatPrimitive, field: FormatField) -> String;

View file

@ -41,12 +41,12 @@ pub struct DivOut<'a> {
pub remainder: Remainder<'a>, pub remainder: Remainder<'a>,
} }
pub fn arrnum_int_div_step<'a>(rem_in: Remainder<'a>, pub fn arrnum_int_div_step<'a>(
rem_in: Remainder<'a>,
radix_in: u8, radix_in: u8,
base_ten_int_divisor: u8, base_ten_int_divisor: u8,
after_decimal: bool) after_decimal: bool,
-> DivOut<'a> { ) -> DivOut<'a> {
let mut rem_out = Remainder { let mut rem_out = Remainder {
position: rem_in.position, position: rem_in.position,
replace: Vec::new(), replace: Vec::new(),
@ -65,8 +65,7 @@ pub fn arrnum_int_div_step<'a>(rem_in: Remainder<'a>,
loop { loop {
let u = match it_replace.next() { let u = match it_replace.next() {
Some(u_rep) => u_rep.clone() as u16, Some(u_rep) => u_rep.clone() as u16,
None => { None => match it_f.next() {
match it_f.next() {
Some(u_orig) => u_orig.clone() as u16, Some(u_orig) => u_orig.clone() as u16,
None => { None => {
if !after_decimal { if !after_decimal {
@ -74,8 +73,7 @@ pub fn arrnum_int_div_step<'a>(rem_in: Remainder<'a>,
} }
0 0
} }
} },
}
}; };
traversed += 1; traversed += 1;
bufferval += u; bufferval += u;
@ -218,7 +216,6 @@ pub fn unsigned_to_arrnum(src: u16) -> Vec<u8> {
result result
} }
// temporary needs-improvement-function // temporary needs-improvement-function
#[allow(unused_variables)] #[allow(unused_variables)]
pub fn base_conv_float(src: &Vec<u8>, radix_src: u8, radix_dest: u8) -> f64 { pub fn base_conv_float(src: &Vec<u8>, radix_src: u8, radix_dest: u8) -> f64 {
@ -272,9 +269,11 @@ pub fn arrnum_to_str(src: &Vec<u8>, radix_def_dest: &RadixDef) -> String {
#[allow(unused_variables)] #[allow(unused_variables)]
pub fn base_conv_str(src: &str, radix_def_src: &RadixDef, radix_def_dest: &RadixDef) -> String { pub fn base_conv_str(src: &str, radix_def_src: &RadixDef, radix_def_dest: &RadixDef) -> String {
let intermed_in: Vec<u8> = str_to_arrnum(src, radix_def_src); let intermed_in: Vec<u8> = str_to_arrnum(src, radix_def_src);
let intermed_out = base_conv_vec(&intermed_in, let intermed_out = base_conv_vec(
&intermed_in,
radix_def_src.get_max(), radix_def_src.get_max(),
radix_def_dest.get_max()); radix_def_dest.get_max(),
);
arrnum_to_str(&intermed_out, radix_def_dest) arrnum_to_str(&intermed_out, radix_def_dest)
} }

View file

@ -1,11 +1,10 @@
//! formatter for %a %F C99 Hex-floating-point subs //! formatter for %a %F C99 Hex-floating-point subs
use super::super::format_field::FormatField; use super::super::format_field::FormatField;
use super::super::formatter::{InPrefix, FormatPrimitive, Formatter}; use super::super::formatter::{FormatPrimitive, Formatter, InPrefix};
use super::float_common::{FloatAnalysis, primitive_to_str_common}; use super::float_common::{primitive_to_str_common, FloatAnalysis};
use super::base_conv; use super::base_conv;
use super::base_conv::RadixDef; use super::base_conv::RadixDef;
pub struct CninetyNineHexFloatf { pub struct CninetyNineHexFloatf {
as_num: f64, as_num: f64,
} }
@ -16,22 +15,22 @@ impl CninetyNineHexFloatf {
} }
impl Formatter for CninetyNineHexFloatf { impl Formatter for CninetyNineHexFloatf {
fn get_primitive(&self, fn get_primitive(
&self,
field: &FormatField, field: &FormatField,
inprefix: &InPrefix, inprefix: &InPrefix,
str_in: &str) str_in: &str,
-> Option<FormatPrimitive> { ) -> Option<FormatPrimitive> {
let second_field = field.second_field.unwrap_or(6) + 1; let second_field = field.second_field.unwrap_or(6) + 1;
let analysis = FloatAnalysis::analyze(&str_in, let analysis =
FloatAnalysis::analyze(&str_in, inprefix, Some(second_field as usize), None, true);
let f = get_primitive_hex(
inprefix, inprefix,
Some(second_field as usize),
None,
true);
let f = get_primitive_hex(inprefix,
&str_in[inprefix.offset..], &str_in[inprefix.offset..],
&analysis, &analysis,
second_field as usize, second_field as usize,
*field.field_char == 'A'); *field.field_char == 'A',
);
Some(f) Some(f)
} }
fn primitive_to_str(&self, prim: &FormatPrimitive, field: FormatField) -> String { fn primitive_to_str(&self, prim: &FormatPrimitive, field: FormatField) -> String {
@ -44,19 +43,15 @@ impl Formatter for CninetyNineHexFloatf {
// on the todo list is to have a trait for get_primitive that is implemented by each float formatter and can override a default. when that happens we can take the parts of get_primitive_dec specific to dec and spin them out to their own functions that can be overridden. // on the todo list is to have a trait for get_primitive that is implemented by each float formatter and can override a default. when that happens we can take the parts of get_primitive_dec specific to dec and spin them out to their own functions that can be overridden.
#[allow(unused_variables)] #[allow(unused_variables)]
#[allow(unused_assignments)] #[allow(unused_assignments)]
fn get_primitive_hex(inprefix: &InPrefix, fn get_primitive_hex(
inprefix: &InPrefix,
str_in: &str, str_in: &str,
analysis: &FloatAnalysis, analysis: &FloatAnalysis,
last_dec_place: usize, last_dec_place: usize,
capitalized: bool) capitalized: bool,
-> FormatPrimitive { ) -> FormatPrimitive {
let mut f: FormatPrimitive = Default::default(); let mut f: FormatPrimitive = Default::default();
f.prefix = Some(String::from(if inprefix.sign == -1 { f.prefix = Some(String::from(if inprefix.sign == -1 { "-0x" } else { "0x" }));
"-0x"
} else {
"0x"
}));
// assign the digits before and after the decimal points // assign the digits before and after the decimal points
// to separate slices. If no digits after decimal point, // to separate slices. If no digits after decimal point,
@ -101,11 +96,7 @@ fn get_primitive_hex(inprefix: &InPrefix,
// directly to base 2 and then at the end translate back to hex. // directly to base 2 and then at the end translate back to hex.
let mantissa = 0; let mantissa = 0;
f.suffix = Some({ f.suffix = Some({
let ind = if capitalized { let ind = if capitalized { "P" } else { "p" };
"P"
} else {
"p"
};
if mantissa >= 0 { if mantissa >= 0 {
format!("{}+{}", ind, mantissa) format!("{}+{}", ind, mantissa)
} else { } else {
@ -122,8 +113,10 @@ fn to_hex(src: &str, before_decimal: bool) -> String {
base_conv::base_conv_str(src, &rten, &rhex) base_conv::base_conv_str(src, &rten, &rhex)
} else { } else {
let as_arrnum_ten = base_conv::str_to_arrnum(src, &rten); let as_arrnum_ten = base_conv::str_to_arrnum(src, &rten);
let s = format!("{}", let s = format!(
base_conv::base_conv_float(&as_arrnum_ten, rten.get_max(), rhex.get_max())); "{}",
base_conv::base_conv_float(&as_arrnum_ten, rten.get_max(), rhex.get_max())
);
if s.len() > 2 { if s.len() > 2 {
String::from(&s[2..]) String::from(&s[2..])
} else { } else {

View file

@ -1,7 +1,7 @@
//! formatter for %g %G decimal subs //! formatter for %g %G decimal subs
use super::super::format_field::FormatField; use super::super::format_field::FormatField;
use super::super::formatter::{InPrefix, FormatPrimitive, Formatter}; use super::super::formatter::{FormatPrimitive, Formatter, InPrefix};
use super::float_common::{FloatAnalysis, get_primitive_dec, primitive_to_str_common}; use super::float_common::{get_primitive_dec, primitive_to_str_common, FloatAnalysis};
fn get_len_fprim(fprim: &FormatPrimitive) -> usize { fn get_len_fprim(fprim: &FormatPrimitive) -> usize {
let mut len = 0; let mut len = 0;
@ -29,24 +29,29 @@ impl Decf {
} }
} }
impl Formatter for Decf { impl Formatter for Decf {
fn get_primitive(&self, fn get_primitive(
&self,
field: &FormatField, field: &FormatField,
inprefix: &InPrefix, inprefix: &InPrefix,
str_in: &str) str_in: &str,
-> Option<FormatPrimitive> { ) -> Option<FormatPrimitive> {
let second_field = field.second_field.unwrap_or(6) + 1; let second_field = field.second_field.unwrap_or(6) + 1;
// default to scif interp. so as to not truncate input vals // default to scif interp. so as to not truncate input vals
// (that would be displayed in scif) based on relation to decimal place // (that would be displayed in scif) based on relation to decimal place
let analysis = FloatAnalysis::analyze(str_in, let analysis = FloatAnalysis::analyze(
str_in,
inprefix, inprefix,
Some(second_field as usize + 1), Some(second_field as usize + 1),
None, None,
false); false,
let mut f_sci = get_primitive_dec(inprefix, );
let mut f_sci = get_primitive_dec(
inprefix,
&str_in[inprefix.offset..], &str_in[inprefix.offset..],
&analysis, &analysis,
second_field as usize, second_field as usize,
Some(*field.field_char == 'G')); Some(*field.field_char == 'G'),
);
// strip trailing zeroes // strip trailing zeroes
match f_sci.post_decimal.clone() { match f_sci.post_decimal.clone() {
Some(ref post_dec) => { Some(ref post_dec) => {
@ -66,11 +71,13 @@ impl Formatter for Decf {
} }
None => {} None => {}
} }
let f_fl = get_primitive_dec(inprefix, let f_fl = get_primitive_dec(
inprefix,
&str_in[inprefix.offset..], &str_in[inprefix.offset..],
&analysis, &analysis,
second_field as usize, second_field as usize,
None); None,
);
Some(if get_len_fprim(&f_fl) >= get_len_fprim(&f_sci) { Some(if get_len_fprim(&f_fl) >= get_len_fprim(&f_sci) {
f_sci f_sci
} else { } else {

View file

@ -1,5 +1,5 @@
use super::super::format_field::FormatField; use super::super::format_field::FormatField;
use super::super::formatter::{InPrefix, Base, FormatPrimitive, warn_incomplete_conv, get_it_at}; use super::super::formatter::{get_it_at, warn_incomplete_conv, Base, FormatPrimitive, InPrefix};
use super::base_conv; use super::base_conv;
use super::base_conv::RadixDef; use super::base_conv::RadixDef;
@ -13,12 +13,13 @@ pub struct FloatAnalysis {
pub decimal_pos: Option<usize>, pub decimal_pos: Option<usize>,
pub follow: Option<char>, pub follow: Option<char>,
} }
fn has_enough_digits(hex_input: bool, fn has_enough_digits(
hex_input: bool,
hex_output: bool, hex_output: bool,
string_position: usize, string_position: usize,
starting_position: usize, starting_position: usize,
limit: usize) limit: usize,
-> bool { ) -> bool {
// -1s are for rounding // -1s are for rounding
if hex_output { if hex_output {
if hex_input { if hex_input {
@ -33,16 +34,16 @@ fn has_enough_digits(hex_input: bool,
((string_position - 1) - starting_position >= limit) ((string_position - 1) - starting_position >= limit)
} }
} }
} }
impl FloatAnalysis { impl FloatAnalysis {
pub fn analyze(str_in: &str, pub fn analyze(
str_in: &str,
inprefix: &InPrefix, inprefix: &InPrefix,
max_sd_opt: Option<usize>, max_sd_opt: Option<usize>,
max_after_dec_opt: Option<usize>, max_after_dec_opt: Option<usize>,
hex_output: bool) hex_output: bool,
-> FloatAnalysis { ) -> FloatAnalysis {
// this fn assumes // this fn assumes
// the input string // the input string
// has no leading spaces or 0s // has no leading spaces or 0s
@ -73,9 +74,9 @@ impl FloatAnalysis {
} }
} }
} }
if ret.decimal_pos.is_some() && if ret.decimal_pos.is_some() && pos_before_first_nonzero_after_decimal.is_none()
pos_before_first_nonzero_after_decimal.is_none() && && e != '0'
e != '0' { {
pos_before_first_nonzero_after_decimal = Some(i - 1); pos_before_first_nonzero_after_decimal = Some(i - 1);
} }
if let Some(max_sd) = max_sd_opt { if let Some(max_sd) = max_sd_opt {
@ -130,8 +131,10 @@ fn de_hex(src: &str, before_decimal: bool) -> String {
base_conv::base_conv_str(src, &rhex, &rten) base_conv::base_conv_str(src, &rhex, &rten)
} else { } else {
let as_arrnum_hex = base_conv::str_to_arrnum(src, &rhex); let as_arrnum_hex = base_conv::str_to_arrnum(src, &rhex);
let s = format!("{}", let s = format!(
base_conv::base_conv_float(&as_arrnum_hex, rhex.get_max(), rten.get_max())); "{}",
base_conv::base_conv_float(&as_arrnum_hex, rhex.get_max(), rten.get_max())
);
if s.len() > 2 { if s.len() > 2 {
String::from(&s[2..]) String::from(&s[2..])
} else { } else {
@ -147,7 +150,6 @@ fn de_hex(src: &str, before_decimal: bool) -> String {
// and if the digit was nine // and if the digit was nine
// propagate to the next, etc. // propagate to the next, etc.
fn _round_str_from(in_str: &str, position: usize) -> (String, bool) { fn _round_str_from(in_str: &str, position: usize) -> (String, bool) {
let mut it = in_str[0..position].chars(); let mut it = in_str[0..position].chars();
let mut rev = String::new(); let mut rev = String::new();
let mut i = position; let mut i = position;
@ -172,11 +174,11 @@ fn _round_str_from(in_str: &str, position: usize) -> (String, bool) {
(fwd, finished_in_dec) (fwd, finished_in_dec)
} }
fn round_terminal_digit(before_dec: String, fn round_terminal_digit(
before_dec: String,
after_dec: String, after_dec: String,
position: usize) position: usize,
-> (String, String) { ) -> (String, String) {
if position < after_dec.len() { if position < after_dec.len() {
let digit_at_pos: char; let digit_at_pos: char;
{ {
@ -202,12 +204,13 @@ fn round_terminal_digit(before_dec: String,
(before_dec, after_dec) (before_dec, after_dec)
} }
pub fn get_primitive_dec(inprefix: &InPrefix, pub fn get_primitive_dec(
inprefix: &InPrefix,
str_in: &str, str_in: &str,
analysis: &FloatAnalysis, analysis: &FloatAnalysis,
last_dec_place: usize, last_dec_place: usize,
sci_mode: Option<bool>) sci_mode: Option<bool>,
-> FormatPrimitive { ) -> FormatPrimitive {
let mut f: FormatPrimitive = Default::default(); let mut f: FormatPrimitive = Default::default();
// add negative sign section // add negative sign section
@ -227,22 +230,24 @@ pub fn get_primitive_dec(inprefix: &InPrefix,
} }
// convert to string, de_hexifying if input is in hex. // convert to string, de_hexifying if input is in hex.
let (first_segment, second_segment) = match inprefix.radix_in { let (first_segment, second_segment) = match inprefix.radix_in {
Base::Hex => { Base::Hex => (
(de_hex(first_segment_raw, true), de_hex(first_segment_raw, true),
de_hex(second_segment_raw, false)) de_hex(second_segment_raw, false),
} ),
_ => { _ => (
(String::from(first_segment_raw), String::from(first_segment_raw),
String::from(second_segment_raw)) String::from(second_segment_raw),
} ),
}; };
let (pre_dec_unrounded, post_dec_unrounded, mantissa) = if sci_mode.is_some() { let (pre_dec_unrounded, post_dec_unrounded, mantissa) = if sci_mode.is_some() {
if first_segment.len() > 1 { if first_segment.len() > 1 {
let mut post_dec = String::from(&first_segment[1..]); let mut post_dec = String::from(&first_segment[1..]);
post_dec.push_str(&second_segment); post_dec.push_str(&second_segment);
(String::from(&first_segment[0..1]), (
String::from(&first_segment[0..1]),
post_dec, post_dec,
first_segment.len() as isize - 1) first_segment.len() as isize - 1,
)
} else { } else {
match first_segment.chars().next() { match first_segment.chars().next() {
Some('0') => { Some('0') => {
@ -273,18 +278,13 @@ pub fn get_primitive_dec(inprefix: &InPrefix,
(first_segment, second_segment, 0) (first_segment, second_segment, 0)
}; };
let (pre_dec_draft, post_dec_draft) = round_terminal_digit(pre_dec_unrounded, let (pre_dec_draft, post_dec_draft) =
post_dec_unrounded, round_terminal_digit(pre_dec_unrounded, post_dec_unrounded, last_dec_place - 1);
last_dec_place - 1);
f.pre_decimal = Some(pre_dec_draft); f.pre_decimal = Some(pre_dec_draft);
f.post_decimal = Some(post_dec_draft); f.post_decimal = Some(post_dec_draft);
if let Some(capitalized) = sci_mode { if let Some(capitalized) = sci_mode {
let si_ind = if capitalized { let si_ind = if capitalized { 'E' } else { 'e' };
'E'
} else {
'e'
};
f.suffix = Some(if mantissa >= 0 { f.suffix = Some(if mantissa >= 0 {
format!("{}+{:02}", si_ind, mantissa) format!("{}+{:02}", si_ind, mantissa)
} else { } else {
@ -310,8 +310,10 @@ pub fn primitive_to_str_common(prim: &FormatPrimitive, field: &FormatField) -> S
final_str.push_str(&pre_decimal); final_str.push_str(&pre_decimal);
} }
None => { None => {
panic!("error, format primitives provided to int, will, incidentally under correct \ panic!(
behavior, always have a pre_dec value."); "error, format primitives provided to int, will, incidentally under correct \
behavior, always have a pre_dec value."
);
} }
} }
let decimal_places = field.second_field.unwrap_or(6); let decimal_places = field.second_field.unwrap_or(6);
@ -338,8 +340,10 @@ pub fn primitive_to_str_common(prim: &FormatPrimitive, field: &FormatField) -> S
} }
} }
None => { None => {
panic!("error, format primitives provided to int, will, incidentally under correct \ panic!(
behavior, always have a pre_dec value."); "error, format primitives provided to int, will, incidentally under correct \
behavior, always have a pre_dec value."
);
} }
} }
match prim.suffix { match prim.suffix {

View file

@ -1,7 +1,7 @@
//! formatter for %f %F common-notation floating-point subs //! formatter for %f %F common-notation floating-point subs
use super::super::format_field::FormatField; use super::super::format_field::FormatField;
use super::super::formatter::{InPrefix, FormatPrimitive, Formatter}; use super::super::formatter::{FormatPrimitive, Formatter, InPrefix};
use super::float_common::{FloatAnalysis, get_primitive_dec, primitive_to_str_common}; use super::float_common::{get_primitive_dec, primitive_to_str_common, FloatAnalysis};
pub struct Floatf { pub struct Floatf {
as_num: f64, as_num: f64,
@ -12,22 +12,22 @@ impl Floatf {
} }
} }
impl Formatter for Floatf { impl Formatter for Floatf {
fn get_primitive(&self, fn get_primitive(
&self,
field: &FormatField, field: &FormatField,
inprefix: &InPrefix, inprefix: &InPrefix,
str_in: &str) str_in: &str,
-> Option<FormatPrimitive> { ) -> Option<FormatPrimitive> {
let second_field = field.second_field.unwrap_or(6) + 1; let second_field = field.second_field.unwrap_or(6) + 1;
let analysis = FloatAnalysis::analyze(&str_in, let analysis =
FloatAnalysis::analyze(&str_in, inprefix, None, Some(second_field as usize), false);
let f = get_primitive_dec(
inprefix, inprefix,
None,
Some(second_field as usize),
false);
let f = get_primitive_dec(inprefix,
&str_in[inprefix.offset..], &str_in[inprefix.offset..],
&analysis, &analysis,
second_field as usize, second_field as usize,
None); None,
);
Some(f) Some(f)
} }
fn primitive_to_str(&self, prim: &FormatPrimitive, field: FormatField) -> String { fn primitive_to_str(&self, prim: &FormatPrimitive, field: FormatField) -> String {

View file

@ -4,8 +4,8 @@
use std::u64; use std::u64;
use std::i64; use std::i64;
use super::super::format_field::FormatField; use super::super::format_field::FormatField;
use super::super::formatter::{InPrefix, FormatPrimitive, Base, Formatter, warn_incomplete_conv, use super::super::formatter::{get_it_at, warn_incomplete_conv, Base, FormatPrimitive, Formatter,
get_it_at}; InPrefix};
pub struct Intf { pub struct Intf {
a: u32, a: u32,
@ -118,15 +118,13 @@ impl Intf {
fn get_max(fchar: char, sign: i8) -> FormatPrimitive { fn get_max(fchar: char, sign: i8) -> FormatPrimitive {
let mut fmt_prim: FormatPrimitive = Default::default(); let mut fmt_prim: FormatPrimitive = Default::default();
fmt_prim.pre_decimal = Some(String::from(match fchar { fmt_prim.pre_decimal = Some(String::from(match fchar {
'd' | 'i' => { 'd' | 'i' => match sign {
match sign {
1 => "9223372036854775807", 1 => "9223372036854775807",
_ => { _ => {
fmt_prim.prefix = Some(String::from("-")); fmt_prim.prefix = Some(String::from("-"));
"9223372036854775808" "9223372036854775808"
} }
} },
}
'x' | 'X' => "ffffffffffffffff", 'x' | 'X' => "ffffffffffffffff",
'o' => "1777777777777777777777", 'o' => "1777777777777777777777",
'u' | _ => "18446744073709551615", 'u' | _ => "18446744073709551615",
@ -150,8 +148,7 @@ impl Intf {
// for u64 output, the u64 max in the output radix // for u64 output, the u64 max in the output radix
fn conv_from_segment(segment: &str, radix_in: Base, fchar: char, sign: i8) -> FormatPrimitive { fn conv_from_segment(segment: &str, radix_in: Base, fchar: char, sign: i8) -> FormatPrimitive {
match fchar { match fchar {
'i' | 'd' => { 'i' | 'd' => match i64::from_str_radix(segment, radix_in as u32) {
match i64::from_str_radix(segment, radix_in as u32) {
Ok(i) => { Ok(i) => {
let mut fmt_prim: FormatPrimitive = Default::default(); let mut fmt_prim: FormatPrimitive = Default::default();
if sign == -1 { if sign == -1 {
@ -161,17 +158,11 @@ impl Intf {
fmt_prim fmt_prim
} }
Err(_) => Intf::get_max(fchar, sign), Err(_) => Intf::get_max(fchar, sign),
} },
} _ => match u64::from_str_radix(segment, radix_in as u32) {
_ => {
match u64::from_str_radix(segment, radix_in as u32) {
Ok(u) => { Ok(u) => {
let mut fmt_prim: FormatPrimitive = Default::default(); let mut fmt_prim: FormatPrimitive = Default::default();
let u_f = if sign == -1 { let u_f = if sign == -1 { u64::MAX - (u - 1) } else { u };
u64::MAX - (u - 1)
} else {
u
};
fmt_prim.pre_decimal = Some(match fchar { fmt_prim.pre_decimal = Some(match fchar {
'X' => format!("{:X}", u_f), 'X' => format!("{:X}", u_f),
'x' => format!("{:x}", u_f), 'x' => format!("{:x}", u_f),
@ -181,25 +172,26 @@ impl Intf {
fmt_prim fmt_prim
} }
Err(_) => Intf::get_max(fchar, sign), Err(_) => Intf::get_max(fchar, sign),
} },
}
} }
} }
} }
impl Formatter for Intf { impl Formatter for Intf {
fn get_primitive(&self, fn get_primitive(
&self,
field: &FormatField, field: &FormatField,
inprefix: &InPrefix, inprefix: &InPrefix,
str_in: &str) str_in: &str,
-> Option<FormatPrimitive> { ) -> Option<FormatPrimitive> {
let begin = inprefix.offset; let begin = inprefix.offset;
// get information about the string. see Intf::Analyze // get information about the string. see Intf::Analyze
// def above. // def above.
let convert_hints = Intf::analyze(str_in, let convert_hints = Intf::analyze(
str_in,
*field.field_char == 'i' || *field.field_char == 'd', *field.field_char == 'i' || *field.field_char == 'd',
inprefix); inprefix,
);
// We always will have a formatprimitive to return // We always will have a formatprimitive to return
Some(if convert_hints.len_digits == 0 || convert_hints.is_zero { Some(if convert_hints.len_digits == 0 || convert_hints.is_zero {
// if non-digit or end is reached before a non-zero digit // if non-digit or end is reached before a non-zero digit
@ -224,10 +216,12 @@ impl Formatter for Intf {
if convert_hints.check_past_max || decr_from_max || radix_mismatch { if convert_hints.check_past_max || decr_from_max || radix_mismatch {
// radix of in and out is the same. // radix of in and out is the same.
let segment = String::from(&str_in[begin..end]); let segment = String::from(&str_in[begin..end]);
let m = Intf::conv_from_segment(&segment, let m = Intf::conv_from_segment(
&segment,
inprefix.radix_in.clone(), inprefix.radix_in.clone(),
*field.field_char, *field.field_char,
inprefix.sign); inprefix.sign,
);
m m
} else { } else {
// otherwise just do a straight string copy. // otherwise just do a straight string copy.
@ -237,7 +231,6 @@ impl Formatter for Intf {
// zero doesn't get a sign, and conv_from_segment // zero doesn't get a sign, and conv_from_segment
// creates its format primitive separately // creates its format primitive separately
if inprefix.sign == -1 && *field.field_char == 'i' { if inprefix.sign == -1 && *field.field_char == 'i' {
fmt_prim.prefix = Some(String::from("-")); fmt_prim.prefix = Some(String::from("-"));
} }
fmt_prim.pre_decimal = Some(String::from(&str_in[begin..end])); fmt_prim.pre_decimal = Some(String::from(&str_in[begin..end]));
@ -246,7 +239,6 @@ impl Formatter for Intf {
} else { } else {
Intf::get_max(*field.field_char, inprefix.sign) Intf::get_max(*field.field_char, inprefix.sign)
}) })
} }
fn primitive_to_str(&self, prim: &FormatPrimitive, field: FormatField) -> String { fn primitive_to_str(&self, prim: &FormatPrimitive, field: FormatField) -> String {
let mut finalstr: String = String::new(); let mut finalstr: String = String::new();
@ -274,8 +266,10 @@ impl Formatter for Intf {
finalstr.push_str(&pre_decimal); finalstr.push_str(&pre_decimal);
} }
None => { None => {
panic!("error, format primitives provided to int, will, incidentally under \ panic!(
correct behavior, always have a pre_dec value."); "error, format primitives provided to int, will, incidentally under \
correct behavior, always have a pre_dec value."
);
} }
} }
finalstr finalstr

View file

@ -1,7 +1,7 @@
//! formatter for %e %E scientific notation subs //! formatter for %e %E scientific notation subs
use super::super::format_field::FormatField; use super::super::format_field::FormatField;
use super::super::formatter::{InPrefix, FormatPrimitive, Formatter}; use super::super::formatter::{FormatPrimitive, Formatter, InPrefix};
use super::float_common::{FloatAnalysis, get_primitive_dec, primitive_to_str_common}; use super::float_common::{get_primitive_dec, primitive_to_str_common, FloatAnalysis};
pub struct Scif { pub struct Scif {
as_num: f64, as_num: f64,
@ -12,22 +12,27 @@ impl Scif {
} }
} }
impl Formatter for Scif { impl Formatter for Scif {
fn get_primitive(&self, fn get_primitive(
&self,
field: &FormatField, field: &FormatField,
inprefix: &InPrefix, inprefix: &InPrefix,
str_in: &str) str_in: &str,
-> Option<FormatPrimitive> { ) -> Option<FormatPrimitive> {
let second_field = field.second_field.unwrap_or(6) + 1; let second_field = field.second_field.unwrap_or(6) + 1;
let analysis = FloatAnalysis::analyze(str_in, let analysis = FloatAnalysis::analyze(
str_in,
inprefix, inprefix,
Some(second_field as usize + 1), Some(second_field as usize + 1),
None, None,
false); false,
let f = get_primitive_dec(inprefix, );
let f = get_primitive_dec(
inprefix,
&str_in[inprefix.offset..], &str_in[inprefix.offset..],
&analysis, &analysis,
second_field as usize, second_field as usize,
Some(*field.field_char == 'E')); Some(*field.field_char == 'E'),
);
Some(f) Some(f)
} }
fn primitive_to_str(&self, prim: &FormatPrimitive, field: FormatField) -> String { fn primitive_to_str(&self, prim: &FormatPrimitive, field: FormatField) -> String {

View file

@ -3,8 +3,8 @@
use std::env; use std::env;
use std::vec::Vec; use std::vec::Vec;
use cli; use cli;
use super::format_field::{FormatField, FieldType}; use super::format_field::{FieldType, FormatField};
use super::formatter::{Formatter, FormatPrimitive, InPrefix, Base}; use super::formatter::{Base, FormatPrimitive, Formatter, InPrefix};
use super::formatters::intf::Intf; use super::formatters::intf::Intf;
use super::formatters::floatf::Floatf; use super::formatters::floatf::Floatf;
use super::formatters::cninetyninehexfloatf::CninetyNineHexFloatf; use super::formatters::cninetyninehexfloatf::CninetyNineHexFloatf;
@ -21,16 +21,16 @@ pub fn warn_expected_numeric(pf_arg: &String) {
fn warn_char_constant_ign(remaining_bytes: Vec<u8>) { fn warn_char_constant_ign(remaining_bytes: Vec<u8>) {
match env::var("POSIXLY_CORRECT") { match env::var("POSIXLY_CORRECT") {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => match e {
match e {
env::VarError::NotPresent => { env::VarError::NotPresent => {
cli::err_msg(&format!("warning: {:?}: character(s) following character \ cli::err_msg(&format!(
"warning: {:?}: character(s) following character \
constant have been ignored", constant have been ignored",
&*remaining_bytes)); &*remaining_bytes
));
} }
_ => {} _ => {}
} },
}
} }
} }
@ -68,8 +68,7 @@ fn get_provided(str_in_opt: Option<&String>) -> Option<u8> {
// first byte is not quote // first byte is not quote
_ => { _ => {
return None; return None;
} } // no first byte
// no first byte
} }
} else { } else {
Some(0 as u8) Some(0 as u8)
@ -193,7 +192,6 @@ fn get_inprefix(str_in: &String, field_type: &FieldType) -> InPrefix {
if first { if first {
first = false; first = false;
} }
} }
} }
} }
@ -205,8 +203,6 @@ fn get_inprefix(str_in: &String, field_type: &FieldType) -> InPrefix {
// if it is a numeric field, passing the field details // if it is a numeric field, passing the field details
// and an iterator to the argument // and an iterator to the argument
pub fn num_format(field: &FormatField, in_str_opt: Option<&String>) -> Option<String> { pub fn num_format(field: &FormatField, in_str_opt: Option<&String>) -> Option<String> {
let fchar = field.field_char.clone(); let fchar = field.field_char.clone();
// num format mainly operates by further delegating to one of // num format mainly operates by further delegating to one of

View file

@ -8,10 +8,10 @@ use std::iter::Peekable;
use std::str::Chars; use std::str::Chars;
use std::process::exit; use std::process::exit;
use cli; use cli;
use itertools::{PutBackN, put_back_n}; use itertools::{put_back_n, PutBackN};
use super::token; use super::token;
use super::unescaped_text::UnescapedText; use super::unescaped_text::UnescapedText;
use super::num_format::format_field::{FormatField, FieldType}; use super::num_format::format_field::{FieldType, FormatField};
use super::num_format::num_format; use super::num_format::num_format;
// use std::collections::HashSet; // use std::collections::HashSet;
@ -57,11 +57,12 @@ pub struct Sub {
orig: String, orig: String,
} }
impl Sub { impl Sub {
pub fn new(min_width: CanAsterisk<Option<isize>>, pub fn new(
min_width: CanAsterisk<Option<isize>>,
second_field: CanAsterisk<Option<u32>>, second_field: CanAsterisk<Option<u32>>,
field_char: char, field_char: char,
orig: String) orig: String,
-> Sub { ) -> Sub {
// for more dry printing, field characters are grouped // for more dry printing, field characters are grouped
// in initialization of token. // in initialization of token.
let field_type = match field_char { let field_type = match field_char {
@ -112,9 +113,10 @@ impl SubParser {
text_so_far: String::new(), text_so_far: String::new(),
} }
} }
fn from_it(it: &mut PutBackN<Chars>, fn from_it(
args: &mut Peekable<Iter<String>>) it: &mut PutBackN<Chars>,
-> Option<Box<token::Token>> { args: &mut Peekable<Iter<String>>,
) -> Option<Box<token::Token>> {
let mut parser = SubParser::new(); let mut parser = SubParser::new();
if parser.sub_vals_retrieved(it) { if parser.sub_vals_retrieved(it) {
let t: Box<token::Token> = SubParser::build_token(parser); let t: Box<token::Token> = SubParser::build_token(parser);
@ -127,7 +129,8 @@ impl SubParser {
fn build_token(parser: SubParser) -> Box<token::Token> { fn build_token(parser: SubParser) -> Box<token::Token> {
// not a self method so as to allow move of subparser vals. // not a self method so as to allow move of subparser vals.
// return new Sub struct as token // return new Sub struct as token
let t: Box<token::Token> = Box::new(Sub::new(if parser.min_width_is_asterisk { let t: Box<token::Token> = Box::new(Sub::new(
if parser.min_width_is_asterisk {
CanAsterisk::Asterisk CanAsterisk::Asterisk
} else { } else {
CanAsterisk::Fixed(parser.min_width_tmp.map(|x| x.parse::<isize>().unwrap())) CanAsterisk::Fixed(parser.min_width_tmp.map(|x| x.parse::<isize>().unwrap()))
@ -138,11 +141,11 @@ impl SubParser {
CanAsterisk::Fixed(parser.second_field_tmp.map(|x| x.parse::<u32>().unwrap())) CanAsterisk::Fixed(parser.second_field_tmp.map(|x| x.parse::<u32>().unwrap()))
}, },
parser.field_char.unwrap(), parser.field_char.unwrap(),
parser.text_so_far)); parser.text_so_far,
));
t t
} }
fn sub_vals_retrieved(&mut self, it: &mut PutBackN<Chars>) -> bool { fn sub_vals_retrieved(&mut self, it: &mut PutBackN<Chars>) -> bool {
if !SubParser::successfully_eat_prefix(it, &mut self.text_so_far) { if !SubParser::successfully_eat_prefix(it, &mut self.text_so_far) {
return false; return false;
} }
@ -298,23 +301,23 @@ impl SubParser {
// check for illegal combinations here when possible vs // check for illegal combinations here when possible vs
// on each application so we check less per application // on each application so we check less per application
// to do: move these checks to Sub::new // to do: move these checks to Sub::new
if (field_char == 's' && self.min_width_tmp == Some(String::from("0"))) || if (field_char == 's' && self.min_width_tmp == Some(String::from("0")))
(field_char == 'c' && || (field_char == 'c'
(self.min_width_tmp == Some(String::from("0")) || self.past_decimal)) || && (self.min_width_tmp == Some(String::from("0")) || self.past_decimal))
(field_char == 'b' && || (field_char == 'b'
(self.min_width_tmp.is_some() || self.past_decimal || && (self.min_width_tmp.is_some() || self.past_decimal
self.second_field_tmp.is_some())) { || self.second_field_tmp.is_some()))
{
err_conv(&self.text_so_far); err_conv(&self.text_so_far);
} }
} }
} }
impl token::Tokenizer for Sub { impl token::Tokenizer for Sub {
fn from_it(it: &mut PutBackN<Chars>, fn from_it(
args: &mut Peekable<Iter<String>>) it: &mut PutBackN<Chars>,
-> Option<Box<token::Token>> { args: &mut Peekable<Iter<String>>,
) -> Option<Box<token::Token>> {
SubParser::from_it(it, args) SubParser::from_it(it, args)
} }
} }
@ -364,12 +367,10 @@ impl token::Token for Sub {
match pf_arg { match pf_arg {
Some(arg_string) => { Some(arg_string) => {
match *field.field_char { match *field.field_char {
's' => { 's' => Some(match field.second_field {
Some(match field.second_field {
Some(max) => String::from(&arg_string[..max as usize]), Some(max) => String::from(&arg_string[..max as usize]),
None => arg_string.clone(), None => arg_string.clone(),
}) }),
}
'b' => { 'b' => {
let mut a_it = put_back_n(arg_string.chars()); let mut a_it = put_back_n(arg_string.chars());
UnescapedText::from_it_core(&mut a_it, true); UnescapedText::from_it_core(&mut a_it, true);
@ -392,11 +393,12 @@ impl token::Token for Sub {
match pre_min_width_opt { match pre_min_width_opt {
// if have a string, print it, ensuring minimum width is met. // if have a string, print it, ensuring minimum width is met.
Some(pre_min_width) => { Some(pre_min_width) => {
print!("{}", print!(
"{}",
match field.min_width { match field.min_width {
Some(min_width) => { Some(min_width) => {
let diff: isize = min_width.abs() as isize - let diff: isize =
pre_min_width.len() as isize; min_width.abs() as isize - pre_min_width.len() as isize;
if diff > 0 { if diff > 0 {
let mut final_str = String::new(); let mut final_str = String::new();
// definitely more efficient ways // definitely more efficient ways
@ -417,7 +419,8 @@ impl token::Token for Sub {
} }
} }
None => pre_min_width, None => pre_min_width,
}); }
);
} }
None => {} None => {}
} }

View file

@ -65,12 +65,12 @@ impl UnescapedText {
preface = 'U'; preface = 'U';
leading_zeros = 8; leading_zeros = 8;
} }
let err_msg = format!("invalid universal character name {0}{1:02$x}", let err_msg = format!(
preface, "invalid universal character name {0}{1:02$x}",
val, preface, val, leading_zeros
leading_zeros); );
if (val < 159 && (val != 36 && val != 64 && val != 96)) || (val > 55296 && val < 57343) { if (val < 159 && (val != 36 && val != 64 && val != 96)) || (val > 55296 && val < 57343) {
println!("{}", err_msg);//todo stderr println!("{}", err_msg); //todo stderr
exit(cli::EXIT_ERR); exit(cli::EXIT_ERR);
} }
} }
@ -167,7 +167,6 @@ impl UnescapedText {
byte_vec.extend(s.bytes()); byte_vec.extend(s.bytes());
} }
}; };
} }
// take an iterator to a string, // take an iterator to a string,
@ -239,9 +238,10 @@ impl UnescapedText {
} }
#[allow(unused_variables)] #[allow(unused_variables)]
impl token::Tokenizer for UnescapedText { impl token::Tokenizer for UnescapedText {
fn from_it(it: &mut PutBackN<Chars>, fn from_it(
args: &mut Peekable<Iter<String>>) it: &mut PutBackN<Chars>,
-> Option<Box<token::Token>> { args: &mut Peekable<Iter<String>>,
) -> Option<Box<token::Token>> {
UnescapedText::from_it_core(it, false) UnescapedText::from_it_core(it, false)
} }
} }

View file

@ -12,19 +12,19 @@
extern crate aho_corasick; extern crate aho_corasick;
extern crate getopts; extern crate getopts;
extern crate memchr; extern crate memchr;
extern crate regex_syntax;
extern crate regex; extern crate regex;
extern crate regex_syntax;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use getopts::{Options, Matches}; use getopts::{Matches, Options};
use regex::Regex; use regex::Regex;
use std::cmp; use std::cmp;
use std::collections::{HashMap, HashSet, BTreeSet}; use std::collections::{BTreeSet, HashMap, HashSet};
use std::default::Default; use std::default::Default;
use std::fs::File; use std::fs::File;
use std::io::{stdin, stdout, BufReader, BufWriter, BufRead, Read, Write}; use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write};
static NAME: &'static str = "ptx"; static NAME: &'static str = "ptx";
static VERSION: &'static str = env!("CARGO_PKG_VERSION"); static VERSION: &'static str = env!("CARGO_PKG_VERSION");
@ -38,33 +38,33 @@ enum OutFormat {
#[derive(Debug)] #[derive(Debug)]
struct Config { struct Config {
format : OutFormat, format: OutFormat,
gnu_ext : bool, gnu_ext: bool,
auto_ref : bool, auto_ref: bool,
input_ref : bool, input_ref: bool,
right_ref : bool, right_ref: bool,
ignore_case : bool, ignore_case: bool,
macro_name : String, macro_name: String,
trunc_str : String, trunc_str: String,
context_regex : String, context_regex: String,
line_width : usize, line_width: usize,
gap_size : usize, gap_size: usize,
} }
impl Default for Config { impl Default for Config {
fn default() -> Config { fn default() -> Config {
Config { Config {
format : OutFormat::Dumb, format: OutFormat::Dumb,
gnu_ext : true, gnu_ext: true,
auto_ref : false, auto_ref: false,
input_ref : false, input_ref: false,
right_ref : false, right_ref: false,
ignore_case : false, ignore_case: false,
macro_name : "xx".to_owned(), macro_name: "xx".to_owned(),
trunc_str : "/".to_owned(), trunc_str: "/".to_owned(),
context_regex : "\\w+".to_owned(), context_regex: "\\w+".to_owned(),
line_width : 72, line_width: 72,
gap_size : 3 gap_size: 3,
} }
} }
} }
@ -90,14 +90,12 @@ struct WordFilter {
impl WordFilter { impl WordFilter {
fn new(matches: &Matches, config: &Config) -> WordFilter { fn new(matches: &Matches, config: &Config) -> WordFilter {
let (o, oset): (bool, HashSet<String>) = let (o, oset): (bool, HashSet<String>) = if matches.opt_present("o") {
if matches.opt_present("o") {
(true, read_word_filter_file(matches, "o")) (true, read_word_filter_file(matches, "o"))
} else { } else {
(false, HashSet::new()) (false, HashSet::new())
}; };
let (i, iset): (bool, HashSet<String>) = let (i, iset): (bool, HashSet<String>) = if matches.opt_present("i") {
if matches.opt_present("i") {
(true, read_word_filter_file(matches, "i")) (true, read_word_filter_file(matches, "i"))
} else { } else {
(false, HashSet::new()) (false, HashSet::new())
@ -105,8 +103,7 @@ impl WordFilter {
if matches.opt_present("b") { if matches.opt_present("b") {
crash!(1, "-b not implemented yet"); crash!(1, "-b not implemented yet");
} }
let reg = let reg = if matches.opt_present("W") {
if matches.opt_present("W") {
matches.opt_str("W").expect("parsing options failed!") matches.opt_str("W").expect("parsing options failed!")
} else if config.gnu_ext { } else if config.gnu_ext {
"\\w+".to_owned() "\\w+".to_owned()
@ -118,7 +115,7 @@ impl WordFilter {
ignore_specified: i, ignore_specified: i,
only_set: oset, only_set: oset,
ignore_set: iset, ignore_set: iset,
word_regex: reg word_regex: reg,
} }
} }
} }
@ -165,22 +162,18 @@ fn get_config(matches: &Matches) -> Config {
config.right_ref &= matches.opt_present("R"); config.right_ref &= matches.opt_present("R");
config.ignore_case = matches.opt_present("f"); config.ignore_case = matches.opt_present("f");
if matches.opt_present("M") { if matches.opt_present("M") {
config.macro_name = config.macro_name = matches.opt_str("M").expect(err_msg);
matches.opt_str("M").expect(err_msg);
} }
if matches.opt_present("F") { if matches.opt_present("F") {
config.trunc_str = config.trunc_str = matches.opt_str("F").expect(err_msg);
matches.opt_str("F").expect(err_msg);
} }
if matches.opt_present("w") { if matches.opt_present("w") {
let width_str = matches.opt_str("w").expect(err_msg); let width_str = matches.opt_str("w").expect(err_msg);
config.line_width = crash_if_err!( config.line_width = crash_if_err!(1, usize::from_str_radix(&width_str, 10));
1, usize::from_str_radix(&width_str, 10));
} }
if matches.opt_present("g") { if matches.opt_present("g") {
let gap_str = matches.opt_str("g").expect(err_msg); let gap_str = matches.opt_str("g").expect(err_msg);
config.gap_size = crash_if_err!( config.gap_size = crash_if_err!(1, usize::from_str_radix(&gap_str, 10));
1, usize::from_str_radix(&gap_str, 10));
} }
if matches.opt_present("O") { if matches.opt_present("O") {
config.format = OutFormat::Roff; config.format = OutFormat::Roff;
@ -191,10 +184,8 @@ fn get_config(matches: &Matches) -> Config {
config config
} }
fn read_input(input_files: &[String], config: &Config) -> fn read_input(input_files: &[String], config: &Config) -> HashMap<String, (Vec<String>, usize)> {
HashMap<String, (Vec<String>, usize)> { let mut file_map: HashMap<String, (Vec<String>, usize)> = HashMap::new();
let mut file_map : HashMap<String, (Vec<String>, usize)> =
HashMap::new();
let mut files = Vec::new(); let mut files = Vec::new();
if input_files.is_empty() { if input_files.is_empty() {
files.push("-"); files.push("-");
@ -209,15 +200,13 @@ fn read_input(input_files: &[String], config: &Config) ->
} }
let mut lines_so_far: usize = 0; let mut lines_so_far: usize = 0;
for filename in files { for filename in files {
let reader: BufReader<Box<Read>> = BufReader::new( let reader: BufReader<Box<Read>> = BufReader::new(if filename == "-" {
if filename == "-" {
Box::new(stdin()) Box::new(stdin())
} else { } else {
let file = crash_if_err!(1, File::open(filename)); let file = crash_if_err!(1, File::open(filename));
Box::new(file) Box::new(file)
}); });
let lines: Vec<String> = reader.lines().map(|x| crash_if_err!(1, x)) let lines: Vec<String> = reader.lines().map(|x| crash_if_err!(1, x)).collect();
.collect();
let size = lines.len(); let size = lines.len();
file_map.insert(filename.to_owned(), (lines, lines_so_far)); file_map.insert(filename.to_owned(), (lines, lines_so_far));
lines_so_far += size lines_so_far += size
@ -225,9 +214,11 @@ fn read_input(input_files: &[String], config: &Config) ->
file_map file_map
} }
fn create_word_set(config: &Config, filter: &WordFilter, fn create_word_set(
file_map: &HashMap<String, (Vec<String>, usize)>)-> config: &Config,
BTreeSet<WordRef> { filter: &WordFilter,
file_map: &HashMap<String, (Vec<String>, usize)>,
) -> BTreeSet<WordRef> {
let reg = Regex::new(&filter.word_regex).unwrap(); let reg = Regex::new(&filter.word_regex).unwrap();
let ref_reg = Regex::new(&config.context_regex).unwrap(); let ref_reg = Regex::new(&config.context_regex).unwrap();
let mut word_set: BTreeSet<WordRef> = BTreeSet::new(); let mut word_set: BTreeSet<WordRef> = BTreeSet::new();
@ -238,7 +229,7 @@ fn create_word_set(config: &Config, filter: &WordFilter,
// if -r, exclude reference from word set // if -r, exclude reference from word set
let (ref_beg, ref_end) = match ref_reg.find(line) { let (ref_beg, ref_end) = match ref_reg.find(line) {
Some(x) => (x.start(), x.end()), Some(x) => (x.start(), x.end()),
None => (0, 0) None => (0, 0),
}; };
// match words with given regex // match words with given regex
for mat in reg.find_iter(line) { for mat in reg.find_iter(line) {
@ -246,25 +237,23 @@ fn create_word_set(config: &Config, filter: &WordFilter,
if config.input_ref && ((beg, end) == (ref_beg, ref_end)) { if config.input_ref && ((beg, end) == (ref_beg, ref_end)) {
continue; continue;
} }
let mut word = line[beg .. end].to_owned(); let mut word = line[beg..end].to_owned();
if filter.only_specified && if filter.only_specified && !(filter.only_set.contains(&word)) {
!(filter.only_set.contains(&word)) {
continue; continue;
} }
if filter.ignore_specified && if filter.ignore_specified && filter.ignore_set.contains(&word) {
filter.ignore_set.contains(&word) {
continue; continue;
} }
if config.ignore_case { if config.ignore_case {
word = word.to_lowercase(); word = word.to_lowercase();
} }
word_set.insert(WordRef{ word_set.insert(WordRef {
word: word, word: word,
filename: String::from(file.clone()), filename: String::from(file.clone()),
global_line_nr: offs + count, global_line_nr: offs + count,
local_line_nr: count, local_line_nr: count,
position: beg, position: beg,
position_end: end position_end: end,
}); });
} }
count += 1; count += 1;
@ -273,17 +262,16 @@ fn create_word_set(config: &Config, filter: &WordFilter,
word_set word_set
} }
fn get_reference(config: &Config, word_ref: &WordRef, line: &str) -> fn get_reference(config: &Config, word_ref: &WordRef, line: &str) -> String {
String {
if config.auto_ref { if config.auto_ref {
format!("{}:{}", word_ref.filename, word_ref.local_line_nr + 1) format!("{}:{}", word_ref.filename, word_ref.local_line_nr + 1)
} else if config.input_ref { } else if config.input_ref {
let reg = Regex::new(&config.context_regex).unwrap(); let reg = Regex::new(&config.context_regex).unwrap();
let (beg, end) = match reg.find(line) { let (beg, end) = match reg.find(line) {
Some(x) => (x.start(), x.end()), Some(x) => (x.start(), x.end()),
None => (0, 0) None => (0, 0),
}; };
format!("{}", &line[beg .. end]) format!("{}", &line[beg..end])
} else { } else {
String::new() String::new()
} }
@ -296,8 +284,7 @@ fn assert_str_integrity(s: &[char], beg: usize, end: usize) {
fn trim_broken_word_left(s: &[char], beg: usize, end: usize) -> usize { fn trim_broken_word_left(s: &[char], beg: usize, end: usize) -> usize {
assert_str_integrity(s, beg, end); assert_str_integrity(s, beg, end);
if beg == end || beg == 0 || s[beg].is_whitespace() || if beg == end || beg == 0 || s[beg].is_whitespace() || s[beg - 1].is_whitespace() {
s[beg-1].is_whitespace() {
return beg; return beg;
} }
let mut b = beg; let mut b = beg;
@ -309,12 +296,11 @@ fn trim_broken_word_left(s: &[char], beg: usize, end: usize) -> usize {
fn trim_broken_word_right(s: &[char], beg: usize, end: usize) -> usize { fn trim_broken_word_right(s: &[char], beg: usize, end: usize) -> usize {
assert_str_integrity(s, beg, end); assert_str_integrity(s, beg, end);
if beg == end || end == s.len() || s[end-1].is_whitespace() || if beg == end || end == s.len() || s[end - 1].is_whitespace() || s[end].is_whitespace() {
s[end].is_whitespace() {
return end; return end;
} }
let mut e = end; let mut e = end;
while beg < e && !s[e-1].is_whitespace() { while beg < e && !s[e - 1].is_whitespace() {
e -= 1; e -= 1;
} }
e e
@ -327,14 +313,18 @@ fn trim_idx(s: &[char], beg: usize, end: usize) -> (usize, usize) {
while b < e && s[b].is_whitespace() { while b < e && s[b].is_whitespace() {
b += 1; b += 1;
} }
while b < e && s[e-1].is_whitespace() { while b < e && s[e - 1].is_whitespace() {
e -= 1; e -= 1;
} }
(b,e) (b, e)
} }
fn get_output_chunks(all_before: &str, keyword: &str, all_after: &str, fn get_output_chunks(
config: &Config) -> (String, String, String, String) { all_before: &str,
keyword: &str,
all_after: &str,
config: &Config,
) -> (String, String, String, String) {
assert_eq!(all_before.trim(), all_before); assert_eq!(all_before.trim(), all_before);
assert_eq!(keyword.trim(), keyword); assert_eq!(keyword.trim(), keyword);
assert_eq!(all_after.trim(), all_after); assert_eq!(all_after.trim(), all_after);
@ -343,28 +333,27 @@ fn get_output_chunks(all_before: &str, keyword: &str, all_after: &str,
let mut after = String::new(); let mut after = String::new();
let mut tail = String::new(); let mut tail = String::new();
let half_line_size = cmp::max((config.line_width/2) as isize - let half_line_size = cmp::max(
(2*config.trunc_str.len()) as isize, 0) as usize; (config.line_width / 2) as isize - (2 * config.trunc_str.len()) as isize,
let max_after_size = cmp::max(half_line_size as isize - 0,
keyword.len() as isize - 1, 0) as usize; ) as usize;
let max_after_size = cmp::max(half_line_size as isize - keyword.len() as isize - 1, 0) as usize;
let max_before_size = half_line_size; let max_before_size = half_line_size;
let all_before_vec: Vec<char> = all_before.chars().collect(); let all_before_vec: Vec<char> = all_before.chars().collect();
let all_after_vec: Vec<char> = all_after.chars().collect(); let all_after_vec: Vec<char> = all_after.chars().collect();
// get before // get before
let mut bb_tmp = let mut bb_tmp = cmp::max(all_before.len() as isize - max_before_size as isize, 0) as usize;
cmp::max(all_before.len() as isize - max_before_size as isize, 0) as usize;
bb_tmp = trim_broken_word_left(&all_before_vec, bb_tmp, all_before.len()); bb_tmp = trim_broken_word_left(&all_before_vec, bb_tmp, all_before.len());
let (before_beg, before_end) = let (before_beg, before_end) = trim_idx(&all_before_vec, bb_tmp, all_before.len());
trim_idx(&all_before_vec, bb_tmp, all_before.len()); before.push_str(&all_before[before_beg..before_end]);
before.push_str(&all_before[before_beg .. before_end]);
assert!(max_before_size >= before.len()); assert!(max_before_size >= before.len());
// get after // get after
let mut ae_tmp = cmp::min(max_after_size, all_after.len()); let mut ae_tmp = cmp::min(max_after_size, all_after.len());
ae_tmp = trim_broken_word_right(&all_after_vec, 0, ae_tmp); ae_tmp = trim_broken_word_right(&all_after_vec, 0, ae_tmp);
let (after_beg, after_end) = trim_idx(&all_after_vec, 0, ae_tmp); let (after_beg, after_end) = trim_idx(&all_after_vec, 0, ae_tmp);
after.push_str(&all_after[after_beg .. after_end]); after.push_str(&all_after[after_beg..after_end]);
assert!(max_after_size >= after.len()); assert!(max_after_size >= after.len());
// get tail // get tail
@ -373,16 +362,15 @@ fn get_output_chunks(all_before: &str, keyword: &str, all_after: &str,
let mut te_tmp = cmp::min(tb + max_tail_size, all_after.len()); let mut te_tmp = cmp::min(tb + max_tail_size, all_after.len());
te_tmp = trim_broken_word_right(&all_after_vec, tb, te_tmp); te_tmp = trim_broken_word_right(&all_after_vec, tb, te_tmp);
let (tail_beg, tail_end) = trim_idx(&all_after_vec, tb, te_tmp); let (tail_beg, tail_end) = trim_idx(&all_after_vec, tb, te_tmp);
tail.push_str(&all_after[tail_beg .. tail_end]); tail.push_str(&all_after[tail_beg..tail_end]);
// get head // get head
let max_head_size = max_after_size - after.len(); let max_head_size = max_after_size - after.len();
let (_, he) = trim_idx(&all_before_vec, 0, before_beg); let (_, he) = trim_idx(&all_before_vec, 0, before_beg);
let mut hb_tmp = let mut hb_tmp = cmp::max(he as isize - max_head_size as isize, 0) as usize;
cmp::max(he as isize - max_head_size as isize, 0) as usize;
hb_tmp = trim_broken_word_left(&all_before_vec, hb_tmp, he); hb_tmp = trim_broken_word_left(&all_before_vec, hb_tmp, he);
let (head_beg, head_end) = trim_idx(&all_before_vec, hb_tmp, he); let (head_beg, head_end) = trim_idx(&all_before_vec, hb_tmp, he);
head.push_str(&all_before[head_beg .. head_end]); head.push_str(&all_before[head_beg..head_end]);
// put right context truncation string if needed // put right context truncation string if needed
if after_end != all_after.len() && tail_beg == tail_end { if after_end != all_after.len() && tail_beg == tail_end {
@ -411,7 +399,7 @@ fn tex_mapper(x: char) -> String {
'\\' => "\\backslash{}".to_owned(), '\\' => "\\backslash{}".to_owned(),
'$' | '%' | '#' | '&' | '_' => format!("\\{}", x), '$' | '%' | '#' | '&' | '_' => format!("\\{}", x),
'}' | '{' => format!("$\\{}$", x), '}' | '{' => format!("$\\{}$", x),
_ => x.to_string() _ => x.to_string(),
} }
} }
@ -423,84 +411,85 @@ fn adjust_tex_str(context: &str) -> String {
fix fix
} }
fn format_tex_line(config: &Config, word_ref: &WordRef, line: &str, fn format_tex_line(config: &Config, word_ref: &WordRef, line: &str, reference: &str) -> String {
reference: &str) -> String {
let mut output = String::new(); let mut output = String::new();
output.push_str(&format!("\\{} ", config.macro_name)); output.push_str(&format!("\\{} ", config.macro_name));
let all_before = if config.input_ref { let all_before = if config.input_ref {
let before = &line[0 .. word_ref.position]; let before = &line[0..word_ref.position];
adjust_tex_str(before.trim().trim_left_matches(reference)) adjust_tex_str(before.trim().trim_left_matches(reference))
} else { } else {
adjust_tex_str(&line[0 .. word_ref.position]) adjust_tex_str(&line[0..word_ref.position])
}; };
let keyword = adjust_tex_str( let keyword = adjust_tex_str(&line[word_ref.position..word_ref.position_end]);
&line[word_ref.position .. word_ref.position_end]); let all_after = adjust_tex_str(&line[word_ref.position_end..line.len()]);
let all_after = adjust_tex_str( let (tail, before, after, head) = get_output_chunks(&all_before, &keyword, &all_after, &config);
&line[word_ref.position_end .. line.len()]); output.push_str(&format!(
let (tail, before, after, head) = "{5}{0}{6}{5}{1}{6}{5}{2}{6}{5}{3}{6}{5}{4}{6}",
get_output_chunks(&all_before, &keyword, &all_after, &config); tail, before, keyword, after, head, "{", "}"
output.push_str(&format!("{5}{0}{6}{5}{1}{6}{5}{2}{6}{5}{3}{6}{5}{4}{6}", ));
tail, before, keyword, after, head, "{", "}"));
if config.auto_ref || config.input_ref { if config.auto_ref || config.input_ref {
output.push_str( output.push_str(&format!("{}{}{}", "{", adjust_tex_str(&reference), "}"));
&format!("{}{}{}", "{", adjust_tex_str(&reference), "}"));
} }
output output
} }
fn adjust_roff_str(context: &str) -> String { fn adjust_roff_str(context: &str) -> String {
let ws_reg = Regex::new(r"[\t\n\v\f\r]").unwrap(); let ws_reg = Regex::new(r"[\t\n\v\f\r]").unwrap();
ws_reg.replace_all(context, " ").replace("\"", "\"\"").trim().to_owned() ws_reg
.replace_all(context, " ")
.replace("\"", "\"\"")
.trim()
.to_owned()
} }
fn format_roff_line(config: &Config, word_ref: &WordRef, line: &str, fn format_roff_line(config: &Config, word_ref: &WordRef, line: &str, reference: &str) -> String {
reference: &str) -> String {
let mut output = String::new(); let mut output = String::new();
output.push_str(&format!(".{}", config.macro_name)); output.push_str(&format!(".{}", config.macro_name));
let all_before = if config.input_ref { let all_before = if config.input_ref {
let before = &line[0 .. word_ref.position]; let before = &line[0..word_ref.position];
adjust_roff_str(before.trim().trim_left_matches(reference)) adjust_roff_str(before.trim().trim_left_matches(reference))
} else { } else {
adjust_roff_str(&line[0 .. word_ref.position]) adjust_roff_str(&line[0..word_ref.position])
}; };
let keyword = adjust_roff_str( let keyword = adjust_roff_str(&line[word_ref.position..word_ref.position_end]);
&line[word_ref.position .. word_ref.position_end]); let all_after = adjust_roff_str(&line[word_ref.position_end..line.len()]);
let all_after = adjust_roff_str( let (tail, before, after, head) = get_output_chunks(&all_before, &keyword, &all_after, &config);
&line[word_ref.position_end .. line.len()]); output.push_str(&format!(
let (tail, before, after, head) = " \"{}\" \"{}\" \"{}{}\" \"{}\"",
get_output_chunks(&all_before, &keyword, &all_after, &config); tail, before, keyword, after, head
output.push_str(&format!(" \"{}\" \"{}\" \"{}{}\" \"{}\"", ));
tail, before, keyword, after, head));
if config.auto_ref || config.input_ref { if config.auto_ref || config.input_ref {
output.push_str(&format!(" \"{}\"", adjust_roff_str(&reference))); output.push_str(&format!(" \"{}\"", adjust_roff_str(&reference)));
} }
output output
} }
fn write_traditional_output(config: &Config, fn write_traditional_output(
file_map: &HashMap<String, (Vec<String>,usize)>, config: &Config,
words: &BTreeSet<WordRef>, output_filename: &str) { file_map: &HashMap<String, (Vec<String>, usize)>,
let mut writer: BufWriter<Box<Write>> = BufWriter::new( words: &BTreeSet<WordRef>,
if output_filename == "-" { output_filename: &str,
) {
let mut writer: BufWriter<Box<Write>> = BufWriter::new(if output_filename == "-" {
Box::new(stdout()) Box::new(stdout())
} else { } else {
let file = crash_if_err!(1, File::create(output_filename)); let file = crash_if_err!(1, File::create(output_filename));
Box::new(file) Box::new(file)
}); });
for word_ref in words.iter() { for word_ref in words.iter() {
let file_map_value : &(Vec<String>, usize) = let file_map_value: &(Vec<String>, usize) = file_map
file_map.get(&(word_ref.filename)) .get(&(word_ref.filename))
.expect("Missing file in file map"); .expect("Missing file in file map");
let (ref lines, _) = *(file_map_value); let (ref lines, _) = *(file_map_value);
let reference = let reference = get_reference(config, word_ref, &lines[word_ref.local_line_nr]);
get_reference(config, word_ref, &lines[word_ref.local_line_nr]);
let output_line: String = match config.format { let output_line: String = match config.format {
OutFormat::Tex => format_tex_line( OutFormat::Tex => {
config, word_ref, &lines[word_ref.local_line_nr], &reference), format_tex_line(config, word_ref, &lines[word_ref.local_line_nr], &reference)
OutFormat::Roff => format_roff_line( }
config, word_ref, &lines[word_ref.local_line_nr], &reference), OutFormat::Roff => {
OutFormat::Dumb => crash!( format_roff_line(config, word_ref, &lines[word_ref.local_line_nr], &reference)
1, "There is no dumb format with GNU extensions disabled") }
OutFormat::Dumb => crash!(1, "There is no dumb format with GNU extensions disabled"),
}; };
crash_if_err!(1, writeln!(writer, "{}", output_line)); crash_if_err!(1, writeln!(writer, "{}", output_line));
} }
@ -508,33 +497,79 @@ fn write_traditional_output(config: &Config,
pub fn uumain(args: Vec<String>) -> i32 { pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = Options::new(); let mut opts = Options::new();
opts.optflag("A", "auto-reference", opts.optflag(
"output automatically generated references"); "A",
"auto-reference",
"output automatically generated references",
);
opts.optflag("G", "traditional", "behave more like System V 'ptx'"); opts.optflag("G", "traditional", "behave more like System V 'ptx'");
opts.optopt("F", "flag-truncation", opts.optopt(
"use STRING for flagging line truncations", "STRING"); "F",
opts.optopt("M", "macro-name", "macro name to use instead of 'xx'", "flag-truncation",
"STRING"); "use STRING for flagging line truncations",
"STRING",
);
opts.optopt(
"M",
"macro-name",
"macro name to use instead of 'xx'",
"STRING",
);
opts.optflag("O", "format=roff", "generate output as roff directives"); opts.optflag("O", "format=roff", "generate output as roff directives");
opts.optflag("R", "right-side-refs", opts.optflag(
"put references at right, not counted in -w"); "R",
opts.optopt("S", "sentence-regexp", "for end of lines or end of sentences", "right-side-refs",
"REGEXP"); "put references at right, not counted in -w",
);
opts.optopt(
"S",
"sentence-regexp",
"for end of lines or end of sentences",
"REGEXP",
);
opts.optflag("T", "format=tex", "generate output as TeX directives"); opts.optflag("T", "format=tex", "generate output as TeX directives");
opts.optopt("W", "word-regexp", "use REGEXP to match each keyword", opts.optopt(
"REGEXP"); "W",
opts.optopt("b", "break-file", "word break characters in this FILE", "word-regexp",
"FILE"); "use REGEXP to match each keyword",
opts.optflag("f", "ignore-case", "REGEXP",
"fold lower case to upper case for sorting"); );
opts.optopt("g", "gap-size", "gap size in columns between output fields", opts.optopt(
"NUMBER"); "b",
opts.optopt("i", "ignore-file", "read ignore word list from FILE", "FILE"); "break-file",
opts.optopt("o", "only-file", "read only word list from this FILE", "word break characters in this FILE",
"FILE"); "FILE",
);
opts.optflag(
"f",
"ignore-case",
"fold lower case to upper case for sorting",
);
opts.optopt(
"g",
"gap-size",
"gap size in columns between output fields",
"NUMBER",
);
opts.optopt(
"i",
"ignore-file",
"read ignore word list from FILE",
"FILE",
);
opts.optopt(
"o",
"only-file",
"read only word list from this FILE",
"FILE",
);
opts.optflag("r", "references", "first field of each line is a reference"); opts.optflag("r", "references", "first field of each line is a reference");
opts.optopt("w", "width", "output width in columns, reference excluded", opts.optopt(
"NUMBER"); "w",
"width",
"output width in columns, reference excluded",
"NUMBER",
);
opts.optflag("", "help", "display this help and exit"); opts.optflag("", "help", "display this help and exit");
opts.optflag("", "version", "output version information and exit"); opts.optflag("", "version", "output version information and exit");
@ -550,8 +585,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
} }
let config = get_config(&matches); let config = get_config(&matches);
let word_filter = WordFilter::new(&matches, &config); let word_filter = WordFilter::new(&matches, &config);
let file_map = let file_map = read_input(&matches.free, &config);
read_input(&matches.free, &config);
let word_set = create_word_set(&config, &word_filter, &file_map); let word_set = create_word_set(&config, &word_filter, &file_map);
let output_file = if !config.gnu_ext && matches.free.len() == 2 { let output_file = if !config.gnu_ext && matches.free.len() == 2 {
matches.free[1].clone() matches.free[1].clone()

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