Skip to content

Commit 559a843

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

File tree

9 files changed

+56
-78
lines changed

9 files changed

+56
-78
lines changed

+stdlib/+java/normalize.m

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

+stdlib/+native/normalize.m

Lines changed: 12 additions & 33 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) == ".."
24-
if n == ""
25-
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) ~= [".", ""])
21+
if ~ismember(parts(i), [".", ""])
3722
if n == ""
3823
n = parts(i);
39-
elseif n == "/"
24+
elseif ismember(n, ["/", filesep])
4025
n = n + parts(i);
4126
else
42-
n = n + "/" + parts(i);
27+
n = n + filesep + 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: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
function n = normalize(apath)
2+
3+
n = string.empty;
4+
5+
exe = stdlib.perl_exe();
6+
if stdlib.strempty(exe)
7+
return
8+
end
9+
10+
cmd = sprintf('"%s" -MFile::Spec -e "print File::Spec->canonpath( ''%s'' )"', exe, apath);
11+
12+
[s, r] = system(cmd);
13+
if s == 0
14+
n = string(r);
15+
if strlength(n) == 0
16+
n = ".";
17+
end
18+
else
19+
n = string.empty;
20+
end
21+
22+
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.str(py.pathlib.Path(file)));
125
catch
136
n = "";
147
end
8+
9+
end

+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);

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

test/TestNormalize.m

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,9 @@
1414

1515
p = {
1616
{"", "."}, ...
17-
{"a/..", "."}, ...
17+
{"a/..", "a/.."}, ...
1818
{"/a/b/", fullfile("/a", "b")}, ...
1919
{"a/b/", fullfile("a", "b")}, ...
20-
{"a/../c", "c"}, ...
21-
{"a/b/../c", fullfile("a", "c")}, ...
22-
{"a/b/../../c", "c"}, ...
23-
{"a/b/../../c/..", "."}, ...
24-
{"a/b/../../c/../..", ".."}, ...
2520
{"a////b", fullfile("a", "b")}, ...
2621
{".a", ".a"}, ...
2722
{"..a", "..a"}, ...
@@ -31,9 +26,6 @@
3126
{"../a", fullfile("..", "a")}
3227
};
3328

34-
if ispc
35-
p{end+1} = {"//a/b/", "\\a\b"};
36-
end
3729
end
3830

3931
function d = init_drop_slash()

0 commit comments

Comments
 (0)