extpath.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import { normalize, posix, sep } from './path.js';
  6. import { isWindows } from './platform.js';
  7. import { startsWithIgnoreCase } from './strings.js';
  8. export function isPathSeparator(code) {
  9. return code === 47 /* Slash */ || code === 92 /* Backslash */;
  10. }
  11. /**
  12. * Takes a Windows OS path and changes backward slashes to forward slashes.
  13. * This should only be done for OS paths from Windows (or user provided paths potentially from Windows).
  14. * Using it on a Linux or MaxOS path might change it.
  15. */
  16. export function toSlashes(osPath) {
  17. return osPath.replace(/[\\/]/g, posix.sep);
  18. }
  19. /**
  20. * Takes a Windows OS path (using backward or forward slashes) and turns it into a posix path:
  21. * - turns backward slashes into forward slashes
  22. * - makes it absolute if it starts with a drive letter
  23. * This should only be done for OS paths from Windows (or user provided paths potentially from Windows).
  24. * Using it on a Linux or MaxOS path might change it.
  25. */
  26. export function toPosixPath(osPath) {
  27. if (osPath.indexOf('/') === -1) {
  28. osPath = toSlashes(osPath);
  29. }
  30. if (/^[a-zA-Z]:(\/|$)/.test(osPath)) { // starts with a drive letter
  31. osPath = '/' + osPath;
  32. }
  33. return osPath;
  34. }
  35. /**
  36. * Computes the _root_ this path, like `getRoot('c:\files') === c:\`,
  37. * `getRoot('files:///files/path') === files:///`,
  38. * or `getRoot('\\server\shares\path') === \\server\shares\`
  39. */
  40. export function getRoot(path, sep = posix.sep) {
  41. if (!path) {
  42. return '';
  43. }
  44. const len = path.length;
  45. const firstLetter = path.charCodeAt(0);
  46. if (isPathSeparator(firstLetter)) {
  47. if (isPathSeparator(path.charCodeAt(1))) {
  48. // UNC candidate \\localhost\shares\ddd
  49. // ^^^^^^^^^^^^^^^^^^^
  50. if (!isPathSeparator(path.charCodeAt(2))) {
  51. let pos = 3;
  52. const start = pos;
  53. for (; pos < len; pos++) {
  54. if (isPathSeparator(path.charCodeAt(pos))) {
  55. break;
  56. }
  57. }
  58. if (start !== pos && !isPathSeparator(path.charCodeAt(pos + 1))) {
  59. pos += 1;
  60. for (; pos < len; pos++) {
  61. if (isPathSeparator(path.charCodeAt(pos))) {
  62. return path.slice(0, pos + 1) // consume this separator
  63. .replace(/[\\/]/g, sep);
  64. }
  65. }
  66. }
  67. }
  68. }
  69. // /user/far
  70. // ^
  71. return sep;
  72. }
  73. else if (isWindowsDriveLetter(firstLetter)) {
  74. // check for windows drive letter c:\ or c:
  75. if (path.charCodeAt(1) === 58 /* Colon */) {
  76. if (isPathSeparator(path.charCodeAt(2))) {
  77. // C:\fff
  78. // ^^^
  79. return path.slice(0, 2) + sep;
  80. }
  81. else {
  82. // C:
  83. // ^^
  84. return path.slice(0, 2);
  85. }
  86. }
  87. }
  88. // check for URI
  89. // scheme://authority/path
  90. // ^^^^^^^^^^^^^^^^^^^
  91. let pos = path.indexOf('://');
  92. if (pos !== -1) {
  93. pos += 3; // 3 -> "://".length
  94. for (; pos < len; pos++) {
  95. if (isPathSeparator(path.charCodeAt(pos))) {
  96. return path.slice(0, pos + 1); // consume this separator
  97. }
  98. }
  99. }
  100. return '';
  101. }
  102. export function isEqualOrParent(base, parentCandidate, ignoreCase, separator = sep) {
  103. if (base === parentCandidate) {
  104. return true;
  105. }
  106. if (!base || !parentCandidate) {
  107. return false;
  108. }
  109. if (parentCandidate.length > base.length) {
  110. return false;
  111. }
  112. if (ignoreCase) {
  113. const beginsWith = startsWithIgnoreCase(base, parentCandidate);
  114. if (!beginsWith) {
  115. return false;
  116. }
  117. if (parentCandidate.length === base.length) {
  118. return true; // same path, different casing
  119. }
  120. let sepOffset = parentCandidate.length;
  121. if (parentCandidate.charAt(parentCandidate.length - 1) === separator) {
  122. sepOffset--; // adjust the expected sep offset in case our candidate already ends in separator character
  123. }
  124. return base.charAt(sepOffset) === separator;
  125. }
  126. if (parentCandidate.charAt(parentCandidate.length - 1) !== separator) {
  127. parentCandidate += separator;
  128. }
  129. return base.indexOf(parentCandidate) === 0;
  130. }
  131. export function isWindowsDriveLetter(char0) {
  132. return char0 >= 65 /* A */ && char0 <= 90 /* Z */ || char0 >= 97 /* a */ && char0 <= 122 /* z */;
  133. }
  134. export function isRootOrDriveLetter(path) {
  135. const pathNormalized = normalize(path);
  136. if (isWindows) {
  137. if (path.length > 3) {
  138. return false;
  139. }
  140. return hasDriveLetter(pathNormalized) &&
  141. (path.length === 2 || pathNormalized.charCodeAt(2) === 92 /* Backslash */);
  142. }
  143. return pathNormalized === posix.sep;
  144. }
  145. export function hasDriveLetter(path, continueAsWindows) {
  146. const isWindowsPath = ((continueAsWindows !== undefined) ? continueAsWindows : isWindows);
  147. if (isWindowsPath) {
  148. return isWindowsDriveLetter(path.charCodeAt(0)) && path.charCodeAt(1) === 58 /* Colon */;
  149. }
  150. return false;
  151. }