Source file src/pkg/path/filepath/path.go
1
2
3
4
5
6
7 package filepath
8
9 import (
10 "errors"
11 "os"
12 "sort"
13 "strings"
14 )
15
16
17
18
19
20 type lazybuf struct {
21 path string
22 buf []byte
23 w int
24 volAndPath string
25 volLen int
26 }
27
28 func (b *lazybuf) index(i int) byte {
29 if b.buf != nil {
30 return b.buf[i]
31 }
32 return b.path[i]
33 }
34
35 func (b *lazybuf) append(c byte) {
36 if b.buf == nil {
37 if b.w < len(b.path) && b.path[b.w] == c {
38 b.w++
39 return
40 }
41 b.buf = make([]byte, len(b.path))
42 copy(b.buf, b.path[:b.w])
43 }
44 b.buf[b.w] = c
45 b.w++
46 }
47
48 func (b *lazybuf) string() string {
49 if b.buf == nil {
50 return b.volAndPath[:b.volLen+b.w]
51 }
52 return b.volAndPath[:b.volLen] + string(b.buf[:b.w])
53 }
54
55 const (
56 Separator = os.PathSeparator
57 ListSeparator = os.PathListSeparator
58 )
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 func Clean(path string) string {
82 originalPath := path
83 volLen := volumeNameLen(path)
84 path = path[volLen:]
85 if path == "" {
86 if volLen > 1 && originalPath[1] != ':' {
87
88 return FromSlash(originalPath)
89 }
90 return originalPath + "."
91 }
92 rooted := os.IsPathSeparator(path[0])
93
94
95
96
97
98
99 n := len(path)
100 out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen}
101 r, dotdot := 0, 0
102 if rooted {
103 out.append(Separator)
104 r, dotdot = 1, 1
105 }
106
107 for r < n {
108 switch {
109 case os.IsPathSeparator(path[r]):
110
111 r++
112 case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
113
114 r++
115 case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
116
117 r += 2
118 switch {
119 case out.w > dotdot:
120
121 out.w--
122 for out.w > dotdot && !os.IsPathSeparator(out.index(out.w)) {
123 out.w--
124 }
125 case !rooted:
126
127 if out.w > 0 {
128 out.append(Separator)
129 }
130 out.append('.')
131 out.append('.')
132 dotdot = out.w
133 }
134 default:
135
136
137 if rooted && out.w != 1 || !rooted && out.w != 0 {
138 out.append(Separator)
139 }
140
141 for ; r < n && !os.IsPathSeparator(path[r]); r++ {
142 out.append(path[r])
143 }
144 }
145 }
146
147
148 if out.w == 0 {
149 out.append('.')
150 }
151
152 return FromSlash(out.string())
153 }
154
155
156
157
158 func ToSlash(path string) string {
159 if Separator == '/' {
160 return path
161 }
162 return strings.Replace(path, string(Separator), "/", -1)
163 }
164
165
166
167
168 func FromSlash(path string) string {
169 if Separator == '/' {
170 return path
171 }
172 return strings.Replace(path, "/", string(Separator), -1)
173 }
174
175
176
177
178 func SplitList(path string) []string {
179 return splitList(path)
180 }
181
182
183
184
185
186
187 func Split(path string) (dir, file string) {
188 vol := VolumeName(path)
189 i := len(path) - 1
190 for i >= len(vol) && !os.IsPathSeparator(path[i]) {
191 i--
192 }
193 return path[:i+1], path[i+1:]
194 }
195
196
197
198
199 func Join(elem ...string) string {
200 for i, e := range elem {
201 if e != "" {
202 return Clean(strings.Join(elem[i:], string(Separator)))
203 }
204 }
205 return ""
206 }
207
208
209
210
211
212 func Ext(path string) string {
213 for i := len(path) - 1; i >= 0 && !os.IsPathSeparator(path[i]); i-- {
214 if path[i] == '.' {
215 return path[i:]
216 }
217 }
218 return ""
219 }
220
221
222
223
224
225 func EvalSymlinks(path string) (string, error) {
226 return evalSymlinks(path)
227 }
228
229
230
231
232
233 func Abs(path string) (string, error) {
234 if IsAbs(path) {
235 return Clean(path), nil
236 }
237 wd, err := os.Getwd()
238 if err != nil {
239 return "", err
240 }
241 return Join(wd, path), nil
242 }
243
244
245
246
247
248
249
250
251 func Rel(basepath, targpath string) (string, error) {
252 baseVol := VolumeName(basepath)
253 targVol := VolumeName(targpath)
254 base := Clean(basepath)
255 targ := Clean(targpath)
256 if targ == base {
257 return ".", nil
258 }
259 base = base[len(baseVol):]
260 targ = targ[len(targVol):]
261 if base == "." {
262 base = ""
263 }
264
265 baseSlashed := len(base) > 0 && base[0] == Separator
266 targSlashed := len(targ) > 0 && targ[0] == Separator
267 if baseSlashed != targSlashed || baseVol != targVol {
268 return "", errors.New("Rel: can't make " + targ + " relative to " + base)
269 }
270
271 bl := len(base)
272 tl := len(targ)
273 var b0, bi, t0, ti int
274 for {
275 for bi < bl && base[bi] != Separator {
276 bi++
277 }
278 for ti < tl && targ[ti] != Separator {
279 ti++
280 }
281 if targ[t0:ti] != base[b0:bi] {
282 break
283 }
284 if bi < bl {
285 bi++
286 }
287 if ti < tl {
288 ti++
289 }
290 b0 = bi
291 t0 = ti
292 }
293 if base[b0:bi] == ".." {
294 return "", errors.New("Rel: can't make " + targ + " relative to " + base)
295 }
296 if b0 != bl {
297
298 seps := strings.Count(base[b0:bl], string(Separator))
299 size := 2 + seps*3
300 if tl != t0 {
301 size += 1 + tl - t0
302 }
303 buf := make([]byte, size)
304 n := copy(buf, "..")
305 for i := 0; i < seps; i++ {
306 buf[n] = Separator
307 copy(buf[n+1:], "..")
308 n += 3
309 }
310 if t0 != tl {
311 buf[n] = Separator
312 copy(buf[n+1:], targ[t0:])
313 }
314 return string(buf), nil
315 }
316 return targ[t0:], nil
317 }
318
319
320
321
322 var SkipDir = errors.New("skip this directory")
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337 type WalkFunc func(path string, info os.FileInfo, err error) error
338
339
340 func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
341 err := walkFn(path, info, nil)
342 if err != nil {
343 if info.IsDir() && err == SkipDir {
344 return nil
345 }
346 return err
347 }
348
349 if !info.IsDir() {
350 return nil
351 }
352
353 list, err := readDir(path)
354 if err != nil {
355 return walkFn(path, info, err)
356 }
357
358 for _, fileInfo := range list {
359 err = walk(Join(path, fileInfo.Name()), fileInfo, walkFn)
360 if err != nil {
361 if !fileInfo.IsDir() || err != SkipDir {
362 return err
363 }
364 }
365 }
366 return nil
367 }
368
369
370
371
372
373
374
375 func Walk(root string, walkFn WalkFunc) error {
376 info, err := os.Lstat(root)
377 if err != nil {
378 return walkFn(root, nil, err)
379 }
380 return walk(root, info, walkFn)
381 }
382
383
384
385
386 func readDir(dirname string) ([]os.FileInfo, error) {
387 f, err := os.Open(dirname)
388 if err != nil {
389 return nil, err
390 }
391 list, err := f.Readdir(-1)
392 f.Close()
393 if err != nil {
394 return nil, err
395 }
396 sort.Sort(byName(list))
397 return list, nil
398 }
399
400
401 type byName []os.FileInfo
402
403 func (f byName) Len() int { return len(f) }
404 func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
405 func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
406
407
408
409
410
411 func Base(path string) string {
412 if path == "" {
413 return "."
414 }
415
416 for len(path) > 0 && os.IsPathSeparator(path[len(path)-1]) {
417 path = path[0 : len(path)-1]
418 }
419
420 path = path[len(VolumeName(path)):]
421
422 i := len(path) - 1
423 for i >= 0 && !os.IsPathSeparator(path[i]) {
424 i--
425 }
426 if i >= 0 {
427 path = path[i+1:]
428 }
429
430 if path == "" {
431 return string(Separator)
432 }
433 return path
434 }
435
436
437
438
439
440
441
442 func Dir(path string) string {
443 vol := VolumeName(path)
444 i := len(path) - 1
445 for i >= len(vol) && !os.IsPathSeparator(path[i]) {
446 i--
447 }
448 dir := Clean(path[len(vol) : i+1])
449 last := len(dir) - 1
450 if last > 0 && os.IsPathSeparator(dir[last]) {
451 dir = dir[:last]
452 }
453 if dir == "" {
454 dir = "."
455 }
456 return vol + dir
457 }
458
459
460
461
462
463 func VolumeName(path string) (v string) {
464 return path[:volumeNameLen(path)]
465 }
View as plain text