win32.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. const os = require('bare-os')
  2. const { normalizeString } = require('./shared')
  3. const {
  4. CHAR_UPPERCASE_A,
  5. CHAR_LOWERCASE_A,
  6. CHAR_UPPERCASE_Z,
  7. CHAR_LOWERCASE_Z,
  8. CHAR_DOT,
  9. CHAR_FORWARD_SLASH,
  10. CHAR_BACKWARD_SLASH,
  11. CHAR_COLON,
  12. CHAR_QUESTION_MARK
  13. } = require('./constants')
  14. function isWindowsPathSeparator (code) {
  15. return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH
  16. }
  17. function isWindowsDeviceRoot (code) {
  18. return (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) ||
  19. (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z)
  20. }
  21. exports.posix = require('./posix')
  22. exports.win32 = exports
  23. exports.sep = '\\'
  24. exports.delimiter = ';'
  25. exports.resolve = function resolve (...args) {
  26. let resolvedDevice = ''
  27. let resolvedTail = ''
  28. let resolvedAbsolute = false
  29. for (let i = args.length - 1; i >= -1; i--) {
  30. let path
  31. if (i >= 0) {
  32. path = args[i]
  33. if (path.length === 0) continue
  34. } else if (resolvedDevice.length === 0) {
  35. path = os.cwd()
  36. } else {
  37. path = os.getEnv(`=${resolvedDevice}`) || os.cwd()
  38. if (path === undefined || (path.substring(0, 2).toLowerCase() !== resolvedDevice.toLowerCase() && path.charCodeAt(2) === CHAR_BACKWARD_SLASH)) {
  39. path = `${resolvedDevice}\\`
  40. }
  41. }
  42. const len = path.length
  43. let rootEnd = 0
  44. let device = ''
  45. let isAbsolute = false
  46. const code = path.charCodeAt(0)
  47. if (len === 1) {
  48. if (isWindowsPathSeparator(code)) {
  49. rootEnd = 1
  50. isAbsolute = true
  51. }
  52. } else if (isWindowsPathSeparator(code)) {
  53. isAbsolute = true
  54. if (isWindowsPathSeparator(path.charCodeAt(1))) {
  55. let j = 2
  56. let last = j
  57. while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) {
  58. j++
  59. }
  60. if (j < len && j !== last) {
  61. const firstPart = path.substring(last, j)
  62. last = j
  63. while (j < len && isWindowsPathSeparator(path.charCodeAt(j))) {
  64. j++
  65. }
  66. if (j < len && j !== last) {
  67. last = j
  68. while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) {
  69. j++
  70. }
  71. if (j === len || j !== last) {
  72. device = `\\\\${firstPart}\\${path.substring(last, j)}`
  73. rootEnd = j
  74. }
  75. }
  76. }
  77. } else {
  78. rootEnd = 1
  79. }
  80. } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) {
  81. device = path.substring(0, 2)
  82. rootEnd = 2
  83. if (len > 2 && isWindowsPathSeparator(path.charCodeAt(2))) {
  84. isAbsolute = true
  85. rootEnd = 3
  86. }
  87. }
  88. if (device.length > 0) {
  89. if (resolvedDevice.length > 0) {
  90. if (device.toLowerCase() !== resolvedDevice.toLowerCase()) { continue }
  91. } else {
  92. resolvedDevice = device
  93. }
  94. }
  95. if (resolvedAbsolute) {
  96. if (resolvedDevice.length > 0) { break }
  97. } else {
  98. resolvedTail = `${path.substring(rootEnd)}\\${resolvedTail}`
  99. resolvedAbsolute = isAbsolute
  100. if (isAbsolute && resolvedDevice.length > 0) {
  101. break
  102. }
  103. }
  104. }
  105. resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\', isWindowsPathSeparator)
  106. return resolvedAbsolute ? `${resolvedDevice}\\${resolvedTail}` : `${resolvedDevice}${resolvedTail}` || '.'
  107. }
  108. exports.normalize = function normalize (path) {
  109. const len = path.length
  110. if (len === 0) return '.'
  111. let rootEnd = 0
  112. let device
  113. let isAbsolute = false
  114. const code = path.charCodeAt(0)
  115. if (len === 1) {
  116. return code === CHAR_FORWARD_SLASH ? '\\' : path
  117. }
  118. if (isWindowsPathSeparator(code)) {
  119. isAbsolute = true
  120. if (isWindowsPathSeparator(path.charCodeAt(1))) {
  121. let j = 2
  122. let last = j
  123. while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) {
  124. j++
  125. }
  126. if (j < len && j !== last) {
  127. const firstPart = path.substring(last, j)
  128. last = j
  129. while (j < len && isWindowsPathSeparator(path.charCodeAt(j))) {
  130. j++
  131. }
  132. if (j < len && j !== last) {
  133. last = j
  134. while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) {
  135. j++
  136. }
  137. if (j === len) {
  138. return `\\\\${firstPart}\\${path.substring(last)}\\`
  139. }
  140. if (j !== last) {
  141. device = `\\\\${firstPart}\\${path.substring(last, j)}`
  142. rootEnd = j
  143. }
  144. }
  145. }
  146. } else {
  147. rootEnd = 1
  148. }
  149. } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) {
  150. device = path.substring(0, 2)
  151. rootEnd = 2
  152. if (len > 2 && isWindowsPathSeparator(path.charCodeAt(2))) {
  153. isAbsolute = true
  154. rootEnd = 3
  155. }
  156. }
  157. let tail = rootEnd < len ? normalizeString(path.substring(rootEnd), !isAbsolute, '\\', isWindowsPathSeparator) : ''
  158. if (tail.length === 0 && !isAbsolute) {
  159. tail = '.'
  160. }
  161. if (tail.length > 0 && isWindowsPathSeparator(path.charCodeAt(len - 1))) {
  162. tail += '\\'
  163. }
  164. if (device === undefined) {
  165. return isAbsolute ? `\\${tail}` : tail
  166. }
  167. return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`
  168. }
  169. exports.isAbsolute = function isAbsolute (path) {
  170. const len = path.length
  171. if (len === 0) return false
  172. const code = path.charCodeAt(0)
  173. return isWindowsPathSeparator(code) || (len > 2 && isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON && isWindowsPathSeparator(path.charCodeAt(2)))
  174. }
  175. exports.join = function join (...args) {
  176. if (args.length === 0) return '.'
  177. let joined
  178. let firstPart
  179. for (let i = 0; i < args.length; ++i) {
  180. const arg = args[i]
  181. if (arg.length > 0) {
  182. if (joined === undefined) joined = firstPart = arg
  183. else joined += `\\${arg}`
  184. }
  185. }
  186. if (joined === undefined) return '.'
  187. let needsReplace = true
  188. let slashCount = 0
  189. if (isWindowsPathSeparator(firstPart.charCodeAt(0))) {
  190. ++slashCount
  191. const firstLen = firstPart.length
  192. if (firstLen > 1 && isWindowsPathSeparator(firstPart.charCodeAt(1))) {
  193. ++slashCount
  194. if (firstLen > 2) {
  195. if (isWindowsPathSeparator(firstPart.charCodeAt(2))) {
  196. ++slashCount
  197. } else {
  198. needsReplace = false
  199. }
  200. }
  201. }
  202. }
  203. if (needsReplace) {
  204. while (slashCount < joined.length && isWindowsPathSeparator(joined.charCodeAt(slashCount))) {
  205. slashCount++
  206. }
  207. if (slashCount >= 2) {
  208. joined = `\\${joined.substring(slashCount)}`
  209. }
  210. }
  211. return exports.normalize(joined)
  212. }
  213. exports.relative = function relative (from, to) {
  214. if (from === to) return ''
  215. const fromOrig = exports.resolve(from)
  216. const toOrig = exports.resolve(to)
  217. if (fromOrig === toOrig) return ''
  218. from = fromOrig.toLowerCase()
  219. to = toOrig.toLowerCase()
  220. if (from === to) return ''
  221. let fromStart = 0
  222. while (fromStart < from.length && from.charCodeAt(fromStart) === CHAR_BACKWARD_SLASH) {
  223. fromStart++
  224. }
  225. let fromEnd = from.length
  226. while (fromEnd - 1 > fromStart && from.charCodeAt(fromEnd - 1) === CHAR_BACKWARD_SLASH) {
  227. fromEnd--
  228. }
  229. const fromLen = fromEnd - fromStart
  230. let toStart = 0
  231. while (toStart < to.length && to.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) {
  232. toStart++
  233. }
  234. let toEnd = to.length
  235. while (toEnd - 1 > toStart && to.charCodeAt(toEnd - 1) === CHAR_BACKWARD_SLASH) {
  236. toEnd--
  237. }
  238. const toLen = toEnd - toStart
  239. const length = fromLen < toLen ? fromLen : toLen
  240. let lastCommonSep = -1
  241. let i = 0
  242. for (; i < length; i++) {
  243. const fromCode = from.charCodeAt(fromStart + i)
  244. if (fromCode !== to.charCodeAt(toStart + i)) {
  245. break
  246. } else if (fromCode === CHAR_BACKWARD_SLASH) {
  247. lastCommonSep = i
  248. }
  249. }
  250. if (i !== length) {
  251. if (lastCommonSep === -1) return toOrig
  252. } else {
  253. if (toLen > length) {
  254. if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) {
  255. return toOrig.substring(toStart + i + 1)
  256. }
  257. if (i === 2) {
  258. return toOrig.substring(toStart + i)
  259. }
  260. }
  261. if (fromLen > length) {
  262. if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) {
  263. lastCommonSep = i
  264. } else if (i === 2) {
  265. lastCommonSep = 3
  266. }
  267. }
  268. if (lastCommonSep === -1) lastCommonSep = 0
  269. }
  270. let out = ''
  271. for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
  272. if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) {
  273. out += out.length === 0 ? '..' : '\\..'
  274. }
  275. }
  276. toStart += lastCommonSep
  277. if (out.length > 0) {
  278. return `${out}${toOrig.substring(toStart, toEnd)}`
  279. }
  280. if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) {
  281. ++toStart
  282. }
  283. return toOrig.substring(toStart, toEnd)
  284. }
  285. exports.toNamespacedPath = function toNamespacedPath (path) {
  286. if (path.length === 0) return path
  287. const resolvedPath = exports.resolve(path)
  288. if (resolvedPath.length <= 2) return path
  289. if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) {
  290. if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) {
  291. const code = resolvedPath.charCodeAt(2)
  292. if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) {
  293. return `\\\\?\\UNC\\${resolvedPath.substring(2)}`
  294. }
  295. }
  296. } else if (
  297. isWindowsDeviceRoot(resolvedPath.charCodeAt(0)) &&
  298. resolvedPath.charCodeAt(1) === CHAR_COLON &&
  299. resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH
  300. ) {
  301. return `\\\\?\\${resolvedPath}`
  302. }
  303. return path
  304. }
  305. exports.dirname = function dirname (path) {
  306. const len = path.length
  307. if (len === 0) return '.'
  308. let rootEnd = -1
  309. let offset = 0
  310. const code = path.charCodeAt(0)
  311. if (len === 1) {
  312. return isWindowsPathSeparator(code) ? path : '.'
  313. }
  314. if (isWindowsPathSeparator(code)) {
  315. rootEnd = offset = 1
  316. if (isWindowsPathSeparator(path.charCodeAt(1))) {
  317. let j = 2
  318. let last = j
  319. while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) {
  320. j++
  321. }
  322. if (j < len && j !== last) {
  323. last = j
  324. while (j < len && isWindowsPathSeparator(path.charCodeAt(j))) {
  325. j++
  326. }
  327. if (j < len && j !== last) {
  328. last = j
  329. while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) {
  330. j++
  331. }
  332. if (j === len) {
  333. return path
  334. }
  335. if (j !== last) {
  336. rootEnd = offset = j + 1
  337. }
  338. }
  339. }
  340. }
  341. } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) {
  342. rootEnd = len > 2 && isWindowsPathSeparator(path.charCodeAt(2)) ? 3 : 2
  343. offset = rootEnd
  344. }
  345. let end = -1
  346. let matchedSlash = true
  347. for (let i = len - 1; i >= offset; --i) {
  348. if (isWindowsPathSeparator(path.charCodeAt(i))) {
  349. if (!matchedSlash) {
  350. end = i
  351. break
  352. }
  353. } else {
  354. matchedSlash = false
  355. }
  356. }
  357. if (end === -1) {
  358. if (rootEnd === -1) return '.'
  359. end = rootEnd
  360. }
  361. return path.substring(0, end)
  362. }
  363. exports.basename = function basename (path, suffix) {
  364. let start = 0
  365. let end = -1
  366. let matchedSlash = true
  367. if (path.length >= 2 && isWindowsDeviceRoot(path.charCodeAt(0)) && path.charCodeAt(1) === CHAR_COLON) {
  368. start = 2
  369. }
  370. if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) {
  371. if (suffix === path) return ''
  372. let extIdx = suffix.length - 1
  373. let firstNonSlashEnd = -1
  374. for (let i = path.length - 1; i >= start; --i) {
  375. const code = path.charCodeAt(i)
  376. if (isWindowsPathSeparator(code)) {
  377. if (!matchedSlash) {
  378. start = i + 1
  379. break
  380. }
  381. } else {
  382. if (firstNonSlashEnd === -1) {
  383. matchedSlash = false
  384. firstNonSlashEnd = i + 1
  385. }
  386. if (extIdx >= 0) {
  387. if (code === suffix.charCodeAt(extIdx)) {
  388. if (--extIdx === -1) {
  389. end = i
  390. }
  391. } else {
  392. extIdx = -1
  393. end = firstNonSlashEnd
  394. }
  395. }
  396. }
  397. }
  398. if (start === end) end = firstNonSlashEnd
  399. else if (end === -1) end = path.length
  400. return path.substring(start, end)
  401. }
  402. for (let i = path.length - 1; i >= start; --i) {
  403. if (isWindowsPathSeparator(path.charCodeAt(i))) {
  404. if (!matchedSlash) {
  405. start = i + 1
  406. break
  407. }
  408. } else if (end === -1) {
  409. matchedSlash = false
  410. end = i + 1
  411. }
  412. }
  413. if (end === -1) return ''
  414. return path.substring(start, end)
  415. }
  416. exports.extname = function extname (path) {
  417. let start = 0
  418. let startDot = -1
  419. let startPart = 0
  420. let end = -1
  421. let matchedSlash = true
  422. let preDotState = 0
  423. if (path.length >= 2 && path.charCodeAt(1) === CHAR_COLON && isWindowsDeviceRoot(path.charCodeAt(0))) {
  424. start = startPart = 2
  425. }
  426. for (let i = path.length - 1; i >= start; --i) {
  427. const code = path.charCodeAt(i)
  428. if (isWindowsPathSeparator(code)) {
  429. if (!matchedSlash) {
  430. startPart = i + 1
  431. break
  432. }
  433. continue
  434. }
  435. if (end === -1) {
  436. matchedSlash = false
  437. end = i + 1
  438. }
  439. if (code === CHAR_DOT) {
  440. if (startDot === -1) startDot = i
  441. else if (preDotState !== 1) preDotState = 1
  442. } else if (startDot !== -1) {
  443. preDotState = -1
  444. }
  445. }
  446. if (startDot === -1 || end === -1 || preDotState === 0 || (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)) {
  447. return ''
  448. }
  449. return path.substring(startDot, end)
  450. }