Projekt

Obecné

Profil

Stáhnout (16.7 KB) Statistiky
| Větev: | Revize:
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

    
24

    
25
var isWindows = process.platform === 'win32';
26
var util = require('util');
27

    
28

    
29
// resolves . and .. elements in a path array with directory names there
30
// must be no slashes or device names (c:\) in the array
31
// (so also no leading and trailing slashes - it does not distinguish
32
// relative and absolute paths)
33
function normalizeArray(parts, allowAboveRoot) {
34
  var res = [];
35
  for (var i = 0; i < parts.length; i++) {
36
    var p = parts[i];
37

    
38
    // ignore empty parts
39
    if (!p || p === '.')
40
      continue;
41

    
42
    if (p === '..') {
43
      if (res.length && res[res.length - 1] !== '..') {
44
        res.pop();
45
      } else if (allowAboveRoot) {
46
        res.push('..');
47
      }
48
    } else {
49
      res.push(p);
50
    }
51
  }
52

    
53
  return res;
54
}
55

    
56
// returns an array with empty elements removed from either end of the input
57
// array or the original array if no elements need to be removed
58
function trimArray(arr) {
59
  var lastIndex = arr.length - 1;
60
  var start = 0;
61
  for (; start <= lastIndex; start++) {
62
    if (arr[start])
63
      break;
64
  }
65

    
66
  var end = lastIndex;
67
  for (; end >= 0; end--) {
68
    if (arr[end])
69
      break;
70
  }
71

    
72
  if (start === 0 && end === lastIndex)
73
    return arr;
74
  if (start > end)
75
    return [];
76
  return arr.slice(start, end + 1);
77
}
78

    
79
// Regex to split a windows path into three parts: [*, device, slash,
80
// tail] windows-only
81
var splitDeviceRe =
82
    /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/;
83

    
84
// Regex to split the tail part of the above into [*, dir, basename, ext]
85
var splitTailRe =
86
    /^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/;
87

    
88
var win32 = {};
89

    
90
// Function to split a filename into [root, dir, basename, ext]
91
function win32SplitPath(filename) {
92
  // Separate device+slash from tail
93
  var result = splitDeviceRe.exec(filename),
94
      device = (result[1] || '') + (result[2] || ''),
95
      tail = result[3] || '';
96
  // Split the tail into dir, basename and extension
97
  var result2 = splitTailRe.exec(tail),
98
      dir = result2[1],
99
      basename = result2[2],
100
      ext = result2[3];
101
  return [device, dir, basename, ext];
102
}
103

    
104
function win32StatPath(path) {
105
  var result = splitDeviceRe.exec(path),
106
      device = result[1] || '',
107
      isUnc = !!device && device[1] !== ':';
108
  return {
109
    device: device,
110
    isUnc: isUnc,
111
    isAbsolute: isUnc || !!result[2], // UNC paths are always absolute
112
    tail: result[3]
113
  };
114
}
115

    
116
function normalizeUNCRoot(device) {
117
  return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\');
118
}
119

    
120
// path.resolve([from ...], to)
121
win32.resolve = function() {
122
  var resolvedDevice = '',
123
      resolvedTail = '',
124
      resolvedAbsolute = false;
125

    
126
  for (var i = arguments.length - 1; i >= -1; i--) {
127
    var path;
128
    if (i >= 0) {
129
      path = arguments[i];
130
    } else if (!resolvedDevice) {
131
      path = process.cwd();
132
    } else {
133
      // Windows has the concept of drive-specific current working
134
      // directories. If we've resolved a drive letter but not yet an
135
      // absolute path, get cwd for that drive. We're sure the device is not
136
      // an unc path at this points, because unc paths are always absolute.
137
      path = process.env['=' + resolvedDevice];
138
      // Verify that a drive-local cwd was found and that it actually points
139
      // to our drive. If not, default to the drive's root.
140
      if (!path || path.substr(0, 3).toLowerCase() !==
141
          resolvedDevice.toLowerCase() + '\\') {
142
        path = resolvedDevice + '\\';
143
      }
144
    }
145

    
146
    // Skip empty and invalid entries
147
    if (!util.isString(path)) {
148
      throw new TypeError('Arguments to path.resolve must be strings');
149
    } else if (!path) {
150
      continue;
151
    }
152

    
153
    var result = win32StatPath(path),
154
        device = result.device,
155
        isUnc = result.isUnc,
156
        isAbsolute = result.isAbsolute,
157
        tail = result.tail;
158

    
159
    if (device &&
160
        resolvedDevice &&
161
        device.toLowerCase() !== resolvedDevice.toLowerCase()) {
162
      // This path points to another device so it is not applicable
163
      continue;
164
    }
165

    
166
    if (!resolvedDevice) {
167
      resolvedDevice = device;
168
    }
169
    if (!resolvedAbsolute) {
170
      resolvedTail = tail + '\\' + resolvedTail;
171
      resolvedAbsolute = isAbsolute;
172
    }
173

    
174
    if (resolvedDevice && resolvedAbsolute) {
175
      break;
176
    }
177
  }
178

    
179
  // Convert slashes to backslashes when `resolvedDevice` points to an UNC
180
  // root. Also squash multiple slashes into a single one where appropriate.
181
  if (isUnc) {
182
    resolvedDevice = normalizeUNCRoot(resolvedDevice);
183
  }
184

    
185
  // At this point the path should be resolved to a full absolute path,
186
  // but handle relative paths to be safe (might happen when process.cwd()
187
  // fails)
188

    
189
  // Normalize the tail path
190
  resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/),
191
                                !resolvedAbsolute).join('\\');
192

    
193
  return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) ||
194
         '.';
195
};
196

    
197

    
198
win32.normalize = function(path) {
199
  var result = win32StatPath(path),
200
      device = result.device,
201
      isUnc = result.isUnc,
202
      isAbsolute = result.isAbsolute,
203
      tail = result.tail,
204
      trailingSlash = /[\\\/]$/.test(tail);
205

    
206
  // Normalize the tail path
207
  tail = normalizeArray(tail.split(/[\\\/]+/), !isAbsolute).join('\\');
208

    
209
  if (!tail && !isAbsolute) {
210
    tail = '.';
211
  }
212
  if (tail && trailingSlash) {
213
    tail += '\\';
214
  }
215

    
216
  // Convert slashes to backslashes when `device` points to an UNC root.
217
  // Also squash multiple slashes into a single one where appropriate.
218
  if (isUnc) {
219
    device = normalizeUNCRoot(device);
220
  }
221

    
222
  return device + (isAbsolute ? '\\' : '') + tail;
223
};
224

    
225

    
226
win32.isAbsolute = function(path) {
227
  return win32StatPath(path).isAbsolute;
228
};
229

    
230
win32.join = function() {
231
  var paths = [];
232
  for (var i = 0; i < arguments.length; i++) {
233
    var arg = arguments[i];
234
    if (!util.isString(arg)) {
235
      throw new TypeError('Arguments to path.join must be strings');
236
    }
237
    if (arg) {
238
      paths.push(arg);
239
    }
240
  }
241

    
242
  var joined = paths.join('\\');
243

    
244
  // Make sure that the joined path doesn't start with two slashes, because
245
  // normalize() will mistake it for an UNC path then.
246
  //
247
  // This step is skipped when it is very clear that the user actually
248
  // intended to point at an UNC path. This is assumed when the first
249
  // non-empty string arguments starts with exactly two slashes followed by
250
  // at least one more non-slash character.
251
  //
252
  // Note that for normalize() to treat a path as an UNC path it needs to
253
  // have at least 2 components, so we don't filter for that here.
254
  // This means that the user can use join to construct UNC paths from
255
  // a server name and a share name; for example:
256
  //   path.join('//server', 'share') -> '\\\\server\\share\')
257
  if (!/^[\\\/]{2}[^\\\/]/.test(paths[0])) {
258
    joined = joined.replace(/^[\\\/]{2,}/, '\\');
259
  }
260

    
261
  return win32.normalize(joined);
262
};
263

    
264

    
265
// path.relative(from, to)
266
// it will solve the relative path from 'from' to 'to', for instance:
267
// from = 'C:\\orandea\\test\\aaa'
268
// to = 'C:\\orandea\\impl\\bbb'
269
// The output of the function should be: '..\\..\\impl\\bbb'
270
win32.relative = function(from, to) {
271
  from = win32.resolve(from);
272
  to = win32.resolve(to);
273

    
274
  // windows is not case sensitive
275
  var lowerFrom = from.toLowerCase();
276
  var lowerTo = to.toLowerCase();
277

    
278
  var toParts = trimArray(to.split('\\'));
279

    
280
  var lowerFromParts = trimArray(lowerFrom.split('\\'));
281
  var lowerToParts = trimArray(lowerTo.split('\\'));
282

    
283
  var length = Math.min(lowerFromParts.length, lowerToParts.length);
284
  var samePartsLength = length;
285
  for (var i = 0; i < length; i++) {
286
    if (lowerFromParts[i] !== lowerToParts[i]) {
287
      samePartsLength = i;
288
      break;
289
    }
290
  }
291

    
292
  if (samePartsLength == 0) {
293
    return to;
294
  }
295

    
296
  var outputParts = [];
297
  for (var i = samePartsLength; i < lowerFromParts.length; i++) {
298
    outputParts.push('..');
299
  }
300

    
301
  outputParts = outputParts.concat(toParts.slice(samePartsLength));
302

    
303
  return outputParts.join('\\');
304
};
305

    
306

    
307
win32._makeLong = function(path) {
308
  // Note: this will *probably* throw somewhere.
309
  if (!util.isString(path))
310
    return path;
311

    
312
  if (!path) {
313
    return '';
314
  }
315

    
316
  var resolvedPath = win32.resolve(path);
317

    
318
  if (/^[a-zA-Z]\:\\/.test(resolvedPath)) {
319
    // path is local filesystem path, which needs to be converted
320
    // to long UNC path.
321
    return '\\\\?\\' + resolvedPath;
322
  } else if (/^\\\\[^?.]/.test(resolvedPath)) {
323
    // path is network UNC path, which needs to be converted
324
    // to long UNC path.
325
    return '\\\\?\\UNC\\' + resolvedPath.substring(2);
326
  }
327

    
328
  return path;
329
};
330

    
331

    
332
win32.dirname = function(path) {
333
  var result = win32SplitPath(path),
334
      root = result[0],
335
      dir = result[1];
336

    
337
  if (!root && !dir) {
338
    // No dirname whatsoever
339
    return '.';
340
  }
341

    
342
  if (dir) {
343
    // It has a dirname, strip trailing slash
344
    dir = dir.substr(0, dir.length - 1);
345
  }
346

    
347
  return root + dir;
348
};
349

    
350

    
351
win32.basename = function(path, ext) {
352
  var f = win32SplitPath(path)[2];
353
  // TODO: make this comparison case-insensitive on windows?
354
  if (ext && f.substr(-1 * ext.length) === ext) {
355
    f = f.substr(0, f.length - ext.length);
356
  }
357
  return f;
358
};
359

    
360

    
361
win32.extname = function(path) {
362
  return win32SplitPath(path)[3];
363
};
364

    
365

    
366
win32.format = function(pathObject) {
367
  if (!util.isObject(pathObject)) {
368
    throw new TypeError(
369
        "Parameter 'pathObject' must be an object, not " + typeof pathObject
370
    );
371
  }
372

    
373
  var root = pathObject.root || '';
374

    
375
  if (!util.isString(root)) {
376
    throw new TypeError(
377
        "'pathObject.root' must be a string or undefined, not " +
378
        typeof pathObject.root
379
    );
380
  }
381

    
382
  var dir = pathObject.dir;
383
  var base = pathObject.base || '';
384
  if (!dir) {
385
    return base;
386
  }
387
  if (dir[dir.length - 1] === win32.sep) {
388
    return dir + base;
389
  }
390
  return dir + win32.sep + base;
391
};
392

    
393

    
394
win32.parse = function(pathString) {
395
  if (!util.isString(pathString)) {
396
    throw new TypeError(
397
        "Parameter 'pathString' must be a string, not " + typeof pathString
398
    );
399
  }
400
  var allParts = win32SplitPath(pathString);
401
  if (!allParts || allParts.length !== 4) {
402
    throw new TypeError("Invalid path '" + pathString + "'");
403
  }
404
  return {
405
    root: allParts[0],
406
    dir: allParts[0] + allParts[1].slice(0, -1),
407
    base: allParts[2],
408
    ext: allParts[3],
409
    name: allParts[2].slice(0, allParts[2].length - allParts[3].length)
410
  };
411
};
412

    
413

    
414
win32.sep = '\\';
415
win32.delimiter = ';';
416

    
417

    
418
// Split a filename into [root, dir, basename, ext], unix version
419
// 'root' is just a slash, or nothing.
420
var splitPathRe =
421
    /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
422
var posix = {};
423

    
424

    
425
function posixSplitPath(filename) {
426
  return splitPathRe.exec(filename).slice(1);
427
}
428

    
429

    
430
// path.resolve([from ...], to)
431
// posix version
432
posix.resolve = function() {
433
  var resolvedPath = '',
434
      resolvedAbsolute = false;
435

    
436
  for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
437
    var path = (i >= 0) ? arguments[i] : process.cwd();
438

    
439
    // Skip empty and invalid entries
440
    if (!util.isString(path)) {
441
      throw new TypeError('Arguments to path.resolve must be strings');
442
    } else if (!path) {
443
      continue;
444
    }
445

    
446
    resolvedPath = path + '/' + resolvedPath;
447
    resolvedAbsolute = path[0] === '/';
448
  }
449

    
450
  // At this point the path should be resolved to a full absolute path, but
451
  // handle relative paths to be safe (might happen when process.cwd() fails)
452

    
453
  // Normalize the path
454
  resolvedPath = normalizeArray(resolvedPath.split('/'),
455
                                !resolvedAbsolute).join('/');
456

    
457
  return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
458
};
459

    
460
// path.normalize(path)
461
// posix version
462
posix.normalize = function(path) {
463
  var isAbsolute = posix.isAbsolute(path),
464
      trailingSlash = path && path[path.length - 1] === '/';
465

    
466
  // Normalize the path
467
  path = normalizeArray(path.split('/'), !isAbsolute).join('/');
468

    
469
  if (!path && !isAbsolute) {
470
    path = '.';
471
  }
472
  if (path && trailingSlash) {
473
    path += '/';
474
  }
475

    
476
  return (isAbsolute ? '/' : '') + path;
477
};
478

    
479
// posix version
480
posix.isAbsolute = function(path) {
481
  return path.charAt(0) === '/';
482
};
483

    
484
// posix version
485
posix.join = function() {
486
  var path = '';
487
  for (var i = 0; i < arguments.length; i++) {
488
    var segment = arguments[i];
489
    if (!util.isString(segment)) {
490
      throw new TypeError('Arguments to path.join must be strings');
491
    }
492
    if (segment) {
493
      if (!path) {
494
        path += segment;
495
      } else {
496
        path += '/' + segment;
497
      }
498
    }
499
  }
500
  return posix.normalize(path);
501
};
502

    
503

    
504
// path.relative(from, to)
505
// posix version
506
posix.relative = function(from, to) {
507
  from = posix.resolve(from).substr(1);
508
  to = posix.resolve(to).substr(1);
509

    
510
  var fromParts = trimArray(from.split('/'));
511
  var toParts = trimArray(to.split('/'));
512

    
513
  var length = Math.min(fromParts.length, toParts.length);
514
  var samePartsLength = length;
515
  for (var i = 0; i < length; i++) {
516
    if (fromParts[i] !== toParts[i]) {
517
      samePartsLength = i;
518
      break;
519
    }
520
  }
521

    
522
  var outputParts = [];
523
  for (var i = samePartsLength; i < fromParts.length; i++) {
524
    outputParts.push('..');
525
  }
526

    
527
  outputParts = outputParts.concat(toParts.slice(samePartsLength));
528

    
529
  return outputParts.join('/');
530
};
531

    
532

    
533
posix._makeLong = function(path) {
534
  return path;
535
};
536

    
537

    
538
posix.dirname = function(path) {
539
  var result = posixSplitPath(path),
540
      root = result[0],
541
      dir = result[1];
542

    
543
  if (!root && !dir) {
544
    // No dirname whatsoever
545
    return '.';
546
  }
547

    
548
  if (dir) {
549
    // It has a dirname, strip trailing slash
550
    dir = dir.substr(0, dir.length - 1);
551
  }
552

    
553
  return root + dir;
554
};
555

    
556

    
557
posix.basename = function(path, ext) {
558
  var f = posixSplitPath(path)[2];
559
  // TODO: make this comparison case-insensitive on windows?
560
  if (ext && f.substr(-1 * ext.length) === ext) {
561
    f = f.substr(0, f.length - ext.length);
562
  }
563
  return f;
564
};
565

    
566

    
567
posix.extname = function(path) {
568
  return posixSplitPath(path)[3];
569
};
570

    
571

    
572
posix.format = function(pathObject) {
573
  if (!util.isObject(pathObject)) {
574
    throw new TypeError(
575
        "Parameter 'pathObject' must be an object, not " + typeof pathObject
576
    );
577
  }
578

    
579
  var root = pathObject.root || '';
580

    
581
  if (!util.isString(root)) {
582
    throw new TypeError(
583
        "'pathObject.root' must be a string or undefined, not " +
584
        typeof pathObject.root
585
    );
586
  }
587

    
588
  var dir = pathObject.dir ? pathObject.dir + posix.sep : '';
589
  var base = pathObject.base || '';
590
  return dir + base;
591
};
592

    
593

    
594
posix.parse = function(pathString) {
595
  if (!util.isString(pathString)) {
596
    throw new TypeError(
597
        "Parameter 'pathString' must be a string, not " + typeof pathString
598
    );
599
  }
600
  var allParts = posixSplitPath(pathString);
601
  if (!allParts || allParts.length !== 4) {
602
    throw new TypeError("Invalid path '" + pathString + "'");
603
  }
604
  allParts[1] = allParts[1] || '';
605
  allParts[2] = allParts[2] || '';
606
  allParts[3] = allParts[3] || '';
607

    
608
  return {
609
    root: allParts[0],
610
    dir: allParts[0] + allParts[1].slice(0, -1),
611
    base: allParts[2],
612
    ext: allParts[3],
613
    name: allParts[2].slice(0, allParts[2].length - allParts[3].length)
614
  };
615
};
616

    
617

    
618
posix.sep = '/';
619
posix.delimiter = ':';
620

    
621

    
622
if (isWindows)
623
  module.exports = win32;
624
else /* posix */
625
  module.exports = posix;
626

    
627
module.exports.posix = posix;
628
module.exports.win32 = win32;
(5-5/5)