Skip to content

Commit ba411f1

Browse files
TimothyGuMylesBorins
authored andcommitted
path: fix win32 volume-relative paths
`path.resolve()` and `path.join()` are left alone in this commit due to the lack of clear semantics. Backport-PR-URL: #14787 PR-URL: #14440Fixes: #14405 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Tobias Nießen <[email protected]>
1 parent 3f2b8a9 commit ba411f1

File tree

3 files changed

+97
-58
lines changed

3 files changed

+97
-58
lines changed

‎lib/path.js‎

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -885,14 +885,28 @@ const win32 ={
885885

886886
extname: functionextname(path){
887887
assertPath(path);
888+
varstart=0;
888889
varstartDot=-1;
889890
varstartPart=0;
890891
varend=-1;
891892
varmatchedSlash=true;
892893
// Track the state of characters (if any) we see before our first dot and
893894
// after any path separator we find
894895
varpreDotState=0;
895-
for(vari=path.length-1;i>=0;--i){
896+
897+
// Check for a drive letter prefix so as not to mistake the following
898+
// path separator as an extra separator at the end of the path that can be
899+
// disregarded
900+
if(path.length>=2){
901+
constcode=path.charCodeAt(0);
902+
if(path.charCodeAt(1)===58/*:*/&&
903+
((code>=65/*A*/&&code<=90/*Z*/)||
904+
(code>=97/*a*/&&code<=122/*z*/))){
905+
start=startPart=2;
906+
}
907+
}
908+
909+
for(vari=path.length-1;i>=start;--i){
896910
constcode=path.charCodeAt(i);
897911
if(code===47/*/*/||code===92/*\*/){
898912
// If we reached a path separator that was not part of a set of path
@@ -956,15 +970,12 @@ const win32 ={
956970
varlen=path.length;
957971
varrootEnd=0;
958972
varcode=path.charCodeAt(0);
959-
varisAbsolute=false;
960973

961974
// Try to match a root
962975
if(len>1){
963976
if(code===47/*/*/||code===92/*\*/){
964977
// Possible UNC root
965978

966-
isAbsolute=true;
967-
968979
code=path.charCodeAt(1);
969980
rootEnd=1;
970981
if(code===47/*/*/||code===92/*\*/){
@@ -1023,7 +1034,6 @@ const win32 ={
10231034
ret.root=ret.dir=path.slice(0,3);
10241035
returnret;
10251036
}
1026-
isAbsolute=true;
10271037
rootEnd=3;
10281038
}
10291039
}else{
@@ -1045,7 +1055,7 @@ const win32 ={
10451055
ret.root=path.slice(0,rootEnd);
10461056

10471057
varstartDot=-1;
1048-
varstartPart=0;
1058+
varstartPart=rootEnd;
10491059
varend=-1;
10501060
varmatchedSlash=true;
10511061
vari=path.length-1;
@@ -1094,26 +1104,21 @@ const win32 ={
10941104
startDot===end-1&&
10951105
startDot===startPart+1)){
10961106
if(end!==-1){
1097-
if(startPart===0&&isAbsolute)
1098-
ret.base=ret.name=path.slice(rootEnd,end);
1099-
else
1100-
ret.base=ret.name=path.slice(startPart,end);
1107+
ret.base=ret.name=path.slice(startPart,end);
11011108
}
11021109
}else{
1103-
if(startPart===0&&isAbsolute){
1104-
ret.name=path.slice(rootEnd,startDot);
1105-
ret.base=path.slice(rootEnd,end);
1106-
}else{
1107-
ret.name=path.slice(startPart,startDot);
1108-
ret.base=path.slice(startPart,end);
1109-
}
1110+
ret.name=path.slice(startPart,startDot);
1111+
ret.base=path.slice(startPart,end);
11101112
ret.ext=path.slice(startDot,end);
11111113
}
11121114

1113-
if(startPart>0)
1115+
// If the directory is the root, use the entire root as the `dir` including
1116+
// the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the
1117+
// trailing slash (`C:\abc\def` -> `C:\abc`).
1118+
if(startPart>0&&startPart!==rootEnd)
11141119
ret.dir=path.slice(0,startPart-1);
1115-
elseif(isAbsolute)
1116-
ret.dir=path.slice(0,rootEnd);
1120+
else
1121+
ret.dir=ret.root;
11171122

11181123
returnret;
11191124
},

‎test/parallel/test-path-parse-format.js‎

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,33 @@ const assert = require('assert');
44
constpath=require('path');
55

66
constwinPaths=[
7-
'C:\\path\\dir\\index.html',
8-
'C:\\another_path\\DIR\\1\\2\\33\\\\index',
9-
'another_path\\DIR with spaces\\1\\2\\33\\index',
10-
'\\foo\\C:',
11-
'file',
12-
'.\\file',
13-
'C:\\',
14-
'C:',
15-
'\\',
16-
'',
7+
// [path, root]
8+
['C:\\path\\dir\\index.html','C:\\'],
9+
['C:\\another_path\\DIR\\1\\2\\33\\\\index','C:\\'],
10+
['another_path\\DIR with spaces\\1\\2\\33\\index',''],
11+
['\\','\\'],
12+
['\\foo\\C:','\\'],
13+
['file',''],
14+
['file:stream',''],
15+
['.\\file',''],
16+
['C:','C:'],
17+
['C:.','C:'],
18+
['C:..','C:'],
19+
['C:abc','C:'],
20+
['C:\\','C:\\'],
21+
['C:\\abc','C:\\'],
22+
['',''],
1723

1824
// unc
19-
'\\\\server\\share\\file_path',
20-
'\\\\server two\\shared folder\\file path.zip',
21-
'\\\\teela\\admin$\\system32',
22-
'\\\\?\\UNC\\server\\share'
25+
['\\\\server\\share\\file_path','\\\\server\\share\\'],
26+
['\\\\server two\\shared folder\\file path.zip',
27+
'\\\\server two\\shared folder\\'],
28+
['\\\\teela\\admin$\\system32','\\\\teela\\admin$\\'],
29+
['\\\\?\\UNC\\server\\share','\\\\?\\UNC\\']
2330
];
2431

2532
constwinSpecialCaseParseTests=[
2633
['/foo/bar',{root: '/'}],
27-
['C:',{root: 'C:',dir: 'C:',base: ''}],
28-
['C:\\',{root: 'C:\\',dir: 'C:\\',base: ''}]
2934
];
3035

3136
constwinSpecialCaseFormatTests=[
@@ -39,26 +44,27 @@ const winSpecialCaseFormatTests = [
3944
];
4045

4146
constunixPaths=[
42-
'/home/user/dir/file.txt',
43-
'/home/user/a dir/another File.zip',
44-
'/home/user/a dir//another&File.',
45-
'/home/user/a$$$dir//another File.zip',
46-
'user/dir/another File.zip',
47-
'file',
48-
'.\\file',
49-
'./file',
50-
'C:\\foo',
51-
'/',
52-
'',
53-
'.',
54-
'..',
55-
'/foo',
56-
'/foo.',
57-
'/foo.bar',
58-
'/.',
59-
'/.foo',
60-
'/.foo.bar',
61-
'/foo/bar.baz',
47+
// [path, root]
48+
['/home/user/dir/file.txt','/'],
49+
['/home/user/a dir/another File.zip','/'],
50+
['/home/user/a dir//another&File.','/'],
51+
['/home/user/a$$$dir//another File.zip','/'],
52+
['user/dir/another File.zip',''],
53+
['file',''],
54+
['.\\file',''],
55+
['./file',''],
56+
['C:\\foo',''],
57+
['/','/'],
58+
['',''],
59+
['.',''],
60+
['..',''],
61+
['/foo','/'],
62+
['/foo.','/'],
63+
['/foo.bar','/'],
64+
['/.','/'],
65+
['/.foo','/'],
66+
['/.foo.bar','/'],
67+
['/foo/bar.baz','/']
6268
];
6369

6470
constunixSpecialCaseFormatTests=[
@@ -169,14 +175,16 @@ function checkErrors(path){
169175
}
170176

171177
functioncheckParseFormat(path,paths){
172-
paths.forEach(function(element){
178+
paths.forEach(function([element,root]){
173179
constoutput=path.parse(element);
174180
assert.strictEqual(typeofoutput.root,'string');
175181
assert.strictEqual(typeofoutput.dir,'string');
176182
assert.strictEqual(typeofoutput.base,'string');
177183
assert.strictEqual(typeofoutput.ext,'string');
178184
assert.strictEqual(typeofoutput.name,'string');
179185
assert.strictEqual(path.format(output),element);
186+
assert.strictEqual(output.root,root);
187+
assert(output.dir.startsWith(output.root));
180188
assert.strictEqual(output.dir,output.dir ? path.dirname(element) : '');
181189
assert.strictEqual(output.base,path.basename(element));
182190
assert.strictEqual(output.ext,path.extname(element));

‎test/parallel/test-path.js‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ assert.strictEqual(path.win32.basename('aaa\\bbb', 'bbb'), 'bbb');
5151
assert.strictEqual(path.win32.basename('aaa\\bbb\\\\\\\\','bbb'),'bbb');
5252
assert.strictEqual(path.win32.basename('aaa\\bbb','bb'),'b');
5353
assert.strictEqual(path.win32.basename('aaa\\bbb','b'),'bb');
54+
assert.strictEqual(path.win32.basename('C:'),'');
55+
assert.strictEqual(path.win32.basename('C:.'),'.');
56+
assert.strictEqual(path.win32.basename('C:\\'),'');
57+
assert.strictEqual(path.win32.basename('C:\\dir\\base.ext'),'base.ext');
58+
assert.strictEqual(path.win32.basename('C:\\basename.ext'),'basename.ext');
59+
assert.strictEqual(path.win32.basename('C:basename.ext'),'basename.ext');
60+
assert.strictEqual(path.win32.basename('C:basename.ext\\'),'basename.ext');
61+
assert.strictEqual(path.win32.basename('C:basename.ext\\\\'),'basename.ext');
62+
assert.strictEqual(path.win32.basename('C:foo'),'foo');
63+
assert.strictEqual(path.win32.basename('file:stream'),'file:stream');
5464

5565
// On unix a backslash is just treated as any other character.
5666
assert.strictEqual(path.posix.basename('\\dir\\basename.ext'),
@@ -99,6 +109,8 @@ assert.strictEqual(path.win32.dirname('c:foo\\'), 'c:');
99109
assert.strictEqual(path.win32.dirname('c:foo\\bar'),'c:foo');
100110
assert.strictEqual(path.win32.dirname('c:foo\\bar\\'),'c:foo');
101111
assert.strictEqual(path.win32.dirname('c:foo\\bar\\baz'),'c:foo\\bar');
112+
assert.strictEqual(path.win32.dirname('file:stream'),'.');
113+
assert.strictEqual(path.win32.dirname('dir\\file:stream'),'dir');
102114
assert.strictEqual(path.win32.dirname('\\\\unc\\share'),
103115
'\\\\unc\\share');
104116
assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo'),
@@ -166,6 +178,7 @@ assert.strictEqual(path.win32.dirname('foo'), '.');
166178
['file./','.'],
167179
['file.//','.'],
168180
].forEach((test)=>{
181+
constexpected=test[1];
169182
[path.posix.extname,path.win32.extname].forEach((extname)=>{
170183
letinput=test[0];
171184
letos;
@@ -176,12 +189,19 @@ assert.strictEqual(path.win32.dirname('foo'), '.');
176189
os='posix';
177190
}
178191
constactual=extname(input);
179-
constexpected=test[1];
180192
constmessage=`path.${os}.extname(${JSON.stringify(input)})\n expect=${
181193
JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
182194
if(actual!==expected)
183195
failures.push(`\n${message}`);
184196
});
197+
{
198+
constinput=`C:${test[0].replace(slashRE,'\\')}`;
199+
constactual=path.win32.extname(input);
200+
constmessage=`path.win32.extname(${JSON.stringify(input)})\n expect=${
201+
JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
202+
if(actual!==expected)
203+
failures.push(`\n${message}`);
204+
}
185205
});
186206
assert.strictEqual(failures.length,0,failures.join(''));
187207

@@ -385,6 +405,12 @@ assert.strictEqual(path.win32.normalize('a//b//.'), 'a\\b');
385405
assert.strictEqual(path.win32.normalize('//server/share/dir/file.ext'),
386406
'\\\\server\\share\\dir\\file.ext');
387407
assert.strictEqual(path.win32.normalize('/a/b/c/../../../x/y/z'),'\\x\\y\\z');
408+
assert.strictEqual(path.win32.normalize('C:'),'C:.');
409+
assert.strictEqual(path.win32.normalize('C:..\\abc'),'C:..\\abc');
410+
assert.strictEqual(path.win32.normalize('C:..\\..\\abc\\..\\def'),
411+
'C:..\\..\\def');
412+
assert.strictEqual(path.win32.normalize('C:\\.'),'C:\\');
413+
assert.strictEqual(path.win32.normalize('file:stream'),'file:stream');
388414

389415
assert.strictEqual(path.posix.normalize('./fixtures///b/../b/c.js'),
390416
'fixtures/b/c.js');

0 commit comments

Comments
(0)