feat: links to folders open as folders, on machines and SSH hosts, with their own icon; deleting a link leaves what it leads to
This commit is contained in:
1 parent
4da1cc4200
commit
055020c3bb
6 files changed
+60
-7
No files matched your search
+10
-2
@@ -114,13 +114,21 @@ func fsList(path string) (proto.FSListResult, error) {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, proto.FSEntry{
|
||||
ent := proto.FSEntry{
|
||||
Name: e.Name(),
|
||||
IsDir: e.IsDir(),
|
||||
Size: info.Size(),
|
||||
Mode: info.Mode().String(),
|
||||
MTime: info.ModTime().Unix(),
|
||||
})
|
||||
}
|
||||
// A link to a folder opens like one.
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
ent.Link = true
|
||||
if t, err := os.Stat(filepath.Join(abs, e.Name())); err == nil && t.IsDir() {
|
||||
ent.IsDir = true
|
||||
}
|
||||
}
|
||||
out = append(out, ent)
|
||||
}
|
||||
// Directories first, then alphabetical.
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
|
||||
+10
-2
@@ -72,7 +72,9 @@ var (
|
||||
// statCommand lists dir's entries as "type|size|mtime|mode|name" lines.
|
||||
func statCommand(flavour, dir string) string {
|
||||
d := shq(strings.TrimSuffix(dir, "/") + "/")
|
||||
loop := "cd " + d + " && for f in .[!.]* ..?* *; do [ -e \"$f\" ] || [ -L \"$f\" ] || continue; "
|
||||
// Each line starts with LD for a link to a folder, so it opens like one.
|
||||
loop := "cd " + d + " && for f in .[!.]* ..?* *; do [ -e \"$f\" ] || [ -L \"$f\" ] || continue; " +
|
||||
"t=; [ -L \"$f\" ] && [ -d \"$f\" ] && t=LD; printf '%s|' \"$t\"; "
|
||||
switch flavour {
|
||||
case "bsd":
|
||||
return loop + "stat -f '%HT|%z|%m|%Sp|%N' -- \"$f\"; done"
|
||||
@@ -84,6 +86,11 @@ func statCommand(flavour, dir string) string {
|
||||
|
||||
// parseStatLine reads one line of statCommand's output.
|
||||
func parseStatLine(line string) (proto.FSEntry, bool) {
|
||||
// The LD mark of statCommand: empty, or LD for a link to a folder.
|
||||
mark := ""
|
||||
if i := strings.IndexByte(line, '|'); i >= 0 && (line[:i] == "" || line[:i] == "LD") {
|
||||
mark, line = line[:i], line[i+1:]
|
||||
}
|
||||
p := strings.SplitN(line, "|", 5)
|
||||
if len(p) != 5 {
|
||||
return proto.FSEntry{}, false
|
||||
@@ -94,7 +101,8 @@ func parseStatLine(line string) (proto.FSEntry, bool) {
|
||||
return proto.FSEntry{}, false
|
||||
}
|
||||
t := strings.ToLower(p[0])
|
||||
return proto.FSEntry{Name: p[4], IsDir: strings.Contains(t, "directory"), Size: size, MTime: mt, Mode: p[3]}, true
|
||||
return proto.FSEntry{Name: p[4], IsDir: strings.Contains(t, "directory") || mark == "LD", Size: size, MTime: mt, Mode: p[3],
|
||||
Link: strings.Contains(t, "link")}, true
|
||||
}
|
||||
|
||||
// parseLsLine reads a line of "ls -la" for a host with no stat: no times.
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -193,3 +195,33 @@ func TestUnpackCommandReplaces(t *testing.T) {
|
||||
t.Fatalf("error: %s", cmd)
|
||||
}
|
||||
}
|
||||
|
||||
// A link to a folder lists as a folder (marked as a link), so it opens.
|
||||
func TestParseStatLineFolderLink(t *testing.T) {
|
||||
e, ok := parseStatLine("LD|symbolic link|7|1700000000|lrwxrwxrwx|www")
|
||||
if !ok || !e.IsDir || !e.Link || e.Name != "www" {
|
||||
t.Fatalf("%+v %v", e, ok)
|
||||
}
|
||||
e, ok = parseStatLine("|regular file|5|1700000000|-rw-r--r--|a|b")
|
||||
if !ok || e.IsDir || e.Name != "a|b" {
|
||||
t.Fatalf("plain: %+v %v", e, ok)
|
||||
}
|
||||
if !strings.Contains(statCommand("gnu", "/x"), `[ -L "$f" ] && [ -d "$f" ]`) {
|
||||
t.Fatal("the command does not mark folder links")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListFolderLink(t *testing.T) {
|
||||
d := t.TempDir()
|
||||
_ = os.Mkdir(filepath.Join(d, "real"), 0o755)
|
||||
_ = os.Symlink(filepath.Join(d, "real"), filepath.Join(d, "ln"))
|
||||
r, err := fsList(d)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, e := range r.Entries {
|
||||
if e.Name == "ln" && (!e.IsDir || !e.Link) {
|
||||
t.Fatalf("link: %+v", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user