OSDN Git Service

Remove the types float and complex.
[pf3gnuchains/gcc-fork.git] / libgo / go / path / path.go
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
5 // The path package implements utility routines for manipulating
6 // slash-separated filename paths.
7 package path
8
9 import (
10         "io/ioutil"
11         "os"
12         "strings"
13 )
14
15 // Clean returns the shortest path name equivalent to path
16 // by purely lexical processing.  It applies the following rules
17 // iteratively until no further processing can be done:
18 //
19 //      1. Replace multiple slashes with a single slash.
20 //      2. Eliminate each . path name element (the current directory).
21 //      3. Eliminate each inner .. path name element (the parent directory)
22 //         along with the non-.. element that precedes it.
23 //      4. Eliminate .. elements that begin a rooted path:
24 //         that is, replace "/.." by "/" at the beginning of a path.
25 //
26 // If the result of this process is an empty string, Clean
27 // returns the string ".".
28 //
29 // See also Rob Pike, ``Lexical File Names in Plan 9 or
30 // Getting Dot-Dot right,''
31 // http://plan9.bell-labs.com/sys/doc/lexnames.html
32 func Clean(path string) string {
33         if path == "" {
34                 return "."
35         }
36
37         rooted := path[0] == '/'
38         n := len(path)
39
40         // Invariants:
41         //      reading from path; r is index of next byte to process.
42         //      writing to buf; w is index of next byte to write.
43         //      dotdot is index in buf where .. must stop, either because
44         //              it is the leading slash or it is a leading ../../.. prefix.
45         buf := []byte(path)
46         r, w, dotdot := 0, 0, 0
47         if rooted {
48                 r, w, dotdot = 1, 1, 1
49         }
50
51         for r < n {
52                 switch {
53                 case path[r] == '/':
54                         // empty path element
55                         r++
56                 case path[r] == '.' && (r+1 == n || path[r+1] == '/'):
57                         // . element
58                         r++
59                 case path[r] == '.' && path[r+1] == '.' && (r+2 == n || path[r+2] == '/'):
60                         // .. element: remove to last /
61                         r += 2
62                         switch {
63                         case w > dotdot:
64                                 // can backtrack
65                                 w--
66                                 for w > dotdot && buf[w] != '/' {
67                                         w--
68                                 }
69                         case !rooted:
70                                 // cannot backtrack, but not rooted, so append .. element.
71                                 if w > 0 {
72                                         buf[w] = '/'
73                                         w++
74                                 }
75                                 buf[w] = '.'
76                                 w++
77                                 buf[w] = '.'
78                                 w++
79                                 dotdot = w
80                         }
81                 default:
82                         // real path element.
83                         // add slash if needed
84                         if rooted && w != 1 || !rooted && w != 0 {
85                                 buf[w] = '/'
86                                 w++
87                         }
88                         // copy element
89                         for ; r < n && path[r] != '/'; r++ {
90                                 buf[w] = path[r]
91                                 w++
92                         }
93                 }
94         }
95
96         // Turn empty string into "."
97         if w == 0 {
98                 buf[w] = '.'
99                 w++
100         }
101
102         return string(buf[0:w])
103 }
104
105 // Split splits path immediately following the final path separator,
106 // separating it into a directory and file name component.
107 // If there is no separator in path, Split returns an empty dir and
108 // file set to path.
109 func Split(path string) (dir, file string) {
110         i := strings.LastIndexAny(path, PathSeps)
111         return path[:i+1], path[i+1:]
112 }
113
114 // Join joins any number of path elements into a single path, adding a
115 // separating slash if necessary.  All empty strings are ignored.
116 func Join(elem ...string) string {
117         for i, e := range elem {
118                 if e != "" {
119                         return Clean(strings.Join(elem[i:], "/"))
120                 }
121         }
122         return ""
123 }
124
125 // Ext returns the file name extension used by path.
126 // The extension is the suffix beginning at the final dot
127 // in the final slash-separated element of path;
128 // it is empty if there is no dot.
129 func Ext(path string) string {
130         for i := len(path) - 1; i >= 0 && path[i] != '/'; i-- {
131                 if path[i] == '.' {
132                         return path[i:]
133                 }
134         }
135         return ""
136 }
137
138 // Visitor methods are invoked for corresponding file tree entries
139 // visited by Walk. The parameter path is the full path of f relative
140 // to root.
141 type Visitor interface {
142         VisitDir(path string, f *os.FileInfo) bool
143         VisitFile(path string, f *os.FileInfo)
144 }
145
146 func walk(path string, f *os.FileInfo, v Visitor, errors chan<- os.Error) {
147         if !f.IsDirectory() {
148                 v.VisitFile(path, f)
149                 return
150         }
151
152         if !v.VisitDir(path, f) {
153                 return // skip directory entries
154         }
155
156         list, err := ioutil.ReadDir(path)
157         if err != nil {
158                 if errors != nil {
159                         errors <- err
160                 }
161         }
162
163         for _, e := range list {
164                 walk(Join(path, e.Name), e, v, errors)
165         }
166 }
167
168 // Walk walks the file tree rooted at root, calling v.VisitDir or
169 // v.VisitFile for each directory or file in the tree, including root.
170 // If v.VisitDir returns false, Walk skips the directory's entries;
171 // otherwise it invokes itself for each directory entry in sorted order.
172 // An error reading a directory does not abort the Walk.
173 // If errors != nil, Walk sends each directory read error
174 // to the channel.  Otherwise Walk discards the error.
175 func Walk(root string, v Visitor, errors chan<- os.Error) {
176         f, err := os.Lstat(root)
177         if err != nil {
178                 if errors != nil {
179                         errors <- err
180                 }
181                 return // can't progress
182         }
183         walk(root, f, v, errors)
184 }
185
186 // Base returns the last path element of the slash-separated name.
187 // Trailing slashes are removed before extracting the last element.  If the name is
188 // empty, "." is returned.  If it consists entirely of slashes, "/" is returned.
189 func Base(name string) string {
190         if name == "" {
191                 return "."
192         }
193         // Strip trailing slashes.
194         for len(name) > 0 && name[len(name)-1] == '/' {
195                 name = name[0 : len(name)-1]
196         }
197         // Find the last element
198         if i := strings.LastIndex(name, "/"); i >= 0 {
199                 name = name[i+1:]
200         }
201         // If empty now, it had only slashes.
202         if name == "" {
203                 return "/"
204         }
205         return name
206 }
207
208 // IsAbs returns true if the path is absolute.
209 func IsAbs(path string) bool {
210         // TODO: Add Windows support
211         return strings.HasPrefix(path, "/")
212 }