• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23
24const {
25  FunctionPrototypeBind,
26  StringPrototypeCharCodeAt,
27  StringPrototypeIndexOf,
28  StringPrototypeLastIndexOf,
29  StringPrototypeReplace,
30  StringPrototypeSlice,
31  StringPrototypeToLowerCase,
32} = primordials;
33
34const {
35  CHAR_UPPERCASE_A,
36  CHAR_LOWERCASE_A,
37  CHAR_UPPERCASE_Z,
38  CHAR_LOWERCASE_Z,
39  CHAR_DOT,
40  CHAR_FORWARD_SLASH,
41  CHAR_BACKWARD_SLASH,
42  CHAR_COLON,
43  CHAR_QUESTION_MARK,
44} = require('internal/constants');
45const {
46  validateObject,
47  validateString,
48} = require('internal/validators');
49
50const platformIsWin32 = (process.platform === 'win32');
51
52function isPathSeparator(code) {
53  return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
54}
55
56function isPosixPathSeparator(code) {
57  return code === CHAR_FORWARD_SLASH;
58}
59
60function isWindowsDeviceRoot(code) {
61  return (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) ||
62         (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z);
63}
64
65// Resolves . and .. elements in a path with directory names
66function normalizeString(path, allowAboveRoot, separator, isPathSeparator) {
67  let res = '';
68  let lastSegmentLength = 0;
69  let lastSlash = -1;
70  let dots = 0;
71  let code = 0;
72  for (let i = 0; i <= path.length; ++i) {
73    if (i < path.length)
74      code = StringPrototypeCharCodeAt(path, i);
75    else if (isPathSeparator(code))
76      break;
77    else
78      code = CHAR_FORWARD_SLASH;
79
80    if (isPathSeparator(code)) {
81      if (lastSlash === i - 1 || dots === 1) {
82        // NOOP
83      } else if (dots === 2) {
84        if (res.length < 2 || lastSegmentLength !== 2 ||
85            StringPrototypeCharCodeAt(res, res.length - 1) !== CHAR_DOT ||
86            StringPrototypeCharCodeAt(res, res.length - 2) !== CHAR_DOT) {
87          if (res.length > 2) {
88            const lastSlashIndex = StringPrototypeLastIndexOf(res, separator);
89            if (lastSlashIndex === -1) {
90              res = '';
91              lastSegmentLength = 0;
92            } else {
93              res = StringPrototypeSlice(res, 0, lastSlashIndex);
94              lastSegmentLength =
95                res.length - 1 - StringPrototypeLastIndexOf(res, separator);
96            }
97            lastSlash = i;
98            dots = 0;
99            continue;
100          } else if (res.length !== 0) {
101            res = '';
102            lastSegmentLength = 0;
103            lastSlash = i;
104            dots = 0;
105            continue;
106          }
107        }
108        if (allowAboveRoot) {
109          res += res.length > 0 ? `${separator}..` : '..';
110          lastSegmentLength = 2;
111        }
112      } else {
113        if (res.length > 0)
114          res += `${separator}${StringPrototypeSlice(path, lastSlash + 1, i)}`;
115        else
116          res = StringPrototypeSlice(path, lastSlash + 1, i);
117        lastSegmentLength = i - lastSlash - 1;
118      }
119      lastSlash = i;
120      dots = 0;
121    } else if (code === CHAR_DOT && dots !== -1) {
122      ++dots;
123    } else {
124      dots = -1;
125    }
126  }
127  return res;
128}
129
130/**
131 * @param {string} sep
132 * @param {{
133 *  dir?: string;
134 *  root?: string;
135 *  base?: string;
136 *  name?: string;
137 *  ext?: string;
138 *  }} pathObject
139 * @returns {string}
140 */
141function _format(sep, pathObject) {
142  validateObject(pathObject, 'pathObject');
143  const dir = pathObject.dir || pathObject.root;
144  const base = pathObject.base ||
145    `${pathObject.name || ''}${pathObject.ext || ''}`;
146  if (!dir) {
147    return base;
148  }
149  return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`;
150}
151
152const win32 = {
153  /**
154   * path.resolve([from ...], to)
155   * @param {...string} args
156   * @returns {string}
157   */
158  resolve(...args) {
159    let resolvedDevice = '';
160    let resolvedTail = '';
161    let resolvedAbsolute = false;
162
163    for (let i = args.length - 1; i >= -1; i--) {
164      let path;
165      if (i >= 0) {
166        path = args[i];
167        validateString(path, `paths[${i}]`);
168
169        // Skip empty entries
170        if (path.length === 0) {
171          continue;
172        }
173      } else if (resolvedDevice.length === 0) {
174        path = process.cwd();
175      } else {
176        // Windows has the concept of drive-specific current working
177        // directories. If we've resolved a drive letter but not yet an
178        // absolute path, get cwd for that drive, or the process cwd if
179        // the drive cwd is not available. We're sure the device is not
180        // a UNC path at this points, because UNC paths are always absolute.
181        path = process.env[`=${resolvedDevice}`] || process.cwd();
182
183        // Verify that a cwd was found and that it actually points
184        // to our drive. If not, default to the drive's root.
185        if (path === undefined ||
186            (StringPrototypeToLowerCase(StringPrototypeSlice(path, 0, 2)) !==
187            StringPrototypeToLowerCase(resolvedDevice) &&
188            StringPrototypeCharCodeAt(path, 2) === CHAR_BACKWARD_SLASH)) {
189          path = `${resolvedDevice}\\`;
190        }
191      }
192
193      const len = path.length;
194      let rootEnd = 0;
195      let device = '';
196      let isAbsolute = false;
197      const code = StringPrototypeCharCodeAt(path, 0);
198
199      // Try to match a root
200      if (len === 1) {
201        if (isPathSeparator(code)) {
202          // `path` contains just a path separator
203          rootEnd = 1;
204          isAbsolute = true;
205        }
206      } else if (isPathSeparator(code)) {
207        // Possible UNC root
208
209        // If we started with a separator, we know we at least have an
210        // absolute path of some kind (UNC or otherwise)
211        isAbsolute = true;
212
213        if (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) {
214          // Matched double path separator at beginning
215          let j = 2;
216          let last = j;
217          // Match 1 or more non-path separators
218          while (j < len &&
219                 !isPathSeparator(StringPrototypeCharCodeAt(path, j))) {
220            j++;
221          }
222          if (j < len && j !== last) {
223            const firstPart = StringPrototypeSlice(path, last, j);
224            // Matched!
225            last = j;
226            // Match 1 or more path separators
227            while (j < len &&
228                   isPathSeparator(StringPrototypeCharCodeAt(path, j))) {
229              j++;
230            }
231            if (j < len && j !== last) {
232              // Matched!
233              last = j;
234              // Match 1 or more non-path separators
235              while (j < len &&
236                     !isPathSeparator(StringPrototypeCharCodeAt(path, j))) {
237                j++;
238              }
239              if (j === len || j !== last) {
240                // We matched a UNC root
241                device =
242                  `\\\\${firstPart}\\${StringPrototypeSlice(path, last, j)}`;
243                rootEnd = j;
244              }
245            }
246          }
247        } else {
248          rootEnd = 1;
249        }
250      } else if (isWindowsDeviceRoot(code) &&
251                  StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) {
252        // Possible device root
253        device = StringPrototypeSlice(path, 0, 2);
254        rootEnd = 2;
255        if (len > 2 && isPathSeparator(StringPrototypeCharCodeAt(path, 2))) {
256          // Treat separator following drive name as an absolute path
257          // indicator
258          isAbsolute = true;
259          rootEnd = 3;
260        }
261      }
262
263      if (device.length > 0) {
264        if (resolvedDevice.length > 0) {
265          if (StringPrototypeToLowerCase(device) !==
266              StringPrototypeToLowerCase(resolvedDevice))
267            // This path points to another device so it is not applicable
268            continue;
269        } else {
270          resolvedDevice = device;
271        }
272      }
273
274      if (resolvedAbsolute) {
275        if (resolvedDevice.length > 0)
276          break;
277      } else {
278        resolvedTail =
279          `${StringPrototypeSlice(path, rootEnd)}\\${resolvedTail}`;
280        resolvedAbsolute = isAbsolute;
281        if (isAbsolute && resolvedDevice.length > 0) {
282          break;
283        }
284      }
285    }
286
287    // At this point the path should be resolved to a full absolute path,
288    // but handle relative paths to be safe (might happen when process.cwd()
289    // fails)
290
291    // Normalize the tail path
292    resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\',
293                                   isPathSeparator);
294
295    return resolvedAbsolute ?
296      `${resolvedDevice}\\${resolvedTail}` :
297      `${resolvedDevice}${resolvedTail}` || '.';
298  },
299
300  /**
301   * @param {string} path
302   * @returns {string}
303   */
304  normalize(path) {
305    validateString(path, 'path');
306    const len = path.length;
307    if (len === 0)
308      return '.';
309    let rootEnd = 0;
310    let device;
311    let isAbsolute = false;
312    const code = StringPrototypeCharCodeAt(path, 0);
313
314    // Try to match a root
315    if (len === 1) {
316      // `path` contains just a single char, exit early to avoid
317      // unnecessary work
318      return isPosixPathSeparator(code) ? '\\' : path;
319    }
320    if (isPathSeparator(code)) {
321      // Possible UNC root
322
323      // If we started with a separator, we know we at least have an absolute
324      // path of some kind (UNC or otherwise)
325      isAbsolute = true;
326
327      if (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) {
328        // Matched double path separator at beginning
329        let j = 2;
330        let last = j;
331        // Match 1 or more non-path separators
332        while (j < len &&
333               !isPathSeparator(StringPrototypeCharCodeAt(path, j))) {
334          j++;
335        }
336        if (j < len && j !== last) {
337          const firstPart = StringPrototypeSlice(path, last, j);
338          // Matched!
339          last = j;
340          // Match 1 or more path separators
341          while (j < len &&
342                 isPathSeparator(StringPrototypeCharCodeAt(path, j))) {
343            j++;
344          }
345          if (j < len && j !== last) {
346            // Matched!
347            last = j;
348            // Match 1 or more non-path separators
349            while (j < len &&
350                   !isPathSeparator(StringPrototypeCharCodeAt(path, j))) {
351              j++;
352            }
353            if (j === len) {
354              // We matched a UNC root only
355              // Return the normalized version of the UNC root since there
356              // is nothing left to process
357              return `\\\\${firstPart}\\${StringPrototypeSlice(path, last)}\\`;
358            }
359            if (j !== last) {
360              // We matched a UNC root with leftovers
361              device =
362                `\\\\${firstPart}\\${StringPrototypeSlice(path, last, j)}`;
363              rootEnd = j;
364            }
365          }
366        }
367      } else {
368        rootEnd = 1;
369      }
370    } else if (isWindowsDeviceRoot(code) &&
371               StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) {
372      // Possible device root
373      device = StringPrototypeSlice(path, 0, 2);
374      rootEnd = 2;
375      if (len > 2 && isPathSeparator(StringPrototypeCharCodeAt(path, 2))) {
376        // Treat separator following drive name as an absolute path
377        // indicator
378        isAbsolute = true;
379        rootEnd = 3;
380      }
381    }
382
383    let tail = rootEnd < len ?
384      normalizeString(StringPrototypeSlice(path, rootEnd),
385                      !isAbsolute, '\\', isPathSeparator) :
386      '';
387    if (tail.length === 0 && !isAbsolute)
388      tail = '.';
389    if (tail.length > 0 &&
390        isPathSeparator(StringPrototypeCharCodeAt(path, len - 1)))
391      tail += '\\';
392    if (device === undefined) {
393      return isAbsolute ? `\\${tail}` : tail;
394    }
395    return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`;
396  },
397
398  /**
399   * @param {string} path
400   * @returns {boolean}
401   */
402  isAbsolute(path) {
403    validateString(path, 'path');
404    const len = path.length;
405    if (len === 0)
406      return false;
407
408    const code = StringPrototypeCharCodeAt(path, 0);
409    return isPathSeparator(code) ||
410      // Possible device root
411      (len > 2 &&
412      isWindowsDeviceRoot(code) &&
413      StringPrototypeCharCodeAt(path, 1) === CHAR_COLON &&
414      isPathSeparator(StringPrototypeCharCodeAt(path, 2)));
415  },
416
417  /**
418   * @param {...string} args
419   * @returns {string}
420   */
421  join(...args) {
422    if (args.length === 0)
423      return '.';
424
425    let joined;
426    let firstPart;
427    for (let i = 0; i < args.length; ++i) {
428      const arg = args[i];
429      validateString(arg, 'path');
430      if (arg.length > 0) {
431        if (joined === undefined)
432          joined = firstPart = arg;
433        else
434          joined += `\\${arg}`;
435      }
436    }
437
438    if (joined === undefined)
439      return '.';
440
441    // Make sure that the joined path doesn't start with two slashes, because
442    // normalize() will mistake it for a UNC path then.
443    //
444    // This step is skipped when it is very clear that the user actually
445    // intended to point at a UNC path. This is assumed when the first
446    // non-empty string arguments starts with exactly two slashes followed by
447    // at least one more non-slash character.
448    //
449    // Note that for normalize() to treat a path as a UNC path it needs to
450    // have at least 2 components, so we don't filter for that here.
451    // This means that the user can use join to construct UNC paths from
452    // a server name and a share name; for example:
453    //   path.join('//server', 'share') -> '\\\\server\\share\\')
454    let needsReplace = true;
455    let slashCount = 0;
456    if (isPathSeparator(StringPrototypeCharCodeAt(firstPart, 0))) {
457      ++slashCount;
458      const firstLen = firstPart.length;
459      if (firstLen > 1 &&
460          isPathSeparator(StringPrototypeCharCodeAt(firstPart, 1))) {
461        ++slashCount;
462        if (firstLen > 2) {
463          if (isPathSeparator(StringPrototypeCharCodeAt(firstPart, 2)))
464            ++slashCount;
465          else {
466            // We matched a UNC path in the first part
467            needsReplace = false;
468          }
469        }
470      }
471    }
472    if (needsReplace) {
473      // Find any more consecutive slashes we need to replace
474      while (slashCount < joined.length &&
475             isPathSeparator(StringPrototypeCharCodeAt(joined, slashCount))) {
476        slashCount++;
477      }
478
479      // Replace the slashes if needed
480      if (slashCount >= 2)
481        joined = `\\${StringPrototypeSlice(joined, slashCount)}`;
482    }
483
484    return win32.normalize(joined);
485  },
486
487  /**
488   * It will solve the relative path from `from` to `to`, for instance
489   * from = 'C:\\orandea\\test\\aaa'
490   * to = 'C:\\orandea\\impl\\bbb'
491   * The output of the function should be: '..\\..\\impl\\bbb'
492   * @param {string} from
493   * @param {string} to
494   * @returns {string}
495   */
496  relative(from, to) {
497    validateString(from, 'from');
498    validateString(to, 'to');
499
500    if (from === to)
501      return '';
502
503    const fromOrig = win32.resolve(from);
504    const toOrig = win32.resolve(to);
505
506    if (fromOrig === toOrig)
507      return '';
508
509    from = StringPrototypeToLowerCase(fromOrig);
510    to = StringPrototypeToLowerCase(toOrig);
511
512    if (from === to)
513      return '';
514
515    // Trim any leading backslashes
516    let fromStart = 0;
517    while (fromStart < from.length &&
518           StringPrototypeCharCodeAt(from, fromStart) === CHAR_BACKWARD_SLASH) {
519      fromStart++;
520    }
521    // Trim trailing backslashes (applicable to UNC paths only)
522    let fromEnd = from.length;
523    while (
524      fromEnd - 1 > fromStart &&
525      StringPrototypeCharCodeAt(from, fromEnd - 1) === CHAR_BACKWARD_SLASH
526    ) {
527      fromEnd--;
528    }
529    const fromLen = fromEnd - fromStart;
530
531    // Trim any leading backslashes
532    let toStart = 0;
533    while (toStart < to.length &&
534           StringPrototypeCharCodeAt(to, toStart) === CHAR_BACKWARD_SLASH) {
535      toStart++;
536    }
537    // Trim trailing backslashes (applicable to UNC paths only)
538    let toEnd = to.length;
539    while (toEnd - 1 > toStart &&
540           StringPrototypeCharCodeAt(to, toEnd - 1) === CHAR_BACKWARD_SLASH) {
541      toEnd--;
542    }
543    const toLen = toEnd - toStart;
544
545    // Compare paths to find the longest common path from root
546    const length = fromLen < toLen ? fromLen : toLen;
547    let lastCommonSep = -1;
548    let i = 0;
549    for (; i < length; i++) {
550      const fromCode = StringPrototypeCharCodeAt(from, fromStart + i);
551      if (fromCode !== StringPrototypeCharCodeAt(to, toStart + i))
552        break;
553      else if (fromCode === CHAR_BACKWARD_SLASH)
554        lastCommonSep = i;
555    }
556
557    // We found a mismatch before the first common path separator was seen, so
558    // return the original `to`.
559    if (i !== length) {
560      if (lastCommonSep === -1)
561        return toOrig;
562    } else {
563      if (toLen > length) {
564        if (StringPrototypeCharCodeAt(to, toStart + i) ===
565            CHAR_BACKWARD_SLASH) {
566          // We get here if `from` is the exact base path for `to`.
567          // For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz'
568          return StringPrototypeSlice(toOrig, toStart + i + 1);
569        }
570        if (i === 2) {
571          // We get here if `from` is the device root.
572          // For example: from='C:\\'; to='C:\\foo'
573          return StringPrototypeSlice(toOrig, toStart + i);
574        }
575      }
576      if (fromLen > length) {
577        if (StringPrototypeCharCodeAt(from, fromStart + i) ===
578            CHAR_BACKWARD_SLASH) {
579          // We get here if `to` is the exact base path for `from`.
580          // For example: from='C:\\foo\\bar'; to='C:\\foo'
581          lastCommonSep = i;
582        } else if (i === 2) {
583          // We get here if `to` is the device root.
584          // For example: from='C:\\foo\\bar'; to='C:\\'
585          lastCommonSep = 3;
586        }
587      }
588      if (lastCommonSep === -1)
589        lastCommonSep = 0;
590    }
591
592    let out = '';
593    // Generate the relative path based on the path difference between `to` and
594    // `from`
595    for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
596      if (i === fromEnd ||
597          StringPrototypeCharCodeAt(from, i) === CHAR_BACKWARD_SLASH) {
598        out += out.length === 0 ? '..' : '\\..';
599      }
600    }
601
602    toStart += lastCommonSep;
603
604    // Lastly, append the rest of the destination (`to`) path that comes after
605    // the common path parts
606    if (out.length > 0)
607      return `${out}${StringPrototypeSlice(toOrig, toStart, toEnd)}`;
608
609    if (StringPrototypeCharCodeAt(toOrig, toStart) === CHAR_BACKWARD_SLASH)
610      ++toStart;
611    return StringPrototypeSlice(toOrig, toStart, toEnd);
612  },
613
614  /**
615   * @param {string} path
616   * @returns {string}
617   */
618  toNamespacedPath(path) {
619    // Note: this will *probably* throw somewhere.
620    if (typeof path !== 'string' || path.length === 0)
621      return path;
622
623    const resolvedPath = win32.resolve(path);
624
625    if (resolvedPath.length <= 2)
626      return path;
627
628    if (StringPrototypeCharCodeAt(resolvedPath, 0) === CHAR_BACKWARD_SLASH) {
629      // Possible UNC root
630      if (StringPrototypeCharCodeAt(resolvedPath, 1) === CHAR_BACKWARD_SLASH) {
631        const code = StringPrototypeCharCodeAt(resolvedPath, 2);
632        if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) {
633          // Matched non-long UNC root, convert the path to a long UNC path
634          return `\\\\?\\UNC\\${StringPrototypeSlice(resolvedPath, 2)}`;
635        }
636      }
637    } else if (
638      isWindowsDeviceRoot(StringPrototypeCharCodeAt(resolvedPath, 0)) &&
639      StringPrototypeCharCodeAt(resolvedPath, 1) === CHAR_COLON &&
640      StringPrototypeCharCodeAt(resolvedPath, 2) === CHAR_BACKWARD_SLASH
641    ) {
642      // Matched device root, convert the path to a long UNC path
643      return `\\\\?\\${resolvedPath}`;
644    }
645
646    return path;
647  },
648
649  /**
650   * @param {string} path
651   * @returns {string}
652   */
653  dirname(path) {
654    validateString(path, 'path');
655    const len = path.length;
656    if (len === 0)
657      return '.';
658    let rootEnd = -1;
659    let offset = 0;
660    const code = StringPrototypeCharCodeAt(path, 0);
661
662    if (len === 1) {
663      // `path` contains just a path separator, exit early to avoid
664      // unnecessary work or a dot.
665      return isPathSeparator(code) ? path : '.';
666    }
667
668    // Try to match a root
669    if (isPathSeparator(code)) {
670      // Possible UNC root
671
672      rootEnd = offset = 1;
673
674      if (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) {
675        // Matched double path separator at beginning
676        let j = 2;
677        let last = j;
678        // Match 1 or more non-path separators
679        while (j < len &&
680               !isPathSeparator(StringPrototypeCharCodeAt(path, j))) {
681          j++;
682        }
683        if (j < len && j !== last) {
684          // Matched!
685          last = j;
686          // Match 1 or more path separators
687          while (j < len &&
688                 isPathSeparator(StringPrototypeCharCodeAt(path, j))) {
689            j++;
690          }
691          if (j < len && j !== last) {
692            // Matched!
693            last = j;
694            // Match 1 or more non-path separators
695            while (j < len &&
696                   !isPathSeparator(StringPrototypeCharCodeAt(path, j))) {
697              j++;
698            }
699            if (j === len) {
700              // We matched a UNC root only
701              return path;
702            }
703            if (j !== last) {
704              // We matched a UNC root with leftovers
705
706              // Offset by 1 to include the separator after the UNC root to
707              // treat it as a "normal root" on top of a (UNC) root
708              rootEnd = offset = j + 1;
709            }
710          }
711        }
712      }
713    // Possible device root
714    } else if (isWindowsDeviceRoot(code) &&
715               StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) {
716      rootEnd =
717        len > 2 && isPathSeparator(StringPrototypeCharCodeAt(path, 2)) ? 3 : 2;
718      offset = rootEnd;
719    }
720
721    let end = -1;
722    let matchedSlash = true;
723    for (let i = len - 1; i >= offset; --i) {
724      if (isPathSeparator(StringPrototypeCharCodeAt(path, i))) {
725        if (!matchedSlash) {
726          end = i;
727          break;
728        }
729      } else {
730        // We saw the first non-path separator
731        matchedSlash = false;
732      }
733    }
734
735    if (end === -1) {
736      if (rootEnd === -1)
737        return '.';
738
739      end = rootEnd;
740    }
741    return StringPrototypeSlice(path, 0, end);
742  },
743
744  /**
745   * @param {string} path
746   * @param {string} [suffix]
747   * @returns {string}
748   */
749  basename(path, suffix) {
750    if (suffix !== undefined)
751      validateString(suffix, 'ext');
752    validateString(path, 'path');
753    let start = 0;
754    let end = -1;
755    let matchedSlash = true;
756
757    // Check for a drive letter prefix so as not to mistake the following
758    // path separator as an extra separator at the end of the path that can be
759    // disregarded
760    if (path.length >= 2 &&
761        isWindowsDeviceRoot(StringPrototypeCharCodeAt(path, 0)) &&
762        StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) {
763      start = 2;
764    }
765
766    if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) {
767      if (suffix === path)
768        return '';
769      let extIdx = suffix.length - 1;
770      let firstNonSlashEnd = -1;
771      for (let i = path.length - 1; i >= start; --i) {
772        const code = StringPrototypeCharCodeAt(path, i);
773        if (isPathSeparator(code)) {
774          // If we reached a path separator that was not part of a set of path
775          // separators at the end of the string, stop now
776          if (!matchedSlash) {
777            start = i + 1;
778            break;
779          }
780        } else {
781          if (firstNonSlashEnd === -1) {
782            // We saw the first non-path separator, remember this index in case
783            // we need it if the extension ends up not matching
784            matchedSlash = false;
785            firstNonSlashEnd = i + 1;
786          }
787          if (extIdx >= 0) {
788            // Try to match the explicit extension
789            if (code === StringPrototypeCharCodeAt(suffix, extIdx)) {
790              if (--extIdx === -1) {
791                // We matched the extension, so mark this as the end of our path
792                // component
793                end = i;
794              }
795            } else {
796              // Extension does not match, so our result is the entire path
797              // component
798              extIdx = -1;
799              end = firstNonSlashEnd;
800            }
801          }
802        }
803      }
804
805      if (start === end)
806        end = firstNonSlashEnd;
807      else if (end === -1)
808        end = path.length;
809      return StringPrototypeSlice(path, start, end);
810    }
811    for (let i = path.length - 1; i >= start; --i) {
812      if (isPathSeparator(StringPrototypeCharCodeAt(path, i))) {
813        // If we reached a path separator that was not part of a set of path
814        // separators at the end of the string, stop now
815        if (!matchedSlash) {
816          start = i + 1;
817          break;
818        }
819      } else if (end === -1) {
820        // We saw the first non-path separator, mark this as the end of our
821        // path component
822        matchedSlash = false;
823        end = i + 1;
824      }
825    }
826
827    if (end === -1)
828      return '';
829    return StringPrototypeSlice(path, start, end);
830  },
831
832  /**
833   * @param {string} path
834   * @returns {string}
835   */
836  extname(path) {
837    validateString(path, 'path');
838    let start = 0;
839    let startDot = -1;
840    let startPart = 0;
841    let end = -1;
842    let matchedSlash = true;
843    // Track the state of characters (if any) we see before our first dot and
844    // after any path separator we find
845    let preDotState = 0;
846
847    // Check for a drive letter prefix so as not to mistake the following
848    // path separator as an extra separator at the end of the path that can be
849    // disregarded
850
851    if (path.length >= 2 &&
852        StringPrototypeCharCodeAt(path, 1) === CHAR_COLON &&
853        isWindowsDeviceRoot(StringPrototypeCharCodeAt(path, 0))) {
854      start = startPart = 2;
855    }
856
857    for (let i = path.length - 1; i >= start; --i) {
858      const code = StringPrototypeCharCodeAt(path, i);
859      if (isPathSeparator(code)) {
860        // If we reached a path separator that was not part of a set of path
861        // separators at the end of the string, stop now
862        if (!matchedSlash) {
863          startPart = i + 1;
864          break;
865        }
866        continue;
867      }
868      if (end === -1) {
869        // We saw the first non-path separator, mark this as the end of our
870        // extension
871        matchedSlash = false;
872        end = i + 1;
873      }
874      if (code === CHAR_DOT) {
875        // If this is our first dot, mark it as the start of our extension
876        if (startDot === -1)
877          startDot = i;
878        else if (preDotState !== 1)
879          preDotState = 1;
880      } else if (startDot !== -1) {
881        // We saw a non-dot and non-path separator before our dot, so we should
882        // have a good chance at having a non-empty extension
883        preDotState = -1;
884      }
885    }
886
887    if (startDot === -1 ||
888        end === -1 ||
889        // We saw a non-dot character immediately before the dot
890        preDotState === 0 ||
891        // The (right-most) trimmed path component is exactly '..'
892        (preDotState === 1 &&
893         startDot === end - 1 &&
894         startDot === startPart + 1)) {
895      return '';
896    }
897    return StringPrototypeSlice(path, startDot, end);
898  },
899
900  format: FunctionPrototypeBind(_format, null, '\\'),
901
902  /**
903   * @param {string} path
904   * @returns {{
905   *  dir: string;
906   *  root: string;
907   *  base: string;
908   *  name: string;
909   *  ext: string;
910   *  }}
911   */
912  parse(path) {
913    validateString(path, 'path');
914
915    const ret = { root: '', dir: '', base: '', ext: '', name: '' };
916    if (path.length === 0)
917      return ret;
918
919    const len = path.length;
920    let rootEnd = 0;
921    let code = StringPrototypeCharCodeAt(path, 0);
922
923    if (len === 1) {
924      if (isPathSeparator(code)) {
925        // `path` contains just a path separator, exit early to avoid
926        // unnecessary work
927        ret.root = ret.dir = path;
928        return ret;
929      }
930      ret.base = ret.name = path;
931      return ret;
932    }
933    // Try to match a root
934    if (isPathSeparator(code)) {
935      // Possible UNC root
936
937      rootEnd = 1;
938      if (isPathSeparator(StringPrototypeCharCodeAt(path, 1))) {
939        // Matched double path separator at beginning
940        let j = 2;
941        let last = j;
942        // Match 1 or more non-path separators
943        while (j < len &&
944               !isPathSeparator(StringPrototypeCharCodeAt(path, j))) {
945          j++;
946        }
947        if (j < len && j !== last) {
948          // Matched!
949          last = j;
950          // Match 1 or more path separators
951          while (j < len &&
952                 isPathSeparator(StringPrototypeCharCodeAt(path, j))) {
953            j++;
954          }
955          if (j < len && j !== last) {
956            // Matched!
957            last = j;
958            // Match 1 or more non-path separators
959            while (j < len &&
960                   !isPathSeparator(StringPrototypeCharCodeAt(path, j))) {
961              j++;
962            }
963            if (j === len) {
964              // We matched a UNC root only
965              rootEnd = j;
966            } else if (j !== last) {
967              // We matched a UNC root with leftovers
968              rootEnd = j + 1;
969            }
970          }
971        }
972      }
973    } else if (isWindowsDeviceRoot(code) &&
974               StringPrototypeCharCodeAt(path, 1) === CHAR_COLON) {
975      // Possible device root
976      if (len <= 2) {
977        // `path` contains just a drive root, exit early to avoid
978        // unnecessary work
979        ret.root = ret.dir = path;
980        return ret;
981      }
982      rootEnd = 2;
983      if (isPathSeparator(StringPrototypeCharCodeAt(path, 2))) {
984        if (len === 3) {
985          // `path` contains just a drive root, exit early to avoid
986          // unnecessary work
987          ret.root = ret.dir = path;
988          return ret;
989        }
990        rootEnd = 3;
991      }
992    }
993    if (rootEnd > 0)
994      ret.root = StringPrototypeSlice(path, 0, rootEnd);
995
996    let startDot = -1;
997    let startPart = rootEnd;
998    let end = -1;
999    let matchedSlash = true;
1000    let i = path.length - 1;
1001
1002    // Track the state of characters (if any) we see before our first dot and
1003    // after any path separator we find
1004    let preDotState = 0;
1005
1006    // Get non-dir info
1007    for (; i >= rootEnd; --i) {
1008      code = StringPrototypeCharCodeAt(path, i);
1009      if (isPathSeparator(code)) {
1010        // If we reached a path separator that was not part of a set of path
1011        // separators at the end of the string, stop now
1012        if (!matchedSlash) {
1013          startPart = i + 1;
1014          break;
1015        }
1016        continue;
1017      }
1018      if (end === -1) {
1019        // We saw the first non-path separator, mark this as the end of our
1020        // extension
1021        matchedSlash = false;
1022        end = i + 1;
1023      }
1024      if (code === CHAR_DOT) {
1025        // If this is our first dot, mark it as the start of our extension
1026        if (startDot === -1)
1027          startDot = i;
1028        else if (preDotState !== 1)
1029          preDotState = 1;
1030      } else if (startDot !== -1) {
1031        // We saw a non-dot and non-path separator before our dot, so we should
1032        // have a good chance at having a non-empty extension
1033        preDotState = -1;
1034      }
1035    }
1036
1037    if (end !== -1) {
1038      if (startDot === -1 ||
1039          // We saw a non-dot character immediately before the dot
1040          preDotState === 0 ||
1041          // The (right-most) trimmed path component is exactly '..'
1042          (preDotState === 1 &&
1043           startDot === end - 1 &&
1044           startDot === startPart + 1)) {
1045        ret.base = ret.name = StringPrototypeSlice(path, startPart, end);
1046      } else {
1047        ret.name = StringPrototypeSlice(path, startPart, startDot);
1048        ret.base = StringPrototypeSlice(path, startPart, end);
1049        ret.ext = StringPrototypeSlice(path, startDot, end);
1050      }
1051    }
1052
1053    // If the directory is the root, use the entire root as the `dir` including
1054    // the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the
1055    // trailing slash (`C:\abc\def` -> `C:\abc`).
1056    if (startPart > 0 && startPart !== rootEnd)
1057      ret.dir = StringPrototypeSlice(path, 0, startPart - 1);
1058    else
1059      ret.dir = ret.root;
1060
1061    return ret;
1062  },
1063
1064  sep: '\\',
1065  delimiter: ';',
1066  win32: null,
1067  posix: null,
1068};
1069
1070const posixCwd = (() => {
1071  if (platformIsWin32) {
1072    // Converts Windows' backslash path separators to POSIX forward slashes
1073    // and truncates any drive indicator
1074    const regexp = /\\/g;
1075    return () => {
1076      const cwd = StringPrototypeReplace(process.cwd(), regexp, '/');
1077      return StringPrototypeSlice(cwd, StringPrototypeIndexOf(cwd, '/'));
1078    };
1079  }
1080
1081  // We're already on POSIX, no need for any transformations
1082  return () => process.cwd();
1083})();
1084
1085const posix = {
1086  /**
1087   * path.resolve([from ...], to)
1088   * @param {...string} args
1089   * @returns {string}
1090   */
1091  resolve(...args) {
1092    let resolvedPath = '';
1093    let resolvedAbsolute = false;
1094
1095    for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) {
1096      const path = i >= 0 ? args[i] : posixCwd();
1097      validateString(path, `paths[${i}]`);
1098
1099      // Skip empty entries
1100      if (path.length === 0) {
1101        continue;
1102      }
1103
1104      resolvedPath = `${path}/${resolvedPath}`;
1105      resolvedAbsolute =
1106        StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH;
1107    }
1108
1109    // At this point the path should be resolved to a full absolute path, but
1110    // handle relative paths to be safe (might happen when process.cwd() fails)
1111
1112    // Normalize the path
1113    resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/',
1114                                   isPosixPathSeparator);
1115
1116    if (resolvedAbsolute) {
1117      return `/${resolvedPath}`;
1118    }
1119    return resolvedPath.length > 0 ? resolvedPath : '.';
1120  },
1121
1122  /**
1123   * @param {string} path
1124   * @returns {string}
1125   */
1126  normalize(path) {
1127    validateString(path, 'path');
1128
1129    if (path.length === 0)
1130      return '.';
1131
1132    const isAbsolute =
1133      StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH;
1134    const trailingSeparator =
1135      StringPrototypeCharCodeAt(path, path.length - 1) === CHAR_FORWARD_SLASH;
1136
1137    // Normalize the path
1138    path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator);
1139
1140    if (path.length === 0) {
1141      if (isAbsolute)
1142        return '/';
1143      return trailingSeparator ? './' : '.';
1144    }
1145    if (trailingSeparator)
1146      path += '/';
1147
1148    return isAbsolute ? `/${path}` : path;
1149  },
1150
1151  /**
1152   * @param {string} path
1153   * @returns {boolean}
1154   */
1155  isAbsolute(path) {
1156    validateString(path, 'path');
1157    return path.length > 0 &&
1158           StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH;
1159  },
1160
1161  /**
1162   * @param {...string} args
1163   * @returns {string}
1164   */
1165  join(...args) {
1166    if (args.length === 0)
1167      return '.';
1168    let joined;
1169    for (let i = 0; i < args.length; ++i) {
1170      const arg = args[i];
1171      validateString(arg, 'path');
1172      if (arg.length > 0) {
1173        if (joined === undefined)
1174          joined = arg;
1175        else
1176          joined += `/${arg}`;
1177      }
1178    }
1179    if (joined === undefined)
1180      return '.';
1181    return posix.normalize(joined);
1182  },
1183
1184  /**
1185   * @param {string} from
1186   * @param {string} to
1187   * @returns {string}
1188   */
1189  relative(from, to) {
1190    validateString(from, 'from');
1191    validateString(to, 'to');
1192
1193    if (from === to)
1194      return '';
1195
1196    // Trim leading forward slashes.
1197    from = posix.resolve(from);
1198    to = posix.resolve(to);
1199
1200    if (from === to)
1201      return '';
1202
1203    const fromStart = 1;
1204    const fromEnd = from.length;
1205    const fromLen = fromEnd - fromStart;
1206    const toStart = 1;
1207    const toLen = to.length - toStart;
1208
1209    // Compare paths to find the longest common path from root
1210    const length = (fromLen < toLen ? fromLen : toLen);
1211    let lastCommonSep = -1;
1212    let i = 0;
1213    for (; i < length; i++) {
1214      const fromCode = StringPrototypeCharCodeAt(from, fromStart + i);
1215      if (fromCode !== StringPrototypeCharCodeAt(to, toStart + i))
1216        break;
1217      else if (fromCode === CHAR_FORWARD_SLASH)
1218        lastCommonSep = i;
1219    }
1220    if (i === length) {
1221      if (toLen > length) {
1222        if (StringPrototypeCharCodeAt(to, toStart + i) === CHAR_FORWARD_SLASH) {
1223          // We get here if `from` is the exact base path for `to`.
1224          // For example: from='/foo/bar'; to='/foo/bar/baz'
1225          return StringPrototypeSlice(to, toStart + i + 1);
1226        }
1227        if (i === 0) {
1228          // We get here if `from` is the root
1229          // For example: from='/'; to='/foo'
1230          return StringPrototypeSlice(to, toStart + i);
1231        }
1232      } else if (fromLen > length) {
1233        if (StringPrototypeCharCodeAt(from, fromStart + i) ===
1234            CHAR_FORWARD_SLASH) {
1235          // We get here if `to` is the exact base path for `from`.
1236          // For example: from='/foo/bar/baz'; to='/foo/bar'
1237          lastCommonSep = i;
1238        } else if (i === 0) {
1239          // We get here if `to` is the root.
1240          // For example: from='/foo/bar'; to='/'
1241          lastCommonSep = 0;
1242        }
1243      }
1244    }
1245
1246    let out = '';
1247    // Generate the relative path based on the path difference between `to`
1248    // and `from`.
1249    for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
1250      if (i === fromEnd ||
1251          StringPrototypeCharCodeAt(from, i) === CHAR_FORWARD_SLASH) {
1252        out += out.length === 0 ? '..' : '/..';
1253      }
1254    }
1255
1256    // Lastly, append the rest of the destination (`to`) path that comes after
1257    // the common path parts.
1258    return `${out}${StringPrototypeSlice(to, toStart + lastCommonSep)}`;
1259  },
1260
1261  /**
1262   * @param {string} path
1263   * @returns {string}
1264   */
1265  toNamespacedPath(path) {
1266    // Non-op on posix systems
1267    return path;
1268  },
1269
1270  /**
1271   * @param {string} path
1272   * @returns {string}
1273   */
1274  dirname(path) {
1275    validateString(path, 'path');
1276    if (path.length === 0)
1277      return '.';
1278    const hasRoot = StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH;
1279    let end = -1;
1280    let matchedSlash = true;
1281    for (let i = path.length - 1; i >= 1; --i) {
1282      if (StringPrototypeCharCodeAt(path, i) === CHAR_FORWARD_SLASH) {
1283        if (!matchedSlash) {
1284          end = i;
1285          break;
1286        }
1287      } else {
1288        // We saw the first non-path separator
1289        matchedSlash = false;
1290      }
1291    }
1292
1293    if (end === -1)
1294      return hasRoot ? '/' : '.';
1295    if (hasRoot && end === 1)
1296      return '//';
1297    return StringPrototypeSlice(path, 0, end);
1298  },
1299
1300  /**
1301   * @param {string} path
1302   * @param {string} [suffix]
1303   * @returns {string}
1304   */
1305  basename(path, suffix) {
1306    if (suffix !== undefined)
1307      validateString(suffix, 'ext');
1308    validateString(path, 'path');
1309
1310    let start = 0;
1311    let end = -1;
1312    let matchedSlash = true;
1313
1314    if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) {
1315      if (suffix === path)
1316        return '';
1317      let extIdx = suffix.length - 1;
1318      let firstNonSlashEnd = -1;
1319      for (let i = path.length - 1; i >= 0; --i) {
1320        const code = StringPrototypeCharCodeAt(path, i);
1321        if (code === CHAR_FORWARD_SLASH) {
1322          // If we reached a path separator that was not part of a set of path
1323          // separators at the end of the string, stop now
1324          if (!matchedSlash) {
1325            start = i + 1;
1326            break;
1327          }
1328        } else {
1329          if (firstNonSlashEnd === -1) {
1330            // We saw the first non-path separator, remember this index in case
1331            // we need it if the extension ends up not matching
1332            matchedSlash = false;
1333            firstNonSlashEnd = i + 1;
1334          }
1335          if (extIdx >= 0) {
1336            // Try to match the explicit extension
1337            if (code === StringPrototypeCharCodeAt(suffix, extIdx)) {
1338              if (--extIdx === -1) {
1339                // We matched the extension, so mark this as the end of our path
1340                // component
1341                end = i;
1342              }
1343            } else {
1344              // Extension does not match, so our result is the entire path
1345              // component
1346              extIdx = -1;
1347              end = firstNonSlashEnd;
1348            }
1349          }
1350        }
1351      }
1352
1353      if (start === end)
1354        end = firstNonSlashEnd;
1355      else if (end === -1)
1356        end = path.length;
1357      return StringPrototypeSlice(path, start, end);
1358    }
1359    for (let i = path.length - 1; i >= 0; --i) {
1360      if (StringPrototypeCharCodeAt(path, i) === CHAR_FORWARD_SLASH) {
1361        // If we reached a path separator that was not part of a set of path
1362        // separators at the end of the string, stop now
1363        if (!matchedSlash) {
1364          start = i + 1;
1365          break;
1366        }
1367      } else if (end === -1) {
1368        // We saw the first non-path separator, mark this as the end of our
1369        // path component
1370        matchedSlash = false;
1371        end = i + 1;
1372      }
1373    }
1374
1375    if (end === -1)
1376      return '';
1377    return StringPrototypeSlice(path, start, end);
1378  },
1379
1380  /**
1381   * @param {string} path
1382   * @returns {string}
1383   */
1384  extname(path) {
1385    validateString(path, 'path');
1386    let startDot = -1;
1387    let startPart = 0;
1388    let end = -1;
1389    let matchedSlash = true;
1390    // Track the state of characters (if any) we see before our first dot and
1391    // after any path separator we find
1392    let preDotState = 0;
1393    for (let i = path.length - 1; i >= 0; --i) {
1394      const code = StringPrototypeCharCodeAt(path, i);
1395      if (code === CHAR_FORWARD_SLASH) {
1396        // If we reached a path separator that was not part of a set of path
1397        // separators at the end of the string, stop now
1398        if (!matchedSlash) {
1399          startPart = i + 1;
1400          break;
1401        }
1402        continue;
1403      }
1404      if (end === -1) {
1405        // We saw the first non-path separator, mark this as the end of our
1406        // extension
1407        matchedSlash = false;
1408        end = i + 1;
1409      }
1410      if (code === CHAR_DOT) {
1411        // If this is our first dot, mark it as the start of our extension
1412        if (startDot === -1)
1413          startDot = i;
1414        else if (preDotState !== 1)
1415          preDotState = 1;
1416      } else if (startDot !== -1) {
1417        // We saw a non-dot and non-path separator before our dot, so we should
1418        // have a good chance at having a non-empty extension
1419        preDotState = -1;
1420      }
1421    }
1422
1423    if (startDot === -1 ||
1424        end === -1 ||
1425        // We saw a non-dot character immediately before the dot
1426        preDotState === 0 ||
1427        // The (right-most) trimmed path component is exactly '..'
1428        (preDotState === 1 &&
1429         startDot === end - 1 &&
1430         startDot === startPart + 1)) {
1431      return '';
1432    }
1433    return StringPrototypeSlice(path, startDot, end);
1434  },
1435
1436  format: FunctionPrototypeBind(_format, null, '/'),
1437
1438  /**
1439   * @param {string} path
1440   * @returns {{
1441   *   dir: string;
1442   *   root: string;
1443   *   base: string;
1444   *   name: string;
1445   *   ext: string;
1446   *   }}
1447   */
1448  parse(path) {
1449    validateString(path, 'path');
1450
1451    const ret = { root: '', dir: '', base: '', ext: '', name: '' };
1452    if (path.length === 0)
1453      return ret;
1454    const isAbsolute =
1455      StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH;
1456    let start;
1457    if (isAbsolute) {
1458      ret.root = '/';
1459      start = 1;
1460    } else {
1461      start = 0;
1462    }
1463    let startDot = -1;
1464    let startPart = 0;
1465    let end = -1;
1466    let matchedSlash = true;
1467    let i = path.length - 1;
1468
1469    // Track the state of characters (if any) we see before our first dot and
1470    // after any path separator we find
1471    let preDotState = 0;
1472
1473    // Get non-dir info
1474    for (; i >= start; --i) {
1475      const code = StringPrototypeCharCodeAt(path, i);
1476      if (code === CHAR_FORWARD_SLASH) {
1477        // If we reached a path separator that was not part of a set of path
1478        // separators at the end of the string, stop now
1479        if (!matchedSlash) {
1480          startPart = i + 1;
1481          break;
1482        }
1483        continue;
1484      }
1485      if (end === -1) {
1486        // We saw the first non-path separator, mark this as the end of our
1487        // extension
1488        matchedSlash = false;
1489        end = i + 1;
1490      }
1491      if (code === CHAR_DOT) {
1492        // If this is our first dot, mark it as the start of our extension
1493        if (startDot === -1)
1494          startDot = i;
1495        else if (preDotState !== 1)
1496          preDotState = 1;
1497      } else if (startDot !== -1) {
1498        // We saw a non-dot and non-path separator before our dot, so we should
1499        // have a good chance at having a non-empty extension
1500        preDotState = -1;
1501      }
1502    }
1503
1504    if (end !== -1) {
1505      const start = startPart === 0 && isAbsolute ? 1 : startPart;
1506      if (startDot === -1 ||
1507          // We saw a non-dot character immediately before the dot
1508          preDotState === 0 ||
1509          // The (right-most) trimmed path component is exactly '..'
1510          (preDotState === 1 &&
1511          startDot === end - 1 &&
1512          startDot === startPart + 1)) {
1513        ret.base = ret.name = StringPrototypeSlice(path, start, end);
1514      } else {
1515        ret.name = StringPrototypeSlice(path, start, startDot);
1516        ret.base = StringPrototypeSlice(path, start, end);
1517        ret.ext = StringPrototypeSlice(path, startDot, end);
1518      }
1519    }
1520
1521    if (startPart > 0)
1522      ret.dir = StringPrototypeSlice(path, 0, startPart - 1);
1523    else if (isAbsolute)
1524      ret.dir = '/';
1525
1526    return ret;
1527  },
1528
1529  sep: '/',
1530  delimiter: ':',
1531  win32: null,
1532  posix: null,
1533};
1534
1535posix.win32 = win32.win32 = win32;
1536posix.posix = win32.posix = posix;
1537
1538// Legacy internal API, docs-only deprecated: DEP0080
1539win32._makeLong = win32.toNamespacedPath;
1540posix._makeLong = posix.toNamespacedPath;
1541
1542module.exports = platformIsWin32 ? win32 : posix;
1543