github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/filesystem/normalize.go (about) 1 package filesystem 2 3 import ( 4 "fmt" 5 "os" 6 "os/user" 7 "path/filepath" 8 ) 9 10 // tildeExpand attempts tilde expansion of paths beginning with ~/ or 11 // ~<username>/. On Windows, it additionally supports ~\ and ~<username>\. 12 func tildeExpand(path string) (string, error) { 13 // Only process relevant paths. 14 if path == "" || path[0] != '~' { 15 return path, nil 16 } 17 18 // Find the first character in the path that's considered a path separator 19 // on the platform. Path seperators are always single-byte, so we can safely 20 // loop over the path's bytes. 21 pathSeparatorIndex := -1 22 for i := 0; i < len(path); i++ { 23 if os.IsPathSeparator(path[i]) { 24 pathSeparatorIndex = i 25 break 26 } 27 } 28 29 // Divide the path into the "username" portion and the "subpath" portion - 30 // i.e. those portions coming before and after the separator, respectively. 31 var username string 32 var remaining string 33 if pathSeparatorIndex > 0 { 34 username = path[1:pathSeparatorIndex] 35 remaining = path[pathSeparatorIndex+1:] 36 } else { 37 username = path[1:] 38 } 39 40 // Compute the relevant home directory. If the username is empty, then we 41 // use the current user's home directory, otherwise we need to do a lookup. 42 var homeDirectory string 43 if username == "" { 44 if h, err := os.UserHomeDir(); err != nil { 45 return "", fmt.Errorf("unable to compute path to home directory: %w", err) 46 } else { 47 homeDirectory = h 48 } 49 } else { 50 if u, err := user.Lookup(username); err != nil { 51 return "", fmt.Errorf("unable to lookup user: %w", err) 52 } else { 53 homeDirectory = u.HomeDir 54 } 55 } 56 57 // Compute the full path. 58 return filepath.Join(homeDirectory, remaining), nil 59 } 60 61 // Normalize normalizes a path, expanding home directory tildes, converting it 62 // to an absolute path, and cleaning the result. 63 func Normalize(path string) (string, error) { 64 // Expand any leading tilde. 65 path, err := tildeExpand(path) 66 if err != nil { 67 return "", fmt.Errorf("unable to perform tilde expansion: %w", err) 68 } 69 70 // Convert to an absolute path. This will also invoke filepath.Clean. 71 path, err = filepath.Abs(path) 72 if err != nil { 73 return "", fmt.Errorf("unable to compute absolute path: %w", err) 74 } 75 76 // Success. 77 return path, nil 78 }