path.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. // Copyright 2009 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Package filepath implements utility routines for manipulating filename paths
  5. // in a way compatible with the target operating system-defined file paths.
  6. package filepath
  7. import (
  8. "errors"
  9. "os"
  10. "sort"
  11. "strings"
  12. )
  13. // A lazybuf is a lazily constructed path buffer.
  14. // It supports append, reading previously appended bytes,
  15. // and retrieving the final string. It does not allocate a buffer
  16. // to hold the output until that output diverges from s.
  17. type lazybuf struct {
  18. path string
  19. buf []byte
  20. w int
  21. volAndPath string
  22. volLen int
  23. }
  24. func (b *lazybuf) index(i int) byte {
  25. if b.buf != nil {
  26. return b.buf[i]
  27. }
  28. return b.path[i]
  29. }
  30. func (b *lazybuf) append(c byte) {
  31. if b.buf == nil {
  32. if b.w < len(b.path) && b.path[b.w] == c {
  33. b.w++
  34. return
  35. }
  36. b.buf = make([]byte, len(b.path))
  37. copy(b.buf, b.path[:b.w])
  38. }
  39. b.buf[b.w] = c
  40. b.w++
  41. }
  42. func (b *lazybuf) string() string {
  43. if b.buf == nil {
  44. return b.volAndPath[:b.volLen+b.w]
  45. }
  46. return b.volAndPath[:b.volLen] + string(b.buf[:b.w])
  47. }
  48. const (
  49. Separator = os.PathSeparator
  50. ListSeparator = os.PathListSeparator
  51. )
  52. // Clean returns the shortest path name equivalent to path
  53. // by purely lexical processing. It applies the following rules
  54. // iteratively until no further processing can be done:
  55. //
  56. // 1. Replace multiple Separator elements with a single one.
  57. // 2. Eliminate each . path name element (the current directory).
  58. // 3. Eliminate each inner .. path name element (the parent directory)
  59. // along with the non-.. element that precedes it.
  60. // 4. Eliminate .. elements that begin a rooted path:
  61. // that is, replace "/.." by "/" at the beginning of a path,
  62. // assuming Separator is '/'.
  63. //
  64. // The returned path ends in a slash only if it represents a root directory,
  65. // such as "/" on Unix or `C:\` on Windows.
  66. //
  67. // If the result of this process is an empty string, Clean
  68. // returns the string ".".
  69. //
  70. // See also Rob Pike, ``Lexical File Names in Plan 9 or
  71. // Getting Dot-Dot Right,''
  72. // http://plan9.bell-labs.com/sys/doc/lexnames.html
  73. func Clean(path string) string {
  74. originalPath := path
  75. volLen := volumeNameLen(path)
  76. path = path[volLen:]
  77. if path == "" {
  78. if volLen > 1 && originalPath[1] != ':' {
  79. // should be UNC
  80. return FromSlash(originalPath)
  81. }
  82. return originalPath + "."
  83. }
  84. rooted := os.IsPathSeparator(path[0])
  85. // Invariants:
  86. // reading from path; r is index of next byte to process.
  87. // writing to buf; w is index of next byte to write.
  88. // dotdot is index in buf where .. must stop, either because
  89. // it is the leading slash or it is a leading ../../.. prefix.
  90. n := len(path)
  91. out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen}
  92. r, dotdot := 0, 0
  93. if rooted {
  94. out.append(Separator)
  95. r, dotdot = 1, 1
  96. }
  97. for r < n {
  98. switch {
  99. case os.IsPathSeparator(path[r]):
  100. // empty path element
  101. r++
  102. case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
  103. // . element
  104. r++
  105. case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
  106. // .. element: remove to last separator
  107. r += 2
  108. switch {
  109. case out.w > dotdot:
  110. // can backtrack
  111. out.w--
  112. for out.w > dotdot && !os.IsPathSeparator(out.index(out.w)) {
  113. out.w--
  114. }
  115. case !rooted:
  116. // cannot backtrack, but not rooted, so append .. element.
  117. if out.w > 0 {
  118. out.append(Separator)
  119. }
  120. out.append('.')
  121. out.append('.')
  122. dotdot = out.w
  123. }
  124. default:
  125. // real path element.
  126. // add slash if needed
  127. if rooted && out.w != 1 || !rooted && out.w != 0 {
  128. out.append(Separator)
  129. }
  130. // copy element
  131. for ; r < n && !os.IsPathSeparator(path[r]); r++ {
  132. out.append(path[r])
  133. }
  134. }
  135. }
  136. // Turn empty string into "."
  137. if out.w == 0 {
  138. out.append('.')
  139. }
  140. return FromSlash(out.string())
  141. }
  142. // ToSlash returns the result of replacing each separator character
  143. // in path with a slash ('/') character. Multiple separators are
  144. // replaced by multiple slashes.
  145. func ToSlash(path string) string {
  146. if Separator == '/' {
  147. return path
  148. }
  149. return strings.Replace(path, string(Separator), "/", -1)
  150. }
  151. // FromSlash returns the result of replacing each slash ('/') character
  152. // in path with a separator character. Multiple slashes are replaced
  153. // by multiple separators.
  154. func FromSlash(path string) string {
  155. if Separator == '/' {
  156. return path
  157. }
  158. return strings.Replace(path, "/", string(Separator), -1)
  159. }
  160. // SplitList splits a list of paths joined by the OS-specific ListSeparator,
  161. // usually found in PATH or GOPATH environment variables.
  162. // Unlike strings.Split, SplitList returns an empty slice when passed an empty string.
  163. func SplitList(path string) []string {
  164. return splitList(path)
  165. }
  166. // Split splits path immediately following the final Separator,
  167. // separating it into a directory and file name component.
  168. // If there is no Separator in path, Split returns an empty dir
  169. // and file set to path.
  170. // The returned values have the property that path = dir+file.
  171. func Split(path string) (dir, file string) {
  172. vol := VolumeName(path)
  173. i := len(path) - 1
  174. for i >= len(vol) && !os.IsPathSeparator(path[i]) {
  175. i--
  176. }
  177. return path[:i+1], path[i+1:]
  178. }
  179. // Join joins any number of path elements into a single path, adding
  180. // a Separator if necessary. The result is Cleaned, in particular
  181. // all empty strings are ignored.
  182. func Join(elem ...string) string {
  183. for i, e := range elem {
  184. if e != "" {
  185. return Clean(strings.Join(elem[i:], string(Separator)))
  186. }
  187. }
  188. return ""
  189. }
  190. // Ext returns the file name extension used by path.
  191. // The extension is the suffix beginning at the final dot
  192. // in the final element of path; it is empty if there is
  193. // no dot.
  194. func Ext(path string) string {
  195. for i := len(path) - 1; i >= 0 && !os.IsPathSeparator(path[i]); i-- {
  196. if path[i] == '.' {
  197. return path[i:]
  198. }
  199. }
  200. return ""
  201. }
  202. // EvalSymlinks returns the path name after the evaluation of any symbolic
  203. // links.
  204. // If path is relative the result will be relative to the current directory,
  205. // unless one of the components is an absolute symbolic link.
  206. func EvalSymlinks(path string) (string, error) {
  207. return evalSymlinks(path)
  208. }
  209. // Abs returns an absolute representation of path.
  210. // If the path is not absolute it will be joined with the current
  211. // working directory to turn it into an absolute path. The absolute
  212. // path name for a given file is not guaranteed to be unique.
  213. func Abs(path string) (string, error) {
  214. return abs(path)
  215. }
  216. func unixAbs(path string) (string, error) {
  217. if IsAbs(path) {
  218. return Clean(path), nil
  219. }
  220. wd, err := os.Getwd()
  221. if err != nil {
  222. return "", err
  223. }
  224. return Join(wd, path), nil
  225. }
  226. // Rel returns a relative path that is lexically equivalent to targpath when
  227. // joined to basepath with an intervening separator. That is,
  228. // Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself.
  229. // On success, the returned path will always be relative to basepath,
  230. // even if basepath and targpath share no elements.
  231. // An error is returned if targpath can't be made relative to basepath or if
  232. // knowing the current working directory would be necessary to compute it.
  233. func Rel(basepath, targpath string) (string, error) {
  234. baseVol := VolumeName(basepath)
  235. targVol := VolumeName(targpath)
  236. base := Clean(basepath)
  237. targ := Clean(targpath)
  238. if targ == base {
  239. return ".", nil
  240. }
  241. base = base[len(baseVol):]
  242. targ = targ[len(targVol):]
  243. if base == "." {
  244. base = ""
  245. }
  246. // Can't use IsAbs - `\a` and `a` are both relative in Windows.
  247. baseSlashed := len(base) > 0 && base[0] == Separator
  248. targSlashed := len(targ) > 0 && targ[0] == Separator
  249. if baseSlashed != targSlashed || baseVol != targVol {
  250. return "", errors.New("Rel: can't make " + targ + " relative to " + base)
  251. }
  252. // Position base[b0:bi] and targ[t0:ti] at the first differing elements.
  253. bl := len(base)
  254. tl := len(targ)
  255. var b0, bi, t0, ti int
  256. for {
  257. for bi < bl && base[bi] != Separator {
  258. bi++
  259. }
  260. for ti < tl && targ[ti] != Separator {
  261. ti++
  262. }
  263. if targ[t0:ti] != base[b0:bi] {
  264. break
  265. }
  266. if bi < bl {
  267. bi++
  268. }
  269. if ti < tl {
  270. ti++
  271. }
  272. b0 = bi
  273. t0 = ti
  274. }
  275. if base[b0:bi] == ".." {
  276. return "", errors.New("Rel: can't make " + targ + " relative to " + base)
  277. }
  278. if b0 != bl {
  279. // Base elements left. Must go up before going down.
  280. seps := strings.Count(base[b0:bl], string(Separator))
  281. size := 2 + seps*3
  282. if tl != t0 {
  283. size += 1 + tl - t0
  284. }
  285. buf := make([]byte, size)
  286. n := copy(buf, "..")
  287. for i := 0; i < seps; i++ {
  288. buf[n] = Separator
  289. copy(buf[n+1:], "..")
  290. n += 3
  291. }
  292. if t0 != tl {
  293. buf[n] = Separator
  294. copy(buf[n+1:], targ[t0:])
  295. }
  296. return string(buf), nil
  297. }
  298. return targ[t0:], nil
  299. }
  300. // SkipDir is used as a return value from WalkFuncs to indicate that
  301. // the directory named in the call is to be skipped. It is not returned
  302. // as an error by any function.
  303. var SkipDir = errors.New("skip this directory")
  304. // WalkFunc is the type of the function called for each file or directory
  305. // visited by Walk. The path argument contains the argument to Walk as a
  306. // prefix; that is, if Walk is called with "dir", which is a directory
  307. // containing the file "a", the walk function will be called with argument
  308. // "dir/a". The info argument is the os.FileInfo for the named path.
  309. //
  310. // If there was a problem walking to the file or directory named by path, the
  311. // incoming error will describe the problem and the function can decide how
  312. // to handle that error (and Walk will not descend into that directory). If
  313. // an error is returned, processing stops. The sole exception is that if path
  314. // is a directory and the function returns the special value SkipDir, the
  315. // contents of the directory are skipped and processing continues as usual on
  316. // the next file.
  317. type WalkFunc func(path string, info os.FileInfo, err error) error
  318. var lstat = os.Lstat // for testing
  319. // walk recursively descends path, calling w.
  320. func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
  321. err := walkFn(path, info, nil)
  322. if err != nil {
  323. if info.IsDir() && err == SkipDir {
  324. return nil
  325. }
  326. return err
  327. }
  328. if !info.IsDir() {
  329. return nil
  330. }
  331. names, err := readDirNames(path)
  332. if err != nil {
  333. return walkFn(path, info, err)
  334. }
  335. for _, name := range names {
  336. filename := Join(path, name)
  337. fileInfo, err := lstat(filename)
  338. if err != nil {
  339. if err := walkFn(filename, fileInfo, err); err != nil && err != SkipDir {
  340. return err
  341. }
  342. } else {
  343. err = walk(filename, fileInfo, walkFn)
  344. if err != nil {
  345. if !fileInfo.IsDir() || err != SkipDir {
  346. return err
  347. }
  348. }
  349. }
  350. }
  351. return nil
  352. }
  353. // Walk walks the file tree rooted at root, calling walkFn for each file or
  354. // directory in the tree, including root. All errors that arise visiting files
  355. // and directories are filtered by walkFn. The files are walked in lexical
  356. // order, which makes the output deterministic but means that for very
  357. // large directories Walk can be inefficient.
  358. // Walk does not follow symbolic links.
  359. func Walk(root string, walkFn WalkFunc) error {
  360. info, err := os.Lstat(root)
  361. if err != nil {
  362. return walkFn(root, nil, err)
  363. }
  364. return walk(root, info, walkFn)
  365. }
  366. // readDirNames reads the directory named by dirname and returns
  367. // a sorted list of directory entries.
  368. func readDirNames(dirname string) ([]string, error) {
  369. f, err := os.Open(dirname)
  370. if err != nil {
  371. return nil, err
  372. }
  373. names, err := f.Readdirnames(-1)
  374. f.Close()
  375. if err != nil {
  376. return nil, err
  377. }
  378. sort.Strings(names)
  379. return names, nil
  380. }
  381. // Base returns the last element of path.
  382. // Trailing path separators are removed before extracting the last element.
  383. // If the path is empty, Base returns ".".
  384. // If the path consists entirely of separators, Base returns a single separator.
  385. func Base(path string) string {
  386. if path == "" {
  387. return "."
  388. }
  389. // Strip trailing slashes.
  390. for len(path) > 0 && os.IsPathSeparator(path[len(path)-1]) {
  391. path = path[0 : len(path)-1]
  392. }
  393. // Throw away volume name
  394. path = path[len(VolumeName(path)):]
  395. // Find the last element
  396. i := len(path) - 1
  397. for i >= 0 && !os.IsPathSeparator(path[i]) {
  398. i--
  399. }
  400. if i >= 0 {
  401. path = path[i+1:]
  402. }
  403. // If empty now, it had only slashes.
  404. if path == "" {
  405. return string(Separator)
  406. }
  407. return path
  408. }
  409. // Dir returns all but the last element of path, typically the path's directory.
  410. // After dropping the final element, the path is Cleaned and trailing
  411. // slashes are removed.
  412. // If the path is empty, Dir returns ".".
  413. // If the path consists entirely of separators, Dir returns a single separator.
  414. // The returned path does not end in a separator unless it is the root directory.
  415. func Dir(path string) string {
  416. vol := VolumeName(path)
  417. i := len(path) - 1
  418. for i >= len(vol) && !os.IsPathSeparator(path[i]) {
  419. i--
  420. }
  421. dir := Clean(path[len(vol) : i+1])
  422. return vol + dir
  423. }
  424. // VolumeName returns leading volume name.
  425. // Given "C:\foo\bar" it returns "C:" under windows.
  426. // Given "\\host\share\foo" it returns "\\host\share".
  427. // On other platforms it returns "".
  428. func VolumeName(path string) (v string) {
  429. return path[:volumeNameLen(path)]
  430. }