@@ -14,21 +14,85 @@ import (
14
14
"strings"
15
15
)
16
16
17
- // CleanPath ensure to clean the path
18
- func CleanPath (p string ) string {
19
- if strings .HasPrefix (p , "/" ) {
20
- return path .Clean (p )
17
+ // SafePathRel joins the path elements into a single path, each element is cleaned separately
18
+ // It only returns the following values (like path.Join):
19
+ //
20
+ // empty => ``
21
+ // `` => ``
22
+ // `..` => `.`
23
+ // `dir` => `dir`
24
+ // `/dir/` => `dir`
25
+ // `foo\..\bar` => `foo\..\bar`
26
+ //
27
+ // any redundant part(relative dots, slashes) is removed.
28
+ func SafePathRel (elem ... string ) string {
29
+ elems := make ([]string , len (elem ))
30
+ for i , e := range elem {
31
+ if e == "" {
32
+ continue
33
+ }
34
+ elems [i ] = path .Clean ("/" + e )
35
+ }
36
+ p := path .Join (elems ... )
37
+ if p == "" {
38
+ return ""
39
+ } else if p == "/" {
40
+ return "."
41
+ } else {
42
+ return p [1 :]
21
43
}
22
- return path .Clean ("/" + p )[1 :]
23
44
}
24
45
25
- // EnsureAbsolutePath ensure that a path is absolute, making it
26
- // relative to absoluteBase if necessary
27
- func EnsureAbsolutePath (path , absoluteBase string ) string {
28
- if filepath .IsAbs (path ) {
29
- return path
46
+ // SafePathRelX joins the path elements into a single path like SafePathRel,
47
+ // and covert all backslashes to slashes. (X means "extended", also means the combination of `\` and `/`)
48
+ // It returns similar results as SafePathRel except:
49
+ //
50
+ // `foo\..\bar` => `bar` (because it's processed as `foo/../bar`)
51
+ //
52
+ // any redundant part(relative dots, slashes) is removed.
53
+ func SafePathRelX (elem ... string ) string {
54
+ elems := make ([]string , len (elem ))
55
+ for i , e := range elem {
56
+ if e == "" {
57
+ continue
58
+ }
59
+ elems [i ] = path .Clean ("/" + strings .ReplaceAll (e , "\\ " , "/" ))
60
+ }
61
+ return SafePathRel (elems ... )
62
+ }
63
+
64
+ const pathSeparator = string (os .PathSeparator )
65
+
66
+ // SafeFilePathAbs joins the path elements into a single file path, each element is cleaned separately
67
+ // and convert all slashes/backslashes to path separators.
68
+ // The first element must be an absolute path, caller should prepare the base path
69
+ func SafeFilePathAbs (elem ... string ) string {
70
+ elems := make ([]string , len (elem ))
71
+
72
+ // POISX filesystem can have `\` in file names. Windows: `\` and `/` are both used for path separators
73
+ // to keep the behavior consistent, we do not allow `\` in file names, replace all `\` with `/`
74
+ if isOSWindows () {
75
+ elems [0 ] = filepath .Clean (elem [0 ])
76
+ } else {
77
+ elems [0 ] = filepath .Clean (strings .ReplaceAll (elem [0 ], "\\ " , pathSeparator ))
78
+ }
79
+ if ! filepath .IsAbs (elems [0 ]) {
80
+ // This shouldn't happen. If there is really necessary to pass in relative path, return the full path with filepath.Abs() instead
81
+ panic ("FilePathJoinAbs: result is not absolute, do not guess a relative path based on current working directory" )
82
+ }
83
+
84
+ for i := 1 ; i < len (elem ); i ++ {
85
+ if elem [i ] == "" {
86
+ continue
87
+ }
88
+ if isOSWindows () {
89
+ elems [i ] = filepath .Clean (pathSeparator + elem [i ])
90
+ } else {
91
+ elems [i ] = filepath .Clean (pathSeparator + strings .ReplaceAll (elem [i ], "\\ " , pathSeparator ))
92
+ }
30
93
}
31
- return filepath .Join (absoluteBase , path )
94
+ // the elems[0] must be an absolute path, just join them together
95
+ return filepath .Join (elems ... )
32
96
}
33
97
34
98
// IsDir returns true if given path is a directory,
0 commit comments