You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

path.go 4.8KB

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