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"
version = "0.0.1"
dependencies = [
"clippy 0.0.143 (registry+https://github.com/rust-lang/crates.io-index)",
"uucore 0.0.1",
]
@ -109,6 +110,16 @@ name = "byteorder"
version = "1.2.1"
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]]
name = "cat"
version = "0.0.1"
@ -149,6 +160,7 @@ dependencies = [
name = "chown"
version = "0.0.1"
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)",
"uucore 0.0.1",
"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)",
]
[[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]]
name = "cmake"
version = "0.1.29"
@ -345,6 +383,11 @@ dependencies = [
"uucore 0.0.1",
]
[[package]]
name = "dtoa"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "du"
version = "0.0.1"
@ -564,6 +607,7 @@ version = "0.0.1"
dependencies = [
"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)",
"time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"uucore 0.0.1",
]
@ -580,6 +624,11 @@ dependencies = [
"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]]
name = "join"
version = "0.0.1"
@ -673,6 +722,11 @@ dependencies = [
"uucore 0.0.1",
]
[[package]]
name = "matches"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "md5"
version = "0.3.7"
@ -970,6 +1024,14 @@ dependencies = [
"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]]
name = "ptx"
version = "0.0.1"
@ -996,11 +1058,24 @@ name = "quick-error"
version = "1.2.1"
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]]
name = "quote"
version = "0.3.15"
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]]
name = "rand"
version = "0.3.22"
@ -1137,6 +1212,14 @@ dependencies = [
"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]]
name = "semver"
version = "0.7.0"
@ -1158,6 +1241,42 @@ dependencies = [
"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]]
name = "sha1"
version = "0.2.0"
@ -1275,6 +1394,16 @@ dependencies = [
"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]]
name = "sync"
version = "0.0.1"
@ -1410,6 +1539,14 @@ dependencies = [
"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]]
name = "touch"
version = "0.0.1"
@ -1490,6 +1627,11 @@ dependencies = [
"uucore 0.0.1",
]
[[package]]
name = "unicode-normalization"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-width"
version = "0.1.4"
@ -1500,6 +1642,11 @@ name = "unicode-xid"
version = "0.0.4"
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]]
name = "unindent"
version = "0.1.2"
@ -1726,6 +1873,7 @@ dependencies = [
name = "who"
version = "0.0.1"
dependencies = [
"clippy 0.0.143 (registry+https://github.com/rust-lang/crates.io-index)",
"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 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 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 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 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 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"
@ -1811,6 +1962,7 @@ dependencies = [
"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 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 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"
@ -1826,11 +1978,13 @@ dependencies = [
"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 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 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 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 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 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"
@ -1848,8 +2002,11 @@ dependencies = [
"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 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 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.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.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"
@ -1861,13 +2018,19 @@ dependencies = [
"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 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-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 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 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 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 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"
@ -1876,10 +2039,13 @@ dependencies = [
"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 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 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-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 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"

View file

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

View file

@ -16,8 +16,9 @@ pub fn main() {
if val == "1" && key.starts_with(feature_prefix) {
let krate = key[feature_prefix.len()..].to_lowercase();
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());
}
@ -27,18 +28,23 @@ pub fn main() {
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();
mf.write_all("
mf.write_all(
"
type UtilityMap = HashMap<&'static str, fn(Vec<String>) -> i32>;
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 {
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() {
"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(\"sha1sum\", 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-512sum\", uu_hashsum::uumain);
map.insert(\"shake128sum\", uu_hashsum::uumain);
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(),
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(),
}
}

View file

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

View file

@ -10,14 +10,15 @@
#[macro_use]
extern crate uucore;
use uucore::encoding::{Data, Format, wrap_print};
use uucore::encoding::{wrap_print, Data, Format};
use std::fs::File;
use std::io::{BufReader, Read, stdin};
use std::io::{stdin, BufReader, Read};
use std::path::Path;
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 = "
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 {
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optflag("d", "decode", "decode data")
.optflag("i",
"ignore-garbage",
"when decoding, ignore non-alphabetic characters")
.optopt("w",
"wrap",
"wrap encoded lines after COLS character (default 76, 0 to disable wrapping)",
"COLS")
.optflag(
"i",
"ignore-garbage",
"when decoding, ignore non-alphabetic characters",
)
.optopt(
"w",
"wrap",
"wrap encoded lines after COLS character (default 76, 0 to disable wrapping)",
"COLS",
)
.parse(args);
let line_wrap = match matches.opt_str("wrap") {
Some(s) => {
match s.parse() {
Ok(n) => n,
Err(e) => {
crash!(1, "invalid wrap size: {}: {}", s, e);
}
Some(s) => match s.parse() {
Ok(n) => n,
Err(e) => {
crash!(1, "invalid wrap size: {}: {}", s, e);
}
}
},
None => 76,
};

View file

@ -9,17 +9,17 @@
// that was distributed with this source code.
//
#[macro_use]
extern crate uucore;
use uucore::encoding::{Data, Format, wrap_print};
use uucore::encoding::{wrap_print, Data, Format};
use std::fs::File;
use std::io::{BufReader, Read, stdin};
use std::io::{stdin, BufReader, Read};
use std::path::Path;
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 = "
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 {
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optflag("d", "decode", "decode data")
.optflag("i",
"ignore-garbage",
"when decoding, ignore non-alphabetic characters")
.optopt("w",
"wrap",
"wrap encoded lines after COLS character (default 76, 0 to disable wrapping)",
"COLS")
.optflag(
"i",
"ignore-garbage",
"when decoding, ignore non-alphabetic characters",
)
.optopt(
"w",
"wrap",
"wrap encoded lines after COLS character (default 76, 0 to disable wrapping)",
"COLS",
)
.parse(args);
let line_wrap = match matches.opt_str("wrap") {
Some(s) => {
match s.parse() {
Ok(n) => n,
Err(e) => {
crash!(1, "invalid wrap size: {}: {}", s, e);
}
Some(s) => match s.parse() {
Ok(n) => n,
Err(e) => {
crash!(1, "invalid wrap size: {}: {}", s, e);
}
}
},
None => 76,
};

View file

@ -25,9 +25,22 @@ pub fn uumain(args: Vec<String>) -> i32 {
// Argument parsing
//
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optflag("a", "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.")
.optflag(
"a",
"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);
// too few arguments
@ -81,7 +94,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
fn basename(fullname: &str, suffix: &str) -> String {
// 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
path = path.chars().rev().collect();
@ -90,7 +107,7 @@ fn basename(fullname: &str, suffix: &str) -> String {
let pb = PathBuf::from(path);
match pb.components().last() {
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
use quick_error::ResultExt;
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;
/// Unix domain socket support
#[cfg(unix)] use std::net::Shutdown;
#[cfg(unix)] use std::os::unix::fs::FileTypeExt;
#[cfg(unix)] use unix_socket::UnixStream;
#[cfg(unix)]
use std::net::Shutdown;
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
#[cfg(unix)]
use unix_socket::UnixStream;
static SYNTAX: &'static str = "[OPTION]... [FILE]...";
static SUMMARY: &'static str = "Concatenate FILE(s), or standard input, to standard output
With no FILE, or when FILE is -, read standard input.";
static LONG_HELP: &'static str = "";
#[derive(PartialEq)]
enum NumberingMode {
NumberNone,
@ -41,7 +43,6 @@ enum NumberingMode {
NumberAll,
}
quick_error! {
#[derive(Debug)]
enum CatError {
@ -75,7 +76,6 @@ quick_error! {
}
}
struct OutputOptions {
/// Line numbering mode
number: NumberingMode,
@ -98,14 +98,12 @@ struct OutputOptions {
show_nonprint: bool,
}
/// Represents an open file handle, stream, or other device
struct InputHandle {
reader: Box<Read>,
is_interactive: bool,
}
/// Concrete enum of recognized file types.
///
/// *Note*: `cat`-ing a directory should result in an
@ -115,31 +113,37 @@ enum InputType {
File,
StdIn,
SymLink,
#[cfg(unix)] BlockDevice,
#[cfg(unix)] CharacterDevice,
#[cfg(unix)] Fifo,
#[cfg(unix)] Socket,
}
#[cfg(unix)]
BlockDevice,
#[cfg(unix)]
CharacterDevice,
#[cfg(unix)]
Fifo,
#[cfg(unix)]
Socket,
}
type CatResult<T> = Result<T, CatError>;
pub fn uumain(args: Vec<String>) -> i32 {
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optflag("A", "show-all", "equivalent to -vET")
.optflag("b",
"number-nonblank",
"number nonempty output lines, overrides -n")
.optflag(
"b",
"number-nonblank",
"number nonempty output lines, overrides -n",
)
.optflag("e", "", "equivalent to -vE")
.optflag("E", "show-ends", "display $ at end of each line")
.optflag("n", "number", "number all output lines")
.optflag("s", "squeeze-blank", "suppress repeated empty output lines")
.optflag("t", "", "equivalent to -vT")
.optflag("T", "show-tabs", "display TAB characters as ^I")
.optflag("v",
"show-nonprinting",
"use ^ and M- notation, except for LF (\\n) and TAB (\\t)")
.optflag(
"v",
"show-nonprinting",
"use ^ and M- notation, except for LF (\\n) and TAB (\\t)",
)
.parse(args);
let number_mode = if matches.opt_present("b") {
@ -150,8 +154,12 @@ pub fn uumain(args: Vec<String>) -> i32 {
NumberingMode::NumberNone
};
let show_nonprint =
matches.opts_present(&["A".to_owned(), "e".to_owned(), "t".to_owned(), "v".to_owned()]);
let show_nonprint = matches.opts_present(&[
"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_tabs = matches.opts_present(&["A".to_owned(), "T".to_owned(), "t".to_owned()]);
let squeeze_blank = matches.opt_present("s");
@ -160,15 +168,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
files.push("-".to_owned());
}
let can_write_fast = !(show_tabs
|| show_nonprint
|| show_ends
|| squeeze_blank
|| number_mode != NumberingMode::NumberNone);
let can_write_fast = !(show_tabs || show_nonprint || show_ends || squeeze_blank
|| number_mode != NumberingMode::NumberNone);
let success = if can_write_fast {
write_fast(files).is_ok()
} else {
let tab = match show_tabs {
true => "^I",
@ -198,7 +202,6 @@ pub fn uumain(args: Vec<String>) -> i32 {
}
}
/// Classifies the `InputType` of file at `path` if possible
///
/// # Arguments
@ -206,18 +209,34 @@ pub fn uumain(args: Vec<String>) -> i32 {
/// * `path` - Path on a file system to classify metadata
fn get_input_type(path: &str) -> CatResult<InputType> {
if path == "-" {
return Ok(InputType::StdIn)
return Ok(InputType::StdIn);
}
match metadata(path).context(path)?.file_type() {
#[cfg(unix)] ft if ft.is_block_device() => 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_file() => Ok(InputType::File),
ft if ft.is_symlink() => Ok(InputType::SymLink),
_ => Err(CatError::UnknownFiletype(path.to_owned()))
#[cfg(unix)]
ft if ft.is_block_device() =>
{
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_file() => Ok(InputType::File),
ft if ft.is_symlink() => Ok(InputType::SymLink),
_ => Err(CatError::UnknownFiletype(path.to_owned())),
}
}
@ -238,21 +257,22 @@ fn open(path: &str) -> CatResult<InputHandle> {
match get_input_type(path)? {
InputType::Directory => Err(CatError::IsDirectory(path.to_owned())),
#[cfg(unix)] InputType::Socket => {
#[cfg(unix)]
InputType::Socket => {
let socket = UnixStream::connect(path).context(path)?;
socket.shutdown(Shutdown::Write).context(path)?;
Ok(InputHandle {
reader: Box::new(socket) as Box<Read>,
is_interactive: false,
})
},
}
_ => {
let file = File::open(path).context(path)?;
Ok(InputHandle {
reader: Box::new(file) as Box<Read>,
is_interactive: false,
})
},
}
}
}
@ -271,18 +291,16 @@ fn write_fast(files: Vec<String>) -> CatResult<()> {
for file in files {
match open(&file[..]) {
Ok(mut handle) => {
while let Ok(n) = handle.reader.read(&mut in_buf) {
if n == 0 {
break;
}
writer.write_all(&in_buf[..n]).context(&file[..])?;
Ok(mut handle) => while let Ok(n) = handle.reader.read(&mut in_buf) {
if n == 0 {
break;
}
writer.write_all(&in_buf[..n]).context(&file[..])?;
},
Err(error) => {
writeln!(&mut stderr(), "{}", error)?;
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,
/// propagating any errors that might occur.
fn write_file_lines(file: &str,
options: &OutputOptions,
state: &mut OutputState) -> CatResult<()> {
fn write_file_lines(file: &str, options: &OutputOptions, state: &mut OutputState) -> CatResult<()> {
let mut handle = open(file)?;
let mut in_buf = [0; 1024 * 31];
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 {
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) => {
writer.write_all(&in_buf[..p]).unwrap();
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();
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::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.";
const FTS_COMFOLLOW: u8 = 1;
@ -334,10 +335,12 @@ impl Chgrper {
_ => {
show_info!("changing group of '{}': {}", path.display(), e);
if self.verbosity == Verbose {
println!("failed to change group of {} from {} to {}",
path.display(),
entries::gid2grp(meta.gid()).unwrap(),
entries::gid2grp(dest_gid).unwrap());
println!(
"failed to change group of {} from {} to {}",
path.display(),
entries::gid2grp(meta.gid()).unwrap(),
entries::gid2grp(dest_gid).unwrap()
);
};
}
}
@ -347,17 +350,21 @@ impl Chgrper {
if changed {
match self.verbosity {
Changes | Verbose => {
println!("changed group of {} from {} to {}",
path.display(),
entries::gid2grp(meta.gid()).unwrap(),
entries::gid2grp(dest_gid).unwrap());
println!(
"changed group of {} from {} to {}",
path.display(),
entries::gid2grp(meta.gid()).unwrap(),
entries::gid2grp(dest_gid).unwrap()
);
}
_ => (),
};
} else if self.verbosity == Verbose {
println!("group of {} retained as {}",
path.display(),
entries::gid2grp(dest_gid).unwrap());
println!(
"group of {} retained as {}",
path.display(),
entries::gid2grp(dest_gid).unwrap()
);
}
}
ret

View file

@ -31,12 +31,17 @@ static LONG_HELP: &'static str = "
";
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]... --reference=RFILE FILE...", NAME);
{0} [OPTION]... --reference=RFILE FILE...",
NAME
);
let mut opts = new_coreopts!(&syntax, SUMMARY, LONG_HELP);
opts.optflag("c", "changes", "like verbose but report only when a change is made (unimplemented)")
.optflag("f", "quiet", "suppress most error messages (unimplemented)") // TODO: support --silent
opts.optflag("c", "changes", "like verbose but report only when a change is made \
(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("", "no-preserve-root", "do not treat '/' specially (the default)")
.optflag("", "preserve-root", "fail to operate recursively on '/'")
@ -58,25 +63,24 @@ pub fn uumain(mut args: Vec<String>) -> i32 {
let verbose = matches.opt_present("verbose");
let preserve_root = matches.opt_present("preserve-root");
let recursive = matches.opt_present("recursive");
let fmode = matches.opt_str("reference").and_then(|ref fref| {
match fs::metadata(fref) {
let fmode = matches
.opt_str("reference")
.and_then(|ref fref| match fs::metadata(fref) {
Ok(meta) => Some(meta.mode()),
Err(err) => crash!(1, "cannot stat attribues of '{}': {}", fref, err)
}
});
let cmode =
if fmode.is_none() {
// If there was a negative option, now it's a good time to
// use it.
if negative_option.is_some() {
negative_option
} else {
Some(matches.free.remove(0))
}
Err(err) => crash!(1, "cannot stat attribues of '{}': {}", fref, err),
});
let cmode = if fmode.is_none() {
// If there was a negative option, now it's a good time to
// use it.
if negative_option.is_some() {
negative_option
} else {
None
};
let chmoder = Chmoder{
Some(matches.free.remove(0))
}
} else {
None
};
let chmoder = Chmoder {
changes: changes,
quiet: quiet,
verbose: verbose,
@ -87,7 +91,7 @@ pub fn uumain(mut args: Vec<String>) -> i32 {
};
match chmoder.chmod(matches.free) {
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) {
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));
},
}
_ => {}
}
}
@ -123,7 +127,6 @@ struct Chmoder {
}
impl Chmoder {
fn chmod(&self, files: Vec<String>) -> Result<(), i32> {
let mut r = Ok(());
@ -145,18 +148,21 @@ impl Chmoder {
// 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
// to chmod() and chmod_file().
r = self.chmod(walk_dir.filter_map(|x| match x {
Ok(o) => match o.path().into_os_string().to_str() {
Some(s) => Some(s.to_owned()),
None => None,
},
Err(_) => None,
}).collect()).and(r);
r = self.chmod(
walk_dir
.filter_map(|x| match x {
Ok(o) => match o.path().into_os_string().to_str() {
Some(s) => Some(s.to_owned()),
None => None,
},
Err(_) => None,
})
.collect(),
).and(r);
r = self.chmod_file(&file, filename).and(r);
}
} else {
show_error!("could not change permissions of directory '{}'",
filename);
show_error!("could not change permissions of directory '{}'", filename);
r = Err(1);
}
} else {
@ -193,14 +199,14 @@ impl Chmoder {
Some(mode) => try!(self.change_file(fperm, mode, file, name)),
None => {
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 result =
if mode.contains(arr) {
mode::parse_numeric(fperm, mode)
} else {
mode::parse_symbolic(fperm, mode, file.is_dir())
};
let result = if mode.contains(arr) {
mode::parse_numeric(fperm, mode)
} else {
mode::parse_symbolic(fperm, mode, file.is_dir())
};
match result {
Ok(mode) => {
try!(self.change_file(fperm, mode, file, name));
@ -226,19 +232,31 @@ impl Chmoder {
show_info!("mode of '{}' retained as {:o}", file.display(), fperm);
}
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 {
show_error!("{}", err);
}
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)
} else {
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(())
}
}
}
}

View file

@ -1,5 +1,4 @@
#![crate_name = "uu_chown"]
// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
@ -7,14 +6,13 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#[macro_use]
extern crate uucore;
use uucore::libc::{self, uid_t, gid_t, lchown};
pub use uucore::entries::{self, Locate, Passwd, Group};
use uucore::libc::{self, gid_t, lchown, uid_t};
pub use uucore::entries::{self, Group, Locate, Passwd};
use uucore::fs::resolve_relative_path;
extern crate walkdir;
@ -32,7 +30,8 @@ use std::convert::AsRef;
use std::ffi::CString;
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";
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();
if usr_only {
Ok((Some(match Passwd::locate(args[0]) {
Ok(v) => v.uid(),
_ => return Err(format!("invalid user: {}", spec)),
}),
None))
Ok((
Some(match Passwd::locate(args[0]) {
Ok(v) => v.uid(),
_ => return Err(format!("invalid user: {}", spec)),
}),
None,
))
} else if grp_only {
Ok((None,
Ok((
None,
Some(match Group::locate(args[1]) {
Ok(v) => v.gid(),
_ => return Err(format!("invalid group: {}", spec)),
})))
Ok(v) => v.gid(),
_ => return Err(format!("invalid group: {}", spec)),
}),
))
} else if usr_grp {
Ok((Some(match Passwd::locate(args[0]) {
Ok(v) => v.uid(),
_ => return Err(format!("invalid user: {}", spec)),
}),
Ok((
Some(match Passwd::locate(args[0]) {
Ok(v) => v.uid(),
_ => return Err(format!("invalid user: {}", spec)),
}),
Some(match Group::locate(args[1]) {
Ok(v) => v.gid(),
_ => return Err(format!("invalid group: {}", spec)),
})))
Ok(v) => v.gid(),
_ => return Err(format!("invalid group: {}", spec)),
}),
))
} else {
Ok((None, None))
}
@ -263,7 +268,13 @@ impl Chowner {
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 s = CString::new(path.as_os_str().as_bytes()).unwrap();
let ret = unsafe {
@ -391,12 +402,14 @@ impl Chowner {
_ => {
show_info!("changing ownership of '{}': {}", path.display(), e);
if self.verbosity == Verbose {
println!("failed to change ownership of {} from {}:{} to {}:{}",
path.display(),
entries::uid2usr(meta.uid()).unwrap(),
entries::gid2grp(meta.gid()).unwrap(),
entries::uid2usr(dest_uid).unwrap(),
entries::gid2grp(dest_gid).unwrap());
println!(
"failed to change ownership of {} from {}:{} to {}:{}",
path.display(),
entries::uid2usr(meta.uid()).unwrap(),
entries::gid2grp(meta.gid()).unwrap(),
entries::uid2usr(dest_uid).unwrap(),
entries::gid2grp(dest_gid).unwrap()
);
};
}
}
@ -406,20 +419,24 @@ impl Chowner {
if changed {
match self.verbosity {
Changes | Verbose => {
println!("changed ownership of {} from {}:{} to {}:{}",
path.display(),
entries::uid2usr(meta.uid()).unwrap(),
entries::gid2grp(meta.gid()).unwrap(),
entries::uid2usr(dest_uid).unwrap(),
entries::gid2grp(dest_gid).unwrap());
println!(
"changed ownership of {} from {}:{} to {}:{}",
path.display(),
entries::uid2usr(meta.uid()).unwrap(),
entries::gid2grp(meta.gid()).unwrap(),
entries::uid2usr(dest_uid).unwrap(),
entries::gid2grp(dest_gid).unwrap()
);
}
_ => (),
};
} else if self.verbosity == Verbose {
println!("ownership of {} retained as {}:{}",
path.display(),
entries::uid2usr(dest_uid).unwrap(),
entries::gid2grp(dest_gid).unwrap());
println!(
"ownership of {} retained as {}:{}",
path.display(),
entries::uid2usr(dest_uid).unwrap(),
entries::gid2grp(dest_gid).unwrap()
);
}
}
ret

View file

@ -14,7 +14,7 @@ extern crate getopts;
#[macro_use]
extern crate uucore;
use uucore::libc::{self, setgid, setuid, chroot, setgroups};
use uucore::libc::{self, chroot, setgid, setgroups, setuid};
use uucore::entries;
use std::ffi::CString;
@ -33,18 +33,33 @@ static LONG_HELP: &'static str = "
pub fn uumain(args: Vec<String>) -> i32 {
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", "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. \
Userspec has higher preference than -u and/or -g", "USER:GROUP")
.optopt(
"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. \
Userspec has higher preference than -u and/or -g",
"USER:GROUP",
)
.parse(args);
if matches.free.is_empty() {
println!("Missing operand: NEWROOT");
println!("Try `{} --help` for more information.", NAME);
return 1
return 1;
}
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][..]);
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() {
@ -62,9 +81,9 @@ pub fn uumain(args: Vec<String>) -> i32 {
Err(_) => default_shell,
Ok(ref s) => s.as_ref(),
};
vec!(shell, default_option)
},
_ => matches.free[1..].iter().map(|x| &x[..]).collect()
vec![shell, default_option]
}
_ => matches.free[1..].iter().map(|x| &x[..]).collect(),
};
set_context(&newroot, &matches);
@ -97,10 +116,18 @@ fn set_context(root: &Path, options: &getopts::Matches) {
};
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);
@ -113,10 +140,18 @@ fn enter_chroot(root: &Path) {
let root_str = root.display();
std::env::set_current_dir(root).unwrap();
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 {
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) };
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"))]
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
unsafe {
setgroups(groups.len() as libc::c_int,
groups.as_ptr())
}
unsafe { setgroups(groups.len() as libc::c_int, groups.as_ptr()) }
}
#[cfg(target_os = "linux")]
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
unsafe {
setgroups(groups.len() as libc::size_t,
groups.as_ptr())
}
unsafe { setgroups(groups.len() as libc::size_t, groups.as_ptr()) }
}
fn set_groups_from_str(groups: &str) {
if !groups.is_empty() {
let groups_vec: Vec<libc::gid_t> = FromIterator::from_iter(
groups.split(',').map(
|x| match entries::grp2gid(x) {
Ok(g) => g,
_ => crash!(1, "no such group: {}", x),
})
);
let groups_vec: Vec<libc::gid_t> =
FromIterator::from_iter(groups.split(',').map(|x| match entries::grp2gid(x) {
Ok(g) => g,
_ => crash!(1, "no such group: {}", x),
}));
let err = set_groups(groups_vec);
if err != 0 {
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);
}
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]

View file

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

View file

@ -16,12 +16,12 @@ extern crate uucore;
use std::cmp::Ordering;
use std::fs::File;
use std::io::{self, BufRead, BufReader, stdin, Stdin};
use std::io::{self, stdin, BufRead, BufReader, Stdin};
use std::path::Path;
static SYNTAX: &'static str = "[OPTIONS] FILE1 FILE2";
static SUMMARY: &'static str = "Compare sorted files line by line";
static LONG_HELP: &'static str = "";
static SYNTAX: &'static str = "[OPTIONS] FILE1 FILE2";
static SUMMARY: &'static str = "Compare sorted files line by line";
static LONG_HELP: &'static str = "";
fn mkdelim(col: usize, opts: &getopts::Matches) -> String {
let mut s = String::new();
@ -43,27 +43,26 @@ fn mkdelim(col: usize, opts: &getopts::Matches) -> String {
fn ensure_nl(line: &mut String) {
match line.chars().last() {
Some('\n') => (),
_ => line.push_str("\n")
_ => line.push_str("\n"),
}
}
enum LineReader {
Stdin(Stdin),
FileIn(BufReader<File>)
FileIn(BufReader<File>),
}
impl LineReader {
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
match *self {
LineReader::Stdin(ref mut r) => r.read_line(buf),
LineReader::Stdin(ref mut r) => r.read_line(buf),
LineReader::FileIn(ref mut r) => r.read_line(buf),
}
}
}
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 mut na = a.read_line(ra);
@ -72,13 +71,13 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &getopts::Matches) {
while na.is_ok() || nb.is_ok() {
let ord = match (na.is_ok(), nb.is_ok()) {
(false, true) => Ordering::Greater,
(true , false) => Ordering::Less,
(true , true) => match(&na, &nb) {
(false, true) => Ordering::Greater,
(true, false) => Ordering::Less,
(true, true) => match (&na, &nb) {
(&Ok(0), &Ok(0)) => break,
(&Ok(0), _) => Ordering::Greater,
(_, &Ok(0)) => Ordering::Less,
_ => ra.cmp(&rb),
_ => ra.cmp(&rb),
},
_ => unreachable!(),
};
@ -91,7 +90,7 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &getopts::Matches) {
}
ra.clear();
na = a.read_line(ra);
},
}
Ordering::Greater => {
if !opts.opt_present("2") {
ensure_nl(rb);
@ -99,7 +98,7 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &getopts::Matches) {
}
rb.clear();
nb = b.read_line(rb);
},
}
Ordering::Equal => {
if !opts.opt_present("3") {
ensure_nl(ra);
@ -117,7 +116,7 @@ fn comm(a: &mut LineReader, b: &mut LineReader, opts: &getopts::Matches) {
fn open_file(name: &str) -> io::Result<LineReader> {
match name {
"-" => Ok(LineReader::Stdin(stdin())),
_ => {
_ => {
let f = try!(File::open(&Path::new(name)));
Ok(LineReader::FileIn(BufReader::new(f)))
}
@ -128,7 +127,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optflag("1", "", "suppress column 1 (lines uniq to FILE1)")
.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")
.parse(args);

View file

@ -10,14 +10,17 @@
* that was distributed with this source code.
*/
extern crate libc;
extern crate clap;
extern crate walkdir;
extern crate filetime;
#[cfg(target_os = "linux")]
#[macro_use] extern crate ioctl_sys;
#[macro_use] extern crate uucore;
#[macro_use] extern crate quick_error;
#[macro_use]
extern crate ioctl_sys;
extern crate libc;
#[macro_use]
extern crate quick_error;
#[macro_use]
extern crate uucore;
extern crate walkdir;
#[cfg(unix)]
extern crate xattr;
@ -35,24 +38,28 @@ extern crate winapi;
use std::mem;
use std::ffi::CString;
use clap::{Arg, App, ArgMatches};
use clap::{App, Arg, ArgMatches};
use quick_error::ResultExt;
use std::collections::HashSet;
use std::fs;
use std::io::{BufReader, BufRead, stdin, stdout, Write};
use std::io::{stdin, stdout, BufRead, BufReader, Write};
use std::io;
use std::path::{Path, PathBuf, StripPrefixError};
use std::str::FromStr;
use uucore::fs::{canonicalize, CanonicalizeMode};
use walkdir::WalkDir;
#[cfg(target_os = "linux")] use std::os::unix::io::IntoRawFd;
#[cfg(target_os = "linux")] use std::fs::File;
#[cfg(target_os = "linux")]
use std::os::unix::io::IntoRawFd;
#[cfg(target_os = "linux")]
use std::fs::File;
use std::fs::OpenOptions;
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! {
#[derive(Debug)]
@ -97,7 +104,6 @@ quick_error! {
}
}
/// Continue next iteration of loop if result of expression is error
macro_rules! or_continue(
($expr:expr) => (match $expr {
@ -109,7 +115,6 @@ macro_rules! or_continue(
})
);
/// Prompts the user yes/no and returns `true` they if successfully
/// answered yes.
macro_rules! prompt_yes(
@ -133,7 +138,7 @@ pub type Source = PathBuf;
pub type Target = PathBuf;
/// Specifies whether when overwrite files
#[derive (Clone, Eq, PartialEq)]
#[derive(Clone, Eq, PartialEq)]
pub enum ClobberMode {
Force,
RemoveDestination,
@ -141,7 +146,7 @@ pub enum ClobberMode {
}
/// Specifies whether when overwrite files
#[derive (Clone, Eq, PartialEq)]
#[derive(Clone, Eq, PartialEq)]
pub enum OverwriteMode {
/// [Default] Always overwrite existing files
Clobber(ClobberMode),
@ -151,9 +156,11 @@ pub enum OverwriteMode {
NoClobber,
}
#[derive (Clone, Eq, PartialEq)]
#[derive(Clone, Eq, PartialEq)]
pub enum ReflinkMode {
Always, Auto, Never
Always,
Auto,
Never,
}
/// Specifies the expected file type of copy target
@ -176,12 +183,13 @@ pub enum CopyMode {
Sparse,
Copy,
Update,
AttrOnly
AttrOnly,
}
#[derive(Clone, Eq, PartialEq)]
pub enum Attribute {
#[cfg(unix)] Mode,
#[cfg(unix)]
Mode,
Ownership,
Timestamps,
Context,
@ -222,59 +230,76 @@ fn print_version() {
}
fn get_usage() -> String {
format!("{0} [OPTION]... [-T] SOURCE DEST
format!(
"{0} [OPTION]... [-T] SOURCE DEST
{0} [OPTION]... SOURCE... DIRECTORY
{0} [OPTION]... -t DIRECTORY SOURCE...", executable!())
{0} [OPTION]... -t DIRECTORY SOURCE...",
executable!()
)
}
// Argument constants
static OPT_ARCHIVE: &str = "archive";
static OPT_ATTRIBUTES_ONLY: &str = "attributes-only";
static OPT_BACKUP: &str = "backup";
static OPT_CLI_SYMBOLIC_LINKS: &str = "cli-symbolic-links";
static OPT_CONTEXT: &str = "context";
static OPT_COPY_CONTENTS: &str = "copy-contents";
static OPT_DEREFERENCE: &str = "dereference";
static OPT_FORCE: &str = "force";
static OPT_INTERACTIVE: &str = "interactive";
static OPT_LINK: &str = "link";
static OPT_NO_CLOBBER: &str = "no-clobber";
static OPT_NO_DEREFERENCE: &str = "no-dereference";
static OPT_ARCHIVE: &str = "archive";
static OPT_ATTRIBUTES_ONLY: &str = "attributes-only";
static OPT_BACKUP: &str = "backup";
static OPT_CLI_SYMBOLIC_LINKS: &str = "cli-symbolic-links";
static OPT_CONTEXT: &str = "context";
static OPT_COPY_CONTENTS: &str = "copy-contents";
static OPT_DEREFERENCE: &str = "dereference";
static OPT_FORCE: &str = "force";
static OPT_INTERACTIVE: &str = "interactive";
static OPT_LINK: &str = "link";
static OPT_NO_CLOBBER: &str = "no-clobber";
static OPT_NO_DEREFERENCE: &str = "no-dereference";
static OPT_NO_DEREFERENCE_PRESERVE_LINKS: &str = "no-dereference-preserve-linkgs";
static OPT_NO_PRESERVE: &str = "no-preserve";
static OPT_NO_TARGET_DIRECTORY: &str = "no-target-directory";
static OPT_ONE_FILE_SYSTEM: &str = "one-file-system";
static OPT_PARENTS: &str = "parents";
static OPT_PATHS: &str = "paths";
static OPT_PRESERVE: &str = "preserve";
static OPT_PRESERVE_DEFAULT_ATTRIBUTES: &str = "preserve-default-attributes";
static OPT_RECURSIVE: &str = "recursive";
static OPT_RECURSIVE_ALIAS: &str = "recursive_alias";
static OPT_REFLINK: &str = "reflink";
static OPT_REMOVE_DESTINATION: &str = "remove-destination";
static OPT_SPARSE: &str = "sparse";
static OPT_STRIP_TRAILING_SLASHES: &str = "strip-trailing-slashes";
static OPT_SUFFIX: &str = "suffix";
static OPT_SYMBOLIC_LINK: &str = "symbolic-link";
static OPT_TARGET_DIRECTORY: &str = "target-directory";
static OPT_UPDATE: &str = "update";
static OPT_VERBOSE: &str = "verbose";
static OPT_VERSION: &str = "version";
static OPT_NO_PRESERVE: &str = "no-preserve";
static OPT_NO_TARGET_DIRECTORY: &str = "no-target-directory";
static OPT_ONE_FILE_SYSTEM: &str = "one-file-system";
static OPT_PARENTS: &str = "parents";
static OPT_PATHS: &str = "paths";
static OPT_PRESERVE: &str = "preserve";
static OPT_PRESERVE_DEFAULT_ATTRIBUTES: &str = "preserve-default-attributes";
static OPT_RECURSIVE: &str = "recursive";
static OPT_RECURSIVE_ALIAS: &str = "recursive_alias";
static OPT_REFLINK: &str = "reflink";
static OPT_REMOVE_DESTINATION: &str = "remove-destination";
static OPT_SPARSE: &str = "sparse";
static OPT_STRIP_TRAILING_SLASHES: &str = "strip-trailing-slashes";
static OPT_SUFFIX: &str = "suffix";
static OPT_SYMBOLIC_LINK: &str = "symbolic-link";
static OPT_TARGET_DIRECTORY: &str = "target-directory";
static OPT_UPDATE: &str = "update";
static OPT_VERBOSE: &str = "verbose";
static OPT_VERSION: &str = "version";
#[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))]
static PRESERVABLE_ATTRIBUTES: &[&str] = &["ownership", "timestamps", "context", "links", "xattr", "all"];
static PRESERVABLE_ATTRIBUTES: &[&str] = &[
"ownership",
"timestamps",
"context",
"links",
"xattr",
"all",
];
static DEFAULT_ATTRIBUTES: &[Attribute] = &[
#[cfg(unix)] Attribute::Mode,
#[cfg(unix)]
Attribute::Mode,
Attribute::Ownership,
Attribute::Timestamps,
];
pub fn uumain(args: Vec<String>) -> i32 {
let usage = get_usage();
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 paths: Vec<String> = matches.values_of("paths")
let paths: Vec<String> = matches
.values_of("paths")
.map(|v| v.map(|p| p.to_string()).collect())
.unwrap_or_default();
@ -513,20 +539,26 @@ impl FromStr for Attribute {
fn from_str(value: &str) -> CopyResult<Attribute> {
Ok(match &*value.to_lowercase() {
#[cfg(unix)] "mode" => Attribute::Mode,
#[cfg(unix)]
"mode" => Attribute::Mode,
"ownership" => Attribute::Ownership,
"timestamps" => Attribute::Timestamps,
"context" => Attribute::Context,
"links" => Attribute::Links,
"xattr" => Attribute::Xattr,
_ => return Err(Error::InvalidArgument(format!("invalid attribute '{}'", value)))
_ => {
return Err(Error::InvalidArgument(format!(
"invalid attribute '{}'",
value
)))
}
})
}
}
impl Options {
fn from_matches(matches: &ArgMatches) -> CopyResult<Options> {
let not_implemented_opts = vec![
let not_implemented_opts = vec![
OPT_ARCHIVE,
OPT_COPY_CONTENTS,
OPT_NO_DEREFERENCE_PRESERVE_LINKS,
@ -537,25 +569,26 @@ impl Options {
OPT_STRIP_TRAILING_SLASHES,
OPT_ONE_FILE_SYSTEM,
OPT_CONTEXT,
#[cfg(windows)] OPT_FORCE,
#[cfg(windows)]
OPT_FORCE,
];
for not_implemented_opt in not_implemented_opts {
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)
|| matches.is_present(OPT_RECURSIVE_ALIAS)
let recursive = matches.is_present(OPT_RECURSIVE) || matches.is_present(OPT_RECURSIVE_ALIAS)
|| matches.is_present(OPT_ARCHIVE);
let backup = matches.is_present(OPT_BACKUP)
|| matches.is_present(OPT_SUFFIX);
let backup = matches.is_present(OPT_BACKUP) || matches.is_present(OPT_SUFFIX);
// Parse target directory options
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
let preserve_attributes: Vec<Attribute> = if matches.is_present(OPT_PRESERVE) {
@ -565,14 +598,14 @@ impl Options {
let mut attributes = Vec::new();
for attribute_str in attribute_strs {
if attribute_str == "all" {
#[cfg(unix)]
attributes.push(Attribute::Mode);
attributes.push(Attribute::Ownership);
attributes.push(Attribute::Timestamps);
attributes.push(Attribute::Context);
attributes.push(Attribute::Xattr);
attributes.push(Attribute::Links);
break;
#[cfg(unix)]
attributes.push(Attribute::Mode);
attributes.push(Attribute::Ownership);
attributes.push(Attribute::Timestamps);
attributes.push(Attribute::Context);
attributes.push(Attribute::Xattr);
attributes.push(Attribute::Links);
break;
} else {
attributes.push(Attribute::from_str(attribute_str)?);
}
@ -601,14 +634,13 @@ impl Options {
reflink_mode: {
if let Some(reflink) = matches.value_of(OPT_REFLINK) {
match reflink {
"always" => {
ReflinkMode::Always
},
"auto" => {
ReflinkMode::Auto
},
"always" => ReflinkMode::Always,
"auto" => ReflinkMode::Auto,
value => {
return Err(Error::InvalidArgument(format!("invalid argument '{}' for \'reflink\'", value)))
return Err(Error::InvalidArgument(format!(
"invalid argument '{}' for \'reflink\'",
value
)))
}
}
} else {
@ -640,7 +672,6 @@ impl TargetType {
}
}
/// Returns tuple of (Source paths, 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<_>>();
@ -673,51 +704,66 @@ fn parse_path_args(path_args: &[String], options: &Options) -> CopyResult<(Vec<S
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
#[cfg(not(target_os = "redox"))]
{
if !source.is_dir() {
unsafe {
let src_path = CString::new(source.as_os_str().to_str().unwrap()).unwrap();
let inode: u64;
let nlinks: u64;
#[cfg(unix)]
{
let mut stat = mem::zeroed();
if libc::lstat(src_path.as_ptr(), &mut stat) < 0 {
return Err(format!("cannot stat {:?}: {}", src_path, std::io::Error::last_os_error()).into());
if !source.is_dir() {
unsafe {
let src_path = CString::new(source.as_os_str().to_str().unwrap()).unwrap();
let inode: u64;
let nlinks: u64;
#[cfg(unix)]
{
let mut stat = mem::zeroed();
if libc::lstat(src_path.as_ptr(), &mut stat) < 0 {
return Err(format!(
"cannot stat {:?}: {}",
src_path,
std::io::Error::last_os_error()
).into());
}
inode = stat.st_ino as u64;
nlinks = stat.st_nlink as u64;
}
inode = stat.st_ino as u64;
nlinks = stat.st_nlink as u64;
}
#[cfg(windows)]
{
let mut stat = mem::uninitialized();
let handle = CreateFile2(src_path.as_ptr() as *const u16,
winapi::um::winnt::GENERIC_READ,
winapi::um::winnt::FILE_SHARE_READ,
0,
std::ptr::null_mut());
if GetFileInformationByHandle(handle, stat) != 0 {
return Err(format!("cannot get file information {:?}: {}", source, std::io::Error::last_os_error()).into());
#[cfg(windows)]
{
let mut stat = mem::uninitialized();
let handle = CreateFile2(
src_path.as_ptr() as *const u16,
winapi::um::winnt::GENERIC_READ,
winapi::um::winnt::FILE_SHARE_READ,
0,
std::ptr::null_mut(),
);
if GetFileInformationByHandle(handle, stat) != 0 {
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);
nlinks = (*stat).nNumberOfLinks as u64;
}
inode = (((*stat).nFileIndexHigh as u64) << 32 | (*stat).nFileIndexLow as u64);
nlinks = (*stat).nNumberOfLinks as u64;
}
for hard_link in hard_links.iter() {
if hard_link.1 == inode {
std::fs::hard_link(hard_link.0.clone(), dest.clone()).unwrap();
*found_hard_link = true;
for hard_link in hard_links.iter() {
if hard_link.1 == inode {
std::fs::hard_link(hard_link.0.clone(), dest.clone()).unwrap();
*found_hard_link = true;
}
}
if !(*found_hard_link) && nlinks > 1 {
hard_links.push((dest.clone().to_str().unwrap().to_string(), inode));
}
}
if !(*found_hard_link) && nlinks > 1 {
hard_links.push((dest.clone().to_str().unwrap().to_string(), inode));
}
}
}
}
Ok(())
}
@ -749,9 +795,9 @@ fn copy(sources: &[Source], target: &Target, options: &Options) -> CopyResult<()
} else {
let mut found_hard_link = false;
if preserve_hard_links {
let dest = construct_dest_path(source, target, &target_type, options)?;
preserve_hardlinks(&mut hard_links, source, dest, &mut found_hard_link).unwrap();
}
let dest = construct_dest_path(source, target, &target_type, options)?;
preserve_hardlinks(&mut hard_links, source, dest, &mut found_hard_link).unwrap();
}
if !found_hard_link {
if let Err(error) = copy_source(source, target, &target_type, options) {
show_error!("{}", error);
@ -767,30 +813,38 @@ fn copy(sources: &[Source], target: &Target, options: &Options) -> CopyResult<()
if non_fatal_errors {
Err(Error::NotAllFilesCopied)
} else {
Ok(())
Ok(())
}
}
fn construct_dest_path(source_path: &Path, target: &Target, target_type: &TargetType, options: &Options)
-> CopyResult<PathBuf>
{
fn construct_dest_path(
source_path: &Path,
target: &Target,
target_type: &TargetType,
options: &Options,
) -> CopyResult<PathBuf> {
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 {
TargetType::Directory => {
let root = source_path.parent().unwrap_or(source_path);
localize_to_target(root, source_path, target)?
},
}
TargetType::File => target.to_path_buf(),
})
}
fn copy_source(source: &Source, target: &Target, target_type: &TargetType, options: &Options)
-> CopyResult<()>
{
fn copy_source(
source: &Source,
target: &Target,
target_type: &TargetType,
options: &Options,
) -> CopyResult<()> {
let source_path = Path::new(&source);
if source_path.is_dir() {
// 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
/// contents to `target`.
///
@ -823,12 +876,12 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult
#[cfg(unix)]
let mut hard_links: Vec<(String, u64)> = vec![];
let mut preserve_hard_links = false;
for attribute in &options.preserve_attributes {
if *attribute == Attribute::Links {
preserve_hard_links = true;
}
let mut preserve_hard_links = false;
for attribute in &options.preserve_attributes {
if *attribute == Attribute::Links {
preserve_hard_links = true;
}
}
// This should be changed once Redox supports hardlinks
#[cfg(any(windows, target_os = "redox"))]
@ -838,7 +891,7 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult
let path = or_continue!(or_continue!(path).path().canonicalize());
let local_to_root_parent = match root_parent {
Some(parent) => or_continue!(path.strip_prefix(&parent)).to_path_buf(),
None => path.clone(),
None => path.clone(),
};
let local_to_target = target.join(&local_to_root_parent);
@ -852,7 +905,7 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult
let dest = local_to_target.as_path().to_path_buf();
preserve_hardlinks(&mut hard_links, &source, dest, &mut found_hard_link).unwrap();
if !found_hard_link {
copy_file(path.as_path(), local_to_target.as_path(), options)?;
copy_file(path.as_path(), local_to_target.as_path(), options)?;
}
} else {
copy_file(path.as_path(), local_to_target.as_path(), options)?;
@ -866,22 +919,26 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult
impl OverwriteMode {
fn verify(&self, path: &Path) -> CopyResult<()> {
match *self {
OverwriteMode::NoClobber => {
Err(Error::Skipped(format!("Not overwriting {} because of option '{}'", path.display(), OPT_NO_CLOBBER)))
},
OverwriteMode::NoClobber => Err(Error::Skipped(format!(
"Not overwriting {} because of option '{}'",
path.display(),
OPT_NO_CLOBBER
))),
OverwriteMode::Interactive(_) => {
if prompt_yes!("{}: overwrite {}? ", executable!(), path.display()) {
Ok(())
} 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(()),
}
}
}
fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResult<()> {
let context = &*format!("'{}' -> '{}'", source.display().to_string(), dest.display());
Ok(match *attribute {
@ -890,32 +947,36 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu
let mode = fs::metadata(source).context(context)?.permissions().mode();
let mut dest_metadata = fs::metadata(source).context(context)?.permissions();
dest_metadata.set_mode(mode);
},
}
Attribute::Ownership => {
let metadata = fs::metadata(source).context(context)?;
fs::set_permissions(dest, metadata.permissions()).context(context)?;
},
}
Attribute::Timestamps => {
let metadata = fs::metadata(source)?;
filetime::set_file_times(Path::new(dest), FileTime::from_last_access_time(&metadata), FileTime::from_last_modification_time(&metadata))?;
},
Attribute::Context => {},
Attribute::Links => {},
Attribute::Xattr => {
filetime::set_file_times(
Path::new(dest),
FileTime::from_last_access_time(&metadata),
FileTime::from_last_modification_time(&metadata),
)?;
}
Attribute::Context => {}
Attribute::Links => {}
Attribute::Xattr => {
#[cfg(unix)]
{
let xattrs = xattr::list(source)?;
for attr in xattrs {
let xattrs = xattr::list(source)?;
for attr in xattrs {
if let Some(attr_value) = xattr::get(source, attr.clone())? {
crash_if_err!(EXIT_ERR, xattr::set(dest, attr, &attr_value[..]));
}
}
}
}
#[cfg(not(unix))]
{
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() {
fs::remove_file(dest)?;
}
},
}
OverwriteMode::Clobber(ClobberMode::RemoveDestination) => {
fs::remove_file(dest)?;
},
}
_ => (),
};
@ -1025,7 +1086,7 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
} else {
copy_helper(source, dest, options)?;
}
},
}
CopyMode::AttrOnly => {
OpenOptions::new()
.write(true)
@ -1064,9 +1125,14 @@ fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()>
ReflinkMode::Always => unsafe {
let result = ficlone(dst_file, src_file as *const i32);
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 {
return Ok(())
return Ok(());
}
},
ReflinkMode::Auto => unsafe {
@ -1090,14 +1156,14 @@ pub fn verify_target_type(target: &Path, target_type: &TargetType) -> CopyResult
(&TargetType::Directory, false) => {
Err(format!("target: '{}' is not a directory", target.display()).into())
}
(&TargetType::File, true) => {
Err(format!("cannot overwrite directory '{}' with non-directory", target.display()).into())
}
(&TargetType::File, true) => Err(format!(
"cannot overwrite directory '{}' with non-directory",
target.display()
).into()),
_ => Ok(()),
}
}
/// Remove the `root` prefix from `source` and prefix it with `target`
/// to create a file that is local to `target`
/// # Examples
@ -1114,7 +1180,6 @@ pub fn localize_to_target(root: &Path, source: &Path, target: &Path) -> CopyResu
Ok(target.join(&local_to_root))
}
pub fn paths_refer_to_same_file(p1: &Path, p2: &Path) -> io::Result<bool> {
// We have to take symlinks and relative paths into account.
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)
}
#[test]
fn test_cp_localize_to_target() {
assert!(localize_to_target(
&Path::new("a/source/"),
&Path::new("a/source/c.txt"),
&Path::new("target/")
).unwrap() == Path::new("target/c.txt"))
assert!(
localize_to_target(
&Path::new("a/source/"),
&Path::new("a/source/c.txt"),
&Path::new("target/")
).unwrap() == Path::new("target/c.txt")
)
}

View file

@ -31,7 +31,10 @@ pub mod Bytes {
}
#[derive(Debug)]
pub struct ByteReader<R> where R: Read {
pub struct ByteReader<R>
where
R: Read,
{
inner: BufReader<R>,
newline_char: u8,
}
@ -40,7 +43,7 @@ impl<R: Read> ByteReader<R> {
pub fn new(read: R, newline_char: u8) -> ByteReader<R> {
ByteReader {
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;
loop {
{ // need filled_buf to go out of scope
{
// need filled_buf to go out of scope
let filled_buf = match self.fill_buf() {
Ok(b) => {
if b.len() == 0 {
return bytes_consumed
return bytes_consumed;
} else {
b
}
},
}
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];
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),
}
},
_ => {
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;
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 ranges::Range;
@ -23,8 +23,10 @@ mod buffer;
mod ranges;
mod searcher;
static SYNTAX: &'static str = "[-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 SYNTAX: &'static str =
"[-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 = "
Each call must specify a mode (what to use for columns),
a sequence (which columns to print), and provide a data source
@ -111,7 +113,7 @@ struct Options {
}
struct FieldOptions {
delimiter: String, // one char long, String because of UTF8 representation
delimiter: String, // one char long, String because of UTF8 representation
out_delimeter: Option<String>,
only_delimited: bool,
zero_terminated: bool,
@ -135,8 +137,7 @@ fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> i32 {
use buffer::Bytes::Select;
use buffer::Bytes::Selected::*;
let newline_char =
if opts.zero_terminated { b'\0' } else { b'\n' };
let newline_char = if opts.zero_terminated { b'\0' } else { b'\n' };
let mut buf_read = buffer::ByteReader::new(reader, newline_char);
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>) {
NewlineFound => {
crash_if_err!(1, out.write_all(&[newline_char]));
continue 'newline
continue 'newline;
}
Complete(len) => {
cur_pos += len;
break
break;
}
Partial(len) => cur_pos += len,
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]));
}
break 'newline
break 'newline;
}
}
}
@ -175,7 +176,7 @@ fn cut_bytes<R: Read>(reader: R, ranges: &[Range], opts: &Options) -> i32 {
}
print_delim = true;
}
None => ()
None => (),
}
// 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,
Complete(_) => {
cur_pos = high + 1;
break
break;
}
EndOfFile => {
if cur_pos != low || low == high {
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
}
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 out = stdout();
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() {
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;
if delim_search.peek().is_none() {
if ! only_delimited {
if !only_delimited {
crash_if_err!(1, out.write_all(line));
if line[line.len() - 1] != newline_char {
crash_if_err!(1, out.write_all(&[newline_char]));
}
}
continue
continue;
}
for &Range { low, high } in ranges.iter() {
if low - fields_pos > 0 {
low_idx = match delim_search.nth(low - fields_pos - 1) {
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));
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 {
let newline_char =
if opts.zero_terminated { b'\0' } else { b'\n' };
let newline_char = if opts.zero_terminated { b'\0' } else { b'\n' };
match opts.out_delimeter {
Some(ref o_delim) => {
return cut_fields_delimiter(reader, ranges, &opts.delimiter,
opts.only_delimited, newline_char, o_delim)
return cut_fields_delimiter(
reader,
ranges,
&opts.delimiter,
opts.only_delimited,
newline_char,
o_delim,
)
}
None => ()
None => (),
}
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() {
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;
if delim_search.peek().is_none() {
if ! opts.only_delimited {
if !opts.only_delimited {
crash_if_err!(1, out.write_all(line));
if line[line.len() - 1] != newline_char {
crash_if_err!(1, out.write_all(&[newline_char]));
}
}
continue
continue;
}
for &Range { low, high } in ranges.iter() {
if low - fields_pos > 0 {
low_idx = match delim_search.nth(low - fields_pos - 1) {
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));
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 exit_code = 0;
if filenames.is_empty() { filenames.push("-".to_owned()); }
if filenames.is_empty() {
filenames.push("-".to_owned());
}
for filename in &filenames {
if filename == "-" {
if stdin_read { continue }
if stdin_read {
continue;
}
exit_code |= match mode {
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() {
show_error!("{}", msg_args_nonexistent_file!(filename));
continue
continue;
}
let file = match File::open(&path) {
Ok(f) => f,
Err(e) => {
show_error!("opening '{}': {}", &filename[..], e);
continue
continue;
}
};
@ -428,82 +445,110 @@ pub fn uumain(args: Vec<String>) -> i32 {
.parse(args);
let complement = matches.opt_present("complement");
let mode_parse = match (matches.opt_str("bytes"),
matches.opt_str("characters"),
matches.opt_str("fields")) {
let mode_parse = match (
matches.opt_str("bytes"),
matches.opt_str("characters"),
matches.opt_str("fields"),
) {
(Some(byte_ranges), None, None) => {
list_to_ranges(&byte_ranges[..], complement)
.map(|ranges| Mode::Bytes(ranges, Options { out_delim: matches.opt_str("output-delimiter"), zero_terminated : matches.opt_present("zero-terminated") }))
list_to_ranges(&byte_ranges[..], complement).map(|ranges| {
Mode::Bytes(
ranges,
Options {
out_delim: matches.opt_str("output-delimiter"),
zero_terminated: matches.opt_present("zero-terminated"),
},
)
})
}
(None, Some(char_ranges), None) => {
list_to_ranges(&char_ranges[..], complement)
.map(|ranges| Mode::Characters(ranges, Options { out_delim: matches.opt_str("output-delimiter"), zero_terminated : matches.opt_present("zero-terminated") }))
list_to_ranges(&char_ranges[..], complement).map(|ranges| {
Mode::Characters(
ranges,
Options {
out_delim: matches.opt_str("output-delimiter"),
zero_terminated: matches.opt_present("zero-terminated"),
},
)
})
}
(None, None, Some(field_ranges)) => {
list_to_ranges(&field_ranges[..], complement).and_then(|ranges|
{
let out_delim = match matches.opt_str("output-delimiter") {
Some(s) => {
if s.is_empty() {
Some("\0".to_owned())
} else {
Some(s)
}
},
None => None,
};
let only_delimited = matches.opt_present("only-delimited");
let zero_terminated = matches.opt_present("zero-terminated");
match matches.opt_str("delimiter") {
Some(delim) => {
if delim.chars().count() > 1 {
Err(msg_opt_invalid_should_be!("empty or 1 character long", "a value 2 characters or longer", "--delimiter", "-d").to_owned())
} else {
let delim = if delim.is_empty() {
"\0".to_owned()
} else {
delim
};
Ok(Mode::Fields(ranges,
FieldOptions {
delimiter: delim,
out_delimeter: out_delim,
only_delimited: only_delimited,
zero_terminated: zero_terminated
}))
}
(None, None, Some(field_ranges)) => list_to_ranges(&field_ranges[..], complement)
.and_then(|ranges| {
let out_delim = match matches.opt_str("output-delimiter") {
Some(s) => {
if s.is_empty() {
Some("\0".to_owned())
} else {
Some(s)
}
None => Ok(Mode::Fields(ranges,
FieldOptions {
delimiter: "\t".to_owned(),
out_delimeter: out_delim,
only_delimited: only_delimited,
zero_terminated: zero_terminated
}))
}
None => None,
};
let only_delimited = matches.opt_present("only-delimited");
let zero_terminated = matches.opt_present("zero-terminated");
match matches.opt_str("delimiter") {
Some(delim) => {
if delim.chars().count() > 1 {
Err(msg_opt_invalid_should_be!(
"empty or 1 character long",
"a value 2 characters or longer",
"--delimiter",
"-d"
).to_owned())
} else {
let delim = if delim.is_empty() {
"\0".to_owned()
} else {
delim
};
Ok(Mode::Fields(
ranges,
FieldOptions {
delimiter: delim,
out_delimeter: out_delim,
only_delimited: only_delimited,
zero_terminated: zero_terminated,
},
))
}
}
None => Ok(Mode::Fields(
ranges,
FieldOptions {
delimiter: "\t".to_owned(),
out_delimeter: out_delim,
only_delimited: only_delimited,
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)").to_owned())
}
_ => Err(msg_expects_one_of!("--fields (-f)", "--chars (-c)", "--bytes (-b)").to_owned())
}),
(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)")
.to_owned(),
),
_ => Err(msg_expects_one_of!("--fields (-f)", "--chars (-c)", "--bytes (-b)").to_owned()),
};
let mode_parse = match mode_parse {
Err(_) => mode_parse,
Ok(mode) => {
match mode {
Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.opt_present("delimiter") =>
Err(msg_opt_only_usable_if!("printing a sequence of fields", "--delimiter", "-d").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 {
Mode::Bytes(_, _) | Mode::Characters(_, _) if matches.opt_present("delimiter") => Err(
msg_opt_only_usable_if!("printing a sequence of fields", "--delimiter", "-d")
.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),
},
};
match mode_parse {

View file

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

View file

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

View file

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

View file

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

View file

@ -14,21 +14,25 @@ extern crate uucore;
use std::path::Path;
static NAME: &'static str = "dirname";
static SYNTAX: &'static str = "[OPTION] NAME...";
static SUMMARY: &'static str = "strip last component from file name";
static NAME: &'static str = "dirname";
static SYNTAX: &'static str = "[OPTION] NAME...";
static SUMMARY: &'static str = "strip last component from file name";
static LONG_HELP: &'static str = "
Output each NAME with its last non-slash component and trailing slashes
removed; if NAME contains no /'s, output '.' (meaning the current
directory).
";
";
pub fn uumain(args: Vec<String>) -> i32 {
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optflag("z", "zero", "separate output with NUL rather than newline")
.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() {
for path in &matches.free {

View file

@ -64,26 +64,29 @@ impl Stat {
nlink: metadata.nlink() as u64,
created: metadata.mtime() 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.
// XXX: this should use the impl Trait return type when it is stabilized
fn du(mut my_stat: Stat, options: &Options, depth: usize)
-> Box<DoubleEndedIterator<Item = Stat>>
{
let mut stats = vec!();
let mut futures = vec!();
fn du(mut my_stat: Stat, options: &Options, depth: usize) -> Box<DoubleEndedIterator<Item = Stat>> {
let mut stats = vec![];
let mut futures = vec![];
if my_stat.is_dir {
let read = match fs::read_dir(&my_stat.path) {
Ok(read) => read,
Err(e) => {
safe_writeln!(stderr(), "{}: cannot read directory {}: {}",
options.program_name, my_stat.path.display(), e);
return Box::new(iter::once(my_stat))
safe_writeln!(
stderr(),
"{}: cannot read directory {}: {}",
options.program_name,
my_stat.path.display(),
e
);
return Box::new(iter::once(my_stat));
}
};
@ -102,24 +105,33 @@ fn du(mut my_stat: Stat, options: &Options, depth: usize)
}
}
stats.extend(futures.into_iter().flat_map(|val| val).rev().filter_map(|stat| {
if !options.separate_dirs && stat.path.parent().unwrap() == my_stat.path {
my_stat.size += stat.size;
my_stat.blocks += stat.blocks;
}
if options.max_depth == None || depth < options.max_depth.unwrap() {
Some(stat)
} else {
None
}
}));
stats.extend(
futures
.into_iter()
.flat_map(|val| val)
.rev()
.filter_map(|stat| {
if !options.separate_dirs && stat.path.parent().unwrap() == my_stat.path {
my_stat.size += stat.size;
my_stat.blocks += stat.blocks;
}
if options.max_depth == None || depth < options.max_depth.unwrap() {
Some(stat)
} else {
None
}
}),
);
stats.push(my_stat);
Box::new(stats.into_iter())
}
pub fn uumain(args: Vec<String>) -> i32 {
let syntax = format!("[OPTION]... [FILE]...
{0} [OPTION]... --files0-from=F", NAME);
let syntax = format!(
"[OPTION]... [FILE]...
{0} [OPTION]... --files0-from=F",
NAME
);
let matches = new_coreopts!(&syntax, SUMMARY, LONG_HELP)
// In task
.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")
// In main
.optflagopt("", "time", "show time of the last modification of any file in the
directory, or any of its subdirectories. If WORD is given, show time as WORD instead of modification time:
atime, access, use, ctime or status", "WORD")
directory, or any of its subdirectories. If WORD is given, show time as WORD instead
of modification time: atime, access, use, ctime or status", "WORD")
// In main
.optopt("", "time-style", "show times using style 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"),
};
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 kb = if matches.opt_present("si") {1000} else {1024};
let mb = if matches.opt_present("si") {
1000 * 1000
} else {
1024 * 1024
};
let kb = if matches.opt_present("si") {
1000
} else {
1024
};
let block_size = match matches.opt_str("block-size") {
Some(s) => {
@ -232,22 +256,30 @@ pub fn uumain(args: Vec<String>) -> i32 {
}
let number = numbers.parse::<u64>().unwrap();
let multiple = match &letters[..] {
"K" => 1024u64.pow(1), "M" => 1024u64.pow(2),
"G" => 1024u64.pow(3), "T" => 1024u64.pow(4),
"P" => 1024u64.pow(5), "E" => 1024u64.pow(6),
"Z" => 1024u64.pow(7), "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),
"K" => 1024u64.pow(1),
"M" => 1024u64.pow(2),
"G" => 1024u64.pow(3),
"T" => 1024u64.pow(4),
"P" => 1024u64.pow(5),
"E" => 1024u64.pow(6),
"Z" => 1024u64.pow(7),
"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);
return 1;
}
};
number * multiple
},
None => 1024
}
None => 1024,
};
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") {
Some(s) => {
match &s[..] {
"full-iso" => "%Y-%m-%d %H:%M:%S.%f %z",
"long-iso" => "%Y-%m-%d %H:%M",
"iso" => "%Y-%m-%d",
_ => {
show_error!("invalid argument '{}' for 'time style'
Some(s) => match &s[..] {
"full-iso" => "%Y-%m-%d %H:%M:%S.%f %z",
"long-iso" => "%Y-%m-%d %H:%M",
"iso" => "%Y-%m-%d",
_ => {
show_error!(
"invalid argument '{}' for 'time style'
Valid arguments are:
- 'full-iso'
- 'long-iso'
- 'iso'
Try '{} --help' for more information.", s, NAME);
return 1;
}
Try '{} --help' for more information.",
s,
NAME
);
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;
for path_str in strs.into_iter() {
@ -313,26 +347,40 @@ Try '{} --help' for more information.", s, NAME);
"created" => stat.created,
"modified" => stat.modified,
_ => {
show_error!("invalid argument 'modified' for '--time'
show_error!(
"invalid argument 'modified' for '--time'
Valid arguments are:
- 'accessed', 'created', 'modified'
Try '{} --help' for more information.", NAME);
Try '{} --help' for more information.",
NAME
);
return 1;
}
},
None => stat.modified
None => stat.modified,
};
((time / 1000) as i64, (time % 1000 * 1000000) as i32)
};
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();
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 {
if !summarize || (summarize && index == len-1) {
print!("{}\t{}{}", convert_size(size), stat.path.display(), line_separator);
if !summarize || (summarize && index == len - 1) {
print!(
"{}\t{}{}",
convert_size(size),
stat.path.display(),
line_separator
);
}
}
if options.total && index == (len - 1) {

View file

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

45
src/env/env.rs vendored
View file

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

View file

@ -11,8 +11,8 @@
* file that was distributed with this source code.
*/
extern crate unicode_width;
extern crate getopts;
extern crate unicode_width;
#[macro_use]
extern crate uucore;
@ -23,9 +23,9 @@ use std::iter::repeat;
use std::str::from_utf8;
use unicode_width::UnicodeWidthChar;
static SYNTAX: &'static str = "[OPTION]... [FILE]...";
static SYNTAX: &'static str = "[OPTION]... [FILE]...";
static SUMMARY: &'static str = "Convert tabs in each FILE to spaces, writing to standard output.
With no FILE, or when FILE is -, read standard input.";
With no FILE, or when FILE is -, read standard input.";
static LONG_HELP: &'static str = "";
static DEFAULT_TABSTOP: usize = 8;
@ -33,18 +33,21 @@ static DEFAULT_TABSTOP: usize = 8;
fn tabstops_parse(s: String) -> Vec<usize> {
let words = s.split(',').collect::<Vec<&str>>();
let nums = words.into_iter()
.map(|sn| sn.parse::<usize>()
.unwrap_or_else(
|_| crash!(1, "{}\n", "tab size contains invalid character(s)"))
)
let nums = words
.into_iter()
.map(|sn| {
sn.parse::<usize>()
.unwrap_or_else(|_| crash!(1, "{}\n", "tab size contains invalid character(s)"))
})
.collect::<Vec<usize>>();
if nums.iter().any(|&n| n == 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");
}
@ -62,8 +65,8 @@ struct Options {
impl Options {
fn new(matches: getopts::Matches) -> Options {
let tabstops = match matches.opt_str("t") {
None => vec!(DEFAULT_TABSTOP),
Some(s) => tabstops_parse(s)
None => vec![DEFAULT_TABSTOP],
Some(s) => tabstops_parse(s),
};
let iflag = matches.opt_present("i");
@ -71,30 +74,53 @@ impl Options {
// avoid allocations when dumping out long sequences of spaces
// by precomputing the longest string of spaces we will ever need
let nspaces = tabstops.iter().scan(0, |pr,&it| {
let ret = Some(it - *pr);
*pr = it;
ret
}).max().unwrap(); // length of tabstops is guaranteed >= 1
let nspaces = tabstops
.iter()
.scan(0, |pr, &it| {
let ret = Some(it - *pr);
*pr = it;
ret
})
.max()
.unwrap(); // length of tabstops is guaranteed >= 1
let tspaces = repeat(' ').take(nspaces).collect();
let files =
if matches.free.is_empty() {
vec!("-".to_owned())
} else {
matches.free
};
let files = if matches.free.is_empty() {
vec!["-".to_owned()]
} else {
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 {
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.optflag("i", "initial", "do not convert tabs after non blanks")
.optopt("t", "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")
.optopt(
"t",
"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);
expand(Options::new(matches));
@ -102,7 +128,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
0
}
fn open(path: String) -> BufReader<Box<Read+'static>> {
fn open(path: String) -> BufReader<Box<Read + 'static>> {
let file_buf;
if path == "-" {
BufReader::new(Box::new(stdin()) as Box<Read>)
@ -158,24 +184,30 @@ fn expand(options: Options) {
if byte + nbytes > buf.len() {
// don't overrun buffer because of invalid UTF-8
(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() {
Some('\t') => (Tab, 0, nbytes),
Some('\x08') => (Backspace, 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)
},
}
}
} 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 {
(match buf[byte] { // always take exactly 1 byte in strict ASCII mode
0x09 => Tab,
0x08 => Backspace,
_ => Other,
}, 1, 1)
(
match buf[byte] {
// always take exactly 1 byte in strict ASCII mode
0x09 => Tab,
0x08 => Backspace,
_ => Other,
},
1,
1,
)
};
// figure out how many columns this char takes up
@ -189,9 +221,9 @@ fn expand(options: Options) {
if init || !options.iflag {
safe_unwrap!(output.write_all(&options.tspaces[..nts].as_bytes()));
} else {
safe_unwrap!(output.write_all(&buf[byte..byte+nbytes]));
safe_unwrap!(output.write_all(&buf[byte..byte + nbytes]));
}
},
}
_ => {
col = if ctype == Other {
col + cwidth
@ -207,14 +239,14 @@ fn expand(options: Options) {
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
}
buf.truncate(0); // clear the buffer
buf.truncate(0); // clear the buffer
}
}
}

View file

@ -9,9 +9,9 @@
* file that was distributed with this source code.
*/
extern crate onig;
#[macro_use]
extern crate uucore;
extern crate onig;
mod tokens;
mod syntax_tree;
@ -20,54 +20,68 @@ static NAME: &'static str = "expr";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub fn uumain(args: Vec<String>) -> i32 {
// For expr utility we do not want getopts.
// The following usage should work without escaping hyphens: `expr -15 = 1 + 2 \* \( 3 - -4 \)`
// For expr utility we do not want getopts.
// The following usage should work without escaping hyphens: `expr -15 = 1 + 2 \* \( 3 - -4 \)`
if maybe_handle_help_or_version( &args ) { 0 }
else {
let token_strings = args[1..].to_vec();
if maybe_handle_help_or_version(&args) {
0
} else {
let token_strings = args[1..].to_vec();
match process_expr( &token_strings ) {
Ok( expr_result ) => print_expr_ok( &expr_result ),
Err( expr_error ) => print_expr_error( &expr_error )
}
}
match process_expr(&token_strings) {
Ok(expr_result) => print_expr_ok(&expr_result),
Err(expr_error) => print_expr_error(&expr_error),
}
}
}
fn process_expr( token_strings: &Vec<String> ) -> Result< String, String > {
let maybe_tokens = tokens::strings_to_tokens( &token_strings );
let maybe_ast = syntax_tree::tokens_to_ast( maybe_tokens );
evaluate_ast( maybe_ast )
fn process_expr(token_strings: &Vec<String>) -> Result<String, String> {
let maybe_tokens = tokens::strings_to_tokens(&token_strings);
let maybe_ast = syntax_tree::tokens_to_ast(maybe_tokens);
evaluate_ast(maybe_ast)
}
fn print_expr_ok( expr_result: &String ) -> i32 {
println!("{}", expr_result);
if expr_result == "0" || expr_result == "" { 1 }
else { 0 }
fn print_expr_ok(expr_result: &String) -> i32 {
println!("{}", expr_result);
if expr_result == "0" || expr_result == "" {
1
} else {
0
}
}
fn print_expr_error( expr_error: &String ) -> ! {
crash!(2, "{}", expr_error)
fn print_expr_error(expr_error: &String) -> ! {
crash!(2, "{}", expr_error)
}
fn evaluate_ast( maybe_ast: Result<Box<syntax_tree::ASTNode>, String> ) -> Result<String, String> {
if maybe_ast.is_err() { Err( maybe_ast.err().unwrap() ) }
else { maybe_ast.ok().unwrap().evaluate() }
fn evaluate_ast(maybe_ast: Result<Box<syntax_tree::ASTNode>, String>) -> Result<String, String> {
if maybe_ast.is_err() {
Err(maybe_ast.err().unwrap())
} else {
maybe_ast.ok().unwrap().evaluate()
}
}
fn maybe_handle_help_or_version( args: &Vec<String> ) -> bool {
if args.len() == 2 {
if args[1] == "--help" { print_help(); true }
else if args[1] == "--version" { print_version(); true }
else { false }
}
else { false }
fn maybe_handle_help_or_version(args: &Vec<String>) -> bool {
if args.len() == 2 {
if args[1] == "--help" {
print_help();
true
} else if args[1] == "--version" {
print_version();
true
} else {
false
}
} else {
false
}
}
fn print_help() {
//! The following is taken from GNU coreutils' "expr --help" output.
print!(
r#"Usage: expr EXPRESSION
//! The following is taken from GNU coreutils' "expr --help" output.
print!(
r#"Usage: expr EXPRESSION
or: expr OPTION
--help display this help and exit
@ -119,9 +133,9 @@ Environment variables:
* EXPR_DEBUG_SYA_STEP=1 dump each parser step
* EXPR_DEBUG_AST=1 dump expression represented abstract syntax tree
"#
);
);
}
fn print_version() {
println!("{} {}", NAME, VERSION);
println!("{} {}", NAME, VERSION);
}

View file

@ -12,170 +12,210 @@
//! * https://en.wikipedia.org/wiki/Shunting-yard_algorithm
//!
use tokens::{Token};
use tokens::Token;
use onig::{Regex, RegexOptions, Syntax};
type TokenStack = Vec<(usize, Token)>;
pub type OperandsList = Vec< Box<ASTNode> >;
pub type OperandsList = Vec<Box<ASTNode>>;
#[derive(Debug)]
pub enum ASTNode {
Leaf { token_idx: usize, value: String },
Node { token_idx: usize, op_type: String, operands: OperandsList }
Leaf {
token_idx: usize,
value: String,
},
Node {
token_idx: usize,
op_type: String,
operands: OperandsList,
},
}
impl ASTNode {
fn debug_dump( &self ) {
self.debug_dump_impl( 1 );
fn debug_dump(&self) {
self.debug_dump_impl(1);
}
fn debug_dump_impl( &self, depth: usize ) {
fn debug_dump_impl(&self, depth: usize) {
for _ in 0..depth {
print!("\t", );
print!("\t",);
}
match *self {
ASTNode::Leaf{ ref token_idx, 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());
ASTNode::Leaf {
ref token_idx,
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 {
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> {
Box::new( ASTNode::Node{
token_idx: token_idx,
op_type: op_type.clone(),
operands: operands
} )
fn new_node(token_idx: usize, op_type: &String, operands: OperandsList) -> Box<ASTNode> {
Box::new(ASTNode::Node {
token_idx: token_idx,
op_type: op_type.clone(),
operands: operands,
})
}
fn new_leaf( token_idx: usize, value: &String ) -> Box<ASTNode> {
Box::new( ASTNode::Leaf{ token_idx: token_idx, value: value.clone() } )
fn new_leaf(token_idx: usize, value: &String) -> Box<ASTNode> {
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 {
ASTNode::Leaf{ ref value, .. } => Ok( value.clone() ),
ASTNode::Node{ ref op_type, .. } =>
match self.operand_values() {
Err( reason ) => Err( reason ),
Ok( 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|
if b == 0 { Err("division by zero".to_owned()) }
else { Ok( a / b ) },
&operand_values ),
"%" => infix_operator_two_ints(
|a: i64, b: i64|
if b == 0 { Err("division by zero".to_owned()) }
else { Ok( a % b ) },
&operand_values ),
ASTNode::Leaf { ref value, .. } => Ok(value.clone()),
ASTNode::Node { ref op_type, .. } => match self.operand_values() {
Err(reason) => Err(reason),
Ok(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| {
if b == 0 {
Err("division by zero".to_owned())
} else {
Ok(a / b)
}
},
&operand_values,
),
"%" => infix_operator_two_ints(
|a: i64, b: i64| {
if b == 0 {
Err("division by zero".to_owned())
} else {
Ok(a % b)
}
},
&operand_values,
),
"=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok( bool_as_int(a == b) ),
|a: &String, b: &String| Ok( bool_as_string(a == b) ),
&operand_values
),
"!=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok( bool_as_int(a != b) ),
|a: &String, b: &String| Ok( bool_as_string(a != b) ),
&operand_values
),
"<" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok( bool_as_int(a < b) ),
|a: &String, b: &String| Ok( bool_as_string(a < b) ),
&operand_values
),
">" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok( bool_as_int(a > b) ),
|a: &String, b: &String| Ok( bool_as_string(a > b) ),
&operand_values
),
"<=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok( bool_as_int(a <= b) ),
|a: &String, b: &String| Ok( bool_as_string(a <= b) ),
&operand_values
),
">=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok( bool_as_int(a >= b) ),
|a: &String, b: &String| Ok( bool_as_string(a >= b) ),
&operand_values
),
"|" => infix_operator_or(&operand_values),
"&" => infix_operator_and(&operand_values),
":" | "match" => operator_match(&operand_values),
"length" => prefix_operator_length( &operand_values ),
"index" => prefix_operator_index( &operand_values ),
"substr" => prefix_operator_substr( &operand_values ),
"=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok(bool_as_int(a == b)),
|a: &String, b: &String| Ok(bool_as_string(a == b)),
&operand_values,
),
"!=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok(bool_as_int(a != b)),
|a: &String, b: &String| Ok(bool_as_string(a != b)),
&operand_values,
),
"<" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok(bool_as_int(a < b)),
|a: &String, b: &String| Ok(bool_as_string(a < b)),
&operand_values,
),
">" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok(bool_as_int(a > b)),
|a: &String, b: &String| Ok(bool_as_string(a > b)),
&operand_values,
),
"<=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok(bool_as_int(a <= b)),
|a: &String, b: &String| Ok(bool_as_string(a <= b)),
&operand_values,
),
">=" => infix_operator_two_ints_or_two_strings(
|a: i64, b: i64| Ok(bool_as_int(a >= b)),
|a: &String, b: &String| Ok(bool_as_string(a >= b)),
&operand_values,
),
"|" => infix_operator_or(&operand_values),
"&" => infix_operator_and(&operand_values),
":" | "match" => operator_match(&operand_values),
"length" => prefix_operator_length(&operand_values),
"index" => prefix_operator_index(&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 {
let mut out = Vec::with_capacity( operands.len() );
pub fn operand_values(&self) -> Result<Vec<String>, String> {
if let &ASTNode::Node { ref operands, .. } = self {
let mut out = Vec::with_capacity(operands.len());
for operand in operands {
match operand.evaluate() {
Ok( value ) => out.push( value ),
Err( reason ) => return Err( reason ),
Ok(value) => out.push(value),
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> {
if maybe_tokens.is_err() { Err( maybe_tokens.err().unwrap() ) }
else {
pub fn tokens_to_ast(
maybe_tokens: Result<Vec<(usize, Token)>, String>,
) -> Result<Box<ASTNode>, String> {
if maybe_tokens.is_err() {
Err(maybe_tokens.err().unwrap())
} else {
let tokens = maybe_tokens.ok().unwrap();
let mut out_stack: TokenStack = Vec::new();
let mut op_stack: TokenStack = Vec::new();
for (token_idx, token) in tokens {
if let Err( reason ) = push_token_to_either_stack( token_idx, &token, &mut out_stack, &mut op_stack ) {
return Err( reason )
if let 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 ) {
return Err( reason )
if let Err(reason) = move_rest_of_ops_to_out(&mut out_stack, &mut op_stack) {
return Err(reason);
}
assert!( op_stack.is_empty() );
assert!(op_stack.is_empty());
maybe_dump_rpn( &out_stack );
let result = ast_from_rpn( &mut out_stack );
maybe_dump_rpn(&out_stack);
let result = ast_from_rpn(&mut out_stack);
if !out_stack.is_empty() {
Err( "syntax error (fist RPN token does not represent expression AST's root)".to_owned() )
}
else {
maybe_dump_ast( &result );
Err("syntax error (fist RPN token does not represent expression AST's root)".to_owned())
} else {
maybe_dump_ast(&result);
result
}
}
}
fn maybe_dump_ast( result: &Result< Box<ASTNode>, String > ) {
fn maybe_dump_ast(result: &Result<Box<ASTNode>, String>) {
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" {
println!("EXPR_DEBUG_AST");
match *result {
Ok( ref ast ) => ast.debug_dump(),
Err( ref reason ) => println!("\terr: {:?}", reason),
}
Ok(ref ast) => ast.debug_dump(),
Err(ref reason) => println!("\terr: {:?}", reason),
}
}
}
}
fn maybe_dump_rpn( rpn: &TokenStack ) {
fn maybe_dump_rpn(rpn: &TokenStack) {
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" {
println!("EXPR_DEBUG_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() {
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 }) ) =>
Ok( ASTNode::new_leaf( token_idx, &value ) ),
Some((token_idx, Token::Value { value })) => Ok(ASTNode::new_leaf(token_idx, &value)),
Some( (token_idx, Token::InfixOp{ value, .. }) ) =>
maybe_ast_node( token_idx, &value, 2, rpn ),
Some((token_idx, Token::InfixOp { value, .. })) => {
maybe_ast_node(token_idx, &value, 2, rpn)
}
Some( (token_idx, Token::PrefixOp{ value, arity }) ) =>
maybe_ast_node( token_idx, &value, arity, rpn ),
Some((token_idx, Token::PrefixOp { value, arity })) => {
maybe_ast_node(token_idx, &value, arity, rpn)
}
Some( (token_idx, unexpected_token) ) =>
panic!("unexpected token at #{} {:?}", token_idx, unexpected_token),
Some((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 > {
let mut operands = Vec::with_capacity( arity );
fn maybe_ast_node(
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 {
match ast_from_rpn( rpn ) {
Err( reason ) => return Err( reason ),
Ok( operand ) => operands.push( operand ),
match ast_from_rpn(rpn) {
Err(reason) => return Err(reason),
Ok(operand) => operands.push(operand),
}
}
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 {
match op_stack.pop() {
None => return Ok( () ),
Some( (token_idx, Token::ParOpen) ) => return Err( format!( "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 )
None => return Ok(()),
Some((token_idx, Token::ParOpen)) => {
return Err(format!(
"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> {
let result =
match *token {
Token::Value{ .. } => Ok( out_stack.push( (token_idx, token.clone()) ) ),
fn push_token_to_either_stack(
token_idx: usize,
token: &Token,
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{ .. } =>
if op_stack.is_empty() { Ok( op_stack.push( (token_idx, token.clone()) ) ) }
else { push_op_to_stack( token_idx, token, out_stack, op_stack ) },
Token::InfixOp { .. } => if op_stack.is_empty() {
Ok(op_stack.push((token_idx, token.clone())))
} 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 )
};
maybe_dump_shunting_yard_step( token_idx, token, out_stack, op_stack, &result );
Token::ParClose => move_till_match_paren(out_stack, op_stack),
};
maybe_dump_shunting_yard_step(token_idx, token, out_stack, op_stack, &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;
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" {
println!("EXPR_DEBUG_SYA_STEP");
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> {
if let &Token::InfixOp{ precedence: prec, left_assoc: la, .. } = token {
fn push_op_to_stack(
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 {
match op_stack.last() {
None =>
return Ok( op_stack.push( (token_idx, token.clone()) ) ),
None => return Ok(op_stack.push((token_idx, token.clone()))),
Some( &(_, Token::ParOpen) ) =>
return Ok( op_stack.push( (token_idx, token.clone()) ) ),
Some(&(_, Token::ParOpen)) => return Ok(op_stack.push((token_idx, token.clone()))),
Some( &(_, Token::InfixOp{ precedence: prev_prec, .. }) ) =>
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::InfixOp {
precedence: prev_prec,
..
},
)) => 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{ .. }) ) =>
return Ok( op_stack.push( (token_idx, token.clone()) ) ),
Some(&(_, Token::PrefixOp { .. })) => {
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")
}
}
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 {
match op_stack.pop() {
None => return Err( "syntax error (Mismatched close-parenthesis)".to_string() ),
Some( (_, Token::ParOpen) ) => return Ok( () ),
Some( other ) => out_stack.push( other )
None => return Err("syntax error (Mismatched close-parenthesis)".to_string()),
Some((_, Token::ParOpen)) => return Ok(()),
Some(other) => out_stack.push(other),
}
}
}
fn infix_operator_two_ints<F>( f: F, values: &Vec<String> ) -> Result<String, String>
where F : Fn( i64, i64 ) -> Result<i64, String>
fn infix_operator_two_ints<F>(f: F, values: &Vec<String>) -> Result<String, String>
where
F: Fn(i64, i64) -> Result<i64, String>,
{
assert!( values.len() == 2 );
if let Some( left ) = values[0].parse::<i64>().ok() {
if let Some( right ) = values[1].parse::<i64>().ok() {
return match f( left, right ) {
assert!(values.len() == 2);
if let Some(left) = values[0].parse::<i64>().ok() {
if let Some(right) = values[1].parse::<i64>().ok() {
return match f(left, right) {
Ok(result) => Ok(result.to_string()),
Err(reason) => Err(reason),
}
};
}
}
Err( "Expected an integer operand".to_string() )
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>
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)
}
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] )
} 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);
if value_as_bool(&values[0]) {
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]) {
Ok(values[0].clone())
} else {
@ -354,30 +441,31 @@ fn infix_operator_and( values: &Vec<String> ) -> Result<String, String> {
fn operator_match(values: &Vec<String>) -> Result<String, String> {
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,
Err(err) => return Err(err.description().to_string())
Err(err) => return Err(err.description().to_string()),
};
if re.captures_len() > 0 {
Ok(match re.captures(&values[0]) {
Some(captures) => captures.at(1).unwrap().to_string(),
None => "".to_string()
None => "".to_string(),
})
} else {
Ok(match re.find(&values[0]) {
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> {
assert!( values.len() == 1 );
Ok( values[0].len().to_string() )
fn prefix_operator_length(values: &Vec<String>) -> Result<String, String> {
assert!(values.len() == 1);
Ok(values[0].len().to_string())
}
fn prefix_operator_index( values: &Vec<String> ) -> Result<String, String> {
assert!( values.len() == 2 );
fn prefix_operator_index(values: &Vec<String>) -> Result<String, String> {
assert!(values.len() == 2);
let haystack = &values[0];
let needles = &values[1];
@ -387,45 +475,61 @@ fn prefix_operator_index( values: &Vec<String> ) -> Result<String, String> {
for ch_n in needles.chars() {
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> {
assert!( values.len() == 3 );
fn prefix_operator_substr(values: &Vec<String>) -> Result<String, String> {
assert!(values.len() == 3);
let subj = &values[0];
let mut idx = match values[1].parse::<i64>() {
Ok( i ) => i,
Err( _ ) => return Err( "expected integer as POS arg to 'substr'".to_string() ),
Ok(i) => i,
Err(_) => return Err("expected integer as POS arg to 'substr'".to_string()),
};
let mut len = match values[2].parse::<i64>() {
Ok( i ) => i,
Err( _ ) => return Err( "expected integer as LENGTH arg to 'substr'".to_string() ),
Ok(i) => i,
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();
for ch in subj.chars() {
idx -= 1;
if idx <= 0 {
if len <= 0 { break; }
if len <= 0 {
break;
}
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_string( b: bool ) -> String { if b { "1".to_string() } else { "0".to_string() } }
fn value_as_bool( s: &str ) -> bool {
fn bool_as_int(b: bool) -> i64 {
if b {
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 {
return false
return false;
}
match s.parse::<i64>() {
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.
//!
#[derive(Debug)]
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum Token {
Value{ value: String },
Value {
value: String,
},
ParOpen,
ParClose,
@ -29,45 +30,42 @@ pub enum Token {
InfixOp {
precedence: u8,
left_assoc: bool,
value: String
value: String,
},
PrefixOp {
arity: usize,
value: String
value: String,
},
}
impl Token {
fn new_infix_op( v: &String, left_assoc: bool, precedence: u8 ) -> Self {
Token::InfixOp{
fn new_infix_op(v: &String, left_assoc: bool, precedence: u8) -> Self {
Token::InfixOp {
left_assoc: left_assoc,
precedence: precedence,
value: v.clone()
value: v.clone(),
}
}
fn new_value( v: &String ) -> Self {
Token::Value{
value: v.clone()
}
fn new_value(v: &String) -> Self {
Token::Value { value: v.clone() }
}
fn is_infix_plus( &self ) -> bool {
fn is_infix_plus(&self) -> bool {
match *self {
Token::InfixOp{ ref value, .. } => value == "+",
_ => false
}
}
fn is_a_number( &self ) -> bool {
match *self {
Token::Value{ ref value, .. } =>
match value.parse::<i64>() {
Ok( _ ) => true,
Err( _ ) => false
},
Token::InfixOp { ref value, .. } => value == "+",
_ => false,
}
}
fn is_a_close_paren( &self ) -> bool {
fn is_a_number(&self) -> bool {
match *self {
Token::Value { ref value, .. } => match value.parse::<i64>() {
Ok(_) => true,
Err(_) => false,
},
_ => false,
}
}
fn is_a_close_paren(&self) -> bool {
match *self {
Token::ParClose => true,
_ => false,
@ -75,57 +73,68 @@ impl Token {
}
}
pub fn strings_to_tokens( strings: &[String] ) -> Result< Vec<(usize, Token)>, String > {
let mut tokens_acc = Vec::with_capacity( strings.len() );
pub fn strings_to_tokens(strings: &[String]) -> Result<Vec<(usize, Token)>, String> {
let mut tokens_acc = Vec::with_capacity(strings.len());
let mut tok_idx = 1;
for s in strings {
let token_if_not_escaped =
match s.as_ref() {
"(" => Token::ParOpen,
")" => Token::ParClose,
let token_if_not_escaped = match s.as_ref() {
"(" => Token::ParOpen,
")" => 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() },
"substr" => Token::PrefixOp{ arity: 3, value: s.clone() },
"index" => Token::PrefixOp{ arity: 2, value: s.clone() },
"length" => Token::PrefixOp{ arity: 1, value: s.clone() },
"match" => Token::PrefixOp {
arity: 2,
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 ),
};
push_token_if_not_escaped( &mut tokens_acc, tok_idx, token_if_not_escaped, &s );
_ => Token::new_value(&s),
};
push_token_if_not_escaped(&mut tokens_acc, tok_idx, token_if_not_escaped, &s);
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;
if let Ok(debug_var) = env::var( "EXPR_DEBUG_TOKENS" ) {
if let Ok(debug_var) = env::var("EXPR_DEBUG_TOKENS") {
if debug_var == "1" {
println!("EXPR_DEBUG_TOKENS");
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... :(
let prev_is_plus =
match acc.last() {
None => false,
Some( ref t ) => t.1.is_infix_plus(),
};
let should_use_as_escaped =
if prev_is_plus && acc.len() >= 2 {
let pre_prev = &acc[acc.len() - 2];
! ( pre_prev.1.is_a_number() || pre_prev.1.is_a_close_paren() )
}
else {
prev_is_plus
};
let prev_is_plus = match acc.last() {
None => false,
Some(ref t) => t.1.is_infix_plus(),
};
let should_use_as_escaped = if prev_is_plus && acc.len() >= 2 {
let pre_prev = &acc[acc.len() - 2];
!(pre_prev.1.is_a_number() || pre_prev.1.is_a_close_paren())
} else {
prev_is_plus
};
if should_use_as_escaped {
acc.pop();
acc.push( (tok_idx, Token::new_value( s )) )
}
else {
acc.push( (tok_idx, token) )
acc.push((tok_idx, Token::new_value(s)))
} else {
acc.push((tok_idx, token))
}
}

View file

@ -54,18 +54,17 @@ fn inv_mod_u64(a: u64) -> Option<u64> {
r
} / newr;
let (tp, Wrapping(newtp)) =
(newt, Wrapping(t) - (Wrapping(quot) * Wrapping(newt)));
let (tp, Wrapping(newtp)) = (newt, Wrapping(t) - (Wrapping(quot) * Wrapping(newt)));
t = tp;
newt = newtp;
let (rp, Wrapping(newrp)) =
(newr, Wrapping(r) - (Wrapping(quot) * Wrapping(newr)));
let (rp, Wrapping(newrp)) = (newr, Wrapping(r) - (Wrapping(quot) * Wrapping(newr)));
r = rp;
newr = newrp;
}
if r > 1 { // not invertible
if r > 1 {
// not invertible
return None;
}
@ -80,7 +79,13 @@ fn main() {
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
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();
let mut cols = 3;
@ -106,7 +111,11 @@ fn main() {
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]
@ -127,8 +136,7 @@ fn test_generator() {
}
const MAX_WIDTH: usize = 102;
const PREAMBLE: &'static str =
r##"/*
const PREAMBLE: &'static str = r##"/*
* This file is part of the uutils coreutils package.
*
* (c) kwantam <kwantam@gmail.com>

View file

@ -19,7 +19,7 @@ extern crate rand;
extern crate uucore;
use numeric::*;
use rand::distributions::{Range, IndependentSample};
use rand::distributions::{IndependentSample, Range};
use std::cmp::{max, min};
use std::io::{stdin, BufRead, BufReader};
use std::num::Wrapping;
@ -111,7 +111,7 @@ fn table_division(mut num: u64, factors: &mut Vec<u64>) {
// See http://math.stackexchange.com/questions/1251327/
// for a nice explanation.
loop {
let Wrapping(x) = Wrapping(num) * Wrapping(inv); // x = num * inv mod 2^64
let Wrapping(x) = Wrapping(num) * Wrapping(inv); // x = num * inv mod 2^64
if x <= ceil {
num = x;
factors.push(prime);
@ -129,11 +129,11 @@ fn table_division(mut num: u64, factors: &mut Vec<u64>) {
// Decide whether to use Pollard Rho or slow divisibility based on
// number's size:
//if num >= 1 << 63 {
// number is too big to use rho pollard without overflowing
//trial_division_slow(num, factors);
// number is too big to use rho pollard without overflowing
//trial_division_slow(num, factors);
//} else if num > 1 {
// number is still greater than 1, but not so big that we have to worry
rho_pollard_factor(num, factors);
// number is still greater than 1, but not so big that we have to worry
rho_pollard_factor(num, factors);
//}
}
@ -158,8 +158,7 @@ fn print_factors_str(num_str: &str) {
}
pub fn uumain(args: Vec<String>) -> i32 {
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.parse(args);
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP).parse(args);
if matches.free.is_empty() {
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
};
if pow(a, m-1, m, mul) != 1 {
if pow(a, m - 1, m, mul) != 1 {
return true;
}
a = pow(a, exponent, m, mul);
@ -103,7 +103,7 @@ fn witness(mut a: u64, exponent: u64, m: u64) -> bool {
if a == 1 {
return true;
}
if a == m-1 {
if a == m - 1 {
return false;
}
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.
// Discovered by Jim Sinclair, according to http://miller-rabin.appspot.com
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

@ -36,7 +36,7 @@ impl Iterator for Sieve {
// need to keep checking the min element of the heap
// until we've found an element that's greater than n
if next > n {
break; // next heap element is bigger than n
break; // next heap element is bigger than n
}
if next == n {
@ -59,13 +59,18 @@ impl Iterator for Sieve {
impl Sieve {
fn new() -> Sieve {
Sieve { inner: Wheel::new(), filts: PrimeHeap::new() }
Sieve {
inner: Wheel::new(),
filts: PrimeHeap::new(),
}
}
#[allow(dead_code)]
#[inline]
pub fn primes() -> PrimeSieve {
fn deref(x: &u64) -> u64 { *x }
fn deref(x: &u64) -> u64 {
*x
}
let deref = deref as fn(&u64) -> u64;
INIT_PRIMES.iter().map(deref).chain(Sieve::new())
}
@ -73,7 +78,9 @@ impl Sieve {
#[allow(dead_code)]
#[inline]
pub fn odd_primes() -> PrimeSieve {
fn deref(x: &u64) -> u64 { *x }
fn deref(x: &u64) -> u64 {
*x
}
let deref = deref as fn(&u64) -> u64;
(&INIT_PRIMES[1..]).iter().map(deref).chain(Sieve::new())
}
@ -97,7 +104,7 @@ impl Iterator for Wheel {
}
#[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 ret = self.next;
self.next = ret + increment;
@ -108,13 +115,19 @@ impl Iterator for Wheel {
impl Wheel {
#[inline]
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
/// (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];
/// 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 (key, _) = self.data[0];
loop {
let child1 = 2*idx + 1;
let child2 = 2*idx + 2;
let child1 = 2 * idx + 1;
let child2 = 2 * idx + 2;
// no more children
if child1 >= len {

View file

@ -15,7 +15,7 @@ extern crate unicode_width;
extern crate uucore;
use std::cmp;
use std::io::{Read, BufReader, BufWriter};
use std::io::{BufReader, BufWriter, Read};
use std::fs::File;
use std::io::{stdin, stdout, Write};
use linebreak::break_lines;
@ -34,27 +34,27 @@ mod linebreak;
mod parasplit;
// program's NAME and VERSION are used for -V and -h
static SYNTAX: &'static str = "[OPTION]... [FILE]...";
static SYNTAX: &'static str = "[OPTION]... [FILE]...";
static SUMMARY: &'static str = "Reformat paragraphs from input files (or stdin) to stdout.";
static LONG_HELP: &'static str = "";
pub type FileOrStdReader = BufReader<Box<Read+'static>>;
pub type FileOrStdReader = BufReader<Box<Read + 'static>>;
pub struct FmtOptions {
crown : bool,
tagged : bool,
mail : bool,
split_only : bool,
use_prefix : bool,
prefix : String,
xprefix : bool,
use_anti_prefix : bool,
anti_prefix : String,
xanti_prefix : bool,
uniform : bool,
quick : bool,
width : usize,
goal : usize,
tabwidth : usize,
crown: bool,
tagged: bool,
mail: bool,
split_only: bool,
use_prefix: bool,
prefix: String,
xprefix: bool,
use_anti_prefix: bool,
anti_prefix: String,
xanti_prefix: bool,
uniform: bool,
quick: bool,
width: usize,
goal: usize,
tabwidth: usize,
}
pub fn uumain(args: Vec<String>) -> i32 {
@ -75,38 +75,57 @@ pub fn uumain(args: Vec<String>) -> i32 {
.parse(args);
let mut fmt_opts = FmtOptions {
crown : false,
tagged : false,
mail : false,
uniform : false,
quick : false,
split_only : false,
use_prefix : false,
prefix : String::new(),
xprefix : false,
use_anti_prefix : false,
anti_prefix : String::new(),
xanti_prefix : false,
width : 79,
goal : 74,
tabwidth : 8,
crown: false,
tagged: false,
mail: false,
uniform: false,
quick: false,
split_only: false,
use_prefix: false,
prefix: String::new(),
xprefix: false,
use_anti_prefix: false,
anti_prefix: String::new(),
xanti_prefix: false,
width: 79,
goal: 74,
tabwidth: 8,
};
if matches.opt_present("t") { fmt_opts.tagged = true; }
if matches.opt_present("c") { fmt_opts.crown = true; fmt_opts.tagged = false; }
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; }
if matches.opt_present("t") {
fmt_opts.tagged = true;
}
if matches.opt_present("c") {
fmt_opts.crown = true;
fmt_opts.tagged = false;
}
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") {
Some(s) => {
fmt_opts.prefix = s;
fmt_opts.use_prefix = true;
}
None => ()
None => (),
};
match matches.opt_str("P") {
@ -114,46 +133,49 @@ pub fn uumain(args: Vec<String>) -> i32 {
fmt_opts.anti_prefix = s;
fmt_opts.use_anti_prefix = true;
}
None => ()
None => (),
};
match matches.opt_str("w") {
Some(s) => {
fmt_opts.width =
match s.parse::<usize>() {
Ok(t) => t,
Err(e) => { crash!(1, "Invalid WIDTH specification: `{}': {}", s, e); }
};
fmt_opts.width = match s.parse::<usize>() {
Ok(t) => t,
Err(e) => {
crash!(1, "Invalid WIDTH specification: `{}': {}", s, e);
}
};
fmt_opts.goal = cmp::min(fmt_opts.width * 94 / 100, fmt_opts.width - 3);
}
None => ()
None => (),
};
match matches.opt_str("g") {
Some(s) => {
fmt_opts.goal =
match s.parse::<usize>() {
Ok(t) => t,
Err(e) => { crash!(1, "Invalid GOAL specification: `{}': {}", s, e); }
};
fmt_opts.goal = match s.parse::<usize>() {
Ok(t) => t,
Err(e) => {
crash!(1, "Invalid GOAL specification: `{}': {}", s, e);
}
};
if !matches.opt_present("w") {
fmt_opts.width = cmp::max(fmt_opts.goal * 100 / 94, fmt_opts.goal + 3);
} else if fmt_opts.goal > fmt_opts.width {
crash!(1, "GOAL cannot be greater than WIDTH.");
}
}
None => ()
None => (),
};
match matches.opt_str("T") {
Some(s) => {
fmt_opts.tabwidth =
match s.parse::<usize>() {
Ok(t) => t,
Err(e) => { crash!(1, "Invalid TABWIDTH specification: `{}': {}", s, e); }
};
fmt_opts.tabwidth = match s.parse::<usize>() {
Ok(t) => t,
Err(e) => {
crash!(1, "Invalid TABWIDTH specification: `{}': {}", s, e);
}
};
}
None => ()
None => (),
};
if fmt_opts.tabwidth < 1 {
@ -172,13 +194,13 @@ pub fn uumain(args: Vec<String>) -> i32 {
for i in files.iter().map(|x| &x[..]) {
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) {
Ok(f) => BufReader::new(Box::new(f) as Box<Read+'static>),
Ok(f) => BufReader::new(Box::new(f) as Box<Read + 'static>),
Err(e) => {
show_warning!("{}: {}", i, e);
continue;
},
}
},
};
let p_stream = ParagraphStream::new(&fmt_opts, &mut fp);
@ -187,8 +209,8 @@ pub fn uumain(args: Vec<String>) -> i32 {
Err(s) => {
silent_unwrap!(ostream.write_all(s.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 parasplit::{Paragraph, ParaWords, WordInfo};
use std::io::{Write, BufWriter, Stdout};
use parasplit::{ParaWords, Paragraph, WordInfo};
use std::io::{BufWriter, Stdout, Write};
use std::i64;
use std::cmp;
use std::mem;
struct BreakArgs<'a> {
opts : &'a FmtOptions,
init_len : usize,
indent_str : &'a str,
indent_len : usize,
uniform : bool,
ostream : &'a mut BufWriter<Stdout>
opts: &'a FmtOptions,
init_len: usize,
indent_str: &'a str,
indent_len: usize,
uniform: bool,
ostream: &'a mut BufWriter<Stdout>,
}
impl<'a> BreakArgs<'a> {
@ -31,7 +31,9 @@ impl<'a> BreakArgs<'a> {
let post = winfo.after_tab;
match winfo.before_tab {
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,19 +58,18 @@ pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<
}
};
// print the init, if it exists, and get its length
let p_init_len = w_len +
if opts.crown || opts.tagged {
// handle "init" portion
silent_unwrap!(ostream.write_all(para.init_str.as_bytes()));
para.init_len
} else if !para.mail_header {
// for non-(crown, tagged) that's the same as a normal indent
silent_unwrap!(ostream.write_all(p_indent.as_bytes()));
p_indent_len
} else {
// except that mail headers get no indent at all
0
};
let p_init_len = w_len + if opts.crown || opts.tagged {
// handle "init" portion
silent_unwrap!(ostream.write_all(para.init_str.as_bytes()));
para.init_len
} else if !para.mail_header {
// for non-(crown, tagged) that's the same as a normal indent
silent_unwrap!(ostream.write_all(p_indent.as_bytes()));
p_indent_len
} else {
// except that mail headers get no indent at all
0
};
// write first word after writing init
silent_unwrap!(ostream.write_all(w.as_bytes()));
@ -76,12 +77,12 @@ pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<
let uniform = para.mail_header || opts.uniform;
let mut break_args = BreakArgs {
opts : opts,
init_len : p_init_len,
indent_str : &p_indent[..],
indent_len : p_indent_len,
uniform : uniform,
ostream : ostream
opts: opts,
init_len: p_init_len,
indent_str: &p_indent[..],
indent_len: p_indent_len,
uniform: uniform,
ostream: ostream,
};
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
// maxlength would be exceeded, then print a linebreak and indent and continue.
fn break_simple<'a, T: Iterator<Item=&'a WordInfo<'a>>>(iter: T, args: &mut BreakArgs<'a>) {
iter.fold((args.init_len, false), |l, winfo| accum_words_simple(args, l, winfo));
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)
});
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
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 {
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,
// Practice and Experience. Vol. 11, No. 11, November 1981.
// 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
let breakpoints = find_kp_breakpoints(iter.clone(), args);
// iterate through the breakpoints (note that breakpoints is in reverse break order, so we .rev() it
let (mut prev_punct, mut fresh) =
breakpoints.iter().rev().fold((false, false), |(mut prev_punct, mut fresh), &(next_break, break_before)| {
let (mut prev_punct, mut fresh) = breakpoints.iter().rev().fold(
(false, false),
|(mut prev_punct, mut fresh), &(next_break, break_before)| {
if fresh {
write_newline(args.indent_str, args.ostream);
}
// at each breakpoint, keep emitting words until we find the word matching this breakpoint
for winfo in &mut iter {
let (slen, word) = slice_if_fresh(fresh, winfo.word, winfo.word_start, args.uniform,
winfo.new_line, winfo.sentence_start, prev_punct);
let (slen, word) = slice_if_fresh(
fresh,
winfo.word,
winfo.word_start,
args.uniform,
winfo.new_line,
winfo.sentence_start,
prev_punct,
);
fresh = false;
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)
});
},
);
// after the last linebreak, write out the rest of the final line.
for winfo in iter {
if fresh {
write_newline(args.indent_str, args.ostream);
}
let (slen, word) = slice_if_fresh(fresh, winfo.word, winfo.word_start, args.uniform,
winfo.new_line, winfo.sentence_start, prev_punct);
let (slen, word) = slice_if_fresh(
fresh,
winfo.word,
winfo.word_start,
args.uniform,
winfo.new_line,
winfo.sentence_start,
prev_punct,
);
prev_punct = winfo.ends_punct;
fresh = false;
write_with_spaces(word, slen, args.ostream);
@ -172,49 +203,57 @@ fn break_knuth_plass<'a, T: Clone + Iterator<Item=&'a WordInfo<'a>>>(mut iter: T
}
struct LineBreak<'a> {
prev : usize,
linebreak : Option<&'a WordInfo<'a>>,
break_before : bool,
demerits : i64,
prev_rat : f32,
length : usize,
fresh : bool
prev: usize,
linebreak: Option<&'a WordInfo<'a>>,
break_before: bool,
demerits: i64,
prev_rat: f32,
length: usize,
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();
// set up the initial null linebreak
let mut linebreaks = vec!(LineBreak {
prev : 0,
linebreak : None,
break_before : false,
demerits : 0,
prev_rat : 0.0f32,
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!();
let mut linebreaks = vec![
LineBreak {
prev: 0,
linebreak: None,
break_before: false,
demerits: 0,
prev_rat: 0.0f32,
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![];
let stretch = (args.opts.width - args.opts.goal) as isize;
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 least_demerits = 0;
loop {
let w =
match iter.next() {
None => break,
Some(w) => w
};
let w = match iter.next() {
None => break,
Some(w) => w,
};
// if this is the last word, we don't add additional demerits for this break
let (is_last_word, is_sentence_end) =
match iter.peek() {
None => (true, true),
Some(&&WordInfo { sentence_start: st, new_line: nl, .. }) => (false, st || (nl && w.ends_punct))
};
let (is_last_word, is_sentence_end) = match iter.peek() {
None => (true, true),
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?
let slen = compute_slen(args.uniform, w.new_line, is_sentence_start, false);
@ -236,7 +275,8 @@ fn find_kp_breakpoints<'a, T: Iterator<Item=&'a WordInfo<'a>>>(iter: T, args: &B
}
// 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
// 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 tlen >= minlength {
let (new_demerits, new_ratio) =
if is_last_word {
// there is no penalty for the final line's length
(0, 0.0)
} else {
compute_demerits((args.opts.goal - tlen) as isize, stretch, w.word_nchars as isize, active.prev_rat)
};
let (new_demerits, new_ratio) = if is_last_word {
// there is no penalty for the final line's length
(0, 0.0)
} else {
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
// also, try to detect overflow by checking signum
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;
new_linebreaks.push(LineBreak {
prev : i,
linebreak : Some(w),
break_before : false,
demerits : total_demerits,
prev_rat : new_ratio,
length : args.indent_len,
fresh : true
prev: i,
linebreak: Some(w),
break_before: false,
demerits: total_demerits,
prev_rat: new_ratio,
length: args.indent_len,
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() {
// 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());
linebreaks.push(new_break);
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)> {
let mut breakwords = vec!();
let mut breakwords = vec![];
// 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) {
None => crash!(1, "Failed to find a k-p linebreak solution. This should never happen."),
Some(&s) => s
None => crash!(
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
@ -342,28 +392,28 @@ const DL_MULT: f32 = 300.0;
fn compute_demerits(delta_len: isize, stretch: isize, wlen: isize, prev_rat: f32) -> (i64, f32) {
// how much stretch are we using?
let ratio =
if delta_len == 0 {
0.0f32
} else {
delta_len as f32 / stretch as f32
};
let ratio = if delta_len == 0 {
0.0f32
} else {
delta_len as f32 / stretch as f32
};
// compute badness given the stretch ratio
let bad_linelen =
if ratio.abs() > 1.0f32 {
BAD_INFTY
} else {
(BAD_MULT * ratio.powf(3f32).abs()) as i64
};
let bad_linelen = if ratio.abs() > 1.0f32 {
BAD_INFTY
} else {
(BAD_MULT * ratio.powf(3f32).abs()) as i64
};
// we penalize lines ending in really short words
let bad_wordlen =
if wlen >= stretch {
0
} else {
(DL_MULT * ((stretch - wlen) as f32 / (stretch - 1) as f32).powf(3f32).abs()) as i64
};
let bad_wordlen = if wlen >= stretch {
0
} else {
(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
let bad_delta_r = (DR_MULT * (((ratio - prev_rat) / 2.0).powf(3f32)).abs()) as i64;
@ -373,33 +423,39 @@ fn compute_demerits(delta_len: isize, stretch: isize, wlen: isize, prev_rat: f32
(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> {
let (break_before, line_length) =
if active.fresh {
// never break before a word if that word would be the first on a line
(false, args.indent_len)
fn restart_active_breaks<'a>(
args: &BreakArgs<'a>,
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
(false, args.indent_len)
} else {
// choose the lesser evil: breaking too early, or breaking too late
let wlen = w.word_nchars + args.compute_width(w, active.length, active.fresh);
let underlen = (min - active.length) as isize;
let overlen = ((wlen + slen + active.length) - args.opts.width) as isize;
if overlen > underlen {
// break early, put this word on the next line
(true, args.indent_len + w.word_nchars)
} else {
// choose the lesser evil: breaking too early, or breaking too late
let wlen = w.word_nchars + args.compute_width(w, active.length, active.fresh);
let underlen = (min - active.length) as isize;
let overlen = ((wlen + slen + active.length) - args.opts.width) as isize;
if overlen > underlen {
// break early, put this word on the next line
(true, args.indent_len + w.word_nchars)
} else {
(false, args.indent_len)
}
};
(false, args.indent_len)
}
};
// restart the linebreak. This will be our only active path.
LineBreak {
prev : act_idx,
linebreak : Some(w),
break_before : break_before,
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 },
length : line_length,
fresh : !break_before
prev: act_idx,
linebreak: Some(w),
break_before: break_before,
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 },
length: line_length,
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.
// 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 {
(0, &word[start..])
} else {

View file

@ -32,7 +32,7 @@ fn char_width(c: char) -> usize {
#[derive(Debug)]
pub enum Line {
FormatLine(FileLine),
NoFormatLine(String, bool)
NoFormatLine(String, bool),
}
impl Line {
@ -40,7 +40,7 @@ impl Line {
fn get_formatline(self) -> FileLine {
match self {
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) {
match self {
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
#[derive(Debug)]
pub struct FileLine {
line : String,
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
indent_len : usize, // display length of indent taking into account tabs
prefix_len : usize, // PREFIX indent length taking into account tabs
line: String,
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
indent_len: usize, // display length of indent taking into account tabs
prefix_len: usize, // PREFIX indent length taking into account tabs
}
// iterator that produces a stream of Lines from a file
pub struct FileLines<'a> {
opts : &'a FmtOptions,
lines : Lines<&'a mut FileOrStdReader>,
opts: &'a FmtOptions,
lines: Lines<&'a mut FileOrStdReader>,
}
impl<'a> FileLines<'a> {
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
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)
}
// returns true if this line should be formatted
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
(_, _) => true,
}
}
@ -141,14 +152,13 @@ impl<'a> Iterator for FileLines<'a> {
type Item = Line;
fn next(&mut self) -> Option<Line> {
let n =
match self.lines.next() {
Some(t) => match t {
Ok(tt) => tt,
Err(_) => return None
},
None => return None
};
let n = match self.lines.next() {
Some(t) => match t {
Ok(tt) => tt,
Err(_) => return None,
},
None => return None,
};
// if this line is entirely whitespace,
// emit a blank line
@ -163,7 +173,10 @@ impl<'a> Iterator for FileLines<'a> {
let (pmatch, poffset) = self.match_prefix(&n[..]);
if !pmatch {
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,
// don't allow lines to be combined through it (that is,
// 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);
Some(Line::FormatLine(FileLine {
line : n,
indent_end : indent_end,
pfxind_end : poffset,
indent_len : indent_len,
prefix_len : prefix_len
line: n,
indent_end: indent_end,
pfxind_end: poffset,
indent_len: indent_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
#[derive(Debug)]
pub struct Paragraph {
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_len : usize, // printable length of the init string considering TABWIDTH
init_end : usize, // byte location of end of init in first line String
pub indent_str : String, // string representing indent
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)
pub mail_header : bool // we need to know if this is a mail header because we do word splitting differently in that case
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_len: usize, // printable length of the init string considering TABWIDTH
init_end: usize, // byte location of end of init in first line String
pub indent_str: String, // string representing indent
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)
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
// given a set of options.
pub struct ParagraphStream<'a> {
lines : Peekable<FileLines<'a>>,
next_mail : bool,
opts : &'a FmtOptions,
lines: Peekable<FileLines<'a>>,
next_mail: bool,
opts: &'a FmtOptions,
}
impl<'a> ParagraphStream<'a> {
pub fn new<'b>(opts: &'b FmtOptions, reader: &'b mut FileOrStdReader) -> ParagraphStream<'b> {
let lines = FileLines::new(opts, reader.lines()).peekable();
// 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
@ -235,18 +252,19 @@ impl<'a> ParagraphStream<'a> {
if l_slice.starts_with("From ") {
true
} else {
let colon_posn =
match l_slice.find(':') {
Some(n) => n,
None => return false
};
let colon_posn = match l_slice.find(':') {
Some(n) => n,
None => return false,
};
// 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 {
y if y < 33 || y > 126 => false,
_ => true
_ => true,
})
}
}
@ -258,14 +276,13 @@ impl<'a> Iterator for ParagraphStream<'a> {
fn next(&mut self) -> Option<Result<Paragraph, String>> {
// return a NoFormatLine in an Err; it should immediately be output
let noformat =
match self.lines.peek() {
None => return None,
Some(l) => match *l {
Line::FormatLine(_) => false,
Line::NoFormatLine(_, _) => true
}
};
let noformat = match self.lines.peek() {
None => return None,
Some(l) => match *l {
Line::FormatLine(_) => false,
Line::NoFormatLine(_, _) => true,
},
};
// found a NoFormatLine, immediately dump it out
if noformat {
@ -286,28 +303,27 @@ impl<'a> Iterator for ParagraphStream<'a> {
let mut p_lines = Vec::new();
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 {
{ // peek ahead
// need to explicitly force fl out of scope before we can call self.lines.next()
let fl =
match self.lines.peek() {
None => break,
Some(l) => {
match *l {
Line::FormatLine(ref x) => x,
Line::NoFormatLine(..) => break
}
}
};
{
// peek ahead
// need to explicitly force fl out of scope before we can call self.lines.next()
let fl = match self.lines.peek() {
None => break,
Some(l) => match *l {
Line::FormatLine(ref x) => x,
Line::NoFormatLine(..) => break,
},
};
if p_lines.is_empty() {
// first time through the loop, get things set up
// detect mail header
if self.opts.mail && self.next_mail && ParagraphStream::is_mail_header(fl) {
in_mail = true;
// there can't be any indent or pfxind because otherwise is_mail_header would fail
// since there cannot be any whitespace before the colon in a valid header field
// there can't be any indent or pfxind because otherwise is_mail_header
// would fail since there cannot be any whitespace before the colon in a
// valid header field
indent_str.push_str(" ");
indent_len = 2;
} else {
@ -343,14 +359,16 @@ impl<'a> Iterator for ParagraphStream<'a> {
} else if in_mail {
// lines following mail headers must begin with spaces
if fl.indent_end == 0 || (self.opts.use_prefix && fl.pfxind_end == 0) {
break; // this line does not begin with spaces
break; // this line does not begin with spaces
}
} else if !second_done {
// now we have enough info to handle crown margin and tagged mode
if prefix_len != fl.prefix_len || pfxind_end != fl.pfxind_end {
// in both crown and tagged modes we require that prefix_len is the same
break;
} 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
break;
} else {
@ -363,7 +381,10 @@ impl<'a> Iterator for ParagraphStream<'a> {
second_done = true;
} else {
// 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;
}
}
@ -383,75 +404,91 @@ impl<'a> Iterator for ParagraphStream<'a> {
self.next_mail = in_mail;
Some(Ok(Paragraph {
lines : p_lines,
init_str : init_str,
init_len : init_len,
init_end : init_end,
indent_str : indent_str,
indent_len : indent_len,
indent_end : indent_end,
mail_header : in_mail
lines: p_lines,
init_str: init_str,
init_len: init_len,
init_end: init_end,
indent_str: indent_str,
indent_len: indent_len,
indent_end: indent_end,
mail_header: in_mail,
}))
}
}
pub struct ParaWords<'a> {
opts : &'a FmtOptions,
para : &'a Paragraph,
words : Vec<WordInfo<'a>>
opts: &'a FmtOptions,
para: &'a Paragraph,
words: Vec<WordInfo<'a>>,
}
impl<'a> ParaWords<'a> {
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
}
fn create_words(& mut self) {
fn create_words(&mut self) {
if self.para.mail_header {
// no extra spacing for mail headers; always exactly 1 space
// safe to trim_left on every line of a mail header, since the
// 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 {
word : x,
word_start : 0,
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
}));
self.words.extend(
self.para
.lines
.iter()
.flat_map(|x| x.split_whitespace())
.map(|x| WordInfo {
word: x,
word_start: 0,
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 {
// first line
self.words.extend(
if self.opts.crown || self.opts.tagged {
// 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..])
} else {
// otherwise we slice from the indent
WordSplit::new(self.opts, &self.para.lines[0][self.para.indent_end..])
});
self.words.extend(if self.opts.crown || self.opts.tagged {
// 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..])
} else {
// otherwise we slice from the indent
WordSplit::new(self.opts, &self.para.lines[0][self.para.indent_end..])
});
if self.para.lines.len() > 1 {
let indent_end = self.para.indent_end;
let opts = self.opts;
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> {
opts : &'a FmtOptions,
string : &'a str,
length : usize,
position : usize,
prev_punct : bool
opts: &'a FmtOptions,
string: &'a str,
length: usize,
position: usize,
prev_punct: bool,
}
impl<'a> WordSplit<'a> {
@ -484,26 +521,32 @@ impl<'a> WordSplit<'a> {
fn new<'b>(opts: &'b FmtOptions, string: &'b str) -> WordSplit<'b> {
// wordsplits *must* start at a non-whitespace character
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 {
match c {
'!' | '.' | '?' => true,
_ => false
_ => false,
}
}
}
pub struct WordInfo<'a> {
pub word : &'a str,
pub word_start : usize,
pub word_nchars : usize,
pub before_tab : Option<usize>,
pub after_tab : usize,
pub sentence_start : bool,
pub ends_punct : bool,
pub new_line : bool
pub word: &'a str,
pub word_start: usize,
pub word_nchars: usize,
pub before_tab: Option<usize>,
pub after_tab: usize,
pub sentence_start: bool,
pub ends_punct: bool,
pub new_line: bool,
}
// returns (&str, is_start_of_sentence)
@ -512,58 +555,69 @@ impl<'a> Iterator for WordSplit<'a> {
fn next(&mut self) -> Option<WordInfo<'a>> {
if self.position >= self.length {
return None
return None;
}
let old_position = self.position;
let new_line = old_position == 0;
// 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..]) {
(b, a, Some(s)) => (b, a, s + old_position),
(_, _, None) => {
self.position = self.length;
return None;
}
};
let (before_tab, after_tab, word_start) =
match self.analyze_tabs(&self.string[old_position..]) {
(b, a, Some(s)) => (b, a, s + old_position),
(_, _, None) => {
self.position = self.length;
return None;
}
};
// find the beginning of the next whitespace
// note that this preserves the invariant that self.position
// points to whitespace character OR end of string
let mut word_nchars = 0;
self.position =
match self.string[word_start..]
.find(|x: char| if !x.is_whitespace() { word_nchars += char_width(x); false } else { true }) {
None => self.length,
Some(s) => s + word_start
};
self.position = match self.string[word_start..].find(|x: char| {
if !x.is_whitespace() {
word_nchars += char_width(x);
false
} else {
true
}
}) {
None => self.length,
Some(s) => s + word_start,
};
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.
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
self.prev_punct = match self.string[..self.position].chars().rev().next() {
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) =
if self.opts.uniform {
(&self.string[word_start..self.position], 0, None, 0)
} else {
(&self.string[old_position..self.position], word_start_relative, before_tab, after_tab)
};
let (word, word_start_relative, before_tab, after_tab) = if self.opts.uniform {
(&self.string[word_start..self.position], 0, None, 0)
} else {
(
&self.string[old_position..self.position],
word_start_relative,
before_tab,
after_tab,
)
};
Some(WordInfo {
word : word,
word_start : word_start_relative,
word_nchars : word_nchars,
before_tab : before_tab,
after_tab : after_tab,
sentence_start : is_start_of_sentence,
ends_punct : self.prev_punct,
new_line : new_line
word: word,
word_start: word_start_relative,
word_nchars: word_nchars,
before_tab: before_tab,
after_tab: after_tab,
sentence_start: is_start_of_sentence,
ends_punct: self.prev_punct,
new_line: new_line,
})
}
}

View file

@ -13,7 +13,7 @@
extern crate uucore;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, stdin};
use std::io::{stdin, BufRead, BufReader, Read};
use std::path::Path;
static SYNTAX: &'static str = "[OPTION]... [FILE]...";
@ -24,28 +24,41 @@ static LONG_HELP: &'static str = "";
pub fn uumain(args: Vec<String>) -> i32 {
let (args, obs_width) = handle_obsolete(&args[..]);
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("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")
.optflag(
"b",
"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);
let bytes = matches.opt_present("b");
let spaces = matches.opt_present("s");
let poss_width =
if matches.opt_present("w") {
matches.opt_str("w")
} else {
obs_width
};
let poss_width = if matches.opt_present("w") {
matches.opt_str("w")
} else {
obs_width
};
let width = match poss_width {
Some(inp_width) => match inp_width.parse::<usize>() {
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() {
vec!("-".to_owned())
vec!["-".to_owned()]
} else {
matches.free
};
@ -57,7 +70,9 @@ pub fn uumain(args: Vec<String>) -> i32 {
fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
for (i, arg) in args.iter().enumerate() {
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();
v.remove(i);
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 mut stdin_buf;
let mut file_buf;
let buffer = BufReader::new(
if filename == "-" {
stdin_buf = stdin();
&mut stdin_buf as &mut Read
} else {
file_buf = safe_unwrap!(File::open(Path::new(filename)));
&mut file_buf as &mut Read
}
);
let buffer = BufReader::new(if filename == "-" {
stdin_buf = stdin();
&mut stdin_buf as &mut Read
} else {
file_buf = safe_unwrap!(File::open(Path::new(filename)));
&mut file_buf as &mut Read
});
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 {
match slice.rfind(|ch: char| ch.is_whitespace()) {
Some(m) => &slice[..m + 1],
None => slice
None => slice,
}
} else {
slice
@ -125,26 +138,29 @@ fn fold_file<T: Read>(mut file: BufReader<T>, bytes: bool, spaces: bool, width:
if count >= width {
let (val, ncount) = {
let slice = &output[..];
let (out, val, ncount) =
if spaces && i + 1 < len {
match rfind_whitespace(slice) {
Some(m) => {
let routput = &slice[m + 1 .. slice.chars().count()];
let ncount = routput.chars().fold(0, |out, ch: char| {
out + match ch {
'\t' => 8,
'\x08' => if out > 0 { !0 } else { 0 },
'\r' => return 0,
_ => 1
}
});
(&slice[0 .. m + 1], routput, ncount)
},
None => (slice, "", 0)
let (out, val, ncount) = if spaces && i + 1 < len {
match rfind_whitespace(slice) {
Some(m) => {
let routput = &slice[m + 1..slice.chars().count()];
let ncount = routput.chars().fold(0, |out, ch: char| {
out + match ch {
'\t' => 8,
'\x08' => if out > 0 {
!0
} else {
0
},
'\r' => return 0,
_ => 1,
}
});
(&slice[0..m + 1], routput, ncount)
}
} else {
(slice, "", 0)
};
None => (slice, "", 0),
}
} else {
(slice, "", 0)
};
println!("{}", out);
(val.to_owned(), ncount)
};
@ -173,7 +189,7 @@ fn fold_file<T: Read>(mut file: BufReader<T>, bytes: bool, spaces: bool, width:
count = 0;
continue;
}
_ => count += 1
_ => count += 1,
};
output.push(ch);
}

View file

@ -12,20 +12,34 @@
#[macro_use]
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 SUMMARY: &'static str = "display current group names";
pub fn uumain(args: Vec<String>) -> i32 {
let matches = new_coreopts!(SYNTAX, SUMMARY, "")
.parse(args);
let matches = new_coreopts!(SYNTAX, SUMMARY, "").parse(args);
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 {
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 {
crash!(1, "unknown user {}", matches.free[0]);
}

View file

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

View file

@ -14,8 +14,8 @@
extern crate getopts;
extern crate hex;
extern crate md5;
extern crate regex_syntax;
extern crate regex;
extern crate regex_syntax;
extern crate sha1;
extern crate sha2;
extern crate sha3;
@ -35,7 +35,7 @@ use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
#[allow(unused_imports)]
use std::ascii::AsciiExt;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read, stdin};
use std::io::{self, stdin, BufRead, BufReader, Read};
use std::path::Path;
static NAME: &'static str = "hashsum";
@ -43,18 +43,17 @@ static VERSION: &'static str = env!("CARGO_PKG_VERSION");
fn is_custom_binary(program: &str) -> bool {
match program {
"md5sum" | "sha1sum"
| "sha224sum" | "sha256sum"
| "sha384sum" | "sha512sum"
| "sha3sum" | "sha3-224sum"
| "sha3-256sum" | "sha3-384sum"
| "sha3-512sum" | "shake128sum"
| "shake256sum" => true,
_ => false
"md5sum" | "sha1sum" | "sha224sum" | "sha256sum" | "sha384sum" | "sha512sum"
| "sha3sum" | "sha3-224sum" | "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 name: &'static str = "";
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),
"sha384sum" => ("SHA384", Box::new(Sha384::new()) as Box<Digest>, 384),
"sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box<Digest>, 512),
"sha3sum" => {
match matches.opt_str("bits") {
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(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(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)"),
Err(err) => crash!(1, "{}", err)
},
None => crash!(1, "--bits required for SHA3")
}
}
"sha3sum" => match matches.opt_str("bits") {
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(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(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)"
),
Err(err) => crash!(1, "{}", err),
},
None => crash!(1, "--bits required for SHA3"),
},
"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-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),
"shake128sum" => {
match matches.opt_str("bits") {
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
Ok(bits) => ("SHAKE128", Box::new(Shake128::new()) as Box<Digest>, bits),
Err(err) => crash!(1, "{}", err)
},
None => crash!(1, "--bits required for SHAKE-128")
}
}
"shake256sum" => {
match matches.opt_str("bits") {
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
Ok(bits) => ("SHAKE256", Box::new(Shake256::new()) as Box<Digest>, bits),
Err(err) => crash!(1, "{}", err)
},
None => crash!(1, "--bits required for SHAKE-256")
}
}
"shake128sum" => match matches.opt_str("bits") {
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
Ok(bits) => ("SHAKE128", Box::new(Shake128::new()) as Box<Digest>, bits),
Err(err) => crash!(1, "{}", err),
},
None => crash!(1, "--bits required for SHAKE-128"),
},
"shake256sum" => match matches.opt_str("bits") {
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
Ok(bits) => ("SHAKE256", Box::new(Shake256::new()) as Box<Digest>, bits),
Err(err) => crash!(1, "{}", err),
},
None => crash!(1, "--bits required for SHAKE-256"),
},
_ => {
{
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;
alg = Some(val);
output_bits = bits
};
if matches.opt_present("md5") { set_or_crash("MD5", Box::new(Md5::new()), 128) }
if matches.opt_present("sha1") { set_or_crash("SHA1", Box::new(Sha1::new()), 160) }
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("md5") {
set_or_crash("MD5", Box::new(Md5::new()), 128)
}
if matches.opt_present("sha1") {
set_or_crash("SHA1", Box::new(Sha1::new()), 160)
}
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") {
match matches.opt_str("bits") {
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(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)
Ok(224) => set_or_crash(
"SHA3-224",
Box::new(Sha3_224::new()) as Box<Digest>,
224,
),
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-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("sha3-224") {
set_or_crash("SHA3-224", Box::new(Sha3_224::new()), 224)
}
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") {
match matches.opt_str("bits") {
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
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") {
match matches.opt_str("bits") {
Some(bits_str) => match usize::from_str_radix(&bits_str, 10) {
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)
}
}
@ -164,14 +203,52 @@ pub fn uumain(args: Vec<String>) -> i32 {
let binary_flag_default = cfg!(windows);
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("", "tag", "create a BSD-style checksum");
opts.optflag("t", "text", &format!("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(
"t",
"text",
&format!(
"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("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-384", "work with SHA3-384");
opts.optflag("", "sha3-512", "work with SHA3-512");
opts.optflag("", "shake128", "work with SHAKE128 using BITS for the output size");
opts.optflag("", "shake256", "work with SHAKE256 using BITS for the output size");
opts.optflag(
"",
"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)
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..]) {
Ok(m) => m,
Err(f) => crash!(1, "{}", f)
Err(f) => crash!(1, "{}", f),
};
if matches.opt_present("help") {
@ -211,7 +301,13 @@ pub fn uumain(args: Vec<String>) -> i32 {
if binary_flag && text_flag {
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 tag = matches.opt_present("tag");
let status = matches.opt_present("status");
@ -219,13 +315,25 @@ pub fn uumain(args: Vec<String>) -> i32 {
let strict = matches.opt_present("strict");
let warn = matches.opt_present("warn") && !status;
let files = if matches.free.is_empty() {
vec!("-".to_owned())
vec!["-".to_owned()]
} else {
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,
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) {
format!(" {} [OPTION]... [FILE]...", program)
} else {
format!(" {} {{--md5|--sha1|--sha224|--sha256|--sha384|--sha512|\
--sha3|--sha3-224|--sha3-256|--sha3-384|--sha3-512|\
--shake128|--shake256}} [OPTION]... [FILE]...", program)
format!(
" {} {{--md5|--sha1|--sha224|--sha256|--sha384|--sha512|\
--sha3|--sha3-224|--sha3-256|--sha3-384|--sha3-512|\
--shake128|--shake256}} [OPTION]... [FILE]...",
program
)
};
let msg = format!("{} {}
let msg = format!(
"{} {}
Usage:
{}
Compute and check message digests.", NAME, VERSION, spec);
Compute and check message digests.",
NAME, VERSION, spec
);
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 failed = 0;
let binary_marker = if binary {
"*"
} else {
" "
};
let binary_marker = if binary { "*" } else { " " };
for filename in &files {
let filename: &str = filename;
let stdin_buf;
let file_buf;
let mut file = BufReader::new(
if filename == "-" {
stdin_buf = stdin();
Box::new(stdin_buf) as Box<Read>
} else {
file_buf = safe_unwrap!(File::open(filename));
Box::new(file_buf) as Box<Read>
}
);
let mut file = BufReader::new(if filename == "-" {
stdin_buf = stdin();
Box::new(stdin_buf) as Box<Read>
} else {
file_buf = safe_unwrap!(File::open(filename));
Box::new(file_buf) as Box<Read>
});
if check {
// Set up Regexes for line validation and parsing
let bytes = digest.output_bits() / 4;
let gnu_re = safe_unwrap!(
Regex::new(
&format!(
r"^(?P<digest>[a-fA-F0-9]{{{}}}) (?P<binary>[ \*])(?P<fileName>.*)",
bytes
)
)
);
let bsd_re = safe_unwrap!(
Regex::new(
&format!(
r"^{algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{{{digest_size}}})",
algorithm = algoname,
digest_size = bytes
)
)
);
let gnu_re = safe_unwrap!(Regex::new(&format!(
r"^(?P<digest>[a-fA-F0-9]{{{}}}) (?P<binary>[ \*])(?P<fileName>.*)",
bytes
)));
let bsd_re = safe_unwrap!(Regex::new(&format!(
r"^{algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{{{digest_size}}})",
algorithm = algoname,
digest_size = bytes
)));
let buffer = file;
for (i, line) in buffer.lines().enumerate() {
let line = safe_unwrap!(line);
let (ck_filename, sum, binary_check) = match gnu_re.captures(&line) {
Some(caps) => (caps.name("fileName").unwrap().as_str(),
caps.name("digest").unwrap().as_str().to_ascii_lowercase(),
caps.name("binary").unwrap().as_str() == "*"),
Some(caps) => (
caps.name("fileName").unwrap().as_str(),
caps.name("digest").unwrap().as_str().to_ascii_lowercase(),
caps.name("binary").unwrap().as_str() == "*",
),
None => match bsd_re.captures(&line) {
Some(caps) => (caps.name("fileName").unwrap().as_str(),
caps.name("digest").unwrap().as_str().to_ascii_lowercase(),
true),
Some(caps) => (
caps.name("fileName").unwrap().as_str(),
caps.name("digest").unwrap().as_str().to_ascii_lowercase(),
true,
),
None => {
bad_format += 1;
if strict {
return Err(1);
}
if warn {
show_warning!("{}: {}: improperly formatted {} checksum line", filename, i + 1, algoname);
show_warning!(
"{}: {}: improperly formatted {} checksum line",
filename,
i + 1,
algoname
);
}
continue;
}
}
},
};
let f = safe_unwrap!(File::open(ck_filename));
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))
.to_ascii_lowercase();
let real_sum = safe_unwrap!(digest_reader(
&mut digest,
&mut ckf,
binary_check,
output_bits
)).to_ascii_lowercase();
if sum == real_sum {
if !quiet {
println!("{}: OK", ck_filename);
@ -358,7 +483,12 @@ fn hashsum(algoname: &str, mut digest: Box<Digest>, files: Vec<String>, binary:
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 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;
loop {
match reader.read_to_end(&mut buffer) {
Ok(0) => { break; },
Ok(0) => {
break;
}
Ok(nread) => {
if windows && !binary {
// Windows text mode returns '\n' when reading '\r\n'
for i in 0 .. nread {
for i in 0..nread {
if looking_for_newline {
if buffer[i] != ('\n' 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 {
digest.input(&buffer[..nread]);
}
},
Err(e) => return Err(e)
}
Err(e) => return Err(e),
}
}
if windows && looking_for_newline {

View file

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

View file

@ -16,9 +16,9 @@ extern crate uucore;
use libc::c_long;
static SYNTAX: &'static str = "[options]";
static SUMMARY: &'static str = "";
static LONG_HELP: &'static str = "";
static SYNTAX: &'static str = "[options]";
static SUMMARY: &'static str = "";
static LONG_HELP: &'static str = "";
pub enum Mode {
HostId,
@ -27,29 +27,28 @@ pub enum Mode {
}
// currently rust libc interface doesn't include gethostid
extern {
extern "C" {
pub fn gethostid() -> c_long;
}
pub fn uumain(args: Vec<String>) -> i32 {
new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.parse(args);
new_coreopts!(SYNTAX, SUMMARY, LONG_HELP).parse(args);
hostid();
0
}
fn hostid() {
/*
* POSIX says gethostid returns a "32-bit identifier" but is silent
* whether it's sign-extended. Turn off any sign-extension. This
* is a no-op unless unsigned int is wider than 32 bits.
*/
/*
* POSIX says gethostid returns a "32-bit identifier" but is silent
* whether it's sign-extended. Turn off any sign-extension. This
* is a no-op unless unsigned int is wider than 32 bits.
*/
let mut result:c_long;
let mut result: c_long;
unsafe {
result = gethostid();
}
result &= 0xffffffff;
result &= 0xffffffff;
println!("{:0>8x}", result);
}

View file

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

View file

@ -1,5 +1,4 @@
#![crate_name = "uu_id"]
// This file is part of the uutils coreutils package.
//
// (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://www.opensource.apple.com/source/shell_cmds/shell_cmds-118/id/id.c
//
#![allow(non_camel_case_types)]
#![allow(dead_code)]
@ -20,8 +18,8 @@
extern crate uucore;
pub use uucore::libc;
use uucore::libc::{getlogin, uid_t};
use uucore::entries::{self, Passwd, Group, Locate};
use uucore::process::{getgid, getuid, getegid, geteuid};
use uucore::entries::{self, Group, Locate, Passwd};
use uucore::process::{getegid, geteuid, getgid, getuid};
use std::ffi::CStr;
macro_rules! cstr2cow {
@ -33,7 +31,7 @@ macro_rules! cstr2cow {
#[cfg(not(target_os = "linux"))]
mod audit {
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_asid_t = pid_t;
@ -56,11 +54,11 @@ mod audit {
#[repr(C)]
pub struct c_auditinfo_addr {
pub ai_auid: au_id_t, // Audit user ID
pub ai_mask: au_mask_t, // Audit masks.
pub ai_auid: au_id_t, // Audit user ID
pub ai_mask: au_mask_t, // Audit masks.
pub ai_termid: au_tid_addr_t, // Terminal ID.
pub ai_asid: au_asid_t, // Audit session ID.
pub ai_flags: uint64_t, // Audit session flags
pub ai_asid: au_asid_t, // Audit session ID.
pub ai_flags: uint64_t, // Audit session flags
}
pub type c_auditinfo_addr_t = c_auditinfo_addr;
@ -74,14 +72,18 @@ static SUMMARY: &'static str = "Print user and group information for the specifi
pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = new_coreopts!(SYNTAX, SUMMARY, "");
opts.optflag("A",
"",
"Display the process audit (not available on Linux)");
opts.optflag(
"A",
"",
"Display the process audit (not available on Linux)",
);
opts.optflag("G", "", "Display the different group IDs");
opts.optflag("g", "", "Display the effective group ID as a number");
opts.optflag("n",
"",
"Display the name of the user or group ID for the -G, -g and -u options");
opts.optflag(
"n",
"",
"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", "", "Make the output human-readable");
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");
if gflag {
let id = possible_pw.map(|p| p.gid()).unwrap_or(if rflag {
getgid()
} else {
getegid()
});
println!("{}",
if nflag {
entries::gid2grp(id).unwrap_or(id.to_string())
} else {
id.to_string()
});
let id = possible_pw
.map(|p| p.gid())
.unwrap_or(if rflag { getgid() } else { getegid() });
println!(
"{}",
if nflag {
entries::gid2grp(id).unwrap_or(id.to_string())
} else {
id.to_string()
}
);
return 0;
}
if uflag {
let id = possible_pw.map(|p| p.uid()).unwrap_or(if rflag {
getuid()
} else {
geteuid()
});
println!("{}",
if nflag {
entries::uid2usr(id).unwrap_or(id.to_string())
} else {
id.to_string()
});
let id = possible_pw
.map(|p| p.uid())
.unwrap_or(if rflag { getuid() } else { geteuid() });
println!(
"{}",
if nflag {
entries::uid2usr(id).unwrap_or(id.to_string())
} else {
id.to_string()
}
);
return 0;
}
if matches.opt_present("G") {
println!("{}",
if nflag {
possible_pw.map(|p| p.belongs_to())
.unwrap_or(entries::get_groups().unwrap())
.iter()
.map(|&id| entries::gid2grp(id).unwrap())
.collect::<Vec<_>>()
.join(" ")
} else {
possible_pw.map(|p| p.belongs_to())
.unwrap_or(entries::get_groups().unwrap())
.iter()
.map(|&id| id.to_string())
.collect::<Vec<_>>()
.join(" ")
});
println!(
"{}",
if nflag {
possible_pw
.map(|p| p.belongs_to())
.unwrap_or(entries::get_groups().unwrap())
.iter()
.map(|&id| entries::gid2grp(id).unwrap())
.collect::<Vec<_>>()
.join(" ")
} else {
possible_pw
.map(|p| p.belongs_to())
.unwrap_or(entries::get_groups().unwrap())
.iter()
.map(|&id| id.to_string())
.collect::<Vec<_>>()
.join(" ")
}
);
return 0;
}
@ -180,8 +186,14 @@ pub fn uumain(args: Vec<String>) -> i32 {
fn pretty(possible_pw: Option<Passwd>) {
if let Some(p) = possible_pw {
print!("uid\t{}\ngroups\t", p.name());
println!("{}",
p.belongs_to().iter().map(|&gr| entries::gid2grp(gr).unwrap()).collect::<Vec<_>>().join(" "));
println!(
"{}",
p.belongs_to()
.iter()
.map(|&gr| entries::gid2grp(gr).unwrap())
.collect::<Vec<_>>()
.join(" ")
);
} else {
let login = cstr2cow!(getlogin() as *const _);
let rid = getuid();
@ -212,13 +224,15 @@ fn pretty(possible_pw: Option<Passwd>) {
}
}
println!("groups\t{}",
entries::get_groups()
.unwrap()
.iter()
.map(|&gr| entries::gid2grp(gr).unwrap())
.collect::<Vec<_>>()
.join(" "));
println!(
"groups\t{}",
entries::get_groups()
.unwrap()
.iter()
.map(|&gr| entries::gid2grp(gr).unwrap())
.collect::<Vec<_>>()
.join(" ")
);
}
}
@ -227,17 +241,19 @@ fn pline(possible_uid: Option<uid_t>) {
let uid = possible_uid.unwrap_or(getuid());
let pw = Passwd::locate(uid).unwrap();
println!("{}:{}:{}:{}:{}:{}:{}:{}:{}:{}",
pw.name(),
pw.user_passwd(),
pw.uid(),
pw.gid(),
pw.user_access_class(),
pw.passwd_change_time(),
pw.expiration(),
pw.user_info(),
pw.user_dir(),
pw.user_shell());
println!(
"{}:{}:{}:{}:{}:{}:{}:{}:{}:{}",
pw.name(),
pw.user_passwd(),
pw.uid(),
pw.gid(),
pw.user_access_class(),
pw.passwd_change_time(),
pw.expiration(),
pw.user_info(),
pw.user_dir(),
pw.user_shell()
);
}
#[cfg(target_os = "linux")]
@ -245,14 +261,16 @@ fn pline(possible_uid: Option<uid_t>) {
let uid = possible_uid.unwrap_or(getuid());
let pw = Passwd::locate(uid).unwrap();
println!("{}:{}:{}:{}:{}:{}:{}",
pw.name(),
pw.user_passwd(),
pw.uid(),
pw.gid(),
pw.user_info(),
pw.user_dir(),
pw.user_shell());
println!(
"{}:{}:{}:{}:{}:{}:{}",
pw.name(),
pw.user_passwd(),
pw.uid(),
pw.gid(),
pw.user_info(),
pw.user_dir(),
pw.user_shell()
);
}
#[cfg(target_os = "linux")]
@ -275,7 +293,9 @@ fn auditid() {
}
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();
@ -292,9 +312,12 @@ fn id_print(possible_pw: Option<Passwd>, p_euid: bool, p_egid: bool) {
print!(" egid={}({})", euid, entries::gid2grp(egid).unwrap());
}
println!(" groups={}",
groups.iter()
.map(|&gr| format!("{}({})", gr, entries::gid2grp(gr).unwrap()))
.collect::<Vec<_>>()
.join(","));
println!(
" groups={}",
groups
.iter()
.map(|&gr| format!("{}({})", gr, entries::gid2grp(gr).unwrap()))
.collect::<Vec<_>>()
.join(",")
);
}

View file

@ -33,7 +33,7 @@ pub struct Behaviour {
main_function: MainFunction,
specified_mode: Option<u32>,
suffix: String,
verbose: bool
verbose: bool,
}
#[derive(Clone, Eq, PartialEq)]
@ -41,7 +41,7 @@ pub enum MainFunction {
/// Create directories
Directory,
/// Install files to locations (primary functionality)
Standard
Standard,
}
impl Behaviour {
@ -49,7 +49,7 @@ impl Behaviour {
pub fn mode(&self) -> u32 {
match self.specified_mode {
Some(x) => x,
None => DEFAULT_MODE
None => DEFAULT_MODE,
}
}
}
@ -84,12 +84,8 @@ pub fn uumain(args: Vec<String>) -> i32 {
};
match behaviour.main_function {
MainFunction::Directory => {
directory(&paths[..], behaviour)
},
MainFunction::Standard => {
standard(&paths[..], behaviour)
}
MainFunction::Directory => directory(&paths[..], behaviour),
MainFunction::Standard => standard(&paths[..], behaviour),
}
}
@ -98,9 +94,12 @@ pub fn uumain(args: Vec<String>) -> i32 {
/// Returns a getopts::Options struct.
///
fn parse_opts(args: Vec<String>) -> getopts::Matches {
let syntax = format!("SOURCE DEST
{} SOURCE... DIRECTORY", NAME);
new_coreopts!(&syntax, SUMMARY, LONG_HELP)
let syntax = format!(
"SOURCE DEST
{} SOURCE... DIRECTORY",
NAME
);
new_coreopts!(&syntax, SUMMARY, LONG_HELP)
// TODO implement flag
.optflagopt("", "backup", "(unimplemented) make a backup of each existing destination\n \
file", "CONTROL")
@ -208,18 +207,19 @@ fn behaviour(matches: &getopts::Matches) -> Result<Behaviour, i32> {
let specified_mode: Option<u32> = if matches.opt_present("mode") {
match matches.opt_str("mode") {
Some(x) => {
match mode::parse(&x[..], considering_dir) {
Ok(y) => Some(y),
Err(err) => {
show_error!("Invalid mode string: {}", err);
return Err(1);
}
Some(x) => match mode::parse(&x[..], considering_dir) {
Ok(y) => Some(y),
Err(err) => {
show_error!("Invalid mode string: {}", err);
return Err(1);
}
},
None => {
show_error!("option '--mode' requires an argument\n \
Try '{} --help' for more information.", NAME);
show_error!(
"option '--mode' requires an argument\n \
Try '{} --help' for more information.",
NAME
);
return Err(1);
}
}
@ -231,8 +231,11 @@ fn behaviour(matches: &getopts::Matches) -> Result<Behaviour, i32> {
match matches.opt_str("suffix") {
Some(x) => x,
None => {
show_error!("option '--suffix' requires an argument\n\
Try '{} --help' for more information.", NAME);
show_error!(
"option '--suffix' requires an argument\n\
Try '{} --help' for more information.",
NAME
);
return Err(1);
}
}
@ -283,15 +286,18 @@ fn directory(paths: &[PathBuf], b: Behaviour) -> i32 {
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
/// created immediately
fn is_new_file_path(path: &Path) -> bool {
path.is_file() ||
! path.exists() && path.parent().map(|p| p.is_dir()).unwrap_or(true)
path.is_file() || !path.exists() && path.parent().map(|p| p.is_dir()).unwrap_or(true)
}
/// 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() {
Some(name) => target_dir.join(name),
None => {
show_error!("cannot stat {}: No such file or directory",
sourcepath.display());
show_error!(
"cannot stat {}: No such file or directory",
sourcepath.display()
);
all_successful = false;
continue;
@ -346,8 +354,12 @@ fn copy_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behaviour) -
if copy(sourcepath, &targetpath, b).is_err() {
all_successful = false;
}
};
if all_successful { 0 } else { 1 }
}
if all_successful {
0
} else {
1
}
}
/// 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
///
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.
@ -379,8 +395,12 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behaviour) -> Result<(), ()> {
let io_result = fs::copy(from, to);
if let Err(err) = io_result {
show_error!("install: cannot install {} to {}: {}",
from.display(), to.display(), err);
show_error!(
"install: cannot install {} to {}: {}",
from.display(),
to.display(),
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
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")
.version_message("display version and exit")
.arg(Arg::with_name("a")
.short("a")
.takes_value(true)
.possible_values(&["1", "2"])
.value_name("FILENUM")
.help("also print unpairable lines from file FILENUM, where
FILENUM is 1 or 2, corresponding to FILE1 or FILE2"))
.arg(Arg::with_name("e")
.short("e")
.takes_value(true)
.value_name("EMPTY")
.help("replace missing input fields with EMPTY"))
.arg(Arg::with_name("i")
.short("i")
.long("ignore-case")
.help("ignore differences in case when comparing fields"))
.arg(Arg::with_name("j")
.short("j")
.takes_value(true)
.value_name("FIELD")
.help("equivalent to '-1 FIELD -2 FIELD'"))
.arg(Arg::with_name("o")
.short("o")
.takes_value(true)
.value_name("FORMAT")
.help("obey FORMAT while constructing output line"))
.arg(Arg::with_name("t")
.short("t")
.takes_value(true)
.value_name("CHAR")
.help("use CHAR as input and output field separator"))
.arg(Arg::with_name("1")
.short("1")
.takes_value(true)
.value_name("FIELD")
.help("join on this FIELD of file 1"))
.arg(Arg::with_name("2")
.short("2")
.takes_value(true)
.value_name("FIELD")
.help("join on this FIELD of file 2"))
.arg(Arg::with_name("check-order")
.long("check-order")
.help("check that the input is correctly sorted, \
even if all input lines are pairable"))
.arg(Arg::with_name("nocheck-order")
.long("nocheck-order")
.help("do not check that the input is correctly sorted"))
.arg(Arg::with_name("file1")
.required(true)
.value_name("FILE1")
.hidden(true))
.arg(Arg::with_name("file2")
.required(true)
.value_name("FILE2")
.hidden(true))
.arg(
Arg::with_name("a")
.short("a")
.takes_value(true)
.possible_values(&["1", "2"])
.value_name("FILENUM")
.help(
"also print unpairable lines from file FILENUM, where
FILENUM is 1 or 2, corresponding to FILE1 or FILE2",
),
)
.arg(
Arg::with_name("e")
.short("e")
.takes_value(true)
.value_name("EMPTY")
.help("replace missing input fields with EMPTY"),
)
.arg(
Arg::with_name("i")
.short("i")
.long("ignore-case")
.help("ignore differences in case when comparing fields"),
)
.arg(
Arg::with_name("j")
.short("j")
.takes_value(true)
.value_name("FIELD")
.help("equivalent to '-1 FIELD -2 FIELD'"),
)
.arg(
Arg::with_name("o")
.short("o")
.takes_value(true)
.value_name("FORMAT")
.help("obey FORMAT while constructing output line"),
)
.arg(
Arg::with_name("t")
.short("t")
.takes_value(true)
.value_name("CHAR")
.help("use CHAR as input and output field separator"),
)
.arg(
Arg::with_name("1")
.short("1")
.takes_value(true)
.value_name("FIELD")
.help("join on this FIELD of file 1"),
)
.arg(
Arg::with_name("2")
.short("2")
.takes_value(true)
.value_name("FIELD")
.help("join on this FIELD of file 2"),
)
.arg(Arg::with_name("check-order").long("check-order").help(
"check that the input is correctly sorted, \
even if all input lines are pairable",
))
.arg(
Arg::with_name("nocheck-order")
.long("nocheck-order")
.help("do not check that the input is correctly sorted"),
)
.arg(
Arg::with_name("file1")
.required(true)
.value_name("FILE1")
.hidden(true),
)
.arg(
Arg::with_name("file2")
.required(true)
.value_name("FILE2")
.hidden(true),
)
.get_matches_from(args);
let keys = parse_field_number_option(matches.value_of("j"));

View file

@ -22,7 +22,7 @@ static SYNTAX: &'static str = "[options] <pid> [...]";
static SUMMARY: &'static str = "";
static LONG_HELP: &'static str = "";
static EXIT_OK: i32 = 0;
static EXIT_OK: i32 = 0;
static EXIT_ERR: i32 = 1;
#[derive(Clone, Copy)]
@ -36,7 +36,12 @@ pub fn uumain(args: Vec<String>) -> i32 {
let (args, obs_signal) = handle_obsolete(args);
let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
.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")
.parse(args);
@ -49,9 +54,16 @@ pub fn uumain(args: Vec<String>) -> i32 {
};
match mode {
Mode::Kill => return kill(&matches.opt_str("signal").unwrap_or(obs_signal.unwrap_or("9".to_owned())), matches.free),
Mode::Table => table(),
Mode::List => list(matches.opt_str("list")),
Mode::Kill => {
return kill(
&matches
.opt_str("signal")
.unwrap_or(obs_signal.unwrap_or("9".to_owned())),
matches.free,
)
}
Mode::Table => table(),
Mode::List => list(matches.opt_str("list")),
}
0
@ -62,7 +74,9 @@ fn handle_obsolete(mut args: Vec<String>) -> (Vec<String>, Option<String>) {
while i < args.len() {
// this is safe because slice is valid when it is referenced
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..];
match val.parse() {
Ok(num) => {
@ -71,7 +85,7 @@ fn handle_obsolete(mut args: Vec<String>) -> (Vec<String>, Option<String>) {
return (args, Some(val.to_owned()));
}
}
Err(_)=> break /* getopts will error out for us */
Err(_) => break, /* getopts will error out for us */
}
}
i += 1;
@ -89,10 +103,10 @@ fn table() {
}
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
if (idx+1) % 7 == 0 {
if (idx + 1) % 7 == 0 {
println!("");
}
}
@ -100,7 +114,9 @@ fn table() {
fn print_signal(signal_name_or_value: &str) {
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);
exit!(EXIT_OK as i32)
} else if signal_name_or_value == signal.value.to_string() {
@ -128,8 +144,8 @@ fn print_signals() {
fn list(arg: Option<String>) {
match arg {
Some(ref x) => print_signal(x),
None => print_signals(),
Some(ref x) => print_signal(x),
None => print_signals(),
};
}
@ -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 signal_value = match optional_signal_value {
Some(x) => x,
None => crash!(EXIT_ERR, "unknown signal name {}", signalname)
None => crash!(EXIT_ERR, "unknown signal name {}", signalname),
};
for pid in &pids {
match pid.parse::<usize>() {
@ -147,8 +163,8 @@ fn kill(signalname: &str, pids: std::vec::Vec<String>) -> i32 {
show_error!("{}", Error::last_os_error());
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

View file

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

View file

@ -9,14 +9,15 @@
* file that was distributed with this source code.
*/
#[macro_use]
extern crate uucore;
use std::fs;
use std::io::{BufRead, BufReader, Result, stdin};
#[cfg(unix)] use std::os::unix::fs::symlink;
#[cfg(windows)] use std::os::windows::fs::{symlink_file,symlink_dir};
use std::io::{stdin, BufRead, BufReader, Result};
#[cfg(unix)]
use std::os::unix::fs::symlink;
#[cfg(windows)]
use std::os::windows::fs::{symlink_dir, symlink_file};
use std::path::{Path, PathBuf};
static NAME: &'static str = "ln";
@ -58,23 +59,31 @@ pub enum BackupMode {
}
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... 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)
.optflag("b", "", "make a backup of each file that would otherwise be overwritten or removed")
.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("b", "", "make a backup of each file that would otherwise be overwritten or \
removed")
.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("i", "interactive", "prompt whether to remove existing destination files")
// 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("r", "relative", "create symbolic links relative to link location");
.optflag("s", "symbolic", "make symbolic links instead of hard links")
.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("v", "verbose", "print name of each linked file")
.parse(args);
@ -94,15 +103,19 @@ pub fn uumain(args: Vec<String>) -> i32 {
None => BackupMode::ExistingBackup,
Some(mode) => match &mode[..] {
"simple" | "never" => BackupMode::SimpleBackup,
"numbered" | "t" => BackupMode::NumberedBackup,
"numbered" | "t" => BackupMode::NumberedBackup,
"existing" | "nil" => BackupMode::ExistingBackup,
"none" | "off" => BackupMode::NoBackup,
"none" | "off" => BackupMode::NoBackup,
x => {
show_error!("invalid argument '{}' for 'backup method'\n\
Try '{} --help' for more information.", x, NAME);
show_error!(
"invalid argument '{}' for 'backup method'\n\
Try '{} --help' for more information.",
x,
NAME
);
return 1;
}
}
},
}
} else {
BackupMode::NoBackup
@ -112,8 +125,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
match matches.opt_str("suffix") {
Some(x) => x,
None => {
show_error!("option '--suffix' requires an argument\n\
Try '{} --help' for more information.", NAME);
show_error!(
"option '--suffix' requires an argument\n\
Try '{} --help' for more information.",
NAME
);
return 1;
}
}
@ -136,7 +152,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
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();
exec(&paths[..], &settings)
@ -144,7 +160,10 @@ pub fn uumain(args: Vec<String>) -> i32 {
fn exec(files: &[PathBuf], settings: &Settings) -> i32 {
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;
}
@ -161,18 +180,25 @@ fn exec(files: &[PathBuf], settings: &Settings) -> i32 {
let last_file = &PathBuf::from(files.last().unwrap());
if files.len() > 2 || last_file.is_dir() {
// 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
// specified we may have a wrong number of operands.
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;
}
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;
}
assert!(files.len() != 0);
@ -206,20 +232,30 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &PathBuf, settings: &Setting
}
}
None => {
show_error!("cannot stat '{}': No such file or directory",
srcpath.display());
show_error!(
"cannot stat '{}': No such file or directory",
srcpath.display()
);
all_successful = false;
continue;
}
};
if let Err(e) = link(srcpath, &targetpath, settings) {
show_error!("cannot link '{}' to '{}': {}",
targetpath.display(), srcpath.display(), e);
show_error!(
"cannot link '{}' to '{}': {}",
targetpath.display(),
srcpath.display(),
e
);
all_successful = false;
}
}
if all_successful { 0 } else { 1 }
if all_successful {
0
} else {
1
}
}
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() {
match settings.overwrite {
OverwriteMode::NoClobber => {},
OverwriteMode::NoClobber => {}
OverwriteMode::Interactive => {
print!("{}: overwrite '{}'? ", NAME, dst.display());
if !read_yes() {
return Ok(());
}
try!(fs::remove_file(dst))
},
OverwriteMode::Force => {
try!(fs::remove_file(dst))
}
OverwriteMode::Force => try!(fs::remove_file(dst)),
};
backup_path = match settings.backup {
BackupMode::NoBackup => None,
BackupMode::SimpleBackup => Some(simple_backup_path(dst, &settings.suffix)),
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 {
try!(fs::rename(dst, p));
@ -261,7 +295,7 @@ fn link(src: &PathBuf, dst: &PathBuf, settings: &Settings) -> Result<()> {
print!("'{}' -> '{}'", dst.display(), src.display());
match backup_path {
Some(path) => println!(" (backup: '{}')", path.display()),
None => println!("")
None => println!(""),
}
}
Ok(())
@ -272,9 +306,9 @@ fn read_yes() -> bool {
match BufReader::new(stdin()).read_line(&mut s) {
Ok(_) => match s.char_indices().nth(0) {
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)]
pub fn symlink<P: AsRef<Path>>(src: P, dst: P) -> Result<()> {
if src.as_ref().is_dir()
{
symlink_dir(src,dst)
}
else
{
symlink_file(src,dst)
if src.as_ref().is_dir() {
symlink_dir(src, dst)
} else {
symlink_file(src, dst)
}
}
pub fn is_symlink<P: AsRef<Path>>(path: P) -> bool {
match fs::symlink_metadata(path) {
Ok(m) => m.file_type().is_symlink(),
Err(_) => false
Err(_) => false,
}
}

View file

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

View file

@ -38,7 +38,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
let matches = match opts.parse(&args[1..]) {
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") {
@ -78,7 +78,10 @@ fn print_help(opts: &getopts::Options) {
println!("{} {}", NAME, VERSION);
println!("");
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() {
Some(parent) => {
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;
} else {
status |= mkdir(path, mode, verbose);
}
},
}
None => {
status |= mkdir(path, mode, verbose);
}
@ -134,11 +140,16 @@ fn mkdir(path: &Path, mode: u16, verbose: bool) -> i32 {
use std::ffi::CString;
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;
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;
}
0

View file

@ -25,7 +25,12 @@ static VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub fn uumain(args: Vec<String>) -> i32 {
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("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() {
let msg = format!("{0} {1}
let msg = format!(
"{0} {1}
Usage:
{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));
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") {
Some(m) => match usize::from_str_radix(&m, 8) {
Ok(m) => m,
Err(e)=> {
Err(e) => {
show_error!("invalid mode: {}", e);
return 1;
}
@ -67,9 +75,18 @@ Create a FIFO with the given name.", NAME, VERSION);
let mut exit_status = 0;
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 {
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;
}
}

View file

@ -16,8 +16,8 @@ mod parsemode;
#[macro_use]
extern crate uucore;
use libc::{mode_t, dev_t};
use libc::{S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP, S_IROTH, S_IWOTH, S_IFIFO, S_IFBLK, S_IFCHR};
use libc::{dev_t, mode_t};
use libc::{S_IFBLK, S_IFCHR, S_IFIFO, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR};
use 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)]
fn makedev(maj: u64, min: u64) -> dev_t {
// pick up from <sys/sysmacros.h>
((min & 0xff) | ((maj & 0xfff) << 8) | (((min & !0xff)) << 12) |
(((maj & !0xfff)) << 32)) as dev_t
((min & 0xff) | ((maj & 0xfff) << 8) | (((min & !0xff)) << 12) | (((maj & !0xfff)) << 32))
as dev_t
}
#[cfg(windows)]
@ -51,10 +51,12 @@ pub fn uumain(args: Vec<String>) -> i32 {
// Linux-specific options, not implemented
// 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("m",
"mode",
"set file permission bits to MODE, not a=rw - umask",
"MODE");
opts.optopt(
"m",
"mode",
"set file permission bits to MODE, not a=rw - umask",
"MODE",
);
opts.optflag("", "help", "display this help 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") {
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.
-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
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;
}
@ -124,7 +128,10 @@ for details about the options it supports.", NAME);
// Only check the first character, to allow mnemonic usage like
// '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 args.len() > 2 {
@ -183,7 +190,7 @@ for details about the options it supports.", NAME);
}
if ret == -1 {
let c_str = CString::new(format!("{}: {}", NAME, matches.free[0]).as_str())
.expect("Failed to convert to CString");
.expect("Failed to convert to CString");
unsafe {
libc::perror(c_str.as_ptr());
}

View file

@ -1,5 +1,5 @@
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;

View file

@ -10,14 +10,14 @@
//
extern crate getopts;
extern crate tempfile;
extern crate rand;
extern crate tempfile;
#[macro_use]
extern crate uucore;
use std::env;
use std::path::{PathBuf, is_separator};
use std::path::{is_separator, PathBuf};
use std::mem::forget;
use std::iter;
@ -30,22 +30,37 @@ static NAME: &'static str = "mktemp";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
static DEFAULT_TEMPLATE: &'static str = "tmp.XXXXXXXXXX";
pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = getopts::Options::new();
opts.optflag("d", "directory", "Make a directory instead of a file");
opts.optflag("u",
"dry-run",
"do not create anything; merely print a name (unsafe)");
opts.optflag(
"u",
"dry-run",
"do not create anything; merely print a name (unsafe)",
);
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("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");
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(
"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
// 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("", "version", "print the version and exit");
// >> early return options
let matches = match opts.parse(&args[1..]) {
Ok(m) => m,
@ -71,7 +86,6 @@ pub fn uumain(args: Vec<String>) -> i32 {
let suffix_opt = matches.opt_str("suffix");
let suppress_file_err = matches.opt_present("quiet");
let template = if matches.free.is_empty() {
DEFAULT_TEMPLATE
} else {
@ -79,19 +93,19 @@ pub fn uumain(args: Vec<String>) -> i32 {
};
let (prefix, rand, suffix) = match parse_template(template) {
Some((p, r, s)) => {
match suffix_opt {
Some(suf) => {
if s == "" {
(p, r, suf)
} else {
crash!(1,
"Template should end with 'X' when you specify suffix option.")
}
Some((p, r, s)) => match suffix_opt {
Some(suf) => {
if s == "" {
(p, r, suf)
} else {
crash!(
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()),
};
@ -103,11 +117,13 @@ pub fn uumain(args: Vec<String>) -> i32 {
crash!(1, "suffix cannot contain any path separators");
}
let tmpdir = match matches.opt_str("tmpdir") {
Some(s) => {
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;
}
PathBuf::from(s)
@ -120,14 +136,15 @@ pub fn uumain(args: Vec<String>) -> i32 {
} else {
exec(tmpdir, prefix, rand, &suffix, make_dir, suppress_file_err)
}
}
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.
If TEMPLATE is not specified, use {}, and --tmpdir is implied",
DEFAULT_TEMPLATE);
DEFAULT_TEMPLATE
);
println!("{} {}", NAME, VERSION);
println!("SYNOPSIS");
@ -174,7 +191,14 @@ pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) ->
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 {
match tempdir::new_in(&tmpdir, prefix, rand, suffix) {
Ok(ref f) => {
@ -206,9 +230,7 @@ fn exec(tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool
}
};
let tmpname = tmpfile.path()
.to_string_lossy()
.to_string();
let tmpname = tmpfile.path().to_string_lossy().to_string();
println!("{}", tmpname);

View file

@ -1,7 +1,7 @@
// Mainly taken from crate `tempdir`
extern crate rand;
use rand::{Rng, thread_rng};
use rand::{thread_rng, Rng};
use std::io::Result as IOResult;
use std::io::{Error, ErrorKind};
@ -24,8 +24,12 @@ fn create_dir<P: AsRef<Path>>(path: P) -> IOResult<()> {
::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();
for _ in 0..NUM_RETRIES {
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,
"too many temporary directories already exist"))
Err(Error::new(
ErrorKind::AlreadyExists,
"too many temporary directories already exist",
))
}

View file

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

View file

@ -16,7 +16,7 @@ extern crate uucore;
use std::fs;
use std::env;
use std::io::{BufRead, BufReader, Result, stdin};
use std::io::{stdin, BufRead, BufReader, Result};
use std::path::{Path, PathBuf};
static NAME: &'static str = "mv";
@ -50,29 +50,37 @@ pub enum BackupMode {
pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = getopts::Options::new();
opts.optflagopt("",
"backup",
"make a backup of each existing destination file",
"CONTROL");
opts.optflagopt(
"",
"backup",
"make a backup of each existing destination file",
"CONTROL",
);
opts.optflag("b", "", "like --backup but does not accept an argument");
opts.optflag("f", "force", "do not prompt before overwriting");
opts.optflag("i", "interactive", "prompt before override");
opts.optflag("n", "no-clobber", "do not overwrite an existing file");
opts.optflag("",
"strip-trailing-slashes",
"remove any trailing slashes from each SOURCE\n \
argument");
opts.optflag(
"",
"strip-trailing-slashes",
"remove any trailing slashes from each SOURCE\n \
argument",
);
opts.optopt("S", "suffix", "override the usual backup suffix", "SUFFIX");
opts.optopt("t",
"target-directory",
"move all SOURCE arguments into DIRECTORY",
"DIRECTORY");
opts.optopt(
"t",
"target-directory",
"move all SOURCE arguments into DIRECTORY",
"DIRECTORY",
);
opts.optflag("T", "no-target-directory", "treat DEST as a normal file");
opts.optflag("u",
"update",
"move only when the SOURCE file is newer\n \
than the destination file or when the\n \
destination file is missing");
opts.optflag(
"u",
"update",
"move only when the SOURCE file is newer\n \
than the destination file or when the\n \
destination file is missing",
);
opts.optflag("v", "verbose", "explain what is being done");
opts.optflag("h", "help", "display this help 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);
if overwrite_mode == OverwriteMode::NoClobber && backup_mode != BackupMode::NoBackup {
show_error!("options --backup and --no-clobber are mutually exclusive\n\
Try '{} --help' for more information.",
NAME);
show_error!(
"options --backup and --no-clobber are mutually exclusive\n\
Try '{} --help' for more information.",
NAME
);
return 1;
}
@ -158,20 +168,21 @@ fn determine_backup_mode(matches: &getopts::Matches) -> BackupMode {
} else if matches.opt_present("backup") {
match matches.opt_str("backup") {
None => BackupMode::SimpleBackup,
Some(mode) => {
match &mode[..] {
"simple" | "never" => BackupMode::SimpleBackup,
"numbered" | "t" => BackupMode::NumberedBackup,
"existing" | "nil" => BackupMode::ExistingBackup,
"none" | "off" => BackupMode::NoBackup,
x => {
crash!(1, "invalid argument {} for backup type\n\
Try '{} --help' for more information.",
x,
NAME);
}
Some(mode) => match &mode[..] {
"simple" | "never" => BackupMode::SimpleBackup,
"numbered" | "t" => BackupMode::NumberedBackup,
"existing" | "nil" => BackupMode::ExistingBackup,
"none" | "off" => BackupMode::NoBackup,
x => {
crash!(
1,
"invalid argument {} for backup type\n\
Try '{} --help' for more information.",
x,
NAME
);
}
}
},
}
} else {
BackupMode::NoBackup
@ -183,9 +194,12 @@ fn determine_backup_suffix(backup_mode: BackupMode, matches: &getopts::Matches)
match matches.opt_str("suffix") {
Some(x) => x,
None => {
crash!(1, "option '--suffix' requires an argument\n\
Try '{} --help' for more information.",
NAME);
crash!(
1,
"option '--suffix' requires an argument\n\
Try '{} --help' for more information.",
NAME
);
}
}
} else {
@ -198,13 +212,13 @@ fn determine_backup_suffix(backup_mode: BackupMode, matches: &getopts::Matches)
}
fn help(usage: &str) {
println!("{0} {1}\n\n\
Usage: {0} SOURCE DEST\n \
or: {0} SOURCE... DIRECTORY\n\n\
{2}",
NAME,
VERSION,
usage);
println!(
"{0} {1}\n\n\
Usage: {0} SOURCE DEST\n \
or: {0} SOURCE... DIRECTORY\n\n\
{2}",
NAME, VERSION, usage
);
}
fn exec(files: &[PathBuf], b: Behaviour) -> i32 {
@ -213,25 +227,31 @@ fn exec(files: &[PathBuf], b: Behaviour) -> i32 {
}
match files.len() {
0 | 1 => {
show_error!("missing file operand\n\
Try '{} --help' for more information.",
NAME);
show_error!(
"missing file operand\n\
Try '{} --help' for more information.",
NAME
);
return 1;
}
2 => {
let source = &files[0];
let target = &files[1];
if !source.exists() {
show_error!("cannot stat {}: No such file or directory",
source.display());
show_error!(
"cannot stat {}: No such file or directory",
source.display()
);
return 1;
}
if target.is_dir() {
if b.no_target_dir {
if !source.is_dir() {
show_error!("cannot overwrite directory {} with non-directory",
target.display());
show_error!(
"cannot overwrite directory {} with non-directory",
target.display()
);
return 1;
}
@ -254,10 +274,12 @@ fn exec(files: &[PathBuf], b: Behaviour) -> i32 {
}
_ => {
if b.no_target_dir {
show_error!("mv: extra operand {}\n\
Try '{} --help' for more information.",
files[2].display(),
NAME);
show_error!(
"mv: extra operand {}\n\
Try '{} --help' for more information.",
files[2].display(),
NAME
);
return 1;
}
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() {
Some(name) => target_dir.join(name),
None => {
show_error!("cannot stat {}: No such file or directory",
sourcepath.display());
show_error!(
"cannot stat {}: No such file or directory",
sourcepath.display()
);
all_successful = false;
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) {
show_error!("mv: cannot move {} to {}: {}",
sourcepath.display(),
targetpath.display(),
e);
show_error!(
"mv: cannot move {} to {}: {}",
sourcepath.display(),
targetpath.display(),
e
);
all_successful = false;
}
}
@ -327,7 +353,8 @@ fn rename(from: &PathBuf, to: &PathBuf, b: &Behaviour) -> Result<()> {
}
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(());
}
}
@ -348,12 +375,10 @@ fn rename(from: &PathBuf, to: &PathBuf, b: &Behaviour) -> Result<()> {
fn read_yes() -> bool {
let mut s = String::new();
match BufReader::new(stdin()).read_line(&mut s) {
Ok(_) => {
match s.chars().nth(0) {
Some(x) => x == 'y' || x == 'Y',
_ => false,
}
}
Ok(_) => match s.chars().nth(0) {
Some(x) => x == 'y' || x == 'Y',
_ => 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.
const PRIO_PROCESS: c_int = 0;
extern {
extern "C" {
fn getpriority(which: c_int, who: 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 {
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("V", "version", "output version information and exit");
@ -51,7 +56,8 @@ pub fn uumain(args: Vec<String>) -> i32 {
}
if matches.opt_present("help") {
let msg = format!("{0} {1}
let msg = format!(
"{0} {1}
Usage:
{0} [OPTIONS] [COMMAND [ARGS]]
@ -59,7 +65,9 @@ Usage:
Run COMMAND with an adjusted niceness, which affects process scheduling.
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
process).", NAME, VERSION);
process).",
NAME, VERSION
);
print!("{}", opts.usage(&msg));
return 0;
@ -74,18 +82,21 @@ process).", NAME, VERSION);
let adjustment = match matches.opt_str("adjustment") {
Some(nstr) => {
if matches.free.is_empty() {
show_error!("A command must be given with an adjustment.
Try \"{} --help\" for more information.", args[0]);
show_error!(
"A command must be given with an adjustment.
Try \"{} --help\" for more information.",
args[0]
);
return 125;
}
match nstr.parse() {
Ok(num) => num,
Err(e)=> {
Err(e) => {
show_error!("\"{}\" is not a valid number: {}", nstr, e);
return 125;
}
}
},
}
None => {
if matches.free.is_empty() {
println!("{}", niceness);
@ -96,16 +107,28 @@ process).", NAME, VERSION);
};
niceness += adjustment;
unsafe { setpriority(PRIO_PROCESS, 0, niceness); }
unsafe {
setpriority(PRIO_PROCESS, 0, niceness);
}
if Error::last_os_error().raw_os_error().unwrap() != 0 {
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();
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());
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![];
settings.renumber = !opts.opt_present("p");
match opts.opt_str("s") {
None => {},
Some(val) => { settings.number_separator = val; }
}
match opts.opt_str("n") {
None => {},
Some(val) => match val.as_ref() {
"ln" => { settings.number_format = ::NumberFormat::Left; },
"rn" => { settings.number_format = ::NumberFormat::Right; },
"rz" => { settings.number_format = ::NumberFormat::RightZero; },
_ => { errs.push(String::from("Illegal value for -n")); },
None => {}
Some(val) => {
settings.number_separator = val;
}
}
match opts.opt_str("n") {
None => {}
Some(val) => match val.as_ref() {
"ln" => {
settings.number_format = ::NumberFormat::Left;
}
"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") {
None => {},
None => {}
Some(val) => {
let chars: Vec<char> = val.chars().collect();
match parse_style(&chars) {
Ok(s) => { settings.body_numbering = s; }
Err(message) => { errs.push(message); }
Ok(s) => {
settings.body_numbering = s;
}
Err(message) => {
errs.push(message);
}
}
}
}
match opts.opt_str("f") {
None => {},
None => {}
Some(val) => {
let chars: Vec<char> = val.chars().collect();
match parse_style(&chars) {
Ok(s) => { settings.footer_numbering = s; }
Err(message) => { errs.push(message); }
Ok(s) => {
settings.footer_numbering = s;
}
Err(message) => {
errs.push(message);
}
}
}
}
match opts.opt_str("h") {
None => {},
None => {}
Some(val) => {
let chars: Vec<char> = val.chars().collect();
match parse_style(&chars) {
Ok(s) => { settings.header_numbering = s; }
Err(message) => { errs.push(message); }
Ok(s) => {
settings.header_numbering = s;
}
Err(message) => {
errs.push(message);
}
}
}
}
@ -74,10 +96,10 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
Some(val) => {
let conv: Option<u64> = val.parse().ok();
match conv {
None => {
errs.push(String::from("Illegal value for -i"));
}
Some(num) => { settings.line_increment = num }
None => {
errs.push(String::from("Illegal value for -i"));
}
Some(num) => settings.line_increment = num,
}
}
}
@ -86,10 +108,10 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
Some(val) => {
let conv: Option<usize> = val.parse().ok();
match conv {
None => {
errs.push(String::from("Illegal value for -w"));
}
Some(num) => { settings.number_width = num }
None => {
errs.push(String::from("Illegal value for -w"));
}
Some(num) => settings.number_width = num,
}
}
}
@ -98,10 +120,10 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
Some(val) => {
let conv: Option<u64> = val.parse().ok();
match conv {
None => {
errs.push(String::from("Illegal value for -v"));
}
Some(num) => { settings.starting_line_number = num }
None => {
errs.push(String::from("Illegal value for -v"));
}
Some(num) => settings.starting_line_number = num,
}
}
}
@ -110,10 +132,10 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
Some(val) => {
let conv: Option<u64> = val.parse().ok();
match conv {
None => {
errs.push(String::from("Illegal value for -l"));
}
Some(num) => { settings.join_blank_lines = num }
None => {
errs.push(String::from("Illegal value for -l"));
}
Some(num) => settings.join_blank_lines = num,
}
}
}

View file

@ -13,14 +13,14 @@
extern crate aho_corasick;
extern crate getopts;
extern crate memchr;
extern crate regex_syntax;
extern crate regex;
extern crate regex_syntax;
#[macro_use]
extern crate uucore;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, stdin};
use std::io::{stdin, BufRead, BufReader, Read};
use std::iter::repeat;
use std::path::Path;
@ -49,7 +49,7 @@ pub struct Settings {
number_format: NumberFormat,
renumber: bool,
// The string appended to each line number output.
number_separator: String
number_separator: String,
}
// NumberingStyle stores which lines are to be numbered.
@ -62,7 +62,7 @@ enum NumberingStyle {
NumberForAll,
NumberForNonEmpty,
NumberForNone,
NumberForRegularExpression(regex::Regex)
NumberForRegularExpression(regex::Regex),
}
// NumberFormat specifies how line numbers are output within their allocated
@ -77,17 +77,71 @@ enum NumberFormat {
pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = getopts::Options::new();
opts.optopt("b", "body-numbering", "use STYLE for numbering body lines", "STYLE");
opts.optopt("d", "section-delimiter", "use CC for separating logical pages", "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.optopt(
"b",
"body-numbering",
"use STYLE for numbering body lines",
"STYLE",
);
opts.optopt(
"d",
"section-delimiter",
"use CC for separating logical pages",
"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("V", "version", "version");
@ -107,11 +161,11 @@ pub fn uumain(args: Vec<String>) -> i32 {
};
let given_options = match opts.parse(&args[1..]) {
Ok (m) => { m }
Ok(m) => m,
Err(f) => {
show_error!("{}", f);
print_usage(&opts);
return 1
return 1;
}
};
@ -119,7 +173,10 @@ pub fn uumain(args: Vec<String>) -> i32 {
print_usage(&opts);
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
// 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
// regular files, and then read from stdin last.
read_stdin = true;
continue
continue;
}
let path = Path::new(file);
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.
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 mut line_no = settings.starting_line_number;
// 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 fill_char = match settings.number_format {
NumberFormat::RightZero => '0',
_ => ' '
_ => ' ',
};
// Initially, we use the body's line counting settings
let mut regex_filter = match settings.body_numbering {
NumberingStyle::NumberForRegularExpression(ref re) => re,
_ => &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()) {
// Sanitize the string. We want to print the newline ourselves.
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
// a header) or the current char does not form part of
// a new group, then this line is not a segment indicator.
if matched_groups >= 3
|| settings.section_delimiter[if odd { 1 } else { 0 }] != c {
if matched_groups >= 3 || settings.section_delimiter[if odd { 1 } else { 0 }] != c {
matched_groups = 0;
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);
}
&settings.header_numbering
},
1 => {
&settings.footer_numbering
},
}
1 => &settings.footer_numbering,
// The only option left is 2, but rust wants
// a catch-all here.
_ => {
&settings.body_numbering
}
_ => &settings.body_numbering,
} {
NumberingStyle::NumberForAll => {
line_filter = pass_all;
},
}
NumberingStyle::NumberForNonEmpty => {
line_filter = pass_nonempty;
},
}
NumberingStyle::NumberForNone => {
line_filter = pass_none;
}
@ -267,7 +319,8 @@ fn nl<T: Read> (reader: &mut BufReader<T>, settings: &Settings) {
empty_line_count = 0;
}
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
// want to print one in the first place, or it is a blank
// 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();
match settings.number_format {
NumberFormat::Left => {
println!("{1}{0}{2}{3}", fill, line_no, settings.number_separator, line)
},
_ => {
println!("{0}{1}{2}{3}", fill, line_no, settings.number_separator, line)
}
NumberFormat::Left => println!(
"{1}{0}{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
// line.
@ -301,7 +356,6 @@ fn nl<T: Read> (reader: &mut BufReader<T>, settings: &Settings) {
line_no_threshold *= 10;
line_no_width += 1;
}
}
}

View file

@ -15,8 +15,8 @@ extern crate libc;
#[macro_use]
extern crate uucore;
use libc::{c_char, signal, dup2, execvp};
use libc::{SIG_IGN, SIGHUP};
use libc::{c_char, execvp, signal, dup2};
use libc::{SIGHUP, SIG_IGN};
use std::ffi::CString;
use std::fs::{File, OpenOptions};
use std::io::Error;
@ -29,12 +29,14 @@ static NAME: &'static str = "nohup";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
#[cfg(target_os = "macos")]
extern {
extern "C" {
fn _vprocmgr_detach_from_console(flags: u32) -> *const libc::c_int;
}
#[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 {
let mut opts = getopts::Options::new();
@ -47,37 +49,47 @@ pub fn uumain(args: Vec<String>) -> i32 {
Err(f) => {
show_error!("{}", f);
show_usage(&opts);
return 1
return 1;
}
};
if matches.opt_present("V") { println!("{} {}", NAME, VERSION); return 0 }
if matches.opt_present("h") { show_usage(&opts); return 0 }
if matches.opt_present("V") {
println!("{} {}", NAME, VERSION);
return 0;
}
if matches.opt_present("h") {
show_usage(&opts);
return 0;
}
if matches.free.is_empty() {
show_error!("Missing operand: COMMAND");
println!("Try `{} --help` for more information.", NAME);
return 1
return 1;
}
replace_fds();
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();
args.push(std::ptr::null());
unsafe { execvp(args[0], args.as_mut_ptr())}
unsafe { execvp(args[0], args.as_mut_ptr()) }
}
fn replace_fds() {
if is_stdin_interactive() {
let new_stdin = match File::open(Path::new("/dev/null")) {
Ok(t) => t,
Err(e) => {
crash!(2, "Cannot replace STDIN: {}", e)
}
Err(e) => crash!(2, "Cannot replace STDIN: {}", e),
};
if unsafe { dup2(new_stdin.as_raw_fd(), 0) } != 0 {
crash!(2, "Cannot replace STDIN: {}", Error::last_os_error())
@ -101,33 +113,42 @@ fn replace_fds() {
}
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) => {
show_warning!("Output is redirected to: nohup.out");
t
},
}
Err(e) => {
let home = match env::var("HOME") {
Err(_) => crash!(2, "Cannot replace STDOUT: {}", e),
Ok(h) => h
Ok(h) => h,
};
let mut homeout = PathBuf::from(home);
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) => {
show_warning!("Output is redirected to: {:?}", homeout);
t
},
Err(e) => {
crash!(2, "Cannot replace STDOUT: {}", e)
}
Err(e) => crash!(2, "Cannot replace STDOUT: {}", e),
}
}
}
}
fn show_usage(opts: &getopts::Options) {
let msg = format!("{0} {1}
let msg = format!(
"{0} {1}
Usage:
{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 output is terminal, it'll be appended to nohup.out instead,
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));
}

View file

@ -29,14 +29,17 @@ pub const _SC_NPROCESSORS_CONF: libc::c_int = 57;
#[cfg(target_os = "netbsd")]
pub const _SC_NPROCESSORS_CONF: libc::c_int = 1001;
static NAME: &'static str = "nproc";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub fn uumain(args: Vec<String>) -> i32 {
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.optflag("h", "help", "display this help 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") {
let msg = format!("{0} {1}
let msg = format!(
"{0} {1}
Usage:
{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));
return 0;
@ -74,16 +80,16 @@ Print the number of cores available to the current process.", NAME, VERSION);
return 1;
}
},
None => 0
None => 0,
};
if !matches.opt_present("all") {
ignore += match env::var("OMP_NUM_THREADS") {
Ok(threadstr) => match threadstr.parse() {
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
}
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd"))]
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd", target_os = "netbsd"))]
fn num_cpus_all() -> usize {
let nprocs = unsafe { libc::sysconf(_SC_NPROCESSORS_CONF) };
if nprocs == 1 {
@ -113,14 +116,16 @@ fn num_cpus_all() -> usize {
// However, we want to guarantee that `nproc --all` >= `nproc`.
num_cpus::get()
} 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.
#[cfg(not(any(target_os = "linux",
target_os = "macos",
target_os = "freebsd",
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "freebsd",
target_os = "netbsd")))]
fn num_cpus_all() -> usize {
num_cpus::get()

View file

@ -75,23 +75,21 @@ fn parse_suffix(s: String) -> Result<(f64, Option<Suffix>)> {
Some('T') => Ok((Some(Suffix::T), 1)),
Some('P') => Ok((Some(Suffix::P), 1)),
Some('E') => Ok((Some(Suffix::E), 1)),
Some('i') => {
match iter.next_back() {
Some('K') => Ok((Some(Suffix::Ki), 2)),
Some('M') => Ok((Some(Suffix::Mi), 2)),
Some('G') => Ok((Some(Suffix::Gi), 2)),
Some('T') => Ok((Some(Suffix::Ti), 2)),
Some('P') => Ok((Some(Suffix::Pi), 2)),
Some('E') => Ok((Some(Suffix::Ei), 2)),
_ => Err("Failed to parse suffix"),
}
}
Some('i') => match iter.next_back() {
Some('K') => Ok((Some(Suffix::Ki), 2)),
Some('M') => Ok((Some(Suffix::Mi), 2)),
Some('G') => Ok((Some(Suffix::Gi), 2)),
Some('T') => Ok((Some(Suffix::Ti), 2)),
Some('P') => Ok((Some(Suffix::Pi), 2)),
Some('E') => Ok((Some(Suffix::Ei), 2)),
_ => Err("Failed to parse suffix"),
},
_ => Ok((None, 0)),
}?;
let number = s[..s.len() - suffix_len].parse::<f64>().map_err(|err| {
err.to_string()
})?;
let number = s[..s.len() - suffix_len]
.parse::<f64>()
.map_err(|err| err.to_string())?;
Ok((number, suffix))
}
@ -120,37 +118,35 @@ struct NumfmtOptions {
fn remove_suffix(i: f64, s: Option<Suffix>, u: &Unit) -> Result<f64> {
match (s, u) {
(None, _) => Ok(i),
(Some(Suffix::K), &Unit::Auto) |
(Some(Suffix::K), &Unit::Si) => Ok(i * 1000.),
(Some(Suffix::M), &Unit::Auto) |
(Some(Suffix::M), &Unit::Si) => Ok(i * 1000_000.),
(Some(Suffix::G), &Unit::Auto) |
(Some(Suffix::G), &Unit::Si) => Ok(i * 1000_000_000.),
(Some(Suffix::T), &Unit::Auto) |
(Some(Suffix::T), &Unit::Si) => Ok(i * 1000_000_000_000.),
(Some(Suffix::P), &Unit::Auto) |
(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::K), &Unit::Auto) | (Some(Suffix::K), &Unit::Si) => Ok(i * 1000.),
(Some(Suffix::M), &Unit::Auto) | (Some(Suffix::M), &Unit::Si) => Ok(i * 1000_000.),
(Some(Suffix::G), &Unit::Auto) | (Some(Suffix::G), &Unit::Si) => Ok(i * 1000_000_000.),
(Some(Suffix::T), &Unit::Auto) | (Some(Suffix::T), &Unit::Si) => Ok(i * 1000_000_000_000.),
(Some(Suffix::P), &Unit::Auto) | (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::IecI) |
(Some(Suffix::K), &Unit::Iec) => Ok(i * 1024.),
(Some(Suffix::Mi), &Unit::Auto) |
(Some(Suffix::Mi), &Unit::IecI) |
(Some(Suffix::M), &Unit::Iec) => Ok(i * 1048576.),
(Some(Suffix::Gi), &Unit::Auto) |
(Some(Suffix::Gi), &Unit::IecI) |
(Some(Suffix::G), &Unit::Iec) => Ok(i * 1073741824.),
(Some(Suffix::Ti), &Unit::Auto) |
(Some(Suffix::Ti), &Unit::IecI) |
(Some(Suffix::T), &Unit::Iec) => Ok(i * 1099511627776.),
(Some(Suffix::Pi), &Unit::Auto) |
(Some(Suffix::Pi), &Unit::IecI) |
(Some(Suffix::P), &Unit::Iec) => Ok(i * 1125899906842624.),
(Some(Suffix::Ei), &Unit::Auto) |
(Some(Suffix::Ei), &Unit::IecI) |
(Some(Suffix::E), &Unit::Iec) => Ok(i * 1152921504606846976.),
(Some(Suffix::Ki), &Unit::Auto)
| (Some(Suffix::Ki), &Unit::IecI)
| (Some(Suffix::K), &Unit::Iec) => Ok(i * 1024.),
(Some(Suffix::Mi), &Unit::Auto)
| (Some(Suffix::Mi), &Unit::IecI)
| (Some(Suffix::M), &Unit::Iec) => Ok(i * 1048576.),
(Some(Suffix::Gi), &Unit::Auto)
| (Some(Suffix::Gi), &Unit::IecI)
| (Some(Suffix::G), &Unit::Iec) => Ok(i * 1073741824.),
(Some(Suffix::Ti), &Unit::Auto)
| (Some(Suffix::Ti), &Unit::IecI)
| (Some(Suffix::T), &Unit::Iec) => Ok(i * 1099511627776.),
(Some(Suffix::Pi), &Unit::Auto)
| (Some(Suffix::Pi), &Unit::IecI)
| (Some(Suffix::P), &Unit::Iec) => Ok(i * 1125899906842624.),
(Some(Suffix::Ei), &Unit::Auto)
| (Some(Suffix::Ei), &Unit::IecI)
| (Some(Suffix::E), &Unit::Iec) => Ok(i * 1152921504606846976.),
(_, _) => Err("This suffix is unsupported for specified unit".to_owned()),
}
@ -163,49 +159,38 @@ fn transform_from(s: String, unit: &Unit) -> Result<String> {
fn consider_suffix(i: f64, u: &Unit) -> Result<(f64, Option<Suffix>)> {
match *u {
Unit::Si => {
match i {
_ if i < 1000. => Ok((i, None)),
_ 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_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_000. => Ok(
(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),
)),
_ => Err("Number is too big and unsupported".to_owned()),
Unit::Si => match i {
_ if i < 1000. => Ok((i, None)),
_ 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_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_000. => Ok((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)))
}
}
Unit::Iec => {
match i {
_ if i < 1024. => Ok((i, None)),
_ if i < 1048576. => Ok((i / 1024., Some(Suffix::K))),
_ if i < 1073741824. => Ok((i / 1048576., Some(Suffix::M))),
_ if i < 1099511627776. => Ok((i / 1073741824., Some(Suffix::G))),
_ if i < 1125899906842624. => Ok((i / 1099511627776., Some(Suffix::T))),
_ if i < 1152921504606846976. => Ok((i / 1125899906842624., Some(Suffix::P))),
_ if i < 1180591620717411303424. => Ok((i / 1152921504606846976., Some(Suffix::E))),
_ => Err("Number is too big and unsupported".to_owned()),
}
}
Unit::IecI => {
match i {
_ if i < 1024. => Ok((i, None)),
_ if i < 1048576. => Ok((i / 1024., Some(Suffix::Ki))),
_ if i < 1073741824. => Ok((i / 1048576., Some(Suffix::Mi))),
_ if i < 1099511627776. => Ok((i / 1073741824., Some(Suffix::Gi))),
_ if i < 1125899906842624. => Ok((i / 1099511627776., Some(Suffix::Ti))),
_ if i < 1152921504606846976. => Ok((i / 1125899906842624., Some(Suffix::Pi))),
_ if i < 1180591620717411303424. => Ok(
(i / 1152921504606846976., Some(Suffix::Ei)),
),
_ => Err("Number is too big and unsupported".to_owned()),
}
}
_ => Err("Number is too big and unsupported".to_owned()),
},
Unit::Iec => match i {
_ if i < 1024. => Ok((i, None)),
_ if i < 1048576. => Ok((i / 1024., Some(Suffix::K))),
_ if i < 1073741824. => Ok((i / 1048576., Some(Suffix::M))),
_ if i < 1099511627776. => Ok((i / 1073741824., Some(Suffix::G))),
_ if i < 1125899906842624. => Ok((i / 1099511627776., Some(Suffix::T))),
_ if i < 1152921504606846976. => Ok((i / 1125899906842624., Some(Suffix::P))),
_ if i < 1180591620717411303424. => Ok((i / 1152921504606846976., Some(Suffix::E))),
_ => Err("Number is too big and unsupported".to_owned()),
},
Unit::IecI => match i {
_ if i < 1024. => Ok((i, None)),
_ if i < 1048576. => Ok((i / 1024., Some(Suffix::Ki))),
_ if i < 1073741824. => Ok((i / 1048576., Some(Suffix::Mi))),
_ if i < 1099511627776. => Ok((i / 1073741824., Some(Suffix::Gi))),
_ if i < 1125899906842624. => Ok((i / 1099511627776., Some(Suffix::Ti))),
_ if i < 1152921504606846976. => Ok((i / 1125899906842624., Some(Suffix::Pi))),
_ if i < 1180591620717411303424. => Ok((i / 1152921504606846976., Some(Suffix::Ei))),
_ => Err("Number is too big and unsupported".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
// 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;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]

View file

@ -35,22 +35,22 @@ impl fmt::Debug for FormatWriter {
&FormatWriter::IntWriter(ref p) => {
try!(f.write_str("IntWriter:"));
fmt::Pointer::fmt(p, f)
},
}
&FormatWriter::FloatWriter(ref p) => {
try!(f.write_str("FloatWriter:"));
fmt::Pointer::fmt(p, f)
},
}
&FormatWriter::MultibyteWriter(ref p) => {
try!(f.write_str("MultibyteWriter:"));
fmt::Pointer::fmt(&(*p as *const ()), f)
},
}
}
}
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct FormatterItemInfo {
pub byte_size: usize,
pub print_width: usize, // including a space in front of the text
pub print_width: usize, // including a space in front of the text
pub formatter: FormatWriter,
}

View file

@ -7,7 +7,10 @@ use half::f16;
/// Processes an input and provides access to the data read in various formats
///
/// 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
input: &'a mut I,
@ -28,9 +31,16 @@ pub struct InputDecoder<'a, I> where I: 'a {
impl<'a, I> InputDecoder<'a, I> {
/// 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.
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);
unsafe { bytes.set_len(normal_length + peek_length); } // fast but uninitialized
unsafe {
bytes.set_len(normal_length + peek_length);
} // fast but uninitialized
InputDecoder {
input: input,
@ -43,12 +53,16 @@ impl<'a, I> InputDecoder<'a, I> {
}
}
impl<'a, I> InputDecoder<'a, I> where I: PeekRead {
impl<'a, I> InputDecoder<'a, I>
where
I: PeekRead,
{
/// 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.
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)) => {
self.used_normal_length = n;
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,
byte_order: self.byte_order,
})
},
}
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.
fn has_error(&self) -> bool {
self.input.has_error()
@ -87,7 +103,7 @@ pub struct MemoryDecoder<'a> {
impl<'a> MemoryDecoder<'a> {
/// Set a part of the internal buffer to zero.
/// 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 {
self.data[i] = 0;
}
@ -128,7 +144,9 @@ impl<'a> MemoryDecoder<'a> {
/// Returns a f32/f64 from the internal buffer at position `start`.
pub fn read_float(&self, start: usize, byte_size: usize) -> f64 {
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,
8 => self.byte_order.read_f64(&self.data[start..start + 8]),
_ => panic!("Invalid byte_size: {}", byte_size),
@ -169,7 +187,9 @@ mod tests {
mem.zero_out_buffer(7, 8);
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() {
@ -177,7 +197,9 @@ mod tests {
assert_eq!(2, mem.length());
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)]
pub enum Radix { Decimal, Hexadecimal, Octal, NoPrefix }
pub enum Radix {
Decimal,
Hexadecimal,
Octal,
NoPrefix,
}
/// provides the byte offset printed at the left margin
pub struct InputOffset {

View file

@ -1,6 +1,6 @@
// 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;
/// `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.
/// `kind` and `message` can be specified to define the exact error.
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> {
if self.repeat_count == 0 {
return Ok(0)
return Ok(0);
} else {
if self.repeat_count > 0 {
self.repeat_count -= 1;
@ -91,8 +95,12 @@ fn test_failing_mock_stream_read() {
#[test]
fn test_failing_mock_stream_chain_interrupted() {
let mut c = Cursor::new(&b"abcd"[..])
.chain(FailingMockStream::new(ErrorKind::Interrupted, "Interrupted", 5))
.chain(Cursor::new(&b"ABCD"[..]));
.chain(FailingMockStream::new(
ErrorKind::Interrupted,
"Interrupted",
5,
))
.chain(Cursor::new(&b"ABCD"[..]));
let mut v = [0; 8];
c.read_exact(v.as_mut()).unwrap();

View file

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

View file

@ -9,8 +9,8 @@
* file that was distributed with this source code.
*/
extern crate getopts;
extern crate byteorder;
extern crate getopts;
extern crate half;
#[macro_use]
@ -44,14 +44,13 @@ use parse_formats::{parse_format_flags, ParsedFormatterItemInfo};
use prn_char::format_ascii_dump;
use parse_inputs::{parse_inputs, CommandLineInputs};
use inputoffset::{InputOffset, Radix};
use inputdecoder::{InputDecoder,MemoryDecoder};
use inputdecoder::{InputDecoder, MemoryDecoder};
use output_info::OutputInfo;
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
const PEEK_BUFFER_SIZE: usize = 4; // utf-8 can be 4 bytes
static USAGE: &'static str =
r#"Usage:
static USAGE: &'static str = r#"Usage:
od [OPTION]... [--] [FILENAME]...
od [-abcdDefFhHiIlLoOsxX] [FILENAME] [[+][0x]OFFSET[.][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 {
let mut opts = getopts::Options::new();
opts.optopt("A", "address-radix",
"Select the base in which file offsets are printed.", "RADIX");
opts.optopt("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 \
BYTES is not specified.",
"BYTES");
opts.optopt(
"A",
"address-radix",
"Select the base in which file offsets are printed.",
"RADIX",
);
opts.optopt(
"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 \
BYTES is not specified.",
"BYTES",
);
opts.optflagmulti("a", "", "named characters, ignoring high-order bit");
opts.optflagmulti("b", "", "octal bytes");
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.optmulti("t", "format", "select output format or formats", "TYPE");
opts.optflag("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 \
specified.",
"BYTES");
opts.optflag(
"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 \
specified.",
"BYTES",
);
opts.optflag("", "help", "display this help 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
}
@ -159,9 +189,9 @@ struct OdOptions {
impl OdOptions {
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) {
None => { ByteOrder::Native },
Some("little") => { ByteOrder::Little },
Some("big") => { ByteOrder::Big },
None => ByteOrder::Native,
Some("little") => ByteOrder::Little,
Some("big") => ByteOrder::Big,
Some(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") {
None => 0,
Some(s) => {
match parse_number_of_bytes(&s) {
Ok(i) => { i }
Err(_) => {
return Err(format!("Invalid argument --skip-bytes={}", s));
}
Some(s) => match parse_number_of_bytes(&s) {
Ok(i) => i,
Err(_) => {
return Err(format!("Invalid argument --skip-bytes={}", s));
}
}
},
};
let mut label: Option<usize> = None;
@ -187,7 +215,7 @@ impl OdOptions {
skip_bytes = s;
label = l;
vec![f]
},
}
Err(e) => {
return Err(format!("Invalid inputs: {}", e));
}
@ -202,14 +230,14 @@ impl OdOptions {
let mut line_bytes = match matches.opt_default("w", "32") {
None => 16,
Some(s) => {
match s.parse::<usize>() {
Ok(i) => { i }
Err(_) => { 0 }
}
}
Some(s) => match s.parse::<usize>() {
Ok(i) => i,
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 {
show_warning!("invalid width {}; using {} instead", line_bytes, min_bytes);
line_bytes = min_bytes;
@ -219,14 +247,12 @@ impl OdOptions {
let read_bytes = match matches.opt_str("read-bytes") {
None => None,
Some(s) => {
match parse_number_of_bytes(&s) {
Ok(i) => { Some(i) }
Err(_) => {
return Err(format!("Invalid argument --read-bytes={}", s));
}
Some(s) => match parse_number_of_bytes(&s) {
Ok(i) => Some(i),
Err(_) => {
return Err(format!("Invalid argument --read-bytes={}", s));
}
}
},
};
let radix = match matches.opt_str("A") {
@ -234,16 +260,16 @@ impl OdOptions {
Some(s) => {
let st = s.into_bytes();
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 {
let radix: char = *(st.get(0)
.expect("byte string of length 1 lacks a 0th elem")) as char;
let radix: char =
*(st.get(0).expect("byte string of length 1 lacks a 0th elem")) as char;
match radix {
'd' => Radix::Decimal,
'x' => Radix::Hexadecimal,
'o' => Radix::Octal,
'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) => {
disp_err!("{}", s);
return 1;
},
}
Ok(o) => o,
};
let mut input_offset = InputOffset::new(od_options.radix, od_options.skip_bytes,
od_options.label);
let mut input_offset =
InputOffset::new(od_options.radix, od_options.skip_bytes, od_options.label);
let mut input = open_input_peek_reader(&od_options.input_strings,
od_options.skip_bytes, od_options.read_bytes);
let mut input_decoder = InputDecoder::new(&mut input, od_options.line_bytes,
PEEK_BUFFER_SIZE, od_options.byte_order);
let mut input = open_input_peek_reader(
&od_options.input_strings,
od_options.skip_bytes,
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[..],
od_options.output_duplicates);
let output_info = OutputInfo::new(
od_options.line_bytes,
&od_options.formats[..],
od_options.output_duplicates,
);
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.
fn odfunc<I>(input_offset: &mut InputOffset, input_decoder: &mut InputDecoder<I>,
output_info: &OutputInfo) -> i32
where I: PeekRead + HasError {
fn odfunc<I>(
input_offset: &mut InputOffset,
input_decoder: &mut InputDecoder<I>,
output_info: &OutputInfo,
) -> i32
where
I: PeekRead + HasError,
{
let mut duplicate_line = false;
let mut previous_bytes: Vec<u8> = Vec::new();
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);
}
if !output_info.output_duplicates
&& length == line_bytes
&& memory_decoder.get_buffer(0) == &previous_bytes[..] {
if !output_info.output_duplicates && length == line_bytes
&& memory_decoder.get_buffer(0) == &previous_bytes[..]
{
if !duplicate_line {
duplicate_line = true;
println!("*");
@ -352,8 +393,11 @@ fn odfunc<I>(input_offset: &mut InputOffset, input_decoder: &mut InputDecoder<I>
memory_decoder.clone_buffer(&mut previous_bytes);
}
print_bytes(&input_offset.format_byte_offset(), &memory_decoder,
&output_info);
print_bytes(
&input_offset.format_byte_offset(),
&memory_decoder,
&output_info,
);
}
input_offset.increase_position(length);
@ -381,9 +425,11 @@ fn print_bytes(prefix: &str, input_decoder: &MemoryDecoder, output_info: &Output
let mut b = 0;
while b < input_decoder.length() {
output_text.push_str(&format!("{:>width$}",
"",
width = f.spacing[b % output_info.byte_size_block]));
output_text.push_str(&format!(
"{:>width$}",
"",
width = f.spacing[b % output_info.byte_size_block]
));
match f.formatter_item_info.formatter {
FormatWriter::IntWriter(func) => {
@ -403,21 +449,25 @@ fn print_bytes(prefix: &str, input_decoder: &MemoryDecoder, output_info: &Output
}
if f.add_ascii_dump {
let missing_spacing = output_info.print_width_line.saturating_sub(output_text.chars().count());
output_text.push_str(&format!("{:>width$} {}",
"",
format_ascii_dump(input_decoder.get_buffer(0)),
width = missing_spacing));
let missing_spacing = output_info
.print_width_line
.saturating_sub(output_text.chars().count());
output_text.push_str(&format!(
"{:>width$} {}",
"",
format_ascii_dump(input_decoder.get_buffer(0)),
width = missing_spacing
));
}
if first {
print!("{}", prefix); // print offset
// if printing in multiple formats offset is printed only once
// if printing in multiple formats offset is printed only once
first = false;
} else {
// this takes the space of the file offset on subsequent
// lines of multi-format rasters.
print!("{:>width$}", "", width=prefix.chars().count());
print!("{:>width$}", "", width = prefix.chars().count());
}
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
/// `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,
read_bytes: Option<usize>) -> PeekReader<PartialReader<MultifileReader<'a>>> {
fn open_input_peek_reader<'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
let inputs = input_strings
.iter()

View file

@ -9,7 +9,7 @@ const MAX_BYTES_PER_UNIT: usize = 8;
/// Contains information to output single output line in human readable form
pub struct SpacedFormatterItemInfo {
/// Contains a function pointer to output data, and information about the output format.
pub formatter_item_info: FormatterItemInfo,
pub formatter_item_info: FormatterItemInfo,
/// Contains the number of spaces to add to align data with other output formats.
///
/// If the corresponding data is a single byte, each entry in this array contains
@ -40,7 +40,6 @@ pub struct OutputInfo {
pub output_duplicates: bool,
}
impl OutputInfo {
/// Returns an iterator over the `SpacedFormatterItemInfo` vector.
pub fn spaced_formatters_iter(&self) -> Iter<SpacedFormatterItemInfo> {
@ -48,16 +47,25 @@ impl OutputInfo {
}
/// Creates a new `OutputInfo` based on the parameters
pub fn new(line_bytes: usize, formats: &[ParsedFormatterItemInfo], output_duplicates: bool) -> OutputInfo {
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))
});
pub fn new(
line_bytes: usize,
formats: &[ParsedFormatterItemInfo],
output_duplicates: bool,
) -> OutputInfo {
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 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 {
byte_size_line: line_bytes,
@ -69,14 +77,17 @@ impl OutputInfo {
}
}
fn create_spaced_formatter_info(formats: &[ParsedFormatterItemInfo],
byte_size_block: usize, print_width_block: usize) -> Vec<SpacedFormatterItemInfo> {
fn create_spaced_formatter_info(
formats: &[ParsedFormatterItemInfo],
byte_size_block: usize,
print_width_block: usize,
) -> Vec<SpacedFormatterItemInfo> {
formats
.iter()
.map(|f| SpacedFormatterItemInfo {
formatter_item_info: f.formatter_item_info,
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()
}
@ -126,12 +137,17 @@ impl OutputInfo {
///
/// 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.
fn calculate_alignment(sf: &TypeSizeInfo, byte_size_block: usize,
print_width_block: usize) -> [usize; MAX_BYTES_PER_UNIT] {
fn calculate_alignment(
sf: &TypeSizeInfo,
byte_size_block: usize,
print_width_block: usize,
) -> [usize; MAX_BYTES_PER_UNIT] {
if byte_size_block > MAX_BYTES_PER_UNIT {
panic!("{}-bits types are unsupported. Current max={}-bits.",
8 * byte_size_block,
8 * MAX_BYTES_PER_UNIT);
panic!(
"{}-bits types are unsupported. Current max={}-bits.",
8 * byte_size_block,
8 * MAX_BYTES_PER_UNIT
);
}
let mut spacing = [0; MAX_BYTES_PER_UNIT];
@ -161,8 +177,12 @@ trait TypeSizeInfo {
}
impl TypeSizeInfo for ParsedFormatterItemInfo {
fn byte_size(&self) -> usize { self.formatter_item_info.byte_size }
fn print_width(&self) -> usize { self.formatter_item_info.print_width }
fn byte_size(&self) -> usize {
self.formatter_item_info.byte_size
}
fn print_width(&self) -> usize {
self.formatter_item_info.print_width
}
}
#[cfg(test)]
@ -173,8 +193,12 @@ struct TypeInfo {
#[cfg(test)]
impl TypeSizeInfo for TypeInfo {
fn byte_size(&self) -> usize { self.byte_size }
fn print_width(&self) -> usize { self.print_width }
fn byte_size(&self) -> usize {
self.byte_size
}
fn print_width(&self) -> usize {
self.print_width
}
}
#[test]
@ -185,14 +209,41 @@ fn test_calculate_alignment() {
// ffff ffff ffff ffff ffff ffff ffff ffff
// the first line has no additional spacing:
assert_eq!([0, 0, 0, 0, 0, 0, 0, 0],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:8, print_width:23}, 8, 23));
assert_eq!(
[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:
assert_eq!([1, 0, 0, 0, 0, 0, 0, 0],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:4, print_width:11}, 8, 23));
assert_eq!(
[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:
assert_eq!([2, 0, 0, 0, 1, 0, 0, 0],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:2, print_width:5}, 8, 23));
assert_eq!(
[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:
// 18446744073709551615 18446744073709551615
@ -200,42 +251,195 @@ fn test_calculate_alignment() {
// 177777 177777 177777 177777 177777 177777 177777 177777
// 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],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:8, print_width:21}, 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));
assert_eq!(
[7, 0, 0, 0, 0, 0, 0, 0],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 8,
print_width: 21,
},
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
assert_eq!([1, 1, 1, 1, 1, 1, 1, 1],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 8, 16 + 8));
assert_eq!([2, 1, 1, 1, 1, 1, 1, 1],
OutputInfo::calculate_alignment(&TypeInfo{byte_size:1, print_width:2}, 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));
assert_eq!(
[1, 1, 1, 1, 1, 1, 1, 1],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 1,
print_width: 2,
},
8,
16 + 8
)
);
assert_eq!(
[2, 1, 1, 1, 1, 1, 1, 1],
OutputInfo::calculate_alignment(
&TypeInfo {
byte_size: 1,
print_width: 2,
},
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)
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!([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));
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!(
[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

@ -5,12 +5,15 @@ use prn_float::*;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct ParsedFormatterItemInfo {
pub formatter_item_info: FormatterItemInfo,
pub formatter_item_info: FormatterItemInfo,
pub add_ascii_dump: bool,
}
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 {
formatter_item_info: formatter_item_info,
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, 2) => Some(FORMAT_ITEM_DEC16S),
(FormatType::DecimalInt, 0) |
(FormatType::DecimalInt, 4) => Some(FORMAT_ITEM_DEC32S),
(FormatType::DecimalInt, 0) | (FormatType::DecimalInt, 4) => Some(FORMAT_ITEM_DEC32S),
(FormatType::DecimalInt, 8) => Some(FORMAT_ITEM_DEC64S),
(FormatType::OctalInt, 1) => Some(FORMAT_ITEM_OCT8),
(FormatType::OctalInt, 2) => Some(FORMAT_ITEM_OCT16),
(FormatType::OctalInt, 0) |
(FormatType::OctalInt, 4) => Some(FORMAT_ITEM_OCT32),
(FormatType::OctalInt, 0) | (FormatType::OctalInt, 4) => Some(FORMAT_ITEM_OCT32),
(FormatType::OctalInt, 8) => Some(FORMAT_ITEM_OCT64),
(FormatType::UnsignedInt, 1) => Some(FORMAT_ITEM_DEC8U),
(FormatType::UnsignedInt, 2) => Some(FORMAT_ITEM_DEC16U),
(FormatType::UnsignedInt, 0) |
(FormatType::UnsignedInt, 4) => Some(FORMAT_ITEM_DEC32U),
(FormatType::UnsignedInt, 0) | (FormatType::UnsignedInt, 4) => Some(FORMAT_ITEM_DEC32U),
(FormatType::UnsignedInt, 8) => Some(FORMAT_ITEM_DEC64U),
(FormatType::HexadecimalInt, 1) => Some(FORMAT_ITEM_HEX8),
(FormatType::HexadecimalInt, 2) => Some(FORMAT_ITEM_HEX16),
(FormatType::HexadecimalInt, 0) |
(FormatType::HexadecimalInt, 4) => Some(FORMAT_ITEM_HEX32),
(FormatType::HexadecimalInt, 0) | (FormatType::HexadecimalInt, 4) => {
Some(FORMAT_ITEM_HEX32)
}
(FormatType::HexadecimalInt, 8) => Some(FORMAT_ITEM_HEX64),
(FormatType::Float, 2) => Some(FORMAT_ITEM_F16),
(FormatType::Float, 0) |
(FormatType::Float, 4) => Some(FORMAT_ITEM_F32),
(FormatType::Float, 0) | (FormatType::Float, 4) => Some(FORMAT_ITEM_F32),
(FormatType::Float, 8) => Some(FORMAT_ITEM_F64),
_ => None,
}
}
fn od_argument_with_option(ch:char) -> bool {
fn od_argument_with_option(ch: char) -> bool {
match ch {
'A' | 'j' | 'N' | 'S' | 'w' => true,
_ => false,
}
}
/// Parses format flags from 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 {
return Err(format!("missing format specification after '--format' / '-t'"));
return Err(format!(
"missing format specification after '--format' / '-t'"
));
}
if formats.is_empty() {
@ -197,53 +198,61 @@ fn format_type(ch: char) -> Option<FormatType> {
}
}
fn format_type_category(t: FormatType) -> FormatTypeCategory {
match t {
FormatType::Ascii | FormatType::Char
=> FormatTypeCategory::Char,
FormatType::DecimalInt | FormatType::OctalInt | FormatType::UnsignedInt | FormatType::HexadecimalInt
=> FormatTypeCategory::Integer,
FormatType::Float
=> FormatTypeCategory::Float,
FormatType::Ascii | FormatType::Char => FormatTypeCategory::Char,
FormatType::DecimalInt
| FormatType::OctalInt
| FormatType::UnsignedInt
| FormatType::HexadecimalInt => FormatTypeCategory::Integer,
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) {
(FormatTypeCategory::Integer, Some('C')) => {
*byte_size = 1;
true
},
}
(FormatTypeCategory::Integer, Some('S')) => {
*byte_size = 2;
true
},
}
(FormatTypeCategory::Integer, Some('I')) => {
*byte_size = 4;
true
},
}
(FormatTypeCategory::Integer, Some('L')) => {
*byte_size = 8;
true
},
}
(FormatTypeCategory::Float, Some('F')) => {
*byte_size = 4;
true
},
}
(FormatTypeCategory::Float, Some('D')) => {
*byte_size = 8;
true
},
}
// FormatTypeCategory::Float, 'L' => *byte_size = 16, // TODO support f128
_ => false,
}
}
fn is_format_size_decimal(ch: Option<char>, format_type: FormatTypeCategory, decimal_size: &mut String) -> bool {
if format_type == FormatTypeCategory::Char { return false; }
fn is_format_size_decimal(
ch: Option<char>,
format_type: FormatTypeCategory,
decimal_size: &mut String,
) -> bool {
if format_type == FormatTypeCategory::Char {
return false;
}
match ch {
Some(d) if d.is_digit(10) => {
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) {
Some(t) => t,
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() {
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,
}
}
@ -304,7 +321,12 @@ fn parse_type_string(params: &String) -> Result<Vec<ParsedFormatterItemInfo>, St
match od_format_type(type_char, byte_size) {
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)]
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();
match parse_format_flags(&args) {
Err(e) => Err(e),
@ -322,195 +346,214 @@ pub fn parse_format_flags_str(args_str: &Vec<&'static str>) -> Result<Vec<Format
.inspect(|f| assert!(!f.add_ascii_dump))
.map(|f| f.formatter_item_info)
.collect())
},
}
}
}
#[test]
fn test_no_options() {
assert_eq!(parse_format_flags_str(
&vec!("od")).unwrap(),
vec!(FORMAT_ITEM_OCT16));
assert_eq!(
parse_format_flags_str(&vec!["od"]).unwrap(),
vec![FORMAT_ITEM_OCT16]
);
}
#[test]
fn test_one_option() {
assert_eq!(parse_format_flags_str(
&vec!("od", "-F")).unwrap(),
vec!(FORMAT_ITEM_F64));
assert_eq!(
parse_format_flags_str(&vec!["od", "-F"]).unwrap(),
vec![FORMAT_ITEM_F64]
);
}
#[test]
fn test_two_separate_options() {
assert_eq!(parse_format_flags_str(
&vec!("od", "-F", "-x")).unwrap(),
vec!(FORMAT_ITEM_F64, FORMAT_ITEM_HEX16));
assert_eq!(
parse_format_flags_str(&vec!["od", "-F", "-x"]).unwrap(),
vec![FORMAT_ITEM_F64, FORMAT_ITEM_HEX16]
);
}
#[test]
fn test_two_combined_options() {
assert_eq!(parse_format_flags_str(
&vec!("od", "-Fx")).unwrap(),
vec!(FORMAT_ITEM_F64, FORMAT_ITEM_HEX16));
assert_eq!(
parse_format_flags_str(&vec!["od", "-Fx"]).unwrap(),
vec![FORMAT_ITEM_F64, FORMAT_ITEM_HEX16]
);
}
#[test]
fn test_ignore_non_format_parameters() {
assert_eq!(parse_format_flags_str(
&vec!("od", "-d", "-Ax")).unwrap(),
vec!(FORMAT_ITEM_DEC16U));
assert_eq!(
parse_format_flags_str(&vec!["od", "-d", "-Ax"]).unwrap(),
vec![FORMAT_ITEM_DEC16U]
);
}
#[test]
fn test_ignore_separate_parameters() {
assert_eq!(parse_format_flags_str(
&vec!("od", "-I", "-A", "x")).unwrap(),
vec!(FORMAT_ITEM_DEC64S));
assert_eq!(
parse_format_flags_str(&vec!["od", "-I", "-A", "x"]).unwrap(),
vec![FORMAT_ITEM_DEC64S]
);
}
#[test]
fn test_ignore_trailing_vals() {
assert_eq!(parse_format_flags_str(
&vec!("od", "-D", "--", "-x")).unwrap(),
vec!(FORMAT_ITEM_DEC32U));
assert_eq!(
parse_format_flags_str(&vec!["od", "-D", "--", "-x"]).unwrap(),
vec![FORMAT_ITEM_DEC32U]
);
}
#[test]
fn test_invalid_long_format() {
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=aC")).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=X"]).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=fI"]).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=x1C")).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=xzz")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=xCC")).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=xz1"]).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=xCC"]).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=d5")).unwrap_err();
parse_format_flags_str(&vec!("od", "--format=f1")).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=d5"]).unwrap_err();
parse_format_flags_str(&vec!["od", "--format=f1"]).unwrap_err();
}
#[test]
fn test_long_format_a() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=a")).unwrap(),
vec!(FORMAT_ITEM_A));
assert_eq!(
parse_format_flags_str(&vec!["od", "--format=a"]).unwrap(),
vec![FORMAT_ITEM_A]
);
}
#[test]
fn test_long_format_cz() {
assert_eq!(parse_format_flags(
&vec!("od".to_string(), "--format=cz".to_string())).unwrap(),
vec!(ParsedFormatterItemInfo::new(FORMAT_ITEM_C, true)));
assert_eq!(
parse_format_flags(&vec!["od".to_string(), "--format=cz".to_string()]).unwrap(),
vec![ParsedFormatterItemInfo::new(FORMAT_ITEM_C, true)]
);
}
#[test]
fn test_long_format_d() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=d8")).unwrap(),
vec!(FORMAT_ITEM_DEC64S));
assert_eq!(
parse_format_flags_str(&vec!["od", "--format=d8"]).unwrap(),
vec![FORMAT_ITEM_DEC64S]
);
}
#[test]
fn test_long_format_d_default() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=d")).unwrap(),
vec!(FORMAT_ITEM_DEC32S));
assert_eq!(
parse_format_flags_str(&vec!["od", "--format=d"]).unwrap(),
vec![FORMAT_ITEM_DEC32S]
);
}
#[test]
fn test_long_format_o_default() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=o")).unwrap(),
vec!(FORMAT_ITEM_OCT32));
assert_eq!(
parse_format_flags_str(&vec!["od", "--format=o"]).unwrap(),
vec![FORMAT_ITEM_OCT32]
);
}
#[test]
fn test_long_format_u_default() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=u")).unwrap(),
vec!(FORMAT_ITEM_DEC32U));
assert_eq!(
parse_format_flags_str(&vec!["od", "--format=u"]).unwrap(),
vec![FORMAT_ITEM_DEC32U]
);
}
#[test]
fn test_long_format_x_default() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=x")).unwrap(),
vec!(FORMAT_ITEM_HEX32));
assert_eq!(
parse_format_flags_str(&vec!["od", "--format=x"]).unwrap(),
vec![FORMAT_ITEM_HEX32]
);
}
#[test]
fn test_long_format_f_default() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format=f")).unwrap(),
vec!(FORMAT_ITEM_F32));
assert_eq!(
parse_format_flags_str(&vec!["od", "--format=f"]).unwrap(),
vec![FORMAT_ITEM_F32]
);
}
#[test]
fn test_long_format_next_arg() {
assert_eq!(parse_format_flags_str(
&vec!("od", "--format", "f8")).unwrap(),
vec!(FORMAT_ITEM_F64));
assert_eq!(
parse_format_flags_str(&vec!["od", "--format", "f8"]).unwrap(),
vec![FORMAT_ITEM_F64]
);
}
#[test]
fn test_short_format_next_arg() {
assert_eq!(parse_format_flags_str(
&vec!("od", "-t", "x8")).unwrap(),
vec!(FORMAT_ITEM_HEX64));
assert_eq!(
parse_format_flags_str(&vec!["od", "-t", "x8"]).unwrap(),
vec![FORMAT_ITEM_HEX64]
);
}
#[test]
fn test_short_format_combined_arg() {
assert_eq!(parse_format_flags_str(
&vec!("od", "-tu8")).unwrap(),
vec!(FORMAT_ITEM_DEC64U));
assert_eq!(
parse_format_flags_str(&vec!["od", "-tu8"]).unwrap(),
vec![FORMAT_ITEM_DEC64U]
);
}
#[test]
fn test_format_next_arg_invalid() {
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", "-t", "-v")).unwrap_err();
parse_format_flags_str(&vec!("od", "-t")).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", "-t", "-v"]).unwrap_err();
parse_format_flags_str(&vec!["od", "-t"]).unwrap_err();
}
#[test]
fn test_mixed_formats() {
assert_eq!(parse_format_flags(
&vec!(
"od".to_string(),
"--skip-bytes=2".to_string(),
"-vItu1z".to_string(),
"-N".to_string(),
"1000".to_string(),
"-xt".to_string(),
"acdx1".to_string(),
"--format=u2c".to_string(),
"--format".to_string(),
"f".to_string(),
"-xAx".to_string(),
"--".to_string(),
"-h".to_string(),
"--format=f8".to_string())).unwrap(),
vec!(
ParsedFormatterItemInfo::new(FORMAT_ITEM_DEC64S, false), // I
ParsedFormatterItemInfo::new(FORMAT_ITEM_DEC8U, true), // tu1z
ParsedFormatterItemInfo::new(FORMAT_ITEM_HEX16, false), // x
ParsedFormatterItemInfo::new(FORMAT_ITEM_A, false), // ta
ParsedFormatterItemInfo::new(FORMAT_ITEM_C, false), // tc
ParsedFormatterItemInfo::new(FORMAT_ITEM_DEC32S, false), // td
ParsedFormatterItemInfo::new(FORMAT_ITEM_HEX8, false), // tx1
ParsedFormatterItemInfo::new(FORMAT_ITEM_DEC16U, false), // tu2
ParsedFormatterItemInfo::new(FORMAT_ITEM_C, false), // tc
ParsedFormatterItemInfo::new(FORMAT_ITEM_F32, false), // tf
ParsedFormatterItemInfo::new(FORMAT_ITEM_HEX16, false), // x
));
assert_eq!(
parse_format_flags(&vec![
"od".to_string(),
"--skip-bytes=2".to_string(),
"-vItu1z".to_string(),
"-N".to_string(),
"1000".to_string(),
"-xt".to_string(),
"acdx1".to_string(),
"--format=u2c".to_string(),
"--format".to_string(),
"f".to_string(),
"-xAx".to_string(),
"--".to_string(),
"-h".to_string(),
"--format=f8".to_string(),
]).unwrap(),
vec![
ParsedFormatterItemInfo::new(FORMAT_ITEM_DEC64S, false), // I
ParsedFormatterItemInfo::new(FORMAT_ITEM_DEC8U, true), // tu1z
ParsedFormatterItemInfo::new(FORMAT_ITEM_HEX16, false), // x
ParsedFormatterItemInfo::new(FORMAT_ITEM_A, false), // ta
ParsedFormatterItemInfo::new(FORMAT_ITEM_C, false), // tc
ParsedFormatterItemInfo::new(FORMAT_ITEM_DEC32S, false), // td
ParsedFormatterItemInfo::new(FORMAT_ITEM_HEX8, false), // tx1
ParsedFormatterItemInfo::new(FORMAT_ITEM_DEC16U, false), // tu2
ParsedFormatterItemInfo::new(FORMAT_ITEM_C, false), // tc
ParsedFormatterItemInfo::new(FORMAT_ITEM_F32, false), // tf
ParsedFormatterItemInfo::new(FORMAT_ITEM_HEX16, false), // x
]
);
}

View file

@ -31,7 +31,6 @@ pub enum CommandLineInputs {
FileAndOffset((String, usize, Option<usize>)),
}
/// Interprets the commandline inputs of od.
///
/// 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 !matches.opts_present(&["A", "j", "N", "t", "v", "w"]) {
// 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 {
Ok(n) => {
// 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)));
}
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)
pub fn parse_inputs_traditional(input_strings: Vec<String>) -> Result<CommandLineInputs, String> {
match input_strings.len() {
0 => {
Ok(CommandLineInputs::FileNames(vec!["-".to_string()]))
}
0 => Ok(CommandLineInputs::FileNames(vec!["-".to_string()])),
1 => {
let offset0 = parse_offset_operand(&input_strings[0]);
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 offset1 = parse_offset_operand(&input_strings[1]);
match (offset0, offset1) {
(Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset(("-".to_string(), n, Some(m)))),
(_, Ok(m)) => Ok(CommandLineInputs::FileAndOffset((input_strings[0].clone(), m, None))),
(Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
"-".to_string(),
n,
Some(m),
))),
(_, Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
input_strings[0].clone(),
m,
None,
))),
_ => 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 label = parse_offset_operand(&input_strings[2]);
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 label: {}", input_strings[2])),
}
}
_ => {
Err(format!("too many inputs after --traditional: {}", input_strings[3]))
}
_ => Err(format!(
"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)]
mod tests {
use super::*;
@ -187,143 +200,151 @@ mod tests {
#[test]
fn test_parse_inputs_normal() {
assert_eq!(CommandLineInputs::FileNames(vec!["-".to_string()]),
parse_inputs(&MockOptions::new(
vec![],
vec![])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec!["-".to_string()]),
parse_inputs(&MockOptions::new(vec![], vec![])).unwrap()
);
assert_eq!(CommandLineInputs::FileNames(vec!["-".to_string()]),
parse_inputs(&MockOptions::new(
vec!["-"],
vec![])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec!["-".to_string()]),
parse_inputs(&MockOptions::new(vec!["-"], vec![])).unwrap()
);
assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string()]),
parse_inputs(&MockOptions::new(
vec!["file1"],
vec![])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec!["file1".to_string()]),
parse_inputs(&MockOptions::new(vec!["file1"], vec![])).unwrap()
);
assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string(), "file2".to_string()]),
parse_inputs(&MockOptions::new(
vec!["file1", "file2"],
vec![])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec!["file1".to_string(), "file2".to_string()]),
parse_inputs(&MockOptions::new(vec!["file1", "file2"], vec![])).unwrap()
);
assert_eq!(CommandLineInputs::FileNames(vec!["-".to_string(), "file1".to_string(), "file2".to_string()]),
parse_inputs(&MockOptions::new(
vec!["-", "file1", "file2"],
vec![])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec![
"-".to_string(),
"file1".to_string(),
"file2".to_string(),
]),
parse_inputs(&MockOptions::new(vec!["-", "file1", "file2"], vec![])).unwrap()
);
}
#[test]
fn test_parse_inputs_with_offset() {
// offset is found without filename, so stdin will be used.
assert_eq!(CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)),
parse_inputs(&MockOptions::new(
vec!["+10"],
vec![])).unwrap());
assert_eq!(
CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)),
parse_inputs(&MockOptions::new(vec!["+10"], vec![])).unwrap()
);
// offset must start with "+" if no input is specified.
assert_eq!(CommandLineInputs::FileNames(vec!["10".to_string()]),
parse_inputs(&MockOptions::new(
vec!["10"],
vec![""])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec!["10".to_string()]),
parse_inputs(&MockOptions::new(vec!["10"], vec![""])).unwrap()
);
// offset is not valid, so it is considered a filename.
assert_eq!(CommandLineInputs::FileNames(vec!["+10a".to_string()]),
parse_inputs(&MockOptions::new(
vec!["+10a"],
vec![""])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec!["+10a".to_string()]),
parse_inputs(&MockOptions::new(vec!["+10a"], vec![""])).unwrap()
);
// if -j is included in the commandline, there cannot be an offset.
assert_eq!(CommandLineInputs::FileNames(vec!["+10".to_string()]),
parse_inputs(&MockOptions::new(
vec!["+10"],
vec!["j"])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec!["+10".to_string()]),
parse_inputs(&MockOptions::new(vec!["+10"], vec!["j"])).unwrap()
);
// if -v is included in the commandline, there cannot be an offset.
assert_eq!(CommandLineInputs::FileNames(vec!["+10".to_string()]),
parse_inputs(&MockOptions::new(
vec!["+10"],
vec!["o", "v"])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec!["+10".to_string()]),
parse_inputs(&MockOptions::new(vec!["+10"], vec!["o", "v"])).unwrap()
);
assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
parse_inputs(&MockOptions::new(
vec!["file1", "+10"],
vec![])).unwrap());
assert_eq!(
CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
parse_inputs(&MockOptions::new(vec!["file1", "+10"], vec![])).unwrap()
);
// offset does not need to start with "+" if a filename is included.
assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
parse_inputs(&MockOptions::new(
vec!["file1", "10"],
vec![])).unwrap());
assert_eq!(
CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
parse_inputs(&MockOptions::new(vec!["file1", "10"], vec![])).unwrap()
);
assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string(), "+10a".to_string()]),
parse_inputs(&MockOptions::new(
vec!["file1", "+10a"],
vec![""])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec!["file1".to_string(), "+10a".to_string()]),
parse_inputs(&MockOptions::new(vec!["file1", "+10a"], vec![""])).unwrap()
);
assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string(), "+10".to_string()]),
parse_inputs(&MockOptions::new(
vec!["file1", "+10"],
vec!["j"])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec!["file1".to_string(), "+10".to_string()]),
parse_inputs(&MockOptions::new(vec!["file1", "+10"], vec!["j"])).unwrap()
);
// offset must be last on the commandline
assert_eq!(CommandLineInputs::FileNames(vec!["+10".to_string(), "file1".to_string()]),
parse_inputs(&MockOptions::new(
vec!["+10", "file1"],
vec![""])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec!["+10".to_string(), "file1".to_string()]),
parse_inputs(&MockOptions::new(vec!["+10", "file1"], vec![""])).unwrap()
);
}
#[test]
fn test_parse_inputs_traditional() {
// it should not return FileAndOffset to signal no offset was entered on the commandline.
assert_eq!(CommandLineInputs::FileNames(vec!["-".to_string()]),
parse_inputs(&MockOptions::new(
vec![],
vec!["traditional"])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec!["-".to_string()]),
parse_inputs(&MockOptions::new(vec![], vec!["traditional"])).unwrap()
);
assert_eq!(CommandLineInputs::FileNames(vec!["file1".to_string()]),
parse_inputs(&MockOptions::new(
vec!["file1"],
vec!["traditional"])).unwrap());
assert_eq!(
CommandLineInputs::FileNames(vec!["file1".to_string()]),
parse_inputs(&MockOptions::new(vec!["file1"], vec!["traditional"])).unwrap()
);
// offset does not need to start with a +
assert_eq!(CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)),
parse_inputs(&MockOptions::new(
vec!["10"],
vec!["traditional"])).unwrap());
assert_eq!(
CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)),
parse_inputs(&MockOptions::new(vec!["10"], vec!["traditional"])).unwrap()
);
// valid offset and valid label
assert_eq!(CommandLineInputs::FileAndOffset(("-".to_string(), 8, Some(8))),
parse_inputs(&MockOptions::new(
vec!["10", "10"],
vec!["traditional"])).unwrap());
assert_eq!(
CommandLineInputs::FileAndOffset(("-".to_string(), 8, Some(8))),
parse_inputs(&MockOptions::new(vec!["10", "10"], vec!["traditional"])).unwrap()
);
assert_eq!(CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
parse_inputs(&MockOptions::new(
vec!["file1", "10"],
vec!["traditional"])).unwrap());
assert_eq!(
CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
parse_inputs(&MockOptions::new(vec!["file1", "10"], vec!["traditional"])).unwrap()
);
// only one file is allowed, it must be the first
parse_inputs(&MockOptions::new(
vec!["10", "file1"],
vec!["traditional"])).unwrap_err();
parse_inputs(&MockOptions::new(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(
vec!["file1", "10", "10"],
vec!["traditional"])).unwrap());
vec!["traditional"]
)).unwrap()
);
parse_inputs(&MockOptions::new(
vec!["10", "file1", "10"],
vec!["traditional"])).unwrap_err();
vec!["10", "file1", "10"],
vec!["traditional"],
)).unwrap_err();
parse_inputs(&MockOptions::new(
vec!["10", "10", "file1"],
vec!["traditional"])).unwrap_err();
vec!["10", "10", "file1"],
vec!["traditional"],
)).unwrap_err();
parse_inputs(&MockOptions::new(
vec!["10", "10", "10", "10"],
vec!["traditional"])).unwrap_err();
vec!["10", "10", "10", "10"],
vec!["traditional"],
)).unwrap_err();
}
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> {
let mut start = 0;
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 => {
multiply = 512;
len -= 1;
},
}
Some('k') | Some('K') => {
multiply = 1024;
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,
_ => return Err("parse failed"),
}
},
_ => {},
}
_ => {}
}
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() {
assert_eq!(1099511627776, parse_number_of_bytes_str("1T").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!(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 {
let buf_size = cmp::min(self.skip, MAX_SKIP_BUFFER);
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 {
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),
Some(0) => Ok(0),
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) {
Err(e) => Err(e),
Ok(r) => {
*limit -= r;
Ok(r)
},
}
}
},
}
}
}
}
@ -71,7 +77,7 @@ impl<R: HasError> HasError for PartialReader<R> {
#[cfg(test)]
mod tests {
use super::*;
use std::io::{Cursor, Read, ErrorKind};
use std::io::{Cursor, ErrorKind, Read};
use std::error::Error;
use mockstream::*;
@ -127,8 +133,7 @@ mod tests {
#[test]
fn test_read_skipping_with_two_reads_during_skip() {
let mut v = [0; 10];
let c = Cursor::new(&b"a"[..])
.chain(Cursor::new(&b"bcdefgh"[..]));
let c = Cursor::new(&b"a"[..]).chain(Cursor::new(&b"bcdefgh"[..]));
let mut sut = PartialReader::new(c, 2, None);
assert_eq!(sut.read(v.as_mut()).unwrap(), 6);

View file

@ -28,7 +28,7 @@ pub trait PeekRead {
///
/// # Panics
/// 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.
@ -53,7 +53,7 @@ impl<R: Read> PeekReader<R> {
Ok(n) => {
self.temp_buffer.drain(..n);
n
},
}
Err(_) => 0,
}
}
@ -83,7 +83,7 @@ impl<R: Read> PeekRead for PeekReader<R> {
///
/// # Panics
/// 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);
match self.read(out) {
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]);
Ok((real_size, actual_peek_size))
}
},
}
}
}
}
@ -127,7 +127,7 @@ mod tests {
let mut sut = PeekReader::new(Cursor::new(&b"abcdefgh"[..]));
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]);
}

View file

@ -13,51 +13,33 @@ pub static FORMAT_ITEM_C: FormatterItemInfo = FormatterItemInfo {
formatter: FormatWriter::MultibyteWriter(format_item_c),
};
static A_CHRS: [&'static str; 128] =
["nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
"bs", "ht", "nl", "vt", "ff", "cr", "so", "si",
"dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
"can", "em", "sub", "esc", "fs", "gs", "rs", "us",
"sp", "!", "\"", "#", "$", "%", "&", "'",
"(", ")", "*", "+", ",", "-", ".", "/",
"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", "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"];
static A_CHRS: [&'static str; 128] = [
"nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", "bs", "ht", "nl", "vt", "ff", "cr",
"so", "si", "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", "can", "em", "sub", "esc",
"fs", "gs", "rs", "us", "sp", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-",
".", "/", "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", "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 {
// itembytes == 1
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] = [
"\\0", "001", "002", "003", "004", "005", "006", "\\a",
"\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "016", "017",
"020", "021", "022", "023", "024", "025", "026", "027",
"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", "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"];
static C_CHRS: [&'static str; 128] = [
"\\0", "001", "002", "003", "004", "005", "006", "\\a", "\\b", "\\t", "\\n", "\\v", "\\f",
"\\r", "016", "017", "020", "021", "022", "023", "024", "025", "026", "027", "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",
"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 {
// itembytes == 1
@ -74,20 +56,20 @@ fn format_item_c(bytes: &[u8]) -> String {
} else if ((b & 0xe0) == 0xc0) && (bytes.len() >= 2) {
// start of a 2 octet utf-8 sequence
match from_utf8(&bytes[0..2]) {
Ok(s) => { format!("{:>4}", s) },
Err(_) => { format!(" {:03o}", b) },
Ok(s) => format!("{:>4}", s),
Err(_) => format!(" {:03o}", b),
}
} else if ((b & 0xf0) == 0xe0) && (bytes.len() >= 3) {
// start of a 3 octet utf-8 sequence
match from_utf8(&bytes[0..3]) {
Ok(s) => { format!("{:>4}", s) },
Err(_) => { format!(" {:03o}", b) },
Ok(s) => format!("{:>4}", s),
Err(_) => format!(" {:03o}", b),
}
} else if ((b & 0xf8) == 0xf0) && (bytes.len() >= 4) {
// start of a 4 octet utf-8 sequence
match from_utf8(&bytes[0..4]) {
Ok(s) => { format!("{:>4}", s) },
Err(_) => { format!(" {:03o}", b) },
Ok(s) => format!("{:>4}", s),
Err(_) => format!(" {:03o}", b),
}
} else {
// 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{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!(" 301", format_item_c(&[0xc1, 0xa1])); // invalid utf-8
@ -162,5 +147,8 @@ fn test_format_item_c() {
#[test]
fn test_format_ascii_dump() {
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 {
if !f.is_normal() {
if f == -0.0 && f.is_sign_negative() { return format!("{:>width$}", "-0", width = width) }
if f == 0.0 || !f.is_finite() { return format!("{:width$}", f, width = width) }
return format!("{:width$e}", f, width = width) // subnormal numbers
if f == -0.0 && f.is_sign_negative() {
return format!("{:>width$}", "-0", width = width);
}
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;
@ -71,115 +75,123 @@ fn format_float(f: f64, width: usize, precision: usize) -> String {
l = l - 1;
}
if l >= 0 && l <= (precision as i32 - 1) {
format!("{:width$.dec$}", f,
if l >= 0 && l <= (precision as i32 - 1) {
format!(
"{:width$.dec$}",
f,
width = width,
dec = (precision-1) - l as usize)
dec = (precision - 1) - l as usize
)
} else if l == -1 {
format!("{:width$.dec$}", f,
width = width,
dec = precision)
format!("{:width$.dec$}", f, width = width, dec = precision)
} else {
format!("{:width$.dec$e}", f,
width = width,
dec = precision - 1)
format!("{:width$.dec$e}", f, width = width, dec = precision - 1)
}
}
#[test]
fn test_format_flo32() {
assert_eq!(format_flo32(1.0), " 1.0000000");
assert_eq!(format_flo32(9.9999990), " 9.9999990");
assert_eq!(format_flo32(10.0), " 10.000000");
assert_eq!(format_flo32(99.999977), " 99.999977");
assert_eq!(format_flo32(99.999992), " 99.999992");
assert_eq!(format_flo32(100.0), " 100.00000");
assert_eq!(format_flo32(999.99994), " 999.99994");
assert_eq!(format_flo32(1000.0), " 1000.0000");
assert_eq!(format_flo32(9999.9990), " 9999.9990");
assert_eq!(format_flo32(10000.0), " 10000.000");
assert_eq!(format_flo32(99999.992), " 99999.992");
assert_eq!(format_flo32(100000.0), " 100000.00");
assert_eq!(format_flo32(999999.94), " 999999.94");
assert_eq!(format_flo32(1000000.0), " 1000000.0");
assert_eq!(format_flo32(9999999.0), " 9999999.0");
assert_eq!(format_flo32(10000000.0), " 10000000");
assert_eq!(format_flo32(99999992.0), " 99999992");
assert_eq!(format_flo32(100000000.0), " 1.0000000e8");
assert_eq!(format_flo32(9.9999994e8), " 9.9999994e8");
assert_eq!(format_flo32(1.0e9), " 1.0000000e9");
assert_eq!(format_flo32(9.9999990e9), " 9.9999990e9");
assert_eq!(format_flo32(1.0e10), " 1.0000000e10");
assert_eq!(format_flo32(1.0), " 1.0000000");
assert_eq!(format_flo32(9.9999990), " 9.9999990");
assert_eq!(format_flo32(10.0), " 10.000000");
assert_eq!(format_flo32(99.999977), " 99.999977");
assert_eq!(format_flo32(99.999992), " 99.999992");
assert_eq!(format_flo32(100.0), " 100.00000");
assert_eq!(format_flo32(999.99994), " 999.99994");
assert_eq!(format_flo32(1000.0), " 1000.0000");
assert_eq!(format_flo32(9999.9990), " 9999.9990");
assert_eq!(format_flo32(10000.0), " 10000.000");
assert_eq!(format_flo32(99999.992), " 99999.992");
assert_eq!(format_flo32(100000.0), " 100000.00");
assert_eq!(format_flo32(999999.94), " 999999.94");
assert_eq!(format_flo32(1000000.0), " 1000000.0");
assert_eq!(format_flo32(9999999.0), " 9999999.0");
assert_eq!(format_flo32(10000000.0), " 10000000");
assert_eq!(format_flo32(99999992.0), " 99999992");
assert_eq!(format_flo32(100000000.0), " 1.0000000e8");
assert_eq!(format_flo32(9.9999994e8), " 9.9999994e8");
assert_eq!(format_flo32(1.0e9), " 1.0000000e9");
assert_eq!(format_flo32(9.9999990e9), " 9.9999990e9");
assert_eq!(format_flo32(1.0e10), " 1.0000000e10");
assert_eq!(format_flo32(0.1), " 0.10000000");
assert_eq!(format_flo32(0.99999994), " 0.99999994");
assert_eq!(format_flo32(0.010000001), " 1.0000001e-2");
assert_eq!(format_flo32(0.099999994), " 9.9999994e-2");
assert_eq!(format_flo32(0.001), " 1.0000000e-3");
assert_eq!(format_flo32(0.0099999998), " 9.9999998e-3");
assert_eq!(format_flo32(0.1), " 0.10000000");
assert_eq!(format_flo32(0.99999994), " 0.99999994");
assert_eq!(format_flo32(0.010000001), " 1.0000001e-2");
assert_eq!(format_flo32(0.099999994), " 9.9999994e-2");
assert_eq!(format_flo32(0.001), " 1.0000000e-3");
assert_eq!(format_flo32(0.0099999998), " 9.9999998e-3");
assert_eq!(format_flo32(-1.0), " -1.0000000");
assert_eq!(format_flo32(-9.9999990), " -9.9999990");
assert_eq!(format_flo32(-10.0), " -10.000000");
assert_eq!(format_flo32(-99.999977), " -99.999977");
assert_eq!(format_flo32(-99.999992), " -99.999992");
assert_eq!(format_flo32(-100.0), " -100.00000");
assert_eq!(format_flo32(-999.99994), " -999.99994");
assert_eq!(format_flo32(-1000.0), " -1000.0000");
assert_eq!(format_flo32(-9999.9990), " -9999.9990");
assert_eq!(format_flo32(-10000.0), " -10000.000");
assert_eq!(format_flo32(-99999.992), " -99999.992");
assert_eq!(format_flo32(-100000.0), " -100000.00");
assert_eq!(format_flo32(-999999.94), " -999999.94");
assert_eq!(format_flo32(-1000000.0), " -1000000.0");
assert_eq!(format_flo32(-9999999.0), " -9999999.0");
assert_eq!(format_flo32(-10000000.0), " -10000000");
assert_eq!(format_flo32(-99999992.0), " -99999992");
assert_eq!(format_flo32(-100000000.0), " -1.0000000e8");
assert_eq!(format_flo32(-9.9999994e8), " -9.9999994e8");
assert_eq!(format_flo32(-1.0e9), " -1.0000000e9");
assert_eq!(format_flo32(-9.9999990e9), " -9.9999990e9");
assert_eq!(format_flo32(-1.0e10), " -1.0000000e10");
assert_eq!(format_flo32(-1.0), " -1.0000000");
assert_eq!(format_flo32(-9.9999990), " -9.9999990");
assert_eq!(format_flo32(-10.0), " -10.000000");
assert_eq!(format_flo32(-99.999977), " -99.999977");
assert_eq!(format_flo32(-99.999992), " -99.999992");
assert_eq!(format_flo32(-100.0), " -100.00000");
assert_eq!(format_flo32(-999.99994), " -999.99994");
assert_eq!(format_flo32(-1000.0), " -1000.0000");
assert_eq!(format_flo32(-9999.9990), " -9999.9990");
assert_eq!(format_flo32(-10000.0), " -10000.000");
assert_eq!(format_flo32(-99999.992), " -99999.992");
assert_eq!(format_flo32(-100000.0), " -100000.00");
assert_eq!(format_flo32(-999999.94), " -999999.94");
assert_eq!(format_flo32(-1000000.0), " -1000000.0");
assert_eq!(format_flo32(-9999999.0), " -9999999.0");
assert_eq!(format_flo32(-10000000.0), " -10000000");
assert_eq!(format_flo32(-99999992.0), " -99999992");
assert_eq!(format_flo32(-100000000.0), " -1.0000000e8");
assert_eq!(format_flo32(-9.9999994e8), " -9.9999994e8");
assert_eq!(format_flo32(-1.0e9), " -1.0000000e9");
assert_eq!(format_flo32(-9.9999990e9), " -9.9999990e9");
assert_eq!(format_flo32(-1.0e10), " -1.0000000e10");
assert_eq!(format_flo32(-0.1), " -0.10000000");
assert_eq!(format_flo32(-0.99999994), " -0.99999994");
assert_eq!(format_flo32(-0.010000001), " -1.0000001e-2");
assert_eq!(format_flo32(-0.099999994), " -9.9999994e-2");
assert_eq!(format_flo32(-0.001), " -1.0000000e-3");
assert_eq!(format_flo32(-0.1), " -0.10000000");
assert_eq!(format_flo32(-0.99999994), " -0.99999994");
assert_eq!(format_flo32(-0.010000001), " -1.0000001e-2");
assert_eq!(format_flo32(-0.099999994), " -9.9999994e-2");
assert_eq!(format_flo32(-0.001), " -1.0000000e-3");
assert_eq!(format_flo32(-0.0099999998), " -9.9999998e-3");
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(-4.7019771e-38),"-4.7019771e-38");
assert_eq!(format_flo32(1e-45), " 1e-45");
assert_eq!(format_flo32(-1.1663108e-38), "-1.1663108e-38");
assert_eq!(format_flo32(-4.7019771e-38), "-4.7019771e-38");
assert_eq!(format_flo32(1e-45), " 1e-45");
assert_eq!(format_flo32(-3.402823466e+38), " -3.4028235e38");
assert_eq!(format_flo32(f32::NAN), " NaN");
assert_eq!(format_flo32(f32::INFINITY), " inf");
assert_eq!(format_flo32(-3.402823466e+38), " -3.4028235e38");
assert_eq!(format_flo32(f32::NAN), " NaN");
assert_eq!(format_flo32(f32::INFINITY), " inf");
assert_eq!(format_flo32(f32::NEG_INFINITY), " -inf");
assert_eq!(format_flo32(-0.0), " -0");
assert_eq!(format_flo32(0.0), " 0");
assert_eq!(format_flo32(-0.0), " -0");
assert_eq!(format_flo32(0.0), " 0");
}
#[test]
fn test_format_flo64() {
assert_eq!(format_flo64(1.0), " 1.0000000000000000");
assert_eq!(format_flo64(10.0), " 10.000000000000000");
assert_eq!(format_flo64(1000000000000000.0), " 1000000000000000.0");
assert_eq!(format_flo64(10000000000000000.0), " 10000000000000000");
assert_eq!(format_flo64(100000000000000000.0), " 1.0000000000000000e17");
assert_eq!(format_flo64(1.0), " 1.0000000000000000");
assert_eq!(format_flo64(10.0), " 10.000000000000000");
assert_eq!(format_flo64(1000000000000000.0), " 1000000000000000.0");
assert_eq!(
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.01), " -1.0000000000000000e-2");
assert_eq!(format_flo64(-0.1), " -0.10000000000000001");
assert_eq!(format_flo64(-0.01), " -1.0000000000000000e-2");
assert_eq!(format_flo64(-2.2250738585072014e-308),"-2.2250738585072014e-308");
assert_eq!(format_flo64(4e-320), " 4e-320");
assert_eq!(format_flo64(f64::NAN), " NaN");
assert_eq!(format_flo64(f64::INFINITY), " inf");
assert_eq!(format_flo64(f64::NEG_INFINITY), " -inf");
assert_eq!(format_flo64(-0.0), " -0");
assert_eq!(format_flo64(0.0), " 0");
assert_eq!(
format_flo64(-2.2250738585072014e-308),
"-2.2250738585072014e-308"
);
assert_eq!(format_flo64(4e-320), " 4e-320");
assert_eq!(format_flo64(f64::NAN), " NaN");
assert_eq!(format_flo64(f64::INFINITY), " inf");
assert_eq!(format_flo64(f64::NEG_INFINITY), " -inf");
assert_eq!(format_flo64(-0.0), " -0");
assert_eq!(format_flo64(0.0), " 0");
}
#[test]
@ -191,20 +203,20 @@ fn test_format_flo16() {
assert_eq!(format_flo16(f16::from_bits(0x8402u16)), "-6.115e-5");
assert_eq!(format_flo16(f16::from_bits(0x8403u16)), "-6.121e-5");
assert_eq!(format_flo16(f16::from_f32(1.0)), " 1.000");
assert_eq!(format_flo16(f16::from_f32(10.0)), " 10.00");
assert_eq!(format_flo16(f16::from_f32(100.0)), " 100.0");
assert_eq!(format_flo16(f16::from_f32(1000.0)), " 1000");
assert_eq!(format_flo16(f16::from_f32(10000.0)), " 1.000e4");
assert_eq!(format_flo16(f16::from_f32(1.0)), " 1.000");
assert_eq!(format_flo16(f16::from_f32(10.0)), " 10.00");
assert_eq!(format_flo16(f16::from_f32(100.0)), " 100.0");
assert_eq!(format_flo16(f16::from_f32(1000.0)), " 1000");
assert_eq!(format_flo16(f16::from_f32(10000.0)), " 1.000e4");
assert_eq!(format_flo16(f16::from_f32(-0.2)), " -0.2000");
assert_eq!(format_flo16(f16::from_f32(-0.02)), "-2.000e-2");
assert_eq!(format_flo16(f16::from_f32(-0.2)), " -0.2000");
assert_eq!(format_flo16(f16::from_f32(-0.02)), "-2.000e-2");
assert_eq!(format_flo16(MIN_POSITIVE_SUBNORMAL), " 5.966e-8");
assert_eq!(format_flo16(MIN), " -6.550e4");
assert_eq!(format_flo16(NAN), " NaN");
assert_eq!(format_flo16(INFINITY), " inf");
assert_eq!(format_flo16(NEG_INFINITY), " -inf");
assert_eq!(format_flo16(NEG_ZERO), " -0");
assert_eq!(format_flo16(ZERO), " 0");
assert_eq!(format_flo16(MIN_POSITIVE_SUBNORMAL), " 5.966e-8");
assert_eq!(format_flo16(MIN), " -6.550e4");
assert_eq!(format_flo16(NAN), " NaN");
assert_eq!(format_flo16(INFINITY), " inf");
assert_eq!(format_flo16(NEG_INFINITY), " -inf");
assert_eq!(format_flo16(NEG_ZERO), " -0");
assert_eq!(format_flo16(ZERO), " 0");
}

View file

@ -51,42 +51,65 @@ macro_rules! int_writer_signed {
}
/// 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;
(item << shift) as i64 >> shift
}
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_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_OCT32, 4, 12, format_item_oct32, OCT!()); // max: 37777777777
int_writer_unsigned!(FORMAT_ITEM_OCT64, 8, 23, format_item_oct64, OCT!()); // max: 1777777777777777777777
int_writer_unsigned!(FORMAT_ITEM_HEX8, 1, 3, format_item_hex8, HEX!()); // max: ff
int_writer_unsigned!(FORMAT_ITEM_HEX16, 2, 5, format_item_hex16, HEX!()); // max: ffff
int_writer_unsigned!(FORMAT_ITEM_HEX32, 4, 9, format_item_hex32, HEX!()); // max: ffffffff
int_writer_unsigned!(FORMAT_ITEM_HEX8, 1, 3, format_item_hex8, HEX!()); // max: ff
int_writer_unsigned!(FORMAT_ITEM_HEX16, 2, 5, format_item_hex16, HEX!()); // max: ffff
int_writer_unsigned!(FORMAT_ITEM_HEX32, 4, 9, format_item_hex32, HEX!()); // max: ffffffff
int_writer_unsigned!(FORMAT_ITEM_HEX64, 8, 17, format_item_hex64, HEX!()); // max: ffffffffffffffff
int_writer_unsigned!(FORMAT_ITEM_DEC8U, 1, 4, format_item_dec_u8, DEC!()); // max: 255
int_writer_unsigned!(FORMAT_ITEM_DEC16U, 2, 6, format_item_dec_u16, DEC!()); // max: 65535
int_writer_unsigned!(FORMAT_ITEM_DEC8U, 1, 4, format_item_dec_u8, DEC!()); // max: 255
int_writer_unsigned!(FORMAT_ITEM_DEC16U, 2, 6, format_item_dec_u16, DEC!()); // max: 65535
int_writer_unsigned!(FORMAT_ITEM_DEC32U, 4, 11, format_item_dec_u32, DEC!()); // max: 4294967295
int_writer_unsigned!(FORMAT_ITEM_DEC64U, 8, 21, format_item_dec_u64, DEC!()); // max: 18446744073709551615
int_writer_signed!(FORMAT_ITEM_DEC8S, 1, 5, format_item_dec_s8, DEC!()); // max: -128
int_writer_signed!(FORMAT_ITEM_DEC16S, 2, 7, format_item_dec_s16, DEC!()); // max: -32768
int_writer_signed!(FORMAT_ITEM_DEC8S, 1, 5, format_item_dec_s8, DEC!()); // max: -128
int_writer_signed!(FORMAT_ITEM_DEC16S, 2, 7, format_item_dec_s16, DEC!()); // max: -32768
int_writer_signed!(FORMAT_ITEM_DEC32S, 4, 12, format_item_dec_s32, DEC!()); // max: -2147483648
int_writer_signed!(FORMAT_ITEM_DEC64S, 8, 21, format_item_dec_s64, DEC!()); // max: -9223372036854775808
#[test]
fn test_sign_extend() {
assert_eq!(0xffffffffffffff80u64 as i64, sign_extend(0x0000000000000080, 1));
assert_eq!(0xffffffffffff8000u64 as i64, sign_extend(0x0000000000008000, 2));
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!(
0xffffffffffffff80u64 as i64,
sign_extend(0x0000000000000080, 1)
);
assert_eq!(
0xffffffffffff8000u64 as i64,
sign_extend(0x0000000000008000, 2)
);
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!(0x0000000000007fff, sign_extend(0x0000000000007fff, 2));
@ -107,7 +130,10 @@ fn test_format_item_oct() {
assert_eq!(" 00000000000", format_item_oct32(0));
assert_eq!(" 37777777777", format_item_oct32(0xffffffff));
assert_eq!(" 0000000000000000000000", format_item_oct64(0));
assert_eq!(" 1777777777777777777777", format_item_oct64(0xffffffffffffffff));
assert_eq!(
" 1777777777777777777777",
format_item_oct64(0xffffffffffffffff)
);
}
#[test]
@ -131,7 +157,10 @@ fn test_format_item_dec_u() {
assert_eq!(" 0", format_item_dec_u32(0));
assert_eq!(" 4294967295", format_item_dec_u32(0xffffffff));
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]
@ -146,6 +175,12 @@ fn test_format_item_dec_s() {
assert_eq!(" 2147483647", format_item_dec_s32(0x7fffffff));
assert_eq!(" -2147483648", format_item_dec_s32(0x80000000));
assert_eq!(" 0", format_item_dec_s64(0));
assert_eq!(" 9223372036854775807", format_item_dec_s64(0x7fffffffffffffff));
assert_eq!(" -9223372036854775808", format_item_dec_s64(0x8000000000000000));
assert_eq!(
" 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]
extern crate uucore;
use std::io::{BufRead, BufReader, Read, stdin};
use std::io::{stdin, BufRead, BufReader, Read};
use std::iter::repeat;
use std::fs::File;
use std::path::Path;
@ -25,24 +25,36 @@ static VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub fn uumain(args: Vec<String>) -> i32 {
let mut opts = getopts::Options::new();
opts.optflag("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(
"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("V", "version", "output version information and exit");
let matches = match opts.parse(&args[1..]) {
Ok(m) => m,
Err(e) => crash!(1, "{}", e)
Err(e) => crash!(1, "{}", e),
};
if matches.opt_present("help") {
let msg = format!("{0} {1}
let msg = format!(
"{0} {1}
Usage:
{0} [OPTION]... [FILE]...
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));
} else if matches.opt_present("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) {
let mut files: Vec<BufReader<Box<Read>>> = filenames.into_iter().map(|name|
BufReader::new(
if name == "-" {
let mut files: Vec<BufReader<Box<Read>>> = filenames
.into_iter()
.map(|name| {
BufReader::new(if name == "-" {
Box::new(stdin()) as Box<Read>
} else {
let r = crash_if_err!(1, File::open(Path::new(&name)));
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;
if serial {
@ -81,11 +97,11 @@ fn paste(filenames: Vec<String>, serial: bool, delimiters: String) {
output.push_str(line.trim_right());
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;
}
println!("{}", &output[..output.len()-1]);
println!("{}", &output[..output.len() - 1]);
}
} else {
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;
}
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()]);
@ -112,7 +128,7 @@ fn paste(filenames: Vec<String>, serial: bool, delimiters: String) {
if files.len() == eof_count {
break;
}
println!("{}", &output[..output.len()-1]);
println!("{}", &output[..output.len() - 1]);
delim_count = 0;
}
}
@ -122,7 +138,7 @@ fn paste(filenames: Vec<String>, serial: bool, delimiters: String) {
// TODO: this will need work to conform to GNU implementation
fn unescape(s: String) -> String {
s.replace("\\n", "\n")
.replace("\\t", "\t")
.replace("\\\\", "\\")
.replace("\\", "")
.replace("\\t", "\t")
.replace("\\\\", "\\")
.replace("\\", "")
}

View file

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

View file

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

View file

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

View file

@ -1,5 +1,4 @@
#![crate_name = "uu_printf"]
#![allow(dead_code)]
extern crate itertools;
@ -9,7 +8,6 @@ mod cli;
mod memo;
mod tokenize;
static NAME: &'static str = "printf";
static VERSION: &'static str = "0.0.1";
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.
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
(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.)
In arguments passed by %b, pass octal-interpreted literals must be in the form of \\0NNN
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
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
%.10f means a decimal floating point with 7 decimal places past 0
%.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.
There is not past-double behavior parity with Coreutils printf, values are not estimated or adjusted beyond input values.
%f - floating point value presented in decimal, truncated and displayed to 6 decimal places by
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
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
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 :
Nathan E. Ross, et al. for the uutils project
@ -269,8 +276,10 @@ COPYRIGHT :
pub fn uumain(args: Vec<String>) -> i32 {
let location = &args[0];
if args.len() <= 1 {
println!("{0}: missing operand\nTry '{0} --help' for more information.",
location);
println!(
"{0}: missing operand\nTry '{0} --help' for more information.",
location
);
return 1;
}
let ref formatstr = args[1];

View file

@ -2,7 +2,7 @@
//! never dealt with above (e.g. Sub Tokenizer never uses these)
use std::str::Chars;
use itertools::{PutBackN, put_back_n};
use itertools::{put_back_n, PutBackN};
use cli;
use super::format_field::FormatField;
@ -28,8 +28,7 @@ impl Default for FormatPrimitive {
}
}
#[derive(Clone)]
#[derive(PartialEq)]
#[derive(Clone, PartialEq)]
pub enum Base {
Ten = 10,
Hex = 16,
@ -48,11 +47,12 @@ pub trait Formatter {
// return a FormatPrimitive for
// particular field char(s), given the argument
// string and prefix information (sign, radix)
fn get_primitive(&self,
field: &FormatField,
inprefix: &InPrefix,
str_in: &str)
-> Option<FormatPrimitive>;
fn get_primitive(
&self,
field: &FormatField,
inprefix: &InPrefix,
str_in: &str,
) -> Option<FormatPrimitive>;
// return a string from a formatprimitive,
// given information about the field
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 fn arrnum_int_div_step<'a>(rem_in: Remainder<'a>,
radix_in: u8,
base_ten_int_divisor: u8,
after_decimal: bool)
-> DivOut<'a> {
pub fn arrnum_int_div_step<'a>(
rem_in: Remainder<'a>,
radix_in: u8,
base_ten_int_divisor: u8,
after_decimal: bool,
) -> DivOut<'a> {
let mut rem_out = Remainder {
position: rem_in.position,
replace: Vec::new(),
@ -65,17 +65,15 @@ pub fn arrnum_int_div_step<'a>(rem_in: Remainder<'a>,
loop {
let u = match it_replace.next() {
Some(u_rep) => u_rep.clone() as u16,
None => {
match it_f.next() {
Some(u_orig) => u_orig.clone() as u16,
None => {
if !after_decimal {
break;
}
0
None => match it_f.next() {
Some(u_orig) => u_orig.clone() as u16,
None => {
if !after_decimal {
break;
}
0
}
}
},
};
traversed += 1;
bufferval += u;
@ -176,7 +174,7 @@ pub fn arrnum_int_add(arrnum: &Vec<u8>, basenum: u8, base_ten_int_term: u8) -> V
let mut it = arrnum.iter().rev();
loop {
let i = it.next();
match i {
match i {
Some(u) => {
new_amount = (u.clone() as u16) + carry;
rem = new_amount % base;
@ -218,7 +216,6 @@ pub fn unsigned_to_arrnum(src: u16) -> Vec<u8> {
result
}
// temporary needs-improvement-function
#[allow(unused_variables)]
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)]
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_out = base_conv_vec(&intermed_in,
radix_def_src.get_max(),
radix_def_dest.get_max());
let intermed_out = base_conv_vec(
&intermed_in,
radix_def_src.get_max(),
radix_def_dest.get_max(),
);
arrnum_to_str(&intermed_out, radix_def_dest)
}

View file

@ -1,11 +1,10 @@
//! formatter for %a %F C99 Hex-floating-point subs
use super::super::format_field::FormatField;
use super::super::formatter::{InPrefix, FormatPrimitive, Formatter};
use super::float_common::{FloatAnalysis, primitive_to_str_common};
use super::super::formatter::{FormatPrimitive, Formatter, InPrefix};
use super::float_common::{primitive_to_str_common, FloatAnalysis};
use super::base_conv;
use super::base_conv::RadixDef;
pub struct CninetyNineHexFloatf {
as_num: f64,
}
@ -16,22 +15,22 @@ impl CninetyNineHexFloatf {
}
impl Formatter for CninetyNineHexFloatf {
fn get_primitive(&self,
field: &FormatField,
inprefix: &InPrefix,
str_in: &str)
-> Option<FormatPrimitive> {
fn get_primitive(
&self,
field: &FormatField,
inprefix: &InPrefix,
str_in: &str,
) -> Option<FormatPrimitive> {
let second_field = field.second_field.unwrap_or(6) + 1;
let analysis = FloatAnalysis::analyze(&str_in,
inprefix,
Some(second_field as usize),
None,
true);
let f = get_primitive_hex(inprefix,
&str_in[inprefix.offset..],
&analysis,
second_field as usize,
*field.field_char == 'A');
let analysis =
FloatAnalysis::analyze(&str_in, inprefix, Some(second_field as usize), None, true);
let f = get_primitive_hex(
inprefix,
&str_in[inprefix.offset..],
&analysis,
second_field as usize,
*field.field_char == 'A',
);
Some(f)
}
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.
#[allow(unused_variables)]
#[allow(unused_assignments)]
fn get_primitive_hex(inprefix: &InPrefix,
str_in: &str,
analysis: &FloatAnalysis,
last_dec_place: usize,
capitalized: bool)
-> FormatPrimitive {
fn get_primitive_hex(
inprefix: &InPrefix,
str_in: &str,
analysis: &FloatAnalysis,
last_dec_place: usize,
capitalized: bool,
) -> FormatPrimitive {
let mut f: FormatPrimitive = Default::default();
f.prefix = Some(String::from(if inprefix.sign == -1 {
"-0x"
} else {
"0x"
}));
f.prefix = Some(String::from(if inprefix.sign == -1 { "-0x" } else { "0x" }));
// assign the digits before and after the decimal points
// 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.
let mantissa = 0;
f.suffix = Some({
let ind = if capitalized {
"P"
} else {
"p"
};
let ind = if capitalized { "P" } else { "p" };
if mantissa >= 0 {
format!("{}+{}", ind, mantissa)
} else {
@ -122,8 +113,10 @@ fn to_hex(src: &str, before_decimal: bool) -> String {
base_conv::base_conv_str(src, &rten, &rhex)
} else {
let as_arrnum_ten = base_conv::str_to_arrnum(src, &rten);
let s = format!("{}",
base_conv::base_conv_float(&as_arrnum_ten, rten.get_max(), rhex.get_max()));
let s = format!(
"{}",
base_conv::base_conv_float(&as_arrnum_ten, rten.get_max(), rhex.get_max())
);
if s.len() > 2 {
String::from(&s[2..])
} else {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,8 +3,8 @@
use std::env;
use std::vec::Vec;
use cli;
use super::format_field::{FormatField, FieldType};
use super::formatter::{Formatter, FormatPrimitive, InPrefix, Base};
use super::format_field::{FieldType, FormatField};
use super::formatter::{Base, FormatPrimitive, Formatter, InPrefix};
use super::formatters::intf::Intf;
use super::formatters::floatf::Floatf;
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>) {
match env::var("POSIXLY_CORRECT") {
Ok(_) => {}
Err(e) => {
match e {
env::VarError::NotPresent => {
cli::err_msg(&format!("warning: {:?}: character(s) following character \
constant have been ignored",
&*remaining_bytes));
}
_ => {}
Err(e) => match e {
env::VarError::NotPresent => {
cli::err_msg(&format!(
"warning: {:?}: character(s) following character \
constant have been ignored",
&*remaining_bytes
));
}
}
_ => {}
},
}
}
@ -68,8 +68,7 @@ fn get_provided(str_in_opt: Option<&String>) -> Option<u8> {
// first byte is not quote
_ => {
return None;
}
// no first byte
} // no first byte
}
} else {
Some(0 as u8)
@ -150,7 +149,7 @@ fn get_inprefix(str_in: &String, field_type: &FieldType) -> InPrefix {
FieldType::Intf => {
ret.radix_in = Base::Octal;
}
_ => {}
_ => {}
}
if e == '0' {
do_clean_lead_zeroes = true;
@ -181,7 +180,7 @@ fn get_inprefix(str_in: &String, field_type: &FieldType) -> InPrefix {
// if decimal, keep last zero if one exists
// (it's possible for last zero to
// not exist at this branch if we're in hex input)
'.' => break,
'.' => break,
// other digit, etc.
_ => {
if !(is_hex && first) {
@ -193,7 +192,6 @@ fn get_inprefix(str_in: &String, field_type: &FieldType) -> InPrefix {
if first {
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
// and an iterator to the argument
pub fn num_format(field: &FormatField, in_str_opt: Option<&String>) -> Option<String> {
let fchar = field.field_char.clone();
// num format mainly operates by further delegating to one of
@ -247,7 +243,7 @@ pub fn num_format(field: &FormatField, in_str_opt: Option<&String>) -> Option<St
let inprefix = get_inprefix(
&as_str,
&field.field_type
);
);
tmp=fmtr.get_primitive(field, &inprefix, &as_str)
.expect("err during default provided num");
},

View file

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

View file

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

View file

@ -12,19 +12,19 @@
extern crate aho_corasick;
extern crate getopts;
extern crate memchr;
extern crate regex_syntax;
extern crate regex;
extern crate regex_syntax;
#[macro_use]
extern crate uucore;
use getopts::{Options, Matches};
use getopts::{Matches, Options};
use regex::Regex;
use std::cmp;
use std::collections::{HashMap, HashSet, BTreeSet};
use std::collections::{BTreeSet, HashMap, HashSet};
use std::default::Default;
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 VERSION: &'static str = env!("CARGO_PKG_VERSION");
@ -38,33 +38,33 @@ enum OutFormat {
#[derive(Debug)]
struct Config {
format : OutFormat,
gnu_ext : bool,
auto_ref : bool,
input_ref : bool,
right_ref : bool,
ignore_case : bool,
macro_name : String,
trunc_str : String,
context_regex : String,
line_width : usize,
gap_size : usize,
format: OutFormat,
gnu_ext: bool,
auto_ref: bool,
input_ref: bool,
right_ref: bool,
ignore_case: bool,
macro_name: String,
trunc_str: String,
context_regex: String,
line_width: usize,
gap_size: usize,
}
impl Default for Config {
fn default() -> Config {
Config {
format : OutFormat::Dumb,
gnu_ext : true,
auto_ref : false,
input_ref : false,
right_ref : false,
ignore_case : false,
macro_name : "xx".to_owned(),
trunc_str : "/".to_owned(),
context_regex : "\\w+".to_owned(),
line_width : 72,
gap_size : 3
format: OutFormat::Dumb,
gnu_ext: true,
auto_ref: false,
input_ref: false,
right_ref: false,
ignore_case: false,
macro_name: "xx".to_owned(),
trunc_str: "/".to_owned(),
context_regex: "\\w+".to_owned(),
line_width: 72,
gap_size: 3,
}
}
}
@ -89,36 +89,33 @@ struct WordFilter {
}
impl WordFilter {
fn new(matches: &Matches, config: &Config) -> WordFilter {
let (o, oset): (bool, HashSet<String>) =
if matches.opt_present("o") {
(true, read_word_filter_file(matches, "o"))
} else {
(false, HashSet::new())
};
let (i, iset): (bool, HashSet<String>) =
if matches.opt_present("i") {
(true, read_word_filter_file(matches, "i"))
} else {
(false, HashSet::new())
};
fn new(matches: &Matches, config: &Config) -> WordFilter {
let (o, oset): (bool, HashSet<String>) = if matches.opt_present("o") {
(true, read_word_filter_file(matches, "o"))
} else {
(false, HashSet::new())
};
let (i, iset): (bool, HashSet<String>) = if matches.opt_present("i") {
(true, read_word_filter_file(matches, "i"))
} else {
(false, HashSet::new())
};
if matches.opt_present("b") {
crash!(1, "-b not implemented yet");
}
let reg =
if matches.opt_present("W") {
matches.opt_str("W").expect("parsing options failed!")
} else if config.gnu_ext {
"\\w+".to_owned()
} else {
"[^ \t\n]+".to_owned()
};
let reg = if matches.opt_present("W") {
matches.opt_str("W").expect("parsing options failed!")
} else if config.gnu_ext {
"\\w+".to_owned()
} else {
"[^ \t\n]+".to_owned()
};
WordFilter {
only_specified: o,
ignore_specified: i,
only_set: oset,
ignore_set: iset,
word_regex: reg
word_regex: reg,
}
}
}
@ -139,11 +136,11 @@ fn print_version() {
fn print_usage(opts: &Options) {
let brief = "Usage: ptx [OPTION]... [INPUT]... (without -G) or: \
ptx -G [OPTION]... [INPUT [OUTPUT]] \n Output a permuted index, \
including context, of the words in the input files. \n\n Mandatory \
arguments to long options are mandatory for short options too.";
ptx -G [OPTION]... [INPUT [OUTPUT]] \n Output a permuted index, \
including context, of the words in the input files. \n\n Mandatory \
arguments to long options are mandatory for short options too.";
let explaination = "With no FILE, or when FILE is -, read standard input. \
Default is '-F /'.";
Default is '-F /'.";
println!("{}\n{}", opts.usage(&brief), explaination);
}
@ -165,22 +162,18 @@ fn get_config(matches: &Matches) -> Config {
config.right_ref &= matches.opt_present("R");
config.ignore_case = matches.opt_present("f");
if matches.opt_present("M") {
config.macro_name =
matches.opt_str("M").expect(err_msg);
config.macro_name = matches.opt_str("M").expect(err_msg);
}
if matches.opt_present("F") {
config.trunc_str =
matches.opt_str("F").expect(err_msg);
config.trunc_str = matches.opt_str("F").expect(err_msg);
}
if matches.opt_present("w") {
let width_str = matches.opt_str("w").expect(err_msg);
config.line_width = crash_if_err!(
1, usize::from_str_radix(&width_str, 10));
config.line_width = crash_if_err!(1, usize::from_str_radix(&width_str, 10));
}
if matches.opt_present("g") {
let gap_str = matches.opt_str("g").expect(err_msg);
config.gap_size = crash_if_err!(
1, usize::from_str_radix(&gap_str, 10));
config.gap_size = crash_if_err!(1, usize::from_str_radix(&gap_str, 10));
}
if matches.opt_present("O") {
config.format = OutFormat::Roff;
@ -191,10 +184,8 @@ fn get_config(matches: &Matches) -> Config {
config
}
fn read_input(input_files: &[String], config: &Config) ->
HashMap<String, (Vec<String>, usize)> {
let mut file_map : HashMap<String, (Vec<String>, usize)> =
HashMap::new();
fn read_input(input_files: &[String], config: &Config) -> HashMap<String, (Vec<String>, usize)> {
let mut file_map: HashMap<String, (Vec<String>, usize)> = HashMap::new();
let mut files = Vec::new();
if input_files.is_empty() {
files.push("-");
@ -209,15 +200,13 @@ fn read_input(input_files: &[String], config: &Config) ->
}
let mut lines_so_far: usize = 0;
for filename in files {
let reader: BufReader<Box<Read>> = BufReader::new(
if filename == "-" {
Box::new(stdin())
} else {
let file = crash_if_err!(1, File::open(filename));
Box::new(file)
});
let lines: Vec<String> = reader.lines().map(|x| crash_if_err!(1, x))
.collect();
let reader: BufReader<Box<Read>> = BufReader::new(if filename == "-" {
Box::new(stdin())
} else {
let file = crash_if_err!(1, File::open(filename));
Box::new(file)
});
let lines: Vec<String> = reader.lines().map(|x| crash_if_err!(1, x)).collect();
let size = lines.len();
file_map.insert(filename.to_owned(), (lines, lines_so_far));
lines_so_far += size
@ -225,9 +214,11 @@ fn read_input(input_files: &[String], config: &Config) ->
file_map
}
fn create_word_set(config: &Config, filter: &WordFilter,
file_map: &HashMap<String, (Vec<String>, usize)>)->
BTreeSet<WordRef> {
fn create_word_set(
config: &Config,
filter: &WordFilter,
file_map: &HashMap<String, (Vec<String>, usize)>,
) -> BTreeSet<WordRef> {
let reg = Regex::new(&filter.word_regex).unwrap();
let ref_reg = Regex::new(&config.context_regex).unwrap();
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
let (ref_beg, ref_end) = match ref_reg.find(line) {
Some(x) => (x.start(), x.end()),
None => (0, 0)
None => (0, 0),
};
// match words with given regex
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)) {
continue;
}
let mut word = line[beg .. end].to_owned();
if filter.only_specified &&
!(filter.only_set.contains(&word)) {
let mut word = line[beg..end].to_owned();
if filter.only_specified && !(filter.only_set.contains(&word)) {
continue;
}
if filter.ignore_specified &&
filter.ignore_set.contains(&word) {
if filter.ignore_specified && filter.ignore_set.contains(&word) {
continue;
}
if config.ignore_case {
word = word.to_lowercase();
}
word_set.insert(WordRef{
word_set.insert(WordRef {
word: word,
filename: String::from(file.clone()),
global_line_nr: offs + count,
local_line_nr: count,
position: beg,
position_end: end
position_end: end,
});
}
count += 1;
@ -273,17 +262,16 @@ fn create_word_set(config: &Config, filter: &WordFilter,
word_set
}
fn get_reference(config: &Config, word_ref: &WordRef, line: &str) ->
String {
fn get_reference(config: &Config, word_ref: &WordRef, line: &str) -> String {
if config.auto_ref {
format!("{}:{}", word_ref.filename, word_ref.local_line_nr + 1)
} else if config.input_ref {
let reg = Regex::new(&config.context_regex).unwrap();
let (beg, end) = match reg.find(line) {
Some(x) => (x.start(), x.end()),
None => (0, 0)
None => (0, 0),
};
format!("{}", &line[beg .. end])
format!("{}", &line[beg..end])
} else {
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 {
assert_str_integrity(s, beg, end);
if beg == end || beg == 0 || s[beg].is_whitespace() ||
s[beg-1].is_whitespace() {
if beg == end || beg == 0 || s[beg].is_whitespace() || s[beg - 1].is_whitespace() {
return 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 {
assert_str_integrity(s, beg, end);
if beg == end || end == s.len() || s[end-1].is_whitespace() ||
s[end].is_whitespace() {
if beg == end || end == s.len() || s[end - 1].is_whitespace() || s[end].is_whitespace() {
return end;
}
let mut e = end;
while beg < e && !s[e-1].is_whitespace() {
while beg < e && !s[e - 1].is_whitespace() {
e -= 1;
}
e
@ -327,14 +313,18 @@ fn trim_idx(s: &[char], beg: usize, end: usize) -> (usize, usize) {
while b < e && s[b].is_whitespace() {
b += 1;
}
while b < e && s[e-1].is_whitespace() {
while b < e && s[e - 1].is_whitespace() {
e -= 1;
}
(b,e)
(b, e)
}
fn get_output_chunks(all_before: &str, keyword: &str, all_after: &str,
config: &Config) -> (String, String, String, String) {
fn get_output_chunks(
all_before: &str,
keyword: &str,
all_after: &str,
config: &Config,
) -> (String, String, String, String) {
assert_eq!(all_before.trim(), all_before);
assert_eq!(keyword.trim(), keyword);
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 tail = String::new();
let half_line_size = cmp::max((config.line_width/2) as isize -
(2*config.trunc_str.len()) as isize, 0) as usize;
let max_after_size = cmp::max(half_line_size as isize -
keyword.len() as isize - 1, 0) as usize;
let half_line_size = cmp::max(
(config.line_width / 2) as isize - (2 * config.trunc_str.len()) as isize,
0,
) 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 all_before_vec: Vec<char> = all_before.chars().collect();
let all_after_vec: Vec<char> = all_after.chars().collect();
// get before
let mut bb_tmp =
cmp::max(all_before.len() as isize - max_before_size as isize, 0) as usize;
let mut bb_tmp = 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());
let (before_beg, before_end) =
trim_idx(&all_before_vec, bb_tmp, all_before.len());
before.push_str(&all_before[before_beg .. before_end]);
let (before_beg, before_end) = trim_idx(&all_before_vec, bb_tmp, all_before.len());
before.push_str(&all_before[before_beg..before_end]);
assert!(max_before_size >= before.len());
// get after
let mut ae_tmp = cmp::min(max_after_size, all_after.len());
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);
after.push_str(&all_after[after_beg .. after_end]);
after.push_str(&all_after[after_beg..after_end]);
assert!(max_after_size >= after.len());
// 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());
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);
tail.push_str(&all_after[tail_beg .. tail_end]);
tail.push_str(&all_after[tail_beg..tail_end]);
// get head
let max_head_size = max_after_size - after.len();
let (_, he) = trim_idx(&all_before_vec, 0, before_beg);
let mut hb_tmp =
cmp::max(he as isize - max_head_size as isize, 0) as usize;
let mut hb_tmp = 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);
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
if after_end != all_after.len() && tail_beg == tail_end {
@ -411,7 +399,7 @@ fn tex_mapper(x: char) -> String {
'\\' => "\\backslash{}".to_owned(),
'$' | '%' | '#' | '&' | '_' => format!("\\{}", x),
'}' | '{' => format!("$\\{}$", x),
_ => x.to_string()
_ => x.to_string(),
}
}
@ -423,84 +411,85 @@ fn adjust_tex_str(context: &str) -> String {
fix
}
fn format_tex_line(config: &Config, word_ref: &WordRef, line: &str,
reference: &str) -> String {
fn format_tex_line(config: &Config, word_ref: &WordRef, line: &str, reference: &str) -> String {
let mut output = String::new();
output.push_str(&format!("\\{} ", config.macro_name));
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))
} else {
adjust_tex_str(&line[0 .. word_ref.position])
adjust_tex_str(&line[0..word_ref.position])
};
let keyword = adjust_tex_str(
&line[word_ref.position .. word_ref.position_end]);
let all_after = adjust_tex_str(
&line[word_ref.position_end .. line.len()]);
let (tail, before, after, head) =
get_output_chunks(&all_before, &keyword, &all_after, &config);
output.push_str(&format!("{5}{0}{6}{5}{1}{6}{5}{2}{6}{5}{3}{6}{5}{4}{6}",
tail, before, keyword, after, head, "{", "}"));
let keyword = adjust_tex_str(&line[word_ref.position..word_ref.position_end]);
let all_after = adjust_tex_str(&line[word_ref.position_end..line.len()]);
let (tail, before, after, head) = get_output_chunks(&all_before, &keyword, &all_after, &config);
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 {
output.push_str(
&format!("{}{}{}", "{", adjust_tex_str(&reference), "}"));
output.push_str(&format!("{}{}{}", "{", adjust_tex_str(&reference), "}"));
}
output
}
fn adjust_roff_str(context: &str) -> String {
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,
reference: &str) -> String {
fn format_roff_line(config: &Config, word_ref: &WordRef, line: &str, reference: &str) -> String {
let mut output = String::new();
output.push_str(&format!(".{}", config.macro_name));
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))
} else {
adjust_roff_str(&line[0 .. word_ref.position])
adjust_roff_str(&line[0..word_ref.position])
};
let keyword = adjust_roff_str(
&line[word_ref.position .. word_ref.position_end]);
let all_after = adjust_roff_str(
&line[word_ref.position_end .. line.len()]);
let (tail, before, after, head) =
get_output_chunks(&all_before, &keyword, &all_after, &config);
output.push_str(&format!(" \"{}\" \"{}\" \"{}{}\" \"{}\"",
tail, before, keyword, after, head));
let keyword = adjust_roff_str(&line[word_ref.position..word_ref.position_end]);
let all_after = adjust_roff_str(&line[word_ref.position_end..line.len()]);
let (tail, before, after, head) = get_output_chunks(&all_before, &keyword, &all_after, &config);
output.push_str(&format!(
" \"{}\" \"{}\" \"{}{}\" \"{}\"",
tail, before, keyword, after, head
));
if config.auto_ref || config.input_ref {
output.push_str(&format!(" \"{}\"", adjust_roff_str(&reference)));
}
output
}
fn write_traditional_output(config: &Config,
file_map: &HashMap<String, (Vec<String>,usize)>,
words: &BTreeSet<WordRef>, output_filename: &str) {
let mut writer: BufWriter<Box<Write>> = BufWriter::new(
if output_filename == "-" {
fn write_traditional_output(
config: &Config,
file_map: &HashMap<String, (Vec<String>, usize)>,
words: &BTreeSet<WordRef>,
output_filename: &str,
) {
let mut writer: BufWriter<Box<Write>> = BufWriter::new(if output_filename == "-" {
Box::new(stdout())
} else {
let file = crash_if_err!(1, File::create(output_filename));
Box::new(file)
});
for word_ref in words.iter() {
let file_map_value : &(Vec<String>, usize) =
file_map.get(&(word_ref.filename))
.expect("Missing file in file map");
let file_map_value: &(Vec<String>, usize) = file_map
.get(&(word_ref.filename))
.expect("Missing file in file map");
let (ref lines, _) = *(file_map_value);
let reference =
get_reference(config, word_ref, &lines[word_ref.local_line_nr]);
let reference = get_reference(config, word_ref, &lines[word_ref.local_line_nr]);
let output_line: String = match config.format {
OutFormat::Tex => 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::Dumb => crash!(
1, "There is no dumb format with GNU extensions disabled")
OutFormat::Tex => {
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::Dumb => crash!(1, "There is no dumb format with GNU extensions disabled"),
};
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 {
let mut opts = Options::new();
opts.optflag("A", "auto-reference",
"output automatically generated references");
opts.optflag(
"A",
"auto-reference",
"output automatically generated references",
);
opts.optflag("G", "traditional", "behave more like System V 'ptx'");
opts.optopt("F", "flag-truncation",
"use STRING for flagging line truncations", "STRING");
opts.optopt("M", "macro-name", "macro name to use instead of 'xx'",
"STRING");
opts.optopt(
"F",
"flag-truncation",
"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("R", "right-side-refs",
"put references at right, not counted in -w");
opts.optopt("S", "sentence-regexp", "for end of lines or end of sentences",
"REGEXP");
opts.optflag(
"R",
"right-side-refs",
"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.optopt("W", "word-regexp", "use REGEXP to match each keyword",
"REGEXP");
opts.optopt("b", "break-file", "word break characters in this 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.optopt(
"W",
"word-regexp",
"use REGEXP to match each keyword",
"REGEXP",
);
opts.optopt(
"b",
"break-file",
"word break characters in this 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.optopt("w", "width", "output width in columns, reference excluded",
"NUMBER");
opts.optopt(
"w",
"width",
"output width in columns, reference excluded",
"NUMBER",
);
opts.optflag("", "help", "display this help 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 word_filter = WordFilter::new(&matches, &config);
let file_map =
read_input(&matches.free, &config);
let file_map = read_input(&matches.free, &config);
let word_set = create_word_set(&config, &word_filter, &file_map);
let output_file = if !config.gnu_ext && matches.free.len() == 2 {
matches.free[1].clone()

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