github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/util/path.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 package util 7 8 import ( 9 "errors" 10 "net/url" 11 "os" 12 "path" 13 "path/filepath" 14 "regexp" 15 "runtime" 16 "strings" 17 ) 18 19 // EnsureAbsolutePath ensure that a path is absolute, making it 20 // relative to absoluteBase if necessary 21 func EnsureAbsolutePath(path, absoluteBase string) string { 22 if filepath.IsAbs(path) { 23 return path 24 } 25 return filepath.Join(absoluteBase, path) 26 } 27 28 const notRegularFileMode os.FileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular 29 30 // GetDirectorySize returns the disk consumption for a given path 31 func GetDirectorySize(path string) (int64, error) { 32 var size int64 33 err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { 34 if info != nil && (info.Mode()¬RegularFileMode) == 0 { 35 size += info.Size() 36 } 37 return err 38 }) 39 return size, err 40 } 41 42 // IsDir returns true if given path is a directory, 43 // or returns false when it's a file or does not exist. 44 func IsDir(dir string) (bool, error) { 45 f, err := os.Stat(dir) 46 if err == nil { 47 return f.IsDir(), nil 48 } 49 if os.IsNotExist(err) { 50 return false, nil 51 } 52 return false, err 53 } 54 55 // IsFile returns true if given path is a file, 56 // or returns false when it's a directory or does not exist. 57 func IsFile(filePath string) (bool, error) { 58 f, err := os.Stat(filePath) 59 if err == nil { 60 return !f.IsDir(), nil 61 } 62 if os.IsNotExist(err) { 63 return false, nil 64 } 65 return false, err 66 } 67 68 // IsExist checks whether a file or directory exists. 69 // It returns false when the file or directory does not exist. 70 func IsExist(path string) (bool, error) { 71 _, err := os.Stat(path) 72 if err == nil || os.IsExist(err) { 73 return true, nil 74 } 75 if os.IsNotExist(err) { 76 return false, nil 77 } 78 return false, err 79 } 80 81 func statDir(dirPath, recPath string, includeDir, isDirOnly, followSymlinks bool) ([]string, error) { 82 dir, err := os.Open(dirPath) 83 if err != nil { 84 return nil, err 85 } 86 defer dir.Close() 87 88 fis, err := dir.Readdir(0) 89 if err != nil { 90 return nil, err 91 } 92 93 statList := make([]string, 0) 94 for _, fi := range fis { 95 if strings.Contains(fi.Name(), ".DS_Store") { 96 continue 97 } 98 99 relPath := path.Join(recPath, fi.Name()) 100 curPath := path.Join(dirPath, fi.Name()) 101 if fi.IsDir() { 102 if includeDir { 103 statList = append(statList, relPath+"/") 104 } 105 s, err := statDir(curPath, relPath, includeDir, isDirOnly, followSymlinks) 106 if err != nil { 107 return nil, err 108 } 109 statList = append(statList, s...) 110 } else if !isDirOnly { 111 statList = append(statList, relPath) 112 } else if followSymlinks && fi.Mode()&os.ModeSymlink != 0 { 113 link, err := os.Readlink(curPath) 114 if err != nil { 115 return nil, err 116 } 117 118 isDir, err := IsDir(link) 119 if err != nil { 120 return nil, err 121 } 122 if isDir { 123 if includeDir { 124 statList = append(statList, relPath+"/") 125 } 126 s, err := statDir(curPath, relPath, includeDir, isDirOnly, followSymlinks) 127 if err != nil { 128 return nil, err 129 } 130 statList = append(statList, s...) 131 } 132 } 133 } 134 return statList, nil 135 } 136 137 // StatDir gathers information of given directory by depth-first. 138 // It returns slice of file list and includes subdirectories if enabled; 139 // it returns error and nil slice when error occurs in underlying functions, 140 // or given path is not a directory or does not exist. 141 // 142 // Slice does not include given path itself. 143 // If subdirectories is enabled, they will have suffix '/'. 144 func StatDir(rootPath string, includeDir ...bool) ([]string, error) { 145 if isDir, err := IsDir(rootPath); err != nil { 146 return nil, err 147 } else if !isDir { 148 return nil, errors.New("not a directory or does not exist: " + rootPath) 149 } 150 151 isIncludeDir := false 152 if len(includeDir) != 0 { 153 isIncludeDir = includeDir[0] 154 } 155 return statDir(rootPath, "", isIncludeDir, false, false) 156 } 157 158 func isOSWindows() bool { 159 return runtime.GOOS == "windows" 160 } 161 162 // FileURLToPath extracts the path information from a file://... url. 163 func FileURLToPath(u *url.URL) (string, error) { 164 if u.Scheme != "file" { 165 return "", errors.New("URL scheme is not 'file': " + u.String()) 166 } 167 168 path := u.Path 169 170 if !isOSWindows() { 171 return path, nil 172 } 173 174 // If it looks like there's a Windows drive letter at the beginning, strip off the leading slash. 175 re := regexp.MustCompile("/[A-Za-z]:/") 176 if re.MatchString(path) { 177 return path[1:], nil 178 } 179 return path, nil 180 } 181 182 // HomeDir returns path of '~'(in Linux) on Windows, 183 // it returns error when the variable does not exist. 184 func HomeDir() (home string, err error) { 185 // TODO: some users run GitBundle with mismatched uid and "HOME=xxx" (they set HOME=xxx by environment manually) 186 // TODO: when running gitbundle as a sub command inside git, the HOME directory is not the user's home directory 187 // so at the moment we can not use `user.Current().HomeDir` 188 if isOSWindows() { 189 home = os.Getenv("USERPROFILE") 190 if home == "" { 191 home = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 192 } 193 } else { 194 home = os.Getenv("HOME") 195 } 196 197 if home == "" { 198 return "", errors.New("cannot get home directory") 199 } 200 201 return home, nil 202 }