File 9331-Rewrite-legacy-catch.patch of Package erlang

From 6b838ff6a6ddcdbfeefa1cd6e17c6cd48bb8c822 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Thu, 19 Feb 2026 16:36:30 +0100
Subject: [PATCH 1/4] Rewrite legacy `catch`

Rewrite to use `try`...`catch`...`end`.

For unexpected internal exceptions maybe details
in error logging have changed.

The misfeature of explicitly loading module code
for callback modules has been removed to not surprise
embedded mode installations.

A really old and undocumented variant of the open/prepare
callback call without the leading `Peer` argument
has been removed.
---
 lib/tftp/src/tftp.hrl        |   4 +-
 lib/tftp/src/tftp_app.erl    |   4 +-
 lib/tftp/src/tftp_binary.erl |  36 +++---
 lib/tftp/src/tftp_engine.erl | 212 ++++++++++++++++++-----------------
 lib/tftp/src/tftp_lib.erl    |  30 ++---
 5 files changed, 146 insertions(+), 140 deletions(-)

diff --git a/lib/tftp/src/tftp.hrl b/lib/tftp/src/tftp.hrl
index 3c132cc432..9c9adfae35 100644
--- a/lib/tftp/src/tftp.hrl
+++ b/lib/tftp/src/tftp.hrl
@@ -50,6 +50,8 @@
 -record(tftp_msg_error,   {code, text, details}).
 -record(tftp_msg_oack,    {options}).
 
+-record(tftp_decode_error, {reply}).
+
 -record(config, {parent_pid   = self(),
 		 udp_socket,
 		 udp_options  = [binary, {reuseaddr, true}, {active, once}],
diff --git a/lib/tftp/src/tftp_app.erl b/lib/tftp/src/tftp_app.erl
index f1d7190732..c072908d50 100644
--- a/lib/tftp/src/tftp_app.erl
+++ b/lib/tftp/src/tftp_app.erl
@@ -51,9 +51,9 @@ stop(_State) ->
 %%====================================================================
 
 get_configuration() ->
-    case (catch application:get_env(tftp, services)) of
+    case application:get_env(tftp, services) of
 	{ok, Services} ->
 	    Services;
-	_ ->
+	undefined ->
 	    []
     end.
diff --git a/lib/tftp/src/tftp_binary.erl b/lib/tftp/src/tftp_binary.erl
index 5362331a47..796b0bfcf3 100644
--- a/lib/tftp/src/tftp_binary.erl
+++ b/lib/tftp/src/tftp_binary.erl
@@ -50,8 +50,8 @@
 prepare(_Peer, Access, Filename, Mode, SuggestedOptions, Initial) when is_list(Initial) ->
     %% Client side
     IsNativeAscii = is_native_ascii(Initial),
-    case catch handle_options(Access, Filename, Mode, SuggestedOptions, IsNativeAscii) of
-	{ok, IsNetworkAscii, AcceptedOptions} when Access =:= read, is_binary(Filename) ->
+    try handle_options(Access, Filename, Mode, SuggestedOptions, IsNativeAscii) of
+	{IsNetworkAscii, AcceptedOptions} when Access =:= read, is_binary(Filename) ->
 	    State = #read_state{options  	 = AcceptedOptions,
 				blksize  	 = lookup_blksize(AcceptedOptions),
 				bin      	 = Filename,
@@ -59,16 +59,16 @@ prepare(_Peer, Access, Filename, Mode, SuggestedOptions, Initial) when is_list(I
 			        count            = byte_size(Filename),
 				is_native_ascii  = IsNativeAscii},
 	    {ok, AcceptedOptions, State};
-	{ok, IsNetworkAscii, AcceptedOptions} when Access =:= write, Filename =:= binary ->
+	{IsNetworkAscii, AcceptedOptions} when Access =:= write, Filename =:= binary ->
 	    State = #write_state{options  	  = AcceptedOptions,
 				 blksize  	  = lookup_blksize(AcceptedOptions),
 				 list     	  = [],
 				 is_network_ascii = IsNetworkAscii,
 				 is_native_ascii  = IsNativeAscii},
 	    {ok, AcceptedOptions, State};
-	{ok, _, _} ->
-	    {error, {undef, "Illegal callback usage. Mode and filename is incompatible."}};
-	{error, {Code, Text}} ->
+	{_, _} ->
+	    {error, {undef, "Illegal callback usage. Mode and filename is incompatible."}}
+    catch throw : {Code, Text} ->
 	    {error, {Code, Text}}
     end;
 prepare(_Peer, _Access, _Bin, _Mode, _SuggestedOptions, _Initial) ->
@@ -88,22 +88,22 @@ open(Peer, Access, Filename, Mode, SuggestedOptions, Initial) when is_list(Initi
     end;
 open(_Peer, Access, Filename, Mode, NegotiatedOptions, State) when is_record(State, read_state) ->
     %% Both sides
-    case catch handle_options(Access, Filename, Mode, NegotiatedOptions, State#read_state.is_native_ascii) of
-	{ok, IsNetworkAscii, Options}
+    try handle_options(Access, Filename, Mode, NegotiatedOptions, State#read_state.is_native_ascii) of
+	{IsNetworkAscii, Options}
 	when Options =:= NegotiatedOptions,
 	     IsNetworkAscii =:= State#read_state.is_network_ascii ->
-	    {ok, NegotiatedOptions, State};
-	{error, {Code, Text}} ->
+	    {ok, NegotiatedOptions, State}
+    catch throw : {Code, Text} ->
 	    {error, {Code, Text}}
     end;
 open(_Peer, Access, Filename, Mode, NegotiatedOptions, State) when is_record(State, write_state) ->
     %% Both sides
-    case catch handle_options(Access, Filename, Mode, NegotiatedOptions, State#write_state.is_native_ascii) of
-	{ok, IsNetworkAscii, Options}
+    try handle_options(Access, Filename, Mode, NegotiatedOptions, State#write_state.is_native_ascii) of
+	{IsNetworkAscii, Options}
 	when Options =:= NegotiatedOptions,
 	     IsNetworkAscii =:= State#write_state.is_network_ascii ->
-	    {ok, NegotiatedOptions, State};
-	{error, {Code, Text}} ->
+	    {ok, NegotiatedOptions, State}
+    catch throw : {Code, Text} ->
 	    {error, {Code, Text}}
     end;
 open(Peer, Access, Filename, Mode, NegotiatedOptions, State) -> 
@@ -171,7 +171,7 @@ abort(Code, Text, State) ->
 handle_options(Access, Bin, Mode, Options, IsNativeAscii) ->
     IsNetworkAscii = handle_mode(Mode, IsNativeAscii),
     Options2 = do_handle_options(Access, Bin, Options),
-    {ok, IsNetworkAscii, Options2}.
+    {IsNetworkAscii, Options2}.
 
 handle_mode("netascii", true) -> true;
 handle_mode("octet", _IsNativeAscii) -> false;
@@ -202,15 +202,15 @@ do_handle_options(_Access, _Bin, []) ->
 
 
 handle_integer(Access, Bin, Key, Val, Options, Min, Max) ->
-    case catch list_to_integer(Val) of
-	{'EXIT', _} ->
-	    do_handle_options(Access, Bin, Options);
+    try list_to_integer(Val) of
 	Int when Int >= Min, Int =< Max ->
 	    [{Key, Val} | do_handle_options(Access, Bin, Options)];
 	Int when Int >= Min, Max =:= infinity ->
 	    [{Key, Val} | do_handle_options(Access, Bin, Options)];
 	_Int ->
 	    throw({error, {badopt, "Illegal " ++ Key ++ " value " ++ Val}})
+    catch error : _ ->
+	    do_handle_options(Access, Bin, Options)
     end.
 
 lookup_blksize(Options) ->
diff --git a/lib/tftp/src/tftp_engine.erl b/lib/tftp/src/tftp_engine.erl
index c62bf992c1..e69478032e 100644
--- a/lib/tftp/src/tftp_engine.erl
+++ b/lib/tftp/src/tftp_engine.erl
@@ -153,7 +153,7 @@ daemon_init(Config) when is_record(Config, config),
                          is_pid(Config#config.parent_pid) ->
     process_flag(trap_exit, true),
     {Port, UdpOptions} = prepare_daemon_udp(Config),
-    case catch gen_udp:open(Port, UdpOptions) of
+    try gen_udp:open(Port, UdpOptions) of
         {ok, Socket} ->
             {ok, ActualPort} = inet:port(Socket),
             proc_lib:init_ack({ok, self()}),
@@ -170,8 +170,8 @@ daemon_init(Config) when is_record(Config, config),
         {error, Reason} ->
             Text = lists:flatten(io_lib:format("UDP open ~p -> ~p", [UdpOptions, Reason])),
             print_debug_info(Config, daemon, open, ?ERROR(open, undef, Text, "")),
-            exit({gen_udp_open, UdpOptions, Reason});
-        Reason ->
+            exit({gen_udp_open, UdpOptions, Reason})
+    catch error : Reason ->
             Text = lists:flatten(io_lib:format("UDP open ~p -> ~p", [UdpOptions, Reason])),
             print_debug_info(Config, daemon, open, ?ERROR(open, undef, Text, "")),
             exit({gen_udp_open, UdpOptions, Reason})
@@ -187,10 +187,10 @@ prepare_daemon_udp(#config{udp_port = Port, udp_options = UdpOptions} = Config)
             InitArg = list_to_atom("tftpd_" ++ integer_to_list(Port)),
             case init:get_argument(InitArg) of
                 {ok, [[FdStr]] = Badarg} when is_list(FdStr) ->
-                    case catch list_to_integer(FdStr) of
-                        Fd when is_integer(Fd) ->
-                            {0, [{fd, Fd} | lists:keydelete(ip, 1, UdpOptions)]};
-                        {'EXIT', _} ->
+                    try list_to_integer(FdStr) of
+                        Fd ->
+                            {0, [{fd, Fd} | lists:keydelete(ip, 1, UdpOptions)]}
+                    catch error : _ ->
                             Text = lists:flatten(io_lib:format("Illegal prebound fd ~p: ~p", [InitArg, Badarg])),
                             print_debug_info(Config, daemon, open, ?ERROR(open, undef, Text, "")),
                             exit({badarg, {prebound_fd, InitArg, Badarg}})
@@ -229,24 +229,24 @@ daemon_loop(#daemon_state{config = DaemonConfig,
             _ = reply({ok, Info}, Ref, FromPid),
             ?MODULE:daemon_loop(State);
         {{change_config, Options}, Ref, FromPid} when is_pid(FromPid) ->
-            case catch tftp_lib:parse_config(Options, DaemonConfig) of
-                {'EXIT', Reason} ->
-                    _ = reply({error, Reason}, Ref, FromPid),
-                    ?MODULE:daemon_loop(State);
+            try tftp_lib:parse_config(Options, DaemonConfig) of
                 DaemonConfig2 when is_record(DaemonConfig2, config) ->
                     _ = reply(ok, Ref, FromPid),
                     ?MODULE:daemon_loop(State#daemon_state{config = DaemonConfig2})
+            catch error : Reason ->
+                    _ = reply({error, Reason}, Ref, FromPid),
+                    ?MODULE:daemon_loop(State)
             end;
         {udp, Socket, RemoteHost, RemotePort, Bin} when is_binary(Bin) ->
             _ = inet:setopts(Socket, [{active, once}]),
             ServerConfig = DaemonConfig#config{parent_pid = self(),
                                                udp_host   = RemoteHost,
                                                udp_port   = RemotePort},
-            Msg = (catch tftp_lib:decode_msg(Bin)),
-            print_debug_info(ServerConfig, daemon, recv, Msg),
-            case Msg of
-                Req when is_record(Req, tftp_msg_req), 
-                N =< DaemonConfig#config.max_conn ->
+            DecodedMsg = tftp_lib:decode_msg(Bin),
+            print_debug_info(ServerConfig, daemon, recv, DecodedMsg),
+            case DecodedMsg of
+                #tftp_msg_req{} = Req when
+                      N =< DaemonConfig#config.max_conn ->
                     Peer = peer_info(ServerConfig),
                     PeerReq = {Peer, Req},
                     PeerInfo = lists:flatten(io_lib:format("~p", [Peer])),
@@ -263,17 +263,17 @@ daemon_loop(#daemon_state{config = DaemonConfig,
                                         [Pid, PeerInfo, Req#tftp_msg_req.filename]),
                             ?MODULE:daemon_loop(State)
                     end;
-                Req when is_record(Req, tftp_msg_req) ->
+                #tftp_msg_req{}->
                     Reply = #tftp_msg_error{code = enospc, text = "Too many connections"},
                     Peer = peer_info(ServerConfig),
                     PeerInfo = lists:flatten(io_lib:format("~p", [Peer])),
-                    warning_msg(DaemonConfig, 
+                    warning_msg(DaemonConfig,
 				"Daemon has too many connections (~p)."
 				"\n\tRejecting request from ~s\n",
 				[N, PeerInfo]),
                     send_msg(ServerConfig, daemon, Reply),
                     ?MODULE:daemon_loop(State);
-                {'EXIT', Reply} when is_record(Reply, tftp_msg_error) ->
+                #tftp_decode_error{reply = Reply} ->
                     send_msg(ServerConfig, daemon, Reply),
                     ?MODULE:daemon_loop(State);
                 Req  ->
@@ -485,14 +485,13 @@ client_prepare(Config, Callback, Req) when is_record(Req, tftp_msg_req) ->
 
 client_open(Config, Callback, Req, BlockNo, #transfer_res{status = Status, decoded_msg = DecodedMsg, prepared = Prepared}) ->
     {LocalAccess, _} = local_file_access(Req),
-    case Status of
-        ok when is_record(Prepared, prepared) ->
+    case Prepared of
+        #prepared{} when Status =:= ok ->
             case DecodedMsg of
-                Msg when is_record(Msg, tftp_msg_oack) ->
-                    ServerOptions = Msg#tftp_msg_oack.options,
+                #tftp_msg_oack{options = ServerOptions} ->
                     OptText = "Protocol violation. Server is not allowed new options",
                     case post_verify_options(Config, Req, ServerOptions, OptText) of
-                        {ok, Config2, Req2} ->              
+                        {ok, Config2, Req2} ->
                             {Config3, Callback2, Req3} =
                                 do_client_open(Config2, Callback, Req2),
                             case LocalAccess of
@@ -525,7 +524,8 @@ client_open(Config, Callback, Req, BlockNo, #transfer_res{status = Status, decod
                 #tftp_msg_error{code = Code, text = Text} ->
                     _ = callback({abort, {Code, Text}}, Config, Callback, Req),
                     terminate(Config, Req, ?ERROR(client_open, Code, Text, Req#tftp_msg_req.filename));
-                {'EXIT', #tftp_msg_error{code = Code, text = Text}} ->
+                #tftp_decode_error{
+                   reply = #tftp_msg_error{code = Code, text = Text}} ->
                     _ = callback({abort, {Code, Text}}, Config, Callback, Req),
                     terminate(Config, Req, ?ERROR(client_open, Code, Text, Req#tftp_msg_req.filename));
                 Msg when is_tuple(Msg) ->
@@ -537,8 +537,7 @@ client_open(Config, Callback, Req, BlockNo, #transfer_res{status = Status, decod
                     Text2 = lists:flatten([Text, ". ", io_lib:format("~p", [element(1, Msg)])]),
                     terminate(Config, Req, ?ERROR(client_open, Code, Text2, Req#tftp_msg_req.filename))
             end;
-        error when is_record(Prepared, tftp_msg_error) ->
-            #tftp_msg_error{code = Code, text = Text} = Prepared,
+        #tftp_msg_error{code = Code, text = Text} when Status =:= error ->
             _ = callback({abort, {Code, Text}}, Config, Callback, Req),
             terminate(Config, Req, ?ERROR(client_open, Code, Text, Req#tftp_msg_req.filename))
     end.
@@ -593,7 +592,9 @@ common_loop(Config, Callback, Req, #transfer_res{status = Status, decoded_msg =
                 #tftp_msg_error{code = Code, text = Text} ->
                     _ = callback({abort, {Code, Text}}, Config, Callback, Req),
                     terminate(Config, Req, ?ERROR(common_loop, Code, Text, Req#tftp_msg_req.filename));
-                {'EXIT', #tftp_msg_error{code = Code, text = Text} = Error} ->
+                #tftp_decode_error{
+                   reply = Error =
+                       #tftp_msg_error{code = Code, text = Text}} ->
                     _ = callback({abort, {Code, Text}}, Config, Callback, Req),
                     send_msg(Config, Req, Error),
                     terminate(Config, Req, ?ERROR(common_loop, Code, Text, Req#tftp_msg_req.filename));
@@ -789,20 +794,20 @@ open_free_port(Config, Who, Req) when is_record(Config, config), is_record(Req,
     case Config#config.port_policy of
         random ->
             %% BUGBUG: Should be a random port
-            case catch gen_udp:open(0, UdpOptions) of
+            try gen_udp:open(0, UdpOptions) of
                 {ok, Socket} ->
                     Config2 = Config#config{udp_socket = Socket},
                     print_debug_info(Config2, Who, open, Req),
                     {ok, Config2};
                 {error, Reason} ->
                     Text = lists:flatten(io_lib:format("UDP open ~p -> ~p", [[0 | UdpOptions], Reason])),
-                    ?ERROR(open, undef, Text, Req#tftp_msg_req.filename);
-                {'EXIT', _} = Reason ->
-                    Text = lists:flatten(io_lib:format("UDP open ~p -> ~p", [[0 | UdpOptions], Reason])),
+                    ?ERROR(open, undef, Text, Req#tftp_msg_req.filename)
+            catch error : Reason ->
+                    Text = lists:flatten(io_lib:format("UDP open ~p -> ~p", [[0 | UdpOptions], {'EXIT', Reason}])),
                     ?ERROR(open, undef, Text, Req#tftp_msg_req.filename)
             end;
         {range, Port, Max} when Port =< Max ->
-            case catch gen_udp:open(Port, UdpOptions) of
+            try gen_udp:open(Port, UdpOptions) of
                 {ok, Socket} ->
                     Config2 = Config#config{udp_socket = Socket},
                     print_debug_info(Config2, Who, open, Req),
@@ -813,9 +818,9 @@ open_free_port(Config, Who, Req) when is_record(Config, config), is_record(Req,
                     open_free_port(Config2, Who, Req);
                 {error, Reason} ->
                     Text = lists:flatten(io_lib:format("UDP open ~p -> ~p", [[Port | UdpOptions], Reason])),
-                    ?ERROR(open, undef, Text, Req#tftp_msg_req.filename);
-                {'EXIT', _} = Reason->
-                    Text = lists:flatten(io_lib:format("UDP open ~p -> ~p", [[Port | UdpOptions], Reason])),
+                    ?ERROR(open, undef, Text, Req#tftp_msg_req.filename)
+            catch error : Reason ->
+                    Text = lists:flatten(io_lib:format("UDP open ~p -> ~p", [[Port | UdpOptions], {'EXIT', Reason}])),
                     ?ERROR(open, undef, Text, Req#tftp_msg_req.filename)
             end;
         {range, Port, _Max} ->
@@ -880,14 +885,15 @@ do_wait_for_msg_and_handle_timeout(Config, Callback, Req, Msg, IoList, LocalAcce
     end.
 
 send_msg(Config, Req, Msg) ->
-    case catch tftp_lib:encode_msg(Msg) of
-        {'EXIT', Reason} ->
-            Code = undef,
-            Text = "Internal error. Encode failed",
-            Msg2 = #tftp_msg_error{code = Code, text = Text, details = Reason},
-            send_msg(Config, Req, Msg2);
+    try tftp_lib:encode_msg(Msg) of
         IoList ->
             do_send_msg(Config, Req, Msg, IoList)
+    catch error : Reason : Stacktrace ->
+            Code = undef,
+            Text = "Internal error. Encode failed",
+            Details = {error, Reason, Stacktrace},
+            Msg2 = #tftp_msg_error{code = Code, text = Text, details = Details},
+            send_msg(Config, Req, Msg2)
     end.
 
 do_send_msg(#config{udp_socket = Socket, udp_host = RemoteHost, udp_port = RemotePort} = Config, Req, Msg, IoList) ->
@@ -910,15 +916,16 @@ do_send_msg(#config{udp_socket = Socket, udp_host = RemoteHost, udp_port = Remot
     %%  DumpPath  ->
     %%      trace_udp_send(Req, Msg, IoList, DumpPath)
     %% end,
-    try
-        ok = gen_udp:send(Socket, RemoteHost, RemotePort, IoList)
-    catch
-        error:{badmatch,{error,einval=Reason}}:StackTrace ->
-            error_msg(Config,
-                      "Stacktrace; ~p\n gen_udp:send(~p, ~p, ~p, ~p) -> ~p\n", 
-                      [StackTrace, Socket, RemoteHost, RemotePort, IoList, {error, Reason}]);
-        error:{badmatch,{error,Reason}} ->
-            {error, Reason} 
+    case gen_udp:send(Socket, RemoteHost, RemotePort, IoList) of
+        ok -> ok;
+        {error, einval} = Error ->
+            StackTrace = try throw(ok) catch throw : ok : ST -> ST end,
+            error_msg(
+              Config,
+              "Stacktrace; ~p\n gen_udp:send(~p, ~p, ~p, ~p) -> ~p\n",
+              [StackTrace, Socket, RemoteHost, RemotePort, IoList, Error]);
+        {error, _} = Error ->
+            Error
     end.
 
 %% trace_udp_send(#tftp_msg_req{filename = [$/ | RelFile]} = Req, Msg, IoList, DumpPath) ->
@@ -947,14 +954,14 @@ wait_for_msg(Config, Callback, Req) ->
             _ = inet:setopts(Socket, [{active, once}]),
             Config2 = Config#config{udp_host = RemoteHost,
                                     udp_port = RemotePort},
-            DecodedMsg = (catch tftp_lib:decode_msg(Bin)),
+            DecodedMsg = tftp_lib:decode_msg(Bin),
             print_debug_info(Config2, Req, recv, DecodedMsg),
             {Config2, DecodedMsg};
         {udp, Socket, Host, Port, Bin} when is_binary(Bin),
                                             Config#config.udp_host =:= Host,
                                             Config#config.udp_port =:= Port ->
             _ = inet:setopts(Socket, [{active, once}]),
-            DecodedMsg = (catch tftp_lib:decode_msg(Bin)),
+            DecodedMsg = tftp_lib:decode_msg(Bin),
             print_debug_info(Config, Req, recv, DecodedMsg),
             {Config, DecodedMsg};
         {info, Ref, FromPid} when is_pid(FromPid) ->
@@ -967,13 +974,14 @@ wait_for_msg(Config, Callback, Req) ->
             _ = reply({ok, Info}, Ref, FromPid),
             wait_for_msg(Config, Callback, Req);
         {{change_config, Options}, Ref, FromPid} when is_pid(FromPid) ->
-            case catch tftp_lib:parse_config(Options, Config) of
-                {'EXIT', Reason} ->
-                    _ = reply({error, Reason}, Ref, FromPid),
-                    wait_for_msg(Config, Callback, Req);
+            try tftp_lib:parse_config(Options, Config) of
                 Config2 when is_record(Config2, config) ->
                     _ = reply(ok, Ref, FromPid),
                     wait_for_msg(Config2, Callback, Req)
+            catch exit : Reason ->
+                    _ = reply({error, Reason}, Ref, FromPid),
+                    wait_for_msg(Config, Callback, Req)
+
             end;
         {system, From, Msg} ->
             Misc = #sys_misc{module = ?MODULE, function = wait_for_msg, arguments = [Config, Callback, Req]},
@@ -1026,7 +1034,7 @@ do_callback(read = Fun, Config, Callback, Req)
        is_record(Req, tftp_msg_req) ->
     Args =  [Callback#callback.state],
     NextBlockNo = Callback#callback.block_no + 1,
-    case catch safe_apply(Callback#callback.module, Fun, Args) of
+    case callback_apply(Callback#callback.module, Fun, Args) of
         {more, Bin, NewState} when is_binary(Bin) ->
             Count = Callback#callback.count + byte_size(Bin),
             Callback2 = Callback#callback{state    = NewState, 
@@ -1060,7 +1068,7 @@ do_callback({write = Fun, Bin}, Config, Callback, Req)
        is_binary(Bin) ->
     Args =  [Bin, Callback#callback.state],
     NextBlockNo = Callback#callback.block_no + 1,
-    case catch safe_apply(Callback#callback.module, Fun, Args) of
+    case callback_apply(Callback#callback.module, Fun, Args) of
         {more, NewState} ->
             Count = Callback#callback.count + byte_size(Bin),
             Callback2 = Callback#callback{state    = NewState, 
@@ -1096,19 +1104,13 @@ do_callback({open, Type}, Config, Callback, Req)
             server_open    -> {open, 0}
         end,
     Mod = Callback#callback.module,
-    Args = [Access,
+    Args = [peer_info(Config),
+            Access,
             Filename,
             Req#tftp_msg_req.mode,
             Req#tftp_msg_req.options,
             Callback#callback.state],
-    PeerInfo = peer_info(Config),
-    _ = fast_ensure_loaded(Mod),
-    Args2 =
-        case erlang:function_exported(Mod, Fun, length(Args)) of
-            true  -> Args;
-            false -> [PeerInfo | Args]
-        end,
-    case catch safe_apply(Mod, Fun, Args2) of
+    case callback_apply(Mod, Fun, Args) of
         {ok, AcceptedOptions, NewState} ->
             Callback2 = Callback#callback{state    = NewState, 
                                           block_no = BlockNo, 
@@ -1132,11 +1134,18 @@ do_callback({abort = Fun, #tftp_msg_error{code = Code, text = Text} = Error}, Co
        is_record(Callback, callback), 
        is_record(Req, tftp_msg_req) ->
     Args =  [Code, Text, Callback#callback.state],
-    catch safe_apply(Callback#callback.module, Fun, Args),
+    _ = callback_apply(Callback#callback.module, Fun, Args),
     {undefined, Error};
 do_callback({abort, Error}, _Config, undefined, _Req) when is_record(Error, tftp_msg_error) ->
     {undefined, Error}.
 
+callback_apply(Mod, Fun, Args) ->
+    try apply(Mod, Fun, Args) of Result -> Result
+    catch throw : Result                -> Result;
+          exit  : Reason                -> {'EXIT', Reason};
+          error : Reason : Stacktrace   -> {'EXIT', {Reason, Stacktrace}}
+    end.
+
 peer_info(#config{udp_host = Host, udp_port = Port}) ->
     if
         tuple_size(Host) =:= 4 ->
@@ -1158,15 +1167,17 @@ match_callback(Filename, Callbacks) ->
     end.
 
 do_match_callback(Filename, [C | Tail]) when is_record(C, callback) ->
-    case catch re:run(Filename, C#callback.internal, [{capture, none}]) of
+    try re:run(Filename, C#callback.internal, [{capture, none}]) of
         match ->
             {ok, C};
         nomatch ->
-            do_match_callback(Filename, Tail);
-        Details ->
+            do_match_callback(Filename, Tail)
+    catch error : Reason : Stacktrace ->
+            Details = {error, Reason, Stacktrace},
             Code = baduser,
             Text = "Internal error. File handler not found",
-            {error, #tftp_msg_error{code = Code, text = Text, details = Details}}
+            {error,
+             #tftp_msg_error{code = Code, text = Text, details = Details}}
     end;
 do_match_callback(Filename, []) ->
     Code = baduser,
@@ -1229,7 +1240,7 @@ local_file_access(#tftp_msg_req{access = Access,
 
 pre_verify_options(Config, Req) ->
     Options = Req#tftp_msg_req.options,
-    case catch verify_reject(Config, Req, Options) of
+    case verify_reject(Config, Req, Options) of
         ok ->
             case verify_integer("tsize", 0, Config#config.max_tsize, Options) of
                 true ->
@@ -1267,9 +1278,16 @@ verify_reject(Config, Req, Options) ->
         true ->
             {error, {eacces, atom_to_list(Access) ++ " mode not allowed"}};
         false ->
-            [throw({error, {badopt, Key ++ " not allowed"}}) ||
-                {Key, _} <- Options, lists:member(Key, Rejected)],
-            ok
+            verify_reject(Options, Rejected)
+    end.
+
+verify_reject([], _Rejected) -> ok;
+verify_reject([{Key, _} | Options], Rejected) ->
+    case lists:member(Key, Rejected) of
+        true  ->
+            {error, {badopt, Key ++ " not allowed"}};
+        false ->
+            verify_reject(Options, Rejected)
     end.
 
 lookup_timeout(Options) ->
@@ -1291,9 +1309,7 @@ lookup_mode(Options) ->
 verify_integer(Key, Min, Max, Options) ->
     case lists:keysearch(Key, 1, Options) of
         {value, {_, Val}} when is_list(Val) ->
-            case catch list_to_integer(Val) of
-                {'EXIT', _} ->
-                    false;
+            try list_to_integer(Val) of
                 Int when Int >= Min, is_integer(Min),
                          Max =:= infinity ->
                     true;
@@ -1302,38 +1318,27 @@ verify_integer(Key, Min, Max, Options) ->
                     true;
                 _ ->
                     false
+            catch error : _ ->
+                    false
             end;
         false ->
             true
     end.
 
 error_msg(#config{logger = Logger, debug_level = _Level}, F, A) ->
-    safe_apply(Logger, error_msg, [F, A]).
+    apply(Logger, error_msg, [F, A]).
 
 warning_msg(#config{logger = Logger, debug_level = Level}, F, A) ->
     case Level of
         none  -> ok;
         error -> ok;
-        _     -> safe_apply(Logger, warning_msg, [F, A])
+        _     -> apply(Logger, warning_msg, [F, A])
     end.
 
 info_msg(#config{logger = Logger}, F, A) ->
-    safe_apply(Logger, info_msg, [F, A]).
+    apply(Logger, info_msg, [F, A]).
 
-safe_apply(Mod, Fun, Args) ->
-    _ = fast_ensure_loaded(Mod),
-    apply(Mod, Fun, Args).
 
-fast_ensure_loaded(Mod) ->
-    case erlang:function_exported(Mod, module_info, 0) of
-        true  ->
-            ok;
-        false ->
-            Res = code:load_file(Mod),
-            %% io:format("tftp: code:load_file(~p) -> ~p\n", [Mod, Res]), %% XXX
-            Res
-    end.
-    
 print_debug_info(#config{debug_level = Level} = Config, Who, Where, Data) ->
     if
         Level =:= none ->
@@ -1370,11 +1375,10 @@ do_print_debug_info(Config, Who, Where, #tftp_msg_req{local_filename = Filename}
     do_print_debug_info(Config, Who, Where, Msg2);
 do_print_debug_info(Config, Who, Where, Data) ->
     Local = 
-        case catch inet:port(Config#config.udp_socket) of
-            {'EXIT', _Reason} ->
-                0;
-            {ok, Port} ->
-                Port
+        try inet:port(Config#config.udp_socket) of
+            {ok, Port}  -> Port;
+            {error, _ } -> 0
+        catch error : _ -> 0
         end,
     %% Remote = Config#config.udp_port,
     PeerInfo = lists:flatten(io_lib:format("~p", [peer_info(Config)])),
diff --git a/lib/tftp/src/tftp_lib.erl b/lib/tftp/src/tftp_lib.erl
index fb8f0ce921..9398a0de39 100644
--- a/lib/tftp/src/tftp_lib.erl
+++ b/lib/tftp/src/tftp_lib.erl
@@ -305,6 +305,12 @@ code_character(N) ->
 %%-------------------------------------------------------------------
 
 decode_msg(Bin) when is_binary(Bin) ->
+    try do_decode_msg(Bin)
+    catch throw : Text ->
+            #tftp_decode_error{reply = #tftp_msg_error{code = undef, text = Text}}
+    end.
+
+do_decode_msg(Bin) ->
     case Bin of
         <<?TFTP_OPCODE_RRQ:16/integer, Tail/binary>> ->
             case decode_strings(Tail, [keep_case, lower_case]) of
@@ -315,11 +321,9 @@ decode_msg(Bin) when is_binary(Bin) ->
                                   mode = to_lower(Mode),
                                   options = Options};
                 [_Filename | _Strings] ->
-                    exit(#tftp_msg_error{code = undef,
-                                         text = "Missing mode"});
+                    throw("Missing mode");
                 _ ->
-                    exit(#tftp_msg_error{code = undef,
-                                         text = "Missing filename"})
+                    throw("Missing filename")
             end;
         <<?TFTP_OPCODE_WRQ:16/integer, Tail/binary>> ->
             case decode_strings(Tail, [keep_case, lower_case]) of
@@ -330,11 +334,9 @@ decode_msg(Bin) when is_binary(Bin) ->
                                   mode = to_lower(Mode),
                                   options = Options};
                 [_Filename | _Strings] ->
-                    exit(#tftp_msg_error{code = undef,
-                                         text = "Missing mode"});
+                    throw("Missing mode");
                 _ ->
-                    exit(#tftp_msg_error{code = undef,
-                                         text = "Missing filename"})
+                    throw("Missing filename")
             end;
         <<?TFTP_OPCODE_DATA:16/integer, SeqNo:16/integer, Data/binary>> ->
             #tftp_msg_data{block_no = SeqNo, data = Data};
@@ -347,16 +349,14 @@ decode_msg(Bin) when is_binary(Bin) ->
                     #tftp_msg_error{code = ErrorCode2,
                                     text = ErrorText};
                 _ ->
-                    exit(#tftp_msg_error{code = undef,
-                                         text = "Trailing garbage"})
+                    throw("Trailing garbage")
             end;
         <<?TFTP_OPCODE_OACK:16/integer, Tail/binary>> ->
             Strings = decode_strings(Tail, [lower_case]),
             Options = decode_options(Strings),
             #tftp_msg_oack{options = Options};
         _ ->
-            exit(#tftp_msg_error{code = undef,
-                                 text = "Invalid syntax"})
+            throw("Invalid syntax")
     end.
 
 decode_strings(Bin, Cases) when is_binary(Bin), is_list(Cases) ->
@@ -384,7 +384,7 @@ decode_string(<<Char:8/integer, Tail/binary>>, Case, String) ->
             decode_string(Tail, Case, [Char2 | String])
     end;
 decode_string(<<>>, _Case, _String) ->
-    exit(#tftp_msg_error{code = undef, text = "Trailing null missing"}).
+    throw("Trailing null missing").
 
 decode_options([Key, Value | Strings]) ->
     [{to_lower(Key), Value} | decode_options(Strings)];
@@ -403,7 +403,7 @@ decode_error_code(Int) ->
         ?TFTP_ERROR_BADUSER -> baduser;
         ?TFTP_ERROR_BADOPT  -> badopt;
         Int when is_integer(Int), Int >= 0, Int =< 65535 -> Int;
-        _ -> exit(#tftp_msg_error{code = undef, text = "Error code outside range."})
+        _ -> throw("Error code outside range.")
     end.
 
 %%-------------------------------------------------------------------
-- 
2.51.0

openSUSE Build Service is sponsored by