| Index: pkg/path/lib/path.dart
|
| diff --git a/pkg/path/lib/path.dart b/pkg/path/lib/path.dart
|
| index 3c5f51eb3ea967285dfa8aee4586088c21e8a588..5f469edd6a6e5b9e67cc4abd2d892a8e8405e89e 100644
|
| --- a/pkg/path/lib/path.dart
|
| +++ b/pkg/path/lib/path.dart
|
| @@ -301,6 +301,13 @@ String normalize(String path) => _builder.normalize(path);
|
| String relative(String path, {String from}) =>
|
| _builder.relative(path, from: from);
|
|
|
| +/// Returns `true` if [child] is a path beneath `parent`, and `false` otherwise.
|
| +///
|
| +/// path.isWithin('/root/path', '/root/path/a'); // -> true
|
| +/// path.isWithin('/root/path', '/root/other'); // -> false
|
| +/// path.isWithin('/root/path', '/root/path') // -> false
|
| +bool isWithin(String parent, String child) => _builder.isWithin(parent, child);
|
| +
|
| /// Removes a trailing extension from the last part of [path].
|
| ///
|
| /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
|
| @@ -560,10 +567,13 @@ class Builder {
|
| if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) {
|
| // If the new part is root-relative, it preserves the previous root but
|
| // replaces the path after it.
|
| - var oldRoot = this.rootPrefix(buffer.toString());
|
| + var parsed = _parse(part);
|
| + parsed.root = this.rootPrefix(buffer.toString());
|
| + if (parsed.root.contains(style.needsSeparatorPattern)) {
|
| + parsed.separators[0] = style.separator;
|
| + }
|
| buffer.clear();
|
| - buffer.write(oldRoot);
|
| - buffer.write(part);
|
| + buffer.write(parsed);
|
| } else if (this.isAbsolute(part)) {
|
| isAbsoluteAndNotRootRelative = !this.isRootRelative(part);
|
| // An absolute path discards everything before it.
|
| @@ -661,6 +671,11 @@ class Builder {
|
| ///
|
| /// var builder = new Builder(r'some/relative/path');
|
| /// builder.relative(r'/absolute/path'); // -> '/absolute/path'
|
| + ///
|
| + /// If [root] is relative, it may be impossible to determine a path from
|
| + /// [from] to [path]. For example, if [root] and [path] are "." and [from] is
|
| + /// "/", no path can be determined. In this case, a [PathException] will be
|
| + /// thrown.
|
| String relative(String path, {String from}) {
|
| from = from == null ? root : this.join(root, from);
|
|
|
| @@ -678,7 +693,7 @@ class Builder {
|
| // If the path is still relative and `from` is absolute, we're unable to
|
| // find a path from `from` to `path`.
|
| if (this.isRelative(path) && this.isAbsolute(from)) {
|
| - throw new ArgumentError('Unable to find a path to "$path" from "$from".');
|
| + throw new PathException('Unable to find a path to "$path" from "$from".');
|
| }
|
|
|
| var fromParsed = _parse(from)..normalize();
|
| @@ -712,7 +727,7 @@ class Builder {
|
| // out of them. If a directory left in the from path is '..', it cannot
|
| // be cancelled by adding a '..'.
|
| if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') {
|
| - throw new ArgumentError('Unable to find a path to "$path" from "$from".');
|
| + throw new PathException('Unable to find a path to "$path" from "$from".');
|
| }
|
| _growListFront(pathParsed.parts, fromParsed.parts.length, '..');
|
| pathParsed.separators[0] = '';
|
| @@ -736,6 +751,27 @@ class Builder {
|
| return pathParsed.toString();
|
| }
|
|
|
| + /// Returns `true` if [child] is a path beneath `parent`, and `false`
|
| + /// otherwise.
|
| + ///
|
| + /// path.isWithin('/root/path', '/root/path/a'); // -> true
|
| + /// path.isWithin('/root/path', '/root/other'); // -> false
|
| + /// path.isWithin('/root/path', '/root/path') // -> false
|
| + bool isWithin(String parent, String child) {
|
| + var relative;
|
| + try {
|
| + relative = this.relative(child, from: parent);
|
| + } on PathException catch (_) {
|
| + // If no relative path from [parent] to [child] is found, [child]
|
| + // definitely isn't a child of [parent].
|
| + return false;
|
| + }
|
| +
|
| + var parts = this.split(relative);
|
| + return this.isRelative(relative) && parts.first != '..' &&
|
| + parts.first != '.';
|
| + }
|
| +
|
| /// Removes a trailing extension from the last part of [path].
|
| ///
|
| /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
|
| @@ -983,6 +1019,10 @@ class _WindowsStyle extends Style {
|
| final needsSeparatorPattern = new RegExp(r'[^/\\]$');
|
| final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])');
|
|
|
| + // Matches a back or forward slash that's not followed by another back or
|
| + // forward slash.
|
| + final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])");
|
| +
|
| String pathFromUri(Uri uri) {
|
| if (uri.scheme != '' && uri.scheme != 'file') {
|
| throw new ArgumentError("Uri $uri must have scheme 'file:'.");
|
| @@ -1197,3 +1237,13 @@ class _ParsedPath {
|
| style, root, isRootRelative,
|
| new List.from(parts), new List.from(separators));
|
| }
|
| +
|
| +/// An exception class that's thrown when a path operation is unable to be
|
| +/// computed accurately.
|
| +class PathException implements Exception {
|
| + String message;
|
| +
|
| + PathException(this.message);
|
| +
|
| + String toString() => "PathException: $message";
|
| +}
|
|
|