Skip to content

Commit 5149dd8

Browse files
committed
normalize: do not walk up
this is for safety as symlink might otherwise be stripped
1 parent 22c0258 commit 5149dd8

File tree

13 files changed

+74
-91
lines changed

13 files changed

+74
-91
lines changed

+stdlib/+java/normalize.m

Lines changed: 0 additions & 17 deletions
This file was deleted.

+stdlib/+native/normalize.m

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,34 @@
1-
function n = normalize(p)
1+
%% NATIVE.NORMALIZE normalize path
2+
% 2-3x as fast as non-native backends
3+
4+
function n = normalize(apath)
25
arguments
3-
p (1,1) string
6+
apath (1,1) string
47
end
58

6-
n = stdlib.posix(p);
7-
8-
uncslash = ispc() && startsWith(n, "//");
9-
10-
% use split to remove /../ and /./ and duplicated /
11-
parts = split(n, '/');
9+
parts = split(apath, ["/", filesep]);
1210
i0 = 1;
13-
if strncmp(n, "/", 1)
14-
n = "/";
15-
elseif ispc() && strlength(n) >= 2 && ~stdlib.strempty(stdlib.root_name(p))
11+
if startsWith(apath, "/" | filesep)
12+
n = extractBefore(apath, 2);
13+
elseif ispc() && strlength(apath) >= 2 && ~stdlib.strempty(stdlib.root_name(apath))
1614
n = parts(1);
1715
i0 = 2;
1816
else
1917
n = "";
2018
end
2119

2220
for i = i0:length(parts)
23-
if parts(i) == ".."
21+
if ~ismember(parts(i), [".", ""])
2422
if n == ""
2523
n = parts(i);
26-
elseif endsWith(n, "..")
27-
n = n + "/" + parts(i);
28-
else
29-
j = strfind(n, "/");
30-
if isempty(j)
31-
n = "";
32-
else
33-
n = n{1}(1:j(end)-1);
34-
end
35-
end
36-
elseif all(parts(i) ~= [".", ""])
37-
if n == ""
38-
n = parts(i);
39-
elseif n == "/"
24+
elseif ismember(n, ["/", filesep])
4025
n = n + parts(i);
4126
else
4227
n = n + "/" + parts(i);
4328
end
4429
end
4530
end
4631

47-
if uncslash
48-
n = strcat("/", n);
49-
end
50-
51-
n = fullfile(n);
52-
5332
if stdlib.strempty(n)
5433
n = ".";
5534
end

+stdlib/+perl/normalize.m

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
%% PERL.NORMALIZE normalize path
2+
%
3+
% Note: on Windows, walks up ".." despite specification and not doing so on Unix,
4+
% so we only allow this backend on Unix-like systems
5+
6+
function n = normalize(apath)
7+
8+
n = string.empty;
9+
10+
exe = stdlib.perl_exe();
11+
if stdlib.strempty(exe)
12+
return
13+
end
14+
15+
cmd = sprintf('"%s" -MFile::Spec -e "print File::Spec->canonpath( ''%s'' )"', exe, apath);
16+
17+
[s, r] = system(cmd);
18+
if s == 0
19+
n = string(r);
20+
if strlength(n) == 0
21+
n = ".";
22+
end
23+
else
24+
n = string.empty;
25+
end
26+
27+
end

+stdlib/+python/normalize.m

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
function n = normalize(file)
2-
arguments
3-
file (1,1) string
4-
end
52

63
try
7-
n = string(py.os.path.normpath(file));
8-
n = strip(n, 'right', '/');
9-
if ispc()
10-
n = strip(n, 'right', filesep);
11-
end
4+
n = string(py.pathlib.Path(file).as_posix());
125
catch
136
n = "";
147
end
8+
9+
end

+stdlib/Backend.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
end
8484
case 'perl'
8585
switch functionName
86-
case {'get_uid'}
86+
case {'get_uid', 'normalize'}
8787
if ispc(), continue, end
8888
end
8989
case 'python'

+stdlib/normalize.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
%% NORMALIZE remove redundant elements of path
22
%
3-
% normalize(p) remove redundant elements of path p
4-
% path need not exist, normalized path is returned
3+
% normalize(p) remove redundant elements of path p. Does not walk up ".." to be safe.
4+
% Path need not exist. Does not access physical filesystem.
55
%
66
%%% Inputs
77
% * p: path to normalize
@@ -13,7 +13,7 @@
1313
function [n, b] = normalize(file, backend)
1414
arguments
1515
file string
16-
backend (1,:) string = ["native", "java", "python"]
16+
backend (1,:) string = ["native", "python", "perl"]
1717
end
1818

1919
o = stdlib.Backend(mfilename(), backend);

+stdlib/relative_to.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
arguments
1717
base (1,1) string
1818
other (1,1) string
19-
backend (1,:) string = ["python", "native"]
19+
backend (1,:) string = ["native", "python"]
2020
end
2121

2222
o = stdlib.Backend(mfilename(), backend);

example/+javafun/normalize.m

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
function n = normalize(p)
1+
function n = normalize(file)
22

3-
% https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/nio/file/Path.html#normalize()
4-
o = javaPathObject(p).normalize();
5-
n = stdlib.drop_slash(jPosix(o));
3+
n = string(javaPathObject(file).normalize());
4+
5+
if stdlib.strempty(n)
6+
n = ".";
7+
else
8+
n = strip(n, 'right', '/');
9+
if ispc()
10+
n = strip(n, 'right', filesep);
11+
end
12+
end
613

714
end

example/BenchmarkNormalize.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
BenchmarkNormalize < matlab.perftest.TestCase
33

44
properties
5-
P = './../asdf/../../././//'
5+
P = './a/././//'
66
end
77

88
properties(TestParameter)

example/BenchmarkNormalizeRun.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
function [r, s] = BenchmarkNormalizeRun()
22
tname = "BenchmarkNormalize";
33

4-
r.same = run_bench(tname);
5-
s.exist = sampleSummary(r.same);
6-
disp(sortrows(s.exist, "Median"))
4+
r = run_bench(tname);
5+
s = sampleSummary(r);
6+
disp(sortrows(s, "Median"))
77
end
88

99

0 commit comments

Comments
 (0)