зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1485409 - update crates to dispense with winapi 0.2.8 requirement; r=glandium
winapi 0.2.8 is used by a number of crates in our dependency graph. The newest version of winapi is 0.3.4, which is a significant restructuring and also more amenable to further development, e.g. adding AArch64 support. This patch does the easy work of updating as many things as possible to winapi 0.3.4 via a simple `cargo update`: cargo update -p atty:0.2.2 -p fs2 -p msdos_time -p parking_lot_core -p aho-corasick and then vendoring the results of those changes.
This commit is contained in:
Родитель
9ca03066d9
Коммит
65e7b0bbd1
|
@ -14,10 +14,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.6.3"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -75,12 +75,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.2"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -316,7 +316,7 @@ version = "2.31.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -685,7 +685,7 @@ name = "env_logger"
|
|||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -767,12 +767,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fs2"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1293,7 +1292,7 @@ name = "memmap"
|
|||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fs2 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1443,12 +1442,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "msdos_time"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1604,19 +1602,18 @@ version = "0.6.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.2.7"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1802,7 +1799,7 @@ dependencies = [
|
|||
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1810,12 +1807,20 @@ name = "redox_syscall"
|
|||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1827,7 +1832,7 @@ name = "regex"
|
|||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2275,6 +2280,16 @@ dependencies = [
|
|||
"wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.9.0"
|
||||
|
@ -2812,7 +2827,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"bzip2 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"msdos_time 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -2820,7 +2835,7 @@ dependencies = [
|
|||
[metadata]
|
||||
"checksum Inflector 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1b33cd9b653730fc539c53c7b3c672d2f47108fa20c6df571fa5817178f5a14c"
|
||||
"checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45"
|
||||
"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
|
||||
"checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9dadc668390b373e73e4abbfc1f07238b09a25858f2f39c06cebc6d8e141d774"
|
||||
"checksum arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd1479b7c29641adbd35ff3b5c293922d696a92f25c8c975da3e0acbc87258f"
|
||||
|
@ -2828,7 +2843,7 @@ dependencies = [
|
|||
"checksum ascii-canvas 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b385d69402821a1c254533a011a312531cbcc0e3e24f19bbb4747a5a2daf37e2"
|
||||
"checksum atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2dcb6e6d35f20276943cc04bb98e538b348d525a04ac79c10021561d202f21"
|
||||
"checksum atty 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d0fd4c0631f06448cc45a6bbb3b710ebb7ff8ccb96a0800c994afe23a70d5df2"
|
||||
"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
|
||||
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||
"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
|
||||
"checksum binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88ceb0d16c4fd0e42876e298d7d3ce3780dd9ebdcbe4199816a32c77e08597ff"
|
||||
"checksum bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bda13183df33055cbb84b847becce220d392df502ebe7a4a78d7021771ed94d0"
|
||||
|
@ -2896,7 +2911,7 @@ dependencies = [
|
|||
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
|
||||
"checksum foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ebc04f19019fff1f2d627b5581574ead502f80c48c88900575a46e0840fe5d0"
|
||||
"checksum freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b659e75b7a7338fe75afd7f909fc2b71937845cffb6ebe54ba2e50f13d8e903d"
|
||||
"checksum fs2 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ab76cfd2aaa59b7bf6688ad9ba15bbae64bff97f04ea02144cfd3443e5c2866"
|
||||
"checksum fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
|
||||
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
"checksum futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "884dbe32a6ae4cd7da5c6db9b78114449df9953b8d490c9d7e1b51720b922c62"
|
||||
|
@ -2949,7 +2964,7 @@ dependencies = [
|
|||
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
||||
"checksum moz_cbor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20c82a57087fd5990d7122dbff1607c3b20c3d2958e9d9ad9765aab415e2c91c"
|
||||
"checksum mp4parse_fallible 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6626c2aef76eb8f984eef02e475883d3fe9112e114720446c5810fc5f045cd30"
|
||||
"checksum msdos_time 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "65ba9d75bcea84e07812618fedf284a64776c2f2ea0cad6bca7f69739695a958"
|
||||
"checksum msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729"
|
||||
"checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0"
|
||||
"checksum new-ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8ccbebba6fb53a6d2bdcfaf79cb339bc136dee3bfff54dc337a334bafe36476a"
|
||||
"checksum new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cdc457076c78ab54d5e0d6fa7c47981757f1e34dc39ff92787f217dede586c4"
|
||||
|
@ -2966,7 +2981,7 @@ dependencies = [
|
|||
"checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063"
|
||||
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
|
||||
"checksum parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "69376b761943787ebd5cc85a5bc95958651a22609c5c1c2b65de21786baec72b"
|
||||
"checksum parking_lot_core 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6c677d78851950b3aec390e681a411f78cc250cba277d4f578758a377f727970"
|
||||
"checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa"
|
||||
"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
"checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
|
||||
"checksum petgraph 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "7a7e5234c228fbfa874c86a77f685886127f82e0aef602ad1d48333fcac6ad61"
|
||||
|
@ -2990,6 +3005,7 @@ dependencies = [
|
|||
"checksum rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "485541959c8ecc49865526fe6c4de9653dd6e60d829d6edf0be228167b60372d"
|
||||
"checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8"
|
||||
"checksum redox_syscall 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ab105df655884ede59d45b7070c8a65002d921461ee813a024558ca16030eea0"
|
||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
|
||||
"checksum regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75ecf88252dce580404a22444fc7d626c01815debba56a7f4f536772a5ff19d3"
|
||||
"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
|
||||
|
@ -3032,6 +3048,7 @@ dependencies = [
|
|||
"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
|
||||
"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
|
||||
"checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83"
|
||||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
|
||||
"checksum thin-slice 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
|
||||
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{".travis.yml":"e17babe5ba0bdd19ec59a37b4a099fd4313bff58be63a2ff506075f9a97dc172","COPYING":"01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f","Cargo.toml":"b3fa06c2147a4749cd984ded69024ddcc8b7d578ab763b60227b3ba474c3ec70","LICENSE-MIT":"0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f","Makefile":"a45a128685a2ae7d4fa39d310786674417ee113055ef290a11f88002285865fc","README.md":"9bc60d2cec222b50f87c85cf9475349bb228a36f89796c5d6481c52560ddde3a","UNLICENSE":"7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c","benches/bench.rs":"acf4844efadeafc7bc396c2b16f2a184e140b6c17d1084dbaf454196de2090cd","benches/random.txt":"9386fb3efedc7ffbd09fb49088347f1056bc2d90a861009fa2f804cdb714efcb","ctags.rust":"3d128d3cc59f702e68953ba2fe6c3f46bc6991fc575308db060482d5da0c79f3","examples/dict-search.rs":"30eb44b1a0b599507db4c23a90f74199faabc64a8ae1d603ecdf3bba7428eb1e","session.vim":"95cb1d7caf0ff7fbe76ec911988d908ddd883381c925ba64b537695bc9f021c4","src/autiter.rs":"98c31a7fbe21cfacaa858f90409f0d86edd46dda1b7651f4e800d929a50afb7b","src/full.rs":"b83a9c8ff3ef611c316b68650915df2d7f361a49b59dab103dc2c5476f2d8303","src/lib.rs":"68bf2ed02d58bebee6f7f7579038f1e4b60a2c4acc334263cb837bcbe15ffe94","src/main.rs":"fc867cb5f0b02d0f49ecab06b72c05a247cbcf3bf9228c235de8e787bda7bef5"},"package":"500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"}
|
||||
{"files":{".travis.yml":"5749b97291a6f4e6cc0b308d2d410e1ae20530f0976575581270d3d4eb60ed19","COPYING":"01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f","Cargo.toml":"64af7d0a0a299d3a05f52baed2ea87cc0d783c4fdeaa555a595aed92e5d780a6","LICENSE-MIT":"0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f","Makefile":"a45a128685a2ae7d4fa39d310786674417ee113055ef290a11f88002285865fc","README.md":"9fb3256ce6fc5b25c9a92fe3b8f7a82a26d380fcf6121c934c2bb6f85102fede","UNLICENSE":"7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c","benches/bench.rs":"c74b297ed2217e4784614573a8b44c61dfe5173985c43616e43a3f608973ff2e","benches/random.txt":"9386fb3efedc7ffbd09fb49088347f1056bc2d90a861009fa2f804cdb714efcb","ci/script.sh":"061b81cf1dba37d34063500117f5aa164919445472086665549ef2aa6a43bf77","ctags.rust":"3d128d3cc59f702e68953ba2fe6c3f46bc6991fc575308db060482d5da0c79f3","examples/dict-search.rs":"0dac88736039262dfb8df9ff85ece18de90dd4da9b8b895cf4d57bf33167b224","session.vim":"95cb1d7caf0ff7fbe76ec911988d908ddd883381c925ba64b537695bc9f021c4","src/autiter.rs":"b3bd067bf6c25708109870afbb5d09e9a04d0f2f596f59c368e42ddbc7c65f93","src/full.rs":"acb6389d210e7bc4125f0ebb6f268b712e60ec50b58b9c4094a8083bb8f4caf2","src/lib.rs":"fdddbb688fbc71461242b94e1cfb3dfbf0a9f06b2c70a6e83de15a0e54804a28","src/main.rs":"fc867cb5f0b02d0f49ecab06b72c05a247cbcf3bf9228c235de8e787bda7bef5"},"package":"68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a"}
|
|
@ -1,13 +1,10 @@
|
|||
language: rust
|
||||
rust:
|
||||
- 1.12.0
|
||||
- 1.13.0
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
script:
|
||||
- cargo build --verbose
|
||||
- cargo test --verbose
|
||||
- cargo doc
|
||||
- if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then
|
||||
cargo bench --verbose;
|
||||
fi
|
||||
script: ci/script.sh
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
|
|
@ -1,42 +1,26 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "aho-corasick"
|
||||
version = "0.6.3" #:version
|
||||
version = "0.6.8"
|
||||
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
||||
exclude = ["benches/sherlock.txt"]
|
||||
description = "Fast multiple substring searching with finite state machines."
|
||||
documentation = "http://burntsushi.net/rustdoc/aho_corasick/"
|
||||
homepage = "https://github.com/BurntSushi/aho-corasick"
|
||||
repository = "https://github.com/BurntSushi/aho-corasick"
|
||||
readme = "README.md"
|
||||
keywords = ["string", "search", "text", "aho", "corasick"]
|
||||
license = "Unlicense/MIT"
|
||||
exclude = ["benches/sherlock.txt"]
|
||||
|
||||
[lib]
|
||||
name = "aho_corasick"
|
||||
|
||||
[[bin]]
|
||||
name = "aho-corasick-dot"
|
||||
test = false
|
||||
doc = false
|
||||
bench = false
|
||||
|
||||
[dependencies]
|
||||
memchr = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
csv = "0.15"
|
||||
docopt = "0.7"
|
||||
memmap = "0.5"
|
||||
quickcheck = { version = "0.4", default-features = false }
|
||||
rand = "0.3"
|
||||
rustc-serialize = "0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "bench"
|
||||
path = "benches/bench.rs"
|
||||
test = false
|
||||
bench = true
|
||||
|
||||
repository = "https://github.com/BurntSushi/aho-corasick"
|
||||
[profile.test]
|
||||
debug = true
|
||||
|
||||
|
@ -45,3 +29,44 @@ debug = true
|
|||
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
||||
[lib]
|
||||
name = "aho_corasick"
|
||||
|
||||
[[bin]]
|
||||
name = "aho-corasick-dot"
|
||||
path = "src/main.rs"
|
||||
test = false
|
||||
bench = false
|
||||
doc = false
|
||||
|
||||
[[bench]]
|
||||
name = "bench"
|
||||
path = "benches/bench.rs"
|
||||
test = false
|
||||
bench = true
|
||||
[dependencies.memchr]
|
||||
version = "2"
|
||||
[dev-dependencies.csv]
|
||||
version = "1"
|
||||
|
||||
[dev-dependencies.docopt]
|
||||
version = "1"
|
||||
|
||||
[dev-dependencies.memmap]
|
||||
version = "0.6"
|
||||
|
||||
[dev-dependencies.quickcheck]
|
||||
version = "0.7"
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies.rand]
|
||||
version = "0.5"
|
||||
|
||||
[dev-dependencies.serde]
|
||||
version = "1"
|
||||
|
||||
[dev-dependencies.serde_derive]
|
||||
version = "1"
|
||||
[badges.travis-ci]
|
||||
repository = "BurntSushi/aho-corasick"
|
||||
|
|
|
@ -17,7 +17,7 @@ Dual-licensed under MIT or the [UNLICENSE](http://unlicense.org).
|
|||
|
||||
### Documentation
|
||||
|
||||
[http://burntsushi.net/rustdoc/aho_corasick/](http://burntsushi.net/rustdoc/aho_corasick/).
|
||||
[https://docs.rs/aho-corasick/](https://docs.rs/aho-corasick/).
|
||||
|
||||
|
||||
### Example
|
||||
|
|
|
@ -57,6 +57,40 @@ fn bench_naive_no_match<S>(b: &mut Bencher, needles: Vec<S>, haystack: &str)
|
|||
b.iter(|| assert!(!naive_find(&needles, haystack)));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_construction(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
AcAutomaton::new(test::black_box(
|
||||
[
|
||||
"ADL", "ADl", "AdL", "Adl", "BAK", "BAk", "BAK", "BaK", "Bak", "BaK", "HOL",
|
||||
"HOl", "HoL", "Hol", "IRE", "IRe", "IrE", "Ire", "JOH", "JOh", "JoH", "Joh", "SHE",
|
||||
"SHe", "ShE", "She", "WAT", "WAt", "WaT", "Wat", "aDL", "aDl", "adL", "adl", "bAK",
|
||||
"bAk", "bAK", "baK", "bak", "baK", "hOL", "hOl", "hoL", "hol", "iRE", "iRe",
|
||||
"irE", "ire", "jOH", "jOh", "joH", "joh", "sHE", "sHe", "shE", "she", "wAT", "wAt",
|
||||
"waT", "wat", "ſHE", "ſHe", "ſhE", "ſhe",
|
||||
].iter()
|
||||
.map(|x| *x),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_full_construction(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
AcAutomaton::new(test::black_box(
|
||||
[
|
||||
"ADL", "ADl", "AdL", "Adl", "BAK", "BAk", "BAK", "BaK", "Bak", "BaK", "HOL",
|
||||
"HOl", "HoL", "Hol", "IRE", "IRe", "IrE", "Ire", "JOH", "JOh", "JoH", "Joh", "SHE",
|
||||
"SHe", "ShE", "She", "WAT", "WAt", "WaT", "Wat", "aDL", "aDl", "adL", "adl", "bAK",
|
||||
"bAk", "bAK", "baK", "bak", "baK", "hOL", "hOl", "hoL", "hol", "iRE", "iRe",
|
||||
"irE", "ire", "jOH", "jOh", "joH", "joh", "sHE", "sHe", "shE", "she", "wAT", "wAt",
|
||||
"waT", "wat", "ſHE", "ſHe", "ſhE", "ſhe",
|
||||
].iter()
|
||||
.map(|x| *x),
|
||||
)).into_full()
|
||||
})
|
||||
}
|
||||
|
||||
fn haystack_same(letter: char) -> String {
|
||||
iter::repeat(letter).take(10000).collect()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
cargo build --verbose
|
||||
cargo doc --verbose
|
||||
|
||||
# If we're testing on an older version of Rust, then only check that we
|
||||
# can build the crate. This is because the dev dependencies might be updated
|
||||
# more frequently, and therefore might require a newer version of Rust.
|
||||
#
|
||||
# This isn't ideal. It's a compromise.
|
||||
if [ "$TRAVIS_RUST_VERSION" = "1.13.0" ]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
cargo test --verbose
|
||||
if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then
|
||||
cargo bench --verbose --no-run
|
||||
fi
|
|
@ -5,7 +5,9 @@ extern crate aho_corasick;
|
|||
extern crate csv;
|
||||
extern crate docopt;
|
||||
extern crate memmap;
|
||||
extern crate rustc_serialize;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
|
@ -14,7 +16,7 @@ use std::process;
|
|||
|
||||
use aho_corasick::{Automaton, AcAutomaton, Match};
|
||||
use docopt::Docopt;
|
||||
use memmap::{Mmap, Protection};
|
||||
use memmap::Mmap;
|
||||
|
||||
static USAGE: &'static str = "
|
||||
Usage: dict-search [options] <input>
|
||||
|
@ -33,7 +35,7 @@ Options:
|
|||
-h, --help Show this usage message.
|
||||
";
|
||||
|
||||
#[derive(Clone, Debug, RustcDecodable)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct Args {
|
||||
arg_input: String,
|
||||
flag_dict: String,
|
||||
|
@ -46,7 +48,7 @@ struct Args {
|
|||
|
||||
fn main() {
|
||||
let args: Args = Docopt::new(USAGE)
|
||||
.and_then(|d| d.decode())
|
||||
.and_then(|d| d.deserialize())
|
||||
.unwrap_or_else(|e| e.exit());
|
||||
match run(&args) {
|
||||
Ok(()) => {}
|
||||
|
@ -70,48 +72,37 @@ fn run(args: &Args) -> Result<(), Box<Error>> {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let rdr = try!(File::open(&args.arg_input));
|
||||
if args.flag_full {
|
||||
let aut = aut.into_full();
|
||||
if args.flag_overlapping {
|
||||
if args.flag_count {
|
||||
let mmap = Mmap::open_path(
|
||||
&args.arg_input, Protection::Read).unwrap();
|
||||
let text = unsafe { mmap.as_slice() };
|
||||
println!("{}", aut.find_overlapping(text).count());
|
||||
let mmap = unsafe { try!(Mmap::map(&rdr)) };
|
||||
println!("{}", aut.find_overlapping(&*mmap).count());
|
||||
} else {
|
||||
let rdr = try!(File::open(&args.arg_input));
|
||||
try!(write_matches(&aut, aut.stream_find_overlapping(rdr)));
|
||||
}
|
||||
} else {
|
||||
if args.flag_count {
|
||||
let mmap = Mmap::open_path(
|
||||
&args.arg_input, Protection::Read).unwrap();
|
||||
let text = unsafe { mmap.as_slice() };
|
||||
println!("{}", aut.find(text).count());
|
||||
let mmap = unsafe { try!(Mmap::map(&rdr)) };
|
||||
println!("{}", aut.find(&*mmap).count());
|
||||
} else {
|
||||
let rdr = try!(File::open(&args.arg_input));
|
||||
try!(write_matches(&aut, aut.stream_find(rdr)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if args.flag_overlapping {
|
||||
if args.flag_count {
|
||||
let mmap = Mmap::open_path(
|
||||
&args.arg_input, Protection::Read).unwrap();
|
||||
let text = unsafe { mmap.as_slice() };
|
||||
println!("{}", aut.find_overlapping(text).count());
|
||||
let mmap = unsafe { try!(Mmap::map(&rdr)) };
|
||||
println!("{}", aut.find_overlapping(&*mmap).count());
|
||||
} else {
|
||||
let rdr = try!(File::open(&args.arg_input));
|
||||
try!(write_matches(&aut, aut.stream_find_overlapping(rdr)));
|
||||
}
|
||||
} else {
|
||||
if args.flag_count {
|
||||
let mmap = Mmap::open_path(
|
||||
&args.arg_input, Protection::Read).unwrap();
|
||||
let text = unsafe { mmap.as_slice() };
|
||||
println!("{}", aut.find(text).count());
|
||||
let mmap = unsafe { try!(Mmap::map(&rdr)) };
|
||||
println!("{}", aut.find(&*mmap).count());
|
||||
} else {
|
||||
let rdr = try!(File::open(&args.arg_input));
|
||||
try!(write_matches(&aut, aut.stream_find(rdr)));
|
||||
}
|
||||
}
|
||||
|
@ -122,14 +113,10 @@ fn run(args: &Args) -> Result<(), Box<Error>> {
|
|||
fn write_matches<A, I>(aut: &A, it: I) -> Result<(), Box<Error>>
|
||||
where A: Automaton<String>, I: Iterator<Item=io::Result<Match>> {
|
||||
let mut wtr = csv::Writer::from_writer(io::stdout());
|
||||
try!(wtr.write(["pattern", "start", "end"].iter()));
|
||||
try!(wtr.serialize(("pattern", "start", "end")));
|
||||
for m in it {
|
||||
let m = try!(m);
|
||||
try!(wtr.write([
|
||||
aut.pattern(m.pati),
|
||||
&m.start.to_string(),
|
||||
&m.end.to_string(),
|
||||
].iter()));
|
||||
try!(wtr.serialize((aut.pattern(m.pati), m.start, m.end)));
|
||||
}
|
||||
try!(wtr.flush());
|
||||
Ok(())
|
||||
|
|
|
@ -42,7 +42,7 @@ pub trait Automaton<P> {
|
|||
/// Returns true if the automaton has no patterns.
|
||||
#[inline]
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
self.patterns().is_empty()
|
||||
}
|
||||
|
||||
/// Returns an iterator of non-overlapping matches in `s`.
|
||||
|
@ -381,7 +381,7 @@ impl<'a, R: io::Read, P, A: Automaton<P>>
|
|||
self.buf.consume(consumed);
|
||||
let bs = match self.buf.fill_buf() {
|
||||
Err(err) => return Some(Err(err)),
|
||||
Ok(bs) if bs.len() == 0 => break,
|
||||
Ok(bs) if bs.is_empty() => break,
|
||||
Ok(bs) => bs,
|
||||
};
|
||||
consumed = bs.len(); // is shortened if we find a match
|
||||
|
@ -505,7 +505,7 @@ impl<'a, R: io::Read, P, A: Automaton<P> + ?Sized>
|
|||
self.buf.consume(consumed);
|
||||
let bs = match self.buf.fill_buf() {
|
||||
Err(err) => return Some(Err(err)),
|
||||
Ok(bs) if bs.len() == 0 => break,
|
||||
Ok(bs) if bs.is_empty() => break,
|
||||
Ok(bs) => bs,
|
||||
};
|
||||
consumed = bs.len(); // is shortened if we find a match
|
||||
|
|
|
@ -44,11 +44,11 @@ impl<P: AsRef<[u8]>> FullAcAutomaton<P> {
|
|||
pub fn memory_usage(&self) -> usize {
|
||||
self.pats.iter()
|
||||
.map(|p| vec_bytes() + p.as_ref().len())
|
||||
.fold(0, |a, b| a + b)
|
||||
.sum::<usize>()
|
||||
+ (4 * self.trans.len())
|
||||
+ self.out.iter()
|
||||
.map(|v| vec_bytes() + (usize_bytes() * v.len()))
|
||||
.fold(0, |a, b| a + b)
|
||||
.sum::<usize>()
|
||||
+ self.start_bytes.len()
|
||||
}
|
||||
|
||||
|
@ -56,11 +56,11 @@ impl<P: AsRef<[u8]>> FullAcAutomaton<P> {
|
|||
pub fn heap_bytes(&self) -> usize {
|
||||
self.pats.iter()
|
||||
.map(|p| mem::size_of::<P>() + p.as_ref().len())
|
||||
.fold(0, |a, b| a + b)
|
||||
.sum::<usize>()
|
||||
+ (4 * self.trans.len())
|
||||
+ self.out.iter()
|
||||
.map(|v| vec_bytes() + (usize_bytes() * v.len()))
|
||||
.fold(0, |a, b| a + b)
|
||||
.sum::<usize>()
|
||||
+ self.start_bytes.len()
|
||||
}
|
||||
|
||||
|
@ -119,14 +119,23 @@ impl<P: AsRef<[u8]>> Automaton<P> for FullAcAutomaton<P> {
|
|||
impl<P: AsRef<[u8]>> FullAcAutomaton<P> {
|
||||
fn build_matrix<T: Transitions>(&mut self, ac: &AcAutomaton<P, T>) {
|
||||
for (si, s) in ac.states.iter().enumerate().skip(1) {
|
||||
for b in (0..256).map(|b| b as u8) {
|
||||
self.set(si as StateIdx, b, ac.next_state(si as StateIdx, b));
|
||||
}
|
||||
for &pati in &s.out {
|
||||
self.out[si].push(pati);
|
||||
}
|
||||
self.set_states(ac, si as StateIdx);
|
||||
self.out[si].extend_from_slice(&s.out);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_states<T: Transitions>(&mut self, ac: &AcAutomaton<P, T>, si: StateIdx) {
|
||||
let current_state = &ac.states[si as usize];
|
||||
let first_fail_state = current_state.fail;
|
||||
current_state.for_each_transition(move |b, maybe_si| {
|
||||
let goto = if maybe_si == FAIL_STATE {
|
||||
ac.memoized_next_state(self, si, first_fail_state, b)
|
||||
} else {
|
||||
maybe_si
|
||||
};
|
||||
self.set(si, b, goto);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: AsRef<[u8]> + fmt::Debug> fmt::Debug for FullAcAutomaton<P> {
|
||||
|
|
|
@ -121,8 +121,10 @@ assert_eq!(matches, vec![Match { pati: 1, start: 0, end: 1}]);
|
|||
#![deny(missing_docs)]
|
||||
|
||||
extern crate memchr;
|
||||
#[cfg(test)] extern crate quickcheck;
|
||||
#[cfg(test)] extern crate rand;
|
||||
#[cfg(test)]
|
||||
extern crate quickcheck;
|
||||
#[cfg(test)]
|
||||
extern crate rand;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
|
@ -241,24 +243,47 @@ impl<P: AsRef<[u8]>, T: Transitions> AcAutomaton<P, T> {
|
|||
pub fn heap_bytes(&self) -> usize {
|
||||
self.pats.iter()
|
||||
.map(|p| mem::size_of::<P>() + p.as_ref().len())
|
||||
.fold(0, |a, b| a + b)
|
||||
.sum::<usize>()
|
||||
+ self.states.iter()
|
||||
.map(|s| mem::size_of::<State<T>>() + s.heap_bytes())
|
||||
.fold(0, |a, b| a + b)
|
||||
.sum::<usize>()
|
||||
+ self.start_bytes.len()
|
||||
}
|
||||
|
||||
// The states of `full_automaton` should be set for all states < si
|
||||
fn memoized_next_state(
|
||||
&self,
|
||||
full_automaton: &FullAcAutomaton<P>,
|
||||
current_si: StateIdx,
|
||||
mut si: StateIdx,
|
||||
b: u8,
|
||||
) -> StateIdx {
|
||||
loop {
|
||||
if si < current_si {
|
||||
return full_automaton.next_state(si, b);
|
||||
}
|
||||
let state = &self.states[si as usize];
|
||||
let maybe_si = state.goto(b);
|
||||
if maybe_si != FAIL_STATE {
|
||||
return maybe_si;
|
||||
} else {
|
||||
si = state.fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: AsRef<[u8]>, T: Transitions> Automaton<P> for AcAutomaton<P, T> {
|
||||
#[inline]
|
||||
fn next_state(&self, mut si: StateIdx, b: u8) -> StateIdx {
|
||||
loop {
|
||||
let maybe_si = self.states[si as usize].goto(b);
|
||||
let state = &self.states[si as usize];
|
||||
let maybe_si = state.goto(b);
|
||||
if maybe_si != FAIL_STATE {
|
||||
si = maybe_si;
|
||||
break;
|
||||
} else {
|
||||
si = self.states[si as usize].fail;
|
||||
si = state.fail;
|
||||
}
|
||||
}
|
||||
si
|
||||
|
@ -297,6 +322,29 @@ impl<P: AsRef<[u8]>, T: Transitions> Automaton<P> for AcAutomaton<P, T> {
|
|||
}
|
||||
}
|
||||
|
||||
// `(0..256).map(|b| b as u8)` optimizes poorly in debug builds so
|
||||
// we use this small explicit iterator instead
|
||||
struct AllBytesIter(i32);
|
||||
impl Iterator for AllBytesIter {
|
||||
type Item = u8;
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.0 < 256 {
|
||||
let b = self.0 as u8;
|
||||
self.0 += 1;
|
||||
Some(b)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AllBytesIter {
|
||||
fn new() -> AllBytesIter {
|
||||
AllBytesIter(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Below contains code for *building* the automaton. It's a reasonably faithful
|
||||
// translation of the description/psuedo-code from:
|
||||
// http://www.cs.uku.fi/~kilpelai/BSA05/lectures/slides04.pdf
|
||||
|
@ -321,11 +369,14 @@ impl<P: AsRef<[u8]>, T: Transitions> AcAutomaton<P, T> {
|
|||
}
|
||||
self.states[previ as usize].out.push(pati);
|
||||
}
|
||||
for c in (0..256).into_iter().map(|c| c as u8) {
|
||||
if self.states[ROOT_STATE as usize].goto(c) == FAIL_STATE {
|
||||
self.states[ROOT_STATE as usize].set_goto(c, ROOT_STATE);
|
||||
} else {
|
||||
self.start_bytes.push(c);
|
||||
{
|
||||
let root_state = &mut self.states[ROOT_STATE as usize];
|
||||
for c in AllBytesIter::new() {
|
||||
if root_state.goto(c) == FAIL_STATE {
|
||||
root_state.set_goto(c, ROOT_STATE);
|
||||
} else {
|
||||
self.start_bytes.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If any of the start bytes are non-ASCII, then remove them all,
|
||||
|
@ -344,26 +395,45 @@ impl<P: AsRef<[u8]>, T: Transitions> AcAutomaton<P, T> {
|
|||
// Fill up the queue with all non-root transitions out of the root
|
||||
// node. Then proceed by breadth first traversal.
|
||||
let mut q = VecDeque::new();
|
||||
for c in (0..256).into_iter().map(|c| c as u8) {
|
||||
let si = self.states[ROOT_STATE as usize].goto(c);
|
||||
self.states[ROOT_STATE as usize].for_each_transition(|_, si| {
|
||||
if si != ROOT_STATE {
|
||||
q.push_front(si);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut transitions = Vec::new();
|
||||
|
||||
while let Some(si) = q.pop_back() {
|
||||
for c in (0..256).into_iter().map(|c| c as u8) {
|
||||
let u = self.states[si as usize].goto(c);
|
||||
if u != FAIL_STATE {
|
||||
q.push_front(u);
|
||||
let mut v = self.states[si as usize].fail;
|
||||
while self.states[v as usize].goto(c) == FAIL_STATE {
|
||||
v = self.states[v as usize].fail;
|
||||
self.states[si as usize].for_each_ok_transition(|c, u| {
|
||||
transitions.push((c, u));
|
||||
q.push_front(u);
|
||||
});
|
||||
|
||||
for (c, u) in transitions.drain(..) {
|
||||
let mut v = self.states[si as usize].fail;
|
||||
loop {
|
||||
let state = &self.states[v as usize];
|
||||
if state.goto(c) == FAIL_STATE {
|
||||
v = state.fail;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
let ufail = self.states[v as usize].goto(c);
|
||||
self.states[u as usize].fail = ufail;
|
||||
let ufail_out = self.states[ufail as usize].out.clone();
|
||||
self.states[u as usize].out.extend(ufail_out);
|
||||
}
|
||||
let ufail = self.states[v as usize].goto(c);
|
||||
self.states[u as usize].fail = ufail;
|
||||
|
||||
fn get_two<T>(xs: &mut [T], i: usize, j: usize) -> (&mut T, &mut T) {
|
||||
if i < j {
|
||||
let (before, after) = xs.split_at_mut(j);
|
||||
(&mut before[i], &mut after[0])
|
||||
} else {
|
||||
let (before, after) = xs.split_at_mut(i);
|
||||
(&mut after[0], &mut before[j])
|
||||
}
|
||||
}
|
||||
|
||||
let (ufail_out, out) = get_two(&mut self.states, ufail as usize, u as usize);
|
||||
out.out.extend_from_slice(&ufail_out.out);
|
||||
}
|
||||
}
|
||||
self
|
||||
|
@ -398,6 +468,18 @@ impl<T: Transitions> State<T> {
|
|||
(self.out.len() * usize_bytes())
|
||||
+ self.goto.heap_bytes()
|
||||
}
|
||||
|
||||
fn for_each_transition<F>(&self, f: F)
|
||||
where F: FnMut(u8, StateIdx)
|
||||
{
|
||||
self.goto.for_each_transition(f)
|
||||
}
|
||||
|
||||
fn for_each_ok_transition<F>(&self, f: F)
|
||||
where F: FnMut(u8, StateIdx)
|
||||
{
|
||||
self.goto.for_each_ok_transition(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// An abstraction over state transition strategies.
|
||||
|
@ -416,6 +498,27 @@ pub trait Transitions {
|
|||
fn set_goto(&mut self, alpha: u8, si: StateIdx);
|
||||
/// The memory use in bytes (on the heap) of this set of transitions.
|
||||
fn heap_bytes(&self) -> usize;
|
||||
|
||||
/// Iterates over each state
|
||||
fn for_each_transition<F>(&self, mut f: F)
|
||||
where F: FnMut(u8, StateIdx)
|
||||
{
|
||||
for b in AllBytesIter::new() {
|
||||
f(b, self.goto(b));
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates over each non-fail state
|
||||
fn for_each_ok_transition<F>(&self, mut f: F)
|
||||
where
|
||||
F: FnMut(u8, StateIdx),
|
||||
{
|
||||
self.for_each_transition(|b, si| {
|
||||
if si != FAIL_STATE {
|
||||
f(b, si);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// State transitions that can be stored either sparsely or densely.
|
||||
|
@ -426,14 +529,14 @@ pub struct Dense(DenseChoice);
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
enum DenseChoice {
|
||||
Sparse(Vec<StateIdx>), // indexed by alphabet
|
||||
Sparse(Box<Sparse>),
|
||||
Dense(Vec<(u8, StateIdx)>),
|
||||
}
|
||||
|
||||
impl Transitions for Dense {
|
||||
fn new(depth: u32) -> Dense {
|
||||
if depth <= DENSE_DEPTH_THRESHOLD {
|
||||
Dense(DenseChoice::Sparse(vec![0; 256]))
|
||||
Dense(DenseChoice::Sparse(Box::new(Sparse::new(depth))))
|
||||
} else {
|
||||
Dense(DenseChoice::Dense(vec![]))
|
||||
}
|
||||
|
@ -441,7 +544,7 @@ impl Transitions for Dense {
|
|||
|
||||
fn goto(&self, b1: u8) -> StateIdx {
|
||||
match self.0 {
|
||||
DenseChoice::Sparse(ref m) => m[b1 as usize],
|
||||
DenseChoice::Sparse(ref m) => m.goto(b1),
|
||||
DenseChoice::Dense(ref m) => {
|
||||
for &(b2, si) in m {
|
||||
if b1 == b2 {
|
||||
|
@ -455,29 +558,75 @@ impl Transitions for Dense {
|
|||
|
||||
fn set_goto(&mut self, b: u8, si: StateIdx) {
|
||||
match self.0 {
|
||||
DenseChoice::Sparse(ref mut m) => m[b as usize] = si,
|
||||
DenseChoice::Sparse(ref mut m) => m.set_goto(b, si),
|
||||
DenseChoice::Dense(ref mut m) => m.push((b, si)),
|
||||
}
|
||||
}
|
||||
|
||||
fn heap_bytes(&self) -> usize {
|
||||
match self.0 {
|
||||
DenseChoice::Sparse(ref m) => m.len() * 4,
|
||||
DenseChoice::Sparse(_) => mem::size_of::<Sparse>(),
|
||||
DenseChoice::Dense(ref m) => m.len() * (1 + 4),
|
||||
}
|
||||
}
|
||||
|
||||
fn for_each_transition<F>(&self, mut f: F)
|
||||
where F: FnMut(u8, StateIdx)
|
||||
{
|
||||
match self.0 {
|
||||
DenseChoice::Sparse(ref m) => m.for_each_transition(f),
|
||||
DenseChoice::Dense(ref m) => {
|
||||
let mut iter = m.iter();
|
||||
let mut b = 0i32;
|
||||
while let Some(&(next_b, next_si)) = iter.next() {
|
||||
while (b as u8) < next_b {
|
||||
f(b as u8, FAIL_STATE);
|
||||
b += 1;
|
||||
}
|
||||
f(b as u8, next_si);
|
||||
b += 1;
|
||||
}
|
||||
while b < 256 {
|
||||
f(b as u8, FAIL_STATE);
|
||||
b += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn for_each_ok_transition<F>(&self, mut f: F)
|
||||
where
|
||||
F: FnMut(u8, StateIdx),
|
||||
{
|
||||
match self.0 {
|
||||
DenseChoice::Sparse(ref m) => m.for_each_ok_transition(f),
|
||||
DenseChoice::Dense(ref m) => for &(b, si) in m {
|
||||
f(b, si)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// State transitions that are always sparse.
|
||||
///
|
||||
/// This can use enormous amounts of memory when there are many patterns,
|
||||
/// but matching is very fast.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Sparse(Vec<StateIdx>);
|
||||
pub struct Sparse([StateIdx; 256]);
|
||||
|
||||
impl Clone for Sparse {
|
||||
fn clone(&self) -> Sparse {
|
||||
Sparse(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Sparse {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_tuple("Sparse").field(&&self.0[..]).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Transitions for Sparse {
|
||||
fn new(_: u32) -> Sparse {
|
||||
Sparse(vec![0; 256])
|
||||
Sparse([0; 256])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -490,7 +639,7 @@ impl Transitions for Sparse {
|
|||
}
|
||||
|
||||
fn heap_bytes(&self) -> usize {
|
||||
self.0.len() * 4
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -525,15 +674,13 @@ impl<T: Transitions> State<T> {
|
|||
}
|
||||
|
||||
fn goto_string(&self, root: bool) -> String {
|
||||
use std::char::from_u32;
|
||||
|
||||
let mut goto = vec![];
|
||||
for b in (0..256).map(|b| b as u8) {
|
||||
for b in AllBytesIter::new() {
|
||||
let si = self.goto(b);
|
||||
if (!root && si == FAIL_STATE) || (root && si == ROOT_STATE) {
|
||||
continue;
|
||||
}
|
||||
goto.push(format!("{} => {}", from_u32(b as u32).unwrap(), si));
|
||||
goto.push(format!("{} => {}", b as char, si));
|
||||
}
|
||||
goto.join(", ")
|
||||
}
|
||||
|
@ -563,13 +710,13 @@ digraph automaton {{
|
|||
"#, self.pats.join(", "));
|
||||
for (i, s) in self.states.iter().enumerate().skip(1) {
|
||||
let i = i as u32;
|
||||
if s.out.len() == 0 {
|
||||
if s.out.is_empty() {
|
||||
w!(out, " {};\n", i);
|
||||
} else {
|
||||
w!(out, " {} [peripheries=2];\n", i);
|
||||
}
|
||||
w!(out, " {} -> {} [style=dashed];\n", i, s.fail);
|
||||
for b in (0..256).map(|b| b as u8) {
|
||||
for b in AllBytesIter::new() {
|
||||
let si = s.goto(b);
|
||||
if si == FAIL_STATE || (i == ROOT_STATE && si == ROOT_STATE) {
|
||||
continue;
|
||||
|
@ -597,8 +744,9 @@ mod tests {
|
|||
use std::io;
|
||||
|
||||
use quickcheck::{Arbitrary, Gen, quickcheck};
|
||||
use rand::Rng;
|
||||
|
||||
use super::{Automaton, AcAutomaton, Match};
|
||||
use super::{AcAutomaton, Automaton, Match, AllBytesIter};
|
||||
|
||||
fn aut_find<S>(xs: &[S], haystack: &str) -> Vec<Match>
|
||||
where S: Clone + AsRef<[u8]> {
|
||||
|
@ -870,7 +1018,7 @@ mod tests {
|
|||
let size = { let s = g.size(); g.gen_range(0, s) };
|
||||
let mut s = String::with_capacity(size);
|
||||
for _ in 0..size {
|
||||
if g.gen_weighted_bool(3) {
|
||||
if g.gen_bool(0.3) {
|
||||
s.push(char::arbitrary(g));
|
||||
} else {
|
||||
for _ in 0..5 {
|
||||
|
@ -922,4 +1070,13 @@ mod tests {
|
|||
}
|
||||
quickcheck(prop as fn(Vec<SmallAscii>, BiasAscii) -> bool);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn all_bytes_iter() {
|
||||
let all_bytes = AllBytesIter::new().collect::<Vec<_>>();
|
||||
assert_eq!(all_bytes[0], 0);
|
||||
assert_eq!(all_bytes[255], 255);
|
||||
assert!(AllBytesIter::new().enumerate().all(|(i, b)| b as usize == i));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{".travis.yml":"4752c993a36dc8b271f25998b2c0b34af65f82fb61f7d71d0e34612a8a7cd5b0","CHANGELOG.md":"a182831141a059342664a8aaf40b9fd7828e8004094fb42e1b17129a090899ec","Cargo.toml":"5fa1586ac82ee945f057b87c81acae6e588de2303536445b4a766028633347e0","LICENSE":"235760c32039b0a6b23207918b71c1aa5d8318ee651c0f245d290ba1f47631cf","README.md":"b23f66e15c8311e11cbc3b10bfc87a7cb10bc4d758c6a352b155127b48b970d7","appveyor.yml":"dfe3d3eddd762a3cc76174e03ea91c93f544ce7fa05fbca4975f1624757d65e4","examples/atty.rs":"1551387a71474d9ac1b5153231f884e9e05213badcfaa3494ad2cb7ea958374a","src/lib.rs":"4530fe39e123b042eb023e4cf98a81d5184d06c938d3604b002f418101beb524"},"package":"d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"}
|
||||
{"files":{".travis.yml":"9002419ea748e146ea85fd5c2646aaa6649168d4baf2e23de834c7e13e5742d3","CHANGELOG.md":"7bd35273865921a87f96ea8d69bd98bde11a96001b74c79cdcdbc83bfbbee53c","Cargo.toml":"77b51a9746012bf939c0fd0a72daa9db816c26798c557c234213a2d38b8aae81","LICENSE":"f3f8d32084848316048c5a1e125a3c5003eb32145a5f5f2a0d5586377324f9ba","README.md":"a62d294c45c9d8b2e54fcf35d9ee1ba8b8e2ab6960fb3d3f4cc9d59e8aed0835","appveyor.yml":"dea9c8da309cbb02bce31c613b697256f4cfada20b2f7b0c8911b73d569daf58","examples/atty.rs":"1551387a71474d9ac1b5153231f884e9e05213badcfaa3494ad2cb7ea958374a","rustfmt.toml":"bd196700242d17913cf8adead6912f55e9347e52ab5a001729d6c18d169f05c4","src/lib.rs":"ec3428266e83b35a8714ab99d9962c9e29f78becb39e313846f042f5b176c723"},"package":"9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"}
|
|
@ -4,41 +4,70 @@ matrix:
|
|||
fast_finish: true
|
||||
include:
|
||||
- rust: nightly
|
||||
- rust: nightly
|
||||
os: osx
|
||||
- rust: beta
|
||||
- rust: beta
|
||||
os: osx
|
||||
- rust: stable
|
||||
- rust: 1.8.0
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
- rust: stable
|
||||
os: osx
|
||||
allow_failures:
|
||||
- rust: nightly
|
||||
|
||||
before_cache:
|
||||
# Travis can't cache files that are not readable by "others"
|
||||
- chmod -R a+r $HOME/.cargo
|
||||
|
||||
before_install:
|
||||
# install kcov
|
||||
- >
|
||||
if [ ! -d "$HOME/.kcov/bin" ]; then
|
||||
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
|
||||
tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build &&
|
||||
cmake -DCMAKE_INSTALL_PREFIX:PATH=$HOME/.kcov .. && make && make install && cd ../..
|
||||
fi
|
||||
- export PATH=$HOME/.kcov/bin:$PATH
|
||||
|
||||
script:
|
||||
- cargo build
|
||||
- cargo test
|
||||
|
||||
cache:
|
||||
cargo: true
|
||||
apt: true
|
||||
directories:
|
||||
- target/debug/deps
|
||||
- target/debug/build
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libcurl4-openssl-dev
|
||||
- libelf-dev
|
||||
- libdw-dev
|
||||
- binutils-dev # required for `kcov --verify`
|
||||
- libbfd-dev # required for `kcov --verify`
|
||||
after_success: |
|
||||
[ $TRAVIS_RUST_VERSION = stable ] &&
|
||||
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
|
||||
tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build && cmake .. && make && make install DESTDIR=../tmp && cd ../.. &&
|
||||
ls target/debug &&
|
||||
./kcov-master/tmp/usr/local/bin/kcov --verify --coveralls-id=$TRAVIS_JOB_ID --exclude-pattern=/.cargo target/kcov target/debug/atty-* &&
|
||||
[ $TRAVIS_BRANCH = master ] &&
|
||||
[ $TRAVIS_PULL_REQUEST = false ] &&
|
||||
cargo doc --no-deps &&
|
||||
echo "<meta http-equiv=refresh content=0;url=`echo $TRAVIS_REPO_SLUG | cut -d '/' -f 2`/index.html>" > target/doc/index.html &&
|
||||
pip install --user ghp-import &&
|
||||
/home/travis/.local/bin/ghp-import -n target/doc &&
|
||||
git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
|
||||
env:
|
||||
global:
|
||||
secure: acjXoBFG4yFklz/iW4q9PLaMmTgug0c8hOov4uiaXYjDkVGhnEePBozGc8ctKuFv2BVlwBSzvE1neE9dHcCS6il0x+G79sVTekfVN5dERja3UpwrC0/QodJuDmErIUpb6zylupPnUGq5pzZabRPNKyAnsFS5wYhLMSLxGPu4pfYdW0Eu8CEPIgPYsI6o2pfKgNpXbeizdHRLMeZCN4cbEPohO1odc+Z6WJvgKn2xEkpAcfhAuaroqGGxRtmDiJZ/JaBijAKY/O9Q3Xq1GSGOPT5lmwJSp3Fxw5dgmeX6LmN0ZODASdnEoYfoqUDUFzkCON3Sk4a7hugxlkZ7cx1tfqXxMg+0BgYIUdGQNloDJnuusWvXPBFdB2jxMsfcbrCjNsrJ8kjN6uBsW9yy0kqN7a8eOJckwh5fYRWfNta0R+BrveNXWmGp4u4aBq/85jEiHi30XKTzaEUbF0Y3cIONweWeWwBOcAvPBhO63Y07TRRe+SSk1NYm7QHGW9RsHhz89OSbaIXqn+r/o+6DZcw5XaO73DtZ62Kx48NErej9kVqcIJ6HnyvCJ/fJoT7h1ixSRI/WmS30l2S/q33Q2G4C/IZ4ZZRD/1thSltAxeA6OAUnr8ITZyW47CqOmyL1IUptrdAb9OLEedYV/QrOhcg2RJLXyP66xnItOwMp014bEp4=
|
||||
- libcurl4-openssl-dev
|
||||
- libelf-dev
|
||||
- libdw-dev
|
||||
- binutils-dev
|
||||
- libiberty-dev
|
||||
|
||||
after_success:
|
||||
- '[ $TRAVIS_RUST_VERSION = stable ] &&
|
||||
[ $TRAVIS_BRANCH = master ] &&
|
||||
[ $TRAVIS_PULL_REQUEST = false ] &&
|
||||
(ls target/debug &&
|
||||
RUSTFLAGS="-C link-dead-code" cargo test --no-run &&
|
||||
for file in target/debug/atty-*; do
|
||||
if [[ "${file: -2}" != ".d" ]]; then
|
||||
mkdir -p "target/cov/$(basename $file)";
|
||||
kcov --exclude-pattern=/.cargo,/usr/lib --verify "target/cov/$(basename $file)" "$file";
|
||||
fi;
|
||||
done &&
|
||||
kcov --coveralls-id=$COVERALLS_REPO_TOKEN --merge target/cov target/cov/* &&
|
||||
echo "covered") || true'
|
||||
- '[ $TRAVIS_RUST_VERSION = stable ] &&
|
||||
[ $TRAVIS_BRANCH = master ] &&
|
||||
[ $TRAVIS_PULL_REQUEST = false ]
|
||||
&& cargo doc --no-deps &&
|
||||
echo "<meta http-equiv=refresh content=0;url=`echo $TRAVIS_REPO_SLUG | cut -d / -f 2`/index.html>" > target/doc/index.html &&
|
||||
pip install --user ghp-import &&
|
||||
/home/travis/.local/bin/ghp-import -n target/doc &&
|
||||
git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages &&
|
||||
echo "documented"'
|
|
@ -1,3 +1,39 @@
|
|||
# 0.2.11
|
||||
|
||||
* fix msys detection with `winapi@0.3.5` [#28](https://github.com/softprops/atty/pull/28)
|
||||
|
||||
# 0.2.10
|
||||
|
||||
* fix wasm regression [#27](https://github.com/softprops/atty/pull/27)
|
||||
|
||||
# 0.2.9
|
||||
|
||||
* Fix fix pty detection [#25](https://github.com/softprops/atty/pull/25)
|
||||
|
||||
# 0.2.8
|
||||
|
||||
* Fix an inverted condition on MinGW [#22](https://github.com/softprops/atty/pull/22)
|
||||
|
||||
# 0.2.7
|
||||
|
||||
* Change `||` to `&&` for whether MSYS is a tty [#24](https://github.com/softprops/atty/pull/24/)
|
||||
|
||||
# 0.2.6
|
||||
|
||||
* updated winapi dependency to [0.3](https://retep998.github.io/blog/winapi-0.3/) [#18](https://github.com/softprops/atty/pull/18)
|
||||
|
||||
# 0.2.5
|
||||
|
||||
* added support for Wasm compile targets [#17](https://github.com/softprops/atty/pull/17)
|
||||
|
||||
# 0.2.4
|
||||
|
||||
* added support for Wasm compile targets [#17](https://github.com/softprops/atty/pull/17)
|
||||
|
||||
# 0.2.3
|
||||
|
||||
* added support for Redox OS [#14](https://github.com/softprops/atty/pull/14)
|
||||
|
||||
# 0.2.2
|
||||
|
||||
* use target specific dependencies [#11](https://github.com/softprops/atty/pull/11)
|
||||
|
|
|
@ -1,17 +1,33 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "atty"
|
||||
version = "0.2.2"
|
||||
version = "0.2.11"
|
||||
authors = ["softprops <d.tangren@gmail.com>"]
|
||||
description = "A simple interface for querying atty"
|
||||
documentation = "http://softprops.github.io/atty"
|
||||
homepage = "https://github.com/softprops/atty"
|
||||
repository = "https://github.com/softprops/atty"
|
||||
documentation = "http://softprops.github.io/atty"
|
||||
readme = "README.md"
|
||||
keywords = ["terminal", "tty"]
|
||||
license = "MIT"
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
kernel32-sys = "0.2"
|
||||
winapi = "0.2"
|
||||
repository = "https://github.com/softprops/atty"
|
||||
[target."cfg(target_os = \"redox\")".dependencies.termion]
|
||||
version = "1.5"
|
||||
[target."cfg(unix)".dependencies.libc]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
[target."cfg(windows)".dependencies.winapi]
|
||||
version = "0.3"
|
||||
features = ["consoleapi", "processenv", "minwinbase", "minwindef", "winbase"]
|
||||
[badges.travis-ci]
|
||||
repository = "softprops/atty"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2015-2016 Doug Tangren
|
||||
Copyright (c) 2015-2017 Doug Tangren
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
# atty
|
||||
|
||||
[![Build Status](https://travis-ci.org/softprops/atty.svg?branch=master)](https://travis-ci.org/softprops/atty) [![Build status](https://ci.appveyor.com/api/projects/status/geggrsnsjsuse8cv?svg=true)](https://ci.appveyor.com/project/softprops/atty) [![Coverage Status](https://coveralls.io/repos/softprops/atty/badge.svg?branch=master&service=github)](https://coveralls.io/github/softprops/atty?branch=master) [![crates.io](http://meritbadge.herokuapp.com/atty)](https://crates.io/crates/atty)
|
||||
[![Build Status](https://travis-ci.org/softprops/atty.svg?branch=master)](https://travis-ci.org/softprops/atty) [![Build status](https://ci.appveyor.com/api/projects/status/geggrsnsjsuse8cv?svg=true)](https://ci.appveyor.com/project/softprops/atty) [![Coverage Status](https://coveralls.io/repos/softprops/atty/badge.svg?branch=master&service=github)](https://coveralls.io/github/softprops/atty?branch=master) [![crates.io](https://img.shields.io/crates/v/atty.svg)](https://crates.io/crates/atty) [![Released API docs](https://docs.rs/atty/badge.svg)](http://docs.rs/atty) [![Master API docs](https://img.shields.io/badge/docs-master-green.svg)](https://softprops.github.io/atty)
|
||||
|
||||
> are you or are you not a tty?
|
||||
|
||||
|
||||
[Api documentation](http://softprops.github.io/atty)
|
||||
## install
|
||||
|
||||
Add the following to your `Cargo.toml`
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
atty = "0.2"
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
|
@ -23,15 +30,6 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
## install
|
||||
|
||||
Add the following to your `Cargo.toml`
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
atty = "0.2"
|
||||
```
|
||||
|
||||
## testing
|
||||
|
||||
This library has been unit tested on both unix and windows platforms (via appveyor).
|
||||
|
@ -75,4 +73,4 @@ stderr? false
|
|||
stdin? true
|
||||
```
|
||||
|
||||
Doug Tangren (softprops) 2015
|
||||
Doug Tangren (softprops) 2015-2017
|
||||
|
|
|
@ -4,7 +4,6 @@ environment:
|
|||
- TARGET: nightly-i686-pc-windows-msvc
|
||||
- TARGET: nightly-x86_64-pc-windows-gnu
|
||||
- TARGET: nightly-i686-pc-windows-gnu
|
||||
- TARGET: 1.8.0-x86_64-pc-windows-gnu
|
||||
install:
|
||||
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:TARGET}.exe" -FileName "rust-install.exe"
|
||||
- ps: .\rust-install.exe /VERYSILENT /NORESTART /DIR="C:\rust" | Out-Null
|
||||
|
@ -14,4 +13,4 @@ install:
|
|||
- cargo -vV
|
||||
build: false
|
||||
test_script:
|
||||
- cargo test --verbose
|
||||
- cargo build
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
# keep imports tidy
|
||||
reorder_imported_names = true
|
||||
reorder_imports = true
|
||||
reorder_imports_in_group = true
|
||||
# there is no try!
|
||||
use_try_shorthand = true
|
||||
# don't create rustfmt artifacts
|
||||
write_mode = "Replace"
|
||||
# reduce wide load
|
||||
max_width = 80
|
|
@ -15,15 +15,19 @@
|
|||
//! }
|
||||
//! ```
|
||||
|
||||
#[cfg(windows)]
|
||||
extern crate kernel32;
|
||||
#[cfg(not(windows))]
|
||||
#![cfg_attr(unix, no_std)]
|
||||
|
||||
#[cfg(unix)]
|
||||
extern crate libc;
|
||||
#[cfg(windows)]
|
||||
extern crate winapi;
|
||||
#[cfg(target_os = "redox")]
|
||||
extern crate termion;
|
||||
|
||||
#[cfg(windows)]
|
||||
use winapi::minwindef::DWORD;
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
#[cfg(windows)]
|
||||
use winapi::shared::ntdef::WCHAR;
|
||||
|
||||
/// possible stream sources
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
@ -34,7 +38,7 @@ pub enum Stream {
|
|||
}
|
||||
|
||||
/// returns true if this is a tty
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(unix, not(target_arch = "wasm32")))]
|
||||
pub fn is(stream: Stream) -> bool {
|
||||
extern crate libc;
|
||||
|
||||
|
@ -49,11 +53,8 @@ pub fn is(stream: Stream) -> bool {
|
|||
/// returns true if this is a tty
|
||||
#[cfg(windows)]
|
||||
pub fn is(stream: Stream) -> bool {
|
||||
use winapi::{
|
||||
STD_INPUT_HANDLE as STD_INPUT,
|
||||
STD_ERROR_HANDLE as STD_ERROR,
|
||||
STD_OUTPUT_HANDLE as STD_OUTPUT
|
||||
};
|
||||
use winapi::um::winbase::{STD_ERROR_HANDLE as STD_ERROR, STD_INPUT_HANDLE as STD_INPUT,
|
||||
STD_OUTPUT_HANDLE as STD_OUTPUT};
|
||||
|
||||
let (fd, others) = match stream {
|
||||
Stream::Stdin => (STD_INPUT, [STD_ERROR, STD_OUTPUT]),
|
||||
|
@ -87,10 +88,13 @@ pub fn isnt(stream: Stream) -> bool {
|
|||
/// Returns true if any of the given fds are on a console.
|
||||
#[cfg(windows)]
|
||||
unsafe fn console_on_any(fds: &[DWORD]) -> bool {
|
||||
use winapi::um::consoleapi::GetConsoleMode;
|
||||
use winapi::um::processenv::GetStdHandle;
|
||||
|
||||
for &fd in fds {
|
||||
let mut out = 0;
|
||||
let handle = kernel32::GetStdHandle(fd);
|
||||
if kernel32::GetConsoleMode(handle, &mut out) != 0 {
|
||||
let handle = GetStdHandle(fd);
|
||||
if GetConsoleMode(handle, &mut out) != 0 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -100,41 +104,64 @@ unsafe fn console_on_any(fds: &[DWORD]) -> bool {
|
|||
/// Returns true if there is an MSYS tty on the given handle.
|
||||
#[cfg(windows)]
|
||||
unsafe fn msys_tty_on(fd: DWORD) -> bool {
|
||||
use std::ffi::OsString;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
use std::slice;
|
||||
|
||||
use kernel32::GetFileInformationByHandleEx;
|
||||
use winapi::fileapi::FILE_NAME_INFO;
|
||||
use winapi::minwinbase::FileNameInfo;
|
||||
use winapi::minwindef::MAX_PATH;
|
||||
use winapi::ctypes::c_void;
|
||||
use winapi::um::winbase::GetFileInformationByHandleEx;
|
||||
use winapi::um::fileapi::FILE_NAME_INFO;
|
||||
use winapi::um::minwinbase::FileNameInfo;
|
||||
use winapi::um::processenv::GetStdHandle;
|
||||
use winapi::shared::minwindef::MAX_PATH;
|
||||
|
||||
let size = mem::size_of::<FILE_NAME_INFO>();
|
||||
let mut name_info_bytes = vec![0u8; size + MAX_PATH];
|
||||
let mut name_info_bytes = vec![0u8; size + MAX_PATH * mem::size_of::<WCHAR>()];
|
||||
let res = GetFileInformationByHandleEx(
|
||||
kernel32::GetStdHandle(fd),
|
||||
GetStdHandle(fd),
|
||||
FileNameInfo,
|
||||
&mut *name_info_bytes as *mut _ as *mut c_void,
|
||||
name_info_bytes.len() as u32);
|
||||
name_info_bytes.len() as u32,
|
||||
);
|
||||
if res == 0 {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
let name_info: FILE_NAME_INFO =
|
||||
*(name_info_bytes[0..size].as_ptr() as *const FILE_NAME_INFO);
|
||||
let name_bytes =
|
||||
&name_info_bytes[size..size + name_info.FileNameLength as usize];
|
||||
let name_u16 = slice::from_raw_parts(
|
||||
name_bytes.as_ptr() as *const u16, name_bytes.len() / 2);
|
||||
let name = OsString::from_wide(name_u16)
|
||||
.as_os_str().to_string_lossy().into_owned();
|
||||
name.contains("msys-") || name.contains("-pty")
|
||||
let name_info: &FILE_NAME_INFO = &*(name_info_bytes.as_ptr() as *const FILE_NAME_INFO);
|
||||
let s = slice::from_raw_parts(
|
||||
name_info.FileName.as_ptr(),
|
||||
name_info.FileNameLength as usize / 2,
|
||||
);
|
||||
let name = String::from_utf16_lossy(s);
|
||||
// This checks whether 'pty' exists in the file name, which indicates that
|
||||
// a pseudo-terminal is attached. To mitigate against false positives
|
||||
// (e.g., an actual file name that contains 'pty'), we also require that
|
||||
// either the strings 'msys-' or 'cygwin-' are in the file name as well.)
|
||||
let is_msys = name.contains("msys-") || name.contains("cygwin-");
|
||||
let is_pty = name.contains("-pty");
|
||||
is_msys && is_pty
|
||||
}
|
||||
|
||||
/// returns true if this is a tty
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn is(stream: Stream) -> bool {
|
||||
use std::io;
|
||||
use termion::is_tty;
|
||||
|
||||
match stream {
|
||||
Stream::Stdin => is_tty(&io::stdin()),
|
||||
Stream::Stdout => is_tty(&io::stdout()),
|
||||
Stream::Stderr => is_tty(&io::stderr()),
|
||||
}
|
||||
}
|
||||
|
||||
/// returns true if this is a tty
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn is(_stream: Stream) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{is, Stream};
|
||||
use super::{Stream, is};
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
|
@ -172,7 +199,7 @@ mod tests {
|
|||
#[cfg(target_os = "macos")]
|
||||
fn is_in() {
|
||||
// macos on travis seems to pipe its input
|
||||
assert!(!is(Stream::Stdin))
|
||||
assert!(is(Stream::Stdin))
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{".appveyor.yml":"15c5548159ad6ebcc02960bb6a3269e729e772df2733b7d4c7cc1583c413ae45",".travis.yml":"5733d01f7cd27cbdd17a46399103e83eca528727e6cad7f355f6748e772ef916","Cargo.toml":"c257476252f17472f1a78c9fa92b137dc435873797ec1a137aa73043b3ad06a7","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0","README.md":"7667acd3dfd050dadccf8b7815435b9108c24c5704944085281beed6a181e220","src/lib.rs":"106e402d1c5ae68558f4e8a3971b646c12f19762363d2cf15c13a1c2aeb1d1e7","src/unix.rs":"67f0244c118cff918f01b6c164dfe604039ce9160a099ba6e4ff86dcf8ec0097","src/windows.rs":"5767d923280998e341504f8d2a015b8b0c3f8b2b1188610aa4c1b6a343da5682"},"package":"9ab76cfd2aaa59b7bf6688ad9ba15bbae64bff97f04ea02144cfd3443e5c2866"}
|
||||
{"files":{".appveyor.yml":"15c5548159ad6ebcc02960bb6a3269e729e772df2733b7d4c7cc1583c413ae45",".travis.yml":"5733d01f7cd27cbdd17a46399103e83eca528727e6cad7f355f6748e772ef916","Cargo.toml":"c47bb59c1d58dc1eb439331bb140e3a4174370f83b0f0b702d41eb64b27cda3f","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0","README.md":"4dda80a0599cc235f8eaf56c33117fcc7cbeb48aece7f7b262f374ee12e3f1f4","src/lib.rs":"38e0f03bb81d76ac8d71b73b00fae648359aa7e1c1b9ba7159cb1b19eb12b987","src/unix.rs":"67f0244c118cff918f01b6c164dfe604039ce9160a099ba6e4ff86dcf8ec0097","src/windows.rs":"4178e02fe48c1148a4d0edcdac6cec8fecf016e636064843b60e7a1d78c817a7"},"package":"9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"}
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "fs2"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
authors = ["Dan Burkert <dan@danburkert.com>"]
|
||||
description = "Cross-platform file locks and file duplication."
|
||||
documentation = "https://docs.rs/fs2"
|
||||
|
@ -21,10 +21,13 @@ license = "MIT/Apache-2.0"
|
|||
repository = "https://github.com/danburkert/fs2-rs"
|
||||
[dev-dependencies.tempdir]
|
||||
version = "0.3"
|
||||
[target."cfg(windows)".dependencies.winapi]
|
||||
version = "0.2"
|
||||
|
||||
[target."cfg(windows)".dependencies.kernel32-sys]
|
||||
version = "0.2"
|
||||
[target."cfg(unix)".dependencies.libc]
|
||||
version = "0.2.2"
|
||||
version = "0.2.30"
|
||||
[target."cfg(windows)".dependencies.winapi]
|
||||
version = "0.3"
|
||||
features = ["handleapi", "processthreadsapi", "winerror", "fileapi", "winbase", "std"]
|
||||
[badges.appveyor]
|
||||
repository = "danburkert/fs2-rs"
|
||||
|
||||
[badges.travis-ci]
|
||||
repository = "danburkert/fs2-rs"
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
Extended utilities for working with files and filesystems in Rust. `fs2`
|
||||
requires Rust stable 1.8 or greater.
|
||||
|
||||
[Documentation](https://docs.rs/fs2)
|
||||
|
||||
[![Linux Status](https://travis-ci.org/danburkert/fs2-rs.svg?branch=master)](https://travis-ci.org/danburkert/fs2-rs)
|
||||
[![Windows Status](https://ci.appveyor.com/api/projects/status/iuvjv1aaaml0rntt/branch/master?svg=true)](https://ci.appveyor.com/project/danburkert/fs2-rs)
|
||||
[![Build Status](https://travis-ci.org/danburkert/fs2-rs.svg?branch=master)](https://travis-ci.org/danburkert/fs2-rs)
|
||||
[![Windows Build status](https://ci.appveyor.com/api/projects/status/iuvjv1aaaml0rntt/branch/master?svg=true)](https://ci.appveyor.com/project/danburkert/fs2-rs/branch/master)
|
||||
[![Documentation](https://docs.rs/fs2/badge.svg)](https://docs.rs/memmap)
|
||||
[![Crate](https://img.shields.io/crates/v/fs2.svg)](https://crates.io/crates/memmap)
|
||||
|
||||
## Features
|
||||
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
//! Extended utilities for working with files and filesystems in Rust.
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/fs2/0.4.3")]
|
||||
|
||||
#![cfg_attr(test, feature(test))]
|
||||
#![deny(warnings)]
|
||||
|
||||
#[cfg(windows)]
|
||||
extern crate winapi;
|
||||
|
||||
#[cfg(unix)]
|
||||
mod unix;
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
extern crate kernel32;
|
||||
extern crate winapi;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Error, Result};
|
||||
use std::mem;
|
||||
|
@ -9,19 +6,30 @@ use std::os::windows::io::{AsRawHandle, FromRawHandle};
|
|||
use std::path::Path;
|
||||
use std::ptr;
|
||||
|
||||
use winapi::shared::minwindef::{BOOL, DWORD};
|
||||
use winapi::shared::winerror::ERROR_LOCK_VIOLATION;
|
||||
use winapi::um::fileapi::{FILE_ALLOCATION_INFO, FILE_STANDARD_INFO, GetDiskFreeSpaceW};
|
||||
use winapi::um::fileapi::{GetVolumePathNameW, LockFileEx, UnlockFile, SetFileInformationByHandle};
|
||||
use winapi::um::handleapi::DuplicateHandle;
|
||||
use winapi::um::minwinbase::{FileAllocationInfo, FileStandardInfo};
|
||||
use winapi::um::minwinbase::{LOCKFILE_FAIL_IMMEDIATELY, LOCKFILE_EXCLUSIVE_LOCK};
|
||||
use winapi::um::processthreadsapi::GetCurrentProcess;
|
||||
use winapi::um::winbase::GetFileInformationByHandleEx;
|
||||
use winapi::um::winnt::DUPLICATE_SAME_ACCESS;
|
||||
|
||||
use FsStats;
|
||||
|
||||
pub fn duplicate(file: &File) -> Result<File> {
|
||||
unsafe {
|
||||
let mut handle = ptr::null_mut();
|
||||
let current_process = kernel32::GetCurrentProcess();
|
||||
let ret = kernel32::DuplicateHandle(current_process,
|
||||
file.as_raw_handle(),
|
||||
current_process,
|
||||
&mut handle,
|
||||
0,
|
||||
true as winapi::BOOL,
|
||||
winapi::DUPLICATE_SAME_ACCESS);
|
||||
let current_process = GetCurrentProcess();
|
||||
let ret = DuplicateHandle(current_process,
|
||||
file.as_raw_handle(),
|
||||
current_process,
|
||||
&mut handle,
|
||||
0,
|
||||
true as BOOL,
|
||||
DUPLICATE_SAME_ACCESS);
|
||||
if ret == 0 {
|
||||
Err(Error::last_os_error())
|
||||
} else {
|
||||
|
@ -32,18 +40,18 @@ pub fn duplicate(file: &File) -> Result<File> {
|
|||
|
||||
pub fn allocated_size(file: &File) -> Result<u64> {
|
||||
unsafe {
|
||||
let mut info: winapi::FILE_STANDARD_INFO = mem::zeroed();
|
||||
let mut info: FILE_STANDARD_INFO = mem::zeroed();
|
||||
|
||||
let ret = kernel32::GetFileInformationByHandleEx(
|
||||
let ret = GetFileInformationByHandleEx(
|
||||
file.as_raw_handle(),
|
||||
winapi::FileStandardInfo,
|
||||
FileStandardInfo,
|
||||
&mut info as *mut _ as *mut _,
|
||||
mem::size_of::<winapi::FILE_STANDARD_INFO>() as winapi::DWORD);
|
||||
mem::size_of::<FILE_STANDARD_INFO>() as DWORD);
|
||||
|
||||
if ret == 0 {
|
||||
Err(Error::last_os_error())
|
||||
} else {
|
||||
Ok(info.AllocationSize as u64)
|
||||
Ok(*info.AllocationSize.QuadPart() as u64)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,13 +59,13 @@ pub fn allocated_size(file: &File) -> Result<u64> {
|
|||
pub fn allocate(file: &File, len: u64) -> Result<()> {
|
||||
if try!(allocated_size(file)) < len {
|
||||
unsafe {
|
||||
let mut info: winapi::FILE_ALLOCATION_INFO = mem::zeroed();
|
||||
info.AllocationSize = len as i64;
|
||||
let ret = kernel32::SetFileInformationByHandle(
|
||||
let mut info: FILE_ALLOCATION_INFO = mem::zeroed();
|
||||
*info.AllocationSize.QuadPart_mut() = len as i64;
|
||||
let ret = SetFileInformationByHandle(
|
||||
file.as_raw_handle(),
|
||||
winapi::FileAllocationInfo,
|
||||
FileAllocationInfo,
|
||||
&mut info as *mut _ as *mut _,
|
||||
mem::size_of::<winapi::FILE_ALLOCATION_INFO>() as winapi::DWORD);
|
||||
mem::size_of::<FILE_ALLOCATION_INFO>() as DWORD);
|
||||
if ret == 0 {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
|
@ -75,32 +83,32 @@ pub fn lock_shared(file: &File) -> Result<()> {
|
|||
}
|
||||
|
||||
pub fn lock_exclusive(file: &File) -> Result<()> {
|
||||
lock_file(file, winapi::LOCKFILE_EXCLUSIVE_LOCK)
|
||||
lock_file(file, LOCKFILE_EXCLUSIVE_LOCK)
|
||||
}
|
||||
|
||||
pub fn try_lock_shared(file: &File) -> Result<()> {
|
||||
lock_file(file, winapi::LOCKFILE_FAIL_IMMEDIATELY)
|
||||
lock_file(file, LOCKFILE_FAIL_IMMEDIATELY)
|
||||
}
|
||||
|
||||
pub fn try_lock_exclusive(file: &File) -> Result<()> {
|
||||
lock_file(file, winapi::LOCKFILE_EXCLUSIVE_LOCK | winapi::LOCKFILE_FAIL_IMMEDIATELY)
|
||||
lock_file(file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY)
|
||||
}
|
||||
|
||||
pub fn unlock(file: &File) -> Result<()> {
|
||||
unsafe {
|
||||
let ret = kernel32::UnlockFile(file.as_raw_handle(), 0, 0, !0, !0);
|
||||
let ret = UnlockFile(file.as_raw_handle(), 0, 0, !0, !0);
|
||||
if ret == 0 { Err(Error::last_os_error()) } else { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock_error() -> Error {
|
||||
Error::from_raw_os_error(winapi::ERROR_LOCK_VIOLATION as i32)
|
||||
Error::from_raw_os_error(ERROR_LOCK_VIOLATION as i32)
|
||||
}
|
||||
|
||||
fn lock_file(file: &File, flags: winapi::DWORD) -> Result<()> {
|
||||
fn lock_file(file: &File, flags: DWORD) -> Result<()> {
|
||||
unsafe {
|
||||
let mut overlapped = mem::zeroed();
|
||||
let ret = kernel32::LockFileEx(file.as_raw_handle(), flags, 0, !0, !0, &mut overlapped);
|
||||
let ret = LockFileEx(file.as_raw_handle(), flags, 0, !0, !0, &mut overlapped);
|
||||
if ret == 0 { Err(Error::last_os_error()) } else { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
@ -108,9 +116,9 @@ fn lock_file(file: &File, flags: winapi::DWORD) -> Result<()> {
|
|||
fn volume_path(path: &Path, volume_path: &mut [u16]) -> Result<()> {
|
||||
let path_utf8: Vec<u16> = path.as_os_str().encode_wide().chain(Some(0)).collect();
|
||||
unsafe {
|
||||
let ret = kernel32::GetVolumePathNameW(path_utf8.as_ptr(),
|
||||
volume_path.as_mut_ptr(),
|
||||
volume_path.len() as winapi::DWORD);
|
||||
let ret = GetVolumePathNameW(path_utf8.as_ptr(),
|
||||
volume_path.as_mut_ptr(),
|
||||
volume_path.len() as DWORD);
|
||||
if ret == 0 { Err(Error::last_os_error()) } else { Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -125,11 +133,11 @@ pub fn statvfs(path: &Path) -> Result<FsStats> {
|
|||
let mut bytes_per_sector = 0;
|
||||
let mut number_of_free_clusters = 0;
|
||||
let mut total_number_of_clusters = 0;
|
||||
let ret = kernel32::GetDiskFreeSpaceW(root_path.as_ptr(),
|
||||
&mut sectors_per_cluster,
|
||||
&mut bytes_per_sector,
|
||||
&mut number_of_free_clusters,
|
||||
&mut total_number_of_clusters);
|
||||
let ret = GetDiskFreeSpaceW(root_path.as_ptr(),
|
||||
&mut sectors_per_cluster,
|
||||
&mut bytes_per_sector,
|
||||
&mut number_of_free_clusters,
|
||||
&mut total_number_of_clusters);
|
||||
if ret == 0 {
|
||||
Err(Error::last_os_error())
|
||||
} else {
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{".travis.yml":"f0902052851b9d4fec53cea56ce4075686365f0075d64a788fe7ba9f9b98fb8a","Cargo.toml":"b2dbd542893d6b03621c7c65c4fdd7ddb7e6e7c713c610ea3ef5ca88108d3644","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"0241727cb9f7e4ab1ad206ff89ec1dc31034a69b146f076237e8c1b1534b6fe0","README.md":"11cdfba96992fcee089bdcad6682ba5357d8161304d35618ee76abad298ffba7","appveyor.yml":"266ca65d48f02c6a3ce1ba1c0772ef9afece737da03b3176e000b7c32a9ea748","script/doc-upload.cfg":"8cae598ef8592842fa8319b18d515e7a322296490cbdf909b29f5e042a95419e","src/lib.rs":"996474c9c88d9c79865b4923d9739f4c9bc650b29ea70db1c7af43fa59947ed1"},"package":"65ba9d75bcea84e07812618fedf284a64776c2f2ea0cad6bca7f69739695a958"}
|
||||
{"files":{".travis.yml":"f0902052851b9d4fec53cea56ce4075686365f0075d64a788fe7ba9f9b98fb8a","Cargo.toml":"3faa730a95b1a21d63ca11b375e9e86793565a573db649df01562bb24a0b1b43","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"0241727cb9f7e4ab1ad206ff89ec1dc31034a69b146f076237e8c1b1534b6fe0","README.md":"11cdfba96992fcee089bdcad6682ba5357d8161304d35618ee76abad298ffba7","appveyor.yml":"266ca65d48f02c6a3ce1ba1c0772ef9afece737da03b3176e000b7c32a9ea748","script/doc-upload.cfg":"8cae598ef8592842fa8319b18d515e7a322296490cbdf909b29f5e042a95419e","src/lib.rs":"282e6beaef56932a3737e1b60e71ddfdf620d35a9cafdfaa1cc59b54f7d43009"},"package":"aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729"}
|
|
@ -1,20 +1,28 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "msdos_time"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = """
|
||||
Converts an MsDosDateTime (FAT time) to a Tm value
|
||||
"""
|
||||
repository = "https://github.com/mvdnes/msdos_time"
|
||||
description = "Converts an MsDosDateTime (FAT time) to a Tm value\n"
|
||||
documentation = "https://mvdnes.github.io/rust-docs/msdos_time/msdos_time/"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/mvdnes/msdos_time"
|
||||
|
||||
[lib]
|
||||
name = "msdos_time"
|
||||
|
||||
[dependencies]
|
||||
time = "0.1"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = "0.2"
|
||||
kernel32-sys = "0.2"
|
||||
[dependencies.time]
|
||||
version = "0.1"
|
||||
[target."cfg(windows)".dependencies.winapi]
|
||||
version = "0.3"
|
||||
features = ["winbase", "timezoneapi"]
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
//! It is currently mostly used in zip files.
|
||||
|
||||
extern crate time;
|
||||
#[cfg(windows)] extern crate kernel32;
|
||||
#[cfg(windows)] extern crate winapi;
|
||||
|
||||
use std::io;
|
||||
|
@ -93,8 +92,10 @@ mod sys {
|
|||
mod sys {
|
||||
use super::MsDosDateTime;
|
||||
use time::{self, Tm};
|
||||
use winapi::*;
|
||||
use kernel32::*;
|
||||
use winapi::shared::minwindef::{WORD, FILETIME};
|
||||
use winapi::um::minwinbase::SYSTEMTIME;
|
||||
use winapi::um::timezoneapi::{FileTimeToSystemTime, SystemTimeToFileTime};
|
||||
use winapi::um::winbase::{DosDateTimeToFileTime, FileTimeToDosDateTime};
|
||||
use std::io;
|
||||
|
||||
pub fn msdos_to_tm(ms: MsDosDateTime) -> Result<Tm, io::Error> {
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"c8a7070b6801c4cc9f410d45819dab3c6efcbe77806a617415289d8de0fbb01b","src/lib.rs":"932b67d85b4176bfb1d7a78b70ec5dd356839158dad22d2e932b88be30925572","src/parking_lot.rs":"aabffbdf465648a1066b24e88c74fbff9cef636354e0bfdd125937f61d771449","src/spinwait.rs":"5aee4a6e8d33eec1b6a81b21ff3b223460d8fa2d37e633b31b8ca27fabe659cb","src/stable.rs":"4562ea9a408bd3917df6d30c8354bf51f46bc69b3360815813730743204adfdc","src/thread_parker/generic.rs":"0c30db3d1c96bd5ef284a4761a829aba8d21fc813b3d1d70b2baf5f00744e006","src/thread_parker/linux.rs":"4e0a142ce3ff59d37e5c452bf57b3481ef00274e8e489ac1a1d11b2f31b473ed","src/thread_parker/unix.rs":"ff5a543f21895c8114bd4f89b5764882beab1f3a37ddbd8fc31c783e1db3f1c1","src/thread_parker/windows/keyed_event.rs":"b54b0855b10ed2c188ce42094c6e4069e92e325f870d0c0f8244bfe2d7811b66","src/thread_parker/windows/mod.rs":"dc5359b10275a4aaee04024c202b115d267e4ea15917546b042c4035c0218136","src/thread_parker/windows/waitaddress.rs":"2da78bfe09e4262a6cd6271d6416a9debdb3fd3abb1993be1c68515952576874","src/util.rs":"2d07c0c010a857790ae2ed6a1215eeed8af76859e076797ea1ba8dec82169e84","src/word_lock.rs":"6ab156a775c46423bbb7dae520f181dde1747140d52ba995850969498559c7b2"},"package":"6c677d78851950b3aec390e681a411f78cc250cba277d4f578758a377f727970"}
|
||||
{"files":{"Cargo.toml":"220144666e4c0a4b3b3235e7d3b10f4f34cb3b8ca292ee19437f23c9a15758de","src/lib.rs":"e80f927665ef24660878e5e4a4ea3c26892c2849889d59aacee6beb59d02020d","src/parking_lot.rs":"2da388ff4c13003fc30531bb6110e4feedac30ad3ce905912e657711a6b0fdad","src/spinwait.rs":"cbd2d2464ef6fa5fb05109bdb3ca588467949dcd4ee9194deafef6004d10215e","src/thread_parker/generic.rs":"0c30db3d1c96bd5ef284a4761a829aba8d21fc813b3d1d70b2baf5f00744e006","src/thread_parker/linux.rs":"1c4c023ebb58fcc16451683c6c8b68311e87ab34537dc17a060ddf5aad02a215","src/thread_parker/unix.rs":"dc6f4af965618cc2d87d3bef6455ba78b44ffe5b38dff9d41fb86e1526cbbcd1","src/thread_parker/windows/keyed_event.rs":"efe64f7bcdfe03049a7b901d2573bc7db1bb73b8ab4a040245423d95c8f9514f","src/thread_parker/windows/mod.rs":"f31eed53f3e402477d80a70a7c6d474c01ba4c9ad952bbe562509448cd3cc1ad","src/thread_parker/windows/waitaddress.rs":"09d1e6a5a6c3f23f375ae4beee946290f7c66d183e69d476ce69b21a4a5aa7af","src/util.rs":"2d07c0c010a857790ae2ed6a1215eeed8af76859e076797ea1ba8dec82169e84","src/word_lock.rs":"692f443c52672c6e88c0cad259cf7c89dc2a1b54aa95eeeea582401b2a7d058d"},"package":"4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa"}
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "parking_lot_core"
|
||||
version = "0.2.7"
|
||||
version = "0.2.14"
|
||||
authors = ["Amanieu d'Antras <amanieu@gmail.com>"]
|
||||
description = "An advanced API for creating custom synchronization primitives."
|
||||
documentation = "https://amanieu.github.io/parking_lot/parking_lot_core/index.html"
|
||||
|
@ -28,7 +28,7 @@ version = "0.4.5"
|
|||
optional = true
|
||||
|
||||
[dependencies.rand]
|
||||
version = "0.3"
|
||||
version = "0.4"
|
||||
|
||||
[dependencies.smallvec]
|
||||
version = "0.6"
|
||||
|
@ -41,9 +41,7 @@ optional = true
|
|||
deadlock_detection = ["petgraph", "thread-id", "backtrace"]
|
||||
nightly = []
|
||||
[target."cfg(unix)".dependencies.libc]
|
||||
version = "0.2.15"
|
||||
[target."cfg(windows)".dependencies.kernel32-sys]
|
||||
version = "0.2"
|
||||
|
||||
version = "0.2.27"
|
||||
[target."cfg(windows)".dependencies.winapi]
|
||||
version = "0.2"
|
||||
version = "0.3"
|
||||
features = ["winnt", "ntstatus", "minwindef", "winerror", "winbase", "errhandlingapi", "handleapi"]
|
||||
|
|
|
@ -11,15 +11,16 @@
|
|||
//! # The parking lot
|
||||
//!
|
||||
//! To keep synchronization primitives small, all thread queuing and suspending
|
||||
//! functionality is offloaded to the *parking lot*. The idea behind this is
|
||||
//! based on the Webkit [`WTF::ParkingLot`]
|
||||
//! (https://webkit.org/blog/6161/locking-in-webkit/) class, which essentially
|
||||
//! consists of a hash table mapping of lock addresses to queues of parked
|
||||
//! (sleeping) threads. The Webkit parking lot was itself inspired by Linux
|
||||
//! [futexes](http://man7.org/linux/man-pages/man2/futex.2.html), but it is more
|
||||
//! powerful since it allows invoking callbacks while holding a queue lock.
|
||||
//! functionality is offloaded to the *parking lot*. The idea behind this is based
|
||||
//! on the Webkit [`WTF::ParkingLot`](https://webkit.org/blog/6161/locking-in-webkit/)
|
||||
//! class, which essentially consists of a hash table mapping of lock addresses
|
||||
//! to queues of parked (sleeping) threads. The Webkit parking lot was itself
|
||||
//! inspired by Linux [futexes](http://man7.org/linux/man-pages/man2/futex.2.html),
|
||||
//! but it is more powerful since it allows invoking callbacks while holding a
|
||||
//! queue lock.
|
||||
//!
|
||||
//! There are two main operations that can be performed on the parking lot:
|
||||
//!
|
||||
//! - *Parking* refers to suspending the thread while simultaneously enqueuing it
|
||||
//! on a queue keyed by some address.
|
||||
//! - *Unparking* refers to dequeuing a thread from a queue keyed by some address
|
||||
|
@ -37,9 +38,7 @@
|
|||
//! reference count and the two mutex bits in the same atomic word.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(feature = "nightly", feature(const_fn, thread_local_state))]
|
||||
#![cfg_attr(all(feature = "nightly", target_os = "linux"), feature(integer_atomics))]
|
||||
#![cfg_attr(feature = "nightly", feature(asm))]
|
||||
|
||||
extern crate rand;
|
||||
extern crate smallvec;
|
||||
|
@ -54,8 +53,6 @@ extern crate thread_id;
|
|||
#[cfg(unix)]
|
||||
extern crate libc;
|
||||
|
||||
#[cfg(windows)]
|
||||
extern crate kernel32;
|
||||
#[cfg(windows)]
|
||||
extern crate winapi;
|
||||
|
||||
|
@ -72,9 +69,6 @@ mod thread_parker;
|
|||
#[path = "thread_parker/generic.rs"]
|
||||
mod thread_parker;
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
mod stable;
|
||||
|
||||
mod util;
|
||||
mod spinwait;
|
||||
mod word_lock;
|
||||
|
|
|
@ -5,10 +5,7 @@
|
|||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
use stable::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
|
||||
use std::time::{Duration, Instant};
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::ptr;
|
||||
|
@ -890,7 +887,7 @@ unsafe fn unpark_requeue_internal(
|
|||
let mut link = &bucket_from.queue_head;
|
||||
let mut current = bucket_from.queue_head.get();
|
||||
let mut previous = ptr::null();
|
||||
let mut requeue_threads = ptr::null();
|
||||
let mut requeue_threads: *const ThreadData = ptr::null();
|
||||
let mut requeue_threads_tail: *const ThreadData = ptr::null();
|
||||
let mut wakeup_thread = None;
|
||||
while !current.is_null() {
|
||||
|
|
|
@ -5,24 +5,39 @@
|
|||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
#[cfg(windows)]
|
||||
use kernel32;
|
||||
#[cfg(unix)]
|
||||
use libc;
|
||||
#[cfg(windows)]
|
||||
use winapi;
|
||||
#[cfg(not(any(windows, unix)))]
|
||||
use std::thread;
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
use std::sync::atomic::{fence, Ordering};
|
||||
use std::sync::atomic::spin_loop_hint;
|
||||
|
||||
// Yields the rest of the current timeslice to the OS
|
||||
#[cfg(windows)]
|
||||
#[inline]
|
||||
fn thread_yield() {
|
||||
// Note that this is manually defined here rather than using the definition
|
||||
// through `winapi`. The `winapi` definition comes from the `synchapi`
|
||||
// header which enables the "synchronization.lib" library. It turns out,
|
||||
// however that `Sleep` comes from `kernel32.dll` so this activation isn't
|
||||
// necessary.
|
||||
//
|
||||
// This was originally identified in rust-lang/rust where on MinGW the
|
||||
// libsynchronization.a library pulls in a dependency on a newer DLL not
|
||||
// present in older versions of Windows. (see rust-lang/rust#49438)
|
||||
//
|
||||
// This is a bit of a hack for now and ideally we'd fix MinGW's own import
|
||||
// libraries, but that'll probably take a lot longer than patching this here
|
||||
// and avoiding the `synchapi` feature entirely.
|
||||
extern "system" {
|
||||
fn Sleep(a: winapi::shared::minwindef::DWORD);
|
||||
}
|
||||
unsafe {
|
||||
// We don't use SwitchToThread here because it doesn't consider all
|
||||
// threads in the system and the thread we are waiting for may not get
|
||||
// selected.
|
||||
kernel32::Sleep(0);
|
||||
Sleep(0);
|
||||
}
|
||||
}
|
||||
#[cfg(unix)]
|
||||
|
@ -38,43 +53,12 @@ fn thread_yield() {
|
|||
thread::yield_now();
|
||||
}
|
||||
|
||||
// Wastes some CPU time for the given number of iterations, preferably also
|
||||
// Wastes some CPU time for the given number of iterations,
|
||||
// using a hint to indicate to the CPU that we are spinning.
|
||||
#[cfg(all(feature = "nightly", any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
#[inline]
|
||||
fn cpu_relax(iterations: u32) {
|
||||
for _ in 0..iterations {
|
||||
unsafe {
|
||||
asm!("pause" ::: "memory" : "volatile");
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(all(feature = "nightly", target_arch = "aarch64"))]
|
||||
#[inline]
|
||||
fn cpu_relax(iterations: u32) {
|
||||
for _ in 0..iterations {
|
||||
unsafe {
|
||||
asm!("yield" ::: "memory" : "volatile");
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(all(feature = "nightly",
|
||||
not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))))]
|
||||
#[inline]
|
||||
fn cpu_relax(iterations: u32) {
|
||||
for _ in 0..iterations {
|
||||
unsafe {
|
||||
asm!("" ::: "memory" : "volatile");
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
#[inline]
|
||||
fn cpu_relax(iterations: u32) {
|
||||
// This is a bit tricky: we rely on the fact that LLVM doesn't optimize
|
||||
// atomic operations and effectively treats them as volatile.
|
||||
for _ in 0..iterations {
|
||||
fence(Ordering::SeqCst);
|
||||
spin_loop_hint()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,14 +69,6 @@ pub struct SpinWait {
|
|||
|
||||
impl SpinWait {
|
||||
/// Creates a new `SpinWait`.
|
||||
#[cfg(feature = "nightly")]
|
||||
#[inline]
|
||||
pub const fn new() -> SpinWait {
|
||||
SpinWait { counter: 0 }
|
||||
}
|
||||
|
||||
/// Creates a new `SpinWait`.
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
#[inline]
|
||||
pub fn new() -> SpinWait {
|
||||
SpinWait { counter: 0 }
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
// Copyright 2016 Amanieu d'Antras
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::sync::atomic;
|
||||
|
||||
// Re-export this for convenience
|
||||
pub use std::sync::atomic::{fence, Ordering};
|
||||
|
||||
// Wrapper around AtomicUsize for non-nightly which has usable compare_exchange
|
||||
// and compare_exchange_weak methods.
|
||||
pub struct AtomicUsize(atomic::AtomicUsize);
|
||||
pub use self::AtomicUsize as AtomicU8;
|
||||
|
||||
// Constants for static initialization
|
||||
pub const ATOMIC_USIZE_INIT: AtomicUsize = AtomicUsize(atomic::ATOMIC_USIZE_INIT);
|
||||
pub use self::ATOMIC_USIZE_INIT as ATOMIC_U8_INIT;
|
||||
|
||||
impl AtomicUsize {
|
||||
#[inline]
|
||||
pub fn new(val: usize) -> AtomicUsize {
|
||||
AtomicUsize(atomic::AtomicUsize::new(val))
|
||||
}
|
||||
#[inline]
|
||||
pub fn load(&self, order: Ordering) -> usize {
|
||||
self.0.load(order)
|
||||
}
|
||||
#[inline]
|
||||
pub fn store(&self, val: usize, order: Ordering) {
|
||||
self.0.store(val, order);
|
||||
}
|
||||
#[inline]
|
||||
pub fn swap(&self, val: usize, order: Ordering) -> usize {
|
||||
self.0.swap(val, order)
|
||||
}
|
||||
#[inline]
|
||||
pub fn fetch_add(&self, val: usize, order: Ordering) -> usize {
|
||||
self.0.fetch_add(val, order)
|
||||
}
|
||||
#[inline]
|
||||
pub fn fetch_sub(&self, val: usize, order: Ordering) -> usize {
|
||||
self.0.fetch_sub(val, order)
|
||||
}
|
||||
#[inline]
|
||||
pub fn fetch_and(&self, val: usize, order: Ordering) -> usize {
|
||||
self.0.fetch_and(val, order)
|
||||
}
|
||||
#[inline]
|
||||
pub fn fetch_or(&self, val: usize, order: Ordering) -> usize {
|
||||
self.0.fetch_or(val, order)
|
||||
}
|
||||
#[inline]
|
||||
pub fn compare_exchange(
|
||||
&self,
|
||||
old: usize,
|
||||
new: usize,
|
||||
order: Ordering,
|
||||
_: Ordering,
|
||||
) -> Result<usize, usize> {
|
||||
let res = self.0.compare_and_swap(old, new, order);
|
||||
if res == old {
|
||||
Ok(res)
|
||||
} else {
|
||||
Err(res)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn compare_exchange_weak(
|
||||
&self,
|
||||
old: usize,
|
||||
new: usize,
|
||||
order: Ordering,
|
||||
_: Ordering,
|
||||
) -> Result<usize, usize> {
|
||||
let res = self.0.compare_and_swap(old, new, order);
|
||||
if res == old {
|
||||
Ok(res)
|
||||
} else {
|
||||
Err(res)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,19 +9,19 @@ use std::sync::atomic::{AtomicI32, Ordering};
|
|||
use std::time::Instant;
|
||||
use libc;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
const SYS_FUTEX: libc::c_long = 240;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const SYS_FUTEX: libc::c_long = 202;
|
||||
#[cfg(target_arch = "arm")]
|
||||
const SYS_FUTEX: libc::c_long = 240;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
const SYS_FUTEX: libc::c_long = 98;
|
||||
|
||||
const FUTEX_WAIT: i32 = 0;
|
||||
const FUTEX_WAKE: i32 = 1;
|
||||
const FUTEX_PRIVATE: i32 = 128;
|
||||
|
||||
// x32 Linux uses a non-standard type for tv_nsec in timespec.
|
||||
// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
|
||||
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
|
||||
#[allow(non_camel_case_types)]
|
||||
type tv_nsec_t = i64;
|
||||
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
|
||||
#[allow(non_camel_case_types)]
|
||||
type tv_nsec_t = libc::c_long;
|
||||
|
||||
// Helper type for putting a thread to sleep until some other thread wakes it up
|
||||
pub struct ThreadParker {
|
||||
futex: AtomicI32,
|
||||
|
@ -49,7 +49,13 @@ impl ThreadParker {
|
|||
// been added to the queue, after unlocking the queue.
|
||||
pub unsafe fn park(&self) {
|
||||
while self.futex.load(Ordering::Acquire) != 0 {
|
||||
let r = libc::syscall(SYS_FUTEX, &self.futex, FUTEX_WAIT | FUTEX_PRIVATE, 1, 0);
|
||||
let r = libc::syscall(
|
||||
libc::SYS_futex,
|
||||
&self.futex,
|
||||
FUTEX_WAIT | FUTEX_PRIVATE,
|
||||
1,
|
||||
0,
|
||||
);
|
||||
debug_assert!(r == 0 || r == -1);
|
||||
if r == -1 {
|
||||
debug_assert!(
|
||||
|
@ -77,9 +83,15 @@ impl ThreadParker {
|
|||
}
|
||||
let ts = libc::timespec {
|
||||
tv_sec: diff.as_secs() as libc::time_t,
|
||||
tv_nsec: diff.subsec_nanos() as libc::c_long,
|
||||
tv_nsec: diff.subsec_nanos() as tv_nsec_t,
|
||||
};
|
||||
let r = libc::syscall(SYS_FUTEX, &self.futex, FUTEX_WAIT | FUTEX_PRIVATE, 1, &ts);
|
||||
let r = libc::syscall(
|
||||
libc::SYS_futex,
|
||||
&self.futex,
|
||||
FUTEX_WAIT | FUTEX_PRIVATE,
|
||||
1,
|
||||
&ts,
|
||||
);
|
||||
debug_assert!(r == 0 || r == -1);
|
||||
if r == -1 {
|
||||
debug_assert!(
|
||||
|
@ -116,7 +128,7 @@ impl UnparkHandle {
|
|||
pub unsafe fn unpark(self) {
|
||||
// The thread data may have been freed at this point, but it doesn't
|
||||
// matter since the syscall will just return EFAULT in that case.
|
||||
let r = libc::syscall(SYS_FUTEX, self.futex, FUTEX_WAKE | FUTEX_PRIVATE, 1);
|
||||
let r = libc::syscall(libc::SYS_futex, self.futex, FUTEX_WAKE | FUTEX_PRIVATE, 1);
|
||||
debug_assert!(r == 0 || r == 1 || r == -1);
|
||||
if r == -1 {
|
||||
debug_assert_eq!(*libc::__errno_location(), libc::EFAULT);
|
||||
|
|
|
@ -218,10 +218,8 @@ unsafe fn timeout_to_timespec(timeout: Duration) -> Option<libc::timespec> {
|
|||
sec = sec.and_then(|sec| sec.checked_add(1));
|
||||
}
|
||||
|
||||
sec.map(|sec| {
|
||||
libc::timespec {
|
||||
tv_nsec: nsec,
|
||||
tv_sec: sec,
|
||||
}
|
||||
sec.map(|sec| libc::timespec {
|
||||
tv_nsec: nsec,
|
||||
tv_sec: sec,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,15 +5,18 @@
|
|||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
use stable::{AtomicUsize, Ordering};
|
||||
use std::time::Instant;
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
use winapi;
|
||||
use kernel32;
|
||||
|
||||
use winapi::shared::minwindef::{TRUE, ULONG};
|
||||
use winapi::shared::ntdef::NTSTATUS;
|
||||
use winapi::shared::ntstatus::{STATUS_SUCCESS, STATUS_TIMEOUT};
|
||||
use winapi::um::handleapi::CloseHandle;
|
||||
use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress};
|
||||
use winapi::um::winnt::{ACCESS_MASK, GENERIC_READ, GENERIC_WRITE, LPCSTR};
|
||||
use winapi::um::winnt::{BOOLEAN, HANDLE, LARGE_INTEGER, PHANDLE, PLARGE_INTEGER, PVOID};
|
||||
|
||||
const STATE_UNPARKED: usize = 0;
|
||||
const STATE_PARKED: usize = 1;
|
||||
|
@ -21,76 +24,71 @@ const STATE_TIMED_OUT: usize = 2;
|
|||
|
||||
#[allow(non_snake_case)]
|
||||
pub struct KeyedEvent {
|
||||
handle: winapi::HANDLE,
|
||||
handle: HANDLE,
|
||||
NtReleaseKeyedEvent: extern "system" fn(
|
||||
EventHandle: winapi::HANDLE,
|
||||
Key: winapi::PVOID,
|
||||
Alertable: winapi::BOOLEAN,
|
||||
Timeout: winapi::PLARGE_INTEGER,
|
||||
) -> winapi::NTSTATUS,
|
||||
EventHandle: HANDLE,
|
||||
Key: PVOID,
|
||||
Alertable: BOOLEAN,
|
||||
Timeout: PLARGE_INTEGER,
|
||||
) -> NTSTATUS,
|
||||
NtWaitForKeyedEvent: extern "system" fn(
|
||||
EventHandle: winapi::HANDLE,
|
||||
Key: winapi::PVOID,
|
||||
Alertable: winapi::BOOLEAN,
|
||||
Timeout: winapi::PLARGE_INTEGER,
|
||||
) -> winapi::NTSTATUS,
|
||||
EventHandle: HANDLE,
|
||||
Key: PVOID,
|
||||
Alertable: BOOLEAN,
|
||||
Timeout: PLARGE_INTEGER,
|
||||
) -> NTSTATUS,
|
||||
}
|
||||
|
||||
impl KeyedEvent {
|
||||
unsafe fn wait_for(
|
||||
&self,
|
||||
key: winapi::PVOID,
|
||||
timeout: winapi::PLARGE_INTEGER,
|
||||
) -> winapi::NTSTATUS {
|
||||
unsafe fn wait_for(&self, key: PVOID, timeout: PLARGE_INTEGER) -> NTSTATUS {
|
||||
(self.NtWaitForKeyedEvent)(self.handle, key, 0, timeout)
|
||||
}
|
||||
|
||||
unsafe fn release(&self, key: winapi::PVOID) -> winapi::NTSTATUS {
|
||||
unsafe fn release(&self, key: PVOID) -> NTSTATUS {
|
||||
(self.NtReleaseKeyedEvent)(self.handle, key, 0, ptr::null_mut())
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn create() -> Option<KeyedEvent> {
|
||||
let ntdll = kernel32::GetModuleHandleA(b"ntdll.dll\0".as_ptr() as winapi::LPCSTR);
|
||||
let ntdll = GetModuleHandleA(b"ntdll.dll\0".as_ptr() as LPCSTR);
|
||||
if ntdll.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let NtCreateKeyedEvent =
|
||||
kernel32::GetProcAddress(ntdll, b"NtCreateKeyedEvent\0".as_ptr() as winapi::LPCSTR);
|
||||
let NtCreateKeyedEvent = GetProcAddress(ntdll, b"NtCreateKeyedEvent\0".as_ptr() as LPCSTR);
|
||||
if NtCreateKeyedEvent.is_null() {
|
||||
return None;
|
||||
}
|
||||
let NtReleaseKeyedEvent =
|
||||
kernel32::GetProcAddress(ntdll, b"NtReleaseKeyedEvent\0".as_ptr() as winapi::LPCSTR);
|
||||
GetProcAddress(ntdll, b"NtReleaseKeyedEvent\0".as_ptr() as LPCSTR);
|
||||
if NtReleaseKeyedEvent.is_null() {
|
||||
return None;
|
||||
}
|
||||
let NtWaitForKeyedEvent =
|
||||
kernel32::GetProcAddress(ntdll, b"NtWaitForKeyedEvent\0".as_ptr() as winapi::LPCSTR);
|
||||
GetProcAddress(ntdll, b"NtWaitForKeyedEvent\0".as_ptr() as LPCSTR);
|
||||
if NtWaitForKeyedEvent.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let NtCreateKeyedEvent: extern "system" fn(
|
||||
KeyedEventHandle: winapi::PHANDLE,
|
||||
DesiredAccess: winapi::ACCESS_MASK,
|
||||
ObjectAttributes: winapi::PVOID,
|
||||
Flags: winapi::ULONG,
|
||||
) -> winapi::NTSTATUS = mem::transmute(NtCreateKeyedEvent);
|
||||
KeyedEventHandle: PHANDLE,
|
||||
DesiredAccess: ACCESS_MASK,
|
||||
ObjectAttributes: PVOID,
|
||||
Flags: ULONG,
|
||||
) -> NTSTATUS = mem::transmute(NtCreateKeyedEvent);
|
||||
let mut handle = mem::uninitialized();
|
||||
let status = NtCreateKeyedEvent(
|
||||
&mut handle,
|
||||
winapi::GENERIC_READ | winapi::GENERIC_WRITE,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
);
|
||||
if status != winapi::STATUS_SUCCESS {
|
||||
if status != STATUS_SUCCESS {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(KeyedEvent {
|
||||
handle: handle,
|
||||
handle,
|
||||
NtReleaseKeyedEvent: mem::transmute(NtReleaseKeyedEvent),
|
||||
NtWaitForKeyedEvent: mem::transmute(NtWaitForKeyedEvent),
|
||||
})
|
||||
|
@ -105,8 +103,8 @@ impl KeyedEvent {
|
|||
}
|
||||
|
||||
pub unsafe fn park(&'static self, key: &AtomicUsize) {
|
||||
let status = self.wait_for(key as *const _ as winapi::PVOID, ptr::null_mut());
|
||||
debug_assert_eq!(status, winapi::STATUS_SUCCESS);
|
||||
let status = self.wait_for(key as *const _ as PVOID, ptr::null_mut());
|
||||
debug_assert_eq!(status, STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
pub unsafe fn park_until(&'static self, key: &AtomicUsize, timeout: Instant) -> bool {
|
||||
|
@ -124,14 +122,14 @@ impl KeyedEvent {
|
|||
|
||||
// NT uses a timeout in units of 100ns. We use a negative value to
|
||||
// indicate a relative timeout based on a monotonic clock.
|
||||
let mut nt_timeout: LARGE_INTEGER = mem::zeroed();
|
||||
let diff = timeout - now;
|
||||
let nt_timeout = (diff.as_secs() as winapi::LARGE_INTEGER)
|
||||
let value = (diff.as_secs() as i64)
|
||||
.checked_mul(-10000000)
|
||||
.and_then(|x| {
|
||||
x.checked_sub((diff.subsec_nanos() as winapi::LARGE_INTEGER + 99) / 100)
|
||||
});
|
||||
let mut nt_timeout = match nt_timeout {
|
||||
Some(x) => x,
|
||||
.and_then(|x| x.checked_sub((diff.subsec_nanos() as i64 + 99) / 100));
|
||||
|
||||
match value {
|
||||
Some(x) => *nt_timeout.QuadPart_mut() = x,
|
||||
None => {
|
||||
// Timeout overflowed, just sleep indefinitely
|
||||
self.park(key);
|
||||
|
@ -139,11 +137,11 @@ impl KeyedEvent {
|
|||
}
|
||||
};
|
||||
|
||||
let status = self.wait_for(key as *const _ as winapi::PVOID, &mut nt_timeout);
|
||||
if status == winapi::STATUS_SUCCESS {
|
||||
let status = self.wait_for(key as *const _ as PVOID, &mut nt_timeout);
|
||||
if status == STATUS_SUCCESS {
|
||||
return true;
|
||||
}
|
||||
debug_assert_eq!(status, winapi::STATUS_TIMEOUT);
|
||||
debug_assert_eq!(status, STATUS_TIMEOUT);
|
||||
|
||||
// If another thread unparked us, we need to call NtWaitForKeyedEvent
|
||||
// otherwise that thread will stay stuck at NtReleaseKeyedEvent.
|
||||
|
@ -173,8 +171,8 @@ impl KeyedEvent {
|
|||
impl Drop for KeyedEvent {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let ok = kernel32::CloseHandle(self.handle);
|
||||
debug_assert_eq!(ok, winapi::TRUE);
|
||||
let ok = CloseHandle(self.handle);
|
||||
debug_assert_eq!(ok, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,8 +190,8 @@ impl UnparkHandle {
|
|||
// released to avoid blocking the queue for too long.
|
||||
pub unsafe fn unpark(self) {
|
||||
if !self.key.is_null() {
|
||||
let status = self.keyed_event.release(self.key as winapi::PVOID);
|
||||
debug_assert_eq!(status, winapi::STATUS_SUCCESS);
|
||||
let status = self.keyed_event.release(self.key as PVOID);
|
||||
debug_assert_eq!(status, STATUS_SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,7 @@
|
|||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
use stable::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
|
||||
use std::time::Instant;
|
||||
|
||||
mod keyed_event;
|
||||
|
|
|
@ -5,24 +5,27 @@
|
|||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
use stable::{AtomicUsize, Ordering};
|
||||
use std::time::Instant;
|
||||
use std::mem;
|
||||
use winapi;
|
||||
use kernel32;
|
||||
|
||||
use winapi::shared::basetsd::SIZE_T;
|
||||
use winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE};
|
||||
use winapi::shared::winerror::ERROR_TIMEOUT;
|
||||
use winapi::um::errhandlingapi::GetLastError;
|
||||
use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress};
|
||||
use winapi::um::winbase::INFINITE;
|
||||
use winapi::um::winnt::{LPCSTR, PVOID};
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub struct WaitAddress {
|
||||
WaitOnAddress: extern "system" fn(
|
||||
Address: winapi::PVOID,
|
||||
CompareAddress: winapi::PVOID,
|
||||
AddressSize: winapi::SIZE_T,
|
||||
dwMilliseconds: winapi::DWORD,
|
||||
) -> winapi::BOOL,
|
||||
WakeByAddressSingle: extern "system" fn(Address: winapi::PVOID),
|
||||
Address: PVOID,
|
||||
CompareAddress: PVOID,
|
||||
AddressSize: SIZE_T,
|
||||
dwMilliseconds: DWORD,
|
||||
) -> BOOL,
|
||||
WakeByAddressSingle: extern "system" fn(Address: PVOID),
|
||||
}
|
||||
|
||||
impl WaitAddress {
|
||||
|
@ -30,21 +33,17 @@ impl WaitAddress {
|
|||
pub unsafe fn create() -> Option<WaitAddress> {
|
||||
// MSDN claims that that WaitOnAddress and WakeByAddressSingle are
|
||||
// located in kernel32.dll, but they are lying...
|
||||
let synch_dll = kernel32::GetModuleHandleA(b"api-ms-win-core-synch-l1-2-0.dll\0".as_ptr()
|
||||
as winapi::LPCSTR);
|
||||
let synch_dll = GetModuleHandleA(b"api-ms-win-core-synch-l1-2-0.dll\0".as_ptr() as LPCSTR);
|
||||
if synch_dll.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let WaitOnAddress =
|
||||
kernel32::GetProcAddress(synch_dll, b"WaitOnAddress\0".as_ptr() as winapi::LPCSTR);
|
||||
let WaitOnAddress = GetProcAddress(synch_dll, b"WaitOnAddress\0".as_ptr() as LPCSTR);
|
||||
if WaitOnAddress.is_null() {
|
||||
return None;
|
||||
}
|
||||
let WakeByAddressSingle = kernel32::GetProcAddress(
|
||||
synch_dll,
|
||||
b"WakeByAddressSingle\0".as_ptr() as winapi::LPCSTR,
|
||||
);
|
||||
let WakeByAddressSingle =
|
||||
GetProcAddress(synch_dll, b"WakeByAddressSingle\0".as_ptr() as LPCSTR);
|
||||
if WakeByAddressSingle.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
@ -66,12 +65,12 @@ impl WaitAddress {
|
|||
while key.load(Ordering::Acquire) != 0 {
|
||||
let cmp = 1usize;
|
||||
let r = (self.WaitOnAddress)(
|
||||
key as *const _ as winapi::PVOID,
|
||||
&cmp as *const _ as winapi::PVOID,
|
||||
mem::size_of::<usize>() as winapi::SIZE_T,
|
||||
winapi::INFINITE,
|
||||
key as *const _ as PVOID,
|
||||
&cmp as *const _ as PVOID,
|
||||
mem::size_of::<usize>() as SIZE_T,
|
||||
INFINITE,
|
||||
);
|
||||
debug_assert!(r == winapi::TRUE);
|
||||
debug_assert!(r == TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,26 +83,24 @@ impl WaitAddress {
|
|||
let diff = timeout - now;
|
||||
let timeout = diff.as_secs()
|
||||
.checked_mul(1000)
|
||||
.and_then(|x| {
|
||||
x.checked_add((diff.subsec_nanos() as u64 + 999999) / 1000000)
|
||||
})
|
||||
.and_then(|x| x.checked_add((diff.subsec_nanos() as u64 + 999999) / 1000000))
|
||||
.map(|ms| {
|
||||
if ms > <winapi::DWORD>::max_value() as u64 {
|
||||
winapi::INFINITE
|
||||
if ms > <DWORD>::max_value() as u64 {
|
||||
INFINITE
|
||||
} else {
|
||||
ms as winapi::DWORD
|
||||
ms as DWORD
|
||||
}
|
||||
})
|
||||
.unwrap_or(winapi::INFINITE);
|
||||
.unwrap_or(INFINITE);
|
||||
let cmp = 1usize;
|
||||
let r = (self.WaitOnAddress)(
|
||||
key as *const _ as winapi::PVOID,
|
||||
&cmp as *const _ as winapi::PVOID,
|
||||
mem::size_of::<usize>() as winapi::SIZE_T,
|
||||
key as *const _ as PVOID,
|
||||
&cmp as *const _ as PVOID,
|
||||
mem::size_of::<usize>() as SIZE_T,
|
||||
timeout,
|
||||
);
|
||||
if r == winapi::FALSE {
|
||||
debug_assert_eq!(kernel32::GetLastError(), winapi::ERROR_TIMEOUT);
|
||||
if r == FALSE {
|
||||
debug_assert_eq!(GetLastError(), ERROR_TIMEOUT);
|
||||
}
|
||||
}
|
||||
true
|
||||
|
@ -120,7 +117,6 @@ impl WaitAddress {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Handle for a thread that is about to be unparked. We need to mark the thread
|
||||
// as unparked while holding the queue lock, but we delay the actual unparking
|
||||
// until after the queue lock is released.
|
||||
|
@ -133,6 +129,6 @@ impl UnparkHandle {
|
|||
// Wakes up the parked thread. This should be called after the queue lock is
|
||||
// released to avoid blocking the queue for too long.
|
||||
pub unsafe fn unpark(self) {
|
||||
(self.waitaddress.WakeByAddressSingle)(self.key as winapi::PVOID);
|
||||
(self.waitaddress.WakeByAddressSingle)(self.key as PVOID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,7 @@
|
|||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use std::sync::atomic::{fence, AtomicUsize, Ordering};
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
use stable::{fence, AtomicUsize, Ordering};
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
use std::cell::Cell;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"e7d20b4132bf22e2c98a5f846f8c16e7628a9181d485fd131b97127ea8719130","LICENSE":"cb46b697c3fd9d27d7bfe1b1ad48f8a58a284984504c6eb215ae2164538df7cb","README.md":"c62ac3d33174db7977cfc7fc77605885f5445c0c9c7d1310dcbe3564c2854edb","src/lib.rs":"af5bc7c59c695880fef0352f9e0f9e9f8682c262a9b1c0fce725b10553647b4e"},"package":"7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"}
|
|
@ -0,0 +1,26 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "redox_termios"
|
||||
version = "0.1.1"
|
||||
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
|
||||
description = "A Rust library to access Redox termios functions"
|
||||
documentation = "https://docs.rs/redox_termios"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/redox-os/termios"
|
||||
|
||||
[lib]
|
||||
name = "redox_termios"
|
||||
path = "src/lib.rs"
|
||||
[dependencies.redox_syscall]
|
||||
version = "0.1"
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Redox OS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,2 @@
|
|||
# termios
|
||||
Redox Rust termios library
|
|
@ -0,0 +1,218 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
#![no_std]
|
||||
|
||||
extern crate syscall;
|
||||
|
||||
use core::{mem, slice};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
pub type tcflag_t = u32;
|
||||
pub type cc_t = u8;
|
||||
|
||||
/* c_cc { */
|
||||
pub const VEOF: usize = 0;
|
||||
pub const VEOL: usize = 1;
|
||||
pub const VEOL2: usize = 2;
|
||||
pub const VERASE: usize = 3;
|
||||
pub const VWERASE: usize = 4;
|
||||
pub const VKILL: usize = 5;
|
||||
pub const VREPRINT: usize = 6;
|
||||
pub const VSWTC: usize = 7;
|
||||
pub const VINTR: usize = 8;
|
||||
pub const VQUIT: usize = 9;
|
||||
pub const VSUSP: usize = 10;
|
||||
pub const VSTART: usize = 12;
|
||||
pub const VSTOP: usize = 13;
|
||||
pub const VLNEXT: usize = 14;
|
||||
pub const VDISCARD: usize = 15;
|
||||
pub const VMIN: usize = 16;
|
||||
pub const VTIME: usize = 17;
|
||||
pub const NCCS: usize = 32;
|
||||
/* } c_cc */
|
||||
|
||||
/* c_iflag { */
|
||||
pub const IGNBRK: tcflag_t = 0o000001;
|
||||
pub const BRKINT: tcflag_t = 0o000002;
|
||||
pub const IGNPAR: tcflag_t = 0o000004;
|
||||
pub const PARMRK: tcflag_t = 0o000010;
|
||||
pub const INPCK: tcflag_t = 0o000020;
|
||||
pub const ISTRIP: tcflag_t = 0o000040;
|
||||
pub const INLCR: tcflag_t = 0o000100;
|
||||
pub const IGNCR: tcflag_t = 0o000200;
|
||||
pub const ICRNL: tcflag_t = 0o000400;
|
||||
pub const IXON: tcflag_t = 0o001000;
|
||||
pub const IXOFF: tcflag_t = 0o002000;
|
||||
/* } c_iflag */
|
||||
|
||||
/* c_oflag { */
|
||||
pub const OPOST: tcflag_t = 0o000001;
|
||||
pub const ONLCR: tcflag_t = 0o000002;
|
||||
pub const OLCUC: tcflag_t = 0o000004;
|
||||
|
||||
pub const OCRNL: tcflag_t = 0o000010;
|
||||
pub const ONOCR: tcflag_t = 0o000020;
|
||||
pub const ONLRET: tcflag_t = 0o000040;
|
||||
|
||||
pub const OFILL: tcflag_t = 0o0000100;
|
||||
pub const OFDEL: tcflag_t = 0o0000200;
|
||||
/* } c_oflag */
|
||||
|
||||
/* c_cflag { */
|
||||
pub const B0: tcflag_t = 0o000000;
|
||||
pub const B50: tcflag_t = 0o000001;
|
||||
pub const B75: tcflag_t = 0o000002;
|
||||
pub const B110: tcflag_t = 0o000003;
|
||||
pub const B134: tcflag_t = 0o000004;
|
||||
pub const B150: tcflag_t = 0o000005;
|
||||
pub const B200: tcflag_t = 0o000006;
|
||||
pub const B300: tcflag_t = 0o000007;
|
||||
pub const B600: tcflag_t = 0o000010;
|
||||
pub const B1200: tcflag_t = 0o000011;
|
||||
pub const B1800: tcflag_t = 0o000012;
|
||||
pub const B2400: tcflag_t = 0o000013;
|
||||
pub const B4800: tcflag_t = 0o000014;
|
||||
pub const B9600: tcflag_t = 0o000015;
|
||||
pub const B19200: tcflag_t = 0o000016;
|
||||
pub const B38400: tcflag_t = 0o000017;
|
||||
pub const B57600: tcflag_t = 0o0020;
|
||||
pub const B115200: tcflag_t = 0o0021;
|
||||
pub const B230400: tcflag_t = 0o0022;
|
||||
pub const B460800: tcflag_t = 0o0023;
|
||||
pub const B500000: tcflag_t = 0o0024;
|
||||
pub const B576000: tcflag_t = 0o0025;
|
||||
pub const B921600: tcflag_t = 0o0026;
|
||||
pub const B1000000: tcflag_t = 0o0027;
|
||||
pub const B1152000: tcflag_t = 0o0030;
|
||||
pub const B1500000: tcflag_t = 0o0031;
|
||||
pub const B2000000: tcflag_t = 0o0032;
|
||||
pub const B2500000: tcflag_t = 0o0033;
|
||||
pub const B3000000: tcflag_t = 0o0034;
|
||||
pub const B3500000: tcflag_t = 0o0035;
|
||||
pub const B4000000: tcflag_t = 0o0036;
|
||||
|
||||
pub const __MAX_BAUD: tcflag_t = B4000000;
|
||||
|
||||
pub const CSIZE: tcflag_t = 0o0001400;
|
||||
pub const CS5: tcflag_t = 0o0000000;
|
||||
pub const CS6: tcflag_t = 0o0000400;
|
||||
pub const CS7: tcflag_t = 0o0001000;
|
||||
pub const CS8: tcflag_t = 0o0001400;
|
||||
|
||||
pub const CSTOPB: tcflag_t = 0o0002000;
|
||||
pub const CREAD: tcflag_t = 0o0004000;
|
||||
pub const PARENB: tcflag_t = 0o0010000;
|
||||
pub const PARODD: tcflag_t = 0o0020000;
|
||||
pub const HUPCL: tcflag_t = 0o0040000;
|
||||
|
||||
pub const CLOCAL: tcflag_t = 0o0100000;
|
||||
/* } c_clfag */
|
||||
|
||||
/* c_lflag { */
|
||||
pub const ISIG: tcflag_t = 0x00000080;
|
||||
pub const ICANON: tcflag_t = 0x00000100;
|
||||
pub const ECHO: tcflag_t = 0x00000008;
|
||||
pub const ECHOE: tcflag_t = 0x00000002;
|
||||
pub const ECHOK: tcflag_t = 0x00000004;
|
||||
pub const ECHONL: tcflag_t = 0x00000010;
|
||||
pub const NOFLSH: tcflag_t = 0x80000000;
|
||||
pub const TOSTOP: tcflag_t = 0x00400000;
|
||||
pub const IEXTEN: tcflag_t = 0x00000400;
|
||||
/* } c_lflag */
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Termios {
|
||||
pub c_iflag: tcflag_t,
|
||||
pub c_oflag: tcflag_t,
|
||||
pub c_cflag: tcflag_t,
|
||||
pub c_lflag: tcflag_t,
|
||||
pub c_cc: [cc_t; 32]
|
||||
}
|
||||
|
||||
impl Default for Termios {
|
||||
fn default() -> Termios {
|
||||
let mut termios = Termios {
|
||||
c_iflag: ICRNL | IXON,
|
||||
c_oflag: OPOST | ONLCR,
|
||||
c_cflag: B38400 | CS8 | CREAD | HUPCL,
|
||||
c_lflag: ISIG | ICANON | ECHO | ECHOE | ECHOK | IEXTEN,
|
||||
c_cc: [0; 32]
|
||||
};
|
||||
|
||||
{
|
||||
let mut cc = |i: usize, b: cc_t| {
|
||||
termios.c_cc[i] = b;
|
||||
};
|
||||
|
||||
cc(VEOF, 0o004); // CTRL-D
|
||||
cc(VEOL, 0o000); // NUL
|
||||
cc(VEOL2, 0o000); // NUL
|
||||
cc(VERASE, 0o177); // DEL
|
||||
cc(VWERASE, 0o027); // CTRL-W
|
||||
cc(VKILL, 0o025); // CTRL-U
|
||||
cc(VREPRINT, 0o022);// CTRL-R
|
||||
cc(VINTR, 0o003); // CTRL-C
|
||||
cc(VQUIT, 0o034); // CTRL-\
|
||||
cc(VSUSP, 0o032); // CTRL-Z
|
||||
cc(VSTART, 0o021); // CTRL-Q
|
||||
cc(VSTOP, 0o023); // CTRL-S
|
||||
cc(VLNEXT, 0o026); // CTRL-V
|
||||
cc(VDISCARD, 0o017);// CTRL-U
|
||||
cc(VMIN, 1);
|
||||
cc(VTIME, 0);
|
||||
}
|
||||
|
||||
termios
|
||||
}
|
||||
}
|
||||
|
||||
impl Termios {
|
||||
pub fn make_raw(&mut self) {
|
||||
self.c_iflag &= !(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
|
||||
self.c_oflag &= !OPOST;
|
||||
self.c_cflag &= !(CSIZE | PARENB);
|
||||
self.c_cflag |= CS8;
|
||||
self.c_lflag &= !(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Termios {
|
||||
type Target = [u8];
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts(self as *const Termios as *const u8, mem::size_of::<Termios>()) as &[u8]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Termios {
|
||||
fn deref_mut(&mut self) -> &mut [u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(self as *mut Termios as *mut u8, mem::size_of::<Termios>()) as &mut [u8]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct Winsize {
|
||||
pub ws_row: u16,
|
||||
pub ws_col: u16
|
||||
}
|
||||
|
||||
impl Deref for Winsize {
|
||||
type Target = [u8];
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts(self as *const Winsize as *const u8, mem::size_of::<Winsize>()) as &[u8]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Winsize {
|
||||
fn deref_mut(&mut self) -> &mut [u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(self as *mut Winsize as *mut u8, mem::size_of::<Winsize>()) as &mut [u8]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{"files":{".travis.yml":"5ac96d0b7956730882c41fa620f04388ff3100205fac606ae3bd8353661794a9","Cargo.toml":"b061b09fc4bbf280c932f4b7d3c4641a5a2f0eb8c57029de7d0e4441888a1765","LICENSE":"6252f0c8d4a0df9b2dc0c6464cb2489dbe8859b0eb727e19c14e6af1ee432394","README.md":"71cac837cd6f1326865add7dd565ea2738756648de87fc2d35d0cf22a2512630","examples/alternate_screen.rs":"37978473e77331ad613843049b4f355e32a51a7b8ece9ee52efe02997391e4ec","examples/alternate_screen_raw.rs":"bfd68f86de929952aaed7e1e7175694d771be2a0b0943092fec4f58bf7473dc3","examples/async.rs":"2fdf5fe69edd3b407de3c1c8caf23f9e19a7f25b55578c84838e4305a9857c42","examples/click.rs":"bb3a76f4817292a82b00d92281a86a400038a8282b6c9d41a34dbaca84fb0caf","examples/color.rs":"808219c739677b9f2645e0ae7975a4bd8981255ed8bfc9df2413f8f8059bbda5","examples/commie.rs":"7bb00a7f669c74ccb3a9e9c8ff39fd01aa984f7c38983e5c1cfe6940f90b0c73","examples/detect_color.rs":"764d6465c6879efc38aca8d980a433bac058c1f03d578ac764437af289931824","examples/is_tty.rs":"9a76bdfb11ea84e7b25f6efc06c8d3a8d13a03448bd826bd283b935852f409c7","examples/keys.rs":"cd89f31a21062486e7c014843b360cee35dcee07d8022fc0282eddc48e46bdc7","examples/mouse.rs":"eeccab8043cec987e40e175f03b63c9c6cdc7bcd5808f51a57d0e970cae1c04e","examples/rainbow.rs":"c015176eba7c7a81c6d302d6fc41abea8817935ee804af59d64e3098b127c333","examples/read.rs":"b95fb9b02d2cbf978096825b18ffa25772467df3417c3f3cf5edfe811181a67a","examples/rustc_fun.rs":"f39bd8dbe224cb592d58a0084d15d1eb80d86eade72423fe51acc455e0e1ea68","examples/simple.rs":"eac1aab0d251f884a7d7d7d0d017b6ac4854b38ffcfb244d6a80102ed7a981ab","examples/size.rs":"ae89e7b98a29040f1b641d75226f5738770e2e043cee3f49649accaaeaabbeac","examples/truecolor.rs":"67128ef4870e9e742b1090fa129097484633d82fb015009b1760295f7a24e6af","logo.svg":"09b7a6bca3185acddc217d3d3baab23627c5e03a192f815116e71cf00cb31ddc","src/async.rs":"f0126ff1ee18c8c355bb86deb76e852c5f6bb47fbbaa286cce3d38cb656dfa28","src/clear.rs":"a9cf9a9f92cef2430239dc33d8ecb4e291c46b650f1f71b63248d0a0215768ba","src/color.rs":"2f2c8f6f572b22fc40a62d0502fc192c843dbeed21bb7ecc1954e881d41c9a1c","src/cursor.rs":"9132638902c4e42728efaac247ca363c1bda3e80d6c3cc20bc6928b9fe9a1d10","src/event.rs":"321c0a73cd6208a8ef17083d1bd907e308d5197cfd198a3f1ed1498b7e2b8055","src/input.rs":"a3e226a53b6536aef89d66a1e4974453389c86116c175c3efc4fc4bb9b41d60c","src/lib.rs":"549beee5f5a6fb7954af37df7fa598ba6c716f2287123e09d300e048af2a70ac","src/macros.rs":"8611f257ffc046ed25870611f1353a96e17a5671efbe2bbf33aae525b53a4df3","src/raw.rs":"6949b164cf1bd0a4146c169b948100ed626b3e0747df15a54b7f41853d348ea1","src/screen.rs":"19a9a2c42abe2afdb75d4850dc6ee54f33808f6da7f59644668e683418cbce4a","src/scroll.rs":"d0ba08663127e09e111fafc9f84c1fe42662b74f9acbca6b5d26d0b335ee4136","src/style.rs":"b2c805d710cf553835a01482f014aff7395f79d5f71f1e5029ae679e002ac010","src/sys/redox/attr.rs":"834d21cc17090fb7989906ce885f4efed252d65086c05e32ecc05792eb69151a","src/sys/redox/mod.rs":"4066ba97f10c8b87a6ebf32257c89d36338366e1230781aeb5fca98bd5f5bae0","src/sys/redox/size.rs":"b892d0053f40c343eeb40ea06a10fb77abd2758eecda6a7cccb0b07c07fff6eb","src/sys/redox/tty.rs":"3369a6ee3a21400053a023c01912bdd92b709915b833e25e557214dfad425224","src/sys/unix/attr.rs":"7b17e4841eab69533d2561764506211e2967cd4d7464a309e6d2832473b75dd7","src/sys/unix/mod.rs":"8ddebce9f5b2dbbd419519beb7d41f6d6a7eaed40a70cd0d0676ee893549f7a7","src/sys/unix/size.rs":"19f3de9ced2b329a50a9752ba1be406b25648b298c1a2b3e83582a34e21d2998","src/sys/unix/tty.rs":"eeea0279f76838aa4badd807325403d32ab359334cf59f9574191afcbe86c811"},"package":"689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"}
|
|
@ -0,0 +1,14 @@
|
|||
language: rust
|
||||
cache: cargo
|
||||
rust:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then FAKETTY="script -q /dev/null"; fi
|
||||
- $FAKETTY cargo build --verbose
|
||||
- $FAKETTY cargo test --verbose
|
||||
- $FAKETTY cargo test --release --verbose
|
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "termion"
|
||||
version = "1.5.1"
|
||||
authors = ["ticki <Ticki@users.noreply.github.com>", "gycos <alexandre.bury@gmail.com>", "IGI-111 <igi-111@protonmail.com>"]
|
||||
description = "A bindless library for manipulating terminals."
|
||||
repository = "https://github.com/ticki/termion"
|
||||
documentation = "https://docs.rs/termion"
|
||||
license = "MIT"
|
||||
keywords = ["tty", "color", "terminal", "password", "tui"]
|
||||
exclude = ["target", "CHANGELOG.md", "image.png", "Cargo.lock"]
|
||||
|
||||
[target.'cfg(not(target_os = "redox"))'.dependencies]
|
||||
libc = "0.2.8"
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
redox_syscall = "0.1"
|
||||
redox_termios = "0.1"
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Ticki
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,181 @@
|
|||
<p align="center">
|
||||
<img alt="Termion logo" src="https://rawgit.com/ticki/termion/master/logo.svg" />
|
||||
</p>
|
||||
|
||||
[![Build Status](https://travis-ci.org/ticki/termion.svg?branch=master)](https://travis-ci.org/ticki/termion) [![Latest Version](https://img.shields.io/crates/v/termion.svg)](https://crates.io/crates/termion) | [Documentation](https://docs.rs/termion) | [Examples](https://github.com/Ticki/termion/tree/master/examples) | [Changelog](https://github.com/Ticki/termion/tree/master/CHANGELOG.md) | [Tutorial](http://ticki.github.io/blog/making-terminal-applications-in-rust-with-termion/)
|
||||
|----|----|----|----|----
|
||||
|
||||
|
||||
**Termion** is a pure Rust, bindless library for low-level handling, manipulating
|
||||
and reading information about terminals. This provides a full-featured
|
||||
alternative to Termbox.
|
||||
|
||||
Termion aims to be simple and yet expressive. It is bindless, meaning that it
|
||||
is not a front-end to some other library (e.g., ncurses or termbox), but a
|
||||
standalone library directly talking to the TTY.
|
||||
|
||||
Termion is quite convenient, due to its complete coverage of essential TTY
|
||||
features, providing one consistent API. Termion is rather low-level containing
|
||||
only abstraction aligned with what actually happens behind the scenes. For
|
||||
something more high-level, refer to inquirer-rs, which uses Termion as backend.
|
||||
|
||||
Termion generates escapes and API calls for the user. This makes it a whole lot
|
||||
cleaner to use escapes.
|
||||
|
||||
Supports Redox, Mac OS X, BSD, and Linux (or, in general, ANSI terminals).
|
||||
|
||||
## A note on stability
|
||||
|
||||
This crate is stable.
|
||||
|
||||
## Cargo.toml
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
termion = "*"
|
||||
```
|
||||
|
||||
## 0.1.0 to 1.0.0 guide
|
||||
|
||||
This sample table gives an idea of how to go about converting to the new major
|
||||
version of Termion.
|
||||
|
||||
| 0.1.0 | 1.0.0
|
||||
|--------------------------------|---------------------------
|
||||
| `use termion::IntoRawMode` | `use termion::raw::IntoRawMode`
|
||||
| `use termion::TermRead` | `use termion::input::TermRead`
|
||||
| `stdout.color(color::Red);` | `write!(stdout, "{}", color::Fg(color::Red));`
|
||||
| `stdout.color_bg(color::Red);` | `write!(stdout, "{}", color::Bg(color::Red));`
|
||||
| `stdout.goto(x, y);` | `write!(stdout, "{}", cursor::Goto(x, y));`
|
||||
| `color::rgb(r, g, b);` | `color::Rgb(r, g, b)` (truecolor)
|
||||
| `x.with_mouse()` | `MouseTerminal::from(x)`
|
||||
|
||||
## Features
|
||||
|
||||
- Raw mode.
|
||||
- TrueColor.
|
||||
- 256-color mode.
|
||||
- Cursor movement.
|
||||
- Text formatting.
|
||||
- Console size.
|
||||
- TTY-only stream.
|
||||
- Control sequences.
|
||||
- Termios control.
|
||||
- Password input.
|
||||
- Redox support.
|
||||
- Safe `isatty` wrapper.
|
||||
- Panic-free error handling.
|
||||
- Special keys events (modifiers, special keys, etc.).
|
||||
- Allocation-free.
|
||||
- Asynchronous key events.
|
||||
- Mouse input.
|
||||
- Carefully tested.
|
||||
- Detailed documentation on every item.
|
||||
|
||||
and much more.
|
||||
|
||||
## Examples
|
||||
|
||||
### Style and colors.
|
||||
|
||||
```rust
|
||||
extern crate termion;
|
||||
|
||||
use termion::{color, style};
|
||||
|
||||
use std::io;
|
||||
|
||||
fn main() {
|
||||
println!("{}Red", color::Fg(color::Red));
|
||||
println!("{}Blue", color::Fg(color::Blue));
|
||||
println!("{}Blue'n'Bold{}", style::Bold, style::Reset);
|
||||
println!("{}Just plain italic", style::Italic);
|
||||
}
|
||||
```
|
||||
|
||||
### Moving the cursor
|
||||
|
||||
```rust
|
||||
extern crate termion;
|
||||
|
||||
fn main() {
|
||||
print!("{}{}Stuff", termion::clear::All, termion::cursor::Goto(1, 1));
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Mouse
|
||||
|
||||
```rust
|
||||
extern crate termion;
|
||||
|
||||
use termion::event::{Key, Event, MouseEvent};
|
||||
use termion::input::{TermRead, MouseTerminal};
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
fn main() {
|
||||
let stdin = stdin();
|
||||
let mut stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap());
|
||||
|
||||
write!(stdout, "{}{}q to exit. Click, click, click!", termion::clear::All, termion::cursor::Goto(1, 1)).unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
for c in stdin.events() {
|
||||
let evt = c.unwrap();
|
||||
match evt {
|
||||
Event::Key(Key::Char('q')) => break,
|
||||
Event::Mouse(me) => {
|
||||
match me {
|
||||
MouseEvent::Press(_, x, y) => {
|
||||
write!(stdout, "{}x", termion::cursor::Goto(x, y)).unwrap();
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Read a password
|
||||
|
||||
```rust
|
||||
extern crate termion;
|
||||
|
||||
use termion::input::TermRead;
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
fn main() {
|
||||
let stdout = stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
let stdin = stdin();
|
||||
let mut stdin = stdin.lock();
|
||||
|
||||
stdout.write_all(b"password: ").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
let pass = stdin.read_passwd(&mut stdout);
|
||||
|
||||
if let Ok(Some(pass)) = pass {
|
||||
stdout.write_all(pass.as_bytes()).unwrap();
|
||||
stdout.write_all(b"\n").unwrap();
|
||||
} else {
|
||||
stdout.write_all(b"Error\n").unwrap();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
See `examples/`, and the documentation, which can be rendered using `cargo doc`.
|
||||
|
||||
For a more complete example, see [a minesweeper implementation](https://github.com/redox-os/games-for-redox/blob/master/src/minesweeper/main.rs), that I made for Redox using termion.
|
||||
|
||||
<img src="image.png" width="200">
|
||||
|
||||
## License
|
||||
|
||||
MIT/X11.
|
|
@ -0,0 +1,17 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::screen::*;
|
||||
use std::io::{Write, stdout};
|
||||
use std::{time, thread};
|
||||
|
||||
fn main() {
|
||||
{
|
||||
let mut screen = AlternateScreen::from(stdout());
|
||||
write!(screen, "Welcome to the alternate screen.\n\nPlease wait patiently until we arrive back at the main screen in a about three seconds.").unwrap();
|
||||
screen.flush().unwrap();
|
||||
|
||||
thread::sleep(time::Duration::from_secs(3));
|
||||
}
|
||||
|
||||
println!("Phew! We are back.");
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::event::Key;
|
||||
use termion::input::TermRead;
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::screen::*;
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
fn write_alt_screen_msg<W: Write>(screen: &mut W) {
|
||||
write!(screen, "{}{}Welcome to the alternate screen.{}Press '1' to switch to the main screen or '2' to switch to the alternate screen.{}Press 'q' to exit (and switch back to the main screen).",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(1, 1),
|
||||
termion::cursor::Goto(1, 3),
|
||||
termion::cursor::Goto(1, 4)).unwrap();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let stdin = stdin();
|
||||
let mut screen = AlternateScreen::from(stdout().into_raw_mode().unwrap());
|
||||
write!(screen, "{}", termion::cursor::Hide).unwrap();
|
||||
write_alt_screen_msg(&mut screen);
|
||||
|
||||
screen.flush().unwrap();
|
||||
|
||||
for c in stdin.keys() {
|
||||
match c.unwrap() {
|
||||
Key::Char('q') => break,
|
||||
Key::Char('1') => {
|
||||
write!(screen, "{}", ToMainScreen).unwrap();
|
||||
}
|
||||
Key::Char('2') => {
|
||||
write!(screen, "{}", ToAlternateScreen).unwrap();
|
||||
write_alt_screen_msg(&mut screen);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
screen.flush().unwrap();
|
||||
}
|
||||
write!(screen, "{}", termion::cursor::Show).unwrap();
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::async_stdin;
|
||||
use std::io::{Read, Write, stdout};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
let stdout = stdout();
|
||||
let mut stdout = stdout.lock().into_raw_mode().unwrap();
|
||||
let mut stdin = async_stdin().bytes();
|
||||
|
||||
write!(stdout,
|
||||
"{}{}",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(1, 1))
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
write!(stdout, "{}", termion::clear::CurrentLine).unwrap();
|
||||
|
||||
let b = stdin.next();
|
||||
write!(stdout, "\r{:?} <- This demonstrates the async read input char. Between each update a 100 ms. is waited, simply to demonstrate the async fashion. \n\r", b).unwrap();
|
||||
if let Some(Ok(b'q')) = b {
|
||||
break;
|
||||
}
|
||||
|
||||
stdout.flush().unwrap();
|
||||
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
stdout.write_all(b"# ").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
stdout.write_all(b"\r #").unwrap();
|
||||
write!(stdout, "{}", termion::cursor::Goto(1, 1)).unwrap();
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::event::{Key, Event, MouseEvent};
|
||||
use termion::input::{TermRead, MouseTerminal};
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
fn main() {
|
||||
let stdin = stdin();
|
||||
let mut stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap());
|
||||
|
||||
write!(stdout,
|
||||
"{}{}q to exit. Click, click, click!",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(1, 1))
|
||||
.unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
for c in stdin.events() {
|
||||
let evt = c.unwrap();
|
||||
match evt {
|
||||
Event::Key(Key::Char('q')) => break,
|
||||
Event::Mouse(me) => {
|
||||
match me {
|
||||
MouseEvent::Press(_, x, y) => {
|
||||
write!(stdout, "{}x", termion::cursor::Goto(x, y)).unwrap();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::{color, style};
|
||||
|
||||
fn main() {
|
||||
println!("{}Red", color::Fg(color::Red));
|
||||
println!("{}Blue", color::Fg(color::Blue));
|
||||
println!("{}Blue'n'Bold{}", style::Bold, style::Reset);
|
||||
println!("{}Just plain italic", style::Italic);
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::{clear, color, cursor};
|
||||
|
||||
use std::{time, thread};
|
||||
|
||||
const COMMUNISM: &'static str = r#"
|
||||
!######### #
|
||||
!########! ##!
|
||||
!########! ###
|
||||
!########## ####
|
||||
######### ##### ######
|
||||
!###! !####! ######
|
||||
! ##### ######!
|
||||
!####! #######
|
||||
##### #######
|
||||
!####! #######!
|
||||
####!########
|
||||
## ##########
|
||||
,######! !#############
|
||||
,#### ########################!####!
|
||||
,####' ##################!' #####
|
||||
,####' ####### !####!
|
||||
####' #####
|
||||
~## ##~
|
||||
"#;
|
||||
|
||||
fn main() {
|
||||
let mut state = 0;
|
||||
|
||||
println!("\n{}{}{}{}{}{}",
|
||||
cursor::Hide,
|
||||
clear::All,
|
||||
cursor::Goto(1, 1),
|
||||
color::Fg(color::Black),
|
||||
color::Bg(color::Red),
|
||||
COMMUNISM);
|
||||
loop {
|
||||
println!("{}{} ☭ GAY ☭ SPACE ☭ COMMUNISM ☭ ",
|
||||
cursor::Goto(1, 1),
|
||||
color::Bg(color::AnsiValue(state)));
|
||||
println!("{}{} WILL PREVAIL, COMRADES! ",
|
||||
cursor::Goto(1, 20),
|
||||
color::Bg(color::AnsiValue(state)));
|
||||
|
||||
state += 1;
|
||||
state %= 8;
|
||||
|
||||
thread::sleep(time::Duration::from_millis(90));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::color::{DetectColors, AnsiValue, Bg};
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::stdout;
|
||||
|
||||
fn main() {
|
||||
let count;
|
||||
{
|
||||
let mut term = stdout().into_raw_mode().unwrap();
|
||||
count = term.available_colors().unwrap();
|
||||
}
|
||||
|
||||
println!("This terminal supports {} colors.", count);
|
||||
for i in 0..count {
|
||||
print!("{} {}", Bg(AnsiValue(i as u8)), Bg(AnsiValue(0)));
|
||||
}
|
||||
println!();
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
extern crate termion;
|
||||
|
||||
use std::fs;
|
||||
|
||||
fn main() {
|
||||
if termion::is_tty(&fs::File::create("/dev/stdout").unwrap()) {
|
||||
println!("This is a TTY!");
|
||||
} else {
|
||||
println!("This is not a TTY :(");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::event::Key;
|
||||
use termion::input::TermRead;
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
fn main() {
|
||||
let stdin = stdin();
|
||||
let mut stdout = stdout().into_raw_mode().unwrap();
|
||||
|
||||
write!(stdout,
|
||||
"{}{}q to exit. Type stuff, use alt, and so on.{}",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(1, 1),
|
||||
termion::cursor::Hide)
|
||||
.unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
for c in stdin.keys() {
|
||||
write!(stdout,
|
||||
"{}{}",
|
||||
termion::cursor::Goto(1, 1),
|
||||
termion::clear::CurrentLine)
|
||||
.unwrap();
|
||||
|
||||
match c.unwrap() {
|
||||
Key::Char('q') => break,
|
||||
Key::Char(c) => println!("{}", c),
|
||||
Key::Alt(c) => println!("^{}", c),
|
||||
Key::Ctrl(c) => println!("*{}", c),
|
||||
Key::Esc => println!("ESC"),
|
||||
Key::Left => println!("←"),
|
||||
Key::Right => println!("→"),
|
||||
Key::Up => println!("↑"),
|
||||
Key::Down => println!("↓"),
|
||||
Key::Backspace => println!("×"),
|
||||
_ => {}
|
||||
}
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
|
||||
write!(stdout, "{}", termion::cursor::Show).unwrap();
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::event::*;
|
||||
use termion::cursor::{self, DetectCursorPos};
|
||||
use termion::input::{TermRead, MouseTerminal};
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::{self, Write};
|
||||
|
||||
fn main() {
|
||||
let stdin = io::stdin();
|
||||
let mut stdout = MouseTerminal::from(io::stdout().into_raw_mode().unwrap());
|
||||
|
||||
writeln!(stdout,
|
||||
"{}{}q to exit. Type stuff, use alt, click around...",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(1, 1))
|
||||
.unwrap();
|
||||
|
||||
for c in stdin.events() {
|
||||
let evt = c.unwrap();
|
||||
match evt {
|
||||
Event::Key(Key::Char('q')) => break,
|
||||
Event::Mouse(me) => {
|
||||
match me {
|
||||
MouseEvent::Press(_, a, b) |
|
||||
MouseEvent::Release(a, b) |
|
||||
MouseEvent::Hold(a, b) => {
|
||||
write!(stdout, "{}", cursor::Goto(a, b)).unwrap();
|
||||
let (x, y) = stdout.cursor_pos().unwrap();
|
||||
write!(stdout,
|
||||
"{}{}Cursor is at: ({},{}){}",
|
||||
cursor::Goto(5, 5),
|
||||
termion::clear::UntilNewline,
|
||||
x,
|
||||
y,
|
||||
cursor::Goto(a, b))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::event::Key;
|
||||
use termion::input::TermRead;
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
fn rainbow<W: Write>(stdout: &mut W, blue: u8) {
|
||||
write!(stdout,
|
||||
"{}{}",
|
||||
termion::cursor::Goto(1, 1),
|
||||
termion::clear::All)
|
||||
.unwrap();
|
||||
|
||||
for red in 0..32 {
|
||||
let red = red * 8;
|
||||
for green in 0..64 {
|
||||
let green = green * 4;
|
||||
write!(stdout,
|
||||
"{} ",
|
||||
termion::color::Bg(termion::color::Rgb(red, green, blue)))
|
||||
.unwrap();
|
||||
}
|
||||
write!(stdout, "\n\r").unwrap();
|
||||
}
|
||||
|
||||
writeln!(stdout, "{}b = {}", termion::style::Reset, blue).unwrap();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let stdin = stdin();
|
||||
let mut stdout = stdout().into_raw_mode().unwrap();
|
||||
|
||||
writeln!(stdout,
|
||||
"{}{}{}Use the up/down arrow keys to change the blue in the rainbow.",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(1, 1),
|
||||
termion::cursor::Hide)
|
||||
.unwrap();
|
||||
|
||||
let mut blue = 172u8;
|
||||
|
||||
for c in stdin.keys() {
|
||||
match c.unwrap() {
|
||||
Key::Up => {
|
||||
blue = blue.saturating_add(4);
|
||||
rainbow(&mut stdout, blue);
|
||||
}
|
||||
Key::Down => {
|
||||
blue = blue.saturating_sub(4);
|
||||
rainbow(&mut stdout, blue);
|
||||
}
|
||||
Key::Char('q') => break,
|
||||
_ => {}
|
||||
}
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
|
||||
write!(stdout, "{}", termion::cursor::Show).unwrap();
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::input::TermRead;
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
fn main() {
|
||||
let stdout = stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
let stdin = stdin();
|
||||
let mut stdin = stdin.lock();
|
||||
|
||||
stdout.write_all(b"password: ").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
let pass = stdin.read_passwd(&mut stdout);
|
||||
|
||||
if let Ok(Some(pass)) = pass {
|
||||
stdout.write_all(pass.as_bytes()).unwrap();
|
||||
stdout.write_all(b"\n").unwrap();
|
||||
} else {
|
||||
stdout.write_all(b"Error\n").unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::{color, style};
|
||||
|
||||
fn main() {
|
||||
println!("{lighgreen}-- src/test/ui/borrow-errors.rs at 82:18 --\n\
|
||||
{red}error: {reset}{bold}two closures require unique access to `vec` at the same time {reset}{bold}{magenta}[E0524]{reset}\n\
|
||||
{line_num_fg}{line_num_bg}79 {reset} let append = |e| {{\n\
|
||||
{line_num_fg}{line_num_bg}{info_line}{reset} {red}^^^{reset} {error_fg}first closure is constructed here\n\
|
||||
{line_num_fg}{line_num_bg}80 {reset} vec.push(e)\n\
|
||||
{line_num_fg}{line_num_bg}{info_line}{reset} {red}^^^{reset} {error_fg}previous borrow occurs due to use of `vec` in closure\n\
|
||||
{line_num_fg}{line_num_bg}84 {reset} }};\n\
|
||||
{line_num_fg}{line_num_bg}85 {reset} }}\n\
|
||||
{line_num_fg}{line_num_bg}{info_line}{reset} {red}^{reset} {error_fg}borrow from first closure ends here",
|
||||
lighgreen = color::Fg(color::LightGreen),
|
||||
red = color::Fg(color::Red),
|
||||
bold = style::Bold,
|
||||
reset = style::Reset,
|
||||
magenta = color::Fg(color::Magenta),
|
||||
line_num_bg = color::Bg(color::AnsiValue::grayscale(3)),
|
||||
line_num_fg = color::Fg(color::AnsiValue::grayscale(18)),
|
||||
info_line = "| ",
|
||||
error_fg = color::Fg(color::AnsiValue::grayscale(17)))
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::color;
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::{Read, Write, stdout, stdin};
|
||||
|
||||
fn main() {
|
||||
// Initialize 'em all.
|
||||
let stdout = stdout();
|
||||
let mut stdout = stdout.lock().into_raw_mode().unwrap();
|
||||
let stdin = stdin();
|
||||
let stdin = stdin.lock();
|
||||
|
||||
write!(stdout,
|
||||
"{}{}{}yo, 'q' will exit.{}{}",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(5, 5),
|
||||
termion::style::Bold,
|
||||
termion::style::Reset,
|
||||
termion::cursor::Goto(20, 10))
|
||||
.unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
let mut bytes = stdin.bytes();
|
||||
loop {
|
||||
let b = bytes.next().unwrap().unwrap();
|
||||
|
||||
match b {
|
||||
// Quit
|
||||
b'q' => return,
|
||||
// Clear the screen
|
||||
b'c' => write!(stdout, "{}", termion::clear::All),
|
||||
// Set red color
|
||||
b'r' => write!(stdout, "{}", color::Fg(color::Rgb(5, 0, 0))),
|
||||
// Write it to stdout.
|
||||
a => write!(stdout, "{}", a),
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::terminal_size;
|
||||
|
||||
fn main() {
|
||||
println!("Size is {:?}", terminal_size().unwrap())
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::{color, cursor, clear};
|
||||
use std::{thread, time};
|
||||
|
||||
fn main() {
|
||||
for r in 0..255 {
|
||||
let c = color::Rgb(r, !r, 2 * ((r % 128) as i8 - 64).abs() as u8);
|
||||
println!("{}{}{}wow", cursor::Goto(1, 1), color::Bg(c), clear::All);
|
||||
thread::sleep(time::Duration::from_millis(100));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="60.099598mm" height="18.291185mm" viewBox="0 0 212.95 64.81">
|
||||
<style>
|
||||
.blink { animation: blinker 3s linear infinite; } @keyframes blinker { 50% { opacity: 0; } }
|
||||
</style>
|
||||
|
||||
<path d="M0 0h212.95v64.82H0z" opacity=".71"/>
|
||||
<path fill="#f9f9f9" d="M12.24 17.8H34.5v3.33h-9.13v25.84H21.4V21.13h-9.16V17.8zm27 0h17.3v3.33H43.2v8.63h12.77v3.32h-12.8v10.57H56.9v3.32H39.24V17.8zM74.3 33.2q1.5.4 2.6 1.48 1.06 1.08 2.66 4.32l3.97 7.97H79.3l-3.5-7.37q-1.5-3.14-2.7-4.04-1.2-.92-3.13-.92H66.2v12.33h-3.96V17.8h8.13q4.8 0 7.36 2.17 2.56 2.17 2.56 6.27 0 2.9-1.6 4.73-1.6 1.82-4.4 2.23zm-8.1-12.15V31.4h4.32q2.83 0 4.22-1.27 1.4-1.27 1.4-3.9 0-2.5-1.5-3.83-1.46-1.35-4.27-1.35H66.2zm19-3.25h5.26l5.04 14.85 5.08-14.84h5.3V47h-3.66V21.2l-5.2 15.38h-2.98L88.82 21.2v25.77H85.2V17.8zm26.3 0h16.2v3.33h-6.12v22.52h6.1v3.32H111.5v-3.32h6.1V21.13h-6.1V17.8zm37.8 14.62q0-6.43-1.32-9.18-1.3-2.76-4.3-2.76t-4.33 2.76q-1.3 2.75-1.3 9.18 0 6.4 1.3 9.16 1.33 2.75 4.32 2.75 3 0 4.3-2.73 1.34-2.76 1.34-9.18zm4.13 0q0 7.6-2.42 11.36-2.4 3.75-7.3 3.75t-7.3-3.73q-2.4-3.73-2.4-11.38 0-7.64 2.4-11.4 2.4-3.74 7.35-3.74t7.34 3.75q2.42 3.75 2.42 11.4zm4.97-14.62h5l9.86 24v-24h3.8v29.17h-5l-9.84-24v24h-3.8V17.8z"/>
|
||||
<path fill="#f9f9f9" d="M192.7 8.66v47.5h-3.93V8.66h3.94z" class="blink"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 1.3 KiB |
|
@ -0,0 +1,78 @@
|
|||
use std::io::{self, Read};
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
|
||||
use sys::tty::get_tty;
|
||||
|
||||
/// Construct an asynchronous handle to the TTY standard input.
|
||||
///
|
||||
/// This allows you to read from standard input _without blocking_ the current thread.
|
||||
/// Specifically, it works by firing up another thread to handle the event stream, which will then
|
||||
/// be buffered in a mpsc queue, which will eventually be read by the current thread.
|
||||
///
|
||||
/// This will not read the piped standard input, but rather read from the TTY device, since reading
|
||||
/// asyncronized from piped input would rarely make sense. In other words, if you pipe standard
|
||||
/// output from another process, it won't be reflected in the stream returned by this function, as
|
||||
/// this represents the TTY device, and not the piped standard input.
|
||||
pub fn async_stdin() -> AsyncReader {
|
||||
let (send, recv) = mpsc::channel();
|
||||
|
||||
thread::spawn(move || for i in get_tty().unwrap().bytes() {
|
||||
if send.send(i).is_err() {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
AsyncReader { recv: recv }
|
||||
}
|
||||
|
||||
/// An asynchronous reader.
|
||||
///
|
||||
/// This acts as any other stream, with the exception that reading from it won't block. Instead,
|
||||
/// the buffer will only be partially updated based on how much the internal buffer holds.
|
||||
pub struct AsyncReader {
|
||||
/// The underlying mpsc receiver.
|
||||
recv: mpsc::Receiver<io::Result<u8>>,
|
||||
}
|
||||
|
||||
// FIXME: Allow constructing an async reader from an arbitrary stream.
|
||||
|
||||
impl Read for AsyncReader {
|
||||
/// Read from the byte stream.
|
||||
///
|
||||
/// This will never block, but try to drain the event queue until empty. If the total number of
|
||||
/// bytes written is lower than the buffer's length, the event queue is empty or that the event
|
||||
/// stream halted.
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let mut total = 0;
|
||||
|
||||
loop {
|
||||
if total >= buf.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
match self.recv.try_recv() {
|
||||
Ok(Ok(b)) => {
|
||||
buf[total] = b;
|
||||
total += 1;
|
||||
}
|
||||
Ok(Err(e)) => return Err(e),
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(total)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::io::Read;
|
||||
|
||||
#[test]
|
||||
fn test_async_stdin() {
|
||||
let stdin = async_stdin();
|
||||
stdin.bytes().next();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
//! Clearing the screen.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
derive_csi_sequence!("Clear the entire screen.", All, "2J");
|
||||
derive_csi_sequence!("Clear everything after the cursor.", AfterCursor, "J");
|
||||
derive_csi_sequence!("Clear everything before the cursor.", BeforeCursor, "1J");
|
||||
derive_csi_sequence!("Clear the current line.", CurrentLine, "2K");
|
||||
derive_csi_sequence!("Clear from cursor to newline.", UntilNewline, "K");
|
|
@ -0,0 +1,242 @@
|
|||
//! Color managemement.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! use termion::color;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! println!("{}Red", color::Fg(color::Red));
|
||||
//! println!("{}Blue", color::Fg(color::Blue));
|
||||
//! println!("{}Back again", color::Fg(color::Reset));
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::fmt;
|
||||
use raw::CONTROL_SEQUENCE_TIMEOUT;
|
||||
use std::io::{self, Write, Read};
|
||||
use std::time::{SystemTime, Duration};
|
||||
use async::async_stdin;
|
||||
use std::env;
|
||||
|
||||
/// A terminal color.
|
||||
pub trait Color {
|
||||
/// Write the foreground version of this color.
|
||||
fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result;
|
||||
/// Write the background version of this color.
|
||||
fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result;
|
||||
}
|
||||
|
||||
macro_rules! derive_color {
|
||||
($doc:expr, $name:ident, $value:expr) => {
|
||||
#[doc = $doc]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct $name;
|
||||
|
||||
impl Color for $name {
|
||||
#[inline]
|
||||
fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("38;5;", $value, "m"))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("48;5;", $value, "m"))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
derive_color!("Black.", Black, "0");
|
||||
derive_color!("Red.", Red, "1");
|
||||
derive_color!("Green.", Green, "2");
|
||||
derive_color!("Yellow.", Yellow, "3");
|
||||
derive_color!("Blue.", Blue, "4");
|
||||
derive_color!("Magenta.", Magenta, "5");
|
||||
derive_color!("Cyan.", Cyan, "6");
|
||||
derive_color!("White.", White, "7");
|
||||
derive_color!("High-intensity light black.", LightBlack, "8");
|
||||
derive_color!("High-intensity light red.", LightRed, "9");
|
||||
derive_color!("High-intensity light green.", LightGreen, "10");
|
||||
derive_color!("High-intensity light yellow.", LightYellow, "11");
|
||||
derive_color!("High-intensity light blue.", LightBlue, "12");
|
||||
derive_color!("High-intensity light magenta.", LightMagenta, "13");
|
||||
derive_color!("High-intensity light cyan.", LightCyan, "14");
|
||||
derive_color!("High-intensity light white.", LightWhite, "15");
|
||||
|
||||
impl<'a> Color for &'a Color {
|
||||
#[inline]
|
||||
fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
(*self).write_fg(f)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
(*self).write_bg(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// An arbitrary ANSI color value.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct AnsiValue(pub u8);
|
||||
|
||||
impl AnsiValue {
|
||||
/// 216-color (r, g, b ≤ 5) RGB.
|
||||
pub fn rgb(r: u8, g: u8, b: u8) -> AnsiValue {
|
||||
debug_assert!(r <= 5,
|
||||
"Red color fragment (r = {}) is out of bound. Make sure r ≤ 5.",
|
||||
r);
|
||||
debug_assert!(g <= 5,
|
||||
"Green color fragment (g = {}) is out of bound. Make sure g ≤ 5.",
|
||||
g);
|
||||
debug_assert!(b <= 5,
|
||||
"Blue color fragment (b = {}) is out of bound. Make sure b ≤ 5.",
|
||||
b);
|
||||
|
||||
AnsiValue(16 + 36 * r + 6 * g + b)
|
||||
}
|
||||
|
||||
/// Grayscale color.
|
||||
///
|
||||
/// There are 24 shades of gray.
|
||||
pub fn grayscale(shade: u8) -> AnsiValue {
|
||||
// Unfortunately, there are a little less than fifty shades.
|
||||
debug_assert!(shade < 24,
|
||||
"Grayscale out of bound (shade = {}). There are only 24 shades of \
|
||||
gray.",
|
||||
shade);
|
||||
|
||||
AnsiValue(0xE8 + shade)
|
||||
}
|
||||
}
|
||||
|
||||
impl Color for AnsiValue {
|
||||
#[inline]
|
||||
fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("38;5;{}m"), self.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("48;5;{}m"), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A truecolor RGB.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Rgb(pub u8, pub u8, pub u8);
|
||||
|
||||
impl Color for Rgb {
|
||||
#[inline]
|
||||
fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("38;2;{};{};{}m"), self.0, self.1, self.2)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("48;2;{};{};{}m"), self.0, self.1, self.2)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset colors to defaults.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Reset;
|
||||
|
||||
impl Color for Reset {
|
||||
#[inline]
|
||||
fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("39m"))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("49m"))
|
||||
}
|
||||
}
|
||||
|
||||
/// A foreground color.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Fg<C: Color>(pub C);
|
||||
|
||||
impl<C: Color> fmt::Display for Fg<C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.write_fg(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A background color.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Bg<C: Color>(pub C);
|
||||
|
||||
impl<C: Color> fmt::Display for Bg<C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.write_bg(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Types that allow detection of the colors they support.
|
||||
pub trait DetectColors {
|
||||
/// How many ANSI colors are supported (from 8 to 256)?
|
||||
///
|
||||
/// Beware: the information given isn't authoritative, it's infered through escape codes or the
|
||||
/// value of `TERM`, more colors may be available.
|
||||
fn available_colors(&mut self) -> io::Result<u16>;
|
||||
}
|
||||
|
||||
impl<W: Write> DetectColors for W {
|
||||
fn available_colors(&mut self) -> io::Result<u16> {
|
||||
let mut stdin = async_stdin();
|
||||
|
||||
if detect_color(self, &mut stdin, 0)? {
|
||||
// OSC 4 is supported, detect how many colors there are.
|
||||
// Do a binary search of the last supported color.
|
||||
let mut min = 8;
|
||||
let mut max = 256;
|
||||
let mut i;
|
||||
while min + 1 < max {
|
||||
i = (min + max) / 2;
|
||||
if detect_color(self, &mut stdin, i)? {
|
||||
min = i
|
||||
} else {
|
||||
max = i
|
||||
}
|
||||
}
|
||||
Ok(max)
|
||||
} else {
|
||||
// OSC 4 is not supported, trust TERM contents.
|
||||
Ok(match env::var_os("TERM") {
|
||||
Some(val) => {
|
||||
if val.to_str().unwrap_or("").contains("256color") {
|
||||
256
|
||||
} else {
|
||||
8
|
||||
}
|
||||
}
|
||||
None => 8,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Detect a color using OSC 4.
|
||||
fn detect_color(stdout: &mut Write, stdin: &mut Read, color: u16) -> io::Result<bool> {
|
||||
// Is the color available?
|
||||
// Use `ESC ] 4 ; color ; ? BEL`.
|
||||
write!(stdout, "\x1B]4;{};?\x07", color)?;
|
||||
stdout.flush()?;
|
||||
|
||||
let mut buf: [u8; 1] = [0];
|
||||
let mut total_read = 0;
|
||||
|
||||
let timeout = Duration::from_millis(CONTROL_SEQUENCE_TIMEOUT);
|
||||
let now = SystemTime::now();
|
||||
let bell = 7u8;
|
||||
|
||||
// Either consume all data up to bell or wait for a timeout.
|
||||
while buf[0] != bell && now.elapsed().unwrap() < timeout {
|
||||
total_read += stdin.read(&mut buf)?;
|
||||
}
|
||||
|
||||
// If there was a response, the color is supported.
|
||||
Ok(total_read > 0)
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
//! Cursor movement.
|
||||
|
||||
use std::fmt;
|
||||
use std::io::{self, Write, Error, ErrorKind, Read};
|
||||
use async::async_stdin;
|
||||
use std::time::{SystemTime, Duration};
|
||||
use raw::CONTROL_SEQUENCE_TIMEOUT;
|
||||
|
||||
derive_csi_sequence!("Hide the cursor.", Hide, "?25l");
|
||||
derive_csi_sequence!("Show the cursor.", Show, "?25h");
|
||||
|
||||
derive_csi_sequence!("Restore the cursor.", Restore, "u");
|
||||
derive_csi_sequence!("Save the cursor.", Save, "s");
|
||||
|
||||
/// Goto some position ((1,1)-based).
|
||||
///
|
||||
/// # Why one-based?
|
||||
///
|
||||
/// ANSI escapes are very poorly designed, and one of the many odd aspects is being one-based. This
|
||||
/// can be quite strange at first, but it is not that big of an obstruction once you get used to
|
||||
/// it.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate termion;
|
||||
///
|
||||
/// fn main() {
|
||||
/// print!("{}{}Stuff", termion::clear::All, termion::cursor::Goto(5, 3));
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Goto(pub u16, pub u16);
|
||||
|
||||
impl Default for Goto {
|
||||
fn default() -> Goto {
|
||||
Goto(1, 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Goto {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
debug_assert!(self != &Goto(0, 0), "Goto is one-based.");
|
||||
|
||||
write!(f, csi!("{};{}H"), self.1, self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Move cursor left.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Left(pub u16);
|
||||
|
||||
impl fmt::Display for Left {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("{}D"), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Move cursor right.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Right(pub u16);
|
||||
|
||||
impl fmt::Display for Right {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("{}C"), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Move cursor up.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Up(pub u16);
|
||||
|
||||
impl fmt::Display for Up {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("{}A"), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Move cursor down.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Down(pub u16);
|
||||
|
||||
impl fmt::Display for Down {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("{}B"), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Types that allow detection of the cursor position.
|
||||
pub trait DetectCursorPos {
|
||||
/// Get the (1,1)-based cursor position from the terminal.
|
||||
fn cursor_pos(&mut self) -> io::Result<(u16, u16)>;
|
||||
}
|
||||
|
||||
impl<W: Write> DetectCursorPos for W {
|
||||
fn cursor_pos(&mut self) -> io::Result<(u16, u16)> {
|
||||
let mut stdin = async_stdin();
|
||||
|
||||
// Where is the cursor?
|
||||
// Use `ESC [ 6 n`.
|
||||
write!(self, "\x1B[6n")?;
|
||||
self.flush()?;
|
||||
|
||||
let mut buf: [u8; 1] = [0];
|
||||
let mut read_chars = Vec::new();
|
||||
|
||||
let timeout = Duration::from_millis(CONTROL_SEQUENCE_TIMEOUT);
|
||||
let now = SystemTime::now();
|
||||
|
||||
// Either consume all data up to R or wait for a timeout.
|
||||
while buf[0] != b'R' && now.elapsed().unwrap() < timeout {
|
||||
if stdin.read(&mut buf)? > 0 {
|
||||
read_chars.push(buf[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if read_chars.len() == 0 {
|
||||
return Err(Error::new(ErrorKind::Other, "Cursor position detection timed out."));
|
||||
}
|
||||
|
||||
// The answer will look like `ESC [ Cy ; Cx R`.
|
||||
|
||||
read_chars.pop(); // remove trailing R.
|
||||
let read_str = String::from_utf8(read_chars).unwrap();
|
||||
let beg = read_str.rfind('[').unwrap();
|
||||
let coords: String = read_str.chars().skip(beg + 1).collect();
|
||||
let mut nums = coords.split(';');
|
||||
|
||||
let cy = nums.next()
|
||||
.unwrap()
|
||||
.parse::<u16>()
|
||||
.unwrap();
|
||||
let cx = nums.next()
|
||||
.unwrap()
|
||||
.parse::<u16>()
|
||||
.unwrap();
|
||||
|
||||
Ok((cx, cy))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
//! Mouse and key events.
|
||||
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::ascii::AsciiExt;
|
||||
use std::str;
|
||||
|
||||
/// An event reported by the terminal.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Event {
|
||||
/// A key press.
|
||||
Key(Key),
|
||||
/// A mouse button press, release or wheel use at specific coordinates.
|
||||
Mouse(MouseEvent),
|
||||
/// An event that cannot currently be evaluated.
|
||||
Unsupported(Vec<u8>),
|
||||
}
|
||||
|
||||
/// A mouse related event.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum MouseEvent {
|
||||
/// A mouse button was pressed.
|
||||
///
|
||||
/// The coordinates are one-based.
|
||||
Press(MouseButton, u16, u16),
|
||||
/// A mouse button was released.
|
||||
///
|
||||
/// The coordinates are one-based.
|
||||
Release(u16, u16),
|
||||
/// A mouse button is held over the given coordinates.
|
||||
///
|
||||
/// The coordinates are one-based.
|
||||
Hold(u16, u16),
|
||||
}
|
||||
|
||||
/// A mouse button.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum MouseButton {
|
||||
/// The left mouse button.
|
||||
Left,
|
||||
/// The right mouse button.
|
||||
Right,
|
||||
/// The middle mouse button.
|
||||
Middle,
|
||||
/// Mouse wheel is going up.
|
||||
///
|
||||
/// This event is typically only used with Mouse::Press.
|
||||
WheelUp,
|
||||
/// Mouse wheel is going down.
|
||||
///
|
||||
/// This event is typically only used with Mouse::Press.
|
||||
WheelDown,
|
||||
}
|
||||
|
||||
/// A key.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Key {
|
||||
/// Backspace.
|
||||
Backspace,
|
||||
/// Left arrow.
|
||||
Left,
|
||||
/// Right arrow.
|
||||
Right,
|
||||
/// Up arrow.
|
||||
Up,
|
||||
/// Down arrow.
|
||||
Down,
|
||||
/// Home key.
|
||||
Home,
|
||||
/// End key.
|
||||
End,
|
||||
/// Page Up key.
|
||||
PageUp,
|
||||
/// Page Down key.
|
||||
PageDown,
|
||||
/// Delete key.
|
||||
Delete,
|
||||
/// Insert key.
|
||||
Insert,
|
||||
/// Function keys.
|
||||
///
|
||||
/// Only function keys 1 through 12 are supported.
|
||||
F(u8),
|
||||
/// Normal character.
|
||||
Char(char),
|
||||
/// Alt modified character.
|
||||
Alt(char),
|
||||
/// Ctrl modified character.
|
||||
///
|
||||
/// Note that certain keys may not be modifiable with `ctrl`, due to limitations of terminals.
|
||||
Ctrl(char),
|
||||
/// Null byte.
|
||||
Null,
|
||||
/// Esc key.
|
||||
Esc,
|
||||
|
||||
#[doc(hidden)]
|
||||
__IsNotComplete,
|
||||
}
|
||||
|
||||
/// Parse an Event from `item` and possibly subsequent bytes through `iter`.
|
||||
pub fn parse_event<I>(item: u8, iter: &mut I) -> Result<Event, Error>
|
||||
where I: Iterator<Item = Result<u8, Error>>
|
||||
{
|
||||
let error = Error::new(ErrorKind::Other, "Could not parse an event");
|
||||
match item {
|
||||
b'\x1B' => {
|
||||
// This is an escape character, leading a control sequence.
|
||||
Ok(match iter.next() {
|
||||
Some(Ok(b'O')) => {
|
||||
match iter.next() {
|
||||
// F1-F4
|
||||
Some(Ok(val @ b'P'...b'S')) => Event::Key(Key::F(1 + val - b'P')),
|
||||
_ => return Err(error),
|
||||
}
|
||||
}
|
||||
Some(Ok(b'[')) => {
|
||||
// This is a CSI sequence.
|
||||
parse_csi(iter).ok_or(error)?
|
||||
}
|
||||
Some(Ok(c)) => {
|
||||
let ch = parse_utf8_char(c, iter);
|
||||
Event::Key(Key::Alt(try!(ch)))
|
||||
}
|
||||
Some(Err(_)) | None => return Err(error),
|
||||
})
|
||||
}
|
||||
b'\n' | b'\r' => Ok(Event::Key(Key::Char('\n'))),
|
||||
b'\t' => Ok(Event::Key(Key::Char('\t'))),
|
||||
b'\x7F' => Ok(Event::Key(Key::Backspace)),
|
||||
c @ b'\x01'...b'\x1A' => Ok(Event::Key(Key::Ctrl((c as u8 - 0x1 + b'a') as char))),
|
||||
c @ b'\x1C'...b'\x1F' => Ok(Event::Key(Key::Ctrl((c as u8 - 0x1C + b'4') as char))),
|
||||
b'\0' => Ok(Event::Key(Key::Null)),
|
||||
c => {
|
||||
Ok({
|
||||
let ch = parse_utf8_char(c, iter);
|
||||
Event::Key(Key::Char(try!(ch)))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a CSI sequence, just after reading ^[
|
||||
///
|
||||
/// Returns None if an unrecognized sequence is found.
|
||||
fn parse_csi<I>(iter: &mut I) -> Option<Event>
|
||||
where I: Iterator<Item = Result<u8, Error>>
|
||||
{
|
||||
Some(match iter.next() {
|
||||
Some(Ok(b'[')) => match iter.next() {
|
||||
Some(Ok(val @ b'A'...b'E')) => Event::Key(Key::F(1 + val - b'A')),
|
||||
_ => return None,
|
||||
},
|
||||
Some(Ok(b'D')) => Event::Key(Key::Left),
|
||||
Some(Ok(b'C')) => Event::Key(Key::Right),
|
||||
Some(Ok(b'A')) => Event::Key(Key::Up),
|
||||
Some(Ok(b'B')) => Event::Key(Key::Down),
|
||||
Some(Ok(b'H')) => Event::Key(Key::Home),
|
||||
Some(Ok(b'F')) => Event::Key(Key::End),
|
||||
Some(Ok(b'M')) => {
|
||||
// X10 emulation mouse encoding: ESC [ CB Cx Cy (6 characters only).
|
||||
let mut next = || iter.next().unwrap().unwrap();
|
||||
|
||||
let cb = next() as i8 - 32;
|
||||
// (1, 1) are the coords for upper left.
|
||||
let cx = next().saturating_sub(32) as u16;
|
||||
let cy = next().saturating_sub(32) as u16;
|
||||
Event::Mouse(match cb & 0b11 {
|
||||
0 => {
|
||||
if cb & 0x40 != 0 {
|
||||
MouseEvent::Press(MouseButton::WheelUp, cx, cy)
|
||||
} else {
|
||||
MouseEvent::Press(MouseButton::Left, cx, cy)
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
if cb & 0x40 != 0 {
|
||||
MouseEvent::Press(MouseButton::WheelDown, cx, cy)
|
||||
} else {
|
||||
MouseEvent::Press(MouseButton::Middle, cx, cy)
|
||||
}
|
||||
}
|
||||
2 => MouseEvent::Press(MouseButton::Right, cx, cy),
|
||||
3 => MouseEvent::Release(cx, cy),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
Some(Ok(b'<')) => {
|
||||
// xterm mouse encoding:
|
||||
// ESC [ < Cb ; Cx ; Cy (;) (M or m)
|
||||
let mut buf = Vec::new();
|
||||
let mut c = iter.next().unwrap().unwrap();
|
||||
while match c {
|
||||
b'm' | b'M' => false,
|
||||
_ => true,
|
||||
} {
|
||||
buf.push(c);
|
||||
c = iter.next().unwrap().unwrap();
|
||||
}
|
||||
let str_buf = String::from_utf8(buf).unwrap();
|
||||
let nums = &mut str_buf.split(';');
|
||||
|
||||
let cb = nums.next()
|
||||
.unwrap()
|
||||
.parse::<u16>()
|
||||
.unwrap();
|
||||
let cx = nums.next()
|
||||
.unwrap()
|
||||
.parse::<u16>()
|
||||
.unwrap();
|
||||
let cy = nums.next()
|
||||
.unwrap()
|
||||
.parse::<u16>()
|
||||
.unwrap();
|
||||
|
||||
let event = match cb {
|
||||
0...2 | 64...65 => {
|
||||
let button = match cb {
|
||||
0 => MouseButton::Left,
|
||||
1 => MouseButton::Middle,
|
||||
2 => MouseButton::Right,
|
||||
64 => MouseButton::WheelUp,
|
||||
65 => MouseButton::WheelDown,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
match c {
|
||||
b'M' => MouseEvent::Press(button, cx, cy),
|
||||
b'm' => MouseEvent::Release(cx, cy),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
32 => MouseEvent::Hold(cx, cy),
|
||||
3 => MouseEvent::Release(cx, cy),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Event::Mouse(event)
|
||||
}
|
||||
Some(Ok(c @ b'0'...b'9')) => {
|
||||
// Numbered escape code.
|
||||
let mut buf = Vec::new();
|
||||
buf.push(c);
|
||||
let mut c = iter.next().unwrap().unwrap();
|
||||
// The final byte of a CSI sequence can be in the range 64-126, so
|
||||
// let's keep reading anything else.
|
||||
while c < 64 || c > 126 {
|
||||
buf.push(c);
|
||||
c = iter.next().unwrap().unwrap();
|
||||
}
|
||||
|
||||
match c {
|
||||
// rxvt mouse encoding:
|
||||
// ESC [ Cb ; Cx ; Cy ; M
|
||||
b'M' => {
|
||||
let str_buf = String::from_utf8(buf).unwrap();
|
||||
|
||||
let nums: Vec<u16> = str_buf.split(';').map(|n| n.parse().unwrap()).collect();
|
||||
|
||||
let cb = nums[0];
|
||||
let cx = nums[1];
|
||||
let cy = nums[2];
|
||||
|
||||
let event = match cb {
|
||||
32 => MouseEvent::Press(MouseButton::Left, cx, cy),
|
||||
33 => MouseEvent::Press(MouseButton::Middle, cx, cy),
|
||||
34 => MouseEvent::Press(MouseButton::Right, cx, cy),
|
||||
35 => MouseEvent::Release(cx, cy),
|
||||
64 => MouseEvent::Hold(cx, cy),
|
||||
96 | 97 => MouseEvent::Press(MouseButton::WheelUp, cx, cy),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Event::Mouse(event)
|
||||
}
|
||||
// Special key code.
|
||||
b'~' => {
|
||||
let str_buf = String::from_utf8(buf).unwrap();
|
||||
|
||||
// This CSI sequence can be a list of semicolon-separated
|
||||
// numbers.
|
||||
let nums: Vec<u8> = str_buf.split(';').map(|n| n.parse().unwrap()).collect();
|
||||
|
||||
if nums.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// TODO: handle multiple values for key modififiers (ex: values
|
||||
// [3, 2] means Shift+Delete)
|
||||
if nums.len() > 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
match nums[0] {
|
||||
1 | 7 => Event::Key(Key::Home),
|
||||
2 => Event::Key(Key::Insert),
|
||||
3 => Event::Key(Key::Delete),
|
||||
4 | 8 => Event::Key(Key::End),
|
||||
5 => Event::Key(Key::PageUp),
|
||||
6 => Event::Key(Key::PageDown),
|
||||
v @ 11...15 => Event::Key(Key::F(v - 10)),
|
||||
v @ 17...21 => Event::Key(Key::F(v - 11)),
|
||||
v @ 23...24 => Event::Key(Key::F(v - 12)),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/// Parse `c` as either a single byte ASCII char or a variable size UTF-8 char.
|
||||
fn parse_utf8_char<I>(c: u8, iter: &mut I) -> Result<char, Error>
|
||||
where I: Iterator<Item = Result<u8, Error>>
|
||||
{
|
||||
let error = Err(Error::new(ErrorKind::Other, "Input character is not valid UTF-8"));
|
||||
if c.is_ascii() {
|
||||
Ok(c as char)
|
||||
} else {
|
||||
let bytes = &mut Vec::new();
|
||||
bytes.push(c);
|
||||
|
||||
loop {
|
||||
match iter.next() {
|
||||
Some(Ok(next)) => {
|
||||
bytes.push(next);
|
||||
if let Ok(st) = str::from_utf8(bytes) {
|
||||
return Ok(st.chars().next().unwrap());
|
||||
}
|
||||
if bytes.len() >= 4 {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
_ => return error,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_parse_utf8() {
|
||||
let st = "abcéŷ¤£€ù%323";
|
||||
let ref mut bytes = st.bytes().map(|x| Ok(x));
|
||||
let chars = st.chars();
|
||||
for c in chars {
|
||||
let b = bytes.next().unwrap().unwrap();
|
||||
assert!(c == parse_utf8_char(b, bytes).unwrap());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,388 @@
|
|||
//! User input.
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::ops;
|
||||
|
||||
use event::{self, Event, Key};
|
||||
use raw::IntoRawMode;
|
||||
|
||||
/// An iterator over input keys.
|
||||
pub struct Keys<R> {
|
||||
iter: Events<R>,
|
||||
}
|
||||
|
||||
impl<R: Read> Iterator for Keys<R> {
|
||||
type Item = Result<Key, io::Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Result<Key, io::Error>> {
|
||||
loop {
|
||||
match self.iter.next() {
|
||||
Some(Ok(Event::Key(k))) => return Some(Ok(k)),
|
||||
Some(Ok(_)) => continue,
|
||||
e @ Some(Err(_)) => e,
|
||||
None => return None,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over input events.
|
||||
pub struct Events<R> {
|
||||
inner: EventsAndRaw<R>
|
||||
}
|
||||
|
||||
impl<R: Read> Iterator for Events<R> {
|
||||
type Item = Result<Event, io::Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Result<Event, io::Error>> {
|
||||
self.inner.next().map(|tuple| tuple.map(|(event, _raw)| event))
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over input events and the bytes that define them.
|
||||
pub struct EventsAndRaw<R> {
|
||||
source: R,
|
||||
leftover: Option<u8>,
|
||||
}
|
||||
|
||||
impl<R: Read> Iterator for EventsAndRaw<R> {
|
||||
type Item = Result<(Event, Vec<u8>), io::Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Result<(Event, Vec<u8>), io::Error>> {
|
||||
let mut source = &mut self.source;
|
||||
|
||||
if let Some(c) = self.leftover {
|
||||
// we have a leftover byte, use it
|
||||
self.leftover = None;
|
||||
return Some(parse_event(c, &mut source.bytes()));
|
||||
}
|
||||
|
||||
// Here we read two bytes at a time. We need to distinguish between single ESC key presses,
|
||||
// and escape sequences (which start with ESC or a x1B byte). The idea is that if this is
|
||||
// an escape sequence, we will read multiple bytes (the first byte being ESC) but if this
|
||||
// is a single ESC keypress, we will only read a single byte.
|
||||
let mut buf = [0u8; 2];
|
||||
let res = match source.read(&mut buf) {
|
||||
Ok(0) => return None,
|
||||
Ok(1) => {
|
||||
match buf[0] {
|
||||
b'\x1B' => Ok((Event::Key(Key::Esc), vec![b'\x1B'])),
|
||||
c => parse_event(c, &mut source.bytes()),
|
||||
}
|
||||
}
|
||||
Ok(2) => {
|
||||
let mut option_iter = &mut Some(buf[1]).into_iter();
|
||||
let result = {
|
||||
let mut iter = option_iter.map(|c| Ok(c)).chain(source.bytes());
|
||||
parse_event(buf[0], &mut iter)
|
||||
};
|
||||
// If the option_iter wasn't consumed, keep the byte for later.
|
||||
self.leftover = option_iter.next();
|
||||
result
|
||||
}
|
||||
Ok(_) => unreachable!(),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_event<I>(item: u8, iter: &mut I) -> Result<(Event, Vec<u8>), io::Error>
|
||||
where I: Iterator<Item = Result<u8, io::Error>>
|
||||
{
|
||||
let mut buf = vec![item];
|
||||
let result = {
|
||||
let mut iter = iter.inspect(|byte| if let &Ok(byte) = byte {
|
||||
buf.push(byte);
|
||||
});
|
||||
event::parse_event(item, &mut iter)
|
||||
};
|
||||
result.or(Ok(Event::Unsupported(buf.clone()))).map(|e| (e, buf))
|
||||
}
|
||||
|
||||
|
||||
/// Extension to `Read` trait.
|
||||
pub trait TermRead {
|
||||
/// An iterator over input events.
|
||||
fn events(self) -> Events<Self> where Self: Sized;
|
||||
|
||||
/// An iterator over key inputs.
|
||||
fn keys(self) -> Keys<Self> where Self: Sized;
|
||||
|
||||
/// Read a line.
|
||||
///
|
||||
/// EOT and ETX will abort the prompt, returning `None`. Newline or carriage return will
|
||||
/// complete the input.
|
||||
fn read_line(&mut self) -> io::Result<Option<String>>;
|
||||
|
||||
/// Read a password.
|
||||
///
|
||||
/// EOT and ETX will abort the prompt, returning `None`. Newline or carriage return will
|
||||
/// complete the input.
|
||||
fn read_passwd<W: Write>(&mut self, writer: &mut W) -> io::Result<Option<String>> {
|
||||
let _raw = try!(writer.into_raw_mode());
|
||||
self.read_line()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<R: Read + TermReadEventsAndRaw> TermRead for R {
|
||||
fn events(self) -> Events<Self> {
|
||||
Events {
|
||||
inner: self.events_and_raw()
|
||||
}
|
||||
}
|
||||
fn keys(self) -> Keys<Self> {
|
||||
Keys { iter: self.events() }
|
||||
}
|
||||
|
||||
fn read_line(&mut self) -> io::Result<Option<String>> {
|
||||
let mut buf = Vec::with_capacity(30);
|
||||
|
||||
for c in self.bytes() {
|
||||
match c {
|
||||
Err(e) => return Err(e),
|
||||
Ok(0) | Ok(3) | Ok(4) => return Ok(None),
|
||||
Ok(0x7f) => {
|
||||
buf.pop();
|
||||
}
|
||||
Ok(b'\n') | Ok(b'\r') => break,
|
||||
Ok(c) => buf.push(c),
|
||||
}
|
||||
}
|
||||
|
||||
let string = try!(String::from_utf8(buf)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)));
|
||||
Ok(Some(string))
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension to `TermRead` trait. A separate trait in order to maintain backwards compatibility.
|
||||
pub trait TermReadEventsAndRaw {
|
||||
/// An iterator over input events and the bytes that define them.
|
||||
fn events_and_raw(self) -> EventsAndRaw<Self> where Self: Sized;
|
||||
}
|
||||
|
||||
impl<R: Read> TermReadEventsAndRaw for R {
|
||||
fn events_and_raw(self) -> EventsAndRaw<Self> {
|
||||
EventsAndRaw {
|
||||
source: self,
|
||||
leftover: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A sequence of escape codes to enable terminal mouse support.
|
||||
const ENTER_MOUSE_SEQUENCE: &'static str = csi!("?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h");
|
||||
|
||||
/// A sequence of escape codes to disable terminal mouse support.
|
||||
const EXIT_MOUSE_SEQUENCE: &'static str = csi!("?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l");
|
||||
|
||||
/// A terminal with added mouse support.
|
||||
///
|
||||
/// This can be obtained through the `From` implementations.
|
||||
pub struct MouseTerminal<W: Write> {
|
||||
term: W,
|
||||
}
|
||||
|
||||
impl<W: Write> From<W> for MouseTerminal<W> {
|
||||
fn from(mut from: W) -> MouseTerminal<W> {
|
||||
from.write_all(ENTER_MOUSE_SEQUENCE.as_bytes()).unwrap();
|
||||
|
||||
MouseTerminal { term: from }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Drop for MouseTerminal<W> {
|
||||
fn drop(&mut self) {
|
||||
self.term.write_all(EXIT_MOUSE_SEQUENCE.as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> ops::Deref for MouseTerminal<W> {
|
||||
type Target = W;
|
||||
|
||||
fn deref(&self) -> &W {
|
||||
&self.term
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> ops::DerefMut for MouseTerminal<W> {
|
||||
fn deref_mut(&mut self) -> &mut W {
|
||||
&mut self.term
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for MouseTerminal<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.term.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.term.flush()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::io;
|
||||
use event::{Key, Event, MouseEvent, MouseButton};
|
||||
|
||||
#[test]
|
||||
fn test_keys() {
|
||||
let mut i = b"\x1Bayo\x7F\x1B[D".keys();
|
||||
|
||||
assert_eq!(i.next().unwrap().unwrap(), Key::Alt('a'));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Key::Char('y'));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Key::Char('o'));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Key::Backspace);
|
||||
assert_eq!(i.next().unwrap().unwrap(), Key::Left);
|
||||
assert!(i.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_events() {
|
||||
let mut i =
|
||||
b"\x1B[\x00bc\x7F\x1B[D\
|
||||
\x1B[M\x00\x22\x24\x1B[<0;2;4;M\x1B[32;2;4M\x1B[<0;2;4;m\x1B[35;2;4Mb"
|
||||
.events();
|
||||
|
||||
assert_eq!(i.next().unwrap().unwrap(),
|
||||
Event::Unsupported(vec![0x1B, b'[', 0x00]));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('b')));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('c')));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Backspace));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Left));
|
||||
assert_eq!(i.next().unwrap().unwrap(),
|
||||
Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, 2, 4)));
|
||||
assert_eq!(i.next().unwrap().unwrap(),
|
||||
Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)));
|
||||
assert_eq!(i.next().unwrap().unwrap(),
|
||||
Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)));
|
||||
assert_eq!(i.next().unwrap().unwrap(),
|
||||
Event::Mouse(MouseEvent::Release(2, 4)));
|
||||
assert_eq!(i.next().unwrap().unwrap(),
|
||||
Event::Mouse(MouseEvent::Release(2, 4)));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('b')));
|
||||
assert!(i.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_events_and_raw() {
|
||||
let input = b"\x1B[\x00bc\x7F\x1B[D\
|
||||
\x1B[M\x00\x22\x24\x1B[<0;2;4;M\x1B[32;2;4M\x1B[<0;2;4;m\x1B[35;2;4Mb";
|
||||
let mut output = Vec::<u8>::new();
|
||||
{
|
||||
let mut i = input.events_and_raw().map(|res| res.unwrap())
|
||||
.inspect(|&(_, ref raw)| { output.extend(raw); }).map(|(event, _)| event);
|
||||
|
||||
assert_eq!(i.next().unwrap(),
|
||||
Event::Unsupported(vec![0x1B, b'[', 0x00]));
|
||||
assert_eq!(i.next().unwrap(), Event::Key(Key::Char('b')));
|
||||
assert_eq!(i.next().unwrap(), Event::Key(Key::Char('c')));
|
||||
assert_eq!(i.next().unwrap(), Event::Key(Key::Backspace));
|
||||
assert_eq!(i.next().unwrap(), Event::Key(Key::Left));
|
||||
assert_eq!(i.next().unwrap(),
|
||||
Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, 2, 4)));
|
||||
assert_eq!(i.next().unwrap(),
|
||||
Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)));
|
||||
assert_eq!(i.next().unwrap(),
|
||||
Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)));
|
||||
assert_eq!(i.next().unwrap(),
|
||||
Event::Mouse(MouseEvent::Release(2, 4)));
|
||||
assert_eq!(i.next().unwrap(),
|
||||
Event::Mouse(MouseEvent::Release(2, 4)));
|
||||
assert_eq!(i.next().unwrap(), Event::Key(Key::Char('b')));
|
||||
assert!(i.next().is_none());
|
||||
}
|
||||
|
||||
assert_eq!(input.iter().map(|b| *b).collect::<Vec<u8>>(), output)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_keys() {
|
||||
let mut st = b"\x1BOP\x1BOQ\x1BOR\x1BOS".keys();
|
||||
for i in 1..5 {
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::F(i));
|
||||
}
|
||||
|
||||
let mut st = b"\x1B[11~\x1B[12~\x1B[13~\x1B[14~\x1B[15~\
|
||||
\x1B[17~\x1B[18~\x1B[19~\x1B[20~\x1B[21~\x1B[23~\x1B[24~"
|
||||
.keys();
|
||||
for i in 1..13 {
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::F(i));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_special_keys() {
|
||||
let mut st = b"\x1B[2~\x1B[H\x1B[7~\x1B[5~\x1B[3~\x1B[F\x1B[8~\x1B[6~".keys();
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::Insert);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::Home);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::Home);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::PageUp);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::Delete);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::End);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::End);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::PageDown);
|
||||
assert!(st.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_esc_key() {
|
||||
let mut st = b"\x1B".keys();
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::Esc);
|
||||
assert!(st.next().is_none());
|
||||
}
|
||||
|
||||
fn line_match(a: &str, b: Option<&str>) {
|
||||
let mut sink = io::sink();
|
||||
|
||||
let line = a.as_bytes().read_line().unwrap();
|
||||
let pass = a.as_bytes().read_passwd(&mut sink).unwrap();
|
||||
|
||||
// godammit rustc
|
||||
|
||||
assert_eq!(line, pass);
|
||||
|
||||
if let Some(l) = line {
|
||||
assert_eq!(Some(l.as_str()), b);
|
||||
} else {
|
||||
assert!(b.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read() {
|
||||
let test1 = "this is the first test";
|
||||
let test2 = "this is the second test";
|
||||
|
||||
line_match(test1, Some(test1));
|
||||
line_match(test2, Some(test2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_backspace() {
|
||||
line_match("this is the\x7f first\x7f\x7f test",
|
||||
Some("this is th fir test"));
|
||||
line_match("this is the seco\x7fnd test\x7f",
|
||||
Some("this is the secnd tes"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_end() {
|
||||
line_match("abc\nhttps://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
Some("abc"));
|
||||
line_match("hello\rhttps://www.youtube.com/watch?v=yPYZpwSpKmA",
|
||||
Some("hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_abort() {
|
||||
line_match("abc\x03https://www.youtube.com/watch?v=dQw4w9WgXcQ", None);
|
||||
line_match("hello\x04https://www.youtube.com/watch?v=yPYZpwSpKmA", None);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
//! Termion is a pure Rust, bindless library for low-level handling, manipulating
|
||||
//! and reading information about terminals. This provides a full-featured
|
||||
//! alternative to Termbox.
|
||||
//!
|
||||
//! Termion aims to be simple and yet expressive. It is bindless, meaning that it
|
||||
//! is not a front-end to some other library (e.g., ncurses or termbox), but a
|
||||
//! standalone library directly talking to the TTY.
|
||||
//!
|
||||
//! Supports Redox, Mac OS X, and Linux (or, in general, ANSI terminals).
|
||||
//!
|
||||
//! For more information refer to the [README](https://github.com/ticki/termion).
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
#[path="sys/redox/mod.rs"]
|
||||
mod sys;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[path="sys/unix/mod.rs"]
|
||||
mod sys;
|
||||
|
||||
pub use sys::size::terminal_size;
|
||||
pub use sys::tty::{is_tty, get_tty};
|
||||
|
||||
mod async;
|
||||
pub use async::{AsyncReader, async_stdin};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
pub mod clear;
|
||||
pub mod color;
|
||||
pub mod cursor;
|
||||
pub mod event;
|
||||
pub mod input;
|
||||
pub mod raw;
|
||||
pub mod screen;
|
||||
pub mod scroll;
|
||||
pub mod style;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::sys;
|
||||
|
||||
#[test]
|
||||
fn test_get_terminal_attr() {
|
||||
sys::attr::get_terminal_attr().unwrap();
|
||||
sys::attr::get_terminal_attr().unwrap();
|
||||
sys::attr::get_terminal_attr().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_terminal_attr() {
|
||||
let ios = sys::attr::get_terminal_attr().unwrap();
|
||||
sys::attr::set_terminal_attr(&ios).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
sys::size::terminal_size().unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/// Create a CSI-introduced sequence.
|
||||
macro_rules! csi {
|
||||
($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) };
|
||||
}
|
||||
|
||||
/// Derive a CSI sequence struct.
|
||||
macro_rules! derive_csi_sequence {
|
||||
($doc:expr, $name:ident, $value:expr) => {
|
||||
#[doc = $doc]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct $name;
|
||||
|
||||
impl fmt::Display for $name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!($value))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
//! Managing raw mode.
|
||||
//!
|
||||
//! Raw mode is a particular state a TTY can have. It signifies that:
|
||||
//!
|
||||
//! 1. No line buffering (the input is given byte-by-byte).
|
||||
//! 2. The input is not written out, instead it has to be done manually by the programmer.
|
||||
//! 3. The output is not canonicalized (for example, `\n` means "go one line down", not "line
|
||||
//! break").
|
||||
//!
|
||||
//! It is essential to design terminal programs.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use termion::raw::IntoRawMode;
|
||||
//! use std::io::{Write, stdout};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let mut stdout = stdout().into_raw_mode().unwrap();
|
||||
//!
|
||||
//! write!(stdout, "Hey there.").unwrap();
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::ops;
|
||||
|
||||
use sys::Termios;
|
||||
use sys::attr::{get_terminal_attr, raw_terminal_attr, set_terminal_attr};
|
||||
|
||||
/// The timeout of an escape code control sequence, in milliseconds.
|
||||
pub const CONTROL_SEQUENCE_TIMEOUT: u64 = 100;
|
||||
|
||||
/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
|
||||
/// dropped.
|
||||
///
|
||||
/// Restoring will entirely bring back the old TTY state.
|
||||
pub struct RawTerminal<W: Write> {
|
||||
prev_ios: Termios,
|
||||
output: W,
|
||||
}
|
||||
|
||||
impl<W: Write> Drop for RawTerminal<W> {
|
||||
fn drop(&mut self) {
|
||||
set_terminal_attr(&self.prev_ios).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> ops::Deref for RawTerminal<W> {
|
||||
type Target = W;
|
||||
|
||||
fn deref(&self) -> &W {
|
||||
&self.output
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> ops::DerefMut for RawTerminal<W> {
|
||||
fn deref_mut(&mut self) -> &mut W {
|
||||
&mut self.output
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for RawTerminal<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.output.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.output.flush()
|
||||
}
|
||||
}
|
||||
|
||||
/// Types which can be converted into "raw mode".
|
||||
///
|
||||
/// # Why is this type defined on writers and not readers?
|
||||
///
|
||||
/// TTYs has their state controlled by the writer, not the reader. You use the writer to clear the
|
||||
/// screen, move the cursor and so on, so naturally you use the writer to change the mode as well.
|
||||
pub trait IntoRawMode: Write + Sized {
|
||||
/// Switch to raw mode.
|
||||
///
|
||||
/// Raw mode means that stdin won't be printed (it will instead have to be written manually by
|
||||
/// the program). Furthermore, the input isn't canonicalised or buffered (that is, you can
|
||||
/// read from stdin one byte of a time). The output is neither modified in any way.
|
||||
fn into_raw_mode(self) -> io::Result<RawTerminal<Self>>;
|
||||
}
|
||||
|
||||
impl<W: Write> IntoRawMode for W {
|
||||
fn into_raw_mode(self) -> io::Result<RawTerminal<W>> {
|
||||
let mut ios = get_terminal_attr()?;
|
||||
let prev_ios = ios;
|
||||
|
||||
raw_terminal_attr(&mut ios);
|
||||
|
||||
set_terminal_attr(&ios)?;
|
||||
|
||||
Ok(RawTerminal {
|
||||
prev_ios: prev_ios,
|
||||
output: self,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::io::{Write, stdout};
|
||||
|
||||
#[test]
|
||||
fn test_into_raw_mode() {
|
||||
let mut out = stdout().into_raw_mode().unwrap();
|
||||
|
||||
out.write_all(b"this is a test, muahhahahah\r\n").unwrap();
|
||||
|
||||
drop(out);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
//! Managing switching between main and alternate screen buffers.
|
||||
//!
|
||||
//! Note that this implementation uses xterm's new escape sequences for screen switching and thus
|
||||
//! only works for xterm compatible terminals (which should be most terminals nowadays).
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! use termion::screen::AlternateScreen;
|
||||
//! use std::io::{Write, stdout};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! {
|
||||
//! let mut screen = AlternateScreen::from(stdout());
|
||||
//! write!(screen, "Writing to alternate screen!").unwrap();
|
||||
//! screen.flush().unwrap();
|
||||
//! }
|
||||
//! println!("Writing to main screen.");
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::ops;
|
||||
use std::fmt;
|
||||
|
||||
/// Switch to the main screen buffer of the terminal.
|
||||
pub struct ToMainScreen;
|
||||
|
||||
impl fmt::Display for ToMainScreen {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("?1049l"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Switch to the alternate screen buffer of the terminal.
|
||||
pub struct ToAlternateScreen;
|
||||
|
||||
impl fmt::Display for ToAlternateScreen {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("?1049h"))
|
||||
}
|
||||
}
|
||||
|
||||
/// A terminal restorer, which wraps a type implementing Write, and causes all writes to be written
|
||||
/// to an alternate screen.
|
||||
///
|
||||
/// This is achieved by switching the terminal to the alternate screen on creation and
|
||||
/// automatically switching it back to the original screen on drop.
|
||||
pub struct AlternateScreen<W: Write> {
|
||||
/// The output target.
|
||||
output: W,
|
||||
}
|
||||
|
||||
impl<W: Write> AlternateScreen<W> {
|
||||
/// Create an alternate screen wrapper struct for the provided output and switch the terminal
|
||||
/// to the alternate screen.
|
||||
pub fn from(mut output: W) -> Self {
|
||||
write!(output, "{}", ToAlternateScreen).expect("switch to alternate screen");
|
||||
AlternateScreen { output: output }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Drop for AlternateScreen<W> {
|
||||
fn drop(&mut self) {
|
||||
write!(self, "{}", ToMainScreen).expect("switch to main screen");
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> ops::Deref for AlternateScreen<W> {
|
||||
type Target = W;
|
||||
|
||||
fn deref(&self) -> &W {
|
||||
&self.output
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> ops::DerefMut for AlternateScreen<W> {
|
||||
fn deref_mut(&mut self) -> &mut W {
|
||||
&mut self.output
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for AlternateScreen<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.output.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.output.flush()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
//! Scrolling.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// Scroll up.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Up(pub u16);
|
||||
|
||||
impl fmt::Display for Up {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("{}S"), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Scroll down.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Down(pub u16);
|
||||
|
||||
impl fmt::Display for Down {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("{}T"), self.0)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
//! Text styling management.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
derive_csi_sequence!("Reset SGR parameters.", Reset, "m");
|
||||
derive_csi_sequence!("Bold text.", Bold, "1m");
|
||||
derive_csi_sequence!("Fainted text (not widely supported).", Faint, "2m");
|
||||
derive_csi_sequence!("Italic text.", Italic, "3m");
|
||||
derive_csi_sequence!("Underlined text.", Underline, "4m");
|
||||
derive_csi_sequence!("Blinking text (not widely supported).", Blink, "5m");
|
||||
derive_csi_sequence!("Inverted colors (negative mode).", Invert, "7m");
|
||||
derive_csi_sequence!("Crossed out text (not widely supported).", CrossedOut, "9m");
|
||||
derive_csi_sequence!("Undo bold text.", NoBold, "21m");
|
||||
derive_csi_sequence!("Undo fainted text (not widely supported).", NoFaint, "22m");
|
||||
derive_csi_sequence!("Undo italic text.", NoItalic, "23m");
|
||||
derive_csi_sequence!("Undo underlined text.", NoUnderline, "24m");
|
||||
derive_csi_sequence!("Undo blinking text (not widely supported).", NoBlink, "25m");
|
||||
derive_csi_sequence!("Undo inverted colors (negative mode).", NoInvert, "27m");
|
||||
derive_csi_sequence!("Undo crossed out text (not widely supported).",
|
||||
NoCrossedOut,
|
||||
"29m");
|
||||
derive_csi_sequence!("Framed text (not widely supported).", Framed, "51m");
|
|
@ -0,0 +1,33 @@
|
|||
use std::io;
|
||||
|
||||
use super::{cvt, syscall, Termios};
|
||||
|
||||
pub fn get_terminal_attr() -> io::Result<Termios> {
|
||||
let mut termios = Termios::default();
|
||||
|
||||
let fd = cvt(syscall::dup(0, b"termios"))?;
|
||||
let res = cvt(syscall::read(fd, &mut termios));
|
||||
let _ = syscall::close(fd);
|
||||
|
||||
if res? == termios.len() {
|
||||
Ok(termios)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal attributes."))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_terminal_attr(termios: &Termios) -> io::Result<()> {
|
||||
let fd = cvt(syscall::dup(0, b"termios"))?;
|
||||
let res = cvt(syscall::write(fd, termios));
|
||||
let _ = syscall::close(fd);
|
||||
|
||||
if res? == termios.len() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Unable to set the terminal attributes."))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw_terminal_attr(ios: &mut Termios) {
|
||||
ios.make_raw()
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
extern crate redox_termios;
|
||||
extern crate syscall;
|
||||
|
||||
use std::io;
|
||||
|
||||
pub use self::redox_termios::Termios;
|
||||
|
||||
pub mod attr;
|
||||
pub mod size;
|
||||
pub mod tty;
|
||||
|
||||
// Support function for converting syscall error to io error
|
||||
fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> {
|
||||
result.map_err(|err| io::Error::from_raw_os_error(err.errno))
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
use std::io;
|
||||
|
||||
use super::{cvt, redox_termios, syscall};
|
||||
|
||||
/// Get the size of the terminal.
|
||||
pub fn terminal_size() -> io::Result<(u16, u16)> {
|
||||
let mut winsize = redox_termios::Winsize::default();
|
||||
|
||||
let fd = cvt(syscall::dup(1, b"winsize"))?;
|
||||
let res = cvt(syscall::read(fd, &mut winsize));
|
||||
let _ = syscall::close(fd);
|
||||
|
||||
if res? == winsize.len() {
|
||||
Ok((winsize.ws_col, winsize.ws_row))
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal size."))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
use std::{env, fs, io};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use super::syscall;
|
||||
|
||||
/// Is this stream a TTY?
|
||||
pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
|
||||
if let Ok(fd) = syscall::dup(stream.as_raw_fd(), b"termios") {
|
||||
let _ = syscall::close(fd);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the TTY device.
|
||||
///
|
||||
/// This allows for getting stdio representing _only_ the TTY, and not other streams.
|
||||
pub fn get_tty() -> io::Result<fs::File> {
|
||||
let tty = try!(env::var("TTY").map_err(|x| io::Error::new(io::ErrorKind::NotFound, x)));
|
||||
fs::OpenOptions::new().read(true).write(true).open(tty)
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
use std::{io, mem};
|
||||
|
||||
use super::{cvt, Termios};
|
||||
use super::libc::c_int;
|
||||
|
||||
pub fn get_terminal_attr() -> io::Result<Termios> {
|
||||
extern "C" {
|
||||
pub fn tcgetattr(fd: c_int, termptr: *mut Termios) -> c_int;
|
||||
}
|
||||
unsafe {
|
||||
let mut termios = mem::zeroed();
|
||||
cvt(tcgetattr(0, &mut termios))?;
|
||||
Ok(termios)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_terminal_attr(termios: &Termios) -> io::Result<()> {
|
||||
extern "C" {
|
||||
pub fn tcsetattr(fd: c_int, opt: c_int, termptr: *const Termios) -> c_int;
|
||||
}
|
||||
cvt(unsafe { tcsetattr(0, 0, termios) }).and(Ok(()))
|
||||
}
|
||||
|
||||
pub fn raw_terminal_attr(termios: &mut Termios) {
|
||||
extern "C" {
|
||||
pub fn cfmakeraw(termptr: *mut Termios);
|
||||
}
|
||||
unsafe { cfmakeraw(termios) }
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
extern crate libc;
|
||||
|
||||
use std::io;
|
||||
|
||||
pub use self::libc::termios as Termios;
|
||||
|
||||
pub mod attr;
|
||||
pub mod size;
|
||||
pub mod tty;
|
||||
|
||||
// Support functions for converting libc return values to io errors {
|
||||
trait IsMinusOne {
|
||||
fn is_minus_one(&self) -> bool;
|
||||
}
|
||||
|
||||
macro_rules! impl_is_minus_one {
|
||||
($($t:ident)*) => ($(impl IsMinusOne for $t {
|
||||
fn is_minus_one(&self) -> bool {
|
||||
*self == -1
|
||||
}
|
||||
})*)
|
||||
}
|
||||
|
||||
impl_is_minus_one! { i8 i16 i32 i64 isize }
|
||||
|
||||
fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
|
||||
if t.is_minus_one() {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(t)
|
||||
}
|
||||
}
|
||||
// } End of support functions
|
|
@ -0,0 +1,48 @@
|
|||
use std::{io, mem};
|
||||
|
||||
use super::cvt;
|
||||
use super::libc::{c_ushort, ioctl, STDOUT_FILENO};
|
||||
|
||||
#[repr(C)]
|
||||
struct TermSize {
|
||||
row: c_ushort,
|
||||
col: c_ushort,
|
||||
_x: c_ushort,
|
||||
_y: c_ushort,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const TIOCGWINSZ: usize = 0x00005413;
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub const TIOCGWINSZ: usize = 0x40087468;
|
||||
|
||||
// Since attributes on non-item statements is not stable yet, we use a function.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
#[cfg(not(target_env = "musl"))]
|
||||
fn tiocgwinsz() -> u64 {
|
||||
TIOCGWINSZ as u64
|
||||
}
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[cfg(not(target_env = "musl"))]
|
||||
fn tiocgwinsz() -> u32 {
|
||||
TIOCGWINSZ as u32
|
||||
}
|
||||
|
||||
#[cfg(any(target_env = "musl", target_os = "android"))]
|
||||
fn tiocgwinsz() -> i32 {
|
||||
TIOCGWINSZ as i32
|
||||
}
|
||||
|
||||
/// Get the size of the terminal.
|
||||
pub fn terminal_size() -> io::Result<(u16, u16)> {
|
||||
unsafe {
|
||||
let mut size: TermSize = mem::zeroed();
|
||||
cvt(ioctl(STDOUT_FILENO, tiocgwinsz(), &mut size as *mut _))?;
|
||||
Ok((size.col as u16, size.row as u16))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
use std::{fs, io};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use super::libc;
|
||||
|
||||
|
||||
/// Is this stream a TTY?
|
||||
pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
|
||||
unsafe { libc::isatty(stream.as_raw_fd()) == 1 }
|
||||
}
|
||||
|
||||
/// Get the TTY device.
|
||||
///
|
||||
/// This allows for getting stdio representing _only_ the TTY, and not other streams.
|
||||
pub fn get_tty() -> io::Result<fs::File> {
|
||||
fs::OpenOptions::new().read(true).write(true).open("/dev/tty")
|
||||
}
|
Загрузка…
Ссылка в новой задаче