ronantakizawa/codereview-bench huggingface dataset:
Repository: rclone/rclone
File: cmd/bisync/operations.go (Go)

Code before:
	}
	if b.abort && !b.InGracefulShutdown {
		fs.Logf(nil, Color(terminal.RedFg, "Bisync aborted. Please try again."))
	}
	if err == nil {
		fs.Infof(nil, Color(terminal.GreenFg, "Bisync successful"))
	}
	return err
}

// runLocked performs a full bisync run
func (b *bisyncRun) runLocked(octx context.Context) (err error) {
	opt := b.opt
	path1 := bilib.FsPath(b.fs1)
	path2 := bilib.FsPath(b.fs2)

	if opt.CheckSync == CheckSyncOnly {
		fs.Infof(nil, "Validating listings for Path1 %s vs Path2 %s", quotePath(path1), quotePath(path2))
		if err = b.checkSync(b.listing1, b.listing2); err != nil {
			b.critical = true
			b.retryable = true
		}
		return err
	}

	fs.Logf(nil, "Synching Path1 %s with Path2 %s", quotePath(path1), quotePath(path2))

	if opt.DryRun {
		// In --dry-run mode, preserve original listings and save updates to the .lst-dry files
		origListing1 := b.listing1
		origListing2 := b.listing2
		b.listing1 += "-dry"
		b.listing2 += "-dry"
		b.newListing1 = b.listing1 + "-new"
		b.newListing2 = b.listing2 + "-new"
		if err := bilib.CopyFileIfExists(origListing1, b.listing1); err != nil {
			return err
		}
		if err := bilib.CopyFileIfExists(origListing2, b.listing2); err != nil {
			return err
		}
	}

	// Create second context with filters
	var fctx context.Context
	if fctx, err = b.opt.applyFilters(octx); err != nil {
		b.critical = true
		b.retryable = true
		return
	}
	b.octx = octx

Review comment (refactor):
Note that this change would affect all runs, not just dry-runs. I don't necessarily mind it (I usually use `-v` anyway), but I wonder if some users would find it unnecessary?

Code after:
	}
	if b.abort && !b.InGracefulShutdown {
		fs.Logf(nil, Color(terminal.RedFg, "Bisync aborted. Please try again."))
	}
	if err == nil {
		fs.Infof(nil, Color(terminal.GreenFg, "Bisync successful"))
	}
	return err
}

// runLocked performs a full bisync run
func (b *bisyncRun) runLocked(octx context.Context) (err error) {
	opt := b.opt
	path1 := bilib.FsPath(b.fs1)
	path2 := bilib.FsPath(b.fs2)

	if opt.CheckSync == CheckSyncOnly {
		fs.Infof(nil, "Validating listings for Path1 %s vs Path2 %s", quotePath(path1), quotePath(path2))
		if err = b.checkSync(b.listing1, b.listing2); err != nil {
			b.critical = true
			b.retryable = true
		}
		return err
	}

	fs.Infof(nil, "Synching Path1 %s with Path2 %s", quotePath(path1), quotePath(path2))

	if opt.DryRun {
		// In --dry-run mode, preserve original listings and save updates to the .lst-dry files
		origListing1 := b.listing1
		origListing2 := b.listing2
		b.listing1 += "-dry"
		b.listing2 += "-dry"
		b.newListing1 = b.listing1 + "-new"
		b.newListing2 = b.listing2 + "-new"
		if err := bilib.CopyFileIfExists(origListing1, b.listing1); err != nil {
			return err
		}
		if err := bilib.CopyFileIfExists(origListing2, b.listing2); err != nil {
			return err
		}
	}

	// Create second context with filters
	var fctx context.Context
	if fctx, err = b.opt.applyFilters(octx); err != nil {
		b.critical = true
		b.retryable = true
		return
	}
	b.octx = octx
Repository: rclone/rclone
File: cmd/bisync/log.go (Go)

Code before:
)

func (b *bisyncRun) indentf(tag, file, format string, args ...interface{}) {
	b.indent(tag, file, fmt.Sprintf(format, args...))
}

func (b *bisyncRun) indent(tag, file, msg string) {
	logf := fs.Infof
	switch {
	case tag == "ERROR":
		tag = ""
		logf = fs.Errorf
	case tag == "INFO":
		tag = ""
	case strings.HasPrefix(tag, "!"):
		tag = tag[1:]
		logf = fs.Logf
	}

	if b.opt.DryRun {
		logf = fs.Logf
	}

	if tag == "Path1" {
		tag = Color(terminal.CyanFg, "Path1")
		msg = strings.Replace(msg, "Queue copy to", Color(terminal.GreenFg, "Queue copy to Path2"), -1)
		msg = strings.Replace(msg, "Queue delete", Color(terminal.RedFg, "Queue delete from Path1"), -1)
	} else if tag == "Path2" {
		tag = Color(terminal.GreenFg, "Path2")
		msg = strings.Replace(msg, "Queue copy to", Color(terminal.CyanFg, "Queue copy to Path1"), -1)
		msg = strings.Replace(msg, "Queue delete", Color(terminal.RedFg, "Queue delete from Path2"), -1)
	} else {
		tag = Color(terminal.BlueFg, tag)
	}

	msg = Color(terminal.MagentaFg, msg)
	file = Color(terminal.CyanFg, escapePath(file, false))
	logf(nil, "- %-18s%-43s - %s", tag, msg, file)
}

// escapePath will escape control characters in path.
// It won't quote just due to backslashes on Windows.
func escapePath(path string, forceQuotes bool) string {
	path = encode(path)
	test := path
	if runtime.GOOS == "windows" {
		test = strings.ReplaceAll(path, "\\", "/")
	}
	if strconv.Quote(test) != `"`+test+`"` {
		return strconv.Quote(path)
	}

Review comment (refactor):
What this would do now is:

```
NOTICE: - Path1    Queue copy to Path2 Path2 - file.txt
```

So the destination path is getting duplicated.

I'm not sure that any change is necessary here, since the info is already in the `msg`.

Code after:
)

func (b *bisyncRun) indentf(tag, file, format string, args ...interface{}) {
	b.indent(tag, file, fmt.Sprintf(format, args...))
}

func (b *bisyncRun) indent(tag, file, msg string) {
	logf := fs.Infof
	switch {
	case tag == "ERROR":
		tag = ""
		logf = fs.Errorf
	case tag == "INFO":
		tag = ""
	case strings.HasPrefix(tag, "!"):
		tag = tag[1:]
		logf = fs.Logf
	}

	if b.opt.DryRun {
		logf = fs.Logf
	}

	if tag == "Path1" {
		tag = Color(terminal.CyanFg, "Path1")
	} else {
		tag = Color(terminal.BlueFg, tag)
	}
	msg = Color(terminal.MagentaFg, msg)
	msg = strings.Replace(msg, "Queue copy to", Color(terminal.GreenFg, "Queue copy to"), -1)
	msg = strings.Replace(msg, "Queue delete", Color(terminal.RedFg, "Queue delete"), -1)
	file = Color(terminal.CyanFg, escapePath(file, false))
	logf(nil, "- %-18s%-43s - %s", tag, msg, file)
}

// escapePath will escape control characters in path.
// It won't quote just due to backslashes on Windows.
func escapePath(path string, forceQuotes bool) string {
	path = encode(path)
	test := path
	if runtime.GOOS == "windows" {
		test = strings.ReplaceAll(path, "\\", "/")
	}
	if strconv.Quote(test) != `"`+test+`"` {
		return strconv.Quote(path)
	}
	if forceQuotes {
		return `"` + path + `"`
	}
	return path
}
Repository: rclone/rclone
File: cmd/bisync/log.go (Go)

Code before:
	b.indent(tag, file, fmt.Sprintf(format, args...))
}

func (b *bisyncRun) indent(tag, file, msg string) {
	logf := fs.Infof
	switch {
	case tag == "ERROR":
		tag = ""
		logf = fs.Errorf
	case tag == "INFO":
		tag = ""
	case strings.HasPrefix(tag, "!"):
		tag = tag[1:]
		logf = fs.Logf
	}

	if b.opt.DryRun {
		logf = fs.Logf
	}

	if tag == "Path1" {
		tag = Color(terminal.CyanFg, "Path1")
		msg = strings.Replace(msg, "Queue copy to", Color(terminal.GreenFg, "Queue copy to Path2"), -1)
		msg = strings.Replace(msg, "Queue delete", Color(terminal.RedFg, "Queue delete from Path1"), -1)
	} else if tag == "Path2" {
		tag = Color(terminal.GreenFg, "Path2")
		msg = strings.Replace(msg, "Queue copy to", Color(terminal.CyanFg, "Queue copy to Path1"), -1)
		msg = strings.Replace(msg, "Queue delete", Color(terminal.RedFg, "Queue delete from Path2"), -1)
	} else {
		tag = Color(terminal.BlueFg, tag)
	}

	msg = Color(terminal.MagentaFg, msg)
	file = Color(terminal.CyanFg, escapePath(file, false))
	logf(nil, "- %-18s%-43s - %s", tag, msg, file)
}

// escapePath will escape control characters in path.
// It won't quote just due to backslashes on Windows.
func escapePath(path string, forceQuotes bool) string {
	path = encode(path)
	test := path
	if runtime.GOOS == "windows" {
		test = strings.ReplaceAll(path, "\\", "/")
	}
	if strconv.Quote(test) != `"`+test+`"` {
		return strconv.Quote(path)
	}
	if forceQuotes {
		return `"` + path + `"`
	}

Review comment (suggestion):
We currently try to use green/yellow/red for their universal meanings of new/modified/deleted or success/warning/error, so I would choose a different color (or leave the default of `BlueFg`).

It should also avoid `CyanFg` (already used for Path1) and `MagentaFg` (already used as the default `msg` color). So that is why `BlueFg` was one of the only options left 🙂 

Code after:
	b.indent(tag, file, fmt.Sprintf(format, args...))
}

func (b *bisyncRun) indent(tag, file, msg string) {
	logf := fs.Infof
	switch {
	case tag == "ERROR":
		tag = ""
		logf = fs.Errorf
	case tag == "INFO":
		tag = ""
	case strings.HasPrefix(tag, "!"):
		tag = tag[1:]
		logf = fs.Logf
	}

	if b.opt.DryRun {
		logf = fs.Logf
	}

	if tag == "Path1" {
		tag = Color(terminal.CyanFg, "Path1")
	} else {
		tag = Color(terminal.BlueFg, tag)
	}
	msg = Color(terminal.MagentaFg, msg)
	msg = strings.Replace(msg, "Queue copy to", Color(terminal.GreenFg, "Queue copy to"), -1)
	msg = strings.Replace(msg, "Queue delete", Color(terminal.RedFg, "Queue delete"), -1)
	file = Color(terminal.CyanFg, escapePath(file, false))
	logf(nil, "- %-18s%-43s - %s", tag, msg, file)
}

// escapePath will escape control characters in path.
// It won't quote just due to backslashes on Windows.
func escapePath(path string, forceQuotes bool) string {
	path = encode(path)
	test := path
	if runtime.GOOS == "windows" {
		test = strings.ReplaceAll(path, "\\", "/")
	}
	if strconv.Quote(test) != `"`+test+`"` {
		return strconv.Quote(path)
	}
	if forceQuotes {
		return `"` + path + `"`
	}
	return path
}

func quotePath(path string) string {
	return escapePath(path, true)
Repository: rclone/rclone
File: fs/sync/sync.go (Go)

Code before:
	return newDst
}

// Set the modtimes for directories
func (s *syncCopyMove) setDelayedDirModTimes(ctx context.Context) error {
	s.setDirModTimeMu.Lock()
	defer s.setDirModTimeMu.Unlock()

	// Timestamp all directories at the same level in parallel, deepest first
	// We do this by iterating the slice multiple times to save memory
	// There could be a lot of directories in this slice.
	var errCount = errcount.New()
	for level := s.setDirModTimesMaxLevel; level >= 0; level-- {
		g, gCtx := errgroup.WithContext(ctx)
		g.SetLimit(s.ci.Checkers)
		for _, item := range s.setDirModTimes {
			if item.level != level {
				continue
			}
			// End early if error
			if gCtx.Err() != nil {
				break
			}
			item := item
			g.Go(func() error {
				if _, ok := s.modifiedDirs[filepath.ToSlash(item.dir)]; !ok {
					return nil
				}
				_, err := operations.SetDirModTime(gCtx, s.fdst, item.dst, item.dir, item.modTime)
				if err != nil {
					err = fs.CountError(err)
					fs.Errorf(item.dir, "Failed to timestamp directory: %v", err)
					errCount.Add(err)
				}
				return nil // don't return errors, just count them
			})
		}
		err := g.Wait()
		if err != nil {
			return err
		}
	}
	return errCount.Err("failed to set directory modtime")
}

// SrcOnly have an object which is in the source only
func (s *syncCopyMove) SrcOnly(src fs.DirEntry) (recurse bool) {
	if s.deleteMode == fs.DeleteModeOnly {
		return false
	}
	switch x := src.(type) {

Review comment (performance):
It would be slightly more efficient to do this before `g.Go` so we don't start go routines we don't need to.

Code after:
			level:   level,
		})
		s.setDirModTimeMu.Unlock()
		fs.Debugf(nil, "Added delayed dir = %q, newDst=%v", dir, newDst)
	}
	s.processError(err)
	if err != nil {
		return nil
	}
	return newDst
}

// Set the modtimes for directories
func (s *syncCopyMove) setDelayedDirModTimes(ctx context.Context) error {
	s.setDirModTimeMu.Lock()
	defer s.setDirModTimeMu.Unlock()

	// Timestamp all directories at the same level in parallel, deepest first
	// We do this by iterating the slice multiple times to save memory
	// There could be a lot of directories in this slice.
	errCount := errcount.New()
	for level := s.setDirModTimesMaxLevel; level >= 0; level-- {
		g, gCtx := errgroup.WithContext(ctx)
		g.SetLimit(s.ci.Checkers)
		for _, item := range s.setDirModTimes {
			if item.level != level {
				continue
			}
			// End early if error
			if gCtx.Err() != nil {
				break
			}
			item := item
			if _, ok := s.modifiedDirs[item.dir]; !ok {
				continue
			}
			g.Go(func() error {
				_, err := operations.SetDirModTime(gCtx, s.fdst, item.dst, item.dir, item.modTime)
				if err != nil {
					err = fs.CountError(err)
					fs.Errorf(item.dir, "Failed to timestamp directory: %v", err)
					errCount.Add(err)
				}
				return nil // don't return errors, just count them
			})
		}
		err := g.Wait()
		if err != nil {
			return err
		}
	}
Repository: rclone/rclone
File: fs/parseduration.go (Go)

Code before:
			break
		}
	}

	return readableString
}

// Set a Duration
func (d *Duration) Set(s string) error {
	duration, err := ParseDuration(s)
	if err != nil {
		return err
	}
	*d = Duration(duration)
	return nil
}

// Type of the value
func (d Duration) Type() string {
	return "Duration"
}

// UnmarshalJSON makes sure the value can be parsed as a string or integer in JSON
func (d *Duration) UnmarshalJSON(in []byte) error {
	// Check if the input is a string value.
	if in[0] == '"' {
		strVal := string(in[1 : len(in)-1]) // Remove the quotes
		// Handle the special "off" case.
		if strVal == "off" {
			*d = DurationOff
			return nil
		}
		// Attempt to parse the string as a duration.
		parsedDuration, err := ParseDuration(strVal)
		if err != nil {
			return err
		}
		*d = Duration(parsedDuration)
		return nil
	}
	// Handle numeric values.
	var i int64
	err := json.Unmarshal(in, &i)
	if err != nil {
		return err
	}
	*d = Duration(i)
	return nil
}

// Scan implements the fmt.Scanner interface

Review comment (suggestion):
You should check `len(in) > 0` before checking `in[0]`.  In fact for this if branch you want `len(in) >= 2` I think.

So I think this should say

```go
if len(in) >= 2 && in[0] == '"' && in[len(in)-1] == '"' {
```

Code after:
			break
		}
	}

	return readableString
}

// Set a Duration
func (d *Duration) Set(s string) error {
	duration, err := ParseDuration(s)
	if err != nil {
		return err
	}
	*d = Duration(duration)
	return nil
}

// Type of the value
func (d Duration) Type() string {
	return "Duration"
}

// UnmarshalJSON makes sure the value can be parsed as a string or integer in JSON
func (d *Duration) UnmarshalJSON(in []byte) error {
	// Check if the input is a string value.
	if len(in) >= 2 && in[0] == '"' && in[len(in)-1] == '"' {
		strVal := string(in[1 : len(in)-1]) // Remove the quotes

		// Attempt to parse the string as a duration.
		parsedDuration, err := ParseDuration(strVal)
		if err != nil {
			return err
		}
		*d = Duration(parsedDuration)
		return nil
	}
	// Handle numeric values.
	var i int64
	err := json.Unmarshal(in, &i)
	if err != nil {
		return err
	}
	*d = Duration(i)
	return nil
}

// Scan implements the fmt.Scanner interface
func (d *Duration) Scan(s fmt.ScanState, ch rune) error {
	token, err := s.Token(true, func(rune) bool { return true })
	if err != nil {
		return err
Repository: rclone/rclone
File: fs/parseduration.go (Go)

Code before:

	return readableString
}

// Set a Duration
func (d *Duration) Set(s string) error {
	duration, err := ParseDuration(s)
	if err != nil {
		return err
	}
	*d = Duration(duration)
	return nil
}

// Type of the value
func (d Duration) Type() string {
	return "Duration"
}

// UnmarshalJSON makes sure the value can be parsed as a string or integer in JSON
func (d *Duration) UnmarshalJSON(in []byte) error {
	// Check if the input is a string value.
	if in[0] == '"' {
		strVal := string(in[1 : len(in)-1]) // Remove the quotes
		// Handle the special "off" case.
		if strVal == "off" {
			*d = DurationOff
			return nil
		}
		// Attempt to parse the string as a duration.
		parsedDuration, err := ParseDuration(strVal)
		if err != nil {
			return err
		}
		*d = Duration(parsedDuration)
		return nil
	}
	// Handle numeric values.
	var i int64
	err := json.Unmarshal(in, &i)
	if err != nil {
		return err
	}
	*d = Duration(i)
	return nil
}

// Scan implements the fmt.Scanner interface
func (d *Duration) Scan(s fmt.ScanState, ch rune) error {
	token, err := s.Token(true, func(rune) bool { return true })
	if err != nil {

Review comment (suggestion):
I'm pretty sure ParseDuration parses `off` so this shouldn't be needed

Code after:

	return readableString
}

// Set a Duration
func (d *Duration) Set(s string) error {
	duration, err := ParseDuration(s)
	if err != nil {
		return err
	}
	*d = Duration(duration)
	return nil
}

// Type of the value
func (d Duration) Type() string {
	return "Duration"
}

// UnmarshalJSON makes sure the value can be parsed as a string or integer in JSON
func (d *Duration) UnmarshalJSON(in []byte) error {
	// Check if the input is a string value.
	if len(in) >= 2 && in[0] == '"' && in[len(in)-1] == '"' {
		strVal := string(in[1 : len(in)-1]) // Remove the quotes

		// Attempt to parse the string as a duration.
		parsedDuration, err := ParseDuration(strVal)
		if err != nil {
			return err
		}
		*d = Duration(parsedDuration)
		return nil
	}
	// Handle numeric values.
	var i int64
	err := json.Unmarshal(in, &i)
	if err != nil {
		return err
	}
	*d = Duration(i)
	return nil
}

// Scan implements the fmt.Scanner interface
func (d *Duration) Scan(s fmt.ScanState, ch rune) error {
	token, err := s.Token(true, func(rune) bool { return true })
	if err != nil {
		return err
	}
	return d.Set(string(token))
}
Repository: rclone/rclone
File: backend/memory/memory.go (Go)

Code before:
	}
	if directory != "" {
		directory += "/"
	}
	b := buckets.getBucket(bucket)
	if b == nil {
		return fs.ErrorDirNotFound
	}
	b.mu.RLock()
	defer b.mu.RUnlock()
	dirs := make(map[string]struct{})
	for absPath, od := range b.objects {
		if strings.HasPrefix(absPath, directory) {
			remote := absPath[len(prefix):]
			if !recurse {
				localPath := absPath[len(directory):]
				slash := strings.IndexRune(localPath, '/')
				if slash >= 0 {
					// send a directory if have a slash
					dir := directory + localPath[:slash]
					if addBucket {
						dir = path.Join(bucket, dir)
					}
					_, found := dirs[dir]
					if !found {
						b.mu.RUnlock()
						err = fn(dir, fs.NewDir(dir, time.Time{}), true)
						b.mu.RLock()
						if err != nil {
							return err
						}
						dirs[dir] = struct{}{}
					}
					continue // don't send this file if not recursing
				}
			}
			// send an object
			if addBucket {
				remote = path.Join(bucket, remote)
			}
			b.mu.RUnlock()
			err = fn(remote, f.newObject(remote, od), false)
			b.mu.RLock()
			if err != nil {
				return err
			}
		}
	}
	return nil
}


Review comment (suggestion):
This is ranging over `b.objects` here - I don't think it is safe to release the lock as it gives the opportunity for other methods to grab it and mutate `b.objects`.

Code after:
	if directory != "" {
		directory += "/"
	}
	b := buckets.getBucket(bucket)
	if b == nil {
		return fs.ErrorDirNotFound
	}
	b.mu.RLock()
	defer b.mu.RUnlock()
	dirs := make(map[string]struct{})
	for absPath, od := range b.objects {
		if strings.HasPrefix(absPath, directory) {
			remote := absPath[len(prefix):]
			if !recurse {
				localPath := absPath[len(directory):]
				slash := strings.IndexRune(localPath, '/')
				if slash >= 0 {
					// send a directory if have a slash
					dir := directory + localPath[:slash]
					if addBucket {
						dir = path.Join(bucket, dir)
					}
					_, found := dirs[dir]
					if !found {
						err = fn(dir, fs.NewDir(dir, time.Time{}), true)
						if err != nil {
							return err
						}
						dirs[dir] = struct{}{}
					}
					continue // don't send this file if not recursing
				}
			}
			// send an object
			if addBucket {
				remote = path.Join(bucket, remote)
			}
			err = fn(remote, f.newObject(remote, od), false)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// listDir lists the bucket to the entries
func (f *Fs) listDir(ctx context.Context, bucket, directory, prefix string, addBucket bool) (entries fs.DirEntries, err error) {
	// List the objects and directories
	err = f.list(ctx, bucket, directory, prefix, addBucket, false, func(remote string, entry fs.DirEntry, isDirectory bool) error {
		entries = append(entries, entry)
Repository: rclone/rclone
File: backend/webdav/webdav.go (Go)

Code before:
		}

		// Check OK
		if !item.Props.StatusOK() {
			fs.Debugf(remote, "Ignoring item with bad status %q", item.Props.Status)
			continue
		}

		if isDir {
			if filesOnly {
				continue
			}
		} else {
			if directoriesOnly {
				continue
			}
		}
		if f.opt.ExcludeShares {
			// https: //owncloud.dev/apis/http/webdav/#supported-webdav-properties
			if strings.Contains(item.Props.Permissions, "S") {
				continue
			}
		}
		if f.opt.ExcludeMounts {
			// https: //owncloud.dev/apis/http/webdav/#supported-webdav-properties
			if strings.Contains(item.Props.Permissions, "S") {
				continue
			}
		}
		// 	item.Name = restoreReservedChars(item.Name)
		if fn(remote, isDir, &item.Props) {
			found = true
			break
		}
	}
	return
}

// List the objects and directories in dir into entries.  The
// entries can be returned in any order but should be for a
// complete directory.
//
// dir should be "" to list the root, and should not have
// trailing slashes.
//
// This should return ErrDirNotFound if the directory isn't
// found.
func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
	var iErr error
	_, err = f.listAll(ctx, dir, false, false, defaultDepth, func(remote string, isDir bool, info *api.Prop) bool {
		if isDir {

Review comment (security):
```suggestion
			if strings.Contains(item.Props.Permissions, "M") {
```

Typo?

Code after:
		}

		// Check OK
		if !item.Props.StatusOK() {
			fs.Debugf(remote, "Ignoring item with bad status %q", item.Props.Status)
			continue
		}

		if isDir {
			if filesOnly {
				continue
			}
		} else {
			if directoriesOnly {
				continue
			}
		}
		if f.opt.ExcludeShares {
			// https: //owncloud.dev/apis/http/webdav/#supported-webdav-properties
			if strings.Contains(item.Props.Permissions, "S") {
				continue
			}
		}
		if f.opt.ExcludeMounts {
			// https: //owncloud.dev/apis/http/webdav/#supported-webdav-properties
			if strings.Contains(item.Props.Permissions, "M") {
				continue
			}
		}
		// 	item.Name = restoreReservedChars(item.Name)
		if fn(remote, isDir, &item.Props) {
			found = true
			break
		}
	}
	return
}

// List the objects and directories in dir into entries.  The
// entries can be returned in any order but should be for a
// complete directory.
//
// dir should be "" to list the root, and should not have
// trailing slashes.
//
// This should return ErrDirNotFound if the directory isn't
// found.
func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
	var iErr error
	_, err = f.listAll(ctx, dir, false, false, defaultDepth, func(remote string, isDir bool, info *api.Prop) bool {
		if isDir {