Index: lib/src/context.dart |
diff --git a/lib/src/context.dart b/lib/src/context.dart |
index db055a19b1a6ed54767ca40974d2aff6f8b93cb0..44bdde994ea46daec0052052e8aaea4bc176be2e 100644 |
--- a/lib/src/context.dart |
+++ b/lib/src/context.dart |
@@ -4,6 +4,7 @@ |
library path.context; |
+import 'characters.dart' as chars; |
import 'internal_style.dart'; |
import 'style.dart'; |
import 'parsed_path.dart'; |
@@ -73,6 +74,15 @@ class Context { |
/// If [current] isn't absolute, this won't return an absolute path. |
String absolute(String part1, [String part2, String part3, String part4, |
String part5, String part6, String part7]) { |
+ _validateArgList( |
+ "absolute", [part1, part2, part3, part4, part5, part6, part7]); |
+ |
+ // If there's a single absolute path, just return it. This is a lot faster |
+ // for the common case of `p.absolute(path)`. |
+ if (part2 == null && isAbsolute(part1) && !isRootRelative(part1)) { |
+ return part1; |
+ } |
+ |
return join(current, part1, part2, part3, part4, part5, part6, part7); |
} |
@@ -295,11 +305,79 @@ class Context { |
/// |
/// context.normalize('path/./to/..//file.text'); // -> 'path/file.txt' |
String normalize(String path) { |
+ if (!_needsNormalization(path)) return path; |
+ |
var parsed = _parse(path); |
parsed.normalize(); |
return parsed.toString(); |
} |
+ /// Returns whether [path] needs to be normalized. |
+ bool _needsNormalization(String path) { |
+ var start = 0; |
+ var codeUnits = path.codeUnits; |
+ var previousPrevious; |
+ var previous; |
+ |
+ // Skip past the root before we start looking for snippets that need |
+ // normalization. We want to normalize "//", but not when it's part of |
+ // "http://". |
+ var root = style.rootLength(path); |
+ if (root != 0) { |
+ start = root; |
+ previous = chars.SLASH; |
+ |
+ // On Windows, the root still needs to be normalized if it contains a |
+ // forward slash. |
+ if (style == Style.windows) { |
+ for (var i = 0; i < root; i++) { |
+ if (codeUnits[i] == chars.SLASH) return true; |
+ } |
+ } |
+ } |
+ |
+ for (var i = start; i < codeUnits.length; i++) { |
+ var codeUnit = codeUnits[i]; |
+ if (style.isSeparator(codeUnit)) { |
+ // Forward slashes in Windows paths are normalized to backslashes. |
+ if (style == Style.windows && codeUnit == chars.SLASH) return true; |
+ |
+ // Multiple separators are normalized to single separators. |
+ if (previous != null && style.isSeparator(previous)) return true; |
+ |
+ // Single dots and double dots are normalized to directory traversals. |
+ // |
+ // This can return false positives for ".../", but that's unlikely |
+ // enough that it's probably not going to cause performance issues. |
+ if (previous == chars.PERIOD && |
+ (previousPrevious == null || |
+ previousPrevious == chars.PERIOD || |
+ style.isSeparator(previousPrevious))) { |
+ return true; |
+ } |
+ } |
+ |
+ previousPrevious = previous; |
+ previous = codeUnit; |
+ } |
+ |
+ // Empty paths are normalized to ".". |
+ if (previous == null) return true; |
+ |
+ // Trailing separators are removed. |
+ if (style.isSeparator(previous)) return true; |
+ |
+ // Single dots and double dots are normalized to directory traversals. |
+ if (previous == chars.PERIOD && |
+ (previousPrevious == null || |
+ previousPrevious == chars.SLASH || |
+ previousPrevious == chars.PERIOD)) { |
+ return true; |
+ } |
+ |
+ return false; |
+ } |
+ |
/// Attempts to convert [path] to an equivalent relative path relative to |
/// [root]. |
/// |