diff --git a/ftp.go b/ftp.go index 5bd19d2..4d2ec86 100644 --- a/ftp.go +++ b/ftp.go @@ -59,10 +59,11 @@ type dialOptions struct { // Entry describes a file and is returned by List(). type Entry struct { - Name string - Type EntryType - Size uint64 - Time time.Time + Name string + Target string // target of symbolic link + Type EntryType + Size uint64 + Time time.Time } // Response represents a data-connection diff --git a/parse.go b/parse.go index 181c020..620401e 100644 --- a/parse.go +++ b/parse.go @@ -135,6 +135,12 @@ func parseLsListLine(line string, now time.Time, loc *time.Location) (*Entry, er e.Type = EntryTypeFolder case 'l': e.Type = EntryTypeLink + + // Split link name and target + if i := strings.Index(e.Name, " -> "); i > 0 { + e.Target = e.Name[i+4:] + e.Name = e.Name[:i] + } default: return nil, errUnknownListEntryType } diff --git a/parse_test.go b/parse_test.go index 20a8c7e..1b5ea8f 100644 --- a/parse_test.go +++ b/parse_test.go @@ -23,6 +23,12 @@ type line struct { time time.Time } +type symlinkLine struct { + line string + name string + target string +} + type unsupportedLine struct { line string err error @@ -35,7 +41,7 @@ var listTests = []line{ {"-rw-r--r-- 1 marketwired marketwired 12016 Mar 16 2016 2016031611G087802-001.newsml", "2016031611G087802-001.newsml", 12016, EntryTypeFile, newTime(2016, time.March, 16)}, {"-rwxr-xr-x 3 110 1002 1234567 Dec 02 2009 fileName", "fileName", 1234567, EntryTypeFile, newTime(2009, time.December, 2)}, - {"lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin", "bin -> usr/bin", 0, EntryTypeLink, newTime(thisYear, time.January, 25, 0, 17)}, + {"lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin", "bin", 0, EntryTypeLink, newTime(thisYear, time.January, 25, 0, 17)}, // Another ls style {"drwxr-xr-x folder 0 Aug 15 05:49 !!!-Tipp des Haus!", "!!!-Tipp des Haus!", 0, EntryTypeFolder, newTime(thisYear, time.August, 15, 5, 49)}, @@ -74,6 +80,11 @@ var listTests = []line{ {"-rwxrw-r--+ 1 521 101 2080 May 21 10:53 data.csv", "data.csv", 2080, EntryTypeFile, newTime(thisYear, time.May, 21, 10, 53)}, } +var listTestsSymlink = []symlinkLine{ + {"lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin", "bin", "usr/bin"}, + {"lrwxrwxrwx 1 0 1001 27 Jul 07 2017 R-3.4.0.pkg -> el-capitan/base/R-3.4.0.pkg", "R-3.4.0.pkg", "el-capitan/base/R-3.4.0.pkg"}, +} + // Not supported, we expect a specific error message var listTestsFail = []unsupportedLine{ {"d [R----F--] supervisor 512 Jan 16 18:53 login", errUnsupportedListLine}, @@ -111,6 +122,25 @@ func TestParseValidListLine(t *testing.T) { } } +func TestParseSymlinks(t *testing.T) { + for _, lt := range listTestsSymlink { + entry, err := parseListLine(lt.line, now, time.UTC) + if err != nil { + t.Errorf("parseListLine(%v) returned err = %v", lt.line, err) + continue + } + if entry.Name != lt.name { + t.Errorf("parseListLine(%v).Name = '%v', want '%v'", lt.line, entry.Name, lt.name) + } + if entry.Target != lt.target { + t.Errorf("parseListLine(%v).Target = '%v', want '%v'", lt.line, entry.Target, lt.target) + } + if entry.Type != EntryTypeLink { + t.Errorf("parseListLine(%v).EntryType = %v, want EntryTypeLink", lt.line, entry.Type) + } + } +} + func TestParseUnsupportedListLine(t *testing.T) { for _, lt := range listTestsFail { t.Run(fmt.Sprintf("parseListLine(%v)", lt.line), func(t *testing.T) {