Line data Source code
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 : import '../characters.dart' as chars;
6 : import '../internal_style.dart';
7 : import '../parsed_path.dart';
8 : import '../utils.dart';
9 :
10 : // `0b100000` can be bitwise-ORed with uppercase ASCII letters to get their
11 : // lowercase equivalents.
12 : const _asciiCaseBit = 0x20;
13 :
14 : /// The style for Windows paths.
15 : class WindowsStyle extends InternalStyle {
16 5 : WindowsStyle();
17 :
18 : final name = 'windows';
19 : final separator = '\\';
20 : final separators = const ['/', '\\'];
21 :
22 : // Deprecated properties.
23 :
24 : final separatorPattern = new RegExp(r'[/\\]');
25 : final needsSeparatorPattern = new RegExp(r'[^/\\]$');
26 : final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])');
27 : final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])");
28 :
29 0 : bool containsSeparator(String path) => path.contains('/');
30 :
31 : bool isSeparator(int codeUnit) =>
32 0 : codeUnit == chars.SLASH || codeUnit == chars.BACKSLASH;
33 :
34 : bool needsSeparator(String path) {
35 0 : if (path.isEmpty) return false;
36 0 : return !isSeparator(path.codeUnitAt(path.length - 1));
37 : }
38 :
39 : int rootLength(String path, {bool withDrive: false}) {
40 0 : if (path.isEmpty) return 0;
41 0 : if (path.codeUnitAt(0) == chars.SLASH) return 1;
42 0 : if (path.codeUnitAt(0) == chars.BACKSLASH) {
43 0 : if (path.length < 2 || path.codeUnitAt(1) != chars.BACKSLASH) return 1;
44 : // The path is a network share. Search for up to two '\'s, as they are
45 : // the server and share - and part of the root part.
46 0 : var index = path.indexOf('\\', 2);
47 0 : if (index > 0) {
48 0 : index = path.indexOf('\\', index + 1);
49 0 : if (index > 0) return index;
50 : }
51 0 : return path.length;
52 : }
53 : // If the path is of the form 'C:/' or 'C:\', with C being any letter, it's
54 : // a root part.
55 0 : if (path.length < 3) return 0;
56 : // Check for the letter.
57 0 : if (!isAlphabetic(path.codeUnitAt(0))) return 0;
58 : // Check for the ':'.
59 0 : if (path.codeUnitAt(1) != chars.COLON) return 0;
60 : // Check for either '/' or '\'.
61 0 : if (!isSeparator(path.codeUnitAt(2))) return 0;
62 : return 3;
63 : }
64 :
65 0 : bool isRootRelative(String path) => rootLength(path) == 1;
66 :
67 : String getRelativeRoot(String path) {
68 0 : var length = rootLength(path);
69 0 : if (length == 1) return path[0];
70 : return null;
71 : }
72 :
73 : String pathFromUri(Uri uri) {
74 0 : if (uri.scheme != '' && uri.scheme != 'file') {
75 0 : throw new ArgumentError("Uri $uri must have scheme 'file:'.");
76 : }
77 :
78 0 : var path = uri.path;
79 0 : if (uri.host == '') {
80 : // Drive-letter paths look like "file:///C:/path/to/file". The
81 : // replaceFirst removes the extra initial slash. Otherwise, leave the
82 : // slash to match IE's interpretation of "/foo" as a root-relative path.
83 0 : if (path.length >= 3 &&
84 0 : path.startsWith('/') &&
85 0 : isDriveLetter(path, 1)) {
86 0 : path = path.replaceFirst("/", "");
87 : }
88 : } else {
89 : // Network paths look like "file://hostname/path/to/file".
90 0 : path = '\\\\${uri.host}$path';
91 : }
92 0 : return Uri.decodeComponent(path.replaceAll("/", "\\"));
93 : }
94 :
95 : Uri absolutePathToUri(String path) {
96 0 : var parsed = new ParsedPath.parse(path, this);
97 0 : if (parsed.root.startsWith(r'\\')) {
98 : // Network paths become "file://server/share/path/to/file".
99 :
100 : // The root is of the form "\\server\share". We want "server" to be the
101 : // URI host, and "share" to be the first element of the path.
102 0 : var rootParts = parsed.root.split('\\').where((part) => part != '');
103 0 : parsed.parts.insert(0, rootParts.last);
104 :
105 0 : if (parsed.hasTrailingSeparator) {
106 : // If the path has a trailing slash, add a single empty component so the
107 : // URI has a trailing slash as well.
108 0 : parsed.parts.add("");
109 : }
110 :
111 0 : return new Uri(
112 0 : scheme: 'file', host: rootParts.first, pathSegments: parsed.parts);
113 : } else {
114 : // Drive-letter paths become "file:///C:/path/to/file".
115 :
116 : // If the path is a bare root (e.g. "C:\"), [parsed.parts] will currently
117 : // be empty. We add an empty component so the URL constructor produces
118 : // "file:///C:/", with a trailing slash. We also add an empty component if
119 : // the URL otherwise has a trailing slash.
120 0 : if (parsed.parts.length == 0 || parsed.hasTrailingSeparator) {
121 0 : parsed.parts.add("");
122 : }
123 :
124 : // Get rid of the trailing "\" in "C:\" because the URI constructor will
125 : // add a separator on its own.
126 0 : parsed.parts.insert(
127 0 : 0, parsed.root.replaceAll("/", "").replaceAll("\\", ""));
128 :
129 0 : return new Uri(scheme: 'file', pathSegments: parsed.parts);
130 : }
131 : }
132 :
133 : bool codeUnitsEqual(int codeUnit1, int codeUnit2) {
134 0 : if (codeUnit1 == codeUnit2) return true;
135 :
136 : /// Forward slashes and backslashes are equivalent on Windows.
137 0 : if (codeUnit1 == chars.SLASH) return codeUnit2 == chars.BACKSLASH;
138 0 : 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 0 : 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 0 : var upperCase1 = codeUnit1 | _asciiCaseBit;
146 0 : 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 0 : if (path1.length != path2.length) return false;
152 0 : for (var i = 0; i < path1.length; i++) {
153 0 : if (!codeUnitsEqual(path1.codeUnitAt(i), path2.codeUnitAt(i))) {
154 : return false;
155 : }
156 : }
157 : return true;
158 : }
159 :
160 : int canonicalizeCodeUnit(int codeUnit) {
161 0 : if (codeUnit == chars.SLASH) return chars.BACKSLASH;
162 0 : if (codeUnit < chars.UPPER_A) return codeUnit;
163 0 : if (codeUnit > chars.UPPER_Z) return codeUnit;
164 0 : return codeUnit | _asciiCaseBit;
165 : }
166 :
167 0 : String canonicalizePart(String part) => part.toLowerCase();
168 : }
|