| Index: sdk/lib/_internal/pub/lib/src/io.dart
|
| diff --git a/sdk/lib/_internal/pub/lib/src/io.dart b/sdk/lib/_internal/pub/lib/src/io.dart
|
| index 1620837c2b2f3362997abbf78ac581a52b543dab..061908c5cc1272c8da28b8ae570a2566833be77e 100644
|
| --- a/sdk/lib/_internal/pub/lib/src/io.dart
|
| +++ b/sdk/lib/_internal/pub/lib/src/io.dart
|
| @@ -40,6 +40,41 @@ bool linkExists(String link) => new Link(link).existsSync();
|
| /// for a symlink only if that symlink is unbroken and points to a file.
|
| bool fileExists(String file) => new File(file).existsSync();
|
|
|
| +/// Returns the canonical path for [pathString]. This is the normalized,
|
| +/// absolute path, with symlinks resolved. As in [transitiveTarget], broken or
|
| +/// recursive symlinks will not be fully resolved.
|
| +///
|
| +/// This doesn't require [pathString] to point to a path that exists on the
|
| +/// filesystem; nonexistent or unreadable path entries are treated as normal
|
| +/// directories.
|
| +String canonicalize(String pathString) {
|
| + var components = path.split(path.normalize(path.absolute(pathString)));
|
| + var newPath = components.removeAt(0);
|
| + // Rebuild the path one level at a time, resolving the symlinks for each level
|
| + // in turn.
|
| + for (var component in components) {
|
| + newPath = transitiveTarget(path.join(newPath, component));
|
| + }
|
| + return newPath;
|
| +}
|
| +
|
| +/// Returns the transitive target of [link] (if A links to B which links to C,
|
| +/// this will return C). If [link] is part of a symlink loop (e.g. A links to B
|
| +/// which links back to A), this returns the path to the first repeated link (so
|
| +/// `transitiveTarget("A")` would return `"A"` and `transitiveTarget("A")` would
|
| +/// return `"B"`).
|
| +///
|
| +/// This accepts paths to non-links or broken links, and returns them as-is.
|
| +String transitiveTarget(String link) {
|
| + var seen = new Set<String>();
|
| + while (linkExists(link) && !seen.contains(link)) {
|
| + seen.add(link);
|
| + link = path.normalize(path.join(
|
| + path.dirname(link), new Link(link).targetSync()));
|
| + }
|
| + return link;
|
| +}
|
| +
|
| /// Reads the contents of the text file [file].
|
| String readTextFile(String file) =>
|
| new File(file).readAsStringSync(encoding: Encoding.UTF_8);
|
| @@ -137,7 +172,7 @@ List<String> listDir(String dir, {bool recursive: false,
|
| var contents = <String>[];
|
|
|
| // Avoid recursive symlinks.
|
| - var resolvedPath = new File(dir).fullPathSync();
|
| + var resolvedPath = canonicalize(dir);
|
| if (listedDirectories.contains(resolvedPath)) return [];
|
|
|
| listedDirectories = new Set<String>.from(listedDirectories);
|
|
|