diff options
93 files changed, 1995 insertions, 2466 deletions
diff --git a/.gitignore b/.gitignore index cba7caa074..aa32bd9582 100644 --- a/.gitignore +++ b/.gitignore @@ -182,6 +182,8 @@ a.out.dSYM/ /lib/jinterface/priv/OtpErlang.jar /lib/jinterface/priv/com/ /lib/jinterface/doc/html/java +/lib/jinterface/pom.xml +/lib/jinterface/target # kernel diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 77a628e82b..54a0a80536 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -2767,8 +2767,10 @@ os_prompt%</pre> Otherwise, some other point in time is chosen. It is also guaranteed that subsequent calls to this BIF returns continuously increasing values. Hence, the return value from - <c>now()</c> can be used to generate unique time-stamps. It - can only be used to check the local time of day if + <c>now()</c> can be used to generate unique time-stamps, + and if it is called in a tight loop on a fast machine + the time of the node can become skewed.</p> + <p>It can only be used to check the local time of day if the time-zone info of the underlying operating system is properly configured.</p> </desc> diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 058c1662e8..6174917807 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -187,6 +187,17 @@ <p> Own Id: OTP-8701</p> </item> + <item> + <p> + A user defined CPU topology set via a call to <seealso + marker="erlang#system_flag_cpu_topology">erlang:system_flag(cpu_topology, + CPUTopology)</seealso> was not properly verified, and + could in worst case cause an emulator crash. The emulator + crash could only occur when a user defined CPU topology + already existed and was redefined.</p> + <p> + Own Id: OTP-8710</p> + </item> </list> </section> @@ -509,6 +520,41 @@ </section> +<section><title>Erts 5.7.5.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Driver threads, such as async threads, using <seealso + marker="erl_driver#ErlDrvPDL">port data locks</seealso> + peeked at the port status field without proper locking + when looking up the driver queue.</p> + <p> + Own Id: OTP-8475</p> + </item> + <item> + <p> + A call to the BIF <c>unregister(RegName)</c> when a port + had the name <c>RegName</c> registered in the runtime + system without SMP support caused a runtime system crash. + (Thanks to Per Hedeland for the bugfix and test case.)</p> + <p> + Own Id: OTP-8487</p> + </item> + <item> + <p> + Fix memory management bug causing crash of non-SMP + emulator with async threads enabled. The bug did first + appear in R13B03.</p> + <p> + Own Id: OTP-8591 Aux Id: seq11554 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 5.7.5</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index bae17d1569..41031f5468 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -3787,11 +3787,11 @@ verify_topology(erts_cpu_topology_t *cpudata, int size) /* Verify logical ids */ logical = erts_alloc(ERTS_ALC_T_TMP, sizeof(int)*size); - for (i = 0; i < user_cpudata_size; i++) - logical[i] = user_cpudata[i].logical; + for (i = 0; i < size; i++) + logical[i] = cpudata[i].logical; - qsort(logical, user_cpudata_size, sizeof(int), int_cmp); - for (i = 0; i < user_cpudata_size-1; i++) { + qsort(logical, size, sizeof(int), int_cmp); + for (i = 0; i < size-1; i++) { if (logical[i] == logical[i+1]) { erts_free(ERTS_ALC_T_TMP, logical); return ERTS_INIT_CPU_TOPOLOGY_NOT_UNIQUE_LIDS; @@ -3804,13 +3804,13 @@ verify_topology(erts_cpu_topology_t *cpudata, int size) /* Verify unique entities */ - for (i = 1; i < user_cpudata_size; i++) { - if (user_cpudata[i-1].processor == user_cpudata[i].processor - && user_cpudata[i-1].node == user_cpudata[i].node - && (user_cpudata[i-1].processor_node - == user_cpudata[i].processor_node) - && user_cpudata[i-1].core == user_cpudata[i].core - && user_cpudata[i-1].thread == user_cpudata[i].thread) { + for (i = 1; i < size; i++) { + if (cpudata[i-1].processor == cpudata[i].processor + && cpudata[i-1].node == cpudata[i].node + && (cpudata[i-1].processor_node + == cpudata[i].processor_node) + && cpudata[i-1].core == cpudata[i].core + && cpudata[i-1].thread == cpudata[i].thread) { return ERTS_INIT_CPU_TOPOLOGY_NOT_UNIQUE_ENTITIES; } } diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c index 8c8029d450..5bcd567b5f 100644 --- a/erts/emulator/beam/packet_parser.c +++ b/erts/emulator/beam/packet_parser.c @@ -679,7 +679,7 @@ int packet_parse_http(const char* buf, int len, int* statep, while (n && SP(ptr)) { ptr++; n--; } - if (ptr==p0) return -1; + if (ptr==p0 && n>0) return -1; /* NOTE: the syntax allows empty reason phrases */ (*statep) = !0; diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl index 6cde286871..d9e961be2f 100644 --- a/erts/emulator/test/decode_packet_SUITE.erl +++ b/erts/emulator/test/decode_packet_SUITE.erl @@ -304,6 +304,10 @@ http(Config) when is_list(Config) -> {ok, {http_request, 'GET', ResB, {1,1}}, Rest} = decode_pkt(http_bin,Bin) end, lists:foreach(UriF, http_uri_variants()), + + %% Response with empty phrase + ?line {ok,{http_response,{1,1},200,[]},<<>>} = decode_pkt(http, <<"HTTP/1.1 200\r\n">>, []), + ?line {ok,{http_response,{1,1},200,<<>>},<<>>} = decode_pkt(http_bin, <<"HTTP/1.1 200\r\n">>, []), ok. http_with_bin(http) -> diff --git a/erts/vsn.mk b/erts/vsn.mk index 6e1338cff5..ce6330e6dd 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,8 +17,8 @@ # %CopyrightEnd% # -VSN = 5.8 -SYSTEM_VSN = R14A +VSN = 5.8.1 +SYSTEM_VSN = R14B # Port number 4365 in 4.2 # Port number 4366 in 4.3 diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl index 4ffe8bc606..9164259a94 100644 --- a/lib/compiler/src/beam_dict.erl +++ b/lib/compiler/src/beam_dict.erl @@ -33,7 +33,7 @@ exports = [] :: [{label(), arity(), label()}], locals = [] :: [{label(), arity(), label()}], imports = gb_trees:empty() :: gb_tree(), %{{M,F,A},Index} - strings = [] :: [string()], %String pool + strings = [] :: string(), %String pool lambdas = [], %[{...}] literals = dict:new() :: dict(), %Format: {Literal,Number} next_atom = 1 :: pos_integer(), @@ -219,7 +219,7 @@ my_term_to_binary(Term) -> %% Search for string Str in the string pool Pool. %% old_string(Str, Pool) -> none | Index --spec old_string(string(), [string()]) -> 'none' | pos_integer(). +-spec old_string(string(), string()) -> 'none' | pos_integer(). old_string([C|Str]=Str0, [C|Pool]) -> case lists:prefix(Str, Pool) of diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 4642fb68b3..ed7a9144a8 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -41,8 +41,7 @@ -type option() :: atom() | {atom(), term()} | {'d', atom(), term()}. --type line() :: integer(). --type err_info() :: {line(), module(), term()}. %% ErrorDescriptor +-type err_info() :: {erl_scan:line(), module(), term()}. %% ErrorDescriptor -type errors() :: [{file:filename(), [err_info()]}]. -type warnings() :: [{file:filename(), [err_info()]}]. -type mod_ret() :: {'ok', module()} @@ -70,7 +69,7 @@ file(File) -> file(File, ?DEFAULT_OPTIONS). --spec file(module() | file:filename(), [option()]) -> comp_ret(). +-spec file(module() | file:filename(), [option()] | option()) -> comp_ret(). file(File, Opts) when is_list(Opts) -> do_compile({file,File}, Opts++env_default_opts()); @@ -88,6 +87,8 @@ forms(Forms, Opt) when is_atom(Opt) -> %% would have generated a Beam file, false otherwise (if only a binary or a %% listing file would have been generated). +-spec output_generated([option()]) -> boolean(). + output_generated(Opts) -> noenv_output_generated(Opts++env_default_opts()). @@ -96,6 +97,8 @@ output_generated(Opts) -> %% for default options. %% +-spec noenv_file(module() | file:filename(), [option()] | option()) -> comp_ret(). + noenv_file(File, Opts) when is_list(Opts) -> do_compile({file,File}, Opts); noenv_file(File, Opt) -> @@ -106,6 +109,8 @@ noenv_forms(Forms, Opts) when is_list(Opts) -> noenv_forms(Forms, Opt) when is_atom(Opt) -> noenv_forms(Forms, [Opt|?DEFAULT_OPTIONS]). +-spec noenv_output_generated([option()]) -> boolean(). + noenv_output_generated(Opts) -> any(fun ({save_binary,_F}) -> true; (_Other) -> false @@ -216,16 +221,16 @@ format_error({module_name,Mod,Filename}) -> [Mod,Filename]). %% The compile state record. --record(compile, {filename="", - dir="", - base="", - ifile="", - ofile="", +-record(compile, {filename="" :: file:filename(), + dir="" :: file:filename(), + base="" :: file:filename(), + ifile="" :: file:filename(), + ofile="" :: file:filename(), module=[], code=[], core_code=[], abstract_code=[], %Abstract code for debugger. - options=[], + options=[] :: [option()], errors=[], warnings=[]}). @@ -362,7 +367,7 @@ mpf(Ms) -> [{File,[M || {F,M} <- Ms, F =:= File]} || File <- lists:usort([F || {F,_} <- Ms])]. -%% passes(form|file, [Option]) -> [{Name,PassFun}] +%% passes(forms|file, [Option]) -> [{Name,PassFun}] %% Figure out which passes that need to be run. passes(forms, Opts) -> @@ -828,7 +833,6 @@ get_core_transforms(Opts) -> [M || {core_transform,M} <- Opts]. core_transforms(St) -> %% The options field holds the complete list of options at this - Ts = get_core_transforms(St#compile.options), foldl_core_transforms(St, Ts). @@ -1172,12 +1176,12 @@ write_binary(Name, Bin, St) -> %% report_errors(State) -> ok %% report_warnings(State) -> ok -report_errors(St) -> - case member(report_errors, St#compile.options) of +report_errors(#compile{options=Opts,errors=Errors}) -> + case member(report_errors, Opts) of true -> foreach(fun ({{F,_L},Eds}) -> list_errors(F, Eds); ({F,Eds}) -> list_errors(F, Eds) end, - St#compile.errors); + Errors); false -> ok end. diff --git a/lib/debugger/src/dbg_icmd.erl b/lib/debugger/src/dbg_icmd.erl index 7ccb9793a3..a26b16c82d 100644 --- a/lib/debugger/src/dbg_icmd.erl +++ b/lib/debugger/src/dbg_icmd.erl @@ -94,7 +94,7 @@ break_p(Mod, Line, Le, Bs) -> Bool = case Cond of null -> true; {CM, CN} -> - try apply(CM, CN, [Bs]) of + try CM:CN(Bs) of true -> true; false -> false; _Term -> false @@ -245,7 +245,7 @@ handle_int_msg({attached, AttPid}, Status, _Bs, %% Tell attached process in which module evalution is located if - Le==1 -> + Le =:= 1 -> tell_attached({attached, undefined, -1, get(trace)}); true -> tell_attached({attached, M, Line, get(trace)}), @@ -269,7 +269,7 @@ handle_int_msg(detached, _Status, _Bs, _Ieval) -> handle_int_msg({old_code,Mod}, Status, Bs, #ieval{level=Le,module=M}=Ieval) -> if - Status==idle, Le==1 -> + Status =:= idle, Le =:= 1 -> erase([Mod|db]), put(cache, []); true -> @@ -352,9 +352,9 @@ set_stack_trace(true) -> set_stack_trace(all); set_stack_trace(Flag) -> if - Flag==false -> + Flag =:= false -> put(stack, []); - Flag==no_tail; Flag==all -> + Flag =:= no_tail; Flag =:= all -> ignore end, put(trace_stack, Flag), @@ -367,7 +367,7 @@ bindings(Bs, nostack) -> Bs; bindings(Bs, SP) -> case dbg_ieval:stack_level() of - Le when SP>Le -> + Le when SP > Le -> Bs; _ -> dbg_ieval:bindings(SP) @@ -377,7 +377,6 @@ messages() -> {messages, Msgs} = erlang:process_info(get(self), messages), Msgs. - %%==================================================================== %% Evaluating expressions within process context %%==================================================================== @@ -398,7 +397,7 @@ eval_restricted({From,_Mod,Cmd,SP}, Bs) -> From ! {self(), {eval_rsp, Rsp}} end. -eval_nonrestricted({From,Mod,Cmd,SP}, Bs, #ieval{level=Le}) when SP<Le-> +eval_nonrestricted({From,Mod,Cmd,SP}, Bs, #ieval{level=Le}) when SP < Le-> %% Evaluate in stack eval_restricted({From, Mod, Cmd, SP}, Bs), Bs; @@ -424,15 +423,15 @@ eval_nonrestricted({From, _Mod, Cmd, _SP}, Bs, eval_nonrestricted_1({match,_,{var,_,Var},Expr}, Bs, Ieval) -> {value,Res,Bs2} = dbg_ieval:eval_expr(Expr, Bs, Ieval#ieval{last_call=false}), - Bs3 = case lists:keysearch(Var, 1, Bs) of - {value, {Var,_Value}} -> + Bs3 = case lists:keyfind(Var, 1, Bs) of + {Var,_Value} -> lists:keyreplace(Var, 1, Bs2, {Var,Res}); false -> [{Var,Res} | Bs2] end, {Res,Bs3}; eval_nonrestricted_1({var,_,Var}, Bs, _Ieval) -> - Res = case lists:keysearch(Var, 1, Bs) of - {value, {Var, Value}} -> Value; + Res = case lists:keyfind(Var, 1, Bs) of + {Var, Value} -> Value; false -> unbound end, {Res,Bs}; @@ -458,7 +457,6 @@ parse_cmd(Cmd, LineNo) -> {ok,Forms} = erl_parse:parse_exprs(Tokens), Forms. - %%==================================================================== %% Library functions for attached process handling %%==================================================================== @@ -470,13 +468,12 @@ tell_attached(Msg) -> AttPid ! {self(), Msg} end. - %%==================================================================== %% get_binding/2 %%==================================================================== get_binding(Var, Bs) -> - case lists:keysearch(Var, 1, Bs) of - {value, {Var, Value}} -> {value, Value}; + case lists:keyfind(Var, 1, Bs) of + {Var, Value} -> {value, Value}; false -> unbound end. diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index c13fda7ac1..476dfd8796 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -120,9 +120,9 @@ check_exit_msg({'EXIT', Int, Reason}, _Bs, #ieval{level=Le}) -> %% This *must* be interpreter which has terminated, %% we are not linked to anyone else if - Le==1 -> + Le =:= 1 -> exit(Reason); - Le>1 -> + Le > 1 -> exit({Int, Reason}) end; check_exit_msg({'DOWN',_,_,_,Reason}, Bs, @@ -139,9 +139,9 @@ check_exit_msg({'DOWN',_,_,_,Reason}, Bs, %% importance in this case %% If we don't save them, however, post-mortem analysis %% of the process isn't possible - undefined when Le==1 -> % died outside interpreted code + undefined when Le =:= 1 -> % died outside interpreted code {}; - undefined when Le>1 -> + undefined when Le > 1 -> StackBin = term_to_binary(get(stack)), {{Mod, Li}, Bs, StackBin}; @@ -152,9 +152,9 @@ check_exit_msg({'DOWN',_,_,_,Reason}, Bs, dbg_iserver:cast(get(int), {set_exit_info,self(),ExitInfo}), if - Le==1 -> + Le =:= 1 -> exit(Reason); - Le>1 -> + Le > 1 -> exit({get(self), Reason}) end; check_exit_msg(_Msg, _Bs, _Ieval) -> @@ -271,7 +271,7 @@ meta_loop(Debugged, Bs, #ieval{level=Le} = Ieval) -> end; %% Re-entry to Meta from non-interpreted code - {re_entry, Debugged, {eval,{M,F,As}}} when Le==1 -> + {re_entry, Debugged, {eval,{M,F,As}}} when Le =:= 1 -> %% Reset process dictionary %% This is really only necessary if the process left %% interpreted code at a call level > 1 @@ -346,7 +346,7 @@ push(MFA, Bs, #ieval{level=Le,module=Cm,line=Li,last_call=Lc}) -> [] -> put(stack, [Entry]); [_Entry|Entries] -> put(stack, [Entry|Entries]) end; - _ -> % all | no_tail when Lc==false + _ -> % all | no_tail when Lc =:= false put(stack, [Entry|get(stack)]) end. @@ -413,10 +413,10 @@ sublist(L, Start, Length) -> lists:sublist(L, Start, Length). fix_stacktrace2([{_,{{M,F,As1},_,_}}, {_,{{M,F,As2},_,_}}|_]) - when length(As1)==length(As2) -> + when length(As1) =:= length(As2) -> [{M,F,As1}]; fix_stacktrace2([{_,{{Fun,As1},_,_}}, {_,{{Fun,As2},_,_}}|_]) - when length(As1)==length(As2) -> + when length(As1) =:= length(As2) -> [{Fun,As1}]; fix_stacktrace2([{_,{MFA,_,_}}|Entries]) -> [MFA|fix_stacktrace2(Entries)]; @@ -465,9 +465,9 @@ stack_frame(SP, Dir, [{SP, _}|Stack]) -> case Stack of [{Le, {_MFA,Where,Bs}}|_] -> {Le, Where, Bs}; - [] when Dir==up -> + [] when Dir =:= up -> top; - [] when Dir==down -> + [] when Dir =:= down -> bottom end; stack_frame(SP, Dir, [_Entry|Stack]) -> @@ -509,7 +509,7 @@ trace(What, Args, true) -> end, io_lib:format(" (~w) receive " ++ Tail, [Le]); - received when Args==null -> + received when Args =:= null -> io_lib:format("~n", []); received -> % Args=Msg io_lib:format("~n<== ~p~n", [Args]); @@ -586,8 +586,8 @@ eval_mfa(Debugged, M, F, As, Ieval) -> end. eval_function(Mod, Fun, As0, Bs0, _Called, Ieval) when is_function(Fun); - Mod==?MODULE, - Fun==eval_fun -> + Mod =:= ?MODULE, + Fun =:= eval_fun -> #ieval{level=Le, line=Li, last_call=Lc} = Ieval, case lambda(Fun, As0) of {Cs,Module,Name,As,Bs} -> @@ -657,7 +657,7 @@ eval_function(Mod, Name, As0, Bs0, Called, Ieval) -> lambda(eval_fun, [Cs,As,Bs,{Mod,Name}=F]) -> %% Fun defined in interpreted code, called from outside if - length(element(3,hd(Cs))) == length(As) -> + length(element(3,hd(Cs))) =:= length(As) -> db_ref(Mod), %% Adds ref between module and process {Cs,Mod,Name,As,Bs}; true -> @@ -672,7 +672,7 @@ lambda(Fun, As) when is_function(Fun) -> {env, [{Mod,Name},Bs,Cs]} = erlang:fun_info(Fun, env), {arity, Arity} = erlang:fun_info(Fun, arity), if - length(As) == Arity -> + length(As) =:= Arity -> db_ref(Mod), %% Adds ref between module and process {Cs,Mod,Name,As,Bs}; true -> @@ -731,7 +731,7 @@ db_ref(Mod) -> ModDb -> Node = node(get(int)), DbRef = if - Node/=node() -> {Node,ModDb}; + Node =/= node() -> {Node,ModDb}; true -> ModDb end, put([Mod|db], DbRef), @@ -741,13 +741,12 @@ db_ref(Mod) -> DbRef end. - cache(Key, Data) -> put(cache, lists:sublist([{Key,Data}|get(cache)], 5)). cached(Key) -> - case lists:keysearch(Key, 1, get(cache)) of - {value,{Key,Data}} -> Data; + case lists:keyfind(Key, 1, get(cache)) of + {Key,Data} -> Data; false -> false end. @@ -844,7 +843,7 @@ expr({'try',Line,Es,CaseCs,CatchCs,[]}, Bs0, Ieval0) -> case_clauses(Val, CaseCs, Bs, try_clause, Ieval) end catch - Class:Reason when CatchCs=/=[] -> + Class:Reason when CatchCs =/= [] -> catch_clauses({Class,Reason,[]}, CatchCs, Bs0, Ieval) end; expr({'try',Line,Es,CaseCs,CatchCs,As}, Bs0, Ieval0) -> @@ -1498,10 +1497,7 @@ guard(Gs, Bs) -> or_guard(Gs, Bs). or_guard([G|Gs], Bs) -> %% Short-circuit OR. - case and_guard(G, Bs) of - true -> true; - false -> or_guard(Gs, Bs) - end; + and_guard(G, Bs) orelse or_guard(Gs, Bs); or_guard([], _) -> false. and_guard([G|Gs], Bs) -> @@ -1598,8 +1594,7 @@ match1({bin,_,Fs}, B, Bs0, BBs0) when is_bitstring(B) -> try eval_bits:match_bits(Fs, B, Bs1, BBs, fun(L, R, Bs) -> match1(L, R, Bs, BBs) end, fun(E, Bs) -> expr(E, Bs, #ieval{}) end, - false) of - Match -> Match + false) catch _:_ -> throw(nomatch) end; @@ -1687,7 +1682,7 @@ merge_bindings([{Name,V}|B1s], B2s, Ieval) -> case binding(Name, B2s) of {value,V} -> % Already there, and the same merge_bindings(B1s, B2s, Ieval); - {value,_} when Name=='_' -> % Already there, but anonymous + {value,_} when Name =:= '_' -> % Already there, but anonymous B2s1 = lists:keydelete('_', 1, B2s), [{Name,V}|merge_bindings(B1s, B2s1, Ieval)]; {value,_} -> % Already there, but different => badmatch diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl index ec54c646c8..2ae0c333da 100644 --- a/lib/debugger/src/dbg_iload.erl +++ b/lib/debugger/src/dbg_iload.erl @@ -26,7 +26,7 @@ %%-------------------------------------------------------------------- %% load_mod(Mod, File, Binary, Db) -> {ok, Mod} -%% Mod = atom() +%% Mod = module() %% File = string() Source file (including path) %% Binary = binary() %% Db = ETS identifier @@ -408,8 +408,7 @@ expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,fault}},[_]=As}) -> {dbg,Line,fault,expr_list(As)}; expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,exit}},[_]=As}) -> {dbg,Line,exit,expr_list(As)}; -expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,apply}},As0}) - when length(As0) == 3 -> +expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,apply}},[_,_,_]=As0}) -> As = expr_list(As0), {apply,Line,As}; expr({call,Line,{remote,_,{atom,_,Mod},{atom,_,Func}},As0}) -> @@ -517,15 +516,9 @@ expr(Other) -> %% here as sys_pre_expand has transformed source. is_guard_test({op,_,Op,L,R}) -> - case erl_internal:comp_op(Op, 2) of - true -> is_gexpr_list([L,R]); - false -> false - end; + erl_internal:comp_op(Op, 2) andalso is_gexpr_list([L,R]); is_guard_test({call,_,{remote,_,{atom,_,erlang},{atom,_,Test}},As}) -> - case erl_internal:type_test(Test, length(As)) of - true -> is_gexpr_list(As); - false -> false - end; + erl_internal:type_test(Test, length(As)) andalso is_gexpr_list(As); is_guard_test({atom,_,true}) -> true; is_guard_test(_) -> false. @@ -542,22 +535,12 @@ is_gexpr({call,_,{remote,_,{atom,_,erlang},{atom,_,F}},As}) -> Ar = length(As), case erl_internal:guard_bif(F, Ar) of true -> is_gexpr_list(As); - false -> - case erl_internal:arith_op(F, Ar) of - true -> is_gexpr_list(As); - false -> false - end + false -> erl_internal:arith_op(F, Ar) andalso is_gexpr_list(As) end; is_gexpr({op,_,Op,A}) -> - case erl_internal:arith_op(Op, 1) of - true -> is_gexpr(A); - false -> false - end; + erl_internal:arith_op(Op, 1) andalso is_gexpr(A); is_gexpr({op,_,Op,A1,A2}) -> - case erl_internal:arith_op(Op, 2) of - true -> is_gexpr_list([A1,A2]); - false -> false - end; + erl_internal:arith_op(Op, 2) andalso is_gexpr_list([A1,A2]); is_gexpr(_) -> false. is_gexpr_list(Es) -> lists:all(fun (E) -> is_gexpr(E) end, Es). diff --git a/lib/debugger/src/dbg_iserver.erl b/lib/debugger/src/dbg_iserver.erl index 4c1e9ccb7b..59188d83a2 100644 --- a/lib/debugger/src/dbg_iserver.erl +++ b/lib/debugger/src/dbg_iserver.erl @@ -155,11 +155,8 @@ handle_call(get_stack_trace, _From, State) -> %% Retrieving information handle_call(snapshot, _From, State) -> - Reply = lists:map(fun(Proc) -> - {Proc#proc.pid, Proc#proc.function, - Proc#proc.status, Proc#proc.info} - end, - State#state.procs), + Reply = [{Proc#proc.pid, Proc#proc.function, + Proc#proc.status, Proc#proc.info} || Proc <- State#state.procs], {reply, Reply, State}; handle_call({get_meta, Pid}, _From, State) -> Reply = case get_proc({pid, Pid}, State#state.procs) of @@ -181,21 +178,21 @@ handle_call({get_attpid, Pid}, _From, State) -> %% Breakpoint handling handle_call({new_break, Point, Options}, _From, State) -> - case lists:keysearch(Point, 1, State#state.breaks) of + case lists:keymember(Point, 1, State#state.breaks) of false -> Break = {Point, Options}, send_all([subscriber, meta, attached], {new_break, Break}, State), Breaks = keyinsert(Break, 1, State#state.breaks), {reply, ok, State#state{breaks=Breaks}}; - {value, _Break} -> + true -> {reply, {error, break_exists}, State} end; handle_call(all_breaks, _From, State) -> {reply, State#state.breaks, State}; handle_call({all_breaks, Mod}, _From, State) -> Reply = lists:filter(fun({{M,_L}, _Options}) -> - if M==Mod -> true; true -> false end + M =/= Mod end, State#state.breaks), {reply, Reply, State}; @@ -276,7 +273,7 @@ handle_call({contents, Mod, Pid}, _From, State) -> Db = State#state.db, [{{Mod, refs}, ModDbs}] = ets:lookup(Db, {Mod, refs}), ModDb = if - Pid==any -> hd(ModDbs); + Pid =:= any -> hd(ModDbs); true -> lists:foldl(fun(T, not_found) -> [{T, Pids}] = ets:lookup(Db, T), @@ -295,7 +292,7 @@ handle_call({raw_contents, Mod, Pid}, _From, State) -> Db = State#state.db, [{{Mod, refs}, ModDbs}] = ets:lookup(Db, {Mod, refs}), ModDb = if - Pid==any -> hd(ModDbs); + Pid =:= any -> hd(ModDbs); true -> lists:foldl(fun(T, not_found) -> [{T, Pids}] = ets:lookup(Db, T), @@ -360,15 +357,15 @@ handle_cast({set_stack_trace, Flag}, State) -> %% Retrieving information handle_cast(clear, State) -> Procs = lists:filter(fun(#proc{status=Status}) -> - if Status==exit -> false; true -> true end + Status =/= exit end, State#state.procs), {noreply, State#state{procs=Procs}}; %% Breakpoint handling handle_cast({delete_break, Point}, State) -> - case lists:keysearch(Point, 1, State#state.breaks) of - {value, _Break} -> + case lists:keymember(Point, 1, State#state.breaks) of + true -> send_all([subscriber, meta, attached], {delete_break, Point}, State), Breaks = lists:keydelete(Point, 1, State#state.breaks), @@ -377,8 +374,8 @@ handle_cast({delete_break, Point}, State) -> {noreply, State} end; handle_cast({break_option, Point, Option, Value}, State) -> - case lists:keysearch(Point, 1, State#state.breaks) of - {value, {Point, Options}} -> + case lists:keyfind(Point, 1, State#state.breaks) of + {Point, Options} -> N = case Option of status -> 1; action -> 2; @@ -399,7 +396,7 @@ handle_cast(no_break, State) -> handle_cast({no_break, Mod}, State) -> send_all([subscriber, meta, attached], {no_break, Mod}, State), Breaks = lists:filter(fun({{M, _L}, _O}) -> - if M==Mod -> false; true -> true end + M =/= Mod end, State#state.breaks), {noreply, State#state{breaks=Breaks}}; @@ -409,7 +406,7 @@ handle_cast({set_status, Meta, Status, Info}, State) -> {true, Proc} = get_proc({meta, Meta}, State#state.procs), send_all(subscriber, {new_status, Proc#proc.pid, Status, Info}, State), if - Status==break -> + Status =:= break -> auto_attach(break, State#state.auto, Proc); true -> ignore end, @@ -526,11 +523,10 @@ code_change(_OldVsn, State, _Extra) -> %% Internal functions %%==================================================================== -auto_attach(Why, Auto, Proc) when is_record(Proc, proc) -> - case Proc#proc.attpid of - AttPid when is_pid(AttPid) -> ignore; - undefined -> - auto_attach(Why, Auto, Proc#proc.pid) +auto_attach(Why, Auto, #proc{attpid = Attpid, pid = Pid}) -> + case Attpid of + undefined -> auto_attach(Why, Auto, Pid); + _ when is_pid(Attpid) -> ignore end; auto_attach(Why, Auto, Pid) when is_pid(Pid) -> case Auto of @@ -545,7 +541,7 @@ auto_attach(Why, Auto, Pid) when is_pid(Pid) -> keyinsert(Tuple1, N, [Tuple2|Tuples]) -> if - element(N, Tuple1)<element(N, Tuple2) -> + element(N, Tuple1) < element(N, Tuple2) -> [Tuple1, Tuple2|Tuples]; true -> [Tuple2 | keyinsert(Tuple1, N, Tuples)] @@ -576,7 +572,7 @@ send_all([], _Msg, _State) -> ok; send_all(subscriber, Msg, State) -> send_all(State#state.subs, Msg); send_all(meta, Msg, State) -> - Metas = lists:map(fun(Proc) -> Proc#proc.meta end, State#state.procs), + Metas = [Proc#proc.meta || Proc <- State#state.procs], send_all(Metas, Msg); send_all(attached, Msg, State) -> AttPids= mapfilter(fun(Proc) -> @@ -600,7 +596,7 @@ get_proc({Type, Pid}, Procs) -> meta -> #proc.meta; attpid -> #proc.attpid end, - case lists:keysearch(Pid, Index, Procs) of - {value, Proc} -> {true, Proc}; - false -> false + case lists:keyfind(Pid, Index, Procs) of + false -> false; + Proc -> {true, Proc} end. diff --git a/lib/debugger/src/dbg_ui_break_win.erl b/lib/debugger/src/dbg_ui_break_win.erl index a56fe36828..0c1e25e703 100644 --- a/lib/debugger/src/dbg_ui_break_win.erl +++ b/lib/debugger/src/dbg_ui_break_win.erl @@ -268,12 +268,7 @@ handle_event({gs, _Id, click, _Data, ["Ok"|_]}, WinInfo) -> IndexL -> Funcs = WinInfo#winInfo.funcs, Breaks = - lists:map(fun(Index) -> - Func = lists:nth(Index+1, - Funcs), - [Mod | Func] - end, - IndexL), + [[Mod|lists:nth(Index+1, Funcs)] || Index <- IndexL], {break, Breaks, enable} end end; diff --git a/lib/debugger/src/dbg_ui_filedialog_win.erl b/lib/debugger/src/dbg_ui_filedialog_win.erl index f7d76076a5..79ccf20946 100644 --- a/lib/debugger/src/dbg_ui_filedialog_win.erl +++ b/lib/debugger/src/dbg_ui_filedialog_win.erl @@ -202,8 +202,7 @@ handle_event({gs, 'Files', doubleclick, _Data, _Arg}, WinInfo) -> handle_event({gs, _Id, click, select, _Arg}, _WinInfo) -> {select, gs:read('Selection', text)}; handle_event({gs, _Id, click, multiselect, _Arg}, WinInfo) -> - Files = lists:map(fun(File) -> untag(File) end, - gs:read('Files', items)), + Files = [untag(File) || File <- gs:read('Files', items)], {multiselect, WinInfo#winInfo.cwd, Files}; handle_event({gs, _Id, click, filter, _Arg}, WinInfo) -> {Cwd, Pattern} = update_win(gs:read('Filter', text), @@ -286,7 +285,7 @@ max_existing([Name | Names]) -> max_existing(Dir, [Name | Names]) -> Dir2 = filename:join(Dir, Name), case filelib:is_file(Dir2, erl_prim_loader) of - true when Names==[] -> {Dir2, []}; + true when Names =:= [] -> {Dir2, []}; true -> max_existing(Dir2, Names); false -> {Dir, [Name | Names]} end. @@ -309,11 +308,8 @@ extra_filter([], _Dir, _Fun) -> []. get_subdirs(Dir) -> case erl_prim_loader:list_dir(Dir) of {ok, FileNames} -> - X = lists:filter(fun(FileName) -> - File = filename:join(Dir, FileName), - filelib:is_dir(File, erl_prim_loader) - end, - FileNames), + X = [FN || FN <- FileNames, + filelib:is_dir(filename:join(Dir, FN), erl_prim_loader)], lists:sort(X); _Error -> [] @@ -335,4 +331,3 @@ compare([], [$/|File]) -> File; compare(_, _) -> error. - diff --git a/lib/debugger/src/dbg_ui_mon.erl b/lib/debugger/src/dbg_ui_mon.erl index 8888075124..82fe210968 100644 --- a/lib/debugger/src/dbg_ui_mon.erl +++ b/lib/debugger/src/dbg_ui_mon.erl @@ -162,7 +162,7 @@ init2(CallingPid, Mode, SFile, GS) -> CallingPid ! {initialization_complete, self()}, if - SFile==default -> + SFile =:= default -> loop(State3); true -> loop(load_settings(SFile, State3)) @@ -226,7 +226,7 @@ loop(State) -> gui_cmd(stopped, State); %% From the GUI - GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent)==gs -> + GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent) =:= gs -> Cmd = dbg_ui_mon_win:handle_event(GuiEvent,State#state.win), State2 = gui_cmd(Cmd, State), loop(State2); @@ -269,7 +269,7 @@ gui_cmd(ignore, State) -> State; gui_cmd(stopped, State) -> if - State#state.starter==true -> int:stop(); + State#state.starter =:= true -> int:stop(); true -> int:auto_attach(false) end, exit(stop); @@ -413,9 +413,9 @@ gui_cmd({'Trace Window', TraceWin}, State) -> State2; gui_cmd({'Auto Attach', When}, State) -> if - When==[] -> int:auto_attach(false); + When =:= [] -> int:auto_attach(false); true -> - Flags = lists:map(fun(Name) -> map(Name) end, When), + Flags = [map(Name) || Name <- When], int:auto_attach(Flags, trace_function(State)) end, State; @@ -676,7 +676,7 @@ load_settings2(Settings, State) -> Break, int:break(Mod, Line), if - Status==inactive -> + Status =:= inactive -> int:disable_break(Mod, Line); true -> ignore end, @@ -700,12 +700,8 @@ save_settings(SFile, State) -> int:auto_attach(), int:stack_trace(), State#state.backtrace, - lists:map(fun(Mod) -> - int:file(Mod) - end, - int:interpreted()), + [int:file(Mod) || Mod <- int:interpreted()], int:all_breaks()}, - Binary = term_to_binary({debugger_settings, Settings}), case file:write_file(SFile, Binary) of ok -> @@ -720,13 +716,12 @@ save_settings(SFile, State) -> %%==================================================================== registered_name(Pid) -> - %% Yield in order to give Pid more time to register its name timer:sleep(200), Node = node(Pid), if - Node==node() -> + Node =:= node() -> case erlang:process_info(Pid, registered_name) of {registered_name, Name} -> Name; _ -> undefined diff --git a/lib/debugger/src/dbg_ui_mon_win.erl b/lib/debugger/src/dbg_ui_mon_win.erl index ba2f94c550..66e59a822a 100644 --- a/lib/debugger/src/dbg_ui_mon_win.erl +++ b/lib/debugger/src/dbg_ui_mon_win.erl @@ -224,8 +224,7 @@ select(MenuItem, Bool) -> %%-------------------------------------------------------------------- add_module(WinInfo, Menu, Mod) -> Modules = WinInfo#winInfo.modules, - case lists:keysearch(Mod, #moduleInfo.module, Modules) of - {value, _ModInfo} -> WinInfo; + case lists:keymember(Mod, #moduleInfo.module, Modules) of false -> %% Create a menu for the module Font = dbg_ui_win:font(normal), @@ -244,7 +243,8 @@ add_module(WinInfo, Menu, Mod) -> gs:config(WinInfo#winInfo.listbox, {add, Mod}), ModInfo = #moduleInfo{module=Mod, menubtn=MenuBtn}, - WinInfo#winInfo{modules=[ModInfo | Modules]} + WinInfo#winInfo{modules=[ModInfo | Modules]}; + true -> WinInfo end. %%-------------------------------------------------------------------- @@ -491,14 +491,13 @@ handle_event({gs, _Id, click, autoattach, _Arg}, WinInfo) -> %% Process grid handle_event({gs, _Id, keypress, _Data, [Key|_]}, WinInfo) when - Key=='Up'; Key=='Down' -> - Dir = if Key=='Up' -> up; Key=='Down' -> down end, + Key =:= 'Up'; Key =:= 'Down' -> + Dir = if Key =:= 'Up' -> up; Key =:= 'Down' -> down end, Row = move(WinInfo, Dir), - if Row>1 -> WinInfo2 = highlight(WinInfo, Row), - {value, #procInfo{pid=Pid}} = - lists:keysearch(Row, #procInfo.row, WinInfo#winInfo.processes), + #procInfo{pid=Pid} = + lists:keyfind(Row, #procInfo.row, WinInfo#winInfo.processes), {focus, Pid, WinInfo2}; true -> ignore @@ -515,10 +514,9 @@ handle_event(_GSEvent, _WinInfo) -> move(WinInfo, Dir) -> Row = WinInfo#winInfo.focus, Last = WinInfo#winInfo.row, - if - Dir==up, Row>1 -> Row-1; - Dir==down, Row<Last -> Row+1; + Dir =:= up, Row > 1 -> Row-1; + Dir =:= down, Row < Last -> Row+1; true -> Row end. @@ -533,7 +531,6 @@ highlight(WinInfo, Row) -> GridLine2 = gs:read(Grid, {obj_at_row, Row}), gs:config(GridLine2, {fg, white}), WinInfo#winInfo{focus=Row}. - %%==================================================================== %% Internal functions @@ -545,7 +542,7 @@ configure(WinInfo, {W, H}) -> Dx = NewW - gs:read(Grid, width), Dy = H-42 - gs:read(Grid, height), if - (Dx+Dy)=/=0 -> + (Dx+Dy) =/= 0 -> gs:config(Grid, [{width, NewW}, {height, H-30}]), Cols = calc_columnwidths(NewW), gs:config(Grid, Cols); @@ -555,10 +552,9 @@ configure(WinInfo, {W, H}) -> calc_columnwidths(Width) -> W = if - Width=<?Wg -> ?Wg; + Width =< ?Wg -> ?Wg; true -> Width end, - First = lists:map(fun (X) -> round(X) end, - [0.13*W, 0.27*W, 0.18*W, 0.18*W]), + First = [round(X) || X <- [0.13*W, 0.27*W, 0.18*W, 0.18*W]], Last = W - lists:sum(First) - 30, {columnwidths, First++[Last]}. diff --git a/lib/debugger/src/dbg_ui_trace_win.erl b/lib/debugger/src/dbg_ui_trace_win.erl index c6f041a63d..82d4199630 100644 --- a/lib/debugger/src/dbg_ui_trace_win.erl +++ b/lib/debugger/src/dbg_ui_trace_win.erl @@ -147,17 +147,17 @@ configure(WinInfo, TraceWin) -> H = gs:read(Win, height), H2 = if - Bu1==close, Bu2==open -> + Bu1 =:= close, Bu2 =:= open -> resize_button_area(open, width, W-4), gs:config('ButtonArea', {height, 30}), H+30; - Bu1==open, Bu2==close -> + Bu1 =:= open, Bu2 =:= close -> gs:config('ButtonArea', [{width, 0}, {height, 0}]), H-30; true -> H end, H3 = if - Ev1==close, Ev2==open, Bi1==open -> + Ev1 =:= close, Ev2 =:= open, Bi1 =:= open -> Wnew1 = round((W-10-4)/2), % W = window/2 - rb - pads Hbi1 = gs:read('BindArea', height), % H = bind area h resize_eval_area(open, width, Wnew1), @@ -167,25 +167,25 @@ configure(WinInfo, TraceWin) -> resize_bind_area(open, width, Wnew1-gs:read('BindArea', width)), H2; - Ev1==close, Ev2==open, Bi1==close -> + Ev1 =:= close, Ev2 =:= open, Bi1 =:= close -> resize_eval_area(open, width, W-4), resize_eval_area(open, height, 200), H2+200; - Ev1==open, Ev2==close, Bi1==open -> + Ev1 =:= open, Ev2 =:= close, Bi1 =:= open -> gs:config('EvalArea', [{width,0}, {height,0}]), gs:config('RB3', [{width, 0}, {height, 0}]), Wnew2 = W-4, resize_bind_area(open, width, Wnew2-gs:read('BindArea', width)), H2; - Ev1==open, Ev2==close, Bi1==close -> + Ev1 =:= open, Ev2 =:= close, Bi1 =:= close -> Hs1 = gs:read('EvalArea', height), gs:config('EvalArea', [{width, 0}, {height, 0}]), H2-Hs1; true -> H2 end, H4 = if - Bi1==close, Bi2==open, Ev2==open -> + Bi1 =:= close, Bi2 =:= open, Ev2 =:= open -> Wnew3 = round((W-10-4)/2), % W = window/2 - rb - pads Hs2 = gs:read('EvalArea', height), % H = eval area h resize_bind_area(open, width, Wnew3), @@ -194,29 +194,29 @@ configure(WinInfo, TraceWin) -> resize_eval_area(open, width, Wnew3-gs:read('EvalArea', width)), H3; - Bi1==close, Bi2==open, Ev2==close -> + Bi1 =:= close, Bi2 =:= open, Ev2 =:= close -> resize_bind_area(open, width, W-4), resize_bind_area(open, height, 200), H3+200; - Bi1==open, Bi2==close, Ev2==open -> + Bi1 =:= open, Bi2 =:= close, Ev2 =:= open -> gs:config('BindArea', [{width, 0}, {height, 0}]), gs:config('RB3', [{width, 0}, {height, 0}]), Wnew4 = W-4, resize_eval_area(open, width, Wnew4-gs:read('EvalArea', width)), H3; - Bi1==open, Bi2==close, Ev2==close -> + Bi1 =:= open, Bi2 =:= close, Ev2 =:= close -> Hbi2 = gs:read('BindArea', height), gs:config('BindArea', [{width, 0}, {height, 0}]), H3-Hbi2; true -> H3 end, H5 = if - Tr1==close, Tr2==open -> + Tr1 =:= close, Tr2 =:= open -> resize_trace_area(open, width, W-4), resize_trace_area(open, height, 200), H4+200; - Tr1==open, Tr2==close -> + Tr1 =:= open, Tr2 =:= close -> Hf = gs:read('TraceArea', height), gs:config('TraceArea', [{width, 0}, {height, 0}]), H4-Hf; @@ -226,10 +226,10 @@ configure(WinInfo, TraceWin) -> RB1old = rb1(OldFlags), RB1new = rb1(NewFlags), if - RB1old==close, RB1new==open -> + RB1old =:= close, RB1new =:= open -> gs:config('RB1', [{width, W-4}, {height, 10}]), gs:config(Win, {height, gs:read(Win, height)+10}); - RB1old==open, RB1new==close -> + RB1old =:= open, RB1new =:= close -> gs:config('RB1', [{width, 0}, {height, 0}, lower]), gs:config(Win, {height, gs:read(Win, height)-10}); true -> ignore @@ -237,10 +237,10 @@ configure(WinInfo, TraceWin) -> RB2old = rb2(OldFlags), RB2new = rb2(NewFlags), if - RB2old==close, RB2new==open -> + RB2old =:= close, RB2new =:= open -> gs:config('RB2', [{width, W-4}, {height, 10}]), gs:config(Win, {height,gs:read(Win, height)+10}); - RB2old==open, RB2new==close -> + RB2old =:= open, RB2new =:= close -> gs:config('RB2', [{width, 0}, {height, 0}, lower]), gs:config(Win, {height, gs:read(Win, height)-10}); true -> ignore @@ -301,15 +301,15 @@ select(MenuItem, Bool) -> %% Cond = null | {Mod, Func} %%-------------------------------------------------------------------- add_break(WinInfo, Menu, {{Mod,Line},[Status|_Options]}=Break) -> - case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of - {value, {Mod, Editor}} -> + case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of + {Mod, Editor} -> add_break_to_code(Editor, Line, Status); false -> ignore end, add_break_to_menu(WinInfo, Menu, Break). add_break_to_code(Editor, Line, Status) -> - Color = if Status==active -> red; Status==inactive -> blue end, + Color = if Status =:= active -> red; Status =:= inactive -> blue end, config_editor(Editor, [{overwrite,{{Line,0},"-@- "}}, {fg,{{{Line,0},{Line,lineend}}, Color}}]). @@ -330,8 +330,8 @@ add_break_to_menu(WinInfo, Menu, {Point, [Status|_Options]=Options}) -> %% Cond = null | {Mod, Func} %%-------------------------------------------------------------------- update_break(WinInfo, {{Mod,Line},[Status|_Options]}=Break) -> - case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of - {value, {Mod, Editor}} -> + case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of + {Mod, Editor} -> add_break_to_code(Editor, Line, Status); false -> ignore end, @@ -352,8 +352,8 @@ update_break_in_menu(WinInfo, {Point, [Status|_Options]=Options}) -> %% Point = {Mod, Line} %%-------------------------------------------------------------------- delete_break(WinInfo, {Mod,Line}=Point) -> - case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of - {value, {Mod, Editor}} -> delete_break_from_code(Editor, Line); + case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of + {Mod, Editor} -> delete_break_from_code(Editor, Line); false -> ignore end, delete_break_from_menu(WinInfo, Point). @@ -379,11 +379,11 @@ clear_breaks(WinInfo) -> clear_breaks(WinInfo, all). clear_breaks(WinInfo, Mod) -> Remove = if - Mod==all -> WinInfo#winInfo.breaks; + Mod =:= all -> WinInfo#winInfo.breaks; true -> lists:filter(fun(#breakInfo{point={Mod2,_L}}) -> if - Mod2==Mod -> true; + Mod2 =:= Mod -> true; true -> false end end, @@ -450,8 +450,8 @@ display(Arg) -> %% Note: remove_code/2 should not be used for currently shown module. %%-------------------------------------------------------------------- is_shown(WinInfo, Mod) -> - case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of - {value, {Mod, Editor}} -> + case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of + {Mod, Editor} -> gs:config(Editor, raise), {true, WinInfo#winInfo{editor={Mod, Editor}}}; false -> false @@ -459,24 +459,22 @@ is_shown(WinInfo, Mod) -> show_code(WinInfo, Mod, Contents) -> Editors = WinInfo#winInfo.editors, - {Flag, Editor} = case lists:keysearch(Mod, 1, Editors) of - {value, {Mod, Ed}} -> {existing, Ed}; + {Flag, Editor} = case lists:keyfind(Mod, 1, Editors) of + {Mod, Ed} -> {existing, Ed}; false -> {new, code_editor()} end, - %% Insert code and update breakpoints, if any config_editor(Editor, [raise, clear]), show_code(Editor, Contents), lists:foreach(fun(BreakInfo) -> case BreakInfo#breakInfo.point of - {Mod2, Line} when Mod2==Mod -> + {Mod2, Line} when Mod2 =:= Mod -> Status = BreakInfo#breakInfo.status, add_break_to_code(Editor, Line,Status); _Point -> ignore end end, WinInfo#winInfo.breaks), - case Flag of existing -> WinInfo#winInfo{editor={Mod, Editor}}; @@ -485,7 +483,7 @@ show_code(WinInfo, Mod, Contents) -> editors=[{Mod, Editor} | Editors]} end. -show_code(Editor, Text) when length(Text)>1500 -> +show_code(Editor, Text) when length(Text) > 1500 -> %% Add some text at a time so that other processes may get scheduled Str = string:sub_string(Text, 1, 1500), config_editor(Editor, {insert,{'end', Str}}), @@ -494,21 +492,19 @@ show_code(Editor, Text) -> config_editor(Editor, {insert,{'end',Text}}). show_no_code(WinInfo) -> - {value, {'$top', Editor}} = - lists:keysearch('$top', 1, WinInfo#winInfo.editors), + {'$top', Editor} = lists:keyfind('$top', 1, WinInfo#winInfo.editors), gs:config(Editor, raise), WinInfo#winInfo{editor={'$top', Editor}}. remove_code(WinInfo, Mod) -> Editors = WinInfo#winInfo.editors, - case lists:keysearch(Mod, 1, Editors) of - {value, {Mod, Editor}} -> + case lists:keyfind(Mod, 1, Editors) of + {Mod, Editor} -> gs:destroy(Editor), WinInfo#winInfo{editors=lists:keydelete(Mod, 1, Editors)}; false -> WinInfo end. - %%-------------------------------------------------------------------- %% mark_line(WinInfo, Line, How) -> WinInfo @@ -522,7 +518,7 @@ mark_line(WinInfo, Line, How) -> mark_line2(Editor, WinInfo#winInfo.marked_line, false), mark_line2(Editor, Line, How), if - Line/=0 -> config_editor(Editor, {vscrollpos, Line-5}); + Line =/= 0 -> config_editor(Editor, {vscrollpos, Line-5}); true -> ignore end, WinInfo#winInfo{marked_line=Line}. @@ -537,7 +533,7 @@ mark_line2(Editor, Line, How) -> false -> " " end, Font = if - How==false -> dbg_ui_win:font(normal); + How =:= false -> dbg_ui_win:font(normal); true -> dbg_ui_win:font(bold) end, config_editor(Editor, [{overwrite, {{Line,5}, Prefix}}, @@ -558,10 +554,10 @@ select_line(WinInfo, Line) -> %% help window, it must be checked that it is correct Size = gs:read(Editor, size), if - Line==0 -> + Line =:= 0 -> select_line(Editor, WinInfo#winInfo.selected_line, false), WinInfo#winInfo{selected_line=0}; - Line<Size -> + Line < Size -> select_line(Editor, Line, true), config_editor(Editor, {vscrollpos, Line-5}), WinInfo#winInfo{selected_line=Line}; @@ -712,10 +708,10 @@ handle_event({gs, Editor, buttonpress, code_editor, _Arg}, WinInfo) -> {Row, _} -> {Mod, _Editor} = WinInfo#winInfo.editor, Point = {Mod, Row}, - case lists:keysearch(Point, #breakInfo.point, + case lists:keymember(Point, #breakInfo.point, WinInfo#winInfo.breaks) of - {value, _BreakInfo} -> {break, Point, delete}; - false -> {break, Point, add} + false -> {break, Point, add}; + true -> {break, Point, delete} end; {Row2, _} -> select_line(Editor, Row2, true), @@ -776,7 +772,7 @@ code_editor() -> code_editor(Name, W, H) -> Editor = if - Name==null -> gs:editor('CodeArea', []); + Name =:= null -> gs:editor('CodeArea', []); true -> gs:editor(Name, 'CodeArea', []) end, gs:config(Editor, [{x,5}, {y,30}, {width,W}, {height,H}, @@ -814,8 +810,8 @@ buttons() -> {'Where','WhereButton'}, {'Up','UpButton'}, {'Down','DownButton'}]. is_button(Name) -> - case lists:keysearch(Name, 1, buttons()) of - {value, {Name, Button}} -> {true, Button}; + case lists:keyfind(Name, 1, buttons()) of + {Name, Button} -> {true, Button}; false -> false end. @@ -847,7 +843,7 @@ resize_button_area(open, width, Diff) -> eval_area({Ev,Bi}, X, Y, FrameOpts, Win) -> {W,H} = if - Ev==open -> {289,200}; + Ev =:= open -> {289,200}; true -> {0,0} end, Font = dbg_ui_win:font(normal), @@ -870,7 +866,7 @@ eval_area({Ev,Bi}, X, Y, FrameOpts, Win) -> {font_style,{{{1,0},'end'},Font}}]), gs:config('EvalEditor', {enable, false}), if - Ev==open, Bi==close -> resize_eval_area(Ev, width, 257); + Ev =:= open, Bi =:= close -> resize_eval_area(Ev, width, 257); true -> ignore end. @@ -891,7 +887,7 @@ resize_eval_area(open, Key, Diff) -> bind_area({Ev,Bi}, X, Y, FrameOpts, Win) -> {W,H} = if - Bi==open -> {249,200}; + Bi =:= open -> {249,200}; true -> {0,0} end, gs:frame('BindArea', Win, @@ -908,7 +904,7 @@ bind_area({Ev,Bi}, X, Y, FrameOpts, Win) -> {text,{1,"Name"}}, {text,{2,"Value"}}, {font,Font}]), gs:config('BindGrid', {rows,{1,1}}), if - Bi==open, Ev==close -> resize_bind_area(Bi, width, 297); + Bi =:= open, Ev =:= close -> resize_bind_area(Bi, width, 297); true -> ignore end. @@ -993,15 +989,15 @@ resizebar(Flag, Name, X, Y, W, H, Obj) -> rb1({_Bu,Ev,Bi,Tr}) -> if - Ev==close, Bi==close, Tr==close -> close; + Ev =:= close, Bi =:= close, Tr =:= close -> close; true -> open end. rb2({_Bu,Ev,Bi,Tr}) -> if - Tr==open -> + Tr =:= open -> if - Ev==close, Bi==close -> close; + Ev =:= close, Bi =:= close -> close; true -> open end; true -> close @@ -1009,7 +1005,7 @@ rb2({_Bu,Ev,Bi,Tr}) -> rb3({_Bu,Ev,Bi,_Tr}) -> if - Ev==open, Bi==open -> open; + Ev =:= open, Bi =:= open -> open; true -> close end. @@ -1067,7 +1063,7 @@ configure(WinInfo, NewW, NewH) -> %% Adjust width unless it is unchanged or less than minimum width if - OldW/=NewW -> + OldW =/= NewW -> {Dcode,Deval,Dbind} = configure_widths(OldW,NewW,Flags), resize_code_area(WinInfo, width, Dcode), case rb1(Flags) of @@ -1090,7 +1086,7 @@ configure(WinInfo, NewW, NewH) -> %% Adjust height unless it is unchanged or less than minimum height if - OldH/=NewH -> + OldH =/= NewH -> {Dcode2,Deval2,Dtrace} = configure_heights(OldH,NewH,Flags), resize_code_area(WinInfo, height, Dcode2), resize_eval_area(Ev, height, Deval2), @@ -1117,11 +1113,11 @@ configure_widths(OldW, NewW, Flags) -> %% Check how much the frames can be resized in reality Limits = if %% Window larger - NewW>OldW -> + NewW > OldW -> if - Ev==open,Bi==open -> {0,Diff,Diff}; - Ev==open -> {0,Diff,0}; - Bi==open -> {0,0,Diff}; + Ev =:= open, Bi =:= open -> {0,Diff,Diff}; + Ev =:= open -> {0,Diff,0}; + Bi =:= open -> {0,0,Diff}; true -> {Diff,0,0} end; @@ -1129,12 +1125,12 @@ configure_widths(OldW, NewW, Flags) -> %% and current size OldW>NewW -> if - Ev==open,Bi==open -> + Ev =:= open, Bi =:= open -> {0, gs:read('EvalArea',width)-204, gs:read('BindArea',width)-112}; - Ev==open -> {0,Diff,0}; - Bi==open -> {0,0,Diff}; + Ev =:= open -> {0,Diff,0}; + Bi =:= open -> {0,0,Diff}; true -> {Diff,0,0} end end, @@ -1142,13 +1138,13 @@ configure_widths(OldW, NewW, Flags) -> case Limits of %% No Shell or Bind frame, larger window - {T,0,0} when NewW>OldW -> {T,0,0}; + {T,0,0} when NewW > OldW -> {T,0,0}; %% No Shell or Bind frame, smaller window - {T,0,0} when OldW>NewW -> {-T,0,0}; + {T,0,0} when OldW > NewW -> {-T,0,0}; %% Window larger; divide Diff among the frames and return result - {_,Sf,B} when NewW>OldW -> + {_,Sf,B} when NewW > OldW -> {_,Sf2,B2} = divide([{0,0},{0,Sf},{0,B}],Diff), {Sf2+B2,Sf2,B2}; @@ -1171,33 +1167,33 @@ configure_heights(OldH, NewH, Flags) -> %% Check how much the frames can be resized in reality {T,Sf,Ff} = if %% Window larger - NewH>OldH -> + NewH > OldH -> {Diff, if - Ev==close, Bi==close -> 0; + Ev =:= close, Bi =:= close -> 0; true -> Diff end, if - Tr==open -> Diff; + Tr =:= open -> Diff; true -> 0 end}; %% Window smaller; get difference between min size %% and current size - OldH>NewH -> + OldH > NewH -> {gs:read('CodeArea',height)-100, if - Ev==close, Bi==close -> 0; + Ev =:= close, Bi =:= close -> 0; true -> if - Ev==open -> + Ev =:= open -> gs:read('EvalArea',height)-100; - Bi==open -> + Bi =:= open -> gs:read('BindArea',height)-100 end end, if - Tr==open -> gs:read('TraceArea',height)-100; + Tr =:= open -> gs:read('TraceArea',height)-100; true -> 0 end} end, @@ -1251,10 +1247,8 @@ divide(L, Diff) -> if %% All of Diff has been distributed - D==0 -> {T,S,F}; - + D =:= 0 -> {T,S,F}; true -> - %% For each element, try to add as much as possible of D {NewT,Dt} = divide2(D,T,Tmax), {NewS,Ds} = divide2(D,S,Smax), @@ -1296,25 +1290,25 @@ resize(WinInfo, ResizeBar) -> rblimits('RB2',W,H), rblimits('RB3',W,H)). -resizeloop(WI, RB, Prev, {Min1,Max1},{Min2,Max2},{Min3,Max3}) -> +resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}) -> receive - {gs,_,motion,_,[_,Y]} when RB=='RB1', Y>Min1,Y<Max1 -> + {gs,_,motion,_,[_,Y]} when RB =:= 'RB1', Y > Min1, Y < Max1 -> gs:config('RB1', {y,Y}), - resizeloop(WI, RB, Y, {Min1,Max1},{Min2,Max2},{Min3,Max3}); - {gs,_,motion,_,_} when RB=='RB1' -> - resizeloop(WI, RB, Prev,{Min1,Max1},{Min2,Max2},{Min3,Max3}); + resizeloop(WI, RB, Y, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); + {gs,_,motion,_,_} when RB =:= 'RB1' -> + resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); - {gs,_,motion,_,[_,Y]} when RB=='RB2', Y>Min2,Y<Max2 -> + {gs,_,motion,_,[_,Y]} when RB =:= 'RB2', Y > Min2, Y < Max2 -> gs:config('RB2', {y,Y}), - resizeloop(WI, RB, Y, {Min1,Max1},{Min2,Max2},{Min3,Max3}); - {gs,_,motion,_,_} when RB=='RB2' -> - resizeloop(WI, RB, Prev,{Min1,Max1},{Min2,Max2},{Min3,Max3}); + resizeloop(WI, RB, Y, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); + {gs,_,motion,_,_} when RB =:= 'RB2' -> + resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); - {gs,_,motion,_,[X,_]} when RB=='RB3', X>Min3,X<Max3 -> + {gs,_,motion,_,[X,_]} when RB =:= 'RB3', X > Min3, X < Max3 -> gs:config('RB3', {x,X}), - resizeloop(WI, RB, X, {Min1,Max1},{Min2,Max2},{Min3,Max3}); - {gs,_,motion,_,_} when RB=='RB3' -> - resizeloop(WI, RB, Prev,{Min1,Max1},{Min2,Max2},{Min3,Max3}); + resizeloop(WI, RB, X, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); + {gs,_,motion,_,_} when RB =:= 'RB3' -> + resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}); {gs,_,buttonrelease,_,_} -> resize_win(WI, RB, Prev) @@ -1329,7 +1323,7 @@ resize_win(WinInfo, 'RB1', Y) -> %% Resize Code, Evaluator and Binding areas resize_code_area(WinInfo, height, -Diff), if - S==close, Bi==close, F==open -> + S =:= close, Bi =:= close, F =:= open -> resize_trace_area(open, height, Diff); true -> resize_eval_area(S, height, Diff), @@ -1388,7 +1382,7 @@ rblimits('RB1',_W,H) -> RB2 = gs:read('RB2',height), FF = gs:read('TraceArea',height), Max = case RB2 of - 0 when FF/=0 -> + 0 when FF =/= 0 -> H-112; _ -> Y = gs:read('RB2',y), @@ -1397,18 +1391,14 @@ rblimits('RB1',_W,H) -> {Min,Max}; rblimits('RB2',_W,H) -> - - %% TraceFrame should not have height <100 + %% TraceFrame should not have height < 100 Max = H-112, - %% Min is decided by a minimum distance to 'RB1' Y = gs:read('RB1',y), Min = erlang:min(Max,Y+140), - {Min,Max}; rblimits('RB3',W,_H) -> - %% Neither CodeArea nor BindArea should occupy %% less than 1/3 of the total window width and EvalFrame should %% be at least 289 pixels wide @@ -1484,7 +1474,7 @@ helpwin_action(gotoline, default, AttPid, _Editor, Data, Win) -> end, Data; helpwin_action(search, case_sensitive, _AttPid, _Ed, {Pos, CS}, _Win) -> - Bool = if CS==true -> false; CS==false -> true end, + Bool = if CS =:= true -> false; CS =:= false -> true end, {Pos, Bool}; helpwin_action(search, default, _AttPid, Editor, {Pos, CS}, Win) -> gs:config(lbl(Win), {label, {text, ""}}), @@ -1517,13 +1507,9 @@ search(Str, Editor, Max, {Row, Col}, CS) -> lowercase(true, Str) -> Str; lowercase(false, Str) -> - lists:map(fun(Char) -> - if - Char>=$A, Char=<$Z -> Char+32; - true -> Char - end - end, - Str). + [if Char >= $A, Char =< $Z -> Char+32; + true -> Char + end || Char <- Str]. mark_string(Editor, {Row, Col}, Str) -> Between = {{Row,Col}, {Row,Col+length(Str)}}, @@ -1540,10 +1526,9 @@ unmark_string(Editor, {Row, Col}) -> {fg, {Between, black}}]). helpwin(Type, GS, {X, Y}) -> - W = 200, Pad=10, Wbtn = 50, + W = 200, Pad = 10, Wbtn = 50, - Title = - case Type of search -> "Search"; gotoline -> "Go To Line" end, + Title = case Type of search -> "Search"; gotoline -> "Go To Line" end, Win = gs:window(GS, [{title, Title}, {x, X}, {y, Y}, {width, W}, {destroy, true}]), diff --git a/lib/debugger/src/dbg_ui_view.erl b/lib/debugger/src/dbg_ui_view.erl index 075275f196..7350a830a8 100644 --- a/lib/debugger/src/dbg_ui_view.erl +++ b/lib/debugger/src/dbg_ui_view.erl @@ -21,9 +21,6 @@ %% External exports -export([start/2]). -%% Internal exports --export([init/3]). - -record(state, {gs, % term() Graphics system id win, % term() Attach process window data coords, % {X,Y} Mouse point position @@ -42,7 +39,7 @@ start(GS, Mod) -> Title = "View Module " ++ atom_to_list(Mod), case dbg_ui_winman:is_started(Title) of true -> ignore; - false -> spawn(?MODULE, init, [GS, Mod, Title]) + false -> spawn(fun () -> init(GS, Mod, Title) end) end. @@ -51,7 +48,6 @@ start(GS, Mod) -> %%==================================================================== init(GS, Mod, Title) -> - %% Subscribe to messages from the interpreter int:subscribe(), diff --git a/lib/debugger/src/dbg_ui_win.erl b/lib/debugger/src/dbg_ui_win.erl index 74ff2503ab..9bed6a1ec5 100644 --- a/lib/debugger/src/dbg_ui_win.erl +++ b/lib/debugger/src/dbg_ui_win.erl @@ -24,7 +24,6 @@ create_menus/2, select/2, selected/1, add_break/2, update_break/2, delete_break/1, motion/2 - ]). -record(break, {mb, smi, emi, dimi, demi}). @@ -49,11 +48,11 @@ init() -> font(Style) -> GS = init(), Style2 = if - Style==normal -> []; + Style =:= normal -> []; true -> [Style] end, case gs:read(GS, {choose_font, {screen,Style2,12}}) of - Font when element(1, Font)==screen -> + Font when element(1, Font) =:= screen -> Font; _ -> gs:read(GS, {choose_font, {courier,Style2,12}}) @@ -168,8 +167,7 @@ select(MenuItem, Bool) -> %%-------------------------------------------------------------------- selected(Menu) -> Children = gs:read(Menu, children), - Selected = lists:filter(fun(Child) -> gs:read(Child, select) end, - Children), + Selected = [gs:read(Child, select) || Child <- Children], lists:map(fun(Child) -> {text, Name} = gs:read(Child, label), list_to_atom(Name) diff --git a/lib/debugger/src/dbg_ui_winman.erl b/lib/debugger/src/dbg_ui_winman.erl index 71023cd0d6..398735a7ca 100644 --- a/lib/debugger/src/dbg_ui_winman.erl +++ b/lib/debugger/src/dbg_ui_winman.erl @@ -120,9 +120,9 @@ init(_Arg) -> {ok, #state{}}. handle_call({is_started, Title}, _From, State) -> - Reply = case lists:keysearch(Title, #win.title, State#state.wins) of - {value, Win} -> {true, Win#win.win}; - false -> false + Reply = case lists:keyfind(Title, #win.title, State#state.wins) of + false -> false; + Win -> {true, Win#win.win} end, {reply, Reply, State}. @@ -134,8 +134,8 @@ handle_cast({insert, Pid, Title, Win}, State) -> handle_cast({clear_process, Title}, State) -> OldWins = State#state.wins, - Wins = case lists:keysearch(Title, #win.title, OldWins) of - {value, #win{owner=Pid}} -> + Wins = case lists:keyfind(Title, #win.title, OldWins) of + #win{owner=Pid} -> Msg = {dbg_ui_winman, destroy}, Pid ! Msg, lists:keydelete(Title, #win.title, OldWins); @@ -147,7 +147,7 @@ handle_cast({clear_process, Title}, State) -> handle_info({'EXIT', Pid, _Reason}, State) -> [Mon | _Wins] = State#state.wins, if - Pid==Mon#win.owner -> {stop, normal, State}; + Pid =:= Mon#win.owner -> {stop, normal, State}; true -> Wins2 = lists:keydelete(Pid, #win.owner, State#state.wins), inform_all(Wins2), diff --git a/lib/debugger/src/dbg_wx_break_win.erl b/lib/debugger/src/dbg_wx_break_win.erl index 5dafb0fbe6..78733c98c8 100644 --- a/lib/debugger/src/dbg_wx_break_win.erl +++ b/lib/debugger/src/dbg_wx_break_win.erl @@ -206,11 +206,7 @@ handle_event(#wx{id=OKorListBox, event=#wxCommand{type=OkorDoubleClick}}, OkorDoubleClick =:= command_listbox_doubleclicked -> Mod = wxComboBox:getValue(Text), {_, IndexL} = wxListBox:getSelections(LB), - Breaks = lists:map(fun(Index) -> - Func = lists:nth(Index+1, Funcs), - [list_to_atom(Mod) | Func] - end, - IndexL), + Breaks = [[list_to_atom(Mod)|lists:nth(Index+1, Funcs)] || Index <- IndexL], wxDialog:destroy(Win), {break, Breaks, enable}; handle_event(#wx{id=?wxID_OK},#winInfo{win=Win,text=Text, entries=Es, trigger=Trigger}) -> diff --git a/lib/debugger/src/dbg_wx_interpret.erl b/lib/debugger/src/dbg_wx_interpret.erl index f711ba679d..ffcfbcf36b 100644 --- a/lib/debugger/src/dbg_wx_interpret.erl +++ b/lib/debugger/src/dbg_wx_interpret.erl @@ -115,11 +115,11 @@ interpret_all(Dir, [File0|Files], Mode, Window, Errors) -> interpret_all(_Dir, [], _Mode, _Window, []) -> true; interpret_all(Dir, [], _Mode, Window, Errors) -> - Msg = lists:map(fun(Name) -> - File = filename:join(Dir, Name), - Error = format_error(int:interpretable(File)), - ["\n ",Name,": ",Error] - end, Errors), + Msg = [begin + File = filename:join(Dir, Name), + Error = format_error(int:interpretable(File)), + ["\n ",Name,": ",Error] + end || Name <- Errors], All = ["Error when interpreting: ", Msg], dbg_wx_win:confirm(Window, lists:flatten(All)), true. diff --git a/lib/debugger/src/dbg_wx_mon.erl b/lib/debugger/src/dbg_wx_mon.erl index 3f55c38d35..6bdec994b1 100644 --- a/lib/debugger/src/dbg_wx_mon.erl +++ b/lib/debugger/src/dbg_wx_mon.erl @@ -170,7 +170,7 @@ init2(CallingPid, Mode, SFile, GS) -> CallingPid ! {initialization_complete, self()}, if - SFile==default -> + SFile =:= default -> loop(State3); true -> loop(load_settings(SFile, State3)) @@ -268,7 +268,7 @@ gui_cmd(ignore, State) -> State; gui_cmd(stopped, State) -> if - State#state.starter==true -> int:stop(); + State#state.starter =:= true -> int:stop(); true -> int:auto_attach(false) end, exit(stop); @@ -420,9 +420,9 @@ gui_cmd({'Trace Window', TraceWin}, State) -> State2; gui_cmd({'Auto Attach', When}, State) -> if - When==[] -> int:auto_attach(false); + When =:= [] -> int:auto_attach(false); true -> - Flags = lists:map(fun(Name) -> map(Name) end, When), + Flags = [map(Name) || Name <- When], int:auto_attach(Flags, trace_function(State)) end, State; @@ -691,12 +691,12 @@ load_settings2(Settings, State) -> Break, int:break(Mod, Line), if - Status==inactive -> + Status =:= inactive -> int:disable_break(Mod, Line); true -> ignore end, if - Action/=enable -> + Action =/= enable -> int:action_at_break(Mod,Line,Action); true -> ignore end, @@ -715,10 +715,7 @@ save_settings(SFile, State) -> int:auto_attach(), int:stack_trace(), State#state.backtrace, - lists:map(fun(Mod) -> - int:file(Mod) - end, - int:interpreted()), + [int:file(Mod) || Mod <- int:interpreted()], int:all_breaks()}, Binary = term_to_binary({debugger_settings, Settings}), @@ -741,7 +738,7 @@ registered_name(Pid) -> Node = node(Pid), if - Node==node() -> + Node =:= node() -> case erlang:process_info(Pid, registered_name) of {registered_name, Name} -> Name; _ -> undefined diff --git a/lib/debugger/src/dbg_wx_mon_win.erl b/lib/debugger/src/dbg_wx_mon_win.erl index 8ad4f4213f..04c3501b8c 100644 --- a/lib/debugger/src/dbg_wx_mon_win.erl +++ b/lib/debugger/src/dbg_wx_mon_win.erl @@ -266,8 +266,7 @@ select(MenuItem, Bool) -> add_module(WinInfo, MenuName, Mod) -> Win = WinInfo#winInfo.window, Modules = WinInfo#winInfo.modules, - case lists:keysearch(Mod, #moduleInfo.module, Modules) of - {value, _ModInfo} -> WinInfo; + case lists:keymember(Mod, #moduleInfo.module, Modules) of false -> %% Create a menu for the module Menu = get(MenuName), @@ -284,8 +283,9 @@ add_module(WinInfo, MenuName, Mod) -> wxListBox:append(WinInfo#winInfo.listbox, atom_to_list(Mod)), ModInfo = #moduleInfo{module=Mod, menubtn={Menu,MenuBtn}}, - WinInfo#winInfo{modules=[ModInfo | Modules]} - end. + WinInfo#winInfo{modules=[ModInfo | Modules]}; + true -> WinInfo + end. %%-------------------------------------------------------------------- %% delete_module(WinInfo, Mod) -> WinInfo @@ -559,8 +559,7 @@ handle_event(#wx{event=#wxCommand{type=command_checkbox_clicked}}, handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Row}}, WinInfo) -> #winInfo{processes=Pids} = WinInfo, - {value, #procInfo{pid=Pid}} = - lists:keysearch(Row, #procInfo.row, Pids), + #procInfo{pid=Pid} = lists:keyfind(Row, #procInfo.row, Pids), {focus, Pid, WinInfo#winInfo{focus=Row}}; handle_event(#wx{event=#wxList{type=command_list_item_activated}}, _WinInfo) -> diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl index f9fdf593c4..6675ea33e7 100644 --- a/lib/debugger/src/dbg_wx_trace.erl +++ b/lib/debugger/src/dbg_wx_trace.erl @@ -180,12 +180,12 @@ init_contents(Breaks, State) -> State#state{win=Win}. -loop(#state{meta=Meta} = State) -> +loop(#state{meta=Meta, win=Win} = State) -> receive %% From the GUI main window - GuiEvent when element(1, GuiEvent)==wx -> + GuiEvent when element(1, GuiEvent) =:= wx -> Cmd = wx:batch(fun() -> - dbg_wx_trace_win:handle_event(GuiEvent,State#state.win) + dbg_wx_trace_win:handle_event(GuiEvent,Win) end), State2 = gui_cmd(Cmd, State), loop(State2); @@ -211,11 +211,11 @@ loop(#state{meta=Meta} = State) -> %% From the dbg_wx_winman process (Debugger window manager) {dbg_ui_winman, update_windows_menu, Data} -> - Window = dbg_wx_trace_win:get_window(State#state.win), + Window = dbg_wx_trace_win:get_window(Win), dbg_wx_winman:update_windows_menu(Window,Data), loop(State); {dbg_ui_winman, destroy} -> - dbg_wx_trace_win:stop(State#state.win), + dbg_wx_trace_win:stop(Win), exit(stop) end. @@ -269,7 +269,7 @@ gui_cmd('Continue', State) -> int:meta(State#state.meta, continue), {Status, Mod, Line} = State#state.status, if - Status==wait_break -> + Status =:= wait_break -> Win = dbg_wx_trace_win:unmark_line(State#state.win), gui_enable_functions(wait_running), State#state{win=Win, status={wait_running,Mod,Line}}; @@ -291,7 +291,7 @@ gui_cmd('Stop', State) -> int:meta(State#state.meta, stop), {Status, Mod, Line} = State#state.status, if - Status==wait_running -> + Status =:= wait_running -> Win = dbg_wx_trace_win:mark_line(State#state.win, Line, break), gui_enable_functions(wait_break), @@ -421,7 +421,7 @@ gui_cmd('Function Break...', State) -> gui_cmd('Enable All', State) -> Breaks = int:all_breaks(), ThisMod = State#state.cm, - lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod -> + lists:foreach(fun ({{Mod, Line}, _Options}) when Mod =:= ThisMod -> int:enable_break(Mod, Line); (_Break) -> ignore @@ -431,7 +431,7 @@ gui_cmd('Enable All', State) -> gui_cmd('Disable All', State) -> Breaks = int:all_breaks(), ThisMod = State#state.cm, - lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod -> + lists:foreach(fun ({{Mod, Line}, _Options}) when Mod =:= ThisMod -> int:disable_break(Mod, Line); (_Break) -> ignore @@ -458,7 +458,7 @@ gui_cmd({'Trace Window', TraceWin}, State) -> Win = dbg_wx_trace_win:configure(State#state.win, TraceWin), {Status,_,_} = State#state.status, if - Status==break; Status==wait_break -> + Status =:= break; Status =:= wait_break -> gui_enable_btrace(Trace, State#state.stack_trace); true -> ignore end, @@ -467,7 +467,7 @@ gui_cmd({'Stack Trace', [Name]}, State) -> int:meta(State#state.meta, stack_trace, map(Name)), {Status,_,_} = State#state.status, if - Status==break; Status==wait_break -> + Status =:= break; Status =:= wait_break -> gui_enable_btrace(State#state.trace, map(Name)); true -> ignore end, @@ -490,9 +490,9 @@ gui_cmd('Debugger', State) -> gui_cmd({user_command, Cmd}, State) -> {Status, _Mod, _Line} = State#state.status, if - Status==break; - Status==wait_break; - Status==wait_running -> + Status =:= break; + Status =:= wait_break; + Status =:= wait_running -> Cm = State#state.cm, Arg = case State#state.stack of {Cur, Max} when Cur<Max -> {Cm, Cmd, Cur}; @@ -531,14 +531,14 @@ add_break(WI, Coords, Type, Mod, Line) -> int_cmd({interpret, Mod}, State) -> if - Mod==State#state.cm -> + Mod =:= State#state.cm -> State#state{cm_obsolete=true}; true -> State end; int_cmd({no_interpret, Mod}, State) -> if - Mod==State#state.cm -> + Mod =:= State#state.cm -> State#state{cm_obsolete=true}; true -> Win = dbg_wx_trace_win:remove_code(State#state.win, Mod), @@ -584,7 +584,7 @@ meta_cmd({re_entry, dbg_ieval, eval_fun}, State) -> meta_cmd({re_entry, Mod, _Func}, State) -> Obs = State#state.cm_obsolete, case State#state.cm of - Mod when Obs==true -> + Mod when Obs =:= true -> Win = gui_load_module(State#state.win, Mod,State#state.pid), State#state{win=Win, cm_obsolete=false}; Mod -> State; @@ -630,11 +630,11 @@ meta_cmd({func_at, Mod, Line, Cur}, State) -> gui_enable_functions(idle), dbg_wx_trace_win:display(State#state.win, idle), State#state{win=Win, cm=Mod, status={idle,Mod,Line}, stack=Stack}; -meta_cmd({wait_at, Mod, Line, Cur}, #state{status={Status,_,_}}=State) - when Status/=init, Status/=break -> +meta_cmd({wait_at, Mod, Line, Cur}, #state{status={Status,_,_}, win=Win}=State) + when Status =/= init, Status =/= break -> Stack = {Cur,Cur}, gui_enable_functions(wait_running), - dbg_wx_trace_win:display(State#state.win, {wait,Mod,Line}), + dbg_wx_trace_win:display(Win, {wait,Mod,Line}), State#state{status={wait_running,Mod,Line}, stack=Stack}; meta_cmd({wait_at, Mod, Line, Cur}, State) -> Stack = {Cur,Cur}, @@ -675,7 +675,7 @@ meta_cmd({stack_trace, Flag}, State) -> gui_enable_updown(Flag, State#state.stack), {Status,_,_} = State#state.status, if - Status==break; Status==wait_break -> + Status =:= break; Status =:= wait_break -> gui_enable_btrace(State#state.trace, Flag); true -> ignore end, @@ -813,7 +813,7 @@ gui_enable_functions(Status) -> gui_enable_updown(Flag, Stack) -> {Enable, Disable} = if - Flag==false -> {[], ['Up', 'Down']}; + Flag =:= false -> {[], ['Up', 'Down']}; true -> case Stack of {1,1} -> {[], ['Up', 'Down']}; @@ -826,14 +826,14 @@ gui_enable_updown(Flag, Stack) -> dbg_wx_trace_win:enable(Enable, true), dbg_wx_trace_win:enable(Disable, false), if - Enable==[] -> dbg_wx_trace_win:enable(['Where'], false); + Enable =:= [] -> dbg_wx_trace_win:enable(['Where'], false); true -> dbg_wx_trace_win:enable(['Where'], true) end. gui_enable_btrace(Trace, StackTrace) -> Bool = if - Trace==false -> false; - StackTrace==false -> false; + Trace =:= false -> false; + StackTrace =:= false -> false; true -> true end, dbg_wx_trace_win:enable(['Back Trace'], Bool). diff --git a/lib/debugger/src/dbg_wx_trace_win.erl b/lib/debugger/src/dbg_wx_trace_win.erl index 2b4a1164ad..720b913024 100755 --- a/lib/debugger/src/dbg_wx_trace_win.erl +++ b/lib/debugger/src/dbg_wx_trace_win.erl @@ -410,11 +410,11 @@ clear_breaks(WinInfo) -> clear_breaks(WinInfo, all). clear_breaks(WinInfo, Mod) -> Remove = if - Mod==all -> WinInfo#winInfo.breaks; + Mod =:= all -> WinInfo#winInfo.breaks; true -> lists:filter(fun(#breakInfo{point={Mod2,_L}}) -> if - Mod2==Mod -> true; + Mod2 =:= Mod -> true; true -> false end end, @@ -481,8 +481,8 @@ display(#winInfo{window=Win, sb=Sb},Arg) -> %% Note: remove_code/2 should not be used for currently shown module. %%-------------------------------------------------------------------- is_shown(WinInfo, Mod) -> - case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of - {value, {Mod, Editor}} -> + case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of + {Mod, Editor} -> gs:config(Editor, raise), %% BUGBUG {true, WinInfo#winInfo{editor={Mod, Editor}}}; false -> false @@ -494,7 +494,7 @@ show_code(WinInfo = #winInfo{editor={_, Ed}}, Mod, Contents) -> lists:foreach(fun(BreakInfo) -> case BreakInfo#breakInfo.point of - {Mod2, Line} when Mod2==Mod -> + {Mod2, Line} when Mod2 =:= Mod -> Status = BreakInfo#breakInfo.status, dbg_wx_code:add_break_to_code(Ed, Line,Status); _Point -> ignore @@ -540,10 +540,10 @@ select_line(WinInfo, Line) -> %% help window, it must be checked that it is correct Size = dbg_wx_code:get_no_lines(Ed), if - Line==0 -> + Line =:= 0 -> dbg_wx_code:goto_line(Ed,1), WinInfo#winInfo{selected_line=0}; - Line<Size -> + Line < Size -> dbg_wx_code:goto_line(Ed,Line), WinInfo#winInfo{selected_line=Line}; true -> @@ -764,8 +764,8 @@ handle_event(#wx{event=#wxStyledText{type=stc_doubleclick}}, WinInfo = #winInfo{editor={Mod,Ed}}) -> Line = wxStyledTextCtrl:getCurrentLine(Ed), Point = {Mod, Line+1}, - case lists:keysearch(Point, #breakInfo.point,WinInfo#winInfo.breaks) of - {value, _BreakInfo} -> {break, Point, delete}; + case lists:keymember(Point, #breakInfo.point, WinInfo#winInfo.breaks) of + true -> {break, Point, delete}; false -> {break, Point, add} end; @@ -837,7 +837,7 @@ handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{cmdString=Str}}, %% Button area handle_event(#wx{id=ID, event=#wxCommand{type=command_button_clicked}},_Wi) -> - {value, {Button, _}} = lists:keysearch(ID, 2, buttons()), + {Button, _} = lists:keyfind(ID, 2, buttons()), Button; %% Evaluator area @@ -908,8 +908,8 @@ buttons() -> {'Where',?WhereButton}, {'Up',?UpButton}, {'Down',?DownButton}]. is_button(Name) -> - case lists:keysearch(Name, 1, buttons()) of - {value, {Name, Button}} -> {true, Button}; + case lists:keyfind(Name, 1, buttons()) of + {Name, Button} -> {true, Button}; false -> false end. diff --git a/lib/debugger/src/dbg_wx_view.erl b/lib/debugger/src/dbg_wx_view.erl index 6d34e5650c..8ff89a4847 100644 --- a/lib/debugger/src/dbg_wx_view.erl +++ b/lib/debugger/src/dbg_wx_view.erl @@ -23,9 +23,6 @@ %% External exports -export([start/2]). -%% Internal exports --export([init/4]). - -record(state, {gs, % term() Graphics system id win, % term() Attach process window data coords, % {X,Y} Mouse point position @@ -46,7 +43,7 @@ start(GS, Mod) -> true -> ignore; false -> Env = wx:get_env(), - spawn_link(?MODULE, init, [GS, Env, Mod, Title]) + spawn_link(fun () -> init(GS, Env, Mod, Title) end) end. @@ -84,7 +81,7 @@ loop(State) -> receive %% From the GUI main window - GuiEvent when element(1, GuiEvent)==wx -> + GuiEvent when element(1, GuiEvent) =:= wx -> Cmd = wx:batch(fun() -> dbg_wx_trace_win:handle_event(GuiEvent, State#state.win) end), @@ -167,7 +164,7 @@ gui_cmd('Function Break...', State) -> gui_cmd('Enable All', State) -> Breaks = int:all_breaks(), ThisMod = State#state.mod, - lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod -> + lists:foreach(fun ({{Mod, Line}, _Options}) when Mod =:= ThisMod -> int:enable_break(Mod, Line); (_Break) -> ignore @@ -177,7 +174,7 @@ gui_cmd('Enable All', State) -> gui_cmd('Disable All', State) -> Breaks = int:all_breaks(), ThisMod = State#state.mod, - lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod -> + lists:foreach(fun ({{Mod, Line}, _Options}) when Mod =:= ThisMod -> int:disable_break(Mod, Line); (_Break) -> ignore @@ -214,21 +211,19 @@ add_break(GS, Coords, Type, Mod, Line) -> %%--Commands from the interpreter------------------------------------- -int_cmd({new_break, {{Mod,_Line},_Options}=Break}, #state{mod=Mod}=State) -> - Win = dbg_wx_trace_win:add_break(State#state.win, 'Break', Break), - State#state{win=Win}; -int_cmd({delete_break, {Mod,_Line}=Point}, #state{mod=Mod}=State) -> - Win = dbg_wx_trace_win:delete_break(State#state.win, Point), - State#state{win=Win}; -int_cmd({break_options, {{Mod,_Line},_Options}=Break}, #state{mod=Mod}=State) -> - Win = dbg_wx_trace_win:update_break(State#state.win, Break), - State#state{win=Win}; -int_cmd(no_break, State) -> - Win = dbg_wx_trace_win:clear_breaks(State#state.win), - State#state{win=Win}; -int_cmd({no_break, _Mod}, State) -> - Win = dbg_wx_trace_win:clear_breaks(State#state.win), - State#state{win=Win}; +int_cmd({new_break, {{Mod,_Line},_Options}=Break}, + #state{mod = Mod, win = Win}=State) -> + State#state{win = dbg_wx_trace_win:add_break(Win, 'Break', Break)}; +int_cmd({delete_break, {Mod,_Line}=Point}, + #state{mod = Mod, win = Win}=State) -> + State#state{win = dbg_wx_trace_win:delete_break(Win, Point)}; +int_cmd({break_options, {{Mod,_Line},_Options}=Break}, + #state{mod = Mod, win = Win}=State) -> + State#state{win = dbg_wx_trace_win:update_break(Win, Break)}; +int_cmd(no_break, #state{win = Win}=State) -> + State#state{win = dbg_wx_trace_win:clear_breaks(Win)}; +int_cmd({no_break, _Mod}, #state{win = Win}=State) -> + State#state{win = dbg_wx_trace_win:clear_breaks(Win)}; int_cmd(_, State) -> State. diff --git a/lib/debugger/src/dbg_wx_winman.erl b/lib/debugger/src/dbg_wx_winman.erl index 1daabe3435..d0ddfeb51a 100755 --- a/lib/debugger/src/dbg_wx_winman.erl +++ b/lib/debugger/src/dbg_wx_winman.erl @@ -118,9 +118,9 @@ init([]) -> {ok, #state{}}. handle_call({is_started, Title}, _From, State) -> - Reply = case lists:keysearch(Title, #win.title, State#state.wins) of - {value, Win} -> {true, Win#win.win}; - false -> false + Reply = case lists:keyfind(Title, #win.title, State#state.wins) of + false -> false; + Win -> {true, Win#win.win} end, {reply, Reply, State}. @@ -132,8 +132,8 @@ handle_cast({insert, Pid, Title, Win}, State) -> handle_cast({clear_process, Title}, State) -> OldWins = State#state.wins, - Wins = case lists:keysearch(Title, #win.title, OldWins) of - {value, #win{owner=Pid}} -> + Wins = case lists:keyfind(Title, #win.title, OldWins) of + #win{owner=Pid} -> Msg = {dbg_ui_winman, destroy}, Pid ! Msg, lists:keydelete(Title, #win.title, OldWins); @@ -145,7 +145,7 @@ handle_cast({clear_process, Title}, State) -> handle_info({'EXIT', Pid, _Reason}, State) -> [Mon | _Wins] = State#state.wins, if - Pid==Mon#win.owner -> {stop, normal, State}; + Pid =:= Mon#win.owner -> {stop, normal, State}; true -> Wins2 = lists:keydelete(Pid, #win.owner, State#state.wins), inform_all(Wins2), diff --git a/lib/debugger/src/i.erl b/lib/debugger/src/i.erl index 7c2fb22946..476a53482e 100644 --- a/lib/debugger/src/i.erl +++ b/lib/debugger/src/i.erl @@ -31,7 +31,6 @@ iv() -> Vsn = string:substr(filename:basename(code:lib_dir(debugger)), 10), list_to_atom(Vsn). - %% ------------------------------------------- %% Start a new graphical monitor. @@ -288,10 +287,9 @@ ia(X,Y,Z) -> %% ------------------------------------------- ia(Pid,Fnk) -> - case lists:keysearch(Pid, 1, int:snapshot()) of - {value, _PidTuple} -> - int:attach(Pid,Fnk); - false -> no_proc + case lists:keymember(Pid, 1, int:snapshot()) of + false -> no_proc; + true -> int:attach(Pid,Fnk) end. ia(X,Y,Z,Fnk) -> diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl index eeb4df4a8e..9ee2102a19 100644 --- a/lib/debugger/src/int.erl +++ b/lib/debugger/src/int.erl @@ -261,7 +261,7 @@ del_break_in(Mod, Func, Arity) when is_atom(Mod), is_atom(Func), is_integer(Arit end. first_lines(Clauses) -> - lists:map(fun(Clause) -> first_line(Clause) end, Clauses). + [first_line(Clause) || Clause <- Clauses]. first_line({clause,_L,_Vars,_,Exprs}) -> first_line(Exprs); @@ -469,10 +469,10 @@ contents(Mod, Pid) -> %% Arity = integer() %%-------------------------------------------------------------------- functions(Mod) -> - lists:filter(fun([module_info, _Arity]) -> false; - (_Func) -> true - end, - dbg_iserver:call({functions, Mod})). + [F || F <- dbg_iserver:call({functions, Mod}), functions_1(F)]. + +functions_1([module_info, _Arity]) -> false; +functions_1(_Func) -> true. %%==================================================================== diff --git a/lib/docbuilder/src/docb_edoc_xml_cb.erl b/lib/docbuilder/src/docb_edoc_xml_cb.erl index 4dba843341..f5cfc0fe18 100644 --- a/lib/docbuilder/src/docb_edoc_xml_cb.erl +++ b/lib/docbuilder/src/docb_edoc_xml_cb.erl @@ -478,31 +478,29 @@ otp_xmlify_a_href("#"++_ = Marker, Es0) -> % <seealso marker="#what"> otp_xmlify_a_href("http:"++_ = URL, Es0) -> % external URL {URL, Es0}; otp_xmlify_a_href("OTPROOT"++AppRef, Es0) -> % <.. marker="App:FileRef - case split(AppRef, "/") of - [AppS, "doc", FileRef1] -> - FileRef = AppS++":"++otp_xmlify_a_fileref(FileRef1, AppS), - [#xmlText{value=Str0} = T] = Es0, - Str = case split(Str0, "/") of - %% //Application - [AppS2] -> - %% AppS2 can differ from AppS - %% Example: xmerl/XMerL - AppS2; - [_AppS,ModRef] -> - case split(ModRef, ":") of - %% //Application/Module - [Module] -> - Module++"(3)"; - %% //Application/Module:Type() - [_Module,_Type] -> - ModRef - end; - %% //Application/Module:Function/Arity - [_AppS,ModFunc,Arity] -> - ModFunc++"/"++Arity - end, - {FileRef, [T#xmlText{value=Str}]} - end; + [AppS, "doc", FileRef1] = split(AppRef, "/"), + FileRef = AppS++":"++otp_xmlify_a_fileref(FileRef1, AppS), + [#xmlText{value=Str0} = T] = Es0, + Str = case split(Str0, "/") of + %% //Application + [AppS2] -> + %% AppS2 can differ from AppS + %% Example: xmerl/XMerL + AppS2; + [_AppS,ModRef] -> + case split(ModRef, ":") of + %% //Application/Module + [Module] -> + Module++"(3)"; + %% //Application/Module:Type() + [_Module,_Type] -> + ModRef + end; + %% //Application/Module:Function/Arity + [_AppS,ModFunc,Arity] -> + ModFunc++"/"++Arity + end, + {FileRef, [T#xmlText{value=Str}]}; otp_xmlify_a_href("../"++File, Es0) -> %% Special case: This kind of relative path is used on some %% places within i.e. EDoc and refers to a file within the same @@ -639,13 +637,13 @@ otp_xmlify_table([#xmlText{} = E|Es]) -> [E | otp_xmlify_table(Es)]; otp_xmlify_table([#xmlElement{name=tbody} = E|Es]) -> otp_xmlify_table(E#xmlElement.content)++otp_xmlify_table(Es); -otp_xmlify_table([#xmlElement{name=tr} = E|Es]) -> +otp_xmlify_table([#xmlElement{name=tr, content=Content}|Es]) -> %% Insert newlines between table rows - otp_xmlify_table(E#xmlElement.content)++[{br,[]}]++otp_xmlify_table(Es); -otp_xmlify_table([#xmlElement{name=th} = E|Es]) -> - [{em, E#xmlElement.content} | otp_xmlify_table(Es)]; -otp_xmlify_table([#xmlElement{name=td} = E|Es]) -> - otp_xmlify_e(E#xmlElement.content) ++ otp_xmlify_table(Es); + otp_xmlify_table(Content)++[{br,[]}]++otp_xmlify_table(Es); +otp_xmlify_table([#xmlElement{name=th, content=Content}|Es]) -> + [{em, Content} | otp_xmlify_table(Es)]; +otp_xmlify_table([#xmlElement{name=td, content=Content}|Es]) -> + otp_xmlify_e(Content) ++ otp_xmlify_table(Es); otp_xmlify_table([]) -> []. @@ -1155,8 +1153,8 @@ get_text(#xmlElement{content=[E]}) -> %% text_only(Es) -> Ts %% Takes a list of xmlElement and xmlText and return a lists of xmlText. -text_only([#xmlElement{} = E |Es]) -> - text_only(E#xmlElement.content) ++ text_only(Es); +text_only([#xmlElement{content = Content}|Es]) -> + text_only(Content) ++ text_only(Es); text_only([#xmlText{} = E |Es]) -> [E | text_only(Es)]; text_only([]) -> diff --git a/lib/docbuilder/src/docb_html.erl b/lib/docbuilder/src/docb_html.erl index 9aea4c8a66..bdfc5ea876 100644 --- a/lib/docbuilder/src/docb_html.erl +++ b/lib/docbuilder/src/docb_html.erl @@ -283,16 +283,16 @@ rule([term|_], {_, [ID], _}, Opts) -> ID, "</strong></em> "]}, Opts}; TermList -> - case lists:keysearch(ID, 1, TermList) of + case lists:keyfind(ID, 1, TermList) of false -> {{drop, ["<em><strong>", ID, "</strong></em> "]}, Opts}; - {value, {ID, Name, _Description, _Resp}} -> + {ID, Name, _Description, _Resp} -> {{drop, ["<em><strong>", Name, "</strong></em> "]}, Opts}; - {value, {ID, Name, _Description}} -> + {ID, Name, _Description} -> {{drop, [ "<em><strong>", Name, "</strong></em> "]}, Opts} @@ -306,16 +306,16 @@ rule([term|_], {_, [ID], _}, Opts) -> TermList -> PartApplication = docb_util:lookup_option(part_application, Opts), - case lists:keysearch(ID, 1, TermList) of + case lists:keyfind(ID, 1, TermList) of false -> {{drop, ["<a href=\"", PartApplication, "_term.html#", ID, "\">", ID, "</a> "]}, Opts}; - {value, {ID, Name, _Description, _Resp}} -> + {ID, Name, _Description, _Resp} -> {{drop, ["<a href=\"", PartApplication, "_term.html#", ID, "\">", Name, "</a> "]}, Opts}; - {value, {ID, Name, _Description}} -> + {ID, Name, _Description} -> {{drop, ["<a href=\"", PartApplication, "_term.html#", ID, "\">", Name, "</a> "]}, Opts} @@ -331,17 +331,16 @@ rule([cite|_], {_, [ID], _}, Opts) -> {{drop, ["<em><strong>", ID, "</strong></em> "]}, Opts}; CiteList -> - case lists:keysearch(ID, 1, CiteList) of + case lists:keyfind(ID, 1, CiteList) of false -> {{drop, ["<em><strong>", ID, "</strong></em> "]}, Opts}; - - {value, {ID, Name, _Description, _Resp}} -> + {ID, Name, _Description, _Resp} -> {{drop, ["<em><strong>", Name, "</strong></em> "]}, Opts}; - {value, {ID, Name, _Description}} -> + {ID, Name, _Description} -> {{drop, ["<em><strong>", Name, "</strong></em> "]}, Opts} @@ -355,18 +354,18 @@ rule([cite|_], {_, [ID], _}, Opts) -> CiteList -> PartApp = docb_util:lookup_option(part_application, Opts), - case lists:keysearch(ID, 1, CiteList) of + case lists:keyfind(ID, 1, CiteList) of false -> {{drop, ["<a href=\"", PartApp, "_cite.html#", ID, "\">", ID, "</a> "]}, Opts}; - {value, {ID, Name, _Description, _Resp}} -> + {ID, Name, _Description, _Resp} -> {{drop, ["<a href=\"", PartApp, "_cite.html#", ID, "\">", Name, "</a> "]}, Opts}; - {value, {ID, Name, _Description}} -> + {ID, Name, _Description} -> {{drop, ["<a href=\"", PartApp, "_cite.html#", ID, "\">", Name, "</a> "]}, @@ -376,7 +375,7 @@ rule([cite|_], {_, [ID], _}, Opts) -> end; rule([code|_], {_, [Type], [{pcdata, _, Code}]}, Opts) -> - case lists:member(Type,["ERL","C","NONE"]) of + case lists:member(Type, ["ERL","C","NONE"]) of true -> {{drop, ["\n<div class=\"example\"><pre>\n", docb_html_util:element_cdata_to_html(Code), "\n</pre></div>\n"]}, Opts}; diff --git a/lib/docbuilder/src/docb_html_util.erl b/lib/docbuilder/src/docb_html_util.erl index b2951706ea..02ce8b52a7 100644 --- a/lib/docbuilder/src/docb_html_util.erl +++ b/lib/docbuilder/src/docb_html_util.erl @@ -136,7 +136,6 @@ copy_pics(Src, Dest, Opts) -> Dir = code:lib_dir(docbuilder), InFile = filename:join([Dir, "etc", Src]), OutFile = docb_util:outfile(Dest, "", Opts), - case filelib:last_modified(OutFile) of 0 -> % File doesn't exist file:copy(InFile, OutFile); @@ -156,10 +155,10 @@ copy_pics(Src, Dest, Opts) -> %%--Resolve header data------------------------------------------------- extract_header_data(Key, {header, [], List}) -> - case lists:keysearch(Key, 1, List) of - {value, {Key, [], []}} -> + case lists:keyfind(Key, 1, List) of + {Key, [], []} -> ""; - {value, {Key, [], [{pcdata, [], Value}]}} -> + {Key, [], [{pcdata, [], Value}]} -> pcdata_to_html(Value); false -> "" @@ -253,7 +252,7 @@ make_anchor_href(HRef) -> {ok, [HRef]} -> %% No `#' in HRef, i.e. only path make_anchor_href(HRef, ""); - {ok, [Path, Fragment]}-> + {ok, [Path, Fragment]} -> make_anchor_href(Path, Fragment) end. @@ -398,10 +397,10 @@ count_sections([]) -> %%--Make a ToC---------------------------------------------------------- format_toc(Toc) -> - lists:map(fun({Number, Title}) -> - [Number, " <a href = \"#", Number, - "\">", Title, "</a><br/>\n"] - end, Toc). + [format_toc1(T) || T <- Toc]. + +format_toc1({Number, Title}) -> + [Number, " <a href = \"#", Number, "\">", Title, "</a><br/>\n"]. %%--Convert HTML ISO Latin 1 characters to ordinary characters---------- diff --git a/lib/docbuilder/src/docb_main.erl b/lib/docbuilder/src/docb_main.erl index ef21f65557..87a1401a02 100644 --- a/lib/docbuilder/src/docb_main.erl +++ b/lib/docbuilder/src/docb_main.erl @@ -55,7 +55,7 @@ process(File, Opts) -> %% If no target format is specified, assume HTML: Tos = if - Tos0==[] -> [html]; + Tos0 =:= [] -> [html]; true -> Tos0 end, @@ -327,12 +327,8 @@ verify(Tree) -> verify(Tree, [], 1). verify({pcdata, Optional, _}, Path, Level) -> verify_optional(Optional, Path, Level); verify({Tag, Optional, Args}, Path, Level) when is_list(Args) -> - case verify_optional(Optional, Path, Level) of - true -> - verify_list(Args, [Tag|Path], Level); - false -> - false - end; + verify_optional(Optional, Path, Level) + andalso verify_list(Args, [Tag|Path], Level); verify(Other, Path, Level) -> verify_error(Other, Path, Level). @@ -342,12 +338,7 @@ verify_error(X, Path, Level) -> false. verify_list([H|T], Path, Level) -> - case verify(H, Path, Level) of - true -> - verify_list(T, Path, Level +1); - false -> - false - end; + verify(H, Path, Level) andalso verify_list(T, Path, Level + 1); verify_list([], _, _) -> true. @@ -419,7 +410,7 @@ edit1_list(_Tag, [], _Op) -> %% Actual transformation of tree structure to desired format. transform(From, To, Opts, File, Tree) -> Filter = if - To==html; To==kwic -> + To =:= html; To =:= kwic -> list_to_atom("docb_tr_" ++ atom_to_list(From) ++ [$2|atom_to_list(To)]); true -> @@ -427,7 +418,7 @@ transform(From, To, Opts, File, Tree) -> [$2|atom_to_list(To)]) end, - case catch apply(Filter, transform, [File, Tree, Opts]) of + case catch Filter:transform(File, Tree, Opts) of %% R5C {'EXIT', {undef, [{Filter, transform, [File, Tree, Opts]}|_]}}-> @@ -459,9 +450,9 @@ transform(From, To, Opts, File, Tree) -> finish_transform(Tree, File, Opts, Filter) -> {Str, NewOpts} = pp(Tree, [], 1, Filter, Opts), Extension = - case catch apply(Filter, extension, [NewOpts]) of + case catch Filter:extension(NewOpts) of {'EXIT', _} -> - apply(Filter, extension, []); + Filter:extension(); Others -> Others end, @@ -606,7 +597,7 @@ include_all(Fd) -> eof -> []; ListOfChars -> - lists:append(ListOfChars, include_all(Fd)) + ListOfChars ++ include_all(Fd) end. extract(File, Fd, StartTag, StopTag, State) -> diff --git a/lib/docbuilder/src/docb_pretty_format.erl b/lib/docbuilder/src/docb_pretty_format.erl index 0c4fb0507b..25dcd8987b 100644 --- a/lib/docbuilder/src/docb_pretty_format.erl +++ b/lib/docbuilder/src/docb_pretty_format.erl @@ -47,7 +47,7 @@ term(Term) -> %% the next line to need an "extra" indent!). term([], Indent) -> {Indent, [$[,$]]}; -term(L, Indent) when list(L) -> +term(L, Indent) when is_list(L) -> case is_string(L) of true -> {Indent, io_lib:write_string(L)}; @@ -59,7 +59,7 @@ term(L, Indent) when list(L) -> write_simple_list(L, Indent) end end; -term(T, Indent) when tuple(T) -> +term(T, Indent) when is_tuple(T) -> case complex_tuple(T) of true -> write_complex_tuple(T, Indent); diff --git a/lib/docbuilder/src/docb_tr_application2html.erl b/lib/docbuilder/src/docb_tr_application2html.erl index 4084cfe6ba..d8cb214d0a 100644 --- a/lib/docbuilder/src/docb_tr_application2html.erl +++ b/lib/docbuilder/src/docb_tr_application2html.erl @@ -119,8 +119,8 @@ transform(File, {application, _Attrs, [Header|Rest]}, Opts0) -> case docb_main:parse1("fascicules", Opts0) of {ok, Parse} -> FascData = get_fasc_data(Parse), - case lists:keysearch(File, 1, FascData) of - {value, {_, _, "YES", _}} -> + case lists:keyfind(File, 1, FascData) of + {_, _, "YES", _} -> OrigFile = docb_util:outfile(File++"_frame", ".html", Opts0), @@ -167,7 +167,7 @@ concat_files([File|Rest], Body, Opts) -> %% Remove the reference manual header [{Ref, [], [_Hdr| NewBody]}] = NewParse, RefParse = [{Ref, [], NewBody}], - lists:append(Body, concat_files(Rest, RefParse, Opts)); + Body ++ concat_files(Rest, RefParse, Opts); errors -> errors end; @@ -216,7 +216,7 @@ make_toc([{lib, Attrs, More}|Rest]) -> make_toc([{com, Attrs, More}|Rest]) -> [{com, Attrs, More}|make_toc(Rest)]; make_toc([{_Tag, _Attrs, More}|Rest]) -> - lists:append(make_toc(More), make_toc(Rest)). + make_toc(More) ++ make_toc(Rest). rule([module|_], {_, [File], _}) -> {"<small><a target=\"document\" href=\"" ++ @@ -280,9 +280,7 @@ get_fasc_data({fascicules, _, Fascs}) -> Fascs). get_avals(Atts) -> - lists:map(fun(Tuple) -> - element(3, Tuple) end, - Atts). + [element(3, Tuple) || Tuple <- Atts]. get_pc_text([{pcdata, _, Text}]) -> Text. diff --git a/lib/docbuilder/src/docb_tr_cite2html.erl b/lib/docbuilder/src/docb_tr_cite2html.erl index 4ecbfa4e91..77f1c4e636 100644 --- a/lib/docbuilder/src/docb_tr_cite2html.erl +++ b/lib/docbuilder/src/docb_tr_cite2html.erl @@ -39,24 +39,22 @@ purge_body([], _) -> purge_body([{pcdata,_Attrs,_More}|Rest], CiteList) -> purge_body(Rest, CiteList); purge_body([{cite,[{"ID","CDATA",ID}],More}|Rest], CiteList) -> - case lists:keysearch(ID, 1, CiteList) of + case lists:keyfind(ID, 1, CiteList) of false -> [{cite, [{"NAME","CDATA",ID}, {"ID","CDATA",ID}], More}| purge_body(Rest, CiteList)]; - {value, {ID, Name, _Description, _Responsible}} -> + {ID, Name, _Description, _Responsible} -> [{cite, [{"NAME","CDATA",Name}, {"ID","CDATA",ID}], More}| purge_body(Rest, CiteList)]; - {value, {ID, Name, _Description}} -> + {ID, Name, _Description} -> [{cite, [{"NAME","CDATA",Name}, {"ID","CDATA",ID}], More}| purge_body(Rest, CiteList)] end; purge_body([{_Tag,_Attrs,More}|Rest], CiteList) -> - lists:append(purge_body(More, CiteList), - purge_body(Rest, CiteList)). + purge_body(More, CiteList) ++ purge_body(Rest, CiteList). rule([header|_], _) -> {drop, ""}; - rule(_, _) -> {drop, ""}. @@ -91,19 +89,19 @@ rule([cite|_], {_,[Name,ID], [{citedef,[],[{pcdata,[],Def}]}]}, Opts) -> false -> []; Value -> Value end, - case lists:keysearch(ID, 1, CiteList) of + case lists:keyfind(ID, 1, CiteList) of false -> {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ ID ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Def) ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description, _Responsible}} -> + {ID, Name, Description, _Responsible} -> docb_util:message(warning, "Global cite ~s overriding local", [ID]), {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description}} -> + {ID, Name, Description} -> docb_util:message(warning, "Global cite ~s overriding local", [ID]), {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ @@ -117,19 +115,19 @@ rule([cite|_], {_,[Name,ID],_}, Opts) -> false -> []; Value -> Value end, - case lists:keysearch(ID, 1, CiteList) of + case lists:keyfind(ID, 1, CiteList) of false -> docb_util:message(error, "The cite ~s has no definition", [ID]), {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ ID ++ "</strong></a></dt>\n<dd>" ++ "??" ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description, _Responsible}} -> + {ID, Name, Description, _Responsible} -> {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description}} -> + {ID, Name, Description} -> {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts} diff --git a/lib/docbuilder/src/docb_tr_index2html.erl b/lib/docbuilder/src/docb_tr_index2html.erl index bbf419f3ef..312342add2 100644 --- a/lib/docbuilder/src/docb_tr_index2html.erl +++ b/lib/docbuilder/src/docb_tr_index2html.erl @@ -73,9 +73,7 @@ prune_flat([], _) -> []. keep_pcdata(Trees) -> - lists:filter(fun({pcdata, _, _}) -> true; - (_) -> false - end, Trees). + [T || T = {pcdata, _, _} <- Trees]. new_trees(FileFuncs) -> Files0 = [{File, RefType} || {File, RefType, _} <- FileFuncs], diff --git a/lib/docbuilder/src/docb_tr_part2html.erl b/lib/docbuilder/src/docb_tr_part2html.erl index dd44c4a8df..30befe8432 100644 --- a/lib/docbuilder/src/docb_tr_part2html.erl +++ b/lib/docbuilder/src/docb_tr_part2html.erl @@ -93,8 +93,8 @@ transform(File, {part, _Attrs, [Header| Rest]}, Opts0) -> case docb_main:parse1("fascicules", Opts0) of {ok, Parse} -> FascData = get_fasc_data(Parse), - case lists:keysearch(File, 1, FascData) of - {value, {_, _, "YES", _}} -> + case lists:keyfind(File, 1, FascData) of + {_, _, "YES", _} -> OrigFile = docb_util:outfile(File++"_frame", ".html", Opts0), @@ -137,7 +137,7 @@ concat_files([File | Rest], Body, ChLevel, Opts, TP, TOpts, Ext) -> case docb_main:parse1(File, Opts) of {ok, Parse} -> {TopTag, Attrs, [Header = {header, _, HeaderContents} | More]} = Parse, - {value,{title,_,Title}} = lists:keysearch(title,1,HeaderContents), + {title,_,Title} = lists:keyfind(title,1,HeaderContents), NewMore = [{section, [], [{title, [], Title}| More]}], NewParse = {TopTag, Attrs, [Header| NewMore]}, if @@ -156,9 +156,8 @@ concat_files([File | Rest], Body, ChLevel, Opts, TP, TOpts, Ext) -> docb_html_util:number(NewParse, integer_to_list(ChLevel), File), {_, [], [_| NewBody]} = NumberTree, - lists:append(Body, - concat_files(Rest, NewBody, ChLevel+1, Opts, - TP, TOpts, Ext)); + Body ++ concat_files(Rest, NewBody, ChLevel+1, Opts, + TP, TOpts, Ext); errors -> throw({error,"Parse error when building chapter "++File}) end; @@ -232,9 +231,7 @@ get_fasc_data({fascicules, _, Fascs}) -> Fascs). get_avals(Atts) -> - lists:map(fun(Tuple) -> - element(3, Tuple) end, - Atts). + [element(3, Tuple) || Tuple <- Atts]. get_pc_text([{pcdata, _, Text}]) -> Text. diff --git a/lib/docbuilder/src/docb_tr_term2html.erl b/lib/docbuilder/src/docb_tr_term2html.erl index 0a993cebb1..a3c4a5312a 100644 --- a/lib/docbuilder/src/docb_tr_term2html.erl +++ b/lib/docbuilder/src/docb_tr_term2html.erl @@ -39,24 +39,22 @@ purge_body([], _) -> purge_body([{pcdata,_Attrs,_More}|Rest], TermList) -> purge_body(Rest, TermList); purge_body([{term,[{"ID","CDATA",ID}],More}|Rest], TermList) -> - case lists:keysearch(ID, 1, TermList) of + case lists:keyfind(ID, 1, TermList) of false -> [{term,[{"NAME","CDATA",ID},{"ID","CDATA",ID}],More}| purge_body(Rest, TermList)]; - {value, {ID, Name, _Description, _Responsible}} -> + {ID, Name, _Description, _Responsible} -> [{term,[{"NAME","CDATA",Name},{"ID","CDATA",ID}],More}| purge_body(Rest, TermList)]; - {value, {ID, Name, _Description}} -> + {ID, Name, _Description} -> [{term,[{"NAME","CDATA",Name},{"ID","CDATA",ID}],More}| purge_body(Rest, TermList)] end; purge_body([{_Tag,_Attrs,More}|Rest], TermList) -> - lists:append(purge_body(More, TermList), - purge_body(Rest, TermList)). + purge_body(More, TermList) ++ purge_body(Rest, TermList). rule([header|_], _) -> {drop, ""}; - rule(_, _) -> {drop, ""}. @@ -82,19 +80,19 @@ rule([term|_], {_,[Name,ID],[{termdef,[],[{pcdata,[],Def}]}]}, Opts) -> false -> []; Value -> Value end, - case lists:keysearch(ID, 1, TermList) of + case lists:keyfind(ID, 1, TermList) of false -> {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ ID ++ "</strong></a>\n</dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Def) ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description, _Responsible}} -> + {ID, Name, Description, _Responsible} -> docb_util:message(warning, "Global term ~s overriding local", [ID]), {{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description}} -> + {ID, Name, Description} -> docb_util:message(warning, "Global term ~s overriding local", [ID]), {{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++ @@ -107,19 +105,19 @@ rule([term|_], {_,[Name,ID],_}, Opts) -> false -> []; Value -> Value end, - case lists:keysearch(ID, 1, TermList) of + case lists:keyfind(ID, 1, TermList) of false -> docb_util:message(error, "The term ~s has no definition", [ID]), {{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ ID ++ "</strong></a></dt>\n<dd>" ++ "??" ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description, _Responsible}} -> + {ID, Name, Description, _Responsible} -> {{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts}; - {value, {ID, Name, Description}} -> + {ID, Name, Description} -> {{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++ "<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++ docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts} diff --git a/lib/docbuilder/src/docb_transform.erl b/lib/docbuilder/src/docb_transform.erl index a432038adf..9c7561b07b 100644 --- a/lib/docbuilder/src/docb_transform.erl +++ b/lib/docbuilder/src/docb_transform.erl @@ -135,8 +135,8 @@ parse([Opt | _RawOpts], _Opts) -> get_defs(Type, File, Opts) -> Key = {defs,Type}, {PrevDefs, Opts2} = - case lists:keysearch(Key, 1, Opts) of - {value, {_, Defs0}} -> + case lists:keyfind(Key, 1, Opts) of + {_, Defs0} -> {Defs0, lists:keydelete(Key, 1, Opts)}; false -> {[], Opts} diff --git a/lib/docbuilder/src/docb_util.erl b/lib/docbuilder/src/docb_util.erl index 59673ef3a4..9b2eec7733 100644 --- a/lib/docbuilder/src/docb_util.erl +++ b/lib/docbuilder/src/docb_util.erl @@ -61,7 +61,7 @@ html_snippet(What, Opts) -> case lookup_option(html_mod, Opts) of false -> ""; Module -> - case catch apply(Module, What, []) of + case catch Module:What() of HTML when is_list(HTML) -> HTML; {'EXIT', {undef, _}} -> @@ -82,7 +82,7 @@ html_snippet(What, Arg, Opts) -> case lookup_option(html_mod, Opts) of false -> ""; Module -> - case catch apply(Module, What, [Arg]) of + case catch Module:What(Arg) of HTML when is_list(HTML) -> HTML; {'EXIT', {undef, _}} -> @@ -106,8 +106,8 @@ html_snippet(What, Arg, Opts) -> %% lookup_option(Opt, Opts) -> Value | false lookup_option(Opt, Opts) -> - case lists:keysearch(Opt, 1, Opts) of - {value, {Opt,Value}} -> Value; + case lists:keyfind(Opt, 1, Opts) of + {Opt,Value} -> Value; false -> false end. diff --git a/lib/docbuilder/src/docb_xmerl_tree_cb.erl b/lib/docbuilder/src/docb_xmerl_tree_cb.erl index d57f55bff8..bc62069230 100644 --- a/lib/docbuilder/src/docb_xmerl_tree_cb.erl +++ b/lib/docbuilder/src/docb_xmerl_tree_cb.erl @@ -188,8 +188,8 @@ attrs(DTD, Tag, GivenAttrs) -> merge_attrs(Tag, default_attrs(DTD, Tag), GivenAttrs). merge_attrs(Tag, [{NameA, Type, DefVal}|Default], GivenAttrs) -> - Val = case lists:keysearch(NameA, #xmlAttribute.name, GivenAttrs) of - {value, #xmlAttribute{value=Val0}} -> Val0; + Val = case lists:keyfind(NameA, #xmlAttribute.name, GivenAttrs) of + #xmlAttribute{value=Val0} -> Val0; false -> DefVal end, Attr = {attr_name(NameA), Type, attr_val(Type, Val)}, diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml index ee74e598d9..ce6ba3d923 100644 --- a/lib/erl_docgen/doc/src/notes.xml +++ b/lib/erl_docgen/doc/src/notes.xml @@ -29,7 +29,25 @@ <file>notes.xml</file> </header> <p>This document describes the changes made to the erl_docgen application.</p> - <section><title>Erl_Docgen 0.2</title> + <section><title>erl_docgen 0.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The text within code examples (CODE and PRE tags) will not be + space normalized for man pages. + </p> + <p> + Own Id: OTP-8476 + </p> + </item> + </list> + </section> + + </section> + + <section><title>erl_docgen 0.2</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/erl_docgen/priv/xsl/db_man.xsl b/lib/erl_docgen/priv/xsl/db_man.xsl index a2b1e755e2..71c4a66707 100644 --- a/lib/erl_docgen/priv/xsl/db_man.xsl +++ b/lib/erl_docgen/priv/xsl/db_man.xsl @@ -336,6 +336,16 @@ <xsl:text>></xsl:text> </xsl:template> + <!-- Do not noramlize any text within pre and code tags. --> + <xsl:template match="pre/text()"> + <xsl:value-of select="."/> + </xsl:template> + + <xsl:template match="code/text()"> + <xsl:value-of select="."/> + </xsl:template> + + <!-- Replace ' by \&' ans . by \&. --> <xsl:template match="text()"> <xsl:variable name="startstring"> diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk index dd74ae9ddd..5b14051034 100644 --- a/lib/erl_docgen/vsn.mk +++ b/lib/erl_docgen/vsn.mk @@ -17,6 +17,6 @@ # %CopyrightEnd% # -ERL_DOCGEN_VSN = 0.2 +ERL_DOCGEN_VSN = 0.2.1 TICKETS = diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h index 729b9fc367..466d84bb99 100644 --- a/lib/erl_interface/include/ei.h +++ b/lib/erl_interface/include/ei.h @@ -719,11 +719,9 @@ int ei_x_encode_bignum(ei_x_buff *x, mpz_t obj); #define EI_LONGLONG __int64 #define EI_ULONGLONG unsigned __int64 #else -#ifndef VXWORKS #define EI_LONGLONG long long #define EI_ULONGLONG unsigned long long #endif -#endif #ifndef VXWORKS int ei_decode_longlong(const char *buf, int *index, EI_LONGLONG *p); diff --git a/lib/jinterface/java_src/Makefile b/lib/jinterface/java_src/Makefile index 37a57352ad..22c55328b8 100644 --- a/lib/jinterface/java_src/Makefile +++ b/lib/jinterface/java_src/Makefile @@ -35,7 +35,21 @@ VSN=$(JINTERFACE_VSN) SPECIAL_TARGETS = +TARGET_FILES= $(POM_TARGET) +SPECIAL_TARGETS = + +POM_FILE= pom.xml + +POM_TARGET= ../$(POM_FILE) +POM_SRC= $(POM_FILE).src + +# ---------------------------------------------------- +# Special Build Targets +# ---------------------------------------------------- + +$(POM_TARGET): $(POM_SRC) ../vsn.mk + sed -e 's;%VSN%;$(VSN);' $< > $@ # ---------------------------------------------------- # Default Subdir Targets @@ -43,7 +57,7 @@ SPECIAL_TARGETS = .PHONY: debug opt instr release docs release_docs tests release_tests clean depend -debug opt instr release docs release_docs tests release_tests clean depend: +debug opt instr release docs release_docs tests release_tests clean depend: $(TARGET_FILES) set -e; set -x; \ case "$(MAKE)" in *clearmake*) tflag="-T";; *) tflag="";; esac; \ if test -f com/ericsson/otp/erlang/ignore_config_record.inf; then xflag=$$tflag; fi; \ diff --git a/lib/jinterface/java_src/pom.xml.src b/lib/jinterface/java_src/pom.xml.src new file mode 100644 index 0000000000..cef49b735a --- /dev/null +++ b/lib/jinterface/java_src/pom.xml.src @@ -0,0 +1,106 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.erlang.otp</groupId> + <artifactId>jinterface</artifactId> + <packaging>jar</packaging> + <version>%VSN%</version> + <name>jinterface</name> + <description> + Jinterface Java package contains java classes, which help you integrate programs written in Java with Erlang. + Erlang is a programming language designed at the Ericsson Computer Science Laboratory. + </description> + <url>http://erlang.org/</url> + <licenses> + <license> + <name>ERLANG PUBLIC LICENSE 1.1</name> + <url>http://www.erlang.org/EPLICENSE</url> + <distribution>repo</distribution> + </license> + </licenses> + <scm> + <connection>git://github.com/erlang/otp.git</connection> + <developerConnection>git://github.com/erlang/otp.git</developerConnection> + <url>http://github.com/erlang/otp</url> + </scm> + <developers> + <developer> + <email>[email protected]</email> + </developer> + </developers> + <organization> + <name>Open Source Erlang</name> + <url>http://www.erlang.org/</url> + </organization> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.5</source> + <target>1.5</target> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>java_src</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + <distributionManagement> + <repository> + <id>ossrh</id> + <url>http://oss.sonatype.org/service/local/staging/deploy/maven2</url> + </repository> + <snapshotRepository> + <id>ossrh</id> + <url>http://oss.sonatype.org/content/repositories/snapshots</url> + </snapshotRepository> + </distributionManagement> + <profiles> + <profile> + <id>release-sign-artifacts</id> + <activation> + <property> + <name>performRelease</name> + <value>true</value> + </property> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-gpg-plugin</artifactId> + <version>1.0-alpha-4</version> + <executions> + <execution> + <id>sign-artifacts</id> + <phase>verify</phase> + <goals> + <goal>sign</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> +</project> diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml index 3a8011e28b..fb09092f1c 100644 --- a/lib/kernel/doc/src/gen_sctp.xml +++ b/lib/kernel/doc/src/gen_sctp.xml @@ -1173,7 +1173,7 @@ client_loop(S, Peer1, Port1, AssocId1, Peer2, Port2, AssocId2) -> <title>SEE ALSO</title> <p><seealso marker="inet">inet(3)</seealso>, <seealso marker="gen_tcp">gen_tcp(3)</seealso>, - <seealso marker="gen_udp">gen_upd(3)</seealso>, + <seealso marker="gen_udp">gen_udp(3)</seealso>, <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC2960</url> (Stream Control Transmission Protocol), <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets API Extensions for SCTP.</url></p> <marker id="authors"></marker> diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index a692aa095d..b503716037 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -67,12 +67,6 @@ <p> Own Id: OTP-8652</p> </item> - <item> - <p>A bug introduced in kernel-2.13.5.3 has been - fixed.</p> - <p> - Own Id: OTP-8686 Aux Id: OTP-8643, seq11584 </p> - </item> </list> </section> @@ -120,6 +114,37 @@ </section> +<section><title>Kernel 2.13.5.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A bug introduced in Kernel 2.13.5.2 has been fixed.</p> + <p> + Own Id: OTP-8686 Aux Id: OTP-8643</p> + </item> + </list> + </section> + +</section> + +<section><title>Kernel 2.13.5.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Under certain circumstances the net kernel could hang. + (Thanks to Scott Lystig Fritchie.)</p> + <p> + Own Id: OTP-8643 Aux Id: seq11584</p> + </item> + </list> + </section> + +</section> + <section><title>Kernel 2.13.5.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/kernel/src/auth.erl b/lib/kernel/src/auth.erl index 7fe30ae828..5c7fe2421d 100644 --- a/lib/kernel/src/auth.erl +++ b/lib/kernel/src/auth.erl @@ -50,6 +50,8 @@ %% Exported functions %%---------------------------------------------------------------------- +-spec start_link() -> {'ok',pid()} | {'error', term()} | 'ignore'. + start_link() -> gen_server:start_link({local, auth}, auth, [], []). @@ -134,7 +136,9 @@ init([]) -> %% The net kernel will let all message to the auth server %% through as is --type calls() :: 'echo' | 'sync_cookie' | {'set_cookie', node(), term()}. +-type calls() :: 'echo' | 'sync_cookie' + | {'get_cookie', node()} + | {'set_cookie', node(), term()}. -spec handle_call(calls(), {pid(), term()}, state()) -> {'reply', 'hello' | 'true' | 'nocookie' | cookie(), state()}. diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 64a358f114..9a191f9aeb 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1,4 +1,4 @@ -# +# # %CopyrightBegin% # # Copyright Ericsson AB 1997-2010. All Rights Reserved. @@ -15,6 +15,6 @@ # under the License. # # %CopyrightEnd% -# +# -KERNEL_VSN = 2.14 +KERNEL_VSN = 2.14.1 diff --git a/lib/public_key/src/public_key.app.src b/lib/public_key/src/public_key.app.src index edede7c874..d5e1705827 100644 --- a/lib/public_key/src/public_key.app.src +++ b/lib/public_key/src/public_key.app.src @@ -13,4 +13,5 @@ {registered, []}, {env, []} ] -}.
\ No newline at end of file +}. + diff --git a/lib/reltool/bin/reltool.escript b/lib/reltool/bin/reltool.escript index 0dcd5ad1e9..0dcd5ad1e9 100644..100755 --- a/lib/reltool/bin/reltool.escript +++ b/lib/reltool/bin/reltool.escript diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile index d6788c1633..3119d37af0 100644 --- a/lib/ssl/doc/src/Makefile +++ b/lib/ssl/doc/src/Makefile @@ -37,7 +37,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # Target Specs # ---------------------------------------------------- XML_APPLICATION_FILES = refman.xml -XML_REF3_FILES = ssl.xml old_ssl.xml +XML_REF3_FILES = ssl.xml old_ssl.xml ssl_session_cache_api.xml XML_REF6_FILES = ssl_app.xml XML_PART_FILES = release_notes.xml usersguide.xml @@ -45,9 +45,7 @@ XML_CHAPTER_FILES = \ ssl_protocol.xml \ using_ssl.xml \ pkix_certs.xml \ - create_certs.xml \ ssl_distribution.xml \ - licenses.xml \ notes.xml BOOK_FILES = book.xml diff --git a/lib/ssl/doc/src/book.xml b/lib/ssl/doc/src/book.xml index 9122addb74..85d6b56b26 100644 --- a/lib/ssl/doc/src/book.xml +++ b/lib/ssl/doc/src/book.xml @@ -28,9 +28,6 @@ <rev>A</rev> <file>book.sgml</file> </header> - <insidecover> - <include file="insidecover"></include> - </insidecover> <pagetext>SSL Application</pagetext> <preamble> <contents level="2"></contents> diff --git a/lib/ssl/doc/src/create_certs.xml b/lib/ssl/doc/src/create_certs.xml deleted file mode 100644 index 79cc8a0537..0000000000 --- a/lib/ssl/doc/src/create_certs.xml +++ /dev/null @@ -1,148 +0,0 @@ -<?xml version="1.0" encoding="latin1" ?> -<!DOCTYPE chapter SYSTEM "chapter.dtd"> - -<chapter> - <header> - <copyright> - <year>2003</year><year>2009</year> - <holder>Ericsson AB. All Rights Reserved.</holder> - </copyright> - <legalnotice> - 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 - compliance with the License. You should have received a copy of the - Erlang Public License along with this software. If not, it can be - retrieved online at http://www.erlang.org/. - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - the License for the specific language governing rights and limitations - under the License. - - </legalnotice> - - <title>Creating Certificates</title> - <prepared>UAB/F/P Peter Högfeldt</prepared> - <docno></docno> - <date>2003-06-16</date> - <rev>A</rev> - <file>create_certs.xml</file> - </header> - <p>Here we consider the creation of example certificates. - </p> - - <section> - <title>The openssl Command</title> - <p>The <c>openssl</c> command is a utility that comes with the - OpenSSL distribution. It provides a variety of subcommands. Each - subcommand is invoked as</p> - <code type="none"><![CDATA[ - openssl subcmd <options and arguments> ]]></code> - <p>where <c>subcmd</c> denotes the subcommand in question. - </p> - <p>We shall use the following subcommands to create certificates for - the purpose of testing Erlang/OTP SSL: - </p> - <list type="bulleted"> - <item><em>req</em> to create certificate requests and a - self-signed certificates, - </item> - <item><em>ca</em> to create certificates from certificate requests.</item> - </list> - <p>We create the following certificates: - </p> - <list type="bulleted"> - <item>the <em>erlangCA</em> root certificate (a self-signed - certificate), </item> - <item>the <em>otpCA</em> certificate signed by the <em>erlangCA</em>, </item> - <item>a client certificate signed by the <em>otpCA</em>, and</item> - <item>a server certificate signed by the <em>otpCA</em>.</item> - </list> - - <section> - <title>The openssl configuration file</title> - <p>An <c>openssl</c> configuration file consist of a number of - sections, where each section starts with one line containing - <c>[ section_name ]</c>, where <c>section_name</c> is the name - of the section. The first section of the file is either - unnamed, or is named <c>[ default ]</c>. For further details - see the OpenSSL config(5) manual page. - </p> - <p>The required sections for the subcommands we are going to - use are as follows: - </p> - <table> - <row> - <cell align="left" valign="middle">subcommand</cell> - <cell align="left" valign="middle">required/default section</cell> - <cell align="left" valign="middle">override command line option</cell> - <cell align="left" valign="middle">configuration file option</cell> - </row> - <row> - <cell align="left" valign="middle">req</cell> - <cell align="left" valign="middle">[req]</cell> - <cell align="left" valign="middle">-</cell> - <cell align="left" valign="middle"><c>-config FILE</c></cell> - </row> - <row> - <cell align="left" valign="middle">ca</cell> - <cell align="left" valign="middle">[ca]</cell> - <cell align="left" valign="middle"><c>-name section</c></cell> - <cell align="left" valign="middle"><c>-config FILE</c></cell> - </row> - <tcaption>openssl subcommands to use</tcaption> - </table> - </section> - - <section> - <title>Creating the Erlang root CA</title> - <p>The Erlang root CA is created with the command</p> - <code type="none"> - openssl req -new -x509 -config /some/path/req.cnf \\ - -keyout /some/path/key.pem -out /some/path/cert.pem </code> - <p>where the option <c>-new</c> indicates that we want to create - a new certificate request and the option <c>-x509</c> implies - that a self-signed certificate is created. - </p> - </section> - - <section> - <title>Creating the OTP CA</title> - <p>The OTP CA is created by first creating a certificate request - with the command</p> - <code type="none"> - openssl req -new -config /some/path/req.cnf \\ - -keyout /some/path/key.pem -out /some/path/req.pem </code> - <p>and the ask the Erlang CA to sign it:</p> - <code type="none"> - openssl ca -batch -notext -config /some/path/req.cnf \\ - -extensions ca_cert -in /some/path/req.pem -out /some/path/cert.pem </code> - <p>where the option <c>-extensions</c> refers to a section in the - configuration file saying that it should create a CA certificate, - and not a plain user certificate. - </p> - <p>The <c>client</c> and <c>server</c> certificates are created - similarly, except that the option <c>-extensions</c> then has the - value <c>user_cert</c>. - </p> - </section> - </section> - - <section> - <title>An Example</title> - <p>The following module <c>create_certs</c> is used by the Erlang/OTP - SSL application for generating certificates to be used in tests. The - source code is also found in <c>ssl-X.Y.Z/examples/certs/src</c>. - </p> - <p>The purpose of the <c>create_certs:all/1</c> function is to make - it possible to provide from the <c>erl</c> command line, the - full path name of the <c>openssl</c> command. - </p> - <p>Note that the module creates temporary OpenSSL configuration files - for the <c>req</c> and <c>ca</c> subcommands. - </p> - <codeinclude file="../../examples/certs/src/make_certs.erl" tag="" type="erl"></codeinclude> - </section> -</chapter> - - diff --git a/lib/ssl/doc/src/insidecover.xml b/lib/ssl/doc/src/insidecover.xml deleted file mode 100644 index 4f3f5e5951..0000000000 --- a/lib/ssl/doc/src/insidecover.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="latin1" ?> -<!DOCTYPE bookinsidecover SYSTEM "bookinsidecover.dtd"> - -<bookinsidecover> -The Erlang/OTP SSL application includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/). Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. <br></br> -This product includes cryptographic software written by Eric Young ([email protected]). This product includes software written by Tim Hudson ([email protected]). Copyright (C) 1995-1998 Eric Young ([email protected]). All rights reserved. <br></br> -For further OpenSSL and SSLeay license information se the chapter <bold>Licenses</bold> -. <vfill></vfill> - <br></br> - <tt>http://www.erlang.org</tt> - <br></br> -</bookinsidecover> - - diff --git a/lib/ssl/doc/src/licenses.xml b/lib/ssl/doc/src/licenses.xml deleted file mode 100644 index 0969f9ad6e..0000000000 --- a/lib/ssl/doc/src/licenses.xml +++ /dev/null @@ -1,156 +0,0 @@ -<?xml version="1.0" encoding="latin1" ?> -<!DOCTYPE chapter SYSTEM "chapter.dtd"> - -<chapter> - <header> - <copyright> - <year>2003</year><year>2009</year> - <holder>Ericsson AB. All Rights Reserved.</holder> - </copyright> - <legalnotice> - 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 - compliance with the License. You should have received a copy of the - Erlang Public License along with this software. If not, it can be - retrieved online at http://www.erlang.org/. - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - the License for the specific language governing rights and limitations - under the License. - - </legalnotice> - - <title>Licenses</title> - <prepared>Peter Högfeldt</prepared> - <docno></docno> - <date>2003-05-26</date> - <rev>A</rev> - <file>licenses.xml</file> - </header> - <p> <marker id="licenses"></marker> -This chapter contains in extenso versions - of the OpenSSL and SSLeay licenses. - </p> - - <section> - <title>OpenSSL License</title> - <code type="none"> -/* ==================================================================== - * Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. All advertising materials mentioning features or use of this - * software must display the following acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - * - * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * [email protected]. - * - * 5. Products derived from this software may not be called "OpenSSL" - * nor may "OpenSSL" appear in their names without prior written - * permission of the OpenSSL Project. - * - * 6. Redistributions of any form whatsoever must retain the following - * acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit (http://www.openssl.org/)" - * - * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY - * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * ==================================================================== - * - * This product includes cryptographic software written by Eric Young - * ([email protected]). This product includes software written by Tim - * Hudson ([email protected]). - * - */ </code> - </section> - - <section> - <title>SSLeay License</title> - <code type="none"> -/* Copyright (C) 1995-1998 Eric Young ([email protected]) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young ([email protected]). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson ([email protected]). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young ([email protected])" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson ([email protected])" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] - */ </code> - </section> -</chapter> - - diff --git a/lib/ssl/doc/src/refman.xml b/lib/ssl/doc/src/refman.xml index 9658e229eb..68f84660f3 100644 --- a/lib/ssl/doc/src/refman.xml +++ b/lib/ssl/doc/src/refman.xml @@ -45,7 +45,8 @@ </description> <xi:include href="ssl_app.xml"/> <xi:include href="ssl.xml"/> - <xi:include href="old_ssl.xml"/> + <xi:include href="old_ssl.xml"/> + <xi:include href="ssl_session_cache_api.xml"/> </application> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 75aa8f2fe9..def61bcf03 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -21,13 +21,6 @@ </legalnotice> <title>ssl</title> - <prepared>Ingela Anderton Andin</prepared> - <responsible>Ingela Anderton Andin</responsible> - <docno></docno> - <approved></approved> - <checked></checked> - <date>2003-03-25</date> - <rev></rev> <file>ssl.xml</file> </header> <module>ssl</module> @@ -185,8 +178,17 @@ end {bad_cert, cert_expired}, {bad_cert, invalid_issuer}, {bad_cert, invalid_signature}, {bad_cert, name_not_permitted}, {bad_cert, cert_revoked} (not implemented yet), - {bad_cert, unknown_critical_extension} or {bad_cert, term()} (Will - be relevant later when an option is added for the user to be able to verify application specific extensions.) + {bad_cert, unknown_critical_extension} or {bad_cert, term()} + </item> + + + <tag>{validate_extensions_fun, fun()}</tag> + <item> + This options makes it possible to supply a fun to validate + possible application specific certificate extensions + during the certificat path validation. This option + will be better documented onec the public_key API is more + mature. </item> <tag>{depth, integer()}</tag> @@ -231,7 +233,8 @@ end </item> <tag>{ssl_imp, ssl_imp()}</tag> - <item>Specify which ssl implementation you want to use. + <item>Specify which ssl implementation you want to use. Defaults to + new. </item> <tag>{reuse_sessions, boolean()}</tag> @@ -248,6 +251,15 @@ end certificate, Compression is an enumeration integer and CipherSuite of type ciphersuite(). </item> + + <tag>{secure_renegotiate, boolean()}</tag> + <item>Specifies if to reject renegotiation attempt that does + not live up to RFC 5746. By default secure_renegotiate is + set to false e.i. secure renegotiation will be used if possible + but it will fallback to unsecure renegotiation if the peer + does not support RFC 5746. + </item> + </taglist> </section> @@ -414,7 +426,6 @@ end <type> <v>Socket = sslsocket()</v> <v>Cert = binary()</v> - <v>Subject = term()</v> </type> <desc> <p>The peer certificate is returned as a DER encoded binary. diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml index fac2053532..2ba6f48611 100644 --- a/lib/ssl/doc/src/ssl_app.xml +++ b/lib/ssl/doc/src/ssl_app.xml @@ -22,38 +22,11 @@ </legalnotice> <title>ssl</title> - <prepared>Peter Högfeldt</prepared> - <responsible>Peter Högfeldt</responsible> - <docno></docno> - <approved>Peter Högfeldt</approved> - <checked>Peter Högfeldt</checked> - <date>2005-03-10</date> - <rev>E</rev> <file>ssl_app.sgml</file> </header> <app>ssl</app> - <appsummary>The SSL Application</appsummary> - <description> - <p>The Secure Socket Layer (SSL) application provides secure - socket communication over TCP/IP. Note that this documentation - is mainly valid for the old ssl implementation and will - be replaced in a future release. - </p> - </description> - - <section> - <title>Warning</title> - <p>In previous versions of Erlang/OTP SSL it was advised, as a - work-around, to set the operating system environment variable - <c>SSL_CERT_FILE</c> to point at a file containing CA - certificates. That variable is no longer needed, and is not - recognised by Erlang/OTP SSL any more. - </p> - <p>However, the OpenSSL package does interpret that environment - variable. Hence a setting of that variable might have - unpredictable effects on the Erlang/OTP SSL application. It is - therefore adviced to not used that environment variable at all.</p> - </section> + <appsummary>The SSL application provides secure communication over + sockets.</appsummary> <section> <title>Environment</title> @@ -63,115 +36,43 @@ </p> <p>Note that the environment parameters can be set on the command line, for instance,</p> - <p><c>erl ... -ssl protocol_version '[sslv2,sslv3]' ...</c>. + <p><c>erl ... -ssl protocol_version '[sslv3, tlsv1]' ...</c>. </p> <taglist> - <tag><c><![CDATA[ephemeral_rsa = true | false <optional>]]></c></tag> + <tag><c><![CDATA[protocol_version = [sslv3|tlsv1] <optional>]]></c>.</tag> <item> - <p>Enables all SSL servers (those that listen and accept) - to use ephemeral RSA key generation when a clients connect with - weak handshake cipher specifications, that need equally weak - ciphers from the server (i.e. obsolete restrictions on export - ciphers). Default is <c>false</c>. - </p> + <p>Protocol that will be supported by started clients and + servers. If this option is not set it will default to all + protocols currently supported by the erlang ssl application. + Note that this option may be overridden by the version option + to ssl:connect/[2,3] and ssl:listen/2. + </p> </item> - <tag><c><![CDATA[debug = true | false <optional>]]></c></tag> - <item> - <p>Causes debug information to be written to standard - output. Default is <c>false</c>. - </p> - </item> - <tag><c><![CDATA[debugdir = path() | false <optional>]]></c></tag> - <item> - <p>Causes debug information output controlled by <c>debug</c> - and <c>msgdebug</c> to be printed to a file named - <c><![CDATA[ssl_esock.<pid>.log]]></c> in the directory specified by - <c>debugdir</c>, where <c><![CDATA[<pid>]]></c> is the operating system - specific textual representation of the process identifier - of the external port program of the SSL application. Default - is <c>false</c>, i.e. no log file is produced. - </p> - </item> - <tag><c><![CDATA[msgdebug = true | false <optional>]]></c></tag> - <item> - <p>Sets <c>debug = true</c> and causes also the contents - of low level messages to be printed to standard output. - Default is <c>false</c>. - </p> - </item> - <tag><c><![CDATA[port_program = string() | false <optional>]]></c></tag> - <item> - <p>Name of port program. The default is <c>ssl_esock</c>. - </p> - </item> - <tag><c><![CDATA[protocol_version = [sslv2|sslv3|tlsv1] <optional>]]></c>.</tag> + + <tag><c><![CDATA[session_lifetime = integer() <optional>]]></c></tag> <item> - <p>Name of protocols to use. If this option is not set, - all protocols are assumed, i.e. the default value is - <c>[sslv2, sslv3, tlsv1]</c>. - </p> + <p>The lifetime of session data in seconds. + </p> </item> - <tag><c><![CDATA[proxylsport = integer() | false <optional>]]></c></tag> + + <tag><c><![CDATA[session_cb = atom() <optional>]]></c></tag> <item> - <p>Define the port number of the listen port of the - SSL port program. Almost never is this option needed. + <p> + Name of session cache callback module that implements + the ssl_session_cache_api behavior, defaults to + ssl_session_cache.erl. </p> </item> - <tag><c><![CDATA[proxylsbacklog = integer() | false <optional>]]></c></tag> + + <tag><c><![CDATA[session_cb_init_args = list() <optional>]]></c></tag> <item> - <p>Set the listen queue size of the listen port of the - SSL port program. The default is 128. - </p> + <p> + List of arguments to the init function in session cache + callback module, defaults to []. + </p> </item> - </taglist> - </section> - - <section> - <title>OpenSSL libraries</title> - <p>The current implementation of the Erlang SSL application is - based on the <em>OpenSSL</em> package version 0.9.7 or higher. - There are source and binary releases on the web. - </p> - <p>Source releases of OpenSSL can be downloaded from the <url href="http://www.openssl.org">OpenSSL</url> project home page, - or mirror sites listed there. - </p> - <p>The same URL also contains links to some compiled binaries and - libraries of OpenSSL (see the <c>Related/Binaries</c> menu) of - which the <url href="http://www.shininglightpro.com/search.php?searchname=Win32+OpenSSL">Shining Light Productions Win32 and OpenSSL</url> pages are of - interest for the Win32 user. - </p> - <p>For some Unix flavours there are binary packages available - on the net. - </p> - <p>If you cannot find a suitable binary OpenSSL package, you - have to fetch an OpenSSL source release and compile it. - </p> - <p>You then have to compile and install the libraries - <c>libcrypto.so</c> and <c>libssl.so</c> (Unix), or the - libraries <c>libeay32.dll</c> and <c>ssleay32.dll</c> (Win32). - </p> - <p>For Unix The <c>ssl_esock</c> port program is delivered linked - to OpenSSL libraries in <c>/usr/local/lib</c>, but the default - dynamic linking will also accept libraries in <c>/lib</c> and - <c>/usr/lib</c>. - </p> - <p>If that is not applicable to the particular Unix operating - system used, the example <c>Makefile</c> in the SSL - <c>priv/obj</c> directory, should be used as a guide to - relinking the final version of the port program. - </p> - <p>For <c>Win32</c> it is only required that the libraries can be - found from the <c>PATH</c> environment variable, or that they - reside in the appropriate <c>SYSTEM32</c> directory; hence no - particular relinking is need. Hence no example <c>Makefile</c> - for Win32 is provided.</p> - </section> - <section> - <title>Restrictions</title> - <p>Users must be aware of export restrictions and patent rights - concerning cryptographic software. - </p> + </taglist> </section> <section> @@ -180,5 +81,3 @@ </section> </appref> - - diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml index c743cd67a3..4067fb8a22 100644 --- a/lib/ssl/doc/src/ssl_distribution.xml +++ b/lib/ssl/doc/src/ssl_distribution.xml @@ -32,7 +32,13 @@ <file>ssl_distribution.xml</file> </header> <p>This chapter describes how the Erlang distribution can use - SSL to get additional verification and security.</p> + SSL to get additional verification and security. + + <note><p>Note this + documentation is written for the old ssl implementation and + will be updated for the new one once this functionallity is + supported by the new implementation.</p></note> + </p> <section> <title>Introduction</title> diff --git a/lib/ssl/doc/src/ssl_protocol.xml b/lib/ssl/doc/src/ssl_protocol.xml index 3dc2332795..726b9a4eeb 100644 --- a/lib/ssl/doc/src/ssl_protocol.xml +++ b/lib/ssl/doc/src/ssl_protocol.xml @@ -13,337 +13,138 @@ compliance with the License. You should have received a copy of the Erlang Public License along with this software. If not, it can be retrieved online at http://www.erlang.org/. - + Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - + </legalnotice> - <title>The SSL Protocol</title> - <prepared>Peter Högfeldt</prepared> - <docno></docno> - <date>2003-04-28</date> - <rev>PA2</rev> + <title>Transport Layer Security (TLS) and its predecessor, Secure Socket Layer (SSL)</title> <file>ssl_protocol.xml</file> </header> - <p>Here we provide a short introduction to the SSL protocol. We only - consider those part of the protocol that are important from a - programming point of view. - </p> - <p>For a very good general introduction to SSL and TLS see the book - <cite id="rescorla"></cite>. - </p> - <p><em>Outline:</em></p> - <list type="bulleted"> - <item>Two types of connections - connection: handshake, data transfer, and - shutdown - - SSL/TLS protocol - server must have certificate - what the the - server sends to the client - client may verify the server - - server may ask client for certificate - what the client sends to - the server - server may then verify the client - verification - - certificate chains - root certificates - public keys - key - agreement - purpose of certificate - references</item> - </list> + + <p>The erlang ssl application currently supports SSL 3.0 and TLS 1.0 + RFC 2246, and will in the future also support later versions of TLS. + SSL 2.0 is not supported. + </p> - <section> - <title>SSL Connections</title> - <p>The SSL protocol is implemented on top of the TCP/IP protocol. - From an endpoint view it also has the same type of connections - as that protocol, almost always created by calls to socket - interface functions <em>listen</em>, <em>accept</em> and - <em>connect</em>. The endpoints are <em>servers</em> and - <em>clients</em>. - </p> - <p>A <em>server</em><em>listen</em>s for connections on a - specific address and port. This is done once. The server then - <em>accept</em>s each connections on that same address and - port. This is typically done indefinitely many times. - </p> - <p>A <em>client</em> connects to a server on a specific address - and port. For each purpose this is done once. - </p> - <p>For a plain TCP/IP connection the establishment of a connection - (through an accept or a connect) is followed by data transfer between - the client and server, finally ended by a connection close. - </p> - <p>An SSL connection also consists of data transfer and connection - close, However, the data transfer contains encrypted data, and - in order to establish the encryption parameters, the data - transfer is preceded by an SSL <em>handshake</em>. In this - handshake the server plays a dominant role, and the main - instrument used in achieving a valid SSL connection is the - server's <em>certificate</em>. We consider certificates in the - next section, and the SSL handshake in a subsequent section.</p> - </section> + <p>By default erlang ssl is run over the TCP/IP protocol even + though you could plug in an other reliable transport protocol + with the same API as gen_tcp.</p> + + <p>If a client and server wants to use an upgrade mechanism, such as + defined by RFC2817, to upgrade a regular TCP/IP connection to a ssl + connection the erlang ssl API supports this. This can be useful for + things such as supporting HTTP and HTTPS on the same port and + implementing virtual hosting. + </p> <section> - <title>Certificates</title> - <p>A certificate is similar to a driver's license, or a - passport. The holder of the certificate is called the - <em>subject</em>. First of all the certificate identifies the - subject in terms of the name of the subject, its postal address, - country name, company name (if applicable), etc. - </p> - <p>Although a driver's license is always issued by a well-known and - distinct authority, a certificate may have an <em>issuer</em> - that is not so well-known. Therefore a certificate also always - contains information on the issuer of the certificate. That - information is of the same type as the information on the - subject. The issuer of a certificate also signs the certificate - with a <em>digital signature</em> (the signature is an inherent - part of the certificate), which allow others to verify that the - issuer really is the issuer of the certificate. - </p> - <p>Now that a certificate can be checked by verifying the - signature of the issuer, the question is how to trust the - issuer. The answer to this question is to require that there is - a certificate for the issuer as well. That issuer has in turn an - issuer, which must also have a certificate, and so on. This - <em>certificate chain</em> has to have en end, which then must - be a certificate that is trusted by other means. We shall cover - this problem of <em>authentication</em> in a subsequent - section. - </p> + <title>Security overview</title> + + <p>To achive authentication and privacy the client and server will + perform a TLS Handshake procedure before transmitting or receiving + any data. During the handshake they agree on a protocol version and + cryptographic algorithms, they generate shared secrets using public + key cryptographics and optionally authenticate each other with + digital certificates.</p> </section> - + <section> - <title>Encryption Algorithms</title> - <p>An encryption algorithm is a mathematical algorithm for - encryption and decryption of messages (arrays of bytes, - say). The algorithm as such is always required to be publicly - known, otherwise its strength cannot be evaluated, and hence it - cannot be used reliably. The secrecy of an encrypted message is - not achieved by the secrecy of the algorithm used, but by the - secrecy of the <em>keys</em> used as input to the encryption and - decryption algorithms. For an account of cryptography in general - see <cite id="schneier"></cite>. - </p> - <p>There are two classes of encryption algorithms: <em>symmetric key</em> algorithms and <em>public key</em> algorithms. Both - types of algorithms are used in the SSL protocol. - </p> - <p>In the sequel we assume holders of keys keep them secret (except - public keys) and that they in that sense are trusted. How a - holder of a secret key is proved to be the one it claims to be - is a question of <em>authentication</em>, which, in the context - of the SSL protocol, is described in a section further below. - </p> - - <section> - <title>Symmetric Key Algorithms</title> - <p>A <em>symmetric key</em> algorithm has one key only. The key - is used for both encryption and decryption. Obviously the key - of a symmetric key algorithm must always be kept secret by the - users of the key. DES is an example of a symmetric key - algorithm. - </p> - <p>Symmetric key algorithms are fast compared to public key - algorithms. They are therefore typically used for encrypting - bulk data. - </p> - </section> - - <section> - <title>Public Key Algorithms</title> - <p>A <em>public key</em> algorithm has two keys. Any of the two - keys can be used for encryption. A message encrypted with one - of the keys, can only be decrypted with the other key. One of - the keys is public (known to the world), while the other key - is private (i.e. kept secret) by the owner of the two keys. - </p> - <p>RSA is an example of a public key algorithm. - </p> - <p>Public key algorithms are slow compared to symmetric key - algorithms, and they are therefore seldom used for bulk data - encryption. They are therefore only used in cases where the - fact that one key is public and the other is private, provides - features that cannot be provided by symmetric algorithms. - </p> - </section> - - <section> - <title>Digital Signature Algorithms</title> - <p>An interesting feature of a public key algorithm is that its - public and private keys can both be used for encryption. - Anyone can use the public key to encrypt a message, and send - that message to the owner of the private key, and be sure of - that only the holder of the private key can decrypt the - message. - </p> - <p>On the other hand, the owner of the private key can encrypt a - message with the private key, thus obtaining an encrypted - message that can decrypted by anyone having the public key. - </p> - <p>The last approach can be used as a digital signature - algorithm. The holder of the private key signs an array of - bytes by performing a specified well-known <em>message digest algorithm</em> to compute a hash of the array, encrypts the - hash value with its private key, an then presents the original - array, the name of the digest algorithm, and the encryption of - the hash value as a <em>signed array of bytes</em>. - </p> - <p>Now anyone having the public key, can decrypt the encrypted - hash value with that key, compute the hash with the specified - digest algorithm, and check that the hash values compare equal - in order to verify that the original array was indeed signed - by the holder of the private key. - </p> - <p>What we have accounted for so far is by no means all that can - be said about digital signatures (see <cite id="schneier"></cite>for - further details). - </p> - </section> - - <section> - <title>Message Digests Algorithms</title> - <p>A message digest algorithm is a hash function that accepts - an array bytes of arbitrary but finite length of input, and - outputs an array of bytes of fixed length. Such an algorithm - is also required to be very hard to invert. - </p> - <p>MD5 (16 bytes output) and SHA1 (20 bytes output) are examples - of message digest algorithms. - </p> - </section> + <title>Data Privacy and Integrity</title> + + <p>A <em>symmetric key</em> algorithm has one key only. The key is + used for both encryption and decryption. These algoritms are fast + compared to public key algorithms (using two keys, a public and a + private one) and are therefore typically used for encrypting bulk + data. + </p> + + <p>The keys for the symmetric encryption are generated uniquely + for each connection and are based on a secret negotiated + in the TLS handshake. </p> + + <p>The TLS handsake protocol and data transfer is run on top of + the TLS Record Protocol that uses a keyed-hash MAC (Message + Authenticity Code), or HMAC, to protect the message's data + integrity. From the TLS RFC "A Message Authentication Code is a + one-way hash computed from a message and some secret data. It is + difficult to forge without knowing the secret data. Its purpose is + to detect if the message has been altered." + </p> + </section> - <section> - <title>SSL Handshake</title> - <p>The main purpose of the handshake performed before an an SSL - connection is established is to negotiate the encryption - algorithm and key to be used for the bulk data transfer between - the client and the server. We are writing <em>the</em> key, - since the algorithm to choose for bulk encryption one of the - symmetric algorithms. - </p> - <p>There is thus only one key to agree upon, and obviously that - key has to be kept secret between the client and the server. To - obtain that the handshake has to be encrypted as well. - </p> - <p>The SSL protocol requires that the server always sends its - certificate to the client in the beginning of the handshake. The - client then retrieves the server's public key from the - certificate, which means that the client can use the server's - public key to encrypt messages to the server, and the server can - decrypt those messages with its private key. Similarly, the - server can encrypt messages to the client with its private key, - and the client can decrypt messages with the server's public - key. It is thus is with the server's public and private keys - that messages in the handshake are encrypted and decrypted, and - hence the key agreed upon for symmetric encryption of bulk data - can be kept secret (there are more things to consider to really - keep it secret, see <cite id="rescorla"></cite>). - </p> - <p>The above indicates that the server does not care who is - connecting, and that only the client has the possibility to - properly identify the server based on the server's certificate. - That is indeed true in the minimal use of the protocol, but it - is possible to instruct the server to request the certificate of - the client, in order to have a means to identify the client, but - it is by no means required to establish an SSL connection. - </p> - <p>If a server request the client certificate, it verifies, as a - part of the protocol, that the client really holds the private - key of the certificate by sending the client a string of bytes - to encrypt with its private key, which the server then decrypts - with the client's public key, the result of which is compared - with the original string of bytes (a similar procedure is always - performed by the client when it has received the server's - certificate). - </p> - <p>The way clients and servers <em>authenticate</em> each other, - i.e. proves that their respective peers are what they claim to - be, is the topic of the next section. - </p> - </section> + <section> + <title>Digital Certificates</title> + <p>A certificate is similar to a driver's license, or a + passport. The holder of the certificate is called the + <em>subject</em>. The certificate is signed + with the private key of the issuer of the certificate. A chain + of trust is build by having the issuer in its turn being + certified by an other certificate and so on until you reach the + so called root certificate that is self signed e.i. issued + by itself.</p> + + <p>Certificates are issued by <em>certification + authorities</em> (<em>CA</em>s) only. There are a handful of + top CAs in the world that issue root certificates. You can + examine the certificates of several of them by clicking + through the menus of your web browser. + </p> + </section> + + <section> + <title>Authentication of Sender</title> + + <p>Authentication of the sender is done by public key path + validation as defined in RFC 3280. Simplified that means that + each certificate in the certificate chain is issued by the one + before, the certificates attributes are valid ones, and the + root cert is a trusted cert that is present in the trusted + certs database kept by the peer.</p> + + <p>The server will always send a certificate chain as part of + the TLS handshake, but the client will only send one if + the server requests it. If the client does not have + an appropriate certificate it may send an "empty" certificate + to the server.</p> + + <p>The client may choose to accept some path evaluation errors + for instance a web browser may ask the user if they want to + accept an unknown CA root certificate. The server, if it request + a certificate, will on the other hand not accept any path validation + errors. It is configurable if the server should accept + or reject an "empty" certificate as response to + a certificate request.</p> + </section> + + <section> + <title>TLS Sessions</title> + + <p>From the TLS RFC "A TLS session is an association between a + client and a server. Sessions are created by the handshake + protocol. Sessions define a set of cryptographic security + parameters, which can be shared among multiple + connections. Sessions are used to avoid the expensive negotiation + of new security parameters for each connection."</p> - <section> - <title>Authentication</title> - <p>As we have already seen the reception of a certificate from a - peer is not enough to prove that the peer is authentic. More - certificates are needed, and we have to consider how certificates - are issued and on what grounds. - </p> - <p>Certificates are issued by <em>certification authorities</em> - (<em>CA</em>s) only. They issue certificates both for other CAs - and ordinary users (which are not CAs). - </p> - <p>Certain CAs are <em>top CAs</em>, i.e. they do not have a - certificate issued by another CA. Instead they issue their own - certificate, where the subject and issuer part of the - certificate are identical (such a certificate is called a - self-signed certificate). A top CA has to be well-known, and has - to have a publicly available policy telling on what grounds it - issues certificates. - </p> - <p>There are a handful of top CAs in the world. You can examine the - certificates of several of them by clicking through the menus of - your web browser. - </p> - <p>A top CA typically issues certificates for other CAs, called - <em>intermediate CAs</em>, but possibly also to ordinary users. Thus - the certificates derivable from a top CA constitute a tree, where - the leaves of the tree are ordinary user certificates. - </p> - <p>A <em>certificate chain</em> is an ordered sequence of - certificates, <c>C1, C2, ..., Cn</c>, say, where <c>C1</c> is a - top CA certificate, and where <c>Cn</c> is an ordinary user - certificate, and where the holder of <c>C1</c> is the issuer of - <c>C2</c>, the holder of <c>C2</c> is the issuer of <c>C3</c>, - ..., and the holder of <c>Cn-1</c> is the issuer of <c>Cn</c>, - the ordinary user certificate. The holders of <c>C2, C3, ..., Cn-1</c> are then intermediate CAs. - </p> - <p>Now to verify that a certificate chain is unbroken we have to - take the public key from each certificate <c>Ck</c>, and apply - that key to decrypt the signature of certificate <c>Ck-1</c>, - thus obtaining the message digest computed by the holder of the - <c>Ck</c> certificate, compute the real message digest of the - <c>Ck-1</c> certificate and compare the results. If they compare - equal the link of the chain between <c>Ck</c> and <c>Ck-1</c> is - considered to unbroken. This is done for each link k = 1, 2, - ..., n-1. If all links are found to be unbroken, the user - certificate <c>Cn</c> is considered authenticated. - </p> + <p>Session data is by default kept by the ssl application in a + memory storage hence session data will be lost at application + restart or takeover. Users may define their own callback module + to handle session data storage if persistent data storage is + required. Session data will also be invalidated after 24 hours + from it was saved, for security reasons. It is of course + possible to configure the amount of time the session data should be + saved.</p> - <section> - <title>Trusted Certificates</title> - <p>Now that there is a way to authenticate a certificate by - checking that all links of a certificate chain are unbroken, - the question is how you can be sure to trust the certificates - in the chain, and in particular the top CA certificate of the - chain. - </p> - <p>To provide an answer to that question consider the - perspective of a client, which have just received the - certificate of the server. In order to authenticate the server - the client has to construct a certificate chain and to prove - that the chain is unbroken. The client has to have a set of CA - certificates (top CA or intermediate CA certificates) not - obtained from the server, but obtained by other means. Those - certificates are kept <c>locally</c> by the client, and are - trusted by the client. - </p> - <p>More specifically, the client does not really have to have - top CA certificates in its local storage. In order to - authenticate a server it is sufficient for the client to - posses the trusted certificate of the issuer of the server - certificate. - </p> - <p>Now that is not the whole story. A server can send an - (incomplete) certificate chain to its client, and then the - task of the client is to construct a certificate chain that - begins with a trusted certificate and ends with the server's - certificate. (A client can also send a chain to its server, - provided the server requested the client's certificate.) - </p> - <p>All this means that an unbroken certificate chain begins with - a trusted certificate (top CA or not), and ends with the peer - certificate. That is the end of the chain is obtained from the - peer, but the beginning of the chain is obtained from local - storage, which is considered trusted. - </p> - </section> - </section> -</chapter> + <p>Ssl clients will by default try to reuse an available session, + ssl servers will by default agree to reuse sessions when clients + ask to do so.</p> + + </section> + </chapter> diff --git a/lib/ssl/doc/src/ssl_session_cache_api.xml b/lib/ssl/doc/src/ssl_session_cache_api.xml new file mode 100644 index 0000000000..7b70c6cf34 --- /dev/null +++ b/lib/ssl/doc/src/ssl_session_cache_api.xml @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>1999</year><year>2010</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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 + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + <title>ssl</title> + <file>ssl_session_cache_api.xml</file> + </header> + <module>ssl_session_cache_api</module> + <modulesummary>Defines the API for the TLS session cache so + that the datastorge scheme can be replaced by + defining a new callback module implementing this API.</modulesummary> + + <section> + <title>Common Data Types</title> + + <p>The following data types are used in the functions below: + </p> + + <p><c>cache_ref() = opaque()</c></p> + + <p><c>key() = {partialkey(), session_id()}</c></p> + + <p><c>partialkey() = opaque()</c></p> + + <p><c>session_id() = binary()</c></p> + + <p><c>session() = opaque()</c></p> + + </section> + + <funcs> + + <func> + <name>delete(Cache, Key) -> _</name> + <fsummary></fsummary> + <type> + <v> Cache = cache_ref()</v> + <v> Key = key()</v> + </type> + <desc> + <p> Delets a cache entry. Will only be called from the cache + handling process. + </p> + </desc> + </func> + + <func> + <name>foldl(Fun, Acc0, Cache) -> Acc</name> + <fsummary></fsummary> + <type> + <v></v> + </type> + <desc> + <p>Calls Fun(Elem, AccIn) on successive elements of the + cache, starting with AccIn == Acc0. Fun/2 must return a new + accumulator which is passed to the next call. The function returns + the final value of the accumulator. Acc0 is returned if the cache is + empty. + </p> + </desc> + </func> + + <func> + <name>init() -> opaque() </name> + <fsummary>Return cache reference</fsummary> + <type> + <v></v> + </type> + <desc> + <p>Performes possible initializations of the cache and returns + a reference to it that will be used as parameter to the other + api functions. Will be called by the cache handling processes + init function, hence puting the same requierments on it as + a normal process init function. + </p> + </desc> + </func> + + <func> + <name>lookup(Cache, Key) -> Entry</name> + <fsummary> Looks up a cach entry.</fsummary> + <type> + <v> Cache = cache_ref()</v> + <v> Key = key()</v> + <v> Entry = session() | undefined </v> + </type> + <desc> + <p>Looks up a cach entry. Should be callable from any + process. + </p> + </desc> + </func> + + <func> + <name>select_session(Cache, PartialKey) -> [session()]</name> + <fsummary>>Selects sessions that could be reused.</fsummary> + <type> + <v> Cache = cache_ref()</v> + <v> PartialKey = partialkey()</v> + <v> Session = session()</v> + </type> + <desc> + <p>Selects sessions that could be reused. Should be callable + from any process. + </p> + </desc> + </func> + + <func> + <name>terminate(Cache) -> _</name> + <fsummary>Called by the process that handles the cache when it + is aboute to terminat.</fsummary> + <type> + <v>Cache = term() - as returned by init/0</v> + </type> + <desc> + <p>Takes care of possible cleanup that is needed when the + cache handling process terminates. + </p> + </desc> + </func> + + <func> + <name>update(Cache, Key, Session) -> _</name> + <fsummary> Caches a new session or updates a already cached one.</fsummary> + <type> + <v> Cache = cache_ref()</v> + <v> Key = key()</v> + <v> Session = session()</v> + </type> + <desc> + <p> Caches a new session or updates a already cached one. Will + only be called from the cache handling process. + </p> + </desc> + </func> + + </funcs> + +</erlref> diff --git a/lib/ssl/doc/src/usersguide.xml b/lib/ssl/doc/src/usersguide.xml index 76ee13dd23..6528c00a0b 100644 --- a/lib/ssl/doc/src/usersguide.xml +++ b/lib/ssl/doc/src/usersguide.xml @@ -23,35 +23,17 @@ <title>SSL User's Guide</title> <prepared>OTP Team</prepared> - <docno></docno> <date>2003-05-26</date> - <rev>B</rev> <file>usersguide.sgml</file> </header> <description> <p>The <em>SSL</em> application provides secure communication over - sockets. Note that this users guide was written for - the old ssl implementation that is now being phased out and - will be replaced by a new guide in a future release. - </p> - <p>This product includes software developed by the OpenSSL Project for - use in the OpenSSL Toolkit (http://www.openssl.org/). - </p> - <p>This product includes cryptographic software written by Eric Young - ([email protected]). - </p> - <p>This product includes software written by Tim Hudson - ([email protected]). - </p> - <p>For full OpenSSL and SSLeay license texts, see <seealso marker="licenses#licenses">Licenses</seealso>. + sockets. </p> </description> <xi:include href="ssl_protocol.xml"/> <xi:include href="using_ssl.xml"/> - <xi:include href="pkix_certs.xml"/> - <xi:include href="create_certs.xml"/> <xi:include href="ssl_distribution.xml"/> - <xi:include href="licenses.xml"/> </part> diff --git a/lib/ssl/doc/src/using_ssl.xml b/lib/ssl/doc/src/using_ssl.xml new file mode 100644 index 0000000000..4bdd8f97b4 --- /dev/null +++ b/lib/ssl/doc/src/using_ssl.xml @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2003</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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 + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Using the SSL API</title> + <file>using_ssl.xml</file> + </header> + + <section> + <title>General information</title> + <p>To see relevant version information for ssl you can + call ssl:versions/0</p> + + <p>To see all supported cipher suites + call ssl:cipher_suites/0. Note that available cipher suites + for a connection will depend on your certificate. It is also + possible to specify a specific cipher suite(s) that you + want your connection to use. Default is to use the strongest + available.</p> + + </section> + + <section> + <title>Setting up connections</title> + + <p>Here follows some small example of how to set up client/server connections + using the erlang shell. The returned value of the sslsocket has been abbreviated with + <c>[...]</c> as it can be fairly large and is opaque.</p> + + <section> + <title>Minmal example</title> + + <note><p> The minimal setup is not the most secure setup of ssl.</p> + </note> + + <p> Start server side</p> + <code type="erl">1 server> ssl:start(). +ok</code> + + <p>Create a ssl listen socket</p> + <code type="erl">2 server> {ok, ListenSocket} = +ssl:listen(9999, [{certfile, "cert.pem"}, {keyfile, "key.pem"},{reuseaddr, true}]). +{ok,{sslsocket, [...]}}</code> + + <p>Do a transport accept on the ssl listen socket</p> + <code type="erl">3 server> {ok, Socket} = ssl:transport_accept(ListenSocket). +{ok,{sslsocket, [...]}}</code> + + <p>Start client side</p> + <code type="erl">1 client> ssl:start(). +ok</code> + + <code type="erl">2 client> {ok, Socket} = ssl:connect("localhost", 9999, [], infinity). +{ok,{sslsocket, [...]}}</code> + + <p>Do the ssl handshake</p> + <code type="erl">4 server> ok = ssl:ssl_accept(Socket). +ok</code> + + <p>Send a messag over ssl</p> + <code type="erl">5 server> ssl:send(Socket, "foo"). +ok</code> + + <p>Flush the shell message queue to see that we got the message + sent on the server side</p> + <code type="erl">3 client> flush(). +Shell got {ssl,{sslsocket,[...]},"foo"} +ok</code> + </section> + + <section> + <title>Upgrade example</title> + + <note><p> To upgrade a TCP/IP connection to a ssl connection the + client and server have to aggre to do so. Agreement + may be accompliced by using a protocol such the one used by HTTP + specified in RFC 2817.</p> </note> + + <p>Start server side</p> + <code type="erl">1 server> ssl:start(). +ok</code> + + <p>Create a normal tcp listen socket</p> + <code type="erl">2 server> {ok, ListenSocket} = gen_tcp:listen(9999, [{reuseaddr, true}]). +{ok, #Port<0.475>}</code> + + <p>Accept client connection</p> + <code type="erl">3 server> {ok, Socket} = gen_tcp:accept(ListenSocket). +{ok, #Port<0.476>}</code> + + <p>Start client side</p> + <code type="erl">1 client> ssl:start(). +ok</code> + + <code type="erl">2 client> {ok, Socket} = gen_tcp:connect("localhost", 9999, [], infinity).</code> + + <p>Make sure active is set to false before trying + to upgrade a connection to a ssl connection, otherwhise + ssl handshake messages may be deliverd to the wrong process.</p> + <code type="erl">4 server> inet:setopts(Socket, [{active, false}]). +ok</code> + + <p>Do the ssl handshake.</p> + <code type="erl">5 server> {ok, SSLSocket} = ssl:ssl_accept(Socket, [{cacertfile, "cacerts.pem"}, +{certfile, "cert.pem"}, {keyfile, "key.pem"}]). +{ok,{sslsocket,[...]}}</code> + + <p> Upgrade to a ssl connection. Note that the client and server + must agree upon the upgrade and the server must call + ssl:accept/2 before the client calls ssl:connect/3.</p> + <code type="erl">3 client>{ok, SSLSocket} = ssl:connect(Socket, [{cacertfile, "cacerts.pem"}, +{certfile, "cert.pem"}, {keyfile, "key.pem"}], infinity). +{ok,{sslsocket,[...]}}</code> + + <p>Send a messag over ssl</p> + <code type="erl">4 client> ssl:send(SSLSocket, "foo"). +ok</code> + + <p>Set active true on the ssl socket</p> + <code type="erl">4 server> ssl:setopts(SSLSocket, [{active, true}]). +ok</code> + + <p>Flush the shell message queue to see that we got the message + sent on the client side</p> + <code type="erl">5 server> flush(). +Shell got {ssl,{sslsocket,[...]},"foo"} +ok</code> + </section> + </section> + </chapter> diff --git a/lib/ssl/doc/src/using_ssl.xmlsrc b/lib/ssl/doc/src/using_ssl.xmlsrc deleted file mode 100644 index ba74dcfef4..0000000000 --- a/lib/ssl/doc/src/using_ssl.xmlsrc +++ /dev/null @@ -1,113 +0,0 @@ -<?xml version="1.0" encoding="latin1" ?> -<!DOCTYPE chapter SYSTEM "chapter.dtd"> - -<chapter> - <header> - <copyright> - <year>2003</year><year>2009</year> - <holder>Ericsson AB. All Rights Reserved.</holder> - </copyright> - <legalnotice> - 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 - compliance with the License. You should have received a copy of the - Erlang Public License along with this software. If not, it can be - retrieved online at http://www.erlang.org/. - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - the License for the specific language governing rights and limitations - under the License. - - </legalnotice> - - <title>Using the SSL application</title> - <prepared>Peter Högfeldt</prepared> - <docno></docno> - <date>2003-04-23</date> - <rev>PA2</rev> - <file>using_ssl.xml</file> - </header> - <p>Here we provide an introduction to using the Erlang/OTP SSL - application, which is accessed through the <c>ssl</c> interface - module. - </p> - <p>We also present example code in the Erlang module - <c>client_server</c>, also provided in the directory - <c>ssl-X.Y.Z/examples</c>, with source code in <c>src</c> and the - compiled module in <c>ebin</c> of that directory. - </p> - - <section> - <title>The ssl Module</title> - <p>The <c>ssl</c> module provides the user interface to the Erlang/OTP - SSL application. The interface functions provided are very similar - to those provided by the <c>gen_tcp</c> and <c>inet</c> modules. - </p> - <p>Servers use the interface functions <c>listen</c> and - <c>accept</c>. The <c>listen</c> function specifies a TCP port - to to listen to, and each call to the <c>accept</c> function - establishes an incoming connection. - </p> - <p>Clients use the <c>connect</c> function which specifies the address - and port of a server to connect to, and a successful call establishes - such a connection. - </p> - <p>The <c>listen</c> and <c>connect</c> functions have almost all - the options that the corresponding functions in <c>gen_tcp/</c> have, - but there are also additional options specific to the SSL protocol. - </p> - <p>The most important SSL specific option is the <c>cacertfile</c> - option which specifies a local file containing trusted CA - certificates which are and used for peer authentication. This - option is used by clients and servers in case they want to - authenticate their peers. - </p> - <p>The <c>certfile</c> option specifies a local path to a file - containing the certificate of the holder of the connection - endpoint. In case of a server endpoint this option is mandatory - since the contents of the sever certificate is needed in the - the handshake preceding the establishment of a connection. - </p> - <p>Similarly, the <c>keyfile</c> option points to a local file - containing the private key of the holder of the endpoint. If the - <c>certfile</c> option is present, this option has to be - specified as well, unless the private key is provided in the - same file as specified by the <c>certfile</c> option (a - certificate and a private key can thus coexist in the same file). - </p> - <p>The <c>verify</c> option specifies how the peer should be verified: - </p> - <taglist> - <tag>0</tag> - <item>Do not verify the peer,</item> - <tag>1</tag> - <item>Verify peer,</item> - <tag>2</tag> - <item>Verify peer, fail the verification if the peer has no - certificate. </item> - </taglist> - <p>The <c>depth</c> option specifies the maximum length of the - verification certificate chain. Depth = 0 means the peer - certificate, depth = 1 the CA certificate, depth = 2 the next CA - certificate etc. If the verification process does not find a - trusted CA certificate within the maximum length, the verification - fails. - </p> - <p>The <c>ciphers</c> option specifies which ciphers to use (a - string of colon separated cipher names). To obtain a list of - available ciphers, evaluate the <c>ssl:ciphers/0</c> function - (the SSL application has to be running). - </p> - </section> - - <section> - <title>A Client-Server Example</title> - <p>Here is a simple client server example. - </p> - <codeinclude file="../../examples/src/client_server.erl" tag="" type="erl"></codeinclude> - </section> -</chapter> - - - diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 000ceb241e..df4cd7c84d 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -34,10 +34,12 @@ %% Should be deprecated as soon as old ssl is removed %%-deprecated({pid, 1, next_major_release}). +-deprecated({peercert, 2, next_major_release}). -include("ssl_int.hrl"). -include("ssl_internal.hrl"). -include("ssl_record.hrl"). +-include("ssl_cipher.hrl"). -include_lib("public_key/include/public_key.hrl"). @@ -49,11 +51,11 @@ }). %%-------------------------------------------------------------------- -%% Function: start([, Type]) -> ok +-spec start() -> ok. +-spec start(permanent | transient | temporary) -> ok. %% -%% Type = permanent | transient | temporary -%% -%% Description: Starts the ssl application. Default type +%% Description: Utility function that starts the ssl, +%% crypto and public_key applications. Default type %% is temporary. see application(3) %%-------------------------------------------------------------------- start() -> @@ -67,7 +69,7 @@ start(Type) -> application:start(ssl, Type). %%-------------------------------------------------------------------- -%% Function: stop() -> ok +-spec stop() -> ok. %% %% Description: Stops the ssl application. %%-------------------------------------------------------------------- @@ -75,7 +77,8 @@ stop() -> application:stop(ssl). %%-------------------------------------------------------------------- -%% Function: connect(Address, Port, Options[, Timeout]) -> {ok, Socket} +-spec connect(host() | port(), port_num(), list()) -> {ok, #sslsocket{}}. +-spec connect(host() | port(), port_num(), list(), timeout()) -> {ok, #sslsocket{}}. %% %% Description: Connect to a ssl server. %%-------------------------------------------------------------------- @@ -101,13 +104,13 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket) -> {error, Reason} end; -connect(Address, Port, Options) -> - connect(Address, Port, Options, infinity). +connect(Host, Port, Options) -> + connect(Host, Port, Options, infinity). -connect(Address, Port, Options0, Timeout) -> - case proplists:get_value(ssl_imp, Options0, old) of +connect(Host, Port, Options0, Timeout) -> + case proplists:get_value(ssl_imp, Options0, new) of new -> - new_connect(Address, Port, Options0, Timeout); + new_connect(Host, Port, Options0, Timeout); old -> %% Allow the option reuseaddr to be present %% so that new and old ssl can be run by the same @@ -115,13 +118,14 @@ connect(Address, Port, Options0, Timeout) -> %% that hardcodes reuseaddr to true in its portprogram. Options1 = proplists:delete(reuseaddr, Options0), Options = proplists:delete(ssl_imp, Options1), - old_connect(Address, Port, Options, Timeout); + old_connect(Host, Port, Options, Timeout); Value -> {error, {eoptions, {ssl_imp, Value}}} end. %%-------------------------------------------------------------------- -%% Function: listen(Port, Options) -> {ok, ListenSock} | {error, Reason} +-spec listen(port_num(), list()) ->{ok, #sslsocket{}} | {error, reason()}. + %% %% Description: Creates a ssl listen socket. %%-------------------------------------------------------------------- @@ -144,7 +148,8 @@ listen(Port, Options0) -> end. %%-------------------------------------------------------------------- -%% Function: transport_accept(ListenSocket[, Timeout]) -> {ok, Socket}. +-spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}}. +-spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}}. %% %% Description: Performs transport accept on a ssl listen socket %%-------------------------------------------------------------------- @@ -182,8 +187,8 @@ transport_accept(#sslsocket{} = ListenSocket, Timeout) -> ssl_broker:transport_accept(Pid, ListenSocket, Timeout). %%-------------------------------------------------------------------- -%% Function: ssl_accept(ListenSocket[, Timeout]) -> {ok, Socket} | -%% {error, Reason} +-spec ssl_accept(#sslsocket{}) -> {ok, #sslsocket{}} | {error, reason()}. +-spec ssl_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on a ssl listen socket. e.i. performs %% ssl handshake. @@ -217,7 +222,7 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> end. %%-------------------------------------------------------------------- -%% Function: close() -> ok +-spec close(#sslsocket{}) -> term(). %% %% Description: Close a ssl connection %%-------------------------------------------------------------------- @@ -230,7 +235,7 @@ close(Socket = #sslsocket{}) -> ssl_broker:close(Socket). %%-------------------------------------------------------------------- -%% Function: send(Socket, Data) -> ok | {error, Reason} +-spec send(#sslsocket{}, iolist()) -> ok | {error, reason()}. %% %% Description: Sends data over the ssl connection %%-------------------------------------------------------------------- @@ -242,7 +247,8 @@ send(#sslsocket{} = Socket, Data) -> ssl_broker:send(Socket, Data). %%-------------------------------------------------------------------- -%% Function: recv(Socket, Length [,Timeout]) -> {ok, Data} | {error, reason} +-spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}. +-spec recv(#sslsocket{}, integer(), timeout()) -> {ok, binary()| list()} | {error, reason()}. %% %% Description: Receives data when active = false %%-------------------------------------------------------------------- @@ -256,8 +262,8 @@ recv(Socket = #sslsocket{}, Length, Timeout) -> ssl_broker:recv(Socket, Length, Timeout). %%-------------------------------------------------------------------- -%% Function: controlling_process(Socket, NewOwner) -> ok | {error, Reason} -%% +-spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}. +%% %% Description: Changes process that receives the messages when active = true %% or once. %%-------------------------------------------------------------------- @@ -270,11 +276,8 @@ controlling_process(Socket, NewOwner) when is_pid(NewOwner) -> ssl_broker:controlling_process(Socket, NewOwner). %%-------------------------------------------------------------------- -%% Function: connection_info(Socket) -> {ok, {Protocol, CipherSuite}} | -%% {error, Reason} -%% Protocol = sslv3 | tlsv1 | tlsv1.1 -%% CipherSuite = {KeyExchange, Chipher, Hash, Exportable} -%% +-spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} | + {error, reason()}. %% %% Description: Returns ssl protocol and cipher used for the connection %%-------------------------------------------------------------------- @@ -286,9 +289,9 @@ connection_info(#sslsocket{} = Socket) -> ssl_broker:connection_info(Socket). %%-------------------------------------------------------------------- -%% Function: peercert(Socket[, Opts]) -> {ok, Cert} | {error, Reason} +-spec peercert(#sslsocket{}) ->{ok, der_cert()} | {error, reason()}. %% -%% Description: +%% Description: Returns the peercert. %%-------------------------------------------------------------------- peercert(Socket) -> peercert(Socket, []). @@ -342,9 +345,9 @@ select_part(plain, {ok, Cert}, Opts) -> end. %%-------------------------------------------------------------------- -%% Function: peername(Socket) -> {ok, {Address, Port}} | {error, Reason} +-spec peername(#sslsocket{}) -> {ok, {tuple(), port_num()}} | {error, reason()}. %% -%% Description: +%% Description: same as inet:peername/1. %%-------------------------------------------------------------------- peername(#sslsocket{fd = new_ssl, pid = Pid}) -> ssl_connection:peername(Pid); @@ -354,9 +357,10 @@ peername(#sslsocket{} = Socket) -> ssl_broker:peername(Socket). %%-------------------------------------------------------------------- -%% Function: cipher_suites() -> -%% -%% Description: +-spec cipher_suites() -> [erl_cipher_suite()]. +-spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()]. + +%% Description: Returns all supported cipher suites. %%-------------------------------------------------------------------- cipher_suites() -> cipher_suites(erlang). @@ -370,7 +374,7 @@ cipher_suites(openssl) -> [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)]. %%-------------------------------------------------------------------- -%% Function: getopts(Socket, OptTags) -> {ok, Options} | {error, Reason} +-spec getopts(#sslsocket{}, [atom()]) -> {ok, [{atom(), term()}]}| {error, reason()}. %% %% Description: %%-------------------------------------------------------------------- @@ -383,7 +387,7 @@ getopts(#sslsocket{} = Socket, Options) -> ssl_broker:getopts(Socket, Options). %%-------------------------------------------------------------------- -%% Function: setopts(Socket, Options) -> ok | {error, Reason} +-spec setopts(#sslsocket{}, [{atom(), term()}]) -> ok | {error, reason()}. %% %% Description: %%-------------------------------------------------------------------- @@ -398,8 +402,8 @@ setopts(#sslsocket{} = Socket, Options) -> ssl_broker:setopts(Socket, Options). %%--------------------------------------------------------------- -%% Function: shutdown(Socket, How) -> ok | {error, Reason} -%% +-spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}. +%% %% Description: Same as gen_tcp:shutdown/2 %%-------------------------------------------------------------------- shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}, fd = new_ssl}, How) -> @@ -408,8 +412,8 @@ shutdown(#sslsocket{pid = Pid, fd = new_ssl}, How) -> ssl_connection:shutdown(Pid, How). %%-------------------------------------------------------------------- -%% Function: sockname(Socket) -> {ok, {Address, Port}} | {error, Reason} -%% +-spec sockname(#sslsocket{}) -> {ok, {tuple(), port_num()}} | {error, reason()}. +%% %% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- sockname(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}) -> @@ -423,9 +427,9 @@ sockname(#sslsocket{} = Socket) -> ssl_broker:sockname(Socket). %%--------------------------------------------------------------- -%% Function: seed(Data) -> ok | {error, edata} +-spec seed(term()) ->term(). %% -%% Description: +%% Description: Only used by old ssl. %%-------------------------------------------------------------------- %% TODO: crypto:seed ? seed(Data) -> @@ -433,20 +437,17 @@ seed(Data) -> ssl_server:seed(Data). %%--------------------------------------------------------------- -%% Function: session_id(Socket) -> {ok, PropList} | {error, Reason} +-spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}. %% -%% Description: +%% Description: Returns list of session info currently [{session_id, session_id(), +%% {cipher_suite, cipher_suite()}] %%-------------------------------------------------------------------- session_info(#sslsocket{pid = Pid, fd = new_ssl}) -> ssl_connection:session_info(Pid). %%--------------------------------------------------------------- -%% Function: versions() -> [{SslAppVer, SupportedSslVer, AvailableSslVsn}] -%% -%% SslAppVer = string() - t.ex: ssl-4.0 -%% SupportedSslVer = [SslVer] -%% AvailableSslVsn = [SSLVer] -%% SSLVer = sslv3 | tlsv1 | 'tlsv1.1' +-spec versions() -> [{{ssl_app, string()}, {supported, [tls_version()]}, + {available, [tls_version()]}}]. %% %% Description: Returns a list of relevant versions. %%-------------------------------------------------------------------- @@ -456,7 +457,11 @@ versions() -> AvailableVsns = ?DEFAULT_SUPPORTED_VERSIONS, [{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}]. - +%%--------------------------------------------------------------- +-spec renegotiate(#sslsocket{}) -> ok | {error, reason()}. +%% +%% Description: +%%-------------------------------------------------------------------- renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) -> ssl_connection:renegotiation(Pid). @@ -660,7 +665,7 @@ validate_option(secure_renegotiate, Value) when Value == true; Value == false -> Value; validate_option(renegotiate_at, Value) when is_integer(Value) -> - min(Value, ?DEFAULT_RENEGOTIATE_AT); + erlang:min(Value, ?DEFAULT_RENEGOTIATE_AT); validate_option(debug, Value) when is_list(Value); Value == true -> Value; @@ -890,10 +895,6 @@ version() -> end, {ok, {SSLVsn, CompVsn, LibVsn}}. -min(N,M) when N < M -> - N; -min(_, M) -> - M. %% Only used to remove exit messages from old ssl %% First is a nonsense clause to provide some diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index db9a883654..eb1228afa4 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -32,6 +32,17 @@ -export([alert_txt/1, reason_code/2]). +%%==================================================================== +%% Internal application API +%%==================================================================== +%%-------------------------------------------------------------------- +-spec reason_code(#alert{}, client | server) -> closed | esslconnect | + esslaccept | string(). +%% +%% Description: Returns the error reason that will be returned to the +%% user. +%%-------------------------------------------------------------------- + reason_code(#alert{description = ?CLOSE_NOTIFY}, _) -> closed; reason_code(#alert{description = ?HANDSHAKE_FAILURE}, client) -> @@ -41,10 +52,19 @@ reason_code(#alert{description = ?HANDSHAKE_FAILURE}, server) -> reason_code(#alert{description = Description}, _) -> description_txt(Description). +%%-------------------------------------------------------------------- +-spec alert_txt(#alert{}) -> string(). +%% +%% Description: Returns the error string for given alert. +%%-------------------------------------------------------------------- + alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}}) -> Mod ++ ":" ++ integer_to_list(Line) ++ ":" ++ level_txt(Level) ++" "++ description_txt(Description). +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- level_txt(?WARNING) -> "Warning:"; level_txt(?FATAL) -> @@ -96,8 +116,3 @@ description_txt(?USER_CANCELED) -> "user canceled"; description_txt(?NO_RENEGOTIATION) -> "no renegotiation". - - - - - diff --git a/lib/ssl/src/ssl_app.erl b/lib/ssl/src/ssl_app.erl index 6ca1c42631..d9a354086d 100644 --- a/lib/ssl/src/ssl_app.erl +++ b/lib/ssl/src/ssl_app.erl @@ -27,14 +27,16 @@ -export([start/2, stop/1]). -%% start/2(Type, StartArgs) -> {ok, Pid} | {ok, Pid, State} | -%% {error, Reason} -%% +%%-------------------------------------------------------------------- +-spec start(normal | {takeover, node()} | {failover, node()}, list()) -> + {ok, pid()} | {ok, pid(), term()} | {error, term()}. +%%-------------------------------------------------------------------- start(_Type, _StartArgs) -> ssl_sup:start_link(). -%% stop(State) -> void() -%% +%-------------------------------------------------------------------- +-spec stop(term())-> ok. +%%-------------------------------------------------------------------- stop(_State) -> ok. diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 9aa31ae8a4..8a79f75725 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -46,6 +46,14 @@ %% Internal application API %%==================================================================== +%%-------------------------------------------------------------------- +-spec trusted_cert_and_path([der_cert()], certdb_ref(), boolean()) -> + {der_cert(), [der_cert()], list()}. +%% +%% Description: Extracts the root cert (if not presents tries to +%% look it up, if not found {bad_cert, unknown_ca} will be added verification +%% errors. Returns {RootCert, Path, VerifyErrors} +%%-------------------------------------------------------------------- trusted_cert_and_path(CertChain, CertDbRef, Verify) -> [Cert | RestPath] = lists:reverse(CertChain), {ok, OtpCert} = public_key:pkix_decode_cert(Cert, otp), @@ -84,19 +92,31 @@ trusted_cert_and_path(CertChain, CertDbRef, Verify) -> end end. - +%%-------------------------------------------------------------------- +-spec certificate_chain(undefined | binary(), certdb_ref()) -> + {error, no_cert} | [der_cert()]. +%% +%% Description: Return the certificate chain to send to peer. +%%-------------------------------------------------------------------- certificate_chain(undefined, _CertsDbRef) -> {error, no_cert}; certificate_chain(OwnCert, CertsDbRef) -> {ok, ErlCert} = public_key:pkix_decode_cert(OwnCert, otp), certificate_chain(ErlCert, OwnCert, CertsDbRef, [OwnCert]). - +%%-------------------------------------------------------------------- +-spec file_to_certificats(string()) -> [der_cert()]. +%% +%% Description: Return list of DER encoded certificates. +%%-------------------------------------------------------------------- file_to_certificats(File) -> {ok, List} = ssl_manager:cache_pem_file(File), [Bin || {cert, Bin, not_encrypted} <- List]. - - -%% Validates ssl/tls specific extensions +%%-------------------------------------------------------------------- +-spec validate_extensions([#'Extension'{}], term(), [#'Extension'{}], + boolean(), list(), client | server) -> {[#'Extension'{}], term(), list()}. +%% +%% Description: Validates ssl/tls specific extensions +%%-------------------------------------------------------------------- validate_extensions([], ValidationState, UnknownExtensions, _, AccErr, _) -> {UnknownExtensions, ValidationState, AccErr}; @@ -119,21 +139,42 @@ validate_extensions([Extension | Rest], ValidationState, UnknownExtensions, validate_extensions(Rest, ValidationState, [Extension | UnknownExtensions], Verify, AccErr, Role). +%%-------------------------------------------------------------------- +-spec is_valid_key_usage(list(), term()) -> boolean(). +%% +%% Description: Checks if Use is a valid key usage. +%%-------------------------------------------------------------------- is_valid_key_usage(KeyUse, Use) -> lists:member(Use, KeyUse). - select_extension(_, []) -> +%%-------------------------------------------------------------------- +-spec select_extension(term(), list()) -> undefined | #'Extension'{}. +%% +%% Description: Selects the extension identified by Id if present in +%% a list of extensions. +%%-------------------------------------------------------------------- +select_extension(_, []) -> undefined; select_extension(Id, [#'Extension'{extnID = Id} = Extension | _]) -> Extension; select_extension(Id, [_ | Extensions]) -> select_extension(Id, Extensions). +%%-------------------------------------------------------------------- +-spec extensions_list(asn1_NOVALUE | list()) -> list(). +%% +%% Description: Handles that +%%-------------------------------------------------------------------- extensions_list(asn1_NOVALUE) -> []; extensions_list(Extensions) -> Extensions. +%%-------------------------------------------------------------------- +-spec signature_type(term()) -> rsa | dsa . +%% +%% Description: +%%-------------------------------------------------------------------- signature_type(RSA) when RSA == ?sha1WithRSAEncryption; RSA == ?md5WithRSAEncryption -> rsa; diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl index b8c3c6f6b7..e953821057 100644 --- a/lib/ssl/src/ssl_certificate_db.erl +++ b/lib/ssl/src/ssl_certificate_db.erl @@ -22,7 +22,7 @@ %%---------------------------------------------------------------------- -module(ssl_certificate_db). - +-include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). -export([create/0, remove/1, add_trusted_certs/3, @@ -34,8 +34,7 @@ %%==================================================================== %%-------------------------------------------------------------------- -%% Function: create() -> Db -%% Db = term() - Reference to the crated database +-spec create() -> certdb_ref(). %% %% Description: Creates a new certificate db. %% Note: lookup_trusted_cert/3 may be called from any process but only @@ -47,8 +46,7 @@ create() -> ets:new(ssl_pid_to_file, [bag, private])]. %%-------------------------------------------------------------------- -%% Function: delete(Db) -> _ -%% Db = Database refererence as returned by create/0 +-spec remove(certdb_ref()) -> term(). %% %% Description: Removes database db %%-------------------------------------------------------------------- @@ -56,11 +54,10 @@ remove(Dbs) -> lists:foreach(fun(Db) -> true = ets:delete(Db) end, Dbs). %%-------------------------------------------------------------------- -%% Function: lookup_trusted_cert(Ref, SerialNumber, Issuer) -> {BinCert,DecodedCert} -%% Ref = ref() +-spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> {der_cert(), #'OTPCertificate'{}}. + %% SerialNumber = integer() %% Issuer = {rdnSequence, IssuerAttrs} -%% BinCert = binary() %% %% Description: Retrives the trusted certificate identified by %% <SerialNumber, Issuer>. Ref is used as it is specified @@ -78,11 +75,7 @@ lookup_cached_certs(File) -> ets:lookup(certificate_db_name(), {file, File}). %%-------------------------------------------------------------------- -%% Function: add_trusted_certs(Pid, File, Db) -> {ok, Ref} -%% Pid = pid() -%% File = string() -%% Db = Database refererence as returned by create/0 -%% Ref = ref() +-spec add_trusted_certs(pid(), string(), certdb_ref()) -> {ok, certdb_ref()}. %% %% Description: Adds the trusted certificates from file <File> to the %% runtime database. Returns Ref that should be handed to lookup_trusted_cert @@ -103,7 +96,7 @@ add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) -> {ok, Ref}. %%-------------------------------------------------------------------- -%% Function: cache_pem_file(Pid, File, Db) -> FileContent +-spec cache_pem_file(pid(), string(), certdb_ref()) -> term(). %% %% Description: Cache file as binary in DB %%-------------------------------------------------------------------- @@ -114,7 +107,8 @@ cache_pem_file(Pid, File, [CertsDb, _FileToRefDb, PidToFileDb]) -> Res. %%-------------------------------------------------------------------- -%% Function: remove_trusted_certs(Pid, Db) -> _ +-spec remove_trusted_certs(pid(), certdb_ref()) -> term(). + %% %% Description: Removes trusted certs originating from %% the file associated to Pid from the runtime database. @@ -144,11 +138,9 @@ remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) -> end. %%-------------------------------------------------------------------- -%% Function: issuer_candidate() -> {Key, Candidate} | no_more_candidates +-spec issuer_candidate(no_candidate | cert_key()) -> + {cert_key(), der_cert()} | no_more_candidates. %% -%% Candidate -%% -%% %% Description: If a certificat does not define its issuer through %% the extension 'ce-authorityKeyIdentifier' we can %% try to find the issuer in the database over known diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 2a71df8ee1..a6e80047c2 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -40,11 +40,8 @@ -compile(inline). %%-------------------------------------------------------------------- -%% Function: security_parameters(CipherSuite, SecParams) -> -%% #security_parameters{} -%% -%% CipherSuite - as defined in ssl_cipher.hrl -%% SecParams - #security_parameters{} +-spec security_parameters(erl_cipher_suite(), #security_parameters{}) -> + #security_parameters{}. %% %% Description: Returns a security parameters record where the %% cipher values has been updated according to <CipherSuite> @@ -63,15 +60,11 @@ security_parameters(CipherSuite, SecParams) -> hash_size = hash_size(Hash)}. %%-------------------------------------------------------------------- -%% Function: cipher(Method, CipherState, Mac, Data) -> -%% {Encrypted, UpdateCipherState} -%% -%% Method - integer() (as defined in ssl_cipher.hrl) -%% CipherState, UpdatedCipherState - #cipher_state{} -%% Data, Encrypted - binary() +-spec cipher(cipher_enum(), #cipher_state{}, binary(), binary()) -> + {binary(), #cipher_state{}}. %% -%% Description: Encrypts the data and the mac using method, updating -%% the cipher state +%% Description: Encrypts the data and the MAC using chipher described +%% by cipher_enum() and updating the cipher state %%------------------------------------------------------------------- cipher(?NULL, CipherState, <<>>, Fragment) -> GenStreamCipherList = [Fragment, <<>>], @@ -125,15 +118,11 @@ block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, {T, CS0#cipher_state{iv=NextIV}}. %%-------------------------------------------------------------------- -%% Function: decipher(Method, CipherState, Mac, Data, Version) -> -%% {Decrypted, UpdateCipherState} -%% -%% Method - integer() (as defined in ssl_cipher.hrl) -%% CipherState, UpdatedCipherState - #cipher_state{} -%% Data, Encrypted - binary() +-spec decipher(cipher_enum(), integer(), #cipher_state{}, binary(), tls_version()) -> + {binary(), #cipher_state{}}. %% -%% Description: Decrypts the data and the mac using method, updating -%% the cipher state +%% Description: Decrypts the data and the MAC using cipher described +%% by cipher_enum() and updating the cipher state. %%------------------------------------------------------------------- decipher(?NULL, _HashSz, CipherState, Fragment, _) -> {Fragment, <<>>, CipherState}; @@ -192,10 +181,7 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, end. %%-------------------------------------------------------------------- -%% Function: suites(Version) -> [Suite] -%% -%% Version = version() -%% Suite = binary() from ssl_cipher.hrl +-spec suites(tls_version()) -> [cipher_suite()]. %% %% Description: Returns a list of supported cipher suites. %%-------------------------------------------------------------------- @@ -205,19 +191,9 @@ suites({3, N}) when N == 1; N == 2 -> ssl_tls1:suites(). %%-------------------------------------------------------------------- -%% Function: suite_definition(CipherSuite) -> -%% {KeyExchange, Cipher, Hash} -%% -%% -%% CipherSuite - as defined in ssl_cipher.hrl -%% KeyExchange - rsa | dh_anon | dhe_dss | dhe_rsa | kerb5 -%% -%% Cipher - null | rc4_128 | idea_cbc | des_cbc | '3des_ede_cbc' -%% des40_cbc | aes_128_cbc | aes_256_cbc -%% Hash - null | md5 | sha +-spec suite_definition(cipher_suite()) -> erl_cipher_suite(). %% -%% Description: Returns a security parameters tuple where the -%% cipher values has been updated according to <CipherSuite> +%% Description: Return erlang cipher suite definition. %% Note: Currently not supported suites are commented away. %% They should be supported or removed in the future. %%------------------------------------------------------------------- @@ -261,6 +237,12 @@ suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) -> suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) -> {dhe_rsa, aes_256_cbc, sha}. +%%-------------------------------------------------------------------- +-spec suite(erl_cipher_suite()) -> cipher_suite(). +%% +%% Description: Return TLS cipher suite definition. +%%-------------------------------------------------------------------- + %% TLS v1.1 suites %%suite({rsa, null, md5}) -> %% ?TLS_RSA_WITH_NULL_MD5; @@ -309,7 +291,11 @@ suite({dhe_rsa, aes_256_cbc, sha}) -> %% suite({dh_anon, aes_256_cbc, sha}) -> %% ?TLS_DH_anon_WITH_AES_256_CBC_SHA. - +%%-------------------------------------------------------------------- +-spec openssl_suite(openssl_cipher_suite()) -> cipher_suite(). +%% +%% Description: Return TLS cipher suite definition. +%%-------------------------------------------------------------------- %% translate constants <-> openssl-strings openssl_suite("DHE-RSA-AES256-SHA") -> ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA; @@ -339,7 +325,11 @@ openssl_suite("EDH-RSA-DES-CBC-SHA") -> ?TLS_DHE_RSA_WITH_DES_CBC_SHA; openssl_suite("DES-CBC-SHA") -> ?TLS_RSA_WITH_DES_CBC_SHA. - +%%-------------------------------------------------------------------- +-spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite(). +%% +%% Description: Return openssl cipher suite name. +%%------------------------------------------------------------------- openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) -> "DHE-RSA-AES256-SHA"; openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) -> @@ -372,6 +362,11 @@ openssl_suite_name(?TLS_RSA_WITH_DES_CBC_SHA) -> openssl_suite_name(Cipher) -> suite_definition(Cipher). +%%-------------------------------------------------------------------- +-spec filter(undefined | binary(), [cipher_suite()]) -> [cipher_suite()]. +%% +%% Description: . +%%------------------------------------------------------------------- filter(undefined, Ciphers) -> Ciphers; filter(DerCert, Ciphers) -> diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl index 80fe527f45..19de709d9c 100644 --- a/lib/ssl/src/ssl_cipher.hrl +++ b/lib/ssl/src/ssl_cipher.hrl @@ -26,6 +26,14 @@ -ifndef(ssl_cipher). -define(ssl_cipher, true). +-type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc' + | aes_128_cbc | aes_256_cbc. +-type hash() :: sha | md5. +-type erl_cipher_suite() :: {key_algo(), cipher(), hash()}. +-type cipher_suite() :: binary(). +-type cipher_enum() :: integer(). +-type openssl_cipher_suite() :: string(). + %%% SSL cipher protocol %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -define(CHANGE_CIPHER_SPEC_PROTO, 1). % _PROTO to not clash with % SSL record protocol diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 07fa101ed4..5b4b129e30 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -98,12 +98,17 @@ #'DHParameter'{prime = ?DEFAULT_DIFFIE_HELLMAN_PRIME, base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}). +-type state_name() :: hello | abbreviated | certify | cipher | connection. +-type gen_fsm_state_return() :: {next_state, state_name(), #state{}} | + {next_state, state_name(), #state{}, timeout()} | + {stop, term(), #state{}}. + %%==================================================================== %% Internal application API %%==================================================================== %%-------------------------------------------------------------------- -%% Function: send(Pid, Data) -> ok | {error, Reason} +-spec send(pid(), iolist()) -> ok | {error, reason()}. %% %% Description: Sends data over the ssl connection %%-------------------------------------------------------------------- @@ -112,15 +117,16 @@ send(Pid, Data) -> erlang:iolist_to_binary(Data)}, infinity). %%-------------------------------------------------------------------- -%% Function: recv(Socket, Length Timeout) -> {ok, Data} | {error, reason} +-spec recv(pid(), integer(), timeout()) -> + {ok, binary() | list()} | {error, reason()}. %% %% Description: Receives data when active = false %%-------------------------------------------------------------------- recv(Pid, Length, Timeout) -> sync_send_all_state_event(Pid, {recv, Length}, Timeout). %%-------------------------------------------------------------------- -%% Function: : connect(Host, Port, Socket, Options, -%% User, CbInfo, Timeout) -> {ok, Socket} +-spec connect(host(), port_num(), port(), list(), pid(), tuple(), timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Connect to a ssl server. %%-------------------------------------------------------------------- @@ -132,8 +138,8 @@ connect(Host, Port, Socket, Options, User, CbInfo, Timeout) -> {error, ssl_not_started} end. %%-------------------------------------------------------------------- -%% Function: accept(Port, Socket, Opts, User, -%% CbInfo, Timeout) -> {ok, Socket} | {error, Reason} +-spec ssl_accept(port_num(), port(), list(), pid(), tuple(), timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on a ssl listen socket. e.i. performs %% ssl handshake. @@ -147,7 +153,7 @@ ssl_accept(Port, Socket, Opts, User, CbInfo, Timeout) -> end. %%-------------------------------------------------------------------- -%% Function: handshake(SslSocket, Timeout) -> ok | {error, Reason} +-spec handshake(#sslsocket{}, timeout()) -> ok | {error, reason()}. %% %% Description: Starts ssl handshake. %%-------------------------------------------------------------------- @@ -159,7 +165,8 @@ handshake(#sslsocket{pid = Pid}, Timeout) -> Error end. %-------------------------------------------------------------------- -%% Function: socket_control(Pid) -> {ok, SslSocket} | {error, Reason} +-spec socket_control(port(), pid(), atom()) -> + {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Set the ssl process to own the accept socket %%-------------------------------------------------------------------- @@ -172,7 +179,7 @@ socket_control(Socket, Pid, CbModule) -> end. %%-------------------------------------------------------------------- -%% Function: close() -> ok +-spec close(pid()) -> ok | {error, reason()}. %% %% Description: Close a ssl connection %%-------------------------------------------------------------------- @@ -185,7 +192,7 @@ close(ConnectionPid) -> end. %%-------------------------------------------------------------------- -%% Function: shutdown(Socket, How) -> ok | {error, Reason} +-spec shutdown(pid(), atom()) -> ok | {error, reason()}. %% %% Description: Same as gen_tcp:shutdown/2 %%-------------------------------------------------------------------- @@ -193,7 +200,7 @@ shutdown(ConnectionPid, How) -> sync_send_all_state_event(ConnectionPid, {shutdown, How}). %%-------------------------------------------------------------------- -%% Function: new_user(ConnectionPid, User) -> ok | {error, Reason} +-spec new_user(pid(), pid()) -> ok | {error, reason()}. %% %% Description: Changes process that receives the messages when active = true %% or once. @@ -201,28 +208,28 @@ shutdown(ConnectionPid, How) -> new_user(ConnectionPid, User) -> sync_send_all_state_event(ConnectionPid, {new_user, User}). %%-------------------------------------------------------------------- -%% Function: sockname(ConnectionPid) -> {ok, {Address, Port}} | {error, Reason} +-spec sockname(pid()) -> {ok, {tuple(), port_num()}} | {error, reason()}. %% %% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- sockname(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, sockname). %%-------------------------------------------------------------------- -%% Function: peername(ConnectionPid) -> {ok, {Address, Port}} | {error, Reason} +-spec peername(pid()) -> {ok, {tuple(), port_num()}} | {error, reason()}. %% %% Description: Same as inet:peername/1 %%-------------------------------------------------------------------- peername(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, peername). %%-------------------------------------------------------------------- -%% Function: get_opts(ConnectionPid, OptTags) -> {ok, Options} | {error, Reason} +-spec get_opts(pid(), list()) -> {ok, list()} | {error, reason()}. %% %% Description: Same as inet:getopts/2 %%-------------------------------------------------------------------- get_opts(ConnectionPid, OptTags) -> sync_send_all_state_event(ConnectionPid, {get_opts, OptTags}). %%-------------------------------------------------------------------- -%% Function: setopts(Socket, Options) -> ok | {error, Reason} +-spec set_opts(pid(), list()) -> ok | {error, reason()}. %% %% Description: Same as inet:setopts/2 %%-------------------------------------------------------------------- @@ -230,8 +237,7 @@ set_opts(ConnectionPid, Options) -> sync_send_all_state_event(ConnectionPid, {set_opts, Options}). %%-------------------------------------------------------------------- -%% Function: info(ConnectionPid) -> {ok, {Protocol, CipherSuite}} | -%% {error, Reason} +-spec info(pid()) -> {ok, {atom(), tuple()}} | {error, reason()}. %% %% Description: Returns ssl protocol and cipher used for the connection %%-------------------------------------------------------------------- @@ -239,7 +245,7 @@ info(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, info). %%-------------------------------------------------------------------- -%% Function: session_info(ConnectionPid) -> {ok, PropList} | {error, Reason} +-spec session_info(pid()) -> {ok, list()} | {error, reason()}. %% %% Description: Returns info about the ssl session %%-------------------------------------------------------------------- @@ -247,7 +253,7 @@ session_info(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, session_info). %%-------------------------------------------------------------------- -%% Function: peercert(ConnectionPid) -> {ok, Cert} | {error, Reason} +-spec peer_certificate(pid()) -> {ok, binary()} | {error, reason()}. %% %% Description: Returns the peer cert %%-------------------------------------------------------------------- @@ -255,7 +261,7 @@ peer_certificate(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, peer_certificate). %%-------------------------------------------------------------------- -%% Function: renegotiation(ConnectionPid) -> ok | {error, Reason} +-spec renegotiation(pid()) -> ok | {error, reason()}. %% %% Description: Starts a renegotiation of the ssl session. %%-------------------------------------------------------------------- @@ -267,7 +273,8 @@ renegotiation(ConnectionPid) -> %%==================================================================== %%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +-spec start_link(atom(), host(), port_num(), port(), list(), pid(), tuple()) -> + {ok, pid()} | ignore | {error, reason()}. %% %% Description: Creates a gen_fsm process which calls Module:init/1 to %% initialize. To ensure a synchronized start-up procedure, this function @@ -281,10 +288,9 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) -> %% gen_fsm callbacks %%==================================================================== %%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, StateName, State} | -%% {ok, StateName, State, Timeout} | -%% ignore | -%% {stop, StopReason} +-spec init(list()) -> {ok, state_name(), #state{}} + | {ok, state_name(), #state{}, timeout()} | + ignore | {stop, term()}. %% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or %% gen_fsm:start_link/3,4, this function is called by the new process to %% initialize. @@ -309,11 +315,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, end. %%-------------------------------------------------------------------- -%% Function: -%% state_name(Event, State) -> {next_state, NextStateName, NextState}| -%% {next_state, NextStateName, -%% NextState, Timeout} | -%% {stop, Reason, NewState} +%% -spec state_name(event(), #state{}) -> gen_fsm_state_return() %% %% Description:There should be one instance of this function for each %% possible state name. Whenever a gen_fsm receives an event sent @@ -322,6 +324,9 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, %% the event. It is also called if a timeout occurs. %% %%-------------------------------------------------------------------- +-spec hello(start | #hello_request{} | #client_hello{} | #server_hello{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- hello(start, #state{host = Host, port = Port, role = client, ssl_options = SslOpts, transport_cb = Transport, socket = Socket, @@ -359,49 +364,30 @@ hello(#hello_request{}, #state{role = client} = State0) -> hello(#server_hello{cipher_suite = CipherSuite, compression_method = Compression} = Hello, - #state{session = Session0 = #session{session_id = OldId}, + #state{session = #session{session_id = OldId}, connection_states = ConnectionStates0, role = client, negotiated_version = ReqVersion, - host = Host, port = Port, renegotiation = {Renegotiation, _}, - ssl_options = SslOptions, - session_cache = Cache, - session_cache_cb = CacheCb} = State0) -> + ssl_options = SslOptions} = State0) -> case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of - {Version, NewId, ConnectionStates1} -> + {Version, NewId, ConnectionStates} -> {KeyAlgorithm, _, _} = ssl_cipher:suite_definition(CipherSuite), PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), - State1 = State0#state{key_algorithm = KeyAlgorithm, - negotiated_version = Version, - connection_states = ConnectionStates1, - premaster_secret = PremasterSecret}, + State = State0#state{key_algorithm = KeyAlgorithm, + negotiated_version = Version, + connection_states = ConnectionStates, + premaster_secret = PremasterSecret}, case ssl_session:is_new(OldId, NewId) of true -> - Session = Session0#session{session_id = NewId, - cipher_suite = CipherSuite, - compression_method = Compression}, - {Record, State} = next_record(State1#state{session = Session}), - next_state(certify, Record, State); + handle_new_session(NewId, CipherSuite, Compression, State); false -> - Session = CacheCb:lookup(Cache, {{Host, Port}, NewId}), - case ssl_handshake:master_secret(Version, Session, - ConnectionStates1, client) of - {_, ConnectionStates2} -> - {Record, State} = - next_record(State1#state{ - connection_states = ConnectionStates2, - session = Session}), - next_state(abbreviated, Record, State); - #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State1), - {stop, normal, State1} - end + handle_resumed_session(NewId, State#state{connection_states = ConnectionStates}) end; #alert{} = Alert -> handle_own_alert(Alert, ReqVersion, hello, State0), @@ -431,7 +417,10 @@ hello(Hello = #client_hello{client_version = ClientVersion}, hello(Msg, State) -> handle_unexpected_message(Msg, hello, State). - +%%-------------------------------------------------------------------- +-spec abbreviated(#hello_request{} | #finished{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- abbreviated(#hello_request{}, State0) -> {Record, State} = next_record(State0), next_state(hello, Record, State); @@ -477,6 +466,11 @@ abbreviated(#finished{verify_data = Data} = Finished, abbreviated(Msg, State) -> handle_unexpected_message(Msg, abbreviated, State). +%%-------------------------------------------------------------------- +-spec certify(#hello_request{} | #certificate{} | #server_key_exchange{} | + #certificate_request{} | #server_hello_done{} | #client_key_exchange{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- certify(#hello_request{}, State0) -> {Record, State} = next_record(State0), next_state(hello, Record, State); @@ -646,6 +640,10 @@ certify(#client_key_exchange{exchange_keys = #client_diffie_hellman_public{ certify(Msg, State) -> handle_unexpected_message(Msg, certify, State). +%%-------------------------------------------------------------------- +-spec cipher(#hello_request{} | #certificate_verify{} | #finished{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- cipher(#hello_request{}, State0) -> {Record, State} = next_record(State0), next_state(hello, Record, State); @@ -676,31 +674,14 @@ cipher(#finished{verify_data = Data} = Finished, role = Role, session = #session{master_secret = MasterSecret} = Session0, - tls_handshake_hashes = Hashes0, - connection_states = ConnectionStates0} = State) -> + tls_handshake_hashes = Hashes0} = State) -> case ssl_handshake:verify_connection(Version, Finished, opposite_role(Role), MasterSecret, Hashes0) of verified -> Session = register_session(Role, Host, Port, Session0), - case Role of - client -> - ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0), - next_state_connection(cipher, ack_connection(State#state{session = Session, - connection_states = ConnectionStates})); - server -> - ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0), - {ConnectionStates, Hashes} = - finalize_handshake(State#state{ - connection_states = ConnectionStates1, - session = Session}, cipher), - next_state_connection(cipher, ack_connection(State#state{connection_states = - ConnectionStates, - session = Session, - tls_handshake_hashes = - Hashes})) - end; + cipher_role(Role, Data, Session, State); #alert{} = Alert -> handle_own_alert(Alert, Version, cipher, State), {stop, normal, State} @@ -709,7 +690,10 @@ cipher(#finished{verify_data = Data} = Finished, cipher(Msg, State) -> handle_unexpected_message(Msg, cipher, State). - +%%-------------------------------------------------------------------- +-spec connection(#hello_request{} | #client_hello{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- connection(#hello_request{}, #state{host = Host, port = Port, socket = Socket, ssl_options = SslOpts, @@ -736,30 +720,22 @@ connection(#client_hello{} = Hello, #state{role = server} = State) -> connection(Msg, State) -> handle_unexpected_message(Msg, connection, State). %%-------------------------------------------------------------------- -%% Function: -%% handle_event(Event, StateName, State) -> {next_state, NextStateName, -%% NextState} | -%% {next_state, NextStateName, -%% NextState, Timeout} | -%% {stop, Reason, NewState} +-spec handle_event(term(), state_name(), #state{}) -> gen_fsm_state_return(). +%% %% Description: Whenever a gen_fsm receives an event sent using %% gen_fsm:send_all_state_event/2, this function is called to handle -%% the event. +%% the event. Not currently used! %%-------------------------------------------------------------------- handle_event(_Event, StateName, State) -> {next_state, StateName, State}. %%-------------------------------------------------------------------- -%% Function: -%% handle_sync_event(Event, From, StateName, -%% State) -> {next_state, NextStateName, NextState} | -%% {next_state, NextStateName, NextState, -%% Timeout} | -%% {reply, Reply, NextStateName, NextState}| -%% {reply, Reply, NextStateName, NextState, -%% Timeout} | -%% {stop, Reason, NewState} | -%% {stop, Reason, Reply, NewState} +-spec handle_sync_event(term(), from(), state_name(), #state{}) -> + gen_fsm_state_return() | + {reply, reply(), state_name(), #state{}} | + {reply, reply(), state_name(), #state{}, timeout()} | + {stop, reason(), reply(), #state{}}. +%% %% Description: Whenever a gen_fsm receives an event sent using %% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle %% the event. @@ -936,11 +912,11 @@ handle_sync_event(peer_certificate, _, StateName, {reply, {ok, Cert}, StateName, State}. %%-------------------------------------------------------------------- -%% Function: -%% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}| -%% {next_state, NextStateName, NextState, -%% Timeout} | -%% {stop, Reason, NewState} +-spec handle_info(msg(),state_name(), #state{}) -> + {next_state, state_name(), #state{}}| + {next_state, state_name(), #state{}, timeout()} | + {stop, reason(), #state{}}. +%% %% Description: This function is called by a gen_fsm when it receives any %% other message than a synchronous or asynchronous event %% (or a system message). @@ -1000,7 +976,8 @@ handle_info(Msg, StateName, State) -> {next_state, StateName, State}. %%-------------------------------------------------------------------- -%% Function: terminate(Reason, StateName, State) -> void() +-spec terminate(reason(), state_name(), #state{}) -> term(). +%% %% Description:This function is called by a gen_fsm when it is about %% to terminate. It should be the opposite of Module:init/1 and do any %% necessary cleaning up. When it returns, the gen_fsm terminates with @@ -1027,7 +1004,8 @@ terminate(_Reason, _StateName, #state{transport_cb = Transport, Transport:close(Socket). %%-------------------------------------------------------------------- -%% Function: +-spec code_change(term(), state_name(), #state{}, list()) -> {ok, state_name(), #state{}}. +%% %% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState} %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- @@ -1039,24 +1017,17 @@ code_change(_OldVsn, StateName, State, _Extra) -> %%-------------------------------------------------------------------- start_fsm(Role, Host, Port, Socket, Opts, User, {CbModule, _,_, _} = CbInfo, Timeout) -> - case ssl_connection_sup:start_child([Role, Host, Port, Socket, - Opts, User, CbInfo]) of - {ok, Pid} -> - case socket_control(Socket, Pid, CbModule) of - {ok, SslSocket} -> - case handshake(SslSocket, Timeout) of - ok -> - {ok, SslSocket}; - {error, Reason} -> - {error, Reason} - end; - {error, Reason} -> - {error, Reason} - end; - {error, Reason} -> - {error, Reason} + try + {ok, Pid} = ssl_connection_sup:start_child([Role, Host, Port, Socket, + Opts, User, CbInfo]), + {ok, SslSocket} = socket_control(Socket, Pid, CbModule), + ok = handshake(SslSocket, Timeout), + {ok, SslSocket} + catch + error:{badmatch, {error, _} = Error} -> + Error end. - + ssl_init(SslOpts, Role) -> {ok, CertDbRef, CacheRef, OwnCert} = init_certificates(SslOpts, Role), PrivateKey = @@ -1267,6 +1238,33 @@ do_server_hello(#server_hello{cipher_suite = CipherSuite, {stop, normal, State0} end. +handle_new_session(NewId, CipherSuite, Compression, #state{session = Session0} = State0) -> + Session = Session0#session{session_id = NewId, + cipher_suite = CipherSuite, + compression_method = Compression}, + {Record, State} = next_record(State0#state{session = Session}), + next_state(certify, Record, State). + +handle_resumed_session(SessId, #state{connection_states = ConnectionStates0, + negotiated_version = Version, + host = Host, port = Port, + session_cache = Cache, + session_cache_cb = CacheCb} = State0) -> + Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}), + case ssl_handshake:master_secret(Version, Session, + ConnectionStates0, client) of + {_, ConnectionStates1} -> + {Record, State} = + next_record(State0#state{ + connection_states = ConnectionStates1, + session = Session}), + next_state(abbreviated, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, hello, State0), + {stop, normal, State0} + end. + + client_certify_and_key_exchange(#state{negotiated_version = Version} = State0) -> try do_client_certify_and_key_exchange(State0) of @@ -1354,9 +1352,7 @@ key_exchange(#state{role = server, key_algorithm = Algo, transport_cb = Transport } = State) when Algo == dhe_dss; - Algo == dhe_dss_export; - Algo == dhe_rsa; - Algo == dhe_rsa_export -> + Algo == dhe_rsa -> Keys = public_key:gen_key(Params), ConnectionState = @@ -1402,9 +1398,7 @@ key_exchange(#state{role = client, socket = Socket, transport_cb = Transport, tls_handshake_hashes = Hashes0} = State) when Algorithm == dhe_dss; - Algorithm == dhe_dss_export; - Algorithm == dhe_rsa; - Algorithm == dhe_rsa_export -> + Algorithm == dhe_rsa -> Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}), {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(Msg, Version, ConnectionStates0, Hashes0), @@ -1545,6 +1539,21 @@ verify_dh_params(Signed, Hash, {?'id-dsa', PublicKey, PublicKeyParams}) -> public_key:verify_signature(Hash, none, Signed, PublicKey, PublicKeyParams). +cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State) -> + ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0), + next_state_connection(cipher, ack_connection(State#state{session = Session, + connection_states = ConnectionStates})); + +cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State) -> + ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0), + {ConnectionStates, Hashes} = + finalize_handshake(State#state{connection_states = ConnectionStates1, + session = Session}, cipher), + next_state_connection(cipher, ack_connection(State#state{connection_states = + ConnectionStates, + session = Session, + tls_handshake_hashes = + Hashes})). encode_alert(#alert{} = Alert, Version, ConnectionStates) -> ?DBG_TERM(Alert), ssl_record:encode_alert_record(Alert, Version, ConnectionStates). diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index f7e3e392ec..3811906d77 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -42,16 +42,15 @@ encode_handshake/3, init_hashes/0, update_hashes/2, decrypt_premaster_secret/2]). +-type tls_handshake() :: #client_hello{} | #server_hello{} | #server_hello_done{} | +#certificate{} | #client_key_exchange{} | #finished{} | #certificate_verify{}. + %%==================================================================== %% Internal application API %%==================================================================== %%-------------------------------------------------------------------- -%% Function: client_hello(Host, Port, ConnectionStates, SslOpts, Cert, Renegotiation) -> -%% #client_hello{} -%% Host -%% Port -%% ConnectionStates = #connection_states{} -%% SslOpts = #ssl_options{} +-spec client_hello(host(), port_num(), #connection_states{}, + #ssl_options{}, binary(), boolean()) -> #client_hello{}. %% %% Description: Creates a client hello message. %%-------------------------------------------------------------------- @@ -79,13 +78,8 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions, }. %%-------------------------------------------------------------------- -%% Function: server_hello(SessionId, Version, -%% ConnectionStates, Renegotiation) -> #server_hello{} -%% SessionId -%% Version -%% ConnectionStates -%% Renegotiation -%% +-spec server_hello(session_id(), tls_version(), #connection_states{}, + boolean()) -> #server_hello{}. %% %% Description: Creates a server hello message. %%-------------------------------------------------------------------- @@ -103,7 +97,7 @@ server_hello(SessionId, Version, ConnectionStates, Renegotiation) -> }. %%-------------------------------------------------------------------- -%% Function: hello_request() -> #hello_request{} +-spec hello_request() -> #hello_request{}. %% %% Description: Creates a hello request message sent by server to %% trigger renegotiation. @@ -112,15 +106,12 @@ hello_request() -> #hello_request{}. %%-------------------------------------------------------------------- -%% Function: hello(Hello, Info, Renegotiation) -> -%% {Version, Id, NewConnectionStates} | -%% #alert{} -%% -%% Hello = #client_hello{} | #server_hello{} -%% Info = ConnectionStates | {Port, #ssl_options{}, Session, -%% Cahce, CahceCb, ConnectionStates} -%% ConnectionStates = #connection_states{} -%% Renegotiation = boolean() +-spec hello(#server_hello{} | #client_hello{}, #ssl_options{}, + #connection_states{} | {port_num(), #session{}, cache_ref(), + atom(), #connection_states{}, binary()}, + boolean()) -> {tls_version(), session_id(), #connection_states{}}| + {tls_version(), {resumed | new, session_id()}, + #connection_states{}} | #alert{}. %% %% Description: Handles a recieved hello message %%-------------------------------------------------------------------- @@ -183,12 +174,9 @@ hello(#client_hello{client_version = ClientVersion, random = Random, end. %%-------------------------------------------------------------------- -%% Function: certify(Certs, CertDbRef, MaxPathLen) -> -%% {PeerCert, PublicKeyInfo} | #alert{} -%% -%% Certs = #certificate{} -%% CertDbRef = reference() -%% MaxPathLen = integer() | nolimit +-spec certify(#certificate{}, term(), integer() | nolimit, + verify_peer | verify_none, fun(), fun(), + client | server) -> {der_cert(), public_key_info()} | #alert{}. %% %% Description: Handles a certificate handshake message %%-------------------------------------------------------------------- @@ -244,10 +232,7 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, end. %%-------------------------------------------------------------------- -%% Function: certificate(OwnCert, CertDbRef, Role) -> #certificate{} -%% -%% OwnCert = binary() -%% CertDbRef = term() as returned by ssl_certificate_db:create() +-spec certificate(der_cert(), term(), client | server) -> #certificate{}. %% %% Description: Creates a certificate message. %%-------------------------------------------------------------------- @@ -273,10 +258,10 @@ certificate(OwnCert, CertDbRef, server) -> end. %%-------------------------------------------------------------------- -%% Function: client_certificate_verify(Cert, ConnectionStates) -> -%% #certificate_verify{} | ignore -%% Cert = #'OTPcertificate'{} -%% ConnectionStates = #connection_states{} +-spec client_certificate_verify(undefined | der_cert(), binary(), + tls_version(), key_algo(), private_key(), + {binary(), binary()}) -> + #certificate_verify{} | ignore. %% %% Description: Creates a certificate_verify message, called by the client. %%-------------------------------------------------------------------- @@ -298,10 +283,9 @@ client_certificate_verify(OwnCert, MasterSecret, Version, Algorithm, end. %%-------------------------------------------------------------------- -%% Function: certificate_verify(Signature, PublicKeyInfo) -> valid | #alert{} -%% -%% Signature = binary() -%% PublicKeyInfo = {Algorithm, PublicKey, PublicKeyParams} +-spec certificate_verify(binary(), public_key_info(), tls_version(), + binary(), key_algo(), + {binary(), binary()}) -> valid | #alert{}. %% %% Description: Checks that the certificate_verify message is valid. %%-------------------------------------------------------------------- @@ -325,8 +309,8 @@ certificate_verify(Signature, {_, PublicKey, PublicKeyParams}, Version, public_key:verify_signature(Hashes, sha, Signature, PublicKey, PublicKeyParams). %%-------------------------------------------------------------------- -%% Function: certificate_request(ConnectionStates, CertDbRef) -> -%% #certificate_request{} +-spec certificate_request(#connection_states{}, certdb_ref()) -> + #certificate_request{}. %% %% Description: Creates a certificate_request message, called by the server. %%-------------------------------------------------------------------- @@ -342,11 +326,12 @@ certificate_request(ConnectionStates, CertDbRef) -> }. %%-------------------------------------------------------------------- -%% Function: key_exchange(Role, Secret, Params) -> -%% #client_key_exchange{} | #server_key_exchange{} -%% -%% Secret - -%% Params - +-spec key_exchange(client | server, + {premaster_secret, binary(), public_key_info()} | + {dh, binary()} | + {dh, binary(), #'DHParameter'{}, key_algo(), + binary(), binary(), private_key()}) -> + #client_key_exchange{} | #server_key_exchange{}. %% %% Description: Creates a keyexchange message. %%-------------------------------------------------------------------- @@ -382,15 +367,9 @@ key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, signed_params = Signed}. %%-------------------------------------------------------------------- -%% Function: master_secret(Version, Session/PremasterSecret, -%% ConnectionStates, Role) -> -%% {MasterSecret, NewConnectionStates} | #alert{} -%% Version = #protocol_version{} -%% Session = #session{} (session contains master secret) -%% PremasterSecret = binary() -%% ConnectionStates = #connection_states{} -%% Role = client | server -%% +-spec master_secret(tls_version(), #session{} | binary(), #connection_states{}, + client | server) -> {binary(), #connection_states{}} | #alert{}. +%% %% Description: Sets or calculates the master secret and calculate keys, %% updating the pending connection states. The Mastersecret and the update %% connection states are returned or an alert if the calculation fails. @@ -427,9 +406,8 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) -> end. %%-------------------------------------------------------------------- -%% Function: finished(Version, Role, MacSecret, Hashes) -> #finished{} -%% -%% ConnectionStates = #connection_states{} +-spec finished(tls_version(), client | server, binary(), {binary(), binary()}) -> + #finished{}. %% %% Description: Creates a handshake finished message %%------------------------------------------------------------------- @@ -438,15 +416,8 @@ finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes calc_finished(Version, Role, MasterSecret, Hashes)}. %%-------------------------------------------------------------------- -%% Function: verify_connection(Finished, Role, -%% MasterSecret, Hashes) -> verified | #alert{} -%% -%% Finished = #finished{} -%% Role = client | server - the role of the process that sent the finished -%% message. -%% MasterSecret = binary() -%% Hashes = binary() - {md5_hash, sha_hash} -%% +-spec verify_connection(tls_version(), #finished{}, client | server, binary(), + {binary(), binary()}) -> verified | #alert{}. %% %% Description: Checks the ssl handshake finished message to verify %% the connection. @@ -462,17 +433,18 @@ verify_connection(Version, #finished{verify_data = Data}, _E -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) end. - +%%-------------------------------------------------------------------- +-spec server_hello_done() -> #server_hello_done{}. +%% +%% Description: Creates a server hello done message. +%%-------------------------------------------------------------------- server_hello_done() -> #server_hello_done{}. %%-------------------------------------------------------------------- -%% Function: encode_handshake(HandshakeRec) -> BinHandshake -%% HandshakeRec = #client_hello | #server_hello{} | server_hello_done | -%% #certificate{} | #client_key_exchange{} | #finished{} | -%% #client_certify_request{} +-spec encode_handshake(tls_handshake(), tls_version(), key_algo()) -> binary(). %% -%% encode a handshake packet to binary +%% Description: Encode a handshake packet to binary %%-------------------------------------------------------------------- encode_handshake(Package, Version, KeyAlg) -> SigAlg = sig_alg(KeyAlg), @@ -481,12 +453,11 @@ encode_handshake(Package, Version, KeyAlg) -> [MsgType, ?uint24(Len), Bin]. %%-------------------------------------------------------------------- -%% Function: get_tls_handshake(Data, Buffer) -> Result -%% Result = {[#handshake{}], [Raw], NewBuffer} -%% Data = Buffer = NewBuffer = Raw = binary() +-spec get_tls_handshake(binary(), binary(), key_algo(), tls_version()) -> + {[tls_handshake()], [binary()], binary()}. %% %% Description: Given buffered and new data from ssl_record, collects -%% and returns it as a list of #handshake, also returns leftover +%% and returns it as a list of handshake messages, also returns leftover %% data. %%-------------------------------------------------------------------- get_tls_handshake(Data, <<>>, KeyAlg, Version) -> @@ -495,6 +466,9 @@ get_tls_handshake(Data, Buffer, KeyAlg, Version) -> get_tls_handshake_aux(list_to_binary([Buffer, Data]), KeyAlg, Version, []). +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), Body:Length/binary,Rest/binary>>, KeyAlg, Version, Acc) -> @@ -504,9 +478,6 @@ get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), get_tls_handshake_aux(Data, _KeyAlg, _Version, Acc) -> {lists:reverse(Acc), Data}. -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- verify_bool(verify_peer) -> true; verify_bool(verify_none) -> diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index fdc0c33750..ddace02dea 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -23,6 +23,8 @@ -ifndef(ssl_internal). -define(ssl_internal, true). +-include_lib("public_key/include/public_key.hrl"). + %% basic binary constructors -define(BOOLEAN(X), X:8/unsigned-big-integer). -define(BYTE(X), X:8/unsigned-big-integer). @@ -88,6 +90,28 @@ active = true }). +-type reason() :: term(). +-type reply() :: term(). +-type msg() :: term(). +-type from() :: term(). +-type host() :: string() | tuple(). +-type port_num() :: integer(). +-type session_id() :: binary(). +-type tls_version() :: {integer(), integer()}. +-type tls_atom_version() :: sslv3 | tlsv1. +-type cache_ref() :: term(). +-type certdb_ref() :: term(). +-type key_algo() :: rsa | dhe_rsa | dhe_dss. +-type enum_algo() :: integer(). +-type public_key() :: #'RSAPublicKey'{} | integer(). +-type public_key_params() :: #'Dss-Parms'{} | term(). +-type public_key_info() :: {enum_algo(), public_key(), public_key_params()}. +-type der_cert() :: binary(). +-type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{}. +-type issuer() :: tuple(). +-type serialnumber() :: integer(). +-type cert_key() :: {reference(), integer(), issuer()}. + -endif. % -ifdef(ssl_internal). diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 19bdcfa1f5..af30f78dbf 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -24,8 +24,10 @@ -module(ssl_manager). -behaviour(gen_server). +-include("ssl_internal.hrl"). + %% Internal application API --export([start_link/0, start_link/1, +-export([start_link/1, connection_init/2, cache_pem_file/1, lookup_trusted_cert/3, issuer_candidate/1, client_session_id/3, server_session_id/3, register_session/2, register_session/3, invalidate_session/2, @@ -58,21 +60,25 @@ %% API %%==================================================================== %%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +-spec start_link(list()) -> {ok, pid()} | ignore | {error, term()}. +%% %% Description: Starts the server %%-------------------------------------------------------------------- -start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). start_link(Opts) -> gen_server:start_link({local, ?MODULE}, ?MODULE, [Opts], []). %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec connection_init(string(), client | server) -> {ok, reference(), cache_ref()}. +%% +%% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- connection_init(TrustedcertsFile, Role) -> call({connection_init, TrustedcertsFile, Role}). - +%%-------------------------------------------------------------------- +-spec cache_pem_file(string()) -> {ok, term()}. +%% +%% Description: Cach a pem file and +%%-------------------------------------------------------------------- cache_pem_file(File) -> case ssl_certificate_db:lookup_cached_certs(File) of [{_,Content}] -> @@ -80,48 +86,51 @@ cache_pem_file(File) -> [] -> call({cache_pem, File}) end. - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> + {der_cert(), #'OTPCertificate'{}}. +%% +%% Description: Lookup the trusted cert with Key = {reference(), serialnumber(), issuer()}. %%-------------------------------------------------------------------- lookup_trusted_cert(Ref, SerialNumber, Issuer) -> ssl_certificate_db:lookup_trusted_cert(Ref, SerialNumber, Issuer). - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec issuer_candidate(cert_key()) -> {cert_key(), der_cert()} | no_more_candidates. +%% +%% Description: Return next issuer candidate. %%-------------------------------------------------------------------- issuer_candidate(PrevCandidateKey) -> ssl_certificate_db:issuer_candidate(PrevCandidateKey). - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec client_session_id(host(), port_num(), #ssl_options{}) -> session_id(). +%% +%% Description: Select a session id for the client. %%-------------------------------------------------------------------- client_session_id(Host, Port, SslOpts) -> call({client_session_id, Host, Port, SslOpts}). - + %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec server_session_id(host(), port_num(), #ssl_options{}) -> session_id(). +%% +%% Description: Select a session id for the server. %%-------------------------------------------------------------------- server_session_id(Port, SuggestedSessionId, SslOpts) -> call({server_session_id, Port, SuggestedSessionId, SslOpts}). %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec register_session(host(), port_num(), #session{}) -> ok. +%% +%% Description: Make the session available for reuse. %%-------------------------------------------------------------------- register_session(Host, Port, Session) -> cast({register_session, Host, Port, Session}). register_session(Port, Session) -> cast({register_session, Port, Session}). - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec invalidate_session(host(), port_num(), #session{}) -> ok. +%% +%% Description: Make the session unavilable for reuse. %%-------------------------------------------------------------------- invalidate_session(Host, Port, Session) -> cast({invalidate_session, Host, Port, Session}). @@ -134,10 +143,9 @@ invalidate_session(Port, Session) -> %%==================================================================== %%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} +-spec init(list()) -> {ok, #state{}} | {ok, #state{}, timeout()} | + ignore | {stop, term()}. +%% %% Description: Initiates the server %%-------------------------------------------------------------------- init([Opts]) -> @@ -156,12 +164,13 @@ init([Opts]) -> session_validation_timer = Timer}}. %%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} +-spec handle_call(msg(), from(), #state{}) -> {reply, reply(), #state{}} | + {reply, reply(), #state{}, timeout()} | + {noreply, #state{}} | + {noreply, #state{}, timeout()} | + {stop, reason(), reply(), #state{}} | + {stop, reason(), #state{}}. +%% %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call({{connection_init, "", _Role}, Pid}, _From, @@ -207,9 +216,10 @@ handle_call({{cache_pem, File},Pid}, _, State = #state{certificate_db = Db}) -> {reply, {error, Reason}, State} end. %%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} +-spec handle_cast(msg(), #state{}) -> {noreply, #state{}} | + {noreply, #state{}, timeout()} | + {stop, reason(), #state{}}. +%% %% Description: Handling cast messages %%-------------------------------------------------------------------- handle_cast({register_session, Host, Port, Session}, @@ -243,9 +253,10 @@ handle_cast({invalidate_session, Port, #session{session_id = ID}}, {noreply, State}. %%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} +-spec handle_info(msg(), #state{}) -> {noreply, #state{}} | + {noreply, #state{}, timeout()} | + {stop, reason(), #state{}}. +%% %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info(validate_sessions, #state{session_cache_cb = CacheCb, @@ -278,7 +289,8 @@ handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() +-spec terminate(reason(), #state{}) -> term(). +%% %% Description: This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any necessary %% cleaning up. When it returns, the gen_server terminates with Reason. @@ -294,7 +306,8 @@ terminate(_Reason, #state{certificate_db = Db, ok. %%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} +-spec code_change(term(), #state{}, list()) -> {ok, #state{}}. +%% %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> @@ -339,4 +352,3 @@ session_validation({{{Host, Port}, _}, Session}, LifeTime) -> session_validation({{Port, _}, Session}, LifeTime) -> validate_session(Port, Session, LifeTime), LifeTime. - diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 6b7cffaa7d..90615c22a1 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -66,10 +66,9 @@ %%==================================================================== %% Internal application API %%==================================================================== + %%-------------------------------------------------------------------- -%% Function: init_connection_states(Role) -> #connection_states{} -%% Role = client | server -%% Random = binary() +-spec init_connection_states(client | server) -> #connection_states{}. %% %% Description: Creates a connection_states record with appropriate %% values for the initial SSL connection setup. @@ -85,9 +84,8 @@ init_connection_states(Role) -> }. %%-------------------------------------------------------------------- -%% Function: current_connection_state(States, Type) -> #connection_state{} -%% States = #connection_states{} -%% Type = read | write +-spec current_connection_state(#connection_states{}, read | write) -> + #connection_state{}. %% %% Description: Returns the instance of the connection_state record %% that is currently defined as the current conection state. @@ -100,9 +98,8 @@ current_connection_state(#connection_states{current_write = Current}, Current. %%-------------------------------------------------------------------- -%% Function: pending_connection_state(States, Type) -> #connection_state{} -%% States = #connection_states{} -%% Type = read | write +-spec pending_connection_state(#connection_states{}, read | write) -> + #connection_state{}. %% %% Description: Returns the instance of the connection_state record %% that is currently defined as the pending conection state. @@ -115,14 +112,11 @@ pending_connection_state(#connection_states{pending_write = Pending}, Pending. %%-------------------------------------------------------------------- -%% Function: update_security_params(Params, States) -> -%% #connection_states{} -%% Params = #security_parameters{} -%% States = #connection_states{} +-spec update_security_params(#security_parameters{}, #security_parameters{}, + #connection_states{}) -> #connection_states{}. %% %% Description: Creates a new instance of the connection_states record -%% where the pending states gets its security parameters -%% updated to <Params>. +%% where the pending states gets its security parameters updated. %%-------------------------------------------------------------------- update_security_params(ReadParams, WriteParams, States = #connection_states{pending_read = Read, @@ -135,14 +129,10 @@ update_security_params(ReadParams, WriteParams, States = WriteParams} }. %%-------------------------------------------------------------------- -%% Function: set_mac_secret(ClientWriteMacSecret, -%% ServerWriteMacSecret, Role, States) -> -%% #connection_states{} -%% MacSecret = binary() -%% States = #connection_states{} -%% Role = server | client +-spec set_mac_secret(binary(), binary(), client | server, + #connection_states{}) -> #connection_states{}. %% -%% update the mac_secret field in pending connection states +%% Description: update the mac_secret field in pending connection states %%-------------------------------------------------------------------- set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, client, States) -> set_mac_secret(ServerWriteMacSecret, ClientWriteMacSecret, States); @@ -159,12 +149,9 @@ set_mac_secret(ReadMacSecret, WriteMacSecret, %%-------------------------------------------------------------------- -%% Function: set_master_secret(MasterSecret, States) -> -%% #connection_states{} -%% MacSecret = -%% States = #connection_states{} +-spec set_master_secret(binary(), #connection_state{}) -> #connection_states{}. %% -%% Set master_secret in pending connection states +%% Description: Set master_secret in pending connection states %%-------------------------------------------------------------------- set_master_secret(MasterSecret, States = #connection_states{pending_read = Read, @@ -180,12 +167,9 @@ set_master_secret(MasterSecret, States#connection_states{pending_read = Read1, pending_write = Write1}. %%-------------------------------------------------------------------- -%% Function: set_renegotiation_flag(Flag, States) -> -%% #connection_states{} -%% Flag = boolean() -%% States = #connection_states{} +-spec set_renegotiation_flag(boolean(), #connection_states{}) -> #connection_states{}. %% -%% Set master_secret in pending connection states +%% Description: Set secure_renegotiation in pending connection states %%-------------------------------------------------------------------- set_renegotiation_flag(Flag, #connection_states{ current_read = CurrentRead0, @@ -203,13 +187,11 @@ set_renegotiation_flag(Flag, #connection_states{ pending_write = PendingWrite}. %%-------------------------------------------------------------------- -%% Function: set_client_verify_data(State, Data, States) -> -%% #connection_states{} -%% State = atom() -%% Data = binary() -%% States = #connection_states{} +-spec set_client_verify_data(current_read | current_write | current_both, + binary(), #connection_states{})-> + #connection_states{}. %% -%% Set verify data in connection states. +%% Description: Set verify data in connection states. %%-------------------------------------------------------------------- set_client_verify_data(current_read, Data, #connection_states{current_read = CurrentRead0, @@ -235,15 +217,12 @@ set_client_verify_data(current_both, Data, CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data}, ConnectionStates#connection_states{current_read = CurrentRead, current_write = CurrentWrite}. - %%-------------------------------------------------------------------- -%% Function: set_server_verify_data(State, Data, States) -> -%% #connection_states{} -%% State = atom() -%% Data = binary() -%% States = #connection_states{} +-spec set_server_verify_data(current_read | current_write | current_both, + binary(), #connection_states{})-> + #connection_states{}. %% -%% Set verify data in pending connection states. +%% Description: Set verify data in pending connection states. %%-------------------------------------------------------------------- set_server_verify_data(current_write, Data, #connection_states{pending_read = PendingRead0, @@ -273,10 +252,8 @@ set_server_verify_data(current_both, Data, current_write = CurrentWrite}. %%-------------------------------------------------------------------- -%% Function: activate_pending_connection_state(States, Type) -> -%% #connection_states{} -%% States = #connection_states{} -%% Type = read | write +-spec activate_pending_connection_state(#connection_states{}, read | write) -> + #connection_states{}. %% %% Description: Creates a new instance of the connection_states record %% where the pending state of <Type> has been activated. @@ -308,11 +285,9 @@ activate_pending_connection_state(States = }. %%-------------------------------------------------------------------- -%% Function: set_pending_cipher_state(States, ClientState, -%% ServerState, Role) -> -%% #connection_states{} -%% ClientState = ServerState = #cipher_state{} -%% States = #connection_states{} +-spec set_pending_cipher_state(#connection_states{}, #cipher_state{}, + #cipher_state{}, client | server) -> + #connection_states{}. %% %% Description: Set the cipher state in the specified pending connection state. %%-------------------------------------------------------------------- @@ -331,12 +306,10 @@ set_pending_cipher_state(#connection_states{pending_read = Read, pending_write = Write#connection_state{cipher_state = ClientState}}. %%-------------------------------------------------------------------- -%% Function: get_tls_record(Data, Buffer) -> Result -%% Result = {[#tls_compressed{}], NewBuffer} -%% Data = Buffer = NewBuffer = binary() -%% -%% Description: given old buffer and new data from TCP, packs up a records -%% and returns it as a list of #tls_compressed, also returns leftover +-spec get_tls_records(binary(), binary()) -> {[binary()], binary()}. +%% +%% Description: Given old buffer and new data from TCP, packs up a records +%% and returns it as a list of tls_compressed binaries also returns leftover %% data %%-------------------------------------------------------------------- get_tls_records(Data, <<>>) -> @@ -399,8 +372,7 @@ get_tls_records_aux(Data, Acc) -> {lists:reverse(Acc), Data}. %%-------------------------------------------------------------------- -%% Function: protocol_version(Version) -> #protocol_version{} -%% Version = atom() +-spec protocol_version(tls_atom_version()) -> tls_version(). %% %% Description: Creates a protocol version record from a version atom %% or vice versa. @@ -420,8 +392,7 @@ protocol_version({3, 1}) -> protocol_version({3, 0}) -> sslv3. %%-------------------------------------------------------------------- -%% Function: protocol_version(Version1, Version2) -> #protocol_version{} -%% Version1 = Version2 = #protocol_version{} +-spec lowest_protocol_version(tls_version(), tls_version()) -> tls_version(). %% %% Description: Lowes protocol version of two given versions %%-------------------------------------------------------------------- @@ -436,8 +407,7 @@ lowest_protocol_version(Version = {M,_}, lowest_protocol_version(_,Version) -> Version. %%-------------------------------------------------------------------- -%% Function: protocol_version(Versions) -> #protocol_version{} -%% Versions = [#protocol_version{}] +-spec highest_protocol_version([tls_version()]) -> tls_version(). %% %% Description: Highest protocol version present in a list %%-------------------------------------------------------------------- @@ -459,9 +429,8 @@ highest_protocol_version(_, [Version | Rest]) -> highest_protocol_version(Version, Rest). %%-------------------------------------------------------------------- -%% Function: supported_protocol_versions() -> Versions -%% Versions = [#protocol_version{}] -%% +-spec supported_protocol_versions() -> [tls_version()]. +%% %% Description: Protocol versions supported %%-------------------------------------------------------------------- supported_protocol_versions() -> @@ -487,8 +456,7 @@ supported_protocol_versions([_|_] = Vsns) -> Vsns. %%-------------------------------------------------------------------- -%% Function: is_acceptable_version(Version) -> true | false -%% Version = #protocol_version{} +-spec is_acceptable_version(tls_version()) -> boolean(). %% %% Description: ssl version 2 is not acceptable security risks are too big. %%-------------------------------------------------------------------- @@ -499,7 +467,7 @@ is_acceptable_version(_) -> false. %%-------------------------------------------------------------------- -%% Function: compressions() -> binary() +-spec compressions() -> binary(). %% %% Description: return a list of compressions supported (currently none) %%-------------------------------------------------------------------- @@ -507,8 +475,8 @@ compressions() -> [?byte(?NULL)]. %%-------------------------------------------------------------------- -%% Function: decode_cipher_text(CipherText, ConnectionStates0) -> -%% {Plain, ConnectionStates} +-spec decode_cipher_text(#ssl_tls{}, #connection_states{}) -> + {#ssl_tls{}, #connection_states{}}. %% %% Description: Decode cipher text %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index bcb10daf69..e9755cb0e1 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -32,11 +32,10 @@ -define(GEN_UNIQUE_ID_MAX_TRIES, 10). +-type seconds() :: integer(). + %%-------------------------------------------------------------------- -%% Function: is_new(ClientSuggestedId, ServerDecidedId) -> true | false -%% -%% ClientSuggestedId = binary() -%% ServerDecidedId = binary() +-spec is_new(binary(), binary()) -> boolean(). %% %% Description: Checks if the session id decided by the server is a %% new or resumed sesion id. @@ -45,17 +44,11 @@ is_new(<<>>, _) -> true; is_new(SessionId, SessionId) -> false; -is_new(_, _) -> +is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- -%% Function: id(ClientInfo, Cache, CacheCb) -> SessionId -%% -%% ClientInfo = {HostIP, Port, SslOpts} -%% HostIP = ipadress() -%% Port = integer() -%% CacheCb = atom() -%% SessionId = binary() +-spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom()) -> binary(). %% %% Description: Should be called by the client side to get an id %% for the client hello message. @@ -69,14 +62,8 @@ id(ClientInfo, Cache, CacheCb) -> end. %%-------------------------------------------------------------------- -%% Function: id(Port, SuggestedSessionId, ReuseFun, CacheCb, -%% SecondLifeTime) -> SessionId -%% -%% Port = integer() -%% SuggestedSessionId = SessionId = binary() -%% ReuseFun = fun(SessionId, PeerCert, Compression, CipherSuite) -> -%% true | false -%% CacheCb = atom() +-spec id(port_num(), binary(), #ssl_options{}, cache_ref(), + atom(), seconds()) -> binary(). %% %% Description: Should be called by the server side to get an id %% for the server hello message. @@ -95,10 +82,7 @@ id(Port, SuggestedSessionId, #ssl_options{reuse_sessions = ReuseEnabled, new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb) end. %%-------------------------------------------------------------------- -%% Function: valid_session(Session, LifeTime) -> true | false -%% -%% Session = #session{} -%% LifeTime = integer() - seconds +-spec valid_session(#session{}, seconds()) -> boolean(). %% %% Description: Check that the session has not expired %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl index 1f2d1fc7d3..823bf7acfa 100644 --- a/lib/ssl/src/ssl_session_cache.erl +++ b/lib/ssl/src/ssl_session_cache.erl @@ -22,13 +22,16 @@ -behaviour(ssl_session_cache_api). +-include("ssl_handshake.hrl"). +-include("ssl_internal.hrl"). + -export([init/1, terminate/1, lookup/2, update/3, delete/2, foldl/3, select_session/2]). +-type key() :: {{host(), port_num()}, session_id()} | {port_num(), session_id()}. + %%-------------------------------------------------------------------- -%% Function: init() -> Cache -%% -%% Cache - Reference to the cash (opaque) +-spec init(list()) -> cache_ref(). %% Returns reference to the cache (opaque) %% %% Description: Return table reference. Called by ssl_manager process. %%-------------------------------------------------------------------- @@ -36,9 +39,7 @@ init(_) -> ets:new(cache_name(), [set, protected]). %%-------------------------------------------------------------------- -%% Function: terminate(Cache) -> -%% -%% Cache - as returned by create/0 +-spec terminate(cache_ref()) -> any(). %% %% %% Description: Handles cache table at termination of ssl manager. %%-------------------------------------------------------------------- @@ -46,9 +47,7 @@ terminate(Cache) -> ets:delete(Cache). %%-------------------------------------------------------------------- -%% Function: lookup(Cache, Key) -> Session | undefined -%% Cache - as returned by create/0 -%% Session = #session{} +-spec lookup(cache_ref(), key()) -> #session{} | undefined. %% %% Description: Looks up a cach entry. Should be callable from any %% process. @@ -62,9 +61,7 @@ lookup(Cache, Key) -> end. %%-------------------------------------------------------------------- -%% Function: update(Cache, Key, Session) -> _ -%% Cache - as returned by create/0 -%% Session = #session{} +-spec update(cache_ref(), key(), #session{}) -> any(). %% %% Description: Caches a new session or updates a already cached one. %% Will only be called from the ssl_manager process. @@ -73,11 +70,7 @@ update(Cache, Key, Session) -> ets:insert(Cache, {Key, Session}). %%-------------------------------------------------------------------- -%% Function: delete(Cache, HostIP, Port, Id) -> _ -%% Cache - as returned by create/0 -%% HostIP = Host = string() | ipadress() -%% Port = integer() -%% Id = +-spec delete(cache_ref(), key()) -> any(). %% %% Description: Delets a cache entry. %% Will only be called from the ssl_manager process. @@ -86,28 +79,19 @@ delete(Cache, Key) -> ets:delete(Cache, Key). %%-------------------------------------------------------------------- -%% Function: foldl(Fun, Acc0, Cache) -> Acc -%% -%% Fun - fun() -%% Acc0 - term() -%% Cache - cache_ref() -%% +-spec foldl(fun(), term(), cache_ref()) -> term(). %% %% Description: Calls Fun(Elem, AccIn) on successive elements of the %% cache, starting with AccIn == Acc0. Fun/2 must return a new %% accumulator which is passed to the next call. The function returns -%% the final value of the accumulator. Acc0 is returned if the cache is -%% empty. -%% Should be callable from any process +%% the final value of the accumulator. Acc0 is returned if the cache +%% is empty.Should be callable from any process %%-------------------------------------------------------------------- foldl(Fun, Acc0, Cache) -> ets:foldl(Fun, Acc0, Cache). %%-------------------------------------------------------------------- -%% Function: select_session(Cache, PartialKey) -> [Sessions] -%% -%% Cache - as returned by create/0 -%% PartialKey - opaque Key = {PartialKey, SessionId} +-spec select_session(cache_ref(), {host(), port_num()} | port_num()) -> [#session{}]. %% %% Description: Selects a session that could be reused. Should be callable %% from any process. @@ -119,6 +103,5 @@ select_session(Cache, PartialKey) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- - cache_name() -> ssl_otp_session_cache. diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index 88b801d46b..375adf263a 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -38,6 +38,8 @@ %% Internal application API %%==================================================================== +-spec master_secret(binary(), binary(), binary()) -> binary(). + master_secret(PremasterSecret, ClientRandom, ServerRandom) -> ?DBG_HEX(PremasterSecret), ?DBG_HEX(ClientRandom), @@ -57,6 +59,8 @@ master_secret(PremasterSecret, ClientRandom, ServerRandom) -> ?DBG_HEX(B), B. +-spec finished(client | server, binary(), {binary(), binary()}) -> binary(). + finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished %% struct { @@ -75,6 +79,8 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> SHA = handshake_hash(?SHA, MasterSecret, Sender, SHAHash), <<MD5/binary, SHA/binary>>. +-spec certificate_verify(key_algo(), binary(), {binary(), binary()}) -> binary(). + certificate_verify(Algorithm, MasterSecret, {MD5Hash, SHAHash}) when Algorithm == rsa; Algorithm == dhe_rsa -> %% md5_hash @@ -94,6 +100,8 @@ certificate_verify(dhe_dss, MasterSecret, {_, SHAHash}) -> %% SHA(handshake_messages + master_secret + pad_1)); handshake_hash(?SHA, MasterSecret, undefined, SHAHash). +-spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary(). + mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) -> %% draft-ietf-tls-ssl-version3-00 - 5.2.3.1 %% hash(MAC_write_secret + pad_2 + @@ -113,6 +121,10 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) -> ?DBG_HEX(Mac), Mac. +-spec setup_keys(binary(), binary(), binary(), binary(), + integer(), integer(), binary()) -> {binary(), binary(), binary(), + binary(), binary(), binary()}. + setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) -> KeyBlock = generate_keyblock(MasterSecret, ServerRandom, ClientRandom, 2*(HS+KML+IVS)), @@ -136,6 +148,8 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) -> {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, ServerWriteKey, ClientIV, ServerIV}. +-spec suites() -> [cipher_suite()]. + suites() -> [ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index 70db632835..d1bc0730ba 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -36,6 +36,8 @@ %% Internal application API %%==================================================================== +-spec master_secret(binary(), binary(), binary()) -> binary(). + master_secret(PreMasterSecret, ClientRandom, ServerRandom) -> %% RFC 2246 & 4346 - 8.1 %% master_secret = PRF(pre_master_secret, %% "master secret", ClientHello.random + @@ -43,6 +45,8 @@ master_secret(PreMasterSecret, ClientRandom, ServerRandom) -> prf(PreMasterSecret, <<"master secret">>, [ClientRandom, ServerRandom], 48). +-spec finished(client | server, binary(), {binary(), binary()}) -> binary(). + finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% RFC 2246 & 4346 - 7.4.9. Finished %% struct { @@ -56,6 +60,7 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> SHA = hash_final(?SHA, SHAHash), prf(MasterSecret, finished_label(Role), [MD5, SHA], 12). +-spec certificate_verify(key_algo(), {binary(), binary()}) -> binary(). certificate_verify(Algorithm, {MD5Hash, SHAHash}) when Algorithm == rsa; Algorithm == dhe_rsa -> @@ -65,7 +70,11 @@ certificate_verify(Algorithm, {MD5Hash, SHAHash}) when Algorithm == rsa; certificate_verify(dhe_dss, {_, SHAHash}) -> hash_final(?SHA, SHAHash). - + +-spec setup_keys(binary(), binary(), binary(), integer(), + integer(), integer()) -> {binary(), binary(), binary(), + binary(), binary(), binary()}. + setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen, IVSize) -> %% RFC 2246 - 6.3. Key calculation @@ -112,6 +121,9 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, %% {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, %% ServerWriteKey, undefined, undefined}. +-spec mac_hash(integer(), binary(), integer(), integer(), tls_version(), + integer(), binary()) -> binary(). + mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, Length, Fragment) -> %% RFC 2246 & 4346 - 6.2.3.1. @@ -132,6 +144,8 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, ?DBG_HEX(Mac), Mac. +-spec suites() -> [cipher_suite()]. + suites() -> [ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml index 0b6807dd6c..1b34e71490 100644 --- a/lib/stdlib/doc/src/timer.xml +++ b/lib/stdlib/doc/src/timer.xml @@ -202,18 +202,33 @@ </func> <func> <name>tc(Module, Function, Arguments) -> {Time, Value}</name> - <fsummary>Measure the real time it takes to evaluate <c>apply(Module, Function, Arguments)</c></fsummary> + <name>tc(Fun, Arguments) -> {Time, Value}</name> + <fsummary>Measure the real time it takes to evaluate <c>apply(Module, + Function, Arguments)</c> or <c>apply(Fun, Arguments)</c></fsummary> <type> <v>Module = Function = atom()</v> + <v>Fun = fun()</v> <v>Arguments = [term()]</v> <v>Time = integer() in microseconds</v> <v>Value = term()</v> </type> <desc> - <p>Evaluates <c>apply(Module, Function, Arguments)</c> and measures - the elapsed real time. Returns <c>{Time, Value}</c>, where - <c>Time</c> is the elapsed real time in <em>microseconds</em>, - and <c>Value</c> is what is returned from the apply.</p> + <p></p> + <taglist> + <tag><c>tc/3</c></tag> + <item> + <p>Evaluates <c>apply(Module, Function, Arguments)</c> and measures + the elapsed real time as reported by <c>now/0</c>. + Returns <c>{Time, Value}</c>, where + <c>Time</c> is the elapsed real time in <em>microseconds</em>, + and <c>Value</c> is what is returned from the apply.</p> + </item> + <tag><c>tc/2</c></tag> + <item> + <p>Evaluates <c>apply(Fun, Arguments)</c>. Otherwise works + like <c>tc/3</c>.</p> + </item> + </taglist> </desc> </func> <func> diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index d26443f277..99e454f593 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.erl @@ -24,35 +24,41 @@ %% Internal API. -export([start/0, start/1]). --include_lib("kernel/include/file.hrl"). +%%----------------------------------------------------------------------- -define(SHEBANG, "/usr/bin/env escript"). -define(COMMENT, "This is an -*- erlang -*- file"). --record(state, {file, - module, +%%----------------------------------------------------------------------- + +-type mode() :: 'compile' | 'debug' | 'interpret' | 'run'. +-type source() :: 'archive' | 'beam' | 'text'. + +-record(state, {file :: file:filename(), + module :: module(), forms_or_bin, - source, - n_errors, - mode, - exports_main, - has_records}). --record(sections, {type, - shebang, - comment, - emu_args, - body}). --record(extract_options, {compile_source}). + source :: source(), + n_errors :: non_neg_integer(), + mode :: mode(), + exports_main :: boolean(), + has_records :: boolean()}). -type shebang() :: string(). -type comment() :: string(). -type emu_args() :: string(). --type escript_filename() :: string(). --type filename() :: string(). + +-record(sections, {type, + shebang :: shebang(), + comment :: comment(), + emu_args :: emu_args(), + body}). + +-record(extract_options, {compile_source}). + -type zip_file() :: - filename() - | {filename(), binary()} - | {filename(), binary(), #file_info{}}. + file:filename() + | {file:filename(), binary()} + | {file:filename(), binary(), file:file_info()}. -type zip_create_option() :: term(). -type section() :: shebang @@ -60,13 +66,15 @@ | comment | {comment, comment()} | {emu_args, emu_args()} - | {source, filename() | binary()} - | {beam, filename() | binary()} - | {archive, filename() | binary()} + | {source, file:filename() | binary()} + | {beam, file:filename() | binary()} + | {archive, file:filename() | binary()} | {archive, [zip_file()], [zip_create_option()]}. +%%----------------------------------------------------------------------- + %% Create a complete escript file with both header and body --spec create(escript_filename() | binary, [section()]) -> +-spec create(file:filename() | binary, [section()]) -> ok | {ok, binary()} | {error, term()}. create(File, Options) when is_list(Options) -> @@ -149,7 +157,9 @@ prepare(BadOptions, _) -> -type section_name() :: shebang | comment | emu_args | body . -type extract_option() :: compile_source | {section, [section_name()]}. --spec extract(filename(), [extract_option()]) -> {ok, [section()]} | {error, term()}. +-spec extract(file:filename(), [extract_option()]) -> + {ok, [section()]} | {error, term()}. + extract(File, Options) when is_list(File), is_list(Options) -> try EO = parse_extract_options(Options, @@ -239,6 +249,7 @@ normalize_section(Name, Chars) -> {Name, Chars}. -spec script_name() -> string(). + script_name() -> [ScriptName|_] = init:get_plain_arguments(), ScriptName. @@ -248,10 +259,12 @@ script_name() -> %% -spec start() -> no_return(). + start() -> start([]). -spec start([string()]) -> no_return(). + start(EscriptOptions) -> try %% Commands run using -run or -s are run in a process @@ -484,18 +497,12 @@ find_first_body_line(Fd, HeaderSz0, LineNo, KeepFirst, Sections) -> classify_line(Line) -> case Line of - [$\#, $\! | _] -> - shebang; - [$P, $K | _] -> - archive; - [$F, $O, $R, $1 | _] -> - beam; - [$%, $%, $\! | _] -> - emu_args; - [$% | _] -> - comment; - _ -> - undefined + "#!" ++ _ -> shebang; + "PK" ++ _ -> archive; + "FOR1" ++ _ -> beam; + "%%!" ++ _ -> emu_args; + "%" ++ _ -> comment; + _ -> undefined end. guess_type(Line) -> @@ -531,7 +538,6 @@ parse_archive(S, File, HeaderSz) -> end, list_to_atom(lists:reverse(RevBase2)) end, - S#state{source = archive, mode = run, module = Mod, @@ -587,8 +593,8 @@ parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) -> epp_parse_file2(Epp, S2, [ModForm, FileForm], OptModRes); {error, _} -> epp_parse_file2(Epp, S2, [FileForm], OptModRes); - {eof,LastLine} -> - S#state{forms_or_bin = [FileForm, {eof,LastLine}]} + {eof, _LastLine} = Eof -> + S#state{forms_or_bin = [FileForm, Eof]} end, ok = epp:close(Epp), ok = file:close(Fd), @@ -683,8 +689,8 @@ epp_parse_file2(Epp, S, Forms, Parsed) -> io:format("~s:~w: ~s\n", [S#state.file,Ln,Mod:format_error(Args)]), epp_parse_file(Epp, S#state{n_errors = S#state.n_errors + 1}, [Form | Forms]); - {eof,LastLine} -> - S#state{forms_or_bin = lists:reverse([{eof, LastLine} | Forms])} + {eof, _LastLine} = Eof -> + S#state{forms_or_bin = lists:reverse([Eof | Forms])} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/stdlib/src/timer.erl b/lib/stdlib/src/timer.erl index 6ee837c3e6..24e14caa69 100644 --- a/lib/stdlib/src/timer.erl +++ b/lib/stdlib/src/timer.erl @@ -22,7 +22,7 @@ send_after/3, send_after/2, exit_after/3, exit_after/2, kill_after/2, kill_after/1, apply_interval/4, send_interval/3, send_interval/2, - cancel/1, sleep/1, tc/3, now_diff/2, + cancel/1, sleep/1, tc/2, tc/3, now_diff/2, seconds/1, minutes/1, hours/1, hms/3]). -export([start_link/0, start/0, @@ -98,6 +98,17 @@ sleep(T) -> after T -> ok end. + +%% +%% Measure the execution time (in microseconds) for Fun(Args). +%% +-spec tc(function(), [_]) -> {time(), term()}. +tc(F, A) -> + Before = erlang:now(), + Val = (catch apply(F, A)), + After = erlang:now(), + {now_diff(After, Before), Val}. + %% %% Measure the execution time (in microseconds) for an MFA. %% diff --git a/lib/stdlib/test/timer_simple_SUITE.erl b/lib/stdlib/test/timer_simple_SUITE.erl index 021a22c61b..6aa2b7b945 100644 --- a/lib/stdlib/test/timer_simple_SUITE.erl +++ b/lib/stdlib/test/timer_simple_SUITE.erl @@ -224,11 +224,19 @@ cancel2(Config) when is_list(Config) -> tc(doc) -> "Test sleep/1 and tc/3."; tc(suite) -> []; tc(Config) when is_list(Config) -> - % This should both sleep and tc - ?line {Res, ok} = timer:tc(timer, sleep, [500]), - ?line ok = if - Res < 500*1000 -> {too_early, Res}; % Too early - Res > 800*1000 -> {too_late, Res}; % Too much time + % This should both sleep and tc/3 + ?line {Res1, ok} = timer:tc(timer, sleep, [500]), + ?line ok = if + Res1 < 500*1000 -> {too_early, Res1}; % Too early + Res1 > 800*1000 -> {too_late, Res1}; % Too much time + true -> ok + end, + + % This should both sleep and tc/2 + ?line {Res2, ok} = timer:tc(fun(T) -> timer:sleep(T) end, [500]), + ?line ok = if + Res2 < 500*1000 -> {too_early, Res2}; % Too early + Res2 > 800*1000 -> {too_late, Res2}; % Too much time true -> ok end, diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index f081bd60e2..02ea6d9d68 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -17,5 +17,4 @@ # %CopyrightEnd% # -STDLIB_VSN = 1.17 - +STDLIB_VSN = 1.17.1 diff --git a/system/doc/top/highlights.xml b/system/doc/top/highlights.xml index a30742aed6..251bb1a47d 100644 --- a/system/doc/top/highlights.xml +++ b/system/doc/top/highlights.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2006</year><year>2009</year> + <year>2006</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,12 +13,12 @@ compliance with the License. You should have received a copy of the Erlang Public License along with this software. If not, it can be retrieved online at http://www.erlang.org/. - + Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - + </legalnotice> <title>Highlights</title> @@ -28,133 +28,53 @@ <rev>A</rev> <file>highlights.xml</file> </header> - <p>This document lists some highlights of Erlang 5.7/OTP R13A - (unpatched), compared to the previous version Erlang 5.6/OTP R12B, - with focus on things not already released as R12B patches.</p> - <p>Note: This document was compiled at the time when R13A was released - and does not list any features introduced in R13 patches.</p> + <p>This document lists some highlights of Erlang 5.8/OTP R14A + (unpatched), compared to the previous version Erlang 5.7.1/OTP R13B, + with focus on things not already released as R13B patches.</p> + <p>Note: This document was compiled at the time when R14A was released + and does not list any features introduced in R14 patches.</p> <section> <title>Erlang Language and Run-time System</title> <section> - <title>Multi-core and SMP performance improvements</title> - <p> - There SMP performance is significantly improved and will - allow most applications to scale much better on systems with many - cores or processors. Listed below are some of the most important improvements: - </p> - <list type="bulleted"> - <item> - <p> - The runtime system with SMP support now uses <em>multiple, - scheduler specific run queues</em>, instead of one globally shared - run queue. - </p> - <p> - The lock protecting the shared run queue was heavily - contended, and the shared run queue also caused Erlang - processes to randomly migrate between schedulers with - negative cache effects as a result. - </p> - <p> - With the current scheduler specific run queue solution, lock - contention due to run queue protection has been reduced, and - Erlang processes are only migrated when needed to balance the - load between the schedulers. The reduced amount of migration - also reduce lock contention on locks protecting the scheduler - specific instances of the erts internal memory allocators. - </p> - <p> - The scheduler specific run queues are also a necessity for a - lot of future planned NUMA (Non-Uniform Memory Access) - specific optimizations. - </p> - </item> - <item> - <p> - Message passing has been further optimized for parallell execution. - This makes parallell sending to one common receiver much more efficient. - </p> - </item> - <item> - <p> - Scheduler threads can now be bound to logical processors on newer - Linux ans Solaris systems. - </p> - </item> - </list> - </section> - <section> - <title>Unicode support</title> - <p> - Support for Unicode is implemented as described in EEP10. - Formatting and reading of unicode data both from terminals - and files is supported by the <c>io</c> and <c>io_lib</c> modules. - Files - can be opened in modes with automatic translation to and from - different unicode formats. The module 'unicode' contains - functions for conversion between external and internal - unicode formats and the re module has support for unicode - data. There is also language syntax for specifying string and - character data beyond the ISO-latin-1 range. - </p> - </section> + <title>New NIF features</title> + <list type="bulleted"> + <item><p>Send messages from a NIF, or from thread created by NIF, to + any local process (enif_send)</p></item> - <section> - <title>New BIF's</title> - <p> - The BIFs <c>atom_to_binary/2</c>, <c>binary_to_atom/2</c> and - <c>binary_to_existing_atom/2</c> have been added. - </p> - </section> - - <section> - <title>Independent Erlang clusters on the same host</title> - <p> - Nodes belonging to different independent clusters can now - co-exist on the same host with the help of a new - environment variable setting <c>ERL_EPMD_PORT</c>. - The environment variable is used by Erl_interface and J_interface - as well. - </p> - </section> - </section> + <item><p>Store terms between NIF calls (enif_alloc_env, + enif_make_copy)</p></item> - <section> - <title>New Applications</title> + <item><p>Create binary terms with user defined memory management + (enif_make_resource_binary)</p></item> + <item><p>Some incompatible changes made to the API. For more + information see the warning text in erl_nif(3).</p></item> + </list> + </section> <section> - <title>Reltool</title> - - <p> - Reltool is a release management tool. - It analyses a given Erlang/OTP installation and determines - various dependencies between applications. - The graphical frontend depicts the dependencies and enables - interactive customization of a target system. - The backend provides a batch interface for generation of - customized target systems. - The application is still somewhat limited and should be regarded - as experimental in this release. The intention is that this - application will be a valuable tool for making both traditional - Erlang target systems as well as standalone components in Erlang. - </p> + <title>Receive optimizations</title> + <p>Receive statements that can only read out a newly created + reference are now specially optimized so that it will execute + in constant time regardless of the number of messages in the + receive queue for the process. That optimization will benefit + calls to gen_server:call(). (See gen:do_call/4 for an example + of a receive statement that will be optimized.)</p> </section> - <section> - <title>WxErlang</title> - <p> - wxErlang is an Erlang binding to the WxWidgets GUI library which - provides support for cross platform GUI applications. - wxErlang is still in beta status and the intention is that it shall - replace GS in a later stage. The Erlang debugger is also shipped in - a wxErlang version. - </p> + <title>Improved eprof</title> + <p>eprof has been reimplemented with support in the Erlang + virtual machine and is now both faster (i.e. slows down the + code being measured less) and scales much better. In + measurements we saw speed-ups compared to the old eprof + ranging from 6 times (for sequential code that only uses one + scheduler/core) up to 84 times (for parallel code that uses 8 + cores).</p> + + <p>Note: The API for the eprof has been cleaned up and extended. + See the documentation.</p> </section> - - </section> - <section> <title>New features in Existing Applications</title> @@ -162,77 +82,29 @@ <title>Common_test</title> <p> - A support client module for SSH and SFTP, <c>ct_ssh</c>, has - been introduced. - </p> - <p> - Test case groups have been introduced. With this feature - it's possible to execute groups (possibly nested) - of test cases. + It is now possible for the user to provide specific callback + modules that handle test configuration data, so that data on + arbitray form can be accessed (e.g. by reading files or by + communicating with a configuration server process). Two + default callback modules have been introduced in Common Test: + ct_config_plain and ct_config_xml. The former is used to + handle the traditional Common Test configuration files (with + terms on key-value tuple form) and the latter to handle + configuration data on XML representation. </p> <p> - A group definition contains a name tag, a list of - properties and a list of test cases (including possible - nested group definitions). The properties make it possible - to execute test cases in parallel, in sequence and in - shuffled order. It is also possible to repeat test cases - according to different criteria. + The run_test script has been replaced by a program (with the + same name) which can be executed without explicit + installation. The start flags are the same as for the legacy + start script. </p> </section> - - <section> - <title>Dialyzer</title> - <p> - The analysis now accepts <em>opaque type declarations</em> and - detects - violations of opaqueness of terms of such types. Starting - with R13, many Erlang/OTP standard libraries (array, dict, - digraph, ets, gb_sets, gb_trees, queue, and sets) contain - opaque type declarations of their main data types. Dialyzer - will spit out warnings in code that explicitly depends on the - structure of these terms. - </p> - <p> - Added support for handling UTF segments in bitstreams and for - detecting obvious type errors in these segments. Warning: - This code is not terribly tested though since there are very - few Erlang programs which use Unicode-based binaries - not - surprising since this is a new language feature of R13. - </p> - <p> - Strengthened the discrepancy identification when testing for - equality and matching between terms of different types. This - detects more bugs in code. - </p> - <p> - See the Dialyzer documentation and release notes for even more - enhancements. - </p> - </section> - - <section> - <title>SSL</title> - <p> - The "new_ssl" implementation is significantly improved and should be - near product status now. - The new SSL is implemented in pure Erlang except for - the crypto routines that are implemented in the crypto driver which - is an interface to libcrypto from OpenSSL. - </p> - </section> - - <section> <title>STDLIB</title> - - <p>The Erlang scanner has been augmented as to return white-space, - comments and exact location of tokens. - This means that the scanner can easily be used in tools such as editors, - pretty printers, syntax highlighters etc. - where it is important to be able recreate the original source document. + <p> + The module binary from EEP31 (and EEP9) is implemented. </p> </section> - </section> </chapter> diff --git a/system/doc/top/incompatible.xml b/system/doc/top/incompatible.xml index ce9522725b..e09fa7272f 100644 --- a/system/doc/top/incompatible.xml +++ b/system/doc/top/incompatible.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2006</year><year>2009</year> + <year>2006</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,12 +13,12 @@ compliance with the License. You should have received a copy of the Erlang Public License along with this software. If not, it can be retrieved online at http://www.erlang.org/. - + Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - + </legalnotice> <title>Potential Incompatibilities</title> @@ -29,353 +29,241 @@ <file>incompatible.xml</file> </header> <p>This document contains a list of potential incompatibilities - between Erlang 5.7/OTP R12A and Erl 5.6.5/OTP R12B-5, + between Erlang 5.8/OTP R14A and Erl 5.7.5/OTP R13B04, and is an extract from the release notes for the respective applications.</p> <section> - <title>compiler</title> + <title>Tools</title> <list type="bulleted"> <item> <p> - The undocumented, unsupported, and deprecated guard BIF - <c>is_constant/1</c> has been removed.</p> - <p> - *** INCOMPATIBILITY with R12B ***</p> - <p> - Own Id: OTP-7673</p> - </item> - <item> - <p>The short-circuit operators <c>andalso</c> and - <c>orelse</c> no longer guarantees that their second - argument is either <c>true</c> or <c>false</c>. As a - consequence, <c>andalso</c>/<c>orelse</c> are now - tail-recursive.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> + Xref has been updated to use the re module instead of the + deprecated regexp module.</p> + <p>*** INCOMPATIBILITY with R12B ***</p> <p> - Own Id: OTP-7748</p> + Own Id: OTP-8472</p> </item> <item> - <p>The compiler will refuse to a compile file where the - module name in the file differs from the output file - name.</p> - <p>When compiling using <c>erlc</c>, the current working - directory will no be included in the code path (unless - explicitly added using "-pa .").</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-7793</p> - </item> - <item> - <p>If a module contains an exported function with the - same name as an auto-imported BIF (such as - <c>length/1</c>), any calls to the BIF must have an - explicit <c>erlang:</c> prefix, or there will be a - compilation error (such calls would only generate a - warning in previous releases).</p> - <p>(The reason for the change is to avoid breaking code - in a future major release, R14 or R15, in which we plan - to make calls without a module prefix always call the - local function in the same module even if there is an - auto-imported BIF with the same name.)</p> + <p>eprof has been reimplemented with support in the Erlang + virtual machine and is now both faster (i.e. slows down the + code being measured less) and scales much better. In + measurements we saw speed-ups compared to the old eprof + ranging from 6 times (for sequential code that only uses one + scheduler/core) up to 84 times (for parallel code that uses 8 + cores).</p> + + <p>Note: The API for the eprof has been cleaned up and extended. + See the documentation.</p> <p> *** POTENTIAL INCOMPATIBILITY ***</p> <p> - Own Id: OTP-7873</p> + Own Id: OTP-8706</p> </item> </list> </section> <section> - <title>Erts</title> + <title>Odbc</title> <list type="bulleted"> <item> - <p>Support for Unicode is implemented as described in - EEP10. Formatting and reading of unicode data both from - terminals and files is supported by the io and io_lib - modules. Files can be opened in modes with automatic - translation to and from different unicode formats. The - module 'unicode' contains functions for conversion - between external and internal unicode formats and the re - module has support for unicode data. There is also - language syntax for specifying string and character data - beyond the ISO-latin-1 range.</p> - <p>The interactive shell will support input and output of - unicode characters when the terminal and operating system - supports it.</p> - <p>Please see the EEP and the io/io_lib manual pages as - well as the stdlib users guide for details.</p> - <p><em>I/O-protocol incompatibilities:</em></p> - <p>The io_protocol between io_Server and client is - updated to handle protocol data in unicode formats. The - updated protocol is now documented. The specification - resides in the stdlib <em>users manual</em>, which is a - new part of the manual.</p> - <p><em>io module incompatibilities:</em></p> - <p>The io:put_chars, io:get_chars and io:get_line all - handle and return unicode data. In the case where - binaries can be provided (as to io:put_chars), they shall - be encoded in UTF-8. When binaries are returned (as by - io:get_line/get_chars when the io_server is set in - <em>binary mode</em>) the returned data is also - <em>always</em> encoded as UTF-8. The file module however - still returns byte-oriented data, why file:read can be - used instead of io:get_chars to read binary data in - ISO-latin-1.</p> - <p><em>io_lib module incompatibilities:</em></p> - <p>io_lib:format can, given new format directives (i.e - "~ts" and "~tc"), return lists containing integers larger - than 255. </p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-7648 Aux Id: OTP-7580 OTP-7514 OTP-7494 - OTP-7443 OTP-7181 EEP10 EEP11 </p> - </item> - <item> - <p> - The undocumented, unsupported, and deprecated guard BIF - <c>is_constant/1</c> has been removed.</p> - <p> - *** INCOMPATIBILITY with R12B ***</p> - <p> - Own Id: OTP-7673</p> - </item> - <item> - <p>The short-circuit operators <c>andalso</c> and - <c>orelse</c> no longer guarantees that their second - argument is either <c>true</c> or <c>false</c>. As a - consequence, <c>andalso</c>/<c>orelse</c> are now - tail-recursive.</p> + <p> + Now supports SQL_TYPE_TIMESTAMP on the format {{YY, MM, DD}, + {HH, MM, SS}}. Thanks to Juhani Ränkimies. + </p> <p> *** POTENTIAL INCOMPATIBILITY ***</p> <p> - Own Id: OTP-7748</p> + Own Id: OTP-8511</p> </item> - <item> - <p>The compiler will refuse to a compile file where the - module name in the file differs from the output file - name.</p> - <p>When compiling using <c>erlc</c>, the current working - directory will no be included in the code path (unless - explicitly added using "-pa .").</p> + </list> + </section> + + <section> + <title>Ssh</title> + <list type="bulleted"> + <item> <p> - *** POTENTIAL INCOMPATIBILITY ***</p> + The configuration parameter ip_v6_disabled is now available, + which makes it possible for the user to alter the IP version + SSH shall use.</p> <p> - Own Id: OTP-7793</p> - </item> - <item> - <p>The deprecated functions <c>erlang:fault/1</c>, - <c>erlang:fault/2</c>, and <c>file:rawopen/2</c> have - been removed.</p> + *** POTENTIAL INCOMPATIBILITY ***</p> <p> - *** POTENTIAL INCOMPATIBILITY ***</p> + Own Id: OTP-8535</p> + </item> + <item> <p> - Own Id: OTP-7812</p> - </item> - <item> - <p>The escape sequences <c>\x</c> and <c>\{</c> have been - assigned new interpretations (they used to return the - ASCII code for <c>x</c> and <c>{</c> respectively). One - or more octal characters inside curly brackets after a - leading backslash is from now on an alternative to the - existing syntax <c>\NNN</c>, but can also be used for - codes greater than 255. In a similar fashion, one or more - hexadecimal characters can be put inside curly brackets - after a leading <c>\x</c>. Furthermore, the escape - sequences <c>\xH</c> and <c>\xHH</c>, where N is a - hexadecimal character, can be used for codes less than - 256.</p> - <p>NOTE: These new escape sequences are still considered - experimental and may be changed in the R13B release.</p> + Removed deprecated modules (ssh_ssh, ssh_sshd and ssh_cm) and + functions (ssh_sftp:connect and ssh_sftp:stop).</p> <p> - *** POTENTIAL INCOMPATIBILITY ***</p> + *** POTENTIAL INCOMPATIBILITY ***</p> <p> - Own Id: OTP-7855</p> - </item> - </list> - </section> - - <section> - <title>Inets</title> - <list type="bulleted"> + Own Id: OTP-8596</p> + </item> <item> <p> - [httpc] - The inets http client will now use persistent - connections without pipelining as default and if a - pipeline timeout is set it will pipeline the requests on - the persistent connections.</p> + Aligned error message with used version (SSH_FX_FAILURE vs + SSH_FX_NOT_A_DIRECTORY, the latter introduced in version 6).</p> <p> *** POTENTIAL INCOMPATIBILITY ***</p> <p> - Own Id: OTP-7463</p> - </item> + Own Id: OTP-8644</p> + </item> </list> </section> <section> - <title>Jinterface</title> + <title>ERTS</title> <list> <item> - <p> - A number of fixes and improvements from the ErlIDE group; - Vlad Dumitrescu and Jakob Cederlund: JDK 1.5 is now a - minimal requirement for building Jinterface. New method: - OtpEpmd.lookupNames. OtpErlangList is now iterable. - Non-proper lists are now allowed - you have to test if a - list is proper or not. Non-proper lists can also be - created. New methods: isProper, getHead, getTail and - getNthTail. The get tail methods creates a sublist object - that re-uses the original list. OtpErlangPid is now - Comparable. Empty atoms can now be constructed, a missing - feature pointed out by Sebastien Boisgerault on - erlang-questions.</p> + <p>Some incompatible changes made to the NIF API. For more + information see the warning text in erl_nif(3).</p> <p> *** POTENTIAL INCOMPATIBILITY ***</p> <p> - Own Id: OTP-7832</p> + Own Id: OTP-8555</p> </item> </list> </section> <section> - <title>Kernel</title> + <title>STDLIB</title> <list type="bulleted"> <item> - <p>The deprecated functions <c>erlang:fault/1</c>, - <c>erlang:fault/2</c>, and <c>file:rawopen/2</c> have - been removed.</p> + <p>When defining macros the closing right parenthesis before the + dot is now mandatory.</p> <p> *** POTENTIAL INCOMPATIBILITY ***</p> <p> - Own Id: OTP-7812</p> + Own Id: OTP-8562</p> </item> </list> </section> <section> - <title>SSH</title> + <title>Compiler and STDLIB</title> <list type="bulleted"> <item> <p> - Ssh timeouts will now behave as expected i.e. defaults to - infinity. Only the user of the ssh application can know of - a reasonable timeout value for their application.</p> + Local and imported functions now override the auto-imported + BIFs when the names clash. The pre R14 behaviour was that + auto-imported BIFs would override local functions. To avoid + that old programs change behaviour, the following will + generate an error:</p> + + <list type="bulleted"> + <item>Doing a call without explicit module name to a local function + having a name clashing with the name of an auto-imported BIF + that was present (and auto-imported) before OTP R14A</item> + + <item>Explicitly importing a function having a name clashing with + the name of an autoimported BIF that was present (and + autoimported) before OTP R14A</item> + + <item>Using any form of the old compiler directive nowarn_bif_clash</item> + </list> + + <p>If the BIF was added or auto-imported in OTP R14A or later, + overriding it with an import or a local function will only + result in a warning.</p> + + <p>To resolve clashes, you can either use the explicit module + name erlang to call the BIF, or you can remove the + auto-import of that specific BIF by using the new compiler + directive -compile({no_auto_import,[F/A]})., which makes all + calls to the local or imported function without explicit + module name pass without warnings or errors.</p> + + <p>The change makes it possible to add auto-imported BIFs + without breaking or silently changing old code in the future. + However some current code ingeniously utilizing the old + behaviour or the nowarn_bif_clash compiler directive, might + need changing to be accepted by the compiler.</p> <p> *** POTENTIAL INCOMPATIBILITY ***</p> <p> - Own Id: OTP-7807</p> + Own Id: OTP-8579</p> </item> <item> <p> - Added the message {ssh_channel_up, ChannelId, - ConnectionManager} that shall be handled by the channel - callback handle_msg/2. This makes the function - handle_msg/2 a mandatory function for ssh channels - implementations which it was not in ssh-1.1.</p> + The recently added BIFs erlang:min/2, erlang:max/2 and + erlang:port_command/3 are now auto-imported (as they were + originally intended to be). Due to the recent compiler change + (OTP-8579), the only impact on old code defining it's own + min/2, max/2 or port_command/3 functions will be a warning, + the local functions will still be used. The warning can be + removed by using + -compile({no_auto_import,[min/2,max/2,port_command/3]}). in + the source file.</p> <p> *** POTENTIAL INCOMPATIBILITY ***</p> <p> - Own Id: OTP-7828</p> + Own Id: OTP-8669</p> </item> - </list> - </section> - - <section> - <title>STDLIB</title> - <list type="bulleted"> <item> - <p>The functions <c>lists:seq/1,2</c> return the empty - list in a few cases when they used to generate an - exception, for example <c>lists:seq(1, 0)</c>. See - lists(3) for details. (Thanks to Richard O'Keefe.)</p> <p> - *** POTENTIAL INCOMPATIBILITY ***</p> + Now, binary_to_term/2 is auto-imported. This will cause a + compile warning if and only if a module has got a local + function with that name.</p> <p> - Own Id: OTP-7230</p> - </item> - <item> - <p>Support for Unicode is implemented as described in - EEP10. Formatting and reading of unicode data both from - terminals and files is supported by the io and io_lib - modules. Files can be opened in modes with automatic - translation to and from different unicode formats. The - module 'unicode' contains functions for conversion - between external and internal unicode formats and the re - module has support for unicode data. There is also - language syntax for specifying string and character data - beyond the ISO-latin-1 range.</p> - <p>The interactive shell will support input and output of - unicode characters when the terminal and operating system - supports it.</p> - <p>Please see the EEP and the io/io_lib manual pages as - well as the stdlib users guide for details.</p> - <p><em>I/O-protocol incompatibilities:</em></p> - <p>The io_protocol between io_Server and client is - updated to handle protocol data in unicode formats. The - updated protocol is now documented. The specification - resides in the stdlib <em>users manual</em>, which is a - new part of the manual.</p> - <p><em>io module incompatibilities:</em></p> - <p>The io:put_chars, io:get_chars and io:get_line all - handle and return unicode data. In the case where - binaries can be provided (as to io:put_chars), they shall - be encoded in UTF-8. When binaries are returned (as by - io:get_line/get_chars when the io_server is set in - <em>binary mode</em>) the returned data is also - <em>always</em> encoded as UTF-8. The file module however - still returns byte-oriented data, why file:read can be - used instead of io:get_chars to read binary data in - ISO-latin-1.</p> - <p><em>io_lib module incompatibilities:</em></p> - <p>io_lib:format can, given new format directives (i.e - "~ts" and "~tc"), return lists containing integers larger - than 255. </p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-7648 Aux Id: OTP-7580 OTP-7514 OTP-7494 - OTP-7443 OTP-7181 EEP10 EEP11 </p> - </item> - <item> - <p> - <c>filelib:fold_files/5</c> now uses the <c>re</c> module - instead of the <c>regexp</c> module for regular - expression matching. In practice, this change will not be - a problem for most regular expressions used for - <c>filelib:fold_files/5</c>. (The major difference in - regular expression is that parenthesis and curly brackets - is treated as literal characters by <c>regexp</c> but as - special characters by <c>re</c>; fortunately, those - characters are rarely used in filenames.)</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> + *** POTENTIAL INCOMPATIBILITY ***</p> <p> - Own Id: OTP-7819</p> + Own Id: OTP-8671</p> </item> + </list> + </section> + + <section> + <title>Erl_interface</title> + <list type="bulleted"> <item> - <p> - <c>digraph:new(Type)</c> will now cause a <c>badarg</c> - exception if <c>Type</c> is not a valid type. Similarly, - <c>digraph_utils:subgraph/2,3</c> will now cause a - <c>badarg</c> if the arguments are invalid. (Those - functions used to return error tuples if something was - wrong.)</p> + <p>Compact IEEE 754 double encoding in external binary format + for ei</p> + + <p>Implement the compact IEEE 754 double encoding in external + binary format for ei. Encoding for ei now always produces the + NEW_FLOAT_EXT format. Decoding and term printing handle both + the old ERL_FLOAT_EXT encoding and the new NEW_FLOAT_EXT + encoding.</p> + + <p>Legacy erl_interface code also handles the new encoding, but + still produces the ERL_FLOAT_EXT encoding by default.</p> + + <p>Also enable the DFLAG_NEW_FLOATS distribution flag.</p> + + <p>ei_get_type() will return ERL_FLOAT_EXT regardless if the + external format is encoded with ERL_FLOAT_EXT or + NEW_FLOAT_EXT for doubles.</p> + + <p>Reduce the number of copies of the code for encoding and + decoding doubles throughout ei and erl_interface by instead + calling the ei encoding and decoding functions wherever + possible.</p> + + <p>Restore commented-out float tests in ei_decode_SUITE and + ei_encode_SUITE in lib/erl_interface/test. Modify them to + make them match the style of other tests in the same suites.</p> + + <p>These changes are based on an ei float patch from Serge + Aleynikov originally submitted against R12B-2 in July 2008 + and reworked by Steve Vinoski May 2010.</p> <p> *** POTENTIAL INCOMPATIBILITY ***</p> <p> - Own Id: OTP-7824</p> + Own Id: OTP-8684</p> </item> + </list> + </section> + <section> + <title>Debugger and STDLIB</title> + <list type="bulleted"> <item> - <p>The argument passed to <c>random:uniform/1</c> must - now be an integer (as stated in the documentation). In - previous releases, a floating point number was also - allowed.</p> + <p> The predefined builtin type tid() has been removed. Instead, + ets:tid() should be used.</p> <p> *** POTENTIAL INCOMPATIBILITY ***</p> <p> - Own Id: OTP-7827</p> + Own Id: OTP-8687</p> </item> </list> </section> |