11cb0ef41Sopenharmony_ci// Copyright Joyent, Inc. and other Node contributors. 21cb0ef41Sopenharmony_ci// 31cb0ef41Sopenharmony_ci// Permission is hereby granted, free of charge, to any person obtaining a 41cb0ef41Sopenharmony_ci// copy of this software and associated documentation files (the 51cb0ef41Sopenharmony_ci// "Software"), to deal in the Software without restriction, including 61cb0ef41Sopenharmony_ci// without limitation the rights to use, copy, modify, merge, publish, 71cb0ef41Sopenharmony_ci// distribute, sublicense, and/or sell copies of the Software, and to permit 81cb0ef41Sopenharmony_ci// persons to whom the Software is furnished to do so, subject to the 91cb0ef41Sopenharmony_ci// following conditions: 101cb0ef41Sopenharmony_ci// 111cb0ef41Sopenharmony_ci// The above copyright notice and this permission notice shall be included 121cb0ef41Sopenharmony_ci// in all copies or substantial portions of the Software. 131cb0ef41Sopenharmony_ci// 141cb0ef41Sopenharmony_ci// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 151cb0ef41Sopenharmony_ci// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 161cb0ef41Sopenharmony_ci// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 171cb0ef41Sopenharmony_ci// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 181cb0ef41Sopenharmony_ci// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 191cb0ef41Sopenharmony_ci// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 201cb0ef41Sopenharmony_ci// USE OR OTHER DEALINGS IN THE SOFTWARE. 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ci'use strict'; 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_ciconst { 251cb0ef41Sopenharmony_ci FunctionPrototypeBind, 261cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt, 271cb0ef41Sopenharmony_ci StringPrototypeIndexOf, 281cb0ef41Sopenharmony_ci StringPrototypeLastIndexOf, 291cb0ef41Sopenharmony_ci StringPrototypeReplace, 301cb0ef41Sopenharmony_ci StringPrototypeSlice, 311cb0ef41Sopenharmony_ci StringPrototypeToLowerCase, 321cb0ef41Sopenharmony_ci} = primordials; 331cb0ef41Sopenharmony_ci 341cb0ef41Sopenharmony_ciconst { 351cb0ef41Sopenharmony_ci CHAR_UPPERCASE_A, 361cb0ef41Sopenharmony_ci CHAR_LOWERCASE_A, 371cb0ef41Sopenharmony_ci CHAR_UPPERCASE_Z, 381cb0ef41Sopenharmony_ci CHAR_LOWERCASE_Z, 391cb0ef41Sopenharmony_ci CHAR_DOT, 401cb0ef41Sopenharmony_ci CHAR_FORWARD_SLASH, 411cb0ef41Sopenharmony_ci CHAR_BACKWARD_SLASH, 421cb0ef41Sopenharmony_ci CHAR_COLON, 431cb0ef41Sopenharmony_ci CHAR_QUESTION_MARK, 441cb0ef41Sopenharmony_ci} = require('internal/constants'); 451cb0ef41Sopenharmony_ciconst { 461cb0ef41Sopenharmony_ci validateObject, 471cb0ef41Sopenharmony_ci validateString, 481cb0ef41Sopenharmony_ci} = require('internal/validators'); 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ciconst platformIsWin32 = (process.platform === 'win32'); 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_cifunction isPathSeparator(code) { 531cb0ef41Sopenharmony_ci return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; 541cb0ef41Sopenharmony_ci} 551cb0ef41Sopenharmony_ci 561cb0ef41Sopenharmony_cifunction isPosixPathSeparator(code) { 571cb0ef41Sopenharmony_ci return code === CHAR_FORWARD_SLASH; 581cb0ef41Sopenharmony_ci} 591cb0ef41Sopenharmony_ci 601cb0ef41Sopenharmony_cifunction isWindowsDeviceRoot(code) { 611cb0ef41Sopenharmony_ci return (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) || 621cb0ef41Sopenharmony_ci (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z); 631cb0ef41Sopenharmony_ci} 641cb0ef41Sopenharmony_ci 651cb0ef41Sopenharmony_ci// Resolves . and .. elements in a path with directory names 661cb0ef41Sopenharmony_cifunction normalizeString(path, allowAboveRoot, separator, isPathSeparator) { 671cb0ef41Sopenharmony_ci let res = ''; 681cb0ef41Sopenharmony_ci let lastSegmentLength = 0; 691cb0ef41Sopenharmony_ci let lastSlash = -1; 701cb0ef41Sopenharmony_ci let dots = 0; 711cb0ef41Sopenharmony_ci let code = 0; 721cb0ef41Sopenharmony_ci for (let i = 0; i <= path.length; ++i) { 731cb0ef41Sopenharmony_ci if (i < path.length) 741cb0ef41Sopenharmony_ci code = StringPrototypeCharCodeAt(path, i); 751cb0ef41Sopenharmony_ci else if (isPathSeparator(code)) 761cb0ef41Sopenharmony_ci break; 771cb0ef41Sopenharmony_ci else 781cb0ef41Sopenharmony_ci code = CHAR_FORWARD_SLASH; 791cb0ef41Sopenharmony_ci 801cb0ef41Sopenharmony_ci if (isPathSeparator(code)) { 811cb0ef41Sopenharmony_ci if (lastSlash === i - 1 || dots === 1) { 821cb0ef41Sopenharmony_ci // NOOP 831cb0ef41Sopenharmony_ci } else if (dots === 2) { 841cb0ef41Sopenharmony_ci if (res.length < 2 || lastSegmentLength !== 2 || 851cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(res, res.length - 1) !== CHAR_DOT || 861cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(res, res.length - 2) !== CHAR_DOT) { 871cb0ef41Sopenharmony_ci if (res.length > 2) { 881cb0ef41Sopenharmony_ci const lastSlashIndex = StringPrototypeLastIndexOf(res, separator); 891cb0ef41Sopenharmony_ci if (lastSlashIndex === -1) { 901cb0ef41Sopenharmony_ci res = ''; 911cb0ef41Sopenharmony_ci lastSegmentLength = 0; 921cb0ef41Sopenharmony_ci } else { 931cb0ef41Sopenharmony_ci res = StringPrototypeSlice(res, 0, lastSlashIndex); 941cb0ef41Sopenharmony_ci lastSegmentLength = 951cb0ef41Sopenharmony_ci res.length - 1 - StringPrototypeLastIndexOf(res, separator); 961cb0ef41Sopenharmony_ci } 971cb0ef41Sopenharmony_ci lastSlash = i; 981cb0ef41Sopenharmony_ci dots = 0; 991cb0ef41Sopenharmony_ci continue; 1001cb0ef41Sopenharmony_ci } else if (res.length !== 0) { 1011cb0ef41Sopenharmony_ci res = ''; 1021cb0ef41Sopenharmony_ci lastSegmentLength = 0; 1031cb0ef41Sopenharmony_ci lastSlash = i; 1041cb0ef41Sopenharmony_ci dots = 0; 1051cb0ef41Sopenharmony_ci continue; 1061cb0ef41Sopenharmony_ci } 1071cb0ef41Sopenharmony_ci } 1081cb0ef41Sopenharmony_ci if (allowAboveRoot) { 1091cb0ef41Sopenharmony_ci res += res.length > 0 ? `${separator}..` : '..'; 1101cb0ef41Sopenharmony_ci lastSegmentLength = 2; 1111cb0ef41Sopenharmony_ci } 1121cb0ef41Sopenharmony_ci } else { 1131cb0ef41Sopenharmony_ci if (res.length > 0) 1141cb0ef41Sopenharmony_ci res += `${separator}${StringPrototypeSlice(path, lastSlash + 1, i)}`; 1151cb0ef41Sopenharmony_ci else 1161cb0ef41Sopenharmony_ci res = StringPrototypeSlice(path, lastSlash + 1, i); 1171cb0ef41Sopenharmony_ci lastSegmentLength = i - lastSlash - 1; 1181cb0ef41Sopenharmony_ci } 1191cb0ef41Sopenharmony_ci lastSlash = i; 1201cb0ef41Sopenharmony_ci dots = 0; 1211cb0ef41Sopenharmony_ci } else if (code === CHAR_DOT && dots !== -1) { 1221cb0ef41Sopenharmony_ci ++dots; 1231cb0ef41Sopenharmony_ci } else { 1241cb0ef41Sopenharmony_ci dots = -1; 1251cb0ef41Sopenharmony_ci } 1261cb0ef41Sopenharmony_ci } 1271cb0ef41Sopenharmony_ci return res; 1281cb0ef41Sopenharmony_ci} 1291cb0ef41Sopenharmony_ci 1301cb0ef41Sopenharmony_ci/** 1311cb0ef41Sopenharmony_ci * @param {string} sep 1321cb0ef41Sopenharmony_ci * @param {{ 1331cb0ef41Sopenharmony_ci * dir?: string; 1341cb0ef41Sopenharmony_ci * root?: string; 1351cb0ef41Sopenharmony_ci * base?: string; 1361cb0ef41Sopenharmony_ci * name?: string; 1371cb0ef41Sopenharmony_ci * ext?: string; 1381cb0ef41Sopenharmony_ci * }} pathObject 1391cb0ef41Sopenharmony_ci * @returns {string} 1401cb0ef41Sopenharmony_ci */ 1411cb0ef41Sopenharmony_cifunction _format(sep, pathObject) { 1421cb0ef41Sopenharmony_ci validateObject(pathObject, 'pathObject'); 1431cb0ef41Sopenharmony_ci const dir = pathObject.dir || pathObject.root; 1441cb0ef41Sopenharmony_ci const base = pathObject.base || 1451cb0ef41Sopenharmony_ci `${pathObject.name || ''}${pathObject.ext || ''}`; 1461cb0ef41Sopenharmony_ci if (!dir) { 1471cb0ef41Sopenharmony_ci return base; 1481cb0ef41Sopenharmony_ci } 1491cb0ef41Sopenharmony_ci return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`; 1501cb0ef41Sopenharmony_ci} 1511cb0ef41Sopenharmony_ci 1521cb0ef41Sopenharmony_ciconst win32 = { 1531cb0ef41Sopenharmony_ci /** 1541cb0ef41Sopenharmony_ci * path.resolve([from ...], to) 1551cb0ef41Sopenharmony_ci * @param {...string} args 1561cb0ef41Sopenharmony_ci * @returns {string} 1571cb0ef41Sopenharmony_ci */ 1581cb0ef41Sopenharmony_ci resolve(...args) { 1591cb0ef41Sopenharmony_ci let resolvedDevice = ''; 1601cb0ef41Sopenharmony_ci let resolvedTail = ''; 1611cb0ef41Sopenharmony_ci let resolvedAbsolute = false; 1621cb0ef41Sopenharmony_ci 1631cb0ef41Sopenharmony_ci for (let i = args.length - 1; i >= -1; i--) { 1641cb0ef41Sopenharmony_ci let path; 1651cb0ef41Sopenharmony_ci if (i >= 0) { 1661cb0ef41Sopenharmony_ci path = args[i]; 1671cb0ef41Sopenharmony_ci validateString(path, `paths[${i}]`); 1681cb0ef41Sopenharmony_ci 1691cb0ef41Sopenharmony_ci // Skip empty entries 1701cb0ef41Sopenharmony_ci if (path.length === 0) { 1711cb0ef41Sopenharmony_ci continue; 1721cb0ef41Sopenharmony_ci } 1731cb0ef41Sopenharmony_ci } else if (resolvedDevice.length === 0) { 1741cb0ef41Sopenharmony_ci path = process.cwd(); 1751cb0ef41Sopenharmony_ci } else { 1761cb0ef41Sopenharmony_ci // Windows has the concept of drive-specific current working 1771cb0ef41Sopenharmony_ci // directories. If we've resolved a drive letter but not yet an 1781cb0ef41Sopenharmony_ci // absolute path, get cwd for that drive, or the process cwd if 1791cb0ef41Sopenharmony_ci // the drive cwd is not available. We're sure the device is not 1801cb0ef41Sopenharmony_ci // a UNC path at this points, because UNC paths are always absolute. 1811cb0ef41Sopenharmony_ci path = process.env[`=${resolvedDevice}`] || process.cwd(); 1821cb0ef41Sopenharmony_ci 1831cb0ef41Sopenharmony_ci // Verify that a cwd was found and that it actually points 1841cb0ef41Sopenharmony_ci // to our drive. If not, default to the drive's root. 1851cb0ef41Sopenharmony_ci if (path === undefined || 1861cb0ef41Sopenharmony_ci (StringPrototypeToLowerCase(StringPrototypeSlice(path, 0, 2)) !== 1871cb0ef41Sopenharmony_ci StringPrototypeToLowerCase(resolvedDevice) && 1881cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(path, 2) === CHAR_BACKWARD_SLASH)) { 1891cb0ef41Sopenharmony_ci path = `${resolvedDevice}\\`; 1901cb0ef41Sopenharmony_ci } 1911cb0ef41Sopenharmony_ci } 1921cb0ef41Sopenharmony_ci 1931cb0ef41Sopenharmony_ci const len = path.length; 1941cb0ef41Sopenharmony_ci let rootEnd = 0; 1951cb0ef41Sopenharmony_ci let device = ''; 1961cb0ef41Sopenharmony_ci let isAbsolute = false; 1971cb0ef41Sopenharmony_ci const code = StringPrototypeCharCodeAt(path, 0); 1981cb0ef41Sopenharmony_ci 1991cb0ef41Sopenharmony_ci // Try to match a root 2001cb0ef41Sopenharmony_ci if (len === 1) { 2011cb0ef41Sopenharmony_ci if (isPathSeparator(code)) { 2021cb0ef41Sopenharmony_ci // `path` contains just a path separator 2031cb0ef41Sopenharmony_ci rootEnd = 1; 2041cb0ef41Sopenharmony_ci isAbsolute = true; 2051cb0ef41Sopenharmony_ci } 2061cb0ef41Sopenharmony_ci } else if (isPathSeparator(code)) { 2071cb0ef41Sopenharmony_ci // Possible UNC root 2081cb0ef41Sopenharmony_ci 2091cb0ef41Sopenharmony_ci // If we started with a separator, we know we at least have an 2101cb0ef41Sopenharmony_ci // absolute path of some kind (UNC or otherwise) 2111cb0ef41Sopenharmony_ci isAbsolute = true; 2121cb0ef41Sopenharmony_ci 2131cb0ef41Sopenharmony_ci if (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) { 2141cb0ef41Sopenharmony_ci // Matched double path separator at beginning 2151cb0ef41Sopenharmony_ci let j = 2; 2161cb0ef41Sopenharmony_ci let last = j; 2171cb0ef41Sopenharmony_ci // Match 1 or more non-path separators 2181cb0ef41Sopenharmony_ci while (j < len && 2191cb0ef41Sopenharmony_ci !isPathSeparator(StringPrototypeCharCodeAt(path, j))) { 2201cb0ef41Sopenharmony_ci j++; 2211cb0ef41Sopenharmony_ci } 2221cb0ef41Sopenharmony_ci if (j < len && j !== last) { 2231cb0ef41Sopenharmony_ci const firstPart = StringPrototypeSlice(path, last, j); 2241cb0ef41Sopenharmony_ci // Matched! 2251cb0ef41Sopenharmony_ci last = j; 2261cb0ef41Sopenharmony_ci // Match 1 or more path separators 2271cb0ef41Sopenharmony_ci while (j < len && 2281cb0ef41Sopenharmony_ci isPathSeparator(StringPrototypeCharCodeAt(path, j))) { 2291cb0ef41Sopenharmony_ci j++; 2301cb0ef41Sopenharmony_ci } 2311cb0ef41Sopenharmony_ci if (j < len && j !== last) { 2321cb0ef41Sopenharmony_ci // Matched! 2331cb0ef41Sopenharmony_ci last = j; 2341cb0ef41Sopenharmony_ci // Match 1 or more non-path separators 2351cb0ef41Sopenharmony_ci while (j < len && 2361cb0ef41Sopenharmony_ci !isPathSeparator(StringPrototypeCharCodeAt(path, j))) { 2371cb0ef41Sopenharmony_ci j++; 2381cb0ef41Sopenharmony_ci } 2391cb0ef41Sopenharmony_ci if (j === len || j !== last) { 2401cb0ef41Sopenharmony_ci // We matched a UNC root 2411cb0ef41Sopenharmony_ci device = 2421cb0ef41Sopenharmony_ci `\\\\${firstPart}\\${StringPrototypeSlice(path, last, j)}`; 2431cb0ef41Sopenharmony_ci rootEnd = j; 2441cb0ef41Sopenharmony_ci } 2451cb0ef41Sopenharmony_ci } 2461cb0ef41Sopenharmony_ci } 2471cb0ef41Sopenharmony_ci } else { 2481cb0ef41Sopenharmony_ci rootEnd = 1; 2491cb0ef41Sopenharmony_ci } 2501cb0ef41Sopenharmony_ci } else if (isWindowsDeviceRoot(code) && 2511cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) { 2521cb0ef41Sopenharmony_ci // Possible device root 2531cb0ef41Sopenharmony_ci device = StringPrototypeSlice(path, 0, 2); 2541cb0ef41Sopenharmony_ci rootEnd = 2; 2551cb0ef41Sopenharmony_ci if (len > 2 && isPathSeparator(StringPrototypeCharCodeAt(path, 2))) { 2561cb0ef41Sopenharmony_ci // Treat separator following drive name as an absolute path 2571cb0ef41Sopenharmony_ci // indicator 2581cb0ef41Sopenharmony_ci isAbsolute = true; 2591cb0ef41Sopenharmony_ci rootEnd = 3; 2601cb0ef41Sopenharmony_ci } 2611cb0ef41Sopenharmony_ci } 2621cb0ef41Sopenharmony_ci 2631cb0ef41Sopenharmony_ci if (device.length > 0) { 2641cb0ef41Sopenharmony_ci if (resolvedDevice.length > 0) { 2651cb0ef41Sopenharmony_ci if (StringPrototypeToLowerCase(device) !== 2661cb0ef41Sopenharmony_ci StringPrototypeToLowerCase(resolvedDevice)) 2671cb0ef41Sopenharmony_ci // This path points to another device so it is not applicable 2681cb0ef41Sopenharmony_ci continue; 2691cb0ef41Sopenharmony_ci } else { 2701cb0ef41Sopenharmony_ci resolvedDevice = device; 2711cb0ef41Sopenharmony_ci } 2721cb0ef41Sopenharmony_ci } 2731cb0ef41Sopenharmony_ci 2741cb0ef41Sopenharmony_ci if (resolvedAbsolute) { 2751cb0ef41Sopenharmony_ci if (resolvedDevice.length > 0) 2761cb0ef41Sopenharmony_ci break; 2771cb0ef41Sopenharmony_ci } else { 2781cb0ef41Sopenharmony_ci resolvedTail = 2791cb0ef41Sopenharmony_ci `${StringPrototypeSlice(path, rootEnd)}\\${resolvedTail}`; 2801cb0ef41Sopenharmony_ci resolvedAbsolute = isAbsolute; 2811cb0ef41Sopenharmony_ci if (isAbsolute && resolvedDevice.length > 0) { 2821cb0ef41Sopenharmony_ci break; 2831cb0ef41Sopenharmony_ci } 2841cb0ef41Sopenharmony_ci } 2851cb0ef41Sopenharmony_ci } 2861cb0ef41Sopenharmony_ci 2871cb0ef41Sopenharmony_ci // At this point the path should be resolved to a full absolute path, 2881cb0ef41Sopenharmony_ci // but handle relative paths to be safe (might happen when process.cwd() 2891cb0ef41Sopenharmony_ci // fails) 2901cb0ef41Sopenharmony_ci 2911cb0ef41Sopenharmony_ci // Normalize the tail path 2921cb0ef41Sopenharmony_ci resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\', 2931cb0ef41Sopenharmony_ci isPathSeparator); 2941cb0ef41Sopenharmony_ci 2951cb0ef41Sopenharmony_ci return resolvedAbsolute ? 2961cb0ef41Sopenharmony_ci `${resolvedDevice}\\${resolvedTail}` : 2971cb0ef41Sopenharmony_ci `${resolvedDevice}${resolvedTail}` || '.'; 2981cb0ef41Sopenharmony_ci }, 2991cb0ef41Sopenharmony_ci 3001cb0ef41Sopenharmony_ci /** 3011cb0ef41Sopenharmony_ci * @param {string} path 3021cb0ef41Sopenharmony_ci * @returns {string} 3031cb0ef41Sopenharmony_ci */ 3041cb0ef41Sopenharmony_ci normalize(path) { 3051cb0ef41Sopenharmony_ci validateString(path, 'path'); 3061cb0ef41Sopenharmony_ci const len = path.length; 3071cb0ef41Sopenharmony_ci if (len === 0) 3081cb0ef41Sopenharmony_ci return '.'; 3091cb0ef41Sopenharmony_ci let rootEnd = 0; 3101cb0ef41Sopenharmony_ci let device; 3111cb0ef41Sopenharmony_ci let isAbsolute = false; 3121cb0ef41Sopenharmony_ci const code = StringPrototypeCharCodeAt(path, 0); 3131cb0ef41Sopenharmony_ci 3141cb0ef41Sopenharmony_ci // Try to match a root 3151cb0ef41Sopenharmony_ci if (len === 1) { 3161cb0ef41Sopenharmony_ci // `path` contains just a single char, exit early to avoid 3171cb0ef41Sopenharmony_ci // unnecessary work 3181cb0ef41Sopenharmony_ci return isPosixPathSeparator(code) ? '\\' : path; 3191cb0ef41Sopenharmony_ci } 3201cb0ef41Sopenharmony_ci if (isPathSeparator(code)) { 3211cb0ef41Sopenharmony_ci // Possible UNC root 3221cb0ef41Sopenharmony_ci 3231cb0ef41Sopenharmony_ci // If we started with a separator, we know we at least have an absolute 3241cb0ef41Sopenharmony_ci // path of some kind (UNC or otherwise) 3251cb0ef41Sopenharmony_ci isAbsolute = true; 3261cb0ef41Sopenharmony_ci 3271cb0ef41Sopenharmony_ci if (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) { 3281cb0ef41Sopenharmony_ci // Matched double path separator at beginning 3291cb0ef41Sopenharmony_ci let j = 2; 3301cb0ef41Sopenharmony_ci let last = j; 3311cb0ef41Sopenharmony_ci // Match 1 or more non-path separators 3321cb0ef41Sopenharmony_ci while (j < len && 3331cb0ef41Sopenharmony_ci !isPathSeparator(StringPrototypeCharCodeAt(path, j))) { 3341cb0ef41Sopenharmony_ci j++; 3351cb0ef41Sopenharmony_ci } 3361cb0ef41Sopenharmony_ci if (j < len && j !== last) { 3371cb0ef41Sopenharmony_ci const firstPart = StringPrototypeSlice(path, last, j); 3381cb0ef41Sopenharmony_ci // Matched! 3391cb0ef41Sopenharmony_ci last = j; 3401cb0ef41Sopenharmony_ci // Match 1 or more path separators 3411cb0ef41Sopenharmony_ci while (j < len && 3421cb0ef41Sopenharmony_ci isPathSeparator(StringPrototypeCharCodeAt(path, j))) { 3431cb0ef41Sopenharmony_ci j++; 3441cb0ef41Sopenharmony_ci } 3451cb0ef41Sopenharmony_ci if (j < len && j !== last) { 3461cb0ef41Sopenharmony_ci // Matched! 3471cb0ef41Sopenharmony_ci last = j; 3481cb0ef41Sopenharmony_ci // Match 1 or more non-path separators 3491cb0ef41Sopenharmony_ci while (j < len && 3501cb0ef41Sopenharmony_ci !isPathSeparator(StringPrototypeCharCodeAt(path, j))) { 3511cb0ef41Sopenharmony_ci j++; 3521cb0ef41Sopenharmony_ci } 3531cb0ef41Sopenharmony_ci if (j === len) { 3541cb0ef41Sopenharmony_ci // We matched a UNC root only 3551cb0ef41Sopenharmony_ci // Return the normalized version of the UNC root since there 3561cb0ef41Sopenharmony_ci // is nothing left to process 3571cb0ef41Sopenharmony_ci return `\\\\${firstPart}\\${StringPrototypeSlice(path, last)}\\`; 3581cb0ef41Sopenharmony_ci } 3591cb0ef41Sopenharmony_ci if (j !== last) { 3601cb0ef41Sopenharmony_ci // We matched a UNC root with leftovers 3611cb0ef41Sopenharmony_ci device = 3621cb0ef41Sopenharmony_ci `\\\\${firstPart}\\${StringPrototypeSlice(path, last, j)}`; 3631cb0ef41Sopenharmony_ci rootEnd = j; 3641cb0ef41Sopenharmony_ci } 3651cb0ef41Sopenharmony_ci } 3661cb0ef41Sopenharmony_ci } 3671cb0ef41Sopenharmony_ci } else { 3681cb0ef41Sopenharmony_ci rootEnd = 1; 3691cb0ef41Sopenharmony_ci } 3701cb0ef41Sopenharmony_ci } else if (isWindowsDeviceRoot(code) && 3711cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) { 3721cb0ef41Sopenharmony_ci // Possible device root 3731cb0ef41Sopenharmony_ci device = StringPrototypeSlice(path, 0, 2); 3741cb0ef41Sopenharmony_ci rootEnd = 2; 3751cb0ef41Sopenharmony_ci if (len > 2 && isPathSeparator(StringPrototypeCharCodeAt(path, 2))) { 3761cb0ef41Sopenharmony_ci // Treat separator following drive name as an absolute path 3771cb0ef41Sopenharmony_ci // indicator 3781cb0ef41Sopenharmony_ci isAbsolute = true; 3791cb0ef41Sopenharmony_ci rootEnd = 3; 3801cb0ef41Sopenharmony_ci } 3811cb0ef41Sopenharmony_ci } 3821cb0ef41Sopenharmony_ci 3831cb0ef41Sopenharmony_ci let tail = rootEnd < len ? 3841cb0ef41Sopenharmony_ci normalizeString(StringPrototypeSlice(path, rootEnd), 3851cb0ef41Sopenharmony_ci !isAbsolute, '\\', isPathSeparator) : 3861cb0ef41Sopenharmony_ci ''; 3871cb0ef41Sopenharmony_ci if (tail.length === 0 && !isAbsolute) 3881cb0ef41Sopenharmony_ci tail = '.'; 3891cb0ef41Sopenharmony_ci if (tail.length > 0 && 3901cb0ef41Sopenharmony_ci isPathSeparator(StringPrototypeCharCodeAt(path, len - 1))) 3911cb0ef41Sopenharmony_ci tail += '\\'; 3921cb0ef41Sopenharmony_ci if (device === undefined) { 3931cb0ef41Sopenharmony_ci return isAbsolute ? `\\${tail}` : tail; 3941cb0ef41Sopenharmony_ci } 3951cb0ef41Sopenharmony_ci return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`; 3961cb0ef41Sopenharmony_ci }, 3971cb0ef41Sopenharmony_ci 3981cb0ef41Sopenharmony_ci /** 3991cb0ef41Sopenharmony_ci * @param {string} path 4001cb0ef41Sopenharmony_ci * @returns {boolean} 4011cb0ef41Sopenharmony_ci */ 4021cb0ef41Sopenharmony_ci isAbsolute(path) { 4031cb0ef41Sopenharmony_ci validateString(path, 'path'); 4041cb0ef41Sopenharmony_ci const len = path.length; 4051cb0ef41Sopenharmony_ci if (len === 0) 4061cb0ef41Sopenharmony_ci return false; 4071cb0ef41Sopenharmony_ci 4081cb0ef41Sopenharmony_ci const code = StringPrototypeCharCodeAt(path, 0); 4091cb0ef41Sopenharmony_ci return isPathSeparator(code) || 4101cb0ef41Sopenharmony_ci // Possible device root 4111cb0ef41Sopenharmony_ci (len > 2 && 4121cb0ef41Sopenharmony_ci isWindowsDeviceRoot(code) && 4131cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(path, 1) === CHAR_COLON && 4141cb0ef41Sopenharmony_ci isPathSeparator(StringPrototypeCharCodeAt(path, 2))); 4151cb0ef41Sopenharmony_ci }, 4161cb0ef41Sopenharmony_ci 4171cb0ef41Sopenharmony_ci /** 4181cb0ef41Sopenharmony_ci * @param {...string} args 4191cb0ef41Sopenharmony_ci * @returns {string} 4201cb0ef41Sopenharmony_ci */ 4211cb0ef41Sopenharmony_ci join(...args) { 4221cb0ef41Sopenharmony_ci if (args.length === 0) 4231cb0ef41Sopenharmony_ci return '.'; 4241cb0ef41Sopenharmony_ci 4251cb0ef41Sopenharmony_ci let joined; 4261cb0ef41Sopenharmony_ci let firstPart; 4271cb0ef41Sopenharmony_ci for (let i = 0; i < args.length; ++i) { 4281cb0ef41Sopenharmony_ci const arg = args[i]; 4291cb0ef41Sopenharmony_ci validateString(arg, 'path'); 4301cb0ef41Sopenharmony_ci if (arg.length > 0) { 4311cb0ef41Sopenharmony_ci if (joined === undefined) 4321cb0ef41Sopenharmony_ci joined = firstPart = arg; 4331cb0ef41Sopenharmony_ci else 4341cb0ef41Sopenharmony_ci joined += `\\${arg}`; 4351cb0ef41Sopenharmony_ci } 4361cb0ef41Sopenharmony_ci } 4371cb0ef41Sopenharmony_ci 4381cb0ef41Sopenharmony_ci if (joined === undefined) 4391cb0ef41Sopenharmony_ci return '.'; 4401cb0ef41Sopenharmony_ci 4411cb0ef41Sopenharmony_ci // Make sure that the joined path doesn't start with two slashes, because 4421cb0ef41Sopenharmony_ci // normalize() will mistake it for a UNC path then. 4431cb0ef41Sopenharmony_ci // 4441cb0ef41Sopenharmony_ci // This step is skipped when it is very clear that the user actually 4451cb0ef41Sopenharmony_ci // intended to point at a UNC path. This is assumed when the first 4461cb0ef41Sopenharmony_ci // non-empty string arguments starts with exactly two slashes followed by 4471cb0ef41Sopenharmony_ci // at least one more non-slash character. 4481cb0ef41Sopenharmony_ci // 4491cb0ef41Sopenharmony_ci // Note that for normalize() to treat a path as a UNC path it needs to 4501cb0ef41Sopenharmony_ci // have at least 2 components, so we don't filter for that here. 4511cb0ef41Sopenharmony_ci // This means that the user can use join to construct UNC paths from 4521cb0ef41Sopenharmony_ci // a server name and a share name; for example: 4531cb0ef41Sopenharmony_ci // path.join('//server', 'share') -> '\\\\server\\share\\') 4541cb0ef41Sopenharmony_ci let needsReplace = true; 4551cb0ef41Sopenharmony_ci let slashCount = 0; 4561cb0ef41Sopenharmony_ci if (isPathSeparator(StringPrototypeCharCodeAt(firstPart, 0))) { 4571cb0ef41Sopenharmony_ci ++slashCount; 4581cb0ef41Sopenharmony_ci const firstLen = firstPart.length; 4591cb0ef41Sopenharmony_ci if (firstLen > 1 && 4601cb0ef41Sopenharmony_ci isPathSeparator(StringPrototypeCharCodeAt(firstPart, 1))) { 4611cb0ef41Sopenharmony_ci ++slashCount; 4621cb0ef41Sopenharmony_ci if (firstLen > 2) { 4631cb0ef41Sopenharmony_ci if (isPathSeparator(StringPrototypeCharCodeAt(firstPart, 2))) 4641cb0ef41Sopenharmony_ci ++slashCount; 4651cb0ef41Sopenharmony_ci else { 4661cb0ef41Sopenharmony_ci // We matched a UNC path in the first part 4671cb0ef41Sopenharmony_ci needsReplace = false; 4681cb0ef41Sopenharmony_ci } 4691cb0ef41Sopenharmony_ci } 4701cb0ef41Sopenharmony_ci } 4711cb0ef41Sopenharmony_ci } 4721cb0ef41Sopenharmony_ci if (needsReplace) { 4731cb0ef41Sopenharmony_ci // Find any more consecutive slashes we need to replace 4741cb0ef41Sopenharmony_ci while (slashCount < joined.length && 4751cb0ef41Sopenharmony_ci isPathSeparator(StringPrototypeCharCodeAt(joined, slashCount))) { 4761cb0ef41Sopenharmony_ci slashCount++; 4771cb0ef41Sopenharmony_ci } 4781cb0ef41Sopenharmony_ci 4791cb0ef41Sopenharmony_ci // Replace the slashes if needed 4801cb0ef41Sopenharmony_ci if (slashCount >= 2) 4811cb0ef41Sopenharmony_ci joined = `\\${StringPrototypeSlice(joined, slashCount)}`; 4821cb0ef41Sopenharmony_ci } 4831cb0ef41Sopenharmony_ci 4841cb0ef41Sopenharmony_ci return win32.normalize(joined); 4851cb0ef41Sopenharmony_ci }, 4861cb0ef41Sopenharmony_ci 4871cb0ef41Sopenharmony_ci /** 4881cb0ef41Sopenharmony_ci * It will solve the relative path from `from` to `to`, for instance 4891cb0ef41Sopenharmony_ci * from = 'C:\\orandea\\test\\aaa' 4901cb0ef41Sopenharmony_ci * to = 'C:\\orandea\\impl\\bbb' 4911cb0ef41Sopenharmony_ci * The output of the function should be: '..\\..\\impl\\bbb' 4921cb0ef41Sopenharmony_ci * @param {string} from 4931cb0ef41Sopenharmony_ci * @param {string} to 4941cb0ef41Sopenharmony_ci * @returns {string} 4951cb0ef41Sopenharmony_ci */ 4961cb0ef41Sopenharmony_ci relative(from, to) { 4971cb0ef41Sopenharmony_ci validateString(from, 'from'); 4981cb0ef41Sopenharmony_ci validateString(to, 'to'); 4991cb0ef41Sopenharmony_ci 5001cb0ef41Sopenharmony_ci if (from === to) 5011cb0ef41Sopenharmony_ci return ''; 5021cb0ef41Sopenharmony_ci 5031cb0ef41Sopenharmony_ci const fromOrig = win32.resolve(from); 5041cb0ef41Sopenharmony_ci const toOrig = win32.resolve(to); 5051cb0ef41Sopenharmony_ci 5061cb0ef41Sopenharmony_ci if (fromOrig === toOrig) 5071cb0ef41Sopenharmony_ci return ''; 5081cb0ef41Sopenharmony_ci 5091cb0ef41Sopenharmony_ci from = StringPrototypeToLowerCase(fromOrig); 5101cb0ef41Sopenharmony_ci to = StringPrototypeToLowerCase(toOrig); 5111cb0ef41Sopenharmony_ci 5121cb0ef41Sopenharmony_ci if (from === to) 5131cb0ef41Sopenharmony_ci return ''; 5141cb0ef41Sopenharmony_ci 5151cb0ef41Sopenharmony_ci // Trim any leading backslashes 5161cb0ef41Sopenharmony_ci let fromStart = 0; 5171cb0ef41Sopenharmony_ci while (fromStart < from.length && 5181cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(from, fromStart) === CHAR_BACKWARD_SLASH) { 5191cb0ef41Sopenharmony_ci fromStart++; 5201cb0ef41Sopenharmony_ci } 5211cb0ef41Sopenharmony_ci // Trim trailing backslashes (applicable to UNC paths only) 5221cb0ef41Sopenharmony_ci let fromEnd = from.length; 5231cb0ef41Sopenharmony_ci while ( 5241cb0ef41Sopenharmony_ci fromEnd - 1 > fromStart && 5251cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(from, fromEnd - 1) === CHAR_BACKWARD_SLASH 5261cb0ef41Sopenharmony_ci ) { 5271cb0ef41Sopenharmony_ci fromEnd--; 5281cb0ef41Sopenharmony_ci } 5291cb0ef41Sopenharmony_ci const fromLen = fromEnd - fromStart; 5301cb0ef41Sopenharmony_ci 5311cb0ef41Sopenharmony_ci // Trim any leading backslashes 5321cb0ef41Sopenharmony_ci let toStart = 0; 5331cb0ef41Sopenharmony_ci while (toStart < to.length && 5341cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(to, toStart) === CHAR_BACKWARD_SLASH) { 5351cb0ef41Sopenharmony_ci toStart++; 5361cb0ef41Sopenharmony_ci } 5371cb0ef41Sopenharmony_ci // Trim trailing backslashes (applicable to UNC paths only) 5381cb0ef41Sopenharmony_ci let toEnd = to.length; 5391cb0ef41Sopenharmony_ci while (toEnd - 1 > toStart && 5401cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(to, toEnd - 1) === CHAR_BACKWARD_SLASH) { 5411cb0ef41Sopenharmony_ci toEnd--; 5421cb0ef41Sopenharmony_ci } 5431cb0ef41Sopenharmony_ci const toLen = toEnd - toStart; 5441cb0ef41Sopenharmony_ci 5451cb0ef41Sopenharmony_ci // Compare paths to find the longest common path from root 5461cb0ef41Sopenharmony_ci const length = fromLen < toLen ? fromLen : toLen; 5471cb0ef41Sopenharmony_ci let lastCommonSep = -1; 5481cb0ef41Sopenharmony_ci let i = 0; 5491cb0ef41Sopenharmony_ci for (; i < length; i++) { 5501cb0ef41Sopenharmony_ci const fromCode = StringPrototypeCharCodeAt(from, fromStart + i); 5511cb0ef41Sopenharmony_ci if (fromCode !== StringPrototypeCharCodeAt(to, toStart + i)) 5521cb0ef41Sopenharmony_ci break; 5531cb0ef41Sopenharmony_ci else if (fromCode === CHAR_BACKWARD_SLASH) 5541cb0ef41Sopenharmony_ci lastCommonSep = i; 5551cb0ef41Sopenharmony_ci } 5561cb0ef41Sopenharmony_ci 5571cb0ef41Sopenharmony_ci // We found a mismatch before the first common path separator was seen, so 5581cb0ef41Sopenharmony_ci // return the original `to`. 5591cb0ef41Sopenharmony_ci if (i !== length) { 5601cb0ef41Sopenharmony_ci if (lastCommonSep === -1) 5611cb0ef41Sopenharmony_ci return toOrig; 5621cb0ef41Sopenharmony_ci } else { 5631cb0ef41Sopenharmony_ci if (toLen > length) { 5641cb0ef41Sopenharmony_ci if (StringPrototypeCharCodeAt(to, toStart + i) === 5651cb0ef41Sopenharmony_ci CHAR_BACKWARD_SLASH) { 5661cb0ef41Sopenharmony_ci // We get here if `from` is the exact base path for `to`. 5671cb0ef41Sopenharmony_ci // For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz' 5681cb0ef41Sopenharmony_ci return StringPrototypeSlice(toOrig, toStart + i + 1); 5691cb0ef41Sopenharmony_ci } 5701cb0ef41Sopenharmony_ci if (i === 2) { 5711cb0ef41Sopenharmony_ci // We get here if `from` is the device root. 5721cb0ef41Sopenharmony_ci // For example: from='C:\\'; to='C:\\foo' 5731cb0ef41Sopenharmony_ci return StringPrototypeSlice(toOrig, toStart + i); 5741cb0ef41Sopenharmony_ci } 5751cb0ef41Sopenharmony_ci } 5761cb0ef41Sopenharmony_ci if (fromLen > length) { 5771cb0ef41Sopenharmony_ci if (StringPrototypeCharCodeAt(from, fromStart + i) === 5781cb0ef41Sopenharmony_ci CHAR_BACKWARD_SLASH) { 5791cb0ef41Sopenharmony_ci // We get here if `to` is the exact base path for `from`. 5801cb0ef41Sopenharmony_ci // For example: from='C:\\foo\\bar'; to='C:\\foo' 5811cb0ef41Sopenharmony_ci lastCommonSep = i; 5821cb0ef41Sopenharmony_ci } else if (i === 2) { 5831cb0ef41Sopenharmony_ci // We get here if `to` is the device root. 5841cb0ef41Sopenharmony_ci // For example: from='C:\\foo\\bar'; to='C:\\' 5851cb0ef41Sopenharmony_ci lastCommonSep = 3; 5861cb0ef41Sopenharmony_ci } 5871cb0ef41Sopenharmony_ci } 5881cb0ef41Sopenharmony_ci if (lastCommonSep === -1) 5891cb0ef41Sopenharmony_ci lastCommonSep = 0; 5901cb0ef41Sopenharmony_ci } 5911cb0ef41Sopenharmony_ci 5921cb0ef41Sopenharmony_ci let out = ''; 5931cb0ef41Sopenharmony_ci // Generate the relative path based on the path difference between `to` and 5941cb0ef41Sopenharmony_ci // `from` 5951cb0ef41Sopenharmony_ci for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { 5961cb0ef41Sopenharmony_ci if (i === fromEnd || 5971cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(from, i) === CHAR_BACKWARD_SLASH) { 5981cb0ef41Sopenharmony_ci out += out.length === 0 ? '..' : '\\..'; 5991cb0ef41Sopenharmony_ci } 6001cb0ef41Sopenharmony_ci } 6011cb0ef41Sopenharmony_ci 6021cb0ef41Sopenharmony_ci toStart += lastCommonSep; 6031cb0ef41Sopenharmony_ci 6041cb0ef41Sopenharmony_ci // Lastly, append the rest of the destination (`to`) path that comes after 6051cb0ef41Sopenharmony_ci // the common path parts 6061cb0ef41Sopenharmony_ci if (out.length > 0) 6071cb0ef41Sopenharmony_ci return `${out}${StringPrototypeSlice(toOrig, toStart, toEnd)}`; 6081cb0ef41Sopenharmony_ci 6091cb0ef41Sopenharmony_ci if (StringPrototypeCharCodeAt(toOrig, toStart) === CHAR_BACKWARD_SLASH) 6101cb0ef41Sopenharmony_ci ++toStart; 6111cb0ef41Sopenharmony_ci return StringPrototypeSlice(toOrig, toStart, toEnd); 6121cb0ef41Sopenharmony_ci }, 6131cb0ef41Sopenharmony_ci 6141cb0ef41Sopenharmony_ci /** 6151cb0ef41Sopenharmony_ci * @param {string} path 6161cb0ef41Sopenharmony_ci * @returns {string} 6171cb0ef41Sopenharmony_ci */ 6181cb0ef41Sopenharmony_ci toNamespacedPath(path) { 6191cb0ef41Sopenharmony_ci // Note: this will *probably* throw somewhere. 6201cb0ef41Sopenharmony_ci if (typeof path !== 'string' || path.length === 0) 6211cb0ef41Sopenharmony_ci return path; 6221cb0ef41Sopenharmony_ci 6231cb0ef41Sopenharmony_ci const resolvedPath = win32.resolve(path); 6241cb0ef41Sopenharmony_ci 6251cb0ef41Sopenharmony_ci if (resolvedPath.length <= 2) 6261cb0ef41Sopenharmony_ci return path; 6271cb0ef41Sopenharmony_ci 6281cb0ef41Sopenharmony_ci if (StringPrototypeCharCodeAt(resolvedPath, 0) === CHAR_BACKWARD_SLASH) { 6291cb0ef41Sopenharmony_ci // Possible UNC root 6301cb0ef41Sopenharmony_ci if (StringPrototypeCharCodeAt(resolvedPath, 1) === CHAR_BACKWARD_SLASH) { 6311cb0ef41Sopenharmony_ci const code = StringPrototypeCharCodeAt(resolvedPath, 2); 6321cb0ef41Sopenharmony_ci if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) { 6331cb0ef41Sopenharmony_ci // Matched non-long UNC root, convert the path to a long UNC path 6341cb0ef41Sopenharmony_ci return `\\\\?\\UNC\\${StringPrototypeSlice(resolvedPath, 2)}`; 6351cb0ef41Sopenharmony_ci } 6361cb0ef41Sopenharmony_ci } 6371cb0ef41Sopenharmony_ci } else if ( 6381cb0ef41Sopenharmony_ci isWindowsDeviceRoot(StringPrototypeCharCodeAt(resolvedPath, 0)) && 6391cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(resolvedPath, 1) === CHAR_COLON && 6401cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(resolvedPath, 2) === CHAR_BACKWARD_SLASH 6411cb0ef41Sopenharmony_ci ) { 6421cb0ef41Sopenharmony_ci // Matched device root, convert the path to a long UNC path 6431cb0ef41Sopenharmony_ci return `\\\\?\\${resolvedPath}`; 6441cb0ef41Sopenharmony_ci } 6451cb0ef41Sopenharmony_ci 6461cb0ef41Sopenharmony_ci return path; 6471cb0ef41Sopenharmony_ci }, 6481cb0ef41Sopenharmony_ci 6491cb0ef41Sopenharmony_ci /** 6501cb0ef41Sopenharmony_ci * @param {string} path 6511cb0ef41Sopenharmony_ci * @returns {string} 6521cb0ef41Sopenharmony_ci */ 6531cb0ef41Sopenharmony_ci dirname(path) { 6541cb0ef41Sopenharmony_ci validateString(path, 'path'); 6551cb0ef41Sopenharmony_ci const len = path.length; 6561cb0ef41Sopenharmony_ci if (len === 0) 6571cb0ef41Sopenharmony_ci return '.'; 6581cb0ef41Sopenharmony_ci let rootEnd = -1; 6591cb0ef41Sopenharmony_ci let offset = 0; 6601cb0ef41Sopenharmony_ci const code = StringPrototypeCharCodeAt(path, 0); 6611cb0ef41Sopenharmony_ci 6621cb0ef41Sopenharmony_ci if (len === 1) { 6631cb0ef41Sopenharmony_ci // `path` contains just a path separator, exit early to avoid 6641cb0ef41Sopenharmony_ci // unnecessary work or a dot. 6651cb0ef41Sopenharmony_ci return isPathSeparator(code) ? path : '.'; 6661cb0ef41Sopenharmony_ci } 6671cb0ef41Sopenharmony_ci 6681cb0ef41Sopenharmony_ci // Try to match a root 6691cb0ef41Sopenharmony_ci if (isPathSeparator(code)) { 6701cb0ef41Sopenharmony_ci // Possible UNC root 6711cb0ef41Sopenharmony_ci 6721cb0ef41Sopenharmony_ci rootEnd = offset = 1; 6731cb0ef41Sopenharmony_ci 6741cb0ef41Sopenharmony_ci if (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) { 6751cb0ef41Sopenharmony_ci // Matched double path separator at beginning 6761cb0ef41Sopenharmony_ci let j = 2; 6771cb0ef41Sopenharmony_ci let last = j; 6781cb0ef41Sopenharmony_ci // Match 1 or more non-path separators 6791cb0ef41Sopenharmony_ci while (j < len && 6801cb0ef41Sopenharmony_ci !isPathSeparator(StringPrototypeCharCodeAt(path, j))) { 6811cb0ef41Sopenharmony_ci j++; 6821cb0ef41Sopenharmony_ci } 6831cb0ef41Sopenharmony_ci if (j < len && j !== last) { 6841cb0ef41Sopenharmony_ci // Matched! 6851cb0ef41Sopenharmony_ci last = j; 6861cb0ef41Sopenharmony_ci // Match 1 or more path separators 6871cb0ef41Sopenharmony_ci while (j < len && 6881cb0ef41Sopenharmony_ci isPathSeparator(StringPrototypeCharCodeAt(path, j))) { 6891cb0ef41Sopenharmony_ci j++; 6901cb0ef41Sopenharmony_ci } 6911cb0ef41Sopenharmony_ci if (j < len && j !== last) { 6921cb0ef41Sopenharmony_ci // Matched! 6931cb0ef41Sopenharmony_ci last = j; 6941cb0ef41Sopenharmony_ci // Match 1 or more non-path separators 6951cb0ef41Sopenharmony_ci while (j < len && 6961cb0ef41Sopenharmony_ci !isPathSeparator(StringPrototypeCharCodeAt(path, j))) { 6971cb0ef41Sopenharmony_ci j++; 6981cb0ef41Sopenharmony_ci } 6991cb0ef41Sopenharmony_ci if (j === len) { 7001cb0ef41Sopenharmony_ci // We matched a UNC root only 7011cb0ef41Sopenharmony_ci return path; 7021cb0ef41Sopenharmony_ci } 7031cb0ef41Sopenharmony_ci if (j !== last) { 7041cb0ef41Sopenharmony_ci // We matched a UNC root with leftovers 7051cb0ef41Sopenharmony_ci 7061cb0ef41Sopenharmony_ci // Offset by 1 to include the separator after the UNC root to 7071cb0ef41Sopenharmony_ci // treat it as a "normal root" on top of a (UNC) root 7081cb0ef41Sopenharmony_ci rootEnd = offset = j + 1; 7091cb0ef41Sopenharmony_ci } 7101cb0ef41Sopenharmony_ci } 7111cb0ef41Sopenharmony_ci } 7121cb0ef41Sopenharmony_ci } 7131cb0ef41Sopenharmony_ci // Possible device root 7141cb0ef41Sopenharmony_ci } else if (isWindowsDeviceRoot(code) && 7151cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) { 7161cb0ef41Sopenharmony_ci rootEnd = 7171cb0ef41Sopenharmony_ci len > 2 && isPathSeparator(StringPrototypeCharCodeAt(path, 2)) ? 3 : 2; 7181cb0ef41Sopenharmony_ci offset = rootEnd; 7191cb0ef41Sopenharmony_ci } 7201cb0ef41Sopenharmony_ci 7211cb0ef41Sopenharmony_ci let end = -1; 7221cb0ef41Sopenharmony_ci let matchedSlash = true; 7231cb0ef41Sopenharmony_ci for (let i = len - 1; i >= offset; --i) { 7241cb0ef41Sopenharmony_ci if (isPathSeparator(StringPrototypeCharCodeAt(path, i))) { 7251cb0ef41Sopenharmony_ci if (!matchedSlash) { 7261cb0ef41Sopenharmony_ci end = i; 7271cb0ef41Sopenharmony_ci break; 7281cb0ef41Sopenharmony_ci } 7291cb0ef41Sopenharmony_ci } else { 7301cb0ef41Sopenharmony_ci // We saw the first non-path separator 7311cb0ef41Sopenharmony_ci matchedSlash = false; 7321cb0ef41Sopenharmony_ci } 7331cb0ef41Sopenharmony_ci } 7341cb0ef41Sopenharmony_ci 7351cb0ef41Sopenharmony_ci if (end === -1) { 7361cb0ef41Sopenharmony_ci if (rootEnd === -1) 7371cb0ef41Sopenharmony_ci return '.'; 7381cb0ef41Sopenharmony_ci 7391cb0ef41Sopenharmony_ci end = rootEnd; 7401cb0ef41Sopenharmony_ci } 7411cb0ef41Sopenharmony_ci return StringPrototypeSlice(path, 0, end); 7421cb0ef41Sopenharmony_ci }, 7431cb0ef41Sopenharmony_ci 7441cb0ef41Sopenharmony_ci /** 7451cb0ef41Sopenharmony_ci * @param {string} path 7461cb0ef41Sopenharmony_ci * @param {string} [suffix] 7471cb0ef41Sopenharmony_ci * @returns {string} 7481cb0ef41Sopenharmony_ci */ 7491cb0ef41Sopenharmony_ci basename(path, suffix) { 7501cb0ef41Sopenharmony_ci if (suffix !== undefined) 7511cb0ef41Sopenharmony_ci validateString(suffix, 'ext'); 7521cb0ef41Sopenharmony_ci validateString(path, 'path'); 7531cb0ef41Sopenharmony_ci let start = 0; 7541cb0ef41Sopenharmony_ci let end = -1; 7551cb0ef41Sopenharmony_ci let matchedSlash = true; 7561cb0ef41Sopenharmony_ci 7571cb0ef41Sopenharmony_ci // Check for a drive letter prefix so as not to mistake the following 7581cb0ef41Sopenharmony_ci // path separator as an extra separator at the end of the path that can be 7591cb0ef41Sopenharmony_ci // disregarded 7601cb0ef41Sopenharmony_ci if (path.length >= 2 && 7611cb0ef41Sopenharmony_ci isWindowsDeviceRoot(StringPrototypeCharCodeAt(path, 0)) && 7621cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) { 7631cb0ef41Sopenharmony_ci start = 2; 7641cb0ef41Sopenharmony_ci } 7651cb0ef41Sopenharmony_ci 7661cb0ef41Sopenharmony_ci if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) { 7671cb0ef41Sopenharmony_ci if (suffix === path) 7681cb0ef41Sopenharmony_ci return ''; 7691cb0ef41Sopenharmony_ci let extIdx = suffix.length - 1; 7701cb0ef41Sopenharmony_ci let firstNonSlashEnd = -1; 7711cb0ef41Sopenharmony_ci for (let i = path.length - 1; i >= start; --i) { 7721cb0ef41Sopenharmony_ci const code = StringPrototypeCharCodeAt(path, i); 7731cb0ef41Sopenharmony_ci if (isPathSeparator(code)) { 7741cb0ef41Sopenharmony_ci // If we reached a path separator that was not part of a set of path 7751cb0ef41Sopenharmony_ci // separators at the end of the string, stop now 7761cb0ef41Sopenharmony_ci if (!matchedSlash) { 7771cb0ef41Sopenharmony_ci start = i + 1; 7781cb0ef41Sopenharmony_ci break; 7791cb0ef41Sopenharmony_ci } 7801cb0ef41Sopenharmony_ci } else { 7811cb0ef41Sopenharmony_ci if (firstNonSlashEnd === -1) { 7821cb0ef41Sopenharmony_ci // We saw the first non-path separator, remember this index in case 7831cb0ef41Sopenharmony_ci // we need it if the extension ends up not matching 7841cb0ef41Sopenharmony_ci matchedSlash = false; 7851cb0ef41Sopenharmony_ci firstNonSlashEnd = i + 1; 7861cb0ef41Sopenharmony_ci } 7871cb0ef41Sopenharmony_ci if (extIdx >= 0) { 7881cb0ef41Sopenharmony_ci // Try to match the explicit extension 7891cb0ef41Sopenharmony_ci if (code === StringPrototypeCharCodeAt(suffix, extIdx)) { 7901cb0ef41Sopenharmony_ci if (--extIdx === -1) { 7911cb0ef41Sopenharmony_ci // We matched the extension, so mark this as the end of our path 7921cb0ef41Sopenharmony_ci // component 7931cb0ef41Sopenharmony_ci end = i; 7941cb0ef41Sopenharmony_ci } 7951cb0ef41Sopenharmony_ci } else { 7961cb0ef41Sopenharmony_ci // Extension does not match, so our result is the entire path 7971cb0ef41Sopenharmony_ci // component 7981cb0ef41Sopenharmony_ci extIdx = -1; 7991cb0ef41Sopenharmony_ci end = firstNonSlashEnd; 8001cb0ef41Sopenharmony_ci } 8011cb0ef41Sopenharmony_ci } 8021cb0ef41Sopenharmony_ci } 8031cb0ef41Sopenharmony_ci } 8041cb0ef41Sopenharmony_ci 8051cb0ef41Sopenharmony_ci if (start === end) 8061cb0ef41Sopenharmony_ci end = firstNonSlashEnd; 8071cb0ef41Sopenharmony_ci else if (end === -1) 8081cb0ef41Sopenharmony_ci end = path.length; 8091cb0ef41Sopenharmony_ci return StringPrototypeSlice(path, start, end); 8101cb0ef41Sopenharmony_ci } 8111cb0ef41Sopenharmony_ci for (let i = path.length - 1; i >= start; --i) { 8121cb0ef41Sopenharmony_ci if (isPathSeparator(StringPrototypeCharCodeAt(path, i))) { 8131cb0ef41Sopenharmony_ci // If we reached a path separator that was not part of a set of path 8141cb0ef41Sopenharmony_ci // separators at the end of the string, stop now 8151cb0ef41Sopenharmony_ci if (!matchedSlash) { 8161cb0ef41Sopenharmony_ci start = i + 1; 8171cb0ef41Sopenharmony_ci break; 8181cb0ef41Sopenharmony_ci } 8191cb0ef41Sopenharmony_ci } else if (end === -1) { 8201cb0ef41Sopenharmony_ci // We saw the first non-path separator, mark this as the end of our 8211cb0ef41Sopenharmony_ci // path component 8221cb0ef41Sopenharmony_ci matchedSlash = false; 8231cb0ef41Sopenharmony_ci end = i + 1; 8241cb0ef41Sopenharmony_ci } 8251cb0ef41Sopenharmony_ci } 8261cb0ef41Sopenharmony_ci 8271cb0ef41Sopenharmony_ci if (end === -1) 8281cb0ef41Sopenharmony_ci return ''; 8291cb0ef41Sopenharmony_ci return StringPrototypeSlice(path, start, end); 8301cb0ef41Sopenharmony_ci }, 8311cb0ef41Sopenharmony_ci 8321cb0ef41Sopenharmony_ci /** 8331cb0ef41Sopenharmony_ci * @param {string} path 8341cb0ef41Sopenharmony_ci * @returns {string} 8351cb0ef41Sopenharmony_ci */ 8361cb0ef41Sopenharmony_ci extname(path) { 8371cb0ef41Sopenharmony_ci validateString(path, 'path'); 8381cb0ef41Sopenharmony_ci let start = 0; 8391cb0ef41Sopenharmony_ci let startDot = -1; 8401cb0ef41Sopenharmony_ci let startPart = 0; 8411cb0ef41Sopenharmony_ci let end = -1; 8421cb0ef41Sopenharmony_ci let matchedSlash = true; 8431cb0ef41Sopenharmony_ci // Track the state of characters (if any) we see before our first dot and 8441cb0ef41Sopenharmony_ci // after any path separator we find 8451cb0ef41Sopenharmony_ci let preDotState = 0; 8461cb0ef41Sopenharmony_ci 8471cb0ef41Sopenharmony_ci // Check for a drive letter prefix so as not to mistake the following 8481cb0ef41Sopenharmony_ci // path separator as an extra separator at the end of the path that can be 8491cb0ef41Sopenharmony_ci // disregarded 8501cb0ef41Sopenharmony_ci 8511cb0ef41Sopenharmony_ci if (path.length >= 2 && 8521cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(path, 1) === CHAR_COLON && 8531cb0ef41Sopenharmony_ci isWindowsDeviceRoot(StringPrototypeCharCodeAt(path, 0))) { 8541cb0ef41Sopenharmony_ci start = startPart = 2; 8551cb0ef41Sopenharmony_ci } 8561cb0ef41Sopenharmony_ci 8571cb0ef41Sopenharmony_ci for (let i = path.length - 1; i >= start; --i) { 8581cb0ef41Sopenharmony_ci const code = StringPrototypeCharCodeAt(path, i); 8591cb0ef41Sopenharmony_ci if (isPathSeparator(code)) { 8601cb0ef41Sopenharmony_ci // If we reached a path separator that was not part of a set of path 8611cb0ef41Sopenharmony_ci // separators at the end of the string, stop now 8621cb0ef41Sopenharmony_ci if (!matchedSlash) { 8631cb0ef41Sopenharmony_ci startPart = i + 1; 8641cb0ef41Sopenharmony_ci break; 8651cb0ef41Sopenharmony_ci } 8661cb0ef41Sopenharmony_ci continue; 8671cb0ef41Sopenharmony_ci } 8681cb0ef41Sopenharmony_ci if (end === -1) { 8691cb0ef41Sopenharmony_ci // We saw the first non-path separator, mark this as the end of our 8701cb0ef41Sopenharmony_ci // extension 8711cb0ef41Sopenharmony_ci matchedSlash = false; 8721cb0ef41Sopenharmony_ci end = i + 1; 8731cb0ef41Sopenharmony_ci } 8741cb0ef41Sopenharmony_ci if (code === CHAR_DOT) { 8751cb0ef41Sopenharmony_ci // If this is our first dot, mark it as the start of our extension 8761cb0ef41Sopenharmony_ci if (startDot === -1) 8771cb0ef41Sopenharmony_ci startDot = i; 8781cb0ef41Sopenharmony_ci else if (preDotState !== 1) 8791cb0ef41Sopenharmony_ci preDotState = 1; 8801cb0ef41Sopenharmony_ci } else if (startDot !== -1) { 8811cb0ef41Sopenharmony_ci // We saw a non-dot and non-path separator before our dot, so we should 8821cb0ef41Sopenharmony_ci // have a good chance at having a non-empty extension 8831cb0ef41Sopenharmony_ci preDotState = -1; 8841cb0ef41Sopenharmony_ci } 8851cb0ef41Sopenharmony_ci } 8861cb0ef41Sopenharmony_ci 8871cb0ef41Sopenharmony_ci if (startDot === -1 || 8881cb0ef41Sopenharmony_ci end === -1 || 8891cb0ef41Sopenharmony_ci // We saw a non-dot character immediately before the dot 8901cb0ef41Sopenharmony_ci preDotState === 0 || 8911cb0ef41Sopenharmony_ci // The (right-most) trimmed path component is exactly '..' 8921cb0ef41Sopenharmony_ci (preDotState === 1 && 8931cb0ef41Sopenharmony_ci startDot === end - 1 && 8941cb0ef41Sopenharmony_ci startDot === startPart + 1)) { 8951cb0ef41Sopenharmony_ci return ''; 8961cb0ef41Sopenharmony_ci } 8971cb0ef41Sopenharmony_ci return StringPrototypeSlice(path, startDot, end); 8981cb0ef41Sopenharmony_ci }, 8991cb0ef41Sopenharmony_ci 9001cb0ef41Sopenharmony_ci format: FunctionPrototypeBind(_format, null, '\\'), 9011cb0ef41Sopenharmony_ci 9021cb0ef41Sopenharmony_ci /** 9031cb0ef41Sopenharmony_ci * @param {string} path 9041cb0ef41Sopenharmony_ci * @returns {{ 9051cb0ef41Sopenharmony_ci * dir: string; 9061cb0ef41Sopenharmony_ci * root: string; 9071cb0ef41Sopenharmony_ci * base: string; 9081cb0ef41Sopenharmony_ci * name: string; 9091cb0ef41Sopenharmony_ci * ext: string; 9101cb0ef41Sopenharmony_ci * }} 9111cb0ef41Sopenharmony_ci */ 9121cb0ef41Sopenharmony_ci parse(path) { 9131cb0ef41Sopenharmony_ci validateString(path, 'path'); 9141cb0ef41Sopenharmony_ci 9151cb0ef41Sopenharmony_ci const ret = { root: '', dir: '', base: '', ext: '', name: '' }; 9161cb0ef41Sopenharmony_ci if (path.length === 0) 9171cb0ef41Sopenharmony_ci return ret; 9181cb0ef41Sopenharmony_ci 9191cb0ef41Sopenharmony_ci const len = path.length; 9201cb0ef41Sopenharmony_ci let rootEnd = 0; 9211cb0ef41Sopenharmony_ci let code = StringPrototypeCharCodeAt(path, 0); 9221cb0ef41Sopenharmony_ci 9231cb0ef41Sopenharmony_ci if (len === 1) { 9241cb0ef41Sopenharmony_ci if (isPathSeparator(code)) { 9251cb0ef41Sopenharmony_ci // `path` contains just a path separator, exit early to avoid 9261cb0ef41Sopenharmony_ci // unnecessary work 9271cb0ef41Sopenharmony_ci ret.root = ret.dir = path; 9281cb0ef41Sopenharmony_ci return ret; 9291cb0ef41Sopenharmony_ci } 9301cb0ef41Sopenharmony_ci ret.base = ret.name = path; 9311cb0ef41Sopenharmony_ci return ret; 9321cb0ef41Sopenharmony_ci } 9331cb0ef41Sopenharmony_ci // Try to match a root 9341cb0ef41Sopenharmony_ci if (isPathSeparator(code)) { 9351cb0ef41Sopenharmony_ci // Possible UNC root 9361cb0ef41Sopenharmony_ci 9371cb0ef41Sopenharmony_ci rootEnd = 1; 9381cb0ef41Sopenharmony_ci if (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) { 9391cb0ef41Sopenharmony_ci // Matched double path separator at beginning 9401cb0ef41Sopenharmony_ci let j = 2; 9411cb0ef41Sopenharmony_ci let last = j; 9421cb0ef41Sopenharmony_ci // Match 1 or more non-path separators 9431cb0ef41Sopenharmony_ci while (j < len && 9441cb0ef41Sopenharmony_ci !isPathSeparator(StringPrototypeCharCodeAt(path, j))) { 9451cb0ef41Sopenharmony_ci j++; 9461cb0ef41Sopenharmony_ci } 9471cb0ef41Sopenharmony_ci if (j < len && j !== last) { 9481cb0ef41Sopenharmony_ci // Matched! 9491cb0ef41Sopenharmony_ci last = j; 9501cb0ef41Sopenharmony_ci // Match 1 or more path separators 9511cb0ef41Sopenharmony_ci while (j < len && 9521cb0ef41Sopenharmony_ci isPathSeparator(StringPrototypeCharCodeAt(path, j))) { 9531cb0ef41Sopenharmony_ci j++; 9541cb0ef41Sopenharmony_ci } 9551cb0ef41Sopenharmony_ci if (j < len && j !== last) { 9561cb0ef41Sopenharmony_ci // Matched! 9571cb0ef41Sopenharmony_ci last = j; 9581cb0ef41Sopenharmony_ci // Match 1 or more non-path separators 9591cb0ef41Sopenharmony_ci while (j < len && 9601cb0ef41Sopenharmony_ci !isPathSeparator(StringPrototypeCharCodeAt(path, j))) { 9611cb0ef41Sopenharmony_ci j++; 9621cb0ef41Sopenharmony_ci } 9631cb0ef41Sopenharmony_ci if (j === len) { 9641cb0ef41Sopenharmony_ci // We matched a UNC root only 9651cb0ef41Sopenharmony_ci rootEnd = j; 9661cb0ef41Sopenharmony_ci } else if (j !== last) { 9671cb0ef41Sopenharmony_ci // We matched a UNC root with leftovers 9681cb0ef41Sopenharmony_ci rootEnd = j + 1; 9691cb0ef41Sopenharmony_ci } 9701cb0ef41Sopenharmony_ci } 9711cb0ef41Sopenharmony_ci } 9721cb0ef41Sopenharmony_ci } 9731cb0ef41Sopenharmony_ci } else if (isWindowsDeviceRoot(code) && 9741cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) { 9751cb0ef41Sopenharmony_ci // Possible device root 9761cb0ef41Sopenharmony_ci if (len <= 2) { 9771cb0ef41Sopenharmony_ci // `path` contains just a drive root, exit early to avoid 9781cb0ef41Sopenharmony_ci // unnecessary work 9791cb0ef41Sopenharmony_ci ret.root = ret.dir = path; 9801cb0ef41Sopenharmony_ci return ret; 9811cb0ef41Sopenharmony_ci } 9821cb0ef41Sopenharmony_ci rootEnd = 2; 9831cb0ef41Sopenharmony_ci if (isPathSeparator(StringPrototypeCharCodeAt(path, 2))) { 9841cb0ef41Sopenharmony_ci if (len === 3) { 9851cb0ef41Sopenharmony_ci // `path` contains just a drive root, exit early to avoid 9861cb0ef41Sopenharmony_ci // unnecessary work 9871cb0ef41Sopenharmony_ci ret.root = ret.dir = path; 9881cb0ef41Sopenharmony_ci return ret; 9891cb0ef41Sopenharmony_ci } 9901cb0ef41Sopenharmony_ci rootEnd = 3; 9911cb0ef41Sopenharmony_ci } 9921cb0ef41Sopenharmony_ci } 9931cb0ef41Sopenharmony_ci if (rootEnd > 0) 9941cb0ef41Sopenharmony_ci ret.root = StringPrototypeSlice(path, 0, rootEnd); 9951cb0ef41Sopenharmony_ci 9961cb0ef41Sopenharmony_ci let startDot = -1; 9971cb0ef41Sopenharmony_ci let startPart = rootEnd; 9981cb0ef41Sopenharmony_ci let end = -1; 9991cb0ef41Sopenharmony_ci let matchedSlash = true; 10001cb0ef41Sopenharmony_ci let i = path.length - 1; 10011cb0ef41Sopenharmony_ci 10021cb0ef41Sopenharmony_ci // Track the state of characters (if any) we see before our first dot and 10031cb0ef41Sopenharmony_ci // after any path separator we find 10041cb0ef41Sopenharmony_ci let preDotState = 0; 10051cb0ef41Sopenharmony_ci 10061cb0ef41Sopenharmony_ci // Get non-dir info 10071cb0ef41Sopenharmony_ci for (; i >= rootEnd; --i) { 10081cb0ef41Sopenharmony_ci code = StringPrototypeCharCodeAt(path, i); 10091cb0ef41Sopenharmony_ci if (isPathSeparator(code)) { 10101cb0ef41Sopenharmony_ci // If we reached a path separator that was not part of a set of path 10111cb0ef41Sopenharmony_ci // separators at the end of the string, stop now 10121cb0ef41Sopenharmony_ci if (!matchedSlash) { 10131cb0ef41Sopenharmony_ci startPart = i + 1; 10141cb0ef41Sopenharmony_ci break; 10151cb0ef41Sopenharmony_ci } 10161cb0ef41Sopenharmony_ci continue; 10171cb0ef41Sopenharmony_ci } 10181cb0ef41Sopenharmony_ci if (end === -1) { 10191cb0ef41Sopenharmony_ci // We saw the first non-path separator, mark this as the end of our 10201cb0ef41Sopenharmony_ci // extension 10211cb0ef41Sopenharmony_ci matchedSlash = false; 10221cb0ef41Sopenharmony_ci end = i + 1; 10231cb0ef41Sopenharmony_ci } 10241cb0ef41Sopenharmony_ci if (code === CHAR_DOT) { 10251cb0ef41Sopenharmony_ci // If this is our first dot, mark it as the start of our extension 10261cb0ef41Sopenharmony_ci if (startDot === -1) 10271cb0ef41Sopenharmony_ci startDot = i; 10281cb0ef41Sopenharmony_ci else if (preDotState !== 1) 10291cb0ef41Sopenharmony_ci preDotState = 1; 10301cb0ef41Sopenharmony_ci } else if (startDot !== -1) { 10311cb0ef41Sopenharmony_ci // We saw a non-dot and non-path separator before our dot, so we should 10321cb0ef41Sopenharmony_ci // have a good chance at having a non-empty extension 10331cb0ef41Sopenharmony_ci preDotState = -1; 10341cb0ef41Sopenharmony_ci } 10351cb0ef41Sopenharmony_ci } 10361cb0ef41Sopenharmony_ci 10371cb0ef41Sopenharmony_ci if (end !== -1) { 10381cb0ef41Sopenharmony_ci if (startDot === -1 || 10391cb0ef41Sopenharmony_ci // We saw a non-dot character immediately before the dot 10401cb0ef41Sopenharmony_ci preDotState === 0 || 10411cb0ef41Sopenharmony_ci // The (right-most) trimmed path component is exactly '..' 10421cb0ef41Sopenharmony_ci (preDotState === 1 && 10431cb0ef41Sopenharmony_ci startDot === end - 1 && 10441cb0ef41Sopenharmony_ci startDot === startPart + 1)) { 10451cb0ef41Sopenharmony_ci ret.base = ret.name = StringPrototypeSlice(path, startPart, end); 10461cb0ef41Sopenharmony_ci } else { 10471cb0ef41Sopenharmony_ci ret.name = StringPrototypeSlice(path, startPart, startDot); 10481cb0ef41Sopenharmony_ci ret.base = StringPrototypeSlice(path, startPart, end); 10491cb0ef41Sopenharmony_ci ret.ext = StringPrototypeSlice(path, startDot, end); 10501cb0ef41Sopenharmony_ci } 10511cb0ef41Sopenharmony_ci } 10521cb0ef41Sopenharmony_ci 10531cb0ef41Sopenharmony_ci // If the directory is the root, use the entire root as the `dir` including 10541cb0ef41Sopenharmony_ci // the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the 10551cb0ef41Sopenharmony_ci // trailing slash (`C:\abc\def` -> `C:\abc`). 10561cb0ef41Sopenharmony_ci if (startPart > 0 && startPart !== rootEnd) 10571cb0ef41Sopenharmony_ci ret.dir = StringPrototypeSlice(path, 0, startPart - 1); 10581cb0ef41Sopenharmony_ci else 10591cb0ef41Sopenharmony_ci ret.dir = ret.root; 10601cb0ef41Sopenharmony_ci 10611cb0ef41Sopenharmony_ci return ret; 10621cb0ef41Sopenharmony_ci }, 10631cb0ef41Sopenharmony_ci 10641cb0ef41Sopenharmony_ci sep: '\\', 10651cb0ef41Sopenharmony_ci delimiter: ';', 10661cb0ef41Sopenharmony_ci win32: null, 10671cb0ef41Sopenharmony_ci posix: null, 10681cb0ef41Sopenharmony_ci}; 10691cb0ef41Sopenharmony_ci 10701cb0ef41Sopenharmony_ciconst posixCwd = (() => { 10711cb0ef41Sopenharmony_ci if (platformIsWin32) { 10721cb0ef41Sopenharmony_ci // Converts Windows' backslash path separators to POSIX forward slashes 10731cb0ef41Sopenharmony_ci // and truncates any drive indicator 10741cb0ef41Sopenharmony_ci const regexp = /\\/g; 10751cb0ef41Sopenharmony_ci return () => { 10761cb0ef41Sopenharmony_ci const cwd = StringPrototypeReplace(process.cwd(), regexp, '/'); 10771cb0ef41Sopenharmony_ci return StringPrototypeSlice(cwd, StringPrototypeIndexOf(cwd, '/')); 10781cb0ef41Sopenharmony_ci }; 10791cb0ef41Sopenharmony_ci } 10801cb0ef41Sopenharmony_ci 10811cb0ef41Sopenharmony_ci // We're already on POSIX, no need for any transformations 10821cb0ef41Sopenharmony_ci return () => process.cwd(); 10831cb0ef41Sopenharmony_ci})(); 10841cb0ef41Sopenharmony_ci 10851cb0ef41Sopenharmony_ciconst posix = { 10861cb0ef41Sopenharmony_ci /** 10871cb0ef41Sopenharmony_ci * path.resolve([from ...], to) 10881cb0ef41Sopenharmony_ci * @param {...string} args 10891cb0ef41Sopenharmony_ci * @returns {string} 10901cb0ef41Sopenharmony_ci */ 10911cb0ef41Sopenharmony_ci resolve(...args) { 10921cb0ef41Sopenharmony_ci let resolvedPath = ''; 10931cb0ef41Sopenharmony_ci let resolvedAbsolute = false; 10941cb0ef41Sopenharmony_ci 10951cb0ef41Sopenharmony_ci for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { 10961cb0ef41Sopenharmony_ci const path = i >= 0 ? args[i] : posixCwd(); 10971cb0ef41Sopenharmony_ci validateString(path, `paths[${i}]`); 10981cb0ef41Sopenharmony_ci 10991cb0ef41Sopenharmony_ci // Skip empty entries 11001cb0ef41Sopenharmony_ci if (path.length === 0) { 11011cb0ef41Sopenharmony_ci continue; 11021cb0ef41Sopenharmony_ci } 11031cb0ef41Sopenharmony_ci 11041cb0ef41Sopenharmony_ci resolvedPath = `${path}/${resolvedPath}`; 11051cb0ef41Sopenharmony_ci resolvedAbsolute = 11061cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH; 11071cb0ef41Sopenharmony_ci } 11081cb0ef41Sopenharmony_ci 11091cb0ef41Sopenharmony_ci // At this point the path should be resolved to a full absolute path, but 11101cb0ef41Sopenharmony_ci // handle relative paths to be safe (might happen when process.cwd() fails) 11111cb0ef41Sopenharmony_ci 11121cb0ef41Sopenharmony_ci // Normalize the path 11131cb0ef41Sopenharmony_ci resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/', 11141cb0ef41Sopenharmony_ci isPosixPathSeparator); 11151cb0ef41Sopenharmony_ci 11161cb0ef41Sopenharmony_ci if (resolvedAbsolute) { 11171cb0ef41Sopenharmony_ci return `/${resolvedPath}`; 11181cb0ef41Sopenharmony_ci } 11191cb0ef41Sopenharmony_ci return resolvedPath.length > 0 ? resolvedPath : '.'; 11201cb0ef41Sopenharmony_ci }, 11211cb0ef41Sopenharmony_ci 11221cb0ef41Sopenharmony_ci /** 11231cb0ef41Sopenharmony_ci * @param {string} path 11241cb0ef41Sopenharmony_ci * @returns {string} 11251cb0ef41Sopenharmony_ci */ 11261cb0ef41Sopenharmony_ci normalize(path) { 11271cb0ef41Sopenharmony_ci validateString(path, 'path'); 11281cb0ef41Sopenharmony_ci 11291cb0ef41Sopenharmony_ci if (path.length === 0) 11301cb0ef41Sopenharmony_ci return '.'; 11311cb0ef41Sopenharmony_ci 11321cb0ef41Sopenharmony_ci const isAbsolute = 11331cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH; 11341cb0ef41Sopenharmony_ci const trailingSeparator = 11351cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(path, path.length - 1) === CHAR_FORWARD_SLASH; 11361cb0ef41Sopenharmony_ci 11371cb0ef41Sopenharmony_ci // Normalize the path 11381cb0ef41Sopenharmony_ci path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator); 11391cb0ef41Sopenharmony_ci 11401cb0ef41Sopenharmony_ci if (path.length === 0) { 11411cb0ef41Sopenharmony_ci if (isAbsolute) 11421cb0ef41Sopenharmony_ci return '/'; 11431cb0ef41Sopenharmony_ci return trailingSeparator ? './' : '.'; 11441cb0ef41Sopenharmony_ci } 11451cb0ef41Sopenharmony_ci if (trailingSeparator) 11461cb0ef41Sopenharmony_ci path += '/'; 11471cb0ef41Sopenharmony_ci 11481cb0ef41Sopenharmony_ci return isAbsolute ? `/${path}` : path; 11491cb0ef41Sopenharmony_ci }, 11501cb0ef41Sopenharmony_ci 11511cb0ef41Sopenharmony_ci /** 11521cb0ef41Sopenharmony_ci * @param {string} path 11531cb0ef41Sopenharmony_ci * @returns {boolean} 11541cb0ef41Sopenharmony_ci */ 11551cb0ef41Sopenharmony_ci isAbsolute(path) { 11561cb0ef41Sopenharmony_ci validateString(path, 'path'); 11571cb0ef41Sopenharmony_ci return path.length > 0 && 11581cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH; 11591cb0ef41Sopenharmony_ci }, 11601cb0ef41Sopenharmony_ci 11611cb0ef41Sopenharmony_ci /** 11621cb0ef41Sopenharmony_ci * @param {...string} args 11631cb0ef41Sopenharmony_ci * @returns {string} 11641cb0ef41Sopenharmony_ci */ 11651cb0ef41Sopenharmony_ci join(...args) { 11661cb0ef41Sopenharmony_ci if (args.length === 0) 11671cb0ef41Sopenharmony_ci return '.'; 11681cb0ef41Sopenharmony_ci let joined; 11691cb0ef41Sopenharmony_ci for (let i = 0; i < args.length; ++i) { 11701cb0ef41Sopenharmony_ci const arg = args[i]; 11711cb0ef41Sopenharmony_ci validateString(arg, 'path'); 11721cb0ef41Sopenharmony_ci if (arg.length > 0) { 11731cb0ef41Sopenharmony_ci if (joined === undefined) 11741cb0ef41Sopenharmony_ci joined = arg; 11751cb0ef41Sopenharmony_ci else 11761cb0ef41Sopenharmony_ci joined += `/${arg}`; 11771cb0ef41Sopenharmony_ci } 11781cb0ef41Sopenharmony_ci } 11791cb0ef41Sopenharmony_ci if (joined === undefined) 11801cb0ef41Sopenharmony_ci return '.'; 11811cb0ef41Sopenharmony_ci return posix.normalize(joined); 11821cb0ef41Sopenharmony_ci }, 11831cb0ef41Sopenharmony_ci 11841cb0ef41Sopenharmony_ci /** 11851cb0ef41Sopenharmony_ci * @param {string} from 11861cb0ef41Sopenharmony_ci * @param {string} to 11871cb0ef41Sopenharmony_ci * @returns {string} 11881cb0ef41Sopenharmony_ci */ 11891cb0ef41Sopenharmony_ci relative(from, to) { 11901cb0ef41Sopenharmony_ci validateString(from, 'from'); 11911cb0ef41Sopenharmony_ci validateString(to, 'to'); 11921cb0ef41Sopenharmony_ci 11931cb0ef41Sopenharmony_ci if (from === to) 11941cb0ef41Sopenharmony_ci return ''; 11951cb0ef41Sopenharmony_ci 11961cb0ef41Sopenharmony_ci // Trim leading forward slashes. 11971cb0ef41Sopenharmony_ci from = posix.resolve(from); 11981cb0ef41Sopenharmony_ci to = posix.resolve(to); 11991cb0ef41Sopenharmony_ci 12001cb0ef41Sopenharmony_ci if (from === to) 12011cb0ef41Sopenharmony_ci return ''; 12021cb0ef41Sopenharmony_ci 12031cb0ef41Sopenharmony_ci const fromStart = 1; 12041cb0ef41Sopenharmony_ci const fromEnd = from.length; 12051cb0ef41Sopenharmony_ci const fromLen = fromEnd - fromStart; 12061cb0ef41Sopenharmony_ci const toStart = 1; 12071cb0ef41Sopenharmony_ci const toLen = to.length - toStart; 12081cb0ef41Sopenharmony_ci 12091cb0ef41Sopenharmony_ci // Compare paths to find the longest common path from root 12101cb0ef41Sopenharmony_ci const length = (fromLen < toLen ? fromLen : toLen); 12111cb0ef41Sopenharmony_ci let lastCommonSep = -1; 12121cb0ef41Sopenharmony_ci let i = 0; 12131cb0ef41Sopenharmony_ci for (; i < length; i++) { 12141cb0ef41Sopenharmony_ci const fromCode = StringPrototypeCharCodeAt(from, fromStart + i); 12151cb0ef41Sopenharmony_ci if (fromCode !== StringPrototypeCharCodeAt(to, toStart + i)) 12161cb0ef41Sopenharmony_ci break; 12171cb0ef41Sopenharmony_ci else if (fromCode === CHAR_FORWARD_SLASH) 12181cb0ef41Sopenharmony_ci lastCommonSep = i; 12191cb0ef41Sopenharmony_ci } 12201cb0ef41Sopenharmony_ci if (i === length) { 12211cb0ef41Sopenharmony_ci if (toLen > length) { 12221cb0ef41Sopenharmony_ci if (StringPrototypeCharCodeAt(to, toStart + i) === CHAR_FORWARD_SLASH) { 12231cb0ef41Sopenharmony_ci // We get here if `from` is the exact base path for `to`. 12241cb0ef41Sopenharmony_ci // For example: from='/foo/bar'; to='/foo/bar/baz' 12251cb0ef41Sopenharmony_ci return StringPrototypeSlice(to, toStart + i + 1); 12261cb0ef41Sopenharmony_ci } 12271cb0ef41Sopenharmony_ci if (i === 0) { 12281cb0ef41Sopenharmony_ci // We get here if `from` is the root 12291cb0ef41Sopenharmony_ci // For example: from='/'; to='/foo' 12301cb0ef41Sopenharmony_ci return StringPrototypeSlice(to, toStart + i); 12311cb0ef41Sopenharmony_ci } 12321cb0ef41Sopenharmony_ci } else if (fromLen > length) { 12331cb0ef41Sopenharmony_ci if (StringPrototypeCharCodeAt(from, fromStart + i) === 12341cb0ef41Sopenharmony_ci CHAR_FORWARD_SLASH) { 12351cb0ef41Sopenharmony_ci // We get here if `to` is the exact base path for `from`. 12361cb0ef41Sopenharmony_ci // For example: from='/foo/bar/baz'; to='/foo/bar' 12371cb0ef41Sopenharmony_ci lastCommonSep = i; 12381cb0ef41Sopenharmony_ci } else if (i === 0) { 12391cb0ef41Sopenharmony_ci // We get here if `to` is the root. 12401cb0ef41Sopenharmony_ci // For example: from='/foo/bar'; to='/' 12411cb0ef41Sopenharmony_ci lastCommonSep = 0; 12421cb0ef41Sopenharmony_ci } 12431cb0ef41Sopenharmony_ci } 12441cb0ef41Sopenharmony_ci } 12451cb0ef41Sopenharmony_ci 12461cb0ef41Sopenharmony_ci let out = ''; 12471cb0ef41Sopenharmony_ci // Generate the relative path based on the path difference between `to` 12481cb0ef41Sopenharmony_ci // and `from`. 12491cb0ef41Sopenharmony_ci for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { 12501cb0ef41Sopenharmony_ci if (i === fromEnd || 12511cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(from, i) === CHAR_FORWARD_SLASH) { 12521cb0ef41Sopenharmony_ci out += out.length === 0 ? '..' : '/..'; 12531cb0ef41Sopenharmony_ci } 12541cb0ef41Sopenharmony_ci } 12551cb0ef41Sopenharmony_ci 12561cb0ef41Sopenharmony_ci // Lastly, append the rest of the destination (`to`) path that comes after 12571cb0ef41Sopenharmony_ci // the common path parts. 12581cb0ef41Sopenharmony_ci return `${out}${StringPrototypeSlice(to, toStart + lastCommonSep)}`; 12591cb0ef41Sopenharmony_ci }, 12601cb0ef41Sopenharmony_ci 12611cb0ef41Sopenharmony_ci /** 12621cb0ef41Sopenharmony_ci * @param {string} path 12631cb0ef41Sopenharmony_ci * @returns {string} 12641cb0ef41Sopenharmony_ci */ 12651cb0ef41Sopenharmony_ci toNamespacedPath(path) { 12661cb0ef41Sopenharmony_ci // Non-op on posix systems 12671cb0ef41Sopenharmony_ci return path; 12681cb0ef41Sopenharmony_ci }, 12691cb0ef41Sopenharmony_ci 12701cb0ef41Sopenharmony_ci /** 12711cb0ef41Sopenharmony_ci * @param {string} path 12721cb0ef41Sopenharmony_ci * @returns {string} 12731cb0ef41Sopenharmony_ci */ 12741cb0ef41Sopenharmony_ci dirname(path) { 12751cb0ef41Sopenharmony_ci validateString(path, 'path'); 12761cb0ef41Sopenharmony_ci if (path.length === 0) 12771cb0ef41Sopenharmony_ci return '.'; 12781cb0ef41Sopenharmony_ci const hasRoot = StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH; 12791cb0ef41Sopenharmony_ci let end = -1; 12801cb0ef41Sopenharmony_ci let matchedSlash = true; 12811cb0ef41Sopenharmony_ci for (let i = path.length - 1; i >= 1; --i) { 12821cb0ef41Sopenharmony_ci if (StringPrototypeCharCodeAt(path, i) === CHAR_FORWARD_SLASH) { 12831cb0ef41Sopenharmony_ci if (!matchedSlash) { 12841cb0ef41Sopenharmony_ci end = i; 12851cb0ef41Sopenharmony_ci break; 12861cb0ef41Sopenharmony_ci } 12871cb0ef41Sopenharmony_ci } else { 12881cb0ef41Sopenharmony_ci // We saw the first non-path separator 12891cb0ef41Sopenharmony_ci matchedSlash = false; 12901cb0ef41Sopenharmony_ci } 12911cb0ef41Sopenharmony_ci } 12921cb0ef41Sopenharmony_ci 12931cb0ef41Sopenharmony_ci if (end === -1) 12941cb0ef41Sopenharmony_ci return hasRoot ? '/' : '.'; 12951cb0ef41Sopenharmony_ci if (hasRoot && end === 1) 12961cb0ef41Sopenharmony_ci return '//'; 12971cb0ef41Sopenharmony_ci return StringPrototypeSlice(path, 0, end); 12981cb0ef41Sopenharmony_ci }, 12991cb0ef41Sopenharmony_ci 13001cb0ef41Sopenharmony_ci /** 13011cb0ef41Sopenharmony_ci * @param {string} path 13021cb0ef41Sopenharmony_ci * @param {string} [suffix] 13031cb0ef41Sopenharmony_ci * @returns {string} 13041cb0ef41Sopenharmony_ci */ 13051cb0ef41Sopenharmony_ci basename(path, suffix) { 13061cb0ef41Sopenharmony_ci if (suffix !== undefined) 13071cb0ef41Sopenharmony_ci validateString(suffix, 'ext'); 13081cb0ef41Sopenharmony_ci validateString(path, 'path'); 13091cb0ef41Sopenharmony_ci 13101cb0ef41Sopenharmony_ci let start = 0; 13111cb0ef41Sopenharmony_ci let end = -1; 13121cb0ef41Sopenharmony_ci let matchedSlash = true; 13131cb0ef41Sopenharmony_ci 13141cb0ef41Sopenharmony_ci if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) { 13151cb0ef41Sopenharmony_ci if (suffix === path) 13161cb0ef41Sopenharmony_ci return ''; 13171cb0ef41Sopenharmony_ci let extIdx = suffix.length - 1; 13181cb0ef41Sopenharmony_ci let firstNonSlashEnd = -1; 13191cb0ef41Sopenharmony_ci for (let i = path.length - 1; i >= 0; --i) { 13201cb0ef41Sopenharmony_ci const code = StringPrototypeCharCodeAt(path, i); 13211cb0ef41Sopenharmony_ci if (code === CHAR_FORWARD_SLASH) { 13221cb0ef41Sopenharmony_ci // If we reached a path separator that was not part of a set of path 13231cb0ef41Sopenharmony_ci // separators at the end of the string, stop now 13241cb0ef41Sopenharmony_ci if (!matchedSlash) { 13251cb0ef41Sopenharmony_ci start = i + 1; 13261cb0ef41Sopenharmony_ci break; 13271cb0ef41Sopenharmony_ci } 13281cb0ef41Sopenharmony_ci } else { 13291cb0ef41Sopenharmony_ci if (firstNonSlashEnd === -1) { 13301cb0ef41Sopenharmony_ci // We saw the first non-path separator, remember this index in case 13311cb0ef41Sopenharmony_ci // we need it if the extension ends up not matching 13321cb0ef41Sopenharmony_ci matchedSlash = false; 13331cb0ef41Sopenharmony_ci firstNonSlashEnd = i + 1; 13341cb0ef41Sopenharmony_ci } 13351cb0ef41Sopenharmony_ci if (extIdx >= 0) { 13361cb0ef41Sopenharmony_ci // Try to match the explicit extension 13371cb0ef41Sopenharmony_ci if (code === StringPrototypeCharCodeAt(suffix, extIdx)) { 13381cb0ef41Sopenharmony_ci if (--extIdx === -1) { 13391cb0ef41Sopenharmony_ci // We matched the extension, so mark this as the end of our path 13401cb0ef41Sopenharmony_ci // component 13411cb0ef41Sopenharmony_ci end = i; 13421cb0ef41Sopenharmony_ci } 13431cb0ef41Sopenharmony_ci } else { 13441cb0ef41Sopenharmony_ci // Extension does not match, so our result is the entire path 13451cb0ef41Sopenharmony_ci // component 13461cb0ef41Sopenharmony_ci extIdx = -1; 13471cb0ef41Sopenharmony_ci end = firstNonSlashEnd; 13481cb0ef41Sopenharmony_ci } 13491cb0ef41Sopenharmony_ci } 13501cb0ef41Sopenharmony_ci } 13511cb0ef41Sopenharmony_ci } 13521cb0ef41Sopenharmony_ci 13531cb0ef41Sopenharmony_ci if (start === end) 13541cb0ef41Sopenharmony_ci end = firstNonSlashEnd; 13551cb0ef41Sopenharmony_ci else if (end === -1) 13561cb0ef41Sopenharmony_ci end = path.length; 13571cb0ef41Sopenharmony_ci return StringPrototypeSlice(path, start, end); 13581cb0ef41Sopenharmony_ci } 13591cb0ef41Sopenharmony_ci for (let i = path.length - 1; i >= 0; --i) { 13601cb0ef41Sopenharmony_ci if (StringPrototypeCharCodeAt(path, i) === CHAR_FORWARD_SLASH) { 13611cb0ef41Sopenharmony_ci // If we reached a path separator that was not part of a set of path 13621cb0ef41Sopenharmony_ci // separators at the end of the string, stop now 13631cb0ef41Sopenharmony_ci if (!matchedSlash) { 13641cb0ef41Sopenharmony_ci start = i + 1; 13651cb0ef41Sopenharmony_ci break; 13661cb0ef41Sopenharmony_ci } 13671cb0ef41Sopenharmony_ci } else if (end === -1) { 13681cb0ef41Sopenharmony_ci // We saw the first non-path separator, mark this as the end of our 13691cb0ef41Sopenharmony_ci // path component 13701cb0ef41Sopenharmony_ci matchedSlash = false; 13711cb0ef41Sopenharmony_ci end = i + 1; 13721cb0ef41Sopenharmony_ci } 13731cb0ef41Sopenharmony_ci } 13741cb0ef41Sopenharmony_ci 13751cb0ef41Sopenharmony_ci if (end === -1) 13761cb0ef41Sopenharmony_ci return ''; 13771cb0ef41Sopenharmony_ci return StringPrototypeSlice(path, start, end); 13781cb0ef41Sopenharmony_ci }, 13791cb0ef41Sopenharmony_ci 13801cb0ef41Sopenharmony_ci /** 13811cb0ef41Sopenharmony_ci * @param {string} path 13821cb0ef41Sopenharmony_ci * @returns {string} 13831cb0ef41Sopenharmony_ci */ 13841cb0ef41Sopenharmony_ci extname(path) { 13851cb0ef41Sopenharmony_ci validateString(path, 'path'); 13861cb0ef41Sopenharmony_ci let startDot = -1; 13871cb0ef41Sopenharmony_ci let startPart = 0; 13881cb0ef41Sopenharmony_ci let end = -1; 13891cb0ef41Sopenharmony_ci let matchedSlash = true; 13901cb0ef41Sopenharmony_ci // Track the state of characters (if any) we see before our first dot and 13911cb0ef41Sopenharmony_ci // after any path separator we find 13921cb0ef41Sopenharmony_ci let preDotState = 0; 13931cb0ef41Sopenharmony_ci for (let i = path.length - 1; i >= 0; --i) { 13941cb0ef41Sopenharmony_ci const code = StringPrototypeCharCodeAt(path, i); 13951cb0ef41Sopenharmony_ci if (code === CHAR_FORWARD_SLASH) { 13961cb0ef41Sopenharmony_ci // If we reached a path separator that was not part of a set of path 13971cb0ef41Sopenharmony_ci // separators at the end of the string, stop now 13981cb0ef41Sopenharmony_ci if (!matchedSlash) { 13991cb0ef41Sopenharmony_ci startPart = i + 1; 14001cb0ef41Sopenharmony_ci break; 14011cb0ef41Sopenharmony_ci } 14021cb0ef41Sopenharmony_ci continue; 14031cb0ef41Sopenharmony_ci } 14041cb0ef41Sopenharmony_ci if (end === -1) { 14051cb0ef41Sopenharmony_ci // We saw the first non-path separator, mark this as the end of our 14061cb0ef41Sopenharmony_ci // extension 14071cb0ef41Sopenharmony_ci matchedSlash = false; 14081cb0ef41Sopenharmony_ci end = i + 1; 14091cb0ef41Sopenharmony_ci } 14101cb0ef41Sopenharmony_ci if (code === CHAR_DOT) { 14111cb0ef41Sopenharmony_ci // If this is our first dot, mark it as the start of our extension 14121cb0ef41Sopenharmony_ci if (startDot === -1) 14131cb0ef41Sopenharmony_ci startDot = i; 14141cb0ef41Sopenharmony_ci else if (preDotState !== 1) 14151cb0ef41Sopenharmony_ci preDotState = 1; 14161cb0ef41Sopenharmony_ci } else if (startDot !== -1) { 14171cb0ef41Sopenharmony_ci // We saw a non-dot and non-path separator before our dot, so we should 14181cb0ef41Sopenharmony_ci // have a good chance at having a non-empty extension 14191cb0ef41Sopenharmony_ci preDotState = -1; 14201cb0ef41Sopenharmony_ci } 14211cb0ef41Sopenharmony_ci } 14221cb0ef41Sopenharmony_ci 14231cb0ef41Sopenharmony_ci if (startDot === -1 || 14241cb0ef41Sopenharmony_ci end === -1 || 14251cb0ef41Sopenharmony_ci // We saw a non-dot character immediately before the dot 14261cb0ef41Sopenharmony_ci preDotState === 0 || 14271cb0ef41Sopenharmony_ci // The (right-most) trimmed path component is exactly '..' 14281cb0ef41Sopenharmony_ci (preDotState === 1 && 14291cb0ef41Sopenharmony_ci startDot === end - 1 && 14301cb0ef41Sopenharmony_ci startDot === startPart + 1)) { 14311cb0ef41Sopenharmony_ci return ''; 14321cb0ef41Sopenharmony_ci } 14331cb0ef41Sopenharmony_ci return StringPrototypeSlice(path, startDot, end); 14341cb0ef41Sopenharmony_ci }, 14351cb0ef41Sopenharmony_ci 14361cb0ef41Sopenharmony_ci format: FunctionPrototypeBind(_format, null, '/'), 14371cb0ef41Sopenharmony_ci 14381cb0ef41Sopenharmony_ci /** 14391cb0ef41Sopenharmony_ci * @param {string} path 14401cb0ef41Sopenharmony_ci * @returns {{ 14411cb0ef41Sopenharmony_ci * dir: string; 14421cb0ef41Sopenharmony_ci * root: string; 14431cb0ef41Sopenharmony_ci * base: string; 14441cb0ef41Sopenharmony_ci * name: string; 14451cb0ef41Sopenharmony_ci * ext: string; 14461cb0ef41Sopenharmony_ci * }} 14471cb0ef41Sopenharmony_ci */ 14481cb0ef41Sopenharmony_ci parse(path) { 14491cb0ef41Sopenharmony_ci validateString(path, 'path'); 14501cb0ef41Sopenharmony_ci 14511cb0ef41Sopenharmony_ci const ret = { root: '', dir: '', base: '', ext: '', name: '' }; 14521cb0ef41Sopenharmony_ci if (path.length === 0) 14531cb0ef41Sopenharmony_ci return ret; 14541cb0ef41Sopenharmony_ci const isAbsolute = 14551cb0ef41Sopenharmony_ci StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH; 14561cb0ef41Sopenharmony_ci let start; 14571cb0ef41Sopenharmony_ci if (isAbsolute) { 14581cb0ef41Sopenharmony_ci ret.root = '/'; 14591cb0ef41Sopenharmony_ci start = 1; 14601cb0ef41Sopenharmony_ci } else { 14611cb0ef41Sopenharmony_ci start = 0; 14621cb0ef41Sopenharmony_ci } 14631cb0ef41Sopenharmony_ci let startDot = -1; 14641cb0ef41Sopenharmony_ci let startPart = 0; 14651cb0ef41Sopenharmony_ci let end = -1; 14661cb0ef41Sopenharmony_ci let matchedSlash = true; 14671cb0ef41Sopenharmony_ci let i = path.length - 1; 14681cb0ef41Sopenharmony_ci 14691cb0ef41Sopenharmony_ci // Track the state of characters (if any) we see before our first dot and 14701cb0ef41Sopenharmony_ci // after any path separator we find 14711cb0ef41Sopenharmony_ci let preDotState = 0; 14721cb0ef41Sopenharmony_ci 14731cb0ef41Sopenharmony_ci // Get non-dir info 14741cb0ef41Sopenharmony_ci for (; i >= start; --i) { 14751cb0ef41Sopenharmony_ci const code = StringPrototypeCharCodeAt(path, i); 14761cb0ef41Sopenharmony_ci if (code === CHAR_FORWARD_SLASH) { 14771cb0ef41Sopenharmony_ci // If we reached a path separator that was not part of a set of path 14781cb0ef41Sopenharmony_ci // separators at the end of the string, stop now 14791cb0ef41Sopenharmony_ci if (!matchedSlash) { 14801cb0ef41Sopenharmony_ci startPart = i + 1; 14811cb0ef41Sopenharmony_ci break; 14821cb0ef41Sopenharmony_ci } 14831cb0ef41Sopenharmony_ci continue; 14841cb0ef41Sopenharmony_ci } 14851cb0ef41Sopenharmony_ci if (end === -1) { 14861cb0ef41Sopenharmony_ci // We saw the first non-path separator, mark this as the end of our 14871cb0ef41Sopenharmony_ci // extension 14881cb0ef41Sopenharmony_ci matchedSlash = false; 14891cb0ef41Sopenharmony_ci end = i + 1; 14901cb0ef41Sopenharmony_ci } 14911cb0ef41Sopenharmony_ci if (code === CHAR_DOT) { 14921cb0ef41Sopenharmony_ci // If this is our first dot, mark it as the start of our extension 14931cb0ef41Sopenharmony_ci if (startDot === -1) 14941cb0ef41Sopenharmony_ci startDot = i; 14951cb0ef41Sopenharmony_ci else if (preDotState !== 1) 14961cb0ef41Sopenharmony_ci preDotState = 1; 14971cb0ef41Sopenharmony_ci } else if (startDot !== -1) { 14981cb0ef41Sopenharmony_ci // We saw a non-dot and non-path separator before our dot, so we should 14991cb0ef41Sopenharmony_ci // have a good chance at having a non-empty extension 15001cb0ef41Sopenharmony_ci preDotState = -1; 15011cb0ef41Sopenharmony_ci } 15021cb0ef41Sopenharmony_ci } 15031cb0ef41Sopenharmony_ci 15041cb0ef41Sopenharmony_ci if (end !== -1) { 15051cb0ef41Sopenharmony_ci const start = startPart === 0 && isAbsolute ? 1 : startPart; 15061cb0ef41Sopenharmony_ci if (startDot === -1 || 15071cb0ef41Sopenharmony_ci // We saw a non-dot character immediately before the dot 15081cb0ef41Sopenharmony_ci preDotState === 0 || 15091cb0ef41Sopenharmony_ci // The (right-most) trimmed path component is exactly '..' 15101cb0ef41Sopenharmony_ci (preDotState === 1 && 15111cb0ef41Sopenharmony_ci startDot === end - 1 && 15121cb0ef41Sopenharmony_ci startDot === startPart + 1)) { 15131cb0ef41Sopenharmony_ci ret.base = ret.name = StringPrototypeSlice(path, start, end); 15141cb0ef41Sopenharmony_ci } else { 15151cb0ef41Sopenharmony_ci ret.name = StringPrototypeSlice(path, start, startDot); 15161cb0ef41Sopenharmony_ci ret.base = StringPrototypeSlice(path, start, end); 15171cb0ef41Sopenharmony_ci ret.ext = StringPrototypeSlice(path, startDot, end); 15181cb0ef41Sopenharmony_ci } 15191cb0ef41Sopenharmony_ci } 15201cb0ef41Sopenharmony_ci 15211cb0ef41Sopenharmony_ci if (startPart > 0) 15221cb0ef41Sopenharmony_ci ret.dir = StringPrototypeSlice(path, 0, startPart - 1); 15231cb0ef41Sopenharmony_ci else if (isAbsolute) 15241cb0ef41Sopenharmony_ci ret.dir = '/'; 15251cb0ef41Sopenharmony_ci 15261cb0ef41Sopenharmony_ci return ret; 15271cb0ef41Sopenharmony_ci }, 15281cb0ef41Sopenharmony_ci 15291cb0ef41Sopenharmony_ci sep: '/', 15301cb0ef41Sopenharmony_ci delimiter: ':', 15311cb0ef41Sopenharmony_ci win32: null, 15321cb0ef41Sopenharmony_ci posix: null, 15331cb0ef41Sopenharmony_ci}; 15341cb0ef41Sopenharmony_ci 15351cb0ef41Sopenharmony_ciposix.win32 = win32.win32 = win32; 15361cb0ef41Sopenharmony_ciposix.posix = win32.posix = posix; 15371cb0ef41Sopenharmony_ci 15381cb0ef41Sopenharmony_ci// Legacy internal API, docs-only deprecated: DEP0080 15391cb0ef41Sopenharmony_ciwin32._makeLong = win32.toNamespacedPath; 15401cb0ef41Sopenharmony_ciposix._makeLong = posix.toNamespacedPath; 15411cb0ef41Sopenharmony_ci 15421cb0ef41Sopenharmony_cimodule.exports = platformIsWin32 ? win32 : posix; 1543