diff options
author | Siri Hansen <[email protected]> | 2017-07-07 12:02:31 +0200 |
---|---|---|
committer | Siri Hansen <[email protected]> | 2017-07-07 12:02:31 +0200 |
commit | cc4de75dcb833610e52c7422b743c43556064a0a (patch) | |
tree | 5a81a1ef04482ebce0304dace8e5cd060dd1f920 /lib | |
parent | 60b478cb8398e834668b664e361aa9b6b461e708 (diff) | |
parent | 0633801a94a7ad5559ef59edc6ba25a5484d1e33 (diff) | |
download | otp-cc4de75dcb833610e52c7422b743c43556064a0a.tar.gz otp-cc4de75dcb833610e52c7422b743c43556064a0a.tar.bz2 otp-cc4de75dcb833610e52c7422b743c43556064a0a.zip |
Merge branch 'siri/unicode-in-release-handling/OTP-14463' into maint
* siri/unicode-in-release-handling/OTP-14463:
[sasl] Improve handling of unicode strings and atoms
[reltool] Improve handling of unicode strings and atoms
Diffstat (limited to 'lib')
21 files changed, 394 insertions, 81 deletions
diff --git a/lib/reltool/src/reltool.erl b/lib/reltool/src/reltool.erl index f6ce5578bc..feb6925044 100644 --- a/lib/reltool/src/reltool.erl +++ b/lib/reltool/src/reltool.erl @@ -80,7 +80,7 @@ get_server(WinPid) -> {ok, _ServerPid} = OK -> OK; {error, Reason} -> - {error, lists:flatten(io_lib:format("~p", [Reason]))} + {error, lists:flatten(io_lib:format("~tp", [Reason]))} end. %% Stop a server or window process @@ -93,7 +93,7 @@ stop(Pid) when is_pid(Pid) -> {'DOWN', Ref, _, _, shutdown} -> ok; {'DOWN', Ref, _, _, Reason} -> - {error, lists:flatten(io_lib:format("~p", [Reason]))} + {error, lists:flatten(io_lib:format("~tp", [Reason]))} end. %% Internal library function diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index 8b4898570b..9c8aae6b7e 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -119,7 +119,7 @@ | {archive, base_file(), [archive_opt()], [target_spec()]} | {copy_file, base_file()} | {copy_file, base_file(), top_file()} - | {write_file, base_file(), iolist()} + | {write_file, base_file(), binary()} | {strip_beam_file, base_file()}. -type target_dir() :: dir(). -type incl_defaults() :: boolean(). diff --git a/lib/reltool/src/reltool_app_win.erl b/lib/reltool/src/reltool_app_win.erl index 468ba297bb..663144861f 100644 --- a/lib/reltool/src/reltool_app_win.erl +++ b/lib/reltool/src/reltool_app_win.erl @@ -174,7 +174,7 @@ loop(#state{xref_pid = Xref, common = C, app = App} = S) -> S#state.mod_wins)}, ?MODULE:loop(S2); Msg -> - error_logger:format("~w~w got unexpected message:\n\t~p\n", + error_logger:format("~w~w got unexpected message:\n\t~tp\n", [?MODULE, self(), Msg]), ?MODULE:loop(S) end. @@ -182,7 +182,7 @@ loop(#state{xref_pid = Xref, common = C, app = App} = S) -> exit_warning({'EXIT', _Pid, shutdown}) -> ok; exit_warning({'EXIT', _Pid, _Reason} = Msg) -> - error_logger:format("~w~w got unexpected message:\n\t~p\n", + error_logger:format("~w~w got unexpected message:\n\t~tp\n", [?MODULE, self(), Msg]). create_window(#state{app = App} = S) -> @@ -629,7 +629,7 @@ handle_event(#state{sys = Sys, app = App} = S, Wx) -> handle_mod_button(S, Items, Action); _ -> error_logger:format("~w~w got unexpected app event from " - "wx:\n\t~p\n", + "wx:\n\t~tp\n", [?MODULE, self(), Wx]), S end. @@ -676,7 +676,7 @@ move_mod(App, {_ItemNo, ModStr}, Action) -> undefined; _ -> error_logger:format("~w~w got unexpected mod " - "button event: ~w\n\t ~p\n", + "button event: ~w\n\t ~tp\n", [?MODULE, self(), ModName, Action]), M#mod.incl_cond end, diff --git a/lib/reltool/src/reltool_fgraph_win.erl b/lib/reltool/src/reltool_fgraph_win.erl index 915330794c..a10a2281db 100644 --- a/lib/reltool/src/reltool_fgraph_win.erl +++ b/lib/reltool/src/reltool_fgraph_win.erl @@ -526,7 +526,7 @@ loop(S, G) -> exit(Reason); Other -> - error_logger:format("~w~w got unexpected message:\n\t~p\n", + error_logger:format("~w~w got unexpected message:\n\t~tp\n", [?MODULE, self(), Other]), loop(S, G) end. diff --git a/lib/reltool/src/reltool_mod_win.erl b/lib/reltool/src/reltool_mod_win.erl index 8cd63bdda1..2d56d74563 100644 --- a/lib/reltool/src/reltool_mod_win.erl +++ b/lib/reltool/src/reltool_mod_win.erl @@ -171,7 +171,7 @@ loop(#state{xref_pid = Xref, common = C, mod = Mod} = S) -> S2 = handle_event(S, Wx), ?MODULE:loop(S2); _ -> - error_logger:format("~w~w got unexpected message:\n\t~p\n", + error_logger:format("~w~w got unexpected message:\n\t~tp\n", [?MODULE, self(), Msg]), ?MODULE:loop(S) end @@ -487,7 +487,7 @@ handle_event(#state{xref_pid = Xref} = S, Wx) -> S; _ -> error_logger:format("~w~w got unexpected mod event from " - "wx:\n\t~p\n", + "wx:\n\t~tp\n", [?MODULE, self(), Wx]), S end. @@ -667,7 +667,7 @@ goto_function(S, Editor) -> wxStyledTextCtrl:setSelection(Editor, Left2, Right2), Text = wxStyledTextCtrl:getSelectedText(Editor), S2 = add_pos_to_history(S, CurrentPos), - do_goto_function(S2, string:tokens(Text, ":")); + do_goto_function(S2, string:lexemes(Text, ":")); _ -> %% No function call wxStyledTextCtrl:hideSelection(Editor, false), diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 89e90670cf..853191c696 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -225,12 +225,12 @@ parse_options([{Key, Val} | KeyVals], S, C, Sys) -> Sys2 = read_config(Sys, {sys, Val}), parse_options(KeyVals, S, C, Sys2); _ -> - reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) + reltool_utils:throw_error("Illegal option: ~tp", [{Key, Val}]) end; parse_options([], S, C, Sys) -> S#state{common = C, sys = Sys}; parse_options(KeyVals, _S, _C, _Sys) -> - reltool_utils:throw_error("Illegal option: ~p", [KeyVals]). + reltool_utils:throw_error("Illegal option: ~tp", [KeyVals]). loop(#state{sys = Sys} = S) -> receive @@ -400,12 +400,12 @@ loop(#state{sys = Sys} = S) -> {'EXIT', Pid, Reason} when Pid =:= S#state.parent_pid -> exit(Reason); {call, ReplyTo, Ref, Msg} when is_pid(ReplyTo), is_reference(Ref) -> - error_logger:format("~w~w got unexpected call:\n\t~p\n", + error_logger:format("~w~w got unexpected call:\n\t~tp\n", [?MODULE, self(), Msg]), reltool_utils:reply(ReplyTo, Ref, {error, {invalid_call, Msg}}), ?MODULE:loop(S); Msg -> - error_logger:format("~w~w got unexpected message:\n\t~p\n", + error_logger:format("~w~w got unexpected message:\n\t~tp\n", [?MODULE, self(), Msg]), ?MODULE:loop(S) end. @@ -1232,7 +1232,7 @@ parse_app_info(File, [{Key, Val} | KeyVals], AI, Status) -> Status); _ -> Status2 = - reltool_utils:add_warning("Unexpected item ~p in app file ~tp.", + reltool_utils:add_warning("Unexpected item ~tp in app file ~tp.", [Key,File], Status), parse_app_info(File, KeyVals, AI, Status2) @@ -1417,9 +1417,12 @@ shrink_app(A) -> do_save_config(S, Filename, InclDef, InclDeriv) -> {ok, Config} = do_get_config(S, InclDef, InclDeriv), - IoList = io_lib:format("%% config generated at ~w ~w\n~p.\n\n", - [date(), time(), Config]), - file:write_file(Filename, IoList). + IoList = io_lib:format("%% ~s\n" + "%% config generated at ~w ~w\n" + "~tp.\n\n", + [epp:encoding_to_string(utf8),date(), time(), Config]), + Bin = unicode:characters_to_binary(IoList), + file:write_file(Filename, Bin). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1455,7 +1458,7 @@ read_config(OldSys, {sys, KeyVals}) -> [NewSys2#sys.boot_rel]) end; read_config(_OldSys, BadConfig) -> - reltool_utils:throw_error("Illegal content: ~p", [BadConfig]). + reltool_utils:throw_error("Illegal content: ~tp", [BadConfig]). decode(#sys{apps = Apps} = Sys, [{erts = Name, AppKeyVals} | SysKeyVals]) when is_atom(Name), is_list(AppKeyVals) -> @@ -1565,7 +1568,7 @@ decode(#sys{} = Sys, [{Key, Val} | KeyVals]) -> debug_info when Val =:= keep; Val =:= strip -> Sys#sys{debug_info = Val}; _ -> - reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) + reltool_utils:throw_error("Illegal option: ~tp", [{Key, Val}]) end, decode(Sys3, KeyVals); decode(#app{} = App, [{Key, Val} | KeyVals]) -> @@ -1620,14 +1623,14 @@ decode(#app{} = App, [{Key, Val} | KeyVals]) -> active_dir = Dir, sorted_dirs = [Dir]}; false -> - reltool_utils:throw_error("Illegal lib dir for ~w: ~p", + reltool_utils:throw_error("Illegal lib dir for ~w: ~tp", [App#app.name, Val]) end; SelectVsn when SelectVsn=:=vsn; SelectVsn=:=lib_dir -> reltool_utils:throw_error("Mutual exclusive options " "'vsn' and 'lib_dir'",[]); _ -> - reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) + reltool_utils:throw_error("Illegal option: ~tp", [{Key, Val}]) end, decode(App2, KeyVals); decode(#app{mods = Mods} = App, [{mod, Name, ModKeyVals} | AppKeyVals]) -> @@ -1641,7 +1644,7 @@ decode(#mod{} = Mod, [{Key, Val} | KeyVals]) -> debug_info when Val =:= keep; Val =:= strip -> Mod#mod{debug_info = Val}; _ -> - reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) + reltool_utils:throw_error("Illegal option: ~tp", [{Key, Val}]) end, decode(Mod2, KeyVals); decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals]) -> @@ -1666,12 +1669,12 @@ decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals]) -> true -> decode(Rel#rel{rel_apps = RelApps ++ [RA]}, KeyVals); false -> - reltool_utils:throw_error("Illegal option: ~p", [RelApp]) + reltool_utils:throw_error("Illegal option: ~tp", [RelApp]) end; decode(Acc, []) -> Acc; decode(_Acc, KeyVal) -> - reltool_utils:throw_error("Illegal option: ~p", [KeyVal]). + reltool_utils:throw_error("Illegal option: ~tp", [KeyVal]). is_type(Type) -> case Type of @@ -1866,7 +1869,7 @@ escripts_to_apps([Escript | Escripts], Apps, Status) -> {ok, AF} -> AF; {error, Reason1} -> - reltool_utils:throw_error("Illegal escript ~tp: ~p", + reltool_utils:throw_error("Illegal escript ~tp: ~tp", [Escript,Reason1]) end, @@ -1950,7 +1953,7 @@ escripts_to_apps([Escript | Escripts], Apps, Status) -> Status2), escripts_to_apps(Escripts, Apps2, Status3); {error, Reason2} -> - reltool_utils:throw_error("Illegal escript ~tp: ~p", + reltool_utils:throw_error("Illegal escript ~tp: ~tp", [Escript,Reason2]) end; escripts_to_apps([], Apps, Status) -> @@ -2013,7 +2016,7 @@ init_escript_app(AppName, EscriptAppName, Dir, Info, Mods, Apps, Status) -> case lists:keymember(AppName, #app.name, Apps) of true -> reltool_utils:throw_error( - "~w: Application name clash. Escript ~tp contains application ~tp.", + "~w: Application name clash. Escript ~tp contains application ~w.", [AppName,Dir,AppName]); false -> {App2, Status} diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index ba0d90ef5f..92df270752 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -136,7 +136,7 @@ init(Options) -> do_init(Options) catch error:Reason -> - io:format("~p: ~p~n",[Reason, erlang:get_stacktrace()]), + io:format("~tp: ~tp~n",[Reason, erlang:get_stacktrace()]), exit({Reason, erlang:get_stacktrace()}) end. @@ -182,7 +182,7 @@ do_init([{safe_config, Safe}, {parent, Parent} | Options]) -> end. restart_server_safe_config(true,Parent,Reason) -> - io:format("~w(~w): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]), + io:format("~w(~w): <ERROR> ~tp\n", [?MODULE, ?LINE, Reason]), proc_lib:init_ack(Parent, {error,Reason}); restart_server_safe_config(false,Parent,Reason) -> wx:new(), @@ -199,7 +199,7 @@ restart_server_safe_config(false,Parent,Reason) -> ?wxID_OK -> do_init([{safe_config,true},{parent,Parent},?safe_config]); ?wxID_CANCEL -> - io:format("~w(~w): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]), + io:format("~w(~w): <ERROR> ~tp\n", [?MODULE, ?LINE, Reason]), proc_lib:init_ack(Parent,{error,Reason}) end. @@ -251,7 +251,7 @@ loop(S) -> ?MODULE:loop(S#state{warning_wins = WWs2}); false -> error_logger:format("~w~w got unexpected " - "message:\n\t~p\n", + "message:\n\t~tp\n", [?MODULE, self(), Msg]), ?MODULE:loop(S) end @@ -292,7 +292,7 @@ loop(S) -> S#state.app_wins), ?MODULE:loop(S#state{fgraph_wins = FWs, app_wins = AWs}); Msg -> - error_logger:format("~w~w got unexpected message:\n\t~p\n", + error_logger:format("~w~w got unexpected message:\n\t~tp\n", [?MODULE, self(), Msg]), ?MODULE:loop(S) end. @@ -316,7 +316,7 @@ handle_child_exit({'EXIT', Pid, _Reason} = Exit, FWs, AWs) -> msg_warning({'EXIT', _Pid, shutdown}, Type) when Type =/= unknown -> ok; msg_warning(Exit, Type) -> - error_logger:format("~w~w got unexpected message (~w):\n\t~p\n", + error_logger:format("~w~w got unexpected message (~w):\n\t~tp\n", [?MODULE, self(), Type, Exit]). create_window(S) -> @@ -1163,12 +1163,12 @@ handle_system_event(#state{sys = Sys} = S, do_set_sys(S#state{sys = Sys2}); handle_system_event(S, Event, ObjRef, UserData) -> error_logger:format("~w~w got unexpected wx sys event to ~p " - "with user data: ~p\n\t ~p\n", + "with user data: ~tp\n\t ~tp\n", [?MODULE, self(), ObjRef, UserData, Event]), S. handle_release_event(S, _Event, _ObjRef, UserData) -> - io:format("Release data: ~p\n", [UserData]), + io:format("Release data: ~tp\n", [UserData]), S. handle_source_event(S, @@ -1225,7 +1225,7 @@ handle_app_event(S, handle_app_button(S, Items, Action); handle_app_event(S, Event, ObjRef, UserData) -> error_logger:format("~w~w got unexpected wx app event to " - "~p with user data: ~p\n\t ~p\n", + "~p with user data: ~tp\n\t ~tp\n", [?MODULE, self(), ObjRef, UserData, Event]), S. @@ -1269,7 +1269,7 @@ move_app(S, {_ItemNo, AppBase}, Action) -> undefined; _ -> error_logger:format("~w~w got unexpected app " - "button event: ~p ~p\n", + "button event: ~tp ~tp\n", [?MODULE, self(), Action, AppBase]), OldApp#app.incl_cond end, @@ -1543,7 +1543,7 @@ check_and_refresh(S, Status) -> display_message(Reason, ?wxICON_ERROR), false; {error, Reason} -> - Msg = lists:flatten(io_lib:format("Error:\n\n~p\n", [Reason])), + Msg = lists:flatten(io_lib:format("Error:\n\n~tp\n", [Reason])), display_message(Msg, ?wxICON_ERROR), false end, diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl index 1615a3e9b7..1b1461178e 100644 --- a/lib/reltool/src/reltool_target.erl +++ b/lib/reltool/src/reltool_target.erl @@ -787,16 +787,20 @@ do_spec_rel_files(#rel{name = RelName} = Rel, Sys) -> {ok, BootBin} = gen_boot(Script), Date = date(), Time = time(), - RelIoList = io_lib:format("%% rel generated at ~w ~w\n~p.\n\n", + RelIoList = io_lib:format("%% rel generated at ~w ~w\n~tp.\n\n", [Date, Time, GenRel]), - ScriptIoList = io_lib:format("%% script generated at ~w ~w\n~p.\n\n", + ScriptIoList = io_lib:format("%% script generated at ~w ~w\n~tp.\n\n", [Date, Time, Script]), [ - {write_file, RelFile, RelIoList}, - {write_file, ScriptFile, ScriptIoList}, + {write_file, RelFile, to_utf8_bin_with_enc_comment(RelIoList)}, + {write_file, ScriptFile, to_utf8_bin_with_enc_comment(ScriptIoList)}, {write_file, BootFile, BootBin} ]. +to_utf8_bin_with_enc_comment(IoList) when is_list(IoList) -> + unicode:characters_to_binary("%% " ++ epp:encoding_to_string(utf8) ++ "\n" + ++ IoList). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate a complete target system %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1001,7 +1005,8 @@ spec_start_file(#sys{boot_rel = BootRelName, {value, Erts} = lists:keysearch(erts, #app.name, Apps), {value, BootRel} = lists:keysearch(BootRelName, #rel.name, Rels), Data = Erts#app.vsn ++ " " ++ BootRel#rel.vsn ++ "\n", - {BootRel#rel.vsn, {write_file, "start_erl.data", Data}}. + {BootRel#rel.vsn, {write_file, "start_erl.data", + unicode:characters_to_binary(Data)}}. lookup_spec(Prefix, Specs) -> lists:filter(fun(S) -> lists:prefix(Prefix, element(2, S)) end, Specs). @@ -1183,18 +1188,18 @@ spec_app_file(#app{name = Name, Info#app_info.modules)], App2 = App#app{info = Info#app_info{modules = ModNames}}, Contents = gen_app(App2), - AppIoList = io_lib:format("%% app generated at ~w ~w\n~p.\n\n", + AppIoList = io_lib:format("%% app generated at ~w ~w\n~tp.\n\n", [date(), time(), Contents]), - [{write_file, AppFilename, AppIoList}]; + [{write_file, AppFilename, to_utf8_bin_with_enc_comment(AppIoList)}]; all -> %% Include all included modules %% Generate new file ModNames = [M#mod.name || M <- Mods, M#mod.is_included], App2 = App#app{info = Info#app_info{modules = ModNames}}, Contents = gen_app(App2), - AppIoList = io_lib:format("%% app generated at ~w ~w\n~p.\n\n", + AppIoList = io_lib:format("%% app generated at ~w ~w\n~tp.\n\n", [date(), time(), Contents]), - [{write_file, AppFilename, AppIoList}] + [{write_file, AppFilename, to_utf8_bin_with_enc_comment(AppIoList)}] end. @@ -1285,7 +1290,7 @@ do_eval_spec({archive, Archive, Options, Files}, {ok, _} -> ok; {error, Reason} -> - reltool_utils:throw_error("create archive ~ts failed: ~p", + reltool_utils:throw_error("create archive ~ts failed: ~tp", [ArchiveFile, Reason]) end; do_eval_spec({copy_file, File}, _OrigSourceDir, SourceDir, TargetDir) -> @@ -1299,12 +1304,12 @@ do_eval_spec({copy_file, File, OldFile}, SourceFile = filename:join([OrigSourceDir, OldFile]), TargetFile = filename:join([TargetDir, File]), reltool_utils:copy_file(SourceFile, TargetFile); -do_eval_spec({write_file, File, IoList}, +do_eval_spec({write_file, File, Bin}, _OrigSourceDir, _SourceDir, TargetDir) -> TargetFile = filename:join([TargetDir, File]), - reltool_utils:write_file(TargetFile, IoList); + reltool_utils:write_file(TargetFile, Bin); do_eval_spec({strip_beam, File}, _OrigSourceDir, SourceDir, TargetDir) -> SourceFile = filename:join([SourceDir, File]), TargetFile = filename:join([TargetDir, File]), @@ -1336,7 +1341,7 @@ cleanup_spec({copy_file, File}, TargetDir) -> cleanup_spec({copy_file, NewFile, _OldFile}, TargetDir) -> TargetFile = filename:join([TargetDir, NewFile]), file:delete(TargetFile); -cleanup_spec({write_file, File, _IoList}, TargetDir) -> +cleanup_spec({write_file, File, _}, TargetDir) -> TargetFile = filename:join([TargetDir, File]), file:delete(TargetFile); cleanup_spec({strip_beam, File}, TargetDir) -> @@ -1406,7 +1411,7 @@ do_filter_spec(Path, ExclRegexps) -> Path2 = opt_join(Path, NewFile), match(Path2, InclRegexps, ExclRegexps); -do_filter_spec(Path, {write_file, File, _IoList}, InclRegexps, ExclRegexps) -> +do_filter_spec(Path, {write_file, File, _}, InclRegexps, ExclRegexps) -> Path2 = opt_join(Path, File), match(Path2, InclRegexps, ExclRegexps); do_filter_spec(Path, {strip_beam, File}, InclRegexps, ExclRegexps) -> @@ -1448,7 +1453,7 @@ do_install(RelName, TargetDir) -> RelDir = filename:join([TargetDir2, "releases"]), DataFile = filename:join([RelDir, "start_erl.data"]), Bin = reltool_utils:read_file(DataFile), - case string:tokens(binary_to_list(Bin), " \n") of + case string:lexemes(unicode:characters_to_list(Bin), " \n") of [ErlVsn, RelVsn | _] -> ErtsBinDir = filename:join([TargetDir2, "erts-" ++ ErlVsn, "bin"]), BinDir = filename:join([TargetDir2, "bin"]), @@ -1501,8 +1506,8 @@ subst_src_script(Script, SrcDir, DestDir, Vars, Opts) -> subst_file(Src, Dest, Vars, Opts) -> Bin = reltool_utils:read_file(Src), - Chars = subst(binary_to_list(Bin), Vars), - reltool_utils:write_file(Dest, Chars), + Chars = subst(unicode:characters_to_list(Bin), Vars), + reltool_utils:write_file(Dest, unicode:characters_to_binary(Chars)), case lists:member(preserve, Opts) of true -> FileInfo = reltool_utils:read_file_info(Src), diff --git a/lib/reltool/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl index 60edc9f3ca..3891b5ae4d 100644 --- a/lib/reltool/src/reltool_utils.erl +++ b/lib/reltool/src/reltool_utils.erl @@ -55,7 +55,7 @@ root_dir() -> code:root_dir(). erl_libs() -> - string:tokens(os:getenv("ERL_LIBS", ""), ":;"). + string:lexemes(os:getenv("ERL_LIBS", ""), ":;"). lib_dirs(Dir) -> case erl_prim_loader:list_dir(Dir) of @@ -286,7 +286,7 @@ split_app_dir(Dir) -> {Name, Vsn} = split_app_name(Base), Vsn2 = try - [list_to_integer(N) || N <- string:tokens(Vsn, ".")] + [list_to_integer(N) || N <- string:lexemes(Vsn, ".")] catch _:_ -> Vsn @@ -427,7 +427,7 @@ scroll_size(ObjRef) -> safe_keysearch(Key, Pos, List, Mod, Line) -> case lists:keysearch(Key, Pos, List) of false -> - io:format("~w(~w): lists:keysearch(~p, ~w, ~p) -> false\n", + io:format("~w(~w): lists:keysearch(~tp, ~w, ~tp) -> false\n", [Mod, Line, Key, Pos, List]), erlang:error({Mod, Line, lists, keysearch, [Key, Pos, List]}); {value, Val} -> @@ -498,8 +498,8 @@ read_file(File) -> throw_error("read file ~ts: ~ts", [File, Text]) end. -write_file(File, IoList) -> - case file:write_file(File, IoList) of +write_file(File, Bin) -> + case file:write_file(File, Bin) of ok -> ok; {error, Reason} -> @@ -601,7 +601,7 @@ do_decode_regexps(Key, [Regexp | Regexps], Acc) -> Regexps, [#regexp{source = Regexp, compiled = MP} | Acc]); _ -> - Text = lists:flatten(io_lib:format("~p", [{Key, Regexp}])), + Text = lists:flatten(io_lib:format("~tp", [{Key, Regexp}])), throw({error, "Illegal option: " ++ Text}) end; do_decode_regexps(_Key, [], Acc) -> diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl index 1f3c6877d5..d0a7c7332d 100644 --- a/lib/sasl/src/release_handler.erl +++ b/lib/sasl/src/release_handler.erl @@ -1143,8 +1143,9 @@ new_emulator_make_hybrid_config(CurrentVsn,ToVsn,TmpVsn,RelDir,Masters) -> Config2 = replace_config(stdlib,Config1,Stdlib), Config3 = replace_config(sasl,Config2,Sasl), - ConfigStr = io_lib:format("~p.~n",[Config3]), - write_file(TmpFile,ConfigStr,Masters). + ConfigStr = io_lib:format("%% ~s~n~tp.~n", + [epp:encoding_to_string(utf8),Config3]), + write_file(TmpFile,unicode:characters_to_binary(ConfigStr),Masters). %% Take the configuration for application App from the new config and %% insert in the old config. @@ -1874,9 +1875,10 @@ write_releases_1(Dir, NewReleases, Masters) -> write_releases_m(Dir, NewReleases, Masters). do_write_release(Dir, RELEASES, NewReleases) -> - case file:open(filename:join(Dir, RELEASES), [write]) of + case file:open(filename:join(Dir, RELEASES), [write,{encoding,utf8}]) of {ok, Fd} -> - ok = io:format(Fd, "~p.~n", [NewReleases]), + ok = io:format(Fd, "%% ~s~n~tp.~n", + [epp:encoding_to_string(utf8),NewReleases]), ok = file:close(Fd); {error, Reason} -> {error, Reason} diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl index b1523dcbb7..391b1fb5cc 100644 --- a/lib/sasl/src/systools_make.erl +++ b/lib/sasl/src/systools_make.erl @@ -1152,10 +1152,10 @@ generate_script(Output, Release, Appls, Flags) -> }, ScriptFile = Output ++ ".script", - case file:open(ScriptFile, [write]) of + case file:open(ScriptFile, [write,{encoding,utf8}]) of {ok, Fd} -> - io:format(Fd, "%% script generated at ~w ~w\n~p.\n", - [date(), time(), Script]), + io:format(Fd, "%% ~s\n%% script generated at ~w ~w\n~tp.\n", + [epp:encoding_to_string(utf8), date(), time(), Script]), case file:close(Fd) of ok -> BootFile = Output ++ ".boot", diff --git a/lib/sasl/src/systools_relup.erl b/lib/sasl/src/systools_relup.erl index 706ae7d631..e836d57670 100644 --- a/lib/sasl/src/systools_relup.erl +++ b/lib/sasl/src/systools_relup.erl @@ -535,9 +535,9 @@ to_list(X) when is_list(X) -> X. write_relup_file(Relup, Opts) -> Filename = filename:join(filename:absname(get_opt(outdir,Opts)), "relup"), - case file:open(Filename, [write]) of + case file:open(Filename, [write,{encoding,utf8}]) of {ok, Fd} -> - io:format(Fd, "~p.~n", [Relup]), + io:format(Fd, "%% ~s~n~tp.~n", [epp:encoding_to_string(utf8),Relup]), case file:close(Fd) of ok -> ok; {error,Reason} -> diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl index 7093158502..50932e89e4 100644 --- a/lib/sasl/test/release_handler_SUITE.erl +++ b/lib/sasl/test/release_handler_SUITE.erl @@ -66,7 +66,7 @@ cases() -> supervisor_which_children_timeout, release_handler_which_releases, install_release_syntax_check, upgrade_supervisor, upgrade_supervisor_fail, otp_9864, - otp_10463_upgrade_script_regexp, no_dot_erlang]. + otp_10463_upgrade_script_regexp, no_dot_erlang, unicode_upgrade]. groups() -> [{release,[], @@ -1875,6 +1875,86 @@ no_dot_erlang(Conf) -> ok end. +%%%----------------------------------------------------------------- +%%% Test unicode handling. Make sure that release name, application +%%% description, and application environment variables may contain +%%% unicode characters. +unicode_upgrade(Conf) -> + %% Set some paths + DataDir = ?config(data_dir, Conf), + PrivDir = priv_dir(Conf), + Dir = filename:join(PrivDir,"unicode"), + LibDir0 = filename:join(DataDir, "unicode"), + LibDir = + case {file:native_name_encoding(),os:type()} of + {utf8,{Os,_}} when Os =/= win32 -> + LD = filename:join(DataDir,"unicode_αβ"), + file:make_symlink("unicode",LD), + LD; + _ -> + LibDir0 + end, + + %% Create the releases + RelName = "unicode_rel_αβ", + Rel1 = create_and_install_fake_first_release(Dir,{RelName,"1"}, + [{u,"1.0",LibDir}]), + Rel2 = create_fake_upgrade_release(Dir, + {RelName,"2"}, + [{u,"1.1",LibDir}], + {[Rel1],[Rel1],[LibDir]}), + Rel1Dir = filename:dirname(Rel1), + Rel2Dir = filename:dirname(Rel2), + + %% Start a slave node + {ok, Node} = t_start_node(unicode_upgrade, Rel1, + filename:join(Rel1Dir,"sys.config"), "+pc unicode"), + + %% Check + Dir1 = filename:join([LibDir, "u-1.0"]), + Dir1 = rpc:call(Node, code, lib_dir, [u]), + UBeam1 = filename:join([Dir1,"ebin","u.beam"]), + UBeam1 = rpc:call(Node,code,which,[u]), + {RelName,"1"} = rpc:call(Node,init,script_id,[]), + {Env,state} = rpc:call(Node,u,u,[]), + 'val_αβ' = proplists:get_value('key_αβ',Env), + [{RelName,"1",_,permanent}|_] = + rpc:call(Node,release_handler,which_releases,[]), + {ok,ReleasesDir} = rpc:call(Node,application,get_env,[sasl,releases_dir]), + {ok,[[{release,RelName,"1",_,_,permanent}|_]]} = + file:consult(filename:join(ReleasesDir,"RELEASES")), + + %% Install second release + {ok, RelVsn2} = + rpc:call(Node, release_handler, set_unpacked, + [Rel2++".rel", [{u,"1.1",LibDir}]]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "relup")]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "sys.config")]), + + {ok, _RelVsn1, []} = + rpc:call(Node, release_handler, install_release, [RelVsn2]), + + %% And check + Dir2 = filename:join([LibDir, "u-1.1"]), + Dir2 = rpc:call(Node, code, lib_dir, [u]), + UBeam2 = filename:join([Dir2,"ebin","u.beam"]), + {file,UBeam2} = rpc:call(Node,code,is_loaded,[u]), + {RelName,"1"} = rpc:call(Node,init,script_id,[]), + {Env,{state,'αβ'}} = rpc:call(Node,u,u,[]), + [{RelName,"2",_,current}|_] = + rpc:call(Node,release_handler,which_releases,[]), + {ok,ReleasesDir2} = rpc:call(Node,application,get_env,[sasl,releases_dir]), + {ok,<<"%% coding: utf-8\n[{release,\"unicode_rel_αβ\",\"2\""/utf8,_/binary>>}= + file:read_file(filename:join(ReleasesDir2,"RELEASES")), + ok. + +unicode_upgrade(cleanup,_Conf) -> + stop_node(node_name(unicode_upgrade)). + %%%================================================================= %%% Misceleaneous functions @@ -2002,6 +2082,8 @@ are_names_reg_gg(Node, Names, N) -> t_start_node(Name, Boot, SysConfig) -> + t_start_node(Name, Boot, SysConfig, ""). +t_start_node(Name, Boot, SysConfig, ArgStr) -> Args = case Boot of [] -> []; @@ -2010,8 +2092,9 @@ t_start_node(Name, Boot, SysConfig) -> case SysConfig of [] -> []; _ -> " -config " ++ SysConfig - end, - test_server:start_node(Name, slave, [{args, Args}]). + end ++ + " " ++ ArgStr, + test_server:start_node(Name, peer, [{args, Args}]). stop_node(Node) -> ?t:stop_node(Node). @@ -2460,7 +2543,9 @@ create_rel_file(RelFile,RelName,RelVsn,Erts,ExtraApps) -> %% Insert a term in a file, which can be read with file:consult/1. write_term_file(File,Term) -> - ok = file:write_file(File,io_lib:format("~p.~n",[Term])). + Str = io_lib:format("%% ~s~n~tp.~n",[epp:encoding_to_string(utf8),Term]), + Bin = unicode:characters_to_binary(Str), + ok = file:write_file(File,Bin). %% Check that global group info is correct - try again for a maximum of 5 sec @@ -2719,8 +2804,8 @@ cover_fun(Node,Func) -> %% and possibly other applications if they are listed in AppDirs = %% [{App,Vsn,LibDir}] create_and_install_fake_first_release(Dir,AppDirs) -> - %% Create the first release - {RelName,RelVsn} = init:script_id(), + create_and_install_fake_first_release(Dir,init:script_id(),AppDirs). +create_and_install_fake_first_release(Dir,{RelName,RelVsn},AppDirs) -> {Rel,_} = create_fake_release(Dir,RelName,RelVsn,AppDirs), ReleasesDir = filename:join(Dir, "releases"), RelDir = filename:dirname(Rel), @@ -2744,9 +2829,11 @@ create_and_install_fake_first_release(Dir,AppDirs) -> %% be upgraded to from the release created by %% create_and_install_fake_first_release/2. Unpack first by calls to %% release_handler:set_unpacked and release_handler:install_file. -create_fake_upgrade_release(Dir,RelVsn,AppDirs,{UpFrom,DownTo,ExtraLibs}) -> - %% Create a new release +create_fake_upgrade_release(Dir,RelVsn,AppDirs,UpgrInstr) when not is_tuple(RelVsn) -> {RelName,_} = init:script_id(), + create_fake_upgrade_release(Dir,{RelName,RelVsn},AppDirs,UpgrInstr); +create_fake_upgrade_release(Dir,{RelName,RelVsn},AppDirs,{UpFrom,DownTo,ExtraLibs}) -> + %% Create a new release {Rel,Paths} = create_fake_release(Dir,RelName,RelVsn,AppDirs), RelDir = filename:dirname(Rel), diff --git a/lib/sasl/test/release_handler_SUITE_data/Makefile.src b/lib/sasl/test/release_handler_SUITE_data/Makefile.src index b794aa0e6f..113d3e2290 100644 --- a/lib/sasl/test/release_handler_SUITE_data/Makefile.src +++ b/lib/sasl/test/release_handler_SUITE_data/Makefile.src @@ -76,7 +76,13 @@ SUP= \ release_handler_timeouts/dummy-0.1/ebin/dummy_sup.@EMULATOR@ \ release_handler_timeouts/dummy-0.1/ebin/dummy_sup_2.@EMULATOR@ -all: $(LIB) $(APP) $(OTP2740) $(C) $(SUP) +UNICODE= \ + unicode/u-1.0/ebin/u.@EMULATOR@ \ + unicode/u-1.0/ebin/u_sup.@EMULATOR@ \ + unicode/u-1.1/ebin/u.@EMULATOR@ \ + unicode/u-1.1/ebin/u_sup.@EMULATOR@ + +all: $(LIB) $(APP) $(OTP2740) $(C) $(SUP) $(UNICODE) lib/a-1.0/ebin/a.@EMULATOR@: lib/a-1.0/src/a.erl erlc $(EFLAGS) -olib/a-1.0/ebin lib/a-1.0/src/a.erl @@ -236,3 +242,13 @@ release_handler_timeouts/dummy-0.1/ebin/dummy_sup.@EMULATOR@: release_handler_ti erlc $(EFLAGS) -orelease_handler_timeouts/dummy-0.1/ebin release_handler_timeouts/dummy-0.1/src/dummy_sup.erl release_handler_timeouts/dummy-0.1/ebin/dummy_sup_2.@EMULATOR@: release_handler_timeouts/dummy-0.1/src/dummy_sup_2.erl erlc $(EFLAGS) -orelease_handler_timeouts/dummy-0.1/ebin release_handler_timeouts/dummy-0.1/src/dummy_sup_2.erl + +unicode/u-1.0/ebin/u.@EMULATOR@: unicode/u-1.0/src/u.erl + erlc $(EFLAGS) -ounicode/u-1.0/ebin unicode/u-1.0/src/u.erl +unicode/u-1.0/ebin/u_sup.@EMULATOR@: unicode/u-1.0/src/u_sup.erl + erlc $(EFLAGS) -ounicode/u-1.0/ebin unicode/u-1.0/src/u_sup.erl + +unicode/u-1.1/ebin/u.@EMULATOR@: unicode/u-1.1/src/u.erl + erlc $(EFLAGS) -ounicode/u-1.1/ebin unicode/u-1.1/src/u.erl +unicode/u-1.1/ebin/u_sup.@EMULATOR@: unicode/u-1.1/src/u_sup.erl + erlc $(EFLAGS) -ounicode/u-1.1/ebin unicode/u-1.1/src/u_sup.erl diff --git a/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.0/ebin/u.app b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.0/ebin/u.app new file mode 100644 index 0000000000..fea4f9992e --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.0/ebin/u.app @@ -0,0 +1,8 @@ +{application, u, + [{description, "This app shall test unicode handling αβ"}, + {vsn, "1.0"}, + {modules, [u, u_sup]}, + {registered, [u_sup]}, + {applications, [kernel, stdlib]}, + {env, [{'key_αβ', 'val_αβ'}]}, + {mod, {u_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.0/src/u.erl b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.0/src/u.erl new file mode 100644 index 0000000000..45fe098c0e --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.0/src/u.erl @@ -0,0 +1,50 @@ +%% ``Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(u). + + +-behaviour(gen_server). + +-vsn(1). + +%% External exports +-export([start_link/0, u/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, uu}, u, [], []). + +u() -> gen_server:call(uu, u). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(u, _From, State) -> + X = application:get_all_env(u), + {reply, {X,State}, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.0/src/u_sup.erl b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.0/src/u_sup.erl new file mode 100644 index 0000000000..b0d4a7b58f --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.0/src/u_sup.erl @@ -0,0 +1,38 @@ +%% ``Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(u_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {u, + {u, start_link, []}, + permanent, 2000, worker, [u]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.1/ebin/u.app b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.1/ebin/u.app new file mode 100644 index 0000000000..8fcc3bba42 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.1/ebin/u.app @@ -0,0 +1,8 @@ +{application, u, + [{description, "This app shall test unicode handling αβ"}, + {vsn, "1.1"}, + {modules, [u, u_sup]}, + {registered, [u_sup]}, + {applications, [kernel, stdlib]}, + {env, [{'key_αβ', 'val_αβ'}]}, + {mod, {u_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.1/ebin/u.appup b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.1/ebin/u.appup new file mode 100644 index 0000000000..0344ce92ab --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.1/ebin/u.appup @@ -0,0 +1,3 @@ +{"1.1", + [{"1.0",[{update,u,{advanced,'αβ'}}]}], + [{"1.0",[{update,u,{advanced,'αβ'}}]}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.1/src/u.erl b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.1/src/u.erl new file mode 100644 index 0000000000..d2544d6fc1 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.1/src/u.erl @@ -0,0 +1,55 @@ +%% ``Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(u). + + +-behaviour(gen_server). + +-vsn(1). + +%% External exports +-export([start_link/0, u/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]). + +start_link() -> gen_server:start_link({local, uu}, u, [], []). + +u() -> gen_server:call(uu, u). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, {state,'αβ'}}. + +handle_call(u, _From, State) -> + X = application:get_all_env(u), + {reply, {X,State}, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change({down,_}, {State,_}, _Extra) -> + {ok, State}; +code_change(_, State, Extra) -> + {ok, {State, Extra}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.1/src/u_sup.erl b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.1/src/u_sup.erl new file mode 100644 index 0000000000..b0d4a7b58f --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/unicode/u-1.1/src/u_sup.erl @@ -0,0 +1,38 @@ +%% ``Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(u_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {u, + {u, start_link, []}, + permanent, 2000, worker, [u]}, + {ok, {SupFlags, [Config]}}. |