mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-11-23 04:22:06 +00:00
Merge branch 'child-nodes'
This commit is contained in:
commit
eee49ece04
@ -1,3 +1,8 @@
|
|||||||
|
before_install:
|
||||||
|
- sudo add-apt-repository --yes ppa:kubuntu-ppa/backports
|
||||||
|
- sudo apt-get update -qq
|
||||||
|
- sudo apt-get install cmake
|
||||||
|
sudo: true
|
||||||
language: rust
|
language: rust
|
||||||
rust: nightly
|
rust: nightly
|
||||||
|
|
||||||
|
146
Cargo.lock
generated
146
Cargo.lock
generated
@ -5,18 +5,27 @@ dependencies = [
|
|||||||
"ansi_term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ansi_term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"datetime 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"datetime 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"getopts 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"getopts 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"git2 0.2.13 (git+https://github.com/alexcrichton/git2-rs.git)",
|
"git2 0.3.0 (git+https://github.com/alexcrichton/git2-rs.git)",
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"locale 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"locale 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"natord 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"natord 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num_cpus 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num_cpus 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"number_prefix 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"number_prefix 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pad 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pad 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"term_grid 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"term_grid 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicode-width 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-width 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"users 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"users 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "advapi32-sys"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -24,7 +33,7 @@ name = "aho-corasick"
|
|||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"memchr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -42,13 +51,21 @@ name = "byteorder"
|
|||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cmake"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"gcc 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "datetime"
|
name = "datetime"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"locale 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"locale 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pad 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pad 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex_macros 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex_macros 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -57,12 +74,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gcc"
|
name = "gcc"
|
||||||
version = "0.3.9"
|
version = "0.3.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getopts"
|
name = "getopts"
|
||||||
version = "0.2.11"
|
version = "0.2.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -70,28 +91,29 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "git2"
|
name = "git2"
|
||||||
version = "0.2.13"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/alexcrichton/git2-rs.git#889cf3dd62bcf8406d7c5381699467cdcb79d55e"
|
source = "git+https://github.com/alexcrichton/git2-rs.git#cbe8e1a65ac9b16bc05137f80673e74c4d36f6e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libgit2-sys 0.2.18 (git+https://github.com/alexcrichton/git2-rs.git)",
|
"libgit2-sys 0.3.2 (git+https://github.com/alexcrichton/git2-rs.git)",
|
||||||
"url 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.1.8"
|
version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libgit2-sys"
|
name = "libgit2-sys"
|
||||||
version = "0.2.18"
|
version = "0.3.2"
|
||||||
source = "git+https://github.com/alexcrichton/git2-rs.git#889cf3dd62bcf8406d7c5381699467cdcb79d55e"
|
source = "git+https://github.com/alexcrichton/git2-rs.git#cbe8e1a65ac9b16bc05137f80673e74c4d36f6e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cmake 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libssh2-sys 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libz-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libssh2-sys 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libz-sys 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"openssl-sys 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"openssl-sys 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -101,26 +123,28 @@ name = "libressl-pnacl-sys"
|
|||||||
version = "2.1.6"
|
version = "2.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pnacl-build-helper 1.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pnacl-build-helper 1.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libssh2-sys"
|
name = "libssh2-sys"
|
||||||
version = "0.1.26"
|
version = "0.1.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cmake 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libz-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libz-sys 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"openssl-sys 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"openssl-sys 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libz-sys"
|
name = "libz-sys"
|
||||||
version = "0.1.6"
|
version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcc 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -129,8 +153,8 @@ name = "locale"
|
|||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -138,7 +162,7 @@ name = "log"
|
|||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -148,24 +172,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "0.1.3"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "natord"
|
name = "natord"
|
||||||
version = "1.0.8"
|
version = "1.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
version = "0.1.25"
|
version = "0.1.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -173,7 +197,7 @@ name = "num_cpus"
|
|||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -181,7 +205,7 @@ name = "number_prefix"
|
|||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -189,8 +213,8 @@ name = "openssl-sys"
|
|||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gcc 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gcc 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libressl-pnacl-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libressl-pnacl-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -200,7 +224,7 @@ name = "pad"
|
|||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-width 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-width 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -210,7 +234,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pnacl-build-helper"
|
name = "pnacl-build-helper"
|
||||||
version = "1.4.5"
|
version = "1.4.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -218,10 +242,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.3.8"
|
version = "0.3.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -230,13 +256,13 @@ version = "0.1.41"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"aho-corasick 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"memchr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"memchr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex-syntax 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex-syntax 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -249,7 +275,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-serialize"
|
name = "rustc-serialize"
|
||||||
version = "0.3.15"
|
version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -257,7 +283,7 @@ name = "tempdir"
|
|||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -265,7 +291,7 @@ name = "term_grid"
|
|||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-width 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-width 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -283,23 +309,33 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "0.2.36"
|
version = "0.2.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "users"
|
name = "users"
|
||||||
version = "0.4.2"
|
version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-build"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
46
src/dir.rs
46
src/dir.rs
@ -1,6 +1,7 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::slice::Iter as SliceIter;
|
||||||
|
|
||||||
use feature::Git;
|
use feature::Git;
|
||||||
use file::{File, fields};
|
use file::{File, fields};
|
||||||
@ -22,31 +23,26 @@ impl Dir {
|
|||||||
|
|
||||||
/// Create a new Dir object filled with all the files in the directory
|
/// Create a new Dir object filled with all the files in the directory
|
||||||
/// pointed to by the given path. Fails if the directory can't be read, or
|
/// pointed to by the given path. Fails if the directory can't be read, or
|
||||||
/// isn't actually a directory.
|
/// isn't actually a directory, or if there's an IO error that occurs
|
||||||
|
/// while scanning.
|
||||||
pub fn readdir(path: &Path, git: bool) -> io::Result<Dir> {
|
pub fn readdir(path: &Path, git: bool) -> io::Result<Dir> {
|
||||||
fs::read_dir(path).map(|dir_obj| Dir {
|
let reader = try!(fs::read_dir(path));
|
||||||
contents: dir_obj.map(|entry| entry.unwrap().path()).collect(),
|
let contents = try!(reader.map(|e| e.map(|e| e.path())).collect());
|
||||||
|
|
||||||
|
Ok(Dir {
|
||||||
|
contents: contents,
|
||||||
path: path.to_path_buf(),
|
path: path.to_path_buf(),
|
||||||
git: if git { Git::scan(path).ok() } else { None },
|
git: if git { Git::scan(path).ok() } else { None },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produce a vector of File objects from an initialised directory,
|
/// Produce an iterator of IO results of trying to read all the files in
|
||||||
/// printing out an error if any of the Files fail to be created.
|
/// this directory.
|
||||||
///
|
pub fn files<'dir>(&'dir self) -> Files<'dir> {
|
||||||
/// Passing in `recurse` means that any directories will be scanned for
|
Files {
|
||||||
/// their contents, as well.
|
inner: self.contents.iter(),
|
||||||
pub fn files(&self, recurse: bool) -> Vec<File> {
|
dir: &self,
|
||||||
let mut files = vec![];
|
|
||||||
|
|
||||||
for path in self.contents.iter() {
|
|
||||||
match File::from_path(path, Some(self), recurse) {
|
|
||||||
Ok(file) => files.push(file),
|
|
||||||
Err(e) => println!("{}: {}", path.display(), e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
files
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this directory contains a file with the given path.
|
/// Whether this directory contains a file with the given path.
|
||||||
@ -73,3 +69,17 @@ impl Dir {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Files<'dir> {
|
||||||
|
inner: SliceIter<'dir, PathBuf>,
|
||||||
|
dir: &'dir Dir,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'dir> Iterator for Files<'dir> {
|
||||||
|
type Item = Result<File<'dir>, (PathBuf, io::Error)>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.inner.next().map(|path| File::from_path(path, Some(self.dir)).map_err(|t| (path.clone(), t)))
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,5 @@
|
|||||||
// Extended attribute support
|
// Extended attribute support
|
||||||
|
pub mod xattr;
|
||||||
#[cfg(target_os = "macos")] mod xattr_darwin;
|
|
||||||
#[cfg(target_os = "macos")] pub use self::xattr_darwin::Attribute;
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")] mod xattr_linux;
|
|
||||||
#[cfg(target_os = "linux")] pub use self::xattr_linux::Attribute;
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "linux")))] mod xattr_dummy;
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "linux")))] pub use self::xattr_dummy::Attribute;
|
|
||||||
|
|
||||||
// Git support
|
// Git support
|
||||||
|
|
||||||
|
238
src/feature/xattr.rs
Normal file
238
src/feature/xattr.rs
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
//! Extended attribute support for Darwin and Linux systems.
|
||||||
|
extern crate libc;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
||||||
|
pub const ENABLED: bool = cfg!(feature="git") && cfg!(any(target_os="macos", target_os="linux"));
|
||||||
|
|
||||||
|
pub trait FileAttributes {
|
||||||
|
fn attributes(&self) -> io::Result<Vec<Attribute>>;
|
||||||
|
fn symlink_attributes(&self) -> io::Result<Vec<Attribute>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileAttributes for Path {
|
||||||
|
fn attributes(&self) -> io::Result<Vec<Attribute>> {
|
||||||
|
list_attrs(lister::Lister::new(FollowSymlinks::Yes), &self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
|
||||||
|
list_attrs(lister::Lister::new(FollowSymlinks::No), &self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attributes which can be passed to `Attribute::list_with_flags`
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum FollowSymlinks {
|
||||||
|
Yes,
|
||||||
|
No
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extended attribute
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Attribute {
|
||||||
|
pub name: String,
|
||||||
|
pub size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_attrs(lister: lister::Lister, path: &Path) -> io::Result<Vec<Attribute>> {
|
||||||
|
let c_path = match path.as_os_str().to_cstring() {
|
||||||
|
Some(cstring) => cstring,
|
||||||
|
None => return Err(io::Error::new(io::ErrorKind::Other, "Error: path somehow contained a NUL?")),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut names = Vec::new();
|
||||||
|
let bufsize = lister.listxattr_first(&c_path);
|
||||||
|
|
||||||
|
if bufsize < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
else if bufsize > 0 {
|
||||||
|
let mut buf = vec![0u8; bufsize as usize];
|
||||||
|
let err = lister.listxattr_second(&c_path, &mut buf, bufsize);
|
||||||
|
|
||||||
|
if err < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
if err > 0 {
|
||||||
|
// End indicies of the attribute names
|
||||||
|
// the buffer contains 0-terminates c-strings
|
||||||
|
let idx = buf.iter().enumerate().filter_map(|(i, v)|
|
||||||
|
if *v == 0 { Some(i) } else { None }
|
||||||
|
);
|
||||||
|
let mut start = 0;
|
||||||
|
|
||||||
|
for end in idx {
|
||||||
|
let c_end = end + 1; // end of the c-string (including 0)
|
||||||
|
let size = lister.getxattr(&c_path, &buf[start..c_end]);
|
||||||
|
|
||||||
|
if size > 0 {
|
||||||
|
names.push(Attribute {
|
||||||
|
name: lister.translate_attribute_name(&buf[start..end]),
|
||||||
|
size: size as usize
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
start = c_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Ok(names)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
mod lister {
|
||||||
|
use std::ffi::CString;
|
||||||
|
use libc::{c_int, size_t, ssize_t, c_char, c_void, uint32_t};
|
||||||
|
use super::FollowSymlinks;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn listxattr(
|
||||||
|
path: *const c_char, namebuf: *mut c_char,
|
||||||
|
size: size_t, options: c_int
|
||||||
|
) -> ssize_t;
|
||||||
|
|
||||||
|
fn getxattr(
|
||||||
|
path: *const c_char, name: *const c_char,
|
||||||
|
value: *mut c_void, size: size_t, position: uint32_t,
|
||||||
|
options: c_int
|
||||||
|
) -> ssize_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Lister {
|
||||||
|
c_flags: c_int,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lister {
|
||||||
|
pub fn new(do_follow: FollowSymlinks) -> Lister {
|
||||||
|
let c_flags: c_int = match do_follow {
|
||||||
|
FollowSymlinks::Yes => 0x0001,
|
||||||
|
FollowSymlinks::No => 0x0000,
|
||||||
|
};
|
||||||
|
|
||||||
|
Lister { c_flags: c_flags }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate_attribute_name(&self, input: &[u8]) -> String {
|
||||||
|
use std::str::from_utf8_unchecked;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
from_utf8_unchecked(input).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
|
||||||
|
unsafe {
|
||||||
|
listxattr(c_path.as_ptr(), ptr::null_mut(), 0, self.c_flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
|
||||||
|
unsafe {
|
||||||
|
listxattr(
|
||||||
|
c_path.as_ptr(),
|
||||||
|
buf.as_mut_ptr() as *mut c_char,
|
||||||
|
bufsize as size_t, self.c_flags
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
|
||||||
|
unsafe {
|
||||||
|
getxattr(
|
||||||
|
c_path.as_ptr(),
|
||||||
|
buf.as_ptr() as *const c_char,
|
||||||
|
ptr::null_mut(), 0, 0, self.c_flags
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod lister {
|
||||||
|
use std::ffi::CString;
|
||||||
|
use libc::{size_t, ssize_t, c_char, c_void};
|
||||||
|
use super::FollowSymlinks;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn listxattr(
|
||||||
|
path: *const c_char, list: *mut c_char, size: size_t
|
||||||
|
) -> ssize_t;
|
||||||
|
|
||||||
|
fn llistxattr(
|
||||||
|
path: *const c_char, list: *mut c_char, size: size_t
|
||||||
|
) -> ssize_t;
|
||||||
|
|
||||||
|
fn getxattr(
|
||||||
|
path: *const c_char, name: *const c_char,
|
||||||
|
value: *mut c_void, size: size_t
|
||||||
|
) -> ssize_t;
|
||||||
|
|
||||||
|
fn lgetxattr(
|
||||||
|
path: *const c_char, name: *const c_char,
|
||||||
|
value: *mut c_void, size: size_t
|
||||||
|
) -> ssize_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Lister {
|
||||||
|
follow_symlinks: FollowSymlinks,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lister {
|
||||||
|
pub fn new(follow_symlinks: FollowSymlinks) -> Lister {
|
||||||
|
Lister { follow_symlinks: follow_symlinks }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate_attribute_name(&self, input: &[u8]) -> String {
|
||||||
|
String::from_utf8_lossy(input).into_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
|
||||||
|
let listxattr = match self.follow_symlinks {
|
||||||
|
FollowSymlinks::Yes => listxattr,
|
||||||
|
FollowSymlinks::No => llistxattr,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
listxattr(c_path.as_ptr(), ptr::null_mut(), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
|
||||||
|
let listxattr = match self.follow_symlinks {
|
||||||
|
FollowSymlinks::Yes => listxattr,
|
||||||
|
FollowSymlinks::No => llistxattr,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
listxattr(
|
||||||
|
c_path.as_ptr(),
|
||||||
|
buf.as_mut_ptr() as *mut c_char,
|
||||||
|
bufsize as size_t
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
|
||||||
|
let getxattr = match self.follow_symlinks {
|
||||||
|
FollowSymlinks::Yes => getxattr,
|
||||||
|
FollowSymlinks::No => lgetxattr,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
getxattr(
|
||||||
|
c_path.as_ptr(),
|
||||||
|
buf.as_ptr() as *const c_char,
|
||||||
|
ptr::null_mut(), 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,133 +0,0 @@
|
|||||||
//! Extended attribute support for darwin
|
|
||||||
extern crate libc;
|
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use std::mem;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
use self::libc::{c_int, size_t, ssize_t, c_char, c_void, uint32_t};
|
|
||||||
|
|
||||||
|
|
||||||
/// Don't follow symbolic links
|
|
||||||
const XATTR_NOFOLLOW: c_int = 0x0001;
|
|
||||||
|
|
||||||
/// Expose HFS Compression extended attributes
|
|
||||||
const XATTR_SHOWCOMPRESSION: c_int = 0x0020;
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
fn listxattr(path: *const c_char, namebuf: *mut c_char,
|
|
||||||
size: size_t, options: c_int) -> ssize_t;
|
|
||||||
fn getxattr(path: *const c_char, name: *const c_char,
|
|
||||||
value: *mut c_void, size: size_t, position: uint32_t,
|
|
||||||
options: c_int) -> ssize_t;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Attributes which can be passed to `Attribute::list_with_flags`
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum ListFlags {
|
|
||||||
/// Don't follow symbolic links
|
|
||||||
NoFollow = XATTR_NOFOLLOW as isize,
|
|
||||||
/// Expose HFS Compression extended attributes
|
|
||||||
ShowCompression = XATTR_SHOWCOMPRESSION as isize
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Extended attribute
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Attribute {
|
|
||||||
name: String,
|
|
||||||
size: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Attribute {
|
|
||||||
/// Lists the extended attribute of `path`.
|
|
||||||
/// Does follow symlinks by default.
|
|
||||||
pub fn list_attrs(path: &Path, flags: &[ListFlags]) -> io::Result<Vec<Attribute>> {
|
|
||||||
let mut c_flags: c_int = 0;
|
|
||||||
for &flag in flags.iter() {
|
|
||||||
c_flags |= flag as c_int
|
|
||||||
}
|
|
||||||
|
|
||||||
let c_path = match path.as_os_str().to_cstring() {
|
|
||||||
Some(cstring) => cstring,
|
|
||||||
None => return Err(io::Error::new(io::ErrorKind::Other, "could not read extended attributes")),
|
|
||||||
};
|
|
||||||
|
|
||||||
let bufsize = unsafe {
|
|
||||||
listxattr(c_path.as_ptr(), ptr::null_mut(), 0, c_flags)
|
|
||||||
};
|
|
||||||
|
|
||||||
if bufsize > 0 {
|
|
||||||
let mut buf = vec![0u8; bufsize as usize];
|
|
||||||
let err = unsafe { listxattr(
|
|
||||||
c_path.as_ptr(),
|
|
||||||
buf.as_mut_ptr() as *mut c_char,
|
|
||||||
bufsize as size_t, c_flags
|
|
||||||
)};
|
|
||||||
if err > 0 {
|
|
||||||
// End indicies of the attribute names
|
|
||||||
// the buffer contains 0-terminates c-strings
|
|
||||||
let idx = buf.iter().enumerate().filter_map(|(i, v)|
|
|
||||||
if *v == 0 { Some(i) } else { None }
|
|
||||||
);
|
|
||||||
let mut names = Vec::new();
|
|
||||||
let mut start = 0;
|
|
||||||
for end in idx {
|
|
||||||
let c_end = end + 1; // end of the c-string (including 0)
|
|
||||||
let size = unsafe {
|
|
||||||
getxattr(
|
|
||||||
c_path.as_ptr(),
|
|
||||||
buf[start..c_end].as_ptr() as *const c_char,
|
|
||||||
ptr::null_mut(), 0, 0, c_flags
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if size > 0 {
|
|
||||||
names.push(Attribute {
|
|
||||||
name: unsafe {
|
|
||||||
// buf is guaranteed to contain valid utf8 strings
|
|
||||||
// see man listxattr
|
|
||||||
mem::transmute::<&[u8], &str>(&buf[start..end]).to_string()
|
|
||||||
},
|
|
||||||
size: size as usize
|
|
||||||
});
|
|
||||||
}
|
|
||||||
start = c_end;
|
|
||||||
}
|
|
||||||
Ok(names)
|
|
||||||
} else {
|
|
||||||
Err(io::Error::new(io::ErrorKind::Other, "could not read extended attributes"))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(io::Error::new(io::ErrorKind::Other, "could not read extended attributes"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Getter for name
|
|
||||||
pub fn name(&self) -> &str {
|
|
||||||
&self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Getter for size
|
|
||||||
pub fn size(&self) -> usize {
|
|
||||||
self.size
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lists the extended attributes.
|
|
||||||
/// Follows symlinks like `metadata`
|
|
||||||
pub fn list(path: &Path) -> io::Result<Vec<Attribute>> {
|
|
||||||
Attribute::list_attrs(path, &[])
|
|
||||||
}
|
|
||||||
/// Lists the extended attributes.
|
|
||||||
/// Does not follow symlinks like `symlink_metadata`
|
|
||||||
pub fn llist(path: &Path) -> io::Result<Vec<Attribute>> {
|
|
||||||
Attribute::list_attrs(path, &[ListFlags::NoFollow])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the extended attribute feature is implemented on this platform.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn feature_implemented() -> bool { true }
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
|||||||
use std::io;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Attribute;
|
|
||||||
|
|
||||||
impl Attribute {
|
|
||||||
|
|
||||||
/// Getter for name
|
|
||||||
pub fn name(&self) -> &str {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Getter for size
|
|
||||||
pub fn size(&self) -> usize {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lists the extended attributes. Follows symlinks like `metadata`
|
|
||||||
pub fn list(_: &Path) -> io::Result<Vec<Attribute>> {
|
|
||||||
Ok(Vec::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lists the extended attributes. Does not follow symlinks like `symlink_metadata`
|
|
||||||
pub fn llist(_: &Path) -> io::Result<Vec<Attribute>> {
|
|
||||||
Ok(Vec::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn feature_implemented() -> bool { false }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
|||||||
//! Extended attribute support for darwin
|
|
||||||
extern crate libc;
|
|
||||||
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::io;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
use self::libc::{size_t, ssize_t, c_char, c_void};
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
fn listxattr(path: *const c_char, list: *mut c_char, size: size_t) -> ssize_t;
|
|
||||||
fn llistxattr(path: *const c_char, list: *mut c_char, size: size_t) -> ssize_t;
|
|
||||||
fn getxattr(path: *const c_char, name: *const c_char,
|
|
||||||
value: *mut c_void, size: size_t
|
|
||||||
) -> ssize_t;
|
|
||||||
fn lgetxattr(path: *const c_char, name: *const c_char,
|
|
||||||
value: *mut c_void, size: size_t
|
|
||||||
) -> ssize_t;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Attributes which can be passed to `Attribute::list_with_flags`
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum FollowSymlinks {
|
|
||||||
Yes,
|
|
||||||
No
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Extended attribute
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Attribute {
|
|
||||||
name: String,
|
|
||||||
size: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Attribute {
|
|
||||||
/// Lists the extended attribute of `path`.
|
|
||||||
/// Does follow symlinks by default.
|
|
||||||
pub fn list_attrs(path: &Path, do_follow: FollowSymlinks) -> io::Result<Vec<Attribute>> {
|
|
||||||
let (listxattr, getxattr) = match do_follow {
|
|
||||||
FollowSymlinks::Yes => (listxattr, getxattr),
|
|
||||||
FollowSymlinks::No => (llistxattr, lgetxattr),
|
|
||||||
};
|
|
||||||
|
|
||||||
let c_path = match path.as_os_str().to_cstring() {
|
|
||||||
Some(cstring) => cstring,
|
|
||||||
None => return Err(io::Error::new(io::ErrorKind::Other, "could not read extended attributes")),
|
|
||||||
};
|
|
||||||
|
|
||||||
let bufsize = unsafe {
|
|
||||||
listxattr(c_path.as_ptr(), ptr::null_mut(), 0)
|
|
||||||
};
|
|
||||||
|
|
||||||
if bufsize > 0 {
|
|
||||||
let mut buf = vec![0u8; bufsize as usize];
|
|
||||||
let err = unsafe { listxattr(
|
|
||||||
c_path.as_ptr(),
|
|
||||||
buf.as_mut_ptr() as *mut c_char,
|
|
||||||
bufsize as size_t
|
|
||||||
)};
|
|
||||||
if err > 0 {
|
|
||||||
// End indicies of the attribute names
|
|
||||||
// the buffer contains 0-terminates c-strings
|
|
||||||
let idx = buf.iter().enumerate().filter_map(|(i, v)|
|
|
||||||
if *v == 0 { Some(i) } else { None }
|
|
||||||
);
|
|
||||||
let mut names = Vec::new();
|
|
||||||
let mut start = 0;
|
|
||||||
for end in idx {
|
|
||||||
let c_end = end + 1; // end of the c-string (including 0)
|
|
||||||
let size = unsafe {
|
|
||||||
getxattr(
|
|
||||||
c_path.as_ptr(),
|
|
||||||
buf[start..c_end].as_ptr() as *const c_char,
|
|
||||||
ptr::null_mut(), 0
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if size > 0 {
|
|
||||||
names.push(Attribute {
|
|
||||||
name: String::from_utf8_lossy(&buf[start..end]).into_owned(),
|
|
||||||
size: size as usize
|
|
||||||
});
|
|
||||||
}
|
|
||||||
start = c_end;
|
|
||||||
}
|
|
||||||
Ok(names)
|
|
||||||
} else {
|
|
||||||
Err(io::Error::new(io::ErrorKind::Other, "could not read extended attributes"))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(io::Error::new(io::ErrorKind::Other, "could not read extended attributes"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Getter for name
|
|
||||||
pub fn name(&self) -> &str {
|
|
||||||
&self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Getter for size
|
|
||||||
pub fn size(&self) -> usize {
|
|
||||||
self.size
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lists the extended attributes.
|
|
||||||
/// Follows symlinks like `metadata`
|
|
||||||
pub fn list(path: &Path) -> io::Result<Vec<Attribute>> {
|
|
||||||
Attribute::list_attrs(path, FollowSymlinks::Yes)
|
|
||||||
}
|
|
||||||
/// Lists the extended attributes.
|
|
||||||
/// Does not follow symlinks like `symlink_metadata`
|
|
||||||
pub fn llist(path: &Path) -> io::Result<Vec<Attribute>> {
|
|
||||||
Attribute::list_attrs(path, FollowSymlinks::No)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the extended attribute feature is implemented on this platform.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn feature_implemented() -> bool { true }
|
|
||||||
}
|
|
45
src/file.rs
45
src/file.rs
@ -12,7 +12,6 @@ use unicode_width::UnicodeWidthStr;
|
|||||||
|
|
||||||
use dir::Dir;
|
use dir::Dir;
|
||||||
use options::TimeType;
|
use options::TimeType;
|
||||||
use feature::Attribute;
|
|
||||||
|
|
||||||
use self::fields as f;
|
use self::fields as f;
|
||||||
|
|
||||||
@ -27,27 +26,23 @@ use self::fields as f;
|
|||||||
pub struct File<'dir> {
|
pub struct File<'dir> {
|
||||||
|
|
||||||
/// This file's name, as a UTF-8 encoded String.
|
/// This file's name, as a UTF-8 encoded String.
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
||||||
/// The file's name's extension, if present, extracted from the name. This
|
/// The file's name's extension, if present, extracted from the name. This
|
||||||
/// is queried a lot, so it's worth being cached.
|
/// is queried a lot, so it's worth being cached.
|
||||||
pub ext: Option<String>,
|
pub ext: Option<String>,
|
||||||
|
|
||||||
/// The path that begat this file. Even though the file's name is
|
/// The path that begat this file. Even though the file's name is
|
||||||
/// extracted, the path needs to be kept around, as certain operations
|
/// extracted, the path needs to be kept around, as certain operations
|
||||||
/// involve looking up the file's absolute location (such as the Git
|
/// involve looking up the file's absolute location (such as the Git
|
||||||
/// status, or searching for compiled files).
|
/// status, or searching for compiled files).
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
|
|
||||||
/// A cached `metadata` call for this file. This is queried multiple
|
/// A cached `metadata` call for this file. This is queried multiple
|
||||||
/// times, and is *not* cached by the OS, as it could easily change
|
/// times, and is *not* cached by the OS, as it could easily change
|
||||||
/// between invocations - but exa is so short-lived it's better to just
|
/// between invocations - but exa is so short-lived it's better to just
|
||||||
/// cache it.
|
/// cache it.
|
||||||
pub metadata: fs::Metadata,
|
pub metadata: fs::Metadata,
|
||||||
|
|
||||||
/// List of this file's extended attributes. These are only loaded if the
|
|
||||||
/// `xattr` feature is in use.
|
|
||||||
pub xattrs: Vec<Attribute>,
|
|
||||||
|
|
||||||
/// A reference to the directory that contains this file, if present.
|
/// A reference to the directory that contains this file, if present.
|
||||||
///
|
///
|
||||||
@ -57,11 +52,7 @@ pub struct File<'dir> {
|
|||||||
/// However, *directories* that get passed in will produce files that
|
/// However, *directories* that get passed in will produce files that
|
||||||
/// contain a reference to it, which is used in certain operations (such
|
/// contain a reference to it, which is used in certain operations (such
|
||||||
/// as looking up a file's Git status).
|
/// as looking up a file's Git status).
|
||||||
pub dir: Option<&'dir Dir>,
|
pub dir: Option<&'dir Dir>,
|
||||||
|
|
||||||
/// If this `File` is also a directory, then this field is the same file
|
|
||||||
/// as a `Dir`.
|
|
||||||
pub this: Option<Dir>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'dir> File<'dir> {
|
impl<'dir> File<'dir> {
|
||||||
@ -70,32 +61,20 @@ impl<'dir> File<'dir> {
|
|||||||
///
|
///
|
||||||
/// This uses `symlink_metadata` instead of `metadata`, which doesn't
|
/// This uses `symlink_metadata` instead of `metadata`, which doesn't
|
||||||
/// follow symbolic links.
|
/// follow symbolic links.
|
||||||
pub fn from_path(path: &Path, parent: Option<&'dir Dir>, recurse: bool) -> io::Result<File<'dir>> {
|
pub fn from_path(path: &Path, parent: Option<&'dir Dir>) -> io::Result<File<'dir>> {
|
||||||
fs::symlink_metadata(path).map(|metadata| File::with_metadata(metadata, path, parent, recurse))
|
fs::symlink_metadata(path).map(|metadata| File::with_metadata(metadata, path, parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new File object from the given metadata result, and other data.
|
/// Create a new File object from the given metadata result, and other data.
|
||||||
pub fn with_metadata(metadata: fs::Metadata, path: &Path, parent: Option<&'dir Dir>, recurse: bool) -> File<'dir> {
|
pub fn with_metadata(metadata: fs::Metadata, path: &Path, parent: Option<&'dir Dir>) -> File<'dir> {
|
||||||
let filename = path_filename(path);
|
let filename = path_filename(path);
|
||||||
|
|
||||||
// If we are recursing, then the `this` field contains a Dir object
|
|
||||||
// that represents the current File as a directory, if it is a
|
|
||||||
// directory. This is used for the --tree option.
|
|
||||||
let this = if recurse && metadata.is_dir() {
|
|
||||||
Dir::readdir(path, false).ok()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
File {
|
File {
|
||||||
path: path.to_path_buf(),
|
path: path.to_path_buf(),
|
||||||
dir: parent,
|
dir: parent,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
ext: ext(&filename),
|
ext: ext(&filename),
|
||||||
xattrs: Attribute::llist(path).unwrap_or(Vec::new()),
|
|
||||||
name: filename.to_string(),
|
name: filename.to_string(),
|
||||||
this: this,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +83,10 @@ impl<'dir> File<'dir> {
|
|||||||
self.metadata.is_dir()
|
self.metadata.is_dir()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_dir(&self) -> io::Result<Dir> {
|
||||||
|
Dir::readdir(&*self.path, false)
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether this file is a regular file on the filesystem - that is, not a
|
/// Whether this file is a regular file on the filesystem - that is, not a
|
||||||
/// directory, a link, or anything else treated specially.
|
/// directory, a link, or anything else treated specially.
|
||||||
pub fn is_file(&self) -> bool {
|
pub fn is_file(&self) -> bool {
|
||||||
@ -199,9 +182,7 @@ impl<'dir> File<'dir> {
|
|||||||
dir: self.dir,
|
dir: self.dir,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
ext: ext(&filename),
|
ext: ext(&filename),
|
||||||
xattrs: Attribute::list(&target_path).unwrap_or(Vec::new()),
|
|
||||||
name: filename.to_string(),
|
name: filename.to_string(),
|
||||||
this: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -316,7 +297,7 @@ impl<'dir> File<'dir> {
|
|||||||
other_read: has_bit(unix::fs::OTHER_READ),
|
other_read: has_bit(unix::fs::OTHER_READ),
|
||||||
other_write: has_bit(unix::fs::OTHER_WRITE),
|
other_write: has_bit(unix::fs::OTHER_WRITE),
|
||||||
other_execute: has_bit(unix::fs::OTHER_EXECUTE),
|
other_execute: has_bit(unix::fs::OTHER_EXECUTE),
|
||||||
attribute: !self.xattrs.is_empty()
|
attribute: false, // !self.xattrs.is_empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
src/main.rs
18
src/main.rs
@ -1,3 +1,4 @@
|
|||||||
|
#![feature(iter_arith)]
|
||||||
#![feature(convert, fs_mode)]
|
#![feature(convert, fs_mode)]
|
||||||
#![feature(slice_splits, vec_resize)]
|
#![feature(slice_splits, vec_resize)]
|
||||||
|
|
||||||
@ -89,11 +90,8 @@ impl<'dir> Exa<'dir> {
|
|||||||
let path = Path::new(&*file);
|
let path = Path::new(&*file);
|
||||||
let _ = tx.send(match fs::metadata(&path) {
|
let _ = tx.send(match fs::metadata(&path) {
|
||||||
Ok(metadata) => {
|
Ok(metadata) => {
|
||||||
if !metadata.is_dir() {
|
if is_tree || !metadata.is_dir() {
|
||||||
StatResult::File(File::with_metadata(metadata, &path, None, false))
|
StatResult::File(File::with_metadata(metadata, &path, None))
|
||||||
}
|
|
||||||
else if is_tree {
|
|
||||||
StatResult::File(File::with_metadata(metadata, &path, None, true))
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
StatResult::Dir(path.to_path_buf())
|
StatResult::Dir(path.to_path_buf())
|
||||||
@ -146,7 +144,15 @@ impl<'dir> Exa<'dir> {
|
|||||||
|
|
||||||
match Dir::readdir(&dir_path, self.options.should_scan_for_git()) {
|
match Dir::readdir(&dir_path, self.options.should_scan_for_git()) {
|
||||||
Ok(ref dir) => {
|
Ok(ref dir) => {
|
||||||
let mut files = dir.files(false);
|
let mut files = Vec::new();
|
||||||
|
|
||||||
|
for file in dir.files() {
|
||||||
|
match file {
|
||||||
|
Ok(file) => files.push(file),
|
||||||
|
Err((path, e)) => println!("[{}: {}]", path.display(), e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.options.transform_files(&mut files);
|
self.options.transform_files(&mut files);
|
||||||
|
|
||||||
// When recursing, add any directories to the dirs stack
|
// When recursing, add any directories to the dirs stack
|
||||||
|
@ -11,7 +11,7 @@ use colours::Colours;
|
|||||||
use column::Column;
|
use column::Column;
|
||||||
use column::Column::*;
|
use column::Column::*;
|
||||||
use dir::Dir;
|
use dir::Dir;
|
||||||
use feature::Attribute;
|
use feature::xattr;
|
||||||
use file::File;
|
use file::File;
|
||||||
use output::{Grid, Details, GridDetails, Lines};
|
use output::{Grid, Details, GridDetails, Lines};
|
||||||
use term::dimensions;
|
use term::dimensions;
|
||||||
@ -62,7 +62,7 @@ impl Options {
|
|||||||
opts.optflag("", "git", "show git status");
|
opts.optflag("", "git", "show git status");
|
||||||
}
|
}
|
||||||
|
|
||||||
if Attribute::feature_implemented() {
|
if xattr::ENABLED {
|
||||||
opts.optflag("@", "extended", "display extended attribute keys and sizes in long (-l) output");
|
opts.optflag("@", "extended", "display extended attribute keys and sizes in long (-l) output");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,7 +281,7 @@ impl View {
|
|||||||
columns: Some(try!(Columns::deduce(matches))),
|
columns: Some(try!(Columns::deduce(matches))),
|
||||||
header: matches.opt_present("header"),
|
header: matches.opt_present("header"),
|
||||||
recurse: dir_action.recurse_options().map(|o| (o, filter)),
|
recurse: dir_action.recurse_options().map(|o| (o, filter)),
|
||||||
xattr: Attribute::feature_implemented() && matches.opt_present("extended"),
|
xattr: xattr::ENABLED && matches.opt_present("extended"),
|
||||||
colours: if dimensions().is_some() { Colours::colourful() } else { Colours::plain() },
|
colours: if dimensions().is_some() { Colours::colourful() } else { Colours::plain() },
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -302,7 +302,7 @@ impl View {
|
|||||||
else if matches.opt_present("level") && !matches.opt_present("recurse") && !matches.opt_present("tree") {
|
else if matches.opt_present("level") && !matches.opt_present("recurse") && !matches.opt_present("tree") {
|
||||||
Err(Useless2("level", "recurse", "tree"))
|
Err(Useless2("level", "recurse", "tree"))
|
||||||
}
|
}
|
||||||
else if Attribute::feature_implemented() && matches.opt_present("extended") {
|
else if xattr::ENABLED && matches.opt_present("extended") {
|
||||||
Err(Useless("extended", false, "long"))
|
Err(Useless("extended", false, "long"))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -640,7 +640,7 @@ impl Columns {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::Options;
|
use super::Options;
|
||||||
use super::Misfire;
|
use super::Misfire;
|
||||||
use feature::Attribute;
|
use feature::xattr;
|
||||||
|
|
||||||
fn is_helpful<T>(misfire: Result<T, Misfire>) -> bool {
|
fn is_helpful<T>(misfire: Result<T, Misfire>) -> bool {
|
||||||
match misfire {
|
match misfire {
|
||||||
@ -742,7 +742,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn extended_without_long() {
|
fn extended_without_long() {
|
||||||
if Attribute::feature_implemented() {
|
if xattr::ENABLED {
|
||||||
let opts = Options::getopts(&[ "--extended".to_string() ]);
|
let opts = Options::getopts(&[ "--extended".to_string() ]);
|
||||||
assert_eq!(opts.unwrap_err(), Misfire::Useless("extended", false, "long"))
|
assert_eq!(opts.unwrap_err(), Misfire::Useless("extended", false, "long"))
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
use std::iter::repeat;
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
|
||||||
use colours::Colours;
|
use colours::Colours;
|
||||||
use column::{Alignment, Column, Cell};
|
use column::{Alignment, Column, Cell};
|
||||||
use dir::Dir;
|
use dir::Dir;
|
||||||
use feature::Attribute;
|
use feature::xattr::{Attribute, FileAttributes};
|
||||||
use file::fields as f;
|
use file::fields as f;
|
||||||
use file::File;
|
use file::File;
|
||||||
use options::{Columns, FileFilter, RecurseOptions, SizeFormat};
|
use options::{Columns, FileFilter, RecurseOptions, SizeFormat};
|
||||||
@ -75,7 +77,7 @@ impl Details {
|
|||||||
|
|
||||||
// Then add files to the table and print it out.
|
// Then add files to the table and print it out.
|
||||||
self.add_files_to_table(&mut table, files, 0);
|
self.add_files_to_table(&mut table, files, 0);
|
||||||
for cell in table.print_table(self.xattr, self.recurse.is_some()) {
|
for cell in table.print_table() {
|
||||||
println!("{}", cell.text);
|
println!("{}", cell.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,26 +86,82 @@ impl Details {
|
|||||||
/// is present.
|
/// is present.
|
||||||
fn add_files_to_table<U: Users>(&self, table: &mut Table<U>, src: &[File], depth: usize) {
|
fn add_files_to_table<U: Users>(&self, table: &mut Table<U>, src: &[File], depth: usize) {
|
||||||
for (index, file) in src.iter().enumerate() {
|
for (index, file) in src.iter().enumerate() {
|
||||||
table.add_file(file, depth, index == src.len() - 1, true);
|
let mut xattrs = Vec::new();
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
|
let has_xattrs = match file.path.attributes() {
|
||||||
|
Ok(xs) => {
|
||||||
|
let r = !xs.is_empty();
|
||||||
|
if self.xattr {
|
||||||
|
for xattr in xs {
|
||||||
|
xattrs.push(xattr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
if self.xattr {
|
||||||
|
errors.push((e, None));
|
||||||
|
}
|
||||||
|
true
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
table.add_file(file, depth, index == src.len() - 1, true, has_xattrs);
|
||||||
|
|
||||||
// There are two types of recursion that exa supports: a tree
|
// There are two types of recursion that exa supports: a tree
|
||||||
// view, which is dealt with here, and multiple listings, which is
|
// view, which is dealt with here, and multiple listings, which is
|
||||||
// dealt with in the main module. So only actually recurse if we
|
// dealt with in the main module. So only actually recurse if we
|
||||||
// are in tree mode - the other case will be dealt with elsewhere.
|
// are in tree mode - the other case will be dealt with elsewhere.
|
||||||
if let Some((r, filter)) = self.recurse {
|
if let Some((r, filter)) = self.recurse {
|
||||||
if r.tree == false || r.is_too_deep(depth) {
|
if file.is_directory() && r.tree && !r.is_too_deep(depth) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the filter to remove unwanted files *before* expanding
|
// Use the filter to remove unwanted files *before* expanding
|
||||||
// them, so we don't examine any directories that wouldn't
|
// them, so we don't examine any directories that wouldn't
|
||||||
// have their contents listed anyway.
|
// have their contents listed anyway.
|
||||||
if let Some(ref dir) = file.this {
|
match file.to_dir() {
|
||||||
let mut files = dir.files(true);
|
Ok(ref dir) => {
|
||||||
filter.transform_files(&mut files);
|
let mut files = Vec::new();
|
||||||
self.add_files_to_table(table, &files, depth + 1);
|
|
||||||
|
for file_to_add in dir.files() {
|
||||||
|
match file_to_add {
|
||||||
|
Ok(f) => files.push(f),
|
||||||
|
Err((path, e)) => errors.push((e, Some(path)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filter.transform_files(&mut files);
|
||||||
|
|
||||||
|
if !files.is_empty() {
|
||||||
|
for xattr in xattrs {
|
||||||
|
table.add_xattr(xattr, depth + 1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (error, path) in errors {
|
||||||
|
table.add_error(&error, depth + 1, false, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.add_files_to_table(table, &files, depth + 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
errors.push((e, None));
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let count = xattrs.len();
|
||||||
|
for (index, xattr) in xattrs.into_iter().enumerate() {
|
||||||
|
table.add_xattr(xattr, depth + 1, errors.is_empty() && index == count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = errors.len();
|
||||||
|
for (index, (error, path)) in errors.into_iter().enumerate() {
|
||||||
|
table.add_error(&error, depth + 1, index == count - 1, path);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,26 +170,38 @@ impl Details {
|
|||||||
struct Row {
|
struct Row {
|
||||||
|
|
||||||
/// Vector of cells to display.
|
/// Vector of cells to display.
|
||||||
cells: Vec<Cell>,
|
///
|
||||||
|
/// Most of the rows will be files that have had their metadata
|
||||||
|
/// successfully queried and displayed in these cells, so this will almost
|
||||||
|
/// always be `Some`. It will be `None` for a row that's only displaying
|
||||||
|
/// an attribute or an error.
|
||||||
|
cells: Option<Vec<Cell>>,
|
||||||
|
|
||||||
|
// Did You Know?
|
||||||
|
// A Vec<Cell> and an Option<Vec<Cell>> actually have the same byte size!
|
||||||
|
|
||||||
/// This file's name, in coloured output. The name is treated separately
|
/// This file's name, in coloured output. The name is treated separately
|
||||||
/// from the other cells, as it never requires padding.
|
/// from the other cells, as it never requires padding.
|
||||||
name: Cell,
|
name: Cell,
|
||||||
|
|
||||||
/// How many directories deep into the tree structure this is. Directories
|
/// How many directories deep into the tree structure this is. Directories
|
||||||
/// on top have depth 0.
|
/// on top have depth 0.
|
||||||
depth: usize,
|
depth: usize,
|
||||||
|
|
||||||
/// Vector of this file's extended attributes, if that feature is active.
|
|
||||||
attrs: Vec<Attribute>,
|
|
||||||
|
|
||||||
/// Whether this is the last entry in the directory. This flag is used
|
/// Whether this is the last entry in the directory. This flag is used
|
||||||
/// when calculating the tree view.
|
/// when calculating the tree view.
|
||||||
last: bool,
|
last: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether this file is a directory and has any children. Also used when
|
impl Row {
|
||||||
/// calculating the tree view.
|
|
||||||
children: bool,
|
/// Gets the 'width' of the indexed column, if present. If not, returns 0.
|
||||||
|
fn column_width(&self, index: usize) -> usize {
|
||||||
|
match self.cells {
|
||||||
|
Some(ref cells) => cells[index].length,
|
||||||
|
None => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -191,30 +261,53 @@ impl<U> Table<U> where U: Users {
|
|||||||
pub fn add_header(&mut self) {
|
pub fn add_header(&mut self) {
|
||||||
let row = Row {
|
let row = Row {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
cells: self.columns.iter().map(|c| Cell::paint(self.colours.header, c.header())).collect(),
|
cells: Some(self.columns.iter().map(|c| Cell::paint(self.colours.header, c.header())).collect()),
|
||||||
name: Cell::paint(self.colours.header, "Name"),
|
name: Cell::paint(self.colours.header, "Name"),
|
||||||
last: false,
|
last: false,
|
||||||
attrs: Vec::new(),
|
};
|
||||||
children: false,
|
|
||||||
|
self.rows.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_error(&mut self, error: &io::Error, depth: usize, last: bool, path: Option<PathBuf>) {
|
||||||
|
let error_message = match path {
|
||||||
|
Some(path) => format!("<{}: {}>", path.display(), error),
|
||||||
|
None => format!("<{}>", error),
|
||||||
|
};
|
||||||
|
|
||||||
|
let row = Row {
|
||||||
|
depth: depth,
|
||||||
|
cells: None,
|
||||||
|
name: Cell::paint(self.colours.broken_arrow, &error_message),
|
||||||
|
last: last,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.rows.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_xattr(&mut self, xattr: Attribute, depth: usize, last: bool) {
|
||||||
|
let row = Row {
|
||||||
|
depth: depth,
|
||||||
|
cells: None,
|
||||||
|
name: Cell::paint(self.colours.perms.attribute, &format!("{}\t{}", xattr.name, xattr.size)),
|
||||||
|
last: last,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.rows.push(row);
|
self.rows.push(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the cells for the given file, and add the result to the table.
|
/// Get the cells for the given file, and add the result to the table.
|
||||||
pub fn add_file(&mut self, file: &File, depth: usize, last: bool, links: bool) {
|
fn add_file(&mut self, file: &File, depth: usize, last: bool, links: bool, xattrs: bool) {
|
||||||
let cells = self.cells_for_file(file);
|
let cells = self.cells_for_file(file, xattrs);
|
||||||
self.add_file_with_cells(cells, file, depth, last, links)
|
self.add_file_with_cells(cells, file, depth, last, links)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_file_with_cells(&mut self, cells: Vec<Cell>, file: &File, depth: usize, last: bool, links: bool) {
|
pub fn add_file_with_cells(&mut self, cells: Vec<Cell>, file: &File, depth: usize, last: bool, links: bool) {
|
||||||
let row = Row {
|
let row = Row {
|
||||||
depth: depth,
|
depth: depth,
|
||||||
cells: cells,
|
cells: Some(cells),
|
||||||
name: Cell { text: filename(file, &self.colours, links), length: file.file_name_width() },
|
name: Cell { text: filename(file, &self.colours, links), length: file.file_name_width() },
|
||||||
last: last,
|
last: last,
|
||||||
attrs: file.xattrs.clone(),
|
|
||||||
children: file.this.is_some(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.rows.push(row);
|
self.rows.push(row);
|
||||||
@ -222,15 +315,15 @@ impl<U> Table<U> where U: Users {
|
|||||||
|
|
||||||
/// Use the list of columns to find which cells should be produced for
|
/// Use the list of columns to find which cells should be produced for
|
||||||
/// this file, per-column.
|
/// this file, per-column.
|
||||||
pub fn cells_for_file(&mut self, file: &File) -> Vec<Cell> {
|
pub fn cells_for_file(&mut self, file: &File, xattrs: bool) -> Vec<Cell> {
|
||||||
self.columns.clone().iter()
|
self.columns.clone().iter()
|
||||||
.map(|c| self.display(file, c))
|
.map(|c| self.display(file, c, xattrs))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display(&mut self, file: &File, column: &Column) -> Cell {
|
fn display(&mut self, file: &File, column: &Column, xattrs: bool) -> Cell {
|
||||||
match *column {
|
match *column {
|
||||||
Column::Permissions => self.render_permissions(file.permissions()),
|
Column::Permissions => self.render_permissions(file.permissions(), xattrs),
|
||||||
Column::FileSize(fmt) => self.render_size(file.size(), fmt),
|
Column::FileSize(fmt) => self.render_size(file.size(), fmt),
|
||||||
Column::Timestamp(t) => self.render_time(file.timestamp(t)),
|
Column::Timestamp(t) => self.render_time(file.timestamp(t)),
|
||||||
Column::HardLinks => self.render_links(file.links()),
|
Column::HardLinks => self.render_links(file.links()),
|
||||||
@ -242,7 +335,7 @@ impl<U> Table<U> where U: Users {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_permissions(&self, permissions: f::Permissions) -> Cell {
|
fn render_permissions(&self, permissions: f::Permissions, xattrs: bool) -> Cell {
|
||||||
let c = self.colours.perms;
|
let c = self.colours.perms;
|
||||||
let bit = |bit, chr: &'static str, style: Style| {
|
let bit = |bit, chr: &'static str, style: Style| {
|
||||||
if bit { style.paint(chr) } else { self.colours.punctuation.paint("-") }
|
if bit { style.paint(chr) } else { self.colours.punctuation.paint("-") }
|
||||||
@ -272,7 +365,7 @@ impl<U> Table<U> where U: Users {
|
|||||||
bit(permissions.other_execute, "x", c.other_execute),
|
bit(permissions.other_execute, "x", c.other_execute),
|
||||||
];
|
];
|
||||||
|
|
||||||
if permissions.attribute {
|
if xattrs {
|
||||||
columns.push(c.attribute.paint("@"));
|
columns.push(c.attribute.paint("@"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,8 +481,8 @@ impl<U> Table<U> where U: Users {
|
|||||||
Cell::paint(style, &*group_name)
|
Cell::paint(style, &*group_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print the table to standard output, consuming it in the process.
|
/// Render the table as a vector of Cells, to be displayed on standard output.
|
||||||
pub fn print_table(&self, xattr: bool, show_children: bool) -> Vec<Cell> {
|
pub fn print_table(&self) -> Vec<Cell> {
|
||||||
let mut stack = Vec::new();
|
let mut stack = Vec::new();
|
||||||
let mut cells = Vec::new();
|
let mut cells = Vec::new();
|
||||||
|
|
||||||
@ -397,19 +490,26 @@ impl<U> Table<U> where U: Users {
|
|||||||
// each column, then formatting each cell in that column to be the
|
// each column, then formatting each cell in that column to be the
|
||||||
// width of that one.
|
// width of that one.
|
||||||
let column_widths: Vec<usize> = (0 .. self.columns.len())
|
let column_widths: Vec<usize> = (0 .. self.columns.len())
|
||||||
.map(|n| self.rows.iter().map(|row| row.cells[n].length).max().unwrap_or(0))
|
.map(|n| self.rows.iter().map(|row| row.column_width(n)).max().unwrap_or(0))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let total_width: usize = self.columns.len() + column_widths.iter().sum::<usize>();
|
||||||
|
|
||||||
for row in self.rows.iter() {
|
for row in self.rows.iter() {
|
||||||
let mut cell = Cell::empty();
|
let mut cell = Cell::empty();
|
||||||
|
|
||||||
for (n, width) in column_widths.iter().enumerate() {
|
if let Some(ref cells) = row.cells {
|
||||||
match self.columns[n].alignment() {
|
for (n, width) in column_widths.iter().enumerate() {
|
||||||
Alignment::Left => { cell.append(&row.cells[n]); cell.add_spaces(width - row.cells[n].length); }
|
match self.columns[n].alignment() {
|
||||||
Alignment::Right => { cell.add_spaces(width - row.cells[n].length); cell.append(&row.cells[n]); }
|
Alignment::Left => { cell.append(&cells[n]); cell.add_spaces(width - cells[n].length); }
|
||||||
}
|
Alignment::Right => { cell.add_spaces(width - cells[n].length); cell.append(&cells[n]); }
|
||||||
|
}
|
||||||
|
|
||||||
cell.add_spaces(1);
|
cell.add_spaces(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cell.add_spaces(total_width)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut filename = String::new();
|
let mut filename = String::new();
|
||||||
@ -419,40 +519,27 @@ impl<U> Table<U> where U: Users {
|
|||||||
// necessary to maintain information about the previously-printed
|
// necessary to maintain information about the previously-printed
|
||||||
// lines, as the output will change based on whether the
|
// lines, as the output will change based on whether the
|
||||||
// *previous* entry was the last in its directory.
|
// *previous* entry was the last in its directory.
|
||||||
if show_children {
|
stack.resize(row.depth + 1, TreePart::Edge);
|
||||||
stack.resize(row.depth + 1, TreePart::Edge);
|
stack[row.depth] = if row.last { TreePart::Corner } else { TreePart::Edge };
|
||||||
stack[row.depth] = if row.last { TreePart::Corner } else { TreePart::Edge };
|
|
||||||
|
|
||||||
for i in 1 .. row.depth + 1 {
|
for i in 1 .. row.depth + 1 {
|
||||||
filename.push_str(&*self.colours.punctuation.paint(stack[i].ascii_art()).to_string());
|
filename.push_str(&*self.colours.punctuation.paint(stack[i].ascii_art()).to_string());
|
||||||
filename_length += 4;
|
filename_length += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if row.children {
|
stack[row.depth] = if row.last { TreePart::Blank } else { TreePart::Line };
|
||||||
stack[row.depth] = if row.last { TreePart::Blank } else { TreePart::Line };
|
|
||||||
}
|
|
||||||
|
|
||||||
// If any tree characters have been printed, then add an extra
|
// If any tree characters have been printed, then add an extra
|
||||||
// space, which makes the output look much better.
|
// space, which makes the output look much better.
|
||||||
if row.depth != 0 {
|
if row.depth != 0 {
|
||||||
filename.push(' ');
|
filename.push(' ');
|
||||||
filename_length += 1;
|
filename_length += 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the name without worrying about padding.
|
// Print the name without worrying about padding.
|
||||||
filename.push_str(&*row.name.text);
|
filename.push_str(&*row.name.text);
|
||||||
filename_length += row.name.length;
|
filename_length += row.name.length;
|
||||||
|
|
||||||
if xattr {
|
|
||||||
let width = row.attrs.iter().map(|a| a.name().len()).max().unwrap_or(0);
|
|
||||||
for attr in row.attrs.iter() {
|
|
||||||
let name = attr.name();
|
|
||||||
let spaces: String = repeat(" ").take(width - name.len()).collect();
|
|
||||||
filename.push_str(&*format!("\n{}{} {}", name, spaces, attr.size()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cell.append(&Cell { text: filename, length: filename_length });
|
cell.append(&Cell { text: filename, length: filename_length });
|
||||||
cells.push(cell);
|
cells.push(cell);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ use term_grid as grid;
|
|||||||
|
|
||||||
use column::{Column, Cell};
|
use column::{Column, Cell};
|
||||||
use dir::Dir;
|
use dir::Dir;
|
||||||
|
use feature::xattr::FileAttributes;
|
||||||
use file::File;
|
use file::File;
|
||||||
use output::details::{Details, Table};
|
use output::details::{Details, Table};
|
||||||
use output::grid::Grid;
|
use output::grid::Grid;
|
||||||
@ -15,6 +16,13 @@ pub struct GridDetails {
|
|||||||
pub details: Details,
|
pub details: Details,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn file_has_xattrs(file: &File) -> bool {
|
||||||
|
match file.path.attributes() {
|
||||||
|
Ok(attrs) => !attrs.is_empty(),
|
||||||
|
Err(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl GridDetails {
|
impl GridDetails {
|
||||||
pub fn view(&self, dir: Option<&Dir>, files: &[File]) {
|
pub fn view(&self, dir: Option<&Dir>, files: &[File]) {
|
||||||
let columns_for_dir = match self.details.columns {
|
let columns_for_dir = match self.details.columns {
|
||||||
@ -23,7 +31,7 @@ impl GridDetails {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut first_table = Table::with_options(self.details.colours, columns_for_dir.clone());
|
let mut first_table = Table::with_options(self.details.colours, columns_for_dir.clone());
|
||||||
let cells: Vec<_> = files.iter().map(|file| first_table.cells_for_file(file)).collect();
|
let cells: Vec<_> = files.iter().map(|file| first_table.cells_for_file(file, file_has_xattrs(file))).collect();
|
||||||
|
|
||||||
let mut last_working_table = self.make_grid(1, &*columns_for_dir, files, cells.clone());
|
let mut last_working_table = self.make_grid(1, &*columns_for_dir, files, cells.clone());
|
||||||
|
|
||||||
@ -73,7 +81,7 @@ impl GridDetails {
|
|||||||
tables[index].add_file_with_cells(row, file, 0, false, false);
|
tables[index].add_file_with_cells(row, file, 0, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let columns: Vec<_> = tables.iter().map(|t| t.print_table(false, false)).collect();
|
let columns: Vec<_> = tables.iter().map(|t| t.print_table()).collect();
|
||||||
|
|
||||||
let direction = if self.grid.across { grid::Direction::LeftToRight }
|
let direction = if self.grid.across { grid::Direction::LeftToRight }
|
||||||
else { grid::Direction::TopToBottom };
|
else { grid::Direction::TopToBottom };
|
||||||
|
Loading…
Reference in New Issue
Block a user