OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of dart.io; | |
6 | |
7 /** | |
8 * [Link] objects are references to filesystem links. | |
9 * | |
10 */ | |
11 abstract class Link implements FileSystemEntity { | |
12 /** | |
13 * Creates a Link object. | |
14 */ | |
15 factory Link(String path) => new _Link(path); | |
16 | |
17 /** | |
18 * Creates a [Link] object. | |
19 * | |
20 * If [path] is a relative path, it will be interpreted relative to the | |
21 * current working directory (see [Directory.current]), when used. | |
22 * | |
23 * If [path] is an absolute path, it will be immune to changes to the | |
24 * current working directory. | |
25 */ | |
26 factory Link.fromUri(Uri uri) => new Link(uri.toFilePath()); | |
27 | |
28 /** | |
29 * Creates a symbolic link. Returns a [:Future<Link>:] that completes with | |
30 * the link when it has been created. If the link exists, | |
31 * the future will complete with an error. | |
32 * | |
33 * If [recursive] is false, the default, the link is created | |
34 * only if all directories in its path exist. | |
35 * If [recursive] is true, all non-existing path | |
36 * components are created. The directories in the path of [target] are | |
37 * not affected, unless they are also in [path]. | |
38 * | |
39 * On the Windows platform, this will only work with directories, and the | |
40 * target directory must exist. The link will be created as a Junction. | |
41 * Only absolute links will be created, and relative paths to the target | |
42 * will be converted to absolute paths by joining them with the path of the | |
43 * directory the link is contained in. | |
44 * | |
45 * On other platforms, the posix symlink() call is used to make a symbolic | |
46 * link containing the string [target]. If [target] is a relative path, | |
47 * it will be interpreted relative to the directory containing the link. | |
48 */ | |
49 Future<Link> create(String target, {bool recursive: false}); | |
50 | |
51 /** | |
52 * Synchronously create the link. Calling [createSync] on an existing link | |
53 * will throw an exception. | |
54 * | |
55 * If [recursive] is false, the default, the link is created only if all | |
56 * directories in its path exist. If [recursive] is true, all | |
57 * non-existing path components are created. The directories in | |
58 * the path of [target] are not affected, unless they are also in [path]. | |
59 * | |
60 * On the Windows platform, this will only work with directories, and the | |
61 * target directory must exist. The link will be created as a Junction. | |
62 * Only absolute links will be created, and relative paths to the target | |
63 * will be converted to absolute paths. | |
64 * | |
65 * On other platforms, the posix symlink() call is used to make a symbolic | |
66 * link containing the string [target]. If [target] is a relative path, | |
67 * it will be interpreted relative to the directory containing the link. | |
68 */ | |
69 void createSync(String target, {bool recursive: false}); | |
70 | |
71 /** | |
72 * Synchronously updates the link. Calling [updateSync] on a non-existing link | |
73 * will throw an exception. | |
74 * | |
75 * On the Windows platform, this will only work with directories, and the | |
76 * target directory must exist. | |
77 */ | |
78 void updateSync(String target); | |
79 | |
80 /** | |
81 * Updates the link. Returns a [:Future<Link>:] that completes with the | |
82 * link when it has been updated. Calling [update] on a non-existing link | |
83 * will complete its returned future with an exception. | |
84 * | |
85 * On the Windows platform, this will only work with directories, and the | |
86 * target directory must exist. | |
87 */ | |
88 Future<Link> update(String target); | |
89 | |
90 Future<String> resolveSymbolicLinks(); | |
91 | |
92 String resolveSymbolicLinksSync(); | |
93 | |
94 /** | |
95 * Renames this link. Returns a `Future<Link>` that completes | |
96 * with a [Link] instance for the renamed link. | |
97 * | |
98 * If [newPath] identifies an existing link, that link is | |
99 * replaced. If [newPath] identifies an existing file or directory, | |
100 * the operation fails and the future completes with an exception. | |
101 */ | |
102 Future<Link> rename(String newPath); | |
103 | |
104 /** | |
105 * Synchronously renames this link. Returns a [Link] | |
106 * instance for the renamed link. | |
107 * | |
108 * If [newPath] identifies an existing link, that link is | |
109 * replaced. If [newPath] identifies an existing file or directory | |
110 * the operation fails and an exception is thrown. | |
111 */ | |
112 Link renameSync(String newPath); | |
113 | |
114 /** | |
115 * Returns a [Link] instance whose path is the absolute path to [this]. | |
116 * | |
117 * The absolute path is computed by prefixing | |
118 * a relative path with the current working directory, and returning | |
119 * an absolute path unchanged. | |
120 */ | |
121 Link get absolute; | |
122 | |
123 /** | |
124 * Gets the target of the link. Returns a future that completes with | |
125 * the path to the target. | |
126 * | |
127 * If the returned target is a relative path, it is relative to the | |
128 * directory containing the link. | |
129 * | |
130 * If the link does not exist, or is not a link, the future completes with | |
131 * a FileSystemException. | |
132 */ | |
133 Future<String> target(); | |
134 | |
135 /** | |
136 * Synchronously gets the target of the link. Returns the path to the target. | |
137 * | |
138 * If the returned target is a relative path, it is relative to the | |
139 * directory containing the link. | |
140 * | |
141 * If the link does not exist, or is not a link, throws a FileSystemException. | |
142 */ | |
143 String targetSync(); | |
144 } | |
145 | |
146 | |
147 class _Link extends FileSystemEntity implements Link { | |
148 final String path; | |
149 | |
150 _Link(this.path) { | |
151 if (path is! String) { | |
152 throw new ArgumentError('${Error.safeToString(path)} ' | |
153 'is not a String'); | |
154 } | |
155 } | |
156 | |
157 String toString() => "Link: '$path'"; | |
158 | |
159 Future<bool> exists() => FileSystemEntity.isLink(path); | |
160 | |
161 bool existsSync() => FileSystemEntity.isLinkSync(path); | |
162 | |
163 Link get absolute => new Link(_absolutePath); | |
164 | |
165 Future<FileStat> stat() => FileStat.stat(path); | |
166 | |
167 FileStat statSync() => FileStat.statSync(path); | |
168 | |
169 Future<Link> create(String target, {bool recursive: false}) { | |
170 if (Platform.isWindows) { | |
171 target = _makeWindowsLinkTarget(target); | |
172 } | |
173 var result = recursive ? parent.create(recursive: true) | |
174 : new Future.value(null); | |
175 return result | |
176 .then((_) => _IOService._dispatch(_FILE_CREATE_LINK, [path, target])) | |
177 .then((response) { | |
178 if (_isErrorResponse(response)) { | |
179 throw _exceptionFromResponse( | |
180 response, "Cannot create link to target '$target'", path); | |
181 } | |
182 return this; | |
183 }); | |
184 } | |
185 | |
186 void createSync(String target, {bool recursive: false}) { | |
187 if (recursive) { | |
188 parent.createSync(recursive: true); | |
189 } | |
190 if (Platform.isWindows) { | |
191 target = _makeWindowsLinkTarget(target); | |
192 } | |
193 var result = _File._createLink(path, target); | |
194 throwIfError(result, "Cannot create link", path); | |
195 } | |
196 | |
197 // Put target into the form "\??\C:\my\target\dir". | |
198 String _makeWindowsLinkTarget(String target) { | |
199 Uri base = new Uri.file('${Directory.current.path}\\'); | |
200 Uri link = new Uri.file(path); | |
201 Uri destination = new Uri.file(target); | |
202 String result = base.resolveUri(link).resolveUri(destination).toFilePath(); | |
203 if (result.length > 3 && result[1] == ':' && result[2] == '\\') { | |
204 return '\\??\\$result'; | |
205 } else { | |
206 throw new FileSystemException( | |
207 'Target $result of Link.create on Windows cannot be converted' + | |
208 ' to start with a drive letter. Unexpected error.'); | |
209 } | |
210 } | |
211 | |
212 void updateSync(String target) { | |
213 // TODO(12414): Replace with atomic update, where supported by platform. | |
214 // Atomically changing a link can be done by creating the new link, with | |
215 // a different name, and using the rename() posix call to move it to | |
216 // the old name atomically. | |
217 deleteSync(); | |
218 createSync(target); | |
219 } | |
220 | |
221 Future<Link> update(String target) { | |
222 // TODO(12414): Replace with atomic update, where supported by platform. | |
223 // Atomically changing a link can be done by creating the new link, with | |
224 // a different name, and using the rename() posix call to move it to | |
225 // the old name atomically. | |
226 return delete().then((_) => create(target)); | |
227 } | |
228 | |
229 Future<Link> _delete({bool recursive: false}) { | |
230 if (recursive) { | |
231 return new Directory(path).delete(recursive: true).then((_) => this); | |
232 } | |
233 return _IOService._dispatch(_FILE_DELETE_LINK, [path]).then((response) { | |
234 if (_isErrorResponse(response)) { | |
235 throw _exceptionFromResponse(response, "Cannot delete link", path); | |
236 } | |
237 return this; | |
238 }); | |
239 } | |
240 | |
241 void _deleteSync({bool recursive: false}) { | |
242 if (recursive) { | |
243 return new Directory(path).deleteSync(recursive: true); | |
244 } | |
245 var result = _File._deleteLinkNative(path); | |
246 throwIfError(result, "Cannot delete link", path); | |
247 } | |
248 | |
249 Future<Link> rename(String newPath) { | |
250 return _IOService._dispatch(_FILE_RENAME_LINK, [path, newPath]) | |
251 .then((response) { | |
252 if (_isErrorResponse(response)) { | |
253 throw _exceptionFromResponse( | |
254 response, "Cannot rename link to '$newPath'", path); | |
255 } | |
256 return new Link(newPath); | |
257 }); | |
258 } | |
259 | |
260 Link renameSync(String newPath) { | |
261 var result = _File._renameLink(path, newPath); | |
262 throwIfError(result, "Cannot rename link '$path' to '$newPath'"); | |
263 return new Link(newPath); | |
264 } | |
265 | |
266 Future<String> target() { | |
267 return _IOService._dispatch(_FILE_LINK_TARGET, [path]).then((response) { | |
268 if (_isErrorResponse(response)) { | |
269 throw _exceptionFromResponse( | |
270 response, "Cannot get target of link", path); | |
271 } | |
272 return response; | |
273 }); | |
274 } | |
275 | |
276 String targetSync() { | |
277 var result = _File._linkTarget(path); | |
278 throwIfError(result, "Cannot read link", path); | |
279 return result; | |
280 } | |
281 | |
282 static throwIfError(Object result, String msg, [String path = ""]) { | |
283 if (result is OSError) { | |
284 throw new FileSystemException(msg, path, result); | |
285 } | |
286 } | |
287 | |
288 bool _isErrorResponse(response) { | |
289 return response is List && response[0] != _SUCCESS_RESPONSE; | |
290 } | |
291 | |
292 _exceptionFromResponse(response, String message, String path) { | |
293 assert(_isErrorResponse(response)); | |
294 switch (response[_ERROR_RESPONSE_ERROR_TYPE]) { | |
295 case _ILLEGAL_ARGUMENT_RESPONSE: | |
296 return new ArgumentError(); | |
297 case _OSERROR_RESPONSE: | |
298 var err = new OSError(response[_OSERROR_RESPONSE_MESSAGE], | |
299 response[_OSERROR_RESPONSE_ERROR_CODE]); | |
300 return new FileSystemException(message, path, err); | |
301 default: | |
302 return new Exception("Unknown error"); | |
303 } | |
304 } | |
305 } | |
OLD | NEW |