Repository: zyedidia/micro
File: internal/util/util.go (Go)

Code before:
	// no lines/columns were specified in the path, return just the path with no cursor location
	if len(match) == 0 {
		return path, nil
	} else if match[len(match)-1] != "" {
		// if the last capture group match isn't empty then both line and column were provided
		return match[1], match[2:]
	}
	// if it was empty, then only a line was provided, so default to column 0
	return match[1], []string{match[2], "0"}
}

// GetModTime returns the last modification time for a given file
func GetModTime(path string) (time.Time, error) {
	info, err := os.Stat(path)
	if err != nil {
		return time.Now(), err
	}
	return info.ModTime(), nil
}

func CreateTempBackupName(path string) string {
	path += ".micro-backup"
	name := filepath.Base(path)
	if len(name) > fileNameLengthLimit {
		dir := filepath.Dir(path)
		path = filepath.Join(dir, HashStringMd5(path))
	}
	return path
}

func HashStringMd5(str string) string {
	return fmt.Sprintf("%x", md5.Sum([]byte(str)))
}

// EscapePathUrl encodes the path in URL query form
func EscapePathUrl(path string) string {
	return url.QueryEscape(filepath.ToSlash(path))
}

// EscapePathLegacy replaces every path separator in a given path with a %
func EscapePathLegacy(path string) string {
	path = filepath.ToSlash(path)
	if runtime.GOOS == "windows" {
		// ':' is not valid in a path name on Windows but is ok on Unix
		path = strings.ReplaceAll(path, ":", "%")
	}
	return strings.ReplaceAll(path, "/", "%")
}

// DetermineEscapePath escapes a path, determining whether it should be escaped
// using URL encoding (preferred, since it encodes unambiguously) or

Review comment (security):
Just the hash, without any extension?

Can't we just adjust `DetermineEscapePath()` to use `fileNameLengthLimit - len(".micro-backup")` instead of `fileNameLengthLimit` instead?

Code after:
	// no lines/columns were specified in the path, return just the path with no cursor location
	if len(match) == 0 {
		return path, nil
	} else if match[len(match)-1] != "" {
		// if the last capture group match isn't empty then both line and column were provided
		return match[1], match[2:]
	}
	// if it was empty, then only a line was provided, so default to column 0
	return match[1], []string{match[2], "0"}
}

// GetModTime returns the last modification time for a given file
func GetModTime(path string) (time.Time, error) {
	info, err := os.Stat(path)
	if err != nil {
		return time.Now(), err
	}
	return info.ModTime(), nil
}

func HashStringMd5(str string) string {
	return fmt.Sprintf("%x", md5.Sum([]byte(str)))
}

// EscapePathUrl encodes the path in URL query form
func EscapePathUrl(path string) string {
	return url.QueryEscape(filepath.ToSlash(path))
}

// EscapePathLegacy replaces every path separator in a given path with a %
func EscapePathLegacy(path string) string {
	path = filepath.ToSlash(path)
	if runtime.GOOS == "windows" {
		// ':' is not valid in a path name on Windows but is ok on Unix
		path = strings.ReplaceAll(path, ":", "%")
	}
	return strings.ReplaceAll(path, "/", "%")
}

// DetermineEscapePath escapes a path, determining whether it should be escaped
// using URL encoding (preferred, since it encodes unambiguously) or
// legacy encoding with '%' (for backward compatibility, if the legacy-escaped
// path exists in the given directory).
// In case the length of the escaped path (plus the backup extension) exceeds
// the filename length limit, a hash of the path is returned instead. In such
// case the second return value is the name of a file the original path should
// be saved to (since the original path cannot be derived from its hash).
// Otherwise the second return value is an empty string.
func DetermineEscapePath(dir string, path string) (string, string) {
	url := filepath.Join(dir, EscapePathUrl(path))
	if _, err := os.Stat(url); err == nil {