aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2019-08-19 15:45:46 +0200
committerBjörn Gustavsson <[email protected]>2019-08-22 12:23:45 +0200
commit72f704c7b83643bec889eabe3f9fe378639bb06e (patch)
tree6812843aaa76b489604466c687e15c90dc64a43b
parentd14919b70af5d08970f0e92aded2b375a79f4d94 (diff)
downloadotp-72f704c7b83643bec889eabe3f9fe378639bb06e.tar.gz
otp-72f704c7b83643bec889eabe3f9fe378639bb06e.tar.bz2
otp-72f704c7b83643bec889eabe3f9fe378639bb06e.zip
Fix filelib:wildcard/1,2 for patterns containing ".." and/or "@"
`..` was broken and only worked when it was used in the beginning of the pattern before any wildcard characters. For example: 1> filelib:wildcard("erts/.."). ["erts/.."] Using `..` preceded by wildcard characters would not work: 1> filelib:wildcard("*/.."). [] `@` is not a wildcard character but is used internally in `filelib` as an escape character and was not handled as other literal characters. That could lead to performance degradation as it disabled an optimization of the matching of the literal prefix of a pattern. It would also cause the following example to fail: 1> filelib:wildcard("@/.."). [] This commit corrects the handling `..` and also makes sure that the use of `@` in a pattern does not degrade performance. https://bugs.erlang.org/browse/ERL-1029
-rw-r--r--lib/stdlib/src/filelib.erl32
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl34
2 files changed, 61 insertions, 5 deletions
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index de839be5cf..d1a5a4dc35 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -281,6 +281,14 @@ do_wildcard_2([], _, Result, _Mod) ->
do_wildcard_3(Base, [[double_star]|Rest], Result, Mod) ->
do_double_star(".", [Base], Rest, Result, Mod, true);
+do_wildcard_3(Base, [".."|Rest], Result, Mod) ->
+ case do_is_dir(Base, Mod) of
+ true ->
+ Matches = [filename:join(Base, "..")],
+ do_wildcard_2(Matches, Rest, Result, Mod);
+ false ->
+ Result
+ end;
do_wildcard_3(Base0, [Pattern|Rest], Result, Mod) ->
case do_list_dir(Base0, Mod) of
{ok, Files} ->
@@ -387,15 +395,29 @@ compile_wildcard(Pattern0, Cwd0) ->
end.
compile_wildcard_2([Part|Rest], Root) ->
- case compile_part(Part) of
- Part ->
- compile_wildcard_2(Rest, compile_join(Root, Part));
- Pattern ->
- compile_wildcard_3(Rest, [Pattern,Root])
+ Pattern = compile_part(Part),
+ case is_literal_pattern(Pattern) of
+ true ->
+ %% Add this literal pattern to the literal pattern prefix.
+ %% This is an optimization to avoid listing all files of
+ %% a directory only to discard all but one. For example,
+ %% without this optimizaton, there would be three
+ %% redundant directory listings when executing this
+ %% wildcard: "./lib/compiler/ebin/*.beam"
+ compile_wildcard_2(Rest, compile_join(Root, Pattern));
+ false ->
+ %% This is the end of the literal prefix. Compile the
+ %% rest of the pattern.
+ compile_wildcard_3(Rest, [Pattern,Root])
end;
compile_wildcard_2([], {root,PrefixLen,Root}) ->
{{exists,Root},PrefixLen}.
+is_literal_pattern([H|T]) ->
+ is_integer(H) andalso is_literal_pattern(T);
+is_literal_pattern([]) ->
+ true.
+
compile_wildcard_3([Part|Rest], Result) ->
compile_wildcard_3(Rest, [compile_part(Part)|Result]);
compile_wildcard_3([], Result) ->
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index 7403d52881..527d083eaa 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -79,9 +79,11 @@ wildcard_one(Config) when is_list(Config) ->
do_wildcard_1(Dir,
fun(Wc) ->
L = filelib:wildcard(Wc),
+ L = filelib:wildcard(disable_prefix_opt(Wc)),
L = filelib:wildcard(Wc, erl_prim_loader),
L = filelib:wildcard(Wc, "."),
L = filelib:wildcard(Wc, Dir),
+ L = filelib:wildcard(disable_prefix_opt(Wc), Dir),
L = filelib:wildcard(Wc, Dir++"/.")
end),
file:set_cwd(OldCwd),
@@ -119,6 +121,14 @@ wcc(Wc, Error) ->
{'EXIT',{{badpattern,Error},
[{filelib,wildcard,2,_}|_]}} = (catch filelib:wildcard(Wc, ".")).
+disable_prefix_opt([C|Wc]) when $a =< C, C =< $z; C =:= $@ ->
+ %% There is an optimization for patterns that have a literal prefix
+ %% (such as "lib/compiler/ebin/*"). Test that we'll get the same result
+ %% if we disable that optimization.
+ [$[, C, $] | Wc];
+disable_prefix_opt(Wc) ->
+ Wc.
+
do_wildcard_1(Dir, Wcf0) ->
do_wildcard_2(Dir, Wcf0),
Wcf = fun(Wc0) ->
@@ -300,6 +310,30 @@ do_wildcard_10(Dir, Wcf) ->
end,
del(Files),
+ wildcard_11(Dir, Wcf).
+
+%% ERL-ERL-1029/OTP-15987: Fix problems with "@/.." and ".." in general.
+wildcard_11(Dir, Wcf) ->
+ Dirs0 = ["@","@dir","dir@"],
+ Dirs = [filename:join(Dir, D) || D <- Dirs0],
+ _ = [ok = file:make_dir(D) || D <- Dirs],
+ Files0 = ["@a","b@","x","y","z"],
+ Files = mkfiles(Files0, Dir),
+
+ ["@","@a","@dir","b@","dir@","x","y","z"] = Wcf("*"),
+ ["@"] = Wcf("@"),
+ ["@","@a","@dir"] = Wcf("@*"),
+ ["@/..","@dir/.."] = Wcf("@*/.."),
+ ["@/../@","@/../@a","@/../@dir",
+ "@dir/../@","@dir/../@a","@dir/../@dir"] = Wcf("@*/../@*"),
+
+ %% Non-directories followed by "/.." should not match any files.
+ [] = Wcf("@a/.."),
+ [] = Wcf("x/.."),
+
+ %% Cleanup.
+ del(Files),
+ [ok = file:del_dir(D) || D <- Dirs],
ok.
fold_files(Config) when is_list(Config) ->