Repository: lsd-rs/lsd (Rust)

Issue:
Broken symbolic links
Consider a directory with broken symlink:

```
$ mkdir temp && cd temp
$ ln -s `pwd`/a `pwd`/b
```

## Expected behavior

Classic ls shows symlink, and, if you enable color, it will show that it is broken.

```
$ /bin/ls -la
total 8
drwxr-xr-x 2 kuviman kuviman 4096 Dec 13 22:52 .
drwxr-xr-x 9 kuviman kuviman 4096 Dec 13 22:51 ..
lrwxrwxrwx 1 kuviman kuviman   25 Dec 13 22:52 b -> /home/kuviman/temp/a
```

## Actual behavior

But `lsd` just throws an error

```
$ lsd -la
cannot access './b': No such file or directory (os error 2)
```

It doesn't crash as before(#2), but still would be better if broken symlinks were highlighted red like in `ls`


Discussion:
Hi @kuviman,

Thanks for the report. It's curious because I don't reproduce the error on my laptop (Arch Linux x86).
```sh
mkdir temp && cd temp
 tmp  temp  %  ⎈ prodild  prod  ln -s `pwd`/a `pwd`/b
 tmp  temp  %  ⎈ prodild  prod  l
lrwxrwxrwx  peltoche  users   -  now ago    b ⇒ /tmp/temp/a
 tmp  temp  %  ⎈ prodild  prod  lsd --version
lsd 0.9.1-pre

```
Can you give the result of `lsd --version` please?
Could not reproduce @kuviman's error on 0.8.0 version.

Agree with him/her that the symlink display color is misleading. On my terminal, the link shows up in cyan color (hope that is the correct one) when `lsd` is used, leading one to presume that the link is correct. `lsd -l` shows the target in red, indicating that it is dangling. 
Right, I was using old version, sorry, its fine on 0.9.0
I am experiencing this on Arch with `lsd 0.16.0`. It's not a problem when listing the current directory contents with no files specified in the command, but is if any files (or files via patterns interpreted by the shell) are provided.

First, GNU `ls` behavior:
```bash
$ ls -l
total 0
lrwxrwxrwx 1 andy andy 19 Nov 12 16:37 anotherlink -> 'no file here either'
lrwxrwxrwx 1 andy andy 16 Nov 12 16:38 rellink -> ./nofilerelative
lrwxrwxrwx 1 andy andy 45 Nov 12 16:40 relspacedeeplink -> './nofile rel and spaces and depth/more/levels'
lrwxrwxrwx 1 andy andy 23 Nov 12 16:40 relspacelink -> './nofile rel and spaces'
lrwxrwxrwx 1 andy andy 10 Nov 12 16:33 symlinkhere -> nofilehere

$ ls -l *
lrwxrwxrwx 1 andy andy 19 Nov 12 16:37 anotherlink -> 'no file here either'
lrwxrwxrwx 1 andy andy 16 Nov 12 16:38 rellink -> ./nofilerelative
lrwxrwxrwx 1 andy andy 45 Nov 12 16:40 relspacedeeplink -> './nofile rel and spaces and depth/more/levels'
lrwxrwxrwx 1 andy andy 23 Nov 12 16:40 relspacelink -> './nofile rel and spaces'
lrwxrwxrwx 1 andy andy 10 Nov 12 16:33 symlinkhere -> nofilehere

$ ls -ld *
lrwxrwxrwx 1 andy andy 19 Nov 12 16:37 anotherlink -> 'no file here either'
lrwxrwxrwx 1 andy andy 16 Nov 12 16:38 rellink -> ./nofilerelative
lrwxrwxrwx 1 andy andy 45 Nov 12 16:40 relspacedeeplink -> './nofile rel and spaces and depth/more/levels'
lrwxrwxrwx 1 andy andy 23 Nov 12 16:40 relspacelink -> './nofile rel and spaces'
lrwxrwxrwx 1 andy andy 10 Nov 12 16:33 symlinkhere -> nofilehere

$ ls -l symlinkhere
lrwxrwxrwx 1 andy andy 10 Nov 12 16:33 symlinkhere -> nofilehere
```

Now with `lsd`:
```bash
$ lsd -l
lrwxrwxrwx andy andy 19 B Tue Nov 12 16:37:09 2019   anotherlink ⇒ no file here either                               
lrwxrwxrwx andy andy 16 B Tue Nov 12 16:38:03 2019   rellink ⇒ ./nofilerelative                                      
lrwxrwxrwx andy andy 45 B Tue Nov 12 16:40:23 2019   relspacedeeplink ⇒ ./nofile rel and spaces and depth/more/levels
lrwxrwxrwx andy andy 23 B Tue Nov 12 16:40:05 2019   relspacelink ⇒ ./nofile rel and spaces                          
lrwxrwxrwx andy andy 10 B Tue Nov 12 16:33:44 2019   symlinkhere ⇒ nofilehere

$ lsd -l *
cannot access 'anotherlink': No such file or directory (os error 2)
cannot access 'rellink': No such file or directory (os error 2)
cannot access 'relspacedeeplink': No such file or directory (os error 2)
cannot access 'relspacelink': No such file or directory (os error 2)
cannot access 'symlinkhere': No such file or directory (os error 2)

$ lsd -ld *
cannot access 'anotherlink': No such file or directory (os error 2)
cannot access 'rellink': No such file or directory (os error 2)
cannot access 'relspacedeeplink': No such file or directory (os error 2)
cannot access 'relspacelink': No such file or directory (os error 2)
cannot access 'symlinkhere': No such file or directory (os error 2)

$ lsd -l symlinkhere
cannot access 'symlinkhere': No such file or directory (os error 2)
```

If you prefer I open a new report, no problem. 
I have a similar problem with Arch Linux lsd (version 0.16.0) .
```sh
~ uname -a                                                                
Linux Archdp 4.19.86-1-lts #1 SMP Sun, 24 Nov 2019 10:16:00 +0000 x86_64 GNU/Linux
~ lsd --version                                                         
lsd 0.16.0

~ touch source  
~ ln -s source slink 
~ /usr/bin/ls -l slink                                                  
lrwxrwxrwx 1 gk07 gk07 6 Dec  2 23:11 slink -> source
~ /usr/bin/lsd -l slink                                                   
lrwxrwxrwx gk07 gk07 6 B Mon Dec  2 23:11:41 2019   slink ⇒ source

~ rm source 

~ /usr/bin/ls -l slink                                                      
lrwxrwxrwx 1 gk07 gk07 6 Dec  2 23:11 slink -> source
~ /usr/bin/lsd -l slink                                                      
cannot access 'slink': No such file or directory (os error 2)           #Here error   
```   
@Peltoche I was working on this, found that the `fs::canonicalize` would resolve the links.

this is why the error occurred.

Also, the later `Meta::from_path` would do the same check.

So I was wondering should we just delete the `fs::canonicalize`, or I resolve the path using `path.components`?
I have the same problem, but also all error messages are shown on the same line.
```
$ lsd -l usr/lib/*.so
cannot access 'usr/lib/libirods_client.so': No such file or directory (os error 2)cannot access 'usr/lib/libirods_common.so': No such file or directory (os error 2)cannot access 'usr/lib/libirods_plugin_dependencies.so': No such file or directory (os error 2)cannot access 'usr/lib/libirods_server.so': No such file or directory (os error 2)
```
I will try to fix it this weekend, sorry for the delay 



Patch:
diff --git a/build.rs b/build.rs
index b7933a3e0..f5f04fd41 100644
--- a/build.rs
+++ b/build.rs
@@ -27,7 +27,7 @@ fn main() {
         }
     }
 
-    let var = std::env::var_os("SHELL_COMPLETIONS_DIR").or(std::env::var_os("OUT_DIR"));
+    let var = std::env::var_os("SHELL_COMPLETIONS_DIR").or_else(|| std::env::var_os("OUT_DIR"));
     let outdir = match var {
         None => return,
         Some(outdir) => outdir,
diff --git a/src/app.rs b/src/app.rs
index d67e713bd..af906c81c 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -220,9 +220,8 @@ pub fn build() -> App<'static, 'static> {
 }
 
 fn validate_date_argument(arg: String) -> Result<(), String> {
-    use std::error::Error;
     if arg.starts_with('+') {
-        validate_time_format(&arg).map_err(|err| err.description().to_string())
+        validate_time_format(&arg).map_err(|err| err.to_string())
     } else if &arg == "date" || &arg == "relative" {
         Result::Ok(())
     } else {
diff --git a/src/core.rs b/src/core.rs
index 6a120b3ac..16aaf8f5a 100644
--- a/src/core.rs
+++ b/src/core.rs
@@ -4,7 +4,6 @@ use crate::flags::{Display, Flags, IconTheme, Layout, WhenFlag};
 use crate::icon::{self, Icons};
 use crate::meta::Meta;
 use crate::{print_error, print_output, sort};
-use std::fs;
 use std::path::PathBuf;
 
 #[cfg(not(target_os = "windows"))]
@@ -83,15 +82,10 @@ impl Core {
         };
 
         for path in paths {
-            if let Err(err) = fs::canonicalize(&path) {
-                print_error!("cannot access '{}': {}", path.display(), err);
-                continue;
-            }
-
             let mut meta = match Meta::from_path(&path) {
                 Ok(meta) => meta,
                 Err(err) => {
-                    print_error!("cannot access '{}': {}", path.display(), err);
+                    print_error!("lsd: {}: {}\n", path.display(), err);
                     continue;
                 }
             };
@@ -107,7 +101,7 @@ impl Core {
                             meta_list.push(meta);
                         }
                         Err(err) => {
-                            print_error!("cannot access '{}': {}", path.display(), err);
+                            print_error!("lsd: {}: {}\n", path.display(), err);
                             continue;
                         }
                     };
diff --git a/src/main.rs b/src/main.rs
index 52518848a..e1537ce5b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -84,6 +84,10 @@ macro_rules! print_output {
 fn main() {
     let matches = app::build().get_matches_from(wild::args_os());
 
+    // input translate glob FILE without single quote into real names
+    // for example:
+    // * to all files matched
+    // '*' remain as '*'
     let inputs = matches
         .values_of("FILE")
         .expect("failed to retrieve cli value")
diff --git a/src/meta/mod.rs b/src/meta/mod.rs
index 2ecc4724c..b29a57a67 100644
--- a/src/meta/mod.rs
+++ b/src/meta/mod.rs
@@ -69,7 +69,7 @@ impl Meta {
         let entries = match self.path.read_dir() {
             Ok(entries) => entries,
             Err(err) => {
-                print_error!("cannot access '{}': {}", self.path.display(), err);
+                print_error!("lsd: {}: {}\n", self.path.display(), err);
                 return Ok(None);
             }
         };
@@ -116,7 +116,7 @@ impl Meta {
             let mut entry_meta = match Self::from_path(&path) {
                 Ok(res) => res,
                 Err(err) => {
-                    print_error!("cannot access '{}': {}", path.display(), err);
+                    print_error!("lsd: {}: {}\n", path.display(), err);
                     continue;
                 }
             };
@@ -124,7 +124,7 @@ impl Meta {
             match entry_meta.recurse_into(depth - 1, display, ignore_globs) {
                 Ok(content) => entry_meta.content = content,
                 Err(err) => {
-                    print_error!("cannot access '{}': {}", path.display(), err);
+                    print_error!("lsd: {}: {}\n", path.display(), err);
                     continue;
                 }
             };
@@ -162,7 +162,7 @@ impl Meta {
         let metadata = match metadata {
             Ok(meta) => meta,
             Err(err) => {
-                print_error!("cannot access '{}': {}", path.display(), err);
+                print_error!("lsd: {}: {}\n", path.display(), err);
                 return 0;
             }
         };
@@ -175,7 +175,7 @@ impl Meta {
             let entries = match path.read_dir() {
                 Ok(entries) => entries,
                 Err(err) => {
-                    print_error!("cannot access '{}': {}", path.display(), err);
+                    print_error!("lsd: {}: {}\n", path.display(), err);
                     return size;
                 }
             };
@@ -183,7 +183,7 @@ impl Meta {
                 let path = match entry {
                     Ok(entry) => entry.path(),
                     Err(err) => {
-                        print_error!("cannot access '{}': {}", path.display(), err);
+                        print_error!("lsd: {}: {}\n", path.display(), err);
                         continue;
                     }
                 };


Test patch:
diff --git a/tests/integration.rs b/tests/integration.rs
index 05dfa9538..8d38fc7b2 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -6,6 +6,9 @@ use assert_fs::prelude::*;
 use predicates::prelude::*;
 use std::process::Command;
 
+#[cfg(unix)]
+use std::os::unix::fs;
+
 #[test]
 fn test_runs_okay() {
     cmd().assert().success();
@@ -134,6 +137,26 @@ fn test_list_inode_with_long_ok() {
     cmd().arg("-i").arg("-l").arg(dir.path()).assert().success();
 }
 
+#[cfg(unix)]
+#[test]
+fn test_list_broken_link_ok() {
+    let dir = tempdir();
+    let broken_link = dir.path().join("broken-softlink");
+    let matched = "No such file or directory";
+    fs::symlink("not-existed-file", &broken_link).unwrap();
+
+    cmd()
+        .arg(&broken_link)
+        .assert()
+        .stderr(predicate::str::contains(matched).not());
+
+    cmd()
+        .arg("-l")
+        .arg(broken_link)
+        .assert()
+        .stderr(predicate::str::contains(matched).not());
+}
+
 fn cmd() -> Command {
     Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap()
 }