drive: handle shared drives with leading/trailing space in name (related to #6618)

This commit is contained in:
albertony 2022-12-12 20:05:12 +01:00
parent 8b9f3bbe29
commit 5a59b49b6b
3 changed files with 51 additions and 30 deletions

View file

@ -18,7 +18,6 @@ import (
"net/http" "net/http"
"os" "os"
"path" "path"
"regexp"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -3431,13 +3430,12 @@ func (f *Fs) Command(ctx context.Context, name string, arg []string, opt map[str
if err != nil { if err != nil {
return nil, err return nil, err
} }
re := regexp.MustCompile(`[^\w\p{L}\p{N}. -]+`)
if _, ok := opt["config"]; ok { if _, ok := opt["config"]; ok {
lines := []string{} lines := []string{}
upstreams := []string{} upstreams := []string{}
names := make(map[string]struct{}, len(drives)) names := make(map[string]struct{}, len(drives))
for i, drive := range drives { for i, drive := range drives {
name := re.ReplaceAllString(drive.Name, "_") name := fspath.MakeConfigName(drive.Name)
for { for {
if _, found := names[name]; !found { if _, found := names[name]; !found {
break break

View file

@ -13,7 +13,8 @@ import (
) )
const ( const (
configNameRe = `[\w\p{L}\p{N}.]+(?:[ -]+[\w\p{L}\p{N}.-]+)*` // don't allow it to start with `-` as it complicates usage (#4261) configNameRe = `[\w\p{L}\p{N}.]+(?:[ -]+[\w\p{L}\p{N}.-]+)*` // May contain Unicode numbers and letters, as well as `_`, `-`, `.` and space, but not start with `-` (it complicates usage, see #4261) or space, and not end with space
illegalPartOfConfigNameRe = `^[ -]+|[^\w\p{L}\p{N}. -]+|[ ]+$`
) )
var ( var (
@ -32,6 +33,9 @@ var (
// configNameMatcher is a pattern to match an rclone config name // configNameMatcher is a pattern to match an rclone config name
configNameMatcher = regexp.MustCompile(`^` + configNameRe + `$`) configNameMatcher = regexp.MustCompile(`^` + configNameRe + `$`)
// illegalPartOfConfigNameMatcher is a pattern to match a sequence of characters not allowed in an rclone config name
illegalPartOfConfigNameMatcher = regexp.MustCompile(illegalPartOfConfigNameRe)
// remoteNameMatcher is a pattern to match an rclone remote name at the start of a config // remoteNameMatcher is a pattern to match an rclone remote name at the start of a config
remoteNameMatcher = regexp.MustCompile(`^:?` + configNameRe + `(?::$|,)`) remoteNameMatcher = regexp.MustCompile(`^:?` + configNameRe + `(?::$|,)`)
) )
@ -44,6 +48,21 @@ func CheckConfigName(configName string) error {
return nil return nil
} }
// MakeConfigName makes an input into something legal to be used as a config name.
// Returns a string where any sequences of illegal characters are replaced with
// a single underscore. If the input is already valid as a config name, it is
// returned unchanged. If the input is an empty string, a single underscore is
// returned.
func MakeConfigName(name string) string {
if name == "" {
return "_"
}
if configNameMatcher.MatchString(name) {
return name
}
return illegalPartOfConfigNameMatcher.ReplaceAllString(name, "_")
}
// checkRemoteName returns an error if remoteName is invalid // checkRemoteName returns an error if remoteName is invalid
func checkRemoteName(remoteName string) error { func checkRemoteName(remoteName string) error {
if remoteName == ":" || remoteName == "::" { if remoteName == ":" || remoteName == "::" {

View file

@ -19,34 +19,38 @@ var (
func TestCheckConfigName(t *testing.T) { func TestCheckConfigName(t *testing.T) {
for _, test := range []struct { for _, test := range []struct {
in string in string
want error problem error
fixed string
}{ }{
{"remote", nil}, {"remote", nil, "remote"},
{"REMOTE", nil}, {"REMOTE", nil, "REMOTE"},
{"", errInvalidCharacters}, {"", errInvalidCharacters, "_"},
{":remote:", errInvalidCharacters}, {":remote:", errInvalidCharacters, "_remote_"},
{"remote:", errInvalidCharacters}, {"remote:", errInvalidCharacters, "remote_"},
{"rem:ote", errInvalidCharacters}, {"rem:ote", errInvalidCharacters, "rem_ote"},
{"rem/ote", errInvalidCharacters}, {"rem/ote", errInvalidCharacters, "rem_ote"},
{"rem\\ote", errInvalidCharacters}, {"rem\\ote", errInvalidCharacters, "rem_ote"},
{"[remote", errInvalidCharacters}, {"[remote", errInvalidCharacters, "_remote"},
{"*", errInvalidCharacters}, {"*", errInvalidCharacters, "_"},
{"-remote", errInvalidCharacters}, {"-remote", errInvalidCharacters, "_remote"},
{"r-emote-", nil}, {"r-emote-", nil, "r-emote-"},
{"_rem_ote_", nil}, {"---rem:::ote???", errInvalidCharacters, "_rem_ote_"},
{".", nil}, {"_rem_ote_", nil, "_rem_ote_"},
{"..", nil}, {".", nil, "."},
{".r.e.m.o.t.e.", nil}, {"..", nil, ".."},
{"rem ote", nil}, {".r.e.m.o.t.e.", nil, ".r.e.m.o.t.e."},
{"blåbær", nil}, {"rem ote", nil, "rem ote"},
{"chữ Quốc ngữ", nil}, {"blåbær", nil, "blåbær"},
{"remote ", errInvalidCharacters}, {"chữ Quốc ngữ", nil, "chữ Quốc ngữ"},
{" remote", errInvalidCharacters}, {"remote ", errInvalidCharacters, "remote_"},
{" remote ", errInvalidCharacters}, {" remote", errInvalidCharacters, "_remote"},
{" remote ", errInvalidCharacters, "_remote_"},
} { } {
got := CheckConfigName(test.in) problem := CheckConfigName(test.in)
assert.Equal(t, test.want, got, test.in) assert.Equal(t, test.problem, problem, test.in)
fixed := MakeConfigName(test.in)
assert.Equal(t, test.fixed, fixed, test.in)
} }
} }