root/trunk/hcf/path.d

Revision 930, 6.3 kB (checked in by Gregor, 3 months ago)

hcf/path.d: Multiple '..' elements in paths will now be interpreted correctly. (see ticket #198)

Line 
1 /**
2  * Helpful path functions
3  *
4  * Authors:
5  *  Gregor Richards
6  *
7  * License:
8  *  Copyright (c) 2006  Gregor Richards
9  * 
10  *  Permission is hereby granted, free of charge, to any person obtaining a
11  *  copy of this software and associated documentation files (the "Software"),
12  *  to deal in the Software without restriction, including without limitation
13  *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  *  and/or sell copies of the Software, and to permit persons to whom the
15  *  Software is furnished to do so, subject to the following conditions:
16  * 
17  *  The above copyright notice and this permission notice shall be included in
18  *  all copies or substantial portions of the Software.
19  * 
20  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  *  DEALINGS IN THE SOFTWARE.
27  */
28
29 module hcf.path;
30
31 public import std.path;
32
33 import std.stdio;
34 import std.file;
35 import std.string;
36
37 import std.c.stdlib;
38
39 version (Windows) {
40     import bcd.windows.windows;
41 }
42
43 /** Get the system PATH */
44 char[][] getPath()
45 {
46     return split(toString(getenv("PATH")), std.path.pathsep);
47 }
48
49 /** From args[0], figure out our path.  Returns 'false' on failure */
50 bool whereAmI(char[] argvz, inout char[] dir, inout char[] bname)
51 {
52     // split it
53     bname = getBaseName(argvz);
54     dir = getDirName(argvz);
55    
56     // on Windows, this is a .exe
57     version (Windows) {
58         bname = defaultExt(bname, "exe");
59     }
60    
61     // is this a directory?
62     if (dir != "") {
63         if (!std.path.isabs(dir)) {
64             // make it absolute
65             dir = getcwd() ~ std.path.sep ~ dir;
66         }
67         return true;
68     }
69    
70     version (Windows) {
71         // is it in cwd?
72         char[] cwd = getcwd();
73         if (exists(cwd ~ std.path.sep ~ bname)) {
74             dir = cwd;
75             return true;
76         }
77     }
78    
79     // rifle through the path
80     char[][] path = getPath();
81     foreach (pe; path) {
82         char[] fullname = pe ~ std.path.sep ~ bname;
83         if (exists(fullname)) {
84             version (Windows) {
85                 dir = pe;
86                 return true;
87             } else {
88                 if (getAttributes(fullname) & 0100) {
89                     dir = pe;
90                     return true;
91                 }
92             }
93         }
94     }
95    
96     // bad
97     return false;
98 }
99
100 /// Return a canonical pathname
101 char[] canonPath(char[] origpath)
102 {
103     char[] ret;
104    
105     version (Windows) {
106         // replace any altsep with sep
107         if (altsep.length) {
108             ret = replace(origpath, altsep, sep);
109         } else {
110             ret = origpath.dup;
111         }
112     } else {
113         ret = origpath.dup;
114     }
115    
116     // expand tildes
117     ret = expandTilde(ret);
118    
119     // get rid of any duplicate separators
120     for (int i = 0; i < ret.length; i++) {
121         if (ret[i .. (i + 1)] == sep) {
122             // drop the duplicate separator
123             i++;
124             while (i < ret.length &&
125                    ret[i .. (i + 1)] == sep) {
126                 ret = ret[0 .. i] ~ ret[(i + 1) .. $];
127             }
128         }
129     }
130    
131     // make sure we don't miss a .. element
132     if (ret.length > 3 && ret[($-3) .. $] == std.path.sep ~ "..") {
133         ret ~= std.path.sep;
134     }
135    
136     // or a . element
137     if (ret.length > 2 && ret[($-2) .. $] == std.path.sep ~ ".") {
138         ret ~= std.path.sep;
139     }
140    
141     // search for .. elements
142     for (int i = 0; ret.length > 4 && i <= ret.length - 4; i++) {
143         if (ret[i .. (i + 4)] == std.path.sep ~ ".." ~ std.path.sep) {
144             // drop the previous path element
145             int j;
146             for (j = i - 1; j > 0 && ret[j..(j+1)] != std.path.sep; j--) {}
147             if (j > 0) {
148                 // cut
149                 if (ret[j..j+2] == "/.") {
150                     j = i + 2; // skip it
151                 } else {
152                     ret = ret[0..j] ~ ret[(i + 3) .. $];
153                 }
154             } else {
155                 // can't cut
156                 j = i + 2;
157             }
158             i = j - 1;
159         }
160     }
161    
162     // search for . elements
163     for (int i = 0; ret.length > 2 && i <= ret.length - 3; i++) {
164         if (ret[i .. (i + 3)] == std.path.sep ~ "." ~ std.path.sep) {
165             // drop this path element
166             ret = ret[0..i] ~ ret[(i + 2) .. $];
167             i--;
168         }
169     }
170
171     // get rid of any introductory ./'s
172     while (ret.length > 2 && ret[0..2] == "." ~ std.path.sep) {
173         ret = ret[2..$];
174     }
175    
176     // finally, get rid of any trailing separators
177     while (ret.length &&
178            ret[($ - 1) .. $] == sep) {
179         ret = ret[0 .. ($ - 1)];
180     }
181    
182     return ret;
183 }
184
185 /** Make a directory and all parent directories */
186 void mkdirP(char[] dir)
187 {
188     dir = canonPath(dir);
189     version (Windows) {
190         dir = std.string.replace(dir, "/", "\\");
191     }
192    
193     // split it into elements
194     char[][] dires = split(dir, sep);
195    
196     char[] curdir;
197    
198     // check for root dir
199     if (dires.length &&
200         dires[0] == "") {
201         curdir = std.path.sep;
202         dires = dires[1..$];
203     }
204    
205     // then go piece-by-piece, making directories
206     foreach (dire; dires) {
207         if (curdir.length) {
208             curdir ~= sep ~ dire;
209         } else {
210             curdir ~= dire;
211         }
212        
213         if (!exists(curdir)) {
214             mkdir(curdir);
215         }
216     }
217 }
218
219 /** Remove a file or directory and all of its children */
220 void rmRecursive(char[] name)
221 {
222     // can only delete writable files on Windows
223     version (Windows) {
224         SetFileAttributesA(toStringz(name),
225                            GetFileAttributesA(toStringz(name)) &
226                            ~FILE_ATTRIBUTE_READONLY);
227     }
228    
229     if (isdir(name)) {
230         foreach (elem; listdir(name)) {
231             // don't delete . or ..
232             if (elem == "." ||
233                 elem == "..") continue;
234             rmRecursive(name ~ std.path.sep ~ elem);
235         }
236        
237         // remove the directory itself
238         rmdir(name);
239     } else {
240         std.file.remove(name);
241     }
242 }
Note: See TracBrowser for help on using the browser.