From 467d338fe42a745f5e182118bc687b7804481b55 Mon Sep 17 00:00:00 2001 From: Michael Ploujnikov Date: Sat, 9 Apr 2016 11:25:06 +0000 Subject: [PATCH] lib/model: Scanning unknown items is OK as long as the parent is known (fixes #2915) --- lib/model/model.go | 28 ++++++++++++++++++---------- lib/model/model_test.go | 30 ++++++++++++++++++------------ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/lib/model/model.go b/lib/model/model.go index 518bde61..f2550adb 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -2086,31 +2086,39 @@ func stringSliceWithout(ss []string, s string) []string { return ss } -// unifySubs takes a list of files or directories and trims them down to -// themselves or the closest parent that exists() returns true for, while -// removing duplicates and subdirectories. That is, if we have foo/ in the -// list, we don't also need foo/bar/ because that's already covered. +// The exists function is expected to return true for all known paths +// (excluding "" and ".") func unifySubs(dirs []string, exists func(dir string) bool) []string { - var subs []string + subs := trimUntilParentKnown(dirs, exists) + sort.Strings(subs) + return simplifySortedPaths(subs) +} - // Trim each item to itself or its closest known parent +func trimUntilParentKnown(dirs []string, exists func(dir string) bool) []string { + var subs []string for _, sub := range dirs { for sub != "" && sub != ".stfolder" && sub != ".stignore" { - if exists(sub) { + sub = filepath.Clean(sub) + parent := filepath.Dir(sub) + if parent == "." || exists(parent) { break } - sub = filepath.Dir(sub) + sub = parent if sub == "." || sub == string(filepath.Separator) { // Shortcut. We are going to scan the full folder, so we can // just return an empty list of subs at this point. return nil } } + if sub == "" { + return nil + } subs = append(subs, sub) } + return subs +} - // Remove any paths that are already covered by their parent - sort.Strings(subs) +func simplifySortedPaths(subs []string) []string { var cleaned []string next: for _, sub := range subs { diff --git a/lib/model/model_test.go b/lib/model/model_test.go index b003833a..dee19257 100644 --- a/lib/model/model_test.go +++ b/lib/model/model_test.go @@ -1236,50 +1236,56 @@ func TestUnifySubs(t *testing.T) { out []string // expected output }{ { - // trailing slashes are cleaned, known paths are just passed on + // 0. trailing slashes are cleaned, known paths are just passed on []string{"foo/", "bar//"}, []string{"foo", "bar"}, []string{"bar", "foo"}, // the output is sorted }, { - // "foo/bar" gets trimmed as it's covered by foo + // 1. "foo/bar" gets trimmed as it's covered by foo []string{"foo", "bar/", "foo/bar/"}, []string{"foo", "bar"}, []string{"bar", "foo"}, }, { - // "bar" gets trimmed to "" as it's unknown, - // "" gets simplified to the empty list - []string{"foo", "bar", "foo/bar"}, + // 2. "" gets simplified to the empty list; ie scan all + []string{"foo", ""}, []string{"foo"}, nil, }, { - // two independent known paths, both are kept + // 3. "foo/bar" is unknown, but it's kept + // because its parent is known + []string{"foo/bar"}, + []string{"foo"}, + []string{"foo/bar"}, + }, + { + // 4. two independent known paths, both are kept // "usr/lib" is not a prefix of "usr/libexec" []string{"usr/lib", "usr/libexec"}, - []string{"usr/lib", "usr/libexec"}, + []string{"usr", "usr/lib", "usr/libexec"}, []string{"usr/lib", "usr/libexec"}, }, { - // "usr/lib" is a prefix of "usr/lib/exec" + // 5. "usr/lib" is a prefix of "usr/lib/exec" []string{"usr/lib", "usr/lib/exec"}, - []string{"usr/lib", "usr/libexec"}, + []string{"usr", "usr/lib", "usr/libexec"}, []string{"usr/lib"}, }, { - // .stignore and .stfolder are special and are passed on + // 6. .stignore and .stfolder are special and are passed on // verbatim even though they are unknown []string{".stfolder", ".stignore"}, []string{}, []string{".stfolder", ".stignore"}, }, { - // but the presense of something else unknown forces an actual + // 7. but the presense of something else unknown forces an actual // scan []string{".stfolder", ".stignore", "foo/bar"}, []string{}, - nil, + []string{".stfolder", ".stignore", "foo"}, }, }