diff options
Diffstat (limited to 'lib/sasl/src')
-rw-r--r-- | lib/sasl/src/erlsrv.erl | 31 | ||||
-rw-r--r-- | lib/sasl/src/release_handler.erl | 191 | ||||
-rw-r--r-- | lib/sasl/src/release_handler_1.erl | 91 | ||||
-rw-r--r-- | lib/sasl/src/systools_lib.erl | 33 |
4 files changed, 208 insertions, 138 deletions
diff --git a/lib/sasl/src/erlsrv.erl b/lib/sasl/src/erlsrv.erl index f9804c41dc..086dc7c651 100644 --- a/lib/sasl/src/erlsrv.erl +++ b/lib/sasl/src/erlsrv.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -75,14 +75,21 @@ write_all_data(Port,[H|T]) -> write_all_data(Port,T). read_all_data(Port) -> + lists:reverse(read_all_data(Port,[],[])). +read_all_data(Port,Line,Lines) -> receive + {Port, {data, {noeol,Data}}} -> + read_all_data(Port,Line++Data,Lines); {Port, {data, {eol,Data}}} -> - [ Data | read_all_data(Port)]; - _ -> + read_all_data(Port,[],[Line++Data|Lines]); + {Port,_Other} -> Port ! {self(), close}, receive {Port, closed} -> - [] + case Line of + [] -> Lines; + _ -> [Line|Lines] + end end end. @@ -208,7 +215,7 @@ store_service(EmulatorVersion,Service) -> false -> {error, no_servicename}; {value, {_,Name}} -> - {Action,Service1} = case get_service(Name) of + {Action,Service1} = case get_service(EmulatorVersion,Name) of {error, no_such_service} -> {"add",Service}; _ -> @@ -377,8 +384,14 @@ pick_argument(_,[],Acc) -> {Acc, ""}; pick_argument(normal,[$ |T],Acc) -> {Acc,T}; +pick_argument(normal,[$\\|T],Acc) -> + pick_argument(normal_escaped,T,[$\\|Acc]); pick_argument(normal,[$"|T],Acc) -> pick_argument(quoted,T,[$"|Acc]); +pick_argument(normal_escaped,[$"|T],Acc) -> + pick_argument(bquoted,T,[$"|Acc]); +pick_argument(normal_escaped,[A|T],Acc) -> + pick_argument(normal,T,[A|Acc]); pick_argument(quoted_escaped,[H|T],Acc) -> pick_argument(quoted,T,[H|Acc]); pick_argument(quoted,[$"|T],Acc) -> @@ -387,6 +400,14 @@ pick_argument(quoted,[$\\|T],Acc) -> pick_argument(quoted_escaped,T,[$\\|Acc]); pick_argument(quoted,[H|T],Acc) -> pick_argument(quoted,T,[H|Acc]); +pick_argument(bquoted_escaped,[$"|T],Acc) -> + pick_argument(normal,T,[$"|Acc]); +pick_argument(bquoted_escaped,[H|T],Acc) -> + pick_argument(bquoted,T,[H|Acc]); +pick_argument(bquoted,[$\\|T],Acc) -> + pick_argument(bquoted_escaped,T,[$\\|Acc]); +pick_argument(bquoted,[H|T],Acc) -> + pick_argument(bquoted,T,[H|Acc]); pick_argument(normal,[H|T],Acc) -> pick_argument(normal,T,[H|Acc]). diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl index b60aa847df..ab54b1d00b 100644 --- a/lib/sasl/src/release_handler.erl +++ b/lib/sasl/src/release_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -291,7 +291,8 @@ check_script(Script, LibDirs) -> release_handler_1:check_script(Script, LibDirs). %%----------------------------------------------------------------- -%% eval_script(Script, Apps, LibDirs, Opts) -> {ok, UnPurged} | +%% eval_script(Script, Apps, LibDirs, NewLibs, Opts) -> +%% {ok, UnPurged} | %% restart_new_emulator | %% {error, Error} %% {'EXIT', Reason} @@ -299,9 +300,13 @@ check_script(Script, LibDirs) -> %% net_kernel:monitor_nodes(true) before calling this function. %% No! No other process than the release_handler can ever call this %% function, if sync_nodes is used. -%%----------------------------------------------------------------- -eval_script(Script, Apps, LibDirs, Opts) -> - catch release_handler_1:eval_script(Script, Apps, LibDirs, Opts). +%% +%% LibDirs is a list of all applications, while NewLibs is a list of +%% applications that have changed version between the current and the +%% new release. +%% ----------------------------------------------------------------- +eval_script(Script, Apps, LibDirs, NewLibs, Opts) -> + catch release_handler_1:eval_script(Script, Apps, LibDirs, NewLibs, Opts). %%----------------------------------------------------------------- %% Func: create_RELEASES(Root, RelFile, LibDirs) -> ok | {error, Reason} @@ -405,6 +410,7 @@ eval_appup_script(App, ToVsn, ToDir, Script) -> Res = release_handler_1:eval_script(Script, [], % [AppSpec] [{App, ToVsn, ToDir}], + [{App, ToVsn, ToDir}], []), % [Opt] case Res of {ok, _Unpurged} -> @@ -890,6 +896,7 @@ do_check_install_release(RelDir, Vsn, Releases, Masters) -> end. do_install_release(#state{start_prg = StartPrg, + root = RootDir, rel_dir = RelDir, releases = Releases, masters = Masters, static_emulator = Static}, @@ -905,7 +912,9 @@ do_install_release(#state{start_prg = StartPrg, EnvBefore = application_controller:prep_config_change(), Apps = change_appl_data(RelDir, Release, Masters), LibDirs = Release#release.libs, - case eval_script(Script, Apps, LibDirs, Opts) of + NewLibs = get_new_libs(LatestRelease#release.libs, + Release#release.libs), + case eval_script(Script, Apps, LibDirs, NewLibs, Opts) of {ok, []} -> application_controller:config_change(EnvBefore), mon_nodes(false), @@ -926,8 +935,8 @@ do_install_release(#state{start_prg = StartPrg, NReleases = set_status(Vsn, current, Releases), NReleases2 = set_status(Vsn,tmp_current,NReleases), write_releases(RelDir, NReleases2, Masters), - prepare_restart_new_emulator(StartPrg, RelDir, - Release, + prepare_restart_new_emulator(StartPrg, RootDir, + RelDir, Release, PermanentRelease, Masters), {restart_new_emulator, CurrentVsn, Descr}; @@ -997,7 +1006,7 @@ do_make_services_permanent(PermanentVsn,Vsn, PermanentEVsn, EVsn) -> throw(Error4) end end. - + do_make_permanent(#state{releases = Releases, rel_dir = RelDir, unpurged = Unpurged, masters = Masters, @@ -1409,8 +1418,8 @@ prepare_restart_nt(#release{erts_vsn = EVsn, vsn = Vsn}, FutureServiceName = hd(string:tokens(atom_to_list(node()),"@")) ++ "_" ++ Vsn, CurrentService = case erlsrv:get_service(PermEVsn,CurrentServiceName) of - {error, Reason} -> - throw({error, Reason}); + {error, _} = Error1 -> + throw(Error1); CS -> CS end, @@ -1425,37 +1434,33 @@ prepare_restart_nt(#release{erts_vsn = EVsn, vsn = Vsn}, CurrentServiceName), case erlsrv:store_service(EVsn, FutureService) of - {error, Rison} -> - throw({error,Rison}); - _ -> + {error, _} = Error2 -> + throw(Error2); + _X -> erlsrv:disable_service(EVsn, FutureServiceName), ErlSrv = filename:nativename(erlsrv:erlsrv(EVsn)), - case heart:set_cmd(ErlSrv ++ " enable " ++ FutureServiceName ++ - " & " ++ ErlSrv ++ " start " ++ - FutureServiceName ++ - " & " ++ ErlSrv ++ " disable " ++ - FutureServiceName) of + StartDisabled = ErlSrv ++ " start_disabled " ++ FutureServiceName, + case heart:set_cmd(StartDisabled) of ok -> ok; - Error -> - throw({error, {'heart:set_cmd() error', Error}}) + Error3 -> + throw({error, {'heart:set_cmd() error', Error3}}) end end. - %%----------------------------------------------------------------- %% Set things up for restarting the new emulator. The actual %% restart is performed by calling init:reboot() higher up. %%----------------------------------------------------------------- -prepare_restart_new_emulator(StartPrg, RelDir, - Release, PRelease, - Masters) -> +prepare_restart_new_emulator(StartPrg, RootDir, RelDir, + Release, PRelease, Masters) -> #release{erts_vsn = EVsn, vsn = Vsn} = Release, Data = EVsn ++ " " ++ Vsn, DataFile = write_new_start_erl(Data, RelDir, Masters), %% Tell heart to use DataFile instead of start_erl.data case os:type() of {win32,nt} -> + write_ini_file(RootDir,EVsn,Masters), prepare_restart_nt(Release,PRelease,DataFile); {unix,_} -> StartP = check_start_prg(StartPrg, Masters), @@ -1832,50 +1837,10 @@ write_start(File, Data, false) -> end; write_start(File, Data, Masters) -> all_masters(Masters), - write_start_m(File, Data, Masters). + safe_write_file_m(File, Data, Masters). %%----------------------------------------------------------------- -%% Write the "start_erl.data" file at all master nodes. -%% 1. Save "start_erl.backup" at all nodes. -%% 2. Write the "start_erl.change" file at all nodes. -%% 3. Move "start_erl.change" to "start_erl.data". -%% 4. Remove "start_erl.backup" at all nodes. -%% -%% If one of the steps above fails, all steps is recovered from -%% (as long as possible), except for 4 which is allowed to fail. -%%----------------------------------------------------------------- -write_start_m(File, Data, Masters) -> - Dir = filename:dirname(File), - Backup = filename:join(Dir, "start_erl.backup"), - Change = filename:join(Dir, "start_erl.change"), - case at_all_masters(Masters, ?MODULE, do_copy_files, - [File, [Backup]]) of - ok -> - case at_all_masters(Masters, ?MODULE, do_write_file, - [Change, Data]) of - ok -> - case at_all_masters(Masters, file, rename, - [Change, File]) of - ok -> - remove_files(all, [Backup, Change], Masters), - ok; - {error, {Master, R}} -> - takewhile(Master, Masters, file, rename, - [Backup, File]), - remove_files(all, [Backup, Change], Masters), - throw({error, {Master, R, move_start_erl}}) - end; - {error, {Master, R}} -> - remove_files(all, [Backup, Change], Masters), - throw({error, {Master, R, write_start_erl}}) - end; - {error, {Master, R}} -> - remove_files(Master, [Backup], Masters), - throw({error, {Master, R, backup_start_erl}}) - end. - -%%----------------------------------------------------------------- %% Copy the "start.boot" and "sys.config" from SrcDir to DestDir at all %% master nodes. %% 1. Save DestDir/"start.backup" and DestDir/"sys.backup" at all nodes. @@ -1917,3 +1882,97 @@ set_static_files(SrcDir, DestDir, Masters) -> remove_files(Master, [BackupBoot, BackupConf], Masters), throw({error, {Master, R, backup_start_config}}) end. + +%%----------------------------------------------------------------- +%% Write erl.ini +%% Writes the erl.ini file used by erl.exe when (re)starting the erlang node. +%% At first installation, this is done by Install.exe, which means that if +%% the format of this file for some reason is changed, then Install.c must +%% also be updated (and probably some other c-files which read erl.ini) +%%----------------------------------------------------------------- +write_ini_file(RootDir,EVsn,Masters) -> + BinDir = filename:join([RootDir,"erts-"++EVsn,"bin"]), + Str0 = io_lib:format("[erlang]~n" + "Bindir=~s~n" + "Progname=erl~n" + "Rootdir=~s~n", + [filename:nativename(BinDir), + filename:nativename(RootDir)]), + Str = re:replace(Str0,"\\\\","\\\\\\\\",[{return,list},global]), + IniFile = filename:join(BinDir,"erl.ini"), + do_write_ini_file(IniFile,Str,Masters). + +do_write_ini_file(File,Data,false) -> + case do_write_file(File, Data) of + ok -> ok; + Error -> throw(Error) + end; +do_write_ini_file(File,Data,Masters) -> + all_masters(Masters), + safe_write_file_m(File, Data, Masters). + + +%%----------------------------------------------------------------- +%% Write the given file at all master nodes. +%% 1. Save <File>.backup at all nodes. +%% 2. Write <File>.change at all nodes. +%% 3. Move <File>.change to <File> +%% 4. Remove <File>.backup at all nodes. +%% +%% If one of the steps above fails, all steps are recovered from +%% (as long as possible), except for 4 which is allowed to fail. +%%----------------------------------------------------------------- +safe_write_file_m(File, Data, Masters) -> + Backup = File ++ ".backup", + Change = File ++ ".change", + case at_all_masters(Masters, ?MODULE, do_copy_files, + [File, [Backup]]) of + ok -> + case at_all_masters(Masters, ?MODULE, do_write_file, + [Change, Data]) of + ok -> + case at_all_masters(Masters, file, rename, + [Change, File]) of + ok -> + remove_files(all, [Backup, Change], Masters), + ok; + {error, {Master, R}} -> + takewhile(Master, Masters, file, rename, + [Backup, File]), + remove_files(all, [Backup, Change], Masters), + throw({error, {Master, R, rename, + filename:basename(Change), + filename:basename(File)}}) + end; + {error, {Master, R}} -> + remove_files(all, [Backup, Change], Masters), + throw({error, {Master, R, write, filename:basename(Change)}}) + end; + {error, {Master, R}} -> + remove_files(Master, [Backup], Masters), + throw({error, {Master, R, backup, + filename:basename(File), + filename:basename(Backup)}}) + end. + +%%----------------------------------------------------------------- +%% Figure out which applications that have changed version between the +%% two releases. The paths for these applications must always be +%% updated, even if the relup script does not load any modules. See +%% OTP-9402. +%% +%% A different situation is when the same application version is used +%% in old and new release, but the path has changed. This is not +%% handled here - instead it must be explicitely indicated by the +%% 'update_paths' option to release_handler:install_release/2 if the +%% code path shall be updated then. +%% ----------------------------------------------------------------- +get_new_libs([{App,Vsn,LibDir}|CurrentLibs], NewLibs) -> + case lists:keyfind(App,1,NewLibs) of + {App,NewVsn,_} = LibInfo when NewVsn =/= Vsn -> + [LibInfo | get_new_libs(CurrentLibs,NewLibs)]; + _ -> + get_new_libs(CurrentLibs,NewLibs) + end; +get_new_libs([],_) -> + []. diff --git a/lib/sasl/src/release_handler_1.erl b/lib/sasl/src/release_handler_1.erl index 8d050fb7b0..ff62f847ac 100644 --- a/lib/sasl/src/release_handler_1.erl +++ b/lib/sasl/src/release_handler_1.erl @@ -19,7 +19,7 @@ -module(release_handler_1). %% External exports --export([eval_script/3, eval_script/4, check_script/2]). +-export([eval_script/1, eval_script/5, check_script/2]). -export([get_current_vsn/1]). %% exported because used in a test case -record(eval_state, {bins = [], stopped = [], suspended = [], apps = [], @@ -33,11 +33,11 @@ %% libdirs = [{Lib, LibVsn, LibDir}] - Maps Lib to Vsn and Directory %% unpurged = [{Mod, soft_purge | brutal_purge}] %% vsns = [{Mod, OldVsn, NewVsn}] - remember the old vsn of a mod -%% before it is removed/a new vsn is loaded; the new vsn +%% before a new vsn is loaded; the new vsn %% is kept in case of a downgrade, where the code_change %% function receives the vsn of the module to downgrade %% *to*. -%% newlibs = [{Lib, Dir}] - list of all new libs; used to change +%% newlibs = [{Lib, LibVsn, LibDir}] - list of all new libs; used to change %% the code path %% opts = [{Tag, Value}] - list of options %%----------------------------------------------------------------- @@ -63,10 +63,11 @@ check_script(Script, LibDirs) -> {error, {old_processes, Mod}} end. -eval_script(Script, Apps, LibDirs) -> - eval_script(Script, Apps, LibDirs, []). +%% eval_script/1 - For testing only - no apps added, just testing instructions +eval_script(Script) -> + eval_script(Script, [], [], [], []). -eval_script(Script, Apps, LibDirs, Opts) -> +eval_script(Script, Apps, LibDirs, NewLibs, Opts) -> case catch check_old_processes(Script) of ok -> {Before, After} = split_instructions(Script), @@ -75,6 +76,7 @@ eval_script(Script, Apps, LibDirs, Opts) -> end, #eval_state{apps = Apps, libdirs = LibDirs, + newlibs = NewLibs, opts = Opts}, Before) of EvalState2 when is_record(EvalState2, eval_state) -> @@ -214,16 +216,15 @@ check_old_code(Mod) -> %%----------------------------------------------------------------- eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) -> case lists:keysearch(Lib, 1, EvalState#eval_state.libdirs) of - {value, {Lib, LibVsn, LibDir}} -> - Ebin = filename:join(LibDir, "ebin"), + {value, {Lib, LibVsn, LibDir} = LibInfo} -> Ext = code:objfile_extension(), {NewBins, NewVsns} = lists:foldl(fun(Mod, {Bins, Vsns}) -> File = lists:concat([Mod, Ext]), - FName = filename:join(Ebin, File), + FName = filename:join([LibDir, "ebin", File]), case erl_prim_loader:get_file(FName) of {ok, Bin, FName2} -> - NVsns = add_new_vsn(Mod, Bin, Vsns), + NVsns = add_vsns(Mod, Bin, Vsns), {[{Mod, Bin, FName2} | Bins],NVsns}; error -> throw({error, {no_such_file,FName}}) @@ -232,7 +233,7 @@ eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) -> {EvalState#eval_state.bins, EvalState#eval_state.vsns}, Modules), - NewLibs = [{Lib, Ebin} | EvalState#eval_state.newlibs], + NewLibs = lists:keystore(Lib,1,EvalState#eval_state.newlibs,LibInfo), EvalState#eval_state{bins = NewBins, newlibs = NewLibs, vsns = NewVsns}; @@ -242,15 +243,14 @@ eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) -> eval(point_of_no_return, EvalState) -> Libs = case get_opt(update_paths, EvalState, false) of false -> - EvalState#eval_state.newlibs; % [{Lib, Path}] + EvalState#eval_state.newlibs; true -> - lists:map(fun({Lib, _LibVsn, LibDir}) -> - Ebin= filename:join(LibDir,"ebin"), - {Lib, Ebin} - end, - EvalState#eval_state.libdirs) + EvalState#eval_state.libdirs end, - lists:foreach(fun({Lib, Path}) -> code:replace_path(Lib, Path) end, + lists:foreach(fun({Lib, _LibVsn, LibDir}) -> + Ebin = filename:join(LibDir,"ebin"), + code:replace_path(Lib, Ebin) + end, Libs), EvalState; eval({load, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) -> @@ -258,32 +258,21 @@ eval({load, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) -> {value, {_Mod, Bin, File}} = lists:keysearch(Mod, 1, Bins), % load_binary kills all procs running old code % if soft_purge, we know that there are no such procs now - Vsns = EvalState#eval_state.vsns, - NewVsns = add_old_vsn(Mod, Vsns), code:load_binary(Mod, File, Bin), % Now, the prev current is old. There might be procs % running it. Find them. Unpurged = do_soft_purge(Mod,PostPurgeMethod,EvalState#eval_state.unpurged), EvalState#eval_state{bins = lists:keydelete(Mod, 1, Bins), - unpurged = Unpurged, - vsns = NewVsns}; + unpurged = Unpurged}; eval({remove, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) -> - % purge kills all procs running old code - % if soft_purge, we know that there are no such procs now - Vsns = EvalState#eval_state.vsns, - NewVsns = add_old_vsn(Mod, Vsns), + %% purge kills all procs running old code + %% if soft_purge, we know that there are no such procs now code:purge(Mod), code:delete(Mod), - % Now, the prev current is old. There might be procs - % running it. Find them. - Unpurged = - case code:soft_purge(Mod) of - true -> EvalState#eval_state.unpurged; - false -> [{Mod, PostPurgeMethod} | EvalState#eval_state.unpurged] - end, -%% Bins = EvalState#eval_state.bins, -%% EvalState#eval_state{bins = lists:keydelete(Mod, 1, Bins), - EvalState#eval_state{unpurged = Unpurged, vsns = NewVsns}; + %% Now, the prev current is old. There might be procs + %% running it. Find them. + Unpurged = do_soft_purge(Mod,PostPurgeMethod,EvalState#eval_state.unpurged), + EvalState#eval_state{unpurged = Unpurged}; eval({purge, Modules}, EvalState) -> % Now, if there are any processes still executing old code, OR % if some new processes started after suspend but before load, @@ -606,26 +595,20 @@ sync_nodes(Id, Nodes) -> end, NNodes). -add_old_vsn(Mod, Vsns) -> +add_vsns(Mod, NewBin, Vsns) -> + OldVsn = get_current_vsn(Mod), + NewVsn = get_vsn(NewBin), case lists:keysearch(Mod, 1, Vsns) of - {value, {Mod, undefined, NewVsn}} -> - OldVsn = get_current_vsn(Mod), - lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn}); - {value, {Mod, _OldVsn, _NewVsn}} -> - Vsns; + {value, {Mod, OldVsn0, NewVsn0}} -> + lists:keyreplace(Mod, 1, Vsns, {Mod, + replace_undefined(OldVsn0,OldVsn), + replace_undefined(NewVsn0,NewVsn)}); false -> - OldVsn = get_current_vsn(Mod), - [{Mod, OldVsn, undefined} | Vsns] + [{Mod, OldVsn, NewVsn} | Vsns] end. -add_new_vsn(Mod, Bin, Vsns) -> - NewVsn = get_vsn(Bin), - case lists:keysearch(Mod, 1, Vsns) of - {value, {Mod, OldVsn, undefined}} -> - lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn}); - false -> - [{Mod, undefined, NewVsn} | Vsns] - end. +replace_undefined(undefined,Vsn) -> Vsn; +replace_undefined(Vsn,_) -> Vsn. %%----------------------------------------------------------------- %% Func: get_current_vsn/1 @@ -645,7 +628,9 @@ get_current_vsn(Mod) -> {ok, Bin, _File2} -> get_vsn(Bin); error -> - throw({error, {no_such_file, File}}) + %% This is the case when a new module is added, there will + %% be no current version of it at the time of this call. + undefined end. %%----------------------------------------------------------------- diff --git a/lib/sasl/src/systools_lib.erl b/lib/sasl/src/systools_lib.erl index b652c109fe..f951647b79 100644 --- a/lib/sasl/src/systools_lib.erl +++ b/lib/sasl/src/systools_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -176,21 +176,26 @@ add_dirs(RegName, Dirs, Root) -> regexp_match(RegName, D0, Root) -> case file:list_dir(D0) of {ok, Files} when length(Files) > 0 -> - FR = fun(F) -> - case regexp:match(F, RegName) of - {match,1,N} when N == length(F) -> - DirF = join(D0, F, Root), - case dir_p(DirF) of - true -> - {true, DirF}; + case re:compile(RegName) of + {ok, MP} -> + FR = fun(F) -> + case re:run(F, MP) of + {match,[{0,N}]} when N == length(F) -> + DirF = join(D0, F, Root), + case dir_p(DirF) of + true -> + {true, DirF}; + _ -> + false + end; _ -> false - end; - _ -> - false - end - end, - {true,lists:zf(FR, Files)}; + end + end, + {true,lists:zf(FR, Files)}; + _ -> + false + end; _ -> false end. |