OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library path.style.windows; | |
6 | |
7 import '../characters.dart' as chars; | 5 import '../characters.dart' as chars; |
8 import '../internal_style.dart'; | 6 import '../internal_style.dart'; |
9 import '../parsed_path.dart'; | 7 import '../parsed_path.dart'; |
10 import '../utils.dart'; | 8 import '../utils.dart'; |
11 | 9 |
| 10 // `0b100000` can be bitwise-ORed with uppercase ASCII letters to get their |
| 11 // lowercase equivalents. |
| 12 const _asciiCaseBit = 0x20; |
| 13 |
12 /// The style for Windows paths. | 14 /// The style for Windows paths. |
13 class WindowsStyle extends InternalStyle { | 15 class WindowsStyle extends InternalStyle { |
14 WindowsStyle(); | 16 WindowsStyle(); |
15 | 17 |
16 final name = 'windows'; | 18 final name = 'windows'; |
17 final separator = '\\'; | 19 final separator = '\\'; |
18 final separators = const ['/', '\\']; | 20 final separators = const ['/', '\\']; |
19 | 21 |
20 // Deprecated properties. | 22 // Deprecated properties. |
21 | 23 |
22 final separatorPattern = new RegExp(r'[/\\]'); | 24 final separatorPattern = new RegExp(r'[/\\]'); |
23 final needsSeparatorPattern = new RegExp(r'[^/\\]$'); | 25 final needsSeparatorPattern = new RegExp(r'[^/\\]$'); |
24 final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); | 26 final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); |
25 final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])"); | 27 final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])"); |
26 | 28 |
27 bool containsSeparator(String path) => path.contains('/'); | 29 bool containsSeparator(String path) => path.contains('/'); |
28 | 30 |
29 bool isSeparator(int codeUnit) => | 31 bool isSeparator(int codeUnit) => |
30 codeUnit == chars.SLASH || codeUnit == chars.BACKSLASH; | 32 codeUnit == chars.SLASH || codeUnit == chars.BACKSLASH; |
31 | 33 |
32 bool needsSeparator(String path) { | 34 bool needsSeparator(String path) { |
33 if (path.isEmpty) return false; | 35 if (path.isEmpty) return false; |
34 return !isSeparator(path.codeUnitAt(path.length - 1)); | 36 return !isSeparator(path.codeUnitAt(path.length - 1)); |
35 } | 37 } |
36 | 38 |
37 int rootLength(String path) { | 39 int rootLength(String path, {bool withDrive: false}) { |
38 if (path.isEmpty) return 0; | 40 if (path.isEmpty) return 0; |
39 if (path.codeUnitAt(0) == chars.SLASH) return 1; | 41 if (path.codeUnitAt(0) == chars.SLASH) return 1; |
40 if (path.codeUnitAt(0) == chars.BACKSLASH) { | 42 if (path.codeUnitAt(0) == chars.BACKSLASH) { |
41 if (path.length < 2 || path.codeUnitAt(1) != chars.BACKSLASH) return 1; | 43 if (path.length < 2 || path.codeUnitAt(1) != chars.BACKSLASH) return 1; |
42 // The path is a network share. Search for up to two '\'s, as they are | 44 // The path is a network share. Search for up to two '\'s, as they are |
43 // the server and share - and part of the root part. | 45 // the server and share - and part of the root part. |
44 var index = path.indexOf('\\', 2); | 46 var index = path.indexOf('\\', 2); |
45 if (index > 0) { | 47 if (index > 0) { |
46 index = path.indexOf('\\', index + 1); | 48 index = path.indexOf('\\', index + 1); |
47 if (index > 0) return index; | 49 if (index > 0) return index; |
(...skipping 21 matching lines...) Expand all Loading... |
69 } | 71 } |
70 | 72 |
71 String pathFromUri(Uri uri) { | 73 String pathFromUri(Uri uri) { |
72 if (uri.scheme != '' && uri.scheme != 'file') { | 74 if (uri.scheme != '' && uri.scheme != 'file') { |
73 throw new ArgumentError("Uri $uri must have scheme 'file:'."); | 75 throw new ArgumentError("Uri $uri must have scheme 'file:'."); |
74 } | 76 } |
75 | 77 |
76 var path = uri.path; | 78 var path = uri.path; |
77 if (uri.host == '') { | 79 if (uri.host == '') { |
78 // Drive-letter paths look like "file:///C:/path/to/file". The | 80 // Drive-letter paths look like "file:///C:/path/to/file". The |
79 // replaceFirst removes the extra initial slash. | 81 // replaceFirst removes the extra initial slash. Otherwise, leave the |
80 if (path.startsWith('/')) path = path.replaceFirst("/", ""); | 82 // slash to match IE's interpretation of "/foo" as a root-relative path. |
| 83 if (path.length >= 3 && |
| 84 path.startsWith('/') && |
| 85 isDriveLetter(path, 1)) { |
| 86 path = path.replaceFirst("/", ""); |
| 87 } |
81 } else { | 88 } else { |
82 // Network paths look like "file://hostname/path/to/file". | 89 // Network paths look like "file://hostname/path/to/file". |
83 path = '\\\\${uri.host}$path'; | 90 path = '\\\\${uri.host}$path'; |
84 } | 91 } |
85 return Uri.decodeComponent(path.replaceAll("/", "\\")); | 92 return Uri.decodeComponent(path.replaceAll("/", "\\")); |
86 } | 93 } |
87 | 94 |
88 Uri absolutePathToUri(String path) { | 95 Uri absolutePathToUri(String path) { |
89 var parsed = new ParsedPath.parse(path, this); | 96 var parsed = new ParsedPath.parse(path, this); |
90 if (parsed.root.startsWith(r'\\')) { | 97 if (parsed.root.startsWith(r'\\')) { |
(...skipping 24 matching lines...) Expand all Loading... |
115 } | 122 } |
116 | 123 |
117 // Get rid of the trailing "\" in "C:\" because the URI constructor will | 124 // Get rid of the trailing "\" in "C:\" because the URI constructor will |
118 // add a separator on its own. | 125 // add a separator on its own. |
119 parsed.parts.insert( | 126 parsed.parts.insert( |
120 0, parsed.root.replaceAll("/", "").replaceAll("\\", "")); | 127 0, parsed.root.replaceAll("/", "").replaceAll("\\", "")); |
121 | 128 |
122 return new Uri(scheme: 'file', pathSegments: parsed.parts); | 129 return new Uri(scheme: 'file', pathSegments: parsed.parts); |
123 } | 130 } |
124 } | 131 } |
| 132 |
| 133 bool codeUnitsEqual(int codeUnit1, int codeUnit2) { |
| 134 if (codeUnit1 == codeUnit2) return true; |
| 135 |
| 136 /// Forward slashes and backslashes are equivalent on Windows. |
| 137 if (codeUnit1 == chars.SLASH) return codeUnit2 == chars.BACKSLASH; |
| 138 if (codeUnit1 == chars.BACKSLASH) return codeUnit2 == chars.SLASH; |
| 139 |
| 140 // If this check fails, the code units are definitely different. If it |
| 141 // succeeds *and* either codeUnit is an ASCII letter, they're equivalent. |
| 142 if (codeUnit1 ^ codeUnit2 != _asciiCaseBit) return false; |
| 143 |
| 144 // Now we just need to verify that one of the code units is an ASCII letter. |
| 145 var upperCase1 = codeUnit1 | _asciiCaseBit; |
| 146 return upperCase1 >= chars.LOWER_A && upperCase1 <= chars.LOWER_Z; |
| 147 } |
| 148 |
| 149 bool pathsEqual(String path1, String path2) { |
| 150 if (identical(path1, path2)) return true; |
| 151 if (path1.length != path2.length) return false; |
| 152 for (var i = 0; i < path1.length; i++) { |
| 153 if (!codeUnitsEqual(path1.codeUnitAt(i), path2.codeUnitAt(i))) { |
| 154 return false; |
| 155 } |
| 156 } |
| 157 return true; |
| 158 } |
| 159 |
| 160 int canonicalizeCodeUnit(int codeUnit) { |
| 161 if (codeUnit == chars.SLASH) return chars.BACKSLASH; |
| 162 if (codeUnit < chars.UPPER_A) return codeUnit; |
| 163 if (codeUnit > chars.UPPER_Z) return codeUnit; |
| 164 return codeUnit | _asciiCaseBit; |
| 165 } |
| 166 |
| 167 String canonicalizePart(String part) => part.toLowerCase(); |
125 } | 168 } |
OLD | NEW |