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