diff options
62 files changed, 1824 insertions, 562 deletions
diff --git a/Makefile.in b/Makefile.in index d1fd64632c..85e0f3c89a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -124,7 +124,7 @@ BINDIR = $(DESTDIR)$(EXTRA_PREFIX)$(bindir) # # Erlang base public files # -ERL_BASE_PUB_FILES=erl erlc epmd run_erl to_erl dialyzer typer escript ct_run +ERL_BASE_PUB_FILES=erl erlc epmd run_erl to_erl dialyzer typer escript reltool ct_run # ERLANG_INST_LIBDIR is the top directory where the Erlang installation # will be located when running. diff --git a/bootstrap/lib/compiler/ebin/cerl_inline.beam b/bootstrap/lib/compiler/ebin/cerl_inline.beam Binary files differindex 0d7a4c0e87..3fa8c7d69f 100644 --- a/bootstrap/lib/compiler/ebin/cerl_inline.beam +++ b/bootstrap/lib/compiler/ebin/cerl_inline.beam diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam Binary files differindex 7e2960fda2..7410397300 100644 --- a/bootstrap/lib/compiler/ebin/v3_core.beam +++ b/bootstrap/lib/compiler/ebin/v3_core.beam diff --git a/bootstrap/lib/stdlib/ebin/edlin.beam b/bootstrap/lib/stdlib/ebin/edlin.beam Binary files differindex bb586c5f6a..7b8e03a722 100644 --- a/bootstrap/lib/stdlib/ebin/edlin.beam +++ b/bootstrap/lib/stdlib/ebin/edlin.beam diff --git a/erts/etc/unix/Install.src b/erts/etc/unix/Install.src index 0f33258a28..420d2773ca 100644 --- a/erts/etc/unix/Install.src +++ b/erts/etc/unix/Install.src @@ -91,6 +91,7 @@ cp -p "$ERL_ROOT/erts-%I_VSN%/bin/dialyzer" . cp -p "$ERL_ROOT/erts-%I_VSN%/bin/typer" . cp -p "$ERL_ROOT/erts-%I_VSN%/bin/ct_run" . cp -p "$ERL_ROOT/erts-%I_VSN%/bin/escript" . +cp -p "reltool" "$ERL_ROOT/erts-%I_VSN%/bin/" # # Set a soft link to epmd diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c index 53c779b1be..b69e31f784 100644 --- a/erts/etc/unix/run_erl.c +++ b/erts/etc/unix/run_erl.c @@ -1142,6 +1142,14 @@ static void daemon_init(void) sf_close(i); } + /* Necessary on some platforms */ + + open("/dev/null", O_RDONLY); /* Order is important! */ + open("/dev/null", O_WRONLY); + open("/dev/null", O_WRONLY); + + errno = 0; /* if set by open */ + OPEN_SYSLOG(); run_daemon = 1; } diff --git a/erts/etc/win32/Install.c b/erts/etc/win32/Install.c index dd02a9c111..2ccd610bb6 100644 --- a/erts/etc/win32/Install.c +++ b/erts/etc/win32/Install.c @@ -46,7 +46,7 @@ int main(int argc, char **argv) HANDLE module = GetModuleHandle(NULL); char *binaries[] = { "erl.exe", "werl.exe", "erlc.exe", "dialyzer.exe", "typer.exe", - "escript.exe", "ct_run.exe", NULL }; + "escript.exe", "reltool", "ct_run.exe", NULL }; char *scripts[] = { "start_clean.boot", "start_sasl.boot", NULL }; char fromname[MAX_PATH]; char toname[MAX_PATH]; diff --git a/erts/etc/win32/erlang.ico b/erts/etc/win32/erlang.ico Binary files differindex cee8b58af9..92857c507a 100644 --- a/erts/etc/win32/erlang.ico +++ b/erts/etc/win32/erlang.ico diff --git a/lib/compiler/src/cerl_inline.erl b/lib/compiler/src/cerl_inline.erl index 2e7554c1ff..c6de63c69f 100644 --- a/lib/compiler/src/cerl_inline.erl +++ b/lib/compiler/src/cerl_inline.erl @@ -52,7 +52,7 @@ clause_pats/1, clause_vars/1, concrete/1, cons_hd/1, cons_tl/1, data_arity/1, data_es/1, data_type/1, fun_body/1, fun_vars/1, get_ann/1, int_val/1, - is_c_atom/1, is_c_cons/1, is_c_fun/1, is_c_int/1, + is_c_atom/1, is_c_cons/1, is_c_fname/1, is_c_int/1, is_c_list/1, is_c_seq/1, is_c_tuple/1, is_c_var/1, is_data/1, is_literal/1, is_literal_term/1, let_arg/1, let_body/1, let_vars/1, letrec_body/1, letrec_defs/1, @@ -1578,7 +1578,7 @@ make_let_binding_1(R, E, S) -> %% completely. copy(R, Opnd, E, Ctxt, Env, S) -> - case is_c_var(E) of + case is_c_var(E) andalso not is_c_fname(E) of true -> %% The operand reduces to another variable - get its %% ref-structure and attempt to propagate further. @@ -1628,12 +1628,12 @@ copy_var(R, Ctxt, Env, S) -> end. copy_1(R, Opnd, E, Ctxt, Env, S) -> - %% Fun-expression (lambdas) are a bit special; they are copyable, - %% but should preferably not be duplicated, so they should not be - %% copy propagated except into application contexts, where they can - %% be inlined. - case is_c_fun(E) of - true -> + case type(E) of + 'fun' -> + %% Fun-expression (lambdas) are a bit special; they are copyable, + %% but should preferably not be duplicated, so they should not be + %% copy propagated except into application contexts, where they can + %% be inlined. case Ctxt of #app{} -> %% First test if the operand is "outer-pending"; if @@ -1649,7 +1649,28 @@ copy_1(R, Opnd, E, Ctxt, Env, S) -> _ -> residualize_var(R, S) end; - false -> + var -> + %% Variables at this point only refer to local functions; they are + %% copyable but can't appear in guards, so they should not be + %% copy propagated except into application contexts, where they can + %% be inlined. + case Ctxt of + #app{} -> + %% First test if the operand is "outer-pending"; if + %% so, don't inline. + case st__test_outer_pending(Opnd#opnd.loc, S) of + false -> + R1 = env__get(var_name(E), Opnd#opnd.env), + copy_var(R1, Ctxt, Env, S); + true -> + %% Cyclic reference forced inlining to stop + %% (avoiding infinite unfolding). + residualize_var(R, S) + end; + _ -> + residualize_var(R, S) + end; + _ -> %% We have no other cases to handle here residualize_var(R, S) end. diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index d6fdcb2b21..1195937d91 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -956,7 +956,8 @@ lc_tq(Line, E, [Fil0|Qs0], Mc, St0) -> args=[], clauses=[#iclause{anno=LAnno,pats=[], guard=Gs,body=Lps ++ [Lc]}], - fc=#iclause{anno=LAnno,pats=[],guard=[],body=[Mc]}}, + fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, + pats=[],guard=[],body=[Mc]}}, [],St2}; false -> {Lc,Lps,St1} = lc_tq(Line, E, Qs0, Mc, St0), @@ -1101,7 +1102,8 @@ bc_tq1(Line, E, [Fil0|Qs0], AccVar, St0) -> clauses=[#iclause{anno=LAnno, pats=[], guard=Gs,body=Bps ++ [Bc]}], - fc=#iclause{anno=LAnno,pats=[],guard=[],body=[AccVar]}}, + fc=#iclause{anno=LAnno#a{anno=[compiler_generated|LA]}, + pats=[],guard=[],body=[AccVar]}}, [],St}; false -> {Bc,Bps,St1} = bc_tq1(Line, E, Qs0, AccVar, St0), diff --git a/lib/compiler/test/inline_SUITE.erl b/lib/compiler/test/inline_SUITE.erl index d9b92766e4..e5c2d4f73a 100644 --- a/lib/compiler/test/inline_SUITE.erl +++ b/lib/compiler/test/inline_SUITE.erl @@ -36,7 +36,7 @@ all() -> groups() -> [{p,test_lib:parallel(), - [attribute,bsdecode,bsdes,barnes2,decode1,smith, + [attribute,bsdecode,bsdes,barnes2,decode1,smith,fname, itracer,pseudoknot,comma_splitter,lists,really_inlined,otp_7223, coverage]}]. @@ -84,6 +84,7 @@ attribute(Config) when is_list(Config) -> ?comp(itracer). ?comp(pseudoknot). ?comp(comma_splitter). +?comp(fname). try_inline(Mod, Config) -> Node = ?config(testing_node, Config), diff --git a/lib/compiler/test/inline_SUITE_data/fname.erl b/lib/compiler/test/inline_SUITE_data/fname.erl new file mode 100644 index 0000000000..7ad4446bf3 --- /dev/null +++ b/lib/compiler/test/inline_SUITE_data/fname.erl @@ -0,0 +1,29 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% 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. +%% +%% %CopyrightEnd% +%% +-module(fname). +-export([?MODULE/0]). + +?MODULE() -> + F = fun bar/1, + G = lists:last([(fun (X) when F =:= X -> X end)]), + F = G(F), + ok. + +bar(X) -> + X. diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl index f00bfe663b..810b2b48c9 100644 --- a/lib/compiler/test/warnings_SUITE.erl +++ b/lib/compiler/test/warnings_SUITE.erl @@ -37,7 +37,7 @@ -export([pattern/1,pattern2/1,pattern3/1,pattern4/1, guard/1,bad_arith/1,bool_cases/1,bad_apply/1, - files/1,effect/1,bin_opt_info/1,bin_construction/1]). + files/1,effect/1,bin_opt_info/1,bin_construction/1, comprehensions/1]). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(2)). @@ -61,7 +61,7 @@ groups() -> [{p,test_lib:parallel(), [pattern,pattern2,pattern3,pattern4,guard, bad_arith,bool_cases,bad_apply,files,effect, - bin_opt_info,bin_construction]}]. + bin_opt_info,bin_construction,comprehensions]}]. init_per_suite(Config) -> Config. @@ -536,6 +536,16 @@ bin_construction(Config) when is_list(Config) -> ok. +comprehensions(Config) when is_list(Config) -> + Ts = [{tautologic_guards, + <<" + f() -> [ true || true ]. + g() -> << <<1>> || true >>. + ">>, + [], []}], + run(Config, Ts), + ok. + %%% %%% End of test cases. %%% diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml index 4fcde495b3..8bf4a14240 100644 --- a/lib/diameter/doc/src/diameter_dict.xml +++ b/lib/diameter/doc/src/diameter_dict.xml @@ -609,7 +609,9 @@ UTF8String() = [integer()] <p> List elements are the UTF-8 encodings of the individual characters in the string. -Invalid codepoints will result in encode/decode failure.</p> +Invalid codepoints will result in encode/decode failure. +On encode, a UTF8String() can be specified as a binary, or as a nested +list of binaries and codepoints.</p> <marker id="DiameterIdentity"/> <pre> diff --git a/lib/diameter/src/base/diameter_types.erl b/lib/diameter/src/base/diameter_types.erl index 9ae289034c..8c07e84777 100644 --- a/lib/diameter/src/base/diameter_types.erl +++ b/lib/diameter/src/base/diameter_types.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -250,13 +250,10 @@ 'Address'(encode, zero) -> <<0:48>>; -'Address'(decode, <<1:16, B/binary>>) - when size(B) == 4 -> - list_to_tuple(binary_to_list(B)); - -'Address'(decode, <<2:16, B/binary>>) - when size(B) == 16 -> - list_to_tuple(v6dec(B, [])); +'Address'(decode, <<A:16, B/binary>>) + when 1 == A, 4 == size(B); + 2 == A, 16 == size(B) -> + list_to_tuple([N || <<N:A/unit:8>> <= B]); 'Address'(decode, <<A:16, _/binary>> = B) when 1 == A; @@ -264,30 +261,10 @@ ?INVALID_LENGTH(B); 'Address'(encode, T) -> - ipenc(diameter_lib:ipaddr(T)). - -ipenc(T) - when is_tuple(T), size(T) == 4 -> - B = list_to_binary(tuple_to_list(T)), - <<1:16, B/binary>>; - -ipenc(T) - when is_tuple(T), size(T) == 8 -> - B = v6enc(lists:reverse(tuple_to_list(T)), <<>>), - <<2:16, B/binary>>. - -v6dec(<<N:16, B/binary>>, Acc) -> - v6dec(B, [N | Acc]); - -v6dec(<<>>, Acc) -> - lists:reverse(Acc). - -v6enc([N | Rest], B) - when ?UINT(16,N) -> - v6enc(Rest, <<N:16, B/binary>>); - -v6enc([], B) -> - B. + Ns = tuple_to_list(diameter_lib:ipaddr(T)), %% length 4 or 8 + A = length(Ns) div 4, %% 1 or 2 + B = << <<N:A/unit:8>> || N <- Ns >>, + <<A:16, B/binary>>. %% -------------------- @@ -354,36 +331,13 @@ v6enc([], B) -> %% -------------------- 'UTF8String'(decode, Bin) -> - udec(Bin, []); + tl([0|_] = unicode:characters_to_list([0, Bin])); %% assert list return 'UTF8String'(encode = M, zero) -> 'UTF8String'(M, []); 'UTF8String'(encode, S) -> - uenc(S, []). - -udec(<<>>, Acc) -> - lists:reverse(Acc); - -udec(<<C/utf8, Rest/binary>>, Acc) -> - udec(Rest, [C | Acc]). - -uenc(E, Acc) - when E == []; - E == <<>> -> - list_to_binary(lists:reverse(Acc)); - -uenc(<<C/utf8, Rest/binary>>, Acc) -> - uenc(Rest, [<<C/utf8>> | Acc]); - -uenc([[] | Rest], Acc) -> - uenc(Rest, Acc); - -uenc([[H|T] | Rest], Acc) -> - uenc([H, T | Rest], Acc); - -uenc([C | Rest], Acc) -> - uenc(Rest, [<<C/utf8>> | Acc]). + <<_/binary>> = unicode:characters_to_binary(S). %% assert binary return %% -------------------- diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl index 24d4c7665e..295d23912b 100644 --- a/lib/diameter/test/diameter_codec_test.erl +++ b/lib/diameter/test/diameter_codec_test.erl @@ -1,3 +1,4 @@ +%% coding: utf-8 %% %% %CopyrightBegin% %% @@ -19,7 +20,9 @@ -module(diameter_codec_test). --compile(export_all). +-export([base/0, + gen/1, + lib/0]). %% %% Test encode/decode of dictionary-related modules. @@ -38,37 +41,34 @@ %% Interface. base() -> - [] = run([{?MODULE, [base, T]} || T <- [zero, decode]]). + [] = run([[fun base/1, T] || T <- [zero, decode]]). gen(Mod) -> Fs = [{Mod, F, []} || F <- [name, id, vendor_id, vendor_name]], - [] = run(Fs ++ [{?MODULE, [gen, Mod, T]} || T <- [messages, - command_codes, - avp_types, - grouped, - enum, - import_avps, - import_groups, - import_enums]]). + [] = run(Fs ++ [[fun gen/2, Mod, T] || T <- [messages, + command_codes, + avp_types, + grouped, + enum, + import_avps, + import_groups, + import_enums]]). lib() -> - Vs = {_,_} = values('Address'), - [] = run([[fun lib/2, N, Vs] || N <- [1,2]]). + Vs = {_,_,_} = values('Address'), + [] = run([[fun lib/2, N, Vs] || N <- [{1, true}, {3, false}]]). %% =========================================================================== %% Internal functions. -lib(N, {_,_} = T) -> - B = 1 == N rem 2, - [] = run([[fun lib/2, A, B] || A <- element(N,T)]); +lib({N,B}, {_,_,_} = T) -> + [] = run([[fun lib/2, A, B] || A <- element(N,T), is_tuple(A)]); lib(IP, B) -> - LA = tuple_to_list(IP), - {SA,Fun} = ip(LA), - [] = run([[fun lib/4, IP, B, Fun, A] || A <- [IP, SA]]). + [] = run([[fun lib/3, IP, B, A] || A <- [IP, ntoa(tuple_to_list(IP))]]). -lib(IP, B, Fun, A) -> - try Fun(A) of +lib(IP, B, A) -> + try diameter_lib:ipaddr(A) of IP when B -> ok catch @@ -76,12 +76,12 @@ lib(IP, B, Fun, A) -> ok end. -ip([_,_,_,_] = A) -> +ntoa([_,_,_,_] = A) -> [$.|S] = lists:append(["." ++ integer_to_list(N) || N <- A]), - {S, fun diameter_lib:ipaddr/1}; -ip([_,_,_,_,_,_,_,_] = A) -> + S; +ntoa([_,_,_,_,_,_,_,_] = A) -> [$:|S] = lists:flatten([":" ++ io_lib:format("~.16B", [N]) || N <- A]), - {S, fun diameter_lib:ipaddr/1}. + S. %% ------------------------------------------------------------------------ %% base/1 @@ -90,7 +90,7 @@ ip([_,_,_,_,_,_,_,_] = A) -> %% ------------------------------------------------------------------------ base(T) -> - [] = run([{?MODULE, [base, T, F]} || F <- types()]). + [] = run([[fun base/2, T, F] || F <- types()]). %% Ensure that 'zero' values encode only zeros. base(zero = T, F) -> @@ -100,32 +100,23 @@ base(zero = T, F) -> %% Ensure that we can decode what we encode and vice-versa, and that %% we can't decode invalid values. base(decode, F) -> - {Eq, Vs, Ns} = b(values(F)), - [] = run([{?MODULE, [base_decode, F, Eq, V]} || V <- Vs]), - [] = run([{?MODULE, [base_invalid, F, Eq, V]} || V <- Ns]). + {Ts, Fs, Is} = values(F), + [] = run([[fun base_decode/3, F, true, V] || V <- Ts]), + [] = run([[fun base_decode/3, F, false, V] || V <- Fs]), + [] = run([[fun base_invalid/2, F, V] || V <- Is]). base_decode(F, Eq, Value) -> d(fun(X,V) -> diameter_types:F(X,V) end, Eq, Value). -base_invalid(F, Eq, Value) -> +base_invalid(F, Value) -> try - base_decode(F, Eq, Value), + base_decode(F, false, Value), exit(nok) catch error: _ -> ok end. -b({_,_,_} = T) -> - T; -b({B,Vs}) - when is_atom(B) -> - {B,Vs,[]}; -b({Vs,Ns}) -> - {true, Vs, Ns}; -b(Vs) -> - {true, Vs, []}. - types() -> [F || {F,2} <- diameter_types:module_info(exports)]. @@ -136,7 +127,7 @@ types() -> %% ------------------------------------------------------------------------ gen(M, T) -> - [] = run(lists:map(fun(X) -> {?MODULE, [gen, M, T, X]} end, + [] = run(lists:map(fun(X) -> [fun gen/3, M, T, X] end, fetch(T, dict(M)))). fetch(T, Spec) -> @@ -197,18 +188,20 @@ gen(M, enum = T, {Name, ED}) gen(M, T, {?A(Name), lists:map(fun({E,D}) -> {?A(E), D} end, ED)}); gen(M, enum, {Name, ED}) -> - [] = run([{?MODULE, [enum, M, Name, T]} || T <- ED]); + [] = run([[fun enum/3, M, Name, T] || T <- ED]); gen(M, Tag, {_Mod, L}) -> T = retag(Tag), - [] = run([{?MODULE, [gen, M, T, I]} || I <- L]). + [] = run([[fun gen/3, M, T, I] || I <- L]). %% avp_decode/3 avp_decode(Mod, Type, Name) -> - {Eq, Vs, _} = b(values(Type, Name, Mod)), - [] = run([{?MODULE, [avp_decode, Mod, Name, Type, Eq, V]} - || V <- v(Vs)]). + {Ts, Fs, _} = values(Type, Name, Mod), + [] = run([[fun avp_decode/5, Mod, Name, Type, true, V] + || V <- v(Ts)]), + [] = run([[fun avp_decode/5, Mod, Name, Type, false, V] + || V <- v(Fs)]). avp_decode(Mod, Name, Type, Eq, Value) -> d(fun(X,V) -> avp(Mod, X, V, Name, Type) end, Eq, Value). @@ -250,7 +243,7 @@ v(N, Ord, E, Acc) -> arity(M, Name, Rname) -> Rec = M:'#new-'(Rname), - [] = run([{?MODULE, [arity, M, Name, F, Rec]} + [] = run([[fun arity/4, M, Name, F, Rec] || F <- M:'#info-'(Rname, fields)]). arity(M, Name, AvpName, Rec) -> @@ -299,68 +292,93 @@ z(B) -> %% tested.) values('OctetString' = T) -> - {["", atom_to_list(T)], [-1, 256]}; + {["", atom_to_list(T)], + [], + [-1, 256]}; values('Integer32') -> Mx = (1 bsl 31) - 1, Mn = -1*Mx, - {[Mn, 0, random(Mn,Mx), Mx], [Mn - 1, Mx + 1]}; + {[Mn, 0, random(Mn,Mx), Mx], + [], + [Mn - 1, Mx + 1]}; values('Integer64') -> Mx = (1 bsl 63) - 1, Mn = -1*Mx, - {[Mn, 0, random(Mn,Mx), Mx], [Mn - 1, Mx + 1]}; + {[Mn, 0, random(Mn,Mx), Mx], + [], + [Mn - 1, Mx + 1]}; values('Unsigned32') -> M = (1 bsl 32) - 1, - {[0, random(M), M], [-1, M + 1]}; + {[0, random(M), M], + [], + [-1, M + 1]}; values('Unsigned64') -> M = (1 bsl 64) - 1, - {[0, random(M), M], [-1, M + 1]}; + {[0, random(M), M], + [], + [-1, M + 1]}; values('Float32') -> E = (1 bsl 8) - 2, F = (1 bsl 23) - 1, <<Mx:32/float>> = <<0:1, E:8, F:23>>, <<Mn:32/float>> = <<1:1, E:8, F:23>>, - {[0.0, infinity, '-infinity', Mx, Mn], [0]}; + {[0.0, infinity, '-infinity', Mx, Mn], + [], + [0]}; values('Float64') -> E = (1 bsl 11) - 2, F = (1 bsl 52) - 1, <<Mx:64/float>> = <<0:1, E:11, F:52>>, <<Mn:64/float>> = <<1:1, E:11, F:52>>, - {[0.0, infinity, '-infinity', Mx, Mn], [0]}; + {[0.0, infinity, '-infinity', Mx, Mn], + [], + [0]}; values('Address') -> {[{255,0,random(16#FF),1}, {65535,0,0,random(16#FFFF),0,0,0,1}], - [{256,0,0,1}, {65536,0,0,0,0,0,0,1}]}; + ["127.0.0.1", "FFFF:FF::1.2.3.4"], + [{256,0,0,1}, {65536,0,0,0,0,0,0,1}, "256.0.0.1", "10000::1"]}; values('DiameterIdentity') -> - {["x", "diameter.com"], [""]}; + {["x", "diameter.com"], + [], + [""]}; values('DiameterURI') -> - {false, ["aaa" ++ S ++ "://diameter.se" ++ P ++ Tr ++ Pr - || S <- ["", "s"], - P <- ["", ":1234"], - Tr <- ["" | [";transport=" ++ X - || X <- ["tcp", "sctp", "udp"]]], - Pr <- ["" | [";protocol=" ++ X - || X <- ["diameter","radius","tacacs+"]]]]}; + {[], + ["aaa" ++ S ++ "://diameter.se" ++ P ++ Tr ++ Pr + || S <- ["", "s"], + P <- ["", ":1234"], + Tr <- ["" | [";transport=" ++ X + || X <- ["tcp", "sctp", "udp"]]], + Pr <- ["" | [";protocol=" ++ X + || X <- ["diameter","radius","tacacs+"]]]], + []}; values(T) when T == 'IPFilterRule'; T == 'QoSFilterRule' -> - ["deny in 0 from 127.0.0.1 to 10.0.0.1"]; + {["deny in 0 from 127.0.0.1 to 10.0.0.1"], + [], + []}; %% RFC 3629 defines the UTF-8 encoding of U+0000 through U+10FFFF with the %% exception of U+D800 through U+DFFF. values('UTF8String') -> + S = "ᚠᚢᚦᚨᚱᚲ", + B = unicode:characters_to_binary(S), {[[], + S, lists:seq(0,16#1FF), [0,16#D7FF,16#E000,16#10FFFF], [random(16#D7FF), random(16#E000,16#10FFFF)]], + [B, [B, S, hd(S)], [S, B]], [[-1], [16#D800], [16#DFFF], @@ -372,6 +390,7 @@ values('Time') -> {{2036,2,7},{6,28,15}}, {{2036,2,7},{6,28,16}}, %% 19000101T000000 + 2 bsl 31 {{2104,2,26},{9,42,23}}], + [], [{{1968,1,20},{3,14,7}}, {{2104,2,26},{9,42,24}}]}. %% 19000101T000000 + 3 bsl 31 @@ -382,18 +401,24 @@ values('Time') -> values('Enumerated', Name, Mod) -> {_Name, Vals} = lists:keyfind(?S(Name), 1, types(enum, Mod)), - lists:map(fun({_,N}) -> N end, Vals); + {lists:map(fun({_,N}) -> N end, Vals), + [], + []}; values('Grouped', Name, Mod) -> Rname = Mod:name2rec(Name), Rec = Mod:'#new-'(Rname), Avps = Mod:'#info-'(Rname, fields), - Enum = diameter_enum:combine(lists:map(fun({_,Vs,_}) -> to_enum(Vs) end, + Enum = diameter_enum:combine(lists:map(fun({Vs,_,_}) -> to_enum(Vs) end, [values(F, Mod) || F <- Avps])), - {false, diameter_enum:append(group(Mod, Name, Rec, Avps, Enum))}; + {[], + diameter_enum:append(group(Mod, Name, Rec, Avps, Enum)), + []}; values(_, 'Framed-IP-Address', _) -> - [{127,0,0,1}]; + {[{127,0,0,1}], + [], + []}; values(Type, _, _) -> values(Type). @@ -407,12 +432,14 @@ to_enum(E) -> %% values/2 values('AVP', _) -> - {true, [#diameter_avp{code = 0, data = <<0>>}], []}; + {[#diameter_avp{code = 0, data = <<0>>}], + [], + []}; values(Name, Mod) -> Avps = types(avp_types, Mod), {_Name, _Code, Type, _Flags} = lists:keyfind(?S(Name), 1, Avps), - b(values(?A(Type), Name, Mod)). + values(?A(Type), Name, Mod). %% group/5 %% diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl index 8ebdb6ba16..84242a09aa 100644 --- a/lib/eunit/include/eunit.hrl +++ b/lib/eunit/include/eunit.hrl @@ -102,7 +102,7 @@ %% X gets a new, local binding. %% (Note that lowercase 'let' is a reserved word.) -ifndef(LET). --define(LET(X,Y,Z), ((fun(X)->(Z)end)(Y))). +-define(LET(X,Y,Z), begin ((fun(X)->(Z)end)(Y)) end). -endif. %% It is important that testing code is short and readable. @@ -110,13 +110,13 @@ %% Compare: case f(X) of true->g(X); false->h(X) end %% and: ?IF(f(X), g(Y), h(Z)) -ifndef(IF). --define(IF(B,T,F), (case (B) of true->(T); false->(F) end)). +-define(IF(B,T,F), begin (case (B) of true->(T); false->(F) end) end). -endif. %% This macro yields 'true' if the value of E matches the guarded %% pattern G, otherwise 'false'. -ifndef(MATCHES). --define(MATCHES(G,E), (case (E) of G -> true; _ -> false end)). +-define(MATCHES(G,E), begin (case (E) of G -> true; _ -> false end) end). -endif. %% This macro can be used at any time to check whether or not the code @@ -140,6 +140,7 @@ %% for clauses that cannot match, even if the expression is a constant. -undef(assert). -define(assert(BoolExpr), + begin ((fun () -> case (BoolExpr) of true -> ok; @@ -152,7 +153,8 @@ _ -> {not_a_boolean,__V} end}]}) end - end)())). + end)()) + end). -endif. -define(assertNot(BoolExpr), ?assert(not (BoolExpr))). @@ -168,6 +170,7 @@ -define(assertMatch(Guard, Expr), ok). -else. -define(assertMatch(Guard, Expr), + begin ((fun () -> case (Expr) of Guard -> ok; @@ -178,7 +181,8 @@ {pattern, (??Guard)}, {value, __V}]}) end - end)())). + end)()) + end). -endif. -define(_assertMatch(Guard, Expr), ?_test(?assertMatch(Guard, Expr))). @@ -187,6 +191,7 @@ -define(assertNotMatch(Guard, Expr), ok). -else. -define(assertNotMatch(Guard, Expr), + begin ((fun () -> __V = (Expr), case __V of @@ -198,7 +203,8 @@ {value, __V}]}); _ -> ok end - end)())). + end)()) + end). -endif. -define(_assertNotMatch(Guard, Expr), ?_test(?assertNotMatch(Guard, Expr))). @@ -208,6 +214,7 @@ -define(assertEqual(Expect, Expr), ok). -else. -define(assertEqual(Expect, Expr), + begin ((fun (__X) -> case (Expr) of __X -> ok; @@ -218,7 +225,8 @@ {expected, __X}, {value, __V}]}) end - end)(Expect))). + end)(Expect)) + end). -endif. -define(_assertEqual(Expect, Expr), ?_test(?assertEqual(Expect, Expr))). @@ -227,6 +235,7 @@ -define(assertNotEqual(Unexpected, Expr), ok). -else. -define(assertNotEqual(Unexpected, Expr), + begin ((fun (__X) -> case (Expr) of __X -> erlang:error({assertNotEqual_failed, @@ -236,7 +245,8 @@ {value, __X}]}); _ -> ok end - end)(Unexpected))). + end)(Unexpected)) + end). -endif. -define(_assertNotEqual(Unexpected, Expr), ?_test(?assertNotEqual(Unexpected, Expr))). @@ -247,6 +257,7 @@ -define(assertException(Class, Term, Expr), ok). -else. -define(assertException(Class, Term, Expr), + begin ((fun () -> try (Expr) of __V -> erlang:error({assertException_failed, @@ -271,7 +282,8 @@ {__C, __T, erlang:get_stacktrace()}}]}) end - end)())). + end)()) + end). -endif. -define(assertError(Term, Expr), ?assertException(error, Term, Expr)). @@ -291,6 +303,7 @@ -define(assertNotException(Class, Term, Expr), ok). -else. -define(assertNotException(Class, Term, Expr), + begin ((fun () -> try (Expr) of _ -> ok @@ -316,7 +329,8 @@ _ -> ok end end - end)())). + end)()) + end). -endif. -define(_assertNotException(Class, Term, Expr), ?_test(?assertNotException(Class, Term, Expr))). @@ -327,6 +341,7 @@ %% these can be used for simply running commands in a controlled way -define(_cmd_(Cmd), (eunit_lib:command(Cmd))). -define(cmdStatus(N, Cmd), + begin ((fun () -> case ?_cmd_(Cmd) of {(N), __Out} -> __Out; @@ -337,7 +352,8 @@ {expected_status,(N)}, {status,__N}]}) end - end)())). + end)()) + end). -define(_cmdStatus(N, Cmd), ?_test(?cmdStatus(N, Cmd))). -define(cmd(Cmd), ?cmdStatus(0, Cmd)). -define(_cmd(Cmd), ?_test(?cmd(Cmd))). @@ -348,6 +364,7 @@ -define(assertCmdStatus(N, Cmd), ok). -else. -define(assertCmdStatus(N, Cmd), + begin ((fun () -> case ?_cmd_(Cmd) of {(N), _} -> ok; @@ -358,7 +375,8 @@ {expected_status,(N)}, {status,__N}]}) end - end)())). + end)()) + end). -endif. -define(assertCmd(Cmd), ?assertCmdStatus(0, Cmd)). @@ -366,6 +384,7 @@ -define(assertCmdOutput(T, Cmd), ok). -else. -define(assertCmdOutput(T, Cmd), + begin ((fun () -> case ?_cmd_(Cmd) of {_, (T)} -> ok; @@ -376,7 +395,8 @@ {expected_output,(T)}, {output,__T}]}) end - end)())). + end)()) + end). -endif. -define(_assertCmdStatus(N, Cmd), ?_test(?assertCmdStatus(N, Cmd))). @@ -394,26 +414,30 @@ -define(debugTime(S, E), (E)). -else. -define(debugMsg(S), - (begin - io:fwrite(user, <<"~s:~w:~w: ~s\n">>, - [?FILE, ?LINE, self(), S]), - ok - end)). + begin + io:fwrite(user, <<"~s:~w:~w: ~s\n">>, + [?FILE, ?LINE, self(), S]), + ok + end). -define(debugHere, (?debugMsg("<-"))). -define(debugFmt(S, As), (?debugMsg(io_lib:format((S), (As))))). -define(debugVal(E), + begin ((fun (__V) -> ?debugFmt(<<"~s = ~P">>, [(??E), __V, 15]), __V - end)(E))). + end)(E)) + end). -define(debugTime(S, E), + begin ((fun () -> {__T0, _} = statistics(wall_clock), __V = (E), {__T1, _} = statistics(wall_clock), ?debugFmt(<<"~s: ~.3f s">>, [(S), (__T1-__T0)/1000]), __V - end)())). + end)()) + end). -endif. diff --git a/lib/eunit/src/eunit_proc.erl b/lib/eunit/src/eunit_proc.erl index ec7d93fd48..7647f0133c 100644 --- a/lib/eunit/src/eunit_proc.erl +++ b/lib/eunit/src/eunit_proc.erl @@ -643,11 +643,11 @@ io_request({get_until, _Prompt, _M, _F, _As}, Buf) -> io_request({setopts, _Opts}, Buf) -> {ok, Buf}; io_request(getopts, Buf) -> - {error, {error, enotsup}, Buf}; + {{error, enotsup}, Buf}; io_request({get_geometry,columns}, Buf) -> - {error, {error, enotsup}, Buf}; + {{error, enotsup}, Buf}; io_request({get_geometry,rows}, Buf) -> - {error, {error, enotsup}, Buf}; + {{error, enotsup}, Buf}; io_request({requests, Reqs}, Buf) -> io_requests(Reqs, {ok, Buf}); io_request(_, Buf) -> diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 741f2abaef..d9a27e7d1e 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -300,11 +300,11 @@ filename() = string() process or to a file. When streaming to the calling process using the option <c>self</c> the following stream messages will be sent to that process: <c>{http, {RequestId, - stream_start, Headers}, {http, {RequestId, stream, - BinBodyPart}, {http, {RequestId, stream_end, Headers}</c>. When + stream_start, Headers}}, {http, {RequestId, stream, + BinBodyPart}}, {http, {RequestId, stream_end, Headers}}</c>. When streaming to to the calling processes using the option <c>{self, once}</c> the first message will have an additional - element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}</c>, + element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}}</c>, this is the process id that should be used as an argument to <c>http:stream_next/1</c> to trigger the next message to be sent to the calling process. </p> diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index 8438961511..3c132d34fa 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -406,7 +406,7 @@ bytes begins with url-path is mapped to local files that begins with directory-filename, for example: - <code>{alias, {"/image", "/ftp/pub/image"}</code> + <code>{alias, {"/image", "/ftp/pub/image"}}</code> and an access to http://your.server.org/image/foo.gif would refer to the file /ftp/pub/image/foo.gif. </p> @@ -421,7 +421,7 @@ bytes by re:replace/3 to produce a path in the local filesystem. For example: - <code>{re_write, {"^/[~]([^/]+)(.*)$", "/home/\\1/public\\2"}</code> + <code>{re_write, {"^/[~]([^/]+)(.*)$", "/home/\\1/public\\2"}}</code> and an access to http://your.server.org/~bob/foo.gif would refer to the file /home/bob/public/foo.gif. @@ -468,7 +468,7 @@ bytes scripts. URLs with a path beginning with url-path are mapped to scripts beginning with directory-filename, for example: - <code>{script_alias, {"/cgi-bin/", "/web/cgi-bin/"}</code> + <code>{script_alias, {"/cgi-bin/", "/web/cgi-bin/"}}</code> and an access to http://your.server.org/cgi-bin/foo would cause the server to run the script /web/cgi-bin/foo. </p> @@ -483,7 +483,7 @@ bytes scripts. URLs with a path beginning with url-path are mapped to scripts beginning with directory-filename, for example: - <code>{script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}</code> + <code>{script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}}</code> and an access to http://your.server.org/cgi-bin/17/foo would cause the server to run the script /web/17/cgi-bin/foo. </p> @@ -517,7 +517,7 @@ bytes the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. - <code>{action, {"text/plain", "/cgi-bin/log_and_deliver_text"}</code> + <code>{action, {"text/plain", "/cgi-bin/log_and_deliver_text"}}</code> </p> </item> @@ -532,7 +532,7 @@ bytes the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. - <code>{script, {"PUT", "/cgi-bin/put"}</code> + <code>{script, {"PUT", "/cgi-bin/put"}}</code> </p> </item> @@ -549,7 +549,7 @@ bytes scheme scripts. A matching URL is mapped into a specific module and function. For example: - <code>{erl_script_alias, {"/cgi-bin/example", [httpd_example]} + <code>{erl_script_alias, {"/cgi-bin/example", [httpd_example]}} </code> and a request to @@ -706,7 +706,7 @@ bytes For example: - <code>{allow_from, ["123.34.56.11", "150.100.23"] </code> + <code>{allow_from, ["123.34.56.11", "150.100.23"]}</code> The host 123.34.56.11 and all machines on the 150.100.23 subnet are allowed access. </p> @@ -719,7 +719,7 @@ bytes which should be denied access to a given directory. For example: - <code>{deny_from, ["123.34.56.11", "150.100.23"] </code> + <code>{deny_from, ["123.34.56.11", "150.100.23"]}</code> The host 123.34.56.11 and all machines on the 150.100.23 subnet are not allowed access. </p> @@ -835,7 +835,7 @@ bytes <p><em>Security properties - requires mod_security </em></p> <marker id="prop_sec_dir"></marker> - <p><em>{security_directory, {path(), [{property(), term()}]}</em></p> + <p><em>{security_directory, {path(), [{property(), term()}]}}</em></p> <marker id="props_sdir"></marker> <p>Here follows the valid properties for security directories</p> @@ -1067,7 +1067,7 @@ bytes <fsummary>Called for each request to the Web server.</fsummary> <type> <v>OldData = list()</v> - <v>NewData = [{response,{StatusCode,Body}}] | [{response,{response,Head,Body}}] | [{response,{already_sent,Statuscode,Size}] </v> + <v>NewData = [{response,{StatusCode,Body}}] | [{response,{response,Head,Body}}] | [{response,{already_sent,Statuscode,Size}}] </v> <v>StausCode = integer()</v> <v>Body = io_list() | nobody | {Fun, Arg}</v> <v>Head = [HeaderOption]</v> diff --git a/lib/inets/doc/src/httpd_util.xml b/lib/inets/doc/src/httpd_util.xml index 9f290084d2..9218ee91e2 100644 --- a/lib/inets/doc/src/httpd_util.xml +++ b/lib/inets/doc/src/httpd_util.xml @@ -337,10 +337,10 @@ <func> <name>rfc1123_date() -> RFC1123Date</name> - <name>rfc1123_date({{YYYY,MM,DD},{Hour,Min,Sec}}}) -> RFC1123Date</name> + <name>rfc1123_date({{YYYY,MM,DD},{Hour,Min,Sec}}) -> RFC1123Date</name> <fsummary>Return the current date in RFC 1123 format.</fsummary> <type> - <v>YYYY = MM = DD = Hour = Min =Sec = integer()</v> + <v>YYYY = MM = DD = Hour = Min = Sec = integer()</v> <v>RFC1123Date = string()</v> </type> <desc> diff --git a/lib/inets/doc/src/mod_alias.xml b/lib/inets/doc/src/mod_alias.xml index 265a1b8e76..b38be5db28 100644 --- a/lib/inets/doc/src/mod_alias.xml +++ b/lib/inets/doc/src/mod_alias.xml @@ -118,7 +118,7 @@ </func> <func> - <name>real_script_name(ConfigDB,RequestURI,ScriptAliases) -> Ret</name> + <name>real_script_name(ConfigDB, RequestURI, ScriptAliases) -> Ret</name> <fsummary>Expand a request uri using ScriptAlias config directives.</fsummary> <type> <v>ConfigDB = config_db()</v> @@ -129,7 +129,7 @@ </type> <desc> <marker id="real_script_name"></marker> - <p><c>real_name/3</c> traverses <c>ScriptAliases</c>, + <p><c>real_script_name/3</c> traverses <c>ScriptAliases</c>, typically extracted from <c>ConfigDB</c>, and matches each <c>FakeName</c> with <c>RequestURI</c>. If a match is found <c>FakeName</c> is replaced with <c>RealName</c> in the diff --git a/lib/inets/doc/src/notes_history.xml b/lib/inets/doc/src/notes_history.xml index bd59c1ba47..4162ab97bb 100644 --- a/lib/inets/doc/src/notes_history.xml +++ b/lib/inets/doc/src/notes_history.xml @@ -834,7 +834,7 @@ <list type="bulleted"> <item> <p>[ftp, client] - A new option {progress, {CBmodule, - CBFunction, InitProgressTerm} has been added to allow + CBFunction, InitProgressTerm}} has been added to allow users to create things such as progress bars in there GUI's. The option affects ftp:send/[3,4] and ftp:recv/[3,4].</p> diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl index 6b6532266b..a45b04f275 100644 --- a/lib/inets/src/http_server/httpd_response.erl +++ b/lib/inets/src/http_server/httpd_response.erl @@ -20,7 +20,7 @@ -module(httpd_response). -export([generate_and_send_response/1, send_status/3, send_header/3, send_body/3, send_chunk/3, send_final_chunk/2, split_header/2, - is_disable_chunked_send/1, cache_headers/1]). + is_disable_chunked_send/1, cache_headers/2]). -export([map_status_code/2]). -include("httpd.hrl"). @@ -266,8 +266,8 @@ get_connection(false,"HTTP/1.1") -> get_connection(_,_) -> "". -cache_headers(#mod{config_db = Db}) -> - case httpd_util:lookup(Db, script_nocache, false) of +cache_headers(#mod{config_db = Db}, NoCacheType) -> + case httpd_util:lookup(Db, NoCacheType, false) of true -> Date = httpd_util:rfc1123_date(), [{"cache-control", "no-cache"}, diff --git a/lib/inets/src/http_server/mod_cgi.erl b/lib/inets/src/http_server/mod_cgi.erl index c854166c29..f1b73810e6 100644 --- a/lib/inets/src/http_server/mod_cgi.erl +++ b/lib/inets/src/http_server/mod_cgi.erl @@ -295,7 +295,7 @@ receive_headers(Port, Module, Function, Args, Timeout) -> end. send_headers(ModData, {StatusCode, _}, HTTPHeaders) -> - ExtraHeaders = httpd_response:cache_headers(ModData), + ExtraHeaders = httpd_response:cache_headers(ModData, script_nocache), httpd_response:send_header(ModData, StatusCode, ExtraHeaders ++ HTTPHeaders). diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl index e36c33b282..b11df34f9e 100644 --- a/lib/inets/src/http_server/mod_esi.erl +++ b/lib/inets/src/http_server/mod_esi.erl @@ -440,7 +440,7 @@ receive_headers(Timeout) -> end. send_headers(ModData, StatusCode, HTTPHeaders) -> - ExtraHeaders = httpd_response:cache_headers(ModData), + ExtraHeaders = httpd_response:cache_headers(ModData, erl_script_nocache), httpd_response:send_header(ModData, StatusCode, ExtraHeaders ++ HTTPHeaders). diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl index ff835e1047..8315744eec 100644 --- a/lib/kernel/src/group.erl +++ b/lib/kernel/src/group.erl @@ -39,6 +39,7 @@ server(Drv, Shell, Options) -> proplists:get_value(expand_fun, Options, fun(B) -> edlin_expand:expand(B) end)), put(echo, proplists:get_value(echo, Options, true)), + put(max_length, unlimited), start_shell(Shell), server_loop(Drv, get(shell), []). @@ -351,12 +352,17 @@ check_valid_opts([{echo,_}|T]) -> check_valid_opts(T); check_valid_opts([{expand_fun,_}|T]) -> check_valid_opts(T); +check_valid_opts([{max_length, X} | T]) when is_integer(X) andalso X > 0 -> + check_valid_opts(T); +check_valid_opts([{max_length, unlimited} | T]) -> + check_valid_opts(T); check_valid_opts(_) -> false. do_setopts(Opts, Drv, Buf) -> put(expand_fun, proplists:get_value(expand_fun, Opts, get(expand_fun))), put(echo, proplists:get_value(echo, Opts, get(echo))), + put(max_length, proplists:get_value(max_length, Opts, get(max_length))), case proplists:get_value(encoding,Opts) of Valid when Valid =:= unicode; Valid =:= utf8 -> set_unicode_state(Drv,true); @@ -398,11 +404,17 @@ getopts(Drv,Buf) -> _ -> false end}, + Max = {max_length, case get(max_length) of + Int when is_integer(Int) -> + Int; + _ -> + unlimited + end}, Uni = {encoding, case get_unicode_state(Drv) of true -> unicode; _ -> latin1 end}, - {ok,[Exp,Echo,Bin,Uni],Buf}. + {ok,[Exp,Echo,Bin,Max,Uni],Buf}. %% get_chars(Prompt, Module, Function, XtraArgument, Drv, Buffer) @@ -439,6 +451,8 @@ get_chars_loop(Pbs, M, F, Xa, Drv, Buf0, State, Encoding) -> case Result of {done,Line,Buf1} -> get_chars_apply(Pbs, M, F, Xa, Drv, Buf1, State, Line, Encoding); + {overlong, Line, Rest} -> + {ok, Line, Rest}; interrupted -> {error,{error,interrupted},[]}; terminated -> @@ -471,13 +485,17 @@ err_func(_, F, _) -> get_line(Chars, Pbs, Drv, Encoding) -> {more_chars,Cont,Rs} = edlin:start(Pbs), send_drv_reqs(Drv, Rs), - get_line1(edlin:edit_line(Chars, Cont), Drv, new_stack(get(line_buffer)), + get_line1_limit(edlin:edit_line(Chars, Cont), Drv, new_stack(get(line_buffer)), Encoding). get_line1({done,Line,Rest,Rs}, Drv, Ls, _Encoding) -> send_drv_reqs(Drv, Rs), save_line_buffer(Line, get_lines(Ls)), {done,Line,Rest}; +get_line1({overlong, Line, Rest, Rs}, Drv, Ls, _Encoding) -> + send_drv_reqs(Drv, Rs), + save_line_buffer(Line, get_lines(Ls)), + {overlong, Line, Rest}; get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding) when ((Mode =:= none) and (Char =:= $\^P)) or ((Mode =:= meta_left_sq_bracket) and (Char =:= $A)) -> @@ -485,7 +503,7 @@ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding) case up_stack(save_line(Ls0, edlin:current_line(Cont))) of {none,_Ls} -> send_drv(Drv, beep), - get_line1(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding); + get_line1_limit(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding); {Lcs,Ls} -> send_drv_reqs(Drv, edlin:erase_line(Cont)), {more_chars,Ncont,Nrs} = edlin:start(edlin:prompt(Cont)), @@ -502,7 +520,7 @@ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding) case down_stack(save_line(Ls0, edlin:current_line(Cont))) of {none,_Ls} -> send_drv(Drv, beep), - get_line1(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding); + get_line1_limit(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding); {Lcs,Ls} -> send_drv_reqs(Drv, edlin:erase_line(Cont)), {more_chars,Ncont,Nrs} = edlin:start(edlin:prompt(Cont)), @@ -548,11 +566,11 @@ get_line1({expand, Before, Cs0, Cont,Rs}, Drv, Ls0, Encoding) -> send_drv(Drv, {put_chars, unicode, unicode:characters_to_binary(MatchStr,unicode)}), [$\^L | Cs1] end, - get_line1(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding); + get_line1_limit(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding); get_line1({undefined,_Char,Cs,Cont,Rs}, Drv, Ls, Encoding) -> send_drv_reqs(Drv, Rs), send_drv(Drv, beep), - get_line1(edlin:edit_line(Cs, Cont), Drv, Ls, Encoding); + get_line1_limit(edlin:edit_line(Cs, Cont), Drv, Ls, Encoding); %% The search item was found and accepted (new line entered on the exact %% result found) get_line1({_What,Cont={line,_Prompt,_Chars,search_found},Rs}, Drv, Ls0, Encoding) -> @@ -608,7 +626,7 @@ get_line1({What,Cont0,Rs}, Drv, Ls, Encoding) -> more_data(What, Cont0, Drv, Ls, Encoding) -> receive {Drv,{data,Cs}} -> - get_line1(edlin:edit_line(Cs, Cont0), Drv, Ls, Encoding); + get_line1_limit(edlin:edit_line(Cs, Cont0), Drv, Ls, Encoding); {Drv,eof} -> get_line1(edlin:edit_line(eof, Cont0), Drv, Ls, Encoding); {io_request,From,ReplyAs,Req} when is_pid(From) -> @@ -626,14 +644,35 @@ more_data(What, Cont0, Drv, Ls, Encoding) -> get_line1(edlin:edit_line([], Cont0), Drv, Ls, Encoding) end. +get_line1_limit({more_chars, Cont, Rs}, Drv, Ls, Encoding) -> + Total = edlin:length_before(Cont) + edlin:length_after(Cont), + case over_maxlength(Total) of + true -> + % we've hit the maximum line length. terminate the current + % line by faking EOF + send_drv_reqs(Drv, Rs), + {done, Line, Cs, Rs0} = edlin:edit_line(eof, Cont), + % we need to output a newline here... + Rs1 = Rs0 ++ [{put_chars,unicode,"\n"}], + % and we better split the line by the maximum amount + % of input we are expected to return + {Ret, Keep} = lists:split(get(max_length), Line), + get_line1({overlong, Ret, Keep ++ Cs, Rs1}, Drv, Ls, Encoding); + false -> + get_line1({more_chars, Cont, Rs}, Drv, Ls, Encoding) + end; +get_line1_limit(Line, Drv, Ls, Encoding) -> + get_line1(Line, Drv, Ls, Encoding). + + get_line_echo_off(Chars, Pbs, Drv) -> send_drv_reqs(Drv, [{put_chars, unicode,Pbs}]), - get_line_echo_off1(edit_line(Chars,[]), Drv). + get_line_echo_off1_limit(edit_line(Chars,[]), Drv). get_line_echo_off1({Chars,[]}, Drv) -> receive {Drv,{data,Cs}} -> - get_line_echo_off1(edit_line(Cs, Chars), Drv); + get_line_echo_off1_limit(edit_line(Cs, Chars), Drv); {Drv,eof} -> get_line_echo_off1(edit_line(eof, Chars), Drv); {io_request,From,ReplyAs,Req} when is_pid(From) -> @@ -647,6 +686,19 @@ get_line_echo_off1({Chars,[]}, Drv) -> get_line_echo_off1({Chars,Rest}, _Drv) -> {done,lists:reverse(Chars),case Rest of done -> []; _ -> Rest end}. +get_line_echo_off1_limit({Chars, []}, Drv) -> + case over_maxlength(length(Chars)) of + true -> + % we've hit the maximum line length... + {Line, Rest} = lists:split(get(max_length), Chars), + {overlong, Line, Rest}; + false -> + get_line_echo_off1({Chars, []}, Drv) + end; +get_line_echo_off1_limit(Line, Drv) -> + get_line_echo_off1(Line, Drv). + + %% We support line editing for the ICANON mode except the following %% line editing characters, which already has another meaning in %% echo-on mode (See Advanced Programming in the Unix Environment, 2nd ed, @@ -775,12 +827,12 @@ search_down_stack(Stack, Substr) -> %% This is get_line without line editing (except for backspace) and %% without echo. get_password_line(Chars, Drv) -> - get_password1(edit_password(Chars,[]),Drv). + get_password1_limit(edit_password(Chars,[]),Drv). get_password1({Chars,[]}, Drv) -> receive {Drv,{data,Cs}} -> - get_password1(edit_password(Cs,Chars),Drv); + get_password1_limit(edit_password(Cs,Chars),Drv); {io_request,From,ReplyAs,Req} when is_pid(From) -> %send_drv_reqs(Drv, [{delete_chars, -length(Pbs)}]), io_request(Req, From, ReplyAs, Drv, []), %WRONG!!! @@ -797,6 +849,20 @@ get_password1({Chars,Rest},Drv) -> send_drv_reqs(Drv,[{put_chars, unicode, "\n"}]), {done,lists:reverse(Chars),case Rest of done -> []; _ -> Rest end}. + +get_password1_limit({Chars, []}, Drv) -> + Line = case over_maxlength(length(Chars)) of + true -> + % we've hit the maximum line length... + lists:split(get(max_length), Chars); + false -> + {Chars, []} + end, + get_password1(Line, Drv); +get_password1_limit(Line, Drv) -> + get_password1(Line, Drv). + + edit_password([],Chars) -> {Chars,[]}; edit_password([$\r],Chars) -> @@ -832,3 +898,12 @@ append(L1, L2, _) when is_list(L1) -> L1++L2; append(_Eof, L, _) -> L. + + +over_maxlength(LineLength) -> + case get(max_length) of + Int when is_integer(Int) -> + Int < LineLength; + _Else -> + false + end. diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 3ea530a366..ef0e7e1177 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -120,6 +120,17 @@ 'addr' | 'broadaddr' | 'dstaddr' | 'mtu' | 'netmask' | 'flags' |'hwaddr'. +-type if_getopt_result() :: + {'addr', ip_address()} | + {'broadaddr', ip_address()} | + {'dstaddr', ip_address()} | + {'mtu', non_neg_integer()} | + {'netmask', ip_address()} | + {'flags', ['up' | 'down' | 'broadcast' | 'no_broadcast' | + 'pointtopoint' | 'no_pointtopoint' | + 'running' | 'multicast' | 'loopback']} | + {'hwaddr', ether_address()}. + -type address_family() :: 'inet' | 'inet6'. -type socket_protocol() :: 'tcp' | 'udp' | 'sctp'. -type socket_type() :: 'stream' | 'dgram' | 'seqpacket'. @@ -248,13 +259,13 @@ getiflist() -> -spec ifget(Socket :: socket(), Name :: string() | atom(), Opts :: [if_getopt()]) -> - {'ok', [if_setopt()]} | {'error', posix()}. + {'ok', [if_getopt_result()]} | {'error', posix()}. ifget(Socket, Name, Opts) -> prim_inet:ifget(Socket, Name, Opts). -spec ifget(Name :: string() | atom(), Opts :: [if_getopt()]) -> - {'ok', [if_setopt()]} | {'error', posix()}. + {'ok', [if_getopt_result()]} | {'error', posix()}. ifget(Name, Opts) -> withsocket(fun(S) -> prim_inet:ifget(S, Name, Opts) end). diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl index 78f7bfa325..b1f8886e7f 100644 --- a/lib/mnesia/src/mnesia_controller.erl +++ b/lib/mnesia/src/mnesia_controller.erl @@ -1676,9 +1676,13 @@ add_active_replica(Tab, Node, Cs = #cstruct{}) -> block_table(Tab) -> Var = {Tab, where_to_commit}, - Old = val(Var), - New = {blocked, Old}, - set(Var, New). % where_to_commit + case is_tab_blocked(val(Var)) of + {false, Old}-> + New = {blocked, Old}, + set(Var, New); % where_to_commit + {true, _Old}-> % already blocked + ignore + end. unblock_table(Tab) -> call({unblock_table, Tab}). diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c index 5730e20774..8de81a30ae 100644 --- a/lib/odbc/c_src/odbcserver.c +++ b/lib/odbc/c_src/odbcserver.c @@ -277,11 +277,15 @@ int main(void) msg = receive_erlang_port_msg(); temp = strtok(msg, ";"); + if (temp == NULL) + DO_EXIT(EXIT_STDIN_BODY); length = strlen(temp); supervisor_port = safe_malloc(length + 1); strcpy(supervisor_port, temp); temp = strtok(NULL, ";"); + if (temp == NULL) + DO_EXIT(EXIT_STDIN_BODY); length = strlen(temp); odbc_port = safe_malloc(length + 1); strcpy(odbc_port, temp); @@ -1819,12 +1823,20 @@ static byte * receive_erlang_port_msg(void) len |= lengthstr[i]; } + if (len <= 0 || len > 1024) { + DO_EXIT(EXIT_STDIN_HEADER); + } + buffer = (byte *)safe_malloc(len); if (read_exact(buffer, len) <= 0) { DO_EXIT(EXIT_STDIN_BODY); } + if (buffer[len-1] != '\0') { + DO_EXIT(EXIT_STDIN_BODY); + } + return buffer; } diff --git a/lib/reltool/bin/reltool b/lib/reltool/bin/reltool new file mode 100755 index 0000000000..589185b2ed --- /dev/null +++ b/lib/reltool/bin/reltool @@ -0,0 +1,289 @@ +#!/usr/bin/env escript +%% -*- erlang -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% 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. +%% +%% %CopyrightEnd% + +-include_lib("reltool/src/reltool.hrl"). + +main(Args) -> + process_flag(trap_exit, true), + try + Tokens = scan_args(Args, [], []), + {Options, Actions} = parse_args(Tokens, []), + case invoke(Options, Actions) of + ok -> + safe_stop(0); + {error, ReasonStr} -> + fatal_error(ReasonStr, 1) + end + catch + throw:usage -> + usage(), + safe_stop(1); + throw:help -> + usage(), + safe_stop(0); + throw:{error, ReasonStr2} -> + fatal_error(ReasonStr2, 1); + exit:Reason -> + ReasonStr2 = lists:flatten(io_lib:format("EXIT: ~p", [Reason])), + fatal_error(ReasonStr2, 2) + end. + +usage() -> + Usage = + [ + "[Config] --gui (start GUI)", + "[Config] --get_config [-defaults] [-derived] [File] (retrieve config info)", + "[Config] --get_rel RelName [File] (retrieve release info)", + "[Config] --get_script RelName [File] (retrieve boot script)", + "[Config] --create_target TargetDir (create target system)", + "[Config] --get_target_spec [File] (retrieve target spec)", + "--eval_target_spec Spec RootDir TargetDir (evaluate target spec)", + "--help (display this help)" + ], + Script = script_name(), + Signature = lists:flatten([[Script, " ", U, "\n"] || U <- Usage]), + io:format("Reltool is the release management tool of Erlang/OTP\n" + "\n" + "It analyses a given Erlang/OTP installation and determines\n" + "various dependencies between its applications. The graphical\n" + "frontend depicts the dependencies and enables interactive\n" + "customization of a target system. The backend provides a\n" + "batch interface for generation of customized target systems.\n" + "\n" + "~s" + "\n" + "Config = File | 'sys()'\n" + "File = filename()\n" + "RelName = rel_name()\n" + "TargetDir = target_dir()\n" + "Spec = File | 'target_spec()'\n" + "RootDir = root_dir()\n" + "\n" + "Exit status:\n" + " 0 - OK\n" + " 1 - error\n" + " 2 - internal error\n" + "\n" + "See the User's guide and Reference manual of reltool" + " for more info.\n", + [Signature]). + +safe_stop(Code) -> + init:stop(Code), + timer:sleep(infinity). + +invoke(Options, Actions) -> + case Actions of + [["--gui"]] -> + start_window(Options); + [["--get_config" | OptArgs]] -> + OptArgs2 = OptArgs -- ["-defaults"], + OptArgs3 = OptArgs2 -- ["-derived"], + InclDef = OptArgs2 =/= OptArgs, + InclDeriv = OptArgs3 =/= OptArgs2, + {ok, Config} = do_invoke(Options, reltool, get_config, + [InclDef, InclDeriv]), + String = pretty("config", Config), + case OptArgs3 of + [] -> + format("~s", [String]); + [ConfigFile] -> + write_file(ConfigFile, String); + _ -> + throw(usage) + end; + [["--get_rel", RelName | OptArgs]] -> + {ok, Rel} = do_invoke(Options, reltool, get_rel, [RelName]), + String = pretty("rel", Rel), + case OptArgs of + [] -> + format("~s", [String]); + [RelFile] -> + write_file(RelFile, String); + _ -> + throw(usage) + end; + [["--get_script", RelName | OptArgs]] -> + {ok, Script} = do_invoke(Options, reltool, get_script, [RelName]), + String = pretty("script", Script), + case OptArgs of + [] -> + format("~s", [String]); + [ScriptFile] -> + write_file(ScriptFile, String); + _ -> + throw(usage) + end; + [["--create_target", TargetDir]] -> + do_invoke(Options, reltool, create_target, [TargetDir]); + [["--get_target_spec" | OptArgs]] -> + {ok, Script} = do_invoke(Options, reltool, get_target_spec, []), + String = pretty("target_spec", Script), + case OptArgs of + [] -> + format("~s", [String]); + [SpecFile] -> + write_file(SpecFile, String); + _ -> + throw(usage) + end; + [["--eval_target_spec", TargetSpec0, RootDir, TargetDir]] -> + {spec, TargetSpec} = read_term(TargetSpec0, spec), + case reltool:eval_target_spec(TargetSpec, RootDir, TargetDir) of + ok -> + ok; + {error, ReasonStr} -> + throw({error, ReasonStr}) + end; + _ -> + throw(usage) + end. + +do_invoke(Options, M, F, A) -> + case reltool:start_server(Options) of + {ok, ServerPid} -> + case reltool:get_status(ServerPid) of + {ok, Warnings} -> + [io:format(standard_error, + "WARNING: ~s\n", [W]) || W <- Warnings], + Res = apply(M, F, [ServerPid | A]), + _StopRes = reltool:stop(ServerPid), + case Res of + {error, ReasonStr} -> + throw({error, ReasonStr}); + _ -> + Res + end; + {error, ReasonStr} -> + throw({error, ReasonStr}) + end; + {error, ReasonStr} -> + throw({error, ReasonStr}) + end. + +start_window(Options) -> + case reltool:start_link(Options) of + {ok, WinPid} -> + receive + {'EXIT', WinPid, shutdown} -> + ok; + {'EXIT', WinPid, normal} -> + ok; + {'EXIT', WinPid, Reason} -> + exit(Reason) + end; + {error, ReasonStr} -> + throw({error, ReasonStr}) + end. + +read_term("-"++_, _Tag) -> + throw(usage); +read_term(String, Tag) -> + case file:consult(String) of + {ok, [{Tag, _} = Term]} -> + Term; + {ok, Terms} -> + ReasonStr = lists:flatten(io_lib:format("Illegal ~p: ~p", + [Tag, Terms])), + throw({error, ReasonStr}); + {error, FileReason} -> + try + {ok, Tokens, _} = erl_scan:string(String ++ ". "), + {ok, {Tag, _} = Term} = erl_parse:parse_term(Tokens), + Term + catch + error:{badmatch, _} -> + Text = file:format_error(FileReason), + throw({error, String ++ ": " ++ Text}) + end + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Helpers +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +script_name() -> + filename:basename(escript:script_name(), ".escript"). + +fatal_error(String, Code) -> + io:format(standard_error, "~s: ~s\n", [script_name(), String]), + safe_stop(Code). + +write_file(File, IoList) -> + case file:write_file(File, IoList) of + ok -> + ok; + {error, Reason} -> + {error, file:format_error(Reason)} + end. + +format(Format, Args) -> + io:format(Format, Args), + %% Wait a while for the I/O to be processed + timer:sleep(timer:seconds(1)). + +pretty(Tag, Term) -> + lists:flatten(io_lib:format("%% ~s generated at ~w ~w\n~p.\n\n", + [Tag, date(), time(), Term])). + +scan_args([H | T], Single, Multi) -> + case H of + "--" ++ _ when Single =:= [] -> + scan_args(T, [H], Multi); + "--" ++ _ -> + scan_args(T, [H], [lists:reverse(Single) | Multi]); + _ -> + scan_args(T, [H | Single], Multi) + end; +scan_args([], [], Multi) -> + lists:reverse(Multi); +scan_args([], Single, Multi) -> + lists:reverse([lists:reverse(Single) | Multi]). + +parse_args([H | T] = Args, Options) -> + case H of + ["--help"] -> + throw(help); + ["--wx_debug" | Levels] -> + Dbg = + fun(L) -> + case catch list_to_integer(L) of + {'EXIT', _} -> + case catch list_to_atom(L) of + {'EXIT', _} -> + exit("Illegal wx debug level: " ++ L); + Atom -> + Atom + end; + Int -> + Int + end + end, + Levels2 = lists:map(Dbg, Levels), + parse_args(T, [{wx_debug, Levels2} | Options]); + ["--" ++ _ | _] -> + %% No more options + {lists:reverse(Options), Args}; + [Config0] -> + Sys = read_term(Config0, sys), + parse_args(T, [{config, Sys} | Options]) + end; +parse_args([], Options) -> + {lists:reverse(Options), []}. diff --git a/lib/reltool/bin/reltool.escript b/lib/reltool/bin/reltool.escript deleted file mode 100755 index 0dcd5ad1e9..0000000000 --- a/lib/reltool/bin/reltool.escript +++ /dev/null @@ -1,249 +0,0 @@ -#!/usr/bin/env escript -%% -*- erlang -*- -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2010. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% 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. -%% -%% %CopyrightEnd% - --include_lib("reltool/src/reltool.hrl"). - -main(Args) -> - process_flag(trap_exit, true), - try - Tokens = scan_args(Args, [], []), - {Options, Actions} = parse_args(Tokens, []), - case invoke(Options, Actions) of - ok -> - safe_stop(0); - {error, ReasonString} -> - fatal_error(ReasonString, 2) - end - catch - throw:usage -> - usage(), - safe_stop(1); - exit:Reason -> - String = lists:flatten(io_lib:format("EXIT: ~p", [Reason])), - fatal_error(String, 3) - end. - -usage() -> - Usage = - [ - "[Config] [--window]", - "[Config] --create_config [-defaults] [-derived] [ConfigFile]", - "[Config] --create_rel RelName [RelFile]", - "[Config] --create_script RelName [ScriptFile]", - "[Config] --create_target TargetDir", - "[Config] --create_target_spec [SpecFile]", - "[Config] --eval_target_spec Spec TargetDir RootDir" - ], - Script = script_name(), - String = lists:flatten([[Script, " ", U, "\n"] || U <- Usage]), - io:format("Erlang/OTP release management tool\n\n" - "~s\nConfig = ConfigFile | '{sys, [sys()]}'\n" - "Spec = SpecFile | '{spec, [target_spec()}']\n\n" - "See User's guide and Reference manual for more info.\n", - [String]). - -safe_stop(Code) -> - init:stop(Code), - timer:sleep(infinity). - -invoke(Options, Actions) -> - case Actions of - [] -> - invoke(Options, [["--window"]]); - [["--window"]] -> - start_window(Options); - [["--create_config" | OptArgs]] -> - DefArg = "-defaults", - DerivArg = "-derived", - InclDef = lists:member(DefArg, OptArgs), - InclDeriv = lists:member(DerivArg, OptArgs), - case reltool:get_config(Options, InclDef, InclDeriv) of - {ok, Config} -> - String = pretty("config", Config), - case OptArgs -- [DefArg, DerivArg] of - [] -> - format("~s", [String]); - [ConfigFile] -> - write_file(ConfigFile, String); - _ -> - throw(usage) - end; - {error, Reason} -> - {error, Reason} - end; - [["--create_rel", RelName | OptArgs]] -> - case reltool:get_rel(Options, RelName) of - {ok, Rel} -> - String = pretty("rel", Rel), - case OptArgs of - [] -> - format("~s", [String]); - [RelFile] -> - write_file(RelFile, String); - _ -> - throw(usage) - end; - {error, Reason} -> - {error, Reason} - end; - [["--create_script", RelName | OptArgs]] -> - case reltool:get_script(Options, RelName) of - {ok, Script} -> - String = pretty("script", Script), - case OptArgs of - [] -> - format("~s", [String]); - [ScriptFile] -> - write_file(ScriptFile, String); - _ -> - throw(usage) - end; - {error, Reason} -> - {error, Reason} - end; - [["--create_target", TargetDir]] -> - reltool:create_target(Options, TargetDir); - [["--create_target_spec" | OptArgs]] -> - case reltool:get_target_spec(Options) of - {ok, Script} -> - String = pretty("target_spec", Script), - case OptArgs of - [] -> - format("~s", [String]); - [SpecFile] -> - write_file(SpecFile, String); - _ -> - throw(usage) - end; - {error, Reason} -> - {error, Reason} - end; - [["--eval_target_spec", TargetSpec, TargetDir, RootDir]] -> - try - {ok, Tokens, _} = erl_scan:string(TargetSpec ++ ". "), - {ok, {spec, Spec}} = erl_parse:parse_term(Tokens), - reltool:eval_target_spec(Spec, TargetDir, RootDir) - catch - error:{badmatch, _} -> - case file:consult(TargetSpec) of - {ok, Spec2} -> - reltool:eval_target_spec(Spec2, TargetDir, RootDir); - {error, Reason} -> - Text = file:format_error(Reason), - {error, TargetSpec ++ ": " ++ Text} - end - end; - _ -> - throw(usage) - end. - -start_window(Options) -> - case reltool:start_link(Options) of - {ok, WinPid} -> - receive - {'EXIT', WinPid, shutdown} -> - ok; - {'EXIT', WinPid, normal} -> - ok; - {'EXIT', WinPid, Reason} -> - exit(Reason) - end; - {error, Reason} -> - {error, Reason} - end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Helpers -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -script_name() -> - filename:basename(escript:script_name(), ".escript"). - -fatal_error(String, Code) -> - io:format(standard_error, "~s: ~s\n", [script_name(), String]), - safe_stop(Code). - -write_file(File, IoList) -> - case file:write_file(File, IoList) of - ok -> - ok; - {error, Reason} -> - {error, file:format_error(Reason)} - end. - -format(Format, Args) -> - io:format(Format, Args), - %% Wait a while for the I/O to be processed - timer:sleep(timer:seconds(1)). - -pretty(Tag, Term) -> - lists:flatten(io_lib:format("%% ~s generated at ~w ~w\n~p.\n\n", - [Tag, date(), time(), Term])). - -scan_args([H | T], Single, Multi) -> - case H of - "--" ++ _ when Single =:= [] -> - scan_args(T, [H], Multi); - "--" ++ _ -> - scan_args(T, [H], [lists:reverse(Single) | Multi]); - _ -> - scan_args(T, [H | Single], Multi) - end; -scan_args([], [], Multi) -> - lists:reverse(Multi); -scan_args([], Single, Multi) -> - lists:reverse([lists:reverse(Single) | Multi]). - -parse_args([H | T] = Args, Options) -> - case H of - ["--wx_debug" | Levels] -> - Dbg = - fun(L) -> - case catch list_to_integer(L) of - {'EXIT', _} -> - case catch list_to_atom(L) of - {'EXIT', _} -> - exit("Illegal wx debug level: " ++ L); - Atom -> - Atom - end; - Int -> - Int - end - end, - Levels2 = lists:map(Dbg, Levels), - parse_args(T, [{wx_debug, Levels2} | Options]); - ["--" ++ _ | _] -> - %% No more options - {lists:reverse(Options), Args}; - [Config] -> - try - {ok, Tokens, _} = erl_scan:string(Config ++ ". "), - {ok, {sys, _} = Sys} = erl_parse:parse_term(Tokens), - parse_args(T, [{config, Sys} | Options]) - catch - error:{badmatch, _} -> - parse_args(T, [{config, Config} | Options]); - X:Y -> - io:format("\n\n~p\n\n", [{X, Y}]) - end - end; -parse_args([], Options) -> - {lists:reverse(Options), []}. diff --git a/lib/reltool/doc/man1/.gitignore b/lib/reltool/doc/man1/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/reltool/doc/man1/.gitignore diff --git a/lib/reltool/doc/src/Makefile b/lib/reltool/doc/src/Makefile index 2373b5e507..ab65303978 100644 --- a/lib/reltool/doc/src/Makefile +++ b/lib/reltool/doc/src/Makefile @@ -40,7 +40,8 @@ include files.mk # ---------------------------------------------------- XML_FILES = \ - $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \ + $(BOOK_FILES) $(XML_APPLICATION_FILES) \ + $(XML_REF1_FILES) $(XML_REF3_FILES) \ $(XML_PART_FILES) $(XML_CHAPTER_FILES) HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \ @@ -48,17 +49,17 @@ HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \ INFO_FILE = ../../info +MAN1_FILES = $(XML_REF1_FILES:%.xml=$(MAN1DIR)/%.1) MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3) HTML_REF_MAN_FILE = $(HTMLDIR)/index.html TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf - # ---------------------------------------------------- -# FLAGS +# FLAGS # ---------------------------------------------------- -XML_FLAGS += +XML_FLAGS += # ---------------------------------------------------- # Targets @@ -74,11 +75,12 @@ pdf: $(TOP_PDF_FILE) html:images $(HTML_REF_MAN_FILE) -man: $(MAN3_FILES) +man: $(MAN1_FILES) $(MAN3_FILES) + mv $(MAN1DIR)/reltool_escript.1 $(MAN1DIR)/reltool.1 images: $(IMAGE_FILES:%=$(HTMLDIR)/%) -debug opt: +debug opt: clean clean_docs: for file in $(XML_FILES); do \ @@ -87,13 +89,13 @@ clean clean_docs: fi \ done rm -rf $(HTMLDIR)/* - rm -f $(MAN3DIR)/* + rm -f $(MAN3DIR)/* $(MAN1DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ # ---------------------------------------------------- # Release Target -# ---------------------------------------------------- +# ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs @@ -103,7 +105,8 @@ release_docs_spec: docs $(INSTALL_DATA) $(HTMLDIR)/* "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN1DIR)/* "$(RELEASE_PATH)/man/man1" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1" $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: - diff --git a/lib/reltool/doc/src/files.mk b/lib/reltool/doc/src/files.mk index 07b52f4934..265b905456 100644 --- a/lib/reltool/doc/src/files.mk +++ b/lib/reltool/doc/src/files.mk @@ -23,6 +23,9 @@ XML_APPLICATION_FILES = \ XML_REF3_FILES = \ reltool.xml +XML_REF1_FILES = \ + reltool_escript.xml + XML_PART_FILES = \ part.xml @@ -35,4 +38,3 @@ XML_CHAPTER_FILES = \ BOOK_FILES = book.xml IMAGE_FILES = - diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml index 8437b7a623..a5b5c47d8b 100644 --- a/lib/reltool/doc/src/reltool.xml +++ b/lib/reltool/doc/src/reltool.xml @@ -39,7 +39,7 @@ <p><em>Reltool</em> is a release management tool. It analyses a given Erlang/OTP installation and determines various dependencies - between applications. The <c>graphical</c> frontend depicts the + between its applications. The <c>graphical</c> frontend depicts the dependencies and enables interactive customization of a target system. The backend provides a <c>batch</c> interface for generation of customized target systems.</p> @@ -697,7 +697,7 @@ target_spec() = [target_spec()] </func> <func> - <name>get_script(Server, Relname) -> {ok, ScriptFile | {error, Reason}</name> + <name>get_script(Server, Relname) -> {ok, ScriptFile} | {error, Reason}</name> <fsummary>Get contents of a boot script file</fsummary> <type> <v>Server = server()</v> diff --git a/lib/reltool/doc/src/reltool_escript.xml b/lib/reltool/doc/src/reltool_escript.xml new file mode 100644 index 0000000000..507f5ef125 --- /dev/null +++ b/lib/reltool/doc/src/reltool_escript.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE comref SYSTEM "comref.dtd"> + +<comref> + <header> + <copyright> + <year>2013</year> + <year>2013</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>erl</title> + <prepared></prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>reltool_escript.xml</file> + </header> + <com>reltool</com> + <comsummary>Erlang/OTP release management tool</comsummary> + <description> + <p><em>Reltool</em> is a release management tool. It analyses a + given Erlang/OTP installation and determines various dependencies + between its applications. The <c>graphical</c> frontend depicts the + dependencies and enables interactive customization of a target + system. The backend provides a <c>batch</c> interface for + generation of customized target systems.</p> + </description> + <funcs> + <func> + <name>reltool --gui></name> + <fsummary>Start GUI</fsummary> + <desc> + <p>Starts the graphical user interface. See <em>reltool:start/1</em>.</p> + </desc> + </func> + <func> + <name>reltool [Config] --get_config [-defaults] [-derived] [File]></name> + <fsummary>Retrieves actual config</fsummary> + <desc> + <p>See <em>reltool:get_config/3</em>.</p> + </desc> + </func> + <func> + <name>reltool [Config] --get_rel RelName [File]></name> + <fsummary>Retrieves actual release info</fsummary> + <desc> + <p>See <em>reltool:get_rel/2</em>.</p> + </desc> + </func> + <func> + <name>reltool [Config] --get_script RelName [File]></name> + <fsummary>Retrieves actual boot script</fsummary> + <desc> + <p>See <em>reltool:get_script/2</em>.</p> + </desc> + </func> + <func> + <name>reltool [Config] --create_target TargetDir></name> + <fsummary>Generate a target system</fsummary> + <desc> + <p>See <em>reltool:create_target/2</em>.</p> + </desc> + </func> + <func> + <name>reltool [Config] --get_target_spec [SpecFile]</name> + <fsummary>Retrieve spec of target system</fsummary> + <desc> + <p>See <em>reltool:get_target_spec/1</em>.</p> + </desc> + </func> + <func> + <name>reltool --eval_target_spec Spec RootDir TargetDir</name> + <fsummary>Generate target system from spec</fsummary> + <desc> + <p>See <em>reltool:eval_target_spec/3</em>.</p> + </desc> + </func> + </funcs> + + <section> + <title>SEE ALSO</title> + <p><seealso marker="reltool">reltool(3)</seealso></p> + </section> +</comref> diff --git a/lib/reltool/src/Makefile b/lib/reltool/src/Makefile index b8387fff96..b95b75f45b 100644 --- a/lib/reltool/src/Makefile +++ b/lib/reltool/src/Makefile @@ -52,6 +52,8 @@ APPUP_FILE = reltool.appup APPUP_SRC = $(APPUP_FILE).src APPUP_TARGET = $(EBIN)/$(APPUP_FILE) +ESCRIPT_FILE = reltool + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- @@ -103,6 +105,7 @@ release_spec: opt $(INSTALL_DIR) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELEASE_PATH)/bin" + $(INSTALL_PROGRAM) ../bin/$(ESCRIPT_FILE) "$(RELEASE_PATH)/bin/$(ESCRIPT_FILE)" release_docs_spec: - diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index f0d8b38519..f38a732b0f 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -287,8 +287,8 @@ "^lib", "^releases"]). -define(EMBEDDED_EXCL_SYS_FILTERS, - ["^bin/(erlc|dialyzer|typer)(|\\.exe)\$", - "^erts.*/bin/(erlc|dialyzer|typer)(|\\.exe)\$", + ["^bin/(erlc|dialyzer|reltool|typer)(|\\.exe)\$", + "^erts.*/bin/(erlc|dialyzer|reltool|typer)(|\\.exe)\$", "^erts.*/bin/.*(debug|pdb)"]). -define(EMBEDDED_INCL_APP_FILTERS, ["^ebin", "^include", @@ -301,7 +301,7 @@ "^erts.*/bin", "^lib\$"]). -define(STANDALONE_EXCL_SYS_FILTERS, - ["^erts.*/bin/(erlc|dialyzer|typer)(|\\.exe)\$", + ["^erts.*/bin/(erlc|dialyzer|reltool|typer)(|\\.exe)\$", "^erts.*/bin/(start|escript|to_erl|run_erl)(|\\.exe)\$", "^erts.*/bin/.*(debug|pdb)"]). -define(STANDALONE_INCL_APP_FILTERS, ["^ebin", diff --git a/lib/reltool/test/Makefile b/lib/reltool/test/Makefile index 52cdef44da..caf36ff991 100644 --- a/lib/reltool/test/Makefile +++ b/lib/reltool/test/Makefile @@ -28,6 +28,7 @@ MODULES= \ reltool_app_SUITE \ reltool_wx_SUITE \ reltool_server_SUITE \ + reltool_escript_SUITE \ reltool_manual_gui_SUITE \ reltool_test_lib diff --git a/lib/reltool/test/reltool_escript_SUITE.erl b/lib/reltool/test/reltool_escript_SUITE.erl new file mode 100644 index 0000000000..0e2685cd93 --- /dev/null +++ b/lib/reltool/test/reltool_escript_SUITE.erl @@ -0,0 +1,657 @@ +%% -*- coding: utf-8 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2013. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% 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. +%% +%% %CopyrightEnd% + +-module(reltool_escript_SUITE). + +-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, + init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2]). + +-compile(export_all). + +-include_lib("reltool/src/reltool.hrl"). +-include("reltool_test_lib.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("kernel/include/file.hrl"). + +-define(NODE_NAME, '__RELTOOL__TEMPORARY_TEST__NODE__'). +-define(WORK_DIR, "reltool_work_dir"). +-define(TMP_IN_FILE, "tmp_reltool_infile"). +-define(TMP_OUT_FILE, "tmp_reltool_outfile"). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Initialization functions. + +init_per_suite(Config) -> + {ok,Cwd} = file:get_cwd(), + ?ignore(file:make_dir(?WORK_DIR)), + [{cwd,Cwd}|reltool_test_lib:init_per_suite(Config)]. + +end_per_suite(Config) -> + reltool_test_lib:end_per_suite(Config). + +init_per_testcase(Func,Config) -> + Node = full_node_name(?NODE_NAME), + case net_adm:ping(Node) of + pong -> stop_node(Node); + pang -> ok + end, + reltool_test_lib:init_per_testcase(Func,Config). + +end_per_testcase(Func,Config) -> + ok = file:set_cwd(filename:join(?config(cwd,Config),?WORK_DIR)), + {ok,All} = file:list_dir("."), + Files = [F || F <- All, false =:= lists:prefix("save.",F)], + case ?config(tc_status,Config) of + ok -> + ok; + _Fail -> + SaveDir = "save."++atom_to_list(Func), + ok = rm_files([SaveDir]), + ok = file:make_dir(SaveDir), + save_test_result(Files,SaveDir) + end, + rm_files(Files), + ok = file:set_cwd(?config(cwd,Config)), + rm_files([?TMP_IN_FILE, ?TMP_OUT_FILE]), + reltool_test_lib:end_per_testcase(Func,Config). + +save_test_result(Files,DestDir) -> + Tar = "copy.tar", + ok = erl_tar:create(Tar, Files), + ok = erl_tar:extract(Tar, [{cwd,DestDir}]), + ok = file:delete(Tar), + ok. + +rm_files([F | Fs]) -> + case file:read_file_info(F) of + {ok,#file_info{type=directory}} -> + rm_dir(F); + {ok,_Regular} -> + ok = file:delete(F); + {error, enoent} -> + ok + end, + rm_files(Fs); +rm_files([]) -> + ok. + +rm_dir(Dir) -> + {ok,Files} = file:list_dir(Dir), + rm_files([filename:join(Dir, F) || F <- Files]), + ok = file:del_dir(Dir). + +oscmd(CmdStr) -> + io:format("os:cmd(~p)\n", [CmdStr]), + Output0 = os:cmd(CmdStr ++ "; echo \";$?\""), + Pred = fun(Char) -> Char =/= $; end, + {ExitStatus, [$; | RevOutput]} = + lists:splitwith(Pred, lists:reverse(Output0)), + Output = lists:reverse(RevOutput), + ExitStatus2 = string:strip(ExitStatus, left, $\n), + {list_to_integer(ExitStatus2), Output}. + +-define(w,'%.*%'). +-define(f(Term), lists:flatten(io_lib:format("~p", [Term]))). + +reltool_plain(Cmd) -> + file:delete(?TMP_OUT_FILE), + {ExitCode, Output} = oscmd(Cmd), + file:write_file(?TMP_OUT_FILE, Output), + case file:consult(?TMP_OUT_FILE) of + {ok, Term} -> + file:delete(?TMP_OUT_FILE), + {ExitCode, {ok, Term}}; + {error, _Reason} -> + file:delete(?TMP_OUT_FILE), + {ExitCode, {error, Output}} + end. + +reltool(Cmd) -> + Plain = reltool_plain(Cmd), + File = reltool_file(Cmd), + ?m(Plain, File). + +reltool_file(Cmd) -> + file:delete(?TMP_OUT_FILE), + {ExitCode, Output} = oscmd(Cmd ++ " " ++ ?TMP_OUT_FILE), + case file:consult(?TMP_OUT_FILE) of + {ok, Term} -> + file:delete(?TMP_OUT_FILE), + {ExitCode, {ok, Term}}; + {error, _Reason} -> + file:delete(?TMP_OUT_FILE), + {ExitCode, {error, _Reason, Output}} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% SUITE specification + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [start_gui, + get_config, + create_release, + create_script, + create_target, + eval_target_spec + ]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%% The test cases + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% A dummy break test case which is NOT in all(), but can be run +%% directly from the command line with ct_run. It just does a +%% test_server:break()... +break(_Config) -> + test_server:break(""), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Start the GUI + +start_gui(_Config) -> + ?m({1, {error, "reltool: noSUCHfile: no such file or directory\n"}}, + reltool_plain("reltool noSUCHfile --gui")), + %% ?m({0,""}, oscmd("reltool --gui")), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Check that get_config returns the expected derivates and defaults +%% as specified +get_config(_Config) -> + KernVsn = latest(kernel), + StdVsn = latest(stdlib), + SaslVsn = latest(sasl), + LibDir = code:lib_dir(), + StdLibDir = filename:join(LibDir,"stdlib-"++StdVsn), + KernLibDir = filename:join(LibDir,"kernel-"++KernVsn), + SaslLibDir = filename:join(LibDir,"sasl-"++SaslVsn), + + ?m({0, {ok, [{sys,[]}]}}, + reltool("reltool --get_config")), + + Sys = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}]}, + {app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir}]}]}, + ?m(ok, file:write_file(?TMP_IN_FILE, ?f(Sys) ++ ".")), + + ?m({0, {ok, [Sys]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ " --get_config")), + + %% Include derived info + ?msym({0, + {ok, [{sys,[{incl_cond, exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir}, + {mod,_,[]}|_]}]}]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ " --get_config -derived")), + + %% Include defaults + ?msym({0, + {ok, [{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {app,kernel,[{incl_cond,include},{vsn,undefined}, + {lib_dir,undefined}]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}, + {lib_dir,undefined}]}, + {app,stdlib,[{incl_cond,include},{vsn,undefined}, + {lib_dir,StdLibDir}]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ " --get_config -defaults")), + + %% Include both defaults and derived info + ?msym({0, + {ok, [{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{vsn,KernVsn}, + {lib_dir,KernLibDir},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}, + {lib_dir,SaslLibDir},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{vsn,StdVsn}, + {lib_dir,StdLibDir},{mod,_,[]}|_]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ + " --get_config -derived -defaults")), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate releases + +create_release(_Config) -> + %% Configure the server + RelName = "Testing", + RelVsn = "1.0", + Sys = + {sys, + [ + {lib_dirs, []}, + {boot_rel, RelName}, + {rel, RelName, RelVsn, [kernel, stdlib]} + ]}, + ?m(ok, file:write_file(?TMP_IN_FILE, ?f(Sys) ++ ".")), + + %% Generate release + ErtsVsn = erlang:system_info(version), + Apps = application:loaded_applications(), + {value, {_, _, KernelVsn}} = lists:keysearch(kernel, 1, Apps), + {value, {_, _, StdlibVsn}} = lists:keysearch(stdlib, 1, Apps), + Rel = + {release, {RelName, RelVsn}, + {erts, ErtsVsn}, + [{kernel, KernelVsn}, {stdlib, StdlibVsn}]}, + ?m({0, {ok,[Rel]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ + " --get_rel " ++ RelName)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate boot scripts + +create_script(_Config) -> + %% Configure the server + RelName = "TestingMore", + RelVsn = "1.0", + Sys = + {sys, + [ + {lib_dirs, []}, + {boot_rel, RelName}, + {rel, RelName, RelVsn, [stdlib, kernel]} + ]}, + ?m(ok, file:write_file(?TMP_IN_FILE, ?f(Sys) ++ ".")), + + %% Generate release file + ErtsVsn = erlang:system_info(version), + Apps = application:loaded_applications(), + {value, {_, _, KernelVsn}} = lists:keysearch(kernel, 1, Apps), + {value, {_, _, StdlibVsn}} = lists:keysearch(stdlib, 1, Apps), + Rel = {release, + {RelName, RelVsn}, + {erts, ErtsVsn}, + [{kernel, KernelVsn}, {stdlib, StdlibVsn}]}, + ?m({0,{ok, [Rel]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ + " --get_rel " ++ RelName)), + + ?m(ok, file:write_file(filename:join([?WORK_DIR, RelName ++ ".rel"]), + io_lib:format("~p.\n", [Rel]))), + + %% Generate script file + {ok, Cwd} = file:get_cwd(), + ?m(ok, file:set_cwd(?WORK_DIR)), + ?m(ok, systools:make_script(RelName, [])), + {ok, [OrigScript]} = ?msym({ok, [_]}, file:consult(RelName ++ ".script")), + ?m(ok, file:set_cwd(Cwd)), + + {_, {ok, [Script]}} = + ?msym({0, {ok, [_]}}, + reltool("reltool " ++ ?TMP_IN_FILE ++ + " --get_script " ++ RelName)), + + %% OrigScript2 = sort_script(OrigScript), + %% Script2 = sort_script(Script), + %% ?m(OrigScript2, Script2), + + ?m(equal, diff_script(OrigScript, Script)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate target system + +create_target(_Config) -> + %% Configure the server + RelName1 = "Testing1", + RelName2 = "Testing2", + RelVsn = "1.0", + Sys = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, []}, + {boot_rel, RelName2}, + {rel, RelName1, RelVsn, [stdlib, kernel]}, + {rel, RelName2, RelVsn, [sasl, stdlib, kernel]}, + {app, sasl, [{incl_cond, include}]} + ]}, + ?m(ok, file:write_file(?TMP_IN_FILE, ?f(Sys) ++ ".")), + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_development"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + + ?log("SPEC: ~p\n", + [?msym({0, {ok,[_]}}, + reltool_plain("reltool " ++ ?TMP_IN_FILE ++ + " --get_target_spec"))]), + + ?msym({0, {ok,[_]}}, + reltool_file("reltool " ++ ?TMP_IN_FILE ++ " --get_target_spec")), + + ?msym({0, {ok,[]}}, + reltool_plain("reltool " ++ ?TMP_IN_FILE ++ + " --create_target " ++ TargetDir)), + + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + ?msym(ok, stop_node(Node)), + + %% Verify that reltool is included + Reltool = filename:join([BinDir, "reltool"]), + ?m(Reltool, os:find_executable("reltool", BinDir)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate target system with eval_target_spec/3 + +eval_target_spec(_Config) -> + %% Configure the server + RelName1 = "Testing3", + RelName2 = "Testing4", + RelVsn = "1.0", + Sys = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, []}, + {boot_rel, RelName2}, + {rel, RelName1, RelVsn, [stdlib, kernel]}, + {rel, RelName2, RelVsn, [sasl, stdlib, kernel]}, + {app, sasl, [{incl_cond, include}]} + ]}, + ?m(ok, file:write_file(?TMP_IN_FILE, ?f(Sys) ++ ".")), + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "eval_target_spec"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + + {_, {ok, Spec}} = + ?msym({0, {ok,_}}, + reltool_plain("reltool " ++ ?TMP_IN_FILE ++ + " --get_target_spec")), + ?msym({0, {ok,_}}, + reltool_file("reltool " ++ ?TMP_IN_FILE ++ " --get_target_spec")), + + ?m(ok, file:write_file(?TMP_IN_FILE, ?f({spec, Spec}) ++ ".")), + + ?msym({0, {ok,""}}, + reltool_plain("reltool --eval_target_spec " ++ " " ++ + ?TMP_IN_FILE ++ " " ++ + code:root_dir() ++ " " ++ + TargetDir)), + + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + ?msym(ok, stop_node(Node)), + + %% Verify that reltool is included + Reltool = filename:join([BinDir, "reltool"]), + ?m(Reltool, os:find_executable("reltool", BinDir)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Library functions + +erl_libs() -> + case os:getenv("ERL_LIBS") of + false -> []; + LibStr -> string:tokens(LibStr, ":;") + end. + +datadir(Config) -> + %% Removes the trailing slash... + filename:nativename(?config(data_dir,Config)). + +latest(App) -> + AppStr = atom_to_list(App), + AppDirs = filelib:wildcard(filename:join(code:lib_dir(),AppStr++"-*")), + [LatestAppDir|_] = lists:reverse(AppDirs), + [_,Vsn] = string:tokens(filename:basename(LatestAppDir),"-"), + Vsn. + +rm_missing_app(Apps) -> + lists:keydelete(?MISSING_APP_NAME,#app.name,Apps). + +diff_script(Script, Script) -> + equal; +diff_script({script, Rel, Commands1}, {script, Rel, Commands2}) -> + diff_cmds(Commands1, Commands2); +diff_script({script, Rel1, _}, {script, Rel2, _}) -> + {error, {Rel1, Rel2}}. + +diff_cmds([Cmd | Commands1], [Cmd | Commands2]) -> + diff_cmds(Commands1, Commands2); +diff_cmds([Cmd1 | _Commands1], [Cmd2 | _Commands2]) -> + {diff, {expected, Cmd1}, {actual, Cmd2}}; +diff_cmds([], []) -> + equal. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Node handling + +start_node(Name, ErlPath) -> + start_node(Name, ErlPath, []). +start_node(Name, ErlPath, Args0) -> + FullName = full_node_name(Name), + Args = mk_node_args(Name, Args0), + io:format("Starting node ~p: ~ts~n", + [FullName, lists:flatten([[X," "] || X <- [ErlPath|Args]])]), + %% io:format("open_port({spawn_executable, ~p}, + %% [{args,~p}])~n",[ErlPath,Args]), + case open_port({spawn_executable, ErlPath}, [{args,Args}]) of + Port when is_port(Port) -> + %% no need to close port since node is detached (see + %% mk_node_args) so port will be closed anyway. + case ping_node(FullName, 50) of + ok -> {ok, FullName}; + Other -> exit({failed_to_start_node, FullName, Other}) + end; + Error -> + exit({failed_to_start_node, FullName, Error}) + end. + +stop_node(Node) -> + rpc:call(Node,erlang,halt,[]), + wait_for_node_down(Node,50). + +wait_for_node_down(Node,0) -> + test_server:fail({cant_terminate_node,Node}); +wait_for_node_down(Node,N) -> + case net_adm:ping(Node) of + pong -> + timer:sleep(1000), + wait_for_node_down(Node,N-1); + pang -> + ok + end. + +mk_node_args(Name, Args) -> + Pa = filename:dirname(code:which(?MODULE)), + NameSw = case net_kernel:longnames() of + false -> "-sname"; + true -> "-name"; + _ -> exit(not_distributed_node) + end, + {ok, Pwd} = file:get_cwd(), + NameStr = atom_to_list(Name), + ["-detached", + NameSw, NameStr, + "-pa", Pa, + "-env", "ERL_CRASH_DUMP", Pwd ++ "/erl_crash_dump." ++ NameStr, + "-setcookie", atom_to_list(erlang:get_cookie()) + | Args]. + +full_node_name(PreName) -> + HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end, + atom_to_list(node())), + list_to_atom(atom_to_list(PreName) ++ HostSuffix). + +ping_node(_Node, 0) -> + {error, net_adm}; +ping_node(Node, N) when is_integer(N), N > 0 -> + case catch net_adm:ping(Node) of + pong -> + wait_for_process(Node, code_server, 50); + _ -> + timer:sleep(1000), + ping_node(Node, N-1) + end. + +wait_for_process(_Node, Name, 0) -> + {error, Name}; +wait_for_process(Node, Name, N) when is_integer(N), N > 0 -> + case rpc:call(Node, erlang, whereis, [Name]) of + undefined -> + timer:sleep(1000), + wait_for_process(Node, Name, N-1); + {badrpc, _} = Reason -> + erlang:error({Reason, Node}); + Pid when is_pid(Pid) -> + ok + end. + +wait_for_app(_Node, Name, 0) -> + {error, Name}; +wait_for_app(Node, Name, N) when is_integer(N), N > 0 -> + case rpc:call(Node,application,which_applications,[]) of + {badrpc,Reason} -> + test_server:fail({failed_to_get_applications,Reason}); + Apps -> + case lists:member(Name,Apps) of + false -> + timer:sleep(1000), + wait_for_app(Node, Name, N-1); + true -> + ok + end + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Run escript + +run(Dir, Script, Args) -> + Cmd0 = filename:rootname(Script) ++ " " ++ Args, + Cmd = case os:type() of + {win32,_} -> filename:nativename(Dir) ++ "\\" ++ Cmd0; + _ -> Cmd0 + end, + do_run(Dir, Cmd). + +run(Dir, Opts, Script, Args) -> + Cmd0 = filename:rootname(Script) ++ " " ++ Args, + Cmd = case os:type() of + {win32,_} -> Opts ++ " " ++ filename:nativename(Dir) ++ "\\" ++ Cmd0; + _ -> Opts ++ " " ++ Dir ++ "/" ++ Cmd0 + end, + do_run(Dir, Cmd). + +do_run(Dir, Cmd) -> + io:format("Run: ~p\n", [Cmd]), + Env = [{"PATH",Dir++":"++os:getenv("PATH")}, + {"ERL_FLAGS",""}, % Make sure no flags are set that can override + {"ERL_ZFLAGS",""}], % any of the flags set in the escript. + Port = open_port({spawn,Cmd}, [exit_status,eof,in,{env,Env}]), + Res = get_data(Port, []), + receive + {Port,{exit_status,ExitCode}} -> + s2b([Res,"ExitCode:"++integer_to_list(ExitCode)]) + end. + +get_data(Port, SoFar) -> + receive + {Port,{data,Bytes}} -> + get_data(Port, [SoFar|Bytes]); + {Port,eof} -> + erlang:port_close(Port), + SoFar + end. + +expected_output([data_dir|T], Data) -> + Slash = case os:type() of + {win32,_} -> "\\"; + _ -> "/" + end, + [filename:nativename(Data)++Slash|expected_output(T, Data)]; +expected_output([H|T], Data) -> + [H|expected_output(T, Data)]; +expected_output([], _) -> + []; +expected_output(Bin, _) when is_binary(Bin) -> + Bin. + +%% Convert the given list to a binary with the same encoding as the +%% file name translation mode +s2b(List) -> + Enc = file:native_name_encoding(), + unicode:characters_to_binary(List,Enc,Enc). diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 752037042d..5df2bdc13f 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -60,6 +60,7 @@ end_per_testcase(Func,Config) -> ok; _Fail -> SaveDir = "save."++atom_to_list(Func), + ok = rm_files([SaveDir]), ok = file:make_dir(SaveDir), save_test_result(Files,SaveDir) end, diff --git a/lib/reltool/vsn.mk b/lib/reltool/vsn.mk index 412e78f49f..163b77dfa0 100644 --- a/lib/reltool/vsn.mk +++ b/lib/reltool/vsn.mk @@ -1 +1 @@ -RELTOOL_VSN = 0.6.4 +RELTOOL_VSN = 0.6.5 diff --git a/lib/sasl/doc/src/rb.xml b/lib/sasl/doc/src/rb.xml index 3da825878e..b94914d8f9 100644 --- a/lib/sasl/doc/src/rb.xml +++ b/lib/sasl/doc/src/rb.xml @@ -138,6 +138,24 @@ </desc> </func> <func> + <name>log_list()</name> + <name>log_list(Type)</name> + <fsummary>Log reports list</fsummary> + <type> + <v>Type = type()</v> + <v>type() = error | error_report | info_msg | info_report | + warning_msg | warning_report | crash_report | + supervisor_report | progress</v> + </type> + <desc> + <p>Same as <c>list/0</c> or <c>list/1</c> functions + but result is printed to logfile, if set, otherwise to standard_io. + </p> + <p>If no <c>Type</c> is given, all reports are listed. + </p> + </desc> + </func> + <func> <name>rescan()</name> <name>rescan(Options)</name> <fsummary>Rescan the report directory</fsummary> @@ -172,7 +190,7 @@ <type> <v>Options = [opt()]</v> <v>opt() = {start_log, FileName} | {max, MaxNoOfReports} | {report_dir, DirString} | {type, ReportType} | {abort_on_error, Bool}</v> - <v>FileName = string() | standard_io</v> + <v>FileName = string() | atom() | pid()</v> <v>MaxNoOfReports = int() | all</v> <v>DirString = string()</v> <v>ReportType = type() | [type()] | all</v> @@ -185,11 +203,13 @@ reports can be browsed. When the <c>rb_server</c> is started, the files in the specified directory are scanned. The other functions assume that the server has - started. + started. </p> - <p><c>{start_log, FileName}</c> starts logging to file. All - reports will be printed to the named file. The default is - <c>standard_io</c>. + <p><c>{start_log, FileName}</c> starts logging to file, + registered name or io_device. All reports will be printed + to the named file. The default is <c>standard_io</c>. + The option {start_log, standard_error} is not allowed and + will be replaced by default standard_io. </p> <p><c>{max, MaxNoOfReports}</c>. Controls how many reports the <c>rb_server</c> should read on start-up. This option is @@ -226,11 +246,11 @@ <name>start_log(FileName)</name> <fsummary>Redirect all output to <c>FileName</c></fsummary> <type> - <v>FileName = string()</v> + <v>FileName = string() | atom() | pid()</v> </type> <desc> <p>Redirects all report output from the RB tool to the - specified file. + specified file, registered name or io_device. </p> </desc> </func> diff --git a/lib/sasl/src/rb.erl b/lib/sasl/src/rb.erl index 8004ef2c5a..767932e659 100644 --- a/lib/sasl/src/rb.erl +++ b/lib/sasl/src/rb.erl @@ -22,7 +22,7 @@ %% External exports -export([start/0, start/1, stop/0, rescan/0, rescan/1]). --export([list/0, list/1, show/0, show/1, grep/1, filter/1, filter/2, start_log/1, stop_log/0]). +-export([list/0, list/1, log_list/0, log_list/1, show/0, show/1, grep/1, filter/1, filter/2, start_log/1, stop_log/0]). -export([h/0, help/0]). %% Internal exports @@ -62,6 +62,9 @@ rescan(Options) -> list() -> list(all). list(Type) -> call({list, Type}). +log_list() -> log_list(all). +log_list(Type) -> call({log_list, Type}). + show() -> call(show). @@ -93,6 +96,8 @@ help() -> io:format("rb:help() - print this help~n"), io:format("rb:list() - list all reports~n"), io:format("rb:list(Type) - list all reports of type Type~n"), + io:format("rb:log_list() - log list of all reports~n"), + io:format("rb:log_list(Type) - log list of all reports of type Type~n"), io:format(" currently supported types are:~n"), print_types(), io:format("rb:grep(RegExp) - print reports containing RegExp.~n"), @@ -113,7 +118,7 @@ help() -> io:format("rb:show(Number) - print report no Number~n"), io:format("rb:show(Type) - print all reports of type Type~n"), io:format("rb:show() - print all reports~n"), - io:format("rb:start_log(File) - redirect all reports to file~n"), + io:format("rb:start_log(File) - redirect all reports to file or io_device~n"), io:format("rb:stop_log() - close the log file and redirect to~n"), io:format(" standard_io~n"), io:format("rb:stop - stop the rb_server~n"). @@ -207,7 +212,10 @@ handle_call({rescan, Options}, _From, State) -> handle_call(_, _From, #state{data = undefined}) -> {reply, {error, no_data}, #state{}}; handle_call({list, Type}, _From, State) -> - print_list(State#state.data, Type), + print_list(standard_io, State#state.data, Type), + {reply, ok, State}; +handle_call({log_list, Type}, _From, State) -> + print_list(State#state.device, State#state.data, Type), {reply, ok, State}; handle_call({start_log, FileName}, _From, State) -> NewDevice = open_log_file(FileName), @@ -262,7 +270,16 @@ code_change(_OldVsn, State, _Extra) -> %% Returns: A Device for later use in call to io:format %%----------------------------------------------------------------- open_log_file(standard_io) -> standard_io; -open_log_file(FileName) -> +open_log_file(Fd) when is_atom(Fd),Fd=/=standard_error -> + case whereis(Fd) of + undefined -> io:format("rb: Registered name not found '~s'.~n", + [Fd]), + io:format("rb: Using standard_io~n"), + open_log_file(standard_io); + Pid -> open_log_file(Pid) + end; +open_log_file(Fd) when is_pid(Fd)-> Fd; +open_log_file(FileName) when is_list(FileName) -> case file:open(FileName, [write,append]) of {ok, Fd} -> Fd; Error -> @@ -270,7 +287,10 @@ open_log_file(FileName) -> [FileName, Error]), io:format("rb: Using standard_io~n"), standard_io - end. + end; +open_log_file(standard_error) -> + io:format("rb: Using standard_io~n"), + standard_io. close_device(Fd) when is_pid(Fd) -> catch file:close(Fd); @@ -550,18 +570,18 @@ local_time_to_universal_time({Date,Time}) -> end. -print_list(Data, Type) -> +print_list(Fd, Data, Type) -> Header = {"No", "Type", "Process", "Date Time"}, Width = find_width([Header | Data], 0)+1, DateWidth = find_date_width([Header | Data], 0) +1, Format = lists:concat(["~4s~20s ~", Width, "s~20s~n"]), - io:format(Format, tuple_to_list(Header)), - io:format(Format, ["==", "====", "=======", "==== ===="]), - print_list(Data, Type, Width, DateWidth). -print_list([], _, _, _) -> true; -print_list([H|T], Type, Width, DateWidth) -> - print_one_report(H, Type, Width, DateWidth), - print_list(T, Type, Width, DateWidth). + io:format(Fd, Format, tuple_to_list(Header)), + io:format(Fd, Format, ["==", "====", "=======", "==== ===="]), + print_list(Fd, Data, Type, Width, DateWidth). +print_list(_, [], _, _, _) -> true; +print_list(Fd, [H|T], Type, Width, DateWidth) -> + print_one_report(Fd, H, Type, Width, DateWidth), + print_list(Fd, T, Type, Width, DateWidth). find_width([], Width) -> Width; find_width([H|T], Width) -> @@ -578,22 +598,22 @@ find_date_width([H|T], Width) -> true -> find_date_width(T, Width) end. -print_one_report({No, RealType, ShortDescr, Date, _Fname, _FilePos}, +print_one_report(Fd, {No, RealType, ShortDescr, Date, _Fname, _FilePos}, WantedType, Width, DateWidth) -> if WantedType == all -> - print_short_descr(No, RealType, ShortDescr, Date, Width, + print_short_descr(Fd, No, RealType, ShortDescr, Date, Width, DateWidth); WantedType == RealType -> - print_short_descr(No, RealType, ShortDescr, Date, Width, + print_short_descr(Fd, No, RealType, ShortDescr, Date, Width, DateWidth); true -> ok end. -print_short_descr(No, Type, ShortDescr, Date, Width, DateWidth) -> +print_short_descr(Fd, No, Type, ShortDescr, Date, Width, DateWidth) -> Format = lists:concat(["~4w~20w ~", Width, "s~", DateWidth,"s~n"]), - io:format(Format, [No, + io:format(Fd, Format, [No, Type, io_lib:format("~s", [ShortDescr]), Date]). diff --git a/lib/sasl/test/rb_SUITE.erl b/lib/sasl/test/rb_SUITE.erl index b0e43be3a2..453f992850 100644 --- a/lib/sasl/test/rb_SUITE.erl +++ b/lib/sasl/test/rb_SUITE.erl @@ -362,18 +362,48 @@ start_stop_log(Config) -> StdioResult = [_|_] = capture(fun() -> rb:show(1) end), {ok,<<>>} = file:read_file(OutFile), - %% Start log and check that show is printed to log and not to standad_io + %% Start log and check that show is printed to log and not to standard_io ok = rb:start_log(OutFile), [] = capture(fun() -> rb:show(1) end), {ok,Bin} = file:read_file(OutFile), true = (Bin =/= <<>>), + %% Start log with atom standard_io and check that show is printed to standard_io + ok = rb:stop_log(), + ok = file:write_file(OutFile,[]), + ok = rb:start_log(standard_io), + StdioResult = [_|_] = capture(fun() -> rb:show(1) end), + {ok,<<>>} = file:read_file(OutFile), + + %% Start log and check that show is printed to iodevice log and not to standard_io + ok = rb:stop_log(), + ok = file:write_file(OutFile,[]), + {ok, IoOutFile} = file:open(OutFile,[write]), + ok = rb:start_log(IoOutFile), + [] = capture(fun() -> rb:show(1) end), + {ok,Bin} = file:read_file(OutFile), + true = (Bin =/= <<>>), + ok = file:close(IoOutFile), + %% Stop log and check that show is printed to standard_io and not to log ok = rb:stop_log(), ok = file:write_file(OutFile,[]), StdioResult = capture(fun() -> rb:show(1) end), {ok,<<>>} = file:read_file(OutFile), + %% Start log and check that list is printed to log and not to standard_io + ok = file:write_file(OutFile,[]), + ok = rb:start_log(OutFile), + [] = capture(fun() -> rb:log_list() end), + {ok,Bin2} = file:read_file(OutFile), + true = (Bin2 =/= <<>>), + + %% Stop log and check that list is printed to standard_io and not to log + ok = rb:stop_log(), + ok = file:write_file(OutFile,[]), + StdioResult2 = capture(fun() -> rb:log_list() end), + {ok,<<>>} = file:read_file(OutFile), + %% Test that standard_io is used if log file can not be opened ok = rb:start_log(filename:join(nonexistingdir,"newfile.txt")), StdioResult = capture(fun() -> rb:show(1) end), diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index da5750b6c3..4fd347ba8f 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -127,7 +127,8 @@ userauth_supported_methods , % userauth_methods, userauth_preference, - available_host_keys + available_host_keys, + authenticated = false }). -record(alg, diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index df6175e27c..9de4dd5967 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -426,10 +426,10 @@ userauth(#ssh_msg_userauth_info_response{} = Msg, language = "en"}, State) end; -userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client}, +userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client} = Ssh, manager = Pid} = State) -> Pid ! ssh_connected, - {next_state, connected, next_packet(State)}; + {next_state, connected, next_packet(State#state{ssh_params = Ssh#ssh{authenticated = true}})}; userauth(#ssh_msg_userauth_failure{}, #state{ssh_params = #ssh{role = client, diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index beaffdc025..682d766d99 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -206,6 +206,7 @@ key_exchange_init_msg(Ssh0) -> kex_init(#ssh{role = Role, opts = Opts, available_host_keys = HostKeyAlgs}) -> Random = ssh_bits:random(16), Compression = case proplists:get_value(compression, Opts, none) of + openssh_zlib -> ["zlib@openssh.com", "none"]; zlib -> ["zlib", "none"]; none -> ["none", "zlib"] end, @@ -855,13 +856,14 @@ decrypt(#ssh{decrypt = 'aes128-cbc', decrypt_keys = Key, IV = crypto:next_iv(aes_cbc, Data), {Ssh#ssh{decrypt_ctx = IV}, Dec}. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Compression %% -%% none REQUIRED no compression -%% zlib OPTIONAL ZLIB (LZ77) compression +%% none REQUIRED no compression +%% zlib OPTIONAL ZLIB (LZ77) compression +%% openssh_zlib OPTIONAL ZLIB (LZ77) compression %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + compress_init(SSH) -> compress_init(SSH, 1). @@ -870,19 +872,32 @@ compress_init(#ssh{compress = none} = Ssh, _) -> compress_init(#ssh{compress = zlib} = Ssh, Level) -> Zlib = zlib:open(), ok = zlib:deflateInit(Zlib, Level), + {ok, Ssh#ssh{compress_ctx = Zlib}}; +compress_init(#ssh{compress = 'zlib@openssh.com'} = Ssh, Level) -> + Zlib = zlib:open(), + ok = zlib:deflateInit(Zlib, Level), {ok, Ssh#ssh{compress_ctx = Zlib}}. - compress_final(#ssh{compress = none} = Ssh) -> {ok, Ssh}; compress_final(#ssh{compress = zlib, compress_ctx = Context} = Ssh) -> zlib:close(Context), + {ok, Ssh#ssh{compress = none, compress_ctx = undefined}}; +compress_final(#ssh{compress = 'zlib@openssh.com', authenticated = false} = Ssh) -> + {ok, Ssh}; +compress_final(#ssh{compress = 'zlib@openssh.com', compress_ctx = Context, authenticated = true} = Ssh) -> + zlib:close(Context), {ok, Ssh#ssh{compress = none, compress_ctx = undefined}}. compress(#ssh{compress = none} = Ssh, Data) -> {Ssh, Data}; compress(#ssh{compress = zlib, compress_ctx = Context} = Ssh, Data) -> Compressed = zlib:deflate(Context, Data, sync), + {Ssh, list_to_binary(Compressed)}; +compress(#ssh{compress = 'zlib@openssh.com', authenticated = false} = Ssh, Data) -> + {Ssh, Data}; +compress(#ssh{compress = 'zlib@openssh.com', compress_ctx = Context, authenticated = true} = Ssh, Data) -> + Compressed = zlib:deflate(Context, Data, sync), {Ssh, list_to_binary(Compressed)}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -894,18 +909,32 @@ decompress_init(#ssh{decompress = none} = Ssh) -> decompress_init(#ssh{decompress = zlib} = Ssh) -> Zlib = zlib:open(), ok = zlib:inflateInit(Zlib), + {ok, Ssh#ssh{decompress_ctx = Zlib}}; +decompress_init(#ssh{decompress = 'zlib@openssh.com'} = Ssh) -> + Zlib = zlib:open(), + ok = zlib:inflateInit(Zlib), {ok, Ssh#ssh{decompress_ctx = Zlib}}. decompress_final(#ssh{decompress = none} = Ssh) -> {ok, Ssh}; decompress_final(#ssh{decompress = zlib, decompress_ctx = Context} = Ssh) -> zlib:close(Context), + {ok, Ssh#ssh{decompress = none, decompress_ctx = undefined}}; +decompress_final(#ssh{decompress = 'zlib@openssh.com', authenticated = false} = Ssh) -> + {ok, Ssh}; +decompress_final(#ssh{decompress = 'zlib@openssh.com', decompress_ctx = Context, authenticated = true} = Ssh) -> + zlib:close(Context), {ok, Ssh#ssh{decompress = none, decompress_ctx = undefined}}. decompress(#ssh{decompress = none} = Ssh, Data) -> {Ssh, Data}; decompress(#ssh{decompress = zlib, decompress_ctx = Context} = Ssh, Data) -> Decompressed = zlib:inflate(Context, Data), + {Ssh, list_to_binary(Decompressed)}; +decompress(#ssh{decompress = 'zlib@openssh.com', authenticated = false} = Ssh, Data) -> + {Ssh, Data}; +decompress(#ssh{decompress = 'zlib@openssh.com', decompress_ctx = Context, authenticated = true} = Ssh, Data) -> + Decompressed = zlib:inflate(Context, Data), {Ssh, list_to_binary(Decompressed)}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml index 90f24c4cbc..a9467557a3 100644 --- a/lib/stdlib/doc/src/io.xml +++ b/lib/stdlib/doc/src/io.xml @@ -206,6 +206,7 @@ [{expand_fun,#Fun<group.0.120017273>}, {echo,true}, {binary,false}, + {max_length,unlimited}, {encoding,unicode}]</pre> <p>This example is, as can be seen, run in an environment where the terminal supports Unicode input and output.</p> </desc> @@ -284,6 +285,14 @@ <p><c>{encoding, utf8}</c> will have the same effect as <c>{encoding, unicode}</c> on files.</p> <p>The extended encodings are only supported on disk files (opened by the <seealso marker="kernel:file#open/2">file:open/2</seealso> function)</p> </item> + <tag><c>{max_length, 'unlimited' | pos_integer()}</c></tag> + <item> + <p>Denotes the maximum line length that the I/O server shall handle. If lines + longer than this value are entered, input processing stops at max_length characters + and the input read up to here is returned. The default value is <c>unlimited</c> which + means that no input length restriction is active.</p> + <p>This option is supported by the standard shell only (<c>group.erl</c>).</p> + </item> </taglist> </desc> </func> diff --git a/lib/stdlib/doc/src/io_protocol.xml b/lib/stdlib/doc/src/io_protocol.xml index d36bf2042f..f52d0382f4 100644 --- a/lib/stdlib/doc/src/io_protocol.xml +++ b/lib/stdlib/doc/src/io_protocol.xml @@ -334,10 +334,12 @@ understands the following options:</p> <em>{echo, boolean()}</em><br/> <em>{expand_fun, fun()}</em><br/> <em>{encoding, unicode/latin1}</em> (or <em>unicode</em>/<em>latin1</em>) +<em>{max_length, integer()}</em><br/> </p> <p>- of which the <c>binary</c> and <c>encoding</c> options are common for all -I/O servers in OTP, while <c>echo</c> and <c>expand</c> are valid only for this +I/O servers in OTP, while <c>echo</c>, <c>expand</c> and <c>max_length</c> +are valid only for this I/O server. It is worth noting that the <c>unicode</c> option notifies how characters are actually put on the physical IO device, i.e. if the terminal per se is Unicode aware, it does not affect how characters diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl index 3192879f09..f5998c54fd 100644 --- a/lib/stdlib/src/edlin.erl +++ b/lib/stdlib/src/edlin.erl @@ -79,6 +79,14 @@ edit([C|Cs], P, {Bef,Aft}, Prefix, Rs0) -> case key_map(C, Prefix) of meta -> edit(Cs, P, {Bef,Aft}, meta, Rs0); + meta_o -> + edit(Cs, P, {Bef,Aft}, meta_o, Rs0); + meta_csi -> + edit(Cs, P, {Bef,Aft}, meta_csi, Rs0); + meta_meta -> + edit(Cs, P, {Bef,Aft}, meta_meta, Rs0); + {csi, _} = Csi -> + edit(Cs, P, {Bef,Aft}, Csi, Rs0); meta_left_sq_bracket -> edit(Cs, P, {Bef,Aft}, meta_left_sq_bracket, Rs0); search_meta -> @@ -178,6 +186,7 @@ key_map($\^U, none) -> ctlu; key_map($\^], none) -> auto_blink; key_map($\^X, none) -> ctlx; key_map($\^Y, none) -> yank; +key_map($\^W, none) -> backward_kill_word; key_map($\e, none) -> meta; key_map($), Prefix) when Prefix =/= meta, Prefix =/= search, @@ -198,11 +207,29 @@ key_map($d, meta) -> kill_word; key_map($f, meta) -> forward_word; key_map($t, meta) -> transpose_word; key_map($y, meta) -> yank_pop; +key_map($O, meta) -> meta_o; +key_map($H, meta_o) -> beginning_of_line; +key_map($F, meta_o) -> end_of_line; key_map($\177, none) -> backward_delete_char; key_map($\177, meta) -> backward_kill_word; key_map($[, meta) -> meta_left_sq_bracket; key_map($D, meta_left_sq_bracket) -> backward_char; key_map($C, meta_left_sq_bracket) -> forward_char; +% support a few <CTRL>+<CURSOR LEFT|RIGHT> combinations... +% - forward: \e\e[C, \e[5C, \e[1;5C +% - backward: \e\e[D, \e[5D, \e[1;5D +key_map($\e, meta) -> meta_meta; +key_map($[, meta_meta) -> meta_csi; +key_map($C, meta_csi) -> forward_word; +key_map($D, meta_csi) -> backward_word; +key_map($1, meta_left_sq_bracket) -> {csi, "1"}; +key_map($5, meta_left_sq_bracket) -> {csi, "5"}; +key_map($5, {csi, "1;"}) -> {csi, "1;5"}; +key_map($C, {csi, "5"}) -> forward_word; +key_map($C, {csi, "1;5"}) -> forward_word; +key_map($D, {csi, "5"}) -> backward_word; +key_map($D, {csi, "1;5"}) -> backward_word; +key_map($;, {csi, "1"}) -> {csi, "1;"}; key_map(C, none) when C >= $\s -> {insert,C}; %% for search, we need smarter line handling and so @@ -363,6 +390,9 @@ do_op(end_of_line, Bef, [C|Aft], Rs) -> {{reverse(Aft, [C|Bef]),[]},[{move_rel,length(Aft)+1}|Rs]}; do_op(end_of_line, Bef, [], Rs) -> {{Bef,[]},Rs}; +do_op(ctlu, Bef, Aft, Rs) -> + put(kill_buffer, Bef), + {{[], Aft}, [{delete_chars, -length(Bef)} | Rs]}; do_op(beep, Bef, Aft, Rs) -> {{Bef,Aft},[beep|Rs]}; do_op(_, Bef, Aft, Rs) -> diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 08b8541014..fae116455a 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -1953,12 +1953,10 @@ expr({string,_Line,_S}, _Vt, St) -> {[],St}; expr({nil,_Line}, _Vt, St) -> {[],St}; expr({cons,_Line,H,T}, Vt, St) -> expr_list([H,T], Vt, St); -expr({lc,_Line,E,Qs}, Vt0, St0) -> - {Vt,St} = handle_comprehension(E, Qs, Vt0, St0), - {vtold(Vt, Vt0),St}; %Don't export local variables -expr({bc,_Line,E,Qs}, Vt0, St0) -> - {Vt,St} = handle_comprehension(E, Qs, Vt0, St0), - {vtold(Vt,Vt0),St}; %Don't export local variables +expr({lc,_Line,E,Qs}, Vt, St) -> + handle_comprehension(E, Qs, Vt, St); +expr({bc,_Line,E,Qs}, Vt, St) -> + handle_comprehension(E, Qs, Vt, St); expr({tuple,_Line,Es}, Vt, St) -> expr_list(Es, Vt, St); expr({record_index,Line,Name,Field}, _Vt, St) -> @@ -2012,8 +2010,7 @@ expr({'fun',Line,Body}, Vt, St) -> %%No one can think funs export! case Body of {clauses,Cs} -> - {Bvt, St1} = fun_clauses(Cs, Vt, St), - {vtupdate(Bvt, Vt), St1}; + fun_clauses(Cs, Vt, St); {function,F,A} -> %% BifClash - Fun expression %% N.B. Only allows BIFs here as well, NO IMPORTS!! @@ -2111,12 +2108,12 @@ expr({'try',Line,Es,Scs,Ccs,As}, Vt, St0) -> {Evt0,St1} = exprs(Es, Vt, St0), TryLine = {'try',Line}, Uvt = vtunsafe(vtnames(vtnew(Evt0, Vt)), TryLine, []), - Evt1 = vtupdate(Uvt, vtupdate(Evt0, Vt)), - {Sccs,St2} = icrt_clauses(Scs++Ccs, TryLine, Evt1, St1), + Evt1 = vtupdate(Uvt, vtsubtract(Evt0, Uvt)), + {Sccs,St2} = icrt_clauses(Scs++Ccs, TryLine, vtupdate(Evt1, Vt), St1), Rvt0 = Sccs, Rvt1 = vtupdate(vtunsafe(vtnames(vtnew(Rvt0, Vt)), TryLine, []), Rvt0), Evt2 = vtmerge(Evt1, Rvt1), - {Avt0,St} = exprs(As, Evt2, St2), + {Avt0,St} = exprs(As, vtupdate(Evt2, Vt), St2), Avt1 = vtupdate(vtunsafe(vtnames(vtnew(Avt0, Vt)), TryLine, []), Avt0), Avt = vtmerge(Evt2, Avt1), {Avt,St}; @@ -2150,10 +2147,11 @@ expr({remote,Line,_M,_F}, _Vt, St) -> %% {UsedVarTable,State} expr_list(Es, Vt, St) -> - foldl(fun (E, {Esvt,St0}) -> - {Evt,St1} = expr(E, Vt, St0), - {vtmerge(Evt, Esvt),St1} - end, {[],St}, Es). + {Vt1,St1} = foldl(fun (E, {Esvt,St0}) -> + {Evt,St1} = expr(E, Vt, St0), + {vtmerge_pat(Evt, Esvt),St1} + end, {[],St}, Es), + {vtmerge(vtnew(Vt1, Vt), vtold(Vt1, Vt)),St1}. record_expr(Line, Rec, Vt, St0) -> St1 = warn_invalid_record(Line, Rec, St0), @@ -2310,7 +2308,7 @@ check_fields(Fs, Name, Fields, Vt, St0, CheckFun) -> check_field({record_field,Lf,{atom,La,F},Val}, Name, Fields, Vt, St, Sfs, CheckFun) -> case member(F, Sfs) of - true -> {Sfs,{Vt,add_error(Lf, {redefine_field,Name,F}, St)}}; + true -> {Sfs,{[],add_error(Lf, {redefine_field,Name,F}, St)}}; false -> {[F|Sfs], case find_field(F, Fields) of @@ -2843,7 +2841,9 @@ icrt_export(Csvt, Vt, In, St) -> Uvt = vtmerge(Evt, Unused), %% Make exported and unsafe unused variables unused in subsequent code: Vt2 = vtmerge(Uvt, vtsubtract(Vt1, Uvt)), - {Vt2,St}. + %% Forget about old variables which were not used: + Vt3 = vtmerge(vtnew(Vt2, Vt), vt_no_unused(vtold(Vt2, Vt))), + {Vt3,St}. handle_comprehension(E, Qs, Vt0, St0) -> {Vt1, Uvt, St1} = lc_quals(Qs, Vt0, St0), @@ -2856,7 +2856,11 @@ handle_comprehension(E, Qs, Vt0, St0) -> %% Local variables that have not been shadowed. {_,St} = check_unused_vars(Vt2, Vt0, St4), Vt3 = vtmerge(vtsubtract(Vt2, Uvt), Uvt), - {Vt3,St}. + %% Don't export local variables. + Vt4 = vtold(Vt3, Vt0), + %% Forget about old variables which were not used. + Vt5 = vt_no_unused(Vt4), + {Vt5,St}. %% lc_quals(Qualifiers, ImportVarTable, State) -> %% {VarTable,ShadowedVarTable,State} @@ -2920,7 +2924,7 @@ fun_clauses(Cs, Vt, St) -> {Cvt,St1} = fun_clause(C, Vt, St0), {vtmerge(Cvt, Bvt0),St1} end, {[],St#lint{recdef_top = false}}, Cs), - {Bvt,St2#lint{recdef_top = OldRecDef}}. + {vt_no_unused(vtold(Bvt, Vt)),St2#lint{recdef_top = OldRecDef}}. fun_clause({clause,_Line,H,G,B}, Vt0, St0) -> {Hvt,Binvt,St1} = head(H, Vt0, [], St0), % No imported pattern variables @@ -3181,6 +3185,8 @@ vt_no_unsafe(Vt) -> [V || {_,{S,_U,_L}}=V <- Vt, _ -> true end]. +vt_no_unused(Vt) -> [V || {_,{_,U,_L}}=V <- Vt, U =/= unused]. + %% vunion(VarTable1, VarTable2) -> [VarName]. %% vunion([VarTable]) -> [VarName]. %% vintersection(VarTable1, VarTable2) -> [VarName]. diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl index b11d41e2eb..f2a241fb1f 100644 --- a/lib/stdlib/src/io.erl +++ b/lib/stdlib/src/io.erl @@ -172,10 +172,12 @@ get_password(Io) -> -type encoding() :: 'latin1' | 'unicode' | 'utf8' | 'utf16' | 'utf32' | {'utf16', 'big' | 'little'} | {'utf32','big' | 'little'}. -type expand_fun() :: fun((term()) -> {'yes'|'no', string(), [string(), ...]}). +-type max_length() :: 'unlimited' | pos_integer(). -type opt_pair() :: {'binary', boolean()} | {'echo', boolean()} | {'expand_fun', expand_fun()} - | {'encoding', encoding()}. + | {'encoding', encoding()} + | {'max_length', max_length()}. -spec getopts() -> [opt_pair()]. @@ -552,6 +554,8 @@ request(Request) -> request(standard_io, Request) -> request(group_leader(), Request); +request(Pid, Request) when Pid=:=self() -> + execute_request(group_leader(), io_request(Pid, Request)); request(Pid, Request) when is_pid(Pid) -> execute_request(Pid, io_request(Pid, Request)); request(Name, Request) when is_atom(Name) -> diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index f8345559c4..5ff35cff9a 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -151,7 +151,16 @@ unused_vars_warn_basic(Config) when is_list(Config) -> {22,erl_lint,{unused_var,'N'}}, {23,erl_lint,{shadowed_var,'N','fun'}}, {28,erl_lint,{unused_var,'B'}}, - {29,erl_lint,{unused_var,'B'}}]}}], + {29,erl_lint,{unused_var,'B'}}]}}, + {basic2, + <<"-record(r, {x,y}). + f({X,Y}) -> {Z=X,Z=Y}; + f([H|T]) -> [Z=H|Z=T]; + f(#r{x=X,y=Y}) -> #r{x=A=X,y=A=Y}. + g({M, F}) -> (Z=M):(Z=F)(); + g({M, F, Arg}) -> (Z=M):F(Z=Arg). + h(X, Y) -> (Z=X) + (Z=Y).">>, + [warn_unused_vars], []}], ?line [] = run(Config, Ts), ok. @@ -537,7 +546,28 @@ unused_vars_warn_rec(Config) when is_list(Config) -> end. ">>, [warn_unused_vars], - {warnings,[{22,erl_lint,{unused_var,'Same'}}]}}], + {warnings,[{22,erl_lint,{unused_var,'Same'}}]}}, + {rec2, + <<"-record(r, {a,b}). + f(X, Y) -> #r{a=[K || K <- Y], b=[K || K <- Y]}. + g(X, Y) -> #r{a=lists:map(fun (K) -> K end, Y), + b=lists:map(fun (K) -> K end, Y)}. + h(X, Y) -> #r{a=case Y of _ when is_list(Y) -> Y end, + b=case Y of _ when is_list(Y) -> Y end}. + i(X, Y) -> #r{a=if is_list(Y) -> Y end, b=if is_list(Y) -> Y end}. + ">>, + [warn_unused_vars], + {warnings,[{2,erl_lint,{unused_var,'X'}}, + {3,erl_lint,{unused_var,'X'}}, + {5,erl_lint,{unused_var,'X'}}, + {7,erl_lint,{unused_var,'X'}}]}}, + {rec3, + <<"-record(r, {a}). + t() -> X = 1, #r{a=foo, a=bar}. + ">>, + [warn_unused_vars], + {error,[{2,erl_lint,{redefine_field,r,a}}], + [{2,erl_lint,{unused_var,'X'}}]}}], ?line [] = run(Config, Ts), ok. @@ -1075,7 +1105,24 @@ unsafe_vars_try(Config) when is_list(Config) -> {10,erl_lint,{unsafe_var,'Ra',{'try',3}}}, {10,erl_lint,{unsafe_var,'Rc',{'try',3}}}, {10,erl_lint,{unsafe_var,'Ro',{'try',3}}}], - []}}], + []}}, + {unsafe_try5, + <<"bang() -> + case 1 of + nil -> + Acc = 2; + _ -> + try + Acc = 3, + Acc + catch _:_ -> + ok + end + end, + Acc. + ">>, + [], + {errors,[{13,erl_lint,{unsafe_var,'Acc',{'try',6}}}],[]}}], ?line [] = run(Config, Ts), ok. diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index bdb2b5bcd7..409805e95f 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -5485,22 +5485,15 @@ revert_implicit_fun(Node) -> arity_qualifier -> F = arity_qualifier_body(Name), A = arity_qualifier_argument(Name), - case {type(F), type(A)} of - {atom, integer} -> - {'fun', Pos, - {function, concrete(F), concrete(A)}}; - _ -> - Node - end; + {'fun', Pos, {function, F, A}}; module_qualifier -> M = module_qualifier_argument(Name), Name1 = module_qualifier_body(Name), - F = arity_qualifier_body(Name1), - A = arity_qualifier_argument(Name1), - case {type(M), type(F), type(A)} of - {atom, atom, integer} -> - {'fun', Pos, - {function, concrete(M), concrete(F), concrete(A)}}; + case type(Name1) of + arity_qualifier -> + F = arity_qualifier_body(Name1), + A = arity_qualifier_argument(Name1), + {'fun', Pos, {function, M, F, A}}; _ -> Node end; diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl index 2c94ac776d..e4665b99fc 100644 --- a/lib/syntax_tools/src/erl_syntax_lib.erl +++ b/lib/syntax_tools/src/erl_syntax_lib.erl @@ -1357,8 +1357,6 @@ analyze_attribute(file, Node) -> analyze_file_attribute(Node); analyze_attribute(record, Node) -> analyze_record_attribute(Node); -analyze_attribute(define, _Node) -> - define; analyze_attribute(spec, _Node) -> spec; analyze_attribute(_, Node) -> diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl index 0c149634f6..7444d8dc67 100644 --- a/lib/syntax_tools/src/erl_tidy.erl +++ b/lib/syntax_tools/src/erl_tidy.erl @@ -269,6 +269,13 @@ file(Name) -> %% is typically most useful if the `verbose' flag is enabled, to %% generate reports about the program files without affecting %% them. The default value is `false'.</dd> +%% +%% <dt>{stdout, boolean()}</dt> +%% +%% <dd>If the value is `true', instead of the file being written +%% to disk it will be printed to stdout. The default value is +%% `false'.</dd> +%% %% </dl> %% %% See the function `module/2' for further options. @@ -309,9 +316,15 @@ file_2(Name, Opts) -> true -> ok; false -> - write_module(Tree, Name, Opts1), - ok - end. + case proplists:get_bool(stdout, Opts1) of + true -> + print_module(Tree, Opts1), + ok; + false -> + write_module(Tree, Name, Opts1), + ok + end + end. read_module(Name, Opts) -> verbose("reading module `~ts'.", [filename(Name)], Opts), @@ -399,6 +412,10 @@ write_module(Tree, Name, Opts) -> throw(R) end. +print_module(Tree, Opts) -> + Printer = proplists:get_value(printer, Opts), + io:format(Printer(Tree, Opts)). + output(FD, Printer, Tree, Opts) -> io:put_chars(FD, Printer(Tree, Opts)), io:nl(FD). diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl index d385c2b690..19b1cd592f 100644 --- a/lib/syntax_tools/src/igor.erl +++ b/lib/syntax_tools/src/igor.erl @@ -1803,20 +1803,25 @@ transform_rule(T, Env, St) -> transform_implicit_fun(T, Env, St) -> {T1, St1} = default_transform(T, Env, St), - F = erl_syntax_lib:analyze_implicit_fun(T1), - {V, Text} = case (Env#code.map)(F) of - F -> - %% Not renamed - {none, []}; - {Atom, Arity} -> - %% Renamed - N = rewrite( - erl_syntax:implicit_fun_name(T1), - erl_syntax:arity_qualifier( - erl_syntax:atom(Atom), - erl_syntax:integer(Arity))), - T2 = erl_syntax:implicit_fun(N), - {{value, T2}, ["function was renamed"]} + {V, Text} = case erl_syntax:type(erl_syntax:implicit_fun_name(T1)) of + arity_qualifier -> + F = erl_syntax_lib:analyze_implicit_fun(T1), + case (Env#code.map)(F) of + F -> + %% Not renamed + {none, []}; + {Atom, Arity} -> + %% Renamed + N = rewrite( + erl_syntax:implicit_fun_name(T1), + erl_syntax:arity_qualifier( + erl_syntax:atom(Atom), + erl_syntax:integer(Arity))), + T2 = erl_syntax:implicit_fun(N), + {{value, T2}, ["function was renamed"]} + end; + module_qualifier -> + {none, []} end, {maybe_modified_quiet(V, T1, 2, Text, Env), St1}. diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index f3bc95e3e5..3ecda1d285 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -2813,6 +2813,9 @@ Return nil if inside string, t if in a comment." (- (+ previous erlang-argument-indent) 1)))) (t (nth 2 stack-top)))) + ((= (following-char) ?,) + ;; a comma at the start of the line: line up with opening parenthesis. + (nth 2 stack-top)) (t (goto-char (nth 1 stack-top)) (let ((base (cond ((looking-at "[({]\\s *\\($\\|%\\)") diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented index 6c9343f6cb..7e61bcc45b 100644 --- a/lib/tools/emacs/test.erl.indented +++ b/lib/tools/emacs/test.erl.indented @@ -700,3 +700,34 @@ some_function_name_xyz(xyzzy, #some_record{ field2 = Field1, field2 = Field2}}), {ok, SomeVariable}. + +commas_first() -> + {abc, [ {some_var, 1} + , {some_other_var, 2} + , {erlang_ftw, 9} + , {erlang_cookie, 'cookie'} + , {cmds, + [ {one, "sudo ls"} + , {one, "sudo ls"} + , {two, "sudo ls"} + , {three, "sudo ls"} + , {four, "sudo ls"} + , {three, "sudo ls"} + ] } + , {ssh_username, "yow"} + , {cluster, + [ {aaaa, [ {"10.198.55.12" , "" } + , {"10.198.55.13" , "" } + ] } + , {bbbb, [ {"10.198.55.151", "" } + , {"10.198.55.123", "" } + , {"10.198.55.34" , "" } + , {"10.198.55.85" , "" } + , {"10.198.55.67" , "" } + ] } + , {cccc, [ {"10.198.55.68" , "" } + , {"10.198.55.69" , "" } + ] } + ] } + ] + }. diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig index 0f8c4a9175..932758997d 100644 --- a/lib/tools/emacs/test.erl.orig +++ b/lib/tools/emacs/test.erl.orig @@ -700,3 +700,34 @@ some_function_name_xyz(xyzzy, #some_record{ field2 = Field1, field2 = Field2}}), {ok, SomeVariable}. + +commas_first() -> + {abc, [ {some_var, 1} + , {some_other_var, 2} + , {erlang_ftw, 9} + , {erlang_cookie, 'cookie'} + , {cmds, + [ {one, "sudo ls"} + , {one, "sudo ls"} + , {two, "sudo ls"} + , {three, "sudo ls"} + , {four, "sudo ls"} + , {three, "sudo ls"} + ] } + , {ssh_username, "yow"} + , {cluster, + [ {aaaa, [ {"10.198.55.12" , "" } + , {"10.198.55.13" , "" } + ] } + , {bbbb, [ {"10.198.55.151", "" } + , {"10.198.55.123", "" } + , {"10.198.55.34" , "" } + , {"10.198.55.85" , "" } + , {"10.198.55.67" , "" } + ] } + , {cccc, [ {"10.198.55.68" , "" } + , {"10.198.55.69" , "" } + ] } + ] } +] +}. |