diff options
Diffstat (limited to 'lib')
357 files changed, 12381 insertions, 13398 deletions
diff --git a/lib/asn1/doc/src/asn1ct.xml b/lib/asn1/doc/src/asn1ct.xml index 9a9bb7ec1b..bb3c6a4f0f 100644 --- a/lib/asn1/doc/src/asn1ct.xml +++ b/lib/asn1/doc/src/asn1ct.xml @@ -120,9 +120,7 @@ File3.asn </pre> <c>Options</c> is a list with options specific for the asn1 compiler and options that are applied to the Erlang compiler. The latter are those that not is recognized as asn1 specific. - For <em>preferred option use</em> see <seealso - marker="asn1_ug#preferred option use">Preferred Option Use - section in users guide</seealso>. Available options are: + Available options are: </p> <taglist> <tag><c>ber | per | uper</c></tag> diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index 90882462ac..98877320a0 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -1288,35 +1288,35 @@ pretty2(Module,AbsFile) -> {ok,F} = file:open(AbsFile,[write]), M = asn1_db:dbget(Module,'MODULE'), io:format(F,"%%%%%%%%%%%%%%%%%%% ~p %%%%%%%%%%%%%%%%%%%~n",[Module]), - io:format(F,"~s\n",[asn1ct_pretty_format:term(M#module.defid)]), - io:format(F,"~s\n",[asn1ct_pretty_format:term(M#module.tagdefault)]), - io:format(F,"~s\n",[asn1ct_pretty_format:term(M#module.exports)]), - io:format(F,"~s\n",[asn1ct_pretty_format:term(M#module.imports)]), - io:format(F,"~s\n\n",[asn1ct_pretty_format:term(M#module.extensiondefault)]), + io:format(F,"~s.\n",[asn1ct_pretty_format:term(M#module.defid)]), + io:format(F,"~s.\n",[asn1ct_pretty_format:term(M#module.tagdefault)]), + io:format(F,"~s.\n",[asn1ct_pretty_format:term(M#module.exports)]), + io:format(F,"~s.\n",[asn1ct_pretty_format:term(M#module.imports)]), + io:format(F,"~s.\n\n",[asn1ct_pretty_format:term(M#module.extensiondefault)]), {Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets} = M#module.typeorval, io:format(F,"%%%%%%%%%%%%%%%%%%% TYPES in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]), - lists:foreach(fun(T)-> io:format(F,"~s\n", + lists:foreach(fun(T)-> io:format(F,"~s.\n", [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))]) end,Types), io:format(F,"%%%%%%%%%%%%%%%%%%% VALUES in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]), - lists:foreach(fun(T)-> io:format(F,"~s\n", + lists:foreach(fun(T)-> io:format(F,"~s.\n", [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))]) end,Values), io:format(F,"%%%%%%%%%%%%%%%%%%% Parameterized Types in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]), - lists:foreach(fun(T)-> io:format(F,"~s\n", + lists:foreach(fun(T)-> io:format(F,"~s.\n", [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))]) end,ParameterizedTypes), io:format(F,"%%%%%%%%%%%%%%%%%%% Classes in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]), - lists:foreach(fun(T)-> io:format(F,"~s\n", + lists:foreach(fun(T)-> io:format(F,"~s.\n", [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))]) end,Classes), io:format(F,"%%%%%%%%%%%%%%%%%%% Objects in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]), - lists:foreach(fun(T)-> io:format(F,"~s\n", + lists:foreach(fun(T)-> io:format(F,"~s.\n", [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))]) end,Objects), io:format(F,"%%%%%%%%%%%%%%%%%%% Object Sets in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]), - lists:foreach(fun(T)-> io:format(F,"~s\n", + lists:foreach(fun(T)-> io:format(F,"~s.\n", [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))]) end,ObjectSets). start() -> diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index fe1b2e14a8..dd77085c39 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -4340,11 +4340,33 @@ permitted_alphabet_merge([C1|Rest],UorI,Acc) -> %% there will be no extension if the last constraint is without extension. %% The rootset of all constraints are considered in the "outermoust %% intersection". See section 13.1.2 in Dubuisson. -constraint_merge(_S,C=[H])when is_tuple(H) -> +constraint_merge(St, Cs0) -> + Cs = constraint_merge_1(St, Cs0), + normalize_cs(Cs). + +normalize_cs([{'SingleValue',[V]}|Cs]) -> + [{'SingleValue',V}|normalize_cs(Cs)]; +normalize_cs([{'SingleValue',[_|_]=L0}|Cs]) -> + [H|T] = L = lists:usort(L0), + [case is_range(H, T) of + false -> {'SingleValue',L}; + true -> {'ValueRange',{H,lists:last(T)}} + end|normalize_cs(Cs)]; +normalize_cs([{'ValueRange',{Sv,Sv}}|Cs]) -> + [{'SingleValue',Sv}|normalize_cs(Cs)]; +normalize_cs([{'ValueRange',{'MIN','MAX'}}|Cs]) -> + normalize_cs(Cs); +normalize_cs(Other) -> Other. + +is_range(Prev, [H|T]) when Prev =:= H - 1 -> is_range(H, T); +is_range(_, [_|_]) -> false; +is_range(_, []) -> true. + +constraint_merge_1(_S, [H]=C) when is_tuple(H) -> C; -constraint_merge(_S,[]) -> +constraint_merge_1(_S, []) -> []; -constraint_merge(S,C) -> +constraint_merge_1(S, C) -> %% skip all extension but the last extension C1 = filter_extensions(C), %% perform all internal level intersections, intersections first @@ -4367,17 +4389,16 @@ constraint_merge(S,C) -> %% get the least common size constraint SZs = get_constraints(C3,'SizeConstraint'), CombSZ = intersection_of_size(S,SZs), - CminusSVs=ordsets:subtract(ordsets:from_list(C3),ordsets:from_list(SVs)), - % CminusSVsVRs = ordsets:subtract(ordsets:from_list(CminusSVs), -% ordsets:from_list(VRs)), - RestC = ordsets:subtract(ordsets:from_list(CminusSVs), - ordsets:from_list(SZs)), + RestC = ordsets:subtract(ordsets:from_list(C3), + ordsets:from_list(SZs ++ VRs ++ SVs)), %% get the least common combined constraint. That is the union of each - %% deep costraint and merge of single value and value range constraints - NewCs = combine_constraints(S,CombSV,CombVR,CombSZ++RestC), - [X||X <- lists:flatten(NewCs), - X /= intersection, - X /= union]. + %% deep constraint and merge of single value and value range constraints. + %% FIXME: Removing 'intersection' from the flattened list essentially + %% means that intersections are converted to unions! + Cs = combine_constraints(S, CombSV, CombVR, CombSZ++RestC), + [X || X <- lists:flatten(Cs), + X =/= intersection, + X =/= union]. %% constraint_union(S,C) takes a list of constraints as input and %% merge them to a union. Unions are performed when two @@ -4407,16 +4428,16 @@ constraint_union(_S,C) -> constraint_union1(S,[A={'ValueRange',_},union,B={'ValueRange',_}|Rest],Acc) -> AunionB = constraint_union_vr([A,B]), - constraint_union1(S,Rest,Acc ++ AunionB); + constraint_union1(S, AunionB++Rest, Acc); constraint_union1(S,[A={'SingleValue',_},union,B={'SingleValue',_}|Rest],Acc) -> AunionB = constraint_union_sv(S,[A,B]), constraint_union1(S,Rest,Acc ++ AunionB); constraint_union1(S,[A={'SingleValue',_},union,B={'ValueRange',_}|Rest],Acc) -> AunionB = union_sv_vr(S,A,B), - constraint_union1(S,Rest,Acc ++ AunionB); + constraint_union1(S, AunionB++Rest, Acc); constraint_union1(S,[A={'ValueRange',_},union,B={'SingleValue',_}|Rest],Acc) -> AunionB = union_sv_vr(S,B,A), - constraint_union1(S,Rest,Acc ++ AunionB); + constraint_union1(S, AunionB++Rest, Acc); constraint_union1(S,[union|Rest],Acc) -> %skip when unsupported constraints constraint_union1(S,Rest,Acc); constraint_union1(S,[A|Rest],Acc) -> @@ -4449,15 +4470,8 @@ constraint_union_vr(VR) -> ({_,{A1,_B1}},{_,{A2,_B2}}) when is_integer(A1),is_integer(A2),A1<A2 -> true; ({_,{A,B1}},{_,{A,B2}}) when B1=<B2->true; (_,_)->false end, - % sort and remove duplicates - SortedVR = lists:sort(Fun,VR), - RemoveDup = fun([],_) ->[]; - ([H],_) -> [H]; - ([H,H|T],F) -> F([H|T],F); - ([H|T],F) -> [H|F(T,F)] - end, - - constraint_union_vr(RemoveDup(SortedVR,RemoveDup),[]). + SortedVR = lists:usort(Fun,VR), + constraint_union_vr(SortedVR, []). constraint_union_vr([],Acc) -> lists:reverse(Acc); @@ -4467,8 +4481,8 @@ constraint_union_vr([{_,{Lb,Ub2}}|Rest],[{_,{Lb,_Ub1}}|Acc]) -> %Ub2 > Ub1 constraint_union_vr(Rest,[{'ValueRange',{Lb,Ub2}}|Acc]); constraint_union_vr([{_,{_,Ub}}|Rest],A=[{_,{_,Ub}}|_Acc]) -> constraint_union_vr(Rest,A); -constraint_union_vr([{_,{Lb2,Ub2}}|Rest],[{_,{Lb1,Ub1}}|Acc]) when Lb2=<Ub1, - Ub2>Ub1-> +constraint_union_vr([{_,{Lb2,Ub2}}|Rest], [{_,{Lb1,Ub1}}|Acc]) + when Ub1 =< Lb2, Ub1 < Ub2 -> constraint_union_vr(Rest,[{'ValueRange',{Lb1,Ub2}}|Acc]); constraint_union_vr([{_,{_,Ub2}}|Rest],A=[{_,{_,Ub1}}|_Acc]) when Ub2=<Ub1-> constraint_union_vr(Rest,A); @@ -4589,9 +4603,11 @@ constraint_intersection(_S,C) -> constraint_intersection1(S,[A,intersection,B|Rest],Acc) -> AisecB = c_intersect(S,A,B), - constraint_intersection1(S,Rest,AisecB++Acc); + constraint_intersection1(S, AisecB++Rest, Acc); constraint_intersection1(S,[A|Rest],Acc) -> constraint_intersection1(S,Rest,[A|Acc]); +constraint_intersection1(_, [], [C]) -> + C; constraint_intersection1(_,[],Acc) -> lists:reverse(Acc). diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index b29a7b3048..27070be966 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -108,7 +108,10 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) -> emit([ {next,val}," = case [X || X <- [",Elements, "],X =/= asn1_NOVALUE] of",nl, - "[] -> ",{curr,val},";",nl, + "[] -> setelement(", + {asis,ExtActualGroupPos+1},",", + {curr,val},",", + "asn1_NOVALUE);",nl, "_ -> setelement(",{asis,ExtActualGroupPos+1},",", {curr,val},",", "{extaddgroup,", Elements,"})",nl, @@ -1099,10 +1102,11 @@ gen_dec_components_call(Erule,TopType,CL={Root1,ExtList,Root2}, {EmitExts,_} = gen_dec_comp_calls(NewExtList, Erule, TopType, OptTable, DecInfObj, Ext, NumberOfOptionals, Tpos, []), + NumExtsToSkip = ext_length(ExtList), Finish = fun(St) -> emit([{next,bytes},"= ?RT_PER:skipextensions(",{curr,bytes},",", - length(ExtList)+1,",Extensions)"]), + NumExtsToSkip+1,",Extensions)"]), asn1ct_name:new(bytes), St end, @@ -1361,7 +1365,7 @@ gen_dec_line_open_type(_, _, _) -> fun() -> St end} end}. -gen_dec_line_special(_, {typefield,_}, _TopType, Comp, +gen_dec_line_special(Erule, {typefield,_}, _TopType, Comp, DecInfObj, Ext) -> #'ComponentType'{name=Cname,typespec=Type,prop=Prop} = Comp, fun({_BytesVar,PrevSt}) -> @@ -1370,13 +1374,14 @@ gen_dec_line_special(_, {typefield,_}, _TopType, Comp, {Name,RestFieldNames} = (Type#type.def)#'ObjectClassFieldType'.fieldname, - asn1ct_name:new(tmpterm), asn1ct_name:new(reason), - emit([indent(2),"{",{curr,tmpterm},", ",{next,bytes}, - "} = ?RT_PER:decode_open_type(",{curr,bytes}, - ", []),",nl]), - emit([indent(2),"case (catch ObjFun(", - {asis,Name},",",{curr,tmpterm},",telltype,", + Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)), + BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)), + {TmpTerm,TempBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar), + emit([com,nl, + {next,bytes}," = ",TempBuf,com,nl, + indent(2),"case (catch ObjFun(", + {asis,Name},",",TmpTerm,",telltype,", {asis,RestFieldNames},")) of", nl]), emit([indent(4),"{'EXIT',",{curr,reason},"} ->",nl]), emit([indent(6),"exit({'Type not ", @@ -1402,8 +1407,10 @@ gen_dec_line_special(_, {typefield,_}, _TopType, Comp, end, {Name,RestFieldNames} = (Type#type.def)#'ObjectClassFieldType'.fieldname, - emit(["?RT_PER:decode_open_type(",{curr,bytes}, - ", []),",nl]), + Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)), + BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)), + asn1ct_imm:dec_code_gen(Imm, BytesVar), + emit([com,nl]), if Ext == noext andalso Prop == mandatory -> emit([{curr,term}," =",nl," "]); @@ -1429,8 +1436,9 @@ gen_dec_line_special(_, {typefield,_}, _TopType, Comp, end, {[],PrevSt}; _ -> - emit(["?RT_PER:decode_open_type(",{curr,bytes}, - ", [])"]), + Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)), + BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)), + asn1ct_imm:dec_code_gen(Imm, BytesVar), RefedFieldName = (Type#type.def)#'ObjectClassFieldType'.fieldname, @@ -1440,10 +1448,12 @@ gen_dec_line_special(_, {typefield,_}, _TopType, Comp, Prop}],PrevSt} end end; -gen_dec_line_special(_, {objectfield,PrimFieldName1,PFNList}, _TopType, +gen_dec_line_special(Erule, {objectfield,PrimFieldName1,PFNList}, _TopType, Comp, _DecInfObj, _Ext) -> fun({_BytesVar,PrevSt}) -> - emit(["?RT_PER:decode_open_type(",{curr,bytes},", [])"]), + Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)), + BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)), + asn1ct_imm:dec_code_gen(Imm, BytesVar), #'ComponentType'{name=Cname,prop=Prop} = Comp, SaveBytes = [{Cname,{PrimFieldName1,PFNList}, asn1ct_gen:mk_var(asn1ct_name:curr(term)), @@ -1666,20 +1676,15 @@ gen_dec_choice1(Erule,TopType,CompList,{ext,ExtPos,ExtNum}) -> length(CompList)-ExtNum,",Ext ),",nl}), emit({"{Cname,{Val,NewBytes}} = case Choice + Ext*",ExtPos-1," of",nl}), gen_dec_choice2(Erule,TopType,CompList,{ext,ExtPos,ExtNum}), - case Erule of - per -> - emit([";",nl,"_ -> {asn1_ExtAlt,",nl, - " fun() -> ",nl, - " {XTerm,XBytes} = ?RT_PER:decode_open_type(", - {curr,bytes},",[]),",nl, - " {binary_to_list(XTerm),XBytes}",nl, - " end()}"]); - _ -> - emit([";",nl,"_ -> {asn1_ExtAlt, ?RT_PER:decode_open_type(", - {curr,bytes},",[])}"]) - end, - emit({nl,"end,",nl}), - emit({nl,"{{Cname,Val},NewBytes}"}). + Imm = asn1ct_imm:per_dec_open_type(is_aligned(Erule)), + BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)), + emit([";",nl, + "_ ->",nl]), + {TmpTerm,TmpBuf} = asn1ct_imm:dec_slim_cg(Imm, BytesVar), + emit([com,nl, + "{asn1_ExtAlt,{",TmpTerm,com,TmpBuf,"}}",nl, + "end,",nl,nl, + "{{Cname,Val},NewBytes}"]). gen_dec_choice2(Erule,TopType,L,Ext) -> diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index 00c3dd98b2..664dfc2086 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -114,16 +114,6 @@ gen_encode(Erules,Typename,Type) when is_record(Type,type) -> _ -> % embedded type with constructed name true end, - case lists:member(InnerType,['SET','SEQUENCE']) of - true -> - true; - _ -> - emit([nl,"'enc_",asn1ct_gen:list2name(Typename), - "'({'",asn1ct_gen:list2name(Typename), - "',Val}, TagIn",ObjFun,") ->",nl]), - emit([" 'enc_",asn1ct_gen:list2name(Typename), - "'(Val, TagIn",ObjFun,");",nl,nl]) - end, emit(["'enc_",asn1ct_gen:list2name(Typename), "'(Val, TagIn",ObjFun,") ->",nl," "]), asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type); @@ -157,15 +147,6 @@ gen_encode_user(Erules,D) when is_record(D,typedef) -> "'(Val",") ->",nl]), emit([" 'enc_",asn1ct_gen:list2name(Typename), "'(Val, ", {asis,lists:reverse(Tag)},").",nl,nl]), - - case lists:member(InnerType,['SET','SEQUENCE']) of - true -> - true; - _ -> - emit({nl,"'enc_",asn1ct_gen:list2name(Typename), - "'({'",asn1ct_gen:list2name(Typename),"',Val}, TagIn) ->",nl}), - emit({" 'enc_",asn1ct_gen:list2name(Typename),"'(Val, TagIn);",nl,nl}) - end, emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val, TagIn) ->",nl}), CurrentMod = get(currmod), case asn1ct_gen:type(InnerType) of diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index eb4cfdccc6..af19edb908 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -79,18 +79,6 @@ gen_encode(Erules,Typename,Type) when is_record(Type,type) -> end, case asn1ct_gen:type(InnerType) of {constructed,bif} -> - case InnerType of - 'SET' -> - true; - 'SEQUENCE' -> - true; - _ -> - emit({nl,"'enc_",asn1ct_gen:list2name(Typename), - "'({'",asn1ct_gen:list2name(Typename), - "',Val}",ObjFun,") ->",nl}), - emit({"'enc_",asn1ct_gen:list2name(Typename), - "'(Val",ObjFun,");",nl,nl}) - end, emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val",ObjFun, ") ->",nl}), asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type); @@ -943,7 +931,6 @@ gen_objset_dec(ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,_ClFields, _NthObj) -> emit({"'getdec_",ObjSetName,"'(_, _) ->",nl}), emit({indent(3),"fun(Attr1, Bytes, _,_) ->",nl}), -%% emit({indent(6),"?RT_PER:decode_open_type(Bytes,[])",nl}), emit({indent(6),"{Bytes,Attr1}",nl}), emit({indent(3),"end.",nl,nl}), ok; @@ -959,76 +946,42 @@ emit_default_getdec(ObjSetName,UniqueName) -> emit([indent(2), "fun(C,V,_,_) -> exit({{component,C},{value,V},{unique_name_and_value,",{asis,UniqueName},",ErrV}}) end"]). -gen_inlined_dec_funs(Fields,[{typefield,Name,_}|Rest], - ObjSetName,NthObj) -> - CurrMod = get(currmod), - InternalDefFunName = [NthObj,Name,ObjSetName], - case lists:keysearch(Name,1,Fields) of - {value,{_,Type}} when is_record(Type,type) -> - emit({indent(3),"fun(Type, Val, _, _) ->",nl, - indent(6),"case Type of",nl}), - N=emit_inner_of_decfun(Type,InternalDefFunName), - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N); - {value,{_,Type}} when is_record(Type,typedef) -> - emit({indent(3),"fun(Type, Val, _, _) ->",nl, - indent(6),"case Type of",nl}), - emit({indent(9),{asis,Name}," ->",nl}), - N=emit_inner_of_decfun(Type,InternalDefFunName), - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N); - {value,{_,#'Externaltypereference'{module=CurrMod,type=T}}} -> - emit({indent(3),"fun(Type, Val, _, _) ->",nl, - indent(6),"case Type of",nl}), - emit({indent(9),{asis,Name}," ->",nl}), - emit([indent(12),"'dec_",T,"'(Val, telltype)"]), - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj); - {value,{_,#'Externaltypereference'{module=M,type=T}}} -> - emit({indent(3),"fun(Type, Val, _, _) ->",nl, - indent(6),"case Type of",nl}), - emit({indent(9),{asis,Name}," ->",nl}), - emit([indent(12),"'",M,"':'dec_",T,"'(Val, telltype)"]), - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj); - false -> - emit([indent(3),"fun(Type, Val, _, _) ->",nl, - indent(6),"case Type of",nl, - indent(9),{asis,Name}," ->{Val,Type}"]), - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj) - end; -gen_inlined_dec_funs(Fields,[_|Rest],ObjSetName,NthObj) -> - gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj); -gen_inlined_dec_funs(_,[],_,NthObj) -> +gen_inlined_dec_funs(Fields, List, ObjSetName, NthObj0) -> + emit([indent(3),"fun(Type, Val, _, _) ->",nl, + indent(6),"case Type of",nl]), + NthObj = gen_inlined_dec_funs1(Fields, List, ObjSetName, "", NthObj0), + emit([nl,indent(6),"end",nl, + indent(3),"end"]), NthObj. -gen_inlined_dec_funs1(Fields,[{typefield,Name,_}|Rest], - ObjSetName,NthObj) -> +gen_inlined_dec_funs1(Fields, [{typefield,Name,_}|Rest], + ObjSetName, Sep0, NthObj) -> CurrentMod = get(currmod), InternalDefFunName = [NthObj,Name,ObjSetName], - N=case lists:keysearch(Name,1,Fields) of - {value,{_,Type}} when is_record(Type,type) -> - emit({";",nl}), - emit_inner_of_decfun(Type,InternalDefFunName); - {value,{_,Type}} when is_record(Type,typedef) -> - emit({";",nl,indent(9),{asis,Name}," ->",nl}), - emit_inner_of_decfun(Type,InternalDefFunName); - {value,{_,#'Externaltypereference'{module=CurrentMod,type=T}}} -> - emit([";",nl,indent(9),{asis,Name}," ->",nl]), - emit([indent(12),"'dec_",T,"'(Val,telltype)"]), - 0; - {value,{_,#'Externaltypereference'{module=M,type=T}}} -> - emit([";",nl,indent(9),{asis,Name}," ->",nl]), - emit([indent(12),"'",M,"'",":'dec_",T,"'(Val,telltype)"]), - 0; - false -> - emit([";",nl, - indent(9),{asis,Name}," ->{Val,Type}"]), - 0 - end, - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N); -gen_inlined_dec_funs1(Fields,[_|Rest],ObjSetName,NthObj)-> - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj); -gen_inlined_dec_funs1(_,[],_,NthObj) -> - emit({nl,indent(6),"end",nl}), - emit({indent(3),"end"}), - NthObj. + emit(Sep0), + Sep = [";",nl], + N = case lists:keyfind(Name, 1, Fields) of + {_,#type{}=Type} -> + emit_inner_of_decfun(Type, InternalDefFunName); + {_,#typedef{}=Type} -> + emit([indent(9),{asis,Name}," ->",nl]), + emit_inner_of_decfun(Type, InternalDefFunName); + {_,#'Externaltypereference'{module=CurrentMod,type=T}} -> + emit([indent(9),{asis,Name}," ->",nl, + indent(12),"'dec_",T,"'(Val,telltype)"]), + 0; + {_,#'Externaltypereference'{module=M,type=T}} -> + emit([indent(9),{asis,Name}," ->",nl, + indent(12),"'",M,"':'dec_",T,"'(Val,telltype)"]), + 0; + false -> + emit([indent(9),{asis,Name}," -> {Val,Type}"]), + 0 + end, + gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj+N); +gen_inlined_dec_funs1(Fields, [_|Rest], ObjSetName, Sep, NthObj) -> + gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj); +gen_inlined_dec_funs1(_, [], _, _, NthObj) -> NthObj. emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type}, InternalDefFunName) -> @@ -1147,6 +1100,10 @@ gen_dec_imm(Erule, #type{def=Name,constraint=C}) -> end, gen_dec_imm_1(Name, C, Aligned). +gen_dec_imm_1('ASN1_OPEN_TYPE', Constraint, Aligned) -> + imm_decode_open_type(Constraint, Aligned); +gen_dec_imm_1('ANY', _Constraint, Aligned) -> + imm_decode_open_type([], Aligned); gen_dec_imm_1('BOOLEAN', _Constr, _Aligned) -> asn1ct_imm:per_dec_boolean(); gen_dec_imm_1({'ENUMERATED',{Base,Ext}}, _Constr, Aligned) -> @@ -1239,36 +1196,6 @@ gen_dec_prim_1(Erule, ",",{asis,Constraint},")"}); 'UTF8String' -> emit({"?RT_PER:decode_UTF8String(",BytesVar,")"}); - 'ANY' -> - case Erule of - per -> - emit(["fun() -> {XTerm,YTermXBytes} = ?RT_PER:decode_open_type(",BytesVar,",",{asis,Constraint}, "), {binary_to_list(XTerm),XBytes} end ()"]); - _ -> - emit(["?RT_PER:decode_open_type(",BytesVar,",", - {asis,Constraint}, ")"]) - end; - 'ASN1_OPEN_TYPE' -> - case Constraint of - [#'Externaltypereference'{type=Tname}] -> - emit(["fun(FBytes) ->",nl, - " {XTerm,XBytes} = "]), - emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]), - emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]), - emit([" {YTerm,XBytes} end(",BytesVar,")"]); - [#type{def=#'Externaltypereference'{type=Tname}}] -> - emit(["fun(FBytes) ->",nl, - " {XTerm,XBytes} = "]), - emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]), - emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]), - emit([" {YTerm,XBytes} end(",BytesVar,")"]); - _ -> - case Erule of - per -> - emit(["fun() -> {XTerm,XBytes} = ?RT_PER:decode_open_type(",BytesVar,", []), {binary_to_list(XTerm),XBytes} end()"]); - _ -> - emit(["?RT_PER:decode_open_type(",BytesVar,",[])"]) - end - end; #'ObjectClassFieldType'{} -> case asn1ct_gen:get_inner(Typename) of {fixedtypevaluefield,_,InnerType} -> @@ -1334,3 +1261,22 @@ extaddgroup2sequence([C|T],ExtNum,Acc) -> extaddgroup2sequence(T,ExtNum,[C|Acc]); extaddgroup2sequence([],_,Acc) -> lists:reverse(Acc). + +imm_decode_open_type([#'Externaltypereference'{type=Tname}], Aligned) -> + imm_dec_open_type_1(Tname, Aligned); +imm_decode_open_type([#type{def=#'Externaltypereference'{type=Tname}}], + Aligned) -> + imm_dec_open_type_1(Tname, Aligned); +imm_decode_open_type(_, Aligned) -> + asn1ct_imm:per_dec_open_type(Aligned). + +imm_dec_open_type_1(Type, Aligned) -> + D = fun(OpenType, Buf) -> + asn1ct_name:new(tmpval), + emit(["begin",nl, + "{",{curr,tmpval},",_} = ", + "dec_",Type,"(",OpenType,", mandatory),",nl, + "{",{curr,tmpval},com,Buf,"}",nl, + "end"]) + end, + {call,D,asn1ct_imm:per_dec_open_type(Aligned)}. diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl index ecd212c3e3..4f4563833f 100644 --- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl +++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl @@ -69,18 +69,6 @@ gen_encode(Erules,Typename,Type) when is_record(Type,type) -> end, case asn1ct_gen:type(InnerType) of {constructed,bif} -> - case InnerType of - 'SET' -> - true; - 'SEQUENCE' -> - true; - _ -> - emit({nl,"'enc_",asn1ct_gen:list2name(Typename), - "'({'",asn1ct_gen:list2name(Typename), - "',Val}",ObjFun,") ->",nl}), - emit({"'enc_",asn1ct_gen:list2name(Typename), - "'(Val",ObjFun,");",nl,nl}) - end, emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val",ObjFun, ") ->",nl}), asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type); @@ -417,35 +405,46 @@ emit_enc_octet_string(_Erules,Constraint,Value) -> asn1ct_name:new(tmpval), emit({" begin",nl}), emit({" [",{curr,tmpval},"] = ",Value,",",nl}), - emit({" [10,8,",{curr,tmpval},"]",nl}), + emit([" [[10,8],",{curr,tmpval},"]",nl]), emit(" end"); 2 -> asn1ct_name:new(tmpval), - emit({" begin",nl}), - emit({" [",{curr,tmpval},",",{next,tmpval},"] = ", - Value,",",nl}), - emit({" [[10,8,",{curr,tmpval},"],[10,8,", - {next,tmpval},"]]",nl}), - emit(" end"), - asn1ct_name:new(tmpval); - Sv when is_integer(Sv),Sv =< 256 -> + emit([" begin",nl, + " ",{curr,tmpval}," = ",Value,",",nl, + " case length(",{curr,tmpval},") of",nl, + " 2 ->",nl, + " [[45,16,2]|",{curr,tmpval},"];",nl, + " _ ->",nl, + " exit({error,{value_out_of_bounds,", + {curr,tmpval},"}})",nl, + " end",nl, + " end"]); + Sv when is_integer(Sv), Sv < 256 -> asn1ct_name:new(tmpval), - emit({" begin",nl}), - emit({" case length(",Value,") of",nl}), - emit([" ",{curr,tmpval}," when ",{curr,tmpval}," == ",Sv," ->"]), - emit([" [2,20,",{curr,tmpval},",",Value,"];",nl]), - emit({" _ -> exit({error,{value_out_of_bounds,", - Value,"}})", nl," end",nl}), - emit(" end"); + asn1ct_name:new(tmplen), + emit([" begin",nl, + " ",{curr,tmpval}," = ",Value,",",nl, + " case length(",{curr,tmpval},") of",nl, + " ",Sv,"=",{curr,tmplen}," ->",nl, + " [20,",{curr,tmplen},"|",{curr,tmpval},"];",nl, + " _ ->",nl, + " exit({error,{value_out_of_bounds,", + {curr,tmpval},"}})",nl, + " end",nl, + " end"]); Sv when is_integer(Sv),Sv =< 65535 -> asn1ct_name:new(tmpval), - emit({" begin",nl}), - emit({" case length(",Value,") of",nl}), - emit([" ",{curr,tmpval}," when ",{curr,tmpval}," == ",Sv," ->"]), - emit([" [2,21,",{curr,tmpval},",",Value,"];",nl]), - emit({" _ -> exit({error,{value_out_of_bounds,", - Value,"}})",nl," end",nl}), - emit(" end"); + asn1ct_name:new(tmplen), + emit([" begin",nl, + " ",{curr,tmpval}," = ",Value,",",nl, + " case length(",{curr,tmpval},") of",nl, + " ",Sv,"=",{curr,tmplen}," ->",nl, + " [<<21,",{curr,tmplen},":16>>|",Value,"];",nl, + " _ ->",nl, + " exit({error,{value_out_of_bounds,", + {curr,tmpval},"}})",nl, + " end",nl, + " end"]); C -> emit({" ?RT_PER:encode_octet_string(",{asis,C},",false,",Value,")",nl}) end. @@ -1292,7 +1291,6 @@ gen_objset_dec(ObjSetName,_,['EXTENSIONMARK'],_ClName,_ClFields, _NthObj) -> emit({"'getdec_",ObjSetName,"'(_, _) ->",nl}), emit({indent(3),"fun(Attr1, Bytes, _, _) ->",nl}), - %% emit({indent(6),"?RT_PER:decode_open_type(Bytes,[])",nl}), emit({indent(6),"{Bytes,Attr1}",nl}), emit({indent(3),"end.",nl,nl}), ok; @@ -1308,77 +1306,42 @@ emit_default_getdec(ObjSetName,UniqueName) -> emit([indent(2), "fun(C,V,_,_) -> exit({{component,C},{value,V},{unique_name_and_value,",{asis,UniqueName},",ErrV}}) end"]). -gen_inlined_dec_funs(Fields,[{typefield,Name,_}|Rest], - ObjSetName,NthObj) -> - CurrMod = get(currmod), - InternalDefFunName = [NthObj,Name,ObjSetName], - case lists:keysearch(Name,1,Fields) of - {value,{_,Type}} when is_record(Type,type) -> - emit({indent(3),"fun(Type, Val, _, _) ->",nl, - indent(6),"case Type of",nl}), - N=emit_inner_of_decfun(Type,InternalDefFunName), - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N); - {value,{_,Type}} when is_record(Type,typedef) -> - emit({indent(3),"fun(Type, Val, _, _) ->",nl, - indent(6),"case Type of",nl}), - emit({indent(9),{asis,Name}," ->",nl}), - N=emit_inner_of_decfun(Type,InternalDefFunName), - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N); - {value,{_,#'Externaltypereference'{module=CurrMod,type=T}}} -> - emit({indent(3),"fun(Type, Val, _, _) ->",nl, - indent(6),"case Type of",nl}), - emit({indent(9),{asis,Name}," ->",nl}), - emit([indent(12),"'dec_",T,"'(Val, telltype)"]), - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj); - {value,{_,#'Externaltypereference'{module=M,type=T}}} -> - emit({indent(3),"fun(Type, Val, _, _) ->",nl, - indent(6),"case Type of",nl}), - emit({indent(9),{asis,Name}," ->",nl}), - emit([indent(12),"'",M,"':'dec_",T,"'(Val, telltype)"]), - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj); - false -> - emit([indent(3),"fun(Type, Val, _, _) ->",nl, - indent(6),"case Type of",nl, - indent(9),{asis,Name}," -> {Val,Type}"]), - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj) - end; -gen_inlined_dec_funs(Fields,[_|Rest],ObjSetName,NthObj) -> - gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj); -gen_inlined_dec_funs(_,[],_,NthObj) -> +gen_inlined_dec_funs(Fields, List, ObjSetName, NthObj0) -> + emit([indent(3),"fun(Type, Val, _, _) ->",nl, + indent(6),"case Type of",nl]), + NthObj = gen_inlined_dec_funs1(Fields, List, ObjSetName, "", NthObj0), + emit([nl,indent(6),"end",nl, + indent(3),"end"]), NthObj. -gen_inlined_dec_funs1(Fields,[{typefield,Name,_}|Rest], - ObjSetName,NthObj) -> +gen_inlined_dec_funs1(Fields, [{typefield,Name,_}|Rest], + ObjSetName, Sep0, NthObj) -> CurrentMod = get(currmod), InternalDefFunName = [NthObj,Name,ObjSetName], - N= - case lists:keysearch(Name,1,Fields) of - {value,{_,Type}} when is_record(Type,type) -> - emit({";",nl}), - emit_inner_of_decfun(Type,InternalDefFunName); - {value,{_,Type}} when is_record(Type,typedef) -> - emit({";",nl,indent(9),{asis,Name}," ->",nl}), - emit_inner_of_decfun(Type,InternalDefFunName); - {value,{_,#'Externaltypereference'{module=CurrentMod,type=T}}} -> - emit([";",nl,indent(9),{asis,Name}," ->",nl]), - emit([indent(12),"'dec_",T,"'(Val,telltype)"]), - 0; - {value,{_,#'Externaltypereference'{module=M,type=T}}} -> - emit([";",nl,indent(9),{asis,Name}," ->",nl]), - emit([indent(12),"'",M,"':'dec_",T,"'(Val,telltype)"]), - 0; - false -> - emit([";",nl, - indent(9),{asis,Name}," -> {Val,Type}"]), - 0 - end, - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N); -gen_inlined_dec_funs1(Fields,[_|Rest],ObjSetName,NthObj)-> - gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj); -gen_inlined_dec_funs1(_,[],_,NthObj) -> - emit({nl,indent(6),"end",nl}), - emit({indent(3),"end"}), - NthObj. + emit(Sep0), + Sep = [";",nl], + N = case lists:keyfind(Name, 1, Fields) of + {_,#type{}=Type} -> + emit_inner_of_decfun(Type, InternalDefFunName); + {_,#typedef{}=Type} -> + emit([indent(9),{asis,Name}," ->",nl]), + emit_inner_of_decfun(Type, InternalDefFunName); + {_,#'Externaltypereference'{module=CurrentMod,type=T}} -> + emit([indent(9),{asis,Name}," ->",nl, + indent(12),"'dec_",T,"'(Val,telltype)"]), + 0; + {_,#'Externaltypereference'{module=M,type=T}} -> + emit([indent(9),{asis,Name}," ->",nl, + indent(12),"'",M,"':'dec_",T,"'(Val,telltype)"]), + 0; + false -> + emit([indent(9),{asis,Name}," -> {Val,Type}"]), + 0 + end, + gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj+N); +gen_inlined_dec_funs1(Fields, [_|Rest], ObjSetName, Sep, NthObj) -> + gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj); +gen_inlined_dec_funs1(_, [], _, _, NthObj) -> NthObj. emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type}, InternalDefFunName) -> @@ -1579,25 +1542,9 @@ gen_dec_prim(Erules,Att,BytesVar) -> 'UTF8String' -> emit({"?RT_PER:decode_UTF8String(",BytesVar,")"}); 'ANY' -> - emit(["?RT_PER:decode_open_type(",BytesVar,",", - {asis,Constraint}, ")"]); + asn1ct_gen_per:gen_dec_prim(Erules, Att, BytesVar); 'ASN1_OPEN_TYPE' -> - case Constraint of - [#'Externaltypereference'{type=Tname}] -> - emit(["fun(FBytes) ->",nl, - " {XTerm,XBytes} = "]), - emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]), - emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]), - emit([" {YTerm,XBytes} end(",BytesVar,")"]); - [#type{def=#'Externaltypereference'{type=Tname}}] -> - emit(["fun(FBytes) ->",nl, - " {XTerm,XBytes} = "]), - emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]), - emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]), - emit([" {YTerm,XBytes} end(",BytesVar,")"]); - _ -> - emit(["?RT_PER:decode_open_type(",BytesVar,",[])"]) - end; + asn1ct_gen_per:gen_dec_prim(Erules, Att, BytesVar); #'ObjectClassFieldType'{} -> case asn1ct_gen:get_inner(Att#type.def) of {fixedtypevaluefield,_,InnerType} -> diff --git a/lib/asn1/src/asn1ct_parser2.erl b/lib/asn1/src/asn1ct_parser2.erl index 7301f49085..9e1fcce2b1 100644 --- a/lib/asn1/src/asn1ct_parser2.erl +++ b/lib/asn1/src/asn1ct_parser2.erl @@ -924,19 +924,8 @@ parse_UnionsRec([{'|',_}|Rest]) -> {V1,V2} -> {[V1,union,V2],Rest3} end; -parse_UnionsRec([{'UNION',_}|Rest]) -> - {InterSec,Rest2} = parse_Intersections(Rest), - {URec,Rest3} = parse_UnionsRec(Rest2), - case {InterSec,URec} of - {V1,[]} -> - {V1,Rest3}; - {{'SingleValue',V1},{'SingleValue',V2}} -> - {{'SingleValue',ordsets:union(to_set(V1),to_set(V2))},Rest3}; - {V1,V2} when is_list(V2) -> - {[V1] ++ [union|V2],Rest3}; - {V1,V2} -> - {[V1,union,V2],Rest3} - end; +parse_UnionsRec([{'UNION',Info}|Rest]) -> + parse_UnionsRec([{'|',Info}|Rest]); parse_UnionsRec(Tokens) -> {[],Tokens}. @@ -971,20 +960,8 @@ parse_IElemsRec([{'^',_}|Rest]) -> {V1,V2} -> {[V1,intersection,V2],Rest3} end; -parse_IElemsRec([{'INTERSECTION',_}|Rest]) -> - {InterSec,Rest2} = parse_IntersectionElements(Rest), - {IRec,Rest3} = parse_IElemsRec(Rest2), - case {InterSec,IRec} of - {{'SingleValue',V1},{'SingleValue',V2}} -> - {{'SingleValue', - ordsets:intersection(to_set(V1),to_set(V2))},Rest3}; - {V1,[]} -> - {V1,Rest3}; - {V1,V2} when is_list(V2) -> - {[V1] ++ [intersection|V2],Rest3}; - {V1,V2} -> - {[V1,intersection,V2],Rest3} - end; +parse_IElemsRec([{'INTERSECTION',Info}|Rest]) -> + parse_IElemsRec([{'^',Info}|Rest]); parse_IElemsRec(Tokens) -> {[],Tokens}. diff --git a/lib/asn1/src/asn1rt_uper_bin.erl b/lib/asn1/src/asn1rt_uper_bin.erl index 9410c3ef90..fc65d80245 100644 --- a/lib/asn1/src/asn1rt_uper_bin.erl +++ b/lib/asn1/src/asn1rt_uper_bin.erl @@ -120,14 +120,15 @@ fixextensions(Pos,ExtPos,Val,Acc) -> end, fixextensions(Pos+1,ExtPos,Val,(Acc bsl 1)+Bit). -skipextensions(Bytes,Nr,ExtensionBitPattern) -> - case (catch element(Nr,ExtensionBitPattern)) of - 1 -> +skipextensions(Bytes,Nr,ExtensionBitstr) when is_bitstring(ExtensionBitstr) -> + Prev = Nr - 1, + case ExtensionBitstr of + <<_:Prev,1:1,_/bitstring>> -> {_,Bytes2} = decode_open_type(Bytes,[]), - skipextensions(Bytes2, Nr+1, ExtensionBitPattern); - 0 -> - skipextensions(Bytes, Nr+1, ExtensionBitPattern); - {'EXIT',_} -> % badarg, no more extensions + skipextensions(Bytes2, Nr+1, ExtensionBitstr); + <<_:Prev,0:1,_/bitstring>> -> + skipextensions(Bytes, Nr+1, ExtensionBitstr); + _ -> Bytes end. diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 38ec72c473..325293f35d 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -20,19 +20,10 @@ -module(asn1_SUITE). --define(only_per(Func), - if Rule =:= per -> Func; - true -> ok - end). -define(only_ber(Func), if Rule =:= ber -> Func; true -> ok end). --define(only_uper(Func), - case Rule of - uper -> Func; - _ -> ok - end). -compile(export_all). @@ -62,7 +53,8 @@ groups() -> [{compile, parallel([]), [c_syntax, c_string, - c_implicit_before_choice]}, + c_implicit_before_choice, + constraint_equivalence]}, {ber, parallel([]), [ber_choiceinseq, @@ -188,7 +180,6 @@ groups() -> testDoubleEllipses, test_x691, ticket_6143, - testExtensionAdditionGroup, test_OTP_9688]}, {performance, [], @@ -321,12 +312,12 @@ testCompactBitString(Config, Rule, Opts) -> asn1_test_lib:compile("PrimStrings", Config, [Rule, compact_bit_string|Opts]), testCompactBitString:compact_bit_string(Rule), - ?only_uper(testCompactBitString:bit_string_unnamed(Rule)), - ?only_per(testCompactBitString:bit_string_unnamed(Rule)), - ?only_per(testCompactBitString:ticket_7734(Rule)), - ?only_per(asn1_test_lib:compile("Constraints", Config, - [Rule, compact_bit_string|Opts])), - ?only_per(testCompactBitString:otp_4869(Rule)). + testCompactBitString:bit_string_unnamed(Rule), + testCompactBitString:bit_string_unnamed(Rule), + testCompactBitString:ticket_7734(Rule), + asn1_test_lib:compile("Constraints", Config, + [Rule, compact_bit_string|Opts]), + testCompactBitString:otp_4869(Rule). testPrimStrings(Config) -> test(Config, fun testPrimStrings/3). testPrimStrings(Config, Rule, Opts) -> @@ -439,7 +430,8 @@ testSeqExtension(Config) -> test(Config, fun testSeqExtension/3). testSeqExtension(Config, Rule, Opts) -> asn1_test_lib:compile_all(["External", "SeqExtension"], Config, [Rule|Opts]), - testSeqExtension:main(Rule). + DataDir = ?config(data_dir, Config), + testSeqExtension:main(DataDir, [Rule|Opts]). testSeqExternal(Config) -> test(Config, fun testSeqExternal/3). testSeqExternal(Config, Rule, Opts) -> @@ -627,6 +619,34 @@ c_implicit_before_choice(Config, Rule, Opts) -> {error, _R2} = asn1ct:compile(filename:join(DataDir, "CCSNARG3"), [Rule, {outdir, CaseDir}|Opts]). +constraint_equivalence(Config) -> + DataDir = ?config(data_dir, Config), + CaseDir = ?config(case_dir, Config), + Asn1Spec = "ConstraintEquivalence", + Asn1Src = filename:join(DataDir, Asn1Spec), + ok = asn1ct:compile(Asn1Src, [abs,{outdir,CaseDir}]), + AbsFile = filename:join(CaseDir, Asn1Spec++".abs"), + {ok,Terms} = file:consult(AbsFile), + Cs = [begin + 'INTEGER' = element(3, Type), %Assertion. + Constraints = element(4, Type), + Name1 = atom_to_list(Name0), + {Name,_} = lists:splitwith(fun(C) -> C =/= $X end, Name1), + {Name,Constraints} + end || {typedef,_,_,Name0,Type} <- Terms], + R = sofs:relation(Cs, [{name,constraint}]), + F0 = sofs:relation_to_family(R), + F = sofs:to_external(F0), + Diff = [E || {_,L}=E <- F, length(L) > 1], + case Diff of + [] -> + ok; + [_|_] -> + io:put_chars("Not equivalent:\n"), + [io:format("~s: ~p\n", [N,D]) || {N,D} <- Diff], + test_server:fail(length(Diff)) + end. + parse(Config) -> [asn1_test_lib:compile(M, Config, [abs]) || M <- test_modules()]. @@ -1063,6 +1083,7 @@ testExtensionAdditionGroup(Config, Rule, Opts) -> [debug_info]), extensionAdditionGroup:run([Rule|Opts]), extensionAdditionGroup:run2([Rule|Opts]), + extensionAdditionGroup:run3(), asn1_test_lib:compile("EUTRA-RRC-Definitions", Config, [Rule, {record_name_prefix, "RRC-"}|Opts]), extensionAdditionGroup:run3([Rule|Opts]). diff --git a/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1 b/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1 new file mode 100644 index 0000000000..6a97c1b38e --- /dev/null +++ b/lib/asn1/test/asn1_SUITE_data/ConstraintEquivalence.asn1 @@ -0,0 +1,42 @@ +ConstraintEquivalence DEFINITIONS AUTOMATIC TAGS ::= +BEGIN + SingleValueX42 ::= INTEGER (42) + SingleValueX1 ::= INTEGER ((42) ^ (42)) + SingleValueX2 ::= INTEGER ((42) INTERSECTION (42)) + SingleValueX3 ::= INTEGER ((42) | (42)) + SingleValueX4 ::= INTEGER ((42) UNION (42)) + SingleValueX5 ::= INTEGER ((42) INTERSECTION (MIN..MAX)) + SingleValueX6 ::= INTEGER ((42) INTERSECTION (40..49)) + SingleValueX7 ::= INTEGER (42..42) + + UnconstrainedX0 ::= INTEGER + UnconstrainedX1 ::= INTEGER (MIN..MAX) + UnconstrainedX2 ::= INTEGER (1|(MIN..MAX)) + UnconstrainedX3 ::= INTEGER (1..10|(MIN..MAX)) + UnconstrainedX4 ::= INTEGER ((MIN..MAX)|9|10) + UnconstrainedX5 ::= INTEGER ((MIN..MAX)|10..20) + UnconstrainedX6 ::= INTEGER ((MIN..MAX) UNION (10..20)) + + RangeX00 ::= INTEGER (5..10) + RangeX01 ::= INTEGER (4<..<11) + RangeX02 ::= INTEGER (5..<11) + RangeX03 ::= INTEGER (4<..10) + RangeX04 ::= INTEGER (5|6|7|8|9|10) + RangeX05 ::= INTEGER (10|9|8|7|6|5) + RangeX06 ::= INTEGER (5|6|7..10) + + RangeX10 ::= INTEGER ((5..6) UNION (7..8) UNION (9|10)) + RangeX11 ::= INTEGER ((5|6) UNION (7..8) UNION (9|10)) + RangeX12 ::= INTEGER ((5|6) UNION (7|8) UNION (9|10)) + RangeX13 ::= INTEGER ((5|6) UNION (7) UNION (8..10)) + RangeX14 ::= INTEGER ((5|6) UNION (7) UNION (8..10)) + RangeX15 ::= INTEGER ((5|6) UNION (7) UNION ((8..8)|(9..9)|(10))) + RangeX16 ::= INTEGER ((5|6) UNION (7) UNION (7<..<11)) + + RangeX20 ::= INTEGER (0..20) (5..10) + RangeX21 ::= INTEGER (0..10) (5..20) + RangeX22 ::= INTEGER (0..10) (5..20) (MIN..MAX) + RangeX23 ::= INTEGER ((0..10) INTERSECTION (5..20) ^ (MIN..MAX)) + RangeX24 ::= INTEGER ((5|6|7|8|9|10) INTERSECTION (5..20) ^ (MIN..MAX)) + +END diff --git a/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn b/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn index 55124f9449..b7cc74ab07 100644 --- a/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn +++ b/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn @@ -95,6 +95,27 @@ AS-Config ::= SEQUENCE { ]] } +SystemInformationBlockType2 ::= SEQUENCE { + timeAlignmentTimerCommon TimeAlignmentTimer, + ..., + lateNonCriticalExtension OCTET STRING OPTIONAL, + [[ ssac-BarringForMMTEL-Voice-r9 AC-BarringConfig OPTIONAL, + ssac-BarringForMMTEL-Video-r9 AC-BarringConfig OPTIONAL + ]], + [[ ac-BarringForCSFB-r10 AC-BarringConfig OPTIONAL + ]] +} + +TimeAlignmentTimer ::= ENUMERATED { + sf500, sf750, sf1280, sf1920, sf2560, sf5120, + sf10240, infinity} +AC-BarringConfig ::= SEQUENCE { + ac-BarringFactor ENUMERATED { + p00, p05, p10, p15, p20, p25, p30, p40, + p50, p60, p70, p75, p80, p85, p90, p95}, + ac-BarringTime ENUMERATED {s4, s8, s16, s32, s64, s128, s256, s512}, + ac-BarringForSpecialAC BIT STRING (SIZE(5)) +} END diff --git a/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 b/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 index 7cb47e9792..9b6b34a776 100644 --- a/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/PrimStrings.asn1 @@ -59,6 +59,19 @@ BS1024 ::= BIT STRING (SIZE (1024)) FixedOs65536 ::= OCTET STRING (SIZE (65536)) FixedOs65537 ::= OCTET STRING (SIZE (65537)) + OsFixedStrings ::= SEQUENCE { + b1 BOOLEAN, -- Unalign + s0 OCTET STRING (SIZE (0)), + s1 OCTET STRING (SIZE (1)), + s2 OCTET STRING (SIZE (2)), + s3 OCTET STRING (SIZE (3)), + b2 BOOLEAN, -- Unalign + s255 OCTET STRING (SIZE (255)), + s256 OCTET STRING (SIZE (256)), + s257 OCTET STRING (SIZE (257)), + i INTEGER (0..1024) + } + OsAlignment ::= SEQUENCE { b1 BOOLEAN, s1 Os, diff --git a/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1 b/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1 index bb0a7cca3a..5fda19303a 100644 --- a/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/SeqExtension.asn1 @@ -31,7 +31,35 @@ SeqExt4 ::= SEQUENCE int INTEGER } +SeqExt5 ::= SEQUENCE +{ + ..., + [[ name OCTET STRING (SIZE (1..8)), + shoesize INTEGER ]] +} + +SeqExt6 ::= SEQUENCE +{ + -- The spaces between the ellipsis and the comma will prevent them + -- from being removed. + ... , + [[ i1 [100] INTEGER, i2 [101] INTEGER, i3 [102] INTEGER ]], + [[ i4 [104] INTEGER, i5 [105] INTEGER ]], + [[ i6 [106] INTEGER, i7 [107] INTEGER ]] +} + SeqExt1X ::= XSeqExt1 SeqExt2X ::= XSeqExt2 +SuperSeq ::= SEQUENCE +{ + s1 SeqExt1, + s2 SeqExt2, + s3 SeqExt3, + s4 SeqExt4, + s5 SeqExt5, + s6 SeqExt6, + i INTEGER +} + END diff --git a/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl index 5fcec23756..8148381d92 100644 --- a/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl +++ b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl @@ -130,3 +130,26 @@ run3(Erule) -> _ -> exit({expected,Val, got, Val2}) end. +run3() -> + SI = #'SystemInformationBlockType2'{ + timeAlignmentTimerCommon = sf500, + lateNonCriticalExtension = asn1_NOVALUE, + 'ssac-BarringForMMTEL-Voice-r9' = asn1_NOVALUE, + 'ssac-BarringForMMTEL-Video-r9' = asn1_NOVALUE, + 'ac-BarringForCSFB-r10' = asn1_NOVALUE}, + Barring = #'AC-BarringConfig'{ + 'ac-BarringFactor' = p00, + 'ac-BarringTime' = s4, + 'ac-BarringForSpecialAC' = [0,0,0,0,0]}, + roundtrip(SI), + roundtrip(SI#'SystemInformationBlockType2'{ + 'ssac-BarringForMMTEL-Voice-r9'=Barring}), + roundtrip(SI#'SystemInformationBlockType2'{ + 'ssac-BarringForMMTEL-Video-r9'=Barring}), + roundtrip(SI#'SystemInformationBlockType2'{ + 'ac-BarringForCSFB-r10'=Barring}). + +roundtrip(V) -> + Mod = 'Extension-Addition-Group', + {ok,E} = Mod:encode('SystemInformationBlockType2', V), + {ok,V} = Mod:decode('SystemInformationBlockType2', iolist_to_binary(E)). diff --git a/lib/asn1/test/asn1_SUITE_data/testobj.erl b/lib/asn1/test/asn1_SUITE_data/testobj.erl index 80942f7e38..d9f60ca8a3 100644 --- a/lib/asn1/test/asn1_SUITE_data/testobj.erl +++ b/lib/asn1/test/asn1_SUITE_data/testobj.erl @@ -883,7 +883,7 @@ initial_ue_ies() -> cn_domain_indicator() -> - {'CN-DomainIndicator', 'ps-domain'}. + 'ps-domain'. init_lai() -> #'ProtocolIE-Field'{ @@ -1279,11 +1279,11 @@ reset() -> protocolIEs = reset_ies() }. reset_ies() -> - {'Reset_protocolIEs', % this identifier is very unneccesary here - [reset_cause(), - cn_domain_ind(), % Se initial Ue - init_global_rnc_id() % ---- " ---- - ]}. + [reset_cause(), + cn_domain_ind(), % Se initial Ue + init_global_rnc_id() % ---- " ---- + ]. + init_global_rnc_id() -> #'ProtocolIE-Field'{ id = 86, % 86 = id-GlobalRNC-ID @@ -1323,8 +1323,7 @@ reset_ack() -> protocolIEs = reset_ack_ies() }. reset_ack_ies() -> - {'ResetAcknowledge_protocolIEs', % very unneccesary - [cn_domain_ind()]}. % Se initial Ue + [cn_domain_ind()]. % Se initial Ue @@ -1336,13 +1335,12 @@ reset_res(IuSCId) -> }. reset_res_ies(IuSCId) -> - {'ResetResource_protocolIEs', % very unneccesary - [ - cn_domain_ind() % Se initial Ue - ,reset_cause() % Se reset - ,reset_res_list(IuSCId) - ,init_global_rnc_id_reset_res() % ---- " ---- - ]}. + [ + cn_domain_ind() % Se initial Ue + ,reset_cause() % Se reset + ,reset_res_list(IuSCId) + ,init_global_rnc_id_reset_res() % ---- " ---- + ]. init_global_rnc_id_reset_res() -> #'ProtocolIE-Field'{ diff --git a/lib/asn1/test/testChoExtension.erl b/lib/asn1/test/testChoExtension.erl index b75cfb6831..067d4d2bf7 100644 --- a/lib/asn1/test/testChoExtension.erl +++ b/lib/asn1/test/testChoExtension.erl @@ -25,42 +25,27 @@ extension(_Rules) -> - - ?line {ok,Bytes1} = asn1_wrapper:encode('ChoExtension','ChoExt1',{'ChoExt1',{bool,true}}), - ?line {ok,{bool,true}} = - asn1_wrapper:decode('ChoExtension','ChoExt1',lists:flatten(Bytes1)), - - ?line {ok,Bytes2} = asn1_wrapper:encode('ChoExtension','ChoExt1',{'ChoExt1',{int,33}}), - ?line {ok,{int,33}} = - asn1_wrapper:decode('ChoExtension','ChoExt1',lists:flatten(Bytes2)), + roundtrip('ChoExt1', {bool,true}), + roundtrip('ChoExt1', {int,33}), %% A trick to encode with another compatible CHOICE type to test reception %% extension alternative - ?line {ok,Bytes2x} = asn1_wrapper:encode('ChoExtension','ChoExt1x',{str,"abc"}), - ?line {ok,Val2x} = + {ok,Bytes2x} = asn1_wrapper:encode('ChoExtension','ChoExt1x',{str,"abc"}), + {ok,Val2x} = asn1_wrapper:decode('ChoExtension','ChoExt1',lists:flatten(Bytes2x)), io:format("Choice extension alternative = ~p~n",[Val2x]), - ?line {ok,Bytes3} = asn1_wrapper:encode('ChoExtension','ChoExt2',{'ChoExt2',{bool,true}}), - ?line {ok,{bool,true}} = - asn1_wrapper:decode('ChoExtension','ChoExt2',lists:flatten(Bytes3)), - - ?line {ok,Bytes4} = asn1_wrapper:encode('ChoExtension','ChoExt2',{'ChoExt2',{int,33}}), - ?line {ok,{int,33}} = - asn1_wrapper:decode('ChoExtension','ChoExt2',lists:flatten(Bytes4)), + roundtrip('ChoExt2', {bool,true}), + roundtrip('ChoExt2', {int,33}), + roundtrip('ChoExt3', {bool,true}), + roundtrip('ChoExt3', {int,33}), + roundtrip('ChoExt4', {str,"abc"}), - ?line {ok,Bytes5} = asn1_wrapper:encode('ChoExtension','ChoExt3',{'ChoExt3',{bool,true}}), - ?line {ok,{bool,true}} = - asn1_wrapper:decode('ChoExtension','ChoExt3',lists:flatten(Bytes5)), - - ?line {ok,Bytes6} = asn1_wrapper:encode('ChoExtension','ChoExt3',{'ChoExt3',{int,33}}), - ?line {ok,{int,33}} = - asn1_wrapper:decode('ChoExtension','ChoExt3',lists:flatten(Bytes6)), - - Val7 = {str,"abc"}, - ?line {ok,Bytes7} = asn1_wrapper:encode('ChoExtension','ChoExt4',Val7), - ?line {ok,Val7} = asn1_wrapper:decode('ChoExtension','ChoExt4',lists:flatten(Bytes7)), + ok. +roundtrip(Type, Value) -> + {ok,Encoded} = 'ChoExtension':encode(Type, Value), + {ok,Value} = 'ChoExtension':decode(Type, Encoded), ok. diff --git a/lib/asn1/test/testChoExternal.erl b/lib/asn1/test/testChoExternal.erl index b2d171f9c7..5fdee48add 100644 --- a/lib/asn1/test/testChoExternal.erl +++ b/lib/asn1/test/testChoExternal.erl @@ -38,62 +38,27 @@ compile(Config, Rules, Optimize) -> external(_Rules) -> + roundtrip('ChoXCho', {boolCho,true}), + roundtrip('ChoXCho', {intCho,77}), - ?line {ok,Bytes11} = asn1_wrapper:encode('ChoExternal','ChoXCho',{'ChoXCho',{boolCho,true}}), - ?line {ok,{boolCho,true}} = asn1_wrapper:decode('ChoExternal','ChoXCho',lists:flatten(Bytes11)), - - - ?line {ok,Bytes12} = asn1_wrapper:encode('ChoExternal','ChoXCho',{'ChoXCho',{intCho,77}}), - ?line {ok,{intCho,77}} = asn1_wrapper:decode('ChoExternal','ChoXCho',lists:flatten(Bytes12)), - - - - ?line {ok,Bytes21} = asn1_wrapper:encode('ChoExternal','ChoXBool',{'ChoXBool',{xbool,true}}), - ?line {ok,{xbool,true}} = asn1_wrapper:decode('ChoExternal','ChoXBool',lists:flatten(Bytes21)), - - - ?line {ok,Bytes22} = asn1_wrapper:encode('ChoExternal','ChoXBool',{'ChoXBool',{xboolImp,true}}), - ?line {ok,{xboolImp,true}} = asn1_wrapper:decode('ChoExternal','ChoXBool',lists:flatten(Bytes22)), - - - ?line {ok,Bytes23} = asn1_wrapper:encode('ChoExternal','ChoXBool',{'ChoXBool',{xboolExp,true}}), - ?line {ok,{xboolExp,true}} = asn1_wrapper:decode('ChoExternal','ChoXBool',lists:flatten(Bytes23)), - - - - ?line {ok,Bytes31} = asn1_wrapper:encode('ChoExternal','NT',{os,"kalle"}), - ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','NT',lists:flatten(Bytes31)), - - ?line {ok,Bytes32} = asn1_wrapper:encode('ChoExternal','Exp',{os,"kalle"}), - ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','Exp',lists:flatten(Bytes32)), - - ?line {ok,Bytes33} = asn1_wrapper:encode('ChoExternal','NTNT',{os,"kalle"}), - ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','NTNT',lists:flatten(Bytes33)), - - ?line {ok,Bytes34} = asn1_wrapper:encode('ChoExternal','NTExp',{os,"kalle"}), - ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','NTExp',lists:flatten(Bytes34)), - - ?line {ok,Bytes35} = asn1_wrapper:encode('ChoExternal','ExpNT',{os,"kalle"}), - ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','ExpNT',lists:flatten(Bytes35)), - - ?line {ok,Bytes36} = asn1_wrapper:encode('ChoExternal','ExpExp',{os,"kalle"}), - ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','ExpExp',lists:flatten(Bytes36)), - - - - - - ?line {ok,Bytes41} = asn1_wrapper:encode('ChoExternal','XNTNT',{os,"kalle"}), - ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','XNTNT',lists:flatten(Bytes41)), - - ?line {ok,Bytes42} = asn1_wrapper:encode('ChoExternal','XNTExp',{os,"kalle"}), - ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','XNTExp',lists:flatten(Bytes42)), - - ?line {ok,Bytes43} = asn1_wrapper:encode('ChoExternal','XExpNT',{os,"kalle"}), - ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','XExpNT',lists:flatten(Bytes43)), - - ?line {ok,Bytes44} = asn1_wrapper:encode('ChoExternal','XExpExp',{os,"kalle"}), - ?line {ok,{os,"kalle"}} = asn1_wrapper:decode('ChoExternal','XExpExp',lists:flatten(Bytes44)), + roundtrip('ChoXBool', {xbool,true}), + roundtrip('ChoXBool', {xboolImp,true}), + roundtrip('ChoXBool', {xboolExp,true}), + + roundtrip('NT', {os,"kalle"}), + roundtrip('Exp', {os,"kalle"}), + roundtrip('NTNT', {os,"kalle"}), + roundtrip('NTExp', {os,"kalle"}), + roundtrip('ExpNT', {os,"kalle"}), + roundtrip('ExpExp', {os,"kalle"}), + roundtrip('XNTNT', {os,"kalle"}), + roundtrip('XNTExp', {os,"kalle"}), + roundtrip('XExpNT', {os,"kalle"}), + roundtrip('XExpExp', {os,"kalle"}), ok. +roundtrip(Type, Value) -> + {ok,Encoded} = 'ChoExternal':encode(Type, Value), + {ok,Value} = 'ChoExternal':decode(Type, Encoded), + ok. diff --git a/lib/asn1/test/testChoRecursive.erl b/lib/asn1/test/testChoRecursive.erl index 22be26cbce..ee26d124a9 100644 --- a/lib/asn1/test/testChoRecursive.erl +++ b/lib/asn1/test/testChoRecursive.erl @@ -28,38 +28,21 @@ -record('ChoRec2_something',{a, b, c}). recursive(_Rules) -> - - ?line {ok,Bytes11} = asn1_wrapper:encode('ChoRecursive','ChoRec',{'ChoRec',{something, - #'ChoRec_something'{a = 77, - b = "some octets here", - c = {'ChoRec',{nothing,'NULL'}}}}}), - ?line {ok,{something,{'ChoRec_something',77,"some octets here",{nothing,'NULL'}}}} = - asn1_wrapper:decode('ChoRecursive','ChoRec',lists:flatten(Bytes11)), - - - ?line {ok,Bytes12} = asn1_wrapper:encode('ChoRecursive','ChoRec',{'ChoRec',{nothing,'NULL'}}), - ?line {ok,{nothing,'NULL'}} = - asn1_wrapper:decode('ChoRecursive','ChoRec',lists:flatten(Bytes12)), - - - - ?line {ok,Bytes21} = - asn1_wrapper:encode('ChoRecursive','ChoRec2',{'ChoRec2', - {something, - #'ChoRec2_something'{a = 77, - b = "some octets here", - c = {'ChoRec2', - {nothing,'NULL'}}}}}), - ?line {ok,{something,{'ChoRec2_something',77,"some octets here",{nothing,'NULL'}}}} = - asn1_wrapper:decode('ChoRecursive','ChoRec2',lists:flatten(Bytes21)), - - - ?line {ok,Bytes22} = - asn1_wrapper:encode('ChoRecursive','ChoRec2',{'ChoRec2',{nothing,'NULL'}}), - ?line {ok,{nothing,'NULL'}} = - asn1_wrapper:decode('ChoRecursive','ChoRec2',lists:flatten(Bytes22)), - - - - + roundtrip('ChoRec', + {something, + #'ChoRec_something'{a = 77, + b = "some octets here", + c = {nothing,'NULL'}}}), + roundtrip('ChoRec', {nothing,'NULL'}), + roundtrip('ChoRec2', + {something, + #'ChoRec2_something'{a = 77, + b = "some octets here", + c = {nothing,'NULL'}}}), + roundtrip('ChoRec2', {nothing,'NULL'}), + ok. + +roundtrip(Type, Value) -> + {ok,Encoded} = 'ChoRecursive':encode(Type, Value), + {ok,Value} = 'ChoRecursive':decode(Type, Encoded), ok. diff --git a/lib/asn1/test/testCompactBitString.erl b/lib/asn1/test/testCompactBitString.erl index db102a3bda..96d9f0fdcb 100644 --- a/lib/asn1/test/testCompactBitString.erl +++ b/lib/asn1/test/testCompactBitString.erl @@ -22,240 +22,132 @@ -export([compact_bit_string/1, bit_string_unnamed/1,otp_4869/1, ticket_7734/1]). --include_lib("test_server/include/test_server.hrl"). - compact_bit_string(Rules) -> %%========================================================== %% Bs1 ::= BIT STRING %%========================================================== - ?line {ok,Bytes1} = asn1_wrapper:encode('PrimStrings','Bs1',0), - ?line {ok,{0,<<>>}} = - asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes1)), - - ?line {ok,Bytes2} = asn1_wrapper:encode('PrimStrings','Bs1',4), - ?line {ok,{5,<<32>>}} = - asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes2)), - - ?line {ok,Bytes3} = asn1_wrapper:encode('PrimStrings','Bs1',15), - ?line {ok,{4,<<240>>}} = - asn1_wrapper:decode('PrimStrings','Bs1', - lists:flatten(Bytes3)), - - ?line {ok,Bytes4} = asn1_wrapper:encode('PrimStrings','Bs1',255), - ?line {ok,{0,<<255>>}} = - asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes4)), - - ?line {ok,Bytes5} = asn1_wrapper:encode('PrimStrings','Bs1',256), - ?line {ok,{7,<<0,128>>}} = - asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes5)), - - ?line {ok,Bytes6} = asn1_wrapper:encode('PrimStrings','Bs1',257), - ?line {ok,{7,<<128,128>>}} = - asn1_wrapper:decode('PrimStrings','Bs1', - lists:flatten(Bytes6)), - - ?line {ok,Bytes7} = asn1_wrapper:encode('PrimStrings','Bs1',444), - ?line {ok,{7,<<61,128>>}} = - asn1_wrapper:decode('PrimStrings','Bs1', - lists:flatten(Bytes7)), - - ?line {ok,Bytes8} = asn1_wrapper:encode('PrimStrings','Bs1', - 12345678901234567890), - ?line {ok,_} = asn1_wrapper:decode('PrimStrings','Bs1', - lists:flatten(Bytes8)), - -%% Removed due to beam cannot handle this big integers -%% Bs1_1 = 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, -%% ?line {ok,Bytes9} = asn1_wrapper:encode('PrimStrings','Bs1',Bs1_1), -%% ?line {ok,_} = asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes9)), - -%% Bs1_2 = 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, -%% ?line {ok,Bytes10} = asn1_wrapper:encode('PrimStrings','Bs1',Bs1_2), -%% ?line {ok,_} = asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes10)), - - ?line {ok,Bytes11} = asn1_wrapper:encode('PrimStrings','Bs1', - [1,1,1,1,1,1,1,1]), - ?line {ok,{0,<<255>>}} = asn1_wrapper:decode('PrimStrings','Bs1', - lists:flatten(Bytes11)), - - ?line {ok,Bytes12} = asn1_wrapper:encode('PrimStrings', - 'Bs1', - [0,1,0,0,1,0]), - ?line {ok,{2,<<72>>}} = - asn1_wrapper:decode('PrimStrings','Bs1', - lists:flatten(Bytes12)), - - ?line {ok,Bytes13} = - asn1_wrapper:encode('PrimStrings', 'Bs1', - [1,0,0,0,0,0,0,0,0]), - ?line {ok,{7,<<128,0>>}} = - asn1_wrapper:decode('PrimStrings','Bs1', - lists:flatten(Bytes13)), - - - ?line {ok,Bytes14} = - asn1_wrapper:encode('PrimStrings','Bs1', - [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]), - ?line {ok,{5,<<75,226,96>>}} = - asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes14)), - - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line Bytes15 = [35,8,3,2,0,73,3,2,4,32], - ?line {ok,{4,<<73,32>>}} = - asn1_wrapper:decode('PrimStrings','Bs1', - lists:flatten(Bytes15)), - - ?line Bytes16 = [35,9,3,2,0,234,3,3,7,156,0], - ?line {ok,{7,<<234,156,0>>}} = - asn1_wrapper:decode('PrimStrings','Bs1', - lists:flatten(Bytes16)), - - ?line Bytes17 = [35,128,3,2,0,73,3,2,4,32,0,0], - ?line {ok,{4,<<73,32>>}} = - asn1_wrapper:decode('PrimStrings','Bs1', - lists:flatten(Bytes17)), - - ?line Bytes18 = [35,128,3,2,0,234,3,3,7,156,0,0,0], - ?line {ok,{7,<<234,156,0>>}} = - asn1_wrapper:decode('PrimStrings','Bs1', - lists:flatten(Bytes18)), - ok; - - per -> - ok - end, + roundtrip('Bs1', 0, {0,<<>>}), + roundtrip('Bs1', 4, {5,<<2#00100000>>}), + roundtrip('Bs1', 15, {4,<<2#11110000>>}), + roundtrip('Bs1', 255, {0,<<2#11111111>>}), + roundtrip('Bs1', 256, {7,<<16#00,16#80>>}), + roundtrip('Bs1', 257, {7,<<16#80,16#80>>}), + roundtrip('Bs1', 444, {7,<<16#3D,16#80>>}), + roundtrip('Bs1', 12345678901234567890, + {0,<<75,80,248,215,49,149,42,213>>}), + + roundtrip('Bs1', [1,1,1,1,1,1,1,1], {0,<<255>>}), + roundtrip('Bs1', [0,1,0,0,1,0], {2,<<16#48>>}), + roundtrip('Bs1', [1,0,0,0,0,0,0,0,0], {7,<<16#80,0>>}), + roundtrip('Bs1', [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1], + {5,<<75,226,96>>}), - %% The following case to test OTP-4200 - ?line {ok,Bytes19} = - asn1_wrapper:encode('PrimStrings','Bs1',{0,<<0,0,1,1>>}), - ?line {ok,{0,<<0,0,1,1>>}} = - asn1_wrapper:decode('PrimStrings','Bs1',lists:flatten(Bytes19)), + case Rules of + ber -> + {ok,{4,<<73,32>>}} = + 'PrimStrings':decode('Bs1', <<35,8,3,2,0,73,3,2,4,32>>), + {ok,{7,<<234,156,0>>}} = + 'PrimStrings':decode('Bs1', <<35,9,3,2,0,234,3,3,7,156,0>>), + {ok,{4,<<73,32>>}} = + 'PrimStrings':decode('Bs1', <<35,128,3,2,0,73,3,2,4,32,0,0>>), + {ok,{7,<<234,156,0>>}} = + 'PrimStrings':decode('Bs1', + <<35,128,3,2,0,234,3,3,7,156,0,0,0>>); + _ -> + ok + end, + + %% Test OTP-4200 + roundtrip('Bs1', {0,<<0,0,1,1>>}), %%========================================================== %% Bs2 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } (SIZE (7)) %%========================================================== - - ?line {ok,Bytes21} = asn1_wrapper:encode('PrimStrings','Bs2',[mo,tu,fr]), - ?line {ok,[mo,tu,fr]} = asn1_wrapper:decode('PrimStrings','Bs2',lists:flatten(Bytes21)), - - ?line {ok,Bytes22} = asn1_wrapper:encode('PrimStrings','Bs2',[0,1,1,0,0,1,0]), - ?line {ok,[mo,tu,fr]} = asn1_wrapper:decode('PrimStrings','Bs2',lists:flatten(Bytes22)), - - % ?line case asn1_wrapper:erule(Rules) of -% ber -> -% ?line {ok,[mo,tu,fr,su,mo,th]} = -% asn1_wrapper:decode('PrimStrings','Bs2',[35,8,3,2,1,100,3,2,2,200]), - -% ?line {ok,[mo,tu,fr,su,mo,th]} = -% asn1_wrapper:decode('PrimStrings','Bs2',[35,128,3,2,1,100,3,2,2,200,0,0]), -% ok; - -% per -> -% ok -% end, - - + + roundtrip('Bs2', [mo,tu,fr]), + roundtrip('Bs2', [0,1,1,0,0,1,0], [mo,tu,fr]), %%========================================================== %% Bs3 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } (SIZE (1..7)) %%========================================================== - ?line {ok,Bytes31} = asn1_wrapper:encode('PrimStrings','Bs3',[mo,tu,fr]), - ?line {ok,[mo,tu,fr]} = asn1_wrapper:decode('PrimStrings','Bs3',lists:flatten(Bytes31)), - - ?line {ok,Bytes32} = asn1_wrapper:encode('PrimStrings','Bs3',[0,1,1,0,0,1,0]), - ?line {ok,[mo,tu,fr]} = asn1_wrapper:decode('PrimStrings','Bs3',lists:flatten(Bytes32)), - - + roundtrip('Bs3', [mo,tu,fr]), + roundtrip('Bs3', [0,1,1,0,0,1,0], [mo,tu,fr]), %%========================================================== %% BsPri ::= [PRIVATE 61] BIT STRING %%========================================================== - - ?line {ok,Bytes41} = asn1_wrapper:encode('PrimStrings','BsPri',45), - ?line {ok,{2,<<180>>}} = - asn1_wrapper:decode('PrimStrings','BsPri',lists:flatten(Bytes41)), - - ?line {ok,Bytes42} = asn1_wrapper:encode('PrimStrings','BsPri',211), - ?line {ok,{0,<<203>>}} = - asn1_wrapper:decode('PrimStrings','BsPri',lists:flatten(Bytes42)), - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {ok,{5,<<75,226,96>>}} = - asn1_wrapper:decode('PrimStrings','BsPri', - [223,61,4,5,75,226,96]), - - ?line {ok,{5,<<75,226,96>>}} = - asn1_wrapper:decode('PrimStrings','BsPri', - [255,61,128,3,4,5,75,226,96,0,0]), - - ?line {ok,{5,<<75,226,96>>}} = - asn1_wrapper:decode('PrimStrings','BsPri', - [255,61,9,3,2,0,75,3,3,5,226,96]), - - ?line {ok,{5,<<75,226,96>>}} = - asn1_wrapper:decode('PrimStrings','BsPri', - [255,61,128,3,2,0,75,3,3,5,226,96,0,0]), - ok; - - per -> - ok - end, - + + roundtrip('BsPri', 45, {2,<<180>>}), + roundtrip('BsPri', 211, {0,<<203>>}), + + case Rules of + ber -> + {ok,{5,<<75,226,96>>}} = + 'PrimStrings':decode('BsPri', + <<223,61,4,5,75,226,96>>), + + {ok,{5,<<75,226,96>>}} = + 'PrimStrings':decode('BsPri', + <<255,61,128,3,4,5,75,226,96,0,0>>), + + {ok,{5,<<75,226,96>>}} = + 'PrimStrings':decode('BsPri', + <<255,61,9,3,2,0,75,3,3,5,226,96>>), + + {ok,{5,<<75,226,96>>}} = + 'PrimStrings':decode('BsPri', + <<255,61,128,3,2,0,75,3,3,5,226,96,0,0>>), + ok; + _ -> + ok + end, %%========================================================== %% BsExpPri ::= [PRIVATE 61] EXPLICIT BIT STRING %%========================================================== - - ?line {ok,Bytes51} = asn1_wrapper:encode('PrimStrings','BsExpPri',45), - ?line {ok,{2,<<180>>}} = - asn1_wrapper:decode('PrimStrings','BsExpPri',lists:flatten(Bytes51)), - - ?line {ok,Bytes52} = asn1_wrapper:encode('PrimStrings','BsExpPri',211), - ?line {ok,{0,<<203>>}} = - asn1_wrapper:decode('PrimStrings','BsExpPri',lists:flatten(Bytes52)), - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {ok,{5,<<75,226,96>>}} = - asn1_wrapper:decode('PrimStrings','BsExpPri',[255,61,6,3,4,5,75,226,96]), - ok; - - per -> - ok - end, - - ok. -ticket_7734(per) -> - ?line BS = {0,list_to_binary(lists:duplicate(128,0))}, - ?line {ok,BSEnc} = asn1_wrapper:encode('PrimStrings','BS1024',BS), - ?line {ok,BS} = asn1_wrapper:decode('PrimStrings','BS1024',BSEnc). + roundtrip('BsExpPri', 45, {2,<<180>>}), + roundtrip('BsExpPri', 211, {0,<<203>>}), -bit_string_unnamed(Rules) -> - case asn1_wrapper:erule(Rules) of + case Rules of ber -> - ok; - per -> - ?line {ok,Bytes1} = - asn1_wrapper:encode('PrimStrings','TransportLayerAddress', - [0,1,1,0]), - ?line {ok,{4,<<96>>}} = - asn1_wrapper:decode('PrimStrings','TransportLayerAddress', - lists:flatten(Bytes1)) - end. + {ok,{5,<<75,226,96>>}} = + 'PrimStrings':decode('BsExpPri', <<255,61,6,3,4,5,75,226,96>>); + _ -> + ok + end, -otp_4869(per) -> - ?line Val1={'IP',[0],{0,<<62,235,90,50,0,0,0,0,0,0,0,0,0,0,0,0>>},asn1_NOVALUE}, - ?line Val2 = {'IP',[0],[0,0,1,1,1,1,1,0,1,1,1,0,1,0,1,1,0,1,0,1,1,0,1,0,0,0,1,1,0,0,1,0] ++ lists:duplicate(128 - 32,0),asn1_NOVALUE}, + ok. + +ticket_7734(_) -> + BS = {0,list_to_binary(lists:duplicate(128, 0))}, + roundtrip('BS1024', BS). - ?line {ok,Bytes1} = asn1_wrapper:encode('Constraints','IP',Val1), - ?line {ok,Bytes1} = asn1_wrapper:encode('Constraints','IP',Val2); +bit_string_unnamed(_Rules) -> + roundtrip('TransportLayerAddress', [0,1,1,0], {4,<<96>>}). + +otp_4869(per) -> + Val1 = {'IP',[0],{0,<<62,235,90,50,0,0,0,0,0,0,0,0,0,0,0,0>>},asn1_NOVALUE}, + Val2 = {'IP',[0],[0,0,1,1,1,1,1,0,1,1,1,0,1,0,1,1,0,1,0,1,1,0, + 1,0,0,0,1,1,0,0,1,0] ++ + lists:duplicate(128 - 32, 0),asn1_NOVALUE}, + {ok,Encoded} = 'Constraints':encode('IP', Val1), + {ok,Encoded} = 'Constraints':encode('IP', Val2), + ok; otp_4869(_) -> ok. + +roundtrip(Type, Val) -> + roundtrip_1('PrimStrings', Type, Val, Val). + +roundtrip(Type, Val1, Val2) -> + roundtrip_1('PrimStrings', Type, Val1, Val2). + +roundtrip_1(Mod, Type, In, Out) -> + {ok,Encoded} = Mod:encode(Type, In), + {ok,Out} = Mod:decode(Type, Encoded), + ok. diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl index 543c106e8a..c8d9008641 100644 --- a/lib/asn1/test/testConstraints.erl +++ b/lib/asn1/test/testConstraints.erl @@ -30,59 +30,20 @@ int_constraints(Rules) -> %% SingleValue ::= INTEGER (1) %%========================================================== - ?line {ok,Bytes1} = asn1_wrapper:encode('Constraints','SingleValue',1), - ?line {ok,1} = asn1_wrapper:decode('Constraints','SingleValue', - lists:flatten(Bytes1)), - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {ok,Bytes2} = - asn1_wrapper:encode('Constraints','SingleValue',0), - ?line {error,{asn1,{integer_range,_,0}}} = - asn1_wrapper:decode('Constraints','SingleValue', - lists:flatten(Bytes2)), - ?line {ok,Bytes3} = - asn1_wrapper:encode('Constraints','SingleValue',1000), - ?line {error,{asn1,{integer_range,_,1000}}} = - asn1_wrapper:decode('Constraints','SingleValue', - lists:flatten(Bytes3)); - per -> - ?line {error,_Reason1} = - asn1_wrapper:encode('Constraints','SingleValue',0), - ?line {error,_Reason2} = - asn1_wrapper:encode('Constraints','SingleValue',1000) - end, + range_error(Rules, 'SingleValue', 0), + roundtrip('SingleValue', 1), + range_error(Rules, 'SingleValue', 2), + range_error(Rules, 'SingleValue', 1000), %%========================================================== %% SingleValue2 ::= INTEGER (1..20) %%========================================================== - ?line {ok,Bytes4} = asn1_wrapper:encode('Constraints','SingleValue2',1), - ?line {ok,1} = asn1_wrapper:decode('Constraints','SingleValue2', - lists:flatten(Bytes4)), - - ?line {ok,Bytes5} = asn1_wrapper:encode('Constraints','SingleValue2',20), - ?line {ok,20} = asn1_wrapper:decode('Constraints','SingleValue2', - lists:flatten(Bytes5)), - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {ok,Bytes6} = - asn1_wrapper:encode('Constraints','SingleValue2',0), - ?line {error,{asn1,{integer_range,{1,20},0}}} = - asn1_wrapper:decode('Constraints','SingleValue2', - lists:flatten(Bytes6)), - ?line {ok,Bytes7} = - asn1_wrapper:encode('Constraints','SingleValue2',21), - ?line {error,{asn1,{integer_range,{1,20},21}}} = - asn1_wrapper:decode('Constraints','SingleValue2', - lists:flatten(Bytes7)); - per -> - ?line {error,_Reason3} = - asn1_wrapper:encode('Constraints','SingleValue',0), - ?line {error,_Reason4} = - asn1_wrapper:encode('Constraints','SingleValue',1000) - end, + range_error(Rules, 'SingleValue2', 0), + roundtrip('SingleValue2', 1), + roundtrip('SingleValue2', 20), + range_error(Rules, 'SingleValue2', 21), + range_error(Rules, 'SingleValue2', 1000), %%========================================================== %% SingleValue3 ::= INTEGER (Predefined | 5 | 10) @@ -90,136 +51,106 @@ int_constraints(Rules) -> %% where one value is predefined. %%========================================================== - ?line {ok,BytesSV3} = asn1_wrapper:encode('Constraints','SingleValue3',1), - ?line {ok,1} = asn1_wrapper:decode('Constraints','SingleValue3', - lists:flatten(BytesSV3)), - ?line {ok,BytesSV3_2} = asn1_wrapper:encode('Constraints','SingleValue3',5), - ?line {ok,5} = asn1_wrapper:decode('Constraints','SingleValue3', - lists:flatten(BytesSV3_2)), - ?line {ok,BytesSV3_3} = asn1_wrapper:encode('Constraints','SingleValue3',10), - ?line {ok,10} = asn1_wrapper:decode('Constraints','SingleValue3', - lists:flatten(BytesSV3_3)), + roundtrip('SingleValue3', 1), + roundtrip('SingleValue3', 5), + roundtrip('SingleValue3', 10), %%========================================================== %% Range2to19 ::= INTEGER (1<..<20) %%========================================================== - ?line {ok,Bytes8} = asn1_wrapper:encode('Constraints','Range2to19',2), - ?line {ok,2} = asn1_wrapper:decode('Constraints','Range2to19',lists:flatten(Bytes8)), - - ?line {ok,Bytes9} = asn1_wrapper:encode('Constraints','Range2to19',19), - ?line {ok,19} = asn1_wrapper:decode('Constraints','Range2to19',lists:flatten(Bytes9)), - - ?line case asn1_wrapper:erule(Rules) of - ber -> - ?line {ok,Bytes10} = - asn1_wrapper:encode('Constraints','Range2to19',1), - ?line {error,{asn1,{integer_range,{2,19},1}}} = - asn1_wrapper:decode('Constraints','Range2to19', - lists:flatten(Bytes10)), - ?line {ok,Bytes11} = - asn1_wrapper:encode('Constraints','Range2to19',20), - ?line {error,{asn1,{integer_range,{2,19},20}}} = - asn1_wrapper:decode('Constraints','Range2to19', - lists:flatten(Bytes11)); - per -> - ?line {error,_Reason5} = - asn1_wrapper:encode('Constraints','Range2to19',1), - ?line {error,_Reason6} = - asn1_wrapper:encode('Constraints','Range2to19',20) - end, + range_error(Rules, 'Range2to19', 1), + roundtrip('Range2to19', 2), + roundtrip('Range2to19', 19), + range_error(Rules, 'Range2to19', 20), %%========================================================== %% Tests for Range above 16^4 up to maximum supported by asn1 assuming the %% octet length field is encoded on max 8 bits %%========================================================== LastNumWithoutLengthEncoding = 65536, - ?line {ok,BytesFoo} = asn1_wrapper:encode('Constraints','Range256to65536', - LastNumWithoutLengthEncoding), - ?line {ok,LastNumWithoutLengthEncoding} = - asn1_wrapper:decode('Constraints','Range256to65536',lists:flatten(BytesFoo)), + roundtrip('Range256to65536', LastNumWithoutLengthEncoding), FirstNumWithLengthEncoding = 65537, - ?line {ok,BytesBar} = asn1_wrapper:encode('LargeConstraints','RangeMax', - FirstNumWithLengthEncoding), - ?line {ok,FirstNumWithLengthEncoding} = - asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBar)), + roundtrip('LargeConstraints', 'RangeMax', FirstNumWithLengthEncoding), FirstNumOver16_6 = 16777217, - ?line {ok, BytesBaz} = - asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_6), - ?line {ok, FirstNumOver16_6} = - asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBaz)), + roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_6), FirstNumOver16_8 = 4294967297, - ?line {ok, BytesQux} = - asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_8), - ?line {ok, FirstNumOver16_8} = - asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesQux)), + roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_8), FirstNumOver16_10 = 1099511627776, - ?line {ok, BytesBur} = - asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_10), - ?line {ok, FirstNumOver16_10} = - asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBur)), + roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_10), FirstNumOver16_10 = 1099511627776, - ?line {ok, BytesBur} = - asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_10), - ?line {ok, FirstNumOver16_10} = - asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBur)), + roundtrip('LargeConstraints', 'RangeMax', FirstNumOver16_10), HalfMax = 1 bsl (128*8), - ?line {ok, BytesHalfMax} = - asn1_wrapper:encode('LargeConstraints','RangeMax', HalfMax), - ?line {ok, HalfMax} = - asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesHalfMax)), + roundtrip('LargeConstraints', 'RangeMax', HalfMax), Max = 1 bsl (255*8), - ?line {ok, BytesMax} = - asn1_wrapper:encode('LargeConstraints','RangeMax', Max), - ?line {ok, Max} = - asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesMax)), + roundtrip('LargeConstraints', 'RangeMax', Max), %% Random number within longlong range LongLong = 12672809400538808320, - ?line {ok, BytesLongLong} = - asn1_wrapper:encode('Constraints','LongLong', LongLong), - ?line {ok, LongLong} = - asn1_wrapper:decode('Constraints','LongLong',lists:flatten(BytesLongLong)), + roundtrip('LongLong', LongLong), %%========================================================== %% Constraint Combinations (Duboisson p. 285) %% I ::= INTEGER (0|15..269) %%========================================================== - ?line {ok,Bytes12} = asn1_wrapper:encode('Constraints','I',0), - ?line {ok,0} = asn1_wrapper:decode('Constraints','I',Bytes12), - ?line {ok,Bytes13} = asn1_wrapper:encode('Constraints','I',20), - ?line {ok,20} = asn1_wrapper:decode('Constraints','I',Bytes13), + range_error(Rules, 'I', -1), + roundtrip('I', 0), + roundtrip('I', 15), + roundtrip('I', 20), + roundtrip('I', 269), + range_error(Rules, 'I', 270), %%========================================================== %% Constraint Combinations (Duboisson p. 285) %% X1 ::= INTEGER (1..4|8|10|20) %%========================================================== - ?line {ok,Bytes14} = asn1_wrapper:encode('Constraints','X1',1), - ?line {ok,1} = asn1_wrapper:decode('Constraints','X1',Bytes14), - ?line {ok,Bytes15} = asn1_wrapper:encode('Constraints','X1',20), - ?line {ok,20} = asn1_wrapper:decode('Constraints','X1',Bytes15), + range_error(Rules, 'X1', 0), + roundtrip('X1', 1), + roundtrip('X1', 4), + roundtrip('X1', 8), + roundtrip('X1', 10), + roundtrip('X1', 20), + range_error(Rules, 'X1', 21), + %%========================================================== %% SIZE Constraint (Duboisson p. 268) %% T ::= IA5String (SIZE (1|2, ..., SIZE (1|2|3))) %% T2 ::= IA5String (SIZE (1|2, ..., 3)) %%========================================================== - ?line {ok,Bytes16} = asn1_wrapper:encode('Constraints','T',"IA"), - ?line {ok,"IA"} = asn1_wrapper:decode('Constraints','T',Bytes16), - ?line {ok,Bytes17} = asn1_wrapper:encode('Constraints','T2',"IA"), - ?line {ok,"IA"} = asn1_wrapper:decode('Constraints','T2',Bytes17). - + roundtrip('T', "IA"), + roundtrip('T2', "IA"). refed_NNL_name(_Erule) -> ?line {ok,_} = asn1_wrapper:encode('Constraints','AnotherThing',fred), ?line {error,_Reason} = asn1_wrapper:encode('Constraints','AnotherThing',fred3). + +roundtrip(Type, Value) -> + roundtrip('Constraints', Type, Value). + +roundtrip(Module, Type, Value) -> + {ok,Encoded} = Module:encode(Type, Value), + {ok,Value} = Module:decode(Type, Encoded), + ok. + +range_error(ber, Type, Value) -> + %% BER: Values outside the effective range should be rejected + %% on decode. + {ok,Encoded} = 'Constraints':encode(Type, Value), + {error,{asn1,{integer_range,_,_}}} = 'Constraints':decode(Type, Encoded), + ok; +range_error(Per, Type, Value) when Per =:= per; Per =:= uper -> + %% (U)PER: Values outside the effective range should be rejected + %% on encode. + {error,_} = 'Constraints':encode(Type, Value), + ok. diff --git a/lib/asn1/test/testDeepTConstr.erl b/lib/asn1/test/testDeepTConstr.erl index aa3afbb58f..3df7bcbaa0 100644 --- a/lib/asn1/test/testDeepTConstr.erl +++ b/lib/asn1/test/testDeepTConstr.erl @@ -26,21 +26,19 @@ -include_lib("test_server/include/test_server.hrl"). main(_Erule) -> - Val1 = {'FilterItem', - {substrings, - {'FilterItem_substrings', - {2,6}, - [{initial,"SE"}, - {any,"DK"}, - {final,"N"}]}}}, + Val1 = {substrings, + {'FilterItem_substrings', + {2,6}, + [{initial,"SE"}, + {any,"DK"}, + {final,"N"}]}}, - Val2 = {'FilterItem', - {substrings, - {'FilterItem_substrings', - {2,6}, - [{initial,"SE"}, - {any,"DK"}, - {final,"NO"}]}}}, + Val2 = {substrings, + {'FilterItem_substrings', + {2,6}, + [{initial,"SE"}, + {any,"DK"}, + {final,"NO"}]}}, ?line {ok,Bytes1} = asn1_wrapper:encode('TConstrChoice','FilterItem',Val1), diff --git a/lib/asn1/test/testEnumExt.erl b/lib/asn1/test/testEnumExt.erl index bb975a1d13..0811f20571 100644 --- a/lib/asn1/test/testEnumExt.erl +++ b/lib/asn1/test/testEnumExt.erl @@ -25,58 +25,41 @@ main(Rule) when Rule =:= per; Rule =:= uper -> io:format("main(~p)~n",[Rule]), - B32=[32],B64=[64], + %% ENUMERATED with extensionmark (value is in root set) - ?line {ok,B32} = asn1_wrapper:encode('EnumExt','Ext',red), - ?line {ok,red} = asn1_wrapper:decode('EnumExt','Ext',B32), + B32 = <<32>>, + B32 = roundtrip('Ext', red), %% ENUMERATED with extensionmark (value is an extensionvalue) - ?line {ok,Or} = asn1_wrapper:encode('EnumExt','Ext1',orange), - ?line {ok,orange} = asn1_wrapper:decode('EnumExt','Ext1',Or), + Or = roundtrip('Ext1', orange), %% unknown extensionvalue - ?line {ok,{asn1_enum,0}} = asn1_wrapper:decode('EnumExt','Ext',Or), - + {ok,{asn1_enum,0}} = asn1_wrapper:decode('EnumExt','Ext',Or), %% ENUMERATED no extensionmark - ?line {ok,B64} = asn1_wrapper:encode('EnumExt','Noext',red), - ?line {ok,red} = asn1_wrapper:decode('EnumExt','Noext',B64), + B64 = <<64>>, + B64 = roundtrip('Noext', red), ok; main(ber) -> io:format("main(ber)~n",[]), %% ENUMERATED with extensionmark (value is in root set) - ?line {ok,Bytes1} = asn1_wrapper:encode('EnumExt','Ext',red), - ?line {ok,red} = asn1_wrapper:decode('EnumExt','Ext',lists:flatten(Bytes1)), + roundtrip('Ext', red), %% value is an extensionvalue - ?line {ok,Bytes1_1} = asn1_wrapper:encode('EnumExt','Ext1',orange), - ?line {ok,{asn1_enum,7}} = asn1_wrapper:decode('EnumExt','Ext',lists:flatten(Bytes1_1)), -%% ?line {ok,Bytes1_1} = asn1_wrapper:encode('EnumExt','Ext',{asn1_enum,7}), + {ok,Bytes1_1} = asn1_wrapper:encode('EnumExt','Ext1',orange), + {ok,{asn1_enum,7}} = asn1_wrapper:decode('EnumExt','Ext',lists:flatten(Bytes1_1)), - %% ENUMERATED no extensionmark - ?line {ok,Bytes2} = asn1_wrapper:encode('EnumExt','Noext',red), - ?line {ok,red} = asn1_wrapper:decode('EnumExt','Noext',lists:flatten(Bytes2)), + %% ENUMERATED no extensionmark + roundtrip('Noext', red), ?line {error,{asn1,_}} = (catch asn1_wrapper:encode('EnumExt','Noext',orange)), -%% ?line {error,{asn1,_}} = (catch asn1_wrapper:encode('EnumExt','Noext',{asn1_enum,7})), - ok, %% ENUMERATED with atom 'com' - ?line {ok,Bytes3} = asn1_wrapper:encode('EnumExt','Globalstate',{'Globalstate',preop}), - ?line {ok,preop} = asn1_wrapper:decode('EnumExt','Globalstate', - lists:flatten(Bytes3)), - ?line {ok,Bytes4} = asn1_wrapper:encode('EnumExt','Globalstate',{'Globalstate',com}), - ?line {ok,com} = asn1_wrapper:decode('EnumExt','Globalstate', - lists:flatten(Bytes4)). - - - - - - - - - - - + roundtrip('Globalstate', preop), + roundtrip('Globalstate', com), + ok. +roundtrip(Type, Value) -> + {ok,Encoded} = 'EnumExt':encode(Type, Value), + {ok,Value} = 'EnumExt':decode(Type, Encoded), + Encoded. diff --git a/lib/asn1/test/testMergeCompile.erl b/lib/asn1/test/testMergeCompile.erl index d63df28c31..8ef7ba3458 100644 --- a/lib/asn1/test/testMergeCompile.erl +++ b/lib/asn1/test/testMergeCompile.erl @@ -37,7 +37,7 @@ main(Erule) -> %% test of RANAP.set.asn1 ?line _PIEVal = [{'ProtocolIE-Field',4,ignore,{'Cause',{radioNetwork,{'CauseRadioNetwork','rab-pre-empted'}}}}], - ?line PIEVal2 = [{'ProtocolIE-Field',4,ignore,{'Cause',{radioNetwork,'rab-pre-empted'}}}], + PIEVal2 = [{'ProtocolIE-Field',4,ignore,{radioNetwork,'rab-pre-empted'}}], ?line _PEVal = [{'ProtocolExtensionField',[0]}], %% ?line EncVal = asn1rt_per_v1:encode_integer([],100), ?line EncVal = diff --git a/lib/asn1/test/testParameterizedInfObj.erl b/lib/asn1/test/testParameterizedInfObj.erl index 6f53595132..17108e285b 100644 --- a/lib/asn1/test/testParameterizedInfObj.erl +++ b/lib/asn1/test/testParameterizedInfObj.erl @@ -86,7 +86,7 @@ main(Erule) -> ranap(_Erule) -> - ?line PIEVal2 = [{'ProtocolIE-Field',4,ignore,{'Cause',{radioNetwork,'rab-pre-empted'}}}], + PIEVal2 = [{'ProtocolIE-Field',4,ignore,{radioNetwork,'rab-pre-empted'}}], ?line Val2 = #'InitiatingMessage'{procedureCode=1, criticality=ignore, diff --git a/lib/asn1/test/testPrimStrings.erl b/lib/asn1/test/testPrimStrings.erl index 9a640a2cb1..263d9e5ed2 100644 --- a/lib/asn1/test/testPrimStrings.erl +++ b/lib/asn1/test/testPrimStrings.erl @@ -372,6 +372,11 @@ octet_string(Rules) -> end, fragmented_octet_string(Rules), + + S255 = lists:seq(1, 255), + FixedStrings = {'OsFixedStrings',true,"","1","12","345",true, + S255,[$a|S255],[$a,$b|S255],397}, + roundtrip('OsFixedStrings', FixedStrings), ok. fragmented_octet_string(Erules) -> diff --git a/lib/asn1/test/testSeqExtension.erl b/lib/asn1/test/testSeqExtension.erl index 7c77ab87e9..1128d9a7c3 100644 --- a/lib/asn1/test/testSeqExtension.erl +++ b/lib/asn1/test/testSeqExtension.erl @@ -20,7 +20,7 @@ -module(testSeqExtension). -include("External.hrl"). --export([main/1]). +-export([main/2]). -include_lib("test_server/include/test_server.hrl"). @@ -28,70 +28,73 @@ -record('SeqExt2',{bool, int}). -record('SeqExt3',{bool, int}). -record('SeqExt4',{bool, int}). - - -main(_Rules) -> - - ?line {ok,Bytes11} = - asn1_wrapper:encode('SeqExtension','SeqExt1',#'SeqExt1'{}), - ?line {ok,{'SeqExt1'}} = - asn1_wrapper:decode('SeqExtension','SeqExt1',lists:flatten(Bytes11)), - - ?line {ok,Bytes21} = - asn1_wrapper:encode('SeqExtension','SeqExt2',#'SeqExt2'{bool = true,int = 99}), - ?line {ok,{'SeqExt2',true,99}} = - asn1_wrapper:decode('SeqExtension','SeqExt2',lists:flatten(Bytes21)), - - ?line {ok,Bytes22} = - asn1_wrapper:encode('SeqExtension','SeqExt2',#'SeqExt2'{int = 99,bool = true}), - ?line {ok,{'SeqExt2',true,99}} = - asn1_wrapper:decode('SeqExtension','SeqExt2',lists:flatten(Bytes22)), - - ?line {ok,Bytes31} = - asn1_wrapper:encode('SeqExtension','SeqExt3',#'SeqExt3'{bool = true,int = 99}), - ?line {ok,{'SeqExt3',true,99}} = - asn1_wrapper:decode('SeqExtension','SeqExt3',lists:flatten(Bytes31)), - - ?line {ok,Bytes32} = - asn1_wrapper:encode('SeqExtension','SeqExt3',#'SeqExt3'{int = 99,bool = true}), - ?line {ok,{'SeqExt3',true,99}} = - asn1_wrapper:decode('SeqExtension','SeqExt3',lists:flatten(Bytes32)), - - ?line {ok,Bytes41} = - asn1_wrapper:encode('SeqExtension','SeqExt4',#'SeqExt4'{bool = true,int = 99}), - ?line {ok,{'SeqExt4',true,99}} = - asn1_wrapper:decode('SeqExtension','SeqExt4',lists:flatten(Bytes41)), - - ?line {ok,Bytes42} = - asn1_wrapper:encode('SeqExtension','SeqExt4',#'SeqExt4'{int = 99,bool = true}), - ?line {ok,{'SeqExt4',true,99}} = - asn1_wrapper:decode('SeqExtension','SeqExt4',lists:flatten(Bytes42)), - - - % test of extension , not ready - - ?line {ok,BytesX11} = - asn1_wrapper:encode('SeqExtension','SeqExt1',#'SeqExt1'{}), - ?line {ok,{'SeqExt1'}} = - asn1_wrapper:decode('SeqExtension','SeqExt1',lists:flatten(BytesX11)), - - ?line {ok,BytesX21} = - asn1_wrapper:encode('SeqExtension','SeqExt2',#'SeqExt2'{bool = true,int = 99}), - ?line {ok,{'SeqExt2',true,99}} = - asn1_wrapper:decode('SeqExtension','SeqExt2',lists:flatten(BytesX21)), - - ?line {ok,BytesX22} = - asn1_wrapper:encode('SeqExtension','SeqExt2',#'SeqExt2'{int = 99,bool = true}), - ?line {ok,{'SeqExt2',true,99}} = - asn1_wrapper:decode('SeqExtension','SeqExt2',lists:flatten(BytesX22)), - - - - - +-record('SeqExt5',{name, shoesize}). +-record('SeqExt6',{i1,i2,i3,i4,i5,i6,i7}). +-record('SuperSeq',{s1,s2,s3,s4,s5,s6,i}). + +main(DataDir, Opts) -> + roundtrip('SeqExt1', #'SeqExt1'{}), + + roundtrip('SeqExt2', #'SeqExt2'{bool=true,int=99}), + roundtrip('SeqExt2', #'SeqExt2'{bool=false,int=42}), + + roundtrip('SeqExt3', #'SeqExt3'{bool=true,int=-77777}), + roundtrip('SeqExt3', #'SeqExt3'{bool=false,int=-42000}), + + roundtrip('SeqExt4', #'SeqExt4'{bool=true,int=12345}), + roundtrip('SeqExt4', #'SeqExt4'{bool=false,int=123456}), + + roundtrip('SeqExt5', #'SeqExt5'{name="Arne",shoesize=47}), + + %% Encode a value with this version of the specification. + BigInt = 128638468966, + SuperSeq = #'SuperSeq'{s1=#'SeqExt1'{}, + s2=#'SeqExt2'{bool=true,int=2345}, + s3=#'SeqExt3'{bool=false,int=17}, + s4=#'SeqExt4'{bool=true,int=38739739}, + s5=#'SeqExt5'{name="Arne",shoesize=47}, + s6=#'SeqExt6'{i1=531,i2=601,i3=999, + i4=777,i5=11953, + i6=13553,i7=77777}, + i=BigInt + }, + {ok,SuperSeqEnc} = 'SeqExtension':encode('SuperSeq', SuperSeq), + {ok,SuperSeq} = 'SeqExtension':decode('SuperSeq', SuperSeqEnc), + + %% Remove all extensions from the ASN.1 specification and compile it. + CaseDir = filename:dirname(code:which('SeqExtension')), + Asn1SrcBase = "SeqExtension.asn1", + Asn1SrcFile0 = filename:join(DataDir, Asn1SrcBase), + {ok,Src0} = file:read_file(Asn1SrcFile0), + %% Remove all declarations following "...," up to the end + %% of the SEQUENCE. + Src1 = re:replace(Src0, "[.][.][.],[^}]*", "...\n", + [global,{return,binary}]), + %% Remove the last double bracket group in the SEQUENCE. + Src = re:replace(Src1, ",\\s*\\[\\[.*?\\]\\]\\s*\\}", "\n}", + [global,{return,binary}]), + io:format("~s\n\n", [Src]), + Asn1SrcFile = filename:join(CaseDir, Asn1SrcBase), + ok = file:write_file(Asn1SrcFile, Src), + ok = asn1ct:compile(Asn1SrcFile, + [{i,DataDir},{outdir,CaseDir}|Opts]), + + %% Decode the encoded sequence with the version of the spec + %% with no extensions following the extension marks + %% (except in SeqExt6). The integer 'i' at the end + %% of the sequence must still be the correct integer (otherwise + %% some extension has not been skipped correctly). + {ok,DecodedSuperSeq} = 'SeqExtension':decode('SuperSeq', SuperSeqEnc), + #'SuperSeq'{s1={'SeqExt1'}, + s2=#'SeqExt2'{bool=true,int=2345}, + s3={'SeqExt3'}, + s4={'SeqExt4',true}, + s5={'SeqExt5'}, + s6={'SeqExt6',531,601,999,777,11953}, + i=BigInt} = DecodedSuperSeq, ok. - - - - +roundtrip(Type, Value) -> + {ok,Encoded} = 'SeqExtension':encode(Type, Value), + {ok,Value} = 'SeqExtension':decode(Type, Encoded), + ok. diff --git a/lib/asn1/test/testSetOptional.erl b/lib/asn1/test/testSetOptional.erl index cef90bc843..bb43ff0a96 100644 --- a/lib/asn1/test/testSetOptional.erl +++ b/lib/asn1/test/testSetOptional.erl @@ -21,8 +21,7 @@ -include("External.hrl"). -export([main/1]). --export([ticket_7533/1,decoder/4]). --include_lib("test_server/include/test_server.hrl"). +-export([ticket_7533/1]). -record('SetOpt1',{bool1 = asn1_NOVALUE, int1, set1 = asn1_NOVALUE}). -record('SetOpt1Imp',{bool1 = asn1_NOVALUE, int1, set1 = asn1_NOVALUE}). @@ -36,171 +35,64 @@ -record('SetIn',{boolIn, intIn}). main(_Rules) -> + roundtrip('SetOpt1', + #'SetOpt1'{bool1=true,int1=15, + set1=#'SetIn'{boolIn=true,intIn=66}}), + roundtrip('SetOpt1', #'SetOpt1'{int1=15}), + + roundtrip('SetOpt2', #'SetOpt2'{bool2=true,int2=15, + set2=#'SetIn'{boolIn=true,intIn=66}}), + roundtrip('SetOpt2', #'SetOpt2'{int2=15,bool2=true}), + + roundtrip('SetOpt3', #'SetOpt3'{bool3=true,int3=15, + set3=#'SetIn'{boolIn=true,intIn=66}}), + roundtrip('SetOpt3', #'SetOpt3'{int3=15}), + + roundtrip('SetOpt1Imp', + #'SetOpt1Imp'{bool1=true,int1 = 15, + set1=#'SetIn'{boolIn = true,intIn = 66}}), + roundtrip('SetOpt1Imp', #'SetOpt1Imp'{int1=15}), - ?line {ok,Bytes11} = - asn1_wrapper:encode('SetOptional','SetOpt1',#'SetOpt1'{bool1 = true, - int1 = 15, - set1 = #'SetIn'{boolIn = true, - intIn = 66}}), - ?line {ok,{'SetOpt1',true,15,{'SetIn',true,66}}} = - asn1_wrapper:decode('SetOptional','SetOpt1',lists:flatten(Bytes11)), - - - ?line {ok,Bytes12} = asn1_wrapper:encode('SetOptional','SetOpt1',#'SetOpt1'{int1 = 15}), - ?line {ok,{'SetOpt1',asn1_NOVALUE,15,asn1_NOVALUE}} = - asn1_wrapper:decode('SetOptional','SetOpt1',lists:flatten(Bytes12)), - - - ?line {ok,Bytes21} = - asn1_wrapper:encode('SetOptional','SetOpt2',#'SetOpt2'{bool2 = true, - int2 = 15, - set2 = #'SetIn'{boolIn = true, - intIn = 66}}), - ?line {ok,{'SetOpt2',{'SetIn',true,66},true,15}} = - asn1_wrapper:decode('SetOptional','SetOpt2',lists:flatten(Bytes21)), - - - ?line {ok,Bytes22} = asn1_wrapper:encode('SetOptional','SetOpt2',#'SetOpt2'{int2 = 15, - bool2 = true}), - ?line {ok,{'SetOpt2',asn1_NOVALUE,true,15}} = - asn1_wrapper:decode('SetOptional','SetOpt2',lists:flatten(Bytes22)), - - - - ?line {ok,Bytes31} = - asn1_wrapper:encode('SetOptional','SetOpt3',#'SetOpt3'{bool3 = true, - int3 = 15, - set3 = #'SetIn'{boolIn = true, - intIn = 66}}), - ?line {ok,{'SetOpt3',true,{'SetIn',true,66},15}} = - asn1_wrapper:decode('SetOptional','SetOpt3',lists:flatten(Bytes31)), - - - ?line {ok,Bytes32} = asn1_wrapper:encode('SetOptional','SetOpt3',#'SetOpt3'{int3 = 15}), - ?line {ok,{'SetOpt3',asn1_NOVALUE,asn1_NOVALUE,15}} = - asn1_wrapper:decode('SetOptional','SetOpt3',lists:flatten(Bytes32)), - - - - - - ?line {ok,Bytes41} = - asn1_wrapper:encode('SetOptional','SetOpt1Imp',#'SetOpt1Imp'{bool1 = true, - int1 = 15, - set1 = #'SetIn'{boolIn = true, - intIn = 66}}), - ?line {ok,{'SetOpt1Imp',true,15,{'SetIn',true,66}}} = - asn1_wrapper:decode('SetOptional','SetOpt1Imp',lists:flatten(Bytes41)), - - - ?line {ok,Bytes42} = asn1_wrapper:encode('SetOptional','SetOpt1Imp',#'SetOpt1Imp'{int1 = 15}), - ?line {ok,{'SetOpt1Imp',asn1_NOVALUE,15,asn1_NOVALUE}} = - asn1_wrapper:decode('SetOptional','SetOpt1Imp',lists:flatten(Bytes42)), - - - ?line {ok,Bytes51} = - asn1_wrapper:encode('SetOptional','SetOpt2Imp',#'SetOpt2Imp'{bool2 = true, - int2 = 15, - set2 = #'SetIn'{boolIn = true, - intIn = 66}}), - ?line {ok,{'SetOpt2Imp',{'SetIn',true,66},true,15}} = - asn1_wrapper:decode('SetOptional','SetOpt2Imp',lists:flatten(Bytes51)), - - - ?line {ok,Bytes52} = asn1_wrapper:encode('SetOptional','SetOpt2Imp',#'SetOpt2Imp'{int2 = 15, - bool2 = true}), - ?line {ok,{'SetOpt2Imp',asn1_NOVALUE,true,15}} = - asn1_wrapper:decode('SetOptional','SetOpt2Imp',lists:flatten(Bytes52)), - - - - ?line {ok,Bytes61} = - asn1_wrapper:encode('SetOptional','SetOpt3Imp',#'SetOpt3Imp'{bool3 = true, - int3 = 15, - set3 = #'SetIn'{boolIn = true, - intIn = 66}}), - ?line {ok,{'SetOpt3Imp',true,{'SetIn',true,66},15}} = - asn1_wrapper:decode('SetOptional','SetOpt3Imp',lists:flatten(Bytes61)), - - - ?line {ok,Bytes62} = asn1_wrapper:encode('SetOptional','SetOpt3Imp',#'SetOpt3Imp'{int3 = 15}), - ?line {ok,{'SetOpt3Imp',asn1_NOVALUE,asn1_NOVALUE,15}} = - asn1_wrapper:decode('SetOptional','SetOpt3Imp',lists:flatten(Bytes62)), - - - - - - - ?line {ok,Bytes71} = - asn1_wrapper:encode('SetOptional','SetOpt1Exp',#'SetOpt1Exp'{bool1 = true, - int1 = 15, - set1 = #'SetIn'{boolIn = true, - intIn = 66}}), - ?line {ok,{'SetOpt1Exp',true,15,{'SetIn',true,66}}} = - asn1_wrapper:decode('SetOptional','SetOpt1Exp',lists:flatten(Bytes71)), - - - ?line {ok,Bytes72} = asn1_wrapper:encode('SetOptional','SetOpt1Exp',#'SetOpt1Exp'{int1 = 15}), - ?line {ok,{'SetOpt1Exp',asn1_NOVALUE,15,asn1_NOVALUE}} = - asn1_wrapper:decode('SetOptional','SetOpt1Exp',lists:flatten(Bytes72)), - - - ?line {ok,Bytes81} = - asn1_wrapper:encode('SetOptional','SetOpt2Exp',#'SetOpt2Exp'{bool2 = true, - int2 = 15, - set2 = #'SetIn'{boolIn = true, - intIn = 66}}), - ?line {ok,{'SetOpt2Exp',{'SetIn',true,66},true,15}} = - asn1_wrapper:decode('SetOptional','SetOpt2Exp',lists:flatten(Bytes81)), - - - ?line {ok,Bytes82} = asn1_wrapper:encode('SetOptional','SetOpt2Exp',#'SetOpt2Exp'{int2 = 15, - bool2 = true}), - ?line {ok,{'SetOpt2Exp',asn1_NOVALUE,true,15}} = - asn1_wrapper:decode('SetOptional','SetOpt2Exp',lists:flatten(Bytes82)), - - - - ?line {ok,Bytes91} = - asn1_wrapper:encode('SetOptional','SetOpt3Exp',#'SetOpt3Exp'{bool3 = true, - int3 = 15, - set3 = #'SetIn'{boolIn = true, - intIn = 66}}), - ?line {ok,{'SetOpt3Exp',true,{'SetIn',true,66},15}} = - asn1_wrapper:decode('SetOptional','SetOpt3Exp',lists:flatten(Bytes91)), - - - ?line {ok,Bytes92} = asn1_wrapper:encode('SetOptional','SetOpt3Exp',#'SetOpt3Exp'{int3 = 15}), - ?line {ok,{'SetOpt3Exp',asn1_NOVALUE,asn1_NOVALUE,15}} = - asn1_wrapper:decode('SetOptional','SetOpt3Exp',lists:flatten(Bytes92)), - + + roundtrip('SetOpt2Imp', + #'SetOpt2Imp'{bool2=true,int2=15, + set2=#'SetIn'{boolIn=true,intIn=66}}), + roundtrip('SetOpt2Imp',#'SetOpt2Imp'{int2=15,bool2=true}), + + roundtrip('SetOpt3Imp', + #'SetOpt3Imp'{bool3=true,int3=15, + set3=#'SetIn'{boolIn=true,intIn=66}}), + roundtrip('SetOpt3Imp', #'SetOpt3Imp'{int3=15}), + + roundtrip('SetOpt1Exp', + #'SetOpt1Exp'{bool1=true,int1=15, + set1=#'SetIn'{boolIn=true,intIn=66}}), + roundtrip('SetOpt1Exp', #'SetOpt1Exp'{int1=15}), + + roundtrip('SetOpt2Exp', + #'SetOpt2Exp'{bool2=true,int2=15, + set2=#'SetIn'{boolIn=true,intIn=66}}), + roundtrip('SetOpt2Exp', #'SetOpt2Exp'{int2=15,bool2=true}), + roundtrip('SetOpt3Exp', + #'SetOpt3Exp'{bool3=true,int3=15, + set3=#'SetIn'{boolIn=true,intIn=66}}), + roundtrip('SetOpt3Exp', #'SetOpt3Exp'{int3=15}), ok. ticket_7533(Ber) when Ber == ber -> - Val = #'SetOpt1'{bool1 = true,int1=12,set1=#'SetIn'{boolIn=false,intIn=13}}, - ?line {ok,B} = asn1_wrapper:encode('SetOptional','SetOpt1',Val), - ?line {ok,Val} = asn1_wrapper:decode('SetOptional','SetOpt1',B), - - CorruptVal = [49,14,1,1,255,2,1,12] ++ lists:duplicate(8,0), - Pid = spawn(?MODULE,decoder,[self(),'SetOptional','SetOpt1',CorruptVal]), - receive - {ok,Pid,Result} -> - io:format("Decode result: ~p~n",[Result]), - ok - after 10000 -> - io:format("Decode timeout~n",[]), - exit(Pid,normal) - end; + Val = #'SetOpt1'{bool1=true,int1=12,set1=#'SetIn'{boolIn=false,intIn=13}}, + roundtrip('SetOpt1', Val), + CorruptVal = <<49,14,1,1,255,2,1,12,0:8/unit:8>>, + {error,_} = 'SetOptional':decode('SetOpt1', CorruptVal), + ok; ticket_7533(_) -> ok. -decoder(Parent,Module,Type,Val) -> - io:format("Decoding~n",[]), - ?line {ok,Res} = asn1_wrapper:decode(Module,Type,Val), - io:format("Decode res: ~p~n",[Res]), - Parent ! {ok,self(),Res}. +roundtrip(Type, Value) -> + {ok,Encoded} = 'SetOptional':encode(Type, Value), + {ok,Value} = 'SetOptional':decode(Type, Encoded), + ok. diff --git a/lib/asn1/test/test_partial_incomplete_decode.erl b/lib/asn1/test/test_partial_incomplete_decode.erl index df56c27115..8ede06938d 100644 --- a/lib/asn1/test/test_partial_incomplete_decode.erl +++ b/lib/asn1/test/test_partial_incomplete_decode.erl @@ -188,7 +188,7 @@ decode_parts('S1_2',PartDecMsg) -> msg('F') -> - {'F',{fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{da,[{'A',16,{'D',17,true}}]}}}}; + {fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{da,[{'A',16,{'D',17,true}}]}}}; msg('F3') -> {fb,{'E',10,[{'D',11,true},{'D',12,false}],false,{dc,{'E_d_dc',13,true,{'E_d_dc_dcc',14,15}}}}}; diff --git a/lib/asn1/test/test_selective_decode.erl b/lib/asn1/test/test_selective_decode.erl index bb348611da..ebe1296cf3 100644 --- a/lib/asn1/test/test_selective_decode.erl +++ b/lib/asn1/test/test_selective_decode.erl @@ -53,7 +53,7 @@ test() -> msg('F') -> - {'F',{fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{da,[{'A',16,{'D',17,true}}]}}}}; + {fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{da,[{'A',16,{'D',17,true}}]}}}; msg('E') -> {'E',10,[{'D',11,true},{'D',12,false}],false,{dc,{'E_d_dc',13,true,{'E_d_dc_dcc',14,15}}}}; diff --git a/lib/asn1/test/test_special_decode_performance.erl b/lib/asn1/test/test_special_decode_performance.erl index dd56d29b28..7dfab1f25a 100644 --- a/lib/asn1/test/test_special_decode_performance.erl +++ b/lib/asn1/test/test_special_decode_performance.erl @@ -31,8 +31,8 @@ go(all) -> {Time_S_c,Time_MGC_c}). go(N,Mod) -> - ?line Val = val(Mod), - ?line {ok,B} = Mod:encode(element(1,Val),Val), + {Type,Val} = val(Mod), + {ok,B} = Mod:encode(Type, Val), ?line go(Mod,B,N). go(Mod,Bin,N) -> @@ -92,7 +92,7 @@ val('PartialDecSeq') -> {'F',{fb,{'E',12,[{'D',13,true},{'D',14,false},{'D',15,true},{'D',16,false},{'D',13,true},{'D',14,false},{'D',15,true},{'D',16,false},{'D',13,true},{'D',14,false},{'D',15,true},{'D',16,false}],true,{da,[{'A',17,{'D',18,false}},{'A',19,{'D',20,true}},{'A',21,{'D',22,false}},{'A',17,{'D',18,false}},{'A',19,{'D',20,true}},{'A',21,{'D',22,false}},{'A',17,{'D',18,false}},{'A',19,{'D',20,true}},{'A',21,{'D',22,false}},{'A',17,{'D',18,false}},{'A',19,{'D',20,true}},{'A',21,{'D',22,false}},{'A',17,{'D',18,false}},{'A',19,{'D',20,true}},{'A',21,{'D',22,false}},{'A',17,{'D',18,false}},{'A',19,{'D',20,true}},{'A',21,{'D',22,false}}]}}}}; val('MEDIA-GATEWAY-CONTROL') -> - {'MegacoMessage',asn1_NOVALUE,{'Message',1,{ip4Address,{'IP4Address',[125,125,125,111],55555}},{transactions,[{transactionReply,{'TransactionReply',50007,asn1_NOVALUE,{actionReplies,[{'ActionReply',0,asn1_NOVALUE,asn1_NOVALUE,[{auditValueReply,{auditResult,{'AuditResult',{'TerminationID',[],[255,255,255]},[{mediaDescriptor,{'MediaDescriptor',asn1_NOVALUE,{multiStream,[{'StreamDescriptor',1,{'StreamParms',{'LocalControlDescriptor',sendRecv,asn1_NOVALUE,asn1_NOVALUE,[{'PropertyParm',[0,11,0,7],[[52,48]],asn1_NOVALUE}]},{'LocalRemoteDescriptor',[[{'PropertyParm',[0,0,176,1],[[48]],asn1_NOVALUE},{'PropertyParm',[0,0,176,8],[[73,78,32,73,80,52,32,49,50,53,46,49,50,53,46,49,50,53,46,49,49,49]],asn1_NOVALUE},{'PropertyParm',[0,0,176,15],[[97,117,100,105,111,32,49,49,49,49,32,82,84,80,47,65,86,80,32,32,52]],asn1_NOVALUE},{'PropertyParm',[0,0,176,12],[[112,116,105,109,101,58,51,48]],asn1_NOVALUE}]]},{'LocalRemoteDescriptor',[[{'PropertyParm',[0,0,176,1],[[48]],asn1_NOVALUE},{'PropertyParm',[0,0,176,8],[[73,78,32,73,80,52,32,49,50,52,46,49,50,52,46,49,50,52,46,50,50,50]],asn1_NOVALUE},{'PropertyParm',[0,0,176,15],[[97,117,100,105,111,32,50,50,50,50,32,82,84,80,47,65,86,80,32,32,52]],asn1_NOVALUE},{'PropertyParm',[0,0,176,12],[[112,116,105,109,101,58,51,48]],asn1_NOVALUE}]]}}}]}}},{packagesDescriptor,[{'PackagesItem',[0,11],1},{'PackagesItem',[0,11],1}]},{statisticsDescriptor,[{'StatisticsParameter',[0,12,0,4],[[49,50,48,48]]},{'StatisticsParameter',[0,11,0,2],[[54,50,51,48,48]]},{'StatisticsParameter',[0,12,0,5],[[55,48,48]]},{'StatisticsParameter',[0,11,0,3],[[52,53,49,48,48]]},{'StatisticsParameter',[0,12,0,6],[[48,46,50]]},{'StatisticsParameter',[0,12,0,7],[[50,48]]},{'StatisticsParameter',[0,12,0,8],[[52,48]]}]}]}}}]}]}}}]}}}. + {'MegacoMessage',{'MegacoMessage',asn1_NOVALUE,{'Message',1,{ip4Address,{'IP4Address',[125,125,125,111],55555}},{transactions,[{transactionReply,{'TransactionReply',50007,asn1_NOVALUE,{actionReplies,[{'ActionReply',0,asn1_NOVALUE,asn1_NOVALUE,[{auditValueReply,{auditResult,{'AuditResult',{'TerminationID',[],[255,255,255]},[{mediaDescriptor,{'MediaDescriptor',asn1_NOVALUE,{multiStream,[{'StreamDescriptor',1,{'StreamParms',{'LocalControlDescriptor',sendRecv,asn1_NOVALUE,asn1_NOVALUE,[{'PropertyParm',[0,11,0,7],[[52,48]],asn1_NOVALUE}]},{'LocalRemoteDescriptor',[[{'PropertyParm',[0,0,176,1],[[48]],asn1_NOVALUE},{'PropertyParm',[0,0,176,8],[[73,78,32,73,80,52,32,49,50,53,46,49,50,53,46,49,50,53,46,49,49,49]],asn1_NOVALUE},{'PropertyParm',[0,0,176,15],[[97,117,100,105,111,32,49,49,49,49,32,82,84,80,47,65,86,80,32,32,52]],asn1_NOVALUE},{'PropertyParm',[0,0,176,12],[[112,116,105,109,101,58,51,48]],asn1_NOVALUE}]]},{'LocalRemoteDescriptor',[[{'PropertyParm',[0,0,176,1],[[48]],asn1_NOVALUE},{'PropertyParm',[0,0,176,8],[[73,78,32,73,80,52,32,49,50,52,46,49,50,52,46,49,50,52,46,50,50,50]],asn1_NOVALUE},{'PropertyParm',[0,0,176,15],[[97,117,100,105,111,32,50,50,50,50,32,82,84,80,47,65,86,80,32,32,52]],asn1_NOVALUE},{'PropertyParm',[0,0,176,12],[[112,116,105,109,101,58,51,48]],asn1_NOVALUE}]]}}}]}}},{packagesDescriptor,[{'PackagesItem',[0,11],1},{'PackagesItem',[0,11],1}]},{statisticsDescriptor,[{'StatisticsParameter',[0,12,0,4],[[49,50,48,48]]},{'StatisticsParameter',[0,11,0,2],[[54,50,51,48,48]]},{'StatisticsParameter',[0,12,0,5],[[55,48,48]]},{'StatisticsParameter',[0,11,0,3],[[52,53,49,48,48]]},{'StatisticsParameter',[0,12,0,6],[[48,46,50]]},{'StatisticsParameter',[0,12,0,7],[[50,48]]},{'StatisticsParameter',[0,12,0,8],[[52,48]]}]}]}}}]}]}}}]}}}}. %% val('PartialDecSeq') -> %% {'F',{fb,{'E',35,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}],false,{dc,{'E_d_dc',15,true,{'E_d_dc_dcc',17,4711}}}}}}. diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml index b6d4a633cb..151159ad69 100644 --- a/lib/common_test/doc/src/common_test_app.xml +++ b/lib/common_test/doc/src/common_test_app.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2003</year><year>2012</year> + <year>2003</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -170,7 +170,9 @@ <v> UserData = term()</v> <v> Conns = [atom()]</v> <v> CSSFile = string()</v> - <v> CTHs = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}]</v> + <v> CTHs = [CTHModule |</v> + <v> {CTHModule, CTHInitArgs} |</v> + <v> {CTHModule, CTHInitArgs, CTHPriority}]</v> <v> CTHModule = atom()</v> <v> CTHInitArgs = term()</v> </type> @@ -297,8 +299,9 @@ <v> UserData = term()</v> <v> Conns = [atom()]</v> <v> CSSFile = string()</v> - <v> CTHs = [CTHModule | {CTHModule, CTHInitArgs} | - {CTHModule, CTHInitArgs, CTHPriority}]</v> + <v> CTHs = [CTHModule |</v> + <v> {CTHModule, CTHInitArgs} |</v> + <v> {CTHModule, CTHInitArgs, CTHPriority}]</v> <v> CTHModule = atom()</v> <v> CTHInitArgs = term()</v> </type> diff --git a/lib/common_test/doc/src/cover_chapter.xml b/lib/common_test/doc/src/cover_chapter.xml index b2e64bfff0..4fa92d5583 100644 --- a/lib/common_test/doc/src/cover_chapter.xml +++ b/lib/common_test/doc/src/cover_chapter.xml @@ -108,8 +108,8 @@ specifications</seealso>).</p> </section> + <marker id="cover_stop"></marker> <section> - <marker id="cover_stop"></marker> <title>Stopping the cover tool when tests are completed</title> <p>By default the Cover tool is automatically stopped when the tests are completed. This causes the original (non cover @@ -175,6 +175,11 @@ %% Specific modules to exclude in cover. {excl_mods, Mods}. + + %% Cross cover compilation + %% Tag = atom(), an identifier for a test run + %% Mod = [atom()], modules to compile for accumulated analysis + {cross,[{Tag,Mods}]}. </pre> <p>The <c>incl_dirs_r</c> and <c>excl_dirs_r</c> terms tell Common @@ -190,6 +195,81 @@ specification file for Common Test).</p> </section> + <marker id="cross_cover"/> + <section> + <title>Cross cover analysis</title> + <p>The cross cover mechanism allows cover analysis of modules + across multiple tests. It is useful if some code, e.g. a library + module, is used by many different tests and the accumulated cover + result is desirable.</p> + + <p>This can of course also be achieved in a more customized way by + using the <c>export</c> parameter in the cover specification and + analysing the result off line, but the cross cover mechanism is a + build in solution which also provides the logging.</p> + + <p>The mechanism is easiest explained via an example:</p> + + <p>Let's say that there are two systems, <c>s1</c> and <c>s2</c>, + which are tested in separate test runs. System <c>s1</c> contains + a library module <c>m1</c> which is tested by the <c>s1</c> test + run and is included in <c>s1</c>'s cover specification:</p> + +<code type="none"> +s1.cover: + {incl_mods,[m1]}.</code> + + <p>When analysing code coverage, the result for <c>m1</c> can be + seen in the cover log in the <c>s1</c> test result.</p> + + <p>Now, let's imagine that since <c>m1</c> is a library module, it + is also used quite a bit by system <c>s2</c>. The <c>s2</c> test + run does not specifically test <c>m1</c>, but it might still be + interesting to see which parts of <c>m1</c> is actually covered by + the <c>s2</c> tests. To do this, <c>m1</c> could be included also + in <c>s2</c>'s cover specification:</p> + +<code type="none"> +s2.cover: + {incl_mods,[m1]}.</code> + + <p>This would give an entry for <c>m1</c> also in the cover log + for the <c>s2</c> test run. The problem is that this would only + reflect the coverage by <c>s2</c> tests, not the accumulated + result over <c>s1</c> and <c>s2</c>. And this is where the cross + cover mechanism comes in handy.</p> + + <p>If instead the cover specification for <c>s2</c> was like + this:</p> + +<code type="none"> +s2.cover: + {cross,[{s1,[m1]}]}.</code> + + <p>then <c>m1</c> would be cover compiled in the <c>s2</c> test + run, but not shown in the coverage log. Instead, if + <c>ct_cover:cross_cover_analyse/2</c> is called after both + <c>s1</c> and <c>s2</c> test runs are completed, the accumulated + result for <c>m1</c> would be available in the cross cover log for + the <c>s1</c> test run.</p> + + <p>The call to the analyse function must be like this:</p> + +<code type="none"> +ct_cover:cross_cover_analyse(Level, [{s1,S1LogDir},{s2,S2LogDir}]).</code> + + <p>where <c>S1LogDir</c> and <c>S2LogDir</c> are the directories + named <c><TestName>.logs</c> for each test respectively.</p> + + <p>Note the tags <c>s1</c> and <c>s2</c> which are used in the + cover specification file and in the call to + <c>ct_cover:cross_cover_analyse/2</c>. The point of these are only + to map the modules specified in the cover specification to the log + directory specified in the call to the analyse function. The name + of the tag has no meaning beyond this.</p> + + </section> + <section> <title>Logging</title> <p>To view the result of a code coverage test, follow the @@ -197,6 +277,11 @@ takes you to the code coverage overview page. If you have successfully performed a detailed coverage analysis, you find links to each individual module coverage page here.</p> + + <p>If cross cover analysis has been performed, and there are + accumulated coverage results for the current test, then the - + "Coverdata collected over all tests" link will take you to these + results.</p> </section> </chapter> diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml index 86237f5fc1..fe871eb516 100644 --- a/lib/common_test/doc/src/ct_hooks_chapter.xml +++ b/lib/common_test/doc/src/ct_hooks_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2011</year><year>2012</year> + <year>2011</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -439,14 +439,14 @@ terminate(State) -> <table> <row> - <cell><em>CTH Name</em></cell> - <cell><em>Is Built-in</em></cell> - <cell><em>Description</em></cell> + <cell align="left"><em>CTH Name</em></cell> + <cell align="left"><em>Is Built-in</em></cell> + <cell align="left"><em>Description</em></cell> </row> <row> - <cell>cth_log_redirect</cell> - <cell>yes</cell> - <cell>Captures all error_logger and SASL logging events and prints them + <cell align="left">cth_log_redirect</cell> + <cell align="left">yes</cell> + <cell align="left">Captures all error_logger and SASL logging events and prints them to the current test case log. If an event can not be associated with a testcase it will be printed in the common test framework log. This will happen for testcases which are run in parallel and events which occur @@ -455,14 +455,29 @@ terminate(State) -> using the normal SASL mechanisms. </cell> </row> <row> - <cell>cth_surefire</cell> - <cell>no</cell> - <cell>Captures all test results and outputs them as surefire XML into - a file. The file which is created is by default called junit_report.xml. - The name can be by setting the path option for this hook. e.g. + <cell align="left">cth_surefire</cell> + <cell align="left">no</cell> + <cell align="left"><p>Captures all test results and outputs them as surefire + XML into a file. The file which is created is by default + called junit_report.xml. The file name can be changed by + setting the <c>path</c> option for this hook, e.g.</p> + <code>-ct_hooks cth_surefire [{path,"/tmp/report.xml"}]</code> - Surefire XML can forinstance be used by Jenkins to display test - results.</cell> + + <p>If the <c>url_base</c> option is set, an additional + attribute named <c>url</c> will be added to each + <c>testsuite</c> and <c>testcase</c> XML element. The value will + be constructed from the <c>url_base</c> and a relative path + to the test suite or test case log respectively, e.g.</p> + + <code>-ct_hooks cth_surefire [{url_base, "http://myserver.com/"}]</code> + <p>will give a url attribute value similar to</p> + + <code>"http://myserver.com/[email protected]_11.19.39/ +x86_64-unknown-linux-gnu.my_test.logs/run.2012-12-12_11.19.39/suite.log.html"</code> + + <p>Surefire XML can for instance be used by Jenkins to display test + results.</p></cell> </row> </table> diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml index 7e33b71de1..8c3b13951d 100644 --- a/lib/common_test/doc/src/notes.xml +++ b/lib/common_test/doc/src/notes.xml @@ -32,6 +32,56 @@ <file>notes.xml</file> </header> +<section><title>Common_Test 1.6.3.1</title> + + <section><title>Known Bugs and Problems</title> + <list> + <item> + <p> + The following corrections/changes are done in the + cth_surefire hook:</p> + <p> + <list> <item> Earlier there would always be a + 'properties' element under the 'testsuites' element. This + would exist even if there were no 'property' element + inside it. This has been changed so if there are no + 'property' elements to display, then there will not be a + 'properties' element either. </item> <item> The XML file + will now (unless other is specified) be stored in the top + log directory. Earlier, the default directory would be + the current working directory for the erlang node, which + would mostly, but not always, be the top log directory. + </item> <item> The 'hostname' attribute in the + 'testsuite' element would earlier never have the correct + value. This has been corrected. </item> <item> The + 'errors' attribute in the 'testsuite' element would + earlier display the number of failed testcases. This has + been changed and will now always have the value 0, while + the 'failures' attribute will show the number of failed + testcases. </item> <item> A new attribute 'skipped' is + added to the 'testsuite' element. This will display the + number of skipped testcases. These would earlier be + included in the number of failed test cases. </item> + <item> The total number of tests displayed by the 'tests' + attribute in the 'testsuite' element would earlier + include init/end_per_suite and init/end_per_group. This + is no longer the case. The 'tests' attribute will now + only count "real" test cases. </item> <item> Earlier, + auto skipped test cases would have no value in the 'log' + attribute. This is now corrected. </item> <item> A new + attributes 'log' is added to the 'testsuite' element. + </item> <item> A new option named 'url_base' is added for + this hook. If this option is used, a new attribute named + 'url' will be added to the 'testcase' and 'testsuite' + elements. </item> </list></p> + <p> + Own Id: OTP-10589</p> + </item> + </list> + </section> + +</section> + <section><title>Common_Test 1.6.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml index b804f134c6..d5f5d89e05 100644 --- a/lib/common_test/doc/src/run_test_chapter.xml +++ b/lib/common_test/doc/src/run_test_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2012</year> + <year>2003</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -752,7 +752,9 @@ PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc EventHandlers = atom() | [atom()] InitArgs = [term()] - CTHModules = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}] + CTHModules = [CTHModule | + {CTHModule, CTHInitArgs} | + {CTHModule, CTHInitArgs, CTHPriority}] CTHModule = atom() CTHInitArgs = term() Dir = string() diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml index 248d7de8b6..cc8d913994 100644 --- a/lib/common_test/doc/src/write_test_chapter.xml +++ b/lib/common_test/doc/src/write_test_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2012</year> + <year>2003</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -982,38 +982,36 @@ <p>Example:</p> <pre> + Some printouts during test case execution: - Some printouts during test case execution: + io:format("1. Standard IO, importance = ~w~n", [?STD_IMPORTANCE]), + ct:log("2. Uncategorized, importance = ~w", [?STD_IMPORTANCE]), + ct:log(info, "3. Categorized info, importance = ~w", [?STD_IMPORTANCE]]), + ct:log(info, ?LOW_IMPORTANCE, "4. Categorized info, importance = ~w", [?LOW_IMPORTANCE]), + ct:log(error, "5. Categorized error, importance = ~w", [?HI_IMPORTANCE]), + ct:log(error, ?HI_IMPORTANCE, "6. Categorized error, importance = ~w", [?MAX_IMPORTANCE]), - io:format("1. Standard IO, importance = ~w~n", [?STD_IMPORTANCE]), - ct:log("2. Uncategorized, importance = ~w", [?STD_IMPORTANCE]), - ct:log(info, "3. Categorized info, importance = ~w", [?STD_IMPORTANCE]]), - ct:log(info, ?LOW_IMPORTANCE, "4. Categorized info, importance = ~w", [?LOW_IMPORTANCE]), - ct:log(error, "5. Categorized error, importance = ~w", [?HI_IMPORTANCE]), - ct:log(error, ?HI_IMPORTANCE, "6. Categorized error, importance = ~w", [?MAX_IMPORTANCE]), + If starting the test without specifying any verbosity levels: - If starting the test without specifying any verbosity levels: + $ ct_run ... - $ ct_run ... + the following gets printed: - the following gets printed: - - 1. Standard IO, importance = 50 - 2. Uncategorized, importance = 50 - 3. Categorized info, importance = 50 - 5. Categorized error, importance = 75 - 6. Categorized error, importance = 99 - - If starting the test with: - - $ ct_run -verbosity 1 and info 75 - - the following gets printed: + 1. Standard IO, importance = 50 + 2. Uncategorized, importance = 50 + 3. Categorized info, importance = 50 + 5. Categorized error, importance = 75 + 6. Categorized error, importance = 99 + + If starting the test with: + + $ ct_run -verbosity 1 and info 75 + + the following gets printed: - 3. Categorized info, importance = 50 - 4. Categorized info, importance = 25 - 6. Categorized error, importance = 99 - </pre> + 3. Categorized info, importance = 50 + 4. Categorized info, importance = 25 + 6. Categorized error, importance = 99</pre> <p>How categories can be mapped to CSS tags is documented in the <seealso marker="run_test_chapter#html_stylesheet">Running Tests</seealso> diff --git a/lib/common_test/src/ct_cover.erl b/lib/common_test/src/ct_cover.erl index d39f50ba00..ae671c750a 100644 --- a/lib/common_test/src/ct_cover.erl +++ b/lib/common_test/src/ct_cover.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 @@ -24,7 +24,7 @@ -module(ct_cover). --export([get_spec/1, add_nodes/1, remove_nodes/1]). +-export([get_spec/1, add_nodes/1, remove_nodes/1, cross_cover_analyse/2]). -include("ct_util.hrl"). @@ -100,6 +100,22 @@ remove_nodes(Nodes) -> %%%----------------------------------------------------------------- +%%% @spec cross_cover_analyse(Level,Tests) -> ok +%%% Level = overview | details +%%% Tests = [{Tag,Dir}] +%%% Tag = atom() +%%% Dir = string() +%%% +%%% @doc Accumulate cover results over multiple tests. +%%% See the chapter about <seealso +%%% marker="cover_chapter#cross_cover">cross cover +%%% analysis</seealso> in the users's guide. +%%% +cross_cover_analyse(Level,Tests) -> + test_server_ctrl:cross_cover_analyse(Level,Tests). + + +%%%----------------------------------------------------------------- %%% @hidden %% Read cover specification file and return the parsed info. @@ -249,9 +265,11 @@ get_app_info(App=#cover{app=Name}, [{excl_mods,Name,Mods1}|Terms]) -> Mods = App#cover.excl_mods, get_app_info(App#cover{excl_mods=Mods++Mods1},Terms); -get_app_info(App=#cover{app=Name}, [{cross_apps,Name,AppMods1}|Terms]) -> - AppMods = App#cover.cross, - get_app_info(App#cover{cross=AppMods++AppMods1},Terms); +get_app_info(App=#cover{app=none}, [{cross,Cross}|Terms]) -> + get_app_info(App, [{cross,none,Cross}|Terms]); +get_app_info(App=#cover{app=Name}, [{cross,Name,Cross1}|Terms]) -> + Cross = App#cover.cross, + get_app_info(App#cover{cross=Cross++Cross1},Terms); get_app_info(App=#cover{app=none}, [{src_dirs,Dirs}|Terms]) -> get_app_info(App, [{src_dirs,none,Dirs}|Terms]); @@ -354,10 +372,10 @@ remove_excludes_and_dups(CoverData=#cover{excl_mods=Excl,incl_mods=Incl}) -> files2mods(Info=#cover{excl_mods=ExclFs, incl_mods=InclFs, - cross=CrossFs}) -> + cross=Cross}) -> Info#cover{excl_mods=files2mods1(ExclFs), incl_mods=files2mods1(InclFs), - cross=files2mods1(CrossFs)}. + cross=[{Tag,files2mods1(Fs)} || {Tag,Fs} <- Cross]}. files2mods1([M|Fs]) when is_atom(M) -> [M|files2mods1(Fs)]; diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl index 11c8235040..1ccbc86d8f 100644 --- a/lib/common_test/src/ct_netconfc.erl +++ b/lib/common_test/src/ct_netconfc.erl @@ -1073,7 +1073,8 @@ handle_msg({get_event_streams=Op,Streams,Timeout}, From, State) -> SimpleXml = encode_rpc_operation(get,[Filter]), do_send_rpc(Op, SimpleXml, Timeout, From, State). -handle_msg({ssh_cm, _CM, {data, _Ch, _Type, Data}}, State) -> +handle_msg({ssh_cm, CM, {data, Ch, _Type, Data}}, State) -> + ssh_connection:adjust_window(CM,Ch,size(Data)), handle_data(Data, State); handle_msg({ssh_cm, _CM, _SshCloseMsg}, State) -> %% _SshCloseMsg can probably be one of @@ -1805,7 +1806,8 @@ get_tag([]) -> %%% SSH stuff ssh_receive_data() -> receive - {ssh_cm, _CM, {data, _Ch, _Type, Data}} -> + {ssh_cm, CM, {data, Ch, _Type, Data}} -> + ssh_connection:adjust_window(CM,Ch,size(Data)), {ok, Data}; {ssh_cm, _CM, {Closed, _Ch}} = X when Closed == closed; Closed == eof -> {error,X}; diff --git a/lib/common_test/src/ct_slave.erl b/lib/common_test/src/ct_slave.erl index 58633b7de6..1fd8c04f8b 100644 --- a/lib/common_test/src/ct_slave.erl +++ b/lib/common_test/src/ct_slave.erl @@ -449,15 +449,29 @@ wait_for_node_alive(Node, N) -> % call init:stop on a remote node do_stop(ENode) -> - case test_server:is_cover() of - true -> - MainCoverNode = cover:get_main_node(), - rpc:call(MainCoverNode,cover,flush,[ENode]); - false -> - ok + {Cover,MainCoverNode} = + case test_server:is_cover() of + true -> + Main = cover:get_main_node(), + rpc:call(Main,cover,flush,[ENode]), + {true,Main}; + false -> + {false,undefined} end, spawn(ENode, init, stop, []), - wait_for_node_dead(ENode, 5). + case wait_for_node_dead(ENode, 5) of + {ok,ENode} -> + if Cover -> + %% To avoid that cover is started again if a node + %% with the same name is started later. + rpc:call(MainCoverNode,cover,stop,[ENode]); + true -> + ok + end, + {ok,ENode}; + Error -> + Error + end. % wait N seconds until node is disconnected wait_for_node_dead(Node, 0) -> diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl index 76b0f0b5ea..e6eaad8d48 100644 --- a/lib/common_test/src/cth_surefire.erl +++ b/lib/common_test/src/cth_surefire.erl @@ -1,3 +1,22 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. 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% +%%-------------------------------------------------------------------- + %%% @doc Common Test Framework functions handling test specifications. %%% %%% <p>This module creates a junit report of the test run if plugged in @@ -27,18 +46,28 @@ -export([terminate/1]). -record(state, { filepath, axis, properties, package, hostname, - curr_suite, curr_suite_ts, curr_group = [], curr_tc, - curr_log_dir, timer, tc_log, + curr_suite, curr_suite_ts, curr_group = [], + curr_log_dir, timer, tc_log, url_base, test_cases = [], test_suites = [] }). --record(testcase, { log, group, classname, name, time, failure, timestamp }). --record(testsuite, { errors, failures, hostname, name, tests, +-record(testcase, { log, url, group, classname, name, time, result, timestamp }). +-record(testsuite, { errors, failures, skipped, hostname, name, tests, time, timestamp, id, package, - properties, testcases }). + properties, testcases, log, url }). + +-define(default_report,"junit_report.xml"). +-define(suite_log,"suite.log.html"). + +%% Number of dirs from log root to testcase log file. +%% ct_run.<node>.<timestamp>/<test_name>/run.<timestamp>/<tc_log>.html +-define(log_depth,3). id(Opts) -> - filename:absname(proplists:get_value(path, Opts, "junit_report.xml")). + case proplists:get_value(path, Opts) of + undefined -> ?default_report; + Path -> filename:absname(Path) + end. init(Path, Opts) -> {ok, Host} = inet:gethostname(), @@ -47,10 +76,24 @@ init(Path, Opts) -> package = proplists:get_value(package,Opts), axis = proplists:get_value(axis,Opts,[]), properties = proplists:get_value(properties,Opts,[]), + url_base = proplists:get_value(url_base,Opts), timer = now() }. pre_init_per_suite(Suite,Config,#state{ test_cases = [] } = State) -> - {Config, init_tc(State#state{ curr_suite = Suite, curr_suite_ts = now() }, + TcLog = proplists:get_value(tc_logfile,Config), + CurrLogDir = filename:dirname(TcLog), + Path = + case State#state.filepath of + ?default_report -> + RootDir = get_test_root(TcLog), + filename:join(RootDir,?default_report); + P -> + P + end, + {Config, init_tc(State#state{ filepath = Path, + curr_suite = Suite, + curr_suite_ts = now(), + curr_log_dir = CurrLogDir}, Config) }; pre_init_per_suite(Suite,Config,State) -> %% Have to close the previous suite @@ -59,7 +102,8 @@ pre_init_per_suite(Suite,Config,State) -> post_init_per_suite(_Suite,Config, Result, State) -> {Result, end_tc(init_per_suite,Config,Result,State)}. -pre_end_per_suite(_Suite,Config,State) -> {Config, init_tc(State, Config)}. +pre_end_per_suite(_Suite,Config,State) -> + {Config, init_tc(State, Config)}. post_end_per_suite(_Suite,Config,Result,State) -> {Result, end_tc(end_per_suite,Config,Result,State)}. @@ -71,13 +115,15 @@ pre_init_per_group(Group,Config,State) -> post_init_per_group(_Group,Config,Result,State) -> {Result, end_tc(init_per_group,Config,Result,State)}. -pre_end_per_group(_Group,Config,State) -> {Config, init_tc(State, Config)}. +pre_end_per_group(_Group,Config,State) -> + {Config, init_tc(State, Config)}. post_end_per_group(_Group,Config,Result,State) -> NewState = end_tc(end_per_group, Config, Result, State), {Result, NewState#state{ curr_group = tl(NewState#state.curr_group)}}. -pre_init_per_testcase(_TC,Config,State) -> {Config, init_tc(State, Config)}. +pre_init_per_testcase(_TC,Config,State) -> + {Config, init_tc(State, Config)}. post_end_per_testcase(TC,Config,Result,State) -> {Result, end_tc(TC,Config, Result,State)}. @@ -88,11 +134,19 @@ on_tc_fail(_TC, Res, State) -> TCs = State#state.test_cases, TC = hd(TCs), NewTC = TC#testcase{ - failure = + result = {fail,lists:flatten(io_lib:format("~p",[Res]))} }, State#state{ test_cases = [NewTC | tl(TCs)]}. -on_tc_skip(Tc,{Type,_Reason} = Res, State) when Type == tc_auto_skip -> +on_tc_skip(Tc,{Type,_Reason} = Res, State0) when Type == tc_auto_skip -> + TcStr = atom_to_list(Tc), + State = + case State0#state.test_cases of + [#testcase{name=TcStr}|TCs] -> + State0#state{test_cases=TCs}; + _ -> + State0 + end, do_tc_skip(Res, end_tc(Tc,[],Res,init_tc(State,[]))); on_tc_skip(_Tc, _Res, State = #state{test_cases = []}) -> State; @@ -103,7 +157,7 @@ do_tc_skip(Res, State) -> TCs = State#state.test_cases, TC = hd(TCs), NewTC = TC#testcase{ - failure = + result = {skipped,lists:flatten(io_lib:format("~p",[Res]))} }, State#state{ test_cases = [NewTC | tl(TCs)]}. @@ -117,33 +171,52 @@ end_tc(Func, Config, Res, State) when is_atom(Func) -> end_tc(atom_to_list(Func), Config, Res, State); end_tc(Name, _Config, _Res, State = #state{ curr_suite = Suite, curr_group = Groups, - timer = TS, tc_log = Log } ) -> + curr_log_dir = CurrLogDir, + timer = TS, + tc_log = Log0, + url_base = UrlBase } ) -> + Log = + case Log0 of + "" -> + LowerSuiteName = string:to_lower(atom_to_list(Suite)), + filename:join(CurrLogDir,LowerSuiteName++"."++Name++".html"); + _ -> + Log0 + end, + Url = make_url(UrlBase,Log), ClassName = atom_to_list(Suite), PGroup = string:join([ atom_to_list(Group)|| Group <- lists:reverse(Groups)],"."), TimeTakes = io_lib:format("~f",[timer:now_diff(now(),TS) / 1000000]), State#state{ test_cases = [#testcase{ log = Log, + url = Url, timestamp = now_to_string(TS), classname = ClassName, group = PGroup, name = Name, time = TimeTakes, - failure = passed }| State#state.test_cases]}. + result = passed }| + State#state.test_cases], + tc_log = ""}. % so old tc_log is not set if next is on_tc_skip close_suite(#state{ test_cases = [] } = State) -> State; -close_suite(#state{ test_cases = TCs } = State) -> - Total = length(TCs), - Succ = length(lists:filter(fun(#testcase{ failure = F }) -> - F == passed - end,TCs)), - Fail = Total - Succ, +close_suite(#state{ test_cases = TCs, url_base = UrlBase } = State) -> + {Total,Fail,Skip} = count_tcs(TCs,0,0,0), TimeTaken = timer:now_diff(now(),State#state.curr_suite_ts) / 1000000, + SuiteLog = filename:join(State#state.curr_log_dir,?suite_log), + SuiteUrl = make_url(UrlBase,SuiteLog), Suite = #testsuite{ name = atom_to_list(State#state.curr_suite), package = State#state.package, + hostname = State#state.hostname, time = io_lib:format("~f",[TimeTaken]), timestamp = now_to_string(State#state.curr_suite_ts), - errors = Fail, tests = Total, - testcases = lists:reverse(TCs) }, + errors = 0, + failures = Fail, + skipped = Skip, + tests = Total, + testcases = lists:reverse(TCs), + log = SuiteLog, + url = SuiteUrl}, State#state{ test_cases = [], test_suites = [Suite | State#state.test_suites]}. @@ -159,14 +232,15 @@ terminate(State) -> -to_xml(#testcase{ group = Group, classname = CL, log = L, name = N, time = T, timestamp = TS, failure = F}) -> +to_xml(#testcase{ group = Group, classname = CL, log = L, url = U, name = N, time = T, timestamp = TS, result = R}) -> ["<testcase ", - [["group=\"",Group,"\""]||Group /= ""]," " + [["group=\"",Group,"\" "]||Group /= ""], "name=\"",N,"\" " "time=\"",T,"\" " - "timestamp=\"",TS,"\" " + "timestamp=\"",TS,"\" ", + [["url=\"",U,"\" "]||U /= undefined], "log=\"",L,"\">", - case F of + case R of passed -> []; {skipped,Reason} -> @@ -176,22 +250,29 @@ to_xml(#testcase{ group = Group, classname = CL, log = L, name = N, time = T, ti ["<failure message=\"Test ",N," in ",CL," failed!\" type=\"crash\">", sanitize(Reason),"</failure>"] end,"</testcase>"]; -to_xml(#testsuite{ package = P, hostname = H, errors = E, time = Time, - timestamp = TS, tests = T, name = N, testcases = Cases }) -> +to_xml(#testsuite{ package = P, hostname = H, errors = E, failures = F, + skipped = S, time = Time, timestamp = TS, tests = T, name = N, + testcases = Cases, log = Log, url = Url }) -> ["<testsuite ", [["package=\"",P,"\" "]||P /= undefined], - [["hostname=\"",P,"\" "]||H /= undefined], - [["name=\"",N,"\" "]||N /= undefined], - [["time=\"",Time,"\" "]||Time /= undefined], - [["timestamp=\"",TS,"\" "]||TS /= undefined], + "hostname=\"",H,"\" " + "name=\"",N,"\" " + "time=\"",Time,"\" " + "timestamp=\"",TS,"\" " "errors=\"",integer_to_list(E),"\" " - "tests=\"",integer_to_list(T),"\">", + "failures=\"",integer_to_list(F),"\" " + "skipped=\"",integer_to_list(S),"\" " + "tests=\"",integer_to_list(T),"\" ", + [["url=\"",Url,"\" "]||Url /= undefined], + "log=\"",Log,"\">", [to_xml(Case) || Case <- Cases], "</testsuite>"]; to_xml(#state{ test_suites = TestSuites, axis = Axis, properties = Props }) -> ["<testsuites>",properties_to_xml(Axis,Props), [to_xml(TestSuite) || TestSuite <- TestSuites],"</testsuites>"]. +properties_to_xml([],[]) -> + []; properties_to_xml(Axis,Props) -> ["<properties>", [["<property name=\"",Name,"\" axis=\"yes\" value=\"",Value,"\" />"] || {Name,Value} <- Axis], @@ -217,3 +298,37 @@ sanitize([]) -> now_to_string(Now) -> {{YY,MM,DD},{HH,Mi,SS}} = calendar:now_to_local_time(Now), io_lib:format("~p-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B",[YY,MM,DD,HH,Mi,SS]). + +make_url(undefined,_) -> + undefined; +make_url(_,[]) -> + undefined; +make_url(UrlBase0,Log) -> + UrlBase = string:strip(UrlBase0,right,$/), + RelativeLog = get_relative_log_url(Log), + string:join([UrlBase,RelativeLog],"/"). + +get_test_root(Log) -> + LogParts = filename:split(Log), + filename:join(lists:sublist(LogParts,1,length(LogParts)-?log_depth)). + +get_relative_log_url(Log) -> + LogParts = filename:split(Log), + Start = length(LogParts)-?log_depth, + Length = ?log_depth+1, + string:join(lists:sublist(LogParts,Start,Length),"/"). + +count_tcs([#testcase{name=ConfCase}|TCs],Ok,F,S) + when ConfCase=="init_per_suite"; + ConfCase=="end_per_suite"; + ConfCase=="init_per_group"; + ConfCase=="end_per_group" -> + count_tcs(TCs,Ok,F,S); +count_tcs([#testcase{result=passed}|TCs],Ok,F,S) -> + count_tcs(TCs,Ok+1,F,S); +count_tcs([#testcase{result={fail,_}}|TCs],Ok,F,S) -> + count_tcs(TCs,Ok,F+1,S); +count_tcs([#testcase{result={skipped,_}}|TCs],Ok,F,S) -> + count_tcs(TCs,Ok,F,S+1); +count_tcs([],Ok,F,S) -> + {Ok+F+S,F,S}. diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index df816f9a61..d469d03e04 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -56,7 +56,8 @@ MODULES= \ ct_snmp_SUITE \ ct_group_leader_SUITE \ ct_cover_SUITE \ - ct_groups_search_SUITE + ct_groups_search_SUITE \ + ct_surefire_SUITE ERL_FILES= $(MODULES:%=%.erl) diff --git a/lib/common_test/test/common_test.cover b/lib/common_test/test/common_test.cover index 66697854ea..3aa49623e7 100644 --- a/lib/common_test/test/common_test.cover +++ b/lib/common_test/test/common_test.cover @@ -1,10 +1,10 @@ %% -*- erlang -*- {incl_app,common_test,details}. -{cross_apps,common_test,[erl2html2, - test_server, - test_server_ctrl, - test_server_gl, - test_server_h, - test_server_io, - test_server_node, - test_server_sup]}. +{cross,common_test,[{test_server,[erl2html2, + test_server, + test_server_ctrl, + test_server_gl, + test_server_h, + test_server_io, + test_server_node, + test_server_sup]}]}. diff --git a/lib/common_test/test/ct_cover_SUITE.erl b/lib/common_test/test/ct_cover_SUITE.erl index bebfce70d0..cb49dc423f 100644 --- a/lib/common_test/test/ct_cover_SUITE.erl +++ b/lib/common_test/test/ct_cover_SUITE.erl @@ -77,7 +77,8 @@ all() -> slave_start_slave, cover_node_option, ct_cover_add_remove_nodes, - otp_9956 + otp_9956, + cross ]. %%-------------------------------------------------------------------- @@ -161,6 +162,43 @@ otp_9956(Config) -> check_calls(Events,{?suite,otp_9956,1},1), ok. +%% Test cross cover mechanism +cross(Config) -> + {ok,Events1} = run_test(cross1,Config), + check_calls(Events1,1), + + CoverFile2 = create_cover_file(cross1,[{cross,[{cross1,[?mod]}]}],Config), + {ok,Events2} = run_test(cross2,[{cover,CoverFile2}],Config), + check_calls(Events2,1), + + %% Get the log dirs for each test and run cross cover analyse + [D11,D12] = lists:sort(get_run_dirs(Events1)), + [D21,D22] = lists:sort(get_run_dirs(Events2)), + + ct_cover:cross_cover_analyse(details,[{cross1,D11},{cross2,D21}]), + ct_cover:cross_cover_analyse(details,[{cross1,D12},{cross2,D22}]), + + %% Get the cross cover logs and read for each test + [C11,C12,C21,C22] = + [filename:join(D,"cross_cover.html") || D <- [D11,D12,D21,D22]], + + {ok,CrossData} = file:read_file(C11), + {ok,CrossData} = file:read_file(C12), + + {ok,Def} = file:read_file(C21), + {ok,Def} = file:read_file(C22), + + %% A simple test: just check that the test module exists in the + %% log from cross1 test, and that it does not exist in the log + %% from cross2 test. + TestMod = list_to_binary(atom_to_list(?mod)), + {_,_} = binary:match(CrossData,TestMod), + nomatch = binary:match(Def,TestMod), + {_,_} = binary:match(Def, + <<"No cross cover modules exist for this application">>), + + ok. + %%%----------------------------------------------------------------- %%% HELP FUNCTIONS @@ -229,15 +267,18 @@ check_cover(Node) when is_atom(Node) -> false end. +%% Get the log dir "run.<timestamp>" for all (both!) tests +get_run_dirs(Events) -> + [filename:dirname(TCLog) || + {ct_test_support_eh, + {event,tc_logfile,_Node, + {{?suite,init_per_suite},TCLog}}} <- Events]. + %% Check that each coverlog includes N calls to ?mod:foo/0 check_calls(Events,N) -> check_calls(Events,{?mod,foo,0},N). check_calls(Events,MFA,N) -> - CoverLogs = - [filename:join(filename:dirname(TCLog),"all.coverdata") || - {ct_test_support_eh, - {event,tc_logfile,ct@falco, - {{?suite,init_per_suite},TCLog}}} <- Events], + CoverLogs = [filename:join(D,"all.coverdata") || D <- get_run_dirs(Events)], do_check_logs(CoverLogs,MFA,N). do_check_logs([CoverLog|CoverLogs],{Mod,_,_} = MFA,N) -> diff --git a/lib/common_test/test/ct_surefire_SUITE.erl b/lib/common_test/test/ct_surefire_SUITE.erl new file mode 100644 index 0000000000..69e98cef48 --- /dev/null +++ b/lib/common_test/test/ct_surefire_SUITE.erl @@ -0,0 +1,351 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. 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% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_surefire_SUITE +%%% +%%% Description: +%%% Test cth_surefire hook +%%% +%%%------------------------------------------------------------------- +-module(ct_surefire_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-include_lib("xmerl/include/xmerl.hrl"). + +-define(eh, ct_test_support_eh). + +-define(url_base,"http://my.host.com/"). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config1 = ct_test_support:init_per_suite(Config), + Config1. + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + ct_test_support:end_per_testcase(TestCase, Config). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + default, + absolute_path, + relative_path, + url, + logdir + ]. + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +default(Config) when is_list(Config) -> + run(default,[cth_surefire],Config), + PrivDir = ?config(priv_dir,Config), + XmlRe = filename:join([PrivDir,"*","junit_report.xml"]), + check_xml(default,XmlRe). + +absolute_path(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir,Config), + Path = filename:join(PrivDir,"abspath.xml"), + run(absolute_path,[{cth_surefire,[{path,Path}]}],Config), + check_xml(absolute_path,Path). + +relative_path(Config) when is_list(Config) -> + Path = "relpath.xml", + run(relative_path,[{cth_surefire,[{path,Path}]}],Config), + PrivDir = ?config(priv_dir,Config), + XmlRe = filename:join([PrivDir,"*",Path]), + check_xml(relative_path,XmlRe). + +url(Config) when is_list(Config) -> + Path = "url.xml", + run(url,[{cth_surefire,[{url_base,?url_base}, + {path,Path}]}],Config), + PrivDir = ?config(priv_dir,Config), + XmlRe = filename:join([PrivDir,"*",Path]), + check_xml(url,XmlRe). + +logdir(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir,Config), + LogDir = filename:join(PrivDir,"specific_logdir"), + file:make_dir(LogDir), + Path = "logdir.xml", + run(logdir,[{cth_surefire,[{path,Path}]}],Config,[{logdir,LogDir}]), + PrivDir = ?config(priv_dir,Config), + XmlRe = filename:join([LogDir,"*",Path]), + check_xml(logdir,XmlRe). + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- +run(Case,CTHs,Config) -> + run(Case,CTHs,Config,[]). +run(Case,CTHs,Config,ExtraOpts) -> + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "surefire_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{ct_hooks,CTHs},{label,Case}|ExtraOpts], + Config), + ok = execute(Case, Opts, ERPid, Config). + +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Opts1 = + case lists:keymember(logdir,1,Test) of + true -> lists:keydelete(logdir,1,Opts0); + false -> Opts0 + end, + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts1 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +execute(Name, Opts, ERPid, Config) -> + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(Name, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + TestEvents = events_to_check(Name), + ct_test_support:verify_events(TestEvents, Events, Config). + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). + +%%%----------------------------------------------------------------- +%%% TEST EVENTS +%%%----------------------------------------------------------------- +events_to_check(Test) -> + %% 2 tests (ct:run_test + script_start) is default + events_to_check(Test, 2). + +events_to_check(_, 0) -> + []; +events_to_check(Test, N) -> + test_events(Test) ++ events_to_check(Test, N-1). + +test_events(_) -> + [{?eh,start_logging,'_'}, + {?eh,start_info,{1,1,9}}, + {?eh,tc_start,{surefire_SUITE,init_per_suite}}, + {?eh,tc_done,{surefire_SUITE,init_per_suite,ok}}, + {?eh,tc_start,{surefire_SUITE,tc_ok}}, + {?eh,tc_done,{surefire_SUITE,tc_ok,ok}}, + {?eh,test_stats,{1,0,{0,0}}}, + {?eh,tc_start,{surefire_SUITE,tc_fail}}, + {?eh,tc_done,{surefire_SUITE,tc_fail, + {failed,{error,{test_case_failed,"this test should fail"}}}}}, + {?eh,test_stats,{1,1,{0,0}}}, + {?eh,tc_start,{surefire_SUITE,tc_skip}}, + {?eh,tc_done,{surefire_SUITE,tc_skip,{skipped,"this test is skipped"}}}, + {?eh,test_stats,{1,1,{1,0}}}, + {?eh,tc_start,{surefire_SUITE,tc_autoskip_require}}, + {?eh,tc_done,{surefire_SUITE,tc_autoskip_require, + {skipped,{require_failed,'_'}}}}, + {?eh,test_stats,{1,1,{1,1}}}, + [{?eh,tc_start,{surefire_SUITE,{init_per_group,g,[]}}}, + {?eh,tc_done,{surefire_SUITE,{init_per_group,g,[]},ok}}, + {?eh,tc_start,{surefire_SUITE,tc_ok}}, + {?eh,tc_done,{surefire_SUITE,tc_ok,ok}}, + {?eh,test_stats,{2,1,{1,1}}}, + {?eh,tc_start,{surefire_SUITE,tc_fail}}, + {?eh,tc_done,{surefire_SUITE,tc_fail, + {failed,{error,{test_case_failed,"this test should fail"}}}}}, + {?eh,test_stats,{2,2,{1,1}}}, + {?eh,tc_start,{surefire_SUITE,tc_skip}}, + {?eh,tc_done,{surefire_SUITE,tc_skip,{skipped,"this test is skipped"}}}, + {?eh,test_stats,{2,2,{2,1}}}, + {?eh,tc_start,{surefire_SUITE,tc_autoskip_require}}, + {?eh,tc_done,{surefire_SUITE,tc_autoskip_require, + {skipped,{require_failed,'_'}}}}, + {?eh,test_stats,{2,2,{2,2}}}, + {?eh,tc_start,{surefire_SUITE,{end_per_group,g,[]}}}, + {?eh,tc_done,{surefire_SUITE,{end_per_group,g,[]},ok}}], + [{?eh,tc_start,{surefire_SUITE,{init_per_group,g_fail,[]}}}, + {?eh,tc_done,{surefire_SUITE,{init_per_group,g_fail,[]}, + {failed,{error,all_cases_should_be_skipped}}}}, + {?eh,tc_auto_skip,{surefire_SUITE,tc_ok, + {failed, + {surefire_SUITE,init_per_group, + {'EXIT',all_cases_should_be_skipped}}}}}, + {?eh,test_stats,{2,2,{2,3}}}, + {?eh,tc_auto_skip,{surefire_SUITE,end_per_group, + {failed, + {surefire_SUITE,init_per_group, + {'EXIT',all_cases_should_be_skipped}}}}}], + {?eh,tc_start,{surefire_SUITE,end_per_suite}}, + {?eh,tc_done,{surefire_SUITE,end_per_suite,ok}}, + {?eh,stop_logging,[]}]. + + +%%%----------------------------------------------------------------- +%%% Check generated xml log files +check_xml(Case,XmlRe) -> + case filelib:wildcard(XmlRe) of + [] -> + ct:fail("No xml files found with regexp ~p~n", [XmlRe]); + [_] = Xmls when Case==absolute_path -> + do_check_xml(Case,Xmls); + [_,_] = Xmls -> + do_check_xml(Case,Xmls) + end. + +%% Allowed structure: +%% <testsuites> +%% <testsuite> +%% <properties> +%% <property/> +%% ... +%% </properties> +%% <testcase> +%% [<failure/> | <error/> | <skipped/> ] +%% </testcase> +%% ... +%% </testsuite> +%% ... +%% </testsuites> +do_check_xml(Case,[Xml|Xmls]) -> + ct:log("Checking <a href=~p>~s</a>~n",[Xml,Xml]), + {E,_} = xmerl_scan:file(Xml), + Expected = events_to_result(lists:flatten(test_events(Case))), + ParseResult = testsuites(Case,E), + ct:log("Expecting: ~p~n",[[Expected]]), + ct:log("Actual : ~p~n",[ParseResult]), + [Expected] = ParseResult, + do_check_xml(Case,Xmls); +do_check_xml(_,[]) -> + ok. + +%% Scanning the XML to get the same type of result as events_to_result/1 +testsuites(Case,#xmlElement{name=testsuites,content=TS}) -> + %% OTP-10589 - move properties element to <testsuite> + false = lists:keytake(properties,#xmlElement.name,TS), + testsuite(Case,TS). + +testsuite(Case,[#xmlElement{name=testsuite,content=TC,attributes=A}|TS]) -> + {ET,EF,ES} = events_to_numbers(lists:flatten(test_events(Case))), + {T,E,F,S} = get_numbers_from_attrs(A,false,false,false,false), + ct:log("Expecting total:~p, error:~p, failure:~p, skipped:~p~n",[ET,0,EF,ES]), + ct:log("Actual total:~p, error:~p, failure:~p, skipped:~p~n",[T,E,F,S]), + {ET,0,EF,ES} = {T,E,F,S}, + + %% properties should only be there if given a options to hook + false = lists:keytake(properties,#xmlElement.name,TC), + %% system-out and system-err is not used by common_test + false = lists:keytake('system-out',#xmlElement.name,TC), + false = lists:keytake('system-err',#xmlElement.name,TC), + R=testcase(Case,TC), + [R|testsuite(Case,TS)]; +testsuite(_Case,[]) -> + []. + +testcase(url=Case,[#xmlElement{name=testcase,attributes=A,content=C}|TC]) -> + R = failed_or_skipped(C), + case R of + [s] -> + case lists:keyfind(url,#xmlAttribute.name,A) of + false -> ok; + #xmlAttribute{value=UrlAttr} -> + lists:keyfind(url,#xmlAttribute.name,A), + true = lists:prefix(?url_base,UrlAttr) + end; + _ -> + #xmlAttribute{value=UrlAttr} = + lists:keyfind(url,#xmlAttribute.name,A), + true = lists:prefix(?url_base,UrlAttr) + end, + [R|testcase(Case,TC)]; +testcase(Case,[#xmlElement{name=testcase,attributes=A,content=C}|TC]) -> + false = lists:keyfind(url,#xmlAttribute.name,A), + R = failed_or_skipped(C), + [R|testcase(Case,TC)]; +testcase(_Case,[]) -> + []. + +failed_or_skipped([#xmlElement{name=failure}|E]) -> + [f|failed_or_skipped(E)]; +failed_or_skipped([#xmlElement{name=error}|E]) -> + [e|failed_or_skipped(E)]; +failed_or_skipped([#xmlElement{name=skipped}|E]) -> + [s|failed_or_skipped(E)]; +failed_or_skipped([]) -> + []. + +%% Using the expected events to produce the expected result of the XML scanning. +%% The result is a list of test suites: +%% Testsuites = [Testsuite] +%% Testsuite = [Testcase] +%% Testcase = [] | [f] | [s], indicating ok, failed and skipped respectively +events_to_result([{?eh,tc_done,{_Suite,_Case,R}}|E]) -> + [result(R)|events_to_result(E)]; +events_to_result([{?eh,tc_auto_skip,_}|E]) -> + [[s]|events_to_result(E)]; +events_to_result([_|E]) -> + events_to_result(E); +events_to_result([]) -> + []. + +result(ok) ->[]; +result({skipped,_}) -> [s]; +result({failed,_}) -> [f]. + +%% Using the expected events' last test_stats element to produce the +%% expected number of totla, errors, failed and skipped testcases. +events_to_numbers(E) -> + RevE = lists:reverse(E), + {?eh,test_stats,{Ok,F,{US,AS}}} = lists:keyfind(test_stats,2,RevE), + {Ok+F+US+AS,F,US+AS}. + +get_numbers_from_attrs([#xmlAttribute{name=tests,value=X}|A],false,E,F,S) -> + get_numbers_from_attrs(A,list_to_integer(X),E,F,S); +get_numbers_from_attrs([#xmlAttribute{name=errors,value=X}|A],T,false,F,S) -> + get_numbers_from_attrs(A,T,list_to_integer(X),F,S); +get_numbers_from_attrs([#xmlAttribute{name=failures,value=X}|A],T,E,false,S) -> + get_numbers_from_attrs(A,T,E,list_to_integer(X),S); +get_numbers_from_attrs([#xmlAttribute{name=skipped,value=X}|A],T,E,F,false) -> + get_numbers_from_attrs(A,T,E,F,list_to_integer(X)); +get_numbers_from_attrs([_|A],T,E,F,S) -> + get_numbers_from_attrs(A,T,E,F,S); +get_numbers_from_attrs([],T,E,F,S) -> + {T,E,F,S}. diff --git a/lib/common_test/test/ct_surefire_SUITE_data/surefire_SUITE.erl b/lib/common_test/test/ct_surefire_SUITE_data/surefire_SUITE.erl new file mode 100644 index 0000000000..677aee46c5 --- /dev/null +++ b/lib/common_test/test/ct_surefire_SUITE_data/surefire_SUITE.erl @@ -0,0 +1,92 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. 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% +%% +%%---------------------------------------------------------------------- +%% File: surefire_SUITE.erl +%% +%% Description: +%% This file contains the test cases for cth_surefire. +%% +%% @author Support +%% @doc Test of surefire support in common_test +%% @end +%%---------------------------------------------------------------------- +%%---------------------------------------------------------------------- +-module(surefire_SUITE). +-include_lib("common_test/include/ct.hrl"). + +-compile(export_all). + +%% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(1)). + +all() -> + testcases() ++ [{group,g},{group,g_fail}]. + +groups() -> + [{g,testcases()}, + {g_fail,[tc_ok]}]. + +testcases() -> + [tc_ok, + tc_fail, + tc_skip, + tc_autoskip_require]. + +init_per_suite(Config) -> + Config. + +end_per_suite(Config) -> + Config. + +init_per_group(g_fail, _Config) -> + exit(all_cases_should_be_skipped); +init_per_group(_, Config) -> + Config. + +end_per_group(_Group, Config) -> + Config. + +init_per_testcase(_Case, Config) -> + Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +%%%----------------------------------------------------------------- +%%% Test cases +break(_Config) -> + test_server:break(""), + ok. + +tc_ok(_Config) -> + ok. + +tc_fail(_Config) -> + ct:fail("this test should fail"). + +tc_skip(_Config) -> + {skip,"this test is skipped"}. + +tc_autoskip_require() -> + [{require,whatever}]. +tc_autoskip_require(Config) -> + ct:fail("this test should never be executed - it should be autoskipped"). diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index e5e2e68fcb..fc572aa82f 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -117,11 +117,7 @@ end_per_suite(Config) -> CTNode = proplists:get_value(ct_node, Config), PrivDir = proplists:get_value(priv_dir, Config), true = rpc:call(CTNode, code, del_path, [filename:join(PrivDir,"")]), - case test_server:is_cover() of - true -> cover:flush(CTNode); - false -> ok - end, - slave:stop(CTNode), + slave_stop(CTNode), ok. %%%----------------------------------------------------------------- @@ -152,11 +148,7 @@ end_per_testcase(_TestCase, Config) -> case wait_for_ct_stop(CTNode) of %% Common test was not stopped to we restart node. false -> - case test_server:is_cover() of - true -> cover:flush(CTNode); - false -> ok - end, - slave:stop(CTNode), + slave_stop(CTNode), start_slave(Config,proplists:get_value(trace_level,Config)), {fail, "Could not stop common_test"}; true -> @@ -1274,3 +1266,22 @@ rm_files([F | Fs]) -> rm_files([]) -> ok. +%%%----------------------------------------------------------------- +%%% +slave_stop(Node) -> + Cover = test_server:is_cover(), + if Cover-> cover:flush(Node); + true -> ok + end, + erlang:monitor_node(Node, true), + slave:stop(Node), + receive + {nodedown, Node} -> + if Cover -> cover:stop(Node); + true -> ok + end + after 5000 -> + erlang:monitor_node(Node, false), + receive {nodedown, Node} -> ok after 0 -> ok end %flush + end, + ok. diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index df1af36eeb..10e7f5e9ce 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -248,6 +248,7 @@ format_error({module_name,Mod,Filename}) -> abstract_code=[], %Abstract code for debugger. options=[] :: [option()], %Options for compilation mod_options=[] :: [option()], %Options for module_info + encoding=none :: none | epp:source_coding(), errors=[], warnings=[]}). @@ -734,8 +735,9 @@ collect_asm([X | Rest], R) -> beam_consult_asm(St) -> case file:consult(St#compile.ifile) of {ok, Forms0} -> + Encoding = epp:read_encoding(St#compile.ifile), {Module, Forms} = preprocess_asm_forms(Forms0), - {ok,St#compile{module=Module, code=Forms}}; + {ok,St#compile{module=Module, code=Forms, encoding=Encoding}}; {error,E} -> Es = [{St#compile.ifile,[{none,?MODULE,{open,E}}]}], {error,St#compile{errors=St#compile.errors ++ Es}} @@ -777,7 +779,8 @@ parse_module(St) -> R = epp:parse_file(St#compile.ifile, IncludePath, pre_defs(Opts)), case R of {ok,Forms} -> - {ok,St#compile{code=Forms}}; + Encoding = epp:read_encoding(St#compile.ifile), + {ok,St#compile{code=Forms,encoding=Encoding}}; {error,E} -> Es = [{St#compile.ifile,[{none,?MODULE,{epp,E}}]}], {error,St#compile{errors=St#compile.errors ++ Es}} @@ -1339,16 +1342,12 @@ save_binary(#compile{code=none}=St) -> {ok,St}; save_binary(#compile{module=Mod,ofile=Outfile, options=Opts}=St) -> %% Test that the module name and output file name match. - %% We must take care to not completely break a packaged module - %% (even though packages still is as an experimental, unsupported - %% feature) - so we will extract the last part of a packaged - %% module name and compare only that. case member(no_error_module_mismatch, Opts) of true -> save_binary_1(St); false -> Base = filename:rootname(filename:basename(Outfile)), - case lists:last(packages:split(Mod)) of + case atom_to_list(Mod) of Base -> save_binary_1(St); _ -> @@ -1424,28 +1423,28 @@ report_warnings(#compile{options=Opts,warnings=Ws0}) -> end. format_message(F, P, [{{Line,Column}=Loc,Mod,E}|Es]) -> - M = {{F,Loc},io_lib:format("~s:~w:~w ~s~s\n", + M = {{F,Loc},io_lib:format("~s:~w:~w ~s~ts\n", [F,Line,Column,P,Mod:format_error(E)])}, [M|format_message(F, P, Es)]; format_message(F, P, [{Line,Mod,E}|Es]) -> - M = {{F,{Line,0}},io_lib:format("~s:~w: ~s~s\n", + M = {{F,{Line,0}},io_lib:format("~s:~w: ~s~ts\n", [F,Line,P,Mod:format_error(E)])}, [M|format_message(F, P, Es)]; format_message(F, P, [{Mod,E}|Es]) -> - M = {none,io_lib:format("~s: ~s~s\n", [F,P,Mod:format_error(E)])}, + M = {none,io_lib:format("~s: ~s~ts\n", [F,P,Mod:format_error(E)])}, [M|format_message(F, P, Es)]; format_message(_, _, []) -> []. %% list_errors(File, ErrorDescriptors) -> ok list_errors(F, [{{Line,Column},Mod,E}|Es]) -> - io:fwrite("~s:~w:~w: ~s\n", [F,Line,Column,Mod:format_error(E)]), + io:fwrite("~s:~w:~w: ~ts\n", [F,Line,Column,Mod:format_error(E)]), list_errors(F, Es); list_errors(F, [{Line,Mod,E}|Es]) -> - io:fwrite("~s:~w: ~s\n", [F,Line,Mod:format_error(E)]), + io:fwrite("~s:~w: ~ts\n", [F,Line,Mod:format_error(E)]), list_errors(F, Es); list_errors(F, [{Mod,E}|Es]) -> - io:fwrite("~s: ~s\n", [F,Mod:format_error(E)]), + io:fwrite("~s: ~ts\n", [F,Mod:format_error(E)]), list_errors(F, Es); list_errors(_F, []) -> ok. @@ -1501,10 +1500,12 @@ src_listing(Ext, St) -> Ext, St). do_src_listing(Lf, Fs) -> - foreach(fun (F) -> io:put_chars(Lf, [erl_pp:form(F),"\n"]) end, + Opts = [lists:keyfind(encoding, 1, io:getopts(Lf))], + foreach(fun (F) -> io:put_chars(Lf, [erl_pp:form(F, Opts),"\n"]) end, Fs). -listing(Ext, St) -> +listing(Ext, St0) -> + St = St0#compile{encoding = none}, listing(fun(Lf, Fs) -> beam_listing:module(Lf, Fs) end, Ext, St). listing(LFun, Ext, St) -> @@ -1512,6 +1513,7 @@ listing(LFun, Ext, St) -> case file:open(Lfile, [write,delayed_write]) of {ok,Lf} -> Code = restore_expanded_types(Ext, St#compile.code), + output_encoding(Lf, St), LFun(Lf, Code), ok = file:close(Lf), {ok,St}; @@ -1520,6 +1522,12 @@ listing(LFun, Ext, St) -> {error,St#compile{errors=St#compile.errors ++ Es}} end. +output_encoding(F, #compile{encoding = none}) -> + ok = io:setopts(F, [{encoding, epp:default_encoding()}]); +output_encoding(F, #compile{encoding = Encoding}) -> + ok = io:setopts(F, [{encoding, Encoding}]), + ok = io:fwrite(F, <<"%% ~s\n">>, [epp:encoding_to_string(Encoding)]). + restore_expanded_types("P", Fs) -> epp:restore_typed_record_fields(Fs); restore_expanded_types("E", {M,I,Fs0}) -> diff --git a/lib/compiler/src/core_scan.erl b/lib/compiler/src/core_scan.erl index 5aab8ae855..0ca2f57dde 100644 --- a/lib/compiler/src/core_scan.erl +++ b/lib/compiler/src/core_scan.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. 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 @@ -31,16 +32,16 @@ %% 173 - 176 { - ~ punctuation %% 177 DEL control %% 200 - 237 control -%% 240 - 277 NBSP - � punctuation -%% 300 - 326 � - � uppercase -%% 327 � punctuation -%% 330 - 336 � - � uppercase -%% 337 - 366 � - � lowercase -%% 367 � punctuation -%% 370 - 377 � - � lowercase +%% 240 - 277 NBSP - ¿ punctuation +%% 300 - 326 À - Ö uppercase +%% 327 × punctuation +%% 330 - 336 Ø - Þ uppercase +%% 337 - 366 ß - ö lowercase +%% 367 ÷ punctuation +%% 370 - 377 ø - ÿ lowercase %% %% Many punctuation characters region have special meaning. Must -%% watch using � \327, bvery close to x \170 +%% watch using × \327, bvery close to x \170 -module(core_scan). @@ -239,11 +240,11 @@ scan1([C|Cs], Toks, Pos) when C >= $\200, C =< $\240 -> scan1(Cs, Toks, Pos); scan1([C|Cs], Toks, Pos) when C >= $a, C =< $z -> %Keywords scan_key_word(C, Cs, Toks, Pos); -scan1([C|Cs], Toks, Pos) when C >= $�, C =< $�, C /= $� -> +scan1([C|Cs], Toks, Pos) when C >= $ß, C =< $ÿ, C /= $÷ -> scan_key_word(C, Cs, Toks, Pos); scan1([C|Cs], Toks, Pos) when C >= $A, C =< $Z -> %Variables scan_variable(C, Cs, Toks, Pos); -scan1([C|Cs], Toks, Pos) when C >= $�, C =< $�, C /= $� -> +scan1([C|Cs], Toks, Pos) when C >= $À, C =< $Þ, C /= $× -> scan_variable(C, Cs, Toks, Pos); scan1([C|Cs], Toks, Pos) when C >= $0, C =< $9 -> %Numbers scan_number(C, Cs, Toks, Pos); @@ -308,9 +309,9 @@ scan_name([], Ncs) -> {Ncs,[]}. name_char(C) when C >= $a, C =< $z -> true; -name_char(C) when C >= $�, C =< $�, C /= $� -> true; +name_char(C) when C >= $ß, C =< $ÿ, C /= $÷ -> true; name_char(C) when C >= $A, C =< $Z -> true; -name_char(C) when C >= $�, C =< $�, C /= $� -> true; +name_char(C) when C >= $À, C =< $Þ, C /= $× -> true; name_char(C) when C >= $0, C =< $9 -> true; name_char($_) -> true; name_char($@) -> true; diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl index e55fb2a037..a8c69c3cb1 100644 --- a/lib/compiler/src/sys_pre_expand.erl +++ b/lib/compiler/src/sys_pre_expand.erl @@ -35,10 +35,8 @@ -record(expand, {module=[], %Module name parameters=undefined, %Module parameters - package="", %Module package exports=[], %Exports imports=[], %Imports - mod_imports, %Module Imports compile=[], %Compile flags attributes=[], %Attributes callbacks=[], %Callbacks @@ -67,12 +65,8 @@ module(Fs0, Opts0) -> %% Set pre-defined exported functions. PreExp = [{module_info,0},{module_info,1}], - %% Set pre-defined module imports. - PreModImp = [{erlang,erlang},{packages,packages}], - %% Build initial expand record. St0 = #expand{exports=PreExp, - mod_imports=dict:from_list(PreModImp), compile=Opts, defined=PreExp, bitdefault = erl_bits:system_bitdefault(), @@ -242,14 +236,12 @@ forms([], St) -> {[],St}. %% Process an attribute, this just affects the state. attribute(module, {Module, As}, _L, St) -> - M = package_to_string(Module), - St#expand{module=list_to_atom(M), - package=packages:strip_last(M), + true = is_atom(Module), + St#expand{module=Module, parameters=As}; attribute(module, Module, _L, St) -> - M = package_to_string(Module), - St#expand{module=list_to_atom(M), - package=packages:strip_last(M)}; + true = is_atom(Module), + St#expand{module=Module}; attribute(export, Es, _L, St) -> St#expand{exports=union(from_list(Es), St#expand.exports)}; attribute(import, Is, _L, St) -> @@ -312,8 +304,6 @@ pattern({tuple,Line,Ps}, St0) -> %%pattern({struct,Line,Tag,Ps}, St0) -> %% {TPs,TPsvs,St1} = pattern_list(Ps, St0), %% {{tuple,Line,[{atom,Line,Tag}|TPs]},TPsvs,St1}; -pattern({record_field,_,_,_}=M, St) -> - {expand_package(M, St),St}; % must be a package name pattern({bin,Line,Es0}, St0) -> {Es1,St1} = pattern_bin(Es0, St0), {{bin,Line,Es1},St1}; @@ -404,8 +394,6 @@ expr({tuple,Line,Es0}, St0) -> %%expr({struct,Line,Tag,Es0}, Vs, St0) -> %% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0), %% {{tuple,Line,[{atom,Line,Tag}|Es1]},Esvs,Esus,St1}; -expr({record_field,_,_,_}=M, St) -> - {expand_package(M, St),St}; % must be a package name expr({bin,Line,Es0}, St0) -> {Es1,St1} = expr_bin(Es0, St0), {{bin,Line,Es1},St1}; @@ -448,12 +436,9 @@ expr({call,Line,{atom,La,N}=Atom,As0}, St0) -> end end end; -expr({call,Line,{record_field,_,_,_}=M,As0}, St0) -> - expr({call,Line,expand_package(M, St0),As0}, St0); -expr({call,Line,{remote,Lr,M,F},As0}, St0) -> - M1 = expand_package(M, St0), - {[M2,F1|As1],St1} = expr_list([M1,F|As0], St0), - {{call,Line,{remote,Lr,M2,F1},As1},St1}; +expr({call,Line,{remote,Lr,M0,F},As0}, St0) -> + {[M1,F1|As1],St1} = expr_list([M0,F|As0], St0), + {{call,Line,{remote,Lr,M1,F1},As1},St1}; expr({call,Line,F,As0}, St0) -> {[Fun1|As1],St1} = expr_list([F|As0], St0), {{call,Line,Fun1,As1},St1}; @@ -666,32 +651,6 @@ string_to_conses(Line, Cs, Tail) -> foldr(fun (C, T) -> {cons,Line,{char,Line,C},T} end, Tail, Cs). -%% In syntax trees, module/package names are atoms or lists of atoms. - -package_to_string(A) when is_atom(A) -> atom_to_list(A); -package_to_string(L) when is_list(L) -> packages:concat(L). - -expand_package({atom,L,A} = M, St) -> - case dict:find(A, St#expand.mod_imports) of - {ok, A1} -> - {atom,L,A1}; - error -> - case packages:is_segmented(A) of - true -> - M; - false -> - M1 = packages:concat(St#expand.package, A), - {atom,L,list_to_atom(M1)} - end - end; -expand_package(M, _St) -> - case erl_parse:package_segments(M) of - error -> - M; - M1 -> - {atom,element(2,M),list_to_atom(package_to_string(M1))} - end. - %% import(Line, Imports, State) -> %% State' %% imported(Name, Arity, State) -> @@ -699,15 +658,10 @@ expand_package(M, _St) -> %% Handle import declarations and test for imported functions. No need to %% check when building imports as code is correct. -import({Mod0,Fs}, St) -> - Mod = list_to_atom(package_to_string(Mod0)), +import({Mod,Fs}, St) -> + true = is_atom(Mod), Mfs = from_list(Fs), - St#expand{imports=add_imports(Mod, Mfs, St#expand.imports)}; -import(Mod0, St) -> - Mod = package_to_string(Mod0), - Key = list_to_atom(packages:last(Mod)), - St#expand{mod_imports=dict:store(Key, list_to_atom(Mod), - St#expand.mod_imports)}. + St#expand{imports=add_imports(Mod, Mfs, St#expand.imports)}. add_imports(Mod, [F|Fs], Is) -> add_imports(Mod, Fs, orddict:store(F, Mod, Is)); diff --git a/lib/compiler/test/compilation_SUITE.erl b/lib/compiler/test/compilation_SUITE.erl index bec97b0199..f8f74e6f7a 100644 --- a/lib/compiler/test/compilation_SUITE.erl +++ b/lib/compiler/test/compilation_SUITE.erl @@ -44,7 +44,7 @@ groups() -> {group,vsn},otp_2380,otp_2141,otp_2173,otp_4790, const_list_256,bin_syntax_1,bin_syntax_2, bin_syntax_3,bin_syntax_4,bin_syntax_5,bin_syntax_6, - live_var,convopts,bad_functional_value, + live_var,convopts, catch_in_catch,redundant_case,long_string,otp_5076, complex_guard,otp_5092,otp_5151,otp_5235,otp_5244, trycatch_4,opt_crash,otp_5404,otp_5436,otp_5481, @@ -143,7 +143,6 @@ split({int, N}, <<N:16,B:N/binary,T/binary>>) -> ?comp(live_var). ?comp(trycatch_4). -?comp(bad_functional_value). ?comp(catch_in_catch). diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index 2cd75944f4..229e5a98a1 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -27,7 +27,7 @@ app_test/1, file_1/1, forms_2/1, module_mismatch/1, big_file/1, outdir/1, binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1, - other_output/1, package_forms/1, encrypted_abstr/1, + other_output/1, encrypted_abstr/1, bad_record_use1/1, bad_record_use2/1, strict_record/1, missing_testheap/1, cover/1, env/1, core/1, asm/1, sys_pre_attributes/1]). @@ -44,7 +44,7 @@ all() -> test_lib:recompile(?MODULE), [app_test, file_1, forms_2, module_mismatch, big_file, outdir, binary, makedep, cond_and_ifdef, listings, listings_big, - other_output, package_forms, encrypted_abstr, + other_output, encrypted_abstr, {group, bad_record_use}, strict_record, missing_testheap, cover, env, core, asm, sys_pre_attributes]. @@ -410,32 +410,6 @@ other_output(Config) when is_list(Config) -> ?line test_server:timetrap_cancel(Dog), ok. -package_forms(Config) when is_list(Config) -> - Fs = [{attribute,1,file,{"./p.erl",1}}, - {attribute,1,module,[p,p]}, - {attribute,3,compile,export_all}, - {attribute,1,file, - {"/clearcase/otp/erts/lib/stdlib/include/qlc.hrl",1}}, - {attribute,6,file,{"./p.erl",6}}, - {function,7,q,0, - [{clause,7,[],[], - [{call,8, - {remote,8,{atom,8,qlc},{atom,8,q}}, - [{tuple,-8, - [{atom,-8,qlc_lc}, - {'fun',-8, - {clauses, - [{clause,-8,[],[], - [{tuple,-8, - [{atom,-8,simple_v1}, - {atom,-8,'X'}, - {'fun',-8,{clauses,[{clause,-8,[],[],[{nil,8}]}]}}, - {integer,-8,8}]}]}]}}, - {atom,-8,undefined}]}]}]}]}, - {eof,9}], - {ok,'p.p',_} = compile:forms(Fs, ['S',report]), - ok. - encrypted_abstr(Config) when is_list(Config) -> ?line Dog = test_server:timetrap(test_server:minutes(10)), ?line {Simple,Target} = files(Config, "encrypted_abstr"), diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 72c9e5b8e8..e77e5fb8f0 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -44,6 +44,7 @@ #include <openssl/md5.h> #include <openssl/md4.h> #include <openssl/sha.h> +#include <openssl/ripemd.h> #include <openssl/bn.h> #include <openssl/objects.h> #include <openssl/rc4.h> @@ -139,6 +140,10 @@ static ERL_NIF_TERM md5(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM md5_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM md5_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM md5_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM ripemd160(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM ripemd160_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM ripemd160_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM ripemd160_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -246,6 +251,10 @@ static ErlNifFunc nif_funcs[] = { {"md5_init", 0, md5_init}, {"md5_update", 2, md5_update}, {"md5_final", 1, md5_final}, + {"ripemd160", 1, ripemd160}, + {"ripemd160_init", 0, ripemd160_init}, + {"ripemd160_update", 2, ripemd160_update}, + {"ripemd160_final", 1, ripemd160_final}, {"sha", 1, sha}, {"sha_init", 0, sha_init}, {"sha_update", 2, sha_update}, @@ -326,6 +335,8 @@ ERL_NIF_INIT(crypto,nif_funcs,load,NULL,upgrade,unload) #define MD5_LEN_96 12 #define MD4_CTX_LEN (sizeof(MD4_CTX)) #define MD4_LEN 16 +#define RIPEMD160_CTX_LEN (sizeof(RIPEMD160_CTX)) +#define RIPEMD160_LEN 20 #define SHA_CTX_LEN (sizeof(SHA_CTX)) #define SHA_LEN 20 #define SHA_LEN_96 12 @@ -600,6 +611,53 @@ static ERL_NIF_TERM md5_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ return ret; } +static ERL_NIF_TERM ripemd160(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data) */ + ErlNifBinary ibin; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) { + return enif_make_badarg(env); + } + RIPEMD160((unsigned char *) ibin.data, ibin.size, + enif_make_new_binary(env,RIPEMD160_LEN, &ret)); + return ret; +} +static ERL_NIF_TERM ripemd160_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ + ERL_NIF_TERM ret; + RIPEMD160_Init((RIPEMD160_CTX *) enif_make_new_binary(env, RIPEMD160_CTX_LEN, &ret)); + return ret; +} +static ERL_NIF_TERM ripemd160_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ + RIPEMD160_CTX* new_ctx; + ErlNifBinary ctx_bin, data_bin; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) + || ctx_bin.size != RIPEMD160_CTX_LEN + || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { + return enif_make_badarg(env); + } + new_ctx = (RIPEMD160_CTX*) enif_make_new_binary(env,RIPEMD160_CTX_LEN, &ret); + memcpy(new_ctx, ctx_bin.data, RIPEMD160_CTX_LEN); + RIPEMD160_Update(new_ctx, data_bin.data, data_bin.size); + return ret; +} +static ERL_NIF_TERM ripemd160_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context) */ + ErlNifBinary ctx_bin; + RIPEMD160_CTX ctx_clone; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != RIPEMD160_CTX_LEN) { + return enif_make_badarg(env); + } + memcpy(&ctx_clone, ctx_bin.data, RIPEMD160_CTX_LEN); /* writable */ + RIPEMD160_Final(enif_make_new_binary(env, RIPEMD160_LEN, &ret), &ctx_clone); + return ret; +} + + static ERL_NIF_TERM sha(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Data) */ ErlNifBinary ibin; diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 5e2a7acb97..14c77c873f 100644..100755 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -265,7 +265,7 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> <name>hash(Type, Data) -> Digest</name> <fsummary></fsummary> <type> - <v>Type = md4 | md5 | sha | sha224 | sha256 | sha384 | sha512</v> + <v>Type = md4 | md5 | ripemd160 | sha | sha224 | sha256 | sha384 | sha512</v> <v>Data = iodata()</v> <v>Digest = binary()</v> </type> @@ -279,7 +279,7 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> <name>hash_init(Type) -> Context</name> <fsummary></fsummary> <type> - <v>Type = md4 | md5 | sha | sha224 | sha256 | sha384 | sha512</v> + <v>Type = md4 | md5 | ripemd160 | sha | sha224 | sha256 | sha384 | sha512</v> </type> <desc> <p>Initializes the context for streaming hash operations. <c>Type</c> determines diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 5e7f1a948a..aa89f6cc61 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -197,43 +197,48 @@ version() -> ?CRYPTO_VSN. %% -spec hash(_, iodata()) -> binary(). -hash(md5, Data) -> md5(Data); -hash(md4, Data) -> md4(Data); -hash(sha, Data) -> sha(Data); -hash(sha224, Data) -> sha224(Data); -hash(sha256, Data) -> sha256(Data); -hash(sha384, Data) -> sha384(Data); -hash(sha512, Data) -> sha512(Data). - --spec hash_init('md5'|'md4'|'sha'|'sha224'|'sha256'|'sha384'|'sha512') -> any(). - -hash_init(md5) -> {md5, md5_init()}; -hash_init(md4) -> {md4, md4_init()}; -hash_init(sha) -> {sha, sha_init()}; -hash_init(sha224) -> {sha224, sha224_init()}; -hash_init(sha256) -> {sha256, sha256_init()}; -hash_init(sha384) -> {sha384, sha384_init()}; -hash_init(sha512) -> {sha512, sha512_init()}. +hash(md5, Data) -> md5(Data); +hash(md4, Data) -> md4(Data); +hash(sha, Data) -> sha(Data); +hash(ripemd160, Data) -> ripemd160(Data); +hash(sha224, Data) -> sha224(Data); +hash(sha256, Data) -> sha256(Data); +hash(sha384, Data) -> sha384(Data); +hash(sha512, Data) -> sha512(Data). + +-spec hash_init('md5'|'md4'|'ripemd160'| + 'sha'|'sha224'|'sha256'|'sha384'|'sha512') -> any(). + +hash_init(md5) -> {md5, md5_init()}; +hash_init(md4) -> {md4, md4_init()}; +hash_init(sha) -> {sha, sha_init()}; +hash_init(ripemd160) -> {ripemd160, ripemd160_init()}; +hash_init(sha224) -> {sha224, sha224_init()}; +hash_init(sha256) -> {sha256, sha256_init()}; +hash_init(sha384) -> {sha384, sha384_init()}; +hash_init(sha512) -> {sha512, sha512_init()}. -spec hash_update(_, iodata()) -> any(). -hash_update({md5,Context}, Data) -> {md5, md5_update(Context,Data)}; -hash_update({md4,Context}, Data) -> {md4, md4_update(Context,Data)}; -hash_update({sha,Context}, Data) -> {sha, sha_update(Context,Data)}; -hash_update({sha224,Context}, Data) -> {sha224, sha224_update(Context,Data)}; -hash_update({sha256,Context}, Data) -> {sha256, sha256_update(Context,Data)}; -hash_update({sha384,Context}, Data) -> {sha384, sha384_update(Context,Data)}; -hash_update({sha512,Context}, Data) -> {sha512, sha512_update(Context,Data)}. +hash_update({md5,Context}, Data) -> {md5, md5_update(Context,Data)}; +hash_update({md4,Context}, Data) -> {md4, md4_update(Context,Data)}; +hash_update({sha,Context}, Data) -> {sha, sha_update(Context,Data)}; +hash_update({ripemd160,Context}, Data) -> {ripemd160, ripemd160_update(Context,Data)}; +hash_update({sha224,Context}, Data) -> {sha224, sha224_update(Context,Data)}; +hash_update({sha256,Context}, Data) -> {sha256, sha256_update(Context,Data)}; +hash_update({sha384,Context}, Data) -> {sha384, sha384_update(Context,Data)}; +hash_update({sha512,Context}, Data) -> {sha512, sha512_update(Context,Data)}. -spec hash_final(_) -> binary(). -hash_final({md5,Context}) -> md5_final(Context); -hash_final({md4,Context}) -> md4_final(Context); -hash_final({sha,Context}) -> sha_final(Context); -hash_final({sha224,Context}) -> sha224_final(Context); -hash_final({sha256,Context}) -> sha256_final(Context); -hash_final({sha384,Context}) -> sha384_final(Context); -hash_final({sha512,Context}) -> sha512_final(Context). +hash_final({md5,Context}) -> md5_final(Context); +hash_final({md4,Context}) -> md4_final(Context); +hash_final({sha,Context}) -> sha_final(Context); +hash_final({ripemd160,Context}) -> ripemd160_final(Context); +hash_final({sha224,Context}) -> sha224_final(Context); +hash_final({sha256,Context}) -> sha256_final(Context); +hash_final({sha384,Context}) -> sha384_final(Context); +hash_final({sha512,Context}) -> sha512_final(Context). %% %% MD5 @@ -263,6 +268,20 @@ md4_update(_Context, _Data) -> ?nif_stub. md4_final(_Context) -> ?nif_stub. %% +%% RIPEMD160 +%% + +-spec ripemd160(iodata()) -> binary(). +-spec ripemd160_init() -> binary(). +-spec ripemd160_update(binary(), iodata()) -> binary(). +-spec ripemd160_final(binary()) -> binary(). + +ripemd160(_Data) -> ?nif_stub. +ripemd160_init() -> ?nif_stub. +ripemd160_update(_Context, _Data) -> ?nif_stub. +ripemd160_final(_Context) -> ?nif_stub. + +%% %% SHA %% -spec sha(iodata()) -> binary(). diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 027881957c..142f06677a 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -40,6 +40,8 @@ hmac_update_md5_n/1, hmac_rfc2202/1, hmac_rfc4231/1, + ripemd160/1, + ripemd160_update/1, sha256/1, sha256_update/1, sha512/1, @@ -87,7 +89,7 @@ groups() -> [{info, [sequence],[info, {group, rest}]}, {rest, [], [md5, md5_update, md4, md4_update, md5_mac, - md5_mac_io, sha, sha_update, + md5_mac_io, ripemd160, ripemd160_update, sha, sha_update, sha256, sha256_update, sha512, sha512_update, hmac_update_sha, hmac_update_sha_n, hmac_update_sha256, hmac_update_sha512, hmac_update_md5_n, hmac_update_md5_io, hmac_update_md5, @@ -973,7 +975,33 @@ hmac_update_md5_n(Config) when is_list(Config) -> ?line Mac = crypto:hmac_final_n(Ctx3, 12), ?line Exp = crypto:md5_mac_96(Key, lists:flatten([Data, Data2])), ?line m(Exp, Mac). - +%% +%% +ripemd160(doc) -> + ["Generate RIPEMD160 message digests and check the result."]; +ripemd160(suite) -> + []; +ripemd160(Config) when is_list(Config) -> + ?line m(crypto:hash(ripemd160,"abc"), + hexstr2bin("8EB208F7E05D987A9B044A8E98C6B087F15A0BFC")), + ?line m(crypto:hash(ripemd160,"abcdbcdecdefdefgefghfghighijhijkijkljklmklm" + "nlmnomnopnopq"), + hexstr2bin("12A053384A9C0C88E405A06C27DCF49ADA62EB2B")). + + +%% +%% +ripemd160_update(doc) -> + ["Generate RIPEMD160 message digests by using ripemd160_init," + "ripemd160_update, and ripemd160_final and check the result."]; +ripemd160_update(suite) -> + []; +ripemd160_update(Config) when is_list(Config) -> + ?line Ctx = crypto:hash_init(ripemd160), + ?line Ctx1 = crypto:hash_update(Ctx, "abcdbcdecdefdefgefghfghighi"), + ?line Ctx2 = crypto:hash_update(Ctx1, "jhijkijkljklmklmnlmnomnopnopq"), + ?line m(crypto:hash_final(Ctx2), + hexstr2bin("12A053384A9C0C88E405A06C27DCF49ADA62EB2B")). %% %% diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl index 2e88c35741..5e5948c965 100644 --- a/lib/debugger/src/dbg_ieval.erl +++ b/lib/debugger/src/dbg_ieval.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 @@ -1248,7 +1248,7 @@ if_clauses([], Bs, Ieval) -> exception(error, if_clause, Bs, Ieval). %% case_clauses(Value, Clauses, Bindings, Error, Ieval) -%% Error = try_clause � case_clause +%% Error = try_clause | case_clause case_clauses(Val, [{clause,_,[P],G,B}|Cs], Bs0, Error, Ieval) -> case match(P, Val, Bs0) of {match,Bs} -> diff --git a/lib/debugger/src/dbg_ui_trace.erl b/lib/debugger/src/dbg_ui_trace.erl index d318987f60..8017069c50 100644 --- a/lib/debugger/src/dbg_ui_trace.erl +++ b/lib/debugger/src/dbg_ui_trace.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 @@ -31,10 +31,10 @@ pid, % pid() Debugged process meta, % pid() Meta process - status, % {Status,Mod,Line} � {exit,Where,Reason} - % Status = init � idle | break - % | wait_break � wait_running - % � running + status, % {Status,Mod,Line} | {exit,Where,Reason} + % Status = init | idle | break + % | wait_break | wait_running + % | running % Where={Mod,Line} | null cm, % atom() | undefined Current module @@ -718,7 +718,7 @@ menus() -> %% enable(Status) -> [MenuItem] %% Status = init % when first message from Meta has arrived -%% | idle | break | exit | wait_break � wait_running | running +%% | idle | break | exit | wait_break | wait_running | running enable(init) -> []; enable(idle) -> ['Stop','Kill']; enable(break) -> ['Step','Next','Continue','Finish','Skip', diff --git a/lib/debugger/src/dbg_ui_trace_win.erl b/lib/debugger/src/dbg_ui_trace_win.erl index 1b439cbf18..beb3fbd71e 100644 --- a/lib/debugger/src/dbg_ui_trace_win.erl +++ b/lib/debugger/src/dbg_ui_trace_win.erl @@ -418,8 +418,8 @@ clear_breaks(WinInfo, Mod) -> %%-------------------------------------------------------------------- %% display(Arg) %% Arg = idle | {Status,Mod,Line} | {running,Mod} -%% � {exit,Where,Reason} | {text,Text} -%% Status = break | wait � Level +%% | {exit,Where,Reason} | {text,Text} +%% Status = break | wait | Level %% Level = int() %% Mod = atom() %% Line = integer() diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl index 2fdf39ba5a..f61df93aec 100644 --- a/lib/debugger/src/dbg_wx_trace.erl +++ b/lib/debugger/src/dbg_wx_trace.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -32,10 +32,10 @@ pid, % pid() Debugged process meta, % pid() Meta process - status, % {Status,Mod,Line} � {exit,Where,Reason} - % Status = init � idle | break - % | wait_break � wait_running - % � running + status, % {Status,Mod,Line} | {exit,Where,Reason} + % Status = init | idle | break + % | wait_break | wait_running + % | running % Where={Mod,Line} | null cm, % atom() | undefined Current module @@ -740,7 +740,7 @@ menus() -> %% enable(Status) -> [MenuItem] %% Status = init % when first message from Meta has arrived -%% | idle | break | exit | wait_break � wait_running | running +%% | idle | break | exit | wait_break | wait_running | running enable(init) -> []; enable(idle) -> ['Stop','Kill']; enable(break) -> ['Step','Next','Continue','Finish','Skip', diff --git a/lib/debugger/src/dbg_wx_trace_win.erl b/lib/debugger/src/dbg_wx_trace_win.erl index 68e8e0b844..40d110f071 100644 --- a/lib/debugger/src/dbg_wx_trace_win.erl +++ b/lib/debugger/src/dbg_wx_trace_win.erl @@ -431,8 +431,8 @@ clear_breaks(WinInfo, Mod) -> %%-------------------------------------------------------------------- %% display(Arg) %% Arg = idle | {Status,Mod,Line} | {running,Mod} -%% � {exit,Where,Reason} | {text,Text} -%% Status = break | wait � Level +%% | {exit,Where,Reason} | {text,Text} +%% Status = break | wait | Level %% Level = int() %% Mod = atom() %% Line = integer() diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl index b3a8a07f03..1c9f2eddd1 100644 --- a/lib/debugger/src/int.erl +++ b/lib/debugger/src/int.erl @@ -626,18 +626,18 @@ find_src(Beam) -> find_beam(Mod, Src) -> SrcDir = filename:dirname(Src), - BeamFile = packages:last(Mod) ++ code:objfile_extension(), + BeamFile = atom_to_list(Mod) ++ code:objfile_extension(), File = filename:join(SrcDir, BeamFile), case is_file(File) of true -> File; - false -> find_beam_1(Mod, SrcDir) + false -> find_beam_1(Mod, BeamFile, SrcDir) end. -find_beam_1(Mod, SrcDir) -> - RootDir = find_root_dir(SrcDir, packages:first(Mod)), +find_beam_1(Mod, BeamFile, SrcDir) -> + RootDir = filename:dirname(SrcDir), EbinDir = filename:join(RootDir, "ebin"), CodePath = [EbinDir | code:get_path()], - BeamFile = to_path(Mod) ++ code:objfile_extension(), + BeamFile = atom_to_list(Mod) ++ code:objfile_extension(), lists:foldl(fun(_, Beam) when is_list(Beam) -> Beam; (Dir, error) -> File = filename:join(Dir, BeamFile), @@ -649,14 +649,6 @@ find_beam_1(Mod, SrcDir) -> error, CodePath). -to_path(X) -> - filename:join(packages:split(X)). - -find_root_dir(Dir, [_|Ss]) -> - find_root_dir(filename:dirname(Dir), Ss); -find_root_dir(Dir, []) -> - filename:dirname(Dir). - check_beam(BeamBin) when is_binary(BeamBin) -> case beam_lib:chunks(BeamBin, [abstract_code,exports]) of {ok,{_Mod,[{abstract_code,no_abstract_code}|_]}} -> @@ -711,14 +703,11 @@ scan_module_name_2(_, _) -> scan_module_name_3(Ts) -> case erl_parse:parse_form(Ts) of - {ok, {attribute,_,module,{M,_}}} -> module_atom(M); - {ok, {attribute,_,module,M}} -> module_atom(M); + {ok, {attribute,_,module,{M,_}}} -> M; + {ok, {attribute,_,module,M}} -> M; _ -> error end. -module_atom(A) when is_atom(A) -> A; -module_atom(L) when is_list(L) -> list_to_atom(packages:concat(L)). - %%--Stop interpreting modules----------------------------------------- del_mod(AbsMod, Dist) -> diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml index 4080dfdf77..5e0c9b51e3 100644 --- a/lib/dialyzer/doc/src/dialyzer.xml +++ b/lib/dialyzer/doc/src/dialyzer.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2006</year><year>2011</year> + <year>2006</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -288,7 +288,11 @@ Option :: {files, [Filename :: string()]} | {include_dirs, [DirName :: string()]} | {output_file, FileName :: string()} | {output_plt, FileName :: string()} - | {analysis_type, 'succ_typings' | 'plt_add' | 'plt_build' | 'plt_check' | 'plt_remove'} + | {analysis_type, 'succ_typings' | + 'plt_add' | + 'plt_build' | + 'plt_check' | + 'plt_remove'} | {warnings, [WarnOpts]} | {get_warnings, bool()} diff --git a/lib/dialyzer/doc/src/dialyzer_chapter.xml b/lib/dialyzer/doc/src/dialyzer_chapter.xml index d15069991e..be75f1cc65 100644 --- a/lib/dialyzer/doc/src/dialyzer_chapter.xml +++ b/lib/dialyzer/doc/src/dialyzer_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2006</year><year>2009</year> + <year>2006</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -140,7 +140,9 @@ <code type="none"> - dialyzer --build_plt -r $ERL_TOP/lib/stdlib/ebin $ERL_TOP/lib/kernel/ebin $ERL_TOP/lib/mnesia/ebin + dialyzer --build_plt -r $ERL_TOP/lib/stdlib/ebin\ + $ERL_TOP/lib/kernel/ebin \ + $ERL_TOP/lib/mnesia/ebin </code> <p>Dialyzer will look if there is an environment variable called diff --git a/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_scan.erl b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_scan.erl index a97270b9f3..f4e609bf5b 100644 --- a/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_scan.erl +++ b/lib/dialyzer/test/options1_SUITE_data/src/compiler/core_scan.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% ``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 @@ -30,16 +31,16 @@ %% 173 - 176 { - ~ punctuation %% 177 DEL control %% 200 - 237 control -%% 240 - 277 NBSP - � punctuation -%% 300 - 326 � - � uppercase -%% 327 � punctuation -%% 330 - 336 � - � uppercase -%% 337 - 366 � - � lowercase -%% 367 � punctuation -%% 370 - 377 � - � lowercase +%% 240 - 277 NBSP - ¿ punctuation +%% 300 - 326 À - Ö uppercase +%% 327 × punctuation +%% 330 - 336 Ø - Þ uppercase +%% 337 - 366 ß - ö lowercase +%% 367 ÷ punctuation +%% 370 - 377 ø - ÿ lowercase %% %% Many punctuation characters region have special meaning. Must -%% watch using � \327, bvery close to x \170 +%% watch using × \327, bvery close to x \170 -module(core_scan). @@ -266,11 +267,11 @@ scan1([C|Cs], Toks, Pos) when C >= $\200, C =< $\240 -> scan1(Cs, Toks, Pos); scan1([C|Cs], Toks, Pos) when C >= $a, C =< $z -> %Keywords scan_key_word(C, Cs, Toks, Pos); -scan1([C|Cs], Toks, Pos) when C >= $�, C =< $�, C /= $� -> +scan1([C|Cs], Toks, Pos) when C >= $ß, C =< $ÿ, C /= $÷ -> scan_key_word(C, Cs, Toks, Pos); scan1([C|Cs], Toks, Pos) when C >= $A, C =< $Z -> %Variables scan_variable(C, Cs, Toks, Pos); -scan1([C|Cs], Toks, Pos) when C >= $�, C =< $�, C /= $� -> +scan1([C|Cs], Toks, Pos) when C >= $À, C =< $Þ, C /= $× -> scan_variable(C, Cs, Toks, Pos); scan1([C|Cs], Toks, Pos) when C >= $0, C =< $9 -> %Numbers scan_number(C, Cs, Toks, Pos); @@ -335,9 +336,9 @@ scan_name([], Ncs) -> {Ncs,[]}. name_char(C) when C >= $a, C =< $z -> true; -name_char(C) when C >= $�, C =< $�, C /= $� -> true; +name_char(C) when C >= $ß, C =< $ÿ, C /= $÷ -> true; name_char(C) when C >= $A, C =< $Z -> true; -name_char(C) when C >= $�, C =< $�, C /= $� -> true; +name_char(C) when C >= $À, C =< $Þ, C /= $× -> true; name_char(C) when C >= $0, C =< $9 -> true; name_char($_) -> true; name_char($@) -> true; diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_check.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_check.erl index 2f0ada122e..4335d8efae 100644 --- a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_check.erl +++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_check.erl @@ -4599,7 +4599,7 @@ get_simple_table_info1(S,#'ComponentType'{typespec=TS},[],Path) -> %% o.w. the asn1 code is wrong. #type{def=OCFT,constraint=Cnstr} = TS, case Cnstr of - [{simpletable,_OSRef}]�-> + [{simpletable,_OSRef}] -> #'ObjectClassFieldType'{classname=ClRef, class=ObjectClass, fieldname=FieldName} = OCFT, diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl index 5d2f7a13bd..32d4a22645 100644 --- a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl +++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1ct_gen.erl @@ -148,7 +148,7 @@ pgen_partial_inc_dec(Erules,Module) -> % io:format("Start partial incomplete decode gen?~n"), case asn1ct:get_gen_state_field(inc_type_pattern) of undefined -> -% io:format("Partial incomplete decode gen not started:�~w~n",[asn1ct:get_gen_state_field(active)]), +% io:format("Partial incomplete decode gen not started: ~w~n",[asn1ct:get_gen_state_field(active)]), ok; % [] -> % ok; diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl index 6064515a7e..b0786200fc 100644 --- a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl +++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin.erl @@ -276,7 +276,7 @@ encode_tag_val({Class, Form, TagNo}) -> <<(Class bsr 6):2, (Form bsr 5):1, 31:5,BinOct/binary>>; %% asumes whole correct tag bitpattern, multiple of 8 -encode_tag_val(Tag) when (Tag =< 255) -> Tag; %% anv�nds denna funktion??!! +encode_tag_val(Tag) when (Tag =< 255) -> Tag; %% is this function used??!! %% asumes correct bitpattern of 0-5 encode_tag_val(Tag) -> encode_tag_val2(Tag,[]). diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl index 50a91cf201..2f25e35cd3 100644 --- a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl +++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl @@ -531,7 +531,7 @@ encode_tag_val({Class, Form, TagNo}) -> <<(Class bsr 6):2, (Form bsr 5):1, 31:5,BinOct/binary>>; %% asumes whole correct tag bitpattern, multiple of 8 -encode_tag_val(Tag) when (Tag =< 255) -> Tag; %% anv�nds denna funktion??!! +encode_tag_val(Tag) when (Tag =< 255) -> Tag; %% is this function used??!! %% asumes correct bitpattern of 0-5 encode_tag_val(Tag) -> encode_tag_val2(Tag,[]). diff --git a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl index 9f02ad4466..4781c81955 100644 --- a/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl +++ b/lib/dialyzer/test/r9c_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl @@ -1198,7 +1198,7 @@ encode_bin_bit_string(C,UnusedAndBin={_,_},NamedBitList) -> % case get_constraint(C,'SizeConstraint') of % 0 -> -% []; % borde avg�ras i compile-time +% []; % should be dont in compile time % V when integer(V),V=<16 -> % {Unused2,Bin2} = pad_list(V,UnusedAndBin1), % <<BitVal:V,_:Unused2>> = Bin2, diff --git a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl index 48cc50ae21..5a8f9710d6 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_connection.erl @@ -1,4 +1,5 @@ -% Copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin +%% -*- coding: utf-8 -*- +% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin % % Licensed under the Apache License, Version 2.0 (the "License"); % you may not use this file except in compliance with the License. @@ -21,7 +22,7 @@ %%% Created : 18 Apr 2008 by Thorsten Schuett <[email protected]> %%%------------------------------------------------------------------- %% @author Thorsten Schuett <[email protected]> -%% @copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin +%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin %% @version $Id $ -module(comm_layer_dir.comm_connection). diff --git a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl index e8169b4673..d9fcb5e625 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/comm_layer/comm_port.erl @@ -1,4 +1,5 @@ -% Copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin +%% -*- coding: utf-8 -*- +% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin % % Licensed under the Apache License, Version 2.0 (the "License"); % you may not use this file except in compliance with the License. @@ -20,7 +21,7 @@ %%% Created : 18 Apr 2008 by Thorsten Schuett <[email protected]> %%%------------------------------------------------------------------- %% @author Thorsten Schuett <[email protected]> -%% @copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin +%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin %% @version $Id $ -module(comm_layer_dir.comm_port). diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index a4a0b80348..91384b8b91 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -1703,16 +1703,20 @@ send(Pid, Pkt) -> %% retransmit/4 retransmit({TPid, Caps, #diameter_app{alias = Alias} = App} = T, - #request{app = Alias, packet = Pkt} + #request{app = Alias, packet = Pkt0} = Req, SvcName, Timeout) -> - have_request(Pkt, TPid) %% Don't failover to a peer we've + have_request(Pkt0, TPid) %% Don't failover to a peer we've andalso ?THROW(timeout), %% already sent to. + #diameter_packet{header = Hdr0} = Pkt0, + Hdr = Hdr0#diameter_header{is_retransmitted = true}, + Pkt = Pkt0#diameter_packet{header = Hdr}, + resend_req(cb(App, prepare_retransmit, [Pkt, SvcName, {TPid, Caps}]), T, - Req, + Req#request{packet = Pkt}, Timeout, []). diff --git a/lib/diameter/test/diameter_failover_SUITE.erl b/lib/diameter/test/diameter_failover_SUITE.erl index ed31670031..bb820a8bf2 100644 --- a/lib/diameter/test/diameter_failover_SUITE.erl +++ b/lib/diameter/test/diameter_failover_SUITE.erl @@ -18,19 +18,19 @@ %% %% -%% Tests of traffic between six Diameter nodes in three realms, +%% Tests of traffic between seven Diameter nodes in four realms, %% connected as follows. %% -%% ----- SERVER1.REALM2 -%% / -%% / ----- SERVER2.REALM2 -%% | / -%% CLIENT.REALM1 ------ SERVER3.REALM2 -%% | \ -%% | \ -%% \ ---- SERVER1.REALM3 -%% \ -%% ----- SERVER2.REALM3 +%% ----- SERVER1.REALM2 ----- +%% / \ +%% / ----- SERVER2.REALM2 ----- \ +%% | / \ | +%% CLIENT.REALM1 ------ SERVER3.REALM2 ------ CLIENT.REALM4 +%% | \ / | +%% | \ / | +%% \ ---- SERVER1.REALM3 ----- / +%% \ / +%% ----- SERVER2.REALM3 ----- %% -module(diameter_failover_SUITE). @@ -44,12 +44,16 @@ connect/1, send_ok/1, send_nok/1, + send_discard_1/1, + send_discard_2/1, stop_services/1, stop/1]). %% diameter callbacks -export([pick_peer/4, prepare_request/3, + prepare_retransmit/3, + handle_error/4, handle_answer/4, handle_request/3]). @@ -62,14 +66,18 @@ -define(ADDR, {127,0,0,1}). --define(CLIENT, "CLIENT.REALM1"). +-define(CLIENT1, "CLIENT.REALM1"). +-define(CLIENT2, "CLIENT.REALM4"). -define(SERVER1, "SERVER1.REALM2"). -define(SERVER2, "SERVER2.REALM2"). -define(SERVER3, "SERVER3.REALM2"). -define(SERVER4, "SERVER1.REALM3"). -define(SERVER5, "SERVER2.REALM3"). --define(SERVICES, [?CLIENT, ?SERVER1, ?SERVER2, ?SERVER3, ?SERVER4, ?SERVER5]). +-define(IS_CLIENT(Svc), Svc == ?CLIENT1; Svc == ?CLIENT2). + +-define(CLIENTS, [?CLIENT1, ?CLIENT2]). +-define(SERVERS, [?SERVER1, ?SERVER2, ?SERVER3, ?SERVER4, ?SERVER5]). -define(DICT_COMMON, ?DIAMETER_DICT_COMMON). @@ -77,26 +85,27 @@ -define(APP_ID, ?DICT_COMMON:id()). %% Config for diameter:start_service/2. --define(SERVICE(Host, Dict), +-define(SERVICE(Host), [{'Origin-Host', Host}, {'Origin-Realm', realm(Host)}, {'Host-IP-Address', [?ADDR]}, {'Vendor-Id', 12345}, {'Product-Name', "OTP/diameter"}, - {'Acct-Application-Id', [Dict:id()]}, + {'Acct-Application-Id', [?APP_ID]}, {application, [{alias, ?APP_ALIAS}, - {dictionary, Dict}, + {dictionary, ?DICT_COMMON}, {module, #diameter_callback {peer_up = false, peer_down = false, - handle_error = false, - prepare_retransmit = false, default = ?MODULE}}, {answer_errors, callback}]}]). -define(SUCCESS, 2001). --define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT'). +%% Value of Termination-Cause determines client/server behaviour. +-define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT'). +-define(MOVED, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_USER_MOVED'). +-define(TIMEOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_SESSION_TIMEOUT'). %% =========================================================================== @@ -109,6 +118,8 @@ all() -> connect, send_ok, send_nok, + send_discard_1, + send_discard_2, stop_services, stop]. @@ -119,19 +130,20 @@ start(_Config) -> ok = diameter:start(). start_services(_Config) -> - S = [server(N, ?DICT_COMMON) || N <- tl(?SERVICES)], - - ok = diameter:start_service(?CLIENT, ?SERVICE(?CLIENT, ?DICT_COMMON)), + Servers = [server(N) || N <- ?SERVERS], + [] = [T || C <- ?CLIENTS, + T <- [diameter:start_service(C, ?SERVICE(C))], + T /= ok], - {save_config, [{?CLIENT, S}]}. + {save_config, Servers}. connect(Config) -> - {_, Conns} = proplists:get_value(saved_config, Config), + {start_services, Servers} = proplists:get_value(saved_config, Config), - lists:foreach(fun({CN,Ss}) -> connect(CN, Ss) end, Conns). + lists:foreach(fun(C) -> connect(C, Servers) end, ?CLIENTS). stop_services(_Config) -> - [] = [{H,T} || H <- ?SERVICES, + [] = [{H,T} || H <- ?CLIENTS ++ ?SERVERS, T <- [diameter:stop_service(H)], T /= ok]. @@ -140,8 +152,8 @@ stop(_Config) -> %% ---------------------------------------- -server(Name, Dict) -> - ok = diameter:start_service(Name, ?SERVICE(Name, Dict)), +server(Name) -> + ok = diameter:start_service(Name, ?SERVICE(Name)), {Name, ?util:listen(Name, tcp)}. connect(Name, Refs) -> @@ -153,30 +165,39 @@ connect(Name, Refs) -> %% Send an STR and expect success after SERVER3 answers after a couple %% of failovers. send_ok(_Config) -> - Req = ['STR', {'Destination-Realm', realm(?SERVER1)}, - {'Termination-Cause', ?LOGOUT}, - {'Auth-Application-Id', ?APP_ID}], + Req = #diameter_base_STR{'Destination-Realm' = realm(?SERVER1), + 'Termination-Cause' = ?LOGOUT, + 'Auth-Application-Id' = ?APP_ID}, #diameter_base_STA{'Result-Code' = ?SUCCESS, 'Origin-Host' = ?SERVER3} - = call(Req, [{filter, realm}]). + = call(?CLIENT1, Req). %% Send an STR and expect failure when both servers fail. send_nok(_Config) -> - Req = ['STR', {'Destination-Realm', realm(?SERVER4)}, - {'Termination-Cause', ?LOGOUT}, - {'Auth-Application-Id', ?APP_ID}], - {error, failover} = call(Req, [{filter, realm}]). + Req = #diameter_base_STR{'Destination-Realm' = realm(?SERVER4), + 'Termination-Cause' = ?LOGOUT, + 'Auth-Application-Id' = ?APP_ID}, + {failover, ?LOGOUT} = call(?CLIENT1, Req). + +%% Send an STR and have prepare_retransmit discard it. +send_discard_1(_Config) -> + Req = #diameter_base_STR{'Destination-Realm' = realm(?SERVER1), + 'Termination-Cause' = ?TIMEOUT, + 'Auth-Application-Id' = ?APP_ID}, + {rejected, ?TIMEOUT} = call(?CLIENT2, Req). +send_discard_2(_Config) -> + Req = #diameter_base_STR{'Destination-Realm' = realm(?SERVER4), + 'Termination-Cause' = ?MOVED, + 'Auth-Application-Id' = ?APP_ID}, + {discarded, ?MOVED} = call(?CLIENT2, Req). %% =========================================================================== realm(Host) -> tl(lists:dropwhile(fun(C) -> C /= $. end, Host)). -call(Req, Opts) -> - diameter:call(?CLIENT, ?APP_ALIAS, Req, Opts). - -set([H|T], Vs) -> - [H | Vs ++ T]. +call(Svc, Req) -> + diameter:call(Svc, ?APP_ALIAS, Req, [{filter, realm}]). %% =========================================================================== %% diameter callbacks @@ -184,7 +205,8 @@ set([H|T], Vs) -> %% pick_peer/4 %% Choose a server other than SERVER3 or SERVER5 if possible. -pick_peer(Peers, _, ?CLIENT, _State) -> +pick_peer(Peers, _, Svc, _State) + when ?IS_CLIENT(Svc) -> case lists:partition(fun({_, #diameter_caps{origin_host = {_, OH}}}) -> OH /= ?SERVER3 andalso OH /= ?SERVER5 end, @@ -198,20 +220,41 @@ pick_peer(Peers, _, ?CLIENT, _State) -> %% prepare_request/3 -prepare_request(Pkt, ?CLIENT, {_Ref, Caps}) -> +prepare_request(Pkt, Svc, {_Ref, Caps}) + when ?IS_CLIENT(Svc) -> {send, prepare(Pkt, Caps)}. prepare(#diameter_packet{msg = Req}, Caps) -> #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}} = Caps, - set(Req, [{'Session-Id', diameter:session_id(OH)}, - {'Origin-Host', OH}, - {'Origin-Realm', OR}]). + Req#diameter_base_STR{'Origin-Host' = OH, + 'Origin-Realm' = OR, + 'Session-Id' = diameter:session_id(OH)}. + +%% prepare_retransmit/3 + +prepare_retransmit(#diameter_packet{header = H} = P, Svc, {_,_}) + when ?IS_CLIENT(Svc) -> + #diameter_header{is_retransmitted = true} = H, %% assert + prepare(P). + +prepare(#diameter_packet{msg = M} = P) -> + case M#diameter_base_STR.'Termination-Cause' of + ?LOGOUT -> {send, P}; + ?MOVED -> discard; + ?TIMEOUT -> {discard, rejected} + end. + +%% handle_error/4 + +handle_error(Reason, Req, _, _) -> + {Reason, Req#diameter_base_STR.'Termination-Cause'}. %% handle_answer/4 -handle_answer(Pkt, _Req, ?CLIENT, _Peer) -> +handle_answer(Pkt, _Req, Svc, _Peer) + when ?IS_CLIENT(Svc) -> #diameter_packet{msg = Rec, errors = []} = Pkt, Rec. @@ -219,8 +262,8 @@ handle_answer(Pkt, _Req, ?CLIENT, _Peer) -> %% Only SERVER3 actually answers. handle_request(Pkt, ?SERVER3, {_, Caps}) -> - #diameter_packet{msg = #diameter_base_STR{'Session-Id' = SId, - 'Origin-Host' = ?CLIENT}} + #diameter_packet{header = #diameter_header{is_retransmitted = true}, + msg = #diameter_base_STR{'Session-Id' = SId}} = Pkt, #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}} diff --git a/lib/edoc/priv/edoc.dtd b/lib/edoc/priv/edoc.dtd index 6a332cf22f..ba4ac0db28 100644 --- a/lib/edoc/priv/edoc.dtd +++ b/lib/edoc/priv/edoc.dtd @@ -4,7 +4,8 @@ <!ELEMENT overview (title, description?, author*, copyright?, version?, since?, see*, reference*, todo?, packages, modules)> <!ATTLIST overview - root CDATA #IMPLIED> + root CDATA #IMPLIED + encoding CDATA #IMPLIED> <!ELEMENT title (#PCDATA)> @@ -25,6 +26,7 @@ name CDATA #REQUIRED private (yes | no) #IMPLIED hidden (yes | no) #IMPLIED + encoding CDATA #IMPLIED root CDATA #IMPLIED> <!ELEMENT description (briefDescription, fullDescription?)> diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl index 544465b14a..599036f380 100644 --- a/lib/edoc/src/edoc.erl +++ b/lib/edoc/src/edoc.erl @@ -120,7 +120,8 @@ file(Name, Options) -> Suffix = proplists:get_value(file_suffix, Options, ?DEFAULT_FILE_SUFFIX), Dir = proplists:get_value(dir, Options, filename:dirname(Name)), - edoc_lib:write_file(Text, Dir, BaseName ++ Suffix). + Encoding = [{encoding, edoc_lib:read_encoding(Name, [])}], + edoc_lib:write_file(Text, Dir, BaseName ++ Suffix, '', Encoding). %% TODO: better documentation of files/1/2, packages/1/2, application/1/2/3 @@ -455,14 +456,14 @@ expand_sources(Ss, Opts) -> end, expand_sources(Ss1, Suffix, sets:new(), [], []). -expand_sources([{P, F, D} | Fs], Suffix, S, As, Ms) -> - M = list_to_atom(packages:concat(P, filename:rootname(F, Suffix))), +expand_sources([{'', F, D} | Fs], Suffix, S, As, Ms) -> + M = list_to_atom(filename:rootname(F, Suffix)), case sets:is_element(M, S) of true -> expand_sources(Fs, Suffix, S, As, Ms); false -> S1 = sets:add_element(M, S), - expand_sources(Fs, Suffix, S1, [{M, P, F, D} | As], + expand_sources(Fs, Suffix, S1, [{M, '', F, D} | As], [M | Ms]) end; expand_sources([], _Suffix, _S, As, Ms) -> diff --git a/lib/edoc/src/edoc.hrl b/lib/edoc/src/edoc.hrl index 98debba4ab..44c5d6fef4 100644 --- a/lib/edoc/src/edoc.hrl +++ b/lib/edoc/src/edoc.hrl @@ -48,7 +48,8 @@ %% functions = ordset(function_name()), %% exports = ordset(function_name()), %% attributes = ordset({atom(), term()}), -%% records = [{atom(), [{atom(), term()}]}]} +%% records = [{atom(), [{atom(), term()}]}], +%% encoding = epp:source_encoding()} %% ordset(T) = sets:ordset(T) %% function_name(T) = {atom(), integer()} @@ -57,7 +58,8 @@ functions = [], exports = [], attributes = [], - records = [] + records = [], + encoding = latin1 }). %% Environment for generating documentation data diff --git a/lib/edoc/src/edoc_data.erl b/lib/edoc/src/edoc_data.erl index 624f9177a2..f88ba05f4b 100644 --- a/lib/edoc/src/edoc_data.erl +++ b/lib/edoc/src/edoc_data.erl @@ -83,7 +83,8 @@ module(Module, Entries, Env, Opts) -> AllTags = get_all_tags(Entries), Functions = function_filter(Entries, Opts), Out = {module, ([{name, Name}, - {root, Env#env.root}] + {root, Env#env.root}, + {encoding, Module#module.encoding}] ++ case is_private(HeaderTags) of true -> [{private, "yes"}]; false -> [] diff --git a/lib/edoc/src/edoc_doclet.erl b/lib/edoc/src/edoc_doclet.erl index 385d20e9ae..a0c1ae1c0f 100644 --- a/lib/edoc/src/edoc_doclet.erl +++ b/lib/edoc/src/edoc_doclet.erl @@ -192,8 +192,9 @@ source({M, P, Name, Path}, Dir, Suffix, Env, Set, Private, Hidden, andalso ((not is_hidden(Doc)) orelse Hidden) of true -> Text = edoc:layout(Doc, Options), - Name1 = packages:last(M) ++ Suffix, - edoc_lib:write_file(Text, Dir, Name1, P), + Name1 = atom_to_list(M) ++ Suffix, + Encoding = [{encoding,encoding(Doc)}], + edoc_lib:write_file(Text, Dir, Name1, P, Encoding), {sets:add_element(Module, Set), Error}; false -> {Set, Error} @@ -204,9 +205,9 @@ source({M, P, Name, Path}, Dir, Suffix, Env, Set, Private, Hidden, end. check_name(M, M0, P0, File) -> - P = list_to_atom(packages:strip_last(M)), - N = packages:last(M), - N0 = packages:last(M0), + P = '', + N = M, + N0 = M0, case N of [$? | _] -> %% A module name of the form '?...' is assumed to be caused @@ -359,14 +360,19 @@ xhtml_1(Title, CSS, Body) -> overview(Dir, Title, Env, Opts) -> File = proplists:get_value(overview, Opts, filename:join(Dir, ?OVERVIEW_FILE)), + Encoding = edoc_lib:read_encoding(File, [{in_comment_only, false}]), Tags = read_file(File, overview, Env, Opts), - Data = edoc_data:overview(Title, Tags, Env, Opts), + Data0 = edoc_data:overview(Title, Tags, Env, Opts), + EncodingAttribute = #xmlAttribute{name = encoding, + value = atom_to_list(Encoding)}, + #xmlElement{attributes = As} = Data0, + Data = Data0#xmlElement{attributes = [EncodingAttribute | As]}, F = fun (M) -> M:overview(Data, Opts) end, Text = edoc_lib:run_layout(F, Opts), - edoc_lib:write_file(Text, Dir, ?OVERVIEW_SUMMARY). - + EncOpts = [{encoding,Encoding}], + edoc_lib:write_file(Text, Dir, ?OVERVIEW_SUMMARY, '', EncOpts). copy_image(Dir) -> case code:priv_dir(?EDOC_APP) of @@ -441,6 +447,12 @@ is_hidden(E) -> _ -> false end. +encoding(E) -> + case get_attrval(encoding, E) of + "latin1" -> latin1; + _ -> utf8 + end. + get_attrval(Name, #xmlElement{attributes = As}) -> case get_attr(Name, As) of [#xmlAttribute{value = V}] -> diff --git a/lib/edoc/src/edoc_extract.erl b/lib/edoc/src/edoc_extract.erl index 5a79e127f6..67a95e80aa 100644 --- a/lib/edoc/src/edoc_extract.erl +++ b/lib/edoc/src/edoc_extract.erl @@ -121,7 +121,7 @@ source1(Tree, File0, Env, Opts, TypeDocs) -> Module = get_module_info(Tree, File), {Header, Footer, Entries} = collect(Forms, Module), Name = Module#module.name, - Package = list_to_atom(packages:strip_last(Name)), + Package = '', Env1 = Env#env{module = Name, package = Package, root = edoc_refs:relative_package_path('', Package)}, @@ -226,7 +226,7 @@ add_macro_defs(Defs0, Opts, Env) -> %% lines of text before the first tag are ignored. `Env' is an %% environment created by {@link edoc_lib:get_doc_env/4}. Upon error, %% `Reason' is an atom returned from the call to {@link -%% //kernel/file:read_file/1}. +%% //kernel/file:read_file/1} or the atom 'invalid_unicode'. %% %% See {@link text/4} for options. @@ -235,7 +235,13 @@ add_macro_defs(Defs0, Opts, Env) -> file(File, Context, Env, Opts) -> case file:read_file(File) of {ok, Bin} -> - {ok, text(binary_to_list(Bin), Context, Env, Opts, File)}; + Enc = edoc_lib:read_encoding(File,[{in_comment_only, false}]), + case catch unicode:characters_to_list(Bin, Enc) of + String when is_list(String) -> + {ok, text(String, Context, Env, Opts, File)}; + _ -> + {error, invalid_unicode} + end; {error, _} = Error -> Error end. @@ -306,12 +312,14 @@ get_module_info(Forms, File) -> Exports = ordsets:from_list(get_list_keyval(exports, L)), Attributes = ordsets:from_list(get_list_keyval(attributes, L)), Records = get_list_keyval(records, L), + Encoding = edoc_lib:read_encoding(File, []), #module{name = Name, parameters = Vars, functions = Functions, exports = ordsets:intersection(Exports, Functions), attributes = Attributes, - records = Records}. + records = Records, + encoding = Encoding}. get_list_keyval(Key, L) -> case lists:keyfind(Key, 1, L) of diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl index 951cec121c..7bd0615f5c 100644 --- a/lib/edoc/src/edoc_layout.erl +++ b/lib/edoc/src/edoc_layout.erl @@ -210,7 +210,8 @@ layout_module(#xmlElement{name = module, content = Es}=E, Opts) -> ++ [hr, ?NL] ++ navigation("bottom") ++ timestamp()), - xhtml(Title, stylesheet(Opts), Body). + Encoding = get_attrval(encoding, E), + xhtml(Title, stylesheet(Opts), Body, Encoding). module_params(Es) -> As = [{get_text(argName, Es1), @@ -956,10 +957,17 @@ local_label(R) -> "#" ++ R. xhtml(Title, CSS, Body) -> + xhtml(Title, CSS, Body, "latin1"). + +xhtml(Title, CSS, Body, Encoding) -> + EncString = case Encoding of + "latin1" -> "ISO-8859-1"; + _ -> "UTF-8" + end, [{html, [?NL, {head, [?NL, {meta, [{'http-equiv',"Content-Type"}, - {content, "text/html; charset=ISO-8859-1"}], + {content, "text/html; charset="++EncString}], []}, ?NL, {title, Title}, @@ -1021,7 +1029,8 @@ overview(E=#xmlElement{name = overview, content = Es}, Options) -> ++ [?NL, hr] ++ navigation("bottom") ++ timestamp()), - XML = xhtml(Title, stylesheet(Opts), Body), + Encoding = get_attrval(encoding, E), + XML = xhtml(Title, stylesheet(Opts), Body, Encoding), xmerl:export_simple(XML, ?HTML_EXPORT, []). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% NYTT diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl index 90fb8a679c..3d193c4bfa 100644 --- a/lib/edoc/src/edoc_lib.erl +++ b/lib/edoc/src/edoc_lib.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% ===================================================================== %% This library is free software; you can redistribute it and/or modify %% it under the terms of the GNU Lesser General Public License as @@ -30,10 +31,10 @@ parse_contact/2, escape_uri/1, join_uri/2, is_relative_uri/1, is_name/1, to_label/1, find_doc_dirs/0, find_sources/2, find_sources/3, find_file/3, try_subdir/2, unique/1, - write_file/3, write_file/4, write_info_file/4, + write_file/3, write_file/4, write_file/5, write_info_file/4, read_info_file/1, get_doc_env/1, get_doc_env/4, copy_file/2, uri_get/1, run_doclet/2, run_layout/2, - simplify_path/1, timestr/1, datestr/1]). + simplify_path/1, timestr/1, datestr/1, read_encoding/2]). -import(edoc_report, [report/2, warning/2]). @@ -57,6 +58,13 @@ datestr({Y,M,D}) -> lists:flatten(io_lib:fwrite("~s ~w ~w",[lists:nth(M, Ms),D,Y])). %% @private +read_encoding(File, Options) -> + case epp:read_encoding(File, Options) of + none -> epp:default_encoding(); + Encoding -> Encoding + end. + +%% @private count(X, Xs) -> count(X, Xs, 0). @@ -228,13 +236,13 @@ end_of_sentence_1(_, false, _) -> %% 173 - 176 { - ~ punctuation %% 177 DEL control %% 200 - 237 control -%% 240 - 277 NBSP - � punctuation -%% 300 - 326 � - � uppercase -%% 327 � punctuation -%% 330 - 336 � - � uppercase -%% 337 - 366 � - � lowercase -%% 367 � punctuation -%% 370 - 377 � - � lowercase +%% 240 - 277 NBSP - ¿ punctuation +%% 300 - 326 À - Ö uppercase +%% 327 × punctuation +%% 330 - 336 Ø - Þ uppercase +%% 337 - 366 ß - ö lowercase +%% 367 ÷ punctuation +%% 370 - 377 ø - ÿ lowercase %% Names must begin with a lowercase letter and contain only %% alphanumerics and underscores. @@ -261,6 +269,10 @@ is_name_1(_) -> false. to_atom(A) when is_atom(A) -> A; to_atom(S) when is_list(S) -> list_to_atom(S). + +to_list(A) when is_atom(A) -> atom_to_list(A); +to_list(S) when is_list(S) -> S. + %% @private unique([X | Xs]) -> [X | unique(Xs, X)]; @@ -677,7 +689,6 @@ try_subdir(Dir, Subdir) -> write_file(Text, Dir, Name) -> write_file(Text, Dir, Name, ''). - %% @spec (Text::deep_string(), Dir::edoc:filename(), %% Name::edoc:filename(), Package::atom()|string()) -> ok %% @doc Like {@link write_file/3}, but adds path components to the target @@ -685,10 +696,12 @@ write_file(Text, Dir, Name) -> %% @private write_file(Text, Dir, Name, Package) -> - Dir1 = filename:join([Dir | packages:split(Package)]), - File = filename:join(Dir1, Name), + write_file(Text, Dir, Name, Package, [{encoding,latin1}]). + +write_file(Text, Dir, Name, Package, Options) -> + File = filename:join([Dir, to_list(Package), Name]), ok = filelib:ensure_dir(File), - case file:open(File, [write]) of + case file:open(File, [write] ++ Options) of {ok, FD} -> io:put_chars(FD, Text), ok = file:close(FD); @@ -705,8 +718,9 @@ write_info_file(App, Packages, Modules, Dir) -> Ts1 = if App =:= ?NO_APP -> Ts; true -> [{application, App} | Ts] end, - S = [io_lib:fwrite("~p.\n", [T]) || T <- Ts1], - write_file(S, Dir, ?INFO_FILE). + S0 = [io_lib:fwrite("~p.\n", [T]) || T <- Ts1], + S = ["%% encoding: UTF-8\n" | S0], + write_file(S, Dir, ?INFO_FILE, '', [{encoding,unicode}]). %% @spec (Name::edoc:filename()) -> {ok, string()} | {error, Reason} %% @@ -714,7 +728,14 @@ write_info_file(App, Packages, Modules, Dir) -> read_file(File) -> case file:read_file(File) of - {ok, Bin} -> {ok, binary_to_list(Bin)}; + {ok, Bin} -> + Enc = edoc_lib:read_encoding(File, []), + case catch unicode:characters_to_list(Bin, Enc) of + String when is_list(String) -> + {ok, String}; + _ -> + {error, invalid_unicode} + end; {error, Reason} -> {error, Reason} end. @@ -818,7 +839,7 @@ find_sources(Path, Pkg, Rec, Ext, Opts) -> lists:flatten(find_sources_1(Path, to_atom(Pkg), Rec, Ext, Skip)). find_sources_1([P | Ps], Pkg, Rec, Ext, Skip) -> - Dir = filename:join(P, filename:join(packages:split(Pkg))), + Dir = filename:join(P, atom_to_list(Pkg)), Fs1 = find_sources_1(Ps, Pkg, Rec, Ext, Skip), case filelib:is_dir(Dir) of true -> @@ -844,9 +865,9 @@ find_sources_2(Dir, Pkg, Rec, Ext, Skip) -> [] end. -find_sources_3(Es, Dir, Pkg, Rec, Ext, Skip) -> +find_sources_3(Es, Dir, '', Rec, Ext, Skip) -> [find_sources_2(filename:join(Dir, E), - to_atom(packages:concat(Pkg, E)), Rec, Ext, Skip) + to_atom(E), Rec, Ext, Skip) || E <- Es, is_package_dir(E, Dir)]. is_source_file(Name, Ext) -> @@ -858,16 +879,16 @@ is_package_dir(Name, Dir) -> andalso filelib:is_dir(filename:join(Dir, Name)). %% @private -find_file([P | Ps], Pkg, Name) -> - Dir = filename:join(P, filename:join(packages:split(Pkg))), - File = filename:join(Dir, Name), +find_file([P | Ps], []=Pkg, Name) -> + Pkg = [], + File = filename:join(P, Name), case filelib:is_file(File) of true -> File; false -> find_file(Ps, Pkg, Name) end; -find_file([], _Pkg, _Name) -> +find_file([], [], _Name) -> "". %% @private diff --git a/lib/edoc/src/edoc_macros.erl b/lib/edoc/src/edoc_macros.erl index 70fb38bf0a..08686c4fb5 100644 --- a/lib/edoc/src/edoc_macros.erl +++ b/lib/edoc/src/edoc_macros.erl @@ -88,13 +88,13 @@ link_macro(S, Line, Env) -> true -> " target=\"_top\""; % note the initial space false -> "" end, - lists:flatten(io_lib:fwrite("<a href=\"~s\"~s>~s</a>", + lists:flatten(io_lib:fwrite("<a href=\"~s\"~s>~ts</a>", [URI, Target, Txt])). section_macro(S, _Line, _Env) -> S1 = lists:reverse(edoc_lib:strip_space( lists:reverse(edoc_lib:strip_space(S)))), - lists:flatten(io_lib:format("<a href=\"#~s\">~s</a>", + lists:flatten(io_lib:format("<a href=\"#~ts\">~ts</a>", [edoc_lib:to_label(S1), S1])). type_macro(S, Line, Env) -> diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl index d7c1b1c045..cf1a2d6b11 100644 --- a/lib/edoc/src/edoc_parser.yrl +++ b/lib/edoc/src/edoc_parser.yrl @@ -1,6 +1,7 @@ +%% -*- coding: utf-8 -*- %% ========================== -*-Erlang-*- ============================= %% EDoc type specification grammar for the Yecc parser generator, -%% adapted from Sven-Olof Nystr�m's type specification parser. +%% adapted from Sven-Olof Nyström's type specification parser. %% %% Also contains entry points for parsing things like typedefs, %% references, and throws-declarations. @@ -317,10 +318,7 @@ tok_val(T) -> element(3, T). tok_line(T) -> element(2, T). -qname([A]) -> - A; % avoid unnecessary call to packages:concat/1. -qname(List) -> - list_to_atom(packages:concat(lists:reverse(List))). +qname([A]) -> A. union(Ts) -> case Ts of diff --git a/lib/edoc/src/edoc_refs.erl b/lib/edoc/src/edoc_refs.erl index 1f578a3b83..ea439490ed 100644 --- a/lib/edoc/src/edoc_refs.erl +++ b/lib/edoc/src/edoc_refs.erl @@ -126,7 +126,7 @@ abs_uri({package, P}, Env) -> module_ref(M, Env) -> case (Env#env.modules)(M) of "" -> - File = packages:last(M) ++ Env#env.file_suffix, + File = atom_to_list(M) ++ Env#env.file_suffix, Path = relative_module_path(M, Env#env.package), join_uri(Path, escape_uri(File)); Base -> @@ -134,8 +134,7 @@ module_ref(M, Env) -> end. module_absref(M, Env) -> - join_segments(packages:split(M)) - ++ escape_uri(Env#env.file_suffix). + escape_uri(atom_to_list(M)) ++ escape_uri(Env#env.file_suffix). package_ref(P, Env) -> case (Env#env.packages)(P) of @@ -147,7 +146,7 @@ package_ref(P, Env) -> end. package_absref(P, Env) -> - join_uri(join_segments(packages:split(P)), + join_uri(escape_uri(atom_to_list(P)), escape_uri(Env#env.package_summary)). app_ref(A, Env) -> @@ -179,14 +178,11 @@ join_segments([S | Ss]) -> %% The empty string is returned if the To module has only one segment, %% implying a local reference. -relative_module_path(To, From) -> - case first(packages:split(To)) of - [] -> ""; - P -> relative_path(P, packages:split(From)) - end. +relative_module_path(_To, _From) -> + "". relative_package_path(To, From) -> - relative_path(packages:split(To), packages:split(From)). + relative_path([atom_to_list(To)], [atom_to_list(From)]). %% This takes two lists of path segments (From, To). Note that an empty %% string will be returned if the paths are the same. Empty leading @@ -210,6 +206,3 @@ relative_path_2([], []) -> ""; relative_path_2([], Ts) -> join_segments(Ts). - -first([H | T]) when T /= [] -> [H | first(T)]; -first(_) -> []. diff --git a/lib/edoc/src/edoc_wiki.erl b/lib/edoc/src/edoc_wiki.erl index 5c71658af5..cc0529d2a9 100644 --- a/lib/edoc/src/edoc_wiki.erl +++ b/lib/edoc/src/edoc_wiki.erl @@ -80,6 +80,7 @@ parse_xml(Data, Line) -> parse_xml_1(Text, Line) -> Text1 = "<doc>" ++ Text ++ "</doc>", + %% Any coding except "utf-8". Opts = [{line, Line}, {encoding, 'iso-8859-1'}], case catch {ok, xmerl_scan:string(Text1, Opts)} of {ok, {E, _}} -> @@ -174,7 +175,7 @@ expand_heading_1(Cs, N, L, As) -> expand_heading_2(Ts, Cs, N, L, As) -> H = ?BASE_HEADING + N, - Ts1 = io_lib:format("<h~w><a name=\"~s\">~s</a></h~w>\n", + Ts1 = io_lib:format("<h~w><a name=\"~ts\">~ts</a></h~w>\n", [H, make_label(Ts), Ts, H]), expand_new_line(Cs, L + 1, lists:reverse(lists:flatten(Ts1), As)). diff --git a/lib/eldap/src/eldap.erl b/lib/eldap/src/eldap.erl index b3249d4f56..5753cc4749 100644 --- a/lib/eldap/src/eldap.erl +++ b/lib/eldap/src/eldap.erl @@ -320,7 +320,7 @@ present(Attribute) when is_list(Attribute) -> %%% will match entries containing: 'sn: Tornkvist' %%% substrings(Type, SubStr) when is_list(Type), is_list(SubStr) -> - Ss = {'SubstringFilter_substrings',v_substr(SubStr)}, + Ss = v_substr(SubStr), {substrings,#'SubstringFilter'{type = Type, substrings = Ss}}. diff --git a/lib/erl_docgen/doc/src/doc-build.xml b/lib/erl_docgen/doc/src/doc-build.xml index 08410a1539..ae1b17dff5 100644 --- a/lib/erl_docgen/doc/src/doc-build.xml +++ b/lib/erl_docgen/doc/src/doc-build.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2011</year> + <year>1997</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -56,7 +56,8 @@ </p> <code> - 1> escript $(ERL_TOP)/lib/erl_docgen/priv/bin/codeline_preprocessing.escript ex1.xmlsrc ex1.xml + 1> escript $(ERL_TOP)/lib/erl_docgen/priv/bin/codeline_preprocessing.escript \ + ex1.xmlsrc ex1.xml </code> </section> </section> diff --git a/lib/erl_docgen/priv/bin/xml_from_edoc.escript b/lib/erl_docgen/priv/bin/xml_from_edoc.escript index 2cb81be1be..65a580dca2 100755 --- a/lib/erl_docgen/priv/bin/xml_from_edoc.escript +++ b/lib/erl_docgen/priv/bin/xml_from_edoc.escript @@ -2,7 +2,7 @@ %% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -100,10 +100,12 @@ module(File, Args) -> users_guide(File, Args) -> case filelib:is_regular(File) of true -> + Enc = epp:read_encoding(File, [{in_comment_only, false}]), + Encoding = [{encoding, Enc} || Enc =/= none], Opts = [{def, Args#args.def}, {app_default, "OTPROOT"}, {file_suffix, Args#args.suffix}, - {layout, Args#args.layout}], + {layout, Args#args.layout}] ++ Encoding, Env = edoc_lib:get_doc_env(Opts), @@ -115,7 +117,7 @@ users_guide(File, Args) -> Text = edoc_lib:run_layout(F, Opts), OutFile = "chapter" ++ Args#args.suffix, - edoc_lib:write_file(Text, ".", OutFile); + edoc_lib:write_file(Text, ".", OutFile, '', Encoding); false -> io:format("~s: not a regular file\n", [File]), usage() diff --git a/lib/erl_docgen/priv/fop.xconf b/lib/erl_docgen/priv/fop.xconf new file mode 100644 index 0000000000..70ecd608c3 --- /dev/null +++ b/lib/erl_docgen/priv/fop.xconf @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<!-- + # + # %CopyrightBegin% + # + # Copyright Ericsson AB 2009-2012. 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% + + --> +<!-- NOTE: This is the version of the configuration --> +<fop version="1.0"> + <!-- The substitutions of DejaVu 700 are there because FOP outputs + warnings about doing the substitutions otherwise --> + <fonts> + <substitutions> + <substitution> + <from font-family="DejaVuSans" font-weight="700"/> + <to font-family="DejaVuSans" font-weight="400"/> + </substitution> + <substitution> + <from font-family="DejaVuSansMono" font-weight="700"/> + <to font-family="DejaVuSansMono" font-weight="400"/> + </substitution> + </substitutions> + </fonts> + <renderers> + <renderer mime="application/pdf"> + <fonts> + <auto-detect/> + </fonts> + </renderer> + </renderers> +</fop> diff --git a/lib/erl_docgen/priv/xsl/db_pdf_params.xsl b/lib/erl_docgen/priv/xsl/db_pdf_params.xsl index 4e61f1f476..2e3b22acf4 100644 --- a/lib/erl_docgen/priv/xsl/db_pdf_params.xsl +++ b/lib/erl_docgen/priv/xsl/db_pdf_params.xsl @@ -3,7 +3,7 @@ # # %CopyrightBegin% # - # Copyright Ericsson AB 2009-2011. All Rights Reserved. + # Copyright Ericsson AB 2009-2012. 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 @@ -52,7 +52,7 @@ <!-- XSL-FO properties --> <xsl:attribute-set name="caption"> - <xsl:attribute name="font-family">sans-serif</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute> <xsl:attribute name="font-size">0.8em</xsl:attribute> <xsl:attribute name="font-weight">bold</xsl:attribute> <xsl:attribute name="keep-with-previous.within-page">always</xsl:attribute> @@ -61,7 +61,7 @@ </xsl:attribute-set> <xsl:attribute-set name="pre"> - <xsl:attribute name="font-family">monospace</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSansMono, monospace</xsl:attribute> <xsl:attribute name="font-size">0.8em</xsl:attribute> <xsl:attribute name="keep-together.within-page">auto</xsl:attribute> <xsl:attribute name="linefeed-treatment">preserve</xsl:attribute> @@ -162,7 +162,7 @@ <xsl:attribute name="border-after-width">1pt</xsl:attribute> <xsl:attribute name="border-color"><xsl:value-of select="$pdfcolor"/></xsl:attribute> <xsl:attribute name="break-before">page</xsl:attribute> - <xsl:attribute name="font-family">sans-serif</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute> <xsl:attribute name="font-size">1.83em</xsl:attribute> <xsl:attribute name="font-weight">normal</xsl:attribute> <xsl:attribute name="space-after">1em</xsl:attribute> @@ -171,7 +171,7 @@ </xsl:attribute-set> <xsl:attribute-set name="h2"> - <xsl:attribute name="font-family">sans-serif</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute> <xsl:attribute name="font-size">1.5em</xsl:attribute> <xsl:attribute name="font-weight">normal</xsl:attribute> <xsl:attribute name="keep-with-next.within-page">always</xsl:attribute> @@ -180,7 +180,7 @@ </xsl:attribute-set> <xsl:attribute-set name="h3"> - <xsl:attribute name="font-family">sans-serif</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute> <xsl:attribute name="font-size">1.33em</xsl:attribute> <xsl:attribute name="font-weight">normal</xsl:attribute> <xsl:attribute name="keep-with-next.within-page">always</xsl:attribute> @@ -189,7 +189,7 @@ </xsl:attribute-set> <xsl:attribute-set name="h4"> - <xsl:attribute name="font-family">sans-serif</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute> <xsl:attribute name="font-size">1.17em</xsl:attribute> <xsl:attribute name="font-weight">normal</xsl:attribute> <xsl:attribute name="keep-with-next.within-page">always</xsl:attribute> @@ -198,7 +198,7 @@ </xsl:attribute-set> <xsl:attribute-set name="h5"> - <xsl:attribute name="font-family">sans-serif</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute> <xsl:attribute name="font-size">1em</xsl:attribute> <xsl:attribute name="font-weight">bold</xsl:attribute> <xsl:attribute name="keep-with-next.within-page">always</xsl:attribute> @@ -207,7 +207,7 @@ </xsl:attribute-set> <xsl:attribute-set name="h6"> - <xsl:attribute name="font-family">sans-serif</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute> <xsl:attribute name="font-size">0.83em</xsl:attribute> <xsl:attribute name="font-weight">bold</xsl:attribute> <xsl:attribute name="keep-with-next.within-page">always</xsl:attribute> @@ -227,20 +227,20 @@ <xsl:attribute name="border-after-style">solid</xsl:attribute> <xsl:attribute name="border-after-width">2pt</xsl:attribute> <xsl:attribute name="border-color"><xsl:value-of select="$pdfcolor"/></xsl:attribute> - <xsl:attribute name="font-family">sans-serif</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute> <xsl:attribute name="font-size">0.9em</xsl:attribute> <xsl:attribute name="font-weight">bold</xsl:attribute> </xsl:attribute-set> <xsl:attribute-set name="page-footer"> - <xsl:attribute name="font-family">sans-serif</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute> <xsl:attribute name="font-size">0.9em</xsl:attribute> <xsl:attribute name="font-weight">bold</xsl:attribute> </xsl:attribute-set> <xsl:attribute-set name="code"> <xsl:attribute name="background-color">#e0e0ff</xsl:attribute> - <xsl:attribute name="font-family">monospace</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSansMono, monospace</xsl:attribute> <xsl:attribute name="font-size">0.8em</xsl:attribute> <xsl:attribute name="keep-together.within-page">auto</xsl:attribute> <xsl:attribute name="linefeed-treatment">preserve</xsl:attribute> @@ -303,7 +303,7 @@ <xsl:attribute-set name="module-name"> <xsl:attribute name="border-after-style">solid</xsl:attribute> <xsl:attribute name="border-after-width">1pt</xsl:attribute> - <xsl:attribute name="font-family">sans-serif</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute> <xsl:attribute name="font-size">1.5em</xsl:attribute> <xsl:attribute name="font-weight">normal</xsl:attribute> <xsl:attribute name="keep-with-next.within-page">always</xsl:attribute> @@ -313,7 +313,7 @@ <xsl:attribute-set name="function-name"> <xsl:attribute name="font-weight">bold</xsl:attribute> - <xsl:attribute name="font-family">monospace</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSansMono, monospace</xsl:attribute> <!-- xsl:attribute name="font-size">0.8em</xsl:attribute --> <xsl:attribute name="keep-with-next.within-page">always</xsl:attribute> <xsl:attribute name="space-after">0.25em</xsl:attribute> @@ -401,7 +401,7 @@ </xsl:attribute-set> <xsl:attribute-set name="caption"> - <xsl:attribute name="font-family">sans-serif</xsl:attribute> + <xsl:attribute name="font-family">DejaVuSans, sans-serif</xsl:attribute> <xsl:attribute name="font-size">0.8em</xsl:attribute> <xsl:attribute name="font-weight">bold</xsl:attribute> <xsl:attribute name="keep-with-previous.within-page">always</xsl:attribute> diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl index cbaa93a15d..e3cc354206 100644 --- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl +++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl @@ -1,19 +1,20 @@ -%% ``The contents of this file are subject to the Erlang Public License, +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-2012. 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 via the world wide web at http://www.erlang.org/. +%% 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 expressed or implied. See -%% the Licence for the specific language governing rights and limitations +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations %% under the License. %% -%% The Initial Developer of the Original Code is Ericsson AB. -%% Portions created by Ericsson are Copyright 1999-2006, Ericsson AB. -%% All Rights Reserved.�� -%% -%% $Id$ +%% %CopyrightEnd% %% -module(docgen_edoc_xml_cb). @@ -39,12 +40,14 @@ module(Element, Opts) -> SortP = proplists:get_value(sort_functions, Opts, true), XML = layout_module(Element, SortP), - xmerl:export_simple([XML], docgen_xmerl_xml_cb, []). + RootAttributes = root_attributes(Element, Opts), + xmerl:export_simple([XML], docgen_xmerl_xml_cb, RootAttributes). %% CHAPTER -overview(Element, _Opts) -> +overview(Element, Opts) -> XML = layout_chapter(Element), - xmerl:export_simple([XML], docgen_xmerl_xml_cb, []). + RootAttributes = root_attributes(Element, Opts), + xmerl:export_simple([XML], docgen_xmerl_xml_cb, RootAttributes). %%--Internal functions-------------------------------------------------- @@ -99,6 +102,16 @@ layout_module(#xmlElement{name = module, content = Es}=E, SortP) -> ?NL,Authors] }. +root_attributes(Element, Opts) -> + Encoding = case get_attrval(encoding, Element) of + "" -> + DefaultEncoding = epp:default_encoding(), + proplists:get_value(encoding, Opts, DefaultEncoding); + Enc -> + Enc + end, + [#xmlAttribute{name=encoding, value=Encoding}]. + layout_chapter(#xmlElement{name=overview, content=Es}) -> Title = get_text(title, Es), Header = {header, [ diff --git a/lib/erl_docgen/src/docgen_xmerl_xml_cb.erl b/lib/erl_docgen/src/docgen_xmerl_xml_cb.erl index 884932ed12..d713b61c0a 100644 --- a/lib/erl_docgen/src/docgen_xmerl_xml_cb.erl +++ b/lib/erl_docgen/src/docgen_xmerl_xml_cb.erl @@ -1,19 +1,20 @@ -%% ``The contents of this file are subject to the Erlang Public License, +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-2012. 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 via the world wide web at http://www.erlang.org/. +%% 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 expressed or implied. See -%% the Licence for the specific language governing rights and limitations +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations %% under the License. %% -%% The Initial Developer of the Original Code is Ericsson AB. -%% Portions created by Ericsson are Copyright 1999-2006, Ericsson AB. -%% All Rights Reserved.�� -%% -%% $Id$ +%% %CopyrightEnd% %% -module(docgen_xmerl_xml_cb). @@ -35,9 +36,14 @@ '#xml-inheritance#'() -> [xmerl_xml]. -'#root#'(Data, _Attrs, [], _E) -> +'#root#'(Data, Attrs, [], _E) -> + Encoding = + case [E || #xmlAttribute{name = encoding, value = E} <- Attrs] of + [E] -> E; + _ -> atom_to_list(epp:default_encoding()) + end, ["<",DTD,">"] = hd(hd(Data)), - ["<?xml version=\"1.0\" encoding=\"latin1\" ?>\n", + ["<?xml version=\"1.0\" encoding=\"",Encoding,"\" ?>\n", "<!DOCTYPE "++DTD++" SYSTEM \""++DTD++".dtd\">\n", Data]. diff --git a/lib/erl_interface/test/ei_decode_SUITE.erl b/lib/erl_interface/test/ei_decode_SUITE.erl index bb44b78854..2c4b6e5541 100644 --- a/lib/erl_interface/test/ei_decode_SUITE.erl +++ b/lib/erl_interface/test/ei_decode_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -204,15 +205,15 @@ test_ei_decode_misc(Config) when is_list(Config) -> ?line send_term_as_binary(P,foo), ?line send_term_as_binary(P,''), - ?line send_term_as_binary(P,'������'), + ?line send_term_as_binary(P,'ÅÄÖåäö'), ?line send_term_as_binary(P,"foo"), ?line send_term_as_binary(P,""), - ?line send_term_as_binary(P,"������"), + ?line send_term_as_binary(P,"ÅÄÖåäö"), ?line send_term_as_binary(P,<<"foo">>), ?line send_term_as_binary(P,<<>>), - ?line send_term_as_binary(P,<<"������">>), + ?line send_term_as_binary(P,<<"ÅÄÖåäö">>), % ?line send_term_as_binary(P,{}), % ?line send_term_as_binary(P,[]), diff --git a/lib/erl_interface/test/ei_encode_SUITE.erl b/lib/erl_interface/test/ei_encode_SUITE.erl index cefd33e5f6..537e9cb01c 100644 --- a/lib/erl_interface/test/ei_encode_SUITE.erl +++ b/lib/erl_interface/test/ei_encode_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -213,19 +214,19 @@ test_ei_encode_misc(Config) when is_list(Config) -> ?line {<<100,0,3,"foo">>,foo} = get_buf_and_term(P), ?line {<<100,0,0,"">>,''} = get_buf_and_term(P), ?line {<<100,0,0,"">>,''} = get_buf_and_term(P), - ?line {<<100,0,6,"������">>,'������'} = get_buf_and_term(P), - ?line {<<100,0,6,"������">>,'������'} = get_buf_and_term(P), + ?line {<<100,0,6,"ÅÄÖåäö">>,'ÅÄÖåäö'} = get_buf_and_term(P), + ?line {<<100,0,6,"ÅÄÖåäö">>,'ÅÄÖåäö'} = get_buf_and_term(P), ?line {<<107,0,3,"foo">>,"foo"} = get_buf_and_term(P), ?line {<<107,0,3,"foo">>,"foo"} = get_buf_and_term(P), ?line {<<106>>,""} = get_buf_and_term(P), ?line {<<106>>,""} = get_buf_and_term(P), - ?line {<<107,0,6,"������">>,"������"} = get_buf_and_term(P), - ?line {<<107,0,6,"������">>,"������"} = get_buf_and_term(P), + ?line {<<107,0,6,"ÅÄÖåäö">>,"ÅÄÖåäö"} = get_buf_and_term(P), + ?line {<<107,0,6,"ÅÄÖåäö">>,"ÅÄÖåäö"} = get_buf_and_term(P), ?line {<<109,0,0,0,3,"foo">>,<<"foo">>} = get_buf_and_term(P), ?line {<<109,0,0,0,0,"">>,<<>>} = get_buf_and_term(P), - ?line {<<109,0,0,0,6,"������">>,<<"������">>} = get_buf_and_term(P), + ?line {<<109,0,0,0,6,"ÅÄÖåäö">>,<<"ÅÄÖåäö">>} = get_buf_and_term(P), ?line {<<104,0>>,{}} = get_buf_and_term(P), % Tuple header for {} ?line {<<106>>,[]} = get_buf_and_term(P), % Empty list [] diff --git a/lib/erl_interface/test/ei_print_SUITE.erl b/lib/erl_interface/test/ei_print_SUITE.erl index 2a3ed81f53..6305302e28 100644 --- a/lib/erl_interface/test/ei_print_SUITE.erl +++ b/lib/erl_interface/test/ei_print_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 @@ -136,7 +136,7 @@ lists(Config) when is_list(Config) -> %% ?line {term, "[{name, 'Madonna'}, {age, 21}, {data, [{addr, "E-street", 42}]}]"} = %% get_term(P), - %% kanske regexp i st�llet? + %% maybe regexp instead? ?line {term, "[{pi, 3.141500}, {'cos(70)', 0.342020}]"} = get_term(P), ?line {term, "[[pi, 3.141500], ['cos(70)', 0.342020]]"} = get_term(P), diff --git a/lib/et/src/et.erl b/lib/et/src/et.erl index e2cd8564c3..c9ba4f6816 100644 --- a/lib/et/src/et.erl +++ b/lib/et/src/et.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. 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 @@ -102,7 +102,7 @@ %% From = actor() %% To = actor() %% FromTo = actor() -%% Label = atom() | string() |�term() +%% Label = atom() | string() | term() %% Contents = [{Key, Value}] | term() %% %% actor() = term() diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl index fba840c3bd..8ebdb6ba16 100644 --- a/lib/eunit/include/eunit.hrl +++ b/lib/eunit/include/eunit.hrl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% This library is free software; you can redistribute it and/or modify %% it under the terms of the GNU Lesser General Public License as %% published by the Free Software Foundation; either version 2 of the @@ -13,7 +14,7 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% Copyright (C) 2004-2006 Micka�l R�mond, Richard Carlsson +%% Copyright (C) 2004-2006 Mickaël Rémond, Richard Carlsson %% Including this file turns on testing and defines TEST, unless NOTEST %% is defined before the file is included. If both NOTEST and TEST are @@ -124,8 +125,8 @@ -ifndef(UNDER_EUNIT). -define(UNDER_EUNIT, (?MATCHES({current_function,{eunit_proc,_,_}}, - .erlang:process_info(.erlang:group_leader(), - current_function)))). + erlang:process_info(erlang:group_leader(), + current_function)))). -endif. %% The plain assert macro should be defined to do nothing if this file @@ -142,14 +143,14 @@ ((fun () -> case (BoolExpr) of true -> ok; - __V -> .erlang:error({assertion_failed, - [{module, ?MODULE}, - {line, ?LINE}, - {expression, (??BoolExpr)}, - {expected, true}, - {value, case __V of false -> __V; - _ -> {not_a_boolean,__V} - end}]}) + __V -> erlang:error({assertion_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {expression, (??BoolExpr)}, + {expected, true}, + {value, case __V of false -> __V; + _ -> {not_a_boolean,__V} + end}]}) end end)())). -endif. @@ -170,12 +171,12 @@ ((fun () -> case (Expr) of Guard -> ok; - __V -> .erlang:error({assertMatch_failed, - [{module, ?MODULE}, - {line, ?LINE}, - {expression, (??Expr)}, - {pattern, (??Guard)}, - {value, __V}]}) + __V -> erlang:error({assertMatch_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {expression, (??Expr)}, + {pattern, (??Guard)}, + {value, __V}]}) end end)())). -endif. @@ -189,12 +190,12 @@ ((fun () -> __V = (Expr), case __V of - Guard -> .erlang:error({assertNotMatch_failed, - [{module, ?MODULE}, - {line, ?LINE}, - {expression, (??Expr)}, - {pattern, (??Guard)}, - {value, __V}]}); + Guard -> erlang:error({assertNotMatch_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {expression, (??Expr)}, + {pattern, (??Guard)}, + {value, __V}]}); _ -> ok end end)())). @@ -210,12 +211,12 @@ ((fun (__X) -> case (Expr) of __X -> ok; - __V -> .erlang:error({assertEqual_failed, - [{module, ?MODULE}, - {line, ?LINE}, - {expression, (??Expr)}, - {expected, __X}, - {value, __V}]}) + __V -> erlang:error({assertEqual_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {expression, (??Expr)}, + {expected, __X}, + {value, __V}]}) end end)(Expect))). -endif. @@ -228,11 +229,11 @@ -define(assertNotEqual(Unexpected, Expr), ((fun (__X) -> case (Expr) of - __X -> .erlang:error({assertNotEqual_failed, - [{module, ?MODULE}, - {line, ?LINE}, - {expression, (??Expr)}, - {value, __X}]}); + __X -> erlang:error({assertNotEqual_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {expression, (??Expr)}, + {value, __X}]}); _ -> ok end end)(Unexpected))). @@ -248,7 +249,7 @@ -define(assertException(Class, Term, Expr), ((fun () -> try (Expr) of - __V -> .erlang:error({assertException_failed, + __V -> erlang:error({assertException_failed, [{module, ?MODULE}, {line, ?LINE}, {expression, (??Expr)}, @@ -259,16 +260,16 @@ catch Class:Term -> ok; __C:__T -> - .erlang:error({assertException_failed, - [{module, ?MODULE}, - {line, ?LINE}, - {expression, (??Expr)}, - {pattern, - "{ "++(??Class)++" , "++(??Term) - ++" , [...] }"}, - {unexpected_exception, - {__C, __T, - .erlang:get_stacktrace()}}]}) + erlang:error({assertException_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {expression, (??Expr)}, + {pattern, + "{ "++(??Class)++" , "++(??Term) + ++" , [...] }"}, + {unexpected_exception, + {__C, __T, + erlang:get_stacktrace()}}]}) end end)())). -endif. @@ -299,17 +300,17 @@ Class -> case __T of Term -> - .erlang:error({assertNotException_failed, - [{module, ?MODULE}, - {line, ?LINE}, - {expression, (??Expr)}, - {pattern, - "{ "++(??Class)++" , " - ++(??Term)++" , [...] }"}, - {unexpected_exception, - {__C, __T, - .erlang:get_stacktrace() - }}]}); + erlang:error({assertNotException_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {expression, (??Expr)}, + {pattern, + "{ "++(??Class)++" , " + ++(??Term)++" , [...] }"}, + {unexpected_exception, + {__C, __T, + erlang:get_stacktrace() + }}]}); _ -> ok end; _ -> ok @@ -324,17 +325,17 @@ %% require EUnit to be present at runtime, or at least eunit_lib.) %% these can be used for simply running commands in a controlled way --define(_cmd_(Cmd), (.eunit_lib:command(Cmd))). +-define(_cmd_(Cmd), (eunit_lib:command(Cmd))). -define(cmdStatus(N, Cmd), ((fun () -> case ?_cmd_(Cmd) of {(N), __Out} -> __Out; - {__N, _} -> .erlang:error({command_failed, - [{module, ?MODULE}, - {line, ?LINE}, - {command, (Cmd)}, - {expected_status,(N)}, - {status,__N}]}) + {__N, _} -> erlang:error({command_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {command, (Cmd)}, + {expected_status,(N)}, + {status,__N}]}) end end)())). -define(_cmdStatus(N, Cmd), ?_test(?cmdStatus(N, Cmd))). @@ -350,12 +351,12 @@ ((fun () -> case ?_cmd_(Cmd) of {(N), _} -> ok; - {__N, _} -> .erlang:error({assertCmd_failed, - [{module, ?MODULE}, - {line, ?LINE}, - {command, (Cmd)}, - {expected_status,(N)}, - {status,__N}]}) + {__N, _} -> erlang:error({assertCmd_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {command, (Cmd)}, + {expected_status,(N)}, + {status,__N}]}) end end)())). -endif. @@ -368,12 +369,12 @@ ((fun () -> case ?_cmd_(Cmd) of {_, (T)} -> ok; - {_, __T} -> .erlang:error({assertCmdOutput_failed, - [{module, ?MODULE}, - {line, ?LINE}, - {command,(Cmd)}, - {expected_output,(T)}, - {output,__T}]}) + {_, __T} -> erlang:error({assertCmdOutput_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {command,(Cmd)}, + {expected_output,(T)}, + {output,__T}]}) end end)())). -endif. @@ -394,12 +395,12 @@ -else. -define(debugMsg(S), (begin - .io:fwrite(user, <<"~s:~w:~w: ~s\n">>, - [?FILE, ?LINE, self(), S]), + 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(debugFmt(S, As), (?debugMsg(io_lib:format((S), (As))))). -define(debugVal(E), ((fun (__V) -> ?debugFmt(<<"~s = ~P">>, [(??E), __V, 15]), diff --git a/lib/eunit/src/eunit.erl b/lib/eunit/src/eunit.erl index 51846d73b3..5763949519 100644 --- a/lib/eunit/src/eunit.erl +++ b/lib/eunit/src/eunit.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% This library is free software; you can redistribute it and/or modify %% it under the terms of the GNU Lesser General Public License as %% published by the Free Software Foundation; either version 2 of the @@ -13,8 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% @copyright 2004-2009 Micka�l R�mond, Richard Carlsson -%% @author Micka�l R�mond <[email protected]> +%% @copyright 2004-2009 Mickaël Rémond, Richard Carlsson +%% @author Mickaël Rémond <[email protected]> %% [http://www.process-one.net/] %% @author Richard Carlsson <[email protected]> %% @version {@version}, {@date} {@time} diff --git a/lib/eunit/src/eunit_autoexport.erl b/lib/eunit/src/eunit_autoexport.erl index 099bcb222e..36ae3b71d7 100644 --- a/lib/eunit/src/eunit_autoexport.erl +++ b/lib/eunit/src/eunit_autoexport.erl @@ -80,10 +80,9 @@ rewrite([F | Fs], As, Module, Test) -> rewrite(Fs, [F | As], Module, Test); rewrite([], As, Module, Test) -> {if Test -> - EUnit = {record_field,0,{atom,0,''},{atom,0,eunit}}, [{function,0,test,0, [{clause,0,[],[], - [{call,0,{remote,0,EUnit,{atom,0,test}}, + [{call,0,{remote,0,{atom,0,eunit},{atom,0,test}}, [{atom,0,Module}]}]}]} | As]; true -> @@ -92,9 +91,7 @@ rewrite([], As, Module, Test) -> Test}. module_decl(Name, M, Fs, Exports) -> - Module = if is_atom(Name) -> Name; - true -> list_to_atom(packages:concat(Name)) - end, + Module = Name, {Fs1, Test} = rewrite(Fs, [], Module, true), Es = if Test -> [{test,0} | Exports]; true -> Exports diff --git a/lib/eunit/src/eunit_lib.erl b/lib/eunit/src/eunit_lib.erl index ea9e944d7e..809cb7ab7b 100644 --- a/lib/eunit/src/eunit_lib.erl +++ b/lib/eunit/src/eunit_lib.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% This library is free software; you can redistribute it and/or modify %% it under the terms of the GNU Lesser General Public License as %% published by the Free Software Foundation; either version 2 of the @@ -13,8 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% @copyright 2004-2007 Micka�l R�mond, Richard Carlsson -%% @author Micka�l R�mond <[email protected]> +%% @copyright 2004-2007 Mickaël Rémond, Richard Carlsson +%% @author Mickaël Rémond <[email protected]> %% [http://www.process-one.net/] %% @author Richard Carlsson <[email protected]> %% @private diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl index 46b8c8b503..cc021625d5 100644 --- a/lib/eunit/src/eunit_surefire.erl +++ b/lib/eunit/src/eunit_surefire.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% This library is free software; you can redistribute it and/or modify %% it under the terms of the GNU Lesser General Public License as %% published by the Free Software Foundation; either version 2 of the @@ -13,8 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% @author Micka�l R�mond <[email protected]> -%% @copyright 2009 Micka�l R�mond, Paul Guyot +%% @author Mickaël Rémond <[email protected]> +%% @copyright 2009 Mickaël Rémond, Paul Guyot %% @see eunit %% @doc Surefire reports for EUnit (Format used by Maven and Atlassian %% Bamboo for example to integrate test results). Based on initial code diff --git a/lib/gs/contribs/mandel/mandel.erl b/lib/gs/contribs/mandel/mandel.erl index 8ecd649532..a7a786ce98 100644 --- a/lib/gs/contribs/mandel/mandel.erl +++ b/lib/gs/contribs/mandel/mandel.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -34,7 +35,7 @@ %%%----------------------------------------------------------------- %%% Distributed Mandelbrot program. %%% Originally written i C++/rpc/lwp/interviews by Klas Eriksson.(1200 lines) -%%% Rewritten in Erlang by Klas Eriksson and Martin Bj�rklund. +%%% Rewritten in Erlang by Klas Eriksson and Martin Björklund. %%%----------------------------------------------------------------- %% unix>erl -sname foo (all nodes will get the same name) diff --git a/lib/gs/src/gstk_editor.erl b/lib/gs/src/gstk_editor.erl index e918d93147..cb422aef95 100644 --- a/lib/gs/src/gstk_editor.erl +++ b/lib/gs/src/gstk_editor.erl @@ -90,9 +90,9 @@ %% type %% -%.t tag names 2.7 -> red blue (blue �r f�rgen) -%.t tag add blue 2.1 2.10 tagga text -%.t tag configure blue -foregr blue skapa tag +%.t tag names 2.7 -> red blue (blue is the colour) +%.t tag add blue 2.1 2.10 tag the text +%.t tag configure blue -foregr blue create tag % .t index end -> MaxRows.cols % .t yview moveto (Row-1)/MaxRows diff --git a/lib/hipe/amd64/hipe_amd64_encode.erl b/lib/hipe/amd64/hipe_amd64_encode.erl index ee68dfb3b8..cbdab25b25 100644 --- a/lib/hipe/amd64/hipe_amd64_encode.erl +++ b/lib/hipe/amd64/hipe_amd64_encode.erl @@ -1,7 +1,7 @@ %%% %%% %CopyrightBegin% %%% -%%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%%% Copyright Ericsson AB 2004-2012. 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 @@ -373,16 +373,16 @@ sse2_arith_binop_encode(Prefix, Opcode, {{xmm, XMM64}, {rm64fp, RM64}}) -> [Prefix, 16#0F, Opcode | encode_rm(RM64, XMM64, [])]. sse2_cvtsi2sd_encode({{xmm,XMM64}, {rm64,RM64}}) -> - [rex([{w, 1}]), 16#F2, 16#0F, 16#2A�| encode_rm(RM64, XMM64, [])]. + [rex([{w, 1}]), 16#F2, 16#0F, 16#2A | encode_rm(RM64, XMM64, [])]. sse2_mov_encode(Opnds) -> case Opnds of {{xmm, XMM64}, {rm64fp, RM64}} -> % movsd - [16#F2, 16#0F, 16#10�| encode_rm(RM64, XMM64, [])]; + [16#F2, 16#0F, 16#10 | encode_rm(RM64, XMM64, [])]; {{rm64fp, RM64}, {xmm, XMM64}} -> % movsd - [16#F2, 16#0F, 16#11�| encode_rm(RM64, XMM64, [])] + [16#F2, 16#0F, 16#11 | encode_rm(RM64, XMM64, [])] % {{xmm, XMM64}, {rm64, RM64}} -> % cvtsi2sd -% [rex([{w, 1}]), 16#F2, 16#0F, 16#2A�| encode_rm(RM64, XMM64, [])] +% [rex([{w, 1}]), 16#F2, 16#0F, 16#2A | encode_rm(RM64, XMM64, [])] end. %% arith_binop_sizeof(Opnds) -> diff --git a/lib/hipe/flow/hipe_dominators.erl b/lib/hipe/flow/hipe_dominators.erl index 17357461a5..1f2c830eaf 100644 --- a/lib/hipe/flow/hipe_dominators.erl +++ b/lib/hipe/flow/hipe_dominators.erl @@ -1,8 +1,8 @@ -%% -*- erlang-indent-level: 2 -*- +%% -*- coding: utf-8; erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-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 @@ -19,7 +19,7 @@ %% %%------------------------------------------------------------------------ %% File : hipe_dominators.erl -%% Author : Christoffer Vikstr�m <[email protected]> +%% Author : Christoffer Vikström <[email protected]> %% Daniel Deogun <[email protected]> %% Jesper Bengtsson <[email protected]> %% Created : 18 Mar 2002 diff --git a/lib/hipe/icode/hipe_icode_mulret.erl b/lib/hipe/icode/hipe_icode_mulret.erl index a3cae621ab..0bf9f89994 100644 --- a/lib/hipe/icode/hipe_icode_mulret.erl +++ b/lib/hipe/icode/hipe_icode_mulret.erl @@ -1,8 +1,8 @@ -%% -*- erlang-indent-level: 2 -*- +%% -*- coding: utf-8; erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -19,9 +19,9 @@ %% %%---------------------------------------------------------------------- %% File : hipe_icode_mulret.erl -%% Author : Christoffer Vikstr�m <[email protected]> +%% Author : Christoffer Vikström <[email protected]> %% Purpose : -%% Created : 23 Jun 2004 by Christoffer Vikstr�m <[email protected]> +%% Created : 23 Jun 2004 by Christoffer Vikström <[email protected]> %%---------------------------------------------------------------------- -module(hipe_icode_mulret). @@ -890,7 +890,7 @@ removeUnElems([I|Code], [OldVar] = OldVars, DstLst, Res, Def, Lab) -> %% [I|Res], Def, Lab) %% end; false -> - io:format("Borde aldrig kunna hamna h�r!", []), + io:format("Borde aldrig kunna hamna här!", []), removeUnElems(Code, OldVars, DstLst, [I|Res], Def, Lab) end end; @@ -1159,8 +1159,8 @@ printCallList([]) -> io:format("~n"). %% % Purpose : %% % Arguments : %% % Return : -%% % Notes : Fixa s� att funktionen anv�nder defines(I) ist�llet och -%% % selektorer ist�llet f�r att matcha p� #call{}. L�tt gjort. +%% % Notes : Fixa så att funktionen använder defines(I) istället och +%% % selektorer istället för att matcha på #call{}. Lätt gjort. %% %%>----------------------------------------------------------------------< %% removeUnElems(List, Var) -> removeUnElems(List, Var, []). %% removeUnElems([#icode_call{'fun'={unsafe_element,_}, args=Var}|List], Var, Res) -> diff --git a/lib/hipe/regalloc/hipe_coalescing_regalloc.erl b/lib/hipe/regalloc/hipe_coalescing_regalloc.erl index 5a4b017c71..7169dd18f3 100644 --- a/lib/hipe/regalloc/hipe_coalescing_regalloc.erl +++ b/lib/hipe/regalloc/hipe_coalescing_regalloc.erl @@ -1,8 +1,8 @@ -%% -*- erlang-indent-level: 2 -*- +%% -*- coding: utf-8; erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 @@ -20,8 +20,8 @@ %%----------------------------------------------------------------------- %% File : hipe_coalescing_regalloc.erl %% Authors : Andreas Wallin <[email protected]> -%% Thorild Sel�n <[email protected]> -%% Ingemar �berg <[email protected]> +%% Thorild Selén <[email protected]> +%% Ingemar Åberg <[email protected]> %% Purpose : Play paintball with registers on a target machine. We win %% if they are all colored. This is an iterated coalescing %% register allocator. diff --git a/lib/hipe/regalloc/hipe_optimistic_regalloc.erl b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl index 183ec1994c..fc3718cbc0 100644 --- a/lib/hipe/regalloc/hipe_optimistic_regalloc.erl +++ b/lib/hipe/regalloc/hipe_optimistic_regalloc.erl @@ -1,8 +1,8 @@ -%% -*- erlang-indent-level: 2 -*- +%% -*- coding: utf-8; erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -1657,9 +1657,9 @@ findPrimitiveNodes(Node, N, Alias, PrimitiveNodes) -> % %?debug_msg("Node ~p~n", [Node]), % NextNode = Node - 1, % Coalesced_to = hipe_reg_worklists:member_coalesced_to(NextNode, Worklists), -% ?debug_msg("��-- member coalesced: ~p~n", [Coalesced_to]), +% ?debug_msg("³³-- member coalesced: ~p~n", [Coalesced_to]), % {Primitives, Alias1} = undoCoalescing(NextNode, No_temporaries, Alias), -% ?debug_msg("��-- primitivenodes ~w\n", [Primitives]), +% ?debug_msg("½½-- primitivenodes ~w\n", [Primitives]), % case (Coalesced_to) of % true -> printAlias(Alias1); % _ -> true @@ -1683,9 +1683,9 @@ findPrimitiveNodes(Node, N, Alias, PrimitiveNodes) -> fixAdj(N, SavedAdj, IG, Target) -> %Saved = hipe_vectors:get(SavedAdj, N), Saved = hipe_adj_list:edges(N, SavedAdj), - ?debug_msg("��--adj to ~p: ~p~n", [N, Saved]), + ?debug_msg("§§--adj to ~p: ~p~n", [N, Saved]), Adj = hipe_ig:node_adj_list(N, IG), - ?debug_msg("��--adj to ~p: ~p~n", [N, Adj]), + ?debug_msg("««--adj to ~p: ~p~n", [N, Adj]), New = findNew(Adj, Saved), ?debug_msg("++--new adj to ~p: ~p~n", [N, New]), removeAdj(New, N, IG, Target), diff --git a/lib/hipe/regalloc/hipe_reg_worklists.erl b/lib/hipe/regalloc/hipe_reg_worklists.erl index 67a5788c7c..e22cc8dc07 100644 --- a/lib/hipe/regalloc/hipe_reg_worklists.erl +++ b/lib/hipe/regalloc/hipe_reg_worklists.erl @@ -1,8 +1,8 @@ -%%% -*- erlang-indent-level: 2 -*- +%%% -*- coding: utf-8; erlang-indent-level: 2 -*- %%% %%% %CopyrightBegin% %%% -%%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%%% Copyright Ericsson AB 2001-2012. 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 @@ -28,7 +28,7 @@ %%%---------------------------------------------------------------------- -module(hipe_reg_worklists). --author(['Andreas Wallin', 'Thorild Sel�n']). +-author(['Andreas Wallin', 'Thorild Selén']). -export([new/5, % only used by optimistic allocator new/6, simplify/1, diff --git a/lib/hipe/rtl/hipe_rtl_arith.inc b/lib/hipe/rtl/hipe_rtl_arith.inc index e608506234..7b587e882d 100644 --- a/lib/hipe/rtl/hipe_rtl_arith.inc +++ b/lib/hipe/rtl/hipe_rtl_arith.inc @@ -1,9 +1,9 @@ %% -*- Erlang -*- -%% -*- erlang-indent-level: 2 -*- +%% -*- coding: utf-8; erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -114,7 +114,7 @@ eval_alu(Op, Arg1, Arg2) eval_alu(Op, Arg1, Arg2) -> ?EXIT({argument_overflow,Op,Arg1,Arg2}). -%% Bj�rn & Bjarni: +%% Björn & Bjarni: %% We need to be able to do evaluations based only on the bits, since %% there are cases where we can evaluate a subset of the bits, but can %% not do a full eval-alub call (eg. a + 0 gives no carry) diff --git a/lib/hipe/rtl/hipe_rtl_mk_switch.erl b/lib/hipe/rtl/hipe_rtl_mk_switch.erl index e5175217d6..d859c50b7d 100644 --- a/lib/hipe/rtl/hipe_rtl_mk_switch.erl +++ b/lib/hipe/rtl/hipe_rtl_mk_switch.erl @@ -1,8 +1,8 @@ -%% -*- erlang-indent-level: 2 -*- +%% -*- coding: utf-8; erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 @@ -31,7 +31,7 @@ %% History : * 2001-02-28 Erik Johansson ([email protected]): %% Created. %% * 2001-04-01 Erik Trulsson ([email protected]): -%% Stefan Lindstr�m ([email protected]): +%% Stefan Lindström ([email protected]): %% Added clustering and inlined binary search trees. %% * 2001-07-30 EJ ([email protected]): %% Fixed some bugs and started cleanup. diff --git a/lib/hipe/rtl/hipe_rtl_primops.erl b/lib/hipe/rtl/hipe_rtl_primops.erl index 53aaa72aa6..d9d08356ce 100644 --- a/lib/hipe/rtl/hipe_rtl_primops.erl +++ b/lib/hipe/rtl/hipe_rtl_primops.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 @@ -738,7 +738,7 @@ gen_mkfun([Dst], {_Mod, _FunId, _Arity} = MFidA, MagicNr, Index, FreeVars) -> %% Tag the thing and increase the heap_pointer. %% make_fun(funp); - WordSize�= hipe_rtl_arch:word_size(), + WordSize = hipe_rtl_arch:word_size(), HeapNeed = (?ERL_FUN_SIZE + NumFree) * WordSize, TagCode = [hipe_tagscheme:tag_fun(Dst, HP), %% AdjustHPCode @@ -829,7 +829,7 @@ load_struct_field(Dest, StructP, Offset, int32) -> gen_free_vars(Vars, HPReg) -> HPVar = hipe_rtl:mk_new_var(), - WordSize�= hipe_rtl_arch:word_size(), + WordSize = hipe_rtl_arch:word_size(), [hipe_rtl:mk_alu(HPVar, HPReg, add, hipe_rtl:mk_imm(?EFT_ENV)) | gen_free_vars(Vars, HPVar, 0, WordSize, [])]. diff --git a/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl b/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl index 194cf29b64..1c900d767e 100644 --- a/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl +++ b/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl @@ -1,8 +1,8 @@ -%% -*- erlang-indent-level: 2 -*- +%% -*- coding: utf-8; erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -190,7 +190,7 @@ set_to(Dst, Val, Env) -> %% Returns : { FlowWorkList, SSAWorkList, NewEnvironment} %%----------------------------------------------------------------------------- -visit_branch(Inst, Env) -> %% Titta ocks� p� exekverbarflagga +visit_branch(Inst, Env) -> %% Titta också på exekverbarflagga Val1 = lookup_lattice_value(hipe_rtl:branch_src1(Inst), Env), Val2 = lookup_lattice_value(hipe_rtl:branch_src2(Inst), Env), CFGWL = case evaluate_relop(Val1, hipe_rtl:branch_cond(Inst), Val2) of diff --git a/lib/hipe/rtl/hipe_rtl_ssapre.erl b/lib/hipe/rtl/hipe_rtl_ssapre.erl index a9e92e5688..34897ba4b7 100644 --- a/lib/hipe/rtl/hipe_rtl_ssapre.erl +++ b/lib/hipe/rtl/hipe_rtl_ssapre.erl @@ -1,8 +1,8 @@ -%% -*- erlang-indent-level: 2 -*- +%% -*- coding: utf-8; erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -19,7 +19,7 @@ %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% File : hipe_rtl_ssapre.erl -%% Author : He Bingwen and Fr�d�ric Haziza +%% Author : He Bingwen and Frédéric Haziza %% Description : Performs Partial Redundancy Elimination on SSA form. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% @doc @@ -552,7 +552,7 @@ emend_with_processed_xsis(E, [I|Rest], Pred, XsiGraph) -> true -> %% It's a computation of E! case xsi_arg(Xsi,Pred) of undetermined_operand -> - exit({?MODULE,check_operand_sharing,"######## �h Dear, we trusted Kostis !!!!!!!!! #############"}); + exit({?MODULE,check_operand_sharing,"######## Ôh Dear, we trusted Kostis !!!!!!!!! #############"}); XsiOp -> {sharing_operand,XsiOp} %% They share operands end; @@ -571,7 +571,7 @@ emend_with_processed_xsis(E, [I|Rest], Pred, XsiGraph) -> NewE = emend(E,Def,A#eop.var), emend_with_processed_xsis(NewE,Rest,Pred,XsiGraph); undetermined_operand -> - exit({?MODULE,emend_with_processed_xsis,"######## �h Dear, we trusted Kostis, again !!!!!!!!! #############"}); + exit({?MODULE,emend_with_processed_xsis,"######## Ôh Dear, we trusted Kostis, again !!!!!!!!! #############"}); XsiOp -> NewE = emend(E,Def,XsiOp), emend_with_processed_xsis(NewE,Rest,Pred,XsiGraph) diff --git a/lib/hipe/ssa/hipe_ssa.inc b/lib/hipe/ssa/hipe_ssa.inc index d15b5ddd56..e766a83c41 100644 --- a/lib/hipe/ssa/hipe_ssa.inc +++ b/lib/hipe/ssa/hipe_ssa.inc @@ -1,8 +1,8 @@ -%% -*- erlang-indent-level: 2 -*- +%% -*- coding: utf-8; erlang-indent-level: 2 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 @@ -19,7 +19,7 @@ %% %%---------------------------------------------------------------------- %% File : hipe_ssa.inc -%% Authors : Christoffer Vikstr�m, Daniel Deogun, and Jesper Bengtsson +%% Authors : Christoffer Vikström, Daniel Deogun, and Jesper Bengtsson %% Created : March 2002 %% Purpose : Provides code which converts the code of a CFG into SSA %% (Static Single Assignment) form and back. diff --git a/lib/hipe/x86/hipe_x86_assemble.erl b/lib/hipe/x86/hipe_x86_assemble.erl index 4e65736db3..7878c7219d 100644 --- a/lib/hipe/x86/hipe_x86_assemble.erl +++ b/lib/hipe/x86/hipe_x86_assemble.erl @@ -2,7 +2,7 @@ %%% %%% %CopyrightBegin% %%% -%%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%%% Copyright Ericsson AB 2001-2012. 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 @@ -698,7 +698,7 @@ mem_to_ea_common(#x86_mem{base=#x86_temp{reg=Base}, off=#x86_imm{value=Off}}) -> %% jmp_switch -ifdef(HIPE_AMD64). -resolve_jmp_switch_arg(I,�_Context) -> +resolve_jmp_switch_arg(I, _Context) -> Base = hipe_x86:temp_reg(hipe_x86:jmp_switch_jtab(I)), Index = hipe_x86:temp_reg(hipe_x86:jmp_switch_temp(I)), SINDEX = hipe_amd64_encode:sindex(3, Index), diff --git a/lib/hipe/x86/hipe_x86_postpass.erl b/lib/hipe/x86/hipe_x86_postpass.erl index 34e3d7a11b..c0918c4f89 100644 --- a/lib/hipe/x86/hipe_x86_postpass.erl +++ b/lib/hipe/x86/hipe_x86_postpass.erl @@ -1,8 +1,8 @@ -%%% -*- erlang-indent-level: 2 -*- +%%% -*- coding: utf-8; erlang-indent-level: 2 -*- %%% %%% %CopyrightBegin% %%% -%%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%%% Copyright Ericsson AB 2003-2012. 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 @@ -19,9 +19,9 @@ %%% %%%---------------------------------------------------------------------- %%% File : hipe_x86_postpass.erl -%%% Author : Christoffer Vikstr�m <[email protected]> +%%% Author : Christoffer Vikström <[email protected]> %%% Purpose : Contain postpass optimisations for x86-assembler code. -%%% Created : 5 Aug 2003 by Christoffer Vikstr�m <[email protected]> +%%% Created : 5 Aug 2003 by Christoffer Vikström <[email protected]> %%%---------------------------------------------------------------------- -ifndef(HIPE_X86_POSTPASS). diff --git a/lib/hipe/x86/hipe_x86_ra_postconditions.erl b/lib/hipe/x86/hipe_x86_ra_postconditions.erl index 0b70764daf..6d7e90df43 100644 --- a/lib/hipe/x86/hipe_x86_ra_postconditions.erl +++ b/lib/hipe/x86/hipe_x86_ra_postconditions.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 @@ -224,7 +224,7 @@ do_byte_move(Src0, Dst0, TempMap, Strategy) -> do_move64(I, TempMap, Strategy) -> #move64{dst=Dst} = I, - case�is_spilled(Dst, TempMap) of + case is_spilled(Dst, TempMap) of false -> {[I], false}; true -> diff --git a/lib/inets/doc/src/http_uri.xml b/lib/inets/doc/src/http_uri.xml index bd31ae42d2..d9e8587bbf 100644 --- a/lib/inets/doc/src/http_uri.xml +++ b/lib/inets/doc/src/http_uri.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2012</year><year>2012</year> + <year>2012</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -55,7 +55,8 @@ string() = list of ASCII characters <p>For more information about URI, see RFC 3986. </p> <code type="none"><![CDATA[ -uri() = string() - Syntax according to the URI definition in rfc 3986, ex: "http://www.erlang.org/" +uri() = string() - Syntax according to the URI definition in rfc 3986, + e.g.: "http://www.erlang.org/" user_info() = string() scheme() = atom() - Example: http, https host() = string() diff --git a/lib/jinterface/test/nc_SUITE.erl b/lib/jinterface/test/nc_SUITE.erl index d5388e54f4..c91c743498 100644 --- a/lib/jinterface/test/nc_SUITE.erl +++ b/lib/jinterface/test/nc_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -361,8 +362,8 @@ unicode(doc) -> []; unicode(suite) -> []; unicode(Config) when is_list(Config) -> S1 = "plain ascii", - S2 = "iso-latin ��� �", - S3 = "Codepoints... ��� \x{1000}", + S2 = "iso-latin åäö ñ", + S3 = "Codepoints... åäö \x{1000}", S4 = [0,1,31,32,63,64,127,128,255], S5 = [0,1,127,128,255,256,16#d7ff, 16#e000,16#fffd,16#10000,16#10ffff], diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index 5e04bff0c1..de3ca1e176 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -58,7 +58,6 @@ XML_REF3_FILES = application.xml \ net_adm.xml \ net_kernel.xml \ os.xml \ - packages.xml \ pg2.xml \ rpc.xml \ seq_trace.xml \ diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index b2a259080d..e30ade1bd2 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -110,7 +110,7 @@ <desc> <p>As returned by <seealso marker="#open/2">file:open/2</seealso>, - a process handling IO protocols.</p> + a process handling I/O-protocols.</p> </desc> </datatype> <datatype> @@ -170,6 +170,18 @@ </desc> </func> <func> + <name name="allocate" arity="3"/> + <fsummary>Allocate file space</fsummary> + <desc> + <p><c>allocate/3</c> can be used to preallocate space for a file.</p> + <p>This function only succeeds in platforms that implement this + feature. When it succeeds, space is preallocated for the file but + the file size might not be updated. This behaviour depends on the + preallocation implementation. To guarantee the file size is updated + one must truncate the file to the new size.</p> + </desc> + </func> + <func> <name name="change_group" arity="2"/> <fsummary>Change group of a file</fsummary> <desc> @@ -261,6 +273,9 @@ {person, "pelle", 30}.</code> <pre>1> <input>file:consult("f.txt").</input> {ok,[{person,"kalle",25},{person,"pelle",30}]}</pre> + <p>The encoding of of <c><anno>Filename</anno></c> can be set + by a comment as described in <seealso + marker="stdlib:epp#encoding">epp(3)</seealso>.</p> </desc> </func> <func> @@ -399,6 +414,9 @@ of the error.</p> </item> </taglist> + <p>The encoding of of <c><anno>Filename</anno></c> can be set + by a comment as described in <seealso + marker="stdlib:epp#encoding">epp(3)</seealso>.</p> </desc> </func> <func> @@ -610,7 +628,7 @@ <name name="open" arity="2"/> <fsummary>Open a file</fsummary> <desc> - <p>Opens the file <c><anno>Filename</anno></c> in the mode determined + <p>Opens the file <c><anno>File</anno></c> in the mode determined by <c><anno>Modes</anno></c>, which may contain one or more of the following items:</p> <taglist> @@ -767,6 +785,10 @@ <p>The Encoding can be changed for a file "on the fly" by using the <seealso marker="stdlib:io#setopts/2">io:setopts/2</seealso> function, why a file can be analyzed in latin1 encoding for i.e. a BOM, positioned beyond the BOM and then be set for the right encoding before further reading.See the <seealso marker="stdlib:unicode">unicode(3)</seealso> module for functions identifying BOM's.</p> <p>This option is not allowed on <c>raw</c> files.</p> </item> + <tag><c>ram</c></tag> + <item> + <p><c>File</c> must be <c>iodata()</c>. Returns an <c>fd()</c> which lets the <c>file</c> module operate on the data in-memory as if it is a file.</p> + </item> </taglist> <p>Returns:</p> <taglist> @@ -861,6 +883,9 @@ the error.</p> </item> </taglist> + <p>The encoding of of <c><anno>Filename</anno></c> can be set + by a comment as described in <seealso + marker="stdlib:epp#encoding">epp(3)</seealso>.</p> </desc> </func> <func> @@ -902,6 +927,9 @@ of the error.</p> </item> </taglist> + <p>The encoding of of <c><anno>Filename</anno></c> can be set + by a comment as described in <seealso + marker="stdlib:epp#encoding">epp(3)</seealso>.</p> </desc> </func> <func> @@ -971,7 +999,10 @@ of the error.</p> </item> </taglist> - </desc> + <p>The encoding of of <c><anno>Filename</anno></c> can be set + by a comment as described in <seealso + marker="stdlib:epp#encoding">epp(3)</seealso>.</p> + </desc> </func> <func> <name name="path_script" arity="3"/> @@ -1502,6 +1533,9 @@ of the error.</p> </item> </taglist> + <p>The encoding of of <c><anno>Filename</anno></c> can be set + by a comment as described in <seealso + marker="stdlib:epp#encoding">epp(3)</seealso>.</p> </desc> </func> <func> diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index a1590c2dce..3d929a772e 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -323,8 +323,11 @@ fe80::204:acff:fe17:bf38 <type name="stat_option"/> <desc> <p>Gets one or more statistic options for a socket.</p> + <p><c>getstat(<anno>Socket</anno>)</c> is equivalent to - <c>getstat(<anno>Socket</anno>, [recv_avg, recv_cnt, recv_dvi, recv_max, recv_oct, send_avg, send_cnt, send_dvi, send_max, send_oct])</c></p> + <c>getstat(<anno>Socket</anno>, [recv_avg, recv_cnt, recv_dvi, + recv_max, recv_oct, send_avg, send_cnt, send_dvi, send_max, + send_oct])</c>.</p> <p>The following options are available:</p> <taglist> <tag><c>recv_avg</c></tag> diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index c76ff9e2f0..60291bbce6 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -107,7 +107,6 @@ MODULES = \ net_adm \ net_kernel \ os \ - packages \ pg2 \ ram_file \ rpc \ diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index c808ac7cb7..361f2bdf8a 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -359,7 +359,6 @@ load_code_server_prerequisites() -> hipe_unified_loader, lists, os, - packages, unicode], [M = M:module_info(module) || M <- Needed], ok. @@ -413,7 +412,7 @@ which(Module) when is_atom(Module) -> end. which2(Module) -> - Base = to_path(Module), + Base = atom_to_list(Module), File = filename:basename(Base) ++ objfile_extension(), Path = get_path(), which(File, filename:dirname(Base), Path). @@ -547,9 +546,6 @@ has_ext(Ext, Extlen, File) -> _ -> false end. -to_path(X) -> - filename:join(packages:split(X)). - -spec load_native_code_for_all_loaded() -> ok. load_native_code_for_all_loaded() -> Architecture = erlang:system_info(hipe_architecture), diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 00ad923466..b2d2c19f78 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1229,7 +1229,7 @@ load_abs(File, Mod0, Caller, St) -> end. try_load_module(Mod, Dir, Caller, St) -> - File = filename:append(Dir, to_path(Mod) ++ + File = filename:append(Dir, to_list(Mod) ++ objfile_extension()), case erl_prim_loader:get_file(File) of error -> @@ -1347,7 +1347,7 @@ load_file_1(Mod, Caller, #state{cache=Cache}=St0) -> end. mod_to_bin([Dir|Tail], Mod) -> - File = filename:append(Dir, to_path(Mod) ++ objfile_extension()), + File = filename:append(Dir, to_list(Mod) ++ objfile_extension()), case erl_prim_loader:get_file(File) of error -> mod_to_bin(Tail, Mod); @@ -1356,7 +1356,7 @@ mod_to_bin([Dir|Tail], Mod) -> end; mod_to_bin([], Mod) -> %% At last, try also erl_prim_loader's own method - File = to_path(Mod) ++ objfile_extension(), + File = to_list(Mod) ++ objfile_extension(), case erl_prim_loader:get_file(File) of error -> error; % No more alternatives ! @@ -1570,6 +1570,3 @@ to_list(X) when is_atom(X) -> atom_to_list(X). to_atom(X) when is_atom(X) -> X; to_atom(X) when is_list(X) -> list_to_atom(X). - -to_path(X) -> - filename:join(packages:split(X)). diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index f0d54a2f3e..e3511988a6 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -757,7 +757,8 @@ setup_timer(Pid, Timeout) -> end. reset_timer(Timer) -> - Timer ! {self(), reset}. + Timer ! {self(), reset}, + ok. cancel_timer(Timer) -> unlink(Timer), diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 22af38c598..16f2dde464 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -38,7 +38,7 @@ %% Specialized -export([ipread_s32bu_p32bu/3]). %% Generic file contents. --export([open/2, close/1, advise/4, +-export([open/2, close/1, advise/4, allocate/3, read/2, write/2, pread/2, pread/3, pwrite/2, pwrite/3, read_line/1, @@ -397,9 +397,10 @@ raw_write_file_info(Name, #file_info{} = Info) -> %% Contemporary mode specification - list of options --spec open(Filename, Modes) -> {ok, IoDevice} | {error, Reason} when +-spec open(File, Modes) -> {ok, IoDevice} | {error, Reason} when + File :: Filename | iodata(), Filename :: name(), - Modes :: [mode()], + Modes :: [mode() | ram], IoDevice :: io_device(), Reason :: posix() | badarg | system_limit. @@ -489,6 +490,18 @@ advise(#file_descriptor{module = Module} = Handle, Offset, Length, Advise) -> advise(_, _, _, _) -> {error, badarg}. +-spec allocate(File, Offset, Length) -> + 'ok' | {'error', posix()} when + File :: io_device(), + Offset :: non_neg_integer(), + Length :: non_neg_integer(). + +allocate(File, Offset, Length) when is_pid(File) -> + R = file_request(File, {allocate, Offset, Length}), + wait_file_reply(File, R); +allocate(#file_descriptor{module = Module} = Handle, Offset, Length) -> + Module:allocate(Handle, Offset, Length). + -spec read(IoDevice, Number) -> {ok, Data} | eof | {error, Reason} when IoDevice :: io_device() | atom(), Number :: non_neg_integer(), @@ -1314,6 +1327,7 @@ sendfile_send(Sock, Data, Old) -> %%% Helpers consult_stream(Fd) -> + _ = epp:set_encoding(Fd), consult_stream(Fd, 1, []). consult_stream(Fd, Line, Acc) -> @@ -1327,6 +1341,7 @@ consult_stream(Fd, Line, Acc) -> end. eval_stream(Fd, Handling, Bs) -> + _ = epp:set_encoding(Fd), eval_stream(Fd, Handling, 1, undefined, [], Bs). eval_stream(Fd, H, Line, Last, E, Bs) -> diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl index 0bff56cf46..fad2ed7fb3 100644 --- a/lib/kernel/src/file_io_server.erl +++ b/lib/kernel/src/file_io_server.erl @@ -40,6 +40,8 @@ format_error({_Line, ?MODULE, Reason}) -> io_lib:format("~w", [Reason]); format_error({_Line, Mod, Reason}) -> Mod:format_error(Reason); +format_error(invalid_unicode) -> + io_lib:format("cannot translate from UTF-8", []); format_error(ErrorId) -> erl_posix_msg:message(ErrorId). @@ -209,6 +211,10 @@ file_request({advise,Offset,Length,Advise}, Reply -> {reply,Reply,State} end; +file_request({allocate, Offset, Length}, + #state{handle = Handle} = State) -> + Reply = ?PRIM_FILE:allocate(Handle, Offset, Length), + {reply, Reply, State}; file_request({pread,At,Sz}, #state{handle=Handle,buf=Buf,read_mode=ReadMode}=State) -> case position(Handle, At, Buf) of @@ -549,7 +555,7 @@ get_chars_notempty(Mod, Func, XtraArg, S, OutEnc, <<>> -> get_chars_apply(Mod, Func, XtraArg, S, OutEnc, State, eof); _ -> - {stop,invalid_unicode,{error,invalid_unicode},State} + {stop,invalid_unicode,invalid_unicode_error(Mod, Func, XtraArg, S),State} end; {error,Reason}=Error -> {stop,Reason,Error,State} @@ -616,12 +622,22 @@ get_chars_apply(Mod, Func, XtraArg, S0, OutEnc, end catch exit:ExReason -> - {stop,ExReason,{error,err_func(Mod, Func, XtraArg)},State}; + {stop,ExReason,invalid_unicode_error(Mod, Func, XtraArg, S0),State}; error:ErrReason -> {stop,ErrReason,{error,err_func(Mod, Func, XtraArg)},State} end. - +%% A hack that tries to inform the caller about the position where the +%% error occured. +invalid_unicode_error(Mod, Func, XtraArg, S) -> + try + {erl_scan,tokens,_Args} = XtraArg, + Location = erl_scan:continuation_location(S), + {error,{Location, ?MODULE, invalid_unicode},Location} + catch + _:_ -> + {error,err_func(Mod, Func, XtraArg)} + end. %% Convert error code to make it look as before err_func(io_lib, get_until, {_,F,_}) -> diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl index f92c6f7208..4d2e31a429 100644 --- a/lib/kernel/src/group.erl +++ b/lib/kernel/src/group.erl @@ -424,7 +424,7 @@ get_password_chars(Drv,Buf) -> end. get_chars(Prompt, M, F, Xa, Drv, Buf, Encoding) -> - Pbs = prompt_bytes(Prompt), + Pbs = prompt_bytes(Prompt, Encoding), get_chars_loop(Pbs, M, F, Xa, Drv, Buf, start, Encoding). get_chars_loop(Pbs, M, F, Xa, Drv, Buf0, State, Encoding) -> @@ -688,9 +688,9 @@ edit_password([Char|Cs],Chars) -> edit_password(Cs,[Char|Chars]). %% prompt_bytes(Prompt) -%% Return a flat list of bytes for the Prompt. -prompt_bytes(Prompt) -> - lists:flatten(io_lib:format_prompt(Prompt)). +%% Return a flat list of characters for the Prompt. +prompt_bytes(Prompt, Encoding) -> + lists:flatten(io_lib:format_prompt(Prompt, Encoding)). cast(L, binary,latin1) when is_list(L) -> list_to_binary(L); diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 17ab84c177..9a20baf8d0 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -28,7 +28,6 @@ application_starter, auth, code, - packages, code_server, dist_util, erl_boot_server, diff --git a/lib/kernel/src/packages.erl b/lib/kernel/src/packages.erl deleted file mode 100644 index e0b1f36b85..0000000000 --- a/lib/kernel/src/packages.erl +++ /dev/null @@ -1,158 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2009. 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(packages). - --export([to_string/1, concat/1, concat/2, is_valid/1, is_segmented/1, - split/1, last/1, first/1, strip_last/1, find_modules/1, - find_modules/2]). - -%% A package name (or a package-qualified module name) may be an atom or -%% a string (list of nonnegative integers) - not a deep list, and not a -%% list containing atoms. A name may be empty, but may not contain two -%% consecutive period (`.') characters or end with a period character. - --type package_name() :: atom() | string(). - --spec to_string(package_name()) -> string(). -to_string(Name) when is_atom(Name) -> - atom_to_list(Name); -to_string(Name) -> - Name. - -%% `concat' does not insert a leading period if the first segment is -%% empty. However, the result may contain leading, consecutive or -%% dangling period characters, if any of the segments after the first -%% are empty. Use 'is_valid' to check the result if necessary. - --spec concat(package_name(), package_name()) -> string(). -concat(A, B) -> - concat([A, B]). - --spec concat([package_name()]) -> string(). -concat([H | T]) when is_atom(H) -> - concat([atom_to_list(H) | T]); -concat(["" | T]) -> - concat_1(T); -concat(L) -> - concat_1(L). - -concat_1([H | T]) when is_atom(H) -> - concat_1([atom_to_list(H) | T]); -concat_1([H]) -> - H; -concat_1([H | T]) -> - H ++ "." ++ concat_1(T); -concat_1([]) -> - ""; -concat_1(Name) -> - erlang:error({badarg, Name}). - --spec is_valid(package_name()) -> boolean(). -is_valid(Name) when is_atom(Name) -> - is_valid_1(atom_to_list(Name)); -is_valid([$. | _]) -> - false; -is_valid(Name) -> - is_valid_1(Name). - -is_valid_1([$.]) -> false; -is_valid_1([$., $. | _]) -> false; -is_valid_1([H | T]) when is_integer(H), H >= 0 -> - is_valid_1(T); -is_valid_1([]) -> true; -is_valid_1(_) -> false. - --spec split(package_name()) -> [string()]. -split(Name) when is_atom(Name) -> - split_1(atom_to_list(Name), []); -split(Name) -> - split_1(Name, []). - -split_1([$. | T], Cs) -> - [lists:reverse(Cs) | split_1(T, [])]; -split_1([H | T], Cs) when is_integer(H), H >= 0 -> - split_1(T, [H | Cs]); -split_1([], Cs) -> - [lists:reverse(Cs)]; -split_1(_, _) -> - erlang:error(badarg). - -%% This is equivalent to testing if `split(Name)' yields a list of -%% length larger than one (i.e., if the name can be split into two or -%% more segments), but is cheaper. - --spec is_segmented(package_name()) -> boolean(). -is_segmented(Name) when is_atom(Name) -> - is_segmented_1(atom_to_list(Name)); -is_segmented(Name) -> - is_segmented_1(Name). - -is_segmented_1([$. | _]) -> true; -is_segmented_1([H | T]) when is_integer(H), H >= 0 -> - is_segmented_1(T); -is_segmented_1([]) -> false; -is_segmented_1(_) -> - erlang:error(badarg). - --spec last(package_name()) -> string(). -last(Name) -> - last_1(split(Name)). - -last_1([H]) -> H; -last_1([_ | T]) -> last_1(T). - --spec first(package_name()) -> [string()]. -first(Name) -> - first_1(split(Name)). - -first_1([H | T]) when T =/= [] -> [H | first_1(T)]; -first_1(_) -> []. - --spec strip_last(package_name()) -> string(). -strip_last(Name) -> - concat(first(Name)). - -%% This finds all modules available for a given package, using the -%% current code server search path. (There is no guarantee that the -%% modules are loadable; only that the object files exist.) - --spec find_modules(package_name()) -> [string()]. -find_modules(P) -> - find_modules(P, code:get_path()). - --spec find_modules(package_name(), [string()]) -> [string()]. -find_modules(P, Paths) -> - P1 = filename:join(packages:split(P)), - find_modules(P1, Paths, code:objfile_extension(), sets:new()). - -find_modules(P, [Path | Paths], Ext, S0) -> - case file:list_dir(filename:join(Path, P)) of - {ok, Fs} -> - Fs1 = [F || F <- Fs, filename:extension(F) =:= Ext], - S1 = lists:foldl(fun (F, S) -> - F1 = filename:rootname(F, Ext), - sets:add_element(F1, S) - end, - S0, Fs1), - find_modules(P, Paths, Ext, S1); - _ -> - find_modules(P, Paths, Ext, S0) - end; -find_modules(_P, [], _Ext, S) -> - sets:to_list(S). diff --git a/lib/kernel/src/ram_file.erl b/lib/kernel/src/ram_file.erl index 48ea871433..ca881ff8a4 100644 --- a/lib/kernel/src/ram_file.erl +++ b/lib/kernel/src/ram_file.erl @@ -29,6 +29,7 @@ %% Specialized file operations -export([get_size/1, get_file/1, set_file/2, get_file_close/1]). -export([compress/1, uncompress/1, uuencode/1, uudecode/1, advise/4]). +-export([allocate/3]). -export([open_mode/1]). %% used by ftp-file @@ -72,6 +73,7 @@ -define(RAM_FILE_UUDECODE, 36). -define(RAM_FILE_SIZE, 37). -define(RAM_FILE_ADVISE, 38). +-define(RAM_FILE_ALLOCATE, 39). %% Open modes for RAM_FILE_OPEN -define(RAM_FILE_MODE_READ, 1). @@ -383,6 +385,11 @@ advise(#file_descriptor{module = ?MODULE, data = Port}, Offset, advise(#file_descriptor{}, _Offset, _Length, _Advise) -> {error, enotsup}. +allocate(#file_descriptor{module = ?MODULE, data = Port}, Offset, Length) -> + call_port(Port, <<?RAM_FILE_ALLOCATE, Offset:64/signed, Length:64/signed>>); +allocate(#file_descriptor{}, _Offset, _Length) -> + {error, enotsup}. + %%%----------------------------------------------------------------- diff --git a/lib/kernel/src/user.erl b/lib/kernel/src/user.erl index 88f32df20b..d6449d9e5e 100644 --- a/lib/kernel/src/user.erl +++ b/lib/kernel/src/user.erl @@ -81,7 +81,7 @@ server(PortName,PortSettings) -> run(P) -> put(read_mode,list), - put(unicode,false), + put(encoding,latin1), case init:get_argument(noshell) of %% non-empty list -> noshell {ok, [_|_]} -> @@ -191,39 +191,27 @@ do_io_request(Req, From, ReplyAs, Port, Q0) -> %% New in R13B %% Encoding option (unicode/latin1) io_request({put_chars,unicode,Chars}, Port, Q) -> % Binary new in R9C - put_chars(wrap_characters_to_binary(Chars,unicode, - case get(unicode) of - true -> unicode; - _ -> latin1 - end), Port, Q); + put_chars(wrap_characters_to_binary(Chars,unicode, get(encoding)), Port, Q); io_request({put_chars,unicode,Mod,Func,Args}, Port, Q) -> Result = case catch apply(Mod,Func,Args) of Data when is_list(Data); is_binary(Data) -> - wrap_characters_to_binary(Data,unicode, - case get(unicode) of - true -> unicode; - _ -> latin1 - end); + wrap_characters_to_binary(Data,unicode,get(encoding)); Undef -> Undef end, put_chars(Result, Port, Q); io_request({put_chars,latin1,Chars}, Port, Q) -> % Binary new in R9C - Data = case get(unicode) of - true -> + Data = case get(encoding) of + unicode -> unicode:characters_to_binary(Chars,latin1,unicode); - false -> + latin1 -> erlang:iolist_to_binary(Chars) end, put_chars(Data, Port, Q); io_request({put_chars,latin1,Mod,Func,Args}, Port, Q) -> Result = case catch apply(Mod,Func,Args) of Data when is_list(Data); is_binary(Data) -> - unicode:characters_to_binary(Data,latin1, - case get(unicode) of - true -> unicode; - _ -> latin1 - end); + unicode:characters_to_binary(Data,latin1,get(encoding)); Undef -> Undef end, @@ -351,9 +339,9 @@ check_valid_opts(_) -> do_setopts(Opts, _Port, Q) -> case proplists:get_value(encoding,Opts) of Valid when Valid =:= unicode; Valid =:= utf8 -> - put(unicode,true); + put(encoding,unicode); latin1 -> - put(unicode,false); + put(encoding,latin1); undefined -> ok end, @@ -370,12 +358,7 @@ do_setopts(Opts, _Port, Q) -> getopts(_Port,Q) -> Bin = {binary, get(read_mode) =:= binary}, - Uni = {encoding, case get(unicode) of - true -> - unicode; - _ -> - latin1 - end}, + Uni = {encoding, get(encoding)}, {ok,[Bin,Uni],Q}. @@ -575,31 +558,32 @@ binrev(L, T) -> %% end %% end %% end. -%% get_chars(Prompt, Module, Function, XtraArg, Port, Queue) +%% get_chars(Prompt, Module, Function, XtraArg, Port, Queue, Encoding) %% Gets characters from the input port until the applied function %% returns {stop,Result,RestBuf}. Does not block output until input -%% has been received. +%% has been received. Encoding is the encoding of the data sent to +%% the client and to Function. %% Returns: %% {Status,Result,NewQueue} %% {exit,Reason} %% Entry function. -get_chars(Prompt, M, F, Xa, Port, Q, Fmt) -> +get_chars(Prompt, M, F, Xa, Port, Q, Enc) -> prompt(Port, Prompt), case {get(eof),queue:is_empty(Q)} of {true,true} -> {ok,eof,Q}; _ -> - get_chars(Prompt, M, F, Xa, Port, Q, start, Fmt) + get_chars(Prompt, M, F, Xa, Port, Q, start, Enc) end. %% First loop. Wait for port data. Respond to output requests. -get_chars(Prompt, M, F, Xa, Port, Q, State, Fmt) -> +get_chars(Prompt, M, F, Xa, Port, Q, State, Enc) -> case queue:is_empty(Q) of true -> receive {Port,{data,Bytes}} -> - get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Fmt); + get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc); {Port, eof} -> put(eof, true), {ok, eof, []}; @@ -610,41 +594,41 @@ get_chars(Prompt, M, F, Xa, Port, Q, State, Fmt) -> do_io_request(Req, From, ReplyAs, Port, queue:new()), %Keep Q over this call %% No prompt. - get_chars(Prompt, M, F, Xa, Port, Q, State, Fmt); + get_chars(Prompt, M, F, Xa, Port, Q, State, Enc); {io_request,From,ReplyAs,Request} when is_pid(From) -> get_chars_req(Prompt, M, F, Xa, Port, Q, State, - Request, From, ReplyAs, Fmt); + Request, From, ReplyAs, Enc); {'EXIT',From,What} when node(From) =:= node() -> {exit,What} end; false -> - get_chars_apply(State, M, F, Xa, Port, Q, Fmt) + get_chars_apply(State, M, F, Xa, Port, Q, Enc) end. get_chars_req(Prompt, M, F, XtraArg, Port, Q, State, - Req, From, ReplyAs, Fmt) -> + Req, From, ReplyAs, Enc) -> do_io_request(Req, From, ReplyAs, Port, queue:new()), %Keep Q over this call prompt(Port, Prompt), - get_chars(Prompt, M, F, XtraArg, Port, Q, State, Fmt). + get_chars(Prompt, M, F, XtraArg, Port, Q, State, Enc). %% Second loop. Pass data to client as long as it wants more. %% A ^G in data interrupts loop if 'noshell' is not undefined. -get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Fmt) -> +get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc) -> case get(shell) of noshell -> - get_chars_apply(State, M, F, Xa, Port, queue:snoc(Q, Bytes),Fmt); + get_chars_apply(State, M, F, Xa, Port, queue:snoc(Q, Bytes),Enc); _ -> case contains_ctrl_g_or_ctrl_c(Bytes) of false -> get_chars_apply(State, M, F, Xa, Port, - queue:snoc(Q, Bytes),Fmt); + queue:snoc(Q, Bytes),Enc); _ -> throw(new_shell) end end. -get_chars_apply(State0, M, F, Xa, Port, Q, Fmt) -> - case catch M:F(State0, cast(queue:head(Q),Fmt), Fmt, Xa) of +get_chars_apply(State0, M, F, Xa, Port, Q, Enc) -> + case catch M:F(State0, cast(queue:head(Q),Enc), Enc, Xa) of {stop,Result,<<>>} -> {ok,Result,queue:tail(Q)}; {stop,Result,[]} -> @@ -653,32 +637,32 @@ get_chars_apply(State0, M, F, Xa, Port, Q, Fmt) -> {ok,Result,queue:tail(Q)}; {stop,Result,Buf} -> {ok,Result,queue:cons(Buf, queue:tail(Q))}; - {'EXIT',_} -> + {'EXIT',_Why} -> {error,{error,err_func(M, F, Xa)},queue:new()}; State1 -> - get_chars_more(State1, M, F, Xa, Port, queue:tail(Q), Fmt) + get_chars_more(State1, M, F, Xa, Port, queue:tail(Q), Enc) end. -get_chars_more(State, M, F, Xa, Port, Q, Fmt) -> +get_chars_more(State, M, F, Xa, Port, Q, Enc) -> case queue:is_empty(Q) of true -> case get(eof) of undefined -> receive {Port,{data,Bytes}} -> - get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Fmt); + get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc); {Port,eof} -> put(eof, true), get_chars_apply(State, M, F, Xa, Port, - queue:snoc(Q, eof), Fmt); + queue:snoc(Q, eof), Enc); {'EXIT',From,What} when node(From) =:= node() -> {exit,What} end; _ -> - get_chars_apply(State, M, F, Xa, Port, queue:snoc(Q, eof), Fmt) + get_chars_apply(State, M, F, Xa, Port, queue:snoc(Q, eof), Enc) end; false -> - get_chars_apply(State, M, F, Xa, Port, Q, Fmt) + get_chars_apply(State, M, F, Xa, Port, Q, Enc) end. @@ -689,11 +673,10 @@ get_chars_more(State, M, F, Xa, Port, Q, Fmt) -> prompt(_Port, '') -> ok; prompt(Port, Prompt) -> - put_port(wrap_characters_to_binary(io_lib:format_prompt(Prompt),unicode, - case get(unicode) of - true -> unicode; - _ -> latin1 - end), Port). + Encoding = get(encoding), + put_port(wrap_characters_to_binary(io_lib:format_prompt(Prompt, Encoding), + unicode, Encoding), + Port). %% Convert error code to make it look as before err_func(io_lib, get_until, {_,F,_}) -> @@ -710,56 +693,65 @@ contains_ctrl_g_or_ctrl_c(BinOrList)-> end. %% Convert a buffer between list and binary -cast(Data, _Format) when is_atom(Data) -> +cast(Data, _Encoding) when is_atom(Data) -> Data; -cast(Data, Format) -> - cast(Data, get(read_mode), Format, get(unicode)). +cast(Data, Encoding) -> + IoEncoding = get(encoding), + cast(Data, get(read_mode), IoEncoding, Encoding). -cast(B, binary, latin1, false) when is_binary(B) -> +cast(B, binary, latin1, latin1) when is_binary(B) -> B; -cast(B, binary, latin1, true) when is_binary(B) -> - unicode:characters_to_binary(B, unicode, latin1); -cast(L, binary, latin1, false) -> - erlang:iolist_to_binary(L); -cast(L, binary, latin1, true) -> - case unicode:characters_to_binary( - erlang:iolist_to_binary(L),unicode,latin1) of % may fail - {error,_,_} -> exit({no_translation, unicode, latin1}); - Else -> Else +cast(L, binary, latin1, latin1) -> + case catch erlang:iolist_to_binary(L) of + Bin when is_binary(Bin) -> Bin; + _ -> exit({no_translation, latin1, latin1}) + end; +cast(Data, binary, unicode, latin1) when is_binary(Data); is_list(Data) -> + case catch unicode:characters_to_binary(Data, unicode, latin1) of + Bin when is_binary(Bin) -> Bin; + _ -> exit({no_translation, unicode, latin1}) + end; +cast(Data, binary, latin1, unicode) when is_binary(Data); is_list(Data) -> + case catch unicode:characters_to_binary(Data, latin1, unicode) of + Bin when is_binary(Bin) -> Bin; + _ -> exit({no_translation, latin1, unicode}) end; -cast(B, binary, unicode, true) when is_binary(B) -> +cast(B, binary, unicode, unicode) when is_binary(B) -> B; -cast(B, binary, unicode, false) when is_binary(B) -> - unicode:characters_to_binary(B,latin1,unicode); -cast(L, binary, unicode, true) -> - % possibly a list containing UTF-8 encoded characters - unicode:characters_to_binary(erlang:iolist_to_binary(L)); -cast(L, binary, unicode, false) -> - unicode:characters_to_binary(L, latin1, unicode); -cast(L, list, latin1, UniTerm) -> - case UniTerm of - true -> % Convert input characters to protocol format (i.e latin1) - case unicode:characters_to_list( - erlang:iolist_to_binary(L),unicode) of % may fail - {error,_,_} -> exit({no_translation, unicode, latin1}); - Else -> [ case X of - High when High > 255 -> - exit({no_translation, unicode, latin1}); - Low -> - Low - end || X <- Else ] - end; - _ -> - binary_to_list(erlang:iolist_to_binary(L)) +cast(L, binary, unicode, unicode) -> + case catch unicode:characters_to_binary(L, unicode) of + Bin when is_binary(Bin) -> Bin; + _ -> exit({no_translation, unicode, unicode}) end; -cast(L, list, unicode, UniTerm) -> - unicode:characters_to_list(erlang:iolist_to_binary(L), - case UniTerm of - true -> unicode; - _ -> latin1 - end); -cast(Other, _, _,_) -> - Other. +cast(B, list, latin1, latin1) when is_binary(B) -> + binary_to_list(B); +cast(L, list, latin1, latin1) -> + case catch erlang:iolist_to_binary(L) of + Bin when is_binary(Bin) -> binary_to_list(Bin); + _ -> exit({no_translation, latin1, latin1}) + end; +cast(Data, list, unicode, latin1) when is_binary(Data); is_list(Data) -> + case catch unicode:characters_to_list(Data, unicode) of + Chars when is_list(Chars) -> + [ case X of + High when High > 255 -> + exit({no_translation, unicode, latin1}); + Low -> + Low + end || X <- Chars ]; + _ -> + exit({no_translation, unicode, latin1}) + end; +cast(Data, list, latin1, unicode) when is_binary(Data); is_list(Data) -> + case catch unicode:characters_to_list(Data, latin1) of + Chars when is_list(Chars) -> Chars; + _ -> exit({no_translation, latin1, unicode}) + end; +cast(Data, list, unicode, unicode) when is_binary(Data); is_list(Data) -> + case catch unicode:characters_to_list(Data, unicode) of + Chars when is_list(Chars) -> Chars; + _ -> exit({no_translation, unicode, unicode}) + end. wrap_characters_to_binary(Chars,unicode,latin1) -> case unicode:characters_to_binary(Chars,unicode,latin1) of diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index 8eca37029d..7fd3afe93c 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -73,6 +73,7 @@ MODULES= \ seq_trace_SUITE \ wrap_log_reader_SUITE \ cleanup \ + ignore_cores \ zlib_SUITE \ loose_node \ sendfile_SUITE diff --git a/lib/kernel/test/error_logger_warn_SUITE.erl b/lib/kernel/test/error_logger_warn_SUITE.erl index 265e1ae4c8..2bf467610e 100644 --- a/lib/kernel/test/error_logger_warn_SUITE.erl +++ b/lib/kernel/test/error_logger_warn_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 @@ -310,7 +310,7 @@ nice_stop_node(Name) -> {nodedown,Name} -> ok end. -%rensa rd() f�re varje rapport-test s� man bara f�r en fil... +%clean out rd() before each report test in order to get only one file... clean_rd() -> {ok,L} = file:list_dir(rd()), lists:foreach(fun(F) -> diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 9c507fd437..914f0d6127 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -84,6 +84,8 @@ -export([advise/1]). +-export([allocate/1]). + -export([standard_io/1,mini_server/1]). %% Debug exports @@ -116,7 +118,7 @@ groups() -> {files, [], [{group, open}, {group, pos}, {group, file_info}, {group, consult}, {group, eval}, {group, script}, - truncate, sync, datasync, advise]}, + truncate, sync, datasync, advise, allocate]}, {open, [], [open1, old_modes, new_modes, path_open, close, access, read_write, pread_write, append, open_errors, @@ -1617,6 +1619,74 @@ advise(Config) when is_list(Config) -> ?line test_server:timetrap_cancel(Dog), ok. +allocate(suite) -> []; +allocate(doc) -> "Tests that ?FILE_MODULE:allocate/3 at least doesn't crash."; +allocate(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(5)), + ?line PrivDir = ?config(priv_dir, Config), + ?line Allocate = filename:join(PrivDir, + atom_to_list(?MODULE) + ++"_allocate.fil"), + + Line1 = "Hello\n", + Line2 = "World!\n", + + ?line {ok, Fd} = ?FILE_MODULE:open(Allocate, [write, binary]), + allocate_and_assert(Fd, 1, iolist_size([Line1, Line2])), + ?line ok = io:format(Fd, "~s", [Line1]), + ?line ok = io:format(Fd, "~s", [Line2]), + ?line ok = ?FILE_MODULE:close(Fd), + + ?line {ok, Fd2} = ?FILE_MODULE:open(Allocate, [write, binary]), + allocate_and_assert(Fd2, 1, iolist_size(Line1)), + ?line ok = io:format(Fd2, "~s", [Line1]), + ?line ok = io:format(Fd2, "~s", [Line2]), + ?line ok = ?FILE_MODULE:close(Fd2), + + ?line {ok, Fd3} = ?FILE_MODULE:open(Allocate, [write, binary]), + allocate_and_assert(Fd3, 1, iolist_size(Line1) + 1), + ?line ok = io:format(Fd3, "~s", [Line1]), + ?line ok = io:format(Fd3, "~s", [Line2]), + ?line ok = ?FILE_MODULE:close(Fd3), + + ?line {ok, Fd4} = ?FILE_MODULE:open(Allocate, [write, binary]), + allocate_and_assert(Fd4, 1, 4 * iolist_size([Line1, Line2])), + ?line ok = io:format(Fd4, "~s", [Line1]), + ?line ok = io:format(Fd4, "~s", [Line2]), + ?line ok = ?FILE_MODULE:close(Fd4), + + ?line [] = flush(), + ?line test_server:timetrap_cancel(Dog), + ok. + +allocate_and_assert(Fd, Offset, Length) -> + % Just verify that calls to ?PRIM_FILE:allocate/3 don't crash or have + % any other negative side effect. We can't really asssert against a + % specific return value, because support for file space pre-allocation + % depends on the OS, OS version and underlying filesystem. + % + % The Linux kernel added support for fallocate() in version 2.6.23, + % which currently works only for the ext4, ocfs2, xfs and btrfs file + % systems. posix_fallocate() is available in glibc as of version + % 2.1.94, but it was buggy until glibc version 2.7. + % + % Mac OS X, as of version 10.3, supports the fcntl operation F_PREALLOCATE. + % + % Solaris supports posix_fallocate() but only for the UFS file system + % apparently (not supported for ZFS). + % + % FreeBSD 9.0 is the first FreeBSD release supporting posix_fallocate(). + % + % For Windows there's apparently no way to pre-allocate file space, at + % least with same semantics as posix_fallocate(), fallocate() and + % fcntl F_PREALLOCATE. + Result = ?FILE_MODULE:allocate(Fd, Offset, Length), + case os:type() of + {win32, _} -> + ?line {error, enotsup} = Result; + _ -> + ?line _ = Result + end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index 3aa010a708..40bde8a736 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -1,4 +1,5 @@ -module(file_name_SUITE). +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -336,7 +337,7 @@ check_normal(Mod) -> check_icky(Mod) -> {ok,Dir} = Mod:get_cwd(), try - ?line true=(length("���") =:= 3), + ?line true=(length("åäö") =:= 3), ?line UniMode = file:native_name_encoding() =/= latin1, ?line make_icky_dir(Mod), ?line {ok, L0} = Mod:list_dir("."), @@ -357,42 +358,42 @@ check_icky(Mod) -> || {T,S,Targ} <- icky_dir(), T =:= symlink ], ?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ], ?line [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ], - ?line chk_cre_dir(Mod,[{directory,"���_dir",icky_dir()}]), + ?line chk_cre_dir(Mod,[{directory,"åäö_dir",icky_dir()}]), ?line {ok,BeginAt} = Mod:get_cwd(), ?line true = is_list(BeginAt), - ?line {error,enoent} = Mod:set_cwd("��_dir"), - ?line ok = Mod:set_cwd("���_dir"), + ?line {error,enoent} = Mod:set_cwd("åä_dir"), + ?line ok = Mod:set_cwd("åäö_dir"), ?line {ok, NowAt} = Mod:get_cwd(), ?line true = is_list(NowAt), ?line true = BeginAt =/= NowAt, ?line ok = Mod:set_cwd(".."), ?line {ok,BeginAt} = Mod:get_cwd(), - ?line rm_r2(Mod,"���_dir"), + ?line rm_r2(Mod,"åäö_dir"), {OS,TYPE} = os:type(), % Check that treat_icky really converts to the same as the OS case UniMode of true -> - ?line chk_cre_dir(Mod,[{directory,"���_dir",[]}]), - ?line ok = Mod:set_cwd("���_dir"), - ?line ok = Mod:write_file(<<"���">>,<<"hello">>), - ?line Treated = treat_icky(<<"���">>), + ?line chk_cre_dir(Mod,[{directory,"åäö_dir",[]}]), + ?line ok = Mod:set_cwd("åäö_dir"), + ?line ok = Mod:write_file(<<"ååå">>,<<"hello">>), + ?line Treated = treat_icky(<<"ååå">>), ?line {ok,[Treated]} = Mod:list_dir("."), - ?line ok = Mod:delete(<<"���">>), + ?line ok = Mod:delete(<<"ååå">>), ?line {ok,[]} = Mod:list_dir("."), ?line ok = Mod:set_cwd(".."), - ?line rm_r2(Mod,"���_dir"); + ?line rm_r2(Mod,"åäö_dir"); false -> ok end, - ?line chk_cre_dir(Mod,[{directory,treat_icky(<<"���_dir">>),icky_dir()}]), + ?line chk_cre_dir(Mod,[{directory,treat_icky(<<"åäö_dir">>),icky_dir()}]), if UniMode and (OS =/= win32) -> - ?line {error,enoent} = Mod:set_cwd("���_dir"); + ?line {error,enoent} = Mod:set_cwd("åäö_dir"); true -> ok end, - ?line ok = Mod:set_cwd(treat_icky(<<"���_dir">>)), + ?line ok = Mod:set_cwd(treat_icky(<<"åäö_dir">>)), ?line {ok, NowAt2} = Mod:get_cwd(), io:format("~p~n",[NowAt2]), % Cannot create raw unicode-breaking filenames on windows or macos @@ -400,22 +401,22 @@ check_icky(Mod) -> ?line true = BeginAt =/= NowAt2, ?line ok = Mod:set_cwd(".."), ?line {ok,BeginAt} = Mod:get_cwd(), - ?line rm_r2(Mod,conv(treat_icky(<<"���_dir">>))), + ?line rm_r2(Mod,conv(treat_icky(<<"åäö_dir">>))), case has_links() of true -> - ?line ok = Mod:make_link("fil1","nisse�"), - ?line {ok, <<"fil1">>} = Mod:read_file("nisse�"), - ?line {ok, #file_info{type = regular}} = Mod:read_link_info("nisse�"), - ?line ok = Mod:delete("nisse�"), - ?line ok = Mod:make_link("fil1",treat_icky(<<"nisse�">>)), - ?line {ok, <<"fil1">>} = Mod:read_file(treat_icky(<<"nisse�">>)), - ?line {ok, #file_info{type = regular}} = Mod:read_link_info(treat_icky(<<"nisse�">>)), - ?line ok = Mod:delete(treat_icky(<<"nisse�">>)), + ?line ok = Mod:make_link("fil1","nisseö"), + ?line {ok, <<"fil1">>} = Mod:read_file("nisseö"), + ?line {ok, #file_info{type = regular}} = Mod:read_link_info("nisseö"), + ?line ok = Mod:delete("nisseö"), + ?line ok = Mod:make_link("fil1",treat_icky(<<"nisseö">>)), + ?line {ok, <<"fil1">>} = Mod:read_file(treat_icky(<<"nisseö">>)), + ?line {ok, #file_info{type = regular}} = Mod:read_link_info(treat_icky(<<"nisseö">>)), + ?line ok = Mod:delete(treat_icky(<<"nisseö">>)), ?line {ok, <<"fil1">>} = Mod:read_file("fil1"), - ?line {error,enoent} = Mod:read_file("nisse�"), - ?line {error,enoent} = Mod:read_link_info("nisse�"), - ?line {error,enoent} = Mod:read_file(treat_icky(<<"nisse�">>)), - ?line {error,enoent} = Mod:read_link_info(treat_icky(<<"nisse�">>)); + ?line {error,enoent} = Mod:read_file("nisseö"), + ?line {error,enoent} = Mod:read_link_info("nisseö"), + ?line {error,enoent} = Mod:read_file(treat_icky(<<"nisseö">>)), + ?line {error,enoent} = Mod:read_link_info(treat_icky(<<"nisseö">>)); false -> ok end, @@ -430,42 +431,42 @@ check_icky(Mod) -> ?line {ok, BC} = Mod:read(FD,1024), ?line ok = file:close(FD) end || {regular,Name,Content} <- icky_dir() ], - ?line Mod:rename("���2","���_fil1"), - ?line {ok, <<"���2">>} = Mod:read_file("���_fil1"), - ?line {error,enoent} = Mod:read_file("���2"), - ?line Mod:rename("���_fil1","���2"), - ?line {ok, <<"���2">>} = Mod:read_file("���2"), - ?line {error,enoent} = Mod:read_file("���_fil1"), + ?line Mod:rename("åäö2","åäö_fil1"), + ?line {ok, <<"åäö2">>} = Mod:read_file("åäö_fil1"), + ?line {error,enoent} = Mod:read_file("åäö2"), + ?line Mod:rename("åäö_fil1","åäö2"), + ?line {ok, <<"åäö2">>} = Mod:read_file("åäö2"), + ?line {error,enoent} = Mod:read_file("åäö_fil1"), - ?line Mod:rename("���2",treat_icky(<<"���_fil1">>)), - ?line {ok, <<"���2">>} = Mod:read_file(treat_icky(<<"���_fil1">>)), + ?line Mod:rename("åäö2",treat_icky(<<"åäö_fil1">>)), + ?line {ok, <<"åäö2">>} = Mod:read_file(treat_icky(<<"åäö_fil1">>)), if UniMode and (OS =/= win32) -> - {error,enoent} = Mod:read_file("���_fil1"); + {error,enoent} = Mod:read_file("åäö_fil1"); true -> ok end, - ?line {error,enoent} = Mod:read_file("���2"), - ?line Mod:rename(treat_icky(<<"���_fil1">>),"���2"), - ?line {ok, <<"���2">>} = Mod:read_file("���2"), - ?line {error,enoent} = Mod:read_file("���_fil1"), - ?line {error,enoent} = Mod:read_file(treat_icky(<<"���_fil1">>)), + ?line {error,enoent} = Mod:read_file("åäö2"), + ?line Mod:rename(treat_icky(<<"åäö_fil1">>),"åäö2"), + ?line {ok, <<"åäö2">>} = Mod:read_file("åäö2"), + ?line {error,enoent} = Mod:read_file("åäö_fil1"), + ?line {error,enoent} = Mod:read_file(treat_icky(<<"åäö_fil1">>)), - ?line {ok,FI} = Mod:read_file_info("���2"), + ?line {ok,FI} = Mod:read_file_info("åäö2"), ?line NewMode = FI#file_info.mode band (bnot 8#333), ?line NewMode2 = NewMode bor 8#222, ?line true = NewMode2 =/= NewMode, - ?line ok = Mod:write_file_info("���2",FI#file_info{mode = NewMode}), - ?line {ok,#file_info{mode = NewMode}} = Mod:read_file_info("���2"), - ?line ok = Mod:write_file_info("���2",FI#file_info{mode = NewMode2}), - ?line {ok,#file_info{mode = NewMode2}} = Mod:read_file_info("���2"), + ?line ok = Mod:write_file_info("åäö2",FI#file_info{mode = NewMode}), + ?line {ok,#file_info{mode = NewMode}} = Mod:read_file_info("åäö2"), + ?line ok = Mod:write_file_info("åäö2",FI#file_info{mode = NewMode2}), + ?line {ok,#file_info{mode = NewMode2}} = Mod:read_file_info("åäö2"), - ?line {ok,FII} = Mod:read_file_info(treat_icky(<<"���5">>)), + ?line {ok,FII} = Mod:read_file_info(treat_icky(<<"åäö5">>)), ?line true = NewMode2 =/= NewMode, - ?line ok = Mod:write_file_info(treat_icky(<<"���5">>),FII#file_info{mode = NewMode}), - ?line {ok,#file_info{mode = NewMode}} = Mod:read_file_info(treat_icky(<<"���5">>)), - ?line ok = Mod:write_file_info(<<"���5">>,FII#file_info{mode = NewMode2}), - ?line {ok,#file_info{mode = NewMode2}} = Mod:read_file_info(treat_icky(<<"���5">>)), + ?line ok = Mod:write_file_info(treat_icky(<<"åäö5">>),FII#file_info{mode = NewMode}), + ?line {ok,#file_info{mode = NewMode}} = Mod:read_file_info(treat_icky(<<"åäö5">>)), + ?line ok = Mod:write_file_info(<<"åäö5">>,FII#file_info{mode = NewMode2}), + ?line {ok,#file_info{mode = NewMode2}} = Mod:read_file_info(treat_icky(<<"åäö5">>)), ok after Mod:set_cwd(Dir), @@ -475,7 +476,7 @@ check_icky(Mod) -> check_very_icky(Mod) -> {ok,Dir} = Mod:get_cwd(), try - ?line true=(length("���") =:= 3), + ?line true=(length("åäö") =:= 3), ?line UniMode = file:native_name_encoding() =/= latin1, if not UniMode -> @@ -497,7 +498,7 @@ check_very_icky(Mod) -> ?line chk_cre_dir(Mod,[{directory,[1088,1079,1091]++"_dir",very_icky_dir()}]), ?line {ok,BeginAt} = Mod:get_cwd(), ?line true = is_list(BeginAt), - ?line {error,enoent} = Mod:set_cwd("��_dir"), + ?line {error,enoent} = Mod:set_cwd("åä_dir"), ?line ok = Mod:set_cwd([1088,1079,1091]++"_dir"), ?line {ok, NowAt} = Mod:get_cwd(), ?line true = is_list(NowAt), @@ -514,16 +515,16 @@ check_very_icky(Mod) -> ?line {ok, #file_info{type = regular}} = Mod:read_link_info("nisse"++[1088,1079,1091]), ?line ok = Mod:delete("nisse"++[1088,1079,1091]), - ?line ok = Mod:make_link("fil1",<<"nisse�">>), - ?line {ok, <<"fil1">>} = Mod:read_file(<<"nisse�">>), + ?line ok = Mod:make_link("fil1",<<"nisseö">>), + ?line {ok, <<"fil1">>} = Mod:read_file(<<"nisseö">>), ?line {ok, #file_info{type = regular}} = - Mod:read_link_info(<<"nisse�">>), - ?line ok = Mod:delete(<<"nisse�">>), + Mod:read_link_info(<<"nisseö">>), + ?line ok = Mod:delete(<<"nisseö">>), ?line {ok, <<"fil1">>} = Mod:read_file("fil1"), ?line {error,enoent} = Mod:read_file("nisse"++[1088,1079,1091]), ?line {error,enoent} = Mod:read_link_info("nisse"++[1088,1079,1091]), - ?line {error,enoent} = Mod:read_file(<<"nisse�">>), - ?line {error,enoent} = Mod:read_link_info(<<"nisse�">>); + ?line {error,enoent} = Mod:read_file(<<"nisseö">>), + ?line {error,enoent} = Mod:read_link_info(<<"nisseö">>); false -> ok end, @@ -540,10 +541,10 @@ check_very_icky(Mod) -> end || {regular,Name,Content} <- very_icky_dir() ], ?line Mod:rename([956,965,963,954,959,49], [956,965,963,954,959]++"_fil1"), - ?line {ok, <<"���2">>} = Mod:read_file([956,965,963,954,959]++"_fil1"), + ?line {ok, <<"åäö2">>} = Mod:read_file([956,965,963,954,959]++"_fil1"), ?line {error,enoent} = Mod:read_file([956,965,963,954,959,49]), ?line Mod:rename([956,965,963,954,959]++"_fil1",[956,965,963,954,959,49]), - ?line {ok, <<"���2">>} = Mod:read_file([956,965,963,954,959,49]), + ?line {ok, <<"åäö2">>} = Mod:read_file([956,965,963,954,959,49]), ?line {error,enoent} = Mod:read_file([956,965,963,954,959]++"_fil1"), ?line {ok,FI} = Mod:read_file_info([956,965,963,954,959,49]), @@ -574,9 +575,9 @@ check_very_icky(Mod) -> end, ?line {NumOK,NumNOK} = filelib:fold_files(".",".*",true,fun(_F,{N,M}) when is_list(_F) -> io:format("~ts~n",[_F]),{N+1,M}; (_F,{N,M}) -> io:format("~p~n",[_F]),{N,M+1} end,{0,0}), ?line ok = filelib:fold_files(".",[1076,1089,1072,124,46,42],true,fun(_F,_) -> ok end,false), - ?line SF3 = unicode:characters_to_binary("���subfil3", + ?line SF3 = unicode:characters_to_binary("åäösubfil3", file:native_name_encoding()), - ?line SF2 = case treat_icky(<<"���subfil2">>) of + ?line SF2 = case treat_icky(<<"åäösubfil2">>) of LF2 when is_list(LF2) -> unicode:characters_to_binary(LF2, file:native_name_encoding()); @@ -584,7 +585,7 @@ check_very_icky(Mod) -> BF2 end, ?line Sorted = lists:sort([SF3,SF2]), - ?line Sorted = lists:sort(filelib:wildcard("*",<<"���subdir2">>)), + ?line Sorted = lists:sort(filelib:wildcard("*",<<"åäösubdir2">>)), ok catch throw:need_unicode_mode -> @@ -744,26 +745,26 @@ hopeless_darwin() -> icky_dir() -> [{regular,"fil1","fil1"}, - {regular,"���2","���2"}] ++ + {regular,"åäö2","åäö2"}] ++ case has_links() of true -> - [{regular,"���3","���2"}, - {symlink,"���4","���2"}]; + [{regular,"åäö3","åäö2"}, + {symlink,"åäö4","åäö2"}]; false -> [] end ++ - [{regular,treat_icky(<<"���5">>),"���5"}] ++ + [{regular,treat_icky(<<"åäö5">>),"åäö5"}] ++ case has_links() of true -> - [{symlink,treat_icky(<<"���6">>),treat_icky(<<"���5">>)}]; + [{symlink,treat_icky(<<"åäö6">>),treat_icky(<<"åäö5">>)}]; false -> [] end ++ - [{directory,treat_icky(<<"���subdir2">>), - [{regular,treat_icky(<<"���subfil2">>),"���subfil12"}, - {regular,"���subfil3","���subfil13"}]}, - {directory,"���subdir", - [{regular,"���subfil1","���subfil1"}]}]. + [{directory,treat_icky(<<"åäösubdir2">>), + [{regular,treat_icky(<<"åäösubfil2">>),"åäösubfil12"}, + {regular,"åäösubfil3","åäösubfil13"}]}, + {directory,"åäösubdir", + [{regular,"åäösubfil1","åäösubfil1"}]}]. make_very_icky_dir(Mod) -> rm_rf(Mod,"very_icky_dir"), @@ -774,26 +775,26 @@ make_very_icky_dir(Mod) -> very_icky_dir() -> [{regular,"fil1","fil1"}, - {regular,[956,965,963,954,959,49],"���2"}] ++ + {regular,[956,965,963,954,959,49],"åäö2"}] ++ case has_links() of true -> - [{regular,[956,965,963,954,959,50],"���2"}, + [{regular,[956,965,963,954,959,50],"åäö2"}, {symlink,[956,965,963,954,959,51],[956,965,963,954,959,49]}]; false -> [] end ++ - [{regular,treat_icky(<<"���5">>),"���5"}] ++ + [{regular,treat_icky(<<"åäö5">>),"åäö5"}] ++ case has_links() of true -> - [{symlink,treat_icky(<<"���6">>),treat_icky(<<"���5">>)}]; + [{symlink,treat_icky(<<"åäö6">>),treat_icky(<<"åäö5">>)}]; false -> [] end ++ - [{directory,treat_icky(<<"���subdir2">>), - [{regular,treat_icky(<<"���subfil2">>),"���subfil12"}, - {regular,"���subfil3","���subfil13"}]}, + [{directory,treat_icky(<<"åäösubdir2">>), + [{regular,treat_icky(<<"åäösubfil2">>),"åäösubfil12"}, + {regular,"åäösubfil3","åäösubfil13"}]}, {directory,[956,965,963,954,959]++"subdir1", - [{regular,[956,965,963,954,959]++"subfil1","���subfil1"}]}]. + [{regular,[956,965,963,954,959]++"subfil1","åäösubfil1"}]}]. %% Some OS'es simply do not allow non UTF8 filenames treat_icky(Bin) -> diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 2354f8accd..cd768813cf 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 @@ -442,7 +443,7 @@ open_fd(suite) -> open_fd(doc) -> ["Test that the 'fd' option works"]; open_fd(Config) when is_list(Config) -> - Msg = "Det g�r ont n�r knoppar brista. Varf�r skulle annars v�ren tveka?", + Msg = "Det gör ont när knoppar brista. Varför skulle annars våren tveka?", Addr = {127,0,0,1}, {ok,S1} = gen_udp:open(0), {ok,P2} = inet:port(S1), diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl index 2ec3b7c297..320b23bea1 100644 --- a/lib/kernel/test/heart_SUITE.erl +++ b/lib/kernel/test/heart_SUITE.erl @@ -83,10 +83,10 @@ init_per_suite(Config) when is_list(Config) -> {win32, windows} -> {skipped, "No use to run on Windows 95/98"}; _ -> - Config + ignore_cores:init(Config) end. end_per_suite(Config) when is_list(Config) -> - Config. + ignore_cores:fini(Config). start_check(Type, Name) -> @@ -188,8 +188,20 @@ reboot(Config) when is_list(Config) -> %% Check that a node is up and running after a crash. %% This test exhausts the atom table on the remote node. %% ERL_CRASH_DUMP_SECONDS=0 will force beam not to dump an erl_crash.dump. +%% May currently dump core in beam debug build due to lock-order violation +%% This should be removed when a non-lockad information retriever is implemented +%% for crash dumps node_start_immediately_after_crash(suite) -> {req, [{time, 10}]}; node_start_immediately_after_crash(Config) when is_list(Config) -> + Config2 = ignore_cores:setup(?MODULE, node_start_immediately_after_crash, Config, true), + try + node_start_immediately_after_crash_test(Config2) + after + ignore_cores:restore(Config2) + end. + + +node_start_immediately_after_crash_test(Config) when is_list(Config) -> {ok, Node} = start_check(loose, heart_test_imm, [{"ERL_CRASH_DUMP_SECONDS", "0"}]), ok = rpc:call(Node, heart, set_cmd, @@ -228,8 +240,19 @@ node_start_immediately_after_crash(Config) when is_list(Config) -> %% This test exhausts the atom table on the remote node. %% ERL_CRASH_DUMP_SECONDS=10 will force beam %% to only dump an erl_crash.dump for 10 seconds. +%% May currently dump core in beam debug build due to lock-order violation +%% This should be removed when a non-lockad information retriever is implemented +%% for crash dumps node_start_soon_after_crash(suite) -> {req, [{time, 10}]}; node_start_soon_after_crash(Config) when is_list(Config) -> + Config2 = ignore_cores:setup(?MODULE, node_start_soon_after_crash, Config, true), + try + node_start_soon_after_crash_test(Config2) + after + ignore_cores:restore(Config2) + end. + +node_start_soon_after_crash_test(Config) when is_list(Config) -> {ok, Node} = start_check(loose, heart_test_soon, [{"ERL_CRASH_DUMP_SECONDS", "10"}]), ok = rpc:call(Node, heart, set_cmd, diff --git a/lib/kernel/test/ignore_cores.erl b/lib/kernel/test/ignore_cores.erl new file mode 100644 index 0000000000..8b1ac0fe6c --- /dev/null +++ b/lib/kernel/test/ignore_cores.erl @@ -0,0 +1,158 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-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% +%% + +%%%------------------------------------------------------------------- +%%% File : ignore_cores.erl +%%% Author : Rickard Green <[email protected]> +%%% Description : +%%% +%%% Created : 11 Feb 2008 by Rickard Green <[email protected]> +%%%------------------------------------------------------------------- + +-module(ignore_cores). + +-include_lib("test_server/include/test_server.hrl"). + +-export([init/1, fini/1, setup/3, setup/4, restore/1, dir/1]). + +-record(ignore_cores, {org_cwd, + org_path, + org_pwd_env, + ign_dir = false, + cores_dir = false}). + +%% +%% Takes a testcase config +%% + +init(Config) -> + {ok, OrgCWD} = file:get_cwd(), + [{ignore_cores, + #ignore_cores{org_cwd = OrgCWD, + org_path = code:get_path(), + org_pwd_env = os:getenv("PWD")}} + | lists:keydelete(ignore_cores, 1, Config)]. + +fini(Config) -> + #ignore_cores{org_cwd = OrgCWD, + org_path = OrgPath, + org_pwd_env = OrgPWD} = ?config(ignore_cores, Config), + ok = file:set_cwd(OrgCWD), + true = code:set_path(OrgPath), + case OrgPWD of + false -> ok; + _ -> true = os:putenv("PWD", OrgPWD) + end, + lists:keydelete(ignore_cores, 1, Config). + +setup(Suite, Testcase, Config) -> + setup(Suite, Testcase, Config, false). + +setup(Suite, Testcase, Config, SetCwd) when is_atom(Suite), + is_atom(Testcase), + is_list(Config) -> + #ignore_cores{org_cwd = OrgCWD, + org_path = OrgPath, + org_pwd_env = OrgPWD} = ?config(ignore_cores, Config), + Path = lists:map(fun (".") -> OrgCWD; (Dir) -> Dir end, OrgPath), + true = code:set_path(Path), + PrivDir = ?config(priv_dir, Config), + IgnDir = filename:join([PrivDir, + atom_to_list(Suite) + ++ "_" + ++ atom_to_list(Testcase) + ++ "_wd"]), + ok = file:make_dir(IgnDir), + case SetCwd of + false -> + ok; + _ -> + ok = file:set_cwd(IgnDir), + OrgPWD = case os:getenv("PWD") of + false -> false; + PWD -> + os:putenv("PWD", IgnDir), + PWD + end + end, + ok = file:write_file(filename:join([IgnDir, "ignore_core_files"]), <<>>), + %% cores are dumped in /cores on MacOS X + CoresDir = case {?t:os_type(), filelib:is_dir("/cores")} of + {{unix,darwin}, true} -> + filelib:fold_files("/cores", + "^core.*$", + false, + fun (C,Cs) -> [C|Cs] end, + []); + _ -> + false + end, + lists:keyreplace(ignore_cores, + 1, + Config, + {ignore_cores, + #ignore_cores{org_cwd = OrgCWD, + org_path = OrgPath, + org_pwd_env = OrgPWD, + ign_dir = IgnDir, + cores_dir = CoresDir}}). + +restore(Config) -> + #ignore_cores{org_cwd = OrgCWD, + org_path = OrgPath, + org_pwd_env = OrgPWD, + ign_dir = IgnDir, + cores_dir = CoresDir} = ?config(ignore_cores, Config), + try + case CoresDir of + false -> + ok; + _ -> + %% Move cores dumped by these testcases in /cores + %% to cwd. + lists:foreach(fun (C) -> + case lists:member(C, CoresDir) of + true -> ok; + _ -> + Dst = filename:join( + [IgnDir, + filename:basename(C)]), + {ok, _} = file:copy(C, Dst), + file:delete(C) + end + end, + filelib:fold_files("/cores", + "^core.*$", + false, + fun (C,Cs) -> [C|Cs] end, + [])) + end + after + catch file:set_cwd(OrgCWD), + catch code:set_path(OrgPath), + case OrgPWD of + false -> ok; + _ -> catch os:putenv("PWD", OrgPWD) + end + end. + + +dir(Config) -> + #ignore_cores{ign_dir = Dir} = ?config(ignore_cores, Config), + Dir. diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index a56746bbc4..4e93a593b3 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -57,6 +57,8 @@ %% System probe functions that might be handy to check from the shell -export([unix_free/1]). +-export([allocate/1]). + -include_lib("test_server/include/test_server.hrl"). -include_lib("kernel/include/file.hrl"). @@ -87,7 +89,7 @@ groups() -> cur_dir_1a, cur_dir_1b]}, {files, [], [{group, open}, {group, pos}, {group, file_info}, - truncate, sync, datasync, advise, large_write]}, + truncate, sync, datasync, advise, large_write, allocate]}, {open, [], [open1, modes, close, access, read_write, pread_write, append, exclusive]}, @@ -1359,6 +1361,76 @@ check_large_write(Dog, Fd, _, _, []) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +allocate(suite) -> []; +allocate(doc) -> "Tests that ?PRIM_FILE:allocate/3 at least doesn't crash."; +allocate(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(5)), + ?line PrivDir = ?config(priv_dir, Config), + ?line Allocate = filename:join(PrivDir, + atom_to_list(?MODULE) + ++"_allocate.fil"), + + Line1 = "Hello\n", + Line2 = "World!\n", + + ?line {ok, Fd} = ?PRIM_FILE:open(Allocate, [write, binary]), + allocate_and_assert(Fd, 1, iolist_size([Line1, Line2])), + ?line ok = ?PRIM_FILE:write(Fd, Line1), + ?line ok = ?PRIM_FILE:write(Fd, Line2), + ?line ok = ?PRIM_FILE:close(Fd), + + ?line {ok, Fd2} = ?PRIM_FILE:open(Allocate, [write, binary]), + allocate_and_assert(Fd2, 1, iolist_size(Line1)), + ?line ok = ?PRIM_FILE:write(Fd2, Line1), + ?line ok = ?PRIM_FILE:write(Fd2, Line2), + ?line ok = ?PRIM_FILE:close(Fd2), + + ?line {ok, Fd3} = ?PRIM_FILE:open(Allocate, [write, binary]), + allocate_and_assert(Fd3, 1, iolist_size(Line1) + 1), + ?line ok = ?PRIM_FILE:write(Fd3, Line1), + ?line ok = ?PRIM_FILE:write(Fd3, Line2), + ?line ok = ?PRIM_FILE:close(Fd3), + + ?line {ok, Fd4} = ?PRIM_FILE:open(Allocate, [write, binary]), + allocate_and_assert(Fd4, 1, 4 * iolist_size([Line1, Line2])), + ?line ok = ?PRIM_FILE:write(Fd4, Line1), + ?line ok = ?PRIM_FILE:write(Fd4, Line2), + ?line ok = ?PRIM_FILE:close(Fd4), + + ?line test_server:timetrap_cancel(Dog), + ok. + +allocate_and_assert(Fd, Offset, Length) -> + % Just verify that calls to ?PRIM_FILE:allocate/3 don't crash or have + % any other negative side effect. We can't really asssert against a + % specific return value, because support for file space pre-allocation + % depends on the OS, OS version and underlying filesystem. + % + % The Linux kernel added support for fallocate() in version 2.6.23, + % which currently works only for the ext4, ocfs2, xfs and btrfs file + % systems. posix_fallocate() is available in glibc as of version + % 2.1.94, but it was buggy until glibc version 2.7. + % + % Mac OS X, as of version 10.3, supports the fcntl operation F_PREALLOCATE. + % + % Solaris supports posix_fallocate() but only for the UFS file system + % apparently (not supported for ZFS). + % + % FreeBSD 9.0 is the first FreeBSD release supporting posix_fallocate(). + % + % For Windows there's apparently no way to pre-allocate file space, at + % least with similar API/semantics as posix_fallocate(), fallocate() or + % fcntl F_PREALLOCATE. + Result = ?PRIM_FILE:allocate(Fd, Offset, Length), + case os:type() of + {win32, _} -> + ?line {error, enotsup} = Result; + _ -> + ?line _ = Result + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + delete_a(suite) -> []; delete_a(doc) -> []; delete_a(Config) when is_list(Config) -> diff --git a/lib/kernel/test/ram_file_SUITE.erl b/lib/kernel/test/ram_file_SUITE.erl index ab95a3ff5f..5c4437d4d3 100644 --- a/lib/kernel/test/ram_file_SUITE.erl +++ b/lib/kernel/test/ram_file_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 @@ -167,7 +168,7 @@ pread_pwrite(suite) -> pread_pwrite(doc) -> ["Test that pread/2,3 and pwrite/2,3 works."]; pread_pwrite(Config) when is_list(Config) -> - ?line Str = "Flygande b�ckaziner s�ka hwila p� mjuqa tuvor x", + ?line Str = "Flygande bäckaziner söka hwila på mjuqa tuvor x", ?line Bin = list_to_binary(Str), %% pread_pwrite_test(?FILE_MODULE, Str, [ram, read, write]), @@ -206,7 +207,7 @@ position(suite) -> position(doc) -> ["Test that position/2 works."]; position(Config) when is_list(Config) -> - ?line Str = "Att vara eller icke vara, det �r fr�gan. ", + ?line Str = "Att vara eller icke vara, det är frågan. ", ?line Bin = list_to_binary(Str), %% position_test(?FILE_MODULE, Str, [ram, read]), @@ -287,8 +288,8 @@ truncate(suite) -> truncate(doc) -> ["Test that truncate/1 works."]; truncate(Config) when is_list(Config) -> - ?line Str = "M�n �dlare att lida och f�rdraga " - ++ "ett bittert �des stygn av pilar, ", + ?line Str = "Mån ädlare att lida och fördraga " + ++ "ett bittert ödes stygn av pilar, ", ?line Bin = list_to_binary(Str), %% ok = truncate_test(?FILE_MODULE, Str, [ram, read, write]), @@ -331,7 +332,7 @@ sync(suite) -> sync(doc) -> ["Test that sync/1 at least does not crash."]; sync(Config) when is_list(Config) -> - ?line Str = "�n att ta till vapen mot ett hav av kval. ", + ?line Str = "än att ta till vapen mot ett hav av kval. ", ?line Bin = list_to_binary(Str), %% sync_test(?FILE_MODULE, Str, [ram, read, write]), @@ -365,8 +366,8 @@ get_set_file(doc) -> ["Tests get_file/1, set_file/2, get_file_close/1 and get_size/1."]; get_set_file(Config) when is_list(Config) -> %% These two strings should not be of equal length. - ?line Str = "N�r h�gan nord blir sn�bet�ckt, ", - ?line Str2 = "f�r alla harar byta dr�kt. ", + ?line Str = "När högan nord blir snöbetäckt, ", + ?line Str2 = "får alla harar byta dräkt. ", ?line Bin = list_to_binary(Str), ?line Bin2 = list_to_binary(Str2), %% diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl index 72b3112053..24dc410dbe 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -204,7 +204,7 @@ decode_name(_Config, Scope, Item) -> %% to command requests from the MGC that audit %% ObservedEventsDescriptor, and found in the %% ObservedEventsDescriptor. See 12.2. If there are no parameters -%% for the ObservedEvents Descriptor, then �none� shall be specified. +%% for the ObservedEvents Descriptor, then 'none' shall be specified. %% %% %% 12.1.4 Signals @@ -225,7 +225,7 @@ decode_name(_Config, Scope, Item) -> %% %% BR (Brief) %% -%% NOTE -�SignalType may be defined such that it is dependent on +%% NOTE - SignalType may be defined such that it is dependent on %% the value of one or more parameters. The package MUST specify a %% default signal type. If the default type is TO, the package MUST %% specify a default duration which may be provisioned. A default diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl index 12e673ac81..d89717c00a 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -204,7 +204,7 @@ decode_name(_Config, Scope, Item) -> %% to command requests from the MGC that audit %% ObservedEventsDescriptor, and found in the %% ObservedEventsDescriptor. See 12.2. If there are no parameters -%% for the ObservedEvents Descriptor, then �none� shall be specified. +%% for the ObservedEvents Descriptor, then 'none' shall be specified. %% %% %% 12.1.4 Signals @@ -225,7 +225,7 @@ decode_name(_Config, Scope, Item) -> %% %% BR (Brief) %% -%% NOTE -�SignalType may be defined such that it is dependent on +%% NOTE - SignalType may be defined such that it is dependent on %% the value of one or more parameters. The package MUST specify a %% default signal type. If the default type is TO, the package MUST %% specify a default duration which may be provisioned. A default diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl index d08231caac..8b4192ad44 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -204,7 +204,7 @@ decode_name(_Config, Scope, Item) -> %% to command requests from the MGC that audit %% ObservedEventsDescriptor, and found in the %% ObservedEventsDescriptor. See 12.2. If there are no parameters -%% for the ObservedEvents Descriptor, then �none� shall be specified. +%% for the ObservedEvents Descriptor, then 'none' shall be specified. %% %% %% 12.1.4 Signals @@ -225,7 +225,7 @@ decode_name(_Config, Scope, Item) -> %% %% BR (Brief) %% -%% NOTE -�SignalType may be defined such that it is dependent on +%% NOTE - SignalType may be defined such that it is dependent on %% the value of one or more parameters. The package MUST specify a %% default signal type. If the default type is TO, the package MUST %% specify a default duration which may be provisioned. A default diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl index c101aa15bc..20954d4c9d 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -204,7 +204,7 @@ decode_name(_Config, Scope, Item) -> %% to command requests from the MGC that audit %% ObservedEventsDescriptor, and found in the %% ObservedEventsDescriptor. See 12.2. If there are no parameters -%% for the ObservedEvents Descriptor, then �none� shall be specified. +%% for the ObservedEvents Descriptor, then 'none' shall be specified. %% %% %% 12.1.4 Signals @@ -225,7 +225,7 @@ decode_name(_Config, Scope, Item) -> %% %% BR (Brief) %% -%% NOTE -�SignalType may be defined such that it is dependent on +%% NOTE - SignalType may be defined such that it is dependent on %% the value of one or more parameters. The package MUST specify a %% default signal type. If the default type is TO, the package MUST %% specify a default duration which may be provisioned. A default diff --git a/lib/megaco/test/megaco_codec_prev3a_test.erl b/lib/megaco/test/megaco_codec_prev3a_test.erl index 4f1160b93f..b2316eb509 100644 --- a/lib/megaco/test/megaco_codec_prev3a_test.erl +++ b/lib/megaco/test/megaco_codec_prev3a_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -3104,7 +3104,7 @@ pretty_otp5068_msg1() -> 190, asn1_NOVALUE, {actionReplies, - [{'ActionReply', %% Comments: Detta upprepas m�nga g�nger + [{'ActionReply', %% Comments: This is repeated many times. 0, asn1_NOVALUE, asn1_NOVALUE, diff --git a/lib/megaco/test/megaco_codec_prev3b_test.erl b/lib/megaco/test/megaco_codec_prev3b_test.erl index 4bd49365e7..fa24f49372 100644 --- a/lib/megaco/test/megaco_codec_prev3b_test.erl +++ b/lib/megaco/test/megaco_codec_prev3b_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -3223,7 +3223,7 @@ pretty_otp5068_msg1() -> 190, asn1_NOVALUE, {actionReplies, - [{'ActionReply', %% Comments: Detta upprepas m�nga g�nger + [{'ActionReply', %% Comments: This is repeated many times. 0, asn1_NOVALUE, asn1_NOVALUE, diff --git a/lib/megaco/test/megaco_codec_prev3c_test.erl b/lib/megaco/test/megaco_codec_prev3c_test.erl index baa1c7f547..7f6d098ed8 100644 --- a/lib/megaco/test/megaco_codec_prev3c_test.erl +++ b/lib/megaco/test/megaco_codec_prev3c_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 @@ -2679,7 +2679,7 @@ pretty_otp5068_msg1() -> 190, asn1_NOVALUE, {actionReplies, - [{'ActionReply', %% Comments: Detta upprepas m�nga g�nger + [{'ActionReply', %% Comments: This is repeated many times. 0, asn1_NOVALUE, asn1_NOVALUE, diff --git a/lib/megaco/test/megaco_codec_v1_test.erl b/lib/megaco/test/megaco_codec_v1_test.erl index 86f332fde0..3be0da3ae4 100644 --- a/lib/megaco/test/megaco_codec_v1_test.erl +++ b/lib/megaco/test/megaco_codec_v1_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 @@ -3298,7 +3298,7 @@ pretty_otp5068_msg1() -> 190, asn1_NOVALUE, {actionReplies, - [{'ActionReply', %% Comments: Detta upprepas m�nga g�nger + [{'ActionReply', %% Comments: This is repeated many times. 0, asn1_NOVALUE, asn1_NOVALUE, diff --git a/lib/megaco/test/megaco_codec_v2_test.erl b/lib/megaco/test/megaco_codec_v2_test.erl index e8db9e1cb1..1f522504d6 100644 --- a/lib/megaco/test/megaco_codec_v2_test.erl +++ b/lib/megaco/test/megaco_codec_v2_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 @@ -3722,7 +3722,7 @@ pretty_otp5068_msg1() -> 190, asn1_NOVALUE, {actionReplies, - [{'ActionReply', %% Comments: Detta upprepas m�nga g�nger + [{'ActionReply', %% Comments: This is repeated many times. 0, asn1_NOVALUE, asn1_NOVALUE, diff --git a/lib/megaco/test/megaco_codec_v3_test.erl b/lib/megaco/test/megaco_codec_v3_test.erl index 67805479f9..9d564a0ae3 100644 --- a/lib/megaco/test/megaco_codec_v3_test.erl +++ b/lib/megaco/test/megaco_codec_v3_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 @@ -2701,7 +2701,7 @@ pretty_otp5068_msg1() -> 190, asn1_NOVALUE, {actionReplies, - [{'ActionReply', %% Comments: Detta upprepas m�nga g�nger + [{'ActionReply', %% Comments: This is repeated many times. 0, asn1_NOVALUE, asn1_NOVALUE, diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl index d488a33d67..ec67d9ec12 100644 --- a/lib/mnesia/src/mnesia_controller.erl +++ b/lib/mnesia/src/mnesia_controller.erl @@ -593,6 +593,12 @@ multicall(Nodes, Msg) -> {PatchedGood, Bad}. %% Make the replies look like rpc:multicalls.. %% rpc:multicall(Nodes, ?MODULE, call, [Msg]). +next_async_dump_log() -> + Interval = mnesia_monitor:get_env(dump_log_time_threshold), + Msg = {next_async_dump_log, time_threshold}, + Ref = erlang:send_after(Interval, self(), Msg), + Ref. + %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- @@ -614,9 +620,7 @@ init([Parent]) -> mnesia_lib:unset(original_nodes), mnesia_recover:connect_nodes(Diff), - Interval = mnesia_monitor:get_env(dump_log_time_threshold), - Msg = {async_dump_log, time_threshold}, - {ok, Ref} = timer:send_interval(Interval, Msg), + Ref = next_async_dump_log(), mnesia_dumper:start_regulator(), Empty = gb_trees:empty(), @@ -1121,6 +1125,11 @@ handle_sync_tabs([], _From) -> %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- +handle_info({next_async_dump_log, InitBy}, State) -> + async_dump_log(InitBy), + Ref = next_async_dump_log(), + noreply(State#state{dump_log_timer_ref=Ref}); + handle_info({async_dump_log, InitBy}, State) -> Worker = #dump_log{initiated_by = InitBy}, State2 = add_worker(Worker, State), diff --git a/lib/mnesia/src/mnesia_event.erl b/lib/mnesia/src/mnesia_event.erl index 8085155fd5..9fd0342d31 100644 --- a/lib/mnesia/src/mnesia_event.erl +++ b/lib/mnesia/src/mnesia_event.erl @@ -153,7 +153,7 @@ handle_system_event({mnesia_down, Node}, State) -> end; handle_system_event({mnesia_overload, Details}, State) -> - report_warning("Mnesia is overloaded: ~p~n", [Details]), + report_warning("Mnesia is overloaded: ~w~n", [Details]), {ok, State}; handle_system_event({mnesia_info, Format, Args}, State) -> diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl index a22c95d454..14011003d3 100644 --- a/lib/mnesia/src/mnesia_locker.erl +++ b/lib/mnesia/src/mnesia_locker.erl @@ -1177,9 +1177,9 @@ system_code_change(State, _Module, _OldVsn, _Extra) -> %% AXD301 patch sort pids according to R9B sort order %%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Om R9B == true, g�rs j�mf�relsen som i R9B plain. -%% Om R9B == false, g�rs j�mf�relsen som i alla andra releaser. -%% cmp_tid(T1, T2) returnerar -1 om T1 < T2, 0 om T1 = T2 och 1 om T1 > T2. +%% Om R9B == true, the comparison is done as in R9B plain. +%% Om R9B == false, the comparison is done as in any other release. +%% cmp_tid(T1, T2) returns -1 if T1 < T2, 0 if T1 = T2 and 1 if T1 > T2. -define(VERSION_MAGIC, 131). -define(ATOM_EXT, 100). diff --git a/lib/mnesia/src/mnesia_recover.erl b/lib/mnesia/src/mnesia_recover.erl index 4750291a10..b64f428f15 100644 --- a/lib/mnesia/src/mnesia_recover.erl +++ b/lib/mnesia/src/mnesia_recover.erl @@ -45,7 +45,8 @@ note_log_decision/2, outcome/2, start/0, - start_garb/0, + next_garb/0, + next_check_overload/0, still_pending/1, sync_trans_tid_serial/1, sync/0, @@ -91,10 +92,38 @@ start() -> init() -> call(init). -start_garb() -> +next_garb() -> Pid = whereis(mnesia_recover), - {ok, _} = timer:send_interval(timer:minutes(2), Pid, garb_decisions), - {ok, _} = timer:send_interval(timer:seconds(10), Pid, check_overload). + erlang:send_after(timer:minutes(2), Pid, garb_decisions). + +next_check_overload() -> + Pid = whereis(mnesia_recover), + erlang:send_after(timer:seconds(10), Pid, check_overload). + + +do_check_overload(S) -> + %% Time to check if mnesia_tm is overloaded + case whereis(mnesia_tm) of + Pid when is_pid(Pid) -> + Threshold = 100, + Prev = S#state.tm_queue_len, + {message_queue_len, Len} = + process_info(Pid, message_queue_len), + if + Len > Threshold, Prev > Threshold -> + What = {mnesia_tm, message_queue_len, [Prev, Len]}, + mnesia_lib:report_system_event({mnesia_overload, What}), + mnesia_lib:overload_set(mnesia_tm, true), + S#state{tm_queue_len = 0}; + Len > Threshold -> + S#state{tm_queue_len = Len}; + true -> + mnesia_lib:overload_set(mnesia_tm, false), + S#state{tm_queue_len = 0} + end; + undefined -> + S + end. allow_garb() -> cast(allow_garb). @@ -853,34 +882,13 @@ handle_info({connect_nodes, Ns, From}, State) -> handle_call({connect_nodes,Ns},From,State); handle_info(check_overload, S) -> - %% Time to check if mnesia_tm is overloaded - case whereis(mnesia_tm) of - Pid when is_pid(Pid) -> - - Threshold = 100, - Prev = S#state.tm_queue_len, - {message_queue_len, Len} = - process_info(Pid, message_queue_len), - if - Len > Threshold, Prev > Threshold -> - What = {mnesia_tm, message_queue_len, [Prev, Len]}, - mnesia_lib:report_system_event({mnesia_overload, What}), - mnesia_lib:overload_set(mnesia_tm, true), - {noreply, S#state{tm_queue_len = 0}}; - - Len > Threshold -> - {noreply, S#state{tm_queue_len = Len}}; - - true -> - mnesia_lib:overload_set(mnesia_tm, false), - {noreply, S#state{tm_queue_len = 0}} - end; - undefined -> - {noreply, S} - end; + State2 = do_check_overload(S), + next_check_overload(), + {noreply, State2}; handle_info(garb_decisions, State) -> do_garb_decisions(), + next_garb(), {noreply, State}; handle_info({force_decision, Tid}, State) -> diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl index 0af7f55c06..b5b14ac05b 100644 --- a/lib/mnesia/src/mnesia_tm.erl +++ b/lib/mnesia/src/mnesia_tm.erl @@ -103,7 +103,8 @@ init(Parent) -> end, mnesia_schema:purge_tmp_files(), - mnesia_recover:start_garb(), + mnesia_recover:next_garb(), + mnesia_recover:next_check_overload(), ?eval_debug_fun({?MODULE, init}, [{nodes, AllOthers}]), diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl index 6f882d0be9..4c04126d4f 100644 --- a/lib/observer/test/crashdump_viewer_SUITE.erl +++ b/lib/observer/test/crashdump_viewer_SUITE.erl @@ -34,6 +34,7 @@ -define(default_timeout, ?t:minutes(30)). -define(sl_alloc_vsns,[r9b]). +-define(failed_file,"failed-cases.txt"). init_per_testcase(_Case, Config) -> DataDir = ?config(data_dir,Config), @@ -42,9 +43,18 @@ init_per_testcase(_Case, Config) -> catch crashdump_viewer:stop(), Dog = ?t:timetrap(?default_timeout), [{watchdog, Dog}|Config]. -end_per_testcase(_Case, Config) -> +end_per_testcase(Case, Config) -> Dog=?config(watchdog, Config), ?t:timetrap_cancel(Dog), + case ?config(tc_status,Config) of + ok -> + ok; + _Fail -> + File = filename:join(?config(data_dir,Config),?failed_file), + {ok,Fd}=file:open(File,[append]), + file:write(Fd,io_lib:format("~w.~n",[Case])), + file:close(Fd) + end, ok. suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -67,15 +77,26 @@ init_per_suite(doc) -> ["Create a lot of crashdumps which can be used in the testcases below"]; init_per_suite(Config) when is_list(Config) -> Dog = ?t:timetrap(?default_timeout), + delete_saved(Config), application:start(inets), % will be using the http client later httpc:set_options([{ipfamily,inet6fb4}]), DataDir = ?config(data_dir,Config), - Rels = [R || R <- [r13b,r14b], ?t:is_release_available(R)] ++ [current], + Rels = [R || R <- [r14b,r15b], ?t:is_release_available(R)] ++ [current], io:format("Creating crash dumps for the following releases: ~p", [Rels]), AllDumps = create_dumps(DataDir,Rels), ?t:timetrap_cancel(Dog), [{dumps,AllDumps}|Config]. +delete_saved(Config) -> + DataDir = ?config(data_dir,Config), + file:delete(filename:join(DataDir,?failed_file)), + SaveDir = filename:join(DataDir,"save"), + Dumps = filelib:wildcard(filename:join(SaveDir,"*")), + lists:foreach(fun(F) -> file:delete(F) end, Dumps), + file:del_dir(SaveDir), + ok. + + translate(suite) -> []; translate(doc) -> @@ -196,6 +217,23 @@ end_per_suite(doc) -> ["Remove generated crashdumps"]; end_per_suite(Config) when is_list(Config) -> Dumps = ?config(dumps,Config), + DataDir = ?config(data_dir,Config), + FailedFile = filename:join(DataDir,?failed_file), + case filelib:is_file(FailedFile) of + true -> + SaveDir = filename:join(DataDir,"save"), + file:make_dir(SaveDir), + file:copy(FailedFile,filename:join(SaveDir,?failed_file)), + lists:foreach( + fun(CD) -> + File = filename:basename(CD), + New = filename:join(SaveDir,File), + file:copy(CD,New) + end, Dumps); + false -> + ok + end, + file:delete(FailedFile), lists:foreach(fun(CD) -> ok = file:delete(CD) end,Dumps), lists:keydelete(dumps,1,Config). @@ -388,7 +426,7 @@ special(Port,File) -> %% I registered a process as aaaaaaaa in the full_dist dumps %% to make sure it will be the first in the list when sorted - %% on names. There are some special data here, s� I'll thoroughly + %% on names. There are some special data here, so I'll thoroughly %% read the process details for this process. Other processes %% are just briefly traversed. {Pid,Rest1} = get_first_process(AllProcs), @@ -568,11 +606,14 @@ expand_link(Html) -> port_details(Port) -> - Port1 = contents(Port,"port?port=Port<0.1>"), - "#Port<0.1>" = title(Port1), - Port0 = contents(Port,"port?port=Port<0.0>"), - "Could not find port: #Port<0.0>" = title(Port0). + Port1 = contents(Port,"port?port=Port<0.1>"), + case title(Port0) of + "#Port<0.0>" -> % R16 or later + "Could not find port: #Port<0.1>" = title(Port1); + "Could not find port: #Port<0.0>" -> % R15 or earlier + "#Port<0.1>" = title(Port1) + end. is_truncated(File) -> case filename:extension(filename:rootname(File)) of @@ -691,6 +732,12 @@ dump_with_strange_module_name(DataDir,Rel,DumpName) -> CD. dump(Node,DataDir,Rel,DumpName) -> + case Rel of + _ when Rel<r15b, Rel=/=current -> + rpc:call(Node,os,putenv,["ERL_CRASH_DUMP_SECONDS","600"]); + _ -> + ok + end, rpc:call(Node,erlang,halt,[DumpName]), Crashdump0 = filename:join(filename:dirname(code:which(?t)), "erl_crash_dump.n1"), @@ -752,6 +799,7 @@ rel_opt(Rel) -> r12b -> [{erl,[{release,"r12b_patched"}]}]; r13b -> [{erl,[{release,"r13b_patched"}]}]; r14b -> [{erl,[{release,"r14b_latest"}]}]; %naming convention changed + r15b -> [{erl,[{release,"r15b_latest"}]}]; current -> [] end. @@ -764,7 +812,8 @@ dump_prefix(Rel) -> r12b -> "r12b_dump."; r13b -> "r13b_dump."; r14b -> "r14b_dump."; - current -> "r15b_dump." + r15b -> "r15b_dump."; + current -> "r16b_dump." end. compat_rel(Rel) -> @@ -776,5 +825,6 @@ compat_rel(Rel) -> r12b -> "+R12 "; r13b -> "+R13 "; r14b -> "+R14 "; + r15b -> "+R15 "; current -> "" end. diff --git a/lib/odbc/doc/src/odbc.xml b/lib/odbc/doc/src/odbc.xml index 0e3386b11f..a984bf4485 100644 --- a/lib/odbc/doc/src/odbc.xml +++ b/lib/odbc/doc/src/odbc.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1999</year><year>2011</year> + <year>1999</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -105,8 +105,14 @@ odbc_data_type() = sql_integer | sql_smallint | sql_tinyint | {sql_decimal, precision(), scale()} | {sql_numeric, precision(), scale()} | - {sql_char, size()} | {sql_wchar, size()} | {sql_varchar, size()} | {sql_wvarchar, size()}| {sql_float, precision()} | - {sql_wlongvarchar, size()} | {sql_float, precision()} | sql_real | sql_double | sql_bit | atom() + {sql_char, size()} | + {sql_wchar, size()} | + {sql_varchar, size()} | + {sql_wvarchar, size()}| + {sql_float, precision()} | + {sql_wlongvarchar, size()} | + {sql_float, precision()} | + sql_real | sql_double | sql_bit | atom() </code> <code type="none"> precision() = integer() </code> diff --git a/lib/odbc/test/odbc_data_type_SUITE.erl b/lib/odbc/test/odbc_data_type_SUITE.erl index d61a91f973..2d33546622 100644 --- a/lib/odbc/test/odbc_data_type_SUITE.erl +++ b/lib/odbc/test/odbc_data_type_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 @@ -1469,7 +1470,7 @@ utf8(Config) when is_list(Config) -> odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ "(FIELD text)"), - Latin1Data = ["���������", + Latin1Data = ["ÖÄÅÄÖÅäöå", "testasdf", "Row 3", "Row 4", @@ -1564,7 +1565,7 @@ timestamp(Config) when is_list(Config) -> %%------------------------------------------------------------------------ w_char_support(Ref, Table, CharType, Size) -> - Latin1Data = ["���������", + Latin1Data = ["ÖÄÅÄÖÅäöå", "testasdf", "Row 3", "Row 4", diff --git a/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml b/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml index 964ae3e92d..1fd2f644cb 100644 --- a/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml +++ b/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2010</year> + <year>1997</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -1142,7 +1142,8 @@ handle_info(_Info, State) -> module, and <c>lookup</c> in <c>DB_Administrator_impl.erl</c><em>and</em><c>DB_CommonUser_impl.erl</c>. But wait, is that really necessary? Actually, it is not. We simple use the IC compile option <em>impl</em>:</p> <pre> -$ <input>erlc +'{{impl, "DB::CommonUser"}, "DBUser_impl"}' +'{{impl, "DB::Administrator"}, "DBUser_impl"}' DB.idl</input> +$ <input>erlc +'{{impl, "DB::CommonUser"}, "DBUser_impl"}'\ + +'{{impl, "DB::Administrator"}, "DBUser_impl"}' DB.idl</input> $ <input>erlc *.erl</input> </pre> <p>Instead of creating, and not the least, maintaining two call-back modules, diff --git a/lib/orber/src/orber_iiop_net.erl b/lib/orber/src/orber_iiop_net.erl index 2eed0b538a..33e02e3c04 100644 --- a/lib/orber/src/orber_iiop_net.erl +++ b/lib/orber/src/orber_iiop_net.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 @@ -415,11 +415,10 @@ handle_info({'EXIT', Pid, _Reason}, State) when is_pid(Pid) -> ref = Ref, options = Options, proxy_options = POpts}] -> ets:delete(?CONNECTION_DB, Pid), unlink(Pid), - NewListen = new_listen_socket(Type, Listen, Port, Options), - {ok, NewPid} = orber_iiop_socketsup:start_accept(Type, NewListen, + {ok, NewPid} = orber_iiop_socketsup:start_accept(Type, Listen, Ref, POpts), link(NewPid), - ets:insert(?CONNECTION_DB, #listen{pid = NewPid, socket = NewListen, + ets:insert(?CONNECTION_DB, #listen{pid = NewPid, socket = Listen, port = Port, type = Type, ref = Ref, options = Options, proxy_options = POpts}), @@ -445,23 +444,6 @@ handle_info({'EXIT', Pid, _Reason}, State) when is_pid(Pid) -> handle_info(_, State) -> {noreply, State}. -new_listen_socket(normal, ListenFd, _Port, _Options) -> - ListenFd; -new_listen_socket(ssl, ListenFd, Port, Options) -> - Generation = orber_env:ssl_generation(), - if - Generation > 2 -> - ListenFd; - true -> - case is_process_alive(ssl:pid(ListenFd)) of - true -> - ListenFd; - _ -> - {ok, Listen, _NP} = orber_socket:listen(ssl, Port, Options, true), - Listen - end - end. - from_list(List) -> from_list(List, queue:new()). diff --git a/lib/os_mon/src/os_mon.erl b/lib/os_mon/src/os_mon.erl index df1eccb064..19acae9f0e 100644 --- a/lib/os_mon/src/os_mon.erl +++ b/lib/os_mon/src/os_mon.erl @@ -176,9 +176,7 @@ services({unix, sunos}) -> services({unix, _}) -> % Other unix. [cpu_sup, disksup, memsup]; services({win32, _}) -> - [disksup, memsup, os_sup, sysinfo]; -services(_) -> - []. + [disksup, memsup, os_sup, sysinfo]. server_name(cpu_sup) -> cpu_sup; server_name(disksup) -> disksup; diff --git a/lib/parsetools/include/yeccpre.hrl b/lib/parsetools/include/yeccpre.hrl index 3672394fc5..e4c3ba52be 100644 --- a/lib/parsetools/include/yeccpre.hrl +++ b/lib/parsetools/include/yeccpre.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -36,7 +36,7 @@ parse_and_scan({M, F, A}) -> -spec format_error(any()) -> [char() | list()]. format_error(Message) -> - case io_lib:deep_char_list(Message) of + case io_lib:deep_unicode_char_list(Message) of true -> Message; _ -> @@ -164,7 +164,7 @@ yecctoken_location(Token) -> yecctoken2string({atom, _, A}) -> io_lib:write(A); yecctoken2string({integer,_,N}) -> io_lib:write(N); yecctoken2string({float,_,F}) -> io_lib:write(F); -yecctoken2string({char,_,C}) -> io_lib:write_char(C); +yecctoken2string({char,_,C}) -> io_lib:write_unicode_char(C); yecctoken2string({var,_,V}) -> io_lib:format("~s", [V]); yecctoken2string({string,_,S}) -> io_lib:write_unicode_string(S); yecctoken2string({reserved_symbol, _, A}) -> io_lib:write(A); diff --git a/lib/parsetools/src/esyntax.yrl b/lib/parsetools/src/esyntax.yrl deleted file mode 100644 index 1ecb54f0a7..0000000000 --- a/lib/parsetools/src/esyntax.yrl +++ /dev/null @@ -1,360 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. 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% -%% -Nonterminals -add_op attribute basic_type bif_test -case_expr clause_body -clause_guard clause_head comp_op cr_clause cr_clauses expr expr_tail -exprs farity farity_list form formal_parameter_list function -function_call function_clause guard guard_call guard_expr -guard_expr_list guard_exprs guard_expr_tail guard_expr_tuple -guard_parameter_list -guard_tests guard_test if_clause if_clauses if_expr list match_expr -mult_op parameter_list pattern patterns pattern_list pattern_tail pattern_tuple -prefix_op receive_expr send_expr tuple. - -Terminals -'!' '(' ')' '*' '+' ',' '-' '->' '/' '/=' ':' ';' '<' '=' '=/=' '=:=' -'=<' '==' '>' '>=' '[' ']' 'after' 'band' 'begin' 'bnot' -'bor' 'bsl' 'bsr' 'bxor' 'case' 'catch' 'div' 'end' 'if' 'of' -'receive' 'rem' 'when' '{' '|' '}' atom float integer string var. -% 'receive' 'rem' 'true' 'when' '{' '|' '}' atom float integer string var. - -Rootsymbol form. - -Endsymbol dot. - -Unary 0 'catch'. -Right 200 '='. -Right 200 '!'. -Left 300 add_op. -Left 400 mult_op. -Unary 500 prefix_op. - - -add_op -> '+' : '$1'. -add_op -> '-' : '$1'. -add_op -> 'bor' : '$1'. -add_op -> 'bxor' : '$1'. -add_op -> 'bsl' : '$1'. -add_op -> 'bsr' : '$1'. - -comp_op -> '==' : '$1'. -comp_op -> '/=' : '$1'. -comp_op -> '=<' : '$1'. -comp_op -> '<' : '$1'. -comp_op -> '>=' : '$1'. -comp_op -> '>' : '$1'. -comp_op -> '=:=' : '$1'. -comp_op -> '=/=' : '$1'. - -mult_op -> '*' : '$1'. -mult_op -> '/' : '$1'. -mult_op -> 'div' : '$1'. -mult_op -> 'rem' : '$1'. -mult_op -> 'band' : '$1'. - -prefix_op -> '+' : '$1'. -prefix_op -> '-' : '$1'. -prefix_op -> 'bnot' : '$1'. - - -basic_type -> atom : '$1'. -basic_type -> float : '$1'. -basic_type -> integer : '$1'. -basic_type -> string : '$1'. -basic_type -> var : '$1'. -% basic_type -> 'true' : {atom, element(2, '$1'), 'true'}. - - -pattern -> basic_type : '$1'. -pattern -> pattern_list : '$1'. -pattern -> pattern_tuple : '$1'. - -pattern_list -> '[' ']' : {nil, element(2, '$1')}. -pattern_list -> '[' pattern pattern_tail ']' : - {cons, element(2, '$1'), '$2', '$3'}. - -pattern_tail -> '|' pattern : '$2'. -pattern_tail -> ',' pattern pattern_tail : - {cons, element(2, '$2'), '$2', '$3'}. -pattern_tail -> '$empty' : {nil, 0}. - -pattern_tuple -> '{' '}' : {tuple, element(2, '$1'), []}. -pattern_tuple -> '{' patterns '}' : {tuple, element(2, '$1'), '$2'}. - -patterns -> pattern : ['$1']. -patterns -> pattern ',' patterns : ['$1' | '$3']. - - -expr -> basic_type : '$1'. -expr -> list : '$1'. -expr -> tuple : '$1'. -expr -> function_call : '$1'. - -expr -> expr add_op expr : - {Op, Pos} = '$2', - {arith, Pos, Op, '$1', '$3'}. -expr -> expr mult_op expr : - {Op, Pos} = '$2', - {arith, Pos, Op, '$1', '$3'}. -expr -> prefix_op expr: - case '$2' of - {float, Pos, N} -> - case '$1' of - {'-', _} -> - {float, Pos, -N}; - {'+', _} -> - {float, Pos, N}; - {Op, Pos1} -> - {arith, Pos1, Op, {float, Pos, N}} - end; - {integer, Pos, N} -> - case '$1' of - {'-', _} -> - {integer, Pos, -N}; - {'+', _} -> - {integer, Pos, N}; - {Op, Pos1} -> - {arith, Pos1, Op, {integer, Pos, N}} - end; - _ -> - {Op, Pos} = '$1', - {arith, Pos, Op, '$2'} - end. - -expr -> '(' expr ')' : '$2'. -expr -> 'begin' exprs 'end' : {block, element(2, '$1'), '$2'}. -expr -> 'catch' expr : {'catch', element(2, '$1'), '$2'}. - -expr -> case_expr : '$1'. -expr -> if_expr : '$1'. -expr -> receive_expr : '$1'. -expr -> match_expr : '$1'. -expr -> send_expr : '$1'. - - -list -> '[' ']' : {nil, element(2, '$1')}. -list -> '[' expr expr_tail ']' : {cons, element(2, '$1'), '$2', '$3'}. - -expr_tail -> '|' expr : '$2'. -expr_tail -> ',' expr expr_tail : {cons, element(2, '$2'), '$2', '$3'}. -expr_tail -> '$empty' : {nil, 0}. - -tuple -> '{' '}' : {tuple, element(2, '$1'), []}. -tuple -> '{' exprs '}' : {tuple, element(2, '$1'), '$2'}. - - -function_call -> atom '(' parameter_list ')' : - case erl_parse:erlang_bif(element(3, '$1'), length('$3')) of - true -> - {bif, element(2, '$1'), element(3, '$1'), '$3'}; - false -> - {call, element(2, '$1'), [], element(3, '$1'), '$3'} - end. -function_call -> atom ':' atom '(' parameter_list ')' : - {call, element(2, '$1'), element(3, '$1'), element(3, '$3'), '$5'}. - -parameter_list -> exprs : '$1'. -parameter_list -> '$empty' : []. - - -case_expr -> 'case' expr 'of' cr_clauses 'end' : - {'case', element(2, '$1'), '$2', '$4'}. - -cr_clause -> pattern clause_guard clause_body : - {clause, element(2, '$1'), ['$1'], '$2', '$3'}. - -cr_clauses -> cr_clause : ['$1']. -cr_clauses -> cr_clause ';' cr_clauses : ['$1' | '$3']. - -if_expr -> 'if' if_clauses 'end' : {'if', element(2, '$1'), '$2'}. - -if_clause -> guard clause_body : {clause, element(2, hd('$2')), '$1', '$2'}. - -if_clauses -> if_clause : ['$1']. -if_clauses -> if_clause ';' if_clauses : ['$1' | '$3']. - -receive_expr -> 'receive' 'after' expr clause_body 'end' : - {'receive', element(2, '$1'), [], '$3', '$4'}. -receive_expr -> 'receive' cr_clauses 'end' : - {'receive', element(2, '$1'), '$2'}. -receive_expr -> 'receive' cr_clauses 'after' expr clause_body 'end' : - {'receive', element(2, '$1'), '$2', '$4', '$5'}. - - -match_expr -> expr '=' expr : - case erl_parse:is_term('$1') of - true -> - {match, element(2, '$1'), '$1', '$3'}; - false -> - throw({error, {element(2, '$1'), yecc, "illegal lhs in match **"}}) - end. - -send_expr -> expr '!' expr : - Pos = element(2, '$1'), - {send, Pos, '$1', '$3'}. - - -exprs -> expr : ['$1']. -exprs -> expr ',' exprs : ['$1' | '$3']. - - -guard_expr -> basic_type : '$1'. -guard_expr -> guard_expr_list : '$1'. -guard_expr -> guard_expr_tuple : '$1'. -guard_expr -> guard_call : '$1'. -guard_expr -> '(' guard_expr ')' : '$2'. -guard_expr -> guard_expr add_op guard_expr : - {Op, Pos} = '$2', - {arith, Pos, Op, '$1', '$3'}. -guard_expr -> guard_expr mult_op guard_expr : - {Op, Pos} = '$2', - {arith, Pos, Op, '$1', '$3'}. -guard_expr -> prefix_op guard_expr: - case '$2' of - {float, Pos, N} -> - case '$1' of - {'-', _} -> - {float, Pos, -N}; - {'+', _} -> - {float, Pos, N}; - {Op, Pos1} -> - {arith, Pos1, Op, {float, Pos, N}} - end; - {integer, Pos, N} -> - case '$1' of - {'-', _} -> - {integer, Pos, -N}; - {'+', _} -> - {integer, Pos, N}; - {Op, Pos1} -> - {arith, Pos1, Op, {integer, Pos, N}} - end; - _ -> - {Op, Pos} = '$1', - {arith, Pos, Op, '$2'} - end. - -guard_expr_list -> '[' ']' : {nil, element(2, '$1')}. -guard_expr_list -> '[' guard_expr guard_expr_tail ']' : - {cons, element(2, '$1'), '$2', '$3'}. - -guard_expr_tail -> '|' guard_expr : '$2'. -guard_expr_tail -> ',' guard_expr guard_expr_tail : - {cons, element(2, '$2'), '$2', '$3'}. -guard_expr_tail -> '$empty' : {nil, 0}. - -guard_expr_tuple -> '{' '}' : {tuple, element(2, '$1'), []}. -guard_expr_tuple -> '{' guard_exprs '}' : {tuple, element(2, '$1'), '$2'}. - -guard_exprs -> guard_expr : ['$1']. -guard_exprs -> guard_expr ',' guard_exprs : ['$1' | '$3']. - - -guard_call -> atom '(' guard_parameter_list ')' : - case erl_parse:erlang_guard_bif(element(3, '$1'), length('$3')) of - true -> - {bif, element(2, '$1'), element(3, '$1'), '$3'}; - false -> - throw({error, {element(2, '$1'), yecc, "illegal test in guard **"}}) - end. - -guard_parameter_list -> guard_exprs : '$1'. -guard_parameter_list -> '$empty' : []. - - -bif_test -> atom '(' guard_parameter_list ')' : - case erl_parse:erlang_guard_test(element(3, '$1'), length('$3')) of - true -> - {test, element(2, '$1'), element(3, '$1'), '$3'}; - false -> - throw({error, {element(2, '$1'), yecc, "illegal test in guard **"}}) - end. - - -guard_test -> bif_test : '$1'. -guard_test -> guard_expr comp_op guard_expr : - {Op, Pos} = '$2', - {comp, Pos, Op, '$1', '$3'}. - -guard_tests -> guard_test : ['$1']. -guard_tests -> guard_test ',' guard_tests : ['$1' | '$3']. - -% guard -> 'true' : []. -guard -> atom : - case '$1' of - {atom, _, true} -> - []; - _ -> - throw({error, {element(2, '$1'), yecc, "illegal test in guard **"}}) - end. -guard -> guard_tests : '$1'. - - -function_clause -> clause_head clause_guard clause_body : - {Name, Line, Arity, Parameters} = '$1', - {function, Line, Name, Arity, - [{clause, element(2, hd('$3')), Parameters, '$2', '$3'}]}. - -clause_head -> atom '(' formal_parameter_list ')' : - {element(3, '$1'), element(2, '$1'), length('$3'), '$3'}. - -formal_parameter_list -> patterns : '$1'. -formal_parameter_list -> '$empty' : []. - -clause_guard -> 'when' guard : '$2'. -clause_guard -> '$empty' : []. - -clause_body -> '->' exprs: '$2'. - - -function -> function_clause : '$1'. -function -> function_clause ';' function : - case '$1' of - {function, Pos1, Name1, Arity1, [Clause]} -> - case '$3' of - {function, _, Name1, Arity2, Clauses} -> - if - Arity1 /= Arity2 -> - throw({error, {Pos1, yecc, - io_lib:format('arity conflict in definition of ~w', - [Name1])}}); - true -> - {function, Pos1, Name1, Arity1, [Clause | Clauses]} - end; - _ -> - throw({error, {Pos1, yecc, - io_lib:format('missing final dot in def of ~w/~w', - [Name1, Arity1])}}) - end - end. - - -attribute -> atom : element(3, '$1'). -attribute -> '[' farity_list ']' : '$2'. - -farity_list -> farity : ['$1']. -farity_list -> farity ',' farity_list : ['$1' | '$3']. - -farity -> atom '/' integer : {element(3, '$1'), element(3, '$3')}. - - -form -> '-' atom '(' attribute ')' : - {attribute, element(2, '$2'), element(3, '$2'), '$4'}. -form -> function : '$1'. diff --git a/lib/parsetools/src/leex.erl b/lib/parsetools/src/leex.erl index cdf20461d9..bbef4053b4 100644 --- a/lib/parsetools/src/leex.erl +++ b/lib/parsetools/src/leex.erl @@ -58,6 +58,7 @@ gfile=[], % Graph file module, % Module name opts=[], % Options + encoding=none, % Encoding of Xrl file % posix=false, % POSIX regular expressions errors=[], warnings=[] @@ -146,7 +147,9 @@ format_error({regexp,E})-> end, ["bad regexp `",Es,"'"]; format_error(ignored_characters) -> - "ignored characters". + "ignored characters"; +format_error(cannot_parse) -> + io_lib:fwrite("cannot parse; probably encoding mismatch", []). %%% %%% Local functions @@ -298,10 +301,10 @@ pack_warnings([]) -> report_errors(St) -> when_opt(fun () -> foreach(fun({File,{none,Mod,E}}) -> - io:fwrite("~s: ~s\n", + io:fwrite("~s: ~ts\n", [File,Mod:format_error(E)]); ({File,{Line,Mod,E}}) -> - io:fwrite("~s:~w: ~s\n", + io:fwrite("~s:~w: ~ts\n", [File,Line,Mod:format_error(E)]) end, sort(St#leex.errors)) end, report_errors, St#leex.opts). @@ -316,11 +319,11 @@ report_warnings(St) -> ShouldReport = member(report_warnings, St#leex.opts) orelse ReportWerror, when_bool(fun () -> foreach(fun({File,{none,Mod,W}}) -> - io:fwrite("~s: ~s~s\n", + io:fwrite("~s: ~s~ts\n", [File,Prefix, Mod:format_error(W)]); ({File,{Line,Mod,W}}) -> - io:fwrite("~s:~w: ~s~s\n", + io:fwrite("~s:~w: ~s~ts\n", [File,Line,Prefix, Mod:format_error(W)]) end, sort(St#leex.warnings)) @@ -396,17 +399,18 @@ verbose_print(St, Format, Args) -> parse_file(St0) -> case file:open(St0#leex.xfile, [read]) of {ok,Xfile} -> + St1 = St0#leex{encoding = epp:set_encoding(Xfile)}, try - verbose_print(St0, "Parsing file ~s, ", [St0#leex.xfile]), + verbose_print(St1, "Parsing file ~s, ", [St1#leex.xfile]), %% We KNOW that errors throw so we can ignore them here. - {ok,Line1,St1} = parse_head(Xfile, St0), - {ok,Line2,Macs,St2} = parse_defs(Xfile, Line1, St1), - {ok,Line3,REAs,Actions,St3} = - parse_rules(Xfile, Line2, Macs, St2), - {ok,Code,St4} = parse_code(Xfile, Line3, St3), - verbose_print(St1, "contained ~w rules.~n", [length(REAs)]), - {ok,REAs,Actions,Code,St4} - after file:close(Xfile) + {ok,Line1,St2} = parse_head(Xfile, St1), + {ok,Line2,Macs,St3} = parse_defs(Xfile, Line1, St2), + {ok,Line3,REAs,Actions,St4} = + parse_rules(Xfile, Line2, Macs, St3), + {ok,Code,St5} = parse_code(Xfile, Line3, St4), + verbose_print(St5, "contained ~w rules.~n", [length(REAs)]), + {ok,REAs,Actions,Code,St5} + after ok = file:close(Xfile) end; {error,Error} -> add_error({none,leex,{file_error,Error}}, St0) @@ -415,7 +419,7 @@ parse_file(St0) -> %% parse_head(File, State) -> {ok,NextLine,State}. %% Parse the head of the file. Skip all comments and blank lines. -parse_head(Ifile, St) -> {ok,nextline(Ifile, 0),St}. +parse_head(Ifile, St) -> {ok,nextline(Ifile, 0, St),St}. %% parse_defs(File, Line, State) -> {ok,NextLine,Macros,State}. %% Parse the macro definition section of a file. This must exist. @@ -423,7 +427,7 @@ parse_head(Ifile, St) -> {ok,nextline(Ifile, 0),St}. parse_defs(Ifile, {ok,?DEFS_HEAD ++ Rest,L}, St) -> St1 = warn_ignored_chars(L, Rest, St), - parse_defs(Ifile, nextline(Ifile, L), [], St1); + parse_defs(Ifile, nextline(Ifile, L, St), [], St1); parse_defs(_, {ok,_,L}, St) -> add_error({L,leex,missing_defs}, St); parse_defs(_, {eof,L}, St) -> @@ -435,7 +439,7 @@ parse_defs(Ifile, {ok,Chars,L}=Line, Ms, St) -> case re:run(Chars, MS, [{capture,all_but_first,list}]) of {match,[Name,Def]} -> %%io:fwrite("~p = ~p\n", [Name,Def]), - parse_defs(Ifile, nextline(Ifile, L), [{Name,Def}|Ms], St); + parse_defs(Ifile, nextline(Ifile, L, St), [{Name,Def}|Ms], St); _ -> {ok,Line,Ms,St} % Anything else end; parse_defs(_, Line, Ms, St) -> @@ -446,7 +450,7 @@ parse_defs(_, Line, Ms, St) -> parse_rules(Ifile, {ok,?RULE_HEAD ++ Rest,L}, Ms, St) -> St1 = warn_ignored_chars(L, Rest, St), - parse_rules(Ifile, nextline(Ifile, L), Ms, [], [], 0, St1); + parse_rules(Ifile, nextline(Ifile, L, St), Ms, [], [], 0, St1); parse_rules(_, {ok,_,L}, _, St) -> add_error({L,leex,missing_rules}, St); parse_rules(_, {eof,L}, _, St) -> @@ -464,7 +468,7 @@ parse_rules(Ifile, NextLine, Ms, REAs, As, N, St) -> case collect_rule(Ifile, Chars, L0) of {ok,Re,Atoks,L1} -> {ok,REA,A,St1} = parse_rule(Re, L0, Atoks, Ms, N, St), - parse_rules(Ifile, nextline(Ifile, L1), Ms, + parse_rules(Ifile, nextline(Ifile, L1, St), Ms, [REA|REAs], [A|As], N+1, St1); {error,E} -> add_error(E, St) end; @@ -497,8 +501,10 @@ collect_rule(Ifile, Chars, L0) -> {error,E,_} -> {error,E} end. +collect_action(_Ifile, {error, _}, L, _Cont0) -> + {error, {L, leex, cannot_parse}, ignored_end_line}; collect_action(Ifile, Chars, L0, Cont0) -> - case erl_scan:tokens(Cont0, Chars, L0) of + case erl_scan:tokens(Cont0, Chars, L0, [unicode]) of {done,{ok,Toks,_},_} -> {ok,Toks,L0}; {done,{eof,_},_} -> {eof,L0}; {done,{error,E,_},_} -> {error,E,L0}; @@ -560,29 +566,32 @@ parse_code(Ifile, {ok,?CODE_HEAD ++ Rest,CodeL}, St) -> St1 = warn_ignored_chars(CodeL, Rest, St), {ok, CodePos} = file:position(Ifile, cur), %% Just count the lines; copy the code from file to file later. - NCodeLines = count_lines(Ifile, 0), + EndCodeLine = count_lines(Ifile, CodeL, St), + NCodeLines = EndCodeLine - CodeL, {ok,{CodeL,CodePos,NCodeLines},St1}; parse_code(_, {ok,_,L}, St) -> add_error({L,leex,missing_code}, St); parse_code(_, {eof,L}, St) -> add_error({L,leex,missing_code}, St). -count_lines(File, N) -> +count_lines(File, N, St) -> case io:get_line(File, leex) of eof -> N; - _Line -> count_lines(File, N+1) + {error, _} -> add_error({N+1, leex, cannot_parse}, St); + _Line -> count_lines(File, N+1, St) end. -%% nextline(InputFile, PrevLineNo) -> {ok,Chars,LineNo} | {eof,LineNo}. +%% nextline(InputFile, PrevLineNo, State) -> {ok,Chars,LineNo} | {eof,LineNo}. %% Get the next line skipping comment lines and blank lines. -nextline(Ifile, L) -> +nextline(Ifile, L, St) -> case io:get_line(Ifile, leex) of eof -> {eof,L}; + {error, _} -> add_error({L+1, leex, cannot_parse}, St); Chars -> case substr(Chars, span(Chars, " \t\n")+1) of - [$%|_Rest] -> nextline(Ifile, L+1); - [] -> nextline(Ifile, L+1); + [$%|_Rest] -> nextline(Ifile, L+1, St); + [] -> nextline(Ifile, L+1, St); _Other -> {ok,Chars,L+1} end end. @@ -1289,19 +1298,21 @@ out_file(St0, DFA, DF, Actions, Code) -> try case file:open(St0#leex.efile, [write]) of {ok,Ofile} -> + set_encoding(St0, Ofile), try + output_encoding_comment(Ofile, St0), output_file_directive(Ofile, St0#leex.ifile, 0), out_file(Ifile, Ofile, St0, DFA, DF, Actions, Code, 1), verbose_print(St0, "ok~n", []), St0 - after file:close(Ofile) + after ok = file:close(Ofile) end; {error,Error} -> verbose_print(St0, "error~n", []), add_error({none,leex,{file_error,Error}}, St0) end - after file:close(Ifile) + after ok = file:close(Ifile) end; {{error,Error},Ifile} -> add_error(Ifile, {none,leex,{file_error,Error}}, St0) @@ -1310,7 +1321,9 @@ out_file(St0, DFA, DF, Actions, Code) -> open_inc_file(State) -> Ifile = State#leex.ifile, case file:open(Ifile, [read]) of - {ok,F} -> {ok,F}; + {ok,F} -> + _ = epp:set_encoding(F), + {ok,F}; Error -> {Error,Ifile} end. @@ -1328,6 +1341,7 @@ inc_file_name(Filename) -> out_file(Ifile, Ofile, St, DFA, DF, Actions, Code, L) -> case io:get_line(Ifile, leex) of eof -> output_file_directive(Ofile, St#leex.ifile, L); + {error, _} -> add_error(St#leex.ifile, {L, leex, cannot_parse}, St); Line -> case substr(Line, 1, 5) of "##mod" -> out_module(Ofile, St); @@ -1347,14 +1361,23 @@ out_erlang_code(File, St, Code, L) -> output_file_directive(File, St#leex.xfile, CodeL), {ok,Xfile} = file:open(St#leex.xfile, [read]), try + set_encoding(St, Xfile), {ok,_} = file:position(Xfile, CodePos), - {ok,_} = file:copy(Xfile, File) + ok = file_copy(Xfile, File) after - file:close(Xfile) + ok = file:close(Xfile) end, io:nl(File), output_file_directive(File, St#leex.ifile, L). +file_copy(From, To) -> + case io:get_line(From, leex) of + eof -> ok; + Line when is_list(Line) -> + io:fwrite(To, "~ts", [Line]), + file_copy(From, To) + end. + out_dfa(File, St, DFA, Code, DF, L) -> {_CodeL,_CodePos,NCodeLines} = Code, %% Three file attributes before this one... @@ -1569,7 +1592,7 @@ out_dfa_graph(St, DFA, DF) -> io:fwrite(Gfile, "}~n", []), verbose_print(St, "ok~n", []), St - after file:close(Gfile) + after ok = file:close(Gfile) end; {error,Error} -> verbose_print(St, "error~n", []), @@ -1610,6 +1633,16 @@ dfa_edgelabel(Cranges) -> (C) -> [quote(C)] end, Cranges) ++ "]". +set_encoding(#leex{encoding = none}, File) -> + ok = io:setopts(File, [{encoding, epp:default_encoding()}]); +set_encoding(#leex{encoding = E}, File) -> + ok = io:setopts(File, [{encoding, E}]). + +output_encoding_comment(_File, #leex{encoding = none}) -> + ok; +output_encoding_comment(File, #leex{encoding = Encoding}) -> + io:fwrite(File, <<"%% ~s\n">>, [epp:encoding_to_string(Encoding)]). + output_file_directive(File, Filename, Line) -> io:fwrite(File, <<"-file(~s, ~w).\n">>, [format_filename(Filename), Line]). diff --git a/lib/parsetools/src/yecc.erl b/lib/parsetools/src/yecc.erl index b0792a6ed8..dbb7d025ae 100644 --- a/lib/parsetools/src/yecc.erl +++ b/lib/parsetools/src/yecc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -42,6 +42,7 @@ includefile, includefile_version, module, + encoding = none, options = [], verbose = false, file_attrs = true, @@ -224,7 +225,11 @@ format_error({unused_nonterminal, Nonterminal}) -> [format_symbol(Nonterminal)]); format_error({unused_terminal, Terminal}) -> io_lib:fwrite("terminal symbol ~s not used", - [format_symbol(Terminal)]). + [format_symbol(Terminal)]); +format_error({bad_symbol, String}) -> + io_lib:fwrite("bad symbol ~ts", [String]); +format_error(cannot_parse) -> + io_lib:fwrite("cannot parse; possibly encoding mismatch", []). file(File) -> file(File, [report_errors, report_warnings]). @@ -257,7 +262,7 @@ yecc(Infile, Outfile, Verbose) -> yecc(Infile, Outfile, Verbose, []). yecc(Infilex, Outfilex, Verbose, Includefilex) -> - statistics(runtime), + _ = statistics(runtime), case file(Infilex, [{parserfile, Outfilex}, {verbose, Verbose}, {report, true}, @@ -407,7 +412,9 @@ infile(Parent, Infilex, Options) -> St = case file:open(St0#yecc.infile, [read, read_ahead]) of {ok, Inport} -> try - outfile(St0#yecc{inport = Inport}) + Encoding = epp:set_encoding(Inport), + St1 = St0#yecc{inport = Inport, encoding = Encoding}, + outfile(St1) after ok = file:close(Inport) end; @@ -428,6 +435,8 @@ outfile(St0) -> case file:open(St0#yecc.outfile, [write, delayed_write]) of {ok, Outport} -> try + %% Set the same encoding as infile: + set_encoding(St0, Outport), generate(St0#yecc{outport = Outport, line = 1}) catch throw: St1 -> @@ -466,13 +475,14 @@ timeit(Name, Fun, St0) -> -define(PASS(P), {P, fun P/1}). generate(St0) -> + St1 = output_encoding_comment(St0), Passes = [?PASS(parse_grammar), ?PASS(check_grammar), ?PASS(states_and_goto_table), ?PASS(parse_actions), ?PASS(action_conflicts), ?PASS(write_file)], - F = case member(time, St0#yecc.options) of + F = case member(time, St1#yecc.options) of true -> io:fwrite(<<"Generating parser from grammar in ~s\n">>, - [format_filename(St0#yecc.infile)]), + [format_filename(St1#yecc.infile)]), fun timeit/3; false -> fun(_Name, Fn, St) -> Fn(St) end @@ -484,13 +494,13 @@ generate(St0) -> true -> throw(St2) end end, - foldl(Fun, St0, Passes). + foldl(Fun, St1, Passes). parse_grammar(St) -> parse_grammar(St#yecc.inport, 1, St). parse_grammar(Inport, Line, St) -> - {NextLine, Grammar} = read_grammar(Inport, Line), + {NextLine, Grammar} = read_grammar(Inport, St, Line), parse_grammar(Grammar, Inport, NextLine, St). parse_grammar(eof, _Inport, _NextLine, St) -> @@ -523,6 +533,8 @@ parse_grammar({rule, Rule, Tokens}, St0) -> St#yecc{rules_list = [RuleDef | St#yecc.rules_list]}; parse_grammar({prec, Prec}, St) -> St#yecc{prec = Prec ++ St#yecc.prec}; +parse_grammar({#symbol{}, [{string,Line,String}]}, St) -> + add_error(Line, {bad_symbol, String}, St); parse_grammar({#symbol{line = Line, name = Name}, Symbols}, St) -> CF = fun(I) -> case element(I, St) of @@ -543,12 +555,17 @@ parse_grammar({#symbol{line = Line, name = Name}, Symbols}, St) -> _ -> add_warning(Line, bad_declaration, St) end. -read_grammar(Inport, Line) -> +read_grammar(Inport, St, Line) -> case yeccscan:scan(Inport, '', Line) of {eof, NextLine} -> {NextLine, eof}; {error, {ErrorLine, Mod, What}, NextLine} -> {NextLine, {error, ErrorLine, {error, Mod, What}}}; + {error, terminated} -> + throw(St); + {error, _} -> + File = St#yecc.infile, + throw(add_error(File, none, cannot_parse, St)); {ok, Input, NextLine} -> {NextLine, case yeccparser:parse(Input) of {error, {ErrorLine, Mod, Message}} -> @@ -738,9 +755,9 @@ states_and_goto_table(St0) -> create_precedence_table(St). parse_actions(St) -> - erase(), % the pd is used when decoding lookahead sets + _ = erase(), % the pd is used when decoding lookahead sets ParseActions = compute_parse_actions(St#yecc.n_states, St, []), - erase(), + _ = erase(), St#yecc{parse_actions = ParseActions, state_tab = []}. action_conflicts(St0) -> @@ -841,10 +858,10 @@ report_errors(St) -> case member(report_errors, St#yecc.options) of true -> foreach(fun({File,{none,Mod,E}}) -> - io:fwrite(<<"~s: ~s\n">>, + io:fwrite(<<"~s: ~ts\n">>, [File,Mod:format_error(E)]); ({File,{Line,Mod,E}}) -> - io:fwrite(<<"~s:~w: ~s\n">>, + io:fwrite(<<"~s:~w: ~ts\n">>, [File,Line,Mod:format_error(E)]) end, sort(St#yecc.errors)); false -> @@ -861,11 +878,11 @@ report_warnings(St) -> case member(report_warnings, St#yecc.options) orelse ReportWerror of true -> foreach(fun({File,{none,Mod,W}}) -> - io:fwrite(<<"~s: ~s~s\n">>, + io:fwrite(<<"~s: ~s~ts\n">>, [File,Prefix, Mod:format_error(W)]); ({File,{Line,Mod,W}}) -> - io:fwrite(<<"~s:~w: ~s~s\n">>, + io:fwrite(<<"~s:~w: ~s~ts\n">>, [File,Line,Prefix, Mod:format_error(W)]) end, sort(St#yecc.warnings)); @@ -1024,7 +1041,7 @@ compute_states(St0) -> rp_info = RulePointerInfo, goto = GotoTab}, - erase(), + _ = erase(), EndsymCode = code_terminal(StC#yecc.endsymbol, StC#yecc.symbol_tab), {StateId, State0} = compute_state([{EndsymCode, 1}], Tables), @@ -1923,9 +1940,10 @@ output_prelude(Outport, Inport, St0) when St0#yecc.includefile =:= [] -> {St20, 0, no_erlang_code}; Next_line -> St_10 = output_file_directive(St20, Infile, Next_line-1), - Nmbr_of_lines = include1([], Inport, Outport), - {St_10, Nmbr_of_lines, - {last_erlang_code_line, Next_line+Nmbr_of_lines}} + Last_line = include1([], Inport, Outport, Infile, + Next_line, St_10), + Nmbr_of_lines = Last_line - Next_line, + {St_10, Nmbr_of_lines, {last_erlang_code_line, Last_line}} end, St30 = nl(St25), IncludeFile = @@ -1946,13 +1964,13 @@ output_prelude(Outport, Inport, St0) -> {St30, N_lines_1, no_erlang_code}; Next_line -> St = output_file_directive(St30, Infile, Next_line-1), - Nmbr_of_lines = include1([], Inport, Outport), - {St, Nmbr_of_lines + N_lines_1, - {last_erlang_code_line, Next_line+Nmbr_of_lines}} + Last_line = include1([], Inport, Outport, Infile, Next_line, St), + Nmbr_of_lines = Last_line - Next_line, + {St, Nmbr_of_lines + N_lines_1, {last_erlang_code_line, Last_line}} end. output_header(St0) -> - lists:foldl(fun(Str, St) -> fwrite(St, <<"~s\n">>, [Str]) + lists:foldl(fun(Str, St) -> fwrite(St, <<"~ts\n">>, [Str]) end, St0, St0#yecc.header). output_goto(St, [{_Nonterminal, []} | Go], StateInfo) -> @@ -2250,8 +2268,8 @@ output_inlined(St0, FunctionName, Reduce, Infile) -> [append(["[", tl(A), " | __Stack]"])]) end, St = St40#yecc{line = St40#yecc.line + NLines}, - fwrite(St, <<" [begin\n ~s\n end | ~s].\n\n">>, - [pp_tokens(Tokens, Line0), Stack]). + fwrite(St, <<" [begin\n ~ts\n end | ~s].\n\n">>, + [pp_tokens(Tokens, Line0, St#yecc.encoding), Stack]). inlined_function_name(State, "Cat") -> inlined_function_name(State, ""); @@ -2421,24 +2439,24 @@ include(St, File, Outport) -> {error, Reason} -> throw(add_error(File, none, {file_error, Reason}, St)); {ok, Inport} -> + _ = epp:set_encoding(Inport), Line = io:get_line(Inport, ''), - N_lines = include1(Line, Inport, Outport), - file:close(Inport), - N_lines + try include1(Line, Inport, Outport, File, 1, St) - 1 + after ok = file:close(Inport) + end end. -include1(Line, Inport, Outport) -> - include1(Line, Inport, Outport, 0). - -include1(eof, _, _, Nmbr_of_lines) -> - Nmbr_of_lines; -include1(Line, Inport, Outport, Nmbr_of_lines) -> +include1(eof, _, _, _File, L, _St) -> + L; +include1({error, _}=_Error, _Inport, _Outport, File, L, St) -> + throw(add_error(File, L, cannot_parse, St)); +include1(Line, Inport, Outport, File, L, St) -> Incr = case member($\n, Line) of true -> 1; false -> 0 end, io:put_chars(Outport, Line), - include1(io:get_line(Inport, ''), Inport, Outport, Nmbr_of_lines + Incr). + include1(io:get_line(Inport, ''), Inport, Outport, File, L + Incr, St). includefile_version([]) -> {1,4}; @@ -2465,18 +2483,22 @@ parse_file(Epp) -> end. %% Keeps the line breaks of the original code. -pp_tokens(Tokens, Line0) -> - concat(pp_tokens1(Tokens, Line0, [])). +pp_tokens(Tokens, Line0, Enc) -> + concat(pp_tokens1(Tokens, Line0, Enc, [])). -pp_tokens1([], _Line0, _T0) -> +pp_tokens1([], _Line0, _Enc, _T0) -> []; -pp_tokens1([T | Ts], Line0, T0) -> +pp_tokens1([T | Ts], Line0, Enc, T0) -> Line = element(2, T), - [pp_sep(Line, Line0, T0), pp_symbol(T) | pp_tokens1(Ts, Line, T)]. + [pp_sep(Line, Line0, T0), pp_symbol(T, Enc)|pp_tokens1(Ts, Line, Enc, T)]. -pp_symbol({var,_,Var}) -> Var; -pp_symbol({_,_,Symbol}) -> io_lib:fwrite(<<"~p">>, [Symbol]); -pp_symbol({Symbol, _}) -> Symbol. +pp_symbol({var,_,Var}, _Enc) -> Var; +pp_symbol({string,_,String}, latin1) -> + io_lib:write_unicode_string_as_latin1(String); +pp_symbol({string,_,String}, _Enc) -> io_lib:write_unicode_string(String); +pp_symbol({_,_,Symbol}, latin1) -> io_lib:fwrite(<<"~p">>, [Symbol]); +pp_symbol({_,_,Symbol}, _Enc) -> io_lib:fwrite(<<"~tp">>, [Symbol]); +pp_symbol({Symbol, _}, _Enc) -> Symbol. pp_sep(Line, Line0, T0) when Line > Line0 -> ["\n " | pp_sep(Line - 1, Line0, T0)]; @@ -2485,6 +2507,16 @@ pp_sep(_Line, _Line0, {'.',_}) -> pp_sep(_Line, _Line0, _T0) -> " ". +set_encoding(#yecc{encoding = none}, Port) -> + ok = io:setopts(Port, [{encoding, epp:default_encoding()}]); +set_encoding(#yecc{encoding = E}, Port) -> + ok = io:setopts(Port, [{encoding, E}]). + +output_encoding_comment(#yecc{encoding = none}=St) -> + St; +output_encoding_comment(#yecc{encoding = Encoding}=St) -> + fwrite(St, <<"%% ~s\n">>, [epp:encoding_to_string(Encoding)]). + output_file_directive(St, Filename, Line) when St#yecc.file_attrs -> fwrite(St, <<"-file(~s, ~w).\n">>, [format_filename(Filename), Line]); @@ -2529,7 +2561,7 @@ format_assoc(nonassoc) -> format_symbol(Symbol) -> String = concat([Symbol]), - case erl_scan:string(String) of + case erl_scan:string(String, 1, [unicode]) of {ok, [{atom, _, _}], _} -> io_lib:fwrite(<<"~w">>, [Symbol]); {ok, [{Word, _}], _} when Word =/= ':', Word =/= '->' -> diff --git a/lib/parsetools/src/yeccscan.erl b/lib/parsetools/src/yeccscan.erl index d7ec3ba8d3..9e0e85143a 100644 --- a/lib/parsetools/src/yeccscan.erl +++ b/lib/parsetools/src/yeccscan.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -24,7 +24,7 @@ scan(Inport) -> scan(Inport, '', 1). scan(Inport, Prompt, Line1) -> - case catch io:scan_erl_form(Inport, Prompt, Line1) of + case catch io:scan_erl_form(Inport, Prompt, Line1, [unicode]) of {eof, Line2} -> {eof, Line2}; {ok, Tokens, Line2} -> @@ -34,6 +34,8 @@ scan(Inport, Prompt, Line1) -> _ -> {ok, lex(Tokens), Line2} end; + {error, Reason} -> + {error, Reason}; {error, Descriptor, Line2} -> {error, Descriptor, Line2}; {'EXIT', Why} -> diff --git a/lib/parsetools/test/leex_SUITE.erl b/lib/parsetools/test/leex_SUITE.erl index 1e50aedf07..4a0f70ba71 100644 --- a/lib/parsetools/test/leex_SUITE.erl +++ b/lib/parsetools/test/leex_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: latin-1 -*- %% %% %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 @@ -42,7 +43,9 @@ -export([ file/1, compile/1, syntax/1, - pt/1, man/1, ex/1, ex2/1, not_yet/1]). + pt/1, man/1, ex/1, ex2/1, not_yet/1, + + otp_10302/1]). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(1)). @@ -63,7 +66,8 @@ all() -> groups() -> [{checks, [], [file, compile, syntax]}, - {examples, [], [pt, man, ex, ex2, not_yet]}]. + {examples, [], [pt, man, ex, ex2, not_yet]}, + {tickets, [], [otp_10302]}]. init_per_suite(Config) -> Config. @@ -875,6 +879,111 @@ not_yet(Config) when is_list(Config) -> ok. +otp_10302(doc) -> + "OTP-10302. Unicode characters scanner/parser."; +otp_10302(suite) -> []; +otp_10302(Config) when is_list(Config) -> + Dir = ?privdir, + Filename = filename:join(Dir, "file.xrl"), + Ret = [return, {report, true}], + + ok = file:write_file(Filename,<< + "%% coding: UTF-8\n" + "�" + >>), + {error,[{_,[{2,leex,cannot_parse}]}],[]} = + leex:file(Filename, Ret), + + ok = file:write_file(Filename,<< + "%% coding: UTF-8\n" + "Definitions.\n" + "�" + >>), + {error,[{_,[{3,leex,cannot_parse}]}],[]} = leex:file(Filename, Ret), + + ok = file:write_file(Filename,<< + "%% coding: UTF-8\n" + "Definitions.\n" + "A = a\n" + "L = [{A}-{Z}]\n" + "Z = z\n" + "Rules.\n" + "{L}+ : {token,{list_to_atom(TokenChars),H�pp}}.\n" + >>), + {error,[{_,[{7,leex,cannot_parse}]}],[]} = leex:file(Filename, Ret), + + ok = file:write_file(Filename,<< + "%% coding: UTF-8\n" + "Definitions.\n" + "A = a\n" + "L = [{A}-{Z}]\n" + "Z = z\n" + "Rules.\n" + "{L}+ : {token,{list_to_atom(TokenChars)}}.\n" + "Erlang code.\n" + "-export([t/0]).\n" + "t() ->\n" + " H�pp\n" + >>), + {error,[{_,[{11,leex,cannot_parse}]}],[]} = leex:file(Filename, Ret), + + Mini = <<"Definitions.\n" + "D = [0-9]\n" + "Rules.\n" + "{L}+ : {token,{word,TokenLine,TokenChars}}.\n" + "Erlang code.\n">>, + LeexPre = filename:join(Dir, "leexinc.hrl"), + ?line ok = file:write_file(LeexPre, <<"%% coding: UTF-8\n �">>), + PreErrors = run_test(Config, Mini, LeexPre), + {error,[{IncludeFile,[{2,leex,cannot_parse}]}],[]} = PreErrors, + "leexinc.hrl" = filename:basename(IncludeFile), + + Ts = [{uni_1, + <<"%% coding: UTF-8\n" + "Definitions.\n" + "A = a\n" + "L = [{A}-{Z}]\n" + "Z = z\n" + "Rules.\n" + "{L}+ : {token,{list_to_atom(TokenChars),\n" + "begin Häpp = foo, Häpp end," + " 'Häpp',\"\\x{400}B\",\"örn_Ѐ\"}}.\n" + "Erlang code.\n" + "-export([t/0]).\n" + "t() ->\n" + " %% Häpp, 'Häpp',\"\\x{400}B\",\"örn_Ѐ\"\n" + " {ok, [R], 1} = string(\"tip\"),\n" + " {tip,foo,'Häpp',[1024,66],[246,114,110,95,1024]} = R,\n" + " Häpp = foo,\n" + " {tip, Häpp, 'Häpp',\"\\x{400}B\",\"örn_Ѐ\"} = R,\n" + " ok.\n">>, + default, + ok}, + {uni_2, + <<"%% coding: Latin-1\n" + "Definitions.\n" + "A = a\n" + "L = [{A}-{Z}]\n" + "Z = z\n" + "Rules.\n" + "{L}+ : {token,{list_to_atom(TokenChars),\n" + "begin H�pp = foo, H�pp end," + " 'H�pp',\"\\x{400}B\",\"örn_Ѐ\"}}.\n" + "Erlang code.\n" + "-export([t/0]).\n" + "t() ->\n" + " %% H�pp, 'H�pp',\"\\x{400}B\",\"örn_Ѐ\"\n" + " {ok, [R], 1} = string(\"tip\"),\n" + " {tip,foo,'H�pp',[1024,66],[195,182,114,110,95,208,128]} = R,\n" + " H�pp = foo,\n" + " {tip, H�pp, 'H�pp',\"\\x{400}B\",\"örn_Ѐ\"} = R,\n" + " ok.\n">>, + default, + ok}], + run(Config, Ts), + + ok. + unwritable(Fname) -> {ok, Info} = file:read_file_info(Fname), Mode = Info#file_info.mode - 8#00200, diff --git a/lib/parsetools/test/yecc_SUITE.erl b/lib/parsetools/test/yecc_SUITE.erl index 3d26adf1be..c306dbe833 100644 --- a/lib/parsetools/test/yecc_SUITE.erl +++ b/lib/parsetools/test/yecc_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: latin-1 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -48,7 +49,7 @@ otp_5369/1, otp_6362/1, otp_7945/1, otp_8483/1, otp_8486/1, - otp_7292/1, otp_7969/1, otp_8919/1]). + otp_7292/1, otp_7969/1, otp_8919/1, otp_10302/1]). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(1)). @@ -75,7 +76,7 @@ groups() -> [empty, prec, yeccpre, lalr, old_yecc, other_examples]}, {bugs, [], [otp_5369, otp_6362, otp_7945, otp_8483, otp_8486]}, - {improvements, [], [otp_7292, otp_7969, otp_8919]}]. + {improvements, [], [otp_7292, otp_7969, otp_8919, otp_10302]}]. init_per_suite(Config) -> Config. @@ -1815,6 +1816,153 @@ otp_8919(Config) when is_list(Config) -> "syntax error before: \"hello\"" = lists:flatten(Mod:format_error(Mess)), ok. +otp_10302(doc) -> + "OTP-10302. Unicode characters scanner/parser."; +otp_10302(suite) -> []; +otp_10302(Config) when is_list(Config) -> + Dir = ?privdir, + Filename = filename:join(Dir, "OTP-10302.yrl"), + Ret = [return, {report, true}], + Mini1 = <<"%% coding: utf-8 + Nonterminals H�pp. + nt -> t.">>, + ok = file:write_file(Filename, Mini1), + %% This could (and should) be refined: + {error,[{Filename,[{2,Mod1,Err1}]}],[]} = + yecc:file(Filename, Ret), + "cannot translate from UTF-8" = Mod1:format_error(Err1), + + Mini2 = <<"%% coding: Utf-8 + Nonterminals Hopp. + Terminals t. + Rootsymbol Hopp. + + Hopp -> t. + + Erlang code. + + t() -> + H�pp.">>, + ok = file:write_file(Filename, Mini2), + {error,[{Filename,[{11,Mod2,Err2}]}],[]} = + yecc:file(Filename, Ret), + "cannot parse; possibly encoding mismatch" = Mod2:format_error(Err2), + + Mini3 = <<"%% coding: latin-1 + Nonterminals Hopp. + Terminals t. + Rootsymbol Hopp. + + Hopp -> t. + + Erlang code. + + t() -> + H�pp.">>, + ok = file:write_file(Filename, Mini3), + YeccPre = filename:join(Dir, "yeccpre.hrl"), + ok = file:write_file(YeccPre, [<<"%% coding: UTF-8\n �.\n">>]), + Inc = [{includefile,YeccPre}], + {error,[{_,[{2,yecc,cannot_parse}]}],[]} = + yecc:file(Filename, Inc ++ Ret), + + ok = file:write_file(Filename, + <<"%% coding: UTF-8 + Nonterminals Hopp. + Terminals t. + Rootsymbol \"örn_Ѐ\". + Hopp -> t : '$1'.">>), + {error,[{Filename,[{4,yecc,{bad_symbol,"�rn_"++[1024]}}]}],[]} = + yecc:file(Filename, Ret), + + ok = file:write_file(Filename, + <<"%% coding: UTF-8 + Nonterminals Hopp. + Terminals t. + Rootsymbol Hopp. + Endsymbol \"örn_Ѐ\". + Hopp -> t : '$1'.">>), + {error,[{Filename,[{5,yecc,{bad_symbol,"�rn_"++[1024]}}]}],[]} = + yecc:file(Filename, Ret), + + ok = file:write_file(Filename, + <<"%% coding: UTF-8 + Nonterminals Hopp. + Terminals t. + Rootsymbol Hopp. + Expect \"örn_Ѐ\". + Hopp -> t : '$1'.">>), + {error,[{Filename,[{5,yecc,{bad_symbol,"�rn_"++[1024]}}]}],[]} = + yecc:file(Filename, Ret), + + ok = file:write_file(Filename, + <<"%% coding: UTF-8 + Nonterminals Hopp. + Terminals t. + Rootsymbol Hopp. + States \"örn_Ѐ\". + Hopp -> t : '$1'.">>), + {error,[{Filename,[{5,yecc,{bad_symbol,"�rn_"++[1024]}}]}],[]} = + yecc:file(Filename, Ret), + + Ts = [{otp_10302_1,<<" + %% coding: UTF-8 + Header \"%% örn_Ѐ\" \"%% \\x{400}B\". + Nonterminals Häpp list. + Terminals element. + Rootsymbol Häpp. + + Häpp -> list : '$1'. + + list -> element : '$1'. + list -> list element : + begin + Häpp = foo, + {Häpp, 'Häpp',\"\\x{400}B\",\"örn_Ѐ\"} + end. + + Erlang code. + + -export([t/0]). + + t() -> + L = [{element, 1}, {element,2}], + {ok, R} = parse(L), + Häpp = foo, + {_,_,[1024,66],[246,114,110,95,1024]} = R, + {Häpp,'Häpp',\"\\x{400}B\",\"örn_Ѐ\"} = R, + ok. + ">>,default,ok}, + {otp_10302_2,<<" + %% coding: Latin-1 + Nonterminals H�pp list. + Terminals element. + Rootsymbol H�pp. + + H�pp -> list : '$1'. + + list -> element : '$1'. + list -> list element : + begin + H�pp = foo, + {H�pp, 'H�pp',\"\\x{400}B\",\"örn_Ѐ\"} + end. + + Erlang code. + + -export([t/0]). + + t() -> + L = [{element, 1}, {element,2}], + {ok, R} = parse(L), + H�pp = foo, + {_,_,[1024,66],[195,182,114,110,95,208,128]} = R, + {H�pp,'H�pp',\"\\x{400}B\",\"örn_Ѐ\"} = R, + ok. + ">>,default,ok}], + run(Config, Ts), + ok. + yeccpre_size() -> yeccpre_size(default_yeccpre()). diff --git a/lib/public_key/doc/src/cert_records.xml b/lib/public_key/doc/src/cert_records.xml index 94c7c46350..b1d2a05200 100644 --- a/lib/public_key/doc/src/cert_records.xml +++ b/lib/public_key/doc/src/cert_records.xml @@ -137,7 +137,7 @@ issuer, % {rdnSequence, [#AttributeTypeAndValue'{}]} validity, % #'Validity'{} subject, % {rdnSequence, [#AttributeTypeAndValue'{}]} - subjectPublicKeyInfo, % #'SubjectPublicKeyInfo'{} + subjectPublicKeyInfo, % #'OTPSubjectPublicKeyInfo'{} issuerUniqueID, % binary() | asn1_novalue subjectUniqueID, % binary() | asn1_novalue extensions % [#'Extension'{}] diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index 2ec1fcff9d..b240d53571 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2008</year> - <year>2012</year> + <year>2013</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -60,8 +60,9 @@ <p><code>string = [bytes()]</code></p> - <p><code>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey' - 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo'| 'PrivateKeyInfo' | 'CertificationRequest'</code></p> + <p><code>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey' | + 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo' | + 'PrivateKeyInfo' | 'CertificationRequest'</code></p> <p><code>pem_entry () = {pki_asn1_type(), binary(), %% DER or encrypted DER not_encrypted | cipher_info()} </code></p> diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml index fbe29753be..8437b7a623 100644 --- a/lib/reltool/doc/src/reltool.xml +++ b/lib/reltool/doc/src/reltool.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2009</year> - <year>2012</year> + <year>2013</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -274,7 +274,8 @@ </taglist> <p>Example:</p> - <p><code>erl -sasl releases_dir \"mytarget/releases\" -boot mytarget/releases/1.0/myrel -boot_var RELTOOL_EXT_LIB mytarget/lib</code></p> + <p><code>erl -sasl releases_dir \"mytarget/releases\" -boot mytarget/releases/1.0/myrel\ + -boot_var RELTOOL_EXT_LIB mytarget/lib</code></p> </item> <tag><c>incl_sys_filters</c></tag> diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl index 6cb7ba0163..1f4ce7226a 100644 --- a/lib/reltool/src/reltool_target.erl +++ b/lib/reltool/src/reltool_target.erl @@ -482,33 +482,12 @@ do_gen_script(#rel{name = RelName, vsn = RelVsn}, %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -load_app_mods(#app{mods = Mods} = App, Mand, PathFlag, Variables) -> +load_app_mods(#app{mods = Mods0} = App, Mand, PathFlag, Variables) -> Path = cr_path(App, PathFlag, Variables), - PartNames = - lists:sort([{packages:split(M),M} || - #mod{name = M, is_included=true} <- Mods, - not lists:member(M, Mand)]), - SplitMods = - lists:foldl( - fun({Parts,M}, [{Last, Acc}|Rest]) -> - [_|Tail] = lists:reverse(Parts), - case lists:reverse(Tail) of - Subs when Subs == Last -> - [{Last,[M|Acc]}|Rest]; - Subs -> - [{Subs, [M]}|[{Last,Acc}|Rest]] - end - end, - [{[], - []}], - PartNames), - lists:foldl( - fun({Subs,Ms}, Cmds) -> - [{path, [filename:join([Path | Subs])]}, - {primLoad, lists:sort(Ms)} | Cmds] - end, - [], - SplitMods). + Mods = [M || #mod{name = M, is_included=true} <- Mods0, + not lists:member(M, Mand)], + [{path, [filename:join([Path])]}, + {primLoad, lists:sort(Mods)}]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sort_used_and_incl_apps(Apps, OrderedApps) -> Apps diff --git a/lib/runtime_tools/doc/src/dbg.xml b/lib/runtime_tools/doc/src/dbg.xml index c7c5cd4ff0..d8c82b2459 100644 --- a/lib/runtime_tools/doc/src/dbg.xml +++ b/lib/runtime_tools/doc/src/dbg.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -101,7 +101,8 @@ allowed:</p> <pre> 4> <input>dbg:fun2ms(fun([M,N]) when N > X, is_atomm(M) -> return_trace() end).</input> -Error: fun containing local erlang function calls ('is_atomm' called in guard) cannot be translated into match_spec +Error: fun containing local erlang function calls ('is_atomm' called in guard)\ + cannot be translated into match_spec {error,transform_error} 5> <input>dbg:fun2ms(fun([M,N]) when N > X, is_atom(M) -> return_trace() end).</input> [{['$1','$2'],[{'>','$2',{const,3}},{is_atom,'$1'}],[{return_trace}]}]</pre> diff --git a/lib/runtime_tools/src/observer_backend.erl b/lib/runtime_tools/src/observer_backend.erl index 01e99f3f5e..9498412505 100644 --- a/lib/runtime_tools/src/observer_backend.erl +++ b/lib/runtime_tools/src/observer_backend.erl @@ -83,7 +83,7 @@ get_table2(Parent, Table, Type) -> ets -> ets:info(Table, size); mnesia -> mnesia:table_info(Table, size) end, - case Size > 0 of + case Size =/= undefined andalso Size > 0 of false -> Parent ! {self(), '$end_of_table'}, normal; diff --git a/lib/runtime_tools/src/runtime_tools_sup.erl b/lib/runtime_tools/src/runtime_tools_sup.erl index c8dab250dd..264e172a3c 100644 --- a/lib/runtime_tools/src/runtime_tools_sup.erl +++ b/lib/runtime_tools/src/runtime_tools_sup.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -18,7 +19,7 @@ %% %% ------------------------------------------------------------------------------ %% File : runtime_tools_sup.erl -%% Author : Lennart �hman <[email protected]> +%% Author : Lennart Öhman <[email protected]> -module(runtime_tools_sup). -behaviour(supervisor). diff --git a/lib/sasl/src/overload.erl b/lib/sasl/src/overload.erl index 5a4782efff..97f7bebe00 100644 --- a/lib/sasl/src/overload.erl +++ b/lib/sasl/src/overload.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -25,7 +26,7 @@ format_status/2]). %%%----------------------------------------------------------------- -%%% This is a rewrite of overload from BS.3, by Peter H�gfeldt. +%%% This is a rewrite of overload from BS.3, by Peter Högfeldt. %%% %%% DESCRIPTION %%% diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl index 29c4a0d168..9b2e2c809b 100644 --- a/lib/sasl/src/systools_make.erl +++ b/lib/sasl/src/systools_make.erl @@ -1067,11 +1067,10 @@ check_mod(Mod,App,Dir,Ext,IncPath) -> end. mod_to_filename(Dir, Mod, Ext) -> - Parts = packages:split(Mod), - filename:join([Dir | Parts]) ++ Ext. + filename:join(Dir, atom_to_list(Mod) ++ Ext). check_module(Mod, Dir, ObjModTime, IncPath) -> - {SrcDirs,_IncDirs}= smart_guess(Mod, Dir,IncPath), + {SrcDirs,_IncDirs}= smart_guess(Dir,IncPath), case locate_src(Mod,SrcDirs) of {ok,_FDir,_File,LastModTime} -> if @@ -1085,7 +1084,7 @@ check_module(Mod, Dir, ObjModTime, IncPath) -> end. locate_src(Mod,[Dir|Dirs]) -> - File = filename:join(Dir, mod_to_fname(Mod) ++ ".erl"), + File = mod_to_filename(Dir, Mod, ".erl"), case file:read_file_info(File) of {ok,FileInfo} -> LastModTime = FileInfo#file_info.mtime, @@ -1096,9 +1095,6 @@ locate_src(Mod,[Dir|Dirs]) -> locate_src(_,[]) -> false. -mod_to_fname(Mod) -> - hd(lists:reverse(packages:split(Mod))). - %%______________________________________________________________________ %% smart_guess(Mod, Dir,IncludePath) -> {[Dirs],[IncDirs]} @@ -1106,17 +1102,12 @@ mod_to_fname(Mod) -> %% src-dir should be one of .../src or .../src/e_src %% If dir does not contain .../ebin set dir to the same directory. -smart_guess(Mod, Dir,IncPath) -> +smart_guess(Dir,IncPath) -> case reverse(filename:split(Dir)) of ["ebin"|D] -> - Subdirs = case packages:split(Mod) of - [_] -> []; - [_|_] = Parts -> - lists:reverse(tl(lists:reverse(Parts))) - end, D1 = reverse(D), - Dirs = [filename:join(D1 ++ ["src" | Subdirs]), - filename:join(D1 ++ ["src", "e_src" | Subdirs])], + Dirs = [filename:join(D1 ++ ["src"]), + filename:join(D1 ++ ["src", "e_src"])], {Dirs,Dirs ++ IncPath}; _ -> {[Dir],[Dir] ++ IncPath} @@ -1423,23 +1414,8 @@ load_appl_mods([], _, _, _) -> [{progress, modules_loaded}]. load_commands(Mods, Path) -> - SplitMods = lists:foldl( - fun({Parts,M}, [{Last, Acc}|Rest]) -> - [_|Tail] = lists:reverse(Parts), - case lists:reverse(Tail) of - Subs when Subs == Last -> - [{Last,[M|Acc]}|Rest]; - Subs -> - [{Subs, [M]}|[{Last,Acc}|Rest]] - end - end, [{[],[]}], - lists:sort([{packages:split(M),M} || M <- Mods])), - lists:foldl( - fun({Subs,Ms}, Cmds) -> - [{path, [filename:join([Path | Subs])]}, - {primLoad,lists:sort(Ms)} | Cmds] - end, [], SplitMods). - + [{path, [filename:join([Path])]}, + {primLoad,lists:sort(Mods)}]. %%______________________________________________________________________ %% Pack an application to an application term. diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index 442837d57d..b6b8751f6c 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -126,6 +126,70 @@ <section> + <title>SNMP Development Toolkit 4.23</title> + <p>Version 4.23 supports code replacement in runtime from/to + version 4.22, + 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and 4.21. </p> + + <section> + <title>Improvements and new features</title> + <p>-</p> + +<!-- + <list type="bulleted"> + <item> + <p>[agent] Documenting previously existing but undocumented function, + <seealso marker="snmp_generic#get_table_info">snmp_generic:get_table_info/2</seealso>. </p> + <p>Own Id: OTP-9942</p> + </item> + + </list> +--> + + </section> + + <section> + <title>Fixed Bugs and Malfunctions</title> + <p>-</p> + + <!-- + <list type="bulleted"> + <item> + <p>[agent] Simultaneous + <seealso marker="snmpa#backup">snmpa:backup/1,2</seealso> + calls can interfere. + The master agent did not check if a backup was already in + progress when a backup request was accepted. </p> + <p>Own Id: OTP-9884</p> + <p>Aux Id: Seq 11995</p> + </item> + + </list> + --> + + </section> + + <section> + <title>Incompatibilities</title> +<!-- + <p>-</p> +--> + + <list type="bulleted"> + <item> + <p>[manager] The old Addr-and-Port based API functions, previously + long deprecated and marked for deletion in R16B, has now been + removed. </p> + <p>Own Id: OTP-10027</p> + </item> + + </list> + </section> + + </section> <!-- 4.23 --> + + + <section> <title>SNMP Development Toolkit 4.22</title> <p>Version 4.22 supports code replacement in runtime from/to version 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and 4.21. </p> diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml index e6e188d5d5..07fdd208ff 100644 --- a/lib/snmp/doc/src/snmpm.xml +++ b/lib/snmp/doc/src/snmpm.xml @@ -279,7 +279,6 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv <fsummary>Register this agent</fsummary> <type> <v>UserId = term()</v> - <v>Addr = ip_address()</v> <v>TargetName = target_name()</v> <v>Config = [agent_config()]</v> <v>agent_config() = {Item, Val}</v> diff --git a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl index 436f15eb9c..ad9540e886 100644 --- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl +++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl @@ -496,7 +496,7 @@ verify_vacmSecurityToGroupTable_col(_, Val) -> %% %%----------------------------------------------------------------- vacmAccessTable(print) -> - %% M�ste jag g�ra om alla entrien till {RowIdx, Row}? + %% Do I need to turn all entries into {RowIdx, Row}? TableInfo = get_table(vacmAccessTable), PrintRow = fun(Prefix, Row) -> diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index f590892c66..6ac0115dad 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -107,63 +107,9 @@ async_get_bulk/5, async_get_bulk/6, async_get_bulk/7, async_get_bulk/8 ]). -%% Backward compatibility exports (API version "1") --deprecated({agent_info, 3}). --deprecated({update_agent_info, 5}). --deprecated({g, 3}). --deprecated({g, 4}). --deprecated({g, 5}). --deprecated({g, 6}). --deprecated({g, 7}). --deprecated({ag, 3}). --deprecated({ag, 4}). --deprecated({ag, 5}). --deprecated({ag, 6}). --deprecated({ag, 7}). --deprecated({gn, 3}). --deprecated({gn, 4}). --deprecated({gn, 5}). --deprecated({gn, 6}). --deprecated({gn, 7}). --deprecated({agn, 3}). --deprecated({agn, 4}). --deprecated({agn, 5}). --deprecated({agn, 6}). --deprecated({agn, 7}). --deprecated({gb, 5}). --deprecated({gb, 6}). --deprecated({gb, 7}). --deprecated({gb, 8}). --deprecated({gb, 9}). --deprecated({agb, 5}). --deprecated({agb, 6}). --deprecated({agb, 7}). --deprecated({agb, 8}). --deprecated({agb, 9}). --deprecated({s, 3}). --deprecated({s, 4}). --deprecated({s, 5}). --deprecated({s, 6}). --deprecated({s, 7}). --deprecated({as, 3}). --deprecated({as, 4}). --deprecated({as, 5}). --deprecated({as, 6}). --deprecated({as, 7}). --export([ - agent_info/3, update_agent_info/5, - g/3, g/4, g/5, g/6, g/7, - ag/3, ag/4, ag/5, ag/6, ag/7, - gn/3, gn/4, gn/5, gn/6, gn/7, - agn/3, agn/4, agn/5, agn/6, agn/7, - gb/5, gb/6, gb/7, gb/8, gb/9, - agb/5, agb/6, agb/7, agb/8, agb/9, - s/3, s/4, s/5, s/6, s/7, - as/3, as/4, as/5, as/6, as/7 - ]). - %% Application internal export -export([start_link/3, snmpm_start_verify/2, snmpm_start_verify/3]). +-export([target_name/1, target_name/2]). -include_lib("snmp/src/misc/snmp_debug.hrl"). @@ -439,32 +385,16 @@ unregister_agent(UserId, Addr, Port) -> Error end. + agent_info(TargetName, Item) -> snmpm_config:agent_info(TargetName, Item). -%% Backward compatibility -agent_info(Addr, Port, Item) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - agent_info(TargetName, Item); - Error -> - Error - end. - update_agent_info(UserId, TargetName, Info) when is_list(Info) -> snmpm_config:update_agent_info(UserId, TargetName, Info). update_agent_info(UserId, TargetName, Item, Val) -> update_agent_info(UserId, TargetName, [{Item, Val}]). -%% Backward compatibility functions -update_agent_info(UserId, Addr, Port, Item, Val) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - update_agent_info(UserId, TargetName, Item, Val); - Error -> - Error - end. which_agents() -> snmpm_config:which_agents(). @@ -552,55 +482,6 @@ sync_get(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) -> %% </BACKWARD-COMPAT> -%% <DEPRECATED> -g(UserId, Addr, Oids) -> - g(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids). - -g(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) -> - g(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids); - -g(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) -> - g(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids); - -g(UserId, Addr, Oids, Timeout) - when is_list(Oids) andalso is_integer(Timeout) -> - g(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids, Timeout). - -g(UserId, Addr, Port, CtxName, Oids) - when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - sync_get(UserId, TargetName, CtxName, Oids); - Error -> - Error - end; - -g(UserId, Addr, Port, Oids, Timeout) - when is_integer(Port) andalso is_list(Oids) andalso is_integer(Timeout) -> - g(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Timeout); - -g(UserId, Addr, CtxName, Oids, Timeout) - when is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> - g(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Timeout). - -g(UserId, Addr, Port, CtxName, Oids, Timeout) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - sync_get(UserId, TargetName, CtxName, Oids, Timeout); - Error -> - Error - end. - -g(UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - sync_get(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo); - Error -> - Error - end. -%% </DEPRECATED> - - %% --- asynchroneous get-request --- %% @@ -637,55 +518,6 @@ async_get(UserId, TargetName, Context, Oids, Expire, ExtraInfo) -> %% </BACKWARD-COMPAT> -%% <DEPRECATED> -ag(UserId, Addr, Oids) -> - ag(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids). - -ag(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) -> - ag(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids); - -ag(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) -> - ag(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids); - -ag(UserId, Addr, Oids, Expire) when is_list(Oids) andalso is_integer(Expire) -> - ag(UserId, Addr, ?DEFAULT_AGENT_PORT, ?DEFAULT_CONTEXT, Oids, Expire). - -ag(UserId, Addr, Port, CtxName, Oids) - when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - async_get(UserId, TargetName, CtxName, Oids); - Error -> - Error - end; - -ag(UserId, Addr, Port, Oids, Expire) - when is_integer(Port) andalso is_list(Oids) andalso is_integer(Expire) -> - ag(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Expire); - -ag(UserId, Addr, CtxName, Oids, Expire) - when is_list(CtxName) andalso is_list(Oids) andalso is_integer(Expire) -> - ag(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Expire). - -ag(UserId, Addr, Port, CtxName, Oids, Expire) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - async_get(UserId, TargetName, CtxName, Oids, Expire); - Error -> - Error - end. - -ag(UserId, Addr, Port, CtxName, Oids, Expire, ExtraInfo) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - async_get(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo); - Error -> - Error - end. -%% </DEPRECATED> - - - %% --- synchroneous get_next-request --- %% @@ -719,55 +551,6 @@ sync_get_next(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) -> %% </BACKWARD-COMPAT> -%% <DEPRECATED> -gn(UserId, Addr, Oids) -> - gn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids). - -gn(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) -> - gn(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids); - -gn(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) -> - gn(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids); - -gn(UserId, Addr, Oids, Timeout) - when is_list(Oids) andalso is_integer(Timeout) -> - gn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids, Timeout). - -gn(UserId, Addr, Port, CtxName, Oids) - when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - sync_get_next(UserId, TargetName, CtxName, Oids); - Error -> - Error - end; - -gn(UserId, Addr, Port, Oids, Timeout) - when is_integer(Port) andalso is_list(Oids) andalso is_integer(Timeout) -> - gn(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Timeout); -gn(UserId, Addr, CtxName, Oids, Timeout) - when is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> - gn(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Timeout). - -gn(UserId, Addr, Port, CtxName, Oids, Timeout) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - sync_get_next(UserId, TargetName, CtxName, Oids, Timeout); - Error -> - Error - end. - -gn(UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo); - Error -> - Error - end. -%% </DEPRECATED> - - - %% --- asynchroneous get_next-request --- %% @@ -801,56 +584,6 @@ async_get_next(UserId, TargetName, Context, Oids, Expire, ExtraInfo) -> %% </BACKWARD-COMPAT> -%% <DEPRECATED> -agn(UserId, Addr, Oids) -> - agn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids). - -agn(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) -> - agn(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids); - -agn(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) -> - agn(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids); - -agn(UserId, Addr, Oids, Expire) - when is_list(Oids) andalso is_integer(Expire) -> - agn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids, Expire). - -agn(UserId, Addr, Port, CtxName, Oids) - when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - async_get_next(UserId, TargetName, CtxName, Oids); - Error -> - Error - end; - -agn(UserId, Addr, Port, Oids, Expire) - when is_integer(Port) andalso is_list(Oids) andalso is_integer(Expire) -> - agn(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Expire); -agn(UserId, Addr, CtxName, Oids, Expire) - when is_list(CtxName) andalso is_list(CtxName) andalso is_integer(Expire) -> - agn(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Expire). - -agn(UserId, Addr, Port, CtxName, Oids, Expire) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - async_get_next(UserId, TargetName, CtxName, Oids, Expire); - Error -> - Error - end. - -agn(UserId, Addr, Port, CtxName, Oids, Expire, ExtraInfo) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - async_get_next(UserId, TargetName, CtxName, Oids, Expire, - ExtraInfo); - Error -> - Error - end. -%% </DEPRECATED> - - - %% --- synchroneous set-request --- %% @@ -884,64 +617,6 @@ sync_set(UserId, TargetName, Context, VarsAndVals, Timeout, ExtraInfo) -> %% </BACKWARD-COMPAT> -%% <DEPRECATED> -s(UserId, Addr, VarsAndVals) -> - s(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals). - -s(UserId, Addr, Port, VarsAndVals) - when is_integer(Port) andalso is_list(VarsAndVals) -> - s(UserId, Addr, Port, ?DEFAULT_CONTEXT, VarsAndVals); - -s(UserId, Addr, CtxName, VarsAndVals) - when is_list(CtxName) andalso is_list(VarsAndVals) -> - s(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, VarsAndVals); - -s(UserId, Addr, VarsAndVals, Timeout) - when is_list(VarsAndVals) andalso is_integer(Timeout) -> - s(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals, Timeout). - -s(UserId, Addr, Port, CtxName, VarsAndVals) - when is_integer(Port) andalso - is_list(CtxName) andalso - is_list(VarsAndVals) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - sync_set(UserId, TargetName, CtxName, VarsAndVals); - Error -> - Error - end; - -s(UserId, Addr, Port, VarsAndVals, Timeout) - when is_integer(Port) andalso - is_list(VarsAndVals) andalso - is_integer(Timeout) -> - s(UserId, Addr, Port, ?DEFAULT_CONTEXT, VarsAndVals, Timeout); - -s(UserId, Addr, CtxName, VarsAndVals, Timeout) - when is_list(CtxName) andalso - is_list(VarsAndVals) andalso - is_integer(Timeout) -> - s(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, VarsAndVals, Timeout). - -s(UserId, Addr, Port, CtxName, VarsAndVals, Timeout) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout); - Error -> - Error - end. - -s(UserId, Addr, Port, CtxName, VarsAndVals, Timeout, ExtraInfo) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo); - Error -> - Error - end. -%% </DEPRECATED> - - - %% --- asynchroneous set-request --- %% @@ -975,63 +650,6 @@ async_set(UserId, TargetName, Context, VarsAndVals, Expire, ExtraInfo) -> %% </BACKWARD-COMPAT> -%% <DEPRECATED> -as(UserId, Addr, VarsAndVals) -> - as(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals). - -as(UserId, Addr, Port, VarsAndVals) - when is_integer(Port) andalso is_list(VarsAndVals) -> - as(UserId, Addr, Port, ?DEFAULT_CONTEXT, VarsAndVals); - -as(UserId, Addr, CtxName, VarsAndVals) - when is_list(CtxName) andalso is_list(VarsAndVals) -> - as(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, VarsAndVals); - -as(UserId, Addr, VarsAndVals, Expire) - when is_list(VarsAndVals) andalso is_integer(Expire) -> - as(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals, Expire). - -as(UserId, Addr, Port, CtxName, VarsAndVals) - when is_integer(Port) andalso - is_list(CtxName) andalso - is_list(VarsAndVals) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - async_set(UserId, TargetName, CtxName, VarsAndVals); - Error -> - Error - end; - -as(UserId, Addr, Port, VarsAndVals, Expire) - when is_integer(Port) andalso - is_list(VarsAndVals) andalso - is_integer(Expire) -> - as(UserId, Addr, Port, ?DEFAULT_CONTEXT, VarsAndVals, Expire); - -as(UserId, Addr, CtxName, VarsAndVals, Expire) - when is_list(CtxName) andalso - is_list(VarsAndVals) andalso - is_integer(Expire) -> - as(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, VarsAndVals, Expire). - -as(UserId, Addr, Port, CtxName, VarsAndVals, Expire) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - async_set(UserId, TargetName, CtxName, VarsAndVals, Expire); - Error -> - Error - end. - -as(UserId, Addr, Port, CtxName, VarsAndVals, Expire, ExtraInfo) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - async_set(UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo); - Error -> - Error - end. -%% </DEPRECATED> - - %% --- synchroneous get-bulk --- %% @@ -1091,162 +709,6 @@ sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Timeout, %% </BACKWARD-COMPAT> -%% <DEPRECATED> -gb(UserId, Addr, NonRep, MaxRep, Oids) -> - %% p("gb -> entry with" - %% "~n UserId: ~p" - %% "~n Addr: ~p" - %% "~n NonRep: ~p" - %% "~n MaxRep: ~p" - %% "~n Oids: ~p", - %% [UserId, Addr, NonRep, MaxRep, Oids]), - gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids). - -gb(UserId, Addr, Port, NonRep, MaxRep, Oids) - when is_integer(Port) andalso - is_integer(NonRep) andalso - is_integer(MaxRep) andalso - is_list(Oids) -> - %% p("gb -> entry with" - %% "~n UserId: ~p" - %% "~n Addr: ~p" - %% "~n Port: ~p" - %% "~n NonRep: ~p" - %% "~n MaxRep: ~p" - %% "~n Oids: ~p", - %% [UserId, Addr, Port, NonRep, MaxRep, Oids]), - gb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids); - -gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids) - when is_integer(NonRep) andalso - is_integer(MaxRep) andalso - is_list(CtxName) andalso - is_list(Oids) -> - %% p("gb -> entry with" - %% "~n UserId: ~p" - %% "~n Addr: ~p" - %% "~n NonRep: ~p" - %% "~n MaxRep: ~p" - %% "~n CtxName: ~p" - %% "~n Oids: ~p", - %% [UserId, Addr, NonRep, MaxRep, CtxName, Oids]), - gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids); - -gb(UserId, Addr, NonRep, MaxRep, Oids, Timeout) - when is_integer(NonRep) andalso - is_integer(MaxRep) andalso - is_list(Oids) andalso - is_integer(Timeout) -> - %% p("gb -> entry with" - %% "~n UserId: ~p" - %% "~n Addr: ~p" - %% "~n NonRep: ~p" - %% "~n MaxRep: ~p" - %% "~n Oids: ~p" - %% "~n Timeout: ~p", - %% [UserId, Addr, NonRep, MaxRep, Oids, Timeout]), - gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids, Timeout). - -gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids) - when is_integer(Port) andalso - is_integer(NonRep) andalso - is_integer(MaxRep) andalso - is_list(CtxName) andalso - is_list(Oids) -> - %% p("gb -> entry with" - %% "~n UserId: ~p" - %% "~n Addr: ~p" - %% "~n Port: ~p" - %% "~n NonRep: ~p" - %% "~n MaxRep: ~p" - %% "~n CtxName: ~p" - %% "~n Oids: ~p", - %% [UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids]), - case target_name(Addr, Port) of - {ok, TargetName} -> - %% p("gb -> TargetName: ~p", [TargetName]), - sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids); - Error -> - Error - end; - -gb(UserId, Addr, Port, NonRep, MaxRep, Oids, Timeout) - when is_integer(Port) andalso - is_integer(NonRep) andalso - is_integer(MaxRep) andalso - is_list(Oids) andalso - is_integer(Timeout) -> - %% p("gb -> entry with" - %% "~n UserId: ~p" - %% "~n Addr: ~p" - %% "~n Port: ~p" - %% "~n NonRep: ~p" - %% "~n MaxRep: ~p" - %% "~n Oids: ~p" - %% "~n Timeout: ~p", - %% [UserId, Addr, Port, NonRep, MaxRep, Oids, Timeout]), - gb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids, Timeout); - -gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids, Timeout) - when is_integer(NonRep) andalso - is_integer(MaxRep) andalso - is_list(CtxName) andalso - is_list(Oids) andalso - is_integer(Timeout) -> - %% p("gb -> entry with" - %% "~n UserId: ~p" - %% "~n Addr: ~p" - %% "~n NonRep: ~p" - %% "~n MaxRep: ~p" - %% "~n CtxName: ~p" - %% "~n Oids: ~p" - %% "~n Timeout: ~p", - %% [UserId, Addr, NonRep, MaxRep, CtxName, Oids, Timeout]), - gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids, - Timeout). - -gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout) -> - %% p("gb -> entry with" - %% "~n UserId: ~p" - %% "~n Addr: ~p" - %% "~n Port: ~p" - %% "~n NonRep: ~p" - %% "~n MaxRep: ~p" - %% "~n CtxName: ~p" - %% "~n Oids: ~p" - %% "~n Timeout: ~p", - %% [UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout]), - case target_name(Addr, Port) of - {ok, TargetName} -> - sync_get_bulk(UserId, TargetName, - NonRep, MaxRep, CtxName, Oids, Timeout); - Error -> - Error - end. - -gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo) -> - %% p("gb -> entry with" - %% "~n UserId: ~p" - %% "~n Addr: ~p" - %% "~n Port: ~p" - %% "~n NonRep: ~p" - %% "~n MaxRep: ~p" - %% "~n CtxName: ~p" - %% "~n Oids: ~p" - %% "~n Timeout: ~p" - %% "~n ExtraInfo: ~p", - %% [UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo]), - case target_name(Addr, Port) of - {ok, TargetName} -> - sync_get_bulk(UserId, TargetName, - NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo); - Error -> - Error - end. -%% </DEPRECATED> - - - %% --- asynchroneous get-bulk --- %% @@ -1291,81 +753,6 @@ async_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Expire, %% </BACKWARD-COMPAT> -%% <DEPRECATED> -agb(UserId, Addr, NonRep, MaxRep, Oids) -> - agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids). - -agb(UserId, Addr, Port, NonRep, MaxRep, Oids) - when is_integer(Port) andalso - is_integer(NonRep) andalso - is_integer(MaxRep) andalso - is_list(Oids) -> - agb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids); - -agb(UserId, Addr, NonRep, MaxRep, CtxName, Oids) - when is_integer(NonRep) andalso - is_integer(MaxRep) andalso - is_list(CtxName) andalso - is_list(Oids) -> - agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids); - -agb(UserId, Addr, NonRep, MaxRep, Oids, Expire) - when is_integer(NonRep) andalso - is_integer(MaxRep) andalso - is_list(Oids) andalso - is_integer(Expire) -> - agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids, Expire). - -agb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids) - when is_integer(Port) andalso - is_integer(NonRep) andalso - is_integer(MaxRep), - is_list(CtxName) andalso - is_list(Oids) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - async_get_bulk(UserId, TargetName, - NonRep, MaxRep, CtxName, Oids); - Error -> - Error - end; - -agb(UserId, Addr, Port, NonRep, MaxRep, Oids, Expire) - when is_integer(Port) andalso - is_integer(NonRep) andalso - is_integer(MaxRep) andalso - is_list(Oids) andalso - is_integer(Expire) -> - agb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids, Expire); - -agb(UserId, Addr, NonRep, MaxRep, CtxName, Oids, Expire) - when is_integer(NonRep) andalso - is_integer(MaxRep) andalso - is_list(CtxName) andalso - is_list(Oids) andalso - is_integer(Expire) -> - agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids). - -agb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Expire) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - async_get_bulk(UserId, TargetName, - NonRep, MaxRep, CtxName, Oids, Expire); - Error -> - Error - end. - -agb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo) -> - case target_name(Addr, Port) of - {ok, TargetName} -> - async_get_bulk(UserId, TargetName, - NonRep, MaxRep, CtxName, Oids, Expire, - ExtraInfo); - Error -> - Error - end. -%% </DEPRECATED> - cancel_async_request(UserId, ReqId) -> snmpm_server:cancel_async_request(UserId, ReqId). @@ -1457,7 +844,7 @@ sys_up_time() -> format_reason(Reason) -> format_reason("", Reason). -format_reason(Prefix, Reason) when is_integer(Prefix) and (Prefix >= 0) -> +format_reason(Prefix, Reason) when is_integer(Prefix) andalso (Prefix >= 0) -> format_reason(lists:duplicate(Prefix, $ ), Reason); format_reason(Prefix, Reason) when is_list(Prefix) -> case (catch do_format_reason(Prefix, Reason)) of @@ -1691,6 +1078,9 @@ format_vb_value(Prefix, _Type, Val) -> %% --- Internal utility functions --- %% +target_name(Addr) -> + target_name(Addr, ?DEFAULT_AGENT_PORT). + target_name(Addr, Port) -> snmpm_config:agent_info(Addr, Port, target_name). diff --git a/lib/snmp/src/manager/snmpm_conf.erl b/lib/snmp/src/manager/snmpm_conf.erl index e50508c489..03dbd028f7 100644 --- a/lib/snmp/src/manager/snmpm_conf.erl +++ b/lib/snmp/src/manager/snmpm_conf.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 @@ -365,7 +366,7 @@ do_write_usm_conf(Fd, do_write_usm_conf(Fd, {EngineID, UserName, SecName, AuthP, AuthKey, PrivP, PrivKey}) -> - io:format(Fd, "{\"~s\", \"~s\", \"~s\", �~w, ~w, ~w, ~w}.~n", + io:format(Fd, "{\"~s\", \"~s\", \"~s\", í~w, ~w, ~w, ~w}.~n", [EngineID, UserName, SecName, AuthP, AuthKey, PrivP, PrivKey]); do_write_usm_conf(_Fd, Crap) -> error({bad_usm_conf, Crap}). diff --git a/lib/snmp/src/manager/snmpm_mpd.erl b/lib/snmp/src/manager/snmpm_mpd.erl index d7148bb4a4..953c94ab54 100644 --- a/lib/snmp/src/manager/snmpm_mpd.erl +++ b/lib/snmp/src/manager/snmpm_mpd.erl @@ -591,8 +591,8 @@ sec_engine_id(TargetName) -> %% BMK BMK BMK -%% Denna verkar v�ldigt lik generate_v1_v2c_response_msg! -%% Gemensam? Borde det finnas olikheter? +%% This one looks very similar to lik generate_v1_v2c_response_msg! +%% Common/shared? Should there be differences? %% generate_v1_v2c_msg(Vsn, Pdu, Community, Log) -> ?vdebug("generate_v1_v2c_msg -> encode pdu", []), diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index bfe14bc080..09e1eb25a9 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -5752,14 +5753,14 @@ otp_4394_config(AgentConfDir, MgrDir, Ip0) -> ?line write_community_conf(AgentConfDir, [C1, C2]), ?line update_vacm(Vsn, AgentConfDir), Ta1 = {"shelob v1", - [134,138,177,177], 5000, 1500, 3, %% Anv�nd Ip och modda + [134,138,177,177], 5000, 1500, 3, %% Use Ip and modify "pc1", "target_v1", "", %% [255,255,255,255,0,0], [], 2048}, Ta2 = {"bifur v1", - [134,138,177,75], 5000, 1500, 3, %% Anv�nd Ip + [134,138,177,75], 5000, 1500, 3, %% Use Ip "pc2", "target_v1", "", %% [255,255,255,255,0,0], diff --git a/lib/snmp/test/snmp_log_test.erl b/lib/snmp/test/snmp_log_test.erl index aeac4d1f71..e9345b44cc 100644 --- a/lib/snmp/test/snmp_log_test.erl +++ b/lib/snmp/test/snmp_log_test.erl @@ -375,9 +375,9 @@ log_to_io1(Config) when is_list(Config) -> %%====================================================================== -%% Starta en logger-process som med ett visst intervall loggar -%% meddelanden. Starta en reader-process som vid ett viss tillf�lle -%% l�ser fr�n loggen. +%% Start a logger-process that logs messages with a certain interval. +%% Start a reader-process that reads messages from the log at a certain +%% point of time. log_to_io2(suite) -> []; log_to_io2(doc) -> "Log to io from a different process than which " @@ -578,9 +578,9 @@ log_to_txt(Name, SeqNoGen, Config) when is_list(Config) -> %%====================================================================== -%% Starta en logger-process som med ett visst intervall loggar -%% meddelanden. Starta en reader-process som vid ett viss tillf�lle -%% l�ser fr�n loggen. +%% Start a logger-process that logs messages with a certain interval. +%% Start a reader-process that reads messages from the log at a certain +%% point of time. %% %% Test: ts:run(snmp, snmp_log_test, log_to_txt2, [batch]). diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index c374a2f0a6..dedbae5ce4 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -174,7 +174,26 @@ end_per_suite(Config) when is_list(Config) -> init_per_testcase(Case, Config) when is_list(Config) -> io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE,Case]), - init_per_testcase2(Case, Config). + %% This version of the API, based on Addr and Port, has been deprecated + DeprecatedApiCases = + [ + simple_sync_get1, + simple_async_get1, + simple_sync_get_next1, + simple_async_get_next1, + simple_sync_set1, + simple_async_set1, + simple_sync_get_bulk1, + simple_async_get_bulk1, + misc_async1 + ], + case lists:member(Case, DeprecatedApiCases) of + true -> + %% ?SKIP(api_no_longer_supported); + {skip, api_no_longer_supported}; + false -> + init_per_testcase2(Case, Config) + end. init_per_testcase2(Case, Config) -> ?DBG("init_per_testcase2 -> ~p", [erlang:nodes()]), @@ -226,18 +245,6 @@ init_per_testcase2(Case, Config) -> Conf2. init_per_testcase3(Case, Config) -> - ApiCases01 = - [ - simple_sync_get1, - simple_async_get1, - simple_sync_get_next1, - simple_async_get_next1, - simple_sync_set1, - simple_async_set1, - simple_sync_get_bulk1, - simple_async_get_bulk1, - misc_async1 - ], ApiCases02 = [ simple_sync_get2, @@ -273,7 +280,6 @@ init_per_testcase3(Case, Config) -> inform_swarm, report ] ++ - ApiCases01 ++ ApiCases02 ++ ApiCases03, case lists:member(Case, Cases) of @@ -319,18 +325,6 @@ end_per_testcase(Case, Config) when is_list(Config) -> Conf2. end_per_testcase2(Case, Config) -> - ApiCases01 = - [ - simple_sync_get1, - simple_async_get1, - simple_sync_get_next1, - simple_async_get_next1, - simple_sync_set1, - simple_async_set1, - simple_sync_get_bulk1, - simple_async_get_bulk1, - misc_async1 - ], ApiCases02 = [ simple_sync_get2, @@ -366,7 +360,6 @@ end_per_testcase2(Case, Config) -> inform_swarm, report ] ++ - ApiCases01 ++ ApiCases02 ++ ApiCases03, case lists:member(Case, Cases) of @@ -1596,12 +1589,14 @@ register_agent3(Config) when is_list(Config) -> simple_sync_get1(doc) -> ["Simple sync get-request - Old style (Addr & Port)"]; simple_sync_get1(suite) -> []; simple_sync_get1(Config) when is_list(Config) -> + ?SKIP(api_no_longer_supported), + process_flag(trap_exit, true), put(tname, ssg1), p("starting with Config: ~p~n", [Config]), Node = ?config(manager_node, Config), - Addr = ?config(ip, Config), + Addr = ?config(manager_agent_target_name, Config), Port = ?AGENT_PORT, p("issue get-request without loading the mib"), @@ -1757,6 +1752,8 @@ simple_async_get1(doc) -> ["Simple (async) get-request - Old style (Addr & Port)"]; simple_async_get1(suite) -> []; simple_async_get1(Config) when is_list(Config) -> + ?SKIP(api_no_longer_supported), + process_flag(trap_exit, true), put(tname, sag1), p("starting with Config: ~p~n", [Config]), @@ -1972,6 +1969,8 @@ simple_sync_get_next1(doc) -> ["Simple (sync) get_next-request - " "Old style (Addr & Port)"]; simple_sync_get_next1(suite) -> []; simple_sync_get_next1(Config) when is_list(Config) -> + ?SKIP(api_no_longer_supported), + process_flag(trap_exit, true), put(tname, ssgn1), p("starting with Config: ~p~n", [Config]), @@ -2264,6 +2263,8 @@ simple_async_get_next1(doc) -> ["Simple (async) get_next-request - " "Old style (Addr & Port)"]; simple_async_get_next1(suite) -> []; simple_async_get_next1(Config) when is_list(Config) -> + ?SKIP(api_no_longer_supported), + process_flag(trap_exit, true), put(tname, ssgn1), p("starting with Config: ~p~n", [Config]), @@ -2516,6 +2517,8 @@ simple_sync_set1(doc) -> ["Simple (sync) set-request - " "Old style (Addr & Port)"]; simple_sync_set1(suite) -> []; simple_sync_set1(Config) when is_list(Config) -> + ?SKIP(api_no_longer_supported), + process_flag(trap_exit, true), put(tname, sss1), p("starting with Config: ~p~n", [Config]), @@ -2686,6 +2689,8 @@ simple_async_set1(doc) -> ["Simple (async) set-request - " "Old style (Addr & Port)"]; simple_async_set1(suite) -> []; simple_async_set1(Config) when is_list(Config) -> + ?SKIP(api_no_longer_supported), + process_flag(trap_exit, true), put(tname, sas1), p("starting with Config: ~p~n", [Config]), @@ -2896,6 +2901,8 @@ simple_sync_get_bulk1(doc) -> ["Simple (sync) get_bulk-request - " "Old style (Addr & Port)"]; simple_sync_get_bulk1(suite) -> []; simple_sync_get_bulk1(Config) when is_list(Config) -> + ?SKIP(api_no_longer_supported), + process_flag(trap_exit, true), put(tname, ssgb1), p("starting with Config: ~p~n", [Config]), @@ -3261,6 +3268,8 @@ simple_async_get_bulk1(doc) -> ["Simple (async) get_bulk-request - " "Old style (Addr & Port)"]; simple_async_get_bulk1(suite) -> []; simple_async_get_bulk1(Config) when is_list(Config) -> + ?SKIP(api_no_longer_supported), + process_flag(trap_exit, true), put(tname, sagb1), p("starting with Config: ~p~n", [Config]), @@ -3605,6 +3614,8 @@ misc_async1(doc) -> ["Misc (async) request(s) - " "Old style (Addr & Port)"]; misc_async1(suite) -> []; misc_async1(Config) when is_list(Config) -> + ?SKIP(api_no_longer_supported), + process_flag(trap_exit, true), put(tname, ms1), p("starting with Config: ~p~n", [Config]), @@ -5591,29 +5602,35 @@ fin_mgr_user(Conf) -> init_mgr_user_data1(Conf) -> Node = ?config(manager_node, Conf), - Addr = ?config(ip, Conf), - Port = ?AGENT_PORT, - ?line ok = mgr_user_register_agent(Node, Addr, Port), + TargetName = ?config(manager_agent_target_name, Conf), + Addr = ?config(ip, Conf), + Port = ?AGENT_PORT, + ?line ok = mgr_user_register_agent(Node, TargetName, + [{address, Addr}, + {port, Port}, + {engine_id, "agentEngine"}]), Agents = mgr_user_which_own_agents(Node), ?DBG("Own agents: ~p", [Agents]), - ?line {ok, DefAgentConf} = mgr_user_agent_info(Node, Addr, Port, all), + ?line {ok, DefAgentConf} = mgr_user_agent_info(Node, TargetName, all), ?DBG("Default agent config: ~n~p", [DefAgentConf]), - ?line ok = mgr_user_update_agent_info(Node, Addr, Port, + ?line ok = mgr_user_update_agent_info(Node, TargetName, community, "all-rights"), - ?line ok = mgr_user_update_agent_info(Node, Addr, Port, + ?line ok = mgr_user_update_agent_info(Node, TargetName, sec_name, "all-rights"), - ?line ok = mgr_user_update_agent_info(Node, Addr, Port, + ?line ok = mgr_user_update_agent_info(Node, TargetName, engine_id, "agentEngine"), - ?line ok = mgr_user_update_agent_info(Node, Addr, Port, + ?line ok = mgr_user_update_agent_info(Node, TargetName, max_message_size, 1024), - ?line {ok, AgentConf} = mgr_user_agent_info(Node, Addr, Port, all), + ?line {ok, AgentConf} = mgr_user_agent_info(Node, TargetName, all), ?DBG("Updated agent config: ~n~p", [AgentConf]), Conf. init_mgr_user_data2(Conf) -> + ?DBG("init_mgr_user_data2 -> entry with" + "~n Conf: ~p", [Conf]), Node = ?config(manager_node, Conf), TargetName = ?config(manager_agent_target_name, Conf), Addr = ?config(ip, Conf), @@ -5641,9 +5658,8 @@ init_mgr_user_data2(Conf) -> fin_mgr_user_data1(Conf) -> Node = ?config(manager_node, Conf), - Addr = ?config(ip, Conf), - Port = ?AGENT_PORT, - mgr_user_unregister_agent(Node, Addr, Port), + TargetName = ?config(manager_agent_target_name, Conf), + mgr_user_unregister_agent(Node, TargetName), mgr_user_which_own_agents(Node), Conf. @@ -5670,33 +5686,41 @@ mgr_user_stop(Node) -> %% mgr_user_register_agent(Node, Addr, ?AGENT_PORT, []). mgr_user_register_agent(Node, TargetName, Conf) when is_list(TargetName) andalso is_list(Conf) -> - rcall(Node, snmp_manager_user, register_agent, [TargetName, Conf]); -mgr_user_register_agent(Node, Addr, Port) -> - mgr_user_register_agent(Node, Addr, Port, []). -mgr_user_register_agent(Node, Addr, Port, Conf) -> - rcall(Node, snmp_manager_user, register_agent, [Addr, Port, Conf]). + rcall(Node, snmp_manager_user, register_agent, [TargetName, Conf]). +%% <REMOVED-IN-R16B> +%% mgr_user_register_agent(Node, Addr, Port) -> +%% mgr_user_register_agent(Node, Addr, Port, []). +%% mgr_user_register_agent(Node, Addr, Port, Conf) -> +%% rcall(Node, snmp_manager_user, register_agent, [Addr, Port, Conf]). +%% </REMOVED-IN-R16B> %% mgr_user_unregister_agent(Node) -> %% mgr_user_unregister_agent(Node, ?LOCALHOST(), ?AGENT_PORT). -mgr_user_unregister_agent(Node, Addr_or_TargetName) -> - rcall(Node, snmp_manager_user, unregister_agent, [Addr_or_TargetName]). -mgr_user_unregister_agent(Node, Addr, Port) -> - rcall(Node, snmp_manager_user, unregister_agent, [Addr, Port]). - -mgr_user_agent_info(Node, Addr_or_TargetName, Item) when is_atom(Item) -> - rcall(Node, snmp_manager_user, agent_info, [Addr_or_TargetName, Item]). -mgr_user_agent_info(Node, Addr, Port, Item) when is_atom(Item) -> - rcall(Node, snmp_manager_user, agent_info, [Addr, Port, Item]). +mgr_user_unregister_agent(Node, TargetName) when is_list(TargetName) -> + rcall(Node, snmp_manager_user, unregister_agent, [TargetName]). +%% <REMOVED-IN-R16B> +%% mgr_user_unregister_agent(Node, Addr, Port) -> +%% rcall(Node, snmp_manager_user, unregister_agent, [Addr, Port]). +%% </REMOVED-IN-R16B> + +mgr_user_agent_info(Node, TargetName, Item) + when is_list(TargetName) andalso is_atom(Item) -> + rcall(Node, snmp_manager_user, agent_info, [TargetName, Item]). +%% <REMOVED-IN-R16B> +%% mgr_user_agent_info(Node, Addr, Port, Item) when is_atom(Item) -> +%% rcall(Node, snmp_manager_user, agent_info, [Addr, Port, Item]). +%% </REMOVED-IN-R16B> %% mgr_user_update_agent_info(Node, Item, Val) when atom(Item) -> %% mgr_user_update_agent_info(Node, ?LOCALHOST(), ?AGENT_PORT, Item, Val). -mgr_user_update_agent_info(Node, Addr_or_TargetName, Item, Val) - when is_atom(Item) -> - rcall(Node, snmp_manager_user, update_agent_info, - [Addr_or_TargetName, Item, Val]). -mgr_user_update_agent_info(Node, Addr, Port, Item, Val) when is_atom(Item) -> - rcall(Node, snmp_manager_user, update_agent_info, - [Addr, Port, Item, Val]). +mgr_user_update_agent_info(Node, TargetName, Item, Val) + when is_list(TargetName) andalso is_atom(Item) -> + rcall(Node, snmp_manager_user, update_agent_info, [TargetName, Item, Val]). +%% <REMOVED-IN-R16B> +%% mgr_user_update_agent_info(Node, Addr, Port, Item, Val) when is_atom(Item) -> +%% rcall(Node, snmp_manager_user, update_agent_info, +%% [Addr, Port, Item, Val]). +%% </REMOVED-IN-R16B> %% mgr_user_which_all_agents(Node) -> %% rcall(Node, snmp_manager_user, which_all_agents, []). @@ -5709,89 +5733,112 @@ mgr_user_load_mib(Node, Mib) -> %% mgr_user_sync_get(Node, Oids) -> %% mgr_user_sync_get(Node, ?LOCALHOST(), ?AGENT_PORT, Oids). -mgr_user_sync_get(Node, Addr_or_TargetName, Oids) -> - rcall(Node, snmp_manager_user, sync_get, [Addr_or_TargetName, Oids]). +mgr_user_sync_get(Node, TargetName, Oids) when is_list(TargetName) -> + rcall(Node, snmp_manager_user, sync_get, [TargetName, Oids]). +%% <REMOVED-IN-R16B> mgr_user_sync_get(Node, Addr, Port, Oids) -> rcall(Node, snmp_manager_user, sync_get, [Addr, Port, Oids]). +%% </REMOVED-IN-R16B> -mgr_user_sync_get2(Node, TargetName, Oids, SendOpts) -> +mgr_user_sync_get2(Node, TargetName, Oids, SendOpts) when is_list(TargetName) -> rcall(Node, snmp_manager_user, sync_get2, [TargetName, Oids, SendOpts]). %% mgr_user_async_get(Node, Oids) -> %% mgr_user_async_get(Node, ?LOCALHOST(), ?AGENT_PORT, Oids). -mgr_user_async_get(Node, Addr_or_TargetName, Oids) -> - rcall(Node, snmp_manager_user, async_get, [Addr_or_TargetName, Oids]). +mgr_user_async_get(Node, TargetName, Oids) when is_list(TargetName) -> + rcall(Node, snmp_manager_user, async_get, [TargetName, Oids]). +%% <REMOVED-IN-R16B> mgr_user_async_get(Node, Addr, Port, Oids) -> rcall(Node, snmp_manager_user, async_get, [Addr, Port, Oids]). +%% </REMOVED-IN-R16B> -mgr_user_async_get2(Node, TargetName, Oids, SendOpts) -> +mgr_user_async_get2(Node, TargetName, Oids, SendOpts) + when is_list(TargetName) -> rcall(Node, snmp_manager_user, async_get2, [TargetName, Oids, SendOpts]). %% mgr_user_sync_get_next(Node, Oids) -> %% mgr_user_sync_get_next(Node, ?LOCALHOST(), ?AGENT_PORT, Oids). -mgr_user_sync_get_next(Node, Addr_or_TargetName, Oids) -> - rcall(Node, snmp_manager_user, sync_get_next, [Addr_or_TargetName, Oids]). +mgr_user_sync_get_next(Node, TargetName, Oids) when is_list(TargetName) -> + rcall(Node, snmp_manager_user, sync_get_next, [TargetName, Oids]). +%% <REMOVED-IN-R16B> mgr_user_sync_get_next(Node, Addr, Port, Oids) -> rcall(Node, snmp_manager_user, sync_get_next, [Addr, Port, Oids]). +%% </REMOVED-IN-R16B> -mgr_user_sync_get_next2(Node, TargetName, Oids, SendOpts) -> +mgr_user_sync_get_next2(Node, TargetName, Oids, SendOpts) + when is_list(TargetName) -> rcall(Node, snmp_manager_user, sync_get_next2, [TargetName, Oids, SendOpts]). %% mgr_user_async_get_next(Node, Oids) -> %% mgr_user_async_get_next(Node, ?LOCALHOST(), ?AGENT_PORT, Oids). -mgr_user_async_get_next(Node, Addr_or_TargetName, Oids) -> - rcall(Node, snmp_manager_user, async_get_next, [Addr_or_TargetName, Oids]). +mgr_user_async_get_next(Node, TargetName, Oids) when is_list(TargetName) -> + rcall(Node, snmp_manager_user, async_get_next, [TargetName, Oids]). +%% <REMOVED-IN-R16B> mgr_user_async_get_next(Node, Addr, Port, Oids) -> rcall(Node, snmp_manager_user, async_get_next, [Addr, Port, Oids]). +%% </REMOVED-IN-R16B> -mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts) -> +mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts) + when is_list(TargetName) -> rcall(Node, snmp_manager_user, async_get_next2, [TargetName, Oids, SendOpts]). %% mgr_user_sync_set(Node, VAV) -> %% mgr_user_sync_set(Node, ?LOCALHOST(), ?AGENT_PORT, VAV). -mgr_user_sync_set(Node, Addr_or_TargetName, VAV) -> - rcall(Node, snmp_manager_user, sync_set, [Addr_or_TargetName, VAV]). +mgr_user_sync_set(Node, TargetName, VAV) when is_list(TargetName) -> + rcall(Node, snmp_manager_user, sync_set, [TargetName, VAV]). +%% <REMOVED-IN-R16B> mgr_user_sync_set(Node, Addr, Port, VAV) -> rcall(Node, snmp_manager_user, sync_set, [Addr, Port, VAV]). +%% </REMOVED-IN-R16B> -mgr_user_sync_set2(Node, TargetName, VAV, SendOpts) -> +mgr_user_sync_set2(Node, TargetName, VAV, SendOpts) when is_list(TargetName) -> rcall(Node, snmp_manager_user, sync_set2, [TargetName, VAV, SendOpts]). %% mgr_user_async_set(Node, VAV) -> %% mgr_user_async_set(Node, ?LOCALHOST(), ?AGENT_PORT, VAV). -mgr_user_async_set(Node, Addr_or_TargetName, VAV) -> - rcall(Node, snmp_manager_user, async_set, [Addr_or_TargetName, VAV]). +mgr_user_async_set(Node, TargetName, VAV) when is_list(TargetName) -> + rcall(Node, snmp_manager_user, async_set, [TargetName, VAV]). +%% <REMOVED-IN-R16B> mgr_user_async_set(Node, Addr, Port, VAV) -> rcall(Node, snmp_manager_user, async_set, [Addr, Port, VAV]). +%% </REMOVED-IN-R16B> -mgr_user_async_set2(Node, TargetName, VAV, SendOpts) -> +mgr_user_async_set2(Node, TargetName, VAV, SendOpts) when is_list(TargetName) -> rcall(Node, snmp_manager_user, async_set2, [TargetName, VAV, SendOpts]). %% mgr_user_sync_get_bulk(Node, NonRep, MaxRep, Oids) -> %% mgr_user_sync_get_bulk(Node, ?LOCALHOST(), ?AGENT_PORT, %% NonRep, MaxRep, Oids). -mgr_user_sync_get_bulk(Node, Addr_or_TargetName, NonRep, MaxRep, Oids) -> +mgr_user_sync_get_bulk(Node, TargetName, NonRep, MaxRep, Oids) + when is_list(TargetName) -> rcall(Node, snmp_manager_user, sync_get_bulk, - [Addr_or_TargetName, NonRep, MaxRep, Oids]). + [TargetName, NonRep, MaxRep, Oids]). +%% <REMOVED-IN-R16B> mgr_user_sync_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) -> rcall(Node, snmp_manager_user, sync_get_bulk, [Addr, Port, NonRep, MaxRep, Oids]). +%% </REMOVED-IN-R16B> -mgr_user_sync_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts) -> +mgr_user_sync_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts) + when is_list(TargetName) -> rcall(Node, snmp_manager_user, sync_get_bulk2, [TargetName, NonRep, MaxRep, Oids, SendOpts]). %% mgr_user_async_get_bulk(Node, NonRep, MaxRep, Oids) -> %% mgr_user_async_get_bulk(Node, ?LOCALHOST(), ?AGENT_PORT, %% NonRep, MaxRep, Oids). -mgr_user_async_get_bulk(Node, Addr_or_TargetName, NonRep, MaxRep, Oids) -> +mgr_user_async_get_bulk(Node, TargetName, NonRep, MaxRep, Oids) + when is_list(TargetName) -> rcall(Node, snmp_manager_user, async_get_bulk, - [Addr_or_TargetName, NonRep, MaxRep, Oids]). + [TargetName, NonRep, MaxRep, Oids]). +%% <REMOVED-IN-R16B> mgr_user_async_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) -> rcall(Node, snmp_manager_user, async_get_bulk, [Addr, Port, NonRep, MaxRep, Oids]). +%% </REMOVED-IN-R16B> -mgr_user_async_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts) -> +mgr_user_async_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts) + when is_list(TargetName) -> rcall(Node, snmp_manager_user, async_get_bulk2, [TargetName, NonRep, MaxRep, Oids, SendOpts]). diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl index 1b62b04960..4e789bbaec 100644 --- a/lib/snmp/test/snmp_manager_user.erl +++ b/lib/snmp/test/snmp_manager_user.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -44,20 +44,20 @@ info/0, system_info/0, simulate_crash/1, - register_agent/2, register_agent/3, - unregister_agent/1, unregister_agent/2, - agent_info/2, agent_info/3, - update_agent_info/3, update_agent_info/4, + register_agent/2, + unregister_agent/1, + agent_info/2, + update_agent_info/3, which_all_agents/0, which_own_agents/0, load_mib/1, unload_mib/1, - sync_get/1, sync_get/2, sync_get/3, sync_get2/3, - async_get/1, async_get/2, async_get/3, async_get2/3, - sync_get_next/1, sync_get_next/2, sync_get_next/3, sync_get_next2/3, - async_get_next/1, async_get_next/2, async_get_next/3, async_get_next2/3, - sync_set/1, sync_set/2, sync_set/3, sync_set2/3, - async_set/1, async_set/2, async_set/3, async_set2/3, - sync_get_bulk/3, sync_get_bulk/4, sync_get_bulk/5, sync_get_bulk2/5, - async_get_bulk/3, async_get_bulk/4, async_get_bulk/5, async_get_bulk2/5, + sync_get/1, sync_get/2, sync_get2/3, + async_get/1, async_get/2, async_get2/3, + sync_get_next/1, sync_get_next/2, sync_get_next2/3, + async_get_next/1, async_get_next/2, async_get_next2/3, + sync_set/1, sync_set/2, sync_set2/3, + async_set/1, async_set/2, async_set2/3, + sync_get_bulk/3, sync_get_bulk/4, sync_get_bulk2/5, + async_get_bulk/3, async_get_bulk/4, async_get_bulk2/5, name_to_oid/1, oid_to_name/1, purify_oid/1 ]). @@ -73,15 +73,11 @@ -export([ handle_error/3, - handle_agent/4, + handle_agent/5, handle_pdu/4, handle_trap/3, handle_inform/3, - handle_report/3, - handle_pdu/5, % For backwards compatibillity - handle_trap/4, % For backwards compatibillity - handle_inform/4, % For backwards compatibillity - handle_report/4 % For backwards compatibillity + handle_report/3 ]). @@ -126,27 +122,16 @@ simulate_crash(Reason) -> register_agent(TargetName, Config) when is_list(TargetName) andalso is_list(Config) -> - call({register_agent, TargetName, Config}); -register_agent(Addr, Port) -> - register_agent(Addr, Port, []). - -register_agent(Addr, Port, Conf) -> - call({register_agent, Addr, Port, Conf}). + call({register_agent, TargetName, Config}). unregister_agent(TargetName) -> call({unregister_agent, TargetName}). -unregister_agent(Addr, Port) -> - call({unregister_agent, Addr, Port}). agent_info(TargetName, Item) -> call({agent_info, TargetName, Item}). -agent_info(Addr, Port, Item) -> - call({agent_info, Addr, Port, Item}). update_agent_info(TargetName, Item, Val) -> call({update_agent_info, TargetName, Item, Val}). -update_agent_info(Addr, Port, Item, Val) -> - call({update_agent_info, Addr, Port, Item, Val}). which_all_agents() -> call(which_all_agents). @@ -165,11 +150,8 @@ unload_mib(Mib) -> sync_get(Oids) -> call({sync_get, Oids}). -sync_get(Addr_or_TargetName, Oids) -> - call({sync_get, Addr_or_TargetName, Oids}). - -sync_get(Addr, Port, Oids) -> - call({sync_get, Addr, Port, Oids}). +sync_get(TargetName, Oids) -> + call({sync_get, TargetName, Oids}). sync_get2(TargetName, Oids, SendOpts) -> call({sync_get2, TargetName, Oids, SendOpts}). @@ -180,11 +162,8 @@ sync_get2(TargetName, Oids, SendOpts) -> async_get(Oids) -> call({async_get, Oids}). -async_get(Addr_or_TargetName, Oids) -> - call({async_get, Addr_or_TargetName, Oids}). - -async_get(Addr, Port, Oids) -> - call({async_get, Addr, Port, Oids}). +async_get(TargetName, Oids) -> + call({async_get, TargetName, Oids}). async_get2(TargetName, Oids, SendOpts) -> call({async_get2, TargetName, Oids, SendOpts}). @@ -194,11 +173,8 @@ async_get2(TargetName, Oids, SendOpts) -> sync_get_next(Oids) -> call({sync_get_next, Oids}). -sync_get_next(Addr_or_TargetName, Oids) -> - call({sync_get_next, Addr_or_TargetName, Oids}). - -sync_get_next(Addr, Port, Oids) -> - call({sync_get_next, Addr, Port, Oids}). +sync_get_next(TargetName, Oids) -> + call({sync_get_next, TargetName, Oids}). sync_get_next2(TargetName, Oids, SendOpts) -> call({sync_get_next2, TargetName, Oids, SendOpts}). @@ -208,11 +184,8 @@ sync_get_next2(TargetName, Oids, SendOpts) -> async_get_next(Oids) -> call({async_get_next, Oids}). -async_get_next(Addr_or_TargetName, Oids) -> - call({async_get_next, Addr_or_TargetName, Oids}). - -async_get_next(Addr, Port, Oids) -> - call({async_get_next, Addr, Port, Oids}). +async_get_next(TargetName, Oids) -> + call({async_get_next, TargetName, Oids}). async_get_next2(TargetName, Oids, SendOpts) -> call({async_get_next2, TargetName, Oids, SendOpts}). @@ -222,11 +195,8 @@ async_get_next2(TargetName, Oids, SendOpts) -> sync_set(VAV) -> call({sync_set, VAV}). -sync_set(Addr_or_TargetName, VAV) -> - call({sync_set, Addr_or_TargetName, VAV}). - -sync_set(Addr, Port, VAV) -> - call({sync_set, Addr, Port, VAV}). +sync_set(TargetName, VAV) -> + call({sync_set, TargetName, VAV}). sync_set2(TargetName, VAV, SendOpts) -> call({sync_set2, TargetName, VAV, SendOpts}). @@ -236,11 +206,8 @@ sync_set2(TargetName, VAV, SendOpts) -> async_set(VAV) -> call({async_set, VAV}). -async_set(Addr_or_TargetName, VAV) -> - call({async_set, Addr_or_TargetName, VAV}). - -async_set(Addr, Port, VAV) -> - call({async_set, Addr, Port, VAV}). +async_set(TargetName, VAV) -> + call({async_set, TargetName, VAV}). async_set2(TargetName, VAV, SendOpts) -> call({async_set2, TargetName, VAV, SendOpts}). @@ -250,11 +217,8 @@ async_set2(TargetName, VAV, SendOpts) -> sync_get_bulk(NonRep, MaxRep, Oids) -> call({sync_get_bulk, NonRep, MaxRep, Oids}). -sync_get_bulk(Addr_or_TargetName, NonRep, MaxRep, Oids) -> - call({sync_get_bulk, Addr_or_TargetName, NonRep, MaxRep, Oids}). - -sync_get_bulk(Addr, Port, NonRep, MaxRep, Oids) -> - call({sync_get_bulk, Addr, Port, NonRep, MaxRep, Oids}). +sync_get_bulk(TargetName, NonRep, MaxRep, Oids) -> + call({sync_get_bulk, TargetName, NonRep, MaxRep, Oids}). sync_get_bulk2(TargetName, NonRep, MaxRep, Oids, SendOpts) -> call({sync_get_bulk2, TargetName, NonRep, MaxRep, Oids, SendOpts}). @@ -264,11 +228,8 @@ sync_get_bulk2(TargetName, NonRep, MaxRep, Oids, SendOpts) -> async_get_bulk(NonRep, MaxRep, Oids) -> call({async_get_bulk, NonRep, MaxRep, Oids}). -async_get_bulk(Addr_or_TargetName, NonRep, MaxRep, Oids) -> - call({async_get_bulk, Addr_or_TargetName, NonRep, MaxRep, Oids}). - -async_get_bulk(Addr, Port, NonRep, MaxRep, Oids) -> - call({async_get_bulk, Addr, Port, NonRep, MaxRep, Oids}). +async_get_bulk(TargetName, NonRep, MaxRep, Oids) -> + call({async_get_bulk, TargetName, NonRep, MaxRep, Oids}). async_get_bulk2(TargetName, NonRep, MaxRep, Oids, SendOpts) -> call({async_get_bulk2, TargetName, NonRep, MaxRep, Oids, SendOpts}). @@ -340,24 +301,12 @@ loop(#state{parent = Parent, id = Id} = S) -> reply(From, Res, Ref), loop(S); - {{register_agent, Addr, Port, Conf}, From, Ref} -> - d("loop -> received register_agent request"), - Res = snmpm:register_agent(Id, Addr, Port, Conf), - reply(From, Res, Ref), - loop(S); - {{unregister_agent, TargetName}, From, Ref} -> d("loop -> received unregister_agent request"), Res = snmpm:unregister_agent(Id, TargetName), reply(From, Res, Ref), loop(S); - {{unregister_agent, Addr, Port}, From, Ref} -> - d("loop -> received unregister_agent request"), - Res = snmpm:unregister_agent(Id, Addr, Port), - reply(From, Res, Ref), - loop(S); - {{agent_info, TargetName, Item}, From, Ref} -> d("loop -> received agent_info request with" "~n TargetName: ~p" @@ -368,15 +317,6 @@ loop(#state{parent = Parent, id = Id} = S) -> reply(From, Res, Ref), loop(S); - {{agent_info, Addr, Port, Item}, From, Ref} -> - d("loop -> received agent_info request with" - "~n Addr: ~p" - "~n Port: ~p" - "~n Item: ~p", [Addr, Port, Item]), - Res = snmpm:agent_info(Addr, Port, Item), - reply(From, Res, Ref), - loop(S); - {{update_agent_info, TargetName, Item, Val}, From, Ref} -> d("loop -> received update_agent_info request with" "~n TargetName: ~p" @@ -386,16 +326,6 @@ loop(#state{parent = Parent, id = Id} = S) -> reply(From, Res, Ref), loop(S); - {{update_agent_info, Addr, Port, Item, Val}, From, Ref} -> - d("loop -> received update_agent_info request with" - "~n Addr: ~p" - "~n Port: ~p" - "~n Item: ~p" - "~n Val: ~p", [Addr, Port, Item, Val]), - Res = snmpm:update_agent_info(Id, Addr, Port, Item, Val), - reply(From, Res, Ref), - loop(S); - {which_all_agents, From, Ref} -> d("loop -> received which_all_agents request"), Res = snmpm:which_agents(), @@ -452,23 +382,6 @@ loop(#state{parent = Parent, id = Id} = S) -> reply(From, Res, Ref), loop(S); - {{sync_get, Addr, Oids}, From, Ref} -> - d("loop -> received sync_get request with" - "~n Addr: ~p" - "~n Oids: ~p", [Addr, Oids]), - Res = snmpm:g(Id, Addr, Oids), - reply(From, Res, Ref), - loop(S); - - {{sync_get, Addr, Port, Oids}, From, Ref} -> - d("loop -> received sync_get request with" - "~n Addr: ~p" - "~n Port: ~p" - "~n Oids: ~p", [Addr, Port, Oids]), - Res = snmpm:g(Id, Addr, Port, Oids), - reply(From, Res, Ref), - loop(S); - %% %% -- (async) get-request -- @@ -500,18 +413,6 @@ loop(#state{parent = Parent, id = Id} = S) -> reply(From, Res, Ref), loop(S); - {{async_get, Addr, Oids}, From, Ref} -> - d("loop -> received async_get request"), - Res = snmpm:ag(Id, Addr, Oids), - reply(From, Res, Ref), - loop(S); - - {{async_get, Addr, Port, Oids}, From, Ref} -> - d("loop -> received async_get request"), - Res = snmpm:ag(Id, Addr, Port, Oids), - reply(From, Res, Ref), - loop(S); - %% %% -- (sync) get_next-request -- @@ -543,18 +444,6 @@ loop(#state{parent = Parent, id = Id} = S) -> reply(From, Res, Ref), loop(S); - {{sync_get_next, Addr, Oids}, From, Ref} -> - d("loop -> received sync_get_next request"), - Res = snmpm:gn(Id, Addr, Oids), - reply(From, Res, Ref), - loop(S); - - {{sync_get_next, Addr, Port, Oids}, From, Ref} -> - d("loop -> received sync_get_next request"), - Res = snmpm:gn(Id, Addr, Port, Oids), - reply(From, Res, Ref), - loop(S); - %% %% -- (async) get_next-request -- @@ -586,18 +475,6 @@ loop(#state{parent = Parent, id = Id} = S) -> reply(From, Res, Ref), loop(S); - {{async_get_next, Addr, Oids}, From, Ref} -> - d("loop -> received async_get_next request"), - Res = snmpm:agn(Id, Addr, Oids), - reply(From, Res, Ref), - loop(S); - - {{async_get_next, Addr, Port, Oids}, From, Ref} -> - d("loop -> received async_get_next request"), - Res = snmpm:agn(Id, Addr, Port, Oids), - reply(From, Res, Ref), - loop(S); - %% %% -- (sync) set-request -- @@ -626,18 +503,6 @@ loop(#state{parent = Parent, id = Id} = S) -> reply(From, Res, Ref), loop(S); - {{sync_set, Addr, VAV}, From, Ref} -> - d("loop -> received sync_set request"), - Res = snmpm:s(Id, Addr, VAV), - reply(From, Res, Ref), - loop(S); - - {{sync_set, Addr, Port, VAV}, From, Ref} -> - d("loop -> received sync_set request"), - Res = snmpm:s(Id, Addr, Port, VAV), - reply(From, Res, Ref), - loop(S); - %% %% -- (async) set-request -- @@ -666,18 +531,6 @@ loop(#state{parent = Parent, id = Id} = S) -> reply(From, Res, Ref), loop(S); - {{async_set, Addr, VAV}, From, Ref} -> - d("loop -> received async_set request"), - Res = snmpm:as(Id, Addr, VAV), - reply(From, Res, Ref), - loop(S); - - {{async_set, Addr, Port, VAV}, From, Ref} -> - d("loop -> received async_set request"), - Res = snmpm:as(Id, Addr, Port, VAV), - reply(From, Res, Ref), - loop(S); - %% %% -- (sync) get-bulk-request -- @@ -718,27 +571,6 @@ loop(#state{parent = Parent, id = Id} = S) -> reply(From, Res, Ref), loop(S); - {{sync_get_bulk, Addr, NonRep, MaxRep, Oids}, From, Ref} -> - d("loop -> received sync_get_bulk request with" - "~n Addr: ~p" - "~n NonRep: ~w" - "~n MaxRep: ~w" - "~n Oids: ~p", [Addr, NonRep, MaxRep, Oids]), - Res = snmpm:gb(Id, Addr, NonRep, MaxRep, Oids), - reply(From, Res, Ref), - loop(S); - - {{sync_get_bulk, Addr, Port, NonRep, MaxRep, Oids}, From, Ref} -> - d("loop -> received sync_get_bulk request with" - "~n Addr: ~p" - "~n Port: ~w" - "~n NonRep: ~w" - "~n MaxRep: ~w" - "~n Oids: ~p", [Addr, Port, NonRep, MaxRep, Oids]), - Res = snmpm:gb(Id, Addr, Port, NonRep, MaxRep, Oids), - reply(From, Res, Ref), - loop(S); - %% %% -- (async) get-bulk-request -- @@ -779,27 +611,6 @@ loop(#state{parent = Parent, id = Id} = S) -> reply(From, Res, Ref), loop(S); - {{async_get_bulk, Addr, NonRep, MaxRep, Oids}, From, Ref} -> - d("loop -> received async_get_bulk request with" - "~n Addr: ~p" - "~n NonRep: ~w" - "~n MaxRep: ~w" - "~n Oids: ~p", [Addr, NonRep, MaxRep, Oids]), - Res = snmpm:agb(Id, Addr, NonRep, MaxRep, Oids), - reply(From, Res, Ref), - loop(S); - - {{async_get_bulk, Addr, Port, NonRep, MaxRep, Oids}, From, Ref} -> - d("loop -> received async_get_bulk request with" - "~n Addr: ~p" - "~n Port: ~w" - "~n NonRep: ~w" - "~n MaxRep: ~w" - "~n Oids: ~p", [Addr, Port, NonRep, MaxRep, Oids]), - Res = snmpm:agb(Id, Addr, Port, NonRep, MaxRep, Oids), - reply(From, Res, Ref), - loop(S); - %% %% -- logical name translation -- @@ -846,12 +657,6 @@ loop(#state{parent = Parent, id = Id} = S) -> Parent ! {async_event, ReqId, {pdu, SnmpResponse}}, loop(S); - %% For backwards compatibillity - {handle_pdu, _Pid, _Addr, _Port, ReqId, SnmpResponse} -> - d("loop -> received pdu callback from manager for ~w", [ReqId]), - Parent ! {async_event, ReqId, {pdu, SnmpResponse}}, - loop(S); - {handle_trap, _Pid, TargetName, SnmpTrap} -> d("loop -> received trap callback from manager for " "~n ~p", @@ -860,15 +665,6 @@ loop(#state{parent = Parent, id = Id} = S) -> Parent ! {async_event, TargetName, {trap, SnmpTrap}}, loop(S); - %% For backwards compatibillity - {handle_trap, _Pid, Addr, Port, SnmpTrap} -> - d("loop -> received trap callback from manager for " - "~n ~p:~w", - "~n ~p", - [Addr, Port, SnmpTrap]), - Parent ! {async_event, {Addr, Port}, {trap, SnmpTrap}}, - loop(S); - {handle_inform, Pid, TargetName, SnmpInform} -> d("loop -> received inform callback from manager for " "~n ~p", @@ -877,15 +673,6 @@ loop(#state{parent = Parent, id = Id} = S) -> Parent ! {async_event, TargetName, {inform, Pid, SnmpInform}}, loop(S); - %% For backwards compatibillity - {handle_inform, Pid, Addr, Port, SnmpInform} -> - d("loop -> received inform callback from manager for " - "~n ~p:~w", - "~n ~p", - [Addr, Port, SnmpInform]), - Parent ! {async_event, {Addr, Port}, {inform, Pid, SnmpInform}}, - loop(S); - {handle_report, _Pid, TargetName, SnmpReport} -> d("loop -> received report callback from manager for " "~n ~p", @@ -894,15 +681,6 @@ loop(#state{parent = Parent, id = Id} = S) -> Parent ! {async_event, TargetName, {report, SnmpReport}}, loop(S); - %% For backwards compatibillity - {handle_report, _Pid, Addr, Port, SnmpReport} -> - d("loop -> received report callback from manager for " - "~n ~p:~w", - "~n ~p", - [Addr, Port, SnmpReport]), - Parent ! {async_event, {Addr, Port}, {report, SnmpReport}}, - loop(S); - {'EXIT', Parent, Reason} -> d("received exit signal from parent: ~n~p", [Reason]), info("received exit signal from parent: ~n~p", [Reason]), @@ -981,8 +759,8 @@ handle_error(ReqId, Reason, UserPid) -> ignore. -handle_agent(Addr, Port, SnmpInfo, UserPid) -> - UserPid ! {handle_agent, self(), Addr, Port, SnmpInfo}, +handle_agent(Addr, Port, SnmpInfo, UserPid, UserData) -> + UserPid ! {handle_agent, self(), Addr, Port, SnmpInfo, UserData}, ignore. @@ -990,22 +768,10 @@ handle_pdu(TargetName, ReqId, SnmpResponse, UserPid) -> UserPid ! {handle_pdu, self(), TargetName, ReqId, SnmpResponse}, ignore. -%% For backwards compatibillity -handle_pdu(Addr, Port, ReqId, SnmpResponse, UserPid) -> - UserPid ! {handle_pdu, self(), Addr, Port, ReqId, SnmpResponse}, - ignore. - - handle_trap(TargetName, SnmpTrap, UserPid) -> UserPid ! {handle_trap, self(), TargetName, SnmpTrap}, ok. -%% For backwards compatibillity -handle_trap(Addr, Port, SnmpTrap, UserPid) -> - UserPid ! {handle_trap, self(), Addr, Port, SnmpTrap}, - ok. - - handle_inform(TargetName, SnmpInform, UserPid) -> UserPid ! {handle_inform, self(), TargetName, SnmpInform}, receive @@ -1015,26 +781,10 @@ handle_inform(TargetName, SnmpInform, UserPid) -> ok end. -%% For backwards compatibillity -handle_inform(Addr, Port, SnmpInform, UserPid) -> - UserPid ! {handle_inform, self(), Addr, Port, SnmpInform}, - receive - {handle_inform_no_response, {Addr, Port}} -> - no_reply; - {handle_inform_response, {Addr, Port}} -> - ok - end. - - handle_report(TargetName, SnmpReport, UserPid) -> UserPid ! {handle_report, self(), TargetName, SnmpReport}, ok. -%% For backwards compatibillity -handle_report(Addr, Port, SnmpReport, UserPid) -> - UserPid ! {handle_report, self(), Addr, Port, SnmpReport}, - ok. - %%---------------------------------------------------------------------- %% Debug diff --git a/lib/snmp/test/snmp_manager_user_old.erl b/lib/snmp/test/snmp_manager_user_old.erl index 6280cef51f..9f951bf64d 100644 --- a/lib/snmp/test/snmp_manager_user_old.erl +++ b/lib/snmp/test/snmp_manager_user_old.erl @@ -107,10 +107,20 @@ unregister_agent(Addr, Port) -> snmpm:unregister_agent(?USER_ID, Addr, Port). agent_info(Addr, Port, Item) -> - snmpm:agent_info(?USER_ID, Addr, Port, Item). + case snmpm:target_name(Addr, Port) of + {ok, TargetName} -> + snmpm:agent_info(TargetName, Item); + Error -> + Error + end. update_agent_info(Addr, Port, Item, Val) -> - snmpm:update_agent_info(?USER_ID, Addr, Port, Item, Val). + case snmpm:target_name(Addr, Port) of + {ok, TargetName} -> + snmpm:update_agent_info(?USER_ID, TargetName, Item, Val); + Error -> + Error + end. which_agents() -> snmpm:which_agents(). @@ -128,73 +138,153 @@ unload_mib(Mib) -> %% -- sync_get(Addr, Oids) -> - snmpm:g(?USER_ID, Addr, Oids). + case snmpm:target_name(Addr) of + {ok, TargetName} -> + snmpm:sync_get2(?USER_ID, TargetName, Oids); + Error -> + Error + end. sync_get(Addr, Port, Oids) -> - snmpm:g(?USER_ID, Addr, Port, Oids). + case snmpm:target_name(Addr, Port) of + {ok, TargetName} -> + snmpm:sync_get2(?USER_ID, TargetName, Oids); + Error -> + Error + end. %% -- async_get(Addr, Oids) -> - snmpm:ag(?USER_ID, Addr, Oids). + case snmpm:target_name(Addr) of + {ok, TargetName} -> + snmpm:async_get2(?USER_ID, TargetName, Oids); + Error -> + Error + end. async_get(Addr, Port, Oids) -> - snmpm:ag(?USER_ID, Addr, Port, Oids). + case snmpm:target_name(Addr, Port) of + {ok, TargetName} -> + snmpm:async_get2(?USER_ID, TargetName, Oids); + Error -> + Error + end. %% -- sync_get_next(Addr, Oids) -> - snmpm:gn(?USER_ID, Addr, Oids). + case snmpm:target_name(Addr) of + {ok, TargetName} -> + snmpm:sync_get_next2(?USER_ID, TargetName, Oids); + Error -> + Error + end. sync_get_next(Addr, Port, Oids) -> - snmpm:gn(?USER_ID, Addr, Port, Oids). + case snmpm:target_name(Addr, Port) of + {ok, TargetName} -> + snmpm:sync_get_next2(?USER_ID, TargetName, Oids); + Error -> + Error + end. %% -- async_get_next(Addr, Oids) -> - snmpm:agn(?USER_ID, Addr, Oids). + case snmpm:target_name(Addr) of + {ok, TargetName} -> + snmpm:async_get_next2(?USER_ID, TargetName, Oids); + Error -> + Error + end. async_get_next(Addr, Port, Oids) -> - snmpm:agn(?USER_ID, Addr, Port, Oids). + case snmpm:target_name(Addr, Port) of + {ok, TargetName} -> + snmpm:async_get_next2(?USER_ID, TargetName, Oids); + Error -> + Error + end. %% -- sync_set(Addr, VAV) -> - snmpm:s(?USER_ID, Addr, VAV). + case snmpm:target_name(Addr) of + {ok, TargetName} -> + snmpm:sync_set2(?USER_ID, TargetName, VAV); + Error -> + Error + end. sync_set(Addr, Port, VAV) -> - snmpm:s(?USER_ID, Addr, Port, VAV). + case snmpm:target_name(Addr, Port) of + {ok, TargetName} -> + snmpm:sync_set2(?USER_ID, TargetName, VAV); + Error -> + Error + end. %% -- async_set(Addr, VAV) -> - snmpm:as(?USER_ID, Addr, VAV). + case snmpm:target_name(Addr) of + {ok, TargetName} -> + snmpm:async_set2(?USER_ID, TargetName, VAV); + Error -> + Error + end. async_set(Addr, Port, VAV) -> - snmpm:as(?USER_ID, Addr, Port, VAV). + case snmpm:target_name(Addr, Port) of + {ok, TargetName} -> + snmpm:async_set2(?USER_ID, TargetName, VAV); + Error -> + Error + end. %% -- sync_get_bulk(Addr, NonRep, MaxRep, Oids) -> - snmpm:gb(?USER_ID, Addr, NonRep, MaxRep, Oids). + case snmpm:target_name(Addr) of + {ok, TargetName} -> + snmpm:sync_get_bulk2(?USER_ID, TargetName, NonRep, MaxRep, Oids); + Error -> + Error + end. sync_get_bulk(Addr, Port, NonRep, MaxRep, Oids) -> - snmpm:gb(?USER_ID, Addr, Port, NonRep, MaxRep, Oids). + case snmpm:target_name(Addr, Port) of + {ok, TargetName} -> + snmpm:sync_get_bulk2(?USER_ID, TargetName, NonRep, MaxRep, Oids); + Error -> + Error + end. %% -- async_get_bulk(Addr, NonRep, MaxRep, Oids) -> - snmpm:agb(?USER_ID, Addr, NonRep, MaxRep, Oids). + case snmpm:target_name(Addr) of + {ok, TargetName} -> + snmpm:async_get_bulk2(?USER_ID, TargetName, NonRep, MaxRep, Oids); + Error -> + Error + end. async_get_bulk(Addr, Port, NonRep, MaxRep, Oids) -> - snmpm:agb(?USER_ID, Addr, Port, NonRep, MaxRep, Oids). + case snmpm:target_name(Addr, Port) of + {ok, TargetName} -> + snmpm:async_get_bulk2(?USER_ID, TargetName, NonRep, MaxRep, Oids); + Error -> + Error + end. %% -- diff --git a/lib/snmp/test/snmp_manager_user_test.erl b/lib/snmp/test/snmp_manager_user_test.erl index fefa1ad713..41d5c50b19 100644 --- a/lib/snmp/test/snmp_manager_user_test.erl +++ b/lib/snmp/test/snmp_manager_user_test.erl @@ -36,7 +36,7 @@ %% -compile(export_all). -export([ -all/0,groups/0,init_per_group/2,end_per_group/2, + all/0,groups/0,init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2, diff --git a/lib/snmp/test/snmp_test_lib.erl b/lib/snmp/test/snmp_test_lib.erl index e327456bc4..f0abae73e8 100644 --- a/lib/snmp/test/snmp_test_lib.erl +++ b/lib/snmp/test/snmp_test_lib.erl @@ -324,7 +324,7 @@ fail(Reason, Mod, Line) -> skip(Reason, Module, Line) -> String = lists:flatten(io_lib:format("Skipping ~p(~p): ~p~n", [Module, Line, Reason])), - exit({skipped, String}). + exit({skip, String}). %% ---------------------------------------------------------------- diff --git a/lib/snmp/test/snmp_test_manager.erl b/lib/snmp/test/snmp_test_manager.erl index 4cc6d36acc..0df2350d58 100644 --- a/lib/snmp/test/snmp_test_manager.erl +++ b/lib/snmp/test/snmp_test_manager.erl @@ -43,7 +43,7 @@ %% Manager callback API: -export([ handle_error/3, - handle_agent/4, + handle_agent/5, handle_pdu/4, handle_trap/3, handle_inform/3, @@ -239,7 +239,7 @@ handle_info({snmp_error, ReqId, Reason}, P ! {snmp_error, ReqId, Reason}, {noreply, State}; -handle_info({snmp_agent, Addr, Port, Info, Pid}, +handle_info({snmp_agent, Addr, Port, Info, Pid, _UserData}, #state{parent = P} = State) -> error_msg("detected new agent: " "~n Addr: ~w" @@ -326,8 +326,8 @@ handle_error(ReqId, Reason, Pid) -> ignore. -handle_agent(Addr, Port, SnmpInfo, Pid) -> - Pid ! {snmp_agent, Addr, Port, SnmpInfo, self()}, +handle_agent(Addr, Port, SnmpInfo, Pid, UserData) -> + Pid ! {snmp_agent, Addr, Port, SnmpInfo, self(), UserData}, receive {snmp_agent_reply, Reply, Pid} -> Reply diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk index b90dbe4eef..8145e415f3 100644 --- a/lib/snmp/vsn.mk +++ b/lib/snmp/vsn.mk @@ -18,6 +18,6 @@ # %CopyrightEnd% APPLICATION = snmp -SNMP_VSN = 4.22.1 +SNMP_VSN = 4.23 PRE_VSN = APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)" diff --git a/lib/ssh/doc/html/SSH_protocols.png b/lib/ssh/doc/html/SSH_protocols.png Binary files differnew file mode 100644 index 0000000000..145c96c4cd --- /dev/null +++ b/lib/ssh/doc/html/SSH_protocols.png diff --git a/lib/ssh/doc/man6/.gitignore b/lib/ssh/doc/man6/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/ssh/doc/man6/.gitignore diff --git a/lib/ssh/doc/src/Makefile b/lib/ssh/doc/src/Makefile index da99c4ea0f..0e79d9979f 100644 --- a/lib/ssh/doc/src/Makefile +++ b/lib/ssh/doc/src/Makefile @@ -37,21 +37,30 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # Target Specs # ---------------------------------------------------- XML_APPLICATION_FILES = ref_man.xml -XML_REF3_FILES = \ - ssh.xml \ +XML_REF3_FILES = ssh.xml \ ssh_channel.xml \ - ssh_connection.xml\ + ssh_connection.xml \ + ssh_client_key_api.xml \ + ssh_server_key_api.xml \ ssh_sftp.xml \ ssh_sftpd.xml \ -XML_PART_FILES = part_notes.xml -XML_CHAPTER_FILES = notes.xml +XML_REF6_FILES = ssh_app.xml + +XML_PART_FILES = part_notes.xml \ + usersguide.xml +XML_CHAPTER_FILES = notes.xml \ + introduction.xml \ + ssh_protocol.xml \ + using_ssh.xml BOOK_FILES = book.xml -XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \ +XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES)\ $(XML_PART_FILES) $(XML_CHAPTER_FILES) +IMAGE_FILES = SSH_protocols.png + # ---------------------------------------------------- HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \ @@ -62,10 +71,12 @@ EXTRA_FILES = \ $(DEFAULT_GIF_FILES) \ $(DEFAULT_HTML_FILES) \ $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \ + $(XML_REF6_FILES:%.xml=$(HTMLDIR)/%.html) \ $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html) MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3) +MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6) HTML_REF_MAN_FILE = $(HTMLDIR)/index.html @@ -80,16 +91,19 @@ DVIPS_FLAGS += # ---------------------------------------------------- # Targets # ---------------------------------------------------- -$(HTMLDIR)/%.gif: %.gif +$(HTMLDIR)/%.png: %.png $(INSTALL_DATA) $< $@ docs: pdf html man $(TOP_PDF_FILE): $(XML_FILES) +images: $(IMAGE_FILES:%=$(HTMLDIR)/%) + pdf: $(TOP_PDF_FILE) -html: $(HTML_REF_MAN_FILE) +html: images $(HTML_REF_MAN_FILE) + clean clean_docs: rm -rf $(HTMLDIR)/* @@ -97,7 +111,7 @@ clean clean_docs: rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ -man: $(MAN3_FILES) +man: $(MAN3_FILES) $(MAN6_FILES) debug opt: @@ -117,5 +131,8 @@ release_docs_spec: docs $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" + release_spec: diff --git a/lib/ssh/doc/src/SSH_protocols.png b/lib/ssh/doc/src/SSH_protocols.png Binary files differnew file mode 100644 index 0000000000..145c96c4cd --- /dev/null +++ b/lib/ssh/doc/src/SSH_protocols.png diff --git a/lib/ssh/doc/src/book.xml b/lib/ssh/doc/src/book.xml index fcec1d6f70..3c2375f96d 100644 --- a/lib/ssh/doc/src/book.xml +++ b/lib/ssh/doc/src/book.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE book SYSTEM "book.dtd"> <book xmlns:xi="http://www.w3.org/2001/XInclude"> <header titlestyle="normal"> <copyright> - <year>2005</year><year>2010</year> + <year>2005</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -34,6 +34,9 @@ <preamble> <contents level="2"></contents> </preamble> + <parts lift="yes"> + <xi:include href="usersguide.xml"/> + </parts> <applications> <xi:include href="ref_man.xml"/> </applications> diff --git a/lib/ssh/doc/src/fascicules.xml b/lib/ssh/doc/src/fascicules.xml index 43090b4aed..069d9002e0 100644 --- a/lib/ssh/doc/src/fascicules.xml +++ b/lib/ssh/doc/src/fascicules.xml @@ -1,7 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE fascicules SYSTEM "fascicules.dtd"> <fascicules> + <fascicule file="usersguide" href="usersguide_frame.html" entry="no"> + User's Guide + </fascicule> <fascicule file="ref_man" href="ref_man_frame.html" entry="yes"> Reference Manual </fascicule> diff --git a/lib/ssh/doc/src/introduction.xml b/lib/ssh/doc/src/introduction.xml new file mode 100644 index 0000000000..aac8de0f76 --- /dev/null +++ b/lib/ssh/doc/src/introduction.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2012</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. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + + <title>Introduction</title> + <prepared>OTP team</prepared> + <file>introduction.xml</file> + </header> + + <section> + <title>Purpose</title> + + <p>Secure Shell (SSH) is a protocol for secure remote login and + other secure network services over an insecure network. SSH + provides a single, full-duplex, byte-oriented connection between + client and server. The protocol also provides privacy, integrity, + server authentication and man-in-the-middle protection.</p> + + <p>The Erlang SSH application is an implementation of the SSH + protocol in Erlang which offers API functions to write customized + SSH clients and servers as well as making the Erlang shell + available via SSH. Also included in the SSH application are an + SFTP (SSH File Transfer Protocol) client <seealso + marker="ssh_sftp">ssh_sftp</seealso> and server <seealso + marker="ssh_sftp">ssh_sftpd</seealso>.</p> + </section> + + <section> + <title>Prerequisites</title> + <p>It is assumed that the reader is familiar with the concepts of <seealso marker="doc/design_principals:users_guide">OTP</seealso> + and has a basic understanding of <url href="http://en.wikipedia.org/wiki/Public-key_cryptography">public keys</url>.</p> + </section> + +</chapter> diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index d4acb2ef1a..e58d4e2c36 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> @@ -780,7 +780,7 @@ <list> <item> <p> - Ssh timeouts will now behave as expected e.i. defaults to + Ssh timeouts will now behave as expected i.e. defaults to infinity only the user of the ssh application can know of a reasonable timeout value for their application.</p> <p> @@ -833,7 +833,7 @@ caused the ssh daemon to close the connections to all currently logged in users if one user logged out. Another problem related to the supervision tree caused the closing - down of clients to leak processes e.i. all processes was + down of clients to leak processes i.e. all processes was not shutdown correctly.</p> <p> Own Id: OTP-7676</p> @@ -925,7 +925,7 @@ slightly changes the options to the API function ssh:daemon/[1,2,3] deprecating all no longer documented options. Note that the new API enforces the "logical way" - of using the old API e.i. making the subsystem process + of using the old API i.e. making the subsystem process part of the ssh applications supervisor tree, so missuses of the old API are not compatible with the new API.</p> <p> diff --git a/lib/ssh/doc/src/ref_man.xml b/lib/ssh/doc/src/ref_man.xml index 9ab56b28ec..88203b5034 100644 --- a/lib/ssh/doc/src/ref_man.xml +++ b/lib/ssh/doc/src/ref_man.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE application SYSTEM "application.dtd"> <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -22,19 +22,22 @@ </legalnotice> <title>SSH Reference Manual</title> - <prepared>Jakob Cederlund</prepared> - <docno></docno> + <prepared>OTP</prepared> <date>2007-10-06</date> <rev>%VSN%</rev> - <file>application.sgml</file> + <file>ref_man.xml</file> </header> <description> <p>The SSH application is an erlang implementation of the - secure shell protocol.</p> + secure shell protocol (SSH) as defined by RFC 4250 - 4254</p> + </description> + <xi:include href="ssh_app.xml"/> <xi:include href="ssh.xml"/> <xi:include href="ssh_channel.xml"/> <xi:include href="ssh_connection.xml"/> + <xi:include href="ssh_client_key_api.xml"/> + <xi:include href="ssh_server_key_api.xml"/> <xi:include href="ssh_sftp.xml"/> <xi:include href="ssh_sftpd.xml"/> </application> diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index 04b7a2ae56..a0afb5056e 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -22,13 +22,7 @@ </legalnotice> <title>ssh</title> - <prepared>Ingela Anderton Andin</prepared> - <responsible>Håkan Mattsson</responsible> - <docno></docno> - <approved>Håkan Mattsson</approved> - <checked></checked> <date>2007-10-06</date> - <rev>PA1</rev> </header> <module>ssh</module> <modulesummary>Main API of the SSH application</modulesummary> @@ -40,80 +34,85 @@ <title>SSH</title> <list type="bulleted"> - <item>ssh requires the crypto and public_key applications.</item> - <item>Supported SSH-version is 2.0 </item> - <item>Currently supports only a minimum of mac and encryption algorithms i.e. - hmac-sha1, and aes128-cb and 3des-cbc.</item> + <item>SSH requires the crypto and public_key applications.</item> + <item>Supported SSH version is 2.0 </item> + <item>Supported MAC algorithms: hmac-sha1</item> + <item>Supported encryption algorithms: aes128-cb and 3des-cbc</item> </list> </section> <section> - <title>COMMON DATA TYPES </title> + <title>DATA TYPES </title> <p>Type definitions that are used more than once in - this module:</p> + this module and/or abstractions to indicate the intended use of the data + type:</p> <p><c>boolean() = true | false </c></p> - <p><c>string() = list of ASCII characters</c></p> + <p><c>string() = [byte()]</c></p> <p><c>ssh_daemon_ref() - opaque to the user returned by ssh:daemon/[1,2,3]</c></p> <p><c>ssh_connection_ref() - opaque to the user returned by ssh:connect/3</c></p> <p><c>ip_address() - {N1,N2,N3,N4} % IPv4 | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6</c></p> - <p><c>subsystem_spec() = {subsystem_name(), {channel_callback(), channel_init_args()}} </c></p> + <p><c>subsystem_spec() = {subsystem_name(), + {channel_callback(), channel_init_args()}} </c></p> <p><c>subsystem_name() = string() </c></p> <p><c>channel_callback() = atom() - Name of the erlang module implementing the subsystem using the ssh_channel behavior see</c> <seealso marker="ssh_channel">ssh_channel(3)</seealso></p> <p><c>channel_init_args() = list()</c></p> </section> - + <funcs> <func> <name>close(ConnectionRef) -> ok </name> - <fsummary>Closes a ssh connection</fsummary> + <fsummary>Closes an SSH connection</fsummary> <type> <v>ConnectionRef = ssh_connection_ref()</v> </type> - <desc><p>Closes a ssh connection.</p> + <desc><p>Closes an SSH connection.</p> </desc> </func> <func> <name>connect(Host, Port, Options) -> </name> - <name>connect(Host, Port, Options, Timeout) -> {ok, ssh_connection_ref()} - | {error, Reason}</name> + <name>connect(Host, Port, Options, Timeout) -> {ok, + ssh_connection_ref()} | {error, Reason}</name> <fsummary>Connect to an ssh server.</fsummary> <type> <v>Host = string()</v> <v>Port = integer()</v> - <d>The default is <c><![CDATA[22]]></c>, the registered port for SSH.</d> + <d>The default is <c><![CDATA[22]]></c>, the assigned well known port + number for SSH.</d> <v>Options = [{Option, Value}]</v> <v>Timeout = infinity | integer(milliseconds)</v> </type> <desc> - <p>Connects to an SSH server. No channel is started this is done + <p>Connects to an SSH server. No channel is started. This is done by calling ssh_connect:session_channel/2.</p> <p>Options are:</p> <taglist> <tag><c><![CDATA[{user_dir, string()}]]></c></tag> <item> - <p>Sets the user directory e.i. the directory containing + <p>Sets the user directory i.e. the directory containing ssh configuration files for the user such as - <c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa, id_dsa]]></c> and - <c><![CDATA[authorized_key]]></c>. Defaults to the directory normally - referred to as <c><![CDATA[~/.ssh]]></c> </p> + <c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa, + id_dsa]]></c> and + <c><![CDATA[authorized_key]]></c>. Defaults to the + directory normally referred to as + <c><![CDATA[~/.ssh]]></c> </p> </item> <tag><c><![CDATA[{dsa_pass_phrase, string()}]]></c></tag> <item> - <p>If the user dsa key is protected by a pass phrase it can be + <p>If the user dsa key is protected by a passphrase it can be supplied with this option. </p> </item> <tag><c><![CDATA[{rsa_pass_phrase, string()}]]></c></tag> <item> - <p>If the user rsa key is protected by a pass phrase it can be + <p>If the user rsa key is protected by a passphrase it can be supplied with this option. </p> </item> @@ -135,25 +134,26 @@ password. Do note that it may not always be desirable to use those options from a security point of view.</p> </item> - <tag><c><![CDATA[{public_key_alg, ssh_rsa | ssh_dsa}]]></c></tag> + <tag><c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></tag> <item> <p>Sets the preferred public key algorithm to use for user - authentication. If the the preferred algorithm fails of + authentication. If the the preferred algorithm fails for some reason, the other algorithm is tried. The default is to try <c><![CDATA[ssh_rsa]]></c> first.</p> </item> <tag><c><![CDATA[{pref_public_key_algs, list()}]]></c></tag> <item> - <p>List of public key algorithms to try to use, ssh_rsa and ssh_dsa available. - Will override <c><![CDATA[{public_key_alg, ssh_rsa | ssh_dsa}]]></c></p> + <p>List of public key algorithms to try to use, 'ssh-rsa' and 'ssh-dss' available. + Will override <c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></p> </item> <tag><c><![CDATA[{connect_timeout, timeout()}]]></c></tag> <item> - <p>Sets a timeout on the transport layer connection. Defaults to infinity.</p> + <p>Sets a timeout on the transport layer + connection. Defaults to <c>infinity</c>.</p> </item> - <tag><c><![CDATA[{user, String}]]></c></tag> + <tag><c><![CDATA[{user, string()}]]></c></tag> <item> - <p>Provide a user name. If this option is not given, ssh + <p>Provides a user name. If this option is not given, ssh reads from the environment (<c><![CDATA[LOGNAME]]></c> or <c><![CDATA[USER]]></c> on unix, <c><![CDATA[USERNAME]]></c> on Windows).</p> @@ -165,23 +165,11 @@ password if the password authentication method is attempted.</p> </item> - <tag><c><![CDATA[{user_auth, Fun/3}]]></c></tag> - <item> - <p>Provide a fun for password authentication. The fun - will be called as <c><![CDATA[fun(User, Password, Opts)]]></c> and - should return <c><![CDATA[true]]></c> or <c><![CDATA[false]]></c>.</p> - </item> - <tag><c><![CDATA[{key_cb, atom() = KeyCallbackModule}]]></c></tag> + <tag><c><![CDATA[{key_cb, atom()}]]></c></tag> <item> - <p>Provide a special call-back module for key handling. - The call-back module should be modeled after the - <c><![CDATA[ssh_file]]></c> module. The functions that must - be exported are: - <c><![CDATA[private_host_rsa_key/2]]></c>, - <c><![CDATA[private_host_dsa_key/2]]></c>, - <c><![CDATA[lookup_host_key/3]]></c> and - <c><![CDATA[add_host_key/3]]></c>. This is considered - somewhat experimental and will be better documented later on.</p> + <p>Module implementing the behaviour <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso>. + Can be used to customize the handling of public keys. + </p> </item> <tag><c><![CDATA[{quiet_mode, atom() = boolean()}]]></c></tag> <item> @@ -189,20 +177,24 @@ </item> <tag><c><![CDATA[{fd, file_descriptor()}]]></c></tag> <item> - <p>Allow an existing file-descriptor to be used + <p>Allow an existing file descriptor to be used (simply passed on to the transport protocol).</p></item> - <tag><c><![CDATA[{ip_v6_disabled, boolean()}]]></c></tag> + <tag><c><![CDATA[{ipv6_disabled, boolean()}]]></c></tag> <item> - <p>Determines if SSH shall use IPv6 or not.</p></item> - <tag><c><![CDATA[{idle_time, timeout()}]]></c></tag> + <p>Determines if SSH shall use IPv6 or not.</p> + </item> + <tag><c><![CDATA[{rekey_limit, integer()}]]></c></tag> <item> - <p>Sets a timeout on connection when no channels are active, default is infinity</p></item> + <p>Provide, in bytes, when rekeying should be initiated, + defaults to one time each GB and one time per hour.</p> + </item> </taglist> </desc> </func> <func> - <name>connection_info(ConnectionRef, [Option]) ->[{Option, Value}] </name> + <name>connection_info(ConnectionRef, [Option]) ->[{Option, + Value}] </name> <fsummary> Retrieves information about a connection. </fsummary> <type> <v>Option = client_version | server_version | peer</v> @@ -217,7 +209,8 @@ <func> <name>daemon(Port) -> </name> <name>daemon(Port, Options) -> </name> - <name>daemon(HostAddress, Port, Options) -> ssh_daemon_ref()</name> + <name>daemon(HostAddress, Port, Options) -> {ok, + ssh_daemon_ref()} | {error, atom()}</name> <fsummary>Starts a server listening for SSH connections on the given port.</fsummary> <type> @@ -228,30 +221,32 @@ <v>Value = term()</v> </type> <desc> - <p>Starts a server listening for SSH connections on the given port.</p> - - <p>Options are:</p> + <p>Starts a server listening for SSH connections on the given + port.</p> + <p>Options are:</p> <taglist> <tag><c><![CDATA[{subsystems, [subsystem_spec()]]]></c></tag> <item> - Provides specifications for handling of subsystems. The - "sftp" subsystem-spec can be retrieved by calling - ssh_sftpd:subsystem_spec/1. If the subsystems option in not present - the value of <c>[ssh_sftpd:subsystem_spec([])]</c> will be used. - It is of course possible to set the option to the empty list - if you do not want the daemon to run any subsystems at all. + Provides specifications for handling of subsystems. The + "sftp" subsystem spec can be retrieved by calling + ssh_sftpd:subsystem_spec/1. If the subsystems option in + not present the value of + <c>[ssh_sftpd:subsystem_spec([])]</c> will be used. It is + of course possible to set the option to the empty list if + you do not want the daemon to run any subsystems at all. </item> - <tag><c><![CDATA[{shell, {Module, Function, Args} | fun(string() = User) - > pid() | - fun(string() = User, ip_address() = PeerAddr) -> pid()}]]></c></tag> + <tag><c><![CDATA[{shell, {Module, Function, Args} | + fun(string() = User) - > pid() | fun(string() = User, + ip_address() = PeerAddr) -> pid()}]]></c></tag> <item> - Defines the read-eval-print loop used when a shell is requested - by the client. Example use the - erlang shell: <c><![CDATA[{shell, start, []}]]></c> which is - the default behavior. + Defines the read-eval-print loop used when a shell is + requested by the client. Default is to use the erlang shell: + <c><![CDATA[{shell, start, []}]]></c> </item> - <tag><c><![CDATA[{ssh_cli,{channel_callback(), channel_init_args()}}]]></c></tag> + <tag><c><![CDATA[{ssh_cli,{channel_callback(), + channel_init_args()}}]]></c></tag> <item> - Provide your own cli implementation, e.i. a channel callback + Provides your own cli implementation, i.e. a channel callback module that implements a shell and command execution. Note that you may customize the shell read-eval-print loop using the option <c>shell</c> which is much less work than implementing @@ -259,27 +254,30 @@ </item> <tag><c><![CDATA[{user_dir, String}]]></c></tag> <item> - <p>Sets the user directory e.i. the directory containing + <p>Sets the user directory i.e. the directory containing ssh configuration files for the user such as - <c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa, id_dsa]]></c> and - <c><![CDATA[authorized_key]]></c>. Defaults to the directory normally - referred to as <c><![CDATA[~/.ssh]]></c> </p> + <c><![CDATA[known_hosts]]></c>, <c><![CDATA[id_rsa, + id_dsa]]></c> and + <c><![CDATA[authorized_key]]></c>. Defaults to the + directory normally referred to as + <c><![CDATA[~/.ssh]]></c> </p> </item> <tag><c><![CDATA[{system_dir, string()}]]></c></tag> <item> - <p>Sets the system directory, containing the host files - that identifies the host for ssh. The default is - <c><![CDATA[/etc/ssh]]></c>, note that SSH normally - requires the host files there to be readable only by - root.</p> + <p>Sets the system directory, containing the host key files + that identifies the host keys for ssh. The default is + <c><![CDATA[/etc/ssh]]></c>, note that for security reasons + this directory is normally only accessible by the root user.</p> </item> <tag><c><![CDATA[{auth_methods, string()}]]></c></tag> <item> - <p>Comma separated string that determines which authentication methodes that the server - should support and in what order they will be tried. Defaults to + <p>Comma separated string that determines which + authentication methodes that the server should support and + in what order they will be tried. Defaults to <c><![CDATA["publickey,keyboard-interactive,password"]]></c></p> </item> - <tag><c><![CDATA[{user_passwords, [{string() = User, string() = Password}]}]]></c></tag> + <tag><c><![CDATA[{user_passwords, [{string() = User, + string() = Password}]}]]></c></tag> <item> <p>Provide passwords for password authentication.They will be used when someone tries to connect to the server and @@ -293,13 +291,19 @@ user. From a security perspective this option makes the server very vulnerable.</p> </item> - <tag><c><![CDATA[{pwdfun, fun/2}]]></c></tag> + <tag><c><![CDATA[{pwdfun, fun(User::string(), password::string() -> boolean()}]]></c></tag> <item> <p>Provide a function for password validation. This is called with user and password as strings, and should return <c><![CDATA[true]]></c> if the password is valid and <c><![CDATA[false]]></c> otherwise.</p> </item> + <tag><c><![CDATA[{key_cb, atom()}]]></c></tag> + <item> + <p>Module implementing the behaviour <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>. + Can be used to customize the handling of public keys. + </p> + </item> <tag><c><![CDATA[{fd, file_descriptor()}]]></c></tag> <item> <p>Allow an existing file-descriptor to be used @@ -335,9 +339,9 @@ <v> Options - see ssh:connect/3</v> </type> <desc> - <p>Starts an interactive shell to an SSH server on the + <p>Starts an interactive shell via an SSH server on the given <c>Host</c>. The function waits for user input, - and will not return until the remote shell is ended (e.g. on + and will not return until the remote shell is ended (i.e. exit from the shell). </p> </desc> @@ -346,25 +350,24 @@ <func> <name>start() -> </name> <name>start(Type) -> ok | {error, Reason}</name> - <fsummary>Starts the Ssh application. </fsummary> + <fsummary>Starts the SSH application. </fsummary> <type> <v>Type = permanent | transient | temporary</v> <v>Reason = term() </v> </type> <desc> - <p>Starts the Ssh application. Default type - is temporary. See also - <seealso marker="kernel:application">application(3)</seealso> - Requires that the crypto application has been started. + <p>Utility function that starts crypto, public_key and the SSH + application. Defult type is temporary. + See also <seealso marker="kernel:application">application(3)</seealso> </p> </desc> </func> <func> <name>stop() -> ok </name> - <fsummary>Stops the Ssh application.</fsummary> + <fsummary>Stops the SSH application.</fsummary> <desc> - <p>Stops the Ssh application. See also + <p>Stops the SSH application. See also <seealso marker="kernel:application">application(3)</seealso></p> </desc> </func> diff --git a/lib/ssh/doc/src/ssh_app.xml b/lib/ssh/doc/src/ssh_app.xml new file mode 100644 index 0000000000..c01f44936a --- /dev/null +++ b/lib/ssh/doc/src/ssh_app.xml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE appref SYSTEM "appref.dtd"> + +<appref> + <header> + <copyright> + <year>2012</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>SSH</title> + <file>ssh_app.xml</file> + </header> + <app>SSH</app> + <appsummary>The ssh application implements the SSH (Secure Shell) protocol and + provides an SFTP (SSH File Transfer Protocol) client and server. </appsummary> + + <section> + <title>DEPENDENCIES</title> + <p>The ssh application uses the Erlang applications public_key and + crypto to handle public keys and encryption, hence these + applications needs to be loaded for the ssh application to work. In + an embedded environment that means they need to be started with + application:start/[1,2] before the ssh application is started. + </p> + </section> + + <section> + <title>CONFIGURATION</title> + + <p>The ssh application does not currently have an application + specific configuration file as described in application(3), + however it will by default use the following configuration files + from openssh: known_hosts, authorized_keys, authorized_keys2, + id_dsa and id_rsa, ssh_host_dsa_key and ssh_host_rsa_key. By + default Erlang SSH will look for id_dsa, id_rsa, known_hosts + and authorized_keys in ~/.ssh, and the host key files in /etc/ssh + . These locations may be changed by the options user_dir and + system_dir. Public key handling may also be customized by + providing a callback module implementing the behaviors + <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso> and + <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>. + </p> + + <section> + <title>PUBLIC KEYS</title> + <p> + id_dsa and id_rsa are the users private key files, note that + the public key is part of the private key so the ssh + application will not use the id_<*>.pub files. These are + for the users convenience when he/she needs to convey their + public key. + </p> + </section> + + <section> + <title>KNOW HOSTS</title> + <p>The known_hosts file contains a list of approved servers and + their public keys. Once a server is listed, it can be verified + without user interaction. + </p> + </section> + + <section> + <title>AUTHORIZED KEYS</title> + <p>The authorized key file keeps track of the user's authorized + public keys. The most common use of this file is to let users + log in without entering their password which is supported by the + Erlang SSH daemon. + </p> + </section> + + <section> + <title>HOST KEYS</title> + <p>Currently rsa and dsa host keys are supported and are + expected to be found in files named ssh_host_rsa_key and + ssh_host_dsa_key. + </p> + </section> + </section> + + <section> + <title>SEE ALSO</title> + <p>application(3)</p> + </section> + +</appref> diff --git a/lib/ssh/doc/src/ssh_channel.xml b/lib/ssh/doc/src/ssh_channel.xml index c2b7aa94a5..f0083ae8d1 100644 --- a/lib/ssh/doc/src/ssh_channel.xml +++ b/lib/ssh/doc/src/ssh_channel.xml @@ -1,11 +1,11 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> <year>2009</year> - <year>2009</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -22,31 +22,35 @@ The Initial Developer of the Original Code is Ericsson AB. </legalnotice> - <title>ssh_channel</title> - <prepared>Ingela Anderton Andin</prepared> - <responsible></responsible> - <docno></docno> - <approved></approved> - <checked></checked> - <date></date> - <rev></rev> </header> <module>ssh_channel</module> - <modulesummary>Generic Ssh Channel Behavior + <modulesummary>-behaviour(ssh_channel). </modulesummary> <description> - <p>Ssh services are implemented as channels that are multiplexed - over an ssh connection and communicates via the ssh connection - protocol. This module provides a callback API that takes care of - generic channel aspects such as flow control and close messages - and lets the callback functions take care of the service specific - parts. + <p>SSH services (clients and servers) are implemented as channels + that are multiplexed over an SSH connection and communicates via + the <url href="http://www.ietf.org/rfc/rfc4254.txt"> SSH + Connection Protocol </url>. This module provides a callback API + that takes care of generic channel aspects such as flow control + and close messages and lets the callback functions take care of + the service (application) specific parts. This behavior also ensures + that the channel process honors the principal of an OTP-process so + that it can be part of a supervisor tree. This is a requirement of + channel processes implementing a subsystem that will be added to + the SSH applications supervisor tree. </p> + + <note> When implementing a SSH subsystem use the + <c>-behaviour(ssh_subsystem).</c> instead of <c>-behaviour(ssh_channel).</c> + as the only relevant callback functions for subsystems are + init/1, handle_ssh_msg/2, handle_msg/2 and terminate/2, so the ssh_subsystem + behaviour is limited version of the ssh_channel behaviour. + </note> </description> <section> - <title>COMMON DATA TYPES </title> + <title>DATA TYPES </title> <p>Type definitions that are used more than once in this module and/or abstractions to indicate the intended use of the data @@ -56,10 +60,10 @@ <p><c>string() = list of ASCII characters</c></p> <p><c>timeout() = infinity | integer() - in milliseconds.</c></p> <p><c>ssh_connection_ref() - opaque to the user returned by - ssh:connect/3 or sent to a ssh channel process</c></p> + ssh:connect/3 or sent to an SSH channel process</c></p> <p><c>ssh_channel_id() = integer() </c></p> <p><c>ssh_data_type_code() = 1 ("stderr") | 0 ("normal") are - currently valid values see RFC 4254 section 5.2.</c></p> + currently valid values see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 5.2.</c></p> </section> <funcs> @@ -74,13 +78,15 @@ <v>Timeout = timeout() </v> <v>Reply = term() </v> <v>Reason = closed | timeout </v> + </type> <desc> <p>Makes a synchronous call to the channel process by sending a message and waiting until a reply arrives or a timeout - occurs. The channel will call - <c>CallbackModule:handle_call/3</c> to handle the message. - If the channel process does not exist <c>{error, closed}</c> is returned. + occurs. The channel will call <seealso mark = + "#Module:handle_call-3">Module:handle_call/3</seealso> + to handle the message. If the channel process does not exist + <c>{error, closed}</c> is returned. </p> </desc> </func> @@ -98,26 +104,27 @@ <p>Sends an asynchronous message to the channel process and returns ok immediately, ignoring if the destination node or channel process does not exist. The channel will call - <c>CallbackModule:handle_cast/2</c> to handle the message. + <seealso mark = "#Module:handle_cast-3">Module:handle_cast/2</seealso> + to handle the message. </p> </desc> </func> <func> <name>enter_loop(State) -> _ </name> - <fsummary> Makes an existing process into a ssh_channel process. </fsummary> + <fsummary> Makes an existing process an ssh_channel process. </fsummary> <type> - <v> State = term() - as returned by ssh_channel:init/1</v> + <v> State = term() - as returned by <seealso mark = "#init-1">ssh_channel:init/1</seealso></v> </type> <desc> - <p> Makes an existing process into a <c>ssh_channel</c> + <p> Makes an existing process an <c>ssh_channel</c> process. Does not return, instead the calling process will - enter the <c>ssh_channel</c> process receive loop and become a + enter the <c>ssh_channel</c> process receive loop and become an <c>ssh_channel process.</c> The process must have been started using one of the start functions in proc_lib, see <seealso marker="stdlib:proc_lib">proc_lib(3)</seealso>. The user is responsible for any initialization of the process - and needs to call ssh_channel:init/1. + and needs to call <seealso mark = "#init-1">ssh_channel:init/1</seealso> </p> </desc> </func> @@ -126,7 +133,10 @@ <name>init(Options) -> {ok, State} | {ok, State, Timeout} | {stop, Reason} </name> <fsummary> Initiates a ssh_channel process.</fsummary> <type> - <v> Options = [{Option, Value}]</v> + <v>Options = [{Option, Value}]</v> + <v>State = term()</v> + <v>Timeout = timeout() </v> + <v>Reason = term() </v> </type> <desc> <p> @@ -134,24 +144,27 @@ </p> <taglist> <tag><c><![CDATA[{channel_cb, atom()}]]></c></tag> - <item>The module that implements the channel behavior.</item> + <item>The module that implements the channel behaviour.</item> <tag><c><![CDATA[{init_args(), list()}]]></c></tag> - <item> The list of arguments to the callback modules + <item> The list of arguments to the callback module's init function.</item> <tag><c><![CDATA[{cm, connection_ref()}]]></c></tag> - <item> Reference to the ssh connection.</item> + <item> Reference to the ssh connection as returned by <seealso + marker="ssh#connect-3">ssh:connect/3</seealso></item> <tag><c><![CDATA[{channel_id, channel_id()}]]></c></tag> <item> Id of the ssh channel.</item> </taglist> - <note><p>This function is normally not called by the user, it is - only needed if for some reason the channel process needs - to be started with help of <c>proc_lib</c> instead calling - <c>ssh_channel:start/4</c> or <c>ssh_channel:start_link/4</c> </p> + <note><p>This function is normally not called by the + user. The user only needs to call if for some reason the + channel process needs to be started with help of + <c>proc_lib</c> instead of calling + <c>ssh_channel:start/4</c> or + <c>ssh_channel:start_link/4</c> </p> </note> </desc> </func> @@ -167,12 +180,12 @@ <p>This function can be used by a channel to explicitly send a reply to a client that called <c>call/[2,3]</c> when the reply cannot be defined in the return value of - <c>CallbackModule:handle_call/3</c>.</p> + <seealso marker ="#Module:handle_call-3">Module:handle_call/3</seealso>.</p> <p><c>Client</c> must be the <c>From</c> argument provided to the callback function <c>handle_call/3</c>. <c>Reply</c> is an arbitrary term, - which will be given back to the client as the return value of - <c>ssh_channel:call/[2,3].</c></p> + which will be given back to the client as the return value of + <seealso marker="#call-2">ssh_channel:call/[2,3].</seealso>></p> </desc> </func> @@ -180,11 +193,12 @@ <name>start(SshConnection, ChannelId, ChannelCb, CbInitArgs) -> </name> <name>start_link(SshConnection, ChannelId, ChannelCb, CbInitArgs) -> {ok, ChannelRef} | {error, Reason}</name> - <fsummary> Starts a processes that handles a ssh channel. </fsummary> + <fsummary> Starts a processes that handles a SSH channel. </fsummary> <type> <v>SshConnection = ssh_connection_ref()</v> <v>ChannelId = ssh_channel_id() </v> - <d> As returned by ssh_connection:session_channel/[2,4]</d> + <d> As returned by cannot be defined in the return value of + <seealso marker ="ssh_connection#session_channel/2">ssh_connection:session_channel/[2,4]</seealso></d> <v>ChannelCb = atom()</v> <d> The name of the module implementing the service specific parts of the channel.</d> @@ -193,10 +207,10 @@ <v>ChannelRef = pid()</v> </type> <desc> - <p>Starts a processes that handles a ssh channel. Will be - called internally by the ssh daemon or explicitly by the ssh - client implementations. A channel process traps exit signals - by default. + <p>Starts a processes that handles an SSH channel. It will be + called internally by the SSH daemon or explicitly by the SSH + client implementations. The behavior will set the + <c>trap_exit</c> flag to true. </p> </desc> </func> @@ -204,72 +218,61 @@ </funcs> <section> - <title>CALLBACK FUNCTIONS</title> - - <p>The functions init/1, terminate/2, handle_ssh_msg/2 and - handle_msg/2 are the functions that are required to provide the - implementation for a server side channel, such as a ssh subsystem - channel that can be plugged into the erlang ssh daemon see - <seealso marker="ssh">ssh:daemon/[2, 3]</seealso>. The - handle_call/3, handle_cast/2 code_change/3 and enter_loop/1 - functions are only relevant when implementing a client side - channel.</p> - </section> - - <section> <marker id="cb_timeouts"></marker> <title> CALLBACK TIMEOUTS</title> - <p> If an integer timeout value is provided in a return value of - one of the callback functions, a timeout will occur unless a - message is received within <c>Timeout</c> milliseconds. A timeout - is represented by the atom <c>timeout</c> which should be handled - by the <seealso marker="#handle_msg">handle_msg/2</seealso> - callback function. The atom infinity can be used to wait - indefinitely, this is the default value. </p> + + <p>The timeout values that may be returned by the callback functions + has the same semantics as in a <seealso marker="stdlib#gen_server">gen_server</seealso> + If the timeout occurs <seealso marker="#handle_msg">handle_msg/2</seealso> + will be called as <c>handle_msg(timeout, State). </c></p> </section> <funcs> <func> - <name>CallbackModule:code_change(OldVsn, State, Extra) -> {ok, + <name>Module:code_change(OldVsn, State, Extra) -> {ok, NewState}</name> <fsummary> Converts process state when code is changed.</fsummary> <type> - <v> Converts process state when code is changed.</v> + <v>OldVsn = term()</v> + <d>In the case of an upgrade, <c>OldVsn</c> is <c>Vsn</c>, and + in the case of a downgrade, <c>OldVsn</c> is + <c>{down,Vsn}</c>. <c>Vsn</c> is defined by the <c>vsn</c> + attribute(s) of the old version of the callback module + <c>Module</c>. If no such attribute is defined, the version is + the checksum of the BEAM file.</d> + <v>State = term()</v> + <d>The internal state of the channel.</d> + <v>Extra = term()</v> + <d>Passed as-is from the <c>{advanced,Extra}</c> + part of the update instruction.</d> </type> <desc> - <p>This function is called by a client side channel when it - should update its internal state during a release - upgrade/downgrade, i.e. when the instruction - <c>{update,Module,Change,...}</c> where - <c>Change={advanced,Extra}</c> is given in the <c>appup</c> - file. See <seealso - marker="doc/design_principles:release_handling#instr">OTP - Design Principles</seealso> for more information. Any new - connection will benefit from a server side upgrade but - already started connections on the server side will not be - affected. - </p> + <p> Converts process state when code is changed.</p> + + <p>This function is called by a client side channel when it + should update its internal state during a release + upgrade/downgrade, i.e. when the instruction + <c>{update,Module,Change,...}</c> where + <c>Change={advanced,Extra}</c> is given in the <c>appup</c> + file. See <seealso marker="doc/design_principles:release_handling#instr">OTP + Design Principles</seealso> for more information. + </p> - <note><p>If there are long lived ssh connections and more - than one upgrade in a short time this may cause the old - connections to fail as only two versions of the code may - be loaded simultaneously.</p></note> + <note><p>Soft upgrade according to the OTP release concept + is not straight forward for the server side, as subsystem + channel processes are spawned by the SSH application and + hence added to its supervisor tree. It could be possible to + upgrade the subsystem channels, when upgrading the user + application, if the callback functions can handle two + versions of the state, but this function can not be used in + the normal way.</p> + </note> - <p>In the case of an upgrade, <c>OldVsn</c> is <c>Vsn</c>, and - in the case of a downgrade, <c>OldVsn</c> is - <c>{down,Vsn}</c>. <c>Vsn</c> is defined by the <c>vsn</c> - attribute(s) of the old version of the callback module - <c>Module</c>. If no such attribute is defined, the version - is the checksum of the BEAM file.</p> - <p><c>State</c> is the internal state of the channel.</p> - <p><c>Extra</c> is passed as-is from the <c>{advanced,Extra}</c> - part of the update instruction.</p> - <p>The function should return the updated internal state.</p> </desc> </func> <func> - <name>CallbackModule:init(Args) -> {ok, State} | {ok, State, Timeout} | + <name>Module:init(Args) -> {ok, State} | {ok, State, timeout()} | {stop, Reason}</name> <fsummary> Makes necessary initializations and returns the initial channel state if the initializations succeed.</fsummary> @@ -277,7 +280,6 @@ <v> Args = term() </v> <d> Last argument to ssh_channel:start_link/4.</d> <v> State = term() </v> - <v>Timeout = timeout() </v> <v> Reason = term() </v> </type> <desc> @@ -290,7 +292,7 @@ </func> <func> - <name>CallbackModule:handle_call(Msg, From, State) -> Result</name> + <name>Module:handle_call(Msg, From, State) -> Result</name> <fsummary> Handles messages sent by calling <c>ssh_channel:call/[2,3]</c></fsummary> <type> @@ -298,17 +300,16 @@ <v>From = opaque to the user should be used as argument to ssh_channel:reply/2</v> <v>State = term()</v> - <v>Result = {reply, Reply, NewState} | {reply, Reply, NewState, Timeout} - | {noreply, NewState} | {noreply , NewState, Timeout} + <v>Result = {reply, Reply, NewState} | {reply, Reply, NewState, timeout()} + | {noreply, NewState} | {noreply , NewState, timeout()} | {stop, Reason, Reply, NewState} | {stop, Reason, NewState} </v> <v>Reply = term() - will be the return value of ssh_channel:call/[2,3]</v> - <v>Timeout = timeout() </v> - <v>NewState = term() - a possible updated version of State</v> + <v>NewState = term()</v> <v>Reason = term()</v> </type> <desc> <p>Handles messages sent by calling - <c>ssh_channel:call/[2,3]</c> + <seealso marker="#call-2">ssh_channel:call/[2,3]</seealso> </p> <p>For more detailed information on timeouts see the section <seealso marker="#cb_timeouts">CALLBACK TIMEOUTS</seealso>. </p> @@ -316,16 +317,15 @@ </func> <func> - <name>CallbackModule:handle_cast(Msg, State) -> Result</name> + <name>Module:handle_cast(Msg, State) -> Result</name> <fsummary> Handles messages sent by calling <c>ssh_channel:cact/2</c></fsummary> <type> <v>Msg = term()</v> <v>State = term()</v> - <v>Result = {noreply, NewState} | {noreply, NewState, Timeout} + <v>Result = {noreply, NewState} | {noreply, NewState, timeout()} | {stop, Reason, NewState}</v> - <v>NewState = term() - a possible updated version of State</v> - <v>Timeout = timeout() </v> + <v>NewState = term() </v> <v>Reason = term()</v> </type> <desc> @@ -339,7 +339,7 @@ </func> <func> - <name>CallbackModule:handle_msg(Msg, State) -> {ok, State} | + <name>Module:handle_msg(Msg, State) -> {ok, State} | {stop, ChannelId, State}</name> <fsummary> Handle other messages than ssh connection protocol, @@ -359,136 +359,37 @@ <taglist> <tag><c><![CDATA[{ssh_channel_up, ssh_channel_id(), ssh_connection_ref()}]]></c></tag> - <item>This is the first messages that will be received - by the channel, it is sent just before - the ssh_channel:init/1 function returns successfully. - This is especially useful if the server wants - to send a message to the client without first receiving - a message from the client. If the message is not useful - for your particular problem just ignore it by immediately - returning {ok, State}. + <item>This is the first messages that will be received by + the channel, it is sent just before the <seealso + marker="#init-2">ssh_channel:init/1</seealso> function + returns successfully. This is especially useful if the + server wants to send a message to the client without first + receiving a message from it. If the message is not + useful for your particular scenario just ignore it by + immediately returning {ok, State}. </item> </taglist> </desc> </func> <func> - <name>CallbackModule:handle_ssh_msg(Msg, State) -> {ok, State} | {stop, + <name>Module:handle_ssh_msg(Msg, State) -> {ok, State} | {stop, ssh_channel_id(), State}</name> <fsummary> Handles ssh connection protocol messages. </fsummary> <type> - <v>Msg = {ssh_cm, ssh_connection_ref(), SshMsg}</v> - <v> SshMsg = tuple() - see message list below</v> + <v>Msg = <seealso marker="ssh_connection"> ssh_connection:event() </seealso> </v> <v>State = term()</v> </type> <desc> <p> Handles ssh connection protocol messages that may need service specific attention. </p> - - <p> All channels should handle the following messages. For - channels implementing subsystems the handle_ssh_msg-callback - will not be called for any other messages. </p> - - <taglist> - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {data, ssh_channel_id(), - ssh_data_type_code(), binary() = Data}}]]></c></tag> - <item> Data has arrived on the channel. When the callback - for this message returns the channel behavior will adjust - the ssh flow control window.</item> - - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {eof, - ssh_channel_id()}}]]></c></tag> - <item>Indicteas that the other side will not send any more - data.</item> - - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {signal, - ssh_channel_id(), ssh_signal()}} ]]></c></tag> - <item>A signal can be delivered to the remote - process/service using the following message. Some systems - may not implement signals, in which case they should ignore - this message.</item> - - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), - {exit_signal, ssh_channel_id(), string() = exit_signal, - string() = ErrorMsg, string() = - LanguageString}}]]></c></tag> - <item>A remote execution may terminate violently due to a - signal then this message may be received. For details on valid string - values see RFC 4254 section 6.10</item> - - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {exit_status, - ssh_channel_id(), integer() = ExitStatus}}]]></c></tag> - <item> When the command running at the other end terminates, - the following message can be sent to return the exit status - of the command. A zero 'exit_status' usually means that the - command terminated successfully.</item> - </taglist> - - <p> Channels implementing a shell and command execution on the server side - should also handle the following messages. </p> - - <taglist> - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {env, ssh_channel_id(), - boolean() = WantReply, string() = Var, string() = Value}}]]></c></tag> - <item> Environment variables may be passed to the - shell/command to be started later. Note that before the - callback returns it should call the function - ssh_connection:reply_request/4 with the boolean value of <c> - WantReply</c> as the second argument. - </item> - - <tag><c><![CDATA[{ssh_cm, ConnectionRef, {exec, ssh_channel_id(), - boolean() = WantReply, string() = Cmd}}]]></c></tag> - <item> This message will request that the server start the - execution of the given command. Note that before the - callback returns it should call the function - ssh_connection:reply_request/4 with the boolean value of <c> - WantReply</c> as the second argument.</item> - - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {pty, ssh_channel_id(), - boolean() = WantReply, {string() = Terminal, integer() = CharWidth, - integer() = RowHeight, integer() = PixelWidth, integer() = PixelHight, - [{atom() | integer() = Opcode, - integer() = Value}] = TerminalModes}}}]]></c></tag> - <item>A pseudo-terminal has been requested for the - session. Terminal is the value of the TERM environment - variable value (e.g., vt100). Zero dimension parameters must - be ignored. The character/row dimensions override the pixel - dimensions (when nonzero). Pixel dimensions refer to the - drawable area of the window. The <c>Opcode</c> in the - <c>TerminalModes</c> list is the mnemonic name, represented - as an lowercase erlang atom, defined in RFC 4254 section 8, - or the opcode if the mnemonic name is not listed in the - RFC. Example <c>OP code: 53, mnemonic name ECHO erlang atom: - echo</c>. Note that before the callback returns it should - call the function ssh_connection:reply_request/4 with the - boolean value of <c> WantReply</c> as the second - argument.</item> - - <tag><c><![CDATA[{ssh_cm, ConnectionRef, {shell, boolean() = - WantReply}}]]></c></tag> - <item> This message will request that the user's default - shell be started at the other end. Note that before the - callback returns it should call the function - ssh_connection:reply_request/4 with the value of <c> - WantReply</c> as the second argument. - </item> - - <tag><c><![CDATA[ {ssh_cm, ssh_connection_ref(), {window_change, - ssh_channel_id(), integer() = CharWidth, integer() = RowHeight, - integer() = PixWidth, integer() = PixHeight}}]]></c></tag> - <item> When the window (terminal) size changes on the client - side, it MAY send a message to the other side to inform it - of the new dimensions.</item> - </taglist> <p> The following message is completely taken care of by the ssh channel behavior</p> <taglist> - <tag><c><![CDATA[{ssh_cm, ssh_connection_ref(), {closed, - ssh_channel_id()}}]]></c></tag> + <tag><c><![CDATA[{closed, ssh_channel_id()}]]></c></tag> <item> The channel behavior will send a close message to the other side if such a message has not already been sent and then terminate the channel with reason normal.</item> @@ -497,7 +398,7 @@ </func> <func> - <name>CallbackModule:terminate(Reason, State) -> _</name> + <name>Module:terminate(Reason, State) -> _</name> <fsummary> </fsummary> <type> <v>Reason = term()</v> @@ -505,12 +406,12 @@ </type> <desc> <p>This function is called by a channel process when it is - about to terminate. Before this function is called ssh_connection:close/2 - will be called if it has not been called earlier. - This function should be the opposite of <c>CallbackModule:init/1</c> - and do any necessary cleaning up. When it returns, the - channel process terminates with reason <c>Reason</c>. The return value is - ignored. + about to terminate. Before this function is called <seealso + marker="ssh_connection#close-2"> ssh_connection:close/2 + </seealso> will be called if it has not been called earlier. + This function should do any necessary cleaning + up. When it returns, the channel process terminates with + reason <c>Reason</c>. The return value is ignored. </p> </desc> </func> diff --git a/lib/ssh/doc/src/ssh_client_key_api.xml b/lib/ssh/doc/src/ssh_client_key_api.xml new file mode 100644 index 0000000000..abc1070e78 --- /dev/null +++ b/lib/ssh/doc/src/ssh_client_key_api.xml @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2012</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. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + <title>ssh_client_key_api</title> + </header> + <module>ssh_client_key_api</module> + <modulesummary> + -behaviour(ssh_client_key_api). + </modulesummary> + <description> + <p> Behavior describing the API for an SSH client's public key handling. + By implementing the callbacks defined. + in this behavior it is possible to customize the SSH client's public key + handling. By default the SSH application implements this behavior + with help of the standard openssh files, see <seealso marker="SSH_app"> ssh(6)</seealso>. </p> + </description> + + <section> + <title>DATA TYPES </title> + + <p>Type definitions that are used more than once in this module + and/or abstractions to indicate the intended use of the data + type:</p> + + <p> boolean() = true | false</p> + <p> string() = [byte()] </p> + <p> public_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p> + <p> private_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p> + <p> public_key_algorithm() = 'ssh-rsa'| 'ssh-dss' | atom()</p> + + </section> + + <funcs> + <func> + <name>Module:add_host_key(HostNames, Key, ConnectOptions) -> ok | {error, Reason}</name> + <fsummary>Adds a host key to the set of trusted host keys</fsummary> + <type> + <v>HostNames = string()</v> + <d>Description of the host that owns the <c>PublicKey</c></d> + + <v>Key = public_key() </v> + <d> Normally an RSA or DSA public key but handling of other public keys can be added</d> + + <v>ConnectOptions = proplists:proplist() </v> + <d>Options provided to <seealso marker="ssh#daemon">ssh:connect/[3,4]</seealso></d> + <v>Reason = term() </v> + </type> + <desc> + <p> Adds a host key to the set of trusted host keys</p> + </desc> + </func> + + <func> + <name>Module:is_host_key(Key, Host, Algorithm, ConnectOptions) -> Result</name> + <fsummary>Checks if a host key is trusted</fsummary> + <type> + <v>Key = public_key() </v> + <d> Normally an RSA or DSA public key but handling of other public keys can be added</d> + + <v>Host = string()</v> + <d>Description of the host</d> + + <v>Algorithm = public_key_algorithm()</v> + <d> Host key algorithm. Should support 'ssh-rsa'| 'ssh-dss' but additional algorithms + can be handled.</d> + + <v> ConnectOptions = proplists:proplist() </v> + <d>Options provided to <seealso marker="ssh#daemon">ssh:connect/[3,4]</seealso></d> + + <v> Result = boolean()</v> + </type> + <desc> + <p>Checks if a host key is trusted</p> + </desc> + </func> + + <func> + <name>Module:user_key(Algorithm, ConnectOptions) -> + {ok, PrivateKey} | {error, Reason}</name> + <fsummary>Fetches the users "public key" matching the <c>Algorithm</c>.</fsummary> + <type> + <v>Algorithm = public_key_algorithm()</v> + <d> Host key algorithm. Should support 'ssh-rsa'| 'ssh-dss' but additional algorithms + can be handled.</d> + + <v> ConnectOptions = proplists:proplist() </v> + <d>Options provided to <seealso marker="ssh#daemon">ssh:connect/[3,4]</seealso></d> + + <v> PrivateKey = private_key()</v> + <d> The private key of the user matching the <c>Algorithm</c></d> + + <v>Reason = term() </v> + </type> + + <desc> + <p>Fetches the users "public key" matching the <c>Algorithm</c>. + <note>The private key contains the public key</note> + </p> + </desc> + </func> + + </funcs> + +</erlref> diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml index a9ae13d556..c66622307f 100644 --- a/lib/ssh/doc/src/ssh_connection.xml +++ b/lib/ssh/doc/src/ssh_connection.xml @@ -1,11 +1,11 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> <year>2008</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -24,69 +24,178 @@ </legalnotice> <title>ssh_connection</title> - <prepared>Ingela Anderton Andin</prepared> - <responsible></responsible> - <docno></docno> - <approved></approved> - <checked></checked> <date></date> - <rev></rev> </header> <module>ssh_connection</module> - <modulesummary>This module provides an API to the ssh connection protocol. + <modulesummary>This module provides API functions to send <url href="http://www.ietf.org/rfc/rfc4254.txt"> SSH Connection Protocol </url> + events to the other side of an SSH channel. </modulesummary> + <description> - <p>This module provides an API to the ssh connection protocol. - Not all features of the connection protocol are officially supported yet. - Only the ones supported are documented here.</p> + <p>The SSH Connection Protocol is used by clients and servers + (i.e. SSH channels) to communicate over the SSH connection. The + API functions in this module sends SSH Connection Protocol events + that are received as messages by the remote channel. + In the case that the receiving channel is an Erlang process the + message will be on the following format + <c><![CDATA[{ssh_cm, ssh_connection_ref(), ssh_event_msg()}]]></c>. If the <seealso + marker="ssh_channel">ssh_channel</seealso> behavior is used to + implement the channel process these will be handled by + <seealso + marker="ssh_channel#CallbackModule:handled_ssh_msg-2">handle_ssh_msg/2 </seealso>.</p> </description> - <section> - <title>COMMON DATA TYPES </title> + <section> + <title>DATA TYPES </title> + <p>Type definitions that are used more than once in this module and/or abstractions to indicate the intended use of the data type:</p> - + <p><c>boolean() = true | false </c></p> <p><c>string() = list of ASCII characters</c></p> <p><c>timeout() = infinity | integer() - in milliseconds.</c></p> <p><c>ssh_connection_ref() - opaque to the user returned by - ssh:connect/3 or sent to a ssh channel processes</c></p> + ssh:connect/3 or sent to an SSH channel processes</c></p> <p><c>ssh_channel_id() = integer() </c></p> <p><c>ssh_data_type_code() = 1 ("stderr") | 0 ("normal") are - currently valid values see RFC 4254 section 5.2.</c></p> + currently valid values see</c> <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 5.2.</p> <p><c>ssh_request_status() = success | failure</c></p> - </section> + <p><c>event() = {ssh_cm, ssh_connection_ref(), ssh_event_msg()} </c></p> + <p><c>ssh_event_msg() = data_events() | status_events() | terminal_events() </c></p> + + <taglist> + <tag><b>data_events()</b></tag> + <item> + <taglist> + <tag><c><![CDATA[{data, ssh_channel_id(), ssh_data_type_code(), binary() = Data}]]></c></tag> + <item> Data has arrived on the channel. This event is sent as + result of calling <seealso marker="#send-3"> ssh_connection:send/[3,4,5] </seealso></item> + + <tag><c><![CDATA[{eof, ssh_channel_id()}]]></c></tag> + <item>Indicates that the other side will not send any more + data. This event is sent as result of calling <seealso + marker="#send_eof-2"> ssh_connection:send_eof/2</seealso> + </item> + </taglist> + </item> + + <tag><b>status_events()</b></tag> + <item> + + <taglist> + <tag><c><![CDATA[{signal, ssh_channel_id(), ssh_signal()}]]></c></tag> + <item>A signal can be delivered to the remote process/service + using the following message. Some systems will not support + signals, in which case they should ignore this message. There is + currently no funtion to generate this event as the signals + refered to are on OS-level and not something generated by an + Erlang program.</item> + + <tag><c><![CDATA[{exit_signal, ssh_channel_id(), string() = ExitSignal, string() = ErrorMsg, + string() = LanguageString}]]></c></tag> + + <item>A remote execution may terminate violently due to a signal + then this message may be received. For details on valid string + values see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> section 6.10. Special case of the signals + mentioned above.</item> + + <tag><c><![CDATA[{exit_status, ssh_channel_id(), integer() = ExitStatus}]]></c></tag> + <item> When the command running at the other end terminates, the + following message can be sent to return the exit status of the + command. A zero 'exit_status' usually means that the command + terminated successfully. This event is sent as result of calling + <seealso marker="#exit_status"> + ssh_connection:exit_status/3</seealso></item> + + <tag><c><![CDATA[{closed, ssh_channel_id()}]]></c></tag> + <item> This event is sent as result of calling + <seealso marker="#close">ssh_connection:close/2</seealso> Both the handling of this + event and sending of it will be taken care of by the + <seealso marker="ssh_channel">ssh_channel</seealso> behavior.</item> + + </taglist> + </item> - <section> - <title>MESSAGES SENT TO CHANNEL PROCESSES</title> + <tag><b>terminal_events()</b></tag> + + <item> + <p> Channels implementing a shell and command execution on the + server side should handle the following messages that may be sent by client channel processes. </p> + + <p><note>Events that includes a <c> WantReply</c> expects the event handling + process to call <seealso marker="#reply_request">ssh_connection:reply_request/4</seealso> + with the boolean value of <c> WantReply</c> as the second + argument. </note> </p> + + <taglist> + <tag><c><![CDATA[{env, ssh_channel_id(), boolean() = WantReply, + string() = Var, string() = Value}]]></c></tag> + <item> Environment variables may be passed to the shell/command + to be started later. This event is sent as result of calling <seealso + marker="#setenv"> ssh_connection:setenv/5</seealso> + </item> + + <tag><c><![CDATA[{pty, ssh_channel_id(), + boolean() = WantReply, {string() = Terminal, integer() = CharWidth, + integer() = RowHeight, integer() = PixelWidth, integer() = PixelHight, + [{atom() | integer() = Opcode, + integer() = Value}] = TerminalModes}}]]></c></tag> + <item>A pseudo-terminal has been requested for the + session. Terminal is the value of the TERM environment + variable value (e.g., vt100). Zero dimension parameters must + be ignored. The character/row dimensions override the pixel + dimensions (when nonzero). Pixel dimensions refer to the + drawable area of the window. The <c>Opcode</c> in the + <c>TerminalModes</c> list is the mnemonic name, represented + as an lowercase erlang atom, defined in + <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254 </url> section 8, + or the opcode if the mnemonic name is not listed in the + RFC. Example <c>OP code: 53, mnemonic name ECHO erlang atom: + echo</c>. There is currently no API function to generate this + event.</item> + + <tag><c><![CDATA[{shell, boolean() = WantReply}]]></c></tag> + <item> This message will request that the user's default shell + be started at the other end. This event is sent as result of calling <seealso + marker="#shell"> ssh_connection:shell/2</seealso> + </item> + + <tag><c><![CDATA[{window_change, ssh_channel_id(), integer() = CharWidth, + integer() = RowHeight, integer() = PixWidth, integer() = PixHeight}]]></c></tag> + <item> When the window (terminal) size changes on the client + side, it MAY send a message to the server side to inform it of + the new dimensions. There is currently no API function to generate this + event.</item> - <p>As a result of the ssh connection protocol messages on the form - <c><![CDATA[{ssh_cm, ssh_connection_ref(), term()}]]></c> - will be sent to a channel process. The term will contain - information regarding the ssh connection protocol event, - for details see the ssh channel behavior callback <seealso - marker="ssh_channel">handle_ssh_msg/2 </seealso> </p> - </section> - - <funcs> + <tag><c><![CDATA[{exec, ssh_channel_id(), + boolean() = WantReply, string() = Cmd}]]></c></tag> + <item> This message will request that the server starts + execution of the given command. This event is sent as result of calling <seealso + marker="#exec">ssh_connection:exec/4 </seealso> + </item> + </taglist> + </item> + </taglist> + </section> + + <funcs> <func> <name>adjust_window(ConnectionRef, ChannelId, NumOfBytes) -> ok</name> - <fsummary>Adjusts the ssh flowcontrol window. </fsummary> + <fsummary>Adjusts the SSH flowcontrol window. </fsummary> <type> <v> ConnectionRef = ssh_connection_ref() </v> <v> ChannelId = ssh_channel_id() </v> <v> NumOfBytes = integer()</v> </type> <desc> - <p>Adjusts the ssh flowcontrol window. </p> + <p>Adjusts the SSH flowcontrol window. This shall be done by both client and server side channel processes.</p> - <note><p>This will be taken care of by the ssh_channel - behavior when the callback <seealso marker="ssh_channel"> - handle_ssh_msg/2 </seealso> has returned after processing a - {ssh_cm, ssh_connection_ref(), {data, ssh_channel_id(), - ssh_data_type_code(), binary()}} - message, and should normally not be called explicitly.</p></note> + <note><p>Channels implemented with the <seealso marker="ssh_channel"> ssh_channel + behavior</seealso> will normaly not need to call this function as flow control + will be handled by the behavior. The behavior will adjust the window every time + the callback <seealso marker="ssh_channel#handled_ssh_msg-2"> + handle_ssh_msg/2 </seealso> has returned after processing channel data</p> </note> </desc> </func> @@ -98,20 +207,19 @@ <v> ChannelId = ssh_channel_id()</v> </type> <desc> - <p>Sends a close message on the channel <c>ChannelId</c> + <p>A server or client channel process can choose to close their session by sending a close event. </p> - <note><p>This function will be called by the ssh channel + <note><p>This function will be called by the ssh_channel behavior when the channel is terminated see <seealso - marker="ssh_channel"> ssh_channel(3) </seealso> and should - normally not be called explicitly.</p></note> + marker="ssh_channel"> ssh_channel(3) </seealso> so channels implemented with the + behavior should not call this function explicitly.</p></note> </desc> </func> <func> <name>exec(ConnectionRef, ChannelId, Command, TimeOut) -> ssh_request_status() </name> - <fsummary>Will request that the server start the - execution of the given command. </fsummary> + <fsummary>Request that the server start the execution of the given command. </fsummary> <type> <v> ConnectionRef = ssh_connection_ref() </v> <v> ChannelId = ssh_channel_id()</v> @@ -119,44 +227,39 @@ <v>Timeout = timeout() </v> </type> <desc> - <p>Will request that the server start the execution of the - given command, the result will be received as:</p> + <p>Should be called by a client channel process to request that the server starts execution of the + given command, the result will be several messages according to the following pattern. Note + that the last message will be a channel close message, as the exec request is a one time + execution that closes the channel when it is done.</p> <taglist> - <tag><c> N X {ssh_cm, - ssh_connection_ref(), {data, ssh_channel_id(), ssh_data_type_code(), - binary() = Data}} </c></tag> + <tag><c> N x {ssh_cm, ssh_connection_ref(), + {data, ssh_channel_id(), ssh_data_type_code(), binary() = Data}} </c></tag> <item>The result of executing the command may be only one line or thousands of lines depending on the command.</item> - <tag><c> 1 X {ssh_cm, ssh_connection_ref(), {eof, ssh_channel_id()}}</c></tag> + <tag><c>0 or 1 x {ssh_cm, ssh_connection_ref(), {eof, ssh_channel_id()}}</c></tag> <item>Indicates that no more data will be sent.</item> - <tag><c>0 or 1 X {ssh_cm, + <tag><c>0 or 1 x {ssh_cm, ssh_connection_ref(), {exit_signal, ssh_channel_id(), string() = ExitSignal, string() = ErrorMsg, string() = LanguageString}}</c></tag> <item>Not all systems send signals. For details on valid string values see RFC 4254 section 6.10 </item> - <tag><c>0 or 1 X {ssh_cm, ssh_connection_ref(), {exit_status, + <tag><c>0 or 1 x {ssh_cm, ssh_connection_ref(), {exit_status, ssh_channel_id(), integer() = ExitStatus}}</c></tag> <item>It is recommended by the <c>ssh connection protocol</c> that this message shall be sent, but that may not always be the case.</item> - <tag><c> 1 X {ssh_cm, ssh_connection_ref(), + <tag><c> 1 x {ssh_cm, ssh_connection_ref(), {closed, ssh_channel_id()}}</c></tag> <item>Indicates that the ssh channel started for the execution of the command has now been shutdown.</item> </taglist> - - <p> These message should be handled by the - client. The <seealso marker="ssh_channel">ssh channel - behavior</seealso> can be used when writing a client. - </p> </desc> </func> - <func> <name>exit_status(ConnectionRef, ChannelId, Status) -> ok</name> <fsummary>Sends the exit status of a command to the client.</fsummary> @@ -166,12 +269,12 @@ <v> Status = integer()</v> </type> <desc> - <p>Sends the exit status of a command to the client.</p> + <p>Should be called by a server channel process to sends the exit status of a command to the client.</p> </desc> - </func> + </func> <func> - <name>reply_request(ConnectionRef, WantReply, Status, CannelId) -> ok</name> + <name>reply_request(ConnectionRef, WantReply, Status, ChannelId) -> ok</name> <fsummary>Send status replies to requests that want such replies. </fsummary> <type> <v> ConnectionRef = ssh_connection_ref() </v> @@ -183,10 +286,9 @@ <p>Sends status replies to requests where the requester has stated that they want a status report e.i .<c> WantReply = true</c>, if <c> WantReply</c> is false calling this function will be a - "noop". Should be called after handling an ssh connection + "noop". Should be called while handling an ssh connection protocol message containing a <c>WantReply</c> boolean - value. See the ssh_channel behavior callback <seealso - marker="ssh_channel"> handle_ssh_msg/2 </seealso> + value. </p> </desc> </func> @@ -206,7 +308,7 @@ <v> Timeout = timeout()</v> </type> <desc> - <p>Sends channel data. + <p>Should be called by client- and server channel processes to send data to each other. </p> </desc> </func> @@ -228,9 +330,7 @@ <name>session_channel(ConnectionRef, Timeout) -> </name> <name>session_channel(ConnectionRef, InitialWindowSize, MaxPacketSize, Timeout) -> {ok, ssh_channel_id()} | {error, Reason}</name> - <fsummary>Opens a channel for a ssh session. A session is a - remote execution of a program. The program may be a shell, an - application, a system command, or some built-in subsystem. </fsummary> + <fsummary>Opens a channel for a ssh session. </fsummary> <type> <v> ConnectionRef = ssh_connection_ref()</v> <v> InitialWindowSize = integer() </v> @@ -239,9 +339,8 @@ <v> Reason = term() </v> </type> <desc> - <p>Opens a channel for a ssh session. A session is a - remote execution of a program. The program may be a shell, an - application, a system command, or some built-in subsystem. + <p>Opens a channel for an SSH session. The channel id returned from this function + is the id used as input to the other funtions in this module. </p> </desc> </func> @@ -258,8 +357,8 @@ <v> Timeout = timeout()</v> </type> <desc> - <p> Environment variables may be passed to the shell/command to be - started later. + <p> Environment variables may be passed before starting the + shell/command. Should be called by a client channel processes. </p> </desc> </func> @@ -267,17 +366,16 @@ <func> <name>shell(ConnectionRef, ChannelId) -> ssh_request_status() </name> - <fsummary> Will request that the user's default shell (typically - defined in /etc/passwd in UNIX systems) be started at the other + <fsummary> Requests that the user's default shell (typically + defined in /etc/passwd in UNIX systems) shall be executed at the server end. </fsummary> <type> <v> ConnectionRef = ssh_connection_ref() </v> <v> ChannelId = ssh_channel_id()</v> </type> <desc> - <p> Will request that the user's default shell (typically - defined in /etc/passwd in UNIX systems) be started at the - other end. + <p> Should be called by a client channel process to request that the user's default shell (typically + defined in /etc/passwd in UNIX systems) shall be executed at the server end. </p> </desc> </func> @@ -292,7 +390,7 @@ <v> Timeout = timeout()</v> </type> <desc> - <p> Sends a request to execute a predefined subsystem. + <p> Should be called by a client channel process for requesting to execute a predefined subsystem on the server. </p> </desc> </func> diff --git a/lib/ssh/doc/src/ssh_protocol.xml b/lib/ssh/doc/src/ssh_protocol.xml new file mode 100644 index 0000000000..28f42f5707 --- /dev/null +++ b/lib/ssh/doc/src/ssh_protocol.xml @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> +<!-- %EricssonCopyright% --> +<chapter> + <header> + <copyright> + <year>2013</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The program may be used and/or copied only with the written permission from + Ericsson AB, or in accordance with the terms and conditions stipulated in + the agreement/contract under which the program has been supplied. + </legalnotice> + <title>Secure Shell (SSH)</title> + <prepared>OTP</prepared> + <date></date> + <rev>%VSN%</rev> + <file>ssh_protocol.xml</file> + </header> + + <section> + <title>SSH Protocol Overview</title> + + <p> Conceptually the SSH protocol can be partitioned into four + layers:</p> + + <image file="SSH_protocols.png"> + <icaption>SSH Protocol Architecture</icaption> + </image> + + <section> + <title>Transport Protocol</title> + + <p> The SSH Transport Protocol is a secure, low level transport. + It provides strong encryption, cryptographic host + authentication and integrity protection. Currently, only a + minimum of MAC- (message authentication code, a short piece of + information used to authenticate a message) and encryption + algorithms are supported see <seealso marker="ssh">ssh(3)</seealso> + </p> + </section> + + <section> + <title>Authentication Protocol</title> + + <p>The SSH authentication protocol is a general-purpose user + authentication protocol run over the SSH transport + protocol. Erlang SSH supports user authentication using public + key technology (RSA and DSA, X509-certificates are currently not + supported). It is also possible to use a so called keyboard + interactive authentication. This method is suitable for + interactive authentication methods that do not need any special + software support on the client side. Instead, all authentication + data should be entered via the keyboard. It is also possible + to use a pure password based authentication scheme, note that in + this case the the plain text password will be encrypted before sent + over the network. There are several configuration options for + authentication handling available in + <seealso marker="ssh#connect-3">ssh:connect/[3,4]</seealso> + and <seealso marker="ssh#daemon-2">ssh:daemon/[2,3]</seealso> + It is also possible to customize the public key handling + by implementing the behaviours <seealso + marker="ssh_client_key_api">ssh_client_key_api</seealso> and + <seealso + marker="ssh_server_key_api">ssh_server_key_api</seealso> + </p> + </section> + + <section> + <title>Connection Protocol</title> + + <p>The SSH Connection Protocol provides application-support + services over the transport pipe, such as channel multiplexing, + flow control, remote program execution, signal propagation, + connection forwarding, etc. Functions for handling the SSH + Connection Protocol can be found in the module <seealso + marker="ssh_connection">ssh_connection</seealso>. + </p> + </section> + + <section> + <title>Channels</title> + + <p>All terminal sessions, forwarded connections etc., are + channels. Multiple channels are multiplexed into a single + connection, and all channels are flow-controlled. Typically an + SSH client will open a channel, send data/commands, receive + data/"control information" and when it is done close the + channel. The <seealso + marker="ssh_channel">ssh_channel</seealso> behaviour makes it easy to + write your own SSH client/server processes that use flow + control. It handles generic parts of SSH channel management and + lets you focus on the application logic. + </p> + + <p>Channels comes in three flavors</p> + + <list type="bulleted"> + <item><em>Subsystem</em> - named services that can be run as + part of an SSH server such as SFTP <seealso + marker="ssh_sftpd">ssh_sftpd</seealso>, that is built in to the + SSH daemon (server) by default but may be disabled. The Erlang SSH + daemon may be configured to run any Erlang + implemented SSH subsystem. + </item> + <item><em>Shell</em> - interactive shell. By default the + Erlang daemon will run the Erlang shell. It is + possible to customize the shell by providing your own + read-eval-print loop. It is also possible, but much more work, + to provide your own CLI (Command Line Interface) implementation. + </item> + <item><em>Exec</em> - one-time remote execution of commands. See <seealso + marker="ssh_connection#exec-4">ssh_connection:exec/4</seealso></item> + </list> + </section> + + <p>Channels are flow controlled. No data may be sent to a channel + peer until a message is received to indicate that window space is + available. The 'initial window size' specifies how many bytes of + channel data that can be sent to the channel peer without adjusting the + window. + </p> + + <p> + For more detailed information about the SSH protocol, see the + following RFCs: + </p> + + <list type="bulleted"> + <item><url href="http://www.ietf.org/rfc/rfc4250.txt">RFC 4250</url> - + Protocol Assigned Numbers.</item> + <item><url href="http://www.ietf.org/rfc/rfc4251.txt">RFC 4251</url> - + Protocol Architecture.</item> + <item><url href="http://www.ietf.org/rfc/rfc4252.txt">RFC 4252</url> - + Authentication Protocol.</item> + <item><url href="http://www.ietf.org/rfc/rfc4253.txt">RFC 4253</url> - + Transport Layer Protocol.</item> + <item><url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> - + Connection Protocol.</item> + <item><url href="http://www.ietf.org/rfc/rfc4255.txt">RFC 4255</url> - + Key Fingerprints.</item> + <item><url href="http://www.ietf.org/rfc/rfc4344.txt">RFC 4344</url> - + Transport Layer Encryption Modes.</item> + <item><url href="http://www.ietf.org/rfc/rfc4716.txt">RFC 4716</url> - + Public Key File Format.</item> + </list> + </section> +</chapter> diff --git a/lib/ssh/doc/src/ssh_server_key_api.xml b/lib/ssh/doc/src/ssh_server_key_api.xml new file mode 100644 index 0000000000..78ff105387 --- /dev/null +++ b/lib/ssh/doc/src/ssh_server_key_api.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2012</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. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + <title>ssh_server_key_api</title> + </header> + <module>ssh_server_key_api</module> + <modulesummary> + -behaviour(ssh_server_key_api). + </modulesummary> + <description> + <p> Behaviour describing the API for an SSH server's public key handling.By implementing the callbacks defined + in this behavior it is possible to customize the SSH server's public key + handling. By default the SSH application implements this behavior + with help of the standard openssh files, see <seealso marker="SSH_app"> ssh(6)</seealso>.</p> + </description> + + <section> + <title>DATA TYPES </title> + + <p>Type definitions that are used more than once in this module + and/or abstractions to indicate the intended use of the data + type:</p> + + <p> boolean() = true | false</p> + <p> string() = [byte()]</p> + <p> public_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p> + <p> private_key() = #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term()</p> + <p> public_key_algorithm() = 'ssh-rsa'| 'ssh-dss' | atom()</p> + </section> + + <funcs> + <func> + <name>Module:host_key(Algorithm, DaemonOptions) -> + {ok, Key} | {error, Reason}</name> + <fsummary>Fetches the hosts private key </fsummary> + <type> + <v>Algorithm = public_key_algorithm()</v> + <d> Host key algorithm. Should support 'ssh-rsa'| 'ssh-dss' but additional algorithms + can be handled.</d> + <v> DaemonOptions = proplists:proplist() </v> + <d>Options provided to <seealso marker="ssh#daemon">ssh:daemon/[2,3]</seealso></d> + <v> Key = private_key()</v> + <d> The private key of the host matching the <c>Algorithm</c></d> + <v>Reason = term() </v> + </type> + <desc> + <p>Fetches the hosts private key</p> + </desc> + </func> + + <func> + <name>Module:is_auth_key(Key, User, DaemonOptions) -> Result</name> + <fsummary> Checks if the user key is authorized</fsummary> + <type> + <v> Key = public_key() </v> + <d> Normally an RSA or DSA public key but handling of other public keys can be added</d> + <v> User = string()</v> + <d> The user owning the public key</d> + <v> DaemonOptions = proplists:proplist() </v> + <d> Options provided to <seealso marker="ssh#daemon">ssh:daemon/[2,3]</seealso></d> + <v> Result = boolean()</v> + </type> + <desc> + <p> Checks if the user key is authorized </p> + </desc> + </func> + + </funcs> + +</erlref> diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml index c1f75461b1..0d61e57edb 100644 --- a/lib/ssh/doc/src/ssh_sftp.xml +++ b/lib/ssh/doc/src/ssh_sftp.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>2005</year><year>2010</year> + <year>2005</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -22,25 +22,20 @@ </legalnotice> <title>ssh_sftp</title> - <prepared>Jakob Cederlund</prepared> - <responsible></responsible> - <docno>1</docno> - <approved></approved> - <checked></checked> + <prepared>OTP</prepared> <date>2005-09-22</date> - <rev>PA1</rev> <file>ssh_sftp.sgml</file> </header> <module>ssh_sftp</module> <modulesummary>SFTP client.</modulesummary> <description> <p>This module implements an SFTP (SSH FTP) client. SFTP is a - secure, encrypted file transfer service available for - SSH.</p> + secure, encrypted file transfer service available for + SSH.</p> </description> <section> - <title>COMMON DATA TYPES </title> + <title>DATA TYPES </title> <p>Type definitions that are used more than once in this module and/or abstractions to indicate the intended use of the data type: </p> @@ -51,8 +46,8 @@ <section> <title>TIMEOUTS </title> - <p>If the request functions for the sftp channel return {error, timeout} - it does not mean that the request did not reach the server and was + <p>If the request functions for the SFTP channel return {error, timeout} + it does not guarantee that the request did not reach the server and was not performed, it only means that we did not receive an answer from the server within the time that was expected.</p> </section> @@ -64,7 +59,7 @@ <name>start_channel(Host, Options) -></name> <name>start_channel(Host, Port, Options) -> {ok, Pid} | {ok, Pid, ConnectionRef} | {error, Reason}</name> - <fsummary>Starts a sftp client</fsummary> + <fsummary>Starts a SFTP client</fsummary> <type> <v>Host = string()</v> <v>ConnectionRef = ssh_connection_ref()</v> @@ -73,11 +68,11 @@ <v>Reason = term()</v> </type> <desc> - <p>If not provided, setups a ssh connection in this case a - connection reference will be returned too. A ssh channel - process is started to handle the communication with the SFTP - server, the returned pid for this process should be used as - input to all other API functions in this module.</p> + <p>If no connection reference is provided, a connection is set + up and the new connection is returned. An SSH channel process + is started to handle the communication with the SFTP server. + The returned pid for this process should be used as input to + all other API functions in this module.</p> <p>Options are:</p> <taglist> @@ -95,13 +90,13 @@ <func> <name>stop_channel(ChannelPid) -> ok</name> - <fsummary>Stops the sftp client channel.</fsummary> + <fsummary>Stops the SFTP client channel.</fsummary> <type> <v>ChannelPid = pid()</v> </type> <desc> - <p>Stops a sftp channel. If the ssh connection should be closed - call <seealso marker="ssh">ssh:close/1</seealso>.</p> + <p>Stops an SFTP channel. Does not close the SSH connetion. + Use <seealso marker="ssh">ssh:close/1</seealso> to close it.</p> </desc> </func> @@ -133,8 +128,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Writes a file to the server, like <c><![CDATA[file:write_file/2]]></c>. - The file is created if it's not there.</p> + <p>Writes a file to the server, like + <c><![CDATA[file:write_file/2]]></c>. The file is created if + it does not exist or is owerwritten if it does.</p> </desc> </func> <func> @@ -169,7 +165,7 @@ </type> <desc> <p>Opens a file on the server, and returns a handle that - is used for reading or writing.</p> + can be used for reading or writing.</p> </desc> </func> <func> @@ -184,7 +180,7 @@ </type> <desc> <p>Opens a handle to a directory on the server, the handle - is used for reading directory contents.</p> + can be used for reading directory contents.</p> </desc> </func> <func> @@ -218,7 +214,7 @@ </type> <desc> <p>Reads <c><![CDATA[Len]]></c> bytes from the file referenced by - <c><![CDATA[Handle]]></c>. Returns <c><![CDATA[{ok, Data}]]></c>, or <c><![CDATA[eof]]></c>, or + <c><![CDATA[Handle]]></c>. Returns <c><![CDATA[{ok, Data}]]></c>, <c><![CDATA[eof]]></c>, or <c><![CDATA[{error, Reason}]]></c>. If the file is opened with <c><![CDATA[binary]]></c>, <c><![CDATA[Data]]></c> is a binary, otherwise it is a string.</p> <p>If the file is read past eof, only the remaining bytes @@ -267,9 +263,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Write <c><![CDATA[data]]></c> to the file referenced by <c><![CDATA[Handle]]></c>. + <p>Writes<c><![CDATA[data]]></c> to the file referenced by <c><![CDATA[Handle]]></c>. The file should be opened with <c><![CDATA[write]]></c> or <c><![CDATA[append]]></c> - flag. Returns <c><![CDATA[ok]]></c> if successful and <c><![CDATA[{error, Reason}]]></c> + flag. Returns <c><![CDATA[ok]]></c> if successful or S<c><![CDATA[{error, Reason}]]></c> otherwise.</p> <p>Typical error reasons are:</p> <taglist> @@ -317,14 +313,14 @@ <v>ChannelPid = pid()</v> <v>Handle = term()</v> <v>Location = Offset | {bof, Offset} | {cur, Offset} | {eof, Offset} | bof | cur | eof</v> - <v>Offset = int()</v> + <v>Offset = integer()</v> <v>Timeout = timeout()</v> <v>NewPosition = integer()</v> <v>Reason = term()</v> </type> <desc> <p>Sets the file position of the file referenced by <c><![CDATA[Handle]]></c>. - Returns <c><![CDATA[{ok, NewPosition]]></c> (as an absolute offset) if + Returns <c><![CDATA[{ok, NewPosition}]]></c> (as an absolute offset) if successful, otherwise <c><![CDATA[{error, Reason}]]></c>. <c><![CDATA[Location]]></c> is one of the following:</p> <taglist> @@ -413,7 +409,7 @@ <v>Reason = term()</v> </type> <desc> - <p>Read the link target from the symbolic link specified + <p>Reads the link target from the symbolic link specified by <c><![CDATA[name]]></c>, like <c><![CDATA[file:read_link/1]]></c>.</p> </desc> </func> @@ -490,8 +486,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Deletes a directory specified by <c><![CDATA[Name]]></c>. The directory - should be empty.</p> + <p>Deletes a directory specified by <c><![CDATA[Name]]></c>. + Note that the directory must be empty before it can be successfully deleted + </p> </desc> </func> diff --git a/lib/ssh/doc/src/ssh_sftpd.xml b/lib/ssh/doc/src/ssh_sftpd.xml index b3d64e72b4..3666bc7692 100644 --- a/lib/ssh/doc/src/ssh_sftpd.xml +++ b/lib/ssh/doc/src/ssh_sftpd.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>2005</year><year>2010</year> + <year>2005</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -22,23 +22,17 @@ </legalnotice> <title>ssh_sftpd</title> - <prepared>Ingela Anderton Andin</prepared> - <responsible></responsible> - <docno>1</docno> - <approved></approved> - <checked></checked> <date>2005-09-22</date> - <rev>PA1</rev> <file>ssh_sftpd.sgml</file> </header> <module>ssh_sftpd</module> - <modulesummary>Specifies a channel process to handle a sftp subsystem.</modulesummary> + <modulesummary>Specifies the channel process to handle an sftp subsystem.</modulesummary> <description> <p>Specifies a channel process to handle a sftp subsystem.</p> </description> <section> - <title>COMMON DATA TYPES </title> + <title>DATA TYPES </title> <p><c>subsystem_spec() = {subsystem_name(), {channel_callback(), channel_init_args()}} </c></p> <p><c>subsystem_name() = "sftp"</c></p> <p><c>channel_callback() = atom()</c> - Name of the erlang module implementing the @@ -65,27 +59,32 @@ </item> <tag><c><![CDATA[{file_handler, CallbackModule}]]></c></tag> <item> - <p>Determines which module to call for communicating with - the file server. Default value is <c>ssh_sftpd_file</c> that uses the - file and filelib API:s to access the standard OTP file - server. This option may be used to plug in the use of - other file servers.</p> - </item> - <tag><c><![CDATA[{max_files, Integer}]]></c></tag> - <item> - <p>The default value is <c>0</c>, which means that there is no upper limit. - If supplied, the number of filenames returned to the sftp client per <c>READDIR</c> - request, is limited to at most the given value.</p> - </item> + <p>Determines which module to call for accessing + the file server. The default value is <c>ssh_sftpd_file</c> that uses the + <seealso marker="kernel#file">file</seealso> and <seealso marker="kernel#filelib">filelib</seealso> API:s to access the standard OTP file + server. This option may be used to plug in + other file servers.</p> + </item> + <tag><c><![CDATA[{max_files, Integer}]]></c></tag> + <item> + <p>The default value is <c>0</c>, which means that there is no upper limit. + If supplied, the number of filenames returned to the sftp client per <c>READDIR</c> + request is limited to at most the given value.</p> + </item> <tag><c><![CDATA[{root, String}]]></c></tag> <item> <p>Sets the sftp root directory. The user will then not be - able to see any files above this root. If for instance - the root is set to <c>/tmp</c> the user will see this - directory as <c>/</c> and if the user does cd <c>/etc</c> - the user will end up in <c>/tmp/etc</c>. + able to see any files above this root. If for instance + the root is set to <c>/tmp</c> the user will see this + directory as <c>/</c> and if the user does cd <c>/etc</c> + the user will end up in <c>/tmp/etc</c>. </p> </item> + <tag><c><![CDATA[{sftpd_vsn, integer()}]]></c></tag> + <item> + <p>Sets the sftp version to use, defaults to 5. Version 6 is under + development and limited.</p> + </item> </taglist> </desc> </func> diff --git a/lib/ssh/doc/src/user_guide.gif b/lib/ssh/doc/src/user_guide.gif Binary files differdeleted file mode 100644 index e6275a803d..0000000000 --- a/lib/ssh/doc/src/user_guide.gif +++ /dev/null diff --git a/lib/ssh/doc/src/usersguide.xml b/lib/ssh/doc/src/usersguide.xml new file mode 100644 index 0000000000..c818003090 --- /dev/null +++ b/lib/ssh/doc/src/usersguide.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE part SYSTEM "part.dtd"> + +<part xmlns:xi="http://www.w3.org/2001/XInclude"> + <header> + <copyright> + <year>2012</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>SSH User's Guide</title> + <prepared>OTP Team</prepared> + <date>2012-10-11</date> + <file>usersguide.xml</file> + </header> + <description> + <p>The <em>SSH</em> application implements the SSH (Secure Shell) protocol and + provides an SFTP (Secret File Transfer Protocol) client and server. + </p> + </description> + <xi:include href="introduction.xml"/> + <xi:include href="ssh_protocol.xml"/> + <xi:include href="using_ssh.xml"/> +</part> diff --git a/lib/ssh/doc/src/using_ssh.xml b/lib/ssh/doc/src/using_ssh.xml new file mode 100644 index 0000000000..87b811d591 --- /dev/null +++ b/lib/ssh/doc/src/using_ssh.xml @@ -0,0 +1,298 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2012</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>Getting started</title> + <file>using_ssh.xml</file> + </header> + + <section> + <title> General information</title> + <p>The examples in the following sections use the utility function + <seealso marker="ssh#start"> ssh:start/0 </seealso> that starts + all needed applications (crypto, public_key and ssh). All examples + are run in an Erlang shell, or in a bash shell using openssh to + illustrate how the erlang ssh application can be used. The + exampels are run as the user otptest on a local network where the + user is authorized to login in over ssh to the host "tarlop". If + nothing else is stated it is persumed that the otptest user has an + entry in tarlop's authorized_keys file (may log in via ssh without + entering a password). Also tarlop is a known host in the user + otptest's known_hosts file so that host verification can be done + without user interaction. + </p> + </section> + + <section> + <title>Using the Erlang SSH Terminal Client</title> + + <p>The user otptest, that has bash as default shell, uses the + ssh:shell/1 client to connect to the openssh daemon running on a + host called tarlop. Note that currently this client is very simple + and you should not be expected to be as fancy as the openssh + client.</p> + + <code type="erl" > + 1> ssh:start(). + ok + 2> {ok, S} = ssh:shell("tarlop"). + >pwd + /home/otptest + >exit + logout + 3> + </code> + </section> + + <section> + <title>Running an Erlang SSH Daemon </title> + + <p> The option system_dir must be a directory containing a host + key file and it defaults to /etc/ssh. For details see section + Configuration Files in <seealso + marker="ssh_app">ssh(6)</seealso>. + </p> + + <note><p>Normally the /etc/ssh directory is only readable by root. </p> + </note> + + <p> The option user_dir defaults to the users ~/.ssh directory</p> + + <p>In the following example we generate new keys and host keys as + to be able to run the example without having root privilages</p> + + <code> + $bash> ssh-keygen -t rsa -f /tmp/ssh_daemon/ssh_host_rsa_key + [...] + $bash> ssh-keygen -t rsa -f /tmp/otptest_user/.ssh/id_rsa + [...] + </code> + + <p>Create the file /tmp/otptest_user/.ssh/authrized_keys and add the content + of /tmp/otptest_user/.ssh/id_rsa.pub Now we can do</p> + + <code type="erl"> + 1> ssh:start(). + ok + 2> {ok, Sshd} = ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"}, + {user_dir, "/tmp/otptest_user/.ssh"}]). + {ok,<0.54.0>} + 3> + </code> + + <p>Use the openssh client from a shell to connect to the Erlang ssh daemon.</p> + + <code> + $bash> ssh tarlop -p 8989 -i /tmp/otptest_user/.ssh/id_rsa\ + -o UserKnownHostsFile=/tmp/otptest_user/.ssh/known_hosts + The authenticity of host 'tarlop' can't be established. + RSA key fingerprint is 14:81:80:50:b1:1f:57:dd:93:a8:2d:2f:dd:90:ae:a8. + Are you sure you want to continue connecting (yes/no)? yes + Warning: Permanently added 'tarlop' (RSA) to the list of known hosts. + Eshell V5.10 (abort with ^G) + 1> + </code> + + <p>There are two ways of shutting down an SSH daemon</p> + + <p>1: Stops the listener, but leaves existing connections started by the listener up and running.</p> + + <code type="erl"> + 3> ssh:stop_listener(Sshd). + ok + 4> + </code> + + <p>2: Stops the listener and all connections started by the listener.</p> + + <code type="erl"> + 3> ssh:stop_daemon(Sshd) + ok + 4> + </code> + + </section> + + <section> + <title>One Time Execution</title> + + <p>In the following example the Erlang shell is the client process + that receives the channel replies. <note> If you run this example + in your environment you may get fewer or more messages back as + this depends on the OS and shell on the machine running the ssh + daemon. See also <seealso marker="ssh_connection#exec-4">ssh_connection:exec/4</seealso> + </note> + </p> + + <code type="erl" > + 1> ssh:start(). + ok + 2> {ok, ConnectionRef} = ssh:connect("tarlop", 22, []). + {ok,<0.57.0>} + 3>{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity). + {ok,0} + 4> success = ssh_connection:exec(ConnectionRef, ChannelId, "pwd", infinity). + 5> flush(). + Shell got {ssh_cm,<0.57.0>,{data,0,0,<<"/home/otptest\n">>}} + Shell got {ssh_cm,<0.57.0>,{eof,0}} + Shell got {ssh_cm,<0.57.0>,{exit_status,0,0}} + Shell got {ssh_cm,<0.57.0>,{closed,0}} + ok + 6> + </code> + + <p>Note only the channel is closed the connection is still up and can handle other channels</p> + + <code type="erl" > + 6> {ok, NewChannelId} = ssh_connection:session_channel(ConnectionRef, infinity). + {ok,1} + ... + </code> + </section> + + <section> + <title>SFTP (SSH File Transport Protocol) server</title> + + <code type="erl" > + 1> ssh:start(). + ok + 2> ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"}, + {user_dir, "/tmp/otptest_user/.ssh"}, + {subsystems, [ssh_sftpd:subsystem_spec([{cwd, "/tmp/sftp/example"}])]}]). + {ok,<0.54.0>} + 3> + </code> + + <p> Run the openssh sftp client</p> + + <code type="erl"> + $bash> sftp -oPort=8989 -o IdentityFile=/tmp/otptest_user/.ssh/id_rsa\ + -o UserKnownHostsFile=/tmp/otptest_user/.ssh/known_hosts tarlop + Connecting to tarlop... + sftp> pwd + Remote working directory: /tmp/sftp/example + sftp> + </code> + </section> + + <section> + <title>SFTP (SSH File Transport Protocol) client</title> + + <code type="erl" > + 1> ssh:start(). + ok + 2> {ok, ChannelPid, Connection} = ssh_sftp:start_channel("tarlop", []). + {ok,<0.57.0>,<0.51.0>} + 3> ssh_sftp:read_file(ChannelPid, "/home/otptest/test.txt"). + {ok,<<"This is a test file\n">>} + </code> + </section> + + <section> + <title>Creating a subsystem</title> + + <p>A very small SSH subsystem that echos N bytes could be implemented like this. + See also <seealso marker="ssh_channel"> ssh_channel(3)</seealso> </p> + + <code type="erl" > +-module(ssh_echo_server). +-behaviour(ssh_subsystem). +-record(state, { + n, + id, + cm + }). +-export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]). + +init([N]) -> + {ok, #state{n = N}}. + +handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) -> + {ok, State#state{id = ChannelId, + cm = ConnectionManager}}. + +handle_ssh_msg({ssh_cm, CM, {data, ChannelId, 0, Data}}, #state{n = N} = State) -> + M = N - size(Data), + case M > 0 of + true -> + ssh_connection:send(CM, ChannelId, Data), + {ok, State#state{n = M}}; + false -> + <<SendData:N/binary, _/binary>> = Data, + ssh_connection:send(CM, ChannelId, SendData), + ssh_connection:send_eof(CM, ChannelId), + {stop, ChannelId, State} + end; +handle_ssh_msg({ssh_cm, _ConnectionManager, + {data, _ChannelId, 1, Data}}, State) -> + error_logger:format(standard_error, " ~p~n", [binary_to_list(Data)]), + {ok, State}; + +handle_ssh_msg({ssh_cm, _ConnectionManager, {eof, _ChannelId}}, State) -> + {ok, State}; + +handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) -> + %% Ignore signals according to RFC 4254 section 6.9. + {ok, State}; + +handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, _Error, _}}, + State) -> + {stop, ChannelId, State}; + +handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, _Status}}, State) -> + {stop, ChannelId, State}. + +terminate(_Reason, _State) -> + ok. + </code> + + <p>And run like this on the host tarlop with the keys generated in section 3.3</p> + + <code type="erl" > + 1> ssh:start(). + ok + 2> ssh:daemon(8989, [{system_dir, "/tmp/ssh_daemon"}, + {user_dir, "/tmp/otptest_user/.ssh"} + {subsystems, [{"echo_n", {ssh_echo_server, [10]}}]}]). + {ok,<0.54.0>} + 3> + </code> + + <code type="erl" > + 1> ssh:start(). + ok + 2>{ok, ConnectionRef} = ssh:connect("tarlop", 8989, [{user_dir, "/tmp/otptest_user/.ssh"}]). + {ok,<0.57.0>} + 3>{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity). + 4> success = ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity). + 5> ok = ssh_connection:send(ConnectionRef, ChannelId, "0123456789", infinity). + 6> flush(). + {ssh_msg, <0.57.0>, {data, 0, 1, "0123456789"}} + {ssh_msg, <0.57.0>, {eof, 0}} + {ssh_msg, <0.57.0>, {closed, 0}} + 7> {error, closed} = ssh_connection:send(ConnectionRef, ChannelId, "10", infinity). + </code> + +</section> + +</chapter> diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index b8eecd3fa2..323f0af191 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -41,7 +41,9 @@ RELSYSDIR = $(RELEASE_PATH)/lib/ssh-$(VSN) BEHAVIOUR_MODULES= \ ssh_sftpd_file_api \ ssh_channel \ - ssh_key_api + ssh_subsystem \ + ssh_client_key_api \ + ssh_server_key_api MODULES= \ ssh \ diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src index 316c09eb06..a0ba7cf7d9 100644 --- a/lib/ssh/src/ssh.app.src +++ b/lib/ssh/src/ssh.app.src @@ -10,6 +10,7 @@ ssh_auth, ssh_bits, ssh_cli, + ssh_client_key_api, ssh_channel, ssh_channel_sup, ssh_connection, @@ -21,13 +22,14 @@ sshd_sup, ssh_file, ssh_io, - ssh_key_api, ssh_math, ssh_no_io, + ssh_server_key_api, ssh_sftp, ssh_sftpd, ssh_sftpd_file, ssh_sftpd_file_api, + ssh_subsystem, ssh_subsystem_sup, ssh_sup, ssh_system_sup, @@ -35,7 +37,7 @@ ssh_userreg, ssh_xfer]}, {registered, []}, - {applications, [kernel, stdlib, crypto]}, + {applications, [kernel, stdlib, crypto, public_key]}, {env, []}, {mod, {ssh_app, []}}]}. diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src index 6ba32e018f..826a11f1f4 100644 --- a/lib/ssh/src/ssh.appup.src +++ b/lib/ssh/src/ssh.appup.src @@ -19,29 +19,15 @@ {"%VSN%", [ - {<<"2.1.1">>, [{restart_application, ssh}]}, - {<<"2.1">>, [{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []}, - {load_module, ssh_connection, soft_purge, soft_purge, []}, - {load_module, ssh_connection_manager, soft_purge, soft_purge, []}, - {load_module, ssh_auth, soft_purge, soft_purge, []}, - {load_module, ssh_connection_handler, soft_purge, soft_purge, []}, - {load_module, ssh_channel, soft_purge, soft_purge, []}, - {load_module, ssh_file, soft_purge, soft_purge, []}]}, - {load_module, ssh, soft_purge, soft_purge, []}]}, + {<<"2.1.1">>, [{restart_application, ssh}]}, + {<<"2.1">>, [{restart_application, ssh}]}, {<<"2.0\\.*">>, [{restart_application, ssh}]}, {<<"1\\.*">>, [{restart_application, ssh}]} ], [ {<<"2.1.1">>, [{restart_application, ssh}]}, - {<<"2.1">>,[{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []}, - {load_module, ssh_connection, soft_purge, soft_purge, []}, - {load_module, ssh_connection_manager, soft_purge, soft_purge, []}, - {load_module, ssh_auth, soft_purge, soft_purge, []}, - {load_module, ssh_connection_handler, soft_purge, soft_purge, []}, - {load_module, ssh_channel, soft_purge, soft_purge, []}, - {load_module, ssh_file, soft_purge, soft_purge, []}]}, - {load_module, ssh, soft_purge, soft_purge, []}]}, + {<<"2.1">>,[{restart_application, ssh}]}, {<<"2.0\\.*">>, [{restart_application, ssh}]}, {<<"1\\.*">>, [{restart_application, ssh}]} ] -}.
\ No newline at end of file +}. diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 719c97e940..3ef26b1678 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -41,19 +41,23 @@ %% %% Type = permanent | transient | temporary %% -%% Description: Starts the inets application. Default type +%% Description: Starts the ssh application. Default type %% is temporary. see application(3) %%-------------------------------------------------------------------- start() -> + application:start(crypto), + application:start(public_key), application:start(ssh). start(Type) -> + application:start(crypto, Type), + application:start(public_key, Type), application:start(ssh, Type). %%-------------------------------------------------------------------- %% Function: stop() -> ok %% -%% Description: Stops the inets application. +%% Description: Stops the ssh application. %%-------------------------------------------------------------------- stop() -> application:stop(ssh). @@ -76,7 +80,7 @@ connect(Host, Port, Options, Timeout) -> {error, _Reason} = Error -> Error; {SocketOptions, SshOptions} -> - DisableIpv6 = proplists:get_value(ip_v6_disabled, SshOptions, false), + DisableIpv6 = proplists:get_value(ipv6_disabled, SshOptions, false), Inet = inetopt(DisableIpv6), do_connect(Host, Port, [Inet | SocketOptions], [{user_pid, self()}, {host, Host} | fix_idle_time(SshOptions)], Timeout, DisableIpv6) @@ -169,7 +173,7 @@ daemon(HostAddr, Port, Options0) -> _ -> Options0 end, - DisableIpv6 = proplists:get_value(ip_v6_disabled, Options0, false), + DisableIpv6 = proplists:get_value(ipv6_disabled, Options0, false), {Host, Inet, Options} = case HostAddr of any -> {ok, Host0} = inet:gethostname(), @@ -264,7 +268,7 @@ start_daemon(Host, Port, Options, Inet) -> do_start_daemon(Host, Port, Options, SocketOptions) -> case ssh_system_sup:system_supervisor(Host, Port) of undefined -> - %% TODO: It would proably make more sense to call the + %% It would proably make more sense to call the %% address option host but that is a too big change at the %% monent. The name is a legacy name! try sshd_sup:start_child([{address, Host}, @@ -325,8 +329,6 @@ handle_option([{user_passwords, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{pwdfun, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); -handle_option([{user_auth, _} = Opt | Rest],SocketOptions, SshOptions ) -> - handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{key_cb, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{role, _} = Opt | Rest], SocketOptions, SshOptions) -> @@ -344,7 +346,10 @@ handle_option([{disconnectfun, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{failfun, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); -handle_option([{ip_v6_disabled, _} = Opt | Rest], SocketOptions, SshOptions) -> +%%Backwards compatibility should not be underscore between ip and v6 in API +handle_option([{ip_v6_disabled, Value} | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option({ipv6_disabled, Value}) | SshOptions]); +handle_option([{ipv6_disabled, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{transport, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); @@ -364,6 +369,8 @@ handle_option([{quiet_mode, _} = Opt|Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{idle_time, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{rekey_limit, _} = Opt|Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions). @@ -377,10 +384,14 @@ handle_ssh_option({silently_accept_hosts, Value} = Opt) when Value == true; Valu Opt; handle_ssh_option({user_interaction, Value} = Opt) when Value == true; Value == false -> Opt; -handle_ssh_option({public_key_alg, Value} = Opt) when Value == ssh_rsa; Value == ssh_dsa -> +handle_ssh_option({public_key_alg, ssh_dsa}) -> + {public_key_alg, 'ssh-dss'}; +handle_ssh_option({public_key_alg, ssh_rsa}) -> + {public_key_alg, 'ssh-rsa'}; +handle_ssh_option({public_key_alg, Value} = Opt) when Value == 'ssh-rsa'; Value == 'ssh-dss' -> Opt; handle_ssh_option({pref_public_key_algs, Value} = Opt) when is_list(Value), length(Value) >= 1 -> - case check_pref_algs(Value) of + case handle_pref_algs(Value, []) of true -> Opt; _ -> @@ -400,8 +411,6 @@ handle_ssh_option({user_passwords, Value} = Opt) when is_list(Value)-> Opt; handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value) -> Opt; -handle_ssh_option({user_auth, Value} = Opt) when is_function(Value) -> - Opt; handle_ssh_option({key_cb, Value} = Opt) when is_atom(Value) -> Opt; handle_ssh_option({compression, Value} = Opt) when is_atom(Value) -> @@ -420,7 +429,9 @@ handle_ssh_option({disconnectfun , Value} = Opt) when is_function(Value) -> Opt; handle_ssh_option({failfun, Value} = Opt) when is_function(Value) -> Opt; -handle_ssh_option({ip_v6_disabled, Value} = Opt) when is_boolean(Value) -> + +handle_ssh_option({ipv6_disabled, Value} = Opt) when Value == true; + Value == false -> Opt; handle_ssh_option({transport, {Protocol, Cb, ClosTag}} = Opt) when is_atom(Protocol), is_atom(Cb), @@ -440,6 +451,8 @@ handle_ssh_option({quiet_mode, Value} = Opt) when Value == true; Opt; handle_ssh_option({idle_time, Value} = Opt) when is_integer(Value), Value > 0 -> Opt; +handle_ssh_option({rekey_limit, Value} = Opt) when is_integer(Value) -> + Opt; handle_ssh_option(Opt) -> throw({error, {eoptions, Opt}}). @@ -448,7 +461,7 @@ handle_inet_option({active, _} = Opt) -> "and activ is handled internaly user is not allowd" "to specify this option"}}); handle_inet_option({inet, _} = Opt) -> - throw({error, {{eoptions, Opt},"Is set internaly use ip_v6_disabled to" + throw({error, {{eoptions, Opt},"Is set internaly use ipv6_disabled to" " enforce iv4 in the server, client will fallback to ipv4 if" " it can not use ipv6"}}); handle_inet_option({reuseaddr, _} = Opt) -> @@ -458,14 +471,18 @@ handle_inet_option({reuseaddr, _} = Opt) -> handle_inet_option(Opt) -> Opt. %% Check preferred algs -check_pref_algs([]) -> - true; -check_pref_algs([H|T]) -> +handle_pref_algs([], Acc) -> + {true, lists:reverse(Acc)}; +handle_pref_algs([H|T], Acc) -> case H of ssh_dsa -> - check_pref_algs(T); + handle_pref_algs(T, ['ssh-dss'| Acc]); ssh_rsa -> - check_pref_algs(T); + handle_pref_algs(T, ['ssh-rsa'| Acc]); + 'ssh-dss' -> + handle_pref_algs(T, ['ssh-dss'| Acc]); + 'ssh-rsa' -> + handle_pref_algs(T, ['ssh-rsa'| Acc]); _ -> false end. diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index c436793dc4..cb0c7751f0 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -48,17 +48,18 @@ publickey_msg([Alg, #ssh{user = User, case KeyCb:user_key(Alg, Opts) of {ok, Key} -> + StrAlgo = algorithm_string(Alg), PubKeyBlob = encode_public_key(Key), SigData = build_sig_data(SessionId, - User, Service, PubKeyBlob, Alg), + User, Service, PubKeyBlob, StrAlgo), Sig = ssh_transport:sign(SigData, Hash, Key), - SigBlob = list_to_binary([?string(Alg), ?binary(Sig)]), + SigBlob = list_to_binary([?string(StrAlgo), ?binary(Sig)]), ssh_transport:ssh_packet( #ssh_msg_userauth_request{user = User, service = Service, method = "publickey", data = [?TRUE, - ?string(Alg), + ?string(StrAlgo), ?binary(PubKeyBlob), ?binary(SigBlob)]}, Ssh); @@ -120,8 +121,7 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) -> data = <<>>}, case proplists:get_value(pref_public_key_algs, Opts, false) of false -> - FirstAlg = algorithm(proplists:get_value(public_key_alg, Opts, - ?PREFERRED_PK_ALG)), + FirstAlg = proplists:get_value(public_key_alg, Opts, ?PREFERRED_PK_ALG), SecondAlg = other_alg(FirstAlg), AllowUserInt = proplists:get_value(user_interaction, Opts, true), Prefs = method_preference(FirstAlg, SecondAlg, AllowUserInt), @@ -130,7 +130,7 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) -> userauth_methods = none, service = "ssh-connection"}); Algs -> - FirstAlg = algorithm(lists:nth(1, Algs)), + FirstAlg = lists:nth(1, Algs), case length(Algs) =:= 2 of true -> SecondAlg = other_alg(FirstAlg), @@ -358,7 +358,7 @@ verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) -> {ok, Key} = decode_public_key_v2(KeyBlob, Alg), KeyCb = proplists:get_value(key_cb, Opts, ssh_file), - case KeyCb:is_auth_key(Key, User, Alg, Opts) of + case KeyCb:is_auth_key(Key, User, Opts) of true -> PlainText = build_sig_data(SessionId, User, Service, KeyBlob, Alg), @@ -381,9 +381,9 @@ build_sig_data(SessionId, User, Service, KeyBlob, Alg) -> ?binary(KeyBlob)], list_to_binary(Sig). -algorithm(ssh_rsa) -> +algorithm_string('ssh-rsa') -> "ssh-rsa"; -algorithm(ssh_dsa) -> +algorithm_string('ssh-dss') -> "ssh-dss". decode_keyboard_interactive_prompts(NumPrompts, Data) -> @@ -457,10 +457,10 @@ userauth_pk_messages() -> binary]} % key blob ]. -other_alg("ssh-rsa") -> - "ssh-dss"; -other_alg("ssh-dss") -> - "ssh-rsa". +other_alg('ssh-rsa') -> + 'ssh-dss'; +other_alg('ssh-dss') -> + 'ssh-rsa'. decode_public_key_v2(K_S, "ssh-rsa") -> case ssh_bits:decode(K_S,[string,mpint,mpint]) of ["ssh-rsa", E, N] -> diff --git a/lib/ssh/src/ssh_auth.hrl b/lib/ssh/src/ssh_auth.hrl index e74ee10041..6cd8e6bf14 100644 --- a/lib/ssh/src/ssh_auth.hrl +++ b/lib/ssh/src/ssh_auth.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -23,7 +23,7 @@ -define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password"). --define(PREFERRED_PK_ALG, ssh_rsa). +-define(PREFERRED_PK_ALG, 'ssh-rsa'). -define(SSH_MSG_USERAUTH_REQUEST, 50). -define(SSH_MSG_USERAUTH_FAILURE, 51). diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl index 1938858420..4e8f8538c2 100644 --- a/lib/ssh/src/ssh_channel.erl +++ b/lib/ssh/src/ssh_channel.erl @@ -23,14 +23,35 @@ -include("ssh_connect.hrl"). -%%% Optional callbacks handle_call/3, handle_cast/2, handle_msg/2, -%%% code_change/3 -%% Should be further specified later --callback init(Options::list()) -> - {ok, State::term()} | {ok, State::term(), Timeout::timeout()} | - {stop, Reason ::term()}. - --callback terminate(term(), term()) -> term(). +-callback init(Args :: term()) -> + {ok, State :: term()} | {ok, State :: term(), timeout() | hibernate} | + {stop, Reason :: term()} | ignore. +-callback handle_call(Request :: term(), From :: {pid(), Tag :: term()}, + State :: term()) -> + {reply, Reply :: term(), NewState :: term()} | + {reply, Reply :: term(), NewState :: term(), timeout() | hibernate} | + {noreply, NewState :: term()} | + {noreply, NewState :: term(), timeout() | hibernate} | + {stop, Reason :: term(), Reply :: term(), NewState :: term()} | + {stop, Reason :: term(), NewState :: term()}. +-callback handle_cast(Request :: term(), State :: term()) -> + {noreply, NewState :: term()} | + {noreply, NewState :: term(), timeout() | hibernate} | + {stop, Reason :: term(), NewState :: term()}. + +-callback terminate(Reason :: (normal | shutdown | {shutdown, term()} | + term()), + State :: term()) -> + term(). +-callback code_change(OldVsn :: (term() | {down, term()}), State :: term(), + Extra :: term()) -> + {ok, NewState :: term()} | {error, Reason :: term()}. + +-callback handle_msg(Msg ::term(), State :: term()) -> + {noreply, NewState :: term()} | + {noreply, NewState :: term(), timeout() | hibernate} | + {stop, Reason :: term(), NewState :: term()}. + -callback handle_ssh_msg({ssh_cm, ConnectionRef::term(), SshMsg::term()}, State::term()) -> {ok, State::term()} | diff --git a/lib/ssh/src/ssh_client_key.erl b/lib/ssh/src/ssh_client_key.erl new file mode 100644 index 0000000000..2c48884dc2 --- /dev/null +++ b/lib/ssh/src/ssh_client_key.erl @@ -0,0 +1,34 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011-2012. 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(ssh_client_key). + +-include_lib("public_key/include/public_key.hrl"). +-include("ssh.hrl"). + +-callback is_host_key(Key :: public_key(), Host :: string(), + Algorithm :: 'ssh-rsa'| 'ssh-dsa'| atom(), Options :: proplists:proplist()) -> + boolean(). + +-callback user_key(Algorithm :: 'ssh-rsa'| 'ssh-dsa'| atom(), Options :: list()) -> + {ok, PrivateKey :: term()} | {error, string()}. + + +-callback add_host_key(Host :: string(), PublicKey :: term(), Options :: list()) -> + ok | {error, Error::term()}. diff --git a/lib/ssh/src/ssh_client_key_api.erl b/lib/ssh/src/ssh_client_key_api.erl new file mode 100644 index 0000000000..eed0b85f47 --- /dev/null +++ b/lib/ssh/src/ssh_client_key_api.erl @@ -0,0 +1,35 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011-2012. 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(ssh_client_key_api). + +-include_lib("public_key/include/public_key.hrl"). +-include("ssh.hrl"). + +-callback is_host_key(PublicKey :: #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term() , Host :: string(), + Algorithm :: 'ssh-rsa'| 'ssh-dss'| atom(), ConnectOptions :: proplists:proplist()) -> + boolean(). + +-callback user_key(Algorithm :: 'ssh-rsa'| 'ssh-dss'| atom(), ConnectOptions :: proplists:proplists()) -> + {ok, PrivateKey :: #'RSAPrivateKey'{}| #'DSAPrivateKey'{} | term()} | {error, string()}. + + +-callback add_host_key(Host :: string(), PublicKey :: #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term(), + Options :: list()) -> + ok | {error, Error::term()}. diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index d8950a7b67..88b45111ff 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -35,7 +35,8 @@ -export([start_link/4, send/2, renegotiate/1, send_event/2, connection_info/3, - peer_address/1]). + peer_address/1, + renegotiate_data/1]). %% gen_fsm callbacks -export([hello/2, kexinit/2, key_exchange/2, new_keys/2, @@ -85,6 +86,8 @@ send(ConnectionHandler, Data) -> renegotiate(ConnectionHandler) -> send_all_state_event(ConnectionHandler, renegotiate). +renegotiate_data(ConnectionHandler) -> + send_all_state_event(ConnectionHandler, data_size). connection_info(ConnectionHandler, From, Options) -> send_all_state_event(ConnectionHandler, {info, From, Options}). @@ -500,7 +503,22 @@ handle_event(renegotiate, StateName, State) -> handle_event({info, From, Options}, StateName, #state{ssh_params = Ssh} = State) -> spawn(?MODULE, ssh_info_handler, [Options, Ssh, From]), {next_state, StateName, State}; - +handle_event(data_size, connected, #state{ssh_params = Ssh0} = State) -> + Sent = inet:getstat(State#state.socket, [send_oct]), + MaxSent = proplists:get_value(rekey_limit, State#state.opts, 1024000000), + case Sent >= MaxSent of + true -> + {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh0), + send_msg(SshPacket, State), + {next_state, connected, + next_packet(State#state{ssh_params = Ssh, + key_exchange_init_msg = KeyInitMsg, + renegotiate = true})}; + _ -> + {next_state, connected, next_packet(State)} + end; +handle_event(data_size, StateName, State) -> + {next_state, StateName, State}; handle_event({unknown, Data}, StateName, State) -> Msg = #ssh_msg_unimplemented{sequence = Data}, send_msg(Msg, State), @@ -749,9 +767,9 @@ extract_algs([], NewList) -> lists:reverse(NewList); extract_algs([H|T], NewList) -> case H of - ssh_dsa -> + 'ssh-dss' -> extract_algs(T, ["ssh-dss"|NewList]); - ssh_rsa -> + 'ssh-rsa' -> extract_algs(T, ["ssh-rsa"|NewList]) end. available_host_key(KeyCb, "ssh-dss"= Alg, Opts) -> diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl index 0c1eee5186..94a9ed505f 100644 --- a/lib/ssh/src/ssh_connection_manager.erl +++ b/lib/ssh/src/ssh_connection_manager.erl @@ -125,7 +125,8 @@ info(ConnectionManager, ChannelProcess) -> %% or amount of data sent counter! renegotiate(ConnectionManager) -> cast(ConnectionManager, renegotiate). - +renegotiate_data(ConnectionManager) -> + cast(ConnectionManager, renegotiate_data). connection_info(ConnectionManager, Options) -> call(ConnectionManager, {connection_info, Options}). @@ -481,7 +482,9 @@ handle_cast({global_request, _, _, _, _} = Request, State0) -> handle_cast(renegotiate, #state{connection = Pid} = State) -> ssh_connection_handler:renegotiate(Pid), {noreply, State}; - +handle_cast(renegotiate_data, #state{connection = Pid} = State) -> + ssh_connection_handler:renegotiate_data(Pid), + {noreply, State}; handle_cast({adjust_window, ChannelId, Bytes}, #state{connection = Pid, connection_state = #connection{channel_cache = Cache}} = State) -> @@ -520,6 +523,8 @@ handle_info({start_connection, server, Exec = proplists:get_value(exec, Options), CliSpec = proplists:get_value(ssh_cli, Options, {ssh_cli, [Shell]}), ssh_connection_handler:send_event(Connection, socket_control), + erlang:send_after(3600000, self(), rekey), + erlang:send_after(60000, self(), rekey_data), {noreply, State#state{connection = Connection, connection_state = CState#connection{address = Address, @@ -536,6 +541,8 @@ handle_info({start_connection, client, case (catch ssh_transport:connect(Parent, Address, Port, SocketOpts, Options)) of {ok, Connection} -> + erlang:send_after(60000, self(), rekey_data), + erlang:send_after(3600000, self(), rekey), {noreply, State#state{connection = Connection}}; Reason -> Pid ! {self(), not_connected, Reason}, @@ -568,8 +575,15 @@ handle_info({'DOWN', _Ref, process, ChannelPid, _Reason}, State) -> %%% So that terminate will be run when supervisor is shutdown handle_info({'EXIT', _Sup, Reason}, State) -> - {stop, Reason, State}. - + {stop, Reason, State}; +handle_info(rekey, State) -> + renegotiate(self()), + erlang:send_after(3600000, self(), rekey), + {noreply, State}; +handle_info(rekey_data, State) -> + renegotiate_data(self()), + erlang:send_after(60000, self(), rekey_data), + {noreply, State}. handle_password(Opts) -> handle_rsa_password(handle_dsa_password(handle_normal_password(Opts))). handle_normal_password(Opts) -> diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl index a6b82a7a13..f115a32710 100644 --- a/lib/ssh/src/ssh_file.erl +++ b/lib/ssh/src/ssh_file.erl @@ -23,7 +23,8 @@ -module(ssh_file). --behaviour(ssh_key_api). +-behaviour(ssh_server_key_api). +-behaviour(ssh_client_key_api). -include_lib("public_key/include/public_key.hrl"). -include_lib("kernel/include/file.hrl"). @@ -34,7 +35,7 @@ user_key/2, is_host_key/4, add_host_key/3, - is_auth_key/4]). + is_auth_key/3]). -define(PERM_700, 8#700). @@ -53,8 +54,8 @@ host_key(Algorithm, Opts) -> decode(File, Password). -is_auth_key(Key, User, Alg, Opts) -> - case lookup_user_key(Key, User, Alg, Opts) of +is_auth_key(Key, User,Opts) -> + case lookup_user_key(Key, User, Opts) of {ok, Key} -> true; _ -> @@ -138,13 +139,13 @@ add_host_key(Host, Key, Opts) -> Error end. -lookup_user_key(Key, User, Alg, Opts) -> +lookup_user_key(Key, User, Opts) -> SshDir = ssh_dir({remoteuser,User}, Opts), - case lookup_user_key_f(Key, User, SshDir, Alg, "authorized_keys", Opts) of + case lookup_user_key_f(Key, User, SshDir, "authorized_keys", Opts) of {ok, Key} -> {ok, Key}; _ -> - lookup_user_key_f(Key, User, SshDir, Alg, "authorized_keys2", Opts) + lookup_user_key_f(Key, User, SshDir, "authorized_keys2", Opts) end. @@ -213,9 +214,9 @@ do_lookup_host_key(Host, Alg, Opts) -> Error -> Error end. -identity_key_filename("ssh-dss") -> +identity_key_filename('ssh-dss') -> "id_dsa"; -identity_key_filename("ssh-rsa") -> +identity_key_filename('ssh-rsa') -> "id_rsa". identity_pass_phrase("ssh-dss") -> @@ -261,9 +262,9 @@ host_name(Atom) when is_atom(Atom) -> host_name(List) -> List. -key_match(#'RSAPublicKey'{}, "ssh-rsa") -> +key_match(#'RSAPublicKey'{}, 'ssh-rsa') -> true; -key_match({_, #'Dss-Parms'{}}, "ssh-dss") -> +key_match({_, #'Dss-Parms'{}}, 'ssh-dss') -> true; key_match(_, _) -> false. @@ -272,11 +273,11 @@ add_key_fd(Fd, Host,Key) -> SshBin = public_key:ssh_encode([{Key, [{hostnames, [Host]}]}], known_hosts), file:write(Fd, SshBin). -lookup_user_key_f(_, _User, [], _Alg, _F, _Opts) -> +lookup_user_key_f(_, _User, [], _F, _Opts) -> {error, nouserdir}; -lookup_user_key_f(_, _User, nouserdir, _Alg, _F, _Opts) -> +lookup_user_key_f(_, _User, nouserdir, _F, _Opts) -> {error, nouserdir}; -lookup_user_key_f(Key, _User, Dir, _Alg, F, _Opts) -> +lookup_user_key_f(Key, _User, Dir, F, _Opts) -> FileName = filename:join(Dir, F), case file:open(FileName, [read, binary]) of {ok, Fd} -> diff --git a/lib/ssh/src/ssh_key_api.erl b/lib/ssh/src/ssh_key_api.erl deleted file mode 100644 index 8085c12e21..0000000000 --- a/lib/ssh/src/ssh_key_api.erl +++ /dev/null @@ -1,45 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2011-2012. 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(ssh_key_api). - --include_lib("public_key/include/public_key.hrl"). --include("ssh.hrl"). - --type ssh_algorithm() :: string(). --type file_error() :: file:posix() | badarg | system_limit | terminated. - --callback host_key(Algorithm :: ssh_algorithm(), Options :: list()) -> - {ok, [{public_key(), Attributes::list()}]} | public_key() - | {error, string()}. - --callback user_key(Algorithm :: ssh_algorithm(), Options :: list()) -> - {ok, [{public_key(), Attributes::list()}]} | public_key() - | {error, string()}. - --callback is_host_key(Key :: public_key(), PeerName :: string(), - Algorithm :: ssh_algorithm(), Options :: list()) -> - boolean(). - --callback add_host_key(Host :: string(), Key :: public_key(), Options :: list()) -> - ok | {error, file_error()}. - --callback is_auth_key(Key :: public_key(), User :: string(), - Algorithm :: ssh_algorithm(), Options :: list()) -> - boolean(). diff --git a/lib/compiler/test/compilation_SUITE_data/bad_functional_value.erl b/lib/ssh/src/ssh_server_key.erl index 126a573e83..8140114990 100644 --- a/lib/compiler/test/compilation_SUITE_data/bad_functional_value.erl +++ b/lib/ssh/src/ssh_server_key.erl @@ -1,28 +1,33 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2011-2012. 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(bad_functional_value). --export([?MODULE/0,a/0]). +-module(ssh_server_key). + +-include_lib("public_key/include/public_key.hrl"). +-include("ssh.hrl"). -?MODULE() -> - ok. +-type ssh_algorithm() :: string(). -a() -> - .list_to_atom("ok"). +-callback host_key(Algorithm :: ssh_algorithm(), Options :: list()) -> + {ok, [{public_key(), Attributes::list()}]} | public_key() + | {error, string()}. +-callback is_auth_key(Key :: public_key(), User :: string(), + Algorithm :: ssh_algorithm(), Options :: list()) -> + boolean(). diff --git a/lib/ssh/src/ssh_server_key_api.erl b/lib/ssh/src/ssh_server_key_api.erl new file mode 100644 index 0000000000..4fd660ecb5 --- /dev/null +++ b/lib/ssh/src/ssh_server_key_api.erl @@ -0,0 +1,30 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011-2012. 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(ssh_server_key_api). + +-include_lib("public_key/include/public_key.hrl"). +-include("ssh.hrl"). + +-callback host_key(Algorithm :: 'ssh-rsa'| 'ssh-dss'| atom(), DaemonOptions :: proplists:proplist()) -> + {ok, PrivateKey :: #'RSAPrivateKey'{}| #'DSAPrivateKey'{} | term()} | {error, string()}. + +-callback is_auth_key(PublicKey :: #'RSAPublicKey'{}| {integer(), #'Dss-Parms'{}}| term(), + User :: string(), DaemonOptions :: proplists:proplist()) -> + boolean(). diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl index ec7b76b0b3..c7e8373840 100644 --- a/lib/ssh/src/ssh_sftpd.erl +++ b/lib/ssh/src/ssh_sftpd.erl @@ -24,7 +24,7 @@ -module(ssh_sftpd). %%-behaviour(gen_server). --behaviour(ssh_channel). +-behaviour(ssh_subsystem). -include_lib("kernel/include/file.hrl"). @@ -36,7 +36,7 @@ -export([subsystem_spec/1, listen/1, listen/2, listen/3, stop/1]). --export([init/1, handle_ssh_msg/2, handle_msg/2, terminate/2, code_change/3]). +-export([init/1, handle_ssh_msg/2, handle_msg/2, terminate/2]). -record(state, { xf, % [{channel,ssh_xfer states}...] @@ -119,23 +119,13 @@ init(Options) -> {Root0, State0} end, MaxLength = proplists:get_value(max_files, Options, 0), - - Vsn = proplists:get_value(vsn, Options, 5), - + Vsn = proplists:get_value(sftpd_vsn, Options, 5), {ok, State#state{cwd = CWD, root = Root, max_files = MaxLength, handles = [], pending = <<>>, xf = #ssh_xfer{vsn = Vsn, ext = []}}}. %%-------------------------------------------------------------------- -%% Function: code_change(OldVsn, State, Extra) -> {ok, NewState} -%% Description: -%%-------------------------------------------------------------------- -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - - -%%-------------------------------------------------------------------- %% Function: handle_ssh_msg(Args) -> {ok, State} | {stop, ChannelId, State} %% %% Description: Handles channel messages @@ -369,17 +359,21 @@ handle_op(?SSH_FXP_FSETSTAT, ReqId, <<?UINT32(HLen), BinHandle:HLen/binary, State0 end; handle_op(?SSH_FXP_REMOVE, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>, - State0 = #state{file_handler = FileMod, file_state = FS0}) -> + State0 = #state{file_handler = FileMod, file_state = FS0, xf = #ssh_xfer{vsn = Vsn}}) -> Path = relate_file_name(BPath, State0), - %% case FileMod:is_dir(Path) of %% This version 6 we still have ver 5 - %% true -> - %% ssh_xfer:xf_send_status(State#state.xf, ReqId, - %% ?SSH_FX_FILE_IS_A_DIRECTORY); - %% false -> - {Status, FS1} = FileMod:delete(Path, FS0), - State1 = State0#state{file_state = FS1}, - send_status(Status, ReqId, State1); - %%end; + {IsDir, _FS1} = FileMod:is_dir(Path, FS0), + case IsDir of %% This version 6 we still have ver 5 + true when Vsn > 5 -> + ssh_xfer:xf_send_status(State0#state.xf, ReqId, + ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory"); + true -> + ssh_xfer:xf_send_status(State0#state.xf, ReqId, + ?SSH_FX_FAILURE, "File is a directory"); + false -> + {Status, FS1} = FileMod:delete(Path, FS0), + State1 = State0#state{file_state = FS1}, + send_status(Status, ReqId, State1) + end; handle_op(?SSH_FXP_RMDIR, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>, State0 = #state{file_handler = FileMod, file_state = FS0}) -> Path = relate_file_name(BPath, State0), @@ -637,31 +631,34 @@ open(Vsn, ReqId, Data, State) when Vsn >= 4 -> do_open(ReqId, State, Path, Flags). do_open(ReqId, State0, Path, Flags) -> - #state{file_handler = FileMod, file_state = FS0, root = Root} = State0, + #state{file_handler = FileMod, file_state = FS0, root = Root, xf = #ssh_xfer{vsn = Vsn}} = State0, XF = State0#state.xf, F = [binary | Flags], - %% case FileMod:is_dir(Path) of %% This is version 6 we still have 5 - %% true -> - %% ssh_xfer:xf_send_status(State#state.xf, ReqId, - %% ?SSH_FX_FILE_IS_A_DIRECTORY); - %% false -> - - AbsPath = case Root of - "" -> - Path; - _ -> - relate_file_name(Path, State0) - end, - - {Res, FS1} = FileMod:open(AbsPath, F, FS0), - State1 = State0#state{file_state = FS1}, - case Res of - {ok, IoDevice} -> - add_handle(State1, XF, ReqId, file, {Path,IoDevice}); - {error, Error} -> - ssh_xfer:xf_send_status(State1#state.xf, ReqId, - ssh_xfer:encode_erlang_status(Error)), - State1 + {IsDir, _FS1} = FileMod:is_dir(Path, FS0), + case IsDir of + true when Vsn > 5 -> + ssh_xfer:xf_send_status(State0#state.xf, ReqId, + ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory"); + true -> + ssh_xfer:xf_send_status(State0#state.xf, ReqId, + ?SSH_FX_FAILURE, "File is a directory"); + false -> + AbsPath = case Root of + "" -> + Path; + _ -> + relate_file_name(Path, State0) + end, + {Res, FS1} = FileMod:open(AbsPath, F, FS0), + State1 = State0#state{file_state = FS1}, + case Res of + {ok, IoDevice} -> + add_handle(State1, XF, ReqId, file, {Path,IoDevice}); + {error, Error} -> + ssh_xfer:xf_send_status(State1#state.xf, ReqId, + ssh_xfer:encode_erlang_status(Error)), + State1 + end end. %% resolve all symlinks in a path diff --git a/lib/ssh/src/ssh_subsystem.erl b/lib/ssh/src/ssh_subsystem.erl new file mode 100644 index 0000000000..5a9fa32668 --- /dev/null +++ b/lib/ssh/src/ssh_subsystem.erl @@ -0,0 +1,47 @@ +-module(ssh_subsystem). + +%% API to special server side channel that can be pluged into the erlang ssh daemeon +-callback init(Args :: term()) -> + {ok, State :: term()} | {ok, State :: term(), timeout() | hibernate} | + {stop, Reason :: term()} | ignore. + +-callback terminate(Reason :: (normal | shutdown | {shutdown, term()} | + term()), + State :: term()) -> + term(). + +-callback handle_msg(Msg ::term(), State :: term()) -> + {noreply, NewState :: term()} | + {noreply, NewState :: term(), timeout() | hibernate} | + {stop, Reason :: term(), NewState :: term()}. + +-callback handle_ssh_msg({ssh_cm, ConnectionRef::term(), SshMsg::term()}, + State::term()) -> {ok, State::term()} | + {stop, ChannelId::integer(), + State::term()}. + +%%% API +-export([start/4, start/5, start_link/4, start_link/5, enter_loop/1]). + +%% gen_server callbacks +-export([init/1, terminate/2]). + +start(ConnectionManager, ChannelId, CallBack, CbInitArgs) -> + ssh_channel:start(ConnectionManager, ChannelId, CallBack, CbInitArgs, undefined). + +start(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec) -> + ssh_channel:start(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec). + +start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs) -> + ssh_channel:start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs, undefined). + +start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec) -> + ssh_channel:start_link(ConnectionManager, ChannelId, CallBack, CbInitArgs, Exec). + +enter_loop(State) -> + ssh_channel:enter_loop(State). + +init(Args) -> + ssh_channel:init(Args). +terminate(Reason, State) -> + ssh_channel:terminate(Reason, State). diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 7f6e7d9946..1abb69921d 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -449,7 +449,7 @@ verify_host_key_rsa(SSH, K_S, H, H_SIG) -> false -> {error, bad_signature}; true -> - known_host_key(SSH, Public, "ssh-rsa") + known_host_key(SSH, Public, 'ssh-rsa') end; _ -> {error, bad_format} @@ -464,7 +464,7 @@ verify_host_key_dss(SSH, K_S, H, H_SIG) -> false -> {error, bad_signature}; true -> - known_host_key(SSH, Public, "ssh-dss") + known_host_key(SSH, Public, 'ssh-dss') end; _ -> {error, bad_host_key_format} diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl index d5b6dd03d1..4dfd9ed8b0 100644 --- a/lib/ssh/src/ssh_xfer.erl +++ b/lib/ssh/src/ssh_xfer.erl @@ -383,6 +383,8 @@ decode_status(Status) -> ?SSH_FX_UNKNOWN_PRINCIPLE -> unknown_principle; ?SSH_FX_LOCK_CONFlICT -> lock_conflict; ?SSH_FX_NOT_A_DIRECTORY -> not_a_directory; + ?SSH_FX_FILE_IS_A_DIRECTORY -> file_is_a_directory; + ?SSH_FX_CANNOT_DELETE -> cannot_delete; _ -> {error,Status} end. @@ -392,6 +394,9 @@ encode_erlang_status(Status) -> eof -> ?SSH_FX_EOF; enoent -> ?SSH_FX_NO_SUCH_FILE; eacces -> ?SSH_FX_PERMISSION_DENIED; + eisdir -> ?SSH_FX_FILE_IS_A_DIRECTORY; + eperm -> ?SSH_FX_CANNOT_DELETE; + eexist -> ?SSH_FX_FILE_ALREADY_EXISTS; _ -> ?SSH_FX_FAILURE end. diff --git a/lib/ssh/src/ssh_xfer.hrl b/lib/ssh/src/ssh_xfer.hrl index c13950eb6e..0d85cf2094 100644 --- a/lib/ssh/src/ssh_xfer.hrl +++ b/lib/ssh/src/ssh_xfer.hrl @@ -58,7 +58,6 @@ %%% # SSH_FX_xxx %%% Description: Response packet types for file transfer protocol. %%%---------------------------------------------------------------------- - -define(SSH_FX_OK, 0). -define(SSH_FX_EOF, 1). -define(SSH_FX_NO_SUCH_FILE, 2). @@ -79,7 +78,18 @@ -define(SSH_FX_LOCK_CONFlICT, 17). -define(SSH_FX_DIR_NOT_EMPTY, 18). -define(SSH_FX_NOT_A_DIRECTORY, 19). +-define(SSH_FX_INVALID_FILENAME, 20). +-define(SSH_FX_LINK_LOOP, 21). +-define(SSH_FX_CANNOT_DELETE, 22). +-define(SSH_FX_INVALID_PARAMETER, 23). -define(SSH_FX_FILE_IS_A_DIRECTORY, 24). +-define(SSH_FX_BYTE_RANGE_LOCK_CONFLICT,25). +-define(SSH_FX_BYTE_RANGE_LOCK_REFUSED, 26). +-define(SSH_FX_DELETE_PENDING, 27). +-define(SSH_FX_FILE_CORRUPT, 28). +-define(SSH_FX_OWNER_INVALID, 29). +-define(SSH_FX_GROUP_INVALID, 30). +-define(SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK,31). %%%---------------------------------------------------------------------- %%% # SSH_FILEXFER_xxx diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 5fec7f0cd7..efcb11f88f 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -42,15 +42,14 @@ all() -> {group, dsa_pass_key}, {group, rsa_pass_key}, {group, internal_error}, - {group, idle_time}, daemon_already_started, server_password_option, server_userpassword_option, close]. groups() -> - [{dsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time]}, - {rsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time]}, + [{dsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey]}, + {rsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey]}, {dsa_pass_key, [], [pass_phrase]}, {rsa_pass_key, [], [pass_phrase]}, {internal_error, [], [internal_error]} @@ -247,7 +246,8 @@ idle_time(Config) -> ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user_dir, UserDir}, - {user_interaction, false}]), + {user_interaction, false}, + {idle_time, 2000}]), {ok, Id} = ssh_connection:session_channel(ConnectionRef, 1000), ssh_connection:close(ConnectionRef, Id), receive @@ -256,6 +256,28 @@ idle_time(Config) -> end, ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- +rekey(doc) -> + ["Idle timeout test"]; +rekey(Config) -> + SystemDir = filename:join(?config(priv_dir, Config), system), + UserDir = ?config(priv_dir, Config), + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}, + {rekey_limit, 0}]), + ConnectionRef = + ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user_dir, UserDir}, + {user_interaction, false}, + {rekey_limit, 0}]), + receive + after 15000 -> + %%By this time rekeying would have been done + ssh:close(ConnectionRef), + ssh:stop_daemon(Pid) + end. +%%-------------------------------------------------------------------- shell(doc) -> ["Test that ssh:shell/2 works"]; shell(Config) when is_list(Config) -> diff --git a/lib/ssh/test/ssh_echo_server.erl b/lib/ssh/test/ssh_echo_server.erl index 739aabe6fb..007b00c373 100644 --- a/lib/ssh/test/ssh_echo_server.erl +++ b/lib/ssh/test/ssh_echo_server.erl @@ -21,7 +21,7 @@ %%% Description: Example ssh server -module(ssh_echo_server). --behaviour(ssh_channel). +-behaviour(ssh_subsytem). -record(state, { n, id, @@ -50,7 +50,7 @@ handle_ssh_msg({ssh_cm, CM, {data, ChannelId, 0, Data}}, #state{n = N} = State) end; handle_ssh_msg({ssh_cm, _ConnectionManager, {data, _ChannelId, 1, Data}}, State) -> - error_logger:format("ssh: STDERR: ~s\n", [binary_to_list(Data)]), + error_logger:format(standard_error, " ~p~n", [binary_to_list(Data)]), {ok, State}; handle_ssh_msg({ssh_cm, _ConnectionManager, {eof, _ChannelId}}, State) -> diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl index b995eb9f0e..5aa46872ee 100644 --- a/lib/ssh/test/ssh_sftpd_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_SUITE.erl @@ -58,7 +58,8 @@ all() -> links, ver3_rename, relpath, - sshd_read_file]. + sshd_read_file, + ver6_basic]. groups() -> []. @@ -106,12 +107,18 @@ init_per_testcase(TestCase, Config) -> SystemDir = filename:join(?config(priv_dir, Config), system), Port = ssh_test_lib:inet_port(node()), - - {ok, Sftpd} = - ssh_sftpd:listen(Port, [{system_dir, SystemDir}, - {user_dir, PrivDir}, - {user_passwords,[{?USER, ?PASSWD}]}, - {pwdfun, fun(_,_) -> true end}]), + Options = [{system_dir, SystemDir}, + {user_dir, PrivDir}, + {user_passwords,[{?USER, ?PASSWD}]}, + {pwdfun, fun(_,_) -> true end}], + {ok, Sftpd} = case TestCase of + ver6_basic -> + SubSystems = [ssh_sftpd:subsystem_spec([{sftpd_vsn, 6}])], + ssh:daemon(Port, [{subsystems, SubSystems}|Options]); + _ -> + SubSystems = [ssh_sftpd:subsystem_spec([])], + ssh:daemon(Port, [{subsystems, SubSystems}|Options]) + end, Cm = ssh_test_lib:connect(Port, [{user_dir, ClientUserDir}, @@ -341,7 +348,7 @@ mk_rm_dir(Config) when is_list(Config) -> _/binary>>, _} = mkdir(DirName, Cm, Channel, ReqId), NewReqId = 1, - {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_FAILURE), + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_FILE_ALREADY_EXISTS), _/binary>>, _} = mkdir(DirName, Cm, Channel, NewReqId), NewReqId1 = 2, @@ -591,7 +598,18 @@ sshd_read_file(Config) when is_list(Config) -> read_file(Handle, 100, 0, Cm, Channel, NewReqId), {ok, Data} = file:read_file(FileName). - +ver6_basic(doc) -> + ["Test SFTP Version 6"]; +ver6_basic(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + %FileName = filename:join(PrivDir, "test.txt"), + {Cm, Channel} = ?config(sftp, Config), + ReqId = 0, + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), % Ver 6 we have 5 + ?UINT32(?SSH_FX_FILE_IS_A_DIRECTORY), _/binary>>, _} = + open_file(PrivDir, Cm, Channel, ReqId, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING). %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl index 7fc2312661..8f722941d4 100644 --- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl @@ -42,7 +42,8 @@ all() -> quit, file_cb, root_dir, - list_dir_limited]. + list_dir_limited, + ver6_basic]. groups() -> []. @@ -112,7 +113,12 @@ init_per_testcase(TestCase, Config) -> [{system_dir, SystemDir}, {user_dir, PrivDir}, {subsystems, [Spec]}]; - + "ver6_basic" -> + Spec = + ssh_sftpd:subsystem_spec([{sftpd_vsn, 6}]), + [{system_dir, SystemDir}, + {user_dir, PrivDir}, + {subsystems, [Spec]}]; _ -> [{user_dir, PrivDir}, {system_dir, SystemDir}] @@ -232,7 +238,7 @@ file_cb(Config) when is_list(Config) -> NewDir = filename:join(PrivDir, "testdir"), ok = ssh_sftp:make_dir(Sftp, NewDir), alt_file_handler_check(alt_make_dir), - + ok = ssh_sftp:del_dir(Sftp, NewDir), alt_file_handler_check(alt_read_link_info), alt_file_handler_check(alt_write_file_info), @@ -260,6 +266,15 @@ list_dir_limited(Config) when is_list(Config) -> ssh_sftp:list_dir(Sftp, "."), ct:pal("Listing: ~p~n", [Listing]). +ver6_basic(doc) -> + ["Test some version 6 features"]; +ver6_basic(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + NewDir = filename:join(PrivDir, "testdir2"), + {Sftp, _} = ?config(sftp, Config), + ok = ssh_sftp:make_dir(Sftp, NewDir), + %%Test file_is_a_directory + {error, file_is_a_directory} = ssh_sftp:delete(Sftp, NewDir). %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl index 9e119c4929..9f8a7c496c 100644 --- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl @@ -48,7 +48,7 @@ get_cwd(State) -> {file:get_cwd(), State}. is_dir(AbsPath, State) -> - sftpd_file_alt_tester ! alt_is_dir, + %sftpd_file_alt_tester ! alt_is_dir, {filelib:is_dir(AbsPath), State}. list_dir(AbsPath, State) -> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index f0eac76264..e45a4c774f 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -36,8 +36,8 @@ <list type="bulleted"> <item>ssl requires the crypto and public_key applications.</item> - <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0, experimental - support for TLS-1.1 and TLS-1.2 is also available (no support for elliptic curve cipher suites yet).</item> + <item>Supported SSL/TLS-versions are SSL-3.0, TLS-1.0, + TLS-1.1 and TLS-1.2 (no support for elliptic curve cipher suites yet).</item> <item>For security reasons sslv2 is not supported.</item> <item>Ephemeral Diffie-Hellman cipher suites are supported but not Diffie Hellman Certificates cipher suites.</item> diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml index 2ba6f48611..178bbcaebb 100644 --- a/lib/ssl/doc/src/ssl_app.xml +++ b/lib/ssl/doc/src/ssl_app.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE appref SYSTEM "appref.dtd"> <appref> @@ -29,7 +29,17 @@ sockets.</appsummary> <section> - <title>Environment</title> + <title>DEPENDENCIES</title> + <p>The ssl application uses the Erlang applications public_key and + crypto to handle public keys and encryption, hence these + applications needs to be loaded for the ssl application to work. In + an embedded environment that means they need to be started with + application:start/[1,2] before the ssl application is started. + </p> + </section> + + <section> + <title>ENVIRONMENT</title> <p>The following application environment configuration parameters are defined for the SSL application. Refer to application(3) for more information about configuration parameters. diff --git a/lib/ssl/doc/src/ssl_protocol.xml b/lib/ssl/doc/src/ssl_protocol.xml index 17268a634d..f540dc999b 100644 --- a/lib/ssl/doc/src/ssl_protocol.xml +++ b/lib/ssl/doc/src/ssl_protocol.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2011</year> + <year>2003</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -25,9 +25,8 @@ <file>ssl_protocol.xml</file> </header> - <p>The erlang SSL application currently supports SSL 3.0 and TLS 1.0 - RFC 2246, and will in the future also support later versions of TLS. - SSL 2.0 is not supported. + <p>The erlang SSL application currently implements the protocol SSL/TLS + for currently supported versions see <seealso marker="ssl">ssl(3)</seealso> </p> <p>By default erlang SSL is run over the TCP/IP protocol even diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 6224334a6e..09f2819ca8 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -28,14 +28,11 @@ cipher_suites/0, cipher_suites/1, suite_definition/1, close/1, shutdown/2, connect/3, connect/2, connect/4, connection_info/1, - controlling_process/2, listen/2, pid/1, peername/1, peercert/1, + controlling_process/2, listen/2, peername/1, peercert/1, recv/2, recv/3, send/2, getopts/2, setopts/2, sockname/1, versions/0, session_info/1, format_error/1, renegotiate/1, prf/5, clear_pem_cache/0, random_bytes/1, negotiated_next_protocol/1]). - --deprecated({pid, 1, next_major_release}). - -include("ssl_internal.hrl"). -include("ssl_record.hrl"). -include("ssl_cipher.hrl"). @@ -956,12 +953,3 @@ make_next_protocol_selector({server, AllProtocols, DefaultProtocol}) -> PreferredProtocol -> PreferredProtocol end end. - -%% Only used to remove exit messages from old ssl -%% First is a nonsense clause to provide some -%% backward compatibility for orber that uses this -%% function in a none recommended way, but will -%% work correctly if a valid pid is returned. -%% Deprcated to be removed in r16 -pid(#sslsocket{})-> - whereis(ssl_connection_sup). diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index a5db2dcee7..ed0dc34adf 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -69,8 +69,8 @@ -define(TRUE, 0). -define(FALSE, 1). --define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). %% Add 'tlsv1.1' in R16 -define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]). +-define(MIN_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]). -record(ssl_options, { versions, % 'tlsv1.2' | 'tlsv1.1' | tlsv1 | sslv3 diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 8e93ce4634..173b9611c6 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -463,10 +463,9 @@ supported_protocol_versions() -> supported_protocol_versions([]) -> Vsns = case sufficient_tlsv1_2_crypto_support() of true -> - %%?ALL_SUPPORTED_VERSIONS; %% Add TlS-1.2 as default in R16 - ?DEFAULT_SUPPORTED_VERSIONS; + ?ALL_SUPPORTED_VERSIONS; false -> - ?DEFAULT_SUPPORTED_VERSIONS + ?MIN_SUPPORTED_VERSIONS end, application:set_env(ssl, protocol_version, Vsns), Vsns; diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile index d36dcb588b..847907cde8 100644 --- a/lib/ssl/test/Makefile +++ b/lib/ssl/test/Makefile @@ -37,15 +37,16 @@ VSN=$(GS_VSN) MODULES = \ ssl_test_lib \ ssl_basic_SUITE \ - ssl_handshake_SUITE \ - ssl_packet_SUITE \ ssl_cipher_SUITE \ - ssl_payload_SUITE \ - ssl_to_openssl_SUITE \ - ssl_session_cache_SUITE \ + ssl_certificate_verify_SUITE\ ssl_dist_SUITE \ + ssl_handshake_SUITE \ ssl_npn_hello_SUITE \ ssl_npn_handshake_SUITE \ + ssl_packet_SUITE \ + ssl_payload_SUITE \ + ssl_session_cache_SUITE \ + ssl_to_openssl_SUITE \ make_certs\ erl_make_certs diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index faed91e559..5ba71f9218 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -41,127 +41,10 @@ -define(RENEGOTIATION_DISABLE_TIME, 12000). -define(CLEAN_SESSION_DB, 60000). -%% Test server callback functions %%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config0) -> - Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), - catch crypto:stop(), - try crypto:start() of - ok -> - application:start(public_key), - - %% make rsa certs using oppenssl - Result = - (catch make_certs:all(?config(data_dir, Config0), - ?config(priv_dir, Config0))), - test_server:format("Make certs ~p~n", [Result]), - - Config1 = ssl_test_lib:make_dsa_cert(Config0), - Config = ssl_test_lib:cert_options(Config1), - [{watchdog, Dog} | Config] - catch _:_ -> - {skip, "Crypto did not start"} - end. -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(_Config) -> - ssl:stop(), - application:stop(crypto). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- -init_per_testcase(no_authority_key_identifier, Config) -> - %% Clear cach so that root cert will not - %% be found. - ssl:clear_pem_cache(), - Config; - -init_per_testcase(protocol_versions, Config) -> - ssl:stop(), - application:load(ssl), - %% For backwards compatibility sslv2 should be filtered out. - application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]), - ssl:start(), - Config; - -init_per_testcase(reuse_session_expired, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - ssl:stop(), - application:load(ssl), - application:set_env(ssl, session_lifetime, ?EXPIRE), - application:set_env(ssl, session_delay_cleanup_time, 500), - ssl:start(), - Config; - -init_per_testcase(empty_protocol_versions, Config) -> - ssl:stop(), - application:load(ssl), - application:set_env(ssl, protocol_version, []), - ssl:start(), - Config; - -%% init_per_testcase(different_ca_peer_sign, Config0) -> -%% ssl_test_lib:make_mix_cert(Config0); - -init_per_testcase(_TestCase, Config0) -> - test_server:format("TLS/SSL version ~p~n ", [ssl_record:supported_protocol_versions()]), - Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- -end_per_testcase(reuse_session_expired, Config) -> - application:unset_env(ssl, session_lifetime), - application:unset_env(ssl, session_delay_cleanup_time), - end_per_testcase(default_action, Config); -end_per_testcase(_TestCase, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end. - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> @@ -183,7 +66,6 @@ groups() -> {'tlsv1', [], all_versions_groups() ++ rizzo_tests()}, {'sslv3', [], all_versions_groups() ++ rizzo_tests()}, {api,[], api_tests()}, - {certificate_verify, [], certificate_verify_tests()}, {session, [], session_tests()}, {renegotiate, [], renegotiate_tests()}, {ciphers, [], cipher_tests()}, @@ -192,29 +74,10 @@ groups() -> all_versions_groups ()-> [{group, api}, - {group, certificate_verify}, {group, renegotiate}, {group, ciphers}, {group, error_handling_tests}]. -init_per_group(GroupName, Config) -> - case ssl_test_lib:is_tls_version(GroupName) of - true -> - case ssl_test_lib:sufficient_crypto_support(GroupName) of - true -> - ssl_test_lib:init_tls_version(GroupName), - Config; - false -> - {skip, "Missing crypto support"} - end; - _ -> - ssl:start(), - Config - end. - - -end_per_group(_GroupName, Config) -> - Config. basic_tests() -> [app, @@ -242,7 +105,8 @@ options_tests() -> protocol_versions, empty_protocol_versions, ipv6, - reuseaddr]. + reuseaddr, + tcp_reuseaddr]. api_tests() -> [connection_info, @@ -264,38 +128,6 @@ api_tests() -> ssl_recv_timeout ]. -certificate_verify_tests() -> - [server_verify_peer_passive, - server_verify_peer_active, - server_verify_peer_active_once, - server_verify_none_passive, - server_verify_none_active, - server_verify_none_active_once, - server_verify_no_cacerts, - server_require_peer_cert_ok, - server_require_peer_cert_fail, - server_verify_client_once_passive, - server_verify_client_once_active, - server_verify_client_once_active_once, - new_server_wants_peer_cert, - client_verify_none_passive, - client_verify_none_active, - client_verify_none_active_once, - extended_key_usage_verify_peer, - extended_key_usage_verify_none, - invalid_signature_client, - invalid_signature_server, - cert_expired, - client_with_cert_cipher_suites_handshake, - verify_fun_always_run_client, - verify_fun_always_run_server, - unknown_server_ca_fail, - unknown_server_ca_accept_verify_none, - unknown_server_ca_accept_verify_peer, - unknown_server_ca_accept_backwardscompatibility, - no_authority_key_identifier - ]. - session_tests() -> [reuse_session, reuse_session_expired, @@ -335,19 +167,109 @@ rizzo_tests() -> [rizzo, no_rizzo_rc4]. -%% Test cases starts here. %%-------------------------------------------------------------------- -app(doc) -> - "Test that the ssl app file is ok"; -app(suite) -> - []; +init_per_suite(Config0) -> + Dog = ct:timetrap(?LONG_TIMEOUT *2), + catch crypto:stop(), + try crypto:start() of + ok -> + application:start(public_key), + + %% make rsa certs using oppenssl + Result = + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), + ct:print("Make certs ~p~n", [Result]), + + Config1 = ssl_test_lib:make_dsa_cert(Config0), + Config = ssl_test_lib:cert_options(Config1), + [{watchdog, Dog} | Config] + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). + +%%-------------------------------------------------------------------- +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName), + Config; + false -> + {skip, "Missing crypto support"} + end; + _ -> + ssl:start(), + Config + end. + + +end_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +init_per_testcase(no_authority_key_identifier, Config) -> + %% Clear cach so that root cert will not + %% be found. + ssl:clear_pem_cache(), + Config; + +init_per_testcase(protocol_versions, Config) -> + ssl:stop(), + application:load(ssl), + %% For backwards compatibility sslv2 should be filtered out. + application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]), + ssl:start(), + Config; + +init_per_testcase(reuse_session_expired, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + ssl:stop(), + application:load(ssl), + application:set_env(ssl, session_lifetime, ?EXPIRE), + application:set_env(ssl, session_delay_cleanup_time, 500), + ssl:start(), + Config; + +init_per_testcase(empty_protocol_versions, Config) -> + ssl:stop(), + application:load(ssl), + application:set_env(ssl, protocol_version, []), + ssl:start(), + Config; + +%% init_per_testcase(different_ca_peer_sign, Config0) -> +%% ssl_test_lib:make_mix_cert(Config0); + +init_per_testcase(_TestCase, Config0) -> + ct:print("TLS/SSL version ~p~n ", [ssl_record:supported_protocol_versions()]), + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ct:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. + +end_per_testcase(reuse_session_expired, Config) -> + application:unset_env(ssl, session_lifetime), + application:unset_env(ssl, session_delay_cleanup_time), + end_per_testcase(default_action, Config); + +end_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +app() -> + [{doc, "Test that the ssl app file is ok"}]. app(Config) when is_list(Config) -> - ok = test_server:app_test(ssl). + ok = ?t:app_test(ssl). %%-------------------------------------------------------------------- -alerts(doc) -> - "Test ssl_alert:alert_txt/1"; -alerts(suite) -> - []; +alerts() -> + [{doc, "Test ssl_alert:alert_txt/1"}]. alerts(Config) when is_list(Config) -> Descriptions = [?CLOSE_NOTIFY, ?UNEXPECTED_MESSAGE, ?BAD_RECORD_MAC, ?DECRYPTION_FAILED, ?RECORD_OVERFLOW, ?DECOMPRESSION_FAILURE, @@ -364,14 +286,12 @@ alerts(Config) when is_list(Config) -> Txt when is_list(Txt) -> ok; Other -> - test_server:fail({unexpected, Other}) + ct:fail({unexpected, Other}) end end, Alerts). %%-------------------------------------------------------------------- -connection_info(doc) -> - ["Test the API function ssl:connection_info/1"]; -connection_info(suite) -> - []; +connection_info() -> + [{doc,"Test the API function ssl:connection_info/1"}]. connection_info(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -390,7 +310,7 @@ connection_info(Config) when is_list(Config) -> [{ciphers,[{rsa,rc4_128,sha,no_export}]} | ClientOpts]}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), Version = @@ -403,58 +323,23 @@ connection_info(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). -connection_info_result(Socket) -> - ssl:connection_info(Socket). - %%-------------------------------------------------------------------- - -protocol_versions(doc) -> - ["Test to set a list of protocol versions in app environment."]; - -protocol_versions(suite) -> - []; +protocol_versions() -> + [{doc,"Test to set a list of protocol versions in app environment."}]. protocol_versions(Config) when is_list(Config) -> basic_test(Config). - -empty_protocol_versions(doc) -> - ["Test to set an empty list of protocol versions in app environment."]; - -empty_protocol_versions(suite) -> - []; +%%-------------------------------------------------------------------- +empty_protocol_versions() -> + [{doc,"Test to set an empty list of protocol versions in app environment."}]. empty_protocol_versions(Config) when is_list(Config) -> basic_test(Config). - -basic_test(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, ClientOpts}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -controlling_process(doc) -> - ["Test API function controlling_process/2"]; - -controlling_process(suite) -> - []; +controlling_process() -> + [{doc,"Test API function controlling_process/2"}]. controlling_process(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -478,7 +363,7 @@ controlling_process(Config) when is_list(Config) -> ClientMsg]}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), receive @@ -497,46 +382,15 @@ controlling_process(Config) when is_list(Config) -> ok end; Unexpected -> - test_server:fail(Unexpected) + ct:fail(Unexpected) end, ssl_test_lib:close(Server), ssl_test_lib:close(Client). -controlling_process_result(Socket, Pid, Msg) -> - ok = ssl:controlling_process(Socket, Pid), - %% Make sure other side has evaluated controlling_process - %% before message is sent - test_server:sleep(?SLEEP), - ssl:send(Socket, Msg), - no_result_msg. - -receive_s_rizzo_duong_beast() -> - receive - {ssl, _, "erver hello"} -> - receive - {ssl, _, "C"} -> - receive - {ssl, _, "lient hello"} -> - ok - end - end - end. -receive_c_rizzo_duong_beast() -> - receive - {ssl, _, "lient hello"} -> - receive - {ssl, _, "S"} -> - receive - {ssl, _, "erver hello"} -> - ok - end - end - end. %%-------------------------------------------------------------------- -controller_dies(doc) -> - ["Test that the socket is closed after controlling process dies"]; -controller_dies(suite) -> []; +controller_dies() -> + [{doc,"Test that the socket is closed after controlling process dies"}]. controller_dies(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -559,8 +413,8 @@ controller_dies(Config) when is_list(Config) -> ClientMsg]}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), - test_server:sleep(?SLEEP), %% so that they are connected + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), + ct:sleep(?SLEEP), %% so that they are connected process_flag(trap_exit, true), @@ -577,7 +431,7 @@ controller_dies(Config) when is_list(Config) -> %% Make sure server finishes and verification %% and is in coonection state before %% killing client - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), Pid ! {self(), connected, Socket}, receive die_nice -> normal end end, @@ -597,13 +451,13 @@ controller_dies(Config) when is_list(Config) -> Client3 ! die_nice end, - test_server:format("Wating on exit ~p~n",[Client3]), + ct:print("Wating on exit ~p~n",[Client3]), receive {'EXIT', Client3, normal} -> ok end, receive %% Client3 is dead but that doesn't matter, socket should not be closed. Unexpected -> - test_server:format("Unexpected ~p~n",[Unexpected]), - test_server:fail({line, ?LINE-1}) + ct:print("Unexpected ~p~n",[Unexpected]), + ct:fail({line, ?LINE-1}) after 1000 -> ok end, @@ -619,39 +473,17 @@ controller_dies(Config) when is_list(Config) -> controller_dies_result, [self(), ClientMsg]}}, {options, [{reuseaddr,true}|ClientOpts]}]), - test_server:sleep(?SLEEP), %% so that they are connected + ct:sleep(?SLEEP), %% so that they are connected exit(Server, killed), get_close(Server, ?LINE), process_flag(trap_exit, false), ssl_test_lib:close(LastClient). -controller_dies_result(_Socket, _Pid, _Msg) -> - receive Result -> Result end. - -get_close(Pid, Where) -> - receive - {'EXIT', Pid, _Reason} -> - receive - {_, {ssl_closed, Socket}} -> - test_server:format("Socket closed ~p~n",[Socket]); - Unexpected -> - test_server:format("Unexpected ~p~n",[Unexpected]), - test_server:fail({line, ?LINE-1}) - after 5000 -> - test_server:fail({timeout, {line, ?LINE, Where}}) - end; - Unexpected -> - test_server:format("Unexpected ~p~n",[Unexpected]), - test_server:fail({line, ?LINE-1}) - after 5000 -> - test_server:fail({timeout, {line, ?LINE, Where}}) - end. - %%-------------------------------------------------------------------- -client_closes_socket(doc) -> - ["Test what happens when client closes socket before handshake is compleated"]; -client_closes_socket(suite) -> []; +client_closes_socket() -> + [{doc,"Test what happens when client closes socket before handshake is compleated"}]. + client_closes_socket(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -668,7 +500,7 @@ client_closes_socket(Config) when is_list(Config) -> [Hostname, Port, TcpOpts]), %% Make sure that ssl_accept is called before %% client process ends and closes socket. - test_server:sleep(?SLEEP) + ct:sleep(?SLEEP) end, _Client = spawn_link(Connect), @@ -676,11 +508,8 @@ client_closes_socket(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, {error,closed}). %%-------------------------------------------------------------------- -connect_dist(doc) -> - ["Test a simple connect as is used by distribution"]; - -connect_dist(suite) -> - []; +connect_dist() -> + [{doc,"Test a simple connect as is used by distribution"}]. connect_dist(Config) when is_list(Config) -> ClientOpts0 = ?config(client_kc_opts, Config), @@ -706,22 +535,9 @@ connect_dist(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). -connect_dist_s(S) -> - Msg = term_to_binary({erlang,term}), - ok = ssl:send(S, Msg). - -connect_dist_c(S) -> - Test = binary_to_list(term_to_binary({erlang,term})), - {ok, Test} = ssl:recv(S, 0, 10000), - ok. - - %%-------------------------------------------------------------------- -peername(doc) -> - ["Test API function peername/1"]; - -peername(suite) -> - []; +peername() -> + [{doc,"Test API function peername/1"}]. peername(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -744,7 +560,7 @@ peername(Config) when is_list(Config) -> ServerMsg = {ok, {ClientIp, ClientPort}}, ClientMsg = {ok, {ServerIp, Port}}, - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), @@ -752,14 +568,9 @@ peername(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). -peername_result(S) -> - ssl:peername(S). - %%-------------------------------------------------------------------- -peercert(doc) -> - [""]; -peercert(suite) -> - []; +peercert() -> + [{doc,"Test API function peercert/1"}]. peercert(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -782,7 +593,7 @@ peercert(Config) when is_list(Config) -> ServerMsg = {error, no_peercert}, ClientMsg = {ok, BinCert}, - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), @@ -794,10 +605,8 @@ peercert_result(Socket) -> ssl:peercert(Socket). %%-------------------------------------------------------------------- -peercert_with_client_cert(doc) -> - [""]; -peercert_with_client_cert(suite) -> - []; +peercert_with_client_cert() -> + [{doc,"Test API function peercert/1"}]. peercert_with_client_cert(Config) when is_list(Config) -> ClientOpts = ?config(client_dsa_opts, Config), ServerOpts = ?config(server_dsa_verify_opts, Config), @@ -822,7 +631,7 @@ peercert_with_client_cert(Config) when is_list(Config) -> ServerMsg = {ok, ClientBinCert}, ClientMsg = {ok, ServerBinCert}, - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), @@ -831,12 +640,8 @@ peercert_with_client_cert(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -sockname(doc) -> - ["Test API function sockname/1"]; - -sockname(suite) -> - []; - +sockname() -> + [{doc,"Test API function sockname/1"}]. sockname(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -858,7 +663,7 @@ sockname(Config) when is_list(Config) -> ServerMsg = {ok, {ServerIp, Port}}, ClientMsg = {ok, {ClientIp, ClientPort}}, - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), @@ -870,11 +675,8 @@ sockname_result(S) -> ssl:sockname(S). %%-------------------------------------------------------------------- -cipher_suites(doc) -> - ["Test API function cipher_suites/0"]; - -cipher_suites(suite) -> - []; +cipher_suites() -> + [{doc,"Test API function cipher_suites/0"}]. cipher_suites(Config) when is_list(Config) -> MandatoryCipherSuite = {rsa,'3des_ede_cbc',sha}, @@ -884,11 +686,8 @@ cipher_suites(Config) when is_list(Config) -> [_|_] =ssl:cipher_suites(openssl). %%-------------------------------------------------------------------- -socket_options(doc) -> - ["Test API function getopts/2 and setopts/2"]; - -socket_options(suite) -> - []; +socket_options() -> + [{doc,"Test API function getopts/2 and setopts/2"}]. socket_options(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -937,16 +736,13 @@ socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) -> ssl:setopts(Socket, [{nodelay, true}]), {ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]), {ok, All} = ssl:getopts(Socket, []), - test_server:format("All opts ~p~n", [All]), + ct:print("All opts ~p~n", [All]), ok. %%-------------------------------------------------------------------- -invalid_inet_get_option(doc) -> - ["Test handling of invalid inet options in getopts"]; - -invalid_inet_get_option(suite) -> - []; +invalid_inet_get_option() -> + [{doc,"Test handling of invalid inet options in getopts"}]. invalid_inet_get_option(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -963,24 +759,16 @@ invalid_inet_get_option(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). - -get_invalid_inet_option(Socket) -> - {error, {eoptions, {inet_option, foo, _}}} = ssl:getopts(Socket, [foo]), - ok. - %%-------------------------------------------------------------------- -invalid_inet_get_option_not_list(doc) -> - ["Test handling of invalid type in getopts"]; - -invalid_inet_get_option_not_list(suite) -> - []; +invalid_inet_get_option_not_list() -> + [{doc,"Test handling of invalid type in getopts"}]. invalid_inet_get_option_not_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -997,7 +785,7 @@ invalid_inet_get_option_not_list(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -1011,11 +799,8 @@ get_invalid_inet_option_not_list(Socket) -> ok. %%-------------------------------------------------------------------- -invalid_inet_get_option_improper_list(doc) -> - ["Test handling of invalid type in getopts"]; - -invalid_inet_get_option_improper_list(suite) -> - []; +invalid_inet_get_option_improper_list() -> + [{doc,"Test handling of invalid type in getopts"}]. invalid_inet_get_option_improper_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1032,7 +817,7 @@ invalid_inet_get_option_improper_list(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -1045,11 +830,8 @@ get_invalid_inet_option_improper_list(Socket) -> ok. %%-------------------------------------------------------------------- -invalid_inet_set_option(doc) -> - ["Test handling of invalid inet options in setopts"]; - -invalid_inet_set_option(suite) -> - []; +invalid_inet_set_option() -> + [{doc,"Test handling of invalid inet options in setopts"}]. invalid_inet_set_option(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1066,7 +848,7 @@ invalid_inet_set_option(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -1080,11 +862,8 @@ set_invalid_inet_option(Socket) -> {error, {eoptions, {inet_opt, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]), ok. %%-------------------------------------------------------------------- -invalid_inet_set_option_not_list(doc) -> - ["Test handling of invalid type in setopts"]; - -invalid_inet_set_option_not_list(suite) -> - []; +invalid_inet_set_option_not_list() -> + [{doc,"Test handling of invalid type in setopts"}]. invalid_inet_set_option_not_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1101,7 +880,7 @@ invalid_inet_set_option_not_list(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -1115,11 +894,8 @@ set_invalid_inet_option_not_list(Socket) -> ok. %%-------------------------------------------------------------------- -invalid_inet_set_option_improper_list(doc) -> - ["Test handling of invalid tye in setopts"]; - -invalid_inet_set_option_improper_list(suite) -> - []; +invalid_inet_set_option_improper_list() -> + [{doc,"Test handling of invalid tye in setopts"}]. invalid_inet_set_option_improper_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1136,7 +912,7 @@ invalid_inet_set_option_improper_list(Config) when is_list(Config) -> {mfa, {ssl_test_lib, no_result, []}}, {options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok), @@ -1149,11 +925,8 @@ set_invalid_inet_option_improper_list(Socket) -> ok. %%-------------------------------------------------------------------- -misc_ssl_options(doc) -> - ["Test what happens when we give valid options"]; - -misc_ssl_options(suite) -> - []; +misc_ssl_options() -> + [{doc,"Test what happens when we give valid options"}]. misc_ssl_options(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1171,17 +944,17 @@ misc_ssl_options(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, TestOpts ++ ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, TestOpts ++ ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1189,23 +962,16 @@ misc_ssl_options(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -versions(doc) -> - ["Test API function versions/0"]; - -versions(suite) -> - []; +versions() -> + [{doc,"Test API function versions/0"}]. versions(Config) when is_list(Config) -> [_|_] = Versions = ssl:versions(), - test_server:format("~p~n", [Versions]). + ct:print("~p~n", [Versions]). %%-------------------------------------------------------------------- -send_recv(doc) -> - [""]; - -send_recv(suite) -> - []; - +send_recv() -> + [{doc,""}]. send_recv(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1213,17 +979,17 @@ send_recv(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ClientOpts]}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1232,12 +998,8 @@ send_recv(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -send_close(doc) -> - [""]; - -send_close(suite) -> - []; - +send_close() -> + [{doc,""}]. send_close(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1245,7 +1007,7 @@ send_close(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), {ok, TcpS} = rpc:call(ClientNode, gen_tcp, connect, @@ -1253,7 +1015,7 @@ send_close(Config) when is_list(Config) -> {ok, SslS} = rpc:call(ClientNode, ssl, connect, [TcpS,[{active, false}|ClientOpts]]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), self(), Server]), ok = ssl:send(SslS, "Hello world"), {ok,<<"Hello world">>} = ssl:recv(SslS, 11), @@ -1261,11 +1023,8 @@ send_close(Config) when is_list(Config) -> {error, _} = ssl:send(SslS, "Hello world"). %%-------------------------------------------------------------------- -close_transport_accept(doc) -> - ["Tests closing ssl socket when waiting on ssl:transport_accept/1"]; - -close_transport_accept(suite) -> - []; +close_transport_accept() -> + [{doc,"Tests closing ssl socket when waiting on ssl:transport_accept/1"}]. close_transport_accept(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), @@ -1275,7 +1034,7 @@ close_transport_accept(Config) when is_list(Config) -> Opts = [{active, false} | ServerOpts], {ok, ListenSocket} = rpc:call(ServerNode, ssl, listen, [Port, Opts]), spawn_link(fun() -> - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), rpc:call(ServerNode, ssl, close, [ListenSocket]) end), case rpc:call(ServerNode, ssl, transport_accept, [ListenSocket]) of @@ -1286,11 +1045,8 @@ close_transport_accept(Config) when is_list(Config) -> end. %%-------------------------------------------------------------------- -dh_params(doc) -> - ["Test to specify DH-params file in server."]; - -dh_params(suite) -> - []; +dh_params() -> + [{doc,"Test to specify DH-params file in server."}]. dh_params(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1302,13 +1058,13 @@ dh_params(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, [{dhfile, DHParamFile} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, [{ciphers,[{dhe_rsa,aes_256_cbc,sha,ignore}]} | ClientOpts]}]), @@ -1319,11 +1075,8 @@ dh_params(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -upgrade(doc) -> - ["Test that you can upgrade an tcp connection to an ssl connection"]; - -upgrade(suite) -> - []; +upgrade() -> + [{doc,"Test that you can upgrade an tcp connection to an ssl connection"}]. upgrade(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1347,7 +1100,7 @@ upgrade(Config) when is_list(Config) -> {tcp_options, TcpOpts}, {ssl_options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1371,11 +1124,8 @@ upgrade_result(Socket) -> end. %%-------------------------------------------------------------------- -upgrade_with_timeout(doc) -> - ["Test ssl_accept/3"]; - -upgrade_with_timeout(suite) -> - []; +upgrade_with_timeout() -> + [{doc,"Test ssl_accept/3"}]. upgrade_with_timeout(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1400,7 +1150,7 @@ upgrade_with_timeout(Config) when is_list(Config) -> {tcp_options, TcpOpts}, {ssl_options, ClientOpts}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1409,11 +1159,8 @@ upgrade_with_timeout(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -tcp_connect(doc) -> - ["Test what happens when a tcp tries to connect, i,e. a bad (ssl) packet is sent first"]; - -tcp_connect(suite) -> - []; +tcp_connect() -> + [{doc,"Test what happens when a tcp tries to connect, i,e. a bad (ssl) packet is sent first"}]. tcp_connect(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), @@ -1429,22 +1176,19 @@ tcp_connect(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(Server), {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), - test_server:format("Testcase ~p connected to Server ~p ~n", [self(), Server]), + ct:print("Testcase ~p connected to Server ~p ~n", [self(), Server]), gen_tcp:send(Socket, "<SOME GARBLED NON SSL MESSAGE>"), receive {tcp_closed, Socket} -> receive {Server, {error, Error}} -> - test_server:format("Error ~p", [Error]) + ct:print("Error ~p", [Error]) end end. - -tcp_connect_big(doc) -> - ["Test what happens when a tcp tries to connect, i,e. a bad big (ssl) packet is sent first"]; - -tcp_connect_big(suite) -> - []; +%%-------------------------------------------------------------------- +tcp_connect_big() -> + [{doc,"Test what happens when a tcp tries to connect, i,e. a bad big (ssl) packet is sent first"}]. tcp_connect_big(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), @@ -1460,7 +1204,7 @@ tcp_connect_big(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(Server), {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), - test_server:format("Testcase ~p connected to Server ~p ~n", [self(), Server]), + ct:print("Testcase ~p connected to Server ~p ~n", [self(), Server]), Rand = crypto:rand_bytes(?MAX_CIPHER_TEXT_LENGTH+1), gen_tcp:send(Socket, <<?BYTE(0), @@ -1470,24 +1214,16 @@ tcp_connect_big(Config) when is_list(Config) -> {tcp_closed, Socket} -> receive {Server, {error, timeout}} -> - test_server:fail("hangs"); + ct:fail("hangs"); {Server, {error, Error}} -> - test_server:format("Error ~p", [Error]) + ct:print("Error ~p", [Error]) end end. -dummy(_Socket) -> - %% Should not happen as the ssl connection will not be established - %% due to fatal handshake failiure - exit(kill). - %%-------------------------------------------------------------------- ipv6() -> - [{require, ipv6_hosts}]. -ipv6(doc) -> - ["Test ipv6."]; -ipv6(suite) -> - []; + [{require, ipv6_hosts}, + {doc,"Test ipv6."}]. ipv6(Config) when is_list(Config) -> {ok, Hostname0} = inet:gethostname(), @@ -1499,18 +1235,18 @@ ipv6(Config) when is_list(Config) -> ssl_test_lib:run_where(Config, ipv6), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [inet6, {active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [inet6, {active, false} | ClientOpts]}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -1523,12 +1259,8 @@ ipv6(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -ekeyfile(doc) -> - ["Test what happens with an invalid key file"]; - -ekeyfile(suite) -> - []; - +ekeyfile() -> + [{doc,"Test what happens with an invalid key file"}]. ekeyfile(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), BadOpts = ?config(server_bad_key, Config), @@ -1551,11 +1283,8 @@ ekeyfile(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -ecertfile(doc) -> - ["Test what happens with an invalid cert file"]; - -ecertfile(suite) -> - []; +ecertfile() -> + [{doc,"Test what happens with an invalid cert file"}]. ecertfile(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1580,11 +1309,8 @@ ecertfile(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -ecacertfile(doc) -> - ["Test what happens with an invalid cacert file"]; - -ecacertfile(suite) -> - []; +ecacertfile() -> + [{doc,"Test what happens with an invalid cacert file"}]. ecacertfile(Config) when is_list(Config) -> ClientOpts = [{reuseaddr, true}|?config(client_opts, Config)], @@ -1632,12 +1358,9 @@ ecacertfile(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -eoptions(doc) -> - ["Test what happens when we give invalid options"]; +eoptions() -> + [{doc,"Test what happens when we give invalid options"}]. -eoptions(suite) -> - []; - eoptions(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1694,12 +1417,8 @@ eoptions(Config) when is_list(Config) -> ok. %%-------------------------------------------------------------------- -shutdown(doc) -> - [""]; - -shutdown(suite) -> - []; - +shutdown() -> + [{doc,"Test API function ssl:shutdown/2"}]. shutdown(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1724,25 +1443,9 @@ shutdown(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). -shutdown_result(Socket, server) -> - ssl:send(Socket, "Hej"), - ssl:shutdown(Socket, write), - {ok, "Hej hopp"} = ssl:recv(Socket, 8), - ok; - -shutdown_result(Socket, client) -> - {ok, "Hej"} = ssl:recv(Socket, 3), - ssl:send(Socket, "Hej hopp"), - ssl:shutdown(Socket, write), - ok. - %%-------------------------------------------------------------------- -shutdown_write(doc) -> - [""]; - -shutdown_write(suite) -> - []; - +shutdown_write() -> + [{doc,"Test API function ssl:shutdown/2 with option write."}]. shutdown_write(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1759,20 +1462,10 @@ shutdown_write(Config) when is_list(Config) -> {options, [{active, false} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, {error, closed}). - -shutdown_write_result(Socket, server) -> - test_server:sleep(?SLEEP), - ssl:shutdown(Socket, write); -shutdown_write_result(Socket, client) -> - ssl:recv(Socket, 0). %%-------------------------------------------------------------------- -shutdown_both(doc) -> - [""]; - -shutdown_both(suite) -> - []; - +shutdown_both() -> + [{doc,"Test API function ssl:shutdown/2 with option both."}]. shutdown_both(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1790,19 +1483,9 @@ shutdown_both(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, {error, closed}). -shutdown_both_result(Socket, server) -> - test_server:sleep(?SLEEP), - ssl:shutdown(Socket, read_write); -shutdown_both_result(Socket, client) -> - ssl:recv(Socket, 0). - %%-------------------------------------------------------------------- -shutdown_error(doc) -> - [""]; - -shutdown_error(suite) -> - []; - +shutdown_error() -> + [{doc,"Test ssl:shutdown/2 error handling"}]. shutdown_error(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), Port = ssl_test_lib:inet_port(node()), @@ -1812,141 +1495,60 @@ shutdown_error(Config) when is_list(Config) -> {error, closed} = ssl:shutdown(Listen, read_write). %%------------------------------------------------------------------- -ciphers_rsa_signed_certs(doc) -> - ["Test all rsa ssl cipher suites in highest support ssl/tls version"]; +ciphers_rsa_signed_certs() -> + [{doc,"Test all rsa ssl cipher suites in highest support ssl/tls version"}]. -ciphers_rsa_signed_certs(suite) -> - []; - ciphers_rsa_signed_certs(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:rsa_suites(), - test_server:format("~p erlang cipher suites ~p~n", [Version, Ciphers]), + ct:print("~p erlang cipher suites ~p~n", [Version, Ciphers]), run_suites(Ciphers, Version, Config, rsa). - -ciphers_rsa_signed_certs_openssl_names(doc) -> - ["Test all rsa ssl cipher suites in highest support ssl/tls version"]; +%%------------------------------------------------------------------- +ciphers_rsa_signed_certs_openssl_names() -> + [{doc,"Test all rsa ssl cipher suites in highest support ssl/tls version"}]. -ciphers_rsa_signed_certs_openssl_names(suite) -> - []; - ciphers_rsa_signed_certs_openssl_names(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:openssl_rsa_suites(), - test_server:format("tls1 openssl cipher suites ~p~n", [Ciphers]), + ct:print("tls1 openssl cipher suites ~p~n", [Ciphers]), run_suites(Ciphers, Version, Config, rsa). - -ciphers_dsa_signed_certs(doc) -> - ["Test all dsa ssl cipher suites in highest support ssl/tls version"]; +%%------------------------------------------------------------------- +ciphers_dsa_signed_certs() -> + [{doc,"Test all dsa ssl cipher suites in highest support ssl/tls version"}]. -ciphers_dsa_signed_certs(suite) -> - []; - ciphers_dsa_signed_certs(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:dsa_suites(), - test_server:format("~p erlang cipher suites ~p~n", [Version, Ciphers]), + ct:print("~p erlang cipher suites ~p~n", [Version, Ciphers]), run_suites(Ciphers, Version, Config, dsa). - -ciphers_dsa_signed_certs_openssl_names(doc) -> - ["Test all dsa ssl cipher suites in highest support ssl/tls version"]; +%%------------------------------------------------------------------- +ciphers_dsa_signed_certs_openssl_names() -> + [{doc,"Test all dsa ssl cipher suites in highest support ssl/tls version"}]. -ciphers_dsa_signed_certs_openssl_names(suite) -> - []; - ciphers_dsa_signed_certs_openssl_names(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:openssl_dsa_suites(), - test_server:format("tls1 openssl cipher suites ~p~n", [Ciphers]), + ct:print("tls1 openssl cipher suites ~p~n", [Ciphers]), run_suites(Ciphers, Version, Config, dsa). - -anonymous_cipher_suites(doc)-> - ["Test the anonymous ciphersuites"]; -anonymous_cipher_suites(suite) -> - []; +%%------------------------------------------------------------------- +anonymous_cipher_suites()-> + [{doc,"Test the anonymous ciphersuites"}]. anonymous_cipher_suites(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:anonymous_suites(), run_suites(Ciphers, Version, Config, anonymous). -run_suites(Ciphers, Version, Config, Type) -> - {ClientOpts, ServerOpts} = - case Type of - rsa -> - {?config(client_opts, Config), - ?config(server_opts, Config)}; - dsa -> - {?config(client_opts, Config), - ?config(server_dsa_opts, Config)}; - anonymous -> - %% No certs in opts! - {?config(client_opts, Config), - ?config(server_anon, Config)} - end, - - Result = lists:map(fun(Cipher) -> - cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end, - Ciphers), - case lists:flatten(Result) of - [] -> - ok; - Error -> - test_server:format("Cipher suite errors: ~p~n", [Error]), - test_server:fail(cipher_suite_failed_see_test_case_log) - end. - -erlang_cipher_suite(Suite) when is_list(Suite)-> - ssl:suite_definition(ssl_cipher:openssl_suite(Suite)); -erlang_cipher_suite(Suite) -> - Suite. - -cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> - %% process_flag(trap_exit, true), - test_server:format("Testing CipherSuite ~p~n", [CipherSuite]), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - ErlangCipherSuite = erlang_cipher_suite(CipherSuite), - - ConnectionInfo = {ok, {Version, ErlangCipherSuite}}, - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, - {options, - [{ciphers,[CipherSuite]} | - ClientOpts]}]), - - Result = ssl_test_lib:wait_for_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - - case Result of - ok -> - []; - Error -> - [{ErlangCipherSuite, Error}] - end. - %%-------------------------------------------------------------------- -default_reject_anonymous(doc)-> - ["Test that by default anonymous cipher suites are rejected "]; -default_reject_anonymous(suite) -> - []; +default_reject_anonymous()-> + [{doc,"Test that by default anonymous cipher suites are rejected "}]. default_reject_anonymous(Config) when is_list(Config) -> {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), ClientOpts = ?config(client_opts, Config), @@ -1969,12 +1571,8 @@ default_reject_anonymous(Config) when is_list(Config) -> Client, {error, "insufficient security"}). %%-------------------------------------------------------------------- -reuse_session(doc) -> - ["Test reuse of sessions (short handshake)"]; - -reuse_session(suite) -> - []; - +reuse_session() -> + [{doc,"Test reuse of sessions (short handshake)"}]. reuse_session(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2000,7 +1598,7 @@ reuse_session(Config) when is_list(Config) -> Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), Client1 = ssl_test_lib:start_client([{node, ClientNode}, @@ -2011,9 +1609,9 @@ reuse_session(Config) when is_list(Config) -> {Client1, SessionInfo} -> ok; {Client1, Other} -> - test_server:format("Expected: ~p, Unexpected: ~p~n", + ct:print("Expected: ~p, Unexpected: ~p~n", [SessionInfo, Other]), - test_server:fail(session_not_reused) + ct:fail(session_not_reused) end, Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, @@ -2026,7 +1624,7 @@ reuse_session(Config) when is_list(Config) -> | ClientOpts]}]), receive {Client2, SessionInfo} -> - test_server:fail( + ct:fail( session_reused_when_session_reuse_disabled_by_client); {Client2, _} -> ok @@ -2056,7 +1654,7 @@ reuse_session(Config) when is_list(Config) -> Server1 ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), Client4 = ssl_test_lib:start_client([{node, ClientNode}, @@ -2066,10 +1664,10 @@ reuse_session(Config) when is_list(Config) -> receive {Client4, SessionInfo1} -> - test_server:fail( + ct:fail( session_reused_when_session_reuse_disabled_by_server); {Client4, _Other} -> - test_server:format("OTHER: ~p ~n", [_Other]), + ct:print("OTHER: ~p ~n", [_Other]), ok end, @@ -2081,12 +1679,8 @@ reuse_session(Config) when is_list(Config) -> ssl_test_lib:close(Client4). %%-------------------------------------------------------------------- -reuse_session_expired(doc) -> - ["Test sessions is not reused when it has expired"]; - -reuse_session_expired(suite) -> - []; - +reuse_session_expired() -> + [{doc,"Test sessions is not reused when it has expired"}]. reuse_session_expired(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2112,7 +1706,7 @@ reuse_session_expired(Config) when is_list(Config) -> Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), Client1 = ssl_test_lib:start_client([{node, ClientNode}, @@ -2123,15 +1717,15 @@ reuse_session_expired(Config) when is_list(Config) -> {Client1, SessionInfo} -> ok; {Client1, Other} -> - test_server:format("Expected: ~p, Unexpected: ~p~n", + ct:print("Expected: ~p, Unexpected: ~p~n", [SessionInfo, Other]), - test_server:fail(session_not_reused) + ct:fail(session_not_reused) end, Server ! listen, %% Make sure session is unregistered due to expiration - test_server:sleep((?EXPIRE+1)), + ct:sleep((?EXPIRE+1)), [{session_id, Id} |_] = SessionInfo, make_sure_expired(Hostname, Port, Id), @@ -2143,7 +1737,7 @@ reuse_session_expired(Config) when is_list(Config) -> {from, self()}, {options, ClientOpts}]), receive {Client2, SessionInfo} -> - test_server:fail(session_reused_when_session_expired); + ct:fail(session_reused_when_session_expired); {Client2, _} -> ok end, @@ -2165,17 +1759,13 @@ make_sure_expired(Host, Port, Id) -> #session{is_resumable = false} -> ok; _ -> - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), make_sure_expired(Host, Port, Id) end. %%-------------------------------------------------------------------- -server_does_not_want_to_reuse_session(doc) -> - ["Test reuse of sessions (short handshake)"]; - -server_does_not_want_to_reuse_session(suite) -> - []; - +server_does_not_want_to_reuse_session() -> + [{doc,"Test reuse of sessions (short handshake)"}]. server_does_not_want_to_reuse_session(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2204,7 +1794,7 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) -> Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), ssl_test_lib:close(Client0), Client1 = @@ -2214,7 +1804,7 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) -> {from, self()}, {options, ClientOpts}]), receive {Client1, SessionInfo} -> - test_server:fail(session_reused_when_server_does_not_want_to); + ct:fail(session_reused_when_server_does_not_want_to); {Client1, _Other} -> ok end, @@ -2223,512 +1813,55 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) -> ssl_test_lib:close(Client1). %%-------------------------------------------------------------------- - -server_verify_peer_passive(doc) -> - ["Test server option verify_peer"]; - -server_verify_peer_passive(suite) -> - []; - -server_verify_peer_passive(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false}, {verify, verify_peer} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -server_verify_peer_active(doc) -> - ["Test server option verify_peer"]; - -server_verify_peer_active(suite) -> - []; - -server_verify_peer_active(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{active, true}, {verify, verify_peer} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{active, true} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -server_verify_peer_active_once(doc) -> - ["Test server option verify_peer"]; - -server_verify_peer_active_once(suite) -> - []; - -server_verify_peer_active_once(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once}, {verify, verify_peer} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -server_verify_none_passive(doc) -> - ["Test server option verify_none"]; - -server_verify_none_passive(suite) -> - []; - -server_verify_none_passive(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false}, {verify, verify_none} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -server_verify_none_active(doc) -> - ["Test server option verify_none"]; - -server_verify_none_active(suite) -> - []; - -server_verify_none_active(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{active, true}, {verify, verify_none} | - ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{active, true} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -server_verify_none_active_once(doc) -> - ["Test server option verify_none"]; - -server_verify_none_active_once(suite) -> - []; - -server_verify_none_active_once(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once}, {verify, verify_none} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -server_verify_client_once_passive(doc) -> - ["Test server option verify_client_once"]; - -server_verify_client_once_passive(suite) -> - []; - -server_verify_client_once_passive(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false}, {verify, verify_peer}, - {verify_client_once, true} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client0, ok), - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - ssl_test_lib:close(Client0), - Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, result_ok, []}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Client1, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- - -server_verify_client_once_active(doc) -> - ["Test server option verify_client_once"]; - -server_verify_client_once_active(suite) -> - []; - -server_verify_client_once_active(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{active, true}, {verify, verify_peer}, - {verify_client_once, true} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{active, true} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client0, ok), - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - ssl_test_lib:close(Client0), - Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, result_ok, []}}, - {options, [{active, true} | ClientOpts]}]), - - ssl_test_lib:check_result(Client1, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- - -server_verify_client_once_active_once(doc) -> - ["Test server option verify_client_once"]; - -server_verify_client_once_active_once(suite) -> - []; - -server_verify_client_once_active_once(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once}, {verify, verify_peer}, - {verify_client_once, true} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client0, ok), - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - ssl_test_lib:close(Client0), - Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, result_ok, []}}, - {options, [{active, once} | ClientOpts]}]), - - ssl_test_lib:check_result(Client1, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- - -server_verify_no_cacerts(doc) -> - ["Test server must have cacerts if it wants to verify client"]; - -server_verify_no_cacerts(suite) -> - []; -server_verify_no_cacerts(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - {_, ServerNode, _} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, [{verify, verify_peer} - | ServerOpts]}]), - - ssl_test_lib:check_result(Server, {error, {eoptions, {cacertfile, ""}}}). - -%%-------------------------------------------------------------------- - -server_require_peer_cert_ok(doc) -> - ["Test server option fail_if_no_peer_cert when peer sends cert"]; - -server_require_peer_cert_ok(suite) -> - []; - -server_require_peer_cert_ok(Config) when is_list(Config) -> - ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} - | ?config(server_verification_opts, Config)], - ClientOpts = ?config(client_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -server_require_peer_cert_fail(doc) -> - ["Test server option fail_if_no_peer_cert when peer doesn't send cert"]; - -server_require_peer_cert_fail(suite) -> - []; - -server_require_peer_cert_fail(Config) when is_list(Config) -> - ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} - | ?config(server_verification_opts, Config)], - BadClientOpts = ?config(client_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, [{active, false} | ServerOpts]}]), - - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{active, false} | BadClientOpts]}]), - - ssl_test_lib:check_result(Server, {error, esslaccept}, - Client, {error, esslconnect}). - -%%-------------------------------------------------------------------- - -client_verify_none_passive(doc) -> - ["Test client option verify_none"]; - -client_verify_none_passive(suite) -> - []; - -client_verify_none_passive(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false}, - {verify, verify_none} - | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -client_verify_none_active(doc) -> - ["Test client option verify_none"]; - -client_verify_none_active(suite) -> - []; - -client_verify_none_active(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, [{active, true} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, [{active, true}, - {verify, verify_none} - | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -client_verify_none_active_once(doc) -> - ["Test client option verify_none"]; - -client_verify_none_active_once(suite) -> - []; - -client_verify_none_active_once(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active_once, []}}, - {options, [{active, once} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active_once, - []}}, - {options, [{active, once}, - {verify, verify_none} - | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -client_renegotiate(doc) -> - ["Test ssl:renegotiate/1 on client."]; - -client_renegotiate(suite) -> - []; - +client_renegotiate() -> + [{doc,"Test ssl:renegotiate/1 on client."}]. client_renegotiate(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - + Data = "From erlang to erlang", - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, + {from, self()}, + {mfa, {?MODULE, renegotiate, [Data]}}, {options, [{reuse_sessions, false} | ClientOpts]}]), - ssl_test_lib:check_result(Client, ok, Server, ok), + ssl_test_lib:check_result(Client, ok, Server, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -server_renegotiate(doc) -> - ["Test ssl:renegotiate/1 on server."]; - -server_renegotiate(suite) -> - []; - +server_renegotiate() -> + [{doc,"Test ssl:renegotiate/1 on server."}]. server_renegotiate(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - + Data = "From erlang to erlang", Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, + {mfa, {?MODULE, renegotiate, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, + {from, self()}, {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, [{reuse_sessions, false} | ClientOpts]}]), @@ -2737,805 +1870,133 @@ server_renegotiate(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -client_renegotiate_reused_session(doc) -> - ["Test ssl:renegotiate/1 on client when the ssl session will be reused."]; - -client_renegotiate_reused_session(suite) -> - []; - +client_renegotiate_reused_session() -> + [{doc,"Test ssl:renegotiate/1 on client when the ssl session will be reused."}]. client_renegotiate_reused_session(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - + Data = "From erlang to erlang", - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, + {from, self()}, + {mfa, {?MODULE, renegotiate_reuse_session, [Data]}}, {options, [{reuse_sessions, true} | ClientOpts]}]), - ssl_test_lib:check_result(Client, ok, Server, ok), + ssl_test_lib:check_result(Client, ok, Server, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -server_renegotiate_reused_session(doc) -> - ["Test ssl:renegotiate/1 on server when the ssl session will be reused."]; - -server_renegotiate_reused_session(suite) -> - []; - +server_renegotiate_reused_session() -> + [{doc,"Test ssl:renegotiate/1 on server when the ssl session will be reused."}]. server_renegotiate_reused_session(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Data = "From erlang to erlang", - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, renegotiate_reuse_session, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, + {from, self()}, {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, [{reuse_sessions, true} | ClientOpts]}]), - + ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -client_no_wrap_sequence_number(doc) -> - ["Test that erlang client will renegotiate session when", +client_no_wrap_sequence_number() -> + [{doc,"Test that erlang client will renegotiate session when", "max sequence number celing is about to be reached. Although" - "in the testcase we use the test option renegotiate_at" - " to lower treashold substantially."]; - -client_no_wrap_sequence_number(suite) -> - []; + "in the testcase we use the test option renegotiate_at" + " to lower treashold substantially."}]. client_no_wrap_sequence_number(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - + ErlData = "From erlang to erlang", N = 10, - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {ssl_test_lib, no_result, []}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - + Version = ssl_record:highest_protocol_version(ssl_record:supported_protocol_versions()), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, + {from, self()}, + {mfa, {ssl_test_lib, trigger_renegotiate, [[ErlData, treashold(N, Version)]]}}, {options, [{reuse_sessions, false}, {renegotiate_at, N} | ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), + + ssl_test_lib:check_result(Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). - %% First two clauses handles 1/n-1 splitting countermeasure Rizzo/Duong-Beast -treashold(N, {3,0}) -> - (N div 2) + 1; -treashold(N, {3,1}) -> - (N div 2) + 1; -treashold(N, _) -> - N + 1. - %%-------------------------------------------------------------------- -server_no_wrap_sequence_number(doc) -> - ["Test that erlang server will renegotiate session when", +server_no_wrap_sequence_number() -> + [{doc, "Test that erlang server will renegotiate session when", "max sequence number celing is about to be reached. Although" - "in the testcase we use the test option renegotiate_at" - " to lower treashold substantially."]; - -server_no_wrap_sequence_number(suite) -> - []; + "in the testcase we use the test option renegotiate_at" + " to lower treashold substantially."}]. server_no_wrap_sequence_number(Config) when is_list(Config) -> - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From erlang to erlang", - N = 10, - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - trigger_renegotiate, [[Data, N+2]]}}, - {options, [{renegotiate_at, N} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{reuse_sessions, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). -%%-------------------------------------------------------------------- -extended_key_usage_verify_peer(doc) -> - ["Test cert that has a critical extended_key_usage extension in verify_peer mode"]; - -extended_key_usage_verify_peer(suite) -> - []; - -extended_key_usage_verify_peer(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - PrivDir = ?config(priv_dir, Config), - - KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - ServerCertFile = proplists:get_value(certfile, ServerOpts), - NewServerCertFile = filename:join(PrivDir, "server/new_cert.pem"), - [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), - ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), - ServerExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']}, - ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, - ServerExtensions = ServerOTPTbsCert#'OTPTBSCertificate'.extensions, - NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions = - [ServerExtKeyUsageExt | - ServerExtensions]}, - NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), - NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], - - ClientCertFile = proplists:get_value(certfile, ClientOpts), - NewClientCertFile = filename:join(PrivDir, "client/new_cert.pem"), - [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), - ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), - ClientExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-clientAuth']}, - ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, - ClientExtensions = ClientOTPTbsCert#'OTPTBSCertificate'.extensions, - NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions = - [ClientExtKeyUsageExt | - ClientExtensions]}, - NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), - NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{verify, verify_peer} | NewServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{verify, verify_peer} | NewClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -extended_key_usage_verify_none(doc) -> - ["Test cert that has a critical extended_key_usage extension in verify_none mode"]; - -extended_key_usage_verify_none(suite) -> - []; - -extended_key_usage_verify_none(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - PrivDir = ?config(priv_dir, Config), - - KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - ServerCertFile = proplists:get_value(certfile, ServerOpts), - NewServerCertFile = filename:join(PrivDir, "server/new_cert.pem"), - [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), - ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), - ServerExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']}, - ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, - ServerExtensions = ServerOTPTbsCert#'OTPTBSCertificate'.extensions, - NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions = - [ServerExtKeyUsageExt | - ServerExtensions]}, - NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), - NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], - - ClientCertFile = proplists:get_value(certfile, ClientOpts), - NewClientCertFile = filename:join(PrivDir, "client/new_cert.pem"), - [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), - ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), - ClientExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-clientAuth']}, - ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, - ClientExtensions = ClientOTPTbsCert#'OTPTBSCertificate'.extensions, - NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions = - [ClientExtKeyUsageExt | - ClientExtensions]}, - NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), - NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{verify, verify_none} | NewServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{verify, verify_none} | NewClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -no_authority_key_identifier(doc) -> - ["Test cert that does not have authorityKeyIdentifier extension" - " but are present in trusted certs db."]; - -no_authority_key_identifier(suite) -> - []; -no_authority_key_identifier(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), ServerOpts = ?config(server_opts, Config), - PrivDir = ?config(priv_dir, Config), - - KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - CertFile = proplists:get_value(certfile, ServerOpts), - NewCertFile = filename:join(PrivDir, "server/new_cert.pem"), - [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(CertFile), - OTPCert = public_key:pkix_decode_cert(DerCert, otp), - OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate, - Extensions = OTPTbsCert#'OTPTBSCertificate'.extensions, - NewExtensions = delete_authority_key_extension(Extensions, []), - NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{extensions = NewExtensions}, - - test_server:format("Extensions ~p~n, NewExtensions: ~p~n", [Extensions, NewExtensions]), - - NewDerCert = public_key:pkix_sign(NewOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewCertFile, [{'Certificate', NewDerCert, not_encrypted}]), - NewServerOpts = [{certfile, NewCertFile} | proplists:delete(certfile, ServerOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, NewServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, - {options, [{verify, verify_peer} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -delete_authority_key_extension([], Acc) -> - lists:reverse(Acc); -delete_authority_key_extension([#'Extension'{extnID = ?'id-ce-authorityKeyIdentifier'} | Rest], - Acc) -> - delete_authority_key_extension(Rest, Acc); -delete_authority_key_extension([Head | Rest], Acc) -> - delete_authority_key_extension(Rest, [Head | Acc]). - -%%-------------------------------------------------------------------- - -invalid_signature_server(doc) -> - ["Test server with invalid signature"]; - -invalid_signature_server(suite) -> - []; - -invalid_signature_server(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - PrivDir = ?config(priv_dir, Config), - - KeyFile = filename:join(PrivDir, "server/key.pem"), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - ServerCertFile = proplists:get_value(certfile, ServerOpts), - NewServerCertFile = filename:join(PrivDir, "server/invalid_cert.pem"), - [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), - ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), - ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, - NewServerDerCert = public_key:pkix_sign(ServerOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), - NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, NewServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{verify, verify_peer} | ClientOpts]}]), - - tcp_delivery_workaround(Server, {error, "bad certificate"}, - Client, {error,"bad certificate"}). - -%%-------------------------------------------------------------------- - -invalid_signature_client(doc) -> - ["Test server with invalid signature"]; - -invalid_signature_client(suite) -> - []; - -invalid_signature_client(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - PrivDir = ?config(priv_dir, Config), - - KeyFile = filename:join(PrivDir, "client/key.pem"), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - ClientCertFile = proplists:get_value(certfile, ClientOpts), - NewClientCertFile = filename:join(PrivDir, "client/invalid_cert.pem"), - [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), - ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), - ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, - NewClientDerCert = public_key:pkix_sign(ClientOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), - NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, [{verify, verify_peer} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, NewClientOpts}]), - - tcp_delivery_workaround(Server, {error, "bad certificate"}, - Client, {error,"bad certificate"}). - -tcp_delivery_workaround(Server, ServerMsg, Client, ClientMsg) -> - receive - {Server, ServerMsg} -> - client_msg(Client, ClientMsg); - {Client, ClientMsg} -> - server_msg(Server, ServerMsg); - {Client, {error,closed}} -> - server_msg(Server, ServerMsg); - {Server, {error,closed}} -> - client_msg(Client, ClientMsg); - {Client, {error, esslconnect}} -> - server_msg(Server, ServerMsg); - {Server, {error, esslaccept}} -> - client_msg(Client, ClientMsg) - end. - -client_msg(Client, ClientMsg) -> - receive - {Client, ClientMsg} -> - ok; - {Client, {error,closed}} -> - test_server:format("client got close"), - ok; - {Client, {error, esslconnect}} -> - test_server:format("client got econnaborted"), - ok; - Unexpected -> - test_server:fail(Unexpected) - end. - -server_msg(Server, ServerMsg) -> - receive - {Server, ServerMsg} -> - ok; - {Server, {error,closed}} -> - test_server:format("server got close"), - ok; - {Server, {error, esslaccept}} -> - test_server:format("server got econnaborted"), - ok; - Unexpected -> - test_server:fail(Unexpected) - end. - -%%-------------------------------------------------------------------- -cert_expired(doc) -> - ["Test server with invalid signature"]; - -cert_expired(suite) -> - []; - -cert_expired(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - PrivDir = ?config(priv_dir, Config), - - KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - ServerCertFile = proplists:get_value(certfile, ServerOpts), - NewServerCertFile = filename:join(PrivDir, "server/expired_cert.pem"), - [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), - OTPCert = public_key:pkix_decode_cert(DerCert, otp), - OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate, - - {Year, Month, Day} = date(), - {Hours, Min, Sec} = time(), - NotBeforeStr = lists:flatten(io_lib:format("~p~s~s~s~s~sZ",[Year-2, - two_digits_str(Month), - two_digits_str(Day), - two_digits_str(Hours), - two_digits_str(Min), - two_digits_str(Sec)])), - NotAfterStr = lists:flatten(io_lib:format("~p~s~s~s~s~sZ",[Year-1, - two_digits_str(Month), - two_digits_str(Day), - two_digits_str(Hours), - two_digits_str(Min), - two_digits_str(Sec)])), - NewValidity = {'Validity', {generalTime, NotBeforeStr}, {generalTime, NotAfterStr}}, - - test_server:format("Validity: ~p ~n NewValidity: ~p ~n", - [OTPTbsCert#'OTPTBSCertificate'.validity, NewValidity]), - - NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{validity = NewValidity}, - NewServerDerCert = public_key:pkix_sign(NewOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), - NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, NewServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{verify, verify_peer} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, {error, "certificate expired"}, - Client, {error, "certificate expired"}). - -two_digits_str(N) when N < 10 -> - lists:flatten(io_lib:format("0~p", [N])); -two_digits_str(N) -> - lists:flatten(io_lib:format("~p", [N])). - -%%-------------------------------------------------------------------- - -client_with_cert_cipher_suites_handshake(doc) -> - ["Test that client with a certificate without keyEncipherment usage " - " extension can connect to a server with restricted cipher suites "]; - -client_with_cert_cipher_suites_handshake(suite) -> - []; - -client_with_cert_cipher_suites_handshake(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts_digital_signature_only, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, [{active, true}, - {ciphers, ssl_test_lib:rsa_non_signed_suites()} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, [{active, true} - | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -verify_fun_always_run_client(doc) -> - ["Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"]; -verify_fun_always_run_client(suite) -> - []; -verify_fun_always_run_client(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - %% If user verify fun is called correctly we fail the connection. - %% otherwise we can not tell this case apart form where we miss - %% to call users verify fun - FunAndState = {fun(_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, [ChainLen]) -> - {valid, [ChainLen + 1]}; - (_, valid_peer, [2]) -> - {fail, "verify_fun_was_always_run"}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, [0]}, - - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, FunAndState} - | ClientOpts]}]), - %% Server error may be esslaccept or closed depending on timing - %% this is not a bug it is a circumstance of how tcp works! - receive - {Server, ServerError} -> - test_server:format("Server Error ~p~n", [ServerError]) - end, - - ssl_test_lib:check_result(Client, {error, esslconnect}). - -%%-------------------------------------------------------------------- -verify_fun_always_run_server(doc) -> - ["Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"]; -verify_fun_always_run_server(suite) -> - []; -verify_fun_always_run_server(Config) when is_list(Config) -> - ClientOpts = ?config(client_verification_opts, Config), - ServerOpts = ?config(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - %% If user verify fun is called correctly we fail the connection. - %% otherwise we can not tell this case apart form where we miss - %% to call users verify fun - FunAndState = {fun(_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, [ChainLen]) -> - {valid, [ChainLen + 1]}; - (_, valid_peer, [2]) -> - {fail, "verify_fun_was_always_run"}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, [0]}, - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, FunAndState} | - ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, - [{verify, verify_peer} - | ClientOpts]}]), - - %% Client error may be esslconnect or closed depending on timing - %% this is not a bug it is a circumstance of how tcp works! - receive - {Client, ClientError} -> - test_server:format("Client Error ~p~n", [ClientError]) - end, - - ssl_test_lib:check_result(Server, {error, esslaccept}). - -%%-------------------------------------------------------------------- -unknown_server_ca_fail(doc) -> - ["Test that the client fails if the ca is unknown in verify_peer mode"]; -unknown_server_ca_fail(suite) -> - []; -unknown_server_ca_fail(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - FunAndState = {fun(_,{bad_cert, unknown_ca} = Reason, _) -> - {fail, Reason}; - (_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, UserState) -> - {valid, [test_to_update_user_state | UserState]}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, []}, - - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, FunAndState} - | ClientOpts]}]), - - ssl_test_lib:check_result(Server, {error,"unknown ca"}, - Client, {error, "unknown ca"}). - -%%-------------------------------------------------------------------- -unknown_server_ca_accept_verify_none(doc) -> - ["Test that the client succeds if the ca is unknown in verify_none mode"]; -unknown_server_ca_accept_verify_none(suite) -> - []; -unknown_server_ca_accept_verify_none(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, - [{verify, verify_none}| ClientOpts]}]), + ClientOpts = ?config(client_opts, Config), - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). -%%-------------------------------------------------------------------- -unknown_server_ca_accept_verify_peer(doc) -> - ["Test that the client succeds if the ca is unknown in verify_peer mode" - " with a verify_fun that accepts the unknown ca error"]; -unknown_server_ca_accept_verify_peer(suite) -> - []; -unknown_server_ca_accept_verify_peer(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - FunAndState = {fun(_,{bad_cert, unknown_ca}, UserState) -> - {valid, UserState}; - (_,{bad_cert, _} = Reason, _) -> - {fail, Reason}; - (_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, UserState) -> - {valid, UserState}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, []}, - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, FunAndState}| ClientOpts]}]), - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + Data = "From erlang to erlang", + N = 10, -%%-------------------------------------------------------------------- -unknown_server_ca_accept_backwardscompatibility(doc) -> - ["Test that old style verify_funs will work"]; -unknown_server_ca_accept_backwardscompatibility(suite) -> - []; -unknown_server_ca_accept_backwardscompatibility(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - AcceptBadCa = fun({bad_cert,unknown_ca}, Acc) -> Acc; - (Other, Acc) -> [Other | Acc] - end, - VerifyFun = - fun(ErrorList) -> - case lists:foldl(AcceptBadCa, [], ErrorList) of - [] -> true; - [_|_] -> false - end - end, + {mfa, {ssl_test_lib, + trigger_renegotiate, [[Data, N+2]]}}, + {options, [{renegotiate_at, N} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, - send_recv_result_active, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, VerifyFun}| ClientOpts]}]), + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{reuse_sessions, false} | ClientOpts]}]), - ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:check_result(Server, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -der_input(doc) -> - ["Test to input certs and key as der"]; - -der_input(suite) -> - []; +der_input() -> + [{doc,"Test to input certs and key as der"}]. der_input(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), @@ -3556,19 +2017,19 @@ der_input(Config) when is_list(Config) -> {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). - +%%-------------------------------------------------------------------- der_input_opts(Opts) -> Certfile = proplists:get_value(certfile, Opts), CaCertsfile = proplists:get_value(cacertfile, Opts), @@ -3585,12 +2046,9 @@ der_input_opts(Opts) -> {Cert, {Asn1Type, Key}, CaCerts, DHParams}. %%-------------------------------------------------------------------- -%% different_ca_peer_sign(doc) -> +%% different_ca_peer_sign() -> %% ["Check that a CA can have a different signature algorithm than the peer cert."]; -%% different_ca_peer_sign(suite) -> -%% []; - %% different_ca_peer_sign(Config) when is_list(Config) -> %% ClientOpts = ?config(client_mix_opts, Config), %% ServerOpts = ?config(server_mix_verify_opts, Config), @@ -3598,7 +2056,7 @@ der_input_opts(Opts) -> %% {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), %% Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, %% {from, self()}, -%% {mfa, {?MODULE, send_recv_result_active_once, []}}, +%% {mfa, {ssl_test_lib, send_recv_result_active_once, []}}, %% {options, [{active, once}, %% {verify, verify_peer} | ServerOpts]}]), %% Port = ssl_test_lib:inet_port(Server), @@ -3606,7 +2064,7 @@ der_input_opts(Opts) -> %% Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, %% {host, Hostname}, %% {from, self()}, -%% {mfa, {?MODULE, +%% {mfa, {ssl_test_lib, %% send_recv_result_active_once, %% []}}, %% {options, [{active, once}, @@ -3619,12 +2077,8 @@ der_input_opts(Opts) -> %%-------------------------------------------------------------------- -no_reuses_session_server_restart_new_cert(doc) -> - ["Check that a session is not reused if the server is restarted with a new cert."]; - -no_reuses_session_server_restart_new_cert(suite) -> - []; - +no_reuses_session_server_restart_new_cert() -> + [{doc,"Check that a session is not reused if the server is restarted with a new cert."}]. no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -3650,7 +2104,7 @@ no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> end, %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), Monitor = erlang:monitor(process, Server), ssl_test_lib:close(Server), ssl_test_lib:close(Client0), @@ -3672,7 +2126,7 @@ no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> {from, self()}, {options, ClientOpts}]), receive {Client1, SessionInfo} -> - test_server:fail(session_reused_when_server_has_new_cert); + ct:fail(session_reused_when_server_has_new_cert); {Client1, _Other} -> ok end, @@ -3680,12 +2134,9 @@ no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> ssl_test_lib:close(Client1). %%-------------------------------------------------------------------- -no_reuses_session_server_restart_new_cert_file(doc) -> - ["Check that a session is not reused if a server is restarted with a new " - "cert contained in a file with the same name as the old cert."]; - -no_reuses_session_server_restart_new_cert_file(suite) -> - []; +no_reuses_session_server_restart_new_cert_file() -> + [{doc,"Check that a session is not reused if a server is restarted with a new " + "cert contained in a file with the same name as the old cert."}]. no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -3715,7 +2166,7 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> %% Make sure session is registered and we get %% new file time stamp when calling new_config! - test_server:sleep(?SLEEP* 2), + ct:sleep(?SLEEP* 2), ssl_test_lib:close(Server), ssl_test_lib:close(Client0), @@ -3735,7 +2186,7 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> {from, self()}, {options, ClientOpts}]), receive {Client1, SessionInfo} -> - test_server:fail(session_reused_when_server_has_new_cert); + ct:fail(session_reused_when_server_has_new_cert); {Client1, _Other} -> ok end, @@ -3743,11 +2194,8 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> ssl_test_lib:close(Client1). %%-------------------------------------------------------------------- -reuseaddr(doc) -> - [""]; - -reuseaddr(suite) -> - []; +reuseaddr() -> + [{doc,"Test reuseaddr option"}]. reuseaddr(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -3765,24 +2213,19 @@ reuseaddr(Config) when is_list(Config) -> {from, self()}, {mfa, {ssl_test_lib, no_result, []}}, {options, [{active, false} | ClientOpts]}]), - Monitor = erlang:monitor(process, Server), ssl_test_lib:close(Server), ssl_test_lib:close(Client), - receive - {'DOWN', Monitor, _, _, _} -> - ok - end, Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ServerOpts]}]), Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ClientOpts]}]), ssl_test_lib:check_result(Server1, ok, Client1, ok), @@ -3790,14 +2233,51 @@ reuseaddr(Config) when is_list(Config) -> ssl_test_lib:close(Client1). %%-------------------------------------------------------------------- +tcp_reuseaddr() -> + [{doc, "Reference test case."}]. +tcp_reuseaddr(Config) when is_list(Config) -> + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {transport, gen_tcp}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{active, false}, {reuseaddr, true}]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {transport, gen_tcp}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{active, false}]}]), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {transport, gen_tcp}, + {mfa, {?MODULE, tcp_send_recv_result, []}}, + {options, [{active, false}, {reuseaddr, true}]}]), + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {transport, gen_tcp}, + {mfa, {?MODULE, tcp_send_recv_result, []}}, + {options, [{active, false}]}]), + + ssl_test_lib:check_result(Server1, ok, Client1, ok), + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1). -hibernate(doc) -> - ["Check that an SSL connection that is started with option " - "{hibernate_after, 1000} indeed hibernates after 1000ms of " - "inactivity"]; +%%-------------------------------------------------------------------- -hibernate(suite) -> - []; +hibernate() -> + [{doc,"Check that an SSL connection that is started with option " + "{hibernate_after, 1000} indeed hibernates after 1000ms of " + "inactivity"}]. hibernate(Config) -> ClientOpts = ?config(client_opts, Config), @@ -3807,14 +2287,14 @@ hibernate(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), {Client, #sslsocket{pid=Pid}} = ssl_test_lib:start_client([return_socket, {node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, [{hibernate_after, 1000}|ClientOpts]}]), {current_function, _} = process_info(Pid, current_function), @@ -3828,11 +2308,8 @@ hibernate(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -listen_socket(doc) -> - ["Check error handling and inet compliance when calling API functions with listen sockets."]; - -listen_socket(suite) -> - []; +listen_socket() -> + [{doc,"Check error handling and inet compliance when calling API functions with listen sockets."}]. listen_socket(Config) -> ServerOpts = ?config(server_opts, Config), @@ -3856,10 +2333,9 @@ listen_socket(Config) -> ok = ssl:close(ListenSocket). %%-------------------------------------------------------------------- -ssl_accept_timeout(doc) -> - ["Test ssl:ssl_accept timeout"]; -ssl_accept_timeout(suite) -> - []; +ssl_accept_timeout() -> + [{doc,"Test ssl:ssl_accept timeout"}]. + ssl_accept_timeout(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -3883,10 +2359,9 @@ ssl_accept_timeout(Config) -> end. %%-------------------------------------------------------------------- -ssl_recv_timeout(doc) -> - ["Test ssl:ssl_accept timeout"]; -ssl_recv_timeout(suite) -> - []; +ssl_recv_timeout() -> + [{doc,"Test ssl:ssl_accept timeout"}]. + ssl_recv_timeout(Config) -> ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -3912,11 +2387,8 @@ ssl_recv_timeout(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- - -connect_twice(doc) -> - [""]; -connect_twice(suite) -> - []; +connect_twice() -> + [{doc,""}]. connect_twice(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -3926,7 +2398,7 @@ connect_twice(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{keepalive, true},{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), @@ -3934,7 +2406,7 @@ connect_twice(Config) when is_list(Config) -> ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{keepalive, true},{active, false} | ClientOpts]}]), Server ! listen, @@ -3944,11 +2416,11 @@ connect_twice(Config) when is_list(Config) -> {node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{keepalive, true},{active, false} | ClientOpts]}]), - test_server:format("Testcase ~p, Client ~p Server ~p ~n", + ct:print("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -3959,13 +2431,9 @@ connect_twice(Config) when is_list(Config) -> ssl_test_lib:close(Client1). %%-------------------------------------------------------------------- -renegotiate_dos_mitigate_active(doc) -> - ["Mitigate DOS computational attack by not allowing client to renegotiate many times in a row", - "immediately after each other"]; - -renegotiate_dos_mitigate_active(suite) -> - []; - +renegotiate_dos_mitigate_active() -> + [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row", + "immediately after each other"}]. renegotiate_dos_mitigate_active(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -3975,7 +2443,7 @@ renegotiate_dos_mitigate_active(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result_active, []}}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, [ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), @@ -3991,13 +2459,9 @@ renegotiate_dos_mitigate_active(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -renegotiate_dos_mitigate_passive(doc) -> - ["Mitigate DOS computational attack by not allowing client to renegotiate many times in a row", - "immediately after each other"]; - -renegotiate_dos_mitigate_passive(suite) -> - []; - +renegotiate_dos_mitigate_passive() -> + [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row", + "immediately after each other"}]. renegotiate_dos_mitigate_passive(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -4007,7 +2471,7 @@ renegotiate_dos_mitigate_passive(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), @@ -4023,8 +2487,8 @@ renegotiate_dos_mitigate_passive(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -tcp_error_propagation_in_active_mode(doc) -> - ["Test that process recives {ssl_error, Socket, closed} when tcp error ocurres"]; +tcp_error_propagation_in_active_mode() -> + [{doc,"Test that process recives {ssl_error, Socket, closed} when tcp error ocurres"}]. tcp_error_propagation_in_active_mode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -4053,11 +2517,9 @@ tcp_error_propagation_in_active_mode(Config) when is_list(Config) -> ssl_test_lib:check_result(Client, {ssl_closed, SslSocket}). - %%-------------------------------------------------------------------- - -recv_error_handling(doc) -> - ["Special case of call error handling"]; +recv_error_handling() -> + [{doc,"Special case of call error handling"}]. recv_error_handling(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -4077,11 +2539,11 @@ recv_error_handling(Config) when is_list(Config) -> ssl:close(SslSocket), ssl_test_lib:check_result(Server, ok). - %%-------------------------------------------------------------------- -rizzo(doc) -> ["Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as it is - vunrable to Rizzo/Dungon attack"]; +rizzo() -> + [{doc, "Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as it is + vunrable to Rizzo/Dungon attack"}]. rizzo(Config) when is_list(Config) -> Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128], @@ -4090,8 +2552,8 @@ rizzo(Config) when is_list(Config) -> run_send_recv_rizzo(Ciphers, Config, Version, {?MODULE, send_recv_result_active_rizzo, []}). %%-------------------------------------------------------------------- -no_rizzo_rc4(doc) -> - ["Test that there is no 1/n-1-split for RC4 as it is not vunrable to Rizzo/Dungon attack"]; +no_rizzo_rc4() -> + [{doc,"Test that there is no 1/n-1-split for RC4 as it is not vunrable to Rizzo/Dungon attack"}]. no_rizzo_rc4(Config) when is_list(Config) -> Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(),Y == rc4_128], @@ -4101,59 +2563,9 @@ no_rizzo_rc4(Config) when is_list(Config) -> {?MODULE, send_recv_result_active_no_rizzo, []}). %%-------------------------------------------------------------------- -run_send_recv_rizzo(Ciphers, Config, Version, Mfa) -> - Result = lists:map(fun(Cipher) -> - rizzo_test(Cipher, Config, Version, Mfa) end, - Ciphers), - case lists:flatten(Result) of - [] -> - ok; - Error -> - test_server:format("Cipher suite errors: ~p~n", [Error]), - test_server:fail(cipher_suite_failed_see_test_case_log) - end. - -rizzo_test(Cipher, Config, Version, Mfa) -> - {ClientOpts, ServerOpts} = client_server_opts(Cipher, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, Mfa}, - {options, [{active, true}, {ciphers, [Cipher]}, - {versions, [Version]} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, Mfa}, - {options, [{active, true} | ClientOpts]}]), - - Result = ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - case Result of - ok -> - []; - Error -> - [{Cipher, Error}] - end. - -client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == rsa orelse KeyAlgo == dhe_rsa -> - {?config(client_opts, Config), - ?config(server_opts, Config)}; -client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss -> - {?config(client_dsa_opts, Config), - ?config(server_dsa_opts, Config)}. - - -%%-------------------------------------------------------------------- - -new_server_wants_peer_cert(doc) -> - ["Test that server configured to do client certification does" - " not reuse session without a client certificate."]; -new_server_wants_peer_cert(suite) -> - []; +new_server_wants_peer_cert() -> + [{doc, "Test that server configured to do client certification does" + " not reuse session without a client certificate."}]. new_server_wants_peer_cert(Config) when is_list(Config) -> ServerOpts = ?config(server_opts, Config), VServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} @@ -4206,14 +2618,50 @@ new_server_wants_peer_cert(Config) when is_list(Config) -> ssl_test_lib:close(Client), ssl_test_lib:close(Client1). +%%-------------------------------------------------------------------- +session_cache_process_list() -> + [{doc,"Test reuse of sessions (short handshake)"}]. +session_cache_process_list(Config) when is_list(Config) -> + session_cache_process(list,Config). +%%-------------------------------------------------------------------- +session_cache_process_mnesia() -> + [{doc,"Test reuse of sessions (short handshake)"}]. +session_cache_process_mnesia(Config) when is_list(Config) -> + session_cache_process(mnesia,Config). %%-------------------------------------------------------------------- -%%% Internal functions +%% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- send_recv_result(Socket) -> ssl:send(Socket, "Hello world"), {ok,"Hello world"} = ssl:recv(Socket, 11), ok. +tcp_send_recv_result(Socket) -> + gen_tcp:send(Socket, "Hello world"), + {ok,"Hello world"} = gen_tcp:recv(Socket, 11), + ok. + +basic_test(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). send_recv_result_timeout_client(Socket) -> {error, timeout} = ssl:recv(Socket, 11, 500), @@ -4241,17 +2689,6 @@ recv_close(Socket) -> ok end. -send_recv_result_active(Socket) -> - ssl:send(Socket, "Hello world"), - receive - {ssl, Socket, "H"} -> - receive - {ssl, Socket, "ello world"} -> - ok - end; - {ssl, Socket, "Hello world"} -> - ok - end. send_recv_result_active_rizzo(Socket) -> ssl:send(Socket, "Hello world"), @@ -4270,26 +2707,13 @@ send_recv_result_active_no_rizzo(Socket) -> ok end. -send_recv_result_active_once(Socket) -> - ssl:send(Socket, "Hello world"), - receive - {ssl, Socket, "H"} -> - ssl:setopts(Socket, [{active, once}]), - receive - {ssl, Socket, "ello world"} -> - ok - end; - {ssl, Socket, "Hello world"} -> - ok - end. - result_ok(_Socket) -> ok. renegotiate(Socket, Data) -> - test_server:format("Renegotiating ~n", []), + ct:print("Renegotiating ~n", []), Result = ssl:renegotiate(Socket), - test_server:format("Result ~p~n", [Result]), + ct:print("Result ~p~n", [Result]), ssl:send(Socket, Data), case Result of ok -> @@ -4300,7 +2724,7 @@ renegotiate(Socket, Data) -> renegotiate_reuse_session(Socket, Data) -> %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), renegotiate(Socket, Data). renegotiate_immediately(Socket) -> @@ -4316,9 +2740,9 @@ renegotiate_immediately(Socket) -> end, ok = ssl:renegotiate(Socket), {error, renegotiation_rejected} = ssl:renegotiate(Socket), - test_server:sleep(?RENEGOTIATION_DISABLE_TIME +1), + ct:sleep(?RENEGOTIATION_DISABLE_TIME +1), ok = ssl:renegotiate(Socket), - test_server:format("Renegotiated again"), + ct:print("Renegotiated again"), ssl:send(Socket, "Hello world"), ok. @@ -4337,27 +2761,11 @@ new_config(PrivDir, ServerOpts0) -> ServerOpts = proplists:delete(keyfile, ServerOpts2), {ok, PEM} = file:read_file(NewCaCertFile), - test_server:format("CA file content: ~p~n", [public_key:pem_decode(PEM)]), + ct:print("CA file content: ~p~n", [public_key:pem_decode(PEM)]), [{cacertfile, NewCaCertFile}, {certfile, NewCertFile}, {keyfile, NewKeyFile} | ServerOpts]. -session_cache_process_list(doc) -> - ["Test reuse of sessions (short handshake)"]; - -session_cache_process_list(suite) -> - []; -session_cache_process_list(Config) when is_list(Config) -> - session_cache_process(list,Config). - -session_cache_process_mnesia(doc) -> - ["Test reuse of sessions (short handshake)"]; - -session_cache_process_mnesia(suite) -> - []; -session_cache_process_mnesia(Config) when is_list(Config) -> - session_cache_process(mnesia,Config). - session_cache_process(_Type,Config) when is_list(Config) -> reuse_session(Config). @@ -4499,9 +2907,9 @@ erlang_ssl_receive(Socket, Data) -> io:format("Received ~p~n",[Byte]), erlang_ssl_receive(Socket, tl(Data)); Other -> - test_server:fail({unexpected_message, Other}) + ct:fail({unexpected_message, Other}) after ?SLEEP * 3 -> - test_server:fail({did_not_get, Data}) + ct:fail({did_not_get, Data}) end. receive_msg(_) -> @@ -4509,3 +2917,222 @@ receive_msg(_) -> Msg -> Msg end. + +controlling_process_result(Socket, Pid, Msg) -> + ok = ssl:controlling_process(Socket, Pid), + %% Make sure other side has evaluated controlling_process + %% before message is sent + ct:sleep(?SLEEP), + ssl:send(Socket, Msg), + no_result_msg. + +receive_s_rizzo_duong_beast() -> + receive + {ssl, _, "erver hello"} -> + receive + {ssl, _, "C"} -> + receive + {ssl, _, "lient hello"} -> + ok + end + end + end. +receive_c_rizzo_duong_beast() -> + receive + {ssl, _, "lient hello"} -> + receive + {ssl, _, "S"} -> + receive + {ssl, _, "erver hello"} -> + ok + end + end + end. + +controller_dies_result(_Socket, _Pid, _Msg) -> + receive Result -> Result end. + +get_close(Pid, Where) -> + receive + {'EXIT', Pid, _Reason} -> + receive + {_, {ssl_closed, Socket}} -> + ct:print("Socket closed ~p~n",[Socket]); + Unexpected -> + ct:print("Unexpected ~p~n",[Unexpected]), + ct:fail({line, ?LINE-1}) + after 5000 -> + ct:fail({timeout, {line, ?LINE, Where}}) + end; + Unexpected -> + ct:print("Unexpected ~p~n",[Unexpected]), + ct:fail({line, ?LINE-1}) + after 5000 -> + ct:fail({timeout, {line, ?LINE, Where}}) + end. + +run_send_recv_rizzo(Ciphers, Config, Version, Mfa) -> + Result = lists:map(fun(Cipher) -> + rizzo_test(Cipher, Config, Version, Mfa) end, + Ciphers), + case lists:flatten(Result) of + [] -> + ok; + Error -> + ct:print("Cipher suite errors: ~p~n", [Error]), + ct:fail(cipher_suite_failed_see_test_case_log) + end. + +rizzo_test(Cipher, Config, Version, Mfa) -> + {ClientOpts, ServerOpts} = client_server_opts(Cipher, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, Mfa}, + {options, [{active, true}, {ciphers, [Cipher]}, + {versions, [Version]} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, Mfa}, + {options, [{active, true} | ClientOpts]}]), + + Result = ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + case Result of + ok -> + []; + Error -> + [{Cipher, Error}] + end. + +client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == rsa orelse KeyAlgo == dhe_rsa -> + {?config(client_opts, Config), + ?config(server_opts, Config)}; +client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss -> + {?config(client_dsa_opts, Config), + ?config(server_dsa_opts, Config)}. + +run_suites(Ciphers, Version, Config, Type) -> + {ClientOpts, ServerOpts} = + case Type of + rsa -> + {?config(client_opts, Config), + ?config(server_opts, Config)}; + dsa -> + {?config(client_opts, Config), + ?config(server_dsa_opts, Config)}; + anonymous -> + %% No certs in opts! + {?config(client_opts, Config), + ?config(server_anon, Config)} + end, + + Result = lists:map(fun(Cipher) -> + cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end, + Ciphers), + case lists:flatten(Result) of + [] -> + ok; + Error -> + ct:print("Cipher suite errors: ~p~n", [Error]), + ct:fail(cipher_suite_failed_see_test_case_log) + end. + +erlang_cipher_suite(Suite) when is_list(Suite)-> + ssl:suite_definition(ssl_cipher:openssl_suite(Suite)); +erlang_cipher_suite(Suite) -> + Suite. + +cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> + %% process_flag(trap_exit, true), + ct:print("Testing CipherSuite ~p~n", [CipherSuite]), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + ErlangCipherSuite = erlang_cipher_suite(CipherSuite), + + ConnectionInfo = {ok, {Version, ErlangCipherSuite}}, + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, + {options, + [{ciphers,[CipherSuite]} | + ClientOpts]}]), + + Result = ssl_test_lib:wait_for_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + + case Result of + ok -> + []; + Error -> + [{ErlangCipherSuite, Error}] + end. + +connection_info_result(Socket) -> + ssl:connection_info(Socket). + +connect_dist_s(S) -> + Msg = term_to_binary({erlang,term}), + ok = ssl:send(S, Msg). + +connect_dist_c(S) -> + Test = binary_to_list(term_to_binary({erlang,term})), + {ok, Test} = ssl:recv(S, 0, 10000), + ok. + + %% First two clauses handles 1/n-1 splitting countermeasure Rizzo/Duong-Beast +treashold(N, {3,0}) -> + (N div 2) + 1; +treashold(N, {3,1}) -> + (N div 2) + 1; +treashold(N, _) -> + N + 1. + +get_invalid_inet_option(Socket) -> + {error, {eoptions, {inet_option, foo, _}}} = ssl:getopts(Socket, [foo]), + ok. + +shutdown_result(Socket, server) -> + ssl:send(Socket, "Hej"), + ssl:shutdown(Socket, write), + {ok, "Hej hopp"} = ssl:recv(Socket, 8), + ok; + +shutdown_result(Socket, client) -> + {ok, "Hej"} = ssl:recv(Socket, 3), + ssl:send(Socket, "Hej hopp"), + ssl:shutdown(Socket, write), + ok. + +shutdown_write_result(Socket, server) -> + ct:sleep(?SLEEP), + ssl:shutdown(Socket, write); +shutdown_write_result(Socket, client) -> + ssl:recv(Socket, 0). + +dummy(_Socket) -> + %% Should not happen as the ssl connection will not be established + %% due to fatal handshake failiure + exit(kill). + +shutdown_both_result(Socket, server) -> + ct:sleep(?SLEEP), + ssl:shutdown(Socket, read_write); +shutdown_both_result(Socket, client) -> + ssl:recv(Socket, 0). + +peername_result(S) -> + ssl:peername(S). diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl new file mode 100644 index 0000000000..9677d98c1b --- /dev/null +++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl @@ -0,0 +1,982 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012-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/.2 +%% +%% 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(ssl_certificate_verify_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +-include("ssl_internal.hrl"). +-include("ssl_alert.hrl"). +-include("ssl_internal.hrl"). +-include("ssl_record.hrl"). +-include("ssl_handshake.hrl"). + +-define(LONG_TIMEOUT, 600000). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, active}, + {group, passive}, + {group, active_once}, + {group, error_handling}]. + + +groups() -> + [{active, [], tests()}, + {active_once, [], tests()}, + {passive, [], tests()}, + {error_handling, [],error_handling_tests()}]. + +tests() -> + [server_verify_peer, + server_verify_none, + server_require_peer_cert_ok, + server_require_peer_cert_fail, + verify_fun_always_run_client, + verify_fun_always_run_server, + cert_expired, + invalid_signature_client, + invalid_signature_server, + extended_key_usage_verify_peer, + extended_key_usage_verify_none]. + +error_handling_tests()-> + [client_with_cert_cipher_suites_handshake, + server_verify_no_cacerts, + unknown_server_ca_fail, + unknown_server_ca_accept_verify_none, + unknown_server_ca_accept_verify_peer, + unknown_server_ca_accept_backwardscompatibility, + no_authority_key_identifier]. + +init_per_suite(Config0) -> + Dog = ct:timetrap(?LONG_TIMEOUT *2), + catch crypto:stop(), + try crypto:start() of + ok -> + application:start(public_key), + application:start(ssl), + %% make rsa certs using oppenssl + Result = + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), + ct:print("Make certs ~p~n", [Result]), + + Config1 = ssl_test_lib:make_dsa_cert(Config0), + Config = ssl_test_lib:cert_options(Config1), + [{watchdog, Dog} | Config] + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). + +init_per_group(active, Config) -> + [{active, true}, {receive_function, send_recv_result_active} | Config]; +init_per_group(active_once, Config) -> + [{active, once}, {receive_function, send_recv_result_active_once} | Config]; +init_per_group(passive, Config) -> + [{active, false}, {receive_function, send_recv_result} | Config]; +init_per_group(_, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +server_verify_peer() -> + [{doc,"Test server option verify_peer"}]. +server_verify_peer(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + Active = ?config(active, Config), + ReceiveFunction = ?config(receive_function, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active}, {verify, verify_peer} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +server_verify_none() -> + [{doc,"Test server option verify_none"}]. + +server_verify_none(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + Active = ?config(active, Config), + ReceiveFunction = ?config(receive_function, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active}, {verify, verify_none} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +server_verify_client_once() -> + [{doc,"Test server option verify_client_once"}]. + +server_verify_client_once(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + Active = ?config(active, Config), + ReceiveFunction = ?config(receive_function, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active}, {verify, verify_peer}, + {verify_client_once, true} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client0, ok), + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, + ssl_test_lib:close(Client0), + Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, result_ok, []}}, + {options, [{active, Active} | ClientOpts]}]), + + ssl_test_lib:check_result(Client1, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- + +server_require_peer_cert_ok() -> + [{doc,"Test server option fail_if_no_peer_cert when peer sends cert"}]. + +server_require_peer_cert_ok(Config) when is_list(Config) -> + ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} + | ?config(server_verification_opts, Config)], + ClientOpts = ?config(client_verification_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib,send_recv_result, []}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +server_require_peer_cert_fail() -> + [{doc,"Test server option fail_if_no_peer_cert when peer doesn't send cert"}]. + +server_require_peer_cert_fail(Config) when is_list(Config) -> + ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} + | ?config(server_verification_opts, Config)], + BadClientOpts = ?config(client_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, [{active, false} | ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{active, false} | BadClientOpts]}]), + + ssl_test_lib:check_result(Server, {error, esslaccept}, + Client, {error, esslconnect}). + + +%%-------------------------------------------------------------------- +verify_fun_always_run_client() -> + [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}]. + +verify_fun_always_run_client(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + %% If user verify fun is called correctly we fail the connection. + %% otherwise we can not tell this case apart form where we miss + %% to call users verify fun + FunAndState = {fun(_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, [ChainLen]) -> + {valid, [ChainLen + 1]}; + (_, valid_peer, [2]) -> + {fail, "verify_fun_was_always_run"}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, [0]}, + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, FunAndState} + | ClientOpts]}]), + %% Server error may be esslaccept or closed depending on timing + %% this is not a bug it is a circumstance of how tcp works! + receive + {Server, ServerError} -> + ct:print("Server Error ~p~n", [ServerError]) + end, + + ssl_test_lib:check_result(Client, {error, esslconnect}). + +%%-------------------------------------------------------------------- +verify_fun_always_run_server() -> + [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}]. +verify_fun_always_run_server(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% If user verify fun is called correctly we fail the connection. + %% otherwise we can not tell this case apart form where we miss + %% to call users verify fun + FunAndState = {fun(_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, [ChainLen]) -> + {valid, [ChainLen + 1]}; + (_, valid_peer, [2]) -> + {fail, "verify_fun_was_always_run"}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, [0]}, + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, FunAndState} | + ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, + [{verify, verify_peer} + | ClientOpts]}]), + + %% Client error may be esslconnect or closed depending on timing + %% this is not a bug it is a circumstance of how tcp works! + receive + {Client, ClientError} -> + ct:print("Client Error ~p~n", [ClientError]) + end, + + ssl_test_lib:check_result(Server, {error, esslaccept}). + +%%-------------------------------------------------------------------- + +client_verify_none_passive() -> + [{doc,"Test client option verify_none"}]. + +client_verify_none_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false}, + {verify, verify_none} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +cert_expired() -> + [{doc,"Test server with invalid signature"}]. + +cert_expired(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join(PrivDir, "server/expired_cert.pem"), + [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), + OTPCert = public_key:pkix_decode_cert(DerCert, otp), + OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate, + + {Year, Month, Day} = date(), + {Hours, Min, Sec} = time(), + NotBeforeStr = lists:flatten(io_lib:format("~p~s~s~s~s~sZ",[Year-2, + two_digits_str(Month), + two_digits_str(Day), + two_digits_str(Hours), + two_digits_str(Min), + two_digits_str(Sec)])), + NotAfterStr = lists:flatten(io_lib:format("~p~s~s~s~s~sZ",[Year-1, + two_digits_str(Month), + two_digits_str(Day), + two_digits_str(Hours), + two_digits_str(Min), + two_digits_str(Sec)])), + NewValidity = {'Validity', {generalTime, NotBeforeStr}, {generalTime, NotAfterStr}}, + + ct:print("Validity: ~p ~n NewValidity: ~p ~n", + [OTPTbsCert#'OTPTBSCertificate'.validity, NewValidity]), + + NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{validity = NewValidity}, + NewServerDerCert = public_key:pkix_sign(NewOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, NewServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{verify, verify_peer} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, {error, "certificate expired"}, + Client, {error, "certificate expired"}). + +two_digits_str(N) when N < 10 -> + lists:flatten(io_lib:format("0~p", [N])); +two_digits_str(N) -> + lists:flatten(io_lib:format("~p", [N])). + +%%-------------------------------------------------------------------- + +client_verify_none_active() -> + [{doc,"Test client option verify_none"}]. + +client_verify_none_active(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, [{active, true} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, [{active, true}, + {verify, verify_none} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +client_verify_none_active_once() -> + [{doc,"Test client option verify_none"}]. + +client_verify_none_active_once(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{active, once} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active_once, + []}}, + {options, [{active, once}, + {verify, verify_none} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +extended_key_usage_verify_peer() -> + [{doc,"Test cert that has a critical extended_key_usage extension in verify_peer mode"}]. + +extended_key_usage_verify_peer(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + Active = ?config(active, Config), + ReceiveFunction = ?config(receive_function, Config), + + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join(PrivDir, "server/new_cert.pem"), + [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), + ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), + ServerExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']}, + ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, + ServerExtensions = ServerOTPTbsCert#'OTPTBSCertificate'.extensions, + NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions = + [ServerExtKeyUsageExt | + ServerExtensions]}, + NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], + + ClientCertFile = proplists:get_value(certfile, ClientOpts), + NewClientCertFile = filename:join(PrivDir, "client/new_cert.pem"), + [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), + ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), + ClientExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-clientAuth']}, + ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, + ClientExtensions = ClientOTPTbsCert#'OTPTBSCertificate'.extensions, + NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions = + [ClientExtKeyUsageExt | + ClientExtensions]}, + NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), + NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{verify, verify_peer}, {active, Active} | NewServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{verify, verify_peer}, {active, Active} | + NewClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +extended_key_usage_verify_none() -> + [{doc,"Test cert that has a critical extended_key_usage extension in verify_none mode"}]. + +extended_key_usage_verify_none(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + Active = ?config(active, Config), + ReceiveFunction = ?config(receive_function, Config), + + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join(PrivDir, "server/new_cert.pem"), + [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), + ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), + ServerExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']}, + ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, + ServerExtensions = ServerOTPTbsCert#'OTPTBSCertificate'.extensions, + NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions = + [ServerExtKeyUsageExt | + ServerExtensions]}, + NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], + + ClientCertFile = proplists:get_value(certfile, ClientOpts), + NewClientCertFile = filename:join(PrivDir, "client/new_cert.pem"), + [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), + ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), + ClientExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-clientAuth']}, + ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, + ClientExtensions = ClientOTPTbsCert#'OTPTBSCertificate'.extensions, + NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions = + [ClientExtKeyUsageExt | + ClientExtensions]}, + NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), + NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{verify, verify_none}, {active, Active} | NewServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{verify, verify_none}, {active, Active} | NewClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +no_authority_key_identifier() -> + [{doc, "Test cert that does not have authorityKeyIdentifier extension" + " but are present in trusted certs db."}]. + +no_authority_key_identifier(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_opts, Config), + PrivDir = ?config(priv_dir, Config), + + KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + CertFile = proplists:get_value(certfile, ServerOpts), + NewCertFile = filename:join(PrivDir, "server/new_cert.pem"), + [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(CertFile), + OTPCert = public_key:pkix_decode_cert(DerCert, otp), + OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate, + Extensions = OTPTbsCert#'OTPTBSCertificate'.extensions, + NewExtensions = delete_authority_key_extension(Extensions, []), + NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{extensions = NewExtensions}, + + ct:print("Extensions ~p~n, NewExtensions: ~p~n", [Extensions, NewExtensions]), + + NewDerCert = public_key:pkix_sign(NewOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewCertFile, [{'Certificate', NewDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewCertFile} | proplists:delete(certfile, ServerOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, NewServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{verify, verify_peer} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +delete_authority_key_extension([], Acc) -> + lists:reverse(Acc); +delete_authority_key_extension([#'Extension'{extnID = ?'id-ce-authorityKeyIdentifier'} | Rest], + Acc) -> + delete_authority_key_extension(Rest, Acc); +delete_authority_key_extension([Head | Rest], Acc) -> + delete_authority_key_extension(Rest, [Head | Acc]). + +%%-------------------------------------------------------------------- + +invalid_signature_server() -> + [{doc,"Test server with invalid signature"}]. + +invalid_signature_server(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + + KeyFile = filename:join(PrivDir, "server/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + NewServerCertFile = filename:join(PrivDir, "server/invalid_cert.pem"), + [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), + ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), + ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, + NewServerDerCert = public_key:pkix_sign(ServerOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), + NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, NewServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{verify, verify_peer} | ClientOpts]}]), + + tcp_delivery_workaround(Server, {error, "bad certificate"}, + Client, {error,"bad certificate"}). + +%%-------------------------------------------------------------------- + +invalid_signature_client() -> + [{doc,"Test server with invalid signature"}]. + +invalid_signature_client(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + PrivDir = ?config(priv_dir, Config), + + KeyFile = filename:join(PrivDir, "client/key.pem"), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + ClientCertFile = proplists:get_value(certfile, ClientOpts), + NewClientCertFile = filename:join(PrivDir, "client/invalid_cert.pem"), + [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), + ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), + ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, + NewClientDerCert = public_key:pkix_sign(ClientOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), + NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, [{verify, verify_peer} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, NewClientOpts}]), + + tcp_delivery_workaround(Server, {error, "bad certificate"}, + Client, {error,"bad certificate"}). + + +%%-------------------------------------------------------------------- + +client_with_cert_cipher_suites_handshake() -> + [{doc, "Test that client with a certificate without keyEncipherment usage " + " extension can connect to a server with restricted cipher suites "}]. +client_with_cert_cipher_suites_handshake(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts_digital_signature_only, Config), + ServerOpts = ?config(server_verification_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, [{active, true}, + {ciphers, ssl_test_lib:rsa_non_signed_suites()} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, [{active, true} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +server_verify_no_cacerts() -> + [{doc,"Test server must have cacerts if it wants to verify client"}]. +server_verify_no_cacerts(Config) when is_list(Config) -> + ServerOpts = ?config(server_opts, Config), + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, [{verify, verify_peer} + | ServerOpts]}]), + + ssl_test_lib:check_result(Server, {error, {eoptions, {cacertfile, ""}}}). + + +%%-------------------------------------------------------------------- +unknown_server_ca_fail() -> + [{doc,"Test that the client fails if the ca is unknown in verify_peer mode"}]. +unknown_server_ca_fail(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + FunAndState = {fun(_,{bad_cert, unknown_ca} = Reason, _) -> + {fail, Reason}; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, [test_to_update_user_state | UserState]}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, []}, + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, FunAndState} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, {error,"unknown ca"}, + Client, {error, "unknown ca"}). + +%%-------------------------------------------------------------------- +unknown_server_ca_accept_verify_none() -> + [{doc,"Test that the client succeds if the ca is unknown in verify_none mode"}]. +unknown_server_ca_accept_verify_none(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, + [{verify, verify_none}| ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +unknown_server_ca_accept_verify_peer() -> + [{doc, "Test that the client succeds if the ca is unknown in verify_peer mode" + " with a verify_fun that accepts the unknown ca error"}]. +unknown_server_ca_accept_verify_peer(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + FunAndState = {fun(_,{bad_cert, unknown_ca}, UserState) -> + {valid, UserState}; + (_,{bad_cert, _} = Reason, _) -> + {fail, Reason}; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, UserState}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, []}, + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, FunAndState}| ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +unknown_server_ca_accept_backwardscompatibility() -> + [{doc,"Test that old style verify_funs will work"}]. +unknown_server_ca_accept_backwardscompatibility(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + AcceptBadCa = fun({bad_cert,unknown_ca}, Acc) -> Acc; + (Other, Acc) -> [Other | Acc] + end, + VerifyFun = + fun(ErrorList) -> + case lists:foldl(AcceptBadCa, [], ErrorList) of + [] -> true; + [_|_] -> false + end + end, + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + send_recv_result_active, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, VerifyFun}| ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- + +tcp_delivery_workaround(Server, ServerMsg, Client, ClientMsg) -> + receive + {Server, ServerMsg} -> + client_msg(Client, ClientMsg); + {Client, ClientMsg} -> + server_msg(Server, ServerMsg); + {Client, {error,closed}} -> + server_msg(Server, ServerMsg); + {Server, {error,closed}} -> + client_msg(Client, ClientMsg); + {Client, {error, esslconnect}} -> + server_msg(Server, ServerMsg); + {Server, {error, esslaccept}} -> + client_msg(Client, ClientMsg) + end. + +client_msg(Client, ClientMsg) -> + receive + {Client, ClientMsg} -> + ok; + {Client, {error,closed}} -> + ct:print("client got close"), + ok; + {Client, {error, esslconnect}} -> + ct:print("client got econnaborted"), + ok; + Unexpected -> + ct:fail(Unexpected) + end. +server_msg(Server, ServerMsg) -> + receive + {Server, ServerMsg} -> + ok; + {Server, {error,closed}} -> + ct:print("server got close"), + ok; + {Server, {error, esslaccept}} -> + ct:print("server got econnaborted"), + ok; + Unexpected -> + ct:fail(Unexpected) + end. diff --git a/lib/ssl/test/ssl_cipher_SUITE.erl b/lib/ssl/test/ssl_cipher_SUITE.erl index ea1d9dc90c..9869812e6e 100644 --- a/lib/ssl/test/ssl_cipher_SUITE.erl +++ b/lib/ssl/test/ssl_cipher_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -31,16 +31,18 @@ -define(TIMEOUT, 600000). -%% Test server callback functions %%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [aes_decipher_good, aes_decipher_good_tls11, aes_decipher_fail, aes_decipher_fail_tls11]. + +groups() -> + []. + init_per_suite(Config) -> try crypto:start() of ok -> @@ -48,81 +50,30 @@ init_per_suite(Config) -> catch _:_ -> {skip, "Crypto did not start"} end. -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- + end_per_suite(_Config) -> ssl:stop(), application:stop(crypto). -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- -init_per_testcase(_TestCase, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = ssl_test_lib:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(_TestCase, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end. - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. - -all() -> - [aes_decipher_good, aes_decipher_good_tls11, aes_decipher_fail, aes_decipher_fail_tls11]. - -groups() -> - []. - init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. +init_per_testcase(_TestCase, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ct:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. -%% Test cases starts here. -%%-------------------------------------------------------------------- -aes_decipher_good(doc) -> - ["Decipher a known cryptotext."]; +end_per_testcase(_TestCase, Config) -> + Config. -aes_decipher_good(suite) -> - []; +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +aes_decipher_good() -> + [{doc,"Decipher a known cryptotext."}]. aes_decipher_good(Config) when is_list(Config) -> HashSz = 32, @@ -142,11 +93,8 @@ aes_decipher_good(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -aes_decipher_good_tls11(doc) -> - ["Decipher a known TLS 1.1 cryptotext."]; - -aes_decipher_good_tls11(suite) -> - []; +aes_decipher_good_tls11() -> + [{doc,"Decipher a known TLS 1.1 cryptotext."}]. %% the fragment is actuall a TLS 1.1 record, with %% Version = TLS 1.1, we get the correct NextIV in #cipher_state @@ -169,11 +117,8 @@ aes_decipher_good_tls11(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -aes_decipher_fail(doc) -> - ["Decipher a known cryptotext."]; - -aes_decipher_fail(suite) -> - []; +aes_decipher_fail() -> + [{doc,"Decipher a known cryptotext."}]. %% same as above, last byte of key replaced aes_decipher_fail(Config) when is_list(Config) -> @@ -196,11 +141,8 @@ aes_decipher_fail(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -aes_decipher_fail_tls11(doc) -> - ["Decipher a known TLS 1.1 cryptotext."]; - -aes_decipher_fail_tls11(suite) -> - []; +aes_decipher_fail_tls11() -> + [{doc,"Decipher a known TLS 1.1 cryptotext."}]. %% same as above, last byte of key replaced %% stricter padding checks in TLS 1.1 mean we get an alert instead @@ -213,9 +155,11 @@ aes_decipher_fail_tls11(Config) when is_list(Config) -> 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, Version = {3,2}, - #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), + #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = + ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), Version1 = {3,3}, - #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), + #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = + ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), ok. %%-------------------------------------------------------------------- diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl index 818f7f1897..7bfd678f4b 100644 --- a/lib/ssl/test/ssl_dist_SUITE.erl +++ b/lib/ssl/test/ssl_dist_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-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 @@ -19,7 +19,7 @@ -module(ssl_dist_SUITE). --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -35,7 +35,10 @@ nodename} ). -%% Test server callback functions +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -54,6 +57,7 @@ end_per_group(_GroupName, Config) -> init_per_suite(Config0) -> try crypto:start() of ok -> + %% Currently no ct function avilable for is_cover! case test_server:is_cover() of false -> Config = add_ssl_opts_config(Config0), @@ -98,11 +102,13 @@ common_end(_, Config) -> Dog = ?config(watchdog, Config), ?t:timetrap_cancel(Dog), ok. + %%-------------------------------------------------------------------- -%% Test cases starts here. +%% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- -basic(doc) -> - ["Test that two nodes can connect via ssl distribution"]; + +basic() -> + [{doc,"Test that two nodes can connect via ssl distribution"}]. basic(Config) when is_list(Config) -> NH1 = start_ssl_node(Config), Node1 = NH1#node_handle.nodename, @@ -162,8 +168,8 @@ basic(Config) when is_list(Config) -> success(Config). %%-------------------------------------------------------------------- -payload(doc) -> - ["Test that send a lot of data between the ssl distributed noes"]; +payload() -> + [{doc,"Test that send a lot of data between the ssl distributed noes"}]. payload(Config) when is_list(Config) -> NH1 = start_ssl_node(Config), Node1 = NH1#node_handle.nodename, @@ -204,8 +210,8 @@ payload(Config) when is_list(Config) -> stop_ssl_node(NH2), success(Config). %%-------------------------------------------------------------------- -plain_options(doc) -> - ["Test specifying additional options"]; +plain_options() -> + [{doc,"Test specifying additional options"}]. plain_options(Config) when is_list(Config) -> DistOpts = "-ssl_dist_opt server_secure_renegotiate true " "client_secure_renegotiate true " @@ -228,8 +234,8 @@ plain_options(Config) when is_list(Config) -> stop_ssl_node(NH2), success(Config). %%-------------------------------------------------------------------- -plain_verify_options(doc) -> - ["Test specifying additional options"]; +plain_verify_options() -> + [{doc,"Test specifying additional options"}]. plain_verify_options(Config) when is_list(Config) -> DistOpts = "-ssl_dist_opt server_secure_renegotiate true " "client_secure_renegotiate true " @@ -251,7 +257,7 @@ plain_verify_options(Config) when is_list(Config) -> success(Config). %%-------------------------------------------------------------------- -%%% Internal functions +%%% Internal functions ----------------------------------------------- %%-------------------------------------------------------------------- %% ssl_node side api diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index 363a0be594..aff0e0fbbc 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -27,6 +27,9 @@ -include("ssl_internal.hrl"). -include("ssl_handshake.hrl"). +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ @@ -34,6 +37,9 @@ all() -> [ decode_single_hello_extension_correctly, decode_unknown_hello_extension_correctly]. +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- decode_hello_handshake(_Config) -> HelloPacket = <<16#02, 16#00, 16#00, 16#44, 16#03, 16#03, 16#4e, 16#7f, 16#c1, 16#03, 16#35, diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl index 8597aa6740..4e848095a5 100644 --- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -24,6 +24,10 @@ -compile(export_all). -include_lib("common_test/include/ct.hrl"). +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> @@ -68,7 +72,7 @@ init_per_suite(Config) -> Result = (catch make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config))), - test_server:format("Make certs ~p~n", [Result]), + ct:print("Make certs ~p~n", [Result]), ssl_test_lib:cert_options(Config) catch _:_ -> {skip, "Crypto did not start"} @@ -94,12 +98,11 @@ init_per_group(GroupName, Config) -> Config end. - end_per_group(_GroupName, Config) -> Config. - -%% Test cases starts here. +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- validate_empty_protocols_are_not_allowed(Config) when is_list(Config) -> @@ -229,9 +232,8 @@ npn_not_supported_server(Config) when is_list(Config)-> {error, {eoptions, {not_supported_in_sslv3, AdvProtocols}}} = ssl:listen(0, ServerOpts). %%-------------------------------------------------------------------- -%%% Internal functions +%% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- - run_npn_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) -> Data = "hello world", @@ -257,13 +259,13 @@ run_npn_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) -> assert_npn(Socket, Protocol) -> - test_server:format("Negotiated Protocol ~p, Expecting: ~p ~n", + ct:print("Negotiated Protocol ~p, Expecting: ~p ~n", [ssl:negotiated_next_protocol(Socket), Protocol]), Protocol = ssl:negotiated_next_protocol(Socket). assert_npn_and_renegotiate_and_send_data(Socket, Protocol, Data) -> assert_npn(Socket, Protocol), - test_server:format("Renegotiating ~n", []), + ct:print("Renegotiating ~n", []), ok = ssl:renegotiate(Socket), ssl:send(Socket, Data), assert_npn(Socket, Protocol), @@ -278,7 +280,7 @@ ssl_receive_and_assert_npn(Socket, Protocol, Data) -> ssl_receive(Socket, Data). ssl_send(Socket, Data) -> - test_server:format("Connection info: ~p~n", + ct:print("Connection info: ~p~n", [ssl:connection_info(Socket)]), ssl:send(Socket, Data). @@ -286,11 +288,11 @@ ssl_receive(Socket, Data) -> ssl_receive(Socket, Data, []). ssl_receive(Socket, Data, Buffer) -> - test_server:format("Connection info: ~p~n", + ct:print("Connection info: ~p~n", [ssl:connection_info(Socket)]), receive {ssl, Socket, MoreData} -> - test_server:format("Received ~p~n",[MoreData]), + ct:print("Received ~p~n",[MoreData]), NewBuffer = Buffer ++ MoreData, case NewBuffer of Data -> @@ -300,9 +302,9 @@ ssl_receive(Socket, Data, Buffer) -> ssl_receive(Socket, Data, NewBuffer) end; Other -> - test_server:fail({unexpected_message, Other}) + ct:fail({unexpected_message, Other}) after 4000 -> - test_server:fail({did_not_get, Data}) + ct:fail({did_not_get, Data}) end. diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl index 5102c74e87..72768bcb55 100644 --- a/lib/ssl/test/ssl_npn_hello_SUITE.erl +++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -27,6 +27,10 @@ -include("ssl_record.hrl"). -include_lib("common_test/include/ct.hrl"). +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> @@ -37,44 +41,26 @@ all() -> create_server_hello_with_advertised_protocols_test, create_server_hello_with_no_advertised_protocols_test]. - -create_client_handshake(Npn) -> - ssl_handshake:encode_handshake(#client_hello{ - client_version = {1, 2}, - random = <<1:256>>, - session_id = <<>>, - cipher_suites = "", - compression_methods = "", - next_protocol_negotiation = Npn, - renegotiation_info = #renegotiation_info{} - }, vsn). - +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- encode_and_decode_client_hello_test(_Config) -> HandShakeData = create_client_handshake(undefined), Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), - {[{DecodedHandshakeMessage, _Raw}], _} = ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>), + {[{DecodedHandshakeMessage, _Raw}], _} = + ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>), NextProtocolNegotiation = DecodedHandshakeMessage#client_hello.next_protocol_negotiation, NextProtocolNegotiation = undefined. - +%%-------------------------------------------------------------------- encode_and_decode_npn_client_hello_test(_Config) -> HandShakeData = create_client_handshake(#next_protocol_negotiation{extension_data = <<>>}), Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), - {[{DecodedHandshakeMessage, _Raw}], _} = ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>), + {[{DecodedHandshakeMessage, _Raw}], _} = + ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>), NextProtocolNegotiation = DecodedHandshakeMessage#client_hello.next_protocol_negotiation, NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<>>}. - -create_server_handshake(Npn) -> - ssl_handshake:encode_handshake(#server_hello{ - server_version = {1, 2}, - random = <<1:256>>, - session_id = <<>>, - cipher_suite = <<1,2>>, - compression_method = 1, - next_protocol_negotiation = Npn, - renegotiation_info = #renegotiation_info{} - }, vsn). - +%%-------------------------------------------------------------------- encode_and_decode_server_hello_test(_Config) -> HandShakeData = create_server_handshake(undefined), Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), @@ -82,15 +68,51 @@ encode_and_decode_server_hello_test(_Config) -> ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>), NextProtocolNegotiation = DecodedHandshakeMessage#server_hello.next_protocol_negotiation, NextProtocolNegotiation = undefined. - +%%-------------------------------------------------------------------- encode_and_decode_npn_server_hello_test(_Config) -> HandShakeData = create_server_handshake(#next_protocol_negotiation{extension_data = <<6, "spdy/2">>}), Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), - {[{DecodedHandshakeMessage, _Raw}], _} = ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>), + {[{DecodedHandshakeMessage, _Raw}], _} = + ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>), NextProtocolNegotiation = DecodedHandshakeMessage#server_hello.next_protocol_negotiation, ct:print("~p ~n", [NextProtocolNegotiation]), NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}. +%%-------------------------------------------------------------------- +create_server_hello_with_no_advertised_protocols_test(_Config) -> + Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), false, undefined), + undefined = Hello#server_hello.next_protocol_negotiation. +%%-------------------------------------------------------------------- +create_server_hello_with_advertised_protocols_test(_Config) -> + Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), + false, [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>]), + #next_protocol_negotiation{extension_data = <<6, "spdy/1", 8, "http/1.0", 8, "http/1.1">>} = + Hello#server_hello.next_protocol_negotiation. +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- +create_client_handshake(Npn) -> + ssl_handshake:encode_handshake(#client_hello{ + client_version = {1, 2}, + random = <<1:256>>, + session_id = <<>>, + cipher_suites = "", + compression_methods = "", + next_protocol_negotiation = Npn, + renegotiation_info = #renegotiation_info{} + }, vsn). + +create_server_handshake(Npn) -> + ssl_handshake:encode_handshake(#server_hello{ + server_version = {1, 2}, + random = <<1:256>>, + session_id = <<>>, + cipher_suite = <<1,2>>, + compression_method = 1, + next_protocol_negotiation = Npn, + renegotiation_info = #renegotiation_info{} + }, vsn). + create_connection_states() -> #connection_states{ pending_read = #connection_state{ @@ -105,13 +127,3 @@ create_connection_states() -> secure_renegotiation = false } }. - -create_server_hello_with_no_advertised_protocols_test(_Config) -> - Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), false, undefined), - undefined = Hello#server_hello.next_protocol_negotiation. - -create_server_hello_with_advertised_protocols_test(_Config) -> - Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), - false, [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>]), - #next_protocol_negotiation{extension_data = <<6, "spdy/1", 8, "http/1.0", 8, "http/1.1">>} = - Hello#server_hello.next_protocol_negotiation. diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index 8ce80cb725..158c40e372 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -41,84 +41,10 @@ -define(MANY, 1000). -define(SOME, 50). - -%% Test server callback functions %%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- -init_per_suite(Config) -> - catch crypto:stop(), - try crypto:start() of - ok -> - application:start(public_key), - ssl:start(), - Result = - (catch make_certs:all(?config(data_dir, Config), - ?config(priv_dir, Config))), - test_server:format("Make certs ~p~n", [Result]), - ssl_test_lib:cert_options(Config) - catch _:_ -> - {skip, "Crypto did not start"} - end. -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(_Config) -> - ssl:stop(), - application:stop(crypto). -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- -init_per_testcase(_TestCase, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = ssl_test_lib:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(_TestCase, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end. - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> @@ -129,12 +55,6 @@ all() -> {group, 'sslv3'} ]. -groups() -> - [{'tlsv1.2', [], packet_tests()}, - {'tlsv1.1', [], packet_tests()}, - {'tlsv1', [], packet_tests()}, - {'sslv3', [], packet_tests()}]. - packet_tests() -> active_packet_tests() ++ active_once_packet_tests() ++ passive_packet_tests() ++ [packet_send_to_large, @@ -208,6 +128,24 @@ active_packet_tests() -> header_decode_two_bytes_one_sent_active ]. +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + application:start(public_key), + ssl:start(), + Result = + (catch make_certs:all(?config(data_dir, Config), + ?config(priv_dir, Config))), + ct:print("Make certs ~p~n", [Result]), + ssl_test_lib:cert_options(Config) + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). init_per_group(GroupName, Config) -> case ssl_test_lib:is_tls_version(GroupName) of @@ -228,1032 +166,262 @@ init_per_group(GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +init_per_testcase(_TestCase, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ct:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. -%% Test cases starts here. -%%-------------------------------------------------------------------- -packet_raw_passive_many_small(doc) -> - ["Test packet option {packet, raw} in passive mode."]; - -packet_raw_passive_many_small(suite) -> - []; -packet_raw_passive_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "Packet option is {packet, raw}", +end_per_testcase(_TestCase, Config) -> + Config. - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?MANY]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_raw, [Data, ?MANY]}}, - {options, - [{active, false}, - {packet, raw} | - ClientOpts]}]), +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- - ssl_test_lib:check_result(Client, ok), +packet_raw_passive_many_small() -> + [{doc,"Test packet option {packet, raw} in passive mode."}]. - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). +packet_raw_passive_many_small(Config) when is_list(Config) -> + Data = "Packet option is {packet, raw}", + packet(Config, Data, send, passive_recv_packet, ?MANY, raw, false). %%-------------------------------------------------------------------- -packet_raw_passive_some_big(doc) -> - ["Test packet option {packet, raw} in passive mode."]; - -packet_raw_passive_some_big(suite) -> - []; - -packet_raw_passive_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_raw_passive_some_big() -> + [{doc,"Test packet option {packet, raw} in passive mode."}]. +packet_raw_passive_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?SOME]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_raw, [Data, ?SOME]}}, - {options, - [{active, false}, - {packet, raw} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send, passive_recv_packet, ?SOME, raw, false). %%-------------------------------------------------------------------- -packet_0_passive_many_small(doc) -> - ["Test packet option {packet, 0} in passive mode."]; - -packet_0_passive_many_small(suite) -> - []; - -packet_0_passive_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_0_passive_many_small() -> + [{doc,"Test packet option {packet, 0} in passive mode."}]. +packet_0_passive_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 0}, equivalent to packet raw.", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?MANY]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_raw, [Data, ?MANY]}}, - {options, [{active, false}, - {packet, 0} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_recv_packet, ?MANY, 0, false). %%-------------------------------------------------------------------- -packet_0_passive_some_big(doc) -> - ["Test packet option {packet, 0} in passive mode."]; - -packet_0_passive_some_big(suite) -> - []; - -packet_0_passive_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_0_passive_some_big() -> + [{doc,"Test packet option {packet, 0} in passive mode."}]. +packet_0_passive_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?SOME]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_raw, [Data, ?SOME]}}, - {options, [{active, false}, - {packet, 0} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_recv_packet, ?SOME, 0, false). %%-------------------------------------------------------------------- -packet_1_passive_many_small(doc) -> - ["Test packet option {packet, 1} in passive mode."]; - -packet_1_passive_many_small(suite) -> - []; - -packet_1_passive_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_1_passive_many_small() -> + [{doc,"Test packet option {packet, 1} in passive mode."}]. +packet_1_passive_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 1}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 1}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_recv_packet, - [Data, ?MANY]}}, - {options, [{active, false}, - {packet, 1} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_recv_packet, ?MANY, 1, false). %%-------------------------------------------------------------------- -packet_1_passive_some_big(doc) -> - ["Test packet option {packet, 1} in passive mode."]; - -packet_1_passive_some_big(suite) -> - []; - -packet_1_passive_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_1_passive_some_big() -> + [{doc,"Test packet option {packet, 1} in passive mode."}]. +packet_1_passive_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(255, "1")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 1}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_recv_packet, - [Data, ?SOME]}}, - {options, [{active, false}, - {packet, 1} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_recv_packet, ?SOME, 1, false). %%-------------------------------------------------------------------- -packet_2_passive_many_small(doc) -> - ["Test packet option {packet, 2} in passive mode"]; - -packet_2_passive_many_small(suite) -> - []; - -packet_2_passive_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_2_passive_many_small() -> + [{doc,"Test packet option {packet, 2} in passive mode"}]. +packet_2_passive_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 2}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 2}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_recv_packet, - [Data, ?MANY]}}, - {options, [{active, false}, - {packet, 2} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_recv_packet, ?MANY, 2, false). %%-------------------------------------------------------------------- -packet_2_passive_some_big(doc) -> - ["Test packet option {packet, 2} in passive mode"]; - -packet_2_passive_some_big(suite) -> - []; - -packet_2_passive_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_2_passive_some_big() -> + [{doc,"Test packet option {packet, 2} in passive mode"}]. +packet_2_passive_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 2}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_recv_packet, - [Data, ?SOME]}}, - {options, [{active, false}, - {packet, 2} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_recv_packet, ?SOME, 2, false). %%-------------------------------------------------------------------- -packet_4_passive_many_small(doc) -> - ["Test packet option {packet, 4} in passive mode"]; - -packet_4_passive_many_small(suite) -> - []; - -packet_4_passive_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_4_passive_many_small() -> + [{doc,"Test packet option {packet, 4} in passive mode"}]. +packet_4_passive_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 4}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, - {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 4}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_recv_packet, - [Data, ?MANY]}}, - {options, [{active, false}, - {packet, 4} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send, passive_recv_packet, ?MANY, 4, false). %%-------------------------------------------------------------------- -packet_4_passive_some_big(doc) -> - ["Test packet option {packet, 4} in passive mode"]; - -packet_4_passive_some_big(suite) -> - []; - -packet_4_passive_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_4_passive_some_big() -> + [{doc,"Test packet option {packet, 4} in passive mode"}]. +packet_4_passive_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 4}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, passive_recv_packet, - [Data, ?SOME]}}, - {options, [{active, false}, - {packet, 4} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send, passive_recv_packet, ?SOME, 4, false). %%-------------------------------------------------------------------- -packet_raw_active_once_many_small(doc) -> - ["Test packet option {packet, raw} in active once mode."]; - -packet_raw_active_once_many_small(suite) -> - []; - -packet_raw_active_once_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_raw_active_once_many_small() -> + [{doc,"Test packet option {packet, raw} in active once mode."}]. +packet_raw_active_once_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, raw}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?MANY]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, active_once_raw, - [Data, ?MANY]}}, - {options, [{active, once}, - {packet, raw} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_once_raw, ?MANY, raw, once). %%-------------------------------------------------------------------- -packet_raw_active_once_some_big(doc) -> - ["Test packet option {packet, raw} in active once mode."]; - -packet_raw_active_once_some_big(suite) -> - []; - -packet_raw_active_once_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_raw_active_once_some_big() -> + [{doc,"Test packet option {packet, raw} in active once mode."}]. +packet_raw_active_once_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?SOME]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, active_once_raw, - [Data, ?SOME]}}, - {options, [{active, once}, - {packet, raw} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_once_raw, ?SOME, raw, once). %%-------------------------------------------------------------------- -packet_0_active_once_many_small(doc) -> - ["Test packet option {packet, 0} in active once mode."]; - -packet_0_active_once_many_small(suite) -> - []; - -packet_0_active_once_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_0_active_once_many_small() -> + [{doc,"Test packet option {packet, 0} in active once mode."}]. +packet_0_active_once_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 0}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?MANY]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, active_once_raw, - [Data, ?MANY]}}, - {options, [{active, once}, - {packet, 0} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send_raw, active_once_raw, ?MANY, 0, once). %%-------------------------------------------------------------------- -packet_0_active_once_some_big(doc) -> - ["Test packet option {packet, 0} in active once mode."]; - -packet_0_active_once_some_big(suite) -> - []; - -packet_0_active_once_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_0_active_once_some_big() -> + [{doc,"Test packet option {packet, 0} in active once mode."}]. +packet_0_active_once_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw , - [Data, ?SOME]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, active_once_raw, - [Data, ?SOME]}}, - {options, [{active, once}, - {packet, 0} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_once_raw, ?SOME, 0, once). %%-------------------------------------------------------------------- -packet_1_active_once_many_small(doc) -> - ["Test packet option {packet, 1} in active once mode."]; - -packet_1_active_once_many_small(suite) -> - []; - -packet_1_active_once_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_1_active_once_many_small() -> + [{doc,"Test packet option {packet, 1} in active once mode."}]. +packet_1_active_once_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 1}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 1}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_once_packet, - [Data, ?MANY]}}, - {options, [{active, once}, - {packet, 1} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_once_raw, ?MANY, 1, once). %%-------------------------------------------------------------------- -packet_1_active_once_some_big(doc) -> - ["Test packet option {packet, 1} in active once mode."]; - -packet_1_active_once_some_big(suite) -> - []; - -packet_1_active_once_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_1_active_once_some_big() -> + [{doc,"Test packet option {packet, 1} in active once mode."}]. +packet_1_active_once_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(255, "1")), + packet(Config, Data, send_raw, active_once_raw, ?SOME, 1, once). - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 1}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_once_packet, - [Data, ?SOME]}}, - {options, [{active, once}, - {packet, 1} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_2_active_once_many_small(doc) -> - ["Test packet option {packet, 2} in active once mode"]; - -packet_2_active_once_many_small(suite) -> - []; - -packet_2_active_once_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_2_active_once_many_small() -> + [{doc,"Test packet option {packet, 2} in active once mode"}]. +packet_2_active_once_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 2}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 2}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_once_packet, - [Data, ?MANY]}}, - {options, [{active, once}, - {packet, 2} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send_raw, active_once_raw, ?MANY, 2, once). %%-------------------------------------------------------------------- -packet_2_active_once_some_big(doc) -> - ["Test packet option {packet, 2} in active once mode"]; - -packet_2_active_once_some_big(suite) -> - []; - -packet_2_active_once_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_2_active_once_some_big() -> + [{doc,"Test packet option {packet, 2} in active once mode"}]. +packet_2_active_once_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 2}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_once_packet, - [Data, ?SOME]}}, - {options, [{active, once}, - {packet, 2} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_once_raw, ?SOME, 2, once). %%-------------------------------------------------------------------- -packet_4_active_once_many_small(doc) -> - ["Test packet option {packet, 4} in active once mode"]; - -packet_4_active_once_many_small(suite) -> - []; - -packet_4_active_once_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_4_active_once_many_small() -> + [{doc,"Test packet option {packet, 4} in active once mode"}]. +packet_4_active_once_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 4}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 4}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_once_packet, - [Data, ?MANY]}}, - {options, [{active, once}, - {packet, 4} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_once_raw, ?MANY, 4, once). %%-------------------------------------------------------------------- -packet_4_active_once_some_big(doc) -> - ["Test packet option {packet, 4} in active once mode"]; - -packet_4_active_once_some_big(suite) -> - []; - -packet_4_active_once_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_4_active_once_some_big() -> + [{doc,"Test packet option {packet, 4} in active once mode"}]. +packet_4_active_once_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 4}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_once_packet, - [Data, ?SOME]}}, - {options, [{active, once}, - {packet, 4} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_once_raw, ?SOME, 4, once). %%-------------------------------------------------------------------- -packet_raw_active_many_small(doc) -> - ["Test packet option {packet, raw} in active mode."]; - -packet_raw_active_many_small(suite) -> - []; +packet_raw_active_many_small() -> + [{doc,"Test packet option {packet, raw} in active mode."}]. packet_raw_active_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Data = "Packet option is {packet, raw}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?MANY]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, active_raw, - [Data, ?MANY]}}, - {options, [{active, true}, - {packet, raw} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send_raw, active_raw, ?MANY, raw, active). %%-------------------------------------------------------------------- -packet_raw_active_some_big(doc) -> - ["Test packet option {packet, raw} in active mode."]; - -packet_raw_active_some_big(suite) -> - []; +packet_raw_active_some_big() -> + [{doc,"Test packet option {packet, raw} in active mode."}]. packet_raw_active_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?SOME]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, active_raw, [Data, ?SOME]}}, - {options, [{active, true}, - {packet, raw} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_raw, ?SOME, raw, active). %%-------------------------------------------------------------------- -packet_0_active_many_small(doc) -> - ["Test packet option {packet, 0} in active mode."]; - -packet_0_active_many_small(suite) -> - []; - -packet_0_active_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_0_active_many_small() -> + [{doc,"Test packet option {packet, 0} in active mode."}]. +packet_0_active_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 0}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?MANY]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, active_raw, - [Data, ?MANY]}}, - {options, [{active, true}, - {packet, 0} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_raw, ?MANY, 0, active). %%-------------------------------------------------------------------- -packet_0_active_some_big(doc) -> - ["Test packet option {packet, 0} in active mode."]; - -packet_0_active_some_big(suite) -> - []; - -packet_0_active_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_0_active_some_big() -> + [{doc,"Test packet option {packet, 0} in active mode."}]. +packet_0_active_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_raw ,[Data, ?SOME]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, active_raw, - [Data, ?SOME]}}, - {options, [{active, true}, - {packet, 0} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send_raw, active_raw, ?SOME, 0, active). %%-------------------------------------------------------------------- -packet_1_active_many_small(doc) -> - ["Test packet option {packet, 1} in active mode."]; - -packet_1_active_many_small(suite) -> - []; - -packet_1_active_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_1_active_many_small() -> + [{doc,"Test packet option {packet, 1} in active mode."}]. +packet_1_active_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 1}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 1}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_packet, [Data, ?MANY]}}, - {options, [{active, true}, - {packet, 1} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_raw, ?MANY, 1, active). %%-------------------------------------------------------------------- -packet_1_active_some_big(doc) -> - ["Test packet option {packet, 1} in active mode."]; - -packet_1_active_some_big(suite) -> - []; - -packet_1_active_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_1_active_some_big() -> + [{doc,"Test packet option {packet, 1} in active mode."}]. +packet_1_active_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(255, "1")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 1}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_packet, [Data, ?SOME]}}, - {options, [{active, true}, - {packet, 1} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_raw, ?SOME, 1, active). %%-------------------------------------------------------------------- -packet_2_active_many_small(doc) -> - ["Test packet option {packet, 2} in active mode"]; - -packet_2_active_many_small(suite) -> - []; - -packet_2_active_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_2_active_many_small() -> + [{doc,"Test packet option {packet, 2} in active mode"}]. +packet_2_active_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 2}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 2}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_packet, [Data, ?MANY]}}, - {options, [{active, true}, - {packet, 2} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_raw, ?MANY, 2, active). %%-------------------------------------------------------------------- -packet_2_active_some_big(doc) -> - ["Test packet option {packet, 2} in active mode"]; - -packet_2_active_some_big(suite) -> - []; - -packet_2_active_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_2_active_some_big() -> + [{doc,"Test packet option {packet, 2} in active mode"}]. +packet_2_active_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 2}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_packet, [Data, ?SOME]}}, - {options, [{active, true}, - {packet, 2} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + packet(Config, Data, send_raw, active_raw, ?SOME, 2, active). %%-------------------------------------------------------------------- -packet_4_active_many_small(doc) -> - ["Test packet option {packet, 4} in active mode"]; - -packet_4_active_many_small(suite) -> - []; - -packet_4_active_many_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_4_active_many_small() -> + [{doc,"Test packet option {packet, 4} in active mode"}]. +packet_4_active_many_small(Config) when is_list(Config) -> Data = "Packet option is {packet, 4}", - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?MANY]}}, - {options, [{packet, 4}|ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_packet, [Data, ?MANY]}}, - {options, [{active, true}, - {packet, 4} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send_raw, active_raw, ?MANY, 4, active). %%-------------------------------------------------------------------- -packet_4_active_some_big(doc) -> - ["Test packet option {packet, 4} in active mode"]; - -packet_4_active_some_big(suite) -> - []; - -packet_4_active_some_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +packet_4_active_some_big() -> + [{doc,"Test packet option {packet, 4} in active mode"}]. +packet_4_active_some_big(Config) when is_list(Config) -> Data = lists:append(lists:duplicate(100, "1234567890")), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send, [Data, ?SOME]}}, - {options, [{packet, 4} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, - active_packet, [Data, ?SOME]}}, - {options, [{active, true}, - {packet, 4} | - ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - + packet(Config, Data, send_raw, active_raw, ?SOME, 4, active). %%-------------------------------------------------------------------- -packet_send_to_large(doc) -> - ["Test setting the packet option {packet, 2} on the send side"]; - -packet_send_to_large(suite) -> []; +packet_send_to_large() -> + [{doc,"Test setting the packet option {packet, 2} on the send side"}]. packet_send_to_large(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1279,16 +447,9 @@ packet_send_to_large(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). - - - - %%-------------------------------------------------------------------- -packet_wait_active(doc) -> - ["Test waiting when complete packages have not arrived"]; - -packet_wait_active(suite) -> - []; +packet_wait_active() -> + [{doc,"Test waiting when complete packages have not arrived"}]. packet_wait_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1320,11 +481,8 @@ packet_wait_active(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -packet_wait_passive(doc) -> - ["Test waiting when complete packages have not arrived"]; - -packet_wait_passive(suite) -> - []; +packet_wait_passive() -> + [{doc,"Test waiting when complete packages have not arrived"}]. packet_wait_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1353,10 +511,8 @@ packet_wait_passive(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_baddata_active(doc) -> - ["Test that if a bad packet arrives error msg is sent and socket is closed"]; -packet_baddata_active(suite) -> - []; +packet_baddata_active() -> + [{doc,"Test that if a bad packet arrives error msg is sent and socket is closed"}]. packet_baddata_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1381,18 +537,15 @@ packet_baddata_active(Config) when is_list(Config) -> {Client, {other, {ssl_error, _Socket, {invalid_packet, _}},{error,closed},1}} -> ok; Unexpected -> - test_server:fail({unexpected, Unexpected}) + ct:fail({unexpected, Unexpected}) end, ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_baddata_passive(doc) -> - ["Test that if a bad packet arrives error msg is sent and socket is closed"]; - -packet_baddata_passive(suite) -> - []; +packet_baddata_passive() -> + [{doc,"Test that if a bad packet arrives error msg is sent and socket is closed"}]. packet_baddata_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1418,19 +571,16 @@ packet_baddata_passive(Config) when is_list(Config) -> receive {Client, {other, {error, {invalid_packet, _}},{error,closed}, 1}} -> ok; Unexpected -> - test_server:fail({unexpected, Unexpected}) + ct:fail({unexpected, Unexpected}) end, ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_size_active(doc) -> - ["Test that if a packet of size larger than - packet_size arrives error msg is sent and socket is closed"]; - -packet_size_active(suite) -> - []; +packet_size_active() -> + [{doc,"Test that if a packet of size larger than + packet_size arrives error msg is sent and socket is closed"}]. packet_size_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1455,17 +605,16 @@ packet_size_active(Config) when is_list(Config) -> {Client, {other, {ssl_error, _Socket, {invalid_packet, _}},{error,closed},1}} -> ok; Unexpected -> - test_server:fail({unexpected, Unexpected}) + ct:fail({unexpected, Unexpected}) end, ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_size_passive(doc) -> - ["Test that if a packet of size larger - than packet_size arrives error msg is sent and socket is closed"]; -packet_size_passive(suite) -> []; +packet_size_passive() -> + [{doc, "Test that if a packet of size larger + than packet_size arrives error msg is sent and socket is closed"}]. packet_size_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1490,17 +639,15 @@ packet_size_passive(Config) when is_list(Config) -> receive {Client, {other, {error, {invalid_packet, _}},{error,closed},1}} -> ok; Unexpected -> - test_server:fail({unexpected, Unexpected}) + ct:fail({unexpected, Unexpected}) end, ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_cdr_decode(doc) -> - ["Test setting the packet option {packet, cdr}, {mode, binary}"]; -packet_cdr_decode(suite) -> - []; +packet_cdr_decode() -> + [{doc,"Test setting the packet option {packet, cdr}, {mode, binary}"}]. packet_cdr_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1532,10 +679,8 @@ packet_cdr_decode(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_cdr_decode_list(doc) -> - ["Test setting the packet option {packet, cdr} {mode, list}"]; -packet_cdr_decode_list(suite) -> - []; +packet_cdr_decode_list() -> + [{doc,"Test setting the packet option {packet, cdr} {mode, list}"}]. packet_cdr_decode_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1567,11 +712,9 @@ packet_cdr_decode_list(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_http_decode(doc) -> - ["Test setting the packet option {packet, http} {mode, binary} " - "(Body will be binary http strings are lists)"]; -packet_http_decode(suite) -> - []; +packet_http_decode() -> + [{doc, "Test setting the packet option {packet, http} {mode, binary} " + "(Body will be binary http strings are lists)"}]. packet_http_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -1652,11 +795,9 @@ client_http_decode(Socket, HttpRequest) -> ok. %%-------------------------------------------------------------------- -packet_http_decode_list(doc) -> - ["Test setting the packet option {packet, http}, {mode, list}" - "(Body will be list too)"]; -packet_http_decode_list(suite) -> - []; +packet_http_decode_list() -> + [{doc, "Test setting the packet option {packet, http}, {mode, list}" + "(Body will be list too)"}]. packet_http_decode_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1712,11 +853,8 @@ client_http_decode_list(Socket, HttpRequest) -> ok. %%-------------------------------------------------------------------- -packet_http_bin_decode_multi(doc) -> - ["Test setting the packet option {packet, http_bin} with multiple requests"]; -packet_http_bin_decode_multi(suite) -> - []; - +packet_http_bin_decode_multi() -> + [{doc,"Test setting the packet option {packet, http_bin} with multiple requests"}]. packet_http_bin_decode_multi(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1803,11 +941,10 @@ client_http_bin_decode(_, _, _) -> ok. %%-------------------------------------------------------------------- -packet_http_error_passive(doc) -> - ["Test setting the packet option {packet, http}, {active, false}" - " with a incorrect http header." ]; -packet_http_error_passive(suite) -> - []; +packet_http_error_passive() -> + [{doc,"Test setting the packet option {packet, http}, {active, false}" + " with a incorrect http header."}]. + packet_http_error_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1865,10 +1002,9 @@ server_http_decode_error(Socket, HttpResponse) -> ok = ssl:send(Socket, HttpResponse), ok. %%-------------------------------------------------------------------- -packet_httph_active(doc) -> - ["Test setting the packet option {packet, httph}"]; -packet_httph_active(suite) -> - []; +packet_httph_active() -> + [{doc,"Test setting the packet option {packet, httph}"}]. + packet_httph_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1922,10 +1058,8 @@ client_http_decode_trailer_active(Socket) -> ok. %%-------------------------------------------------------------------- -packet_httph_bin_active(doc) -> - ["Test setting the packet option {packet, httph_bin}"]; -packet_httph_bin_active(suite) -> - []; +packet_httph_bin_active() -> + [{doc,"Test setting the packet option {packet, httph_bin}"}]. packet_httph_bin_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -1973,10 +1107,9 @@ client_http_decode_trailer_bin_active(Socket) -> end, ok. %%-------------------------------------------------------------------- -packet_httph_active_once(doc) -> - ["Test setting the packet option {packet, httph}"]; -packet_httph_active_once(suite) -> - []; +packet_httph_active_once() -> + [{doc,"Test setting the packet option {packet, httph}"}]. + packet_httph_active_once(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2027,10 +1160,9 @@ client_http_decode_trailer_active_once(Socket) -> end, ok. %%-------------------------------------------------------------------- -packet_httph_bin_active_once(doc) -> - ["Test setting the packet option {packet, httph_bin}"]; -packet_httph_bin_active_once(suite) -> - []; +packet_httph_bin_active_once() -> + [{doc,"Test setting the packet option {packet, httph_bin}"}]. + packet_httph_bin_active_once(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2082,10 +1214,9 @@ client_http_decode_trailer_bin_active_once(Socket) -> %%-------------------------------------------------------------------- -packet_httph_passive(doc) -> - ["Test setting the packet option {packet, httph}"]; -packet_httph_passive(suite) -> - []; +packet_httph_passive() -> + [{doc,"Test setting the packet option {packet, httph}"}]. + packet_httph_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2123,10 +1254,9 @@ client_http_decode_trailer_passive(Socket) -> ok. %%-------------------------------------------------------------------- -packet_httph_bin_passive(doc) -> - ["Test setting the packet option {packet, httph_bin}"]; -packet_httph_bin_passive(suite) -> - []; +packet_httph_bin_passive() -> + [{doc,"Test setting the packet option {packet, httph_bin}"}]. + packet_httph_bin_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2164,10 +1294,9 @@ client_http_decode_trailer_bin_passive(Socket) -> ok. %%-------------------------------------------------------------------- -packet_line_decode(doc) -> - ["Test setting the packet option {packet, line}, {mode, binary}"]; -packet_line_decode(suite) -> - []; +packet_line_decode() -> + [{doc,"Test setting the packet option {packet, line}, {mode, binary}"}]. + packet_line_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2201,10 +1330,9 @@ packet_line_decode(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -packet_line_decode_list(doc) -> - ["Test setting the packet option {packet, line}, {mode, list}"]; -packet_line_decode_list(suite) -> - []; +packet_line_decode_list() -> + [{doc,"Test setting the packet option {packet, line}, {mode, list}"}]. + packet_line_decode_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2240,10 +1368,9 @@ packet_line_decode_list(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -packet_asn1_decode(doc) -> - ["Test setting the packet option {packet, asn1}"]; -packet_asn1_decode(suite) -> - []; +packet_asn1_decode() -> + [{doc,"Test setting the packet option {packet, asn1}"}]. + packet_asn1_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2276,10 +1403,9 @@ packet_asn1_decode(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_asn1_decode_list(doc) -> - ["Test setting the packet option {packet, asn1}"]; -packet_asn1_decode_list(suite) -> - []; +packet_asn1_decode_list() -> + [{doc,"Test setting the packet option {packet, asn1}"}]. + packet_asn1_decode_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2314,10 +1440,9 @@ packet_asn1_decode_list(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_tpkt_decode(doc) -> - ["Test setting the packet option {packet, tpkt}"]; -packet_tpkt_decode(suite) -> - []; +packet_tpkt_decode() -> + [{doc,"Test setting the packet option {packet, tpkt}"}]. + packet_tpkt_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2347,10 +1472,9 @@ packet_tpkt_decode(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_tpkt_decode_list(doc) -> - ["Test setting the packet option {packet, tpkt}"]; -packet_tpkt_decode_list(suite) -> - []; +packet_tpkt_decode_list() -> + [{doc,"Test setting the packet option {packet, tpkt}"}]. + packet_tpkt_decode_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2381,10 +1505,9 @@ packet_tpkt_decode_list(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -%% packet_fcgi_decode(doc) -> -%% ["Test setting the packet option {packet, fcgi}"]; -%% packet_fcgi_decode(suite) -> -%% []; +%% packet_fcgi_decode() -> +%% [{doc,"Test setting the packet option {packet, fcgi}"}]. + %% packet_fcgi_decode(Config) when is_list(Config) -> %% ClientOpts = ?config(client_opts, Config), %% ServerOpts = ?config(server_opts, Config), @@ -2416,10 +1539,8 @@ packet_tpkt_decode_list(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -packet_sunrm_decode(doc) -> - ["Test setting the packet option {packet, sunrm}"]; -packet_sunrm_decode(suite) -> - []; +packet_sunrm_decode() -> + [{doc,"Test setting the packet option {packet, sunrm}"}]. packet_sunrm_decode(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2449,10 +1570,9 @@ packet_sunrm_decode(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -packet_sunrm_decode_list(doc) -> - ["Test setting the packet option {packet, sunrm}"]; -packet_sunrm_decode_list(suite) -> - []; +packet_sunrm_decode_list() -> + [{doc,"Test setting the packet option {packet, sunrm}"}]. + packet_sunrm_decode_list(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2482,10 +1602,9 @@ packet_sunrm_decode_list(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -header_decode_one_byte_active(doc) -> - ["Test setting the packet option {header, 1}"]; -header_decode_one_byte_active(suite) -> - []; +header_decode_one_byte_active() -> + [{doc,"Test setting the packet option {header, 1}"}]. + header_decode_one_byte_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2516,10 +1635,9 @@ header_decode_one_byte_active(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_active(doc) -> - ["Test setting the packet option {header, 2}"]; -header_decode_two_bytes_active(suite) -> - []; +header_decode_two_bytes_active() -> + [{doc,"Test setting the packet option {header, 2}"}]. + header_decode_two_bytes_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2551,10 +1669,9 @@ header_decode_two_bytes_active(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_two_sent_active(doc) -> - ["Test setting the packet option {header, 2} and sending two byte"]; -header_decode_two_bytes_two_sent_active(suite) -> - []; +header_decode_two_bytes_two_sent_active() -> + [{doc,"Test setting the packet option {header, 2} and sending two byte"}]. + header_decode_two_bytes_two_sent_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2586,10 +1703,9 @@ header_decode_two_bytes_two_sent_active(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_one_sent_active(doc) -> - ["Test setting the packet option {header, 2} and sending one byte"]; -header_decode_two_bytes_one_sent_active(suite) -> - []; +header_decode_two_bytes_one_sent_active() -> + [{doc,"Test setting the packet option {header, 2} and sending one byte"}]. + header_decode_two_bytes_one_sent_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2620,10 +1736,9 @@ header_decode_two_bytes_one_sent_active(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_one_byte_passive(doc) -> - ["Test setting the packet option {header, 1}"]; -header_decode_one_byte_passive(suite) -> - []; +header_decode_one_byte_passive() -> + [{doc,"Test setting the packet option {header, 1}"}]. + header_decode_one_byte_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2654,10 +1769,9 @@ header_decode_one_byte_passive(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_passive(doc) -> - ["Test setting the packet option {header, 2}"]; -header_decode_two_bytes_passive(suite) -> - []; +header_decode_two_bytes_passive() -> + [{doc,"Test setting the packet option {header, 2}"}]. + header_decode_two_bytes_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2689,10 +1803,9 @@ header_decode_two_bytes_passive(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_two_sent_passive(doc) -> - ["Test setting the packet option {header, 2} and sending two byte"]; -header_decode_two_bytes_two_sent_passive(suite) -> - []; +header_decode_two_bytes_two_sent_passive() -> + [{doc,"Test setting the packet option {header, 2} and sending two byte"}]. + header_decode_two_bytes_two_sent_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2724,10 +1837,9 @@ header_decode_two_bytes_two_sent_passive(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_one_sent_passive(doc) -> - ["Test setting the packet option {header, 2} and sending one byte"]; -header_decode_two_bytes_one_sent_passive(suite) -> - []; +header_decode_two_bytes_one_sent_passive() -> + [{doc,"Test setting the packet option {header, 2} and sending one byte"}]. + header_decode_two_bytes_one_sent_passive(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), @@ -2757,7 +1869,30 @@ header_decode_two_bytes_one_sent_passive(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -%% Internal functions +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- +packet(Config, Data, Send, Recv, Quantity, Packet, Active) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, Send ,[Data, Quantity]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, Recv, [Data, Quantity]}}, + {options, [{active, Active}, + {packet, Packet} | + ClientOpts]}]), + + ssl_test_lib:check_result(Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). send_raw(Socket,_, 0) -> ssl:send(Socket, <<>>), @@ -2928,7 +2063,7 @@ client_packet_decode(Socket, [Head | Tail] = Packet) -> client_packet_decode(Socket, [Head], Tail, Packet). client_packet_decode(Socket, P1, P2, Packet) -> - test_server:format("Packet: ~p ~n", [Packet]), + ct:print("Packet: ~p ~n", [Packet]), ok = ssl:send(Socket, P1), ok = ssl:send(Socket, P2), receive diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index c97f97e70b..77ad546420 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -26,79 +26,8 @@ -define(TIMEOUT, 600000). -%% Test server callback functions %%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config) -> - catch crypto:stop(), - try crypto:start() of - ok -> - application:start(public_key), - ssl:start(), - make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config)), - ssl_test_lib:cert_options(Config) - catch _:_ -> - {skip, "Crypto did not start"} - end. -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(_Config) -> - ssl:stop(), - application:stop(crypto). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- -init_per_testcase(_TestCase, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = ssl_test_lib:timetrap(?TIMEOUT), - [{watchdog, Dog} | Config]. - -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- -end_per_testcase(_TestCase, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end. - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -138,6 +67,21 @@ payload_tests() -> client_echos_active_once_huge, client_echos_active_huge]. +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + application:start(public_key), + ssl:start(), + make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config)), + ssl_test_lib:cert_options(Config) + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). init_per_group(GroupName, Config) -> case ssl_test_lib:is_tls_version(GroupName) of @@ -157,15 +101,20 @@ init_per_group(GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +init_per_testcase(_TestCase, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ct:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. -%% Test cases starts here. +end_per_testcase(_TestCase, Config) -> + Config. +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- -server_echos_passive_small(doc) -> - ["Client sends 1000 bytes in passive mode to server, that receives them, " - "sends them back, and closes."]; -server_echos_passive_small(suite) -> - []; +server_echos_passive_small() -> + [{doc, "Client sends 1000 bytes in passive mode to server, that receives them, " + "sends them back, and closes."}]. server_echos_passive_small(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -179,12 +128,9 @@ server_echos_passive_small(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -server_echos_active_once_small(doc) -> - ["Client sends 1000 bytes in active once mode to server, that receives " - " them, sends them back, and closes."]; - -server_echos_active_once_small(suite) -> - []; +server_echos_active_once_small() -> + [{doc, "Client sends 1000 bytes in active once mode to server, that receives " + " them, sends them back, and closes."}]. server_echos_active_once_small(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -198,12 +144,9 @@ server_echos_active_once_small(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -server_echos_active_small(doc) -> - ["Client sends 1000 bytes in active mode to server, that receives them, " - "sends them back, and closes."]; - -server_echos_active_small(suite) -> - []; +server_echos_active_small() -> + [{doc, "Client sends 1000 bytes in active mode to server, that receives them, " + "sends them back, and closes."}]. server_echos_active_small(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -216,12 +159,9 @@ server_echos_active_small(Config) when is_list(Config) -> ClientNode, ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_passive_small(doc) -> - ["Server sends 1000 bytes in passive mode to client, that receives them, " - "sends them back, and closes."]; - -client_echos_passive_small(suite) -> - []; +client_echos_passive_small() -> + [{doc, "Server sends 1000 bytes in passive mode to client, that receives them, " + "sends them back, and closes."}]. client_echos_passive_small(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -234,12 +174,9 @@ client_echos_passive_small(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_active_once_small(doc) -> +client_echos_active_once_small() -> ["Server sends 1000 bytes in active once mode to client, that receives " - "them, sends them back, and closes."]; - -client_echos_active_once_small(suite) -> - []; + "them, sends them back, and closes."]. client_echos_active_once_small(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -252,15 +189,12 @@ client_echos_active_once_small(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_active_small(doc) -> - ["Server sends 1000 bytes in active mode to client, that receives them, " - "sends them back, and closes."]; - -client_echos_active_small(suite) -> - []; +client_echos_active_small() -> + [{doc, "Server sends 1000 bytes in active mode to client, that receives them, " + "sends them back, and closes."}]. client_echos_active_small(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), + ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -271,12 +205,9 @@ client_echos_active_small(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -server_echos_passive_big(doc) -> - ["Client sends 50000 bytes to server in passive mode, that receives them, " - "sends them back, and closes."]; - -server_echos_passive_big(suite) -> - []; +server_echos_passive_big() -> + [{doc, "Client sends 50000 bytes to server in passive mode, that receives them, " + "sends them back, and closes."}]. server_echos_passive_big(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -290,15 +221,12 @@ server_echos_passive_big(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -server_echos_active_once_big(doc) -> - ["Client sends 50000 bytes to server in active once mode, that receives " - "them, sends them back, and closes."]; - -server_echos_active_once_big(suite) -> - []; +server_echos_active_once_big() -> + [{doc,"Client sends 50000 bytes to server in active once mode, that receives " + "them, sends them back, and closes."}]. server_echos_active_once_big(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), + ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -309,12 +237,9 @@ server_echos_active_once_big(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -server_echos_active_big(doc) -> - ["Client sends 50000 bytes to server in active once mode, that receives " - " them, sends them back, and closes."]; - -server_echos_active_big(suite) -> - []; +server_echos_active_big() -> + [{doc, "Client sends 50000 bytes to server in active once mode, that receives " + " them, sends them back, and closes."}]. server_echos_active_big(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -327,12 +252,9 @@ server_echos_active_big(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_passive_big(doc) -> - ["Server sends 50000 bytes to client in passive mode, that receives them, " - "sends them back, and closes."]; - -client_echos_passive_big(suite) -> - []; +client_echos_passive_big() -> + [{doc, "Server sends 50000 bytes to client in passive mode, that receives them, " + "sends them back, and closes."}]. client_echos_passive_big(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -345,12 +267,9 @@ client_echos_passive_big(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_active_once_big(doc) -> - ["Server sends 50000 bytes to client in active once mode, that receives" - " them, sends them back, and closes."]; - -client_echos_active_once_big(suite) -> - []; +client_echos_active_once_big() -> + [{doc, "Server sends 50000 bytes to client in active once mode, that receives" + " them, sends them back, and closes."}]. client_echos_active_once_big(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -363,12 +282,9 @@ client_echos_active_once_big(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_active_big(doc) -> - ["Server sends 50000 bytes to client in active mode, that receives them, " - "sends them back, and closes."]; - -client_echos_active_big(suite) -> - []; +client_echos_active_big() -> + [{doc, "Server sends 50000 bytes to client in active mode, that receives them, " + "sends them back, and closes."}]. client_echos_active_big(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -381,12 +297,9 @@ client_echos_active_big(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -server_echos_passive_huge(doc) -> - ["Client sends 500000 bytes to server in passive mode, that receives " - " them, sends them back, and closes."]; - -server_echos_passive_huge(suite) -> - []; +server_echos_passive_huge() -> + [{doc, "Client sends 500000 bytes to server in passive mode, that receives " + " them, sends them back, and closes."}]. server_echos_passive_huge(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -399,12 +312,9 @@ server_echos_passive_huge(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -server_echos_active_once_huge(doc) -> - ["Client sends 500000 bytes to server in active once mode, that receives " - "them, sends them back, and closes."]; - -server_echos_active_once_huge(suite) -> - []; +server_echos_active_once_huge() -> + [{doc, "Client sends 500000 bytes to server in active once mode, that receives " + "them, sends them back, and closes."}]. server_echos_active_once_huge(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -417,12 +327,9 @@ server_echos_active_once_huge(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -server_echos_active_huge(doc) -> - ["Client sends 500000 bytes to server in active mode, that receives them, " - "sends them back, and closes."]; - -server_echos_active_huge(suite) -> - []; +server_echos_active_huge() -> + [{doc, "Client sends 500000 bytes to server in active mode, that receives them, " + "sends them back, and closes."}]. server_echos_active_huge(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -435,12 +342,9 @@ server_echos_active_huge(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_passive_huge(doc) -> - ["Server sends 500000 bytes to client in passive mode, that receives " - "them, sends them back, and closes."]; - -client_echos_passive_huge(suite) -> - []; +client_echos_passive_huge() -> + [{doc, "Server sends 500000 bytes to client in passive mode, that receives " + "them, sends them back, and closes."}]. client_echos_passive_huge(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -452,12 +356,9 @@ client_echos_passive_huge(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_active_once_huge(doc) -> - ["Server sends 500000 bytes to client in active once mode, that receives " - "them, sends them back, and closes."]; - -client_echos_active_once_huge(suite) -> - []; +client_echos_active_once_huge() -> + [{doc, "Server sends 500000 bytes to client in active once mode, that receives " + "them, sends them back, and closes."}]. client_echos_active_once_huge(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -469,12 +370,9 @@ client_echos_active_once_huge(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- -client_echos_active_huge(doc) -> - ["Server sends 500000 bytes to client in active mode, that receives them, " - "sends them back, and closes."]; - -client_echos_active_huge(suite) -> - []; +client_echos_active_huge() -> + [{doc, "Server sends 500000 bytes to client in active mode, that receives them, " + "sends them back, and closes."}]. client_echos_active_huge(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), @@ -486,6 +384,8 @@ client_echos_active_huge(Config) when is_list(Config) -> ServerNode, Hostname). %%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- server_echos_passive(Data, Length, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname) -> @@ -656,33 +556,33 @@ send(Socket, Data, Size, Repeate,F) -> sender(Socket, Data, Size) -> ok = send(Socket, Data, Size, 100, fun() -> do_recv(Socket, Data, Size, <<>>, false) end), - test_server:format("Sender recv: ~p~n", [ssl:getopts(Socket, [active])]), + ct:print("Sender recv: ~p~n", [ssl:getopts(Socket, [active])]), ok. sender_once(Socket, Data, Size) -> send(Socket, Data, Size, 100, fun() -> do_active_once(Socket, Data, Size, <<>>, false) end), - test_server:format("Sender active once: ~p~n", + ct:print("Sender active once: ~p~n", [ssl:getopts(Socket, [active])]), ok. sender_active(Socket, Data, Size) -> F = fun() -> do_active(Socket, Data, Size, <<>>, false) end, send(Socket, Data, Size, 100, F), - test_server:format("Sender active: ~p~n", [ssl:getopts(Socket, [active])]), + ct:print("Sender active: ~p~n", [ssl:getopts(Socket, [active])]), ok. echoer(Socket, Data, Size) -> - test_server:format("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]), + ct:print("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]), echo(fun() -> do_recv(Socket, Data, Size, <<>>, true) end, 100). echoer_once(Socket, Data, Size) -> - test_server:format("Echoer active once: ~p ~n", + ct:print("Echoer active once: ~p ~n", [ssl:getopts(Socket, [active])]), echo(fun() -> do_active_once(Socket, Data, Size, <<>>, true) end, 100). echoer_active(Socket, Data, Size) -> - test_server:format("Echoer active: ~p~n", [ssl:getopts(Socket, [active])]), + ct:print("Echoer active: ~p~n", [ssl:getopts(Socket, [active])]), echo(fun() -> do_active(Socket, Data, Size, <<>>, true) end, 100). echo(_Fun, 0) -> ok; diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index 1d71efd40c..fd9a0a594c 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2012. 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 @@ -37,18 +37,22 @@ -export([init/1, terminate/1, lookup/2, update/3, delete/2, foldl/3, select_session/2]). -%% Test server callback functions %%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [session_cleanup, + session_cache_process_list, + session_cache_process_mnesia]. + +groups() -> + []. + init_per_suite(Config0) -> - Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), + Dog = ct:timetrap(?LONG_TIMEOUT *2), catch crypto:stop(), try crypto:start() of ok -> @@ -59,7 +63,7 @@ init_per_suite(Config0) -> Result = (catch make_certs:all(?config(data_dir, Config0), ?config(priv_dir, Config0))), - test_server:format("Make certs ~p~n", [Result]), + ct:print("Make certs ~p~n", [Result]), Config1 = ssl_test_lib:make_dsa_cert(Config0), Config = ssl_test_lib:cert_options(Config1), @@ -68,29 +72,16 @@ init_per_suite(Config0) -> {skip, "Crypto did not start"} end. -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- end_per_suite(_Config) -> ssl:stop(), application:stop(crypto). -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + init_per_testcase(session_cache_process_list, Config) -> init_customized_session_cache(list, Config); @@ -100,7 +91,7 @@ init_per_testcase(session_cache_process_mnesia, Config) -> init_per_testcase(session_cleanup, Config0) -> Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), + Dog = ct:timetrap(?TIMEOUT), ssl:stop(), application:load(ssl), application:set_env(ssl, session_lifetime, 5), @@ -110,12 +101,12 @@ init_per_testcase(session_cleanup, Config0) -> init_per_testcase(_TestCase, Config0) -> Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), + Dog = ct:timetrap(?TIMEOUT), [{watchdog, Dog} | Config]. init_customized_session_cache(Type, Config0) -> Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), + Dog = ct:timetrap(?TIMEOUT), ssl:stop(), application:load(ssl), application:set_env(ssl, session_cb, ?MODULE), @@ -123,14 +114,6 @@ init_customized_session_cache(Type, Config0) -> ssl:start(), [{watchdog, Dog} | Config]. -%%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case -%%-------------------------------------------------------------------- end_per_testcase(session_cache_process_list, Config) -> application:unset_env(ssl, session_cb), end_per_testcase(default_action, Config); @@ -146,43 +129,14 @@ end_per_testcase(session_cleanup, Config) -> application:unset_env(ssl, session_lifetime), end_per_testcase(default_action, Config); end_per_testcase(_TestCase, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end. - -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. - -all() -> - [session_cleanup, - session_cache_process_list, - session_cache_process_mnesia]. - -groups() -> - []. - -init_per_group(_GroupName, Config) -> Config. -end_per_group(_GroupName, Config) -> - Config. %%-------------------------------------------------------------------- -session_cleanup(doc) -> - ["Test that sessions are cleand up eventually, so that the session table " - "does not grow and grow ..."]; -session_cleanup(suite) -> - []; +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +session_cleanup() -> + [{doc, "Test that sessions are cleand up eventually, so that the session table " + "does not grow and grow ..."}]. session_cleanup(Config)when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_opts, Config), @@ -207,7 +161,7 @@ session_cleanup(Config)when is_list(Config) -> end, %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), [_, _,_, _, Prop] = StatusInfo, @@ -224,14 +178,14 @@ session_cleanup(Config)when is_list(Config) -> %% Make sure session has expired and been cleaned up check_timer(SessionTimer), - test_server:sleep(?DELAY *2), %% Delay time + some extra time + ct:sleep(?DELAY *2), %% Delay time + some extra time {ServerDelayTimer, ClientDelayTimer} = get_delay_timers(), check_timer(ServerDelayTimer), check_timer(ClientDelayTimer), - test_server:sleep(?SLEEP), %% Make sure clean has had time to run + ct:sleep(?SLEEP), %% Make sure clean has had time to run undefined = ssl_session_cache:lookup(Cache, {{Hostname, Port}, Id}), undefined = ssl_session_cache:lookup(Cache, {Port, Id}), @@ -248,7 +202,7 @@ check_timer(Timer) -> {status, _, _, _} = sys:get_status(whereis(ssl_manager)), ok; Int -> - test_server:sleep(Int), + ct:sleep(Int), check_timer(Timer) end. @@ -258,31 +212,25 @@ get_delay_timers() -> State = ssl_test_lib:state(Prop), case element(7, State) of {undefined, undefined} -> - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), get_delay_timers(); {undefined, _} -> - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), get_delay_timers(); {_, undefined} -> - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), get_delay_timers(); DelayTimers -> DelayTimers end. %%-------------------------------------------------------------------- -session_cache_process_list(doc) -> - ["Test reuse of sessions (short handshake)"]; - -session_cache_process_list(suite) -> - []; +session_cache_process_list() -> + [{doc,"Test reuse of sessions (short handshake)"}]. session_cache_process_list(Config) when is_list(Config) -> session_cache_process(list,Config). %%-------------------------------------------------------------------- -session_cache_process_mnesia(doc) -> - ["Test reuse of sessions (short handshake)"]; - -session_cache_process_mnesia(suite) -> - []; +session_cache_process_mnesia() -> + [{doc,"Test reuse of sessions (short handshake)"}]. session_cache_process_mnesia(Config) when is_list(Config) -> session_cache_process(mnesia,Config). diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index f1f5b9ae0a..76b302b1cb 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -20,8 +20,7 @@ %% -module(ssl_test_lib). --include("test_server.hrl"). --include("test_server_line.hrl"). +-include_lib("common_test/include/ct.hrl"). -include_lib("public_key/include/public_key.hrl"). %% Note: This directive should only be used in test suites. @@ -29,12 +28,6 @@ -record(sslsocket, { fd = nil, pid = nil}). -timetrap(Time) -> - Mul = try - test_server:timetrap_scale_factor() - catch _:_ -> 1 end, - test_server:timetrap(1000+Time*Mul). - %% For now always run locally run_where(_) -> ClientNode = node(), @@ -65,8 +58,9 @@ run_server(Opts) -> Port = proplists:get_value(port, Opts), Options = proplists:get_value(options, Opts), Pid = proplists:get_value(from, Opts), - test_server:format("ssl:listen(~p, ~p)~n", [Port, Options]), - {ok, ListenSocket} = rpc:call(Node, ssl, listen, [Port, Options]), + Transport = proplists:get_value(transport, Opts, ssl), + ct:print("ssl:listen(~p, ~p)~n", [Port, Options]), + {ok, ListenSocket} = rpc:call(Node, Transport, listen, [Port, Options]), Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), run_server(ListenSocket, Opts). @@ -81,14 +75,15 @@ do_run_server(_, {error, timeout} = Result, Opts) -> do_run_server(ListenSocket, AcceptSocket, Opts) -> Node = proplists:get_value(node, Opts), Pid = proplists:get_value(from, Opts), + Transport = proplists:get_value(transport, Opts, ssl), {Module, Function, Args} = proplists:get_value(mfa, Opts), - test_server:format("Server: apply(~p,~p,~p)~n", + ct:print("Server: apply(~p,~p,~p)~n", [Module, Function, [AcceptSocket | Args]]), case rpc:call(Node, Module, Function, [AcceptSocket | Args]) of no_result_msg -> ok; Msg -> - test_server:format("Server Msg: ~p ~n", [Msg]), + ct:print("Server Msg: ~p ~n", [Msg]), Pid ! {self(), Msg} end, receive @@ -97,15 +92,16 @@ do_run_server(ListenSocket, AcceptSocket, Opts) -> {listen, MFA} -> run_server(ListenSocket, [MFA | proplists:delete(mfa, Opts)]); close -> - test_server:format("Server closing ~p ~n", [self()]), - Result = rpc:call(Node, ssl, close, [AcceptSocket], 500), - test_server:format("Result ~p ~n", [Result]); + ct:print("Server closing ~p ~n", [self()]), + Result = rpc:call(Node, Transport, close, [AcceptSocket], 500), + Result1 = rpc:call(Node, Transport, close, [ListenSocket], 500), + ct:print("Result ~p : ~p ~n", [Result, Result1]); {ssl_closed, _} -> ok end. %%% To enable to test with s_client -reconnect -connect(ListenSocket, Opts) -> +connect(#sslsocket{} = ListenSocket, Opts) -> Node = proplists:get_value(node, Opts), ReconnectTimes = proplists:get_value(reconnect_times, Opts, 0), Timeout = proplists:get_value(timeout, Opts, infinity), @@ -116,15 +112,21 @@ connect(ListenSocket, Opts) -> _ -> remove_close_msg(ReconnectTimes), AcceptSocket - end. - + end; +connect(ListenSocket, Opts) -> + Node = proplists:get_value(node, Opts), + ct:print("gen_tcp:accept(~p)~n", [ListenSocket]), + {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, + [ListenSocket]), + AcceptSocket. + connect(_, _, 0, AcceptSocket, _) -> AcceptSocket; connect(ListenSocket, Node, N, _, Timeout) -> - test_server:format("ssl:transport_accept(~p)~n", [ListenSocket]), + ct:print("ssl:transport_accept(~p)~n", [ListenSocket]), {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept, [ListenSocket]), - test_server:format("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, Timeout]), + ct:print("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, Timeout]), case rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Timeout]) of ok -> @@ -157,45 +159,48 @@ run_client(Opts) -> Host = proplists:get_value(host, Opts), Port = proplists:get_value(port, Opts), Pid = proplists:get_value(from, Opts), + Transport = proplists:get_value(transport, Opts, ssl), Options = proplists:get_value(options, Opts), - test_server:format("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]), - case rpc:call(Node, ssl, connect, [Host, Port, Options]) of + ct:print("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]), + case rpc:call(Node, Transport, connect, [Host, Port, Options]) of {ok, Socket} -> Pid ! { connected, Socket }, - test_server:format("Client: connected~n", []), + ct:print("Client: connected~n", []), %% In special cases we want to know the client port, it will %% be indicated by sending {port, 0} in options list! send_selected_port(Pid, proplists:get_value(port, Options), Socket), {Module, Function, Args} = proplists:get_value(mfa, Opts), - test_server:format("Client: apply(~p,~p,~p)~n", + ct:print("Client: apply(~p,~p,~p)~n", [Module, Function, [Socket | Args]]), case rpc:call(Node, Module, Function, [Socket | Args]) of no_result_msg -> ok; Msg -> - test_server:format("Client Msg: ~p ~n", [Msg]), + ct:print("Client Msg: ~p ~n", [Msg]), Pid ! {self(), Msg} end, receive close -> - test_server:format("Client closing~n", []), - rpc:call(Node, ssl, close, [Socket]); + ct:print("Client closing~n", []), + rpc:call(Node, Transport, close, [Socket]); {ssl_closed, Socket} -> + ok; + {gen_tcp, closed} -> ok end; {error, Reason} -> - test_server:format("Client: connection failed: ~p ~n", [Reason]), + ct:print("Client: connection failed: ~p ~n", [Reason]), Pid ! {self(), {error, Reason}} end. close(Pid) -> - test_server:format("Close ~p ~n", [Pid]), + ct:print("Close ~p ~n", [Pid]), Monitor = erlang:monitor(process, Pid), Pid ! close, receive {'DOWN', Monitor, process, Pid, Reason} -> erlang:demonitor(Monitor), - test_server:format("Pid: ~p down due to:~p ~n", [Pid, Reason]) + ct:print("Pid: ~p down due to:~p ~n", [Pid, Reason]) end. check_result(Server, ServerMsg, Client, ClientMsg) -> @@ -207,7 +212,7 @@ check_result(Server, ServerMsg, Client, ClientMsg) -> Unexpected -> Reason = {{expected, {Client, ClientMsg}}, {got, Unexpected}}, - test_server:fail(Reason) + ct:fail(Reason) end; {Client, ClientMsg} -> receive @@ -216,7 +221,7 @@ check_result(Server, ServerMsg, Client, ClientMsg) -> Unexpected -> Reason = {{expected, {Server, ClientMsg}}, {got, Unexpected}}, - test_server:fail(Reason) + ct:fail(Reason) end; {Port, {data,Debug}} when is_port(Port) -> io:format("openssl ~s~n",[Debug]), @@ -225,7 +230,7 @@ check_result(Server, ServerMsg, Client, ClientMsg) -> Unexpected -> Reason = {{expected, {Client, ClientMsg}}, {expected, {Server, ServerMsg}}, {got, Unexpected}}, - test_server:fail(Reason) + ct:fail(Reason) end. check_result(Pid, Msg) -> @@ -238,7 +243,7 @@ check_result(Pid, Msg) -> Unexpected -> Reason = {{expected, {Pid, Msg}}, {got, Unexpected}}, - test_server:fail(Reason) + ct:fail(Reason) end. wait_for_result(Server, ServerMsg, Client, ClientMsg) -> @@ -405,33 +410,33 @@ run_upgrade_server(Opts) -> SslOptions = proplists:get_value(ssl_options, Opts), Pid = proplists:get_value(from, Opts), - test_server:format("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), + ct:print("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), {ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]), Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), - test_server:format("gen_tcp:accept(~p)~n", [ListenSocket]), + ct:print("gen_tcp:accept(~p)~n", [ListenSocket]), {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]), try {ok, SslAcceptSocket} = case TimeOut of infinity -> - test_server:format("ssl:ssl_accept(~p, ~p)~n", + ct:print("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, SslOptions]), rpc:call(Node, ssl, ssl_accept, [AcceptSocket, SslOptions]); _ -> - test_server:format("ssl:ssl_accept(~p, ~p, ~p)~n", + ct:print("ssl:ssl_accept(~p, ~p, ~p)~n", [AcceptSocket, SslOptions, TimeOut]), rpc:call(Node, ssl, ssl_accept, [AcceptSocket, SslOptions, TimeOut]) end, {Module, Function, Args} = proplists:get_value(mfa, Opts), Msg = rpc:call(Node, Module, Function, [SslAcceptSocket | Args]), - test_server:format("Upgrade Server Msg: ~p ~n", [Msg]), + ct:print("Upgrade Server Msg: ~p ~n", [Msg]), Pid ! {self(), Msg}, receive close -> - test_server:format("Upgrade Server closing~n", []), + ct:print("Upgrade Server closing~n", []), rpc:call(Node, ssl, close, [SslAcceptSocket]) end catch error:{badmatch, Error} -> @@ -449,24 +454,24 @@ run_upgrade_client(Opts) -> TcpOptions = proplists:get_value(tcp_options, Opts), SslOptions = proplists:get_value(ssl_options, Opts), - test_server:format("gen_tcp:connect(~p, ~p, ~p)~n", + ct:print("gen_tcp:connect(~p, ~p, ~p)~n", [Host, Port, TcpOptions]), {ok, Socket} = rpc:call(Node, gen_tcp, connect, [Host, Port, TcpOptions]), send_selected_port(Pid, Port, Socket), - test_server:format("ssl:connect(~p, ~p)~n", [Socket, SslOptions]), + ct:print("ssl:connect(~p, ~p)~n", [Socket, SslOptions]), {ok, SslSocket} = rpc:call(Node, ssl, connect, [Socket, SslOptions]), {Module, Function, Args} = proplists:get_value(mfa, Opts), - test_server:format("apply(~p, ~p, ~p)~n", + ct:print("apply(~p, ~p, ~p)~n", [Module, Function, [SslSocket | Args]]), Msg = rpc:call(Node, Module, Function, [SslSocket | Args]), - test_server:format("Upgrade Client Msg: ~p ~n", [Msg]), + ct:print("Upgrade Client Msg: ~p ~n", [Msg]), Pid ! {self(), Msg}, receive close -> - test_server:format("Upgrade Client closing~n", []), + ct:print("Upgrade Client closing~n", []), rpc:call(Node, ssl, close, [SslSocket]) end. @@ -485,20 +490,20 @@ run_upgrade_server_error(Opts) -> SslOptions = proplists:get_value(ssl_options, Opts), Pid = proplists:get_value(from, Opts), - test_server:format("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), + ct:print("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), {ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]), Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), - test_server:format("gen_tcp:accept(~p)~n", [ListenSocket]), + ct:print("gen_tcp:accept(~p)~n", [ListenSocket]), {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]), Error = case TimeOut of infinity -> - test_server:format("ssl:ssl_accept(~p, ~p)~n", + ct:print("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, SslOptions]), rpc:call(Node, ssl, ssl_accept, [AcceptSocket, SslOptions]); _ -> - test_server:format("ssl:ssl_accept(~p, ~p, ~p)~n", + ct:print("ssl:ssl_accept(~p, ~p, ~p)~n", [AcceptSocket, SslOptions, TimeOut]), rpc:call(Node, ssl, ssl_accept, [AcceptSocket, SslOptions, TimeOut]) @@ -517,22 +522,31 @@ run_server_error(Opts) -> Port = proplists:get_value(port, Opts), Options = proplists:get_value(options, Opts), Pid = proplists:get_value(from, Opts), - test_server:format("ssl:listen(~p, ~p)~n", [Port, Options]), - case rpc:call(Node, ssl, listen, [Port, Options]) of - {ok, ListenSocket} -> + Transport = proplists:get_value(transport, Opts, ssl), + ct:print("ssl:listen(~p, ~p)~n", [Port, Options]), + case rpc:call(Node, Transport, listen, [Port, Options]) of + {ok, #sslsocket{} = ListenSocket} -> %% To make sure error_client will %% get {error, closed} and not {error, connection_refused} Pid ! {listen, up}, send_selected_port(Pid, Port, ListenSocket), - test_server:format("ssl:transport_accept(~p)~n", [ListenSocket]), - case rpc:call(Node, ssl, transport_accept, [ListenSocket]) of + ct:print("ssl:transport_accept(~p)~n", [ListenSocket]), + case rpc:call(Node, Transport, transport_accept, [ListenSocket]) of {error, _} = Error -> Pid ! {self(), Error}; {ok, AcceptSocket} -> - test_server:format("ssl:ssl_accept(~p)~n", [AcceptSocket]), + ct:print("ssl:ssl_accept(~p)~n", [AcceptSocket]), Error = rpc:call(Node, ssl, ssl_accept, [AcceptSocket]), Pid ! {self(), Error} end; + {ok, ListenSocket} -> + Pid ! {listen, up}, + send_selected_port(Pid, Port, ListenSocket), + ct:print("~p:accept(~p)~n", [Transport, ListenSocket]), + case rpc:call(Node, Transport, accept, [ListenSocket]) of + {error, _} = Error -> + Pid ! {self(), Error} + end; Error -> %% Not really true but as this is an error test %% this is what we want. @@ -548,9 +562,10 @@ run_client_error(Opts) -> Host = proplists:get_value(host, Opts), Port = proplists:get_value(port, Opts), Pid = proplists:get_value(from, Opts), + Transport = proplists:get_value(transport, Opts, ssl), Options = proplists:get_value(options, Opts), - test_server:format("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]), - Error = rpc:call(Node, ssl, connect, [Host, Port, Options]), + ct:print("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]), + Error = rpc:call(Node, Transport, connect, [Host, Port, Options]), Pid ! {self(), Error}. inet_port(Pid) when is_pid(Pid)-> @@ -577,7 +592,7 @@ trigger_renegotiate(Socket, [ErlData, N]) -> trigger_renegotiate(Socket, ErlData, N, Id). trigger_renegotiate(Socket, _, 0, Id) -> - test_server:sleep(1000), + ct:sleep(1000), case ssl:session_info(Socket) of [{session_id, Id} | _ ] -> fail_session_not_renegotiated; @@ -670,7 +685,7 @@ der_to_pem(File, Entries) -> cipher_result(Socket, Result) -> Result = ssl:connection_info(Socket), - test_server:format("Successfull connect: ~p~n", [Result]), + ct:print("Successfull connect: ~p~n", [Result]), %% Importante to send two packets here %% to properly test "cipher state" handling ssl:send(Socket, "Hello\n"), @@ -751,3 +766,33 @@ sufficient_crypto_support('tlsv1.2') -> end; sufficient_crypto_support(_) -> true. + +send_recv_result_active(Socket) -> + ssl:send(Socket, "Hello world"), + receive + {ssl, Socket, "H"} -> + receive + {ssl, Socket, "ello world"} -> + ok + end; + {ssl, Socket, "Hello world"} -> + ok + end. + +send_recv_result(Socket) -> + ssl:send(Socket, "Hello world"), + {ok,"Hello world"} = ssl:recv(Socket, 11), + ok. + +send_recv_result_active_once(Socket) -> + ssl:send(Socket, "Hello world"), + receive + {ssl, Socket, "H"} -> + ssl:setopts(Socket, [{active, once}]), + receive + {ssl, Socket, "ello world"} -> + ok + end; + {ssl, Socket, "Hello world"} -> + ok + end. diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index f4e19b3f87..d5e7d515fd 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2012. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 @@ -16,7 +16,6 @@ %% %% %CopyrightEnd% %% - %% -module(ssl_to_openssl_SUITE). @@ -34,131 +33,10 @@ -define(OPENSSL_GARBAGE, "P\n"). -define(EXPIRE, 10). -%% Test server callback functions -%%-------------------------------------------------------------------- -%% Function: init_per_suite(Config) -> Config -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Initialization before the whole suite -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%%-------------------------------------------------------------------- -init_per_suite(Config0) -> - Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), - case os:find_executable("openssl") of - false -> - {skip, "Openssl not found"}; - _ -> - catch crypto:stop(), - try crypto:start() of - ok -> - application:start(public_key), - ssl:start(), - Result = - (catch make_certs:all(?config(data_dir, Config0), - ?config(priv_dir, Config0))), - test_server:format("Make certs ~p~n", [Result]), - Config1 = ssl_test_lib:make_dsa_cert(Config0), - Config = ssl_test_lib:cert_options(Config1), - [{watchdog, Dog} | Config] - catch _:_ -> - {skip, "Crypto did not start"} - end - end. - -%%-------------------------------------------------------------------- -%% Function: end_per_suite(Config) -> _ -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after the whole suite -%%-------------------------------------------------------------------- -end_per_suite(_Config) -> - ssl:stop(), - application:stop(crypto). - -%%-------------------------------------------------------------------- -%% Function: init_per_testcase(TestCase, Config) -> Config -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% -%% Description: Initialization before each test case -%% -%% Note: This function is free to add any key/value pairs to the Config -%% variable, but should NOT alter/remove any existing entries. -%% Description: Initialization before each test case -%%-------------------------------------------------------------------- -init_per_testcase(expired_session, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5), - ssl:stop(), - application:load(ssl), - application:set_env(ssl, session_lifetime, ?EXPIRE), - ssl:start(), - [{watchdog, Dog} | Config]; - -init_per_testcase(TestCase, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = ssl_test_lib:timetrap(?TIMEOUT), - special_init(TestCase, [{watchdog, Dog} | Config]). - -special_init(TestCase, Config) - when TestCase == erlang_client_openssl_server_renegotiate; - TestCase == erlang_client_openssl_server_no_wrap_sequence_number; - TestCase == erlang_server_openssl_client_no_wrap_sequence_number - -> - check_sane_openssl_renegotaite(Config); - -special_init(ssl2_erlang_server_openssl_client, Config) -> - check_sane_openssl_sslv2(Config); - -special_init(TestCase, Config) - when TestCase == erlang_client_openssl_server_npn; - TestCase == erlang_server_openssl_client_npn; - TestCase == erlang_server_openssl_client_npn_renegotiate; - TestCase == erlang_client_openssl_server_npn_renegotiate; - TestCase == erlang_server_openssl_client_npn_only_server; - TestCase == erlang_server_openssl_client_npn_only_client; - TestCase == erlang_client_openssl_server_npn_only_client; - TestCase == erlang_client_openssl_server_npn_only_server -> - check_openssl_npn_support(Config); - -special_init(_, Config) -> - Config. - %%-------------------------------------------------------------------- -%% Function: end_per_testcase(TestCase, Config) -> _ -%% Case - atom() -%% Name of the test case that is about to be run. -%% Config - [tuple()] -%% A list of key/value pairs, holding the test case configuration. -%% Description: Cleanup after each test case +%% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- -end_per_testcase(reuse_session_expired, Config) -> - application:unset_env(ssl, session_lifetime), - end_per_testcase(default_action, Config); - -end_per_testcase(default_action, Config) -> - Dog = ?config(watchdog, Config), - case Dog of - undefined -> - ok; - _ -> - test_server:timetrap_cancel(Dog) - end; -end_per_testcase(_, Config) -> - end_per_testcase(default_action, Config). -%%-------------------------------------------------------------------- -%% Function: all(Clause) -> TestCases -%% Clause - atom() - suite | doc -%% TestCases - [Case] -%% Case - atom() -%% Name of a test case. -%% Description: Returns a list of all test cases in this test suite -%%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> @@ -211,6 +89,34 @@ npn_tests() -> erlang_client_openssl_server_npn_only_client, erlang_client_openssl_server_npn_only_server]. + +init_per_suite(Config0) -> + Dog = ct:timetrap(?LONG_TIMEOUT *2), + case os:find_executable("openssl") of + false -> + {skip, "Openssl not found"}; + _ -> + catch crypto:stop(), + try crypto:start() of + ok -> + application:start(public_key), + ssl:start(), + Result = + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), + ct:print("Make certs ~p~n", [Result]), + Config1 = ssl_test_lib:make_dsa_cert(Config0), + Config = ssl_test_lib:cert_options(Config1), + [{watchdog, Dog} | Config] + catch _:_ -> + {skip, "Crypto did not start"} + end + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). + init_per_group(GroupName, Config) -> case ssl_test_lib:is_tls_version(GroupName) of true -> @@ -229,13 +135,55 @@ init_per_group(GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +init_per_testcase(expired_session, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ct:timetrap(?EXPIRE * 1000 * 5), + ssl:stop(), + application:load(ssl), + application:set_env(ssl, session_lifetime, ?EXPIRE), + ssl:start(), + [{watchdog, Dog} | Config]; + +init_per_testcase(TestCase, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ct:timetrap(?TIMEOUT), + special_init(TestCase, [{watchdog, Dog} | Config]). + +special_init(TestCase, Config) + when TestCase == erlang_client_openssl_server_renegotiate; + TestCase == erlang_client_openssl_server_nowrap_seqnum; + TestCase == erlang_server_openssl_client_nowrap_seqnum + -> + check_sane_openssl_renegotaite(Config); + +special_init(ssl2_erlang_server_openssl_client, Config) -> + check_sane_openssl_sslv2(Config); + +special_init(TestCase, Config) + when TestCase == erlang_client_openssl_server_npn; + TestCase == erlang_server_openssl_client_npn; + TestCase == erlang_server_openssl_client_npn_renegotiate; + TestCase == erlang_client_openssl_server_npn_renegotiate; + TestCase == erlang_server_openssl_client_npn_only_server; + TestCase == erlang_server_openssl_client_npn_only_client; + TestCase == erlang_client_openssl_server_npn_only_client; + TestCase == erlang_client_openssl_server_npn_only_server -> + check_openssl_npn_support(Config); + +special_init(_, Config) -> + Config. + +end_per_testcase(reuse_session_expired, Config) -> + application:unset_env(ssl, session_lifetime), + Config; +end_per_testcase(_, Config) -> + Config. -%% Test cases starts here. %%-------------------------------------------------------------------- -basic_erlang_client_openssl_server(doc) -> - ["Test erlang client with openssl server"]; -basic_erlang_client_openssl_server(suite) -> - []; +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +basic_erlang_client_openssl_server() -> + [{doc,"Test erlang client with openssl server"}]. basic_erlang_client_openssl_server(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -252,7 +200,7 @@ basic_erlang_client_openssl_server(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile, - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -271,14 +219,11 @@ basic_erlang_client_openssl_server(Config) when is_list(Config) -> %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -basic_erlang_server_openssl_client(doc) -> - ["Test erlang server with openssl client"]; -basic_erlang_server_openssl_client(suite) -> - []; +basic_erlang_server_openssl_client() -> + [{doc,"Test erlang server with openssl client"}]. basic_erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -296,7 +241,7 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) -> Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ " -host localhost", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), port_command(OpenSslPort, Data), @@ -309,10 +254,8 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, false), ok. %%-------------------------------------------------------------------- -erlang_client_openssl_server(doc) -> - ["Test erlang client with openssl server"]; -erlang_client_openssl_server(suite) -> - []; +erlang_client_openssl_server() -> + [{doc,"Test erlang client with openssl server"}]. erlang_client_openssl_server(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -329,7 +272,7 @@ erlang_client_openssl_server(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile, - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -348,15 +291,11 @@ erlang_client_openssl_server(Config) when is_list(Config) -> %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. - + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_server_openssl_client(doc) -> - ["Test erlang server with openssl client"]; -erlang_server_openssl_client(suite) -> - []; +erlang_server_openssl_client() -> + [{doc,"Test erlang server with openssl client"}]. erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -375,7 +314,7 @@ erlang_server_openssl_client(Config) when is_list(Config) -> Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), port_command(OpenSslPort, Data), @@ -385,15 +324,12 @@ erlang_server_openssl_client(Config) when is_list(Config) -> %% Clean close down! Server needs to be closed first !! ssl_test_lib:close(Server), close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_client_openssl_server_dsa_cert(doc) -> - ["Test erlang server with openssl client"]; -erlang_client_openssl_server_dsa_cert(suite) -> - []; +erlang_client_openssl_server_dsa_cert() -> + [{doc,"Test erlang server with openssl client"}]. erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_dsa_opts, Config), @@ -413,7 +349,7 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile ++ " -key " ++ KeyFile ++ " -Verify 2 -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -436,10 +372,8 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> process_flag(trap_exit, false), ok. %%-------------------------------------------------------------------- -erlang_server_openssl_client_dsa_cert(doc) -> - ["Test erlang server with openssl client"]; -erlang_server_openssl_client_dsa_cert(suite) -> - []; +erlang_server_openssl_client_dsa_cert() -> + [{doc,"Test erlang server with openssl client"}]. erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_dsa_opts, Config), @@ -462,7 +396,7 @@ erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile ++ " -key " ++ KeyFile ++ " -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), port_command(OpenSslPort, Data), @@ -472,16 +406,13 @@ erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> %% Clean close down! Server needs to be closed first !! ssl_test_lib:close(Server), close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_server_openssl_client_reuse_session(doc) -> - ["Test erlang server with openssl client that reconnects with the" - "same session id, to test reusing of sessions."]; -erlang_server_openssl_client_reuse_session(suite) -> - []; +erlang_server_openssl_client_reuse_session() -> + [{doc, "Test erlang server with openssl client that reconnects with the" + "same session id, to test reusing of sessions."}]. erlang_server_openssl_client_reuse_session(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -500,7 +431,7 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) -> Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost -reconnect", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -516,10 +447,8 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -erlang_client_openssl_server_renegotiate(doc) -> - ["Test erlang client when openssl server issuses a renegotiate"]; -erlang_client_openssl_server_renegotiate(suite) -> - []; +erlang_client_openssl_server_renegotiate() -> + [{doc,"Test erlang client when openssl server issuses a renegotiate"}]. erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -538,7 +467,7 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -552,7 +481,7 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> {options, ClientOpts}]), port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), port_command(OpensslPort, OpenSslData), ssl_test_lib:check_result(Client, ok), @@ -565,13 +494,11 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -erlang_client_openssl_server_nowrap_seqnum(doc) -> - ["Test that erlang client will renegotiate session when", +erlang_client_openssl_server_nowrap_seqnum() -> + [{doc, "Test that erlang client will renegotiate session when", "max sequence number celing is about to be reached. Although" "in the testcase we use the test option renegotiate_at" - " to lower treashold substantially."]; -erlang_client_openssl_server_nowrap_seqnum(suite) -> - []; + " to lower treashold substantially."}]. erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -589,7 +516,7 @@ erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -608,17 +535,13 @@ erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) -> %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_server_openssl_client_nowrap_seqnum(doc) -> - ["Test that erlang client will renegotiate session when", +erlang_server_openssl_client_nowrap_seqnum() -> + [{doc, "Test that erlang client will renegotiate session when", "max sequence number celing is about to be reached. Although" "in the testcase we use the test option renegotiate_at" - " to lower treashold substantially."]; - -erlang_server_openssl_client_nowrap_seqnum(suite) -> - []; + " to lower treashold substantially."}]. erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -639,7 +562,7 @@ erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) -> Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -650,16 +573,14 @@ erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) -> %% Clean close down! Server needs to be closed first !! ssl_test_lib:close(Server), close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). + %%-------------------------------------------------------------------- -erlang_client_openssl_server_no_server_ca_cert(doc) -> - ["Test erlang client when openssl server sends a cert chain not" +erlang_client_openssl_server_no_server_ca_cert() -> + [{doc, "Test erlang client when openssl server sends a cert chain not" "including the ca cert. Explicitly test this even if it is" - "implicitly tested eleswhere."]; -erlang_client_openssl_server_no_server_ca_cert(suite) -> - []; + "implicitly tested eleswhere."}]. erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -676,7 +597,7 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -696,14 +617,11 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_client_openssl_server_client_cert(doc) -> - ["Test erlang client with openssl server when client sends cert"]; -erlang_client_openssl_server_client_cert(suite) -> - []; +erlang_client_openssl_server_client_cert() -> + [{doc,"Test erlang client with openssl server when client sends cert"}]. erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), @@ -722,7 +640,7 @@ erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile ++ " -key " ++ KeyFile ++ " -Verify 2", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -741,15 +659,12 @@ erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_server_openssl_client_client_cert(doc) -> - ["Test erlang server with openssl client when client sends cert"]; -erlang_server_openssl_client_client_cert(suite) -> - []; +erlang_server_openssl_client_client_cert() -> + [{doc,"Test erlang server with openssl client when client sends cert"}]. erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), @@ -776,7 +691,7 @@ erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), port_command(OpenSslPort, Data), @@ -786,16 +701,12 @@ erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> %% Clean close down! Server needs to be closed first !! close_port(OpenSslPort), ssl_test_lib:close(Server), - process_flag(trap_exit, false), - ok. - + process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_server_erlang_client_client_cert(doc) -> - ["Test erlang server with erlang client when client sends cert"]; -erlang_server_erlang_client_client_cert(suite) -> - []; +erlang_server_erlang_client_client_cert() -> + [{doc,"Test erlang server with erlang client when client sends cert"}]. erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), @@ -828,30 +739,22 @@ erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. -%%-------------------------------------------------------------------- + process_flag(trap_exit, false). -ciphers_rsa_signed_certs(doc) -> - ["Test cipher suites that uses rsa certs"]; - -ciphers_rsa_signed_certs(suite) -> - []; +%%-------------------------------------------------------------------- +ciphers_rsa_signed_certs() -> + [{doc,"Test cipher suites that uses rsa certs"}]. ciphers_rsa_signed_certs(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:rsa_suites(), run_suites(Ciphers, Version, Config, rsa). +%%-------------------------------------------------------------------- - -ciphers_dsa_signed_certs(doc) -> - ["Test cipher suites that uses dsa certs"]; - -ciphers_dsa_signed_certs(suite) -> - []; - +ciphers_dsa_signed_certs() -> + [{doc,"Test cipher suites that uses dsa certs"}]. ciphers_dsa_signed_certs(Config) when is_list(Config) -> Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), @@ -859,90 +762,9 @@ ciphers_dsa_signed_certs(Config) when is_list(Config) -> Ciphers = ssl_test_lib:dsa_suites(), run_suites(Ciphers, Version, Config, dsa). -run_suites(Ciphers, Version, Config, Type) -> - {ClientOpts, ServerOpts} = - case Type of - rsa -> - {?config(client_opts, Config), - ?config(server_opts, Config)}; - dsa -> - {?config(client_opts, Config), - ?config(server_dsa_opts, Config)} - end, - - Result = lists:map(fun(Cipher) -> - cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end, - Ciphers), - case lists:flatten(Result) of - [] -> - ok; - Error -> - test_server:format("Cipher suite errors: ~p~n", [Error]), - test_server:fail(cipher_suite_failed_see_test_case_log) - end. - -cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> - process_flag(trap_exit, true), - test_server:format("Testing CipherSuite ~p~n", [CipherSuite]), - {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ - " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - - wait_for_openssl_server(), - - ConnectionInfo = {ok, {Version, CipherSuite}}, - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, - {options, - [{ciphers,[CipherSuite]} | - ClientOpts]}]), - - port_command(OpenSslPort, "Hello\n"), - - receive - {Port, {data, _}} when is_port(Port) -> - ok - after 500 -> - test_server:format("Time out on openssl port, check that" - " the messages Hello and world are received" - " during close of port" , []), - ok - end, - - port_command(OpenSslPort, " world\n"), - - Result = ssl_test_lib:wait_for_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - close_port(OpenSslPort), - ssl_test_lib:close(Client), - - Return = case Result of - ok -> - []; - Error -> - [{CipherSuite, Error}] - end, - process_flag(trap_exit, false), - Return. - %%-------------------------------------------------------------------- -erlang_client_bad_openssl_server(doc) -> - [""]; -erlang_client_bad_openssl_server(suite) -> - []; +erlang_client_bad_openssl_server() -> + [{doc,"Test what happens if openssl server sends garbage to erlang ssl client"}]. erlang_client_bad_openssl_server(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), @@ -957,7 +779,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -973,7 +795,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> %% Send garbage port_command(OpensslPort, ?OPENSSL_GARBAGE), - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), Client0 ! server_sent_garbage, @@ -997,13 +819,9 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -expired_session(doc) -> - ["Test our ssl client handling of expired sessions. Will make" - "better code coverage of the ssl_manager module"]; - -expired_session(suite) -> - []; - +expired_session() -> + [{doc, "Test our ssl client handling of expired sessions. Will make" + "better code coverage of the ssl_manager module"}]. expired_session(Config) when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_opts, Config), @@ -1017,7 +835,7 @@ expired_session(Config) when is_list(Config) -> Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -1032,7 +850,7 @@ expired_session(Config) when is_list(Config) -> ssl_test_lib:close(Client0), %% Make sure session is registered - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), Client1 = ssl_test_lib:start_client([{node, ClientNode}, @@ -1042,7 +860,7 @@ expired_session(Config) when is_list(Config) -> ssl_test_lib:close(Client1), %% Make sure session is unregistered due to expiration - test_server:sleep((?EXPIRE+1) * 1000), + ct:sleep((?EXPIRE+1) * 1000), Client2 = ssl_test_lib:start_client([{node, ClientNode}, @@ -1056,10 +874,9 @@ expired_session(Config) when is_list(Config) -> process_flag(trap_exit, false). %%-------------------------------------------------------------------- -ssl2_erlang_server_openssl_client(doc) -> - ["Test that ssl v2 clients are rejected"]; -ssl2_erlang_server_openssl_client(suite) -> - []; +ssl2_erlang_server_openssl_client() -> + [{doc,"Test that ssl v2 clients are rejected"}]. + ssl2_erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -1076,7 +893,7 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) -> Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ " -host localhost -ssl2 -msg", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), port_command(OpenSslPort, Data), @@ -1089,10 +906,9 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, false). %%-------------------------------------------------------------------- -erlang_client_openssl_server_npn(doc) -> - ["Test erlang client with openssl server doing npn negotiation"]; -erlang_client_openssl_server_npn(suite) -> - []; +erlang_client_openssl_server_npn() -> + [{doc,"Test erlang client with openssl server doing npn negotiation"}]. + erlang_client_openssl_server_npn(Config) when is_list(Config) -> Data = "From openssl to erlang", start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) -> @@ -1100,33 +916,25 @@ erlang_client_openssl_server_npn(Config) when is_list(Config) -> ssl_test_lib:check_result(Client, ok) end), - ok. - %%-------------------------------------------------------------------- -erlang_client_openssl_server_npn_renegotiate(doc) -> - ["Test erlang client with openssl server doing npn negotiation and renegotiate"]; -erlang_client_openssl_server_npn_renegotiate(suite) -> - []; +erlang_client_openssl_server_npn_renegotiate() -> + [{doc,"Test erlang client with openssl server doing npn negotiation and renegotiate"}]. + erlang_client_openssl_server_npn_renegotiate(Config) when is_list(Config) -> Data = "From openssl to erlang", start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) -> port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), port_command(OpensslPort, Data), ssl_test_lib:check_result(Client, ok) end), ok. - - %%-------------------------------------------------------------------------- +erlang_server_openssl_client_npn() -> + [{doc,"Test erlang server with openssl client and npn negotiation"}]. - -erlang_server_openssl_client_npn(doc) -> - ["Test erlang server with openssl client and npn negotiation"]; -erlang_server_openssl_client_npn(suite) -> - []; erlang_server_openssl_client_npn(Config) when is_list(Config) -> Data = "From openssl to erlang", @@ -1137,25 +945,23 @@ erlang_server_openssl_client_npn(Config) when is_list(Config) -> ok. %%-------------------------------------------------------------------------- +erlang_server_openssl_client_npn_renegotiate() -> + [{doc,"Test erlang server with openssl client and npn negotiation with renegotiation"}]. -erlang_server_openssl_client_npn_renegotiate(doc) -> - ["Test erlang server with openssl client and npn negotiation with renegotiation"]; -erlang_server_openssl_client_npn_renegotiate(suite) -> - []; erlang_server_openssl_client_npn_renegotiate(Config) when is_list(Config) -> Data = "From openssl to erlang", start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) -> port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), port_command(OpensslPort, Data), ssl_test_lib:check_result(Server, ok) end), ok. %%-------------------------------------------------------------------------- - erlang_client_openssl_server_npn_only_server(Config) when is_list(Config) -> Data = "From openssl to erlang", - start_erlang_client_and_openssl_server_with_opts(Config, [], "-nextprotoneg spdy/2", Data, fun(Server, OpensslPort) -> + start_erlang_client_and_openssl_server_with_opts(Config, [], + "-nextprotoneg spdy/2", Data, fun(Server, OpensslPort) -> port_command(OpensslPort, Data), ssl_test_lib:check_result(Server, ok) end), @@ -1165,7 +971,10 @@ erlang_client_openssl_server_npn_only_server(Config) when is_list(Config) -> erlang_client_openssl_server_npn_only_client(Config) when is_list(Config) -> Data = "From openssl to erlang", - start_erlang_client_and_openssl_server_with_opts(Config, [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}}], "", Data, fun(Server, OpensslPort) -> + start_erlang_client_and_openssl_server_with_opts(Config, + [{client_preferred_next_protocols, + {client, [<<"spdy/2">>], <<"http/1.1">>}}], "", + Data, fun(Server, OpensslPort) -> port_command(OpensslPort, Data), ssl_test_lib:check_result(Server, ok) end), @@ -1174,7 +983,8 @@ erlang_client_openssl_server_npn_only_client(Config) when is_list(Config) -> %%-------------------------------------------------------------------------- erlang_server_openssl_client_npn_only_server(Config) when is_list(Config) -> Data = "From openssl to erlang", - start_erlang_server_and_openssl_client_with_opts(Config, [{next_protocols_advertised, [<<"spdy/2">>]}], "", Data, fun(Server, OpensslPort) -> + start_erlang_server_and_openssl_client_with_opts(Config, [{next_protocols_advertised, [<<"spdy/2">>]}], "", + Data, fun(Server, OpensslPort) -> port_command(OpensslPort, Data), ssl_test_lib:check_result(Server, ok) end), @@ -1182,13 +992,94 @@ erlang_server_openssl_client_npn_only_server(Config) when is_list(Config) -> erlang_server_openssl_client_npn_only_client(Config) when is_list(Config) -> Data = "From openssl to erlang", - start_erlang_server_and_openssl_client_with_opts(Config, [], "-nextprotoneg spdy/2", Data, fun(Server, OpensslPort) -> + start_erlang_server_and_openssl_client_with_opts(Config, [], "-nextprotoneg spdy/2", + Data, fun(Server, OpensslPort) -> port_command(OpensslPort, Data), ssl_test_lib:check_result(Server, ok) end), ok. -%%-------------------------------------------------------------------------- +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- +run_suites(Ciphers, Version, Config, Type) -> + {ClientOpts, ServerOpts} = + case Type of + rsa -> + {?config(client_opts, Config), + ?config(server_opts, Config)}; + dsa -> + {?config(client_opts, Config), + ?config(server_dsa_opts, Config)} + end, + + Result = lists:map(fun(Cipher) -> + cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end, + Ciphers), + case lists:flatten(Result) of + [] -> + ok; + Error -> + ct:print("Cipher suite errors: ~p~n", [Error]), + ct:fail(cipher_suite_failed_see_test_case_log) + end. + +cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> + process_flag(trap_exit, true), + ct:print("Testing CipherSuite ~p~n", [CipherSuite]), + {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ + " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", + + ct:print("openssl cmd: ~p~n", [Cmd]), + + OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + + wait_for_openssl_server(), + + ConnectionInfo = {ok, {Version, CipherSuite}}, + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, + {options, + [{ciphers,[CipherSuite]} | + ClientOpts]}]), + + port_command(OpenSslPort, "Hello\n"), + + receive + {Port, {data, _}} when is_port(Port) -> + ok + after 500 -> + ct:print("Time out on openssl port, check that" + " the messages Hello and world are received" + " during close of port" , []), + ok + end, + + port_command(OpenSslPort, " world\n"), + + Result = ssl_test_lib:wait_for_result(Client, ok), + + %% Clean close down! Server needs to be closed first !! + close_port(OpenSslPort), + ssl_test_lib:close(Client), + + Return = case Result of + ok -> + []; + Error -> + [{CipherSuite, Error}] + end, + process_flag(trap_exit, false), + Return. start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, OpensslServerOpts, Data, Callback) -> process_flag(trap_exit, true), @@ -1209,7 +1100,7 @@ start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, Opens integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile, - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -1248,7 +1139,7 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac Cmd = "openssl s_server -msg -nextprotoneg http/1.1,spdy/2 -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile, - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -1286,7 +1177,7 @@ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callbac Cmd = "openssl s_client -nextprotoneg http/1.0,spdy/2 -msg -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -1297,6 +1188,7 @@ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callbac close_port(OpenSslPort), process_flag(trap_exit, false). + start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenSSLClientOpts, Data, Callback) -> process_flag(trap_exit, true), ServerOpts0 = ?config(server_opts, Config), @@ -1314,7 +1206,7 @@ start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenS Cmd = "openssl s_client " ++ OpenSSLClientOpts ++ " -msg -port " ++ integer_to_list(Port) ++ " -host localhost", - test_server:format("openssl cmd: ~p~n", [Cmd]), + ct:print("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -1333,7 +1225,7 @@ erlang_ssl_receive_and_assert_npn(Socket, Protocol, Data) -> ok. erlang_ssl_receive(Socket, Data) -> - test_server:format("Connection info: ~p~n", + ct:print("Connection info: ~p~n", [ssl:connection_info(Socket)]), receive {ssl, Socket, Data} -> @@ -1347,15 +1239,15 @@ erlang_ssl_receive(Socket, Data) -> io:format("openssl ~s~n",[Debug]), erlang_ssl_receive(Socket,Data); Other -> - test_server:fail({unexpected_message, Other}) + ct:fail({unexpected_message, Other}) after 4000 -> - test_server:fail({did_not_get, Data}) + ct:fail({did_not_get, Data}) end. connection_info(Socket, Version) -> case ssl:connection_info(Socket) of {ok, {Version, _} = Info} -> - test_server:format("Connection info: ~p~n", [Info]), + ct:print("Connection info: ~p~n", [Info]), ok; {ok, {OtherVersion, _}} -> {wrong_version, OtherVersion} @@ -1366,7 +1258,7 @@ connection_info_result(Socket) -> delayed_send(Socket, [ErlData, OpenSslData]) -> - test_server:sleep(?SLEEP), + ct:sleep(?SLEEP), ssl:send(Socket, ErlData), erlang_ssl_receive(Socket, OpenSslData). @@ -1418,7 +1310,7 @@ wait_for_openssl_server() -> %% it will be in accept. Parsing %% output is too error prone. (Even %% more so than sleep!) - test_server:sleep(?SLEEP) + ct:sleep(?SLEEP) end. version_flag(tlsv1) -> diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile index 50f6427eaa..6f1e61e70c 100644 --- a/lib/stdlib/doc/src/Makefile +++ b/lib/stdlib/doc/src/Makefile @@ -101,7 +101,6 @@ XML_REF6_FILES = stdlib_app.xml XML_PART_FILES = part.xml part_notes.xml part_notes_history.xml XML_CHAPTER_FILES = io_protocol.xml unicode_usage.xml notes.xml notes_history.xml -GIF_FILES = ushell1.gif ushell2.gif ushell3.gif BOOK_FILES = book.xml @@ -112,8 +111,7 @@ XML_FILES = \ # ---------------------------------------------------- HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \ - $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html) \ - $(GIF_FILES:%.gif=$(HTMLDIR)/%.gif) + $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html) INFO_FILE = ../../info @@ -138,21 +136,16 @@ SPECS_FLAGS = -I../../include -I../../../kernel/include # ---------------------------------------------------- # Targets # ---------------------------------------------------- -$(HTMLDIR)/%.gif: %.gif - $(INSTALL_DATA) $< $@ - docs: man pdf html $(TOP_PDF_FILE): $(XML_FILES) pdf: $(TOP_PDF_FILE) -html: gifs $(HTML_REF_MAN_FILE) +html: $(HTML_REF_MAN_FILE) man: $(MAN3_FILES) $(MAN6_FILES) -gifs: $(GIF_FILES:%=$(HTMLDIR)/%) - debug opt: clean clean_docs: diff --git a/lib/stdlib/doc/src/epp.xml b/lib/stdlib/doc/src/epp.xml index 386ed89fe1..3e8aba2e5f 100644 --- a/lib/stdlib/doc/src/epp.xml +++ b/lib/stdlib/doc/src/epp.xml @@ -37,6 +37,18 @@ <p>The Erlang code preprocessor includes functions which are used by <c>compile</c> to preprocess macros and include files before the actual parsing takes place.</p> + <p>The Erlang source file <marker + id="encoding"><em>encoding</em></marker> is selected by a + comment in one of the first two lines of the source file. The + first string that matches the regular expression + <c>coding\s*[:=]\s*([-a-zA-Z0-9])+</c> selects the encoding. If + the matching string is not a valid encoding it is ignored. The + valid encodings are <c>Latin-1</c> and <c>UTF-8</c> where the + case of the characters can be chosen freely. Examples:</p> + <pre> +%% coding: utf-8 +%% For this file we have chosen encoding = Latin-1 +%% -*- coding: latin-1 -*-</pre> </description> <datatypes> <datatype> @@ -46,6 +58,9 @@ <name name="epp_handle"></name> <desc><p>Handle to the epp server.</p></desc> </datatype> + <datatype> + <name name="source_encoding"></name> + </datatype> </datatypes> <funcs> <func> @@ -83,6 +98,50 @@ </desc> </func> <func> + <name name="default_encoding" arity="0"/> + <fsummary>Return the default encoding of Erlang source files</fsummary> + <desc> + <p>Returns the default encoding of Erlang source files.</p> + </desc> + </func> + <func> + <name name="encoding_to_string" arity="1"/> + <fsummary>Return a string representation of an encoding</fsummary> + <desc> + <p>Returns a string representation of an encoding. The string + is recognized by <c>read_encoding/1,2</c> and + <c>set_encoding/1</c> as a valid encoding.</p> + </desc> + </func> + <func> + <name name="read_encoding" arity="1"/> + <name name="read_encoding" arity="2"/> + <fsummary>Read the encoding from a file</fsummary> + <desc> + <p>Read the <seealso marker="#encoding">encoding</seealso> from + a file. Returns the read encoding, or <c>none</c> if no + valid encoding was found.</p> + <p>The option <c>in_comment_only</c> is <c>true</c> by + default, which is correct for Erlang source files. If set to + <c>false</c> the encoding string does not necessarily have to + occur in a comment.</p> + </desc> + </func> + <func> + <name name="set_encoding" arity="1"/> + <fsummary>Read and set the encoding of an IO device</fsummary> + <desc> + <p>Reads the <seealso marker="#encoding">encoding</seealso> from + an IO device and sets the encoding of the device + accordingly. The position of the IO device referenced by + <c><anno>File</anno></c> is not affected. If no valid + encoding can be read from the IO device the encoding of the + IO device is set to the default encoding.</p> + <p>Returns the read encoding, or <c>none</c> if no valid + encoding was found.</p> + </desc> + </func> + <func> <name name="format_error" arity="1"/> <fsummary>Format an error descriptor</fsummary> <desc> diff --git a/lib/stdlib/doc/src/erl_pp.xml b/lib/stdlib/doc/src/erl_pp.xml index 57b5828bcd..9ae4f3d91f 100644 --- a/lib/stdlib/doc/src/erl_pp.xml +++ b/lib/stdlib/doc/src/erl_pp.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -63,6 +63,12 @@ breaks and only a space is used as a separator.</p> </desc> </datatype> + <datatype> + <name name="option"/> + </datatype> + <datatype> + <name name="options"/> + </datatype> </datatypes> <funcs> <func> diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml index e6d262466c..22cd45a482 100644 --- a/lib/stdlib/doc/src/io.xml +++ b/lib/stdlib/doc/src/io.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -28,9 +28,9 @@ <rev></rev> </header> <module>io</module> - <modulesummary>Standard IO Server Interface Functions</modulesummary> + <modulesummary>Standard I/O Server Interface Functions</modulesummary> <description> - <p>This module provides an interface to standard Erlang IO servers. + <p>This module provides an interface to standard Erlang I/O servers. The output functions all return <c>ok</c> if they are successful, or exit if they are not.</p> <p>In the following description, all functions have an optional @@ -38,17 +38,16 @@ process which handles the IO protocols. Normally, it is the <c>IoDevice</c> returned by <seealso marker="kernel:file#open/2">file:open/2</seealso>.</p> - <p>For a description of the IO protocols refer to the STDLIB Users Guide.</p> + <p>For a description of the IO protocols refer to the <seealso marker="io_protocol">STDLIB User's Guide</seealso>.</p> <warning> <p>As of R13A, data supplied to the <seealso marker="#put_chars/2">put_chars</seealso> function should be in the <seealso marker="unicode#type-chardata"><c>unicode:chardata()</c></seealso> format. This means that programs supplying binaries to this function need to convert them to UTF-8 - before trying to output the data on an - <c>io_device()</c>.</p> + before trying to output the data on an IO device.</p> - <p>If an io_device() is set in binary mode, the functions <seealso + <p>If an IO device is set in binary mode, the functions <seealso marker="#get_chars/3">get_chars</seealso> and <seealso marker="#get_line/2">get_line</seealso> may return binaries instead of lists. The binaries will, as of R13A, be encoded in @@ -68,9 +67,9 @@ <datatype> <name name="device"/> <desc> - <p>Either <c>standard_io</c>, <c>standard_error</c>, a + <p>An IO device. Either <c>standard_io</c>, <c>standard_error</c>, a registered name, or a pid handling IO protocols (returned from - <seealso marker="kernel:file#open/2">file:open/2</seealso>).</p> + <seealso marker="kernel:file#open/2">file:open/2</seealso>).</p> </desc> </datatype> <datatype> @@ -89,17 +88,14 @@ <name name="format"/> </datatype> <datatype> - <name name="line"/> + <name name="location"/> </datatype> <datatype> <name name="prompt"/> </datatype> <datatype> - <name name="request_error"/> - </datatype> - <datatype> - <name name="error_description"/> - <desc><p>Whatever the I/O-server sends.</p></desc> + <name name="server_no_data"/> + <desc><p>What the I/O-server sends when there is no data.</p></desc> </datatype> </datatypes> @@ -107,11 +103,11 @@ <func> <name name="columns" arity="0"/> <name name="columns" arity="1"/> - <fsummary>Get the number of columns of a device</fsummary> + <fsummary>Get the number of columns of an IO device</fsummary> <desc> <p>Retrieves the number of columns of the <c><anno>IoDevice</anno></c> (i.e. the width of a terminal). The function - only succeeds for terminal devices, for all other devices + only succeeds for terminal devices, for all other IO devices the function returns <c>{error, enotsup}</c></p> </desc> </func> @@ -120,7 +116,7 @@ <name name="put_chars" arity="2"/> <fsummary>Write a list of characters</fsummary> <desc> - <p>Writes the characters of <c><anno>CharData</anno></c> to the io_server() + <p>Writes the characters of <c><anno>CharData</anno></c> to the I/O server (<c><anno>IoDevice</anno></c>).</p> </desc> </func> @@ -135,6 +131,7 @@ <func> <name name="get_chars" arity="2"/> <name name="get_chars" arity="3"/> + <type name="server_no_data"/> <fsummary>Read a specified number of characters</fsummary> <desc> <p>Reads <c><anno>Count</anno></c> characters from standard input @@ -143,19 +140,19 @@ <taglist> <tag><c><anno>Data</anno></c></tag> <item> - <p>The input characters. If the device supports Unicode, + <p>The input characters. If the IO device supports Unicode, the data may represent codepoints larger than 255 (the - latin1 range). If the io_server() is set to deliver + latin1 range). If the I/O server is set to deliver binaries, they will be encoded in UTF-8 (regardless of if - the device actually supports Unicode or not).</p> + the IO device actually supports Unicode or not).</p> </item> <tag><c>eof</c></tag> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error,<anno>Reason</anno>}</c></tag> + <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag> <item> - <p>Other (rare) error condition, for instance <c>{error,estale}</c> + <p>Other (rare) error condition, for instance <c>{error, estale}</c> if reading from an NFS file system.</p> </item> </taglist> @@ -164,6 +161,7 @@ <func> <name name="get_line" arity="1"/> <name name="get_line" arity="2"/> + <type name="server_no_data"/> <fsummary>Read a line</fsummary> <desc> <p>Reads a line from the standard input (<c><anno>IoDevice</anno></c>), @@ -172,19 +170,19 @@ <tag><c><anno>Data</anno></c></tag> <item> <p>The characters in the line terminated by a LF (or end of - file). If the device supports Unicode, + file). If the IO device supports Unicode, the data may represent codepoints larger than 255 (the - latin1 range). If the io_server() is set to deliver + latin1 range). If the I/O server is set to deliver binaries, they will be encoded in UTF-8 (regardless of if - the device actually supports Unicode or not).</p> + the IO device actually supports Unicode or not).</p> </item> <tag><c>eof</c></tag> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error,<anno>Reason</anno>}</c></tag> + <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag> <item> - <p>Other (rare) error condition, for instance <c>{error,estale}</c> + <p>Other (rare) error condition, for instance <c>{error, estale}</c> if reading from an NFS file system.</p> </item> </taglist> @@ -195,7 +193,7 @@ <name name="getopts" arity="1"/> <fsummary>Get the supported options and values from an I/O-server</fsummary> <desc> - <p>This function requests all available options and their current values for a specific io_device(). Example:</p> + <p>This function requests all available options and their current values for a specific IO device. Example:</p> <pre> 1> <input>{ok,F} = file:open("/dev/null",[read]).</input> {ok,<0.42.0>} @@ -217,30 +215,30 @@ <name name="setopts" arity="2"/> <fsummary>Set options</fsummary> <desc> - <p>Set options for the io_device() (<c><anno>IoDevice</anno></c>).</p> + <p>Set options for the standard IO device (<c><anno>IoDevice</anno></c>).</p> <p>Possible options and values vary depending on the actual - io_device(). For a list of supported options and their current values - on a specific device, use the <seealso + IO device. For a list of supported options and their current values + on a specific IO device, use the <seealso marker="#getopts/1">getopts/1</seealso> function.</p> - <p>The options and values supported by the current OTP io_devices are:</p> + <p>The options and values supported by the current OTP IO devices are:</p> <taglist> <tag><c>binary, list or {binary, boolean()}</c></tag> <item> - <p>If set in binary mode (binary or {binary,true}), the io_server() sends binary data (encoded in UTF-8) as answers to the get_line, get_chars and, if possible, get_until requests (see the I/O protocol description in STDLIB User's Guide for details). The immediate effect is that <c>get_chars/2,3</c> and <c>get_line/1,2</c> return UTF-8 binaries instead of lists of chars for the affected device.</p> - <p>By default, all io_devices in OTP are set in list mode, but the io functions can handle any of these modes and so should other, user written, modules behaving as clients to I/O-servers.</p> - <p>This option is supported by the standard shell (group.erl), the 'oldshell' (user.erl) and the file I/O servers.</p> + <p>If set in binary mode (<c>binary</c> or <c>{binary, true}</c>), the I/O server sends binary data (encoded in UTF-8) as answers to the <c>get_line</c>, <c>get_chars</c> and, if possible, <c>get_until</c> requests (see the I/O protocol description in <seealso marker="io_protocol">STDLIB User's Guide</seealso> for details). The immediate effect is that <c>get_chars/2,3</c> and <c>get_line/1,2</c> return UTF-8 binaries instead of lists of chars for the affected IO device.</p> + <p>By default, all IO devices in OTP are set in list mode, but the I/O functions can handle any of these modes and so should other, user written, modules behaving as clients to I/O-servers.</p> + <p>This option is supported by the standard shell (<c>group.erl</c>), the 'oldshell' (<c>user.erl</c>) and the file I/O servers.</p> </item> <tag><c>{echo, boolean()}</c></tag> <item> - <p>Denotes if the terminal should echo input. Only supported for the standard shell I/O-server (group.erl)</p> + <p>Denotes if the terminal should echo input. Only supported for the standard shell I/O-server (<c>group.erl</c>)</p> </item> <tag><c>{expand_fun, expand_fun()}</c></tag> <item> <p>Provide a function for tab-completion (expansion) - like the erlang shell. This function is called - when the user presses the Tab key. The expansion is + like the Erlang shell. This function is called + when the user presses the TAB key. The expansion is active when calling line-reading functions such as <c>get_line/1,2</c>.</p> <p>The function is called with the current line, upto @@ -253,25 +251,25 @@ will be printed and the current input line will be written once again.</p> <p>Trivial example (beep on anything except empty line, which - is expanded to "quit"):</p> + is expanded to <c>"quit"</c>):</p> <code type="none"> fun("") -> {yes, "quit", []}; (_) -> {no, "", ["quit"]} end</code> - <p>This option is supported by the standard shell only (group.erl).</p> + <p>This option is supported by the standard shell only (<c>group.erl</c>).</p> </item> <tag><c>{encoding, latin1 | unicode}</c></tag> <item> - <p>Specifies how characters are input or output from or to the actual device, implying that i.e. a terminal is set to handle Unicode input and output or a file is set to handle UTF-8 data encoding.</p> - <p>The option <em>does not</em> affect how data is returned from the io-functions or how it is sent in the I/O-protocol, it only affects how the io_device() is to handle Unicode characters towards the "physical" device.</p> - <p>The standard shell will be set for either unicode or latin1 encoding when the system is started. The actual encoding is set with the help of the "LANG" or "LC_CTYPE" environment variables on Unix-like system or by other means on other systems. The bottom line is that the user can input Unicode characters and the device will be in {encoding, unicode} mode if the device supports it. The mode can be changed, if the assumption of the runtime system is wrong, by setting this option.</p> - <p>The io_device() used when Erlang is started with the "-oldshell" or "-noshell" flags is by default set to latin1 encoding, meaning that any characters beyond codepoint 255 will be escaped and that input is expected to be plain 8-bit ISO-latin-1. If the encoding is changed to Unicode, input and output from the standard file descriptors will be in UTF-8 (regardless of operating system).</p> - <p>Files can also be set in {encoding, unicode}, meaning that data is written and read as UTF-8. More encodings are possible for files, see below.</p> - <p>{encoding, unicode | latin1} is supported by both the standard shell (group.erl including werl on windows), the 'oldshell' (user.erl) and the file I/O servers.</p> + <p>Specifies how characters are input or output from or to the actual IO device, implying that i.e. a terminal is set to handle Unicode input and output or a file is set to handle UTF-8 data encoding.</p> + <p>The option <em>does not</em> affect how data is returned from the I/O functions or how it is sent in the I/O-protocol, it only affects how the IO device is to handle Unicode characters towards the "physical" device.</p> + <p>The standard shell will be set for either Unicode or latin1 encoding when the system is started. The actual encoding is set with the help of the <c>LANG</c> or <c>LC_CTYPE</c> environment variables on Unix-like system or by other means on other systems. The bottom line is that the user can input Unicode characters and the IO device will be in <c>{encoding, unicode}</c> mode if the IO device supports it. The mode can be changed, if the assumption of the runtime system is wrong, by setting this option.</p> + <p>The IO device used when Erlang is started with the "-oldshell" or "-noshell" flags is by default set to latin1 encoding, meaning that any characters beyond codepoint 255 will be escaped and that input is expected to be plain 8-bit ISO-latin-1. If the encoding is changed to Unicode, input and output from the standard file descriptors will be in UTF-8 (regardless of operating system).</p> + <p>Files can also be set in <c>{encoding, unicode}</c>, meaning that data is written and read as UTF-8. More encodings are possible for files, see below.</p> + <p><c>{encoding, unicode | latin1}</c> is supported by both the standard shell (<c>group.erl</c> including <c>werl</c> on Windows®), the 'oldshell' (<c>user.erl</c>) and the file I/O servers.</p> </item> <tag><c>{encoding, utf8 | utf16 | utf32 | {utf16,big} | {utf16,little} | {utf32,big} | {utf32,little}}</c></tag> <item> <p>For disk files, the encoding can be set to various UTF variants. This will have the effect that data is expected to be read as the specified encoding from the file and the data will be written in the specified encoding to the disk file.</p> - <p>{encoding, utf8} will have the same effect as {encoding,unicode} on files.</p> + <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> </taglist> @@ -289,6 +287,7 @@ <func> <name name="read" arity="1"/> <name name="read" arity="2"/> + <type name="server_no_data"/> <fsummary>Read a term</fsummary> <desc> <p>Reads a term <c><anno>Term</anno></c> from the standard input @@ -312,21 +311,25 @@ </func> <func> <name name="read" arity="3"/> + <name name="read" arity="4"/> + <type name="server_no_data"/> <fsummary>Read a term</fsummary> <desc> <p>Reads a term <c><anno>Term</anno></c> from <c><anno>IoDevice</anno></c>, prompting it - with <c><anno>Prompt</anno></c>. Reading starts at line number - <c><anno>StartLine</anno></c>. It returns:</p> + with <c><anno>Prompt</anno></c>. Reading starts at location + <c><anno>StartLocation</anno></c>. The argument + <c><anno>Options</anno></c> is passed on as the <c>Options</c> + argument of the <c>erl_scan:tokens/4</c> function. It returns:</p> <taglist> - <tag><c>{ok, Term, <anno>EndLine</anno>}</c></tag> + <tag><c>{ok, Term, <anno>EndLocation</anno>}</c></tag> <item> <p>The parsing was successful.</p> </item> - <tag><c>{eof, <anno>EndLine</anno>}</c></tag> + <tag><c>{eof, <anno>EndLocation</anno>}</c></tag> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, <anno>ErrorInfo</anno>, <anno>ErrorLine</anno>}</c></tag> + <tag><c>{error, <anno>ErrorInfo</anno>, <anno>ErrorLocation</anno>}</c></tag> <item> <p>The parsing failed.</p> </item> @@ -377,7 +380,7 @@ ok</pre> applicable, it is used for both the field width and precision. The default padding character is <c>' '</c> (space).</p> <p><c>Mod</c> is the control sequence modifier. It is either a - single character (currently only 't', for unicode translation, + single character (currently only <c>t</c>, for Unicode translation, is supported) that changes the interpretation of Data.</p> <p>The following control sequences are available:</p> @@ -397,9 +400,9 @@ ok</pre> 2> <input>io:fwrite("|~10.5c|~-10.5c|~5c|~n", [$a, $b, $c]).</input> | aaaaa|bbbbb |ccccc| ok</pre> - <p>If the Unicode translation modifier ('t') is in effect, + <p>If the Unicode translation modifier (<c>t</c>) is in effect, the integer argument can be any number representing a - valid unicode codepoint, otherwise it should be an integer + valid Unicode codepoint, otherwise it should be an integer less than or equal to 255, otherwise it is masked with 16#FF:</p> <pre> 1> <input>io:fwrite("~tc~n",[1024]).</input> @@ -439,7 +442,7 @@ ok</pre> <item> <p>Prints the argument with the <c>string</c> syntax. The argument is, if no Unicode translation modifier is present, an - iolist(), a binary, or an atom. If the Unicode translation modifier ('t') is in effect, the argument is unicode:chardata(), meaning that binaries are in UTF-8. The characters + iolist(), a binary, or an atom. If the Unicode translation modifier (<c>t</c>) is in effect, the argument is unicode:chardata(), meaning that binaries are in UTF-8. The characters are printed without quotes. The string is first truncated by the given precision and then padded and justified to the given field width. The default precision is the field width.</p> @@ -476,8 +479,10 @@ ok <p>Writes the data with standard syntax in the same way as <c>~w</c>, but breaks terms whose printed representation is longer than one line into many lines and indents each - line sensibly. It also tries to detect lists of printable - characters and to output these as strings. For example:</p> + line sensibly. It also tries to detect lists of + printable characters and to output these as strings. The + Unicode translation modifier is used for determining + what characters are printable. For example:</p> <pre> 5> <input>T = [{attributes,[[{id,age,1.50000},{mode,explicit},</input> <input>{typename,"INTEGER"}], [{id,cho},{mode,explicit},{typename,'Cho'}]]},</input> @@ -516,6 +521,19 @@ Here T = [{attributes,[[{id,age,1.5}, {tag,{'PRIVATE',3}}, {mode,implicit}] ok</pre> + <p>Binaries that look like UTF-8 encoded strings will be + output with the string syntax if the Unicode translation + modifier is given:</p> + <pre> +9> <input>io:fwrite("~p~n",[[1024]]).</input> +[1024] +10> <input>io:fwrite("~tp~n",[[1024]]).</input> +"\x{400}" +11> <input>io:fwrite("~tp~n", [<<128,128>>]).</input> +<<128,128>> +12> <input>io:fwrite("~tp~n", [<<208,128>>]).</input> +<<"\x{400}"/utf8>> +ok</pre> </item> <tag><c>W</c></tag> <item> @@ -583,7 +601,7 @@ ok</pre> <tag><c>#</c></tag> <item> <p>Like <c>B</c>, but prints the number with an Erlang style - '#'-separated base prefix.</p> + <c>#</c>-separated base prefix.</p> <pre> 16> <input>io:fwrite("~.10#~n", [31]).</input> 10#31 @@ -633,13 +651,14 @@ ok {shell,eval_loop,3}]} in function io:o_request/2</pre> <p>In this example, an attempt was made to output the single - character '65' with the aid of the string formatting directive + character 65 with the aid of the string formatting directive "~s".</p> </desc> </func> <func> <name name="fread" arity="2"/> <name name="fread" arity="3"/> + <type name="server_no_data"/> <fsummary>Read formatted input</fsummary> <desc> <p>Reads characters from the standard input (<c><anno>IoDevice</anno></c>), @@ -664,7 +683,7 @@ ok return suppression character. It provides a method to specify a field which is to be omitted. <c>F</c> is the <c>field width</c> of the input field, <c>M</c> is an optional - translation modifier (of which 't' is the only currently + translation modifier (of which <c>t</c> is the only currently supported, meaning Unicode translation) and <c>C</c> determines the type of control sequence.</p> @@ -690,8 +709,8 @@ ok <tag><c>-</c></tag> <item> <p>An optional sign character is expected. A sign - character '-' gives the return value <c>-1</c>. Sign - character '+' or none gives <c>1</c>. The field width + character <c>-</c> gives the return value <c>-1</c>. Sign + character <c>+</c> or none gives <c>1</c>. The field width parameter is ignored. Leading white-space characters are not skipped.</p> </item> @@ -713,7 +732,7 @@ ok characters are stripped. An Erlang string (list of characters) is returned.</p> - <p>If Unicode translation is in effect (~ts), + <p>If Unicode translation is in effect (<c>~ts</c>), characters larger than 255 are accepted, otherwise not. With the translation modifier, the list returned may as a consequence also contain @@ -769,10 +788,15 @@ Prompt> <input><Character beyond latin1 range not printable in this medium> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, <anno>What</anno>}</c></tag> + <tag><c>{error, <anno>FreadError</anno>}</c></tag> + <item> + <p>The reading failed and <c>FreadError</c> gives a + hint about the error.</p> + </item> + <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag> <item> <p>The read operation failed and the parameter - <c><anno>What</anno></c> gives a hint about the error.</p> + <c><anno>ErrorDescription</anno></c> gives a hint about the error.</p> </item> </taglist> </item> @@ -793,11 +817,11 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in <func> <name name="rows" arity="0"/> <name name="rows" arity="1"/> - <fsummary>Get the number of rows of a device</fsummary> + <fsummary>Get the number of rows of an IO device</fsummary> <desc> <p>Retrieves the number of rows of the <c><anno>IoDevice</anno></c> (i.e. the height of a terminal). The function - only succeeds for terminal devices, for all other devices + only succeeds for terminal devices, for all other IO devices the function returns <c>{error, enotsup}</c></p> </desc> </func> @@ -805,23 +829,28 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in <name name="scan_erl_exprs" arity="1"/> <name name="scan_erl_exprs" arity="2"/> <name name="scan_erl_exprs" arity="3"/> + <name name="scan_erl_exprs" arity="4"/> + <type name="server_no_data"/> <fsummary>Read and tokenize Erlang expressions</fsummary> <desc> <p>Reads data from the standard input (<c>IoDevice</c>), - prompting it with <c>Prompt</c>. Reading starts at line number - <c>StartLine</c> (1). The data is tokenized as if it were a - sequence of Erlang expressions until a final <c>'.'</c> is + prompting it with <c>Prompt</c>. Reading starts at location + <c>StartLocation</c> (<c>1</c>). The argument <c><anno>Options</anno></c> + is passed on as the <c>Options</c> argument of the + <c>erl_scan:tokens/4</c> function. The data is tokenized as if + it were a + sequence of Erlang expressions until a final dot (<c>.</c>) is reached. This token is also returned. It returns:</p> <taglist> - <tag><c>{ok, Tokens, EndLine}</c></tag> + <tag><c>{ok, Tokens, EndLocation}</c></tag> <item> <p>The tokenization succeeded.</p> </item> - <tag><c>{eof, EndLine}</c></tag> + <tag><c>{eof, EndLocation}</c></tag> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, ErrorInfo, ErrorLine}</c></tag> + <tag><c>{error, ErrorInfo, ErrorLocation}</c></tag> <item> <p>An error occurred.</p> </item> @@ -840,13 +869,18 @@ enter><input>1.0er.</input> <name name="scan_erl_form" arity="1"/> <name name="scan_erl_form" arity="2"/> <name name="scan_erl_form" arity="3"/> + <name name="scan_erl_form" arity="4"/> + <type name="server_no_data"/> <fsummary>Read and tokenize an Erlang form</fsummary> <desc> <p>Reads data from the standard input (<c><anno>IoDevice</anno></c>), - prompting it with <c><anno>Prompt</anno></c>. Starts reading at line number - <c><anno>StartLine</anno></c> (1). The data is tokenized as if it were an + prompting it with <c><anno>Prompt</anno></c>. Starts reading + at location <c><anno>StartLocation</anno></c> (<c>1</c>). The + argument <c><anno>Options</anno></c> is passed on as the + <c>Options</c> argument of the <c>erl_scan:tokens/4</c> + function. The data is tokenized as if it were an Erlang form - one of the valid Erlang expressions in an - Erlang source file - until a final <c>'.'</c> is reached. + Erlang source file - until a final dot (<c>.</c>) is reached. This last token is also returned. The return values are the same as for <c>scan_erl_exprs/1,2,3</c> above.</p> </desc> @@ -855,24 +889,30 @@ enter><input>1.0er.</input> <name name="parse_erl_exprs" arity="1"/> <name name="parse_erl_exprs" arity="2"/> <name name="parse_erl_exprs" arity="3"/> + <name name="parse_erl_exprs" arity="4"/> <type name="parse_ret"/> + <type name="server_no_data"/> <fsummary>Read, tokenize and parse Erlang expressions</fsummary> <desc> - <p>Reads data from the standard input (<c><anno>IoDevice</anno></c>), - prompting it with <c><anno>Prompt</anno></c>. Starts reading at line number - <c><anno>StartLine</anno></c> (1). The data is tokenized and parsed as if - it were a sequence of Erlang expressions until a final '.' is - reached. It returns:</p> + <p>Reads data from the standard input + (<c><anno>IoDevice</anno></c>), prompting it with + <c><anno>Prompt</anno></c>. Starts reading at location + <c><anno>StartLocation</anno></c> (<c>1</c>). The argument + <c><anno>Options</anno></c> is passed on as the + <c>Options</c> argument of the <c>erl_scan:tokens/4</c> + function. The data is tokenized and parsed as if it were a + sequence of Erlang expressions until a final dot (<c>.</c>) is reached. + It returns:</p> <taglist> - <tag><c>{ok, ExprList, EndLine}</c></tag> + <tag><c>{ok, ExprList, EndLocation}</c></tag> <item> <p>The parsing was successful.</p> </item> - <tag><c>{eof, EndLine}</c></tag> + <tag><c>{eof, EndLocation}</c></tag> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, ErrorInfo, ErrorLine}</c></tag> + <tag><c>{error, ErrorInfo, ErrorLocation}</c></tag> <item> <p>An error occurred.</p> </item> @@ -891,25 +931,30 @@ enter><input>abc("hey".</input> <name name="parse_erl_form" arity="1"/> <name name="parse_erl_form" arity="2"/> <name name="parse_erl_form" arity="3"/> + <name name="parse_erl_form" arity="4"/> <type name="parse_form_ret"/> + <type name="server_no_data"/> <fsummary>Read, tokenize and parse an Erlang form</fsummary> <desc> <p>Reads data from the standard input (<c><anno>IoDevice</anno></c>), - prompting it with <c><anno>Prompt</anno></c>. Starts reading at line number - <c><anno>StartLine</anno></c> (1). The data is tokenized and parsed as if + prompting it with <c><anno>Prompt</anno></c>. Starts reading at + location <c><anno>StartLocation</anno></c> (<c>1</c>). The argument + <c><anno>Options</anno></c> is passed on as the + <c>Options</c> argument of the <c>erl_scan:tokens/4</c> + function. The data is tokenized and parsed as if it were an Erlang form - one of the valid Erlang expressions - in an Erlang source file - until a final '.' is reached. It + in an Erlang source file - until a final dot (<c>.</c>) is reached. It returns:</p> <taglist> - <tag><c>{ok, AbsForm, EndLine}</c></tag> + <tag><c>{ok, AbsForm, EndLocation}</c></tag> <item> <p>The parsing was successful.</p> </item> - <tag><c>{eof, EndLine}</c></tag> + <tag><c>{eof, EndLocation}</c></tag> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, ErrorInfo, ErrorLine}</c></tag> + <tag><c>{error, ErrorInfo, ErrorLocation}</c></tag> <item> <p>An error occurred.</p> </item> @@ -940,7 +985,7 @@ enter><input>bar.</input> </section> <section> <title>Standard Error</title> - <p>In certain situations, especially when the standard output is redirected, access to an io_server() specific for error messages might be convenient. The io_device 'standard_error' can be used to direct output to whatever the current operating system considers a suitable device for error output. Example on a Unix-like operating system:</p> + <p>In certain situations, especially when the standard output is redirected, access to an I/O-server specific for error messages might be convenient. The IO device <c>standard_error</c> can be used to direct output to whatever the current operating system considers a suitable IO device for error output. Example on a Unix-like operating system:</p> <pre> $ <input>erl -noshell -noinput -eval 'io:format(standard_error,"Error: ~s~n",["error 11"]),'\</input> <input>'init:stop().' > /dev/null</input> @@ -956,7 +1001,7 @@ Error: error 11</pre> <c>ErrorInfo</c> structure which is returned from all IO modules. It has the format:</p> <code type="none"> -{ErrorLine, Module, ErrorDescriptor}</code> +{ErrorLocation, Module, ErrorDescriptor}</code> <p>A string which describes the error is obtained with the following call:</p> <code type="none"> diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml index 506c1792f1..617a6b74fc 100644 --- a/lib/stdlib/doc/src/io_lib.xml +++ b/lib/stdlib/doc/src/io_lib.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -43,6 +43,12 @@ <name name="chars"/> </datatype> <datatype> + <name name="unicode_chars"/> + </datatype> + <datatype> + <name name="unicode_string"/> + </datatype> + <datatype> <name name="continuation"/> <desc><p>A continuation as returned by <seealso marker="#fread/3"><c>fread/3</c></seealso>.</p> </desc> @@ -50,6 +56,9 @@ <datatype> <name name="depth"/> </datatype> + <datatype> + <name name="fread_error"/> + </datatype> </datatypes> <funcs> <func> @@ -209,6 +218,23 @@ </desc> </func> <func> + <name name="write_unicode_string" arity="1"/> + <fsummary>Write a Unicode string</fsummary> + <desc> + <p>Returns the list of characters needed to print + <c><anno>UnicodeString</anno></c> as a string.</p> + </desc> + </func> + <func> + <name name="write_unicode_string_as_latin1" arity="1"/> + <fsummary>Write a Unicode string</fsummary> + <desc> + <p>Returns the list of characters needed to print + <c><anno>UnicodeString</anno></c> as a string. Non-Latin-1 + characters are escaped.</p> + </desc> + </func> + <func> <name name="write_char" arity="1"/> <fsummary>Write a character</fsummary> <desc> @@ -217,6 +243,23 @@ </desc> </func> <func> + <name name="write_unicode_char" arity="1"/> + <fsummary>Write a Unicode character</fsummary> + <desc> + <p>Returns the list of characters needed to print a character + constant in the Unicode character set.</p> + </desc> + </func> + <func> + <name name="write_unicode_char_as_latin1" arity="1"/> + <fsummary>Write a Unicode character</fsummary> + <desc> + <p>Returns the list of characters needed to print a character + constant in the Unicode character set. Non-Latin-1 characters + are escaped.</p> + </desc> + </func> + <func> <name name="indentation" arity="2"/> <fsummary>Indentation after printing string</fsummary> <desc> @@ -233,6 +276,14 @@ </desc> </func> <func> + <name name="unicode_char_list" arity="1"/> + <fsummary>Test for a list of Unicode characters</fsummary> + <desc> + <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of + characters in the Unicode range, otherwise it returns <c>false</c>.</p> + </desc> + </func> + <func> <name name="deep_char_list" arity="1"/> <fsummary>Test for a deep list of characters</fsummary> <desc> @@ -241,6 +292,14 @@ </desc> </func> <func> + <name name="deep_unicode_char_list" arity="1"/> + <fsummary>Test for a deep list of Unicode characters</fsummary> + <desc> + <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a, possibly deep, list + of characters in the Unicode range, otherwise it returns <c>false</c>.</p> + </desc> + </func> + <func> <name name="printable_list" arity="1"/> <fsummary>Test for a list of printable ISO-latin-1 characters</fsummary> <desc> @@ -248,6 +307,14 @@ printable ISO-latin-1 characters, otherwise it returns <c>false</c>.</p> </desc> </func> + <func> + <name name="printable_unicode_list" arity="1"/> + <fsummary>Test for a list of printable Unicode characters</fsummary> + <desc> + <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of + printable Unicode characters, otherwise it returns <c>false</c>.</p> + </desc> + </func> </funcs> </erlref> diff --git a/lib/stdlib/doc/src/io_protocol.xml b/lib/stdlib/doc/src/io_protocol.xml index 0ff3d5c1ee..d36bf2042f 100644 --- a/lib/stdlib/doc/src/io_protocol.xml +++ b/lib/stdlib/doc/src/io_protocol.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1999</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -35,10 +35,10 @@ <p>The I/O-protocol in Erlang specifies a way for a client to communicate -with an io_server and vice versa. The io_server is a process handling the -requests and that performs the requested task on i.e. a device. The +with an I/O server and vice versa. The I/O server is a process that handles +the requests and performs the requested task on e.g. an IO device. The client is any Erlang process wishing to read or write data from/to the -device.</p> +IO device.</p> <p>The common I/O-protocol has been present in OTP since the beginning, but has been fairly undocumented and has also somewhat @@ -53,85 +53,85 @@ implement than the original. It can certainly be argumented that the current protocol is too complex, but this text describes how it looks today, not how it should have looked.</p> -<p>The basic ideas from the original protocol still hold. The io_server +<p>The basic ideas from the original protocol still hold. The I/O server and client communicate with one single, rather simplistic protocol and -no server state is ever present in the client. Any io_server can be +no server state is ever present in the client. Any I/O server can be used together with any client code and client code need not be aware -of the actual device the io_server communicates with.</p> +of the actual IO device the I/O server communicates with.</p> <section> -<title>Protocol basics</title> +<title>Protocol Basics</title> -<p>As described in Robert's paper, servers and clients communicate using -io_request/io_reply tuples as follows:</p> +<p>As described in Robert's paper, I/O servers and clients communicate using +<c>io_request</c>/<c>io_reply</c> tuples as follows:</p> <p><em>{io_request, From, ReplyAs, Request}</em><br/> <em>{io_reply, ReplyAs, Reply}</em></p> -<p>The client sends an io_request to the io_server and the server -eventually sends a corresponding reply.</p> +<p>The client sends an <c>io_request</c> tuple to the I/O server and +the server eventually sends a corresponding <c>io_reply</c> tuple.</p> <list type="bulleted"> -<item>From is the pid() of the client, the process which the io_server -sends the reply to.</item> - -<item>ReplyAs can be any datum and is simply returned in the corresponding -io_reply. The io-module in the Erlang standard library simply uses the pid() -of the io_server as the ReplyAs datum, but a more complicated client -could have several outstanding io-requests to the same server and -would then use i.e. a reference() or something else to differentiate among -the incoming io_reply's. The ReplyAs element should be considered -opaque by the io_server. Note that the pid() of the server is not -explicitly present in the io_reply. The reply can be sent from any -process, not necessarily the actual io_server. The ReplyAs element is -the only thing that connects one io_request with an io_reply.</item> - -<item>Request and Reply are described below.</item> +<item><c>From</c> is the <c>pid()</c> of the client, the process which +the I/O server sends the IO reply to.</item> + +<item><c>ReplyAs</c> can be any datum and is returned in the corresponding +<c>io_reply</c>. The <seealso marker="stdlib:io">io</seealso> module simply uses the pid() +of the I/O server as the <c>ReplyAs</c> datum, but a more complicated client +could have several outstanding I/O requests to the same I/O server and +would then use i.e. a <c>reference()</c> or something else to differentiate among +the incoming IO replies. The <c>ReplyAs</c> element should be considered +opaque by the I/O server. Note that the <c>pid()</c> of the I/O server is not +explicitly present in the <c>io_reply</c> tuple. The reply can be sent from any +process, not necessarily the actual I/O server. The <c>ReplyAs</c> element is +the only thing that connects one I/O request with an I/O-reply.</item> + +<item><c>Request</c> and <c>Reply</c> are described below.</item> </list> -<p>When an io_server receives an io_request, it acts upon the actual -Request part and eventually sends an io_reply with the corresponding -Reply part.</p> +<p>When an I/O server receives an <c>io_request</c> tuple, it acts upon the actual +<c>Request</c> part and eventually sends an <c>io_reply</c> tuple with the corresponding +<c>Reply</c> part.</p> </section> <section> -<title>Output requests</title> +<title>Output Requests</title> -<p>To output characters on a device, the following Requests exist:</p> +<p>To output characters on an IO device, the following <c>Request</c>s exist:</p> <p> <em>{put_chars, Encoding, Characters}</em><br/> <em>{put_chars, Encoding, Module, Function, Args}</em> </p> <list type="bulleted"> -<item>Encoding is either 'unicode' or 'latin1', meaning that the +<item><c>Encoding</c> is either <c>unicode</c> or <c>latin1</c>, meaning that the characters are (in case of binaries) encoded as either UTF-8 or - iso-latin-1 (pure bytes). A well behaved io_server should also - return error if list elements contain integers > 255 when the - Encoding is set to latin1. Note that this does not in any way tell - how characters should be put on the actual device or how the - io_server should handle them. Different io_servers may handle the - characters however they want, this simply tells the io_server which - format the data is expected to have. In the Module/Function/argument - case, the Encoding tells which format the designated function - produces. Note that byte-oriented data is simplest sent using latin1 - Encoding</item> - -<item>Characters are the data to be put on the device. If Encoding is - latin1, this is an iolist(). If Encoding is unicode, this is an - Erlang standard mixed unicode list (one integer in a list per + ISO-latin-1 (pure bytes). A well behaved I/O server should also + return error if list elements contain integers > 255 when + <c>Encoding</c> is set to <c>latin1</c>. Note that this does not in any way tell + how characters should be put on the actual IO device or how the + I/O server should handle them. Different I/O servers may handle the + characters however they want, this simply tells the I/O server which + format the data is expected to have. In the <c>Module</c>/<c>Function</c>/<c>Args</c> + case, <c>Encoding</c> tells which format the designated function + produces. Note that byte-oriented data is simplest sent using the ISO-latin-1 + encoding.</item> + +<item>Characters are the data to be put on the IO device. If <c>Encoding</c> is + <c>latin1</c>, this is an <c>iolist()</c>. If <c>Encoding</c> is <c>unicode</c>, this is an + Erlang standard mixed Unicode list (one integer in a list per character, characters in binaries represented as UTF-8).</item> -<item>Module, Function, Args denotes a function which will be called to - produce the data (like io_lib:format). Args is a list of arguments +<item><c>Module</c>, <c>Function</c>, and <c>Args</c> denote a function which will be called to + produce the data (like <c>io_lib:format/2</c>). <c>Args</c> is a list of arguments to the function. The function should produce data in the given - Encoding. The io_server should call the function as apply(Mod, Func, - Args) and will put the returned data on the device as if it was sent - in a {put_chars, Encoding, Characters} request. If the function + <c>Encoding</c>. The I/O server should call the function as + <c>apply(Mod, Func, Args)</c> and will put the returned data on the IO device as if it was sent + in a <c>{put_chars, Encoding, Characters}</c> request. If the function returns anything else than a binary or list or throws an exception, an error should be sent back to the client.</item> </list> -<p>The server replies to the client with an io_reply where the Reply +<p>The I/O server replies to the client with an <c>io_reply</c> tuple where the <c>Reply</c> element is one of:</p> <p> <em>ok</em><br/> @@ -139,49 +139,50 @@ element is one of:</p> </p> <list type="bulleted"> -<item>Error describes the error to the client, which may do whatever it - wants with it. The Erlang io-module typically returns it as is.</item> +<item><c>Error</c> describes the error to the client, which may do whatever + it wants with it. The Erlang <seealso marker="stdlib:io">io</seealso> + module typically returns it as is.</item> </list> -<p>For backward compatibility the following Requests should also be -handled by an io_server (these messages should not be present after +<p>For backward compatibility the following <c>Request</c>s should also be +handled by an I/O server (these requests should not be present after R15B of OTP):</p> <p> <em>{put_chars, Characters}</em><br/> <em>{put_chars, Module, Function, Args}</em> </p> -<p>These should behave as {put_chars, latin1, Characters} and {put_chars, -latin1, Module, Function, Args} respectively. </p> +<p>These should behave as <c>{put_chars, latin1, Characters}</c> and +<c>{put_chars, latin1, Module, Function, Args}</c> respectively. </p> </section> <section> <title>Input Requests</title> -<p>To read characters from a device, the following Requests exist:</p> +<p>To read characters from an IO device, the following <c>Request</c>s exist:</p> <p><em>{get_until, Encoding, Prompt, Module, Function, ExtraArgs}</em></p> <list type="bulleted"> -<item>Encoding denotes how data is to be sent back to the client and +<item><c>Encoding</c> denotes how data is to be sent back to the client and what data is sent to the function denoted by - Module/Function/ExtraArgs. If the function supplied returns data as a + <c>Module</c>/<c>Function</c>/<c>ExtraArgs</c>. If the function supplied returns data as a list, the data is converted to this encoding. If however the function supplied returns data in some other format, no conversion - can be done and it's up to the client supplied function to return - data in a proper way. If Encoding is latin1, lists of integers + can be done and it is up to the client supplied function to return + data in a proper way. If <c>Encoding</c> is <c>latin1</c>, lists of integers 0..255 or binaries containing plain bytes are sent back to the - client when possible, if Encoding is unicode, lists with integers in - the whole unicode range or binaries encoded in UTF-8 are sent to the + client when possible; if <c>Encoding</c> is <c>unicode</c>, lists with integers in + the whole Unicode range or binaries encoded in UTF-8 are sent to the client. The user supplied function will always see lists of integers, never - binaries, but the list may contain numbers > 255 if the Encoding is - 'unicode'.</item> + binaries, but the list may contain numbers > 255 if the <c>Encoding</c> is + <c>unicode</c>.</item> -<item>Prompt is a list of characters (not mixed, no binaries) or an atom() - to be output as a prompt for input on the device. The Prompt is - often ignored by the io_server and a Prompt set to '' should always - be ignored (and result in nothing being written to the device).</item> +<item><c>Prompt</c> is a list of characters (not mixed, no binaries) or an atom + to be output as a prompt for input on the IO device. <c>Prompt</c> is + often ignored by the I/O server and if set to <c>''</c> it should always + be ignored (and result in nothing being written to the IO device).</item> -<item><p>Module, Function, ExtraArgs denotes a function and arguments to +<item><p><c>Module</c>, <c>Function</c>, and <c>ExtraArgs</c> denote a function and arguments to determine when enough data is written. The function should take two additional arguments, the last state, and a list of characters. The function should return one of:</p> @@ -189,23 +190,23 @@ latin1, Module, Function, Args} respectively. </p> <em>{done, Result, RestChars}</em><br/> <em>{more, Continuation}</em> </p> - <p>The Result can be any Erlang term, but if it is a list(), the - io_server may convert it to a binary() of appropriate format before - returning it to the client, if the server is set in binary mode (see + <p>The <c>Result</c> can be any Erlang term, but if it is a <c>list()</c>, the + I/O server may convert it to a <c>binary()</c> of appropriate format before + returning it to the client, if the I/O server is set in binary mode (see below).</p> - <p>The function will be called with the data the io_server finds on - its device, returning {done, Result, RestChars} when enough data is - read (in which case Result is sent to the client and RestChars are - kept in the io_server as a buffer for subsequent input) or {more, - Continuation}, indicating that more characters are needed to - complete the request. The Continuation will be sent as the state in + <p>The function will be called with the data the I/O server finds on + its IO device, returning <c>{done, Result, RestChars}</c> when enough data is + read (in which case <c>Result</c> is sent to the client and <c>RestChars</c> is + kept in the I/O server as a buffer for subsequent input) or + <c>{more, Continuation}</c>, indicating that more characters are needed to + complete the request. The <c>Continuation</c> will be sent as the state in subsequent calls to the function when more characters are available. When no more characters are available, the function - shall return {done,eof,Rest}. + shall return <c>{done, eof, Rest}</c>. The initial state is the empty list and the data when an - end of file is reached on the device is the atom 'eof'. An emulation - of the get_line request could be (inefficiently) implemented using + end of file is reached on the IO device is the atom <c>eof</c>. An emulation + of the <c>get_line</c> request could be (inefficiently) implemented using the following functions:</p> <code> -module(demo). @@ -214,7 +215,9 @@ latin1, Module, Function, Args} respectively. </p> until_newline(_ThisFar,eof,_MyStopCharacter) -> {done,eof,[]}; until_newline(ThisFar,CharList,MyStopCharacter) -> - case lists:splitwith(fun(X) -> X =/= MyStopCharacter end, CharList) of + case + lists:splitwith(fun(X) -> X =/= MyStopCharacter end, CharList) + of {L,[]} -> {more,ThisFar++L}; {L2,[MyStopCharacter|Rest]} -> @@ -222,45 +225,47 @@ until_newline(ThisFar,CharList,MyStopCharacter) -> end. get_line(IoServer) -> - IoServer ! {io_request, self(), IoServer, {get_until, unicode, '', - ?MODULE, until_newline, [$\n]}}, + IoServer ! {io_request, + self(), + IoServer, + {get_until, unicode, '', ?MODULE, until_newline, [$\n]}}, receive {io_reply, IoServer, Data} -> Data end. </code> - <p>Note especially that the last element in the Request tuple ([$\n]) + <p>Note especially that the last element in the <c>Request</c> tuple (<c>[$\n]</c>) is appended to the argument list when the function is called. The function should be called like - apply(Module, Function, [ State, Data | ExtraArgs ]) by the io_server</p> + <c>apply(Module, Function, [ State, Data | ExtraArgs ])</c> by the I/O server</p> </item> </list> -<p>A defined number of characters is requested using this Request:</p> +<p>A fixed number of characters is requested using this <c>Request</c>:</p> <p> <em>{get_chars, Encoding, Prompt, N}</em> </p> <list type="bulleted"> -<item>Encoding and Prompt as for get_until.</item> +<item><c>Encoding</c> and <c>Prompt</c> as for <c>get_until</c>.</item> -<item>N is the number of characters to be read from the device.</item> +<item><c>N</c> is the number of characters to be read from the IO device.</item> </list> -<p>A single line (like in the example above) is requested with this Request:</p> +<p>A single line (like in the example above) is requested with this <c>Request</c>:</p> <p> <em>{get_line, Encoding, Prompt}</em> </p> <list type="bulleted"> -<item>Encoding and prompt as above.</item> +<item><c>Encoding</c> and <c>Prompt</c> as above.</item> </list> -<p>Obviously, get_chars and get_line could be implemented with the -get_until request (and indeed was originally), but demands for +<p>Obviously, the <c>get_chars</c> and <c>get_line</c> could be implemented with the +<c>get_until</c> request (and indeed they were originally), but demands for efficiency has made these additions necessary.</p> -<p>The server replies to the client with an io_reply where the Reply +<p>The I/O server replies to the client with an <c>io_reply</c> tuple where the <c>Reply</c> element is one of:</p> <p> <em>Data</em><br/> @@ -269,16 +274,17 @@ element is one of:</p> </p> <list type="bulleted"> -<item>Data is the characters read, in either list or binary form - (depending on the io_server mode, see below).</item> -<item>Error describes the error to the client, which may do whatever it - wants with it. The Erlang io-module typically returns it as is.</item> -<item>eof is returned when input end is reached and no more data is +<item><c>Data</c> is the characters read, in either list or binary form + (depending on the I/O server mode, see below).</item> +<item><c>Error</c> describes the error to the client, which may do whatever it + wants with it. The Erlang <seealso marker="stdlib:io">io</seealso> + module typically returns it as is.</item> +<item><c>eof</c> is returned when input end is reached and no more data is available to the client process.</item> </list> -<p>For backward compatibility the following Requests should also be -handled by an io_server (these messages should not be present after +<p>For backward compatibility the following <c>Request</c>s should also be +handled by an I/O server (these reqeusts should not be present after R15B of OTP):</p> <p> @@ -287,30 +293,30 @@ R15B of OTP):</p> <em>{get_line, Prompt}</em><br/> </p> -<p>These should behave as {get_until, latin1, Prompt, Module, Function, -ExtraArgs}, {get_chars, latin1, Prompt, N} and {get_line, latin1, -Prompt} respectively.</p> +<p>These should behave as <c>{get_until, latin1, Prompt, Module, Function, +ExtraArgs}</c>, <c>{get_chars, latin1, Prompt, N}</c> and <c>{get_line, latin1, +Prompt}</c> respectively.</p> </section> <section> -<title>I/O-server modes</title> - -<p>Demands for efficiency when reading data from an io_server has not -only lead to the addition of the get_line and get_chars requests, but -has also added the concept of io_server options. No options are -mandatory to implement, but all io_servers in the Erlang standard -libraries honor the 'binary' option, which allows the Data in the -io_reply to be binary instead of in list form <em>when possible</em>. +<title>I/O-server Modes</title> + +<p>Demands for efficiency when reading data from an I/O server has not +only lead to the addition of the <c>get_line</c> and <c>get_chars</c> requests, but +has also added the concept of I/O server options. No options are +mandatory to implement, but all I/O servers in the Erlang standard +libraries honor the <c>binary</c> option, which allows the <c>Data</c> element of the +<c>io_reply</c> tuple to be a binary instead of a list <em>when possible</em>. If the data is sent as a binary, Unicode data will be sent in the -standard Erlang unicode -format, i.e. UTF-8 (note that the function in get_until still gets -list data regardless of the io_server mode).</p> +standard Erlang Unicode +format, i.e. UTF-8 (note that the function of the <c>get_until</c> request still gets +list data regardless of the I/O server mode).</p> -<p>Note that i.e. the <c>get_until</c> request allows for a function with the data specified as always being a list. Also the return value data from such a function can be of any type (as is indeed the case when an io:fread request is sent to an io_server). The client has to be prepared for data received as answers to those requests to be in a variety of forms, but the server should convert the results to binaries whenever possible (i.e. when the function supplied to get_until actually returns a list). The example shown later in this text does just that.</p> +<p>Note that i.e. the <c>get_until</c> request allows for a function with the data specified as always being a list. Also the return value data from such a function can be of any type (as is indeed the case when an <c>io:fread</c> request is sent to an I/O server). The client has to be prepared for data received as answers to those requests to be in a variety of forms, but the I/O server should convert the results to binaries whenever possible (i.e. when the function supplied to <c>get_until</c> actually returns a list). The example shown later in this text does just that.</p> <p>An I/O-server in binary mode will affect the data sent to the client, so that it has to be able to handle binary data. For convenience, it -is possible to set and retrieve the modes of an io_server using the -following I/O-requests:</p> +is possible to set and retrieve the modes of an I/O server using the +following I/O requests:</p> <p> <em>{setopts, Opts}</em> @@ -318,72 +324,72 @@ following I/O-requests:</p> <list type="bulleted"> -<item>Opts is a list of options in the format recognized by proplists (and - of course by the io_server itself).</item> +<item><c>Opts</c> is a list of options in the format recognized by <seealso marker="stdlib:proplists">proplists</seealso> (and + of course by the I/O server itself).</item> </list> -<p>As an example, the io_server for the interactive shell (in group.erl) +<p>As an example, the I/O server for the interactive shell (in <c>group.erl</c>) understands the following options:</p> <p> -<em>{binary, bool()} (or 'binary'/'list')</em><br/> -<em>{echo, bool()}</em><br/> +<em>{binary, boolean()}</em> (or <em>binary</em>/<em>list</em>)<br/> +<em>{echo, boolean()}</em><br/> <em>{expand_fun, fun()}</em><br/> -<em>{encoding, 'unicode'/'latin1'} (or 'unicode'/'latin1')</em> +<em>{encoding, unicode/latin1}</em> (or <em>unicode</em>/<em>latin1</em>) </p> -<p>- of which the 'binary' and 'encoding' options are common for all -io_servers in OTP, while 'echo' and 'expand' is valid only for this -io_server. It's worth noting that the 'unicode' option notifies how -characters are actually put on the physical device, i.e. if the -terminal per se is unicode aware, it does not affect how characters +<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 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 are sent in the I/O-protocol, where each request contains encoding information for the provided or returned data.</p> -<p>The server should send one of the following as Reply:</p> +<p>The I/O server should send one of the following as <c>Reply</c>:</p> <p> <em>ok</em><br/> <em>{error, Error}</em> </p> -<p>An error (preferably enotsup) is to be expected if the option is -not supported by the io_server (like if an 'echo' option is sent in a -setopt Request to a plain file).</p> +<p>An error (preferably <c>enotsup</c>) is to be expected if the option is +not supported by the I/O server (like if an <c>echo</c> option is sent in a +<c>setopts</c> request to a plain file).</p> -<p>To retrieve options, this message is used:</p> +<p>To retrieve options, this request is used:</p> <p> <em>getopts</em> </p> -<p>The 'getopts' message requests a complete list of all options -supported by the io_server as well as their current values.</p> +<p>The <c>getopts</c> request asks for a complete list of all options +supported by the I/O server as well as their current values.</p> -<p>The server replies:</p> +<p>The I/O server replies:</p> <p> <em>OptList</em><br/> -<em>{error,Error}</em> +<em>{error, Error}</em> </p> <list type="bulleted"> -<item>OptList is a list of tuples {Option, Value} where Option is always +<item><c>OptList</c> is a list of tuples <c>{Option, Value}</c> where <c>Option</c> is always an atom.</item> </list> </section> <section> -<title>Multiple I/O requests</title> +<title>Multiple I/O Requests</title> -<p>The Request element can in itself contain several Requests by using +<p>The <c>Request</c> element can in itself contain several <c>Request</c>s by using the following format:</p> <p> <em>{requests, Requests}</em> </p> <list type="bulleted"> -<item>Requests is a list of valid Request tuples for the protocol, they +<item><c>Requests</c> is a list of valid <c>io_request</c> tuples for the protocol, they shall be executed in the order in which they appear in the list and the execution should continue until one of the requests result in an error or the list is consumed. The result of the last request is sent back to the client.</item> </list> -<p>The server can for a list of requests send any of the valid results in +<p>The I/O server can for a list of requests send any of the valid results in the reply:</p> <p> @@ -395,7 +401,7 @@ the reply:</p> <p>- depending on the actual requests in the list.</p> </section> <section> -<title>Optional I/O-requests</title> +<title>Optional I/O Requests</title> <p>The following I/O request is optional to implement and a client should be prepared for an error return:</p> @@ -403,47 +409,47 @@ should be prepared for an error return:</p> <em>{get_geometry, Geometry}</em> </p> <list type="bulleted"> -<item>Geometry is either the atom 'rows' or the atom 'columns'.</item> +<item><c>Geometry</c> is either the atom <c>rows</c> or the atom <c>columns</c>.</item> </list> -<p>The server should send the Reply as:</p> +<p>The I/O server should send the <c>Reply</c> as:</p> <p> <em>{ok, N}</em><br/> <em>{error, Error}</em> </p> <list type="bulleted"> -<item>N is the number of character rows or columns the device has, if - applicable to the device the io_server handles, otherwise {error, - enotsup} is a good answer.</item> +<item><c>N</c> is the number of character rows or columns the IO device has, if + applicable to the IO device the I/O server handles, otherwise <c>{error, + enotsup}</c> is a good answer.</item> </list> </section> <section> -<title>Unimplemented request types:</title> +<title>Unimplemented Request Types</title> -<p>If an io_server encounters a request it does not recognize (i.e. the -io_request tuple is in the expected format, but the actual Request is -unknown), the server should send a valid reply with the error tuple:</p> +<p>If an I/O server encounters a request it does not recognize (i.e. the +<c>io_request</c> tuple is in the expected format, but the actual <c>Request</c> is +unknown), the I/O server should send a valid reply with the error tuple:</p> <p> <em>{error, request}</em> </p> -<p>This makes it possible to extend the protocol with optional messages +<p>This makes it possible to extend the protocol with optional requests and for the clients to be somewhat backwards compatible.</p> </section> <section> -<title>An annotated and working example io_server:</title> +<title>An Annotated and Working Example I/O Server</title> -<p>An io_server is any process capable of handling the protocol. There is -no generic io_server behavior, but could well be. The framework is +<p>An I/O server is any process capable of handling the I/O protocol. There is +no generic I/O server behavior, but could well be. The framework is simple enough, a process handling incoming requests, usually both -io_requests and other device-specific requests (for i.e. positioning , +I/O-requests and other IO device-specific requests (for i.e. positioning, closing etc.).</p> -<p>Our example io_server stores characters in an ets table, making up a +<p>Our example I/O server stores characters in an ETS table, making up a fairly crude ram-file (it is probably not useful, but working).</p> <p>The module begins with the usual directives, a function to start the -server and a main loop handling the requests:</p> +I/O server and a main loop handling the requests:</p> <code> -module(ets_io_server). @@ -486,18 +492,19 @@ loop(State) -> </code> <p>The main loop receives messages from the client (which might be using -the io-module to send requests). For each request the function -request/2 is called and a reply is eventually sent using the reply/3 +the <seealso marker="stdlib:io">io</seealso> module to send requests). +For each request the function +<c>request/2</c> is called and a reply is eventually sent using the <c>reply/3</c> function.</p> -<p>The "private" message {From, rewind} results in the +<p>The "private" message <c>{From, rewind}</c> results in the current position in the pseudo-file to be reset to 0 (the beginning of -the "file"). This is a typical example of device-specific +the "file"). This is a typical example of IO device-specific messages not being part of the I/O-protocol. It is usually a bad idea -to embed such private messages in io_request tuples, as that might be +to embed such private messages in <c>io_request</c> tuples, as that might be confusing to the reader.</p> -<p>Let's look at the reply function first...</p> +<p>Let us look at the reply function first...</p> <code> @@ -506,8 +513,8 @@ reply(From, ReplyAs, Reply) -> </code> -<p>Simple enough, it sends the io_reply tuple back to the client, -providing the ReplyAs element received in the request along with the +<p>Simple enough, it sends the <c>io_reply</c> tuple back to the client, +providing the <c>ReplyAs</c> element received in the request along with the result of the request, as described above.</p> <p>Now look at the different requests we need to handle. First the @@ -525,18 +532,18 @@ request({put_chars, Encoding, Module, Function, Args}, State) -> end; </code> -<p>The Encoding tells us how the characters in the message are +<p>The <c>Encoding</c> tells us how the characters in the request are represented. We want to store the characters as lists in the -ets-table, so we convert them to lists using the -unicode:characters_to_list/2 function. The conversion function -conveniently accepts the encoding types unicode or latin1, so we can -use the Encoding parameter directly.</p> +ETS table, so we convert them to lists using the +<seealso marker="stdlib:unicode#characters_to_list/2"><c>unicode:characters_to_list/2</c></seealso> function. The conversion function +conveniently accepts the encoding types <c>unicode</c> or <c>latin1</c>, so we can +use <c>Encoding</c> directly.</p> -<p>When Module, Function and Arguments are provided, we simply apply it +<p>When <c>Module</c>, <c>Function</c> and <c>Arguments</c> are provided, we simply apply it and do the same thing with the result as if the data was provided directly.</p> -<p>Let's handle the requests for retrieving data too:</p> +<p>Let us handle the requests for retrieving data too:</p> <code> request({get_until, Encoding, _Prompt, M, F, As}, State) -> @@ -550,11 +557,11 @@ request({get_line, Encoding, _Prompt}, State) -> </code> <p>Here we have cheated a little by more or less only implementing -get_until and using internal helpers to implement get_chars and -get_line. In production code, this might be to inefficient, but that +<c>get_until</c> and using internal helpers to implement <c>get_chars</c> and +<c>get_line</c>. In production code, this might be too inefficient, but that of course depends on the frequency of the different requests. Before -we start actually implementing the functions put_chars/2 and -get_until/5, lets look into the few remaining requests:</p> +we start actually implementing the functions <c>put_chars/2</c> and +<c>get_until/5</c>, let us look into the few remaining requests:</p> <code> request({get_geometry,_}, State) -> @@ -567,18 +574,18 @@ request({requests, Reqs}, State) -> multi_request(Reqs, {ok, ok, State}); </code> -<p>The get_geometry request has no meaning for this io_server, so the -reply will be {error, enotsup}. The only option we handle is the -binary/list option, which is done in separate functions.</p> +<p>The <c>get_geometry</c> request has no meaning for this I/O server, so the +reply will be <c>{error, enotsup}</c>. The only option we handle is the +<c>binary</c>/<c>list</c> option, which is done in separate functions.</p> -<p>The multi-request tag (requests) is handled in a separate loop +<p>The multi-request tag (<c>requests</c>) is handled in a separate loop function applying the requests in the list one after another, returning the last result.</p> -<p>What's left is to handle backward compatibility and the file-module +<p>What is left is to handle backward compatibility and the <seealso marker="kernel:file">file</seealso> module (which uses the old requests until backward compatibility with pre-R13 -nodes is no longer needed). Note that the io_server will not work with -a simple file:write if these are not added:</p> +nodes is no longer needed). Note that the I/O server will not work with +a simple <c>file:write/2</c> if these are not added:</p> <code> request({put_chars,Chars}, State) -> @@ -593,7 +600,7 @@ request({get_until, Prompt,M,F,As}, State) -> request({get_until,latin1,Prompt,M,F,As}, State); </code> -<p>Ok, what's left now is to return {error, request} if the request is +<p>OK, what is left now is to return <c>{error, request}</c> if the request is not recognized:</p> <code> @@ -601,7 +608,7 @@ request(_Other, State) -> {error, {error, request}, State}. </code> -<p>Let's move further and actually handle the different requests, first +<p>Let us move further and actually handle the different requests, first the fairly generic multi-request type:</p> <code> @@ -615,10 +622,10 @@ multi_request([], Result) -> <p>We loop through the requests one at the time, stopping when we either encounter an error or the list is exhausted. The last return value is -sent back to the client (it's first returned to the main loop and then -sent back by the function io_reply).</p> +sent back to the client (it is first returned to the main loop and then +sent back by the function <c>io_reply</c>).</p> -<p>The getopt and setopt requests are also simple to handle, we just +<p>The <c>getopts</c> and <c>setopts</c> requests are also simple to handle, we just change or read our state record:</p> <code> @@ -656,24 +663,24 @@ getopts(#state{mode=M} = S) -> end}],S}. </code> -<p>As a convention, all io_servers handle both {setopts, [binary]}, -{setopts, [list]} and {setopts,[{binary, bool()}]}, hence the trick -with proplists:substitute_negations/2 and proplists:unfold/1. If -invalid options are sent to us, we send {error,enotsup} back to the +<p>As a convention, all I/O servers handle both <c>{setopts, [binary]}</c>, +<c>{setopts, [list]}</c> and <c>{setopts,[{binary, boolean()}]}</c>, hence the trick +with <c>proplists:substitute_negations/2</c> and <c>proplists:unfold/1</c>. If +invalid options are sent to us, we send <c>{error, enotsup}</c> back to the client.</p> -<p>The getopts request should return a list of {Option, Value} tuples, +<p>The <c>getopts</c> request should return a list of <c>{Option, Value}</c> tuples, which has the twofold function of providing both the current values -and the available options of this io_server. We have only one option, +and the available options of this I/O server. We have only one option, and hence return that.</p> -<p>So far our io_server has been fairly generic (except for the rewind -request handled in the main loop and the creation of an ets table). -Most io_servers contain code similar to what's above.</p> +<p>So far our I/O server has been fairly generic (except for the <c>rewind</c> +request handled in the main loop and the creation of an ETS table). +Most I/O servers contain code similar to the one above.</p> <p>To make the example runnable, we now start implementing the actual -reading and writing of the data to/from the ets-table. First the -put_chars function:</p> +reading and writing of the data to/from the ETS table. First the +<c>put_chars/3</c> function:</p> <code> put_chars(Chars, #state{table = T, position = P} = State) -> @@ -686,10 +693,10 @@ put_chars(Chars, #state{table = T, position = P} = State) -> <p>We already have the data as (Unicode) lists and therefore just split the list in runs of a predefined size and put each run in the table at the current position (and forward). The functions -split_data/3 and apply_update/2 are implemented below.</p> +<c>split_data/3</c> and <c>apply_update/2</c> are implemented below.</p> -<p>Now we want to read data from the table. The get_until function reads -data and applies the function until it says it's done. The result is +<p>Now we want to read data from the table. The <c>get_until/5</c> function reads +data and applies the function until it says it is done. The result is sent back to the client:</p> <code> @@ -700,11 +707,12 @@ get_until(Encoding, Mod, Func, As, if M =:= binary -> {ok, - unicode:characters_to_binary(Data,unicode,Encoding), + unicode:characters_to_binary(Data, unicode, Encoding), State#state{position = NewP}}; true -> case check(Encoding, - unicode:characters_to_list(Data, unicode)) of + unicode:characters_to_list(Data, unicode)) + of {error, _} = E -> {error, E, State}; List -> @@ -730,24 +738,24 @@ get_loop(M,F,A,T,P,C) -> end. </code> -<p>Here we also handle the mode (binary or list) that can be set by -the setopts request. By default, all OTP io_servers send data back to -the client as lists, but switching mode to binary might increase -efficiency if the server handles it in an appropriate way. The -implementation of get_until is hard to get efficient as the supplied -function is defined to take lists as arguments, but get_chars and -get_line can be optimized for binary mode. This example does not +<p>Here we also handle the mode (<c>binary</c> or <c>list</c>) that can be set by +the <c>setopts</c> request. By default, all OTP I/O servers send data back to +the client as lists, but switching mode to <c>binary</c> might increase +efficiency if the I/O server handles it in an appropriate way. The +implementation of <c>get_until</c> is hard to get efficient as the supplied +function is defined to take lists as arguments, but <c>get_chars</c> and +<c>get_line</c> can be optimized for binary mode. This example does not optimize anything however. It is important though that the returned data is of the right type depending on the options set, so we convert the lists to binaries in the correct encoding <em>if possible</em> -before returning. The function supplied in the get_until request may, +before returning. The function supplied in the <c>get_until</c> request tuple may, as its final result return anything, so only functions actually returning lists can get them converted to binaries. If the request -contained the encoding tag unicode, the lists can contain all unicode +contained the encoding tag <c>unicode</c>, the lists can contain all Unicode codepoints and the binaries should be in UTF-8, if the encoding tag -was latin1, the client should only get characters in the range -0..255. The function check/2 takes care of not returning arbitrary -unicode codepoints in lists if the encoding was given as latin1. If +was <c>latin1</c>, the client should only get characters in the range +0..255. The function <c>check/2</c> takes care of not returning arbitrary +Unicode codepoints in lists if the encoding was given as <c>latin1</c>. If the function did not return a list, the check cannot be performed and the result will be that of the supplied function untouched.</p> @@ -768,13 +776,13 @@ check(latin1, List) -> end. </code> -<p>The function check takes care of providing an error tuple if unicode +<p>The function check takes care of providing an error tuple if Unicode codepoints above 255 is to be returned if the client requested latin1.</p> -<p>The two functions until_newline/3 and until_enough/3 are helpers used -together with the get_until function to implement get_chars and -get_line (inefficiently):</p> +<p>The two functions <c>until_newline/3</c> and <c>until_enough/3</c> are helpers used +together with the <c>get_until/5</c> function to implement <c>get_chars</c> and +<c>get_line</c> (inefficiently):</p> <code> until_newline([],eof,_MyStopCharacter) -> @@ -782,7 +790,9 @@ until_newline([],eof,_MyStopCharacter) -> until_newline(ThisFar,eof,_MyStopCharacter) -> {done,ThisFar,[]}; until_newline(ThisFar,CharList,MyStopCharacter) -> - case lists:splitwith(fun(X) -> X =/= MyStopCharacter end, CharList) of + case + lists:splitwith(fun(X) -> X =/= MyStopCharacter end, CharList) + of {L,[]} -> {more,ThisFar++L}; {L2,[MyStopCharacter|Rest]} -> @@ -802,10 +812,10 @@ until_enough(ThisFar,CharList,_N) -> </code> <p>As can be seen, the functions above are just the type of functions -that should be provided in get_until requests.</p> +that should be provided in <c>get_until</c> requests.</p> <p>Now we only need to read and write the table in an appropriate way to -complete the server:</p> +complete the I/O server:</p> <code> get(P,Tab) -> @@ -847,13 +857,13 @@ apply_update(Table, {Row, Col, List}) -> end. </code> -<p>The table is read or written in chunks of ?CHARS_PER_REC, overwriting +<p>The table is read or written in chunks of <c>?CHARS_PER_REC</c>, overwriting when necessary. The implementation is obviously not efficient, it is just working.</p> <p>This concludes the example. It is fully runnable and you can read or -write to the io_server by using i.e. the io_module or even the file -module. It's as simple as that to implement a fully fledged io_server +write to the I/O server by using i.e. the <seealso marker="stdlib:io">io</seealso> module or even the <seealso marker="kernel:file">file</seealso> +module. It is as simple as that to implement a fully fledged I/O server in Erlang.</p> </section> </chapter> diff --git a/lib/stdlib/doc/src/proplists.xml b/lib/stdlib/doc/src/proplists.xml index 225c5e97eb..8d64319344 100644 --- a/lib/stdlib/doc/src/proplists.xml +++ b/lib/stdlib/doc/src/proplists.xml @@ -70,7 +70,7 @@ <fsummary></fsummary> <desc> <p>Minimizes the representation of all entries in the list. This is - equivalent to <c><![CDATA[[property(P) || P <- List]]]></c>.</p> + equivalent to <c><![CDATA[[property(P) || P <- ListIn]]]></c>.</p> <p>See also: <c>property/1</c>, <c>unfold/1</c>.</p> </desc> </func> @@ -88,11 +88,11 @@ <desc> <p>Expands particular properties to corresponding sets of properties (or other terms). For each pair <c>{<anno>Property</anno>, <anno>Expansion</anno>}</c> in <c><anno>Expansions</anno></c>, if <c>E</c> is - the first entry in <c><anno>List</anno></c> with the same key as + the first entry in <c><anno>ListIn</anno></c> with the same key as <c><anno>Property</anno></c>, and <c>E</c> and <c><anno>Property</anno></c> have equivalent normal forms, then <c>E</c> is replaced with the terms in <c><anno>Expansion</anno></c>, and any following entries with - the same key are deleted from <c><anno>List</anno></c>.</p> + the same key are deleted from <c><anno>ListIn</anno></c>.</p> <p>For example, the following expressions all return <c>[fie, bar, baz, fum]</c>:</p> <code type="none"> expand([{foo, [bar, baz]}], @@ -198,7 +198,7 @@ <name name="normalize" arity="2"/> <fsummary></fsummary> <desc> - <p>Passes <c><anno>List</anno></c> through a sequence of + <p>Passes <c><anno>ListIn</anno></c> through a sequence of substitution/expansion stages. For an <c>aliases</c> operation, the function <c>substitute_aliases/2</c> is applied using the given list of aliases; for a <c>negations</c> operation, @@ -221,9 +221,9 @@ <fsummary></fsummary> <desc> <p>Creates a normal form (minimal) representation of a property. If - <c><anno>Property</anno></c> is <c>{Key, true}</c> where <c>Key</c> is - an atom, this returns <c>Key</c>, otherwise the whole term - <c><anno>Property</anno></c> is returned.</p> + <c><anno>PropertyIn</anno></c> is <c>{Key, true}</c> where + <c>Key</c> is an atom, this returns <c>Key</c>, otherwise + the whole term <c><anno>PropertyIn</anno></c> is returned.</p> <p>See also: <c>property/2</c>.</p> </desc> </func> @@ -260,7 +260,7 @@ <fsummary></fsummary> <desc> <p>Substitutes keys of properties. For each entry in - <c><anno>List</anno></c>, if it is associated with some key <c>K1</c> + <c><anno>ListIn</anno></c>, if it is associated with some key <c>K1</c> such that <c>{K1, K2}</c> occurs in <c><anno>Aliases</anno></c>, the key of the entry is changed to <c>K2</c>. If the same <c>K1</c> occurs more than once in <c><anno>Aliases</anno></c>, only @@ -278,13 +278,13 @@ <desc> <p>Substitutes keys of boolean-valued properties and simultaneously negates their values. For each entry in - <c><anno>List</anno></c>, if it is associated with some key <c>K1</c> + <c><anno>ListIn</anno></c>, if it is associated with some key <c>K1</c> such that <c>{K1, K2}</c> occurs in <c><anno>Negations</anno></c>, then if the entry was <c>{K1, true}</c> it will be replaced with <c>{K2, false}</c>, otherwise it will be replaced with <c>{K2, true}</c>, thus changing the name of the option and simultaneously negating the value given by - <c>get_bool(List)</c>. If the same <c>K1</c> occurs more + <c>get_bool(ListIn)</c>. If the same <c>K1</c> occurs more than once in <c><anno>Negations</anno></c>, only the first occurrence is used.</p> <p>Example: <c>substitute_negations([{no_foo, foo}], L)</c> @@ -300,7 +300,7 @@ <name name="unfold" arity="1"/> <fsummary></fsummary> <desc> - <p>Unfolds all occurrences of atoms in <c><anno>List</anno></c> to tuples + <p>Unfolds all occurrences of atoms in <c><anno>ListIn</anno></c> to tuples <c>{Atom, true}</c>.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml index 2211bfb925..71a6e34513 100644 --- a/lib/stdlib/doc/src/re.xml +++ b/lib/stdlib/doc/src/re.xml @@ -96,12 +96,12 @@ subjects during the program's lifetime. Compiling once and executing many times is far more efficient than compiling each time one wants to match.</p> - <p>When the unicode option is given, the regular expression should be given as a valid unicode <c>charlist()</c>, otherwise as any valid <c>iodata()</c>.</p> + <p>When the unicode option is given, the regular expression should be given as a valid Unicode <c>charlist()</c>, otherwise as any valid <c>iodata()</c>.</p> <p><marker id="compile_options"/>The options have the following meanings:</p> <taglist> <tag><c>unicode</c></tag> - <item>The regular expression is given as a unicode <c>charlist()</c> and the resulting regular expression code is to be run against a valid unicode <c>charlist()</c> subject.</item> + <item>The regular expression is given as a Unicode <c>charlist()</c> and the resulting regular expression code is to be run against a valid Unicode <c>charlist()</c> subject.</item> <tag><c>anchored</c></tag> <item>The pattern is forced to be "anchored", that is, it is constrained to match only at the first matching point in the string that is being searched (the "subject string"). This effect can also be achieved by appropriate constructs in the pattern itself.</item> <tag><c>caseless</c></tag> @@ -478,7 +478,7 @@ This option makes it possible to include comments inside complicated patterns. N <p>Replaces the matched part of the <c><anno>Subject</anno></c> string with the contents of <c><anno>Replacement</anno></c>.</p> <p>The permissible options are the same as for <c>re:run/3</c>, except that the <c>capture</c> option is not allowed. Instead a <c>{return, <anno>ReturnType</anno>}</c> is present. The default return type is <c>iodata</c>, constructed in a - way to minimize copying. The <c>iodata</c> result can be used directly in many i/o-operations. If a flat <c>list()</c> is + way to minimize copying. The <c>iodata</c> result can be used directly in many I/O-operations. If a flat <c>list()</c> is desired, specify <c>{return, list}</c> and if a binary is preferred, specify <c>{return, binary}</c>.</p> <p>As in the <c>re:run/3</c> function, an <c>mp()</c> compiled diff --git a/lib/stdlib/doc/src/unicode.xml b/lib/stdlib/doc/src/unicode.xml index 1f6cbaccd7..d235f3e180 100644 --- a/lib/stdlib/doc/src/unicode.xml +++ b/lib/stdlib/doc/src/unicode.xml @@ -32,9 +32,9 @@ <module>unicode</module> <modulesummary>Functions for converting Unicode characters</modulesummary> <description> - <p>This module contains functions for converting between different character representations. Basically it converts between iso-latin-1 characters and Unicode ditto, but it can also convert between different Unicode encodings (like UTF-8, UTF-16 and UTF-32).</p> + <p>This module contains functions for converting between different character representations. Basically it converts between ISO-latin-1 characters and Unicode ditto, but it can also convert between different Unicode encodings (like UTF-8, UTF-16 and UTF-32).</p> <p>The default Unicode encoding in Erlang is in binaries UTF-8, which is also the format in which built in functions and libraries in OTP expect to find binary Unicode data. In lists, Unicode data is encoded as integers, each integer representing one character and encoded simply as the Unicode codepoint for the character.</p> - <p>Other Unicode encodings than integers representing codepoints or UTF-8 in binaries are referred to as "external encodings". The iso-latin-1 encoding is in binaries and lists referred to as latin1-encoding.</p> + <p>Other Unicode encodings than integers representing codepoints or UTF-8 in binaries are referred to as "external encodings". The ISO-latin-1 encoding is in binaries and lists referred to as latin1-encoding.</p> <p>It is recommended to only use external encodings for communication with external entities where this is required. When working inside the Erlang/OTP environment, it is recommended to keep binaries in UTF-8 when representing Unicode characters. Latin1 encoding is supported both for backward compatibility and for communication with external entities not supporting Unicode character sets.</p> </description> @@ -48,13 +48,13 @@ <datatype> <name name="unicode_binary"/> <desc> - <p>A binary() with characters encoded in the UTF-8 coding standard.</p> + <p>A <c>binary()</c> with characters encoded in the UTF-8 coding standard.</p> </desc> </datatype> <datatype> <name name="unicode_char"/> <desc> - <p>An integer() representing a valid unicode codepoint.</p> + <p>An <c>integer()</c> representing a valid Unicode codepoint.</p> </desc> </datatype> <datatype> @@ -63,7 +63,7 @@ <datatype> <name name="charlist"/> <desc> - <p>A unicode_binary is allowed as the tail of the list.</p> + <p>A <c>unicode_binary()</c> is allowed as the tail of the list.</p> </desc> </datatype> <datatype> @@ -85,7 +85,7 @@ </datatype> <datatype> <name name="latin1_binary"/> - <desc><p>A <c>binary()</c> with characters coded in iso-latin-1.</p> + <desc><p>A <c>binary()</c> with characters coded in ISO-latin-1.</p> </desc> </datatype> <datatype> @@ -110,7 +110,9 @@ <name name="bom_to_encoding" arity="1"/> <fsummary>Identify UTF byte order marks in a binary.</fsummary> <type name="endian"/> - <type_desc variable="Bin">A binary() of byte_size 4 or more.</type_desc> + <type_desc variable="Bin"> + A <c>binary()</c> such that <c>byte_size(<anno>Bin</anno>) >= 4</c>. + </type_desc> <desc> <p>Check for a UTF byte order mark (BOM) in the beginning of a @@ -126,7 +128,7 @@ <name name="characters_to_list" arity="1"/> <fsummary>Convert a collection of characters to list of Unicode characters</fsummary> <desc> - <p>Same as characters_to_list(<anno>Data</anno>,unicode).</p> + <p>Same as <c>characters_to_list(<anno>Data</anno>, unicode)</c>.</p> </desc> </func> <func> @@ -134,8 +136,8 @@ <fsummary>Convert a collection of characters to list of Unicode characters</fsummary> <desc> - <p>This function converts a possibly deep list of integers and - binaries into a list of integers representing unicode + <p>Converts a possibly deep list of integers and + binaries into a list of integers representing Unicode characters. The binaries in the input may have characters encoded as latin1 (0 - 255, one character per byte), in which case the <c><anno>InEncoding</anno></c> parameter should be given as @@ -148,18 +150,18 @@ <p>If <c><anno>InEncoding</anno></c> is <c>latin1</c>, the <c><anno>Data</anno></c> parameter corresponds to the <c>iodata()</c> type, but for <c>unicode</c>, the <c><anno>Data</anno></c> parameter can contain integers greater than 255 - (unicode characters beyond the iso-latin-1 range), which would + (Unicode characters beyond the ISO-latin-1 range), which would make it invalid as <c>iodata()</c>.</p> <p>The purpose of the function is mainly to be able to convert - combinations of unicode characters into a pure unicode + combinations of Unicode characters into a pure Unicode string in list representation for further processing. For writing the data to an external entity, the reverse function <seealso - marker="#characters_to_binary/3">characters_to_binary/3</seealso> + marker="#characters_to_binary/3"><c>characters_to_binary/3</c></seealso> comes in handy.</p> - <p>The option <c>unicode</c> is an alias for <c>utf8</c>, as this is the + <p>The option <c>unicode</c> is an alias for <c>utf8</c>, as this is the preferred encoding for Unicode characters in binaries. <c>utf16</c> is an alias for <c>{utf16,big}</c> and <c>utf32</c> is an alias for <c>{utf32,big}</c>. The <c>big</c> @@ -167,7 +169,7 @@ encoding.</p> <p>If for some reason, the data cannot be converted, either - because of illegal unicode/latin1 characters in the list, or + because of illegal Unicode/latin1 characters in the list, or because of invalid UTF encoding in any binaries, an error tuple is returned. The error tuple contains the tag <c>error</c>, a list representing the characters that could be @@ -176,7 +178,7 @@ last part is mostly for debugging as it still constitutes a possibly deep and/or mixed list, not necessarily of the same depth as the original data. The error occurs when traversing the - list and whatever's left to decode is simply returned as is.</p> + list and whatever is left to decode is simply returned as is.</p> <p>However, if the input <c><anno>Data</anno></c> is a pure binary, the third part of the error tuple is guaranteed to be a binary as @@ -191,7 +193,7 @@ of a Unicode type, an error occurs whenever an integer <list type="bulleted"> <item>greater than <c>16#10FFFF</c> - (the maximum unicode character),</item> + (the maximum Unicode character),</item> <item>in the range <c>16#D800</c> to <c>16#DFFF</c> (invalid range reserved for UTF-16 surrogate pairs)</item> </list> @@ -205,14 +207,14 @@ (like the upper bits of the bytes being wrong), the bytes are decoded to a too large number, the bytes are decoded to a code-point in the - invalid unicode - range or encoding is "overlong", meaning that a + invalid Unicode + range, or encoding is "overlong", meaning that a number should have been encoded in fewer bytes. The case of a truncated UTF is handled specially, see the paragraph about incomplete binaries below. If <c><anno>InEncoding</anno></c> is <c>latin1</c>, binaries are always valid as long as they contain whole bytes, - as each byte falls into the valid iso-latin-1 range.</item> + as each byte falls into the valid ISO-latin-1 range.</item> </list> @@ -250,7 +252,7 @@ ever be decoded.</p> <p>If any parameters are of the wrong type, the list structure - is invalid (a number as tail) or the binaries does not contain + is invalid (a number as tail) or the binaries do not contain whole bytes (bit-strings), a <c>badarg</c> exception is thrown.</p> @@ -258,28 +260,27 @@ </func> <func> <name name="characters_to_binary" arity="1"/> - <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> + <fsummary>Convert a collection of characters to a UTF-8 binary</fsummary> <desc> - <p>Same as characters_to_binary(Data, unicode, unicode).</p> + <p>Same as <c>characters_to_binary(<anno>Data</anno>, unicode, unicode)</c>.</p> </desc> </func> <func> <name name="characters_to_binary" arity="2"/> - <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> + <fsummary>Convert a collection of characters to a UTF-8 binary</fsummary> <desc> - <p>Same as characters_to_binary(<anno>Data</anno>, <anno>InEncoding</anno>, unicode).</p> + <p>Same as <c>characters_to_binary(<anno>Data</anno>, <anno>InEncoding</anno>, unicode)</c>.</p> </desc> </func> <func> <name name="characters_to_binary" arity="3"/> - <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> + <fsummary>Convert a collection of characters to a UTF-8 binary</fsummary> <desc> - <p>This function behaves as <seealso - marker="#characters_to_list/2"> - characters_to_list/2</seealso>, but produces an binary - instead of a unicode list. The + <p>Behaves as <seealso marker="#characters_to_list/2"> + <c>characters_to_list/2</c></seealso>, but produces an binary + instead of a Unicode list. The <c><anno>InEncoding</anno></c> defines how input is to be interpreted if binaries are present in the <c>Data</c>, while <c><anno>OutEncoding</anno></c> defines in what format output is to be @@ -294,7 +295,7 @@ <p>Errors and exceptions occur as in <seealso marker="#characters_to_list/2"> - characters_to_list/2</seealso>, but the second element + <c>characters_to_list/2</c></seealso>, but the second element in the <c>error</c> or <c>incomplete</c> tuple will be a <c>binary()</c> and not a <c>list()</c>.</p> @@ -304,16 +305,18 @@ <func> <name name="encoding_to_bom" arity="1"/> <fsummary>Create a binary UTF byte order mark from encoding.</fsummary> - <type_desc variable="Bin">A binary() of byte_size 4 or more.</type_desc> + <type_desc variable="Bin"> + A <c>binary()</c> such that <c>byte_size(<anno>Bin</anno>) >= 4</c>. + </type_desc> <desc> - <p>Create an UTF byte order mark (BOM) as a binary from the + <p>Create a UTF byte order mark (BOM) as a binary from the supplied <c><anno>InEncoding</anno></c>. The BOM is, if supported at all, expected to be placed first in UTF encoded files or messages.</p> <p>The function returns <c><<>></c> for the - <c>latin1</c> encoding, there is no BOM for ISO-latin-1.</p> + <c>latin1</c> encoding as there is no BOM for ISO-latin-1.</p> <p>It can be noted that the BOM for UTF-8 is seldom used, and it is really not a <em>byte order</em> mark. There are obviously no diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml index bbcd49a934..320b5b2e84 100644 --- a/lib/stdlib/doc/src/unicode_usage.xml +++ b/lib/stdlib/doc/src/unicode_usage.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf8" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> @@ -33,14 +33,14 @@ <file>unicode_usage.xml</file> </header> <p>Implementing support for Unicode character sets is an ongoing process. The Erlang Enhancement Proposal (EEP) 10 outlines the basics of Unicode support and also specifies a default encoding in binaries that all Unicode-aware modules should handle in the future.</p> -<p>The functionality described in EEP10 is implemented in Erlang/OTP as of R13A, but that's by no means the end of it. More functionality will be needed in the future and more OTP-libraries might need updating to cope with Unicode data. One example of future development is obvious when reading this manual, our documentation format is limited to the ISO-latin-1 character range, why no Unicode characters beyond that range will occur in this document.</p> +<p>The functionality described in EEP10 is implemented in Erlang/OTP as of R13A, but that is by no means the end of it. More functionality will be needed in the future and more OTP-libraries might need updating to cope with Unicode data.</p> <p>This guide outlines the current Unicode support and gives a couple of recipes for working with Unicode data.</p> <section> -<title>What Unicode is</title> +<title>What Unicode Is</title> <p>Unicode is a standard defining codepoints (numbers) for all known, living or dead, scripts. In principle, every known symbol used in any language has a Unicode codepoint.</p> <p>Unicode codepoints are defined and published by the <em>Unicode Consortium</em>, which is a non profit organization.</p> <p>Support for Unicode is increasing throughout the world of computing, as the benefits of one common character set are overwhelming when programs are used in a global environment.</p> -<p>Along with the base of the standard, the codepoints for all the scripts, there are a couple of encoding standards available. Different operating systems and tools support different encodings. For example Linux and MacOS X has chosen the UTF-8 encoding, which is backwards compatible with 7-bit ASCII and therefore affects programs written in plain English the least. Windows® on the other hand supports a limited version of UTF-16, namely all the code planes where the characters can be stored in one single 16-bit entity, which includes most living languages.</p> +<p>Along with the base of the standard, the codepoints for all the scripts, there are a couple of encoding standards available. Different operating systems and tools support different encodings. For example Linux and MacOSX has chosen the UTF-8 encoding, which is backwards compatible with 7-bit ASCII and therefore affects programs written in plain English the least. Windows® on the other hand supports a limited version of UTF-16, namely all the code planes where the characters can be stored in one single 16-bit entity, which includes most living languages.</p> <p>The most widely spread encodings are:</p> <taglist> <tag>UTF-8</tag> @@ -56,38 +56,47 @@ <p>Additionally, the codepoint 16#FEFF is used for byte order marks (BOM's) and use of that character is not encouraged in other contexts than that. It actually is valid though, as the character "ZWNBS" (Zero Width Non Breaking Space). BOM's are used to identify encodings and byte order for programs where such parameters are not known in advance. Byte order marks are more seldom used than one could expect, but their use is becoming more widely spread as they provide the means for programs to make educated guesses about the Unicode format of a certain file.</p> </section> <section> -<title>Standard Unicode representation in Erlang</title> +<title>Standard Unicode Representation in Erlang</title> <p>In Erlang, strings are actually lists of integers. A string is defined to be encoded in the ISO-latin-1 (ISO8859-1) character set, which is, codepoint by codepoint, a sub-range of the Unicode character set.</p> <p>The standard list encoding for strings is therefore easily extendible to cope with the whole Unicode range: A Unicode string in Erlang is simply a list containing integers, each integer being a valid Unicode codepoint and representing one character in the Unicode character set.</p> -<p>Regular Erlang strings in ISO-latin-1 are a subset of their Unicode strings.</p> +<p>Regular Erlang strings in ISO-latin-1 are a subset of their Unicode +strings.</p> -<p>Binaries on the other hand are more troublesome. For performance reasons, programs often store textual data in binaries instead of lists, mainly because they are more compact (one byte per character instead of two words per character, as is the case with lists). Using erlang:list_to_binary/1, an regular Erlang string can be converted into a binary, effectively using the ISO-latin-1 encoding in the binary - one byte per character. This is very convenient for those regular Erlang strings, but cannot be done for Unicode lists.</p> +<p>Binaries on the other hand are more troublesome. For performance reasons, programs often store textual data in binaries instead of lists, mainly because they are more compact (one byte per character instead of two words per character, as is the case with lists). Using <c>erlang:list_to_binary/1</c>, a regular Erlang string can be converted into a binary, effectively using the ISO-latin-1 encoding in the binary - one byte per character. This is very convenient for those regular Erlang strings, but cannot be done for Unicode lists.</p> <p>As the UTF-8 encoding is widely spread and provides the most compact storage, it is selected as the standard encoding of Unicode characters in binaries for Erlang.</p> <p>The standard binary encoding is used whenever a library function in Erlang should cope with Unicode data in binaries, but is of course not enforced when communicating externally. Functions and bit-syntax exist to encode and decode both UTF-8, UTF-16 and UTF-32 in binaries. Library functions dealing with binaries and Unicode in general, however, only deal with the default encoding.</p> -<p>Character data may be combined from several sources, sometimes available in a mix of strings and binaries. Erlang has for long had the concept of iodata or iolists, where binaries and lists can be combined to represent a sequence of bytes. In the same way, the Unicode aware modules often allow for combinations of binaries and lists where the binaries have characters encoded in UTF-8 and the lists contain such binaries or numbers representing Unicode codepoints:</p> +<p>Character data may be combined from several sources, sometimes available in a mix of strings and binaries. Erlang has for long had the concept of <c>iodata</c> or <c>iolists</c>, where binaries and lists can be combined to represent a sequence of bytes. In the same way, the Unicode aware modules often allow for combinations of binaries and lists where the binaries have characters encoded in UTF-8 and the lists contain such binaries or numbers representing Unicode codepoints:</p> <code type="none"> unicode_binary() = binary() with characters encoded in UTF-8 coding standard -unicode_char() = integer() representing valid unicode codepoint +unicode_char() = integer() >= 0 representing valid Unicode codepoint chardata() = charlist() | unicode_binary() charlist() = [unicode_char() | unicode_binary() | charlist()] a unicode_binary is allowed as the tail of the list</code> -<p>The module <c>unicode</c> in stdlib even supports similar mixes with binaries containing other encodings than UTF-8, but that is a special case to allow for conversions to and from external data:</p> +<p>The module <c>unicode</c> in STDLIB even supports similar mixes with binaries containing other encodings than UTF-8, but that is a special case to allow for conversions to and from external data:</p> <code type="none"> -external_unicode_binary() = binary() with characters coded in a user specified Unicode - encoding other than UTF-8 (UTF-16 or UTF-32) +external_unicode_binary() = binary() with characters coded in + a user specified Unicode encoding other than UTF-8 (UTF-16 or UTF-32) external_chardata() = external_charlist() | external_unicode_binary() -external_charlist() = [unicode_char() | external_unicode_binary() | external_charlist()] - an external_unicode_binary is allowed as the tail of the list</code> +external_charlist() = [unicode_char() | + external_unicode_binary() | + external_charlist()] + an external_unicode_binary() is allowed as the tail of the list</code> </section> <section> -<title>Basic language support for Unicode</title> -<p>First of all, Erlang is still defined to be written in the ISO-latin-1 character set. Functions have to be named in that character set, atoms are restricted to ISO-latin-1 and regular strings are still lists of characters 0..255 in the ISO-latin-1 encoding. This has not (yet) changed, but the language has been slightly extended to cope with Unicode characters and encodings.</p> - +<title>Basic Language Support for Unicode</title> +<p><marker id="unicode_in_erlang"/>As of Erlang/OTP R16 Erlang can be +written in ISO-latin-1 or Unicode (UTF-8). The details on how to state +the encoding of an Erlang source file can be found in <seealso +marker="stdlib:epp#encoding">epp(3)</seealso>. Strings and comments +can be written using Unicode, but functions still have to be named in +ISO-latin-1 and atoms are restricted to ISO-latin-1. Erlang/OTP R18 is +expected to handle functions named in Unicode as well as Unicode +atoms.</p> <section> <title>Bit-syntax</title> <p>The bit-syntax contains types for coping with binary data in the three main encodings. The types are named <c>utf8</c>, <c>utf16</c> and <c>utf32</c> respectively. The <c>utf16</c> and <c>utf32</c> types can be in a big- or little-endian variant:</p> @@ -101,74 +110,79 @@ Bin3 = <<$H/utf32-little, $e/utf32-little, $l/utf32-little, $l/utf32-littl Bin4 = <<"Hello"/utf16>>,</code> </section> <section> -<title>String- and character-literals</title> -<warning> -<p>The literal syntax described here may be subject to change in R13B, it has not yet passed the usual process for language changes approval.</p> -</warning> -<p>It is convenient to be able to write a list of Unicode characters in the string syntax. However, the language specifies strings as being in the ISO-latin-1 character set which the compiler tool chain as well as many other tools expect.</p> -<p>Also the source code is (for now) still expected to be written using the ISO-latin-1 character set, why Unicode characters beyond that range cannot be entered in string literals.</p> -<p>To make it easier to enter Unicode characters in the shell, it allows strings with Unicode characters on input, immediately converting them to regular lists of integers. They will, by the evaluator etc be viewed as if they were input using the regular list syntax, which is - in the end - how the language actually treats them. They will in the same way not be output as strings by i.e <c>io:write/2</c> or <c>io:format/3</c> unless the format string supplied to <c>io:format</c> uses the Unicode translation modifier (which we will talk about later).</p> -<p>For source code, there is an extension to the \OOO (backslash followed by three octal numbers) and \xHH (backslash followed by 'x', followed by two hexadecimal characters) syntax, namely \x{H ...} (a backslash followed by an 'x', followed by left curly bracket, any number of hexadecimal digits and a terminating right curly bracket). This allows for entering characters of any codepoint literally in a string. The string is immediately converted into a list by the scanner however, which is obvious when calling it directly:</p> -<pre> -1> <input>erl_scan:string("\"X\".").</input> -{ok,[{string,1,"X"},{dot,1}],1} -2> <input>erl_scan:string("\"\x{400}\".").</input> -{ok,[{'[',1},{integer,1,1024},{']',1},{dot,1}],1}</pre> -<p>Character literals, or rather integers representing Unicode codepoints can be expressed in a similar way using $\x{H ...}:</p> -<pre> -4> <input>$\x{400}.</input> -1024</pre> -<p>This also is a translation by the scanner:</p> -<pre> -5> <input>erl_scan:string("$Y.").</input> -{ok,[{char,1,89},{dot,1}],1} -6> <input>erl_scan:string("$\x{400}.").</input> -{ok,[{integer,1,1024},{dot,1}],1}</pre> -<p>In the shell, if using a Unicode input device, '$' can be followed directly by a Unicode character producing an integer. In the following example, let's imagine the character 'c' is actually a Cyrillic 's' (looking fairly similar):</p> +<title>String- and Character-literals</title> +<p>For source code, there is an extension to the <c>\</c>OOO (backslash +followed by three octal numbers) and <c>\x</c>HH (backslash followed by <c>x</c>, +followed by two hexadecimal characters) syntax, namely <c>\x{</c>H ...<c>}</c> (a +backslash followed by an <c>x</c>, followed by left curly bracket, any +number of hexadecimal digits and a terminating right curly bracket). +This allows for entering characters of any codepoint literally in a +string even when the encoding is ISO-latin-1.</p> +</section> +<p>In the shell, if using a Unicode input device, <c>$</c> can be followed directly by a Unicode character producing an integer. In the following example the codepoint of a Cyrillic <c>s</c> is output:</p> <pre> -7> <input>$c.</input> +7> <input>$с.</input> 1089</pre> </section> -<p>The literal syntax allowing Unicode characters is to be viewed as "syntactic sugar", but is, as such, fairly useful.</p> -</section> <section> -<title>The interactive shell</title> +<title>The Interactive Shell</title> <p>The interactive Erlang shell, when started towards a terminal or started using the <c>werl</c> command on windows, can support Unicode input and output.</p> -<p>On Windows®, proper operation requires that a suitable font is installed and selected for the Erlang application to use. If no suitable font is available on your system, try installing the DejaVu fonts (dejavu-fonts.org), which are freely available and then select that font in the Erlang shell application.</p> -<p>On Unix®-like operating systems, the terminal should be able to handle UTF-8 on input and output (modern versions of XTerm, KDE konsole and the Gnome terminal do for example) and your locale settings have to be proper. As an example, my LANG environment variable is set as this:</p> +<p>On Windows®, proper operation requires that a suitable font is installed and selected for the Erlang application to use. If no suitable font is available on your system, try installing the DejaVu fonts (<c>dejavu-fonts.org</c>), which are freely available and then select that font in the Erlang shell application.</p> +<p>On Unix®-like operating systems, the terminal should be able to handle UTF-8 on input and output (modern versions of XTerm, KDE konsole and the Gnome terminal do for example) and your locale settings have to be proper. As an example, my <c>LANG</c> environment variable is set as this:</p> <pre> $ <input>echo $LANG</input> en_US.UTF-8</pre> -<p>Actually, most systems handle the LC_CTYPE variable before LANG, so if that is set, it has to be set to UTF-8:</p> +<p>Actually, most systems handle the <c>LC_CTYPE</c> variable before <c>LANG</c>, so if that is set, it has to be set to <c>UTF-8</c>:</p> <pre> $ echo <input>$LC_CTYPE</input> en_US.UTF-8</pre> -<p>The LANG or LC_CTYPE setting should be consistent with what the terminal is capable of, there is no portable way for Erlang to ask the actual terminal about its UTF-8 capacity, we have to rely on the language and character type settings.</p> +<p>The <c>LANG</c> or <c>LC_CTYPE</c> setting should be consistent with what the terminal is capable of, there is no portable way for Erlang to ask the actual terminal about its UTF-8 capacity, we have to rely on the language and character type settings.</p> <p>To investigate what Erlang thinks about the terminal, the <c>io:getopts()</c> call can be used when the shell is started:</p> <pre> $ <input>LC_CTYPE=en_US.ISO-8859-1 erl</input> -Erlang R13A (erts-5.7) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false] +Erlang R16B (erts-5.10) [source] [async-threads:0] [hipe] [kernel-poll:false] -Eshell V5.7 (abort with ^G) -1> <input>lists:keyfind(encoding,1,io:getopts()).</input> +Eshell V5.10 (abort with ^G) +1> <input>lists:keyfind(encoding, 1, io:getopts()).</input> {encoding,latin1} 2> <input>q().</input> ok $ <input>LC_CTYPE=en_US.UTF-8 erl</input> -Erlang R13A (erts-5.7) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false] +Erlang R16B (erts-5.10) [source] [async-threads:0] [hipe] [kernel-poll:false] -Eshell V5.7 (abort with ^G) -1> <input>lists:keyfind(encoding,1,io:getopts()).</input> +Eshell V5.10 (abort with ^G) +1> <input>lists:keyfind(encoding, 1, io:getopts()).</input> {encoding,unicode} 2></pre> <p>When (finally?) everything is in order with the locale settings, fonts and the terminal emulator, you probably also have discovered a way to input characters in the script you desire. For testing, the simplest way is to add some keyboard mappings for other languages, usually done with some applet in your desktop environment. In my KDE environment, I start the KDE Control Center (Personal Settings), select "Regional and Accessibility" and then "Keyboard Layout". On Windows XP®, I start Control Panel->Regional and Language Options, select the Language tab and click the Details... button in the square named "Text services and input Languages". Your environment probably provides similar means of changing the keyboard layout. Make sure you have a way to easily switch back and forth between keyboards if you are not used to this, entering commands using a Cyrillic character set is, as an example, not easily done in the Erlang shell.</p> <p>Now you are set up for some Unicode input and output. The simplest thing to do is of course to enter a string in the shell:</p> -<image file="ushell1.gif"><icaption>Cyrillic characters in an Erlang shell</icaption></image> +<pre> +$ <input>erl</input> +Erlang R16B (erts-5.10) [source] [async-threads:0] [hipe] [kernel-poll:false] + +Eshell V5.10 (abort with ^G) +1> <input>lists:keyfind(encoding, 1, io:getopts()).</input> +{encoding,unicode} +2> <input>"уницоде"</input> +"уницоде" +3> <input>io:format("~ts~n", [v(2)]).</input> +уницоде +ok +4> </pre> <p>While strings can be input as Unicode characters, the language elements are still limited to the ISO-latin-1 character set. Only character constants and strings are allowed to be beyond that range:</p> -<image file="ushell2.gif"><icaption>Unicode characters in allowed and disallowed context</icaption></image> +<pre> +$ <input>erl</input> +Erlang R16B (erts-5.10) [source] [async-threads:0] [hipe] [kernel-poll:false] + +Eshell V5.10 (abort with ^G) +1> <input>$ξ</input> +958 +2> <input>уницоде.</input> +* 1: illegal character +2> </pre> </section> <section> -<title>Unicode file names</title> +<title>Unicode File Names</title> <p>Most modern operating systems support Unicode file names in some way or another. There are several different ways to do this and Erlang by default treats the different approaches differently:</p> <taglist> <tag>Mandatory Unicode file naming</tag> @@ -183,7 +197,7 @@ Eshell V5.7 (abort with ^G) <p>A raw file name is not a list, but a binary. Many non core applications still do not handle file names given as binaries, why such raw names are avoided by default. This means that systems having implemented Unicode file naming through transparent file systems and an UTF-8 convention, do not by default have Unicode file naming turned on. Explicitly turning Unicode file name handling on for these types of systems is considered experimental.</p> </item> </taglist> -<p>The Unicode file naming support was introduced with OTP release R14B01. A VM operating in Unicode file mode can work with files having names in any language or character set (as long as it's supported by the underlying OS and file system). The Unicode character list is used to denote file or directory names and if the file system content is listed, you will also be able to get Unicode lists as return value. The support lies in the kernel and stdlib modules, why most applications (that does not explicitly require the file names to be in the ISO-latin-1 range) will benefit from the Unicode support without change.</p> +<p>The Unicode file naming support was introduced with OTP release R14B01. A VM operating in Unicode file mode can work with files having names in any language or character set (as long as it is supported by the underlying OS and file system). The Unicode character list is used to denote file or directory names and if the file system content is listed, you will also be able to get Unicode lists as return value. The support lies in the Kernel and STDLIB modules, why most applications (that does not explicitly require the file names to be in the ISO-latin-1 range) will benefit from the Unicode support without change.</p> <p>On Operating systems with mandatory Unicode file names, this means that you more easily conform to the file names of other (non Erlang) applications, and you can also process file names that, at least on Windows, were completely inaccessible (due to having names that could not be represented in ISO-latin-1). Also you will avoid creating incomprehensible file names on MacOSX as the vfs layer of the OS will accept all your file names as UTF-8 and will not rewrite them.</p> @@ -193,31 +207,32 @@ Eshell V5.7 (abort with ^G) <p>It is worth noting that the file <c>encoding</c> options given when opening a file has nothing to do with the file <em>name</em> encoding convention. You can very well open files containing UTF-8 but having file names in ISO-latin-1 or vice versa.</p> -<note>Erlang drivers and NIF shared objects still can not be named with names containing codepoints beyond 127. This is a known limitation to be removed in a future release. Erlang modules however can, but it is definitely not a good idea and is still considered experimental.</note> +<note><p>Erlang drivers and NIF shared objects still can not be named with names containing codepoints beyond 127. This is a known limitation to be removed in a future release. Erlang modules however can, but it is definitely not a good idea and is still considered experimental.</p></note> <section> -<title>Notes about raw file names and automatic file name conversion</title> -<p>Raw file names is introduced together with Unicode file name support in erts-5.8.2 (OTP R14B01). The reason "raw file names" is introduced in the system is to be able to consistently represent file names given in different encodings on the same system. Having the VM automatically translate a file name that is not in UTF-8 to a list of Unicode characters might seem practical, but this would open up for both duplicate file names and other inconsistent behavior. Consider a directory containing a file named "bj�rn" in ISO-latin-1, while the Erlang VM is operating in Unicode file name mode (and therefore expecting UTF-8 file naming). The ISO-latin-1 name is not valid UTF-8 and one could be tempted to think that automatic conversion in for example <c>file:list_dir/1</c> is a good idea. But what would happen if we later tried to open the file and have the name as a Unicode list (magically converted from the ISO-latin-1 file name)? The VM will convert the file name given to UTF-8, as this is the encoding expected. Effectively this means trying to open the file named <<"bj�rn"/utf8>>. This file does not exist, and even if it existed it would not be the same file as the one that was listed. We could even create two files named "bj�rn", one named in the UTF-8 encoding and one not. If <c>file:list_dir/1</c> would automatically convert the ISO-latin-1 file name to a list, we would get two identical file names as the result. To avoid this, we need to differentiate between file names being properly encoded according to the Unicode file naming convention (i.e. UTF-8) and file names being invalid under the encoding. This is done by representing invalid encoding as "raw" file names, i.e. as binaries.</p> -<p>The core system of Erlang (kernel and stdlib) accepts raw file names except for loadable drivers and executables invoked using <c>open_port({spawn, ...} ...)</c>. <c>open_port({spawn_executable, ...} ...)</c> however does accept them. As mentioned earlier, the arguments given in the option list to <c>open_port({spawn_executable, ...} ...)</c> undergo the same conversion as the file names, meaning that the executable will be provided with arguments in UTF-8 as well. This translation is avoided consistently with how the file names are treated, by giving the argument as a binary.</p> +<title>Notes About Raw File Names and Automatic File Name Conversion</title> + +<p>Raw file names is introduced together with Unicode file name support in erts-5.8.2 (OTP R14B01). The reason "raw file names" is introduced in the system is to be able to consistently represent file names given in different encodings on the same system. Having the VM automatically translate a file name that is not in UTF-8 to a list of Unicode characters might seem practical, but this would open up for both duplicate file names and other inconsistent behavior. Consider a directory containing a file named "björn" in ISO-latin-1, while the Erlang VM is operating in Unicode file name mode (and therefore expecting UTF-8 file naming). The ISO-latin-1 name is not valid UTF-8 and one could be tempted to think that automatic conversion in for example <c>file:list_dir/1</c> is a good idea. But what would happen if we later tried to open the file and have the name as a Unicode list (magically converted from the ISO-latin-1 file name)? The VM will convert the file name given to UTF-8, as this is the encoding expected. Effectively this means trying to open the file named <<"björn"/utf8>>. This file does not exist, and even if it existed it would not be the same file as the one that was listed. We could even create two files named "björn", one named in the UTF-8 encoding and one not. If <c>file:list_dir/1</c> would automatically convert the ISO-latin-1 file name to a list, we would get two identical file names as the result. To avoid this, we need to differentiate between file names being properly encoded according to the Unicode file naming convention (i.e. UTF-8) and file names being invalid under the encoding. This is done by representing invalid encoding as "raw" file names, i.e. as binaries.</p> +<p>The core system of Erlang (Kernel and STDLIB) accepts raw file names except for loadable drivers and executables invoked using <c>open_port({spawn, ...} ...)</c>. <c>open_port({spawn_executable, ...} ...)</c> however does accept them. As mentioned earlier, the arguments given in the option list to <c>open_port({spawn_executable, ...} ...)</c> undergo the same conversion as the file names, meaning that the executable will be provided with arguments in UTF-8 as well. This translation is avoided consistently with how the file names are treated, by giving the argument as a binary.</p> <p>To force Unicode file name translation mode on systems where this is not the default is considered experimental in OTP R14B01 due to the raw file names possibly being a new experience to the programmer and that the non core applications of OTP are not tested for compliance with raw file names yet. Unicode file name translation is expected to be default in future releases.</p> -<p>If working with raw file names, one can still conform to the encoding convention of the Erlang VM by using the <c>file:native_name_encoding/0</c> function, which returns either the atom <c>latin1</c> or the atom <c>utf8</c> depending on the file name translation mode. On Linux, a VM started without explicitly stating the file name translation mode will default to <c>latin1</c> as the native file name encoding, why file names on the disk encoded as UTF-8 will be returned as a list of the names interpreted as ISO-latin-1. The "UTF-8 list" is not a practical type for displaying or operating on in Erlang, but it is backward compatible and usable in all functions requiring a file name. On Windows and MacOSX, the default behavior is that of file name translation, why the <c>file:native_name_encoding/0</c> by default returns <c>utf8</c> on those systems (the fact that Windows actually does not use UTF-8 on the file system level can safely be ignored by the Erlang programmer). The default behavior can be changed using the <c>+fnu</c> or <c>+fnl</c> options to the VM, see the <c>erl</c> command manual page.</p> +<p>If working with raw file names, one can still conform to the encoding convention of the Erlang VM by using the <c>file:native_name_encoding/0</c> function, which returns either the atom <c>latin1</c> or the atom <c>utf8</c> depending on the file name translation mode. On Linux, a VM started without explicitly stating the file name translation mode will default to <c>latin1</c> as the native file name encoding, why file names on the disk encoded as UTF-8 will be returned as a list of the names interpreted as ISO-latin-1. The "UTF-8 list" is not a practical type for displaying or operating on in Erlang, but it is backward compatible and usable in all functions requiring a file name. On Windows and MacOSX, the default behavior is that of file name translation, why the <c>file:native_name_encoding/0</c> by default returns <c>utf8</c> on those systems (the fact that Windows actually does not use UTF-8 on the file system level can safely be ignored by the Erlang programmer). The default behavior can be changed using the <c>+fnu</c> or <c>+fnl</c> options to the VM, see the <seealso marker="erts:erl"><c>erl(1)</c></seealso> command manual page.</p> <p>Even if you are operating without Unicode file naming translation automatically done by the VM, you can access and create files with names in UTF-8 encoding by using raw file names encoded as UTF-8. Enforcing the UTF-8 encoding regardless of the mode the Erlang VM is started in might, in some circumstances be a good idea, as the convention of using UTF-8 file names is spreading.</p> </section> <section> -<title>Notes about MacOSX</title> +<title>Notes About MacOSX</title> <p>MacOSXs vfs layer enforces UTF-8 file names in a quite aggressive way. Older versions did this by simply refusing to create non UTF-8 conforming file names, while newer versions replace offending bytes with the sequence "%HH", where HH is the original character in hexadecimal notation. As Unicode translation is enabled by default on MacOSX, the only way to come up against this is to either start the VM with the <c>+fnl</c> flag or to use a raw file name in <c>latin1</c> encoding. In that case, the file can not be opened with the same name as the one used to create this. The problem is by design in newer versions of MacOSX.</p> -<p>MacOSX also reorganizes the names of files so that the representation of accents etc is denormalized, i.e. the character <c>�</c> is represented as the codepoints [111,776], where 111 is the character <c>o</c> and 776 is a special accent character. This type of denormalized Unicode is otherwise very seldom used and Erlang normalizes those file names on retrieval, so that denormalized file names is not passed up to the Erlang application. In Erlang the file name "bj�rn" is retrieved as [98,106,246,114,110], not as [98,106,117,776,114,110], even though the file system might think differently.</p> +<p>MacOSX also reorganizes the names of files so that the representation of accents etc is denormalized, i.e. the character <c>ö</c> is represented as the codepoints [111,776], where 111 is the character <c>o</c> and 776 is a special accent character. This type of denormalized Unicode is otherwise very seldom used and Erlang normalizes those file names on retrieval, so that denormalized file names is not passed up to the Erlang application. In Erlang the file name "björn" is retrieved as [98,106,246,114,110], not as [98,106,117,776,114,110], even though the file system might think differently.</p> </section> </section> <section> -<title>Unicode in environment variables and parameters</title> +<title>Unicode in Environment Variables and Parameters</title> <p>Environment variables and their interpretation is handled much in the same way as file names. If Unicode file names are enabled, environment variables as well as parameters to the Erlang VM are expected to be in Unicode.</p> <p>If Unicode file names are enabled, the calls to <seealso marker="kernel:os#getenv/0"><c>os:getenv/0</c></seealso>, <seealso marker="kernel:os#getenv/1"><c>os:getenv/1</c></seealso> and <seealso marker="kernel:os#putenv/2"><c>os:putenv/2</c></seealso> will handle Unicode strings. On Unix-like platforms, the built-in functions will translate environment variables in UTF-8 to/from Unicode strings, possibly with codepoints > 255. On Windows the Unicode versions of the environment system API will be used, also allowing for codepoints > 255.</p> <p>On Unix-like operating systems, parameters are expected to be UTF-8 without translation if Unicode file names are enabled.</p> </section> <section> -<title>Unicode-aware modules</title> -<p>Most of the modules in Erlang/OTP are of course Unicode-unaware in the sense that they have no notion of Unicode and really shouldn't have. Typically they handle non-textual or byte-oriented data (like <c>gen_tcp</c> etc).</p> +<title>Unicode-aware Modules</title> +<p>Most of the modules in Erlang/OTP are of course Unicode-unaware in the sense that they have no notion of Unicode and really should not have. Typically they handle non-textual or byte-oriented data (like <c>gen_tcp</c> etc).</p> <p>Modules that actually handle textual data (like <c>io_lib</c>, <c>string</c> etc) are sometimes subject to conversion or extension to be able to handle Unicode characters.</p> <p>Fortunately, most textual data has been stored in lists and range checking has been sparse, why modules like <c>string</c> works well for Unicode lists with little need for conversion or extension.</p> <p>Some modules are however changed to be explicitly Unicode-aware. These modules include:</p> @@ -230,7 +245,7 @@ Eshell V5.7 (abort with ^G) <item> <p>The <seealso marker="stdlib:io">io</seealso> module has been extended along with the actual I/O-protocol to handle Unicode data. This means that several functions require binaries to be in UTF-8 and there are modifiers to formatting control sequences to allow for outputting of Unicode strings.</p> </item> -<tag><c>file</c>, <c>group</c> and <c>user</c></tag> +<tag><c>file</c>, <c>group</c>, <c>user</c></tag> <item> <p>I/O-servers throughout the system are able both to handle Unicode data and has options for converting data upon actual output or input to/from the device. As shown earlier, the <seealso marker="stdlib:shell">shell</seealso> has support for Unicode terminals and the <seealso marker="kernel:file">file</seealso> module allows for translation to and from various Unicode formats on disk.</p> <p>The actual reading and writing of files with Unicode data is however not best done with the <c>file</c> module as its interface is byte oriented. A file opened with a Unicode encoding (like UTF-8), is then best read or written using the <seealso marker="stdlib:io">io</seealso> module.</p> @@ -247,10 +262,10 @@ Eshell V5.7 (abort with ^G) <p>The module <seealso marker="stdlib:string">string</seealso> works perfect for Unicode strings as well as for ISO-latin-1 strings with the exception of the language-dependent <seealso marker="stdlib:string#to_upper/1">to_upper</seealso> and <seealso marker="stdlib:string#to_lower/1">to_lower</seealso> functions, which are only correct for the ISO-latin-1 character set. Actually they can never function correctly for Unicode characters in their current form, there are language and locale issues as well as multi-character mappings to consider when conversion text between cases. Converting case in an international environment is a big subject not yet addressed in OTP.</p> </section> <section> -<title>Unicode recipes</title> +<title>Unicode Recipes</title> <p>When starting with Unicode, one often stumbles over some common issues. I try to outline some methods of dealing with Unicode data in this section.</p> <section> -<title>Byte order marks</title> +<title>Byte Order Marks</title> <p>A common method of identifying encoding in text-files is to put a byte order mark (BOM) first in the file. The BOM is the codepoint 16#FEFF encoded in the same way as the rest of the file. If such a file is to be read, the first few bytes (depending on encoding) is not part of the actual text. This code outlines how to open a file which is believed to have a BOM and set the files encoding and position for further sequential reading (preferably using the <seealso marker="stdlib:io">io</seealso> module). Note that error handling is omitted from the code:</p> <code> open_bom_file_for_reading(File) -> @@ -261,7 +276,7 @@ open_bom_file_for_reading(File) -> io:setopts(F,[{encoding,Type}]), {ok,F}. </code> -<p>The <c>unicode:bom_to_encoding/1</c> function identifies the encoding from a binary of at least four bytes. It returns, along with an term suitable for setting the encoding of the file, the actual length of the BOM, so that the file position can be set accordingly. Note that <c>file:position</c> always works on byte-offsets, so that the actual byte-length of the BOM is needed.</p> +<p>The <c>unicode:bom_to_encoding/1</c> function identifies the encoding from a binary of at least four bytes. It returns, along with an term suitable for setting the encoding of the file, the actual length of the BOM, so that the file position can be set accordingly. Note that <c>file:position/2</c> always works on byte-offsets, so that the actual byte-length of the BOM is needed.</p> <p>To open a file for writing and putting the BOM first is even simpler:</p> <code> open_bom_file_for_writing(File,Encoding) -> @@ -273,24 +288,33 @@ open_bom_file_for_writing(File,Encoding) -> <p>In both cases the file is then best processed using the <c>io</c> module, as the functions in <c>io</c> can handle codepoints beyond the ISO-latin-1 range.</p> </section> <section> -<title>Formatted input and output</title> -<p>When reading and writing to Unicode-aware entities, like the User or a file opened for Unicode translation, you will probably want to format text strings using the functions in <seealso marker="stdlib:io">io</seealso> or <seealso marker="stdlib:io_lib">io_lib</seealso>. For backward compatibility reasons, these functions don't accept just any list as a string, but require e special "translation modifier" when working with Unicode texts. The modifier is "t". When applied to the "s" control character in a formatting string, it accepts all Unicode codepoints and expect binaries to be in UTF-8:</p> +<title>Formatted Input and Output</title> +<p>When reading and writing to Unicode-aware entities, like the User or a file opened for Unicode translation, you will probably want to format text strings using the functions in <seealso marker="stdlib:io">io</seealso> or <seealso marker="stdlib:io_lib">io_lib</seealso>. For backward compatibility reasons, these functions do not accept just any list as a string, but require a special <em>translation modifier</em> when working with Unicode texts. The modifier is <c>t</c>. When applied to the <c>s</c> control character in a formatting string, it accepts all Unicode codepoints and expect binaries to be in UTF-8:</p> <pre> -1> <input>io:format("~ts~n",[<<"���"/utf8>>]).</input> -��� -ok -2> <input>io:format("~s~n",[<<"���"/utf8>>]).</input> +1> <input>io:format("~ts~n",[<<"åäö"/utf8>>]).</input> åäö +ok +2> <input>io:format("~s~n",[<<"åäö"/utf8>>]).</input> +åäö +ok</pre> +<p>Obviously the second <c>io:format/2</c> gives undesired output because the UTF-8 binary is not in latin1. Because ISO-latin-1 is still the defined character set of Erlang, the non prefixed <c>s</c> control character expects ISO-latin-1 in binaries as well as lists.</p> +<p>As long as the data is always lists, the <c>t</c> modifier can be used for any string, but when binary data is involved, care must be taken to make the right choice of formatting characters.</p> +<p>The function <c>format/2</c> in <c>io_lib</c> behaves similarly. This function is defined to return a deep list of characters and the output could easily be converted to binary data for outputting on a device of any kind by a simple <c>erlang:list_to_binary/1</c>. When the translation modifier is used, the list can however contain characters that cannot be stored in one byte. The call to <c>erlang:list_to_binary/1</c> will in that case fail. However, if the I/O server you want to communicate with is Unicode-aware, the list returned can still be used directly:</p> +<pre> +$ <input>erl</input> +Erlang R16B (erts-5.10) [source] [async-threads:0] [hipe] [kernel-poll:false] + +Eshell V5.10 (abort with ^G) +1> <input>io_lib:format("~ts~n", ["θνιψοδε"]).</input> +["θνιψοδε","\n"] +2> <input>io:put_chars(io_lib:format("~ts~n", ["θνιψοδε"])).</input> +θνιψοδε ok</pre> -<p>Obviously the second <c>io:format</c> gives undesired output because the UTF-8 binary is not in latin1. Because ISO-latin-1 is still the defined character set of Erlang, the non prefixed "s" control character expects ISO-latin-1 in binaries as well as lists.</p> -<p>As long as the data is always lists, the "t" modifier can be used for any string, but when binary data is involved, care must be taken to make the tight choice of formatting characters.</p> -<p>The function <c>format</c> in <c>io_lib</c> behaves similarly. This function is defined to return a deep list of characters and the output could easily be converted to binary data for outputting on a device of any kind by a simple <c>erlang:list_to_binary</c>. When the translation modifier is used, the list can however contain characters that cannot be stored in one byte. The call to <c>erlang:list_to_binary</c> will in that case fail. However, if the io_server you want to communicate with is Unicode-aware, the list returned can still be used directly:</p> -<image file="ushell3.gif"><icaption>io_lib:format with Unicode translation</icaption></image> -<p>The Unicode string is returned as a Unicode list, why the return value of <c>io_lib:format</c> no longer qualifies as a regular Erlang string (the function <seealso marker="stdlib:io_lib#deep_char_list/1">io_lib:deep_char_list</seealso> will, as an example, return <c>false</c>). The Unicode list is however valid input to the <seealso marker="stdlib:io#put_chars/2">io:put_chars</seealso> function, so data can be output on any Unicode capable device anyway. If the device is a terminal, characters will be output in the \x{H ...} format if encoding is <c>latin1</c> otherwise in UTF-8 (for the non-interactive terminal - "oldshell" or "noshell") or whatever is suitable to show the character properly (for an interactive terminal - the regular shell). The bottom line is that you can always send Unicode data to the <c>standard_io</c> device. Files will however only accept Unicode codepoints beyond ISO-latin-1 if <c>encoding</c> is set to something else than <c>latin1</c>.</p> +<p>The Unicode string is returned as a Unicode list, which is recognized as such since the Erlang shell uses the Unicode encoding. The Unicode list is valid input to the <seealso marker="stdlib:io#put_chars/2">io:put_chars/2</seealso> function, so data can be output on any Unicode capable device. If the device is a terminal, characters will be output in the <c>\x{</c>H ...<c>}</c> format if encoding is <c>latin1</c> otherwise in UTF-8 (for the non-interactive terminal - "oldshell" or "noshell") or whatever is suitable to show the character properly (for an interactive terminal - the regular shell). The bottom line is that you can always send Unicode data to the <c>standard_io</c> device. Files will however only accept Unicode codepoints beyond ISO-latin-1 if <c>encoding</c> is set to something else than <c>latin1</c>.</p> </section> <section> -<title>Heuristic identification of UTF-8</title> -<p>While it's strongly encouraged that the actual encoding of characters in binary data is known prior to processing, that is not always possible. On a typical Linux® system, there is a mix of UTF-8 and ISO-latin-1 text files and there are seldom any BOM's in the files to identify them.</p> +<title>Heuristic Identification of UTF-8</title> +<p>While it iss strongly encouraged that the actual encoding of characters in binary data is known prior to processing, that is not always possible. On a typical Linux® system, there is a mix of UTF-8 and ISO-latin-1 text files and there are seldom any BOM's in the files to identify them.</p> <p>UTF-8 is designed in such a way that ISO-latin-1 characters with numbers beyond the 7-bit ASCII range are seldom considered valid when decoded as UTF-8. Therefore one can usually use heuristics to determine if a file is in UTF-8 or if it is encoded in ISO-latin-1 (one byte per character) encoding. The <c>unicode</c> module can be used to determine if data can be interpreted as UTF-8:</p> <code> heuristic_encoding_bin(Bin) when is_binary(Bin) -> diff --git a/lib/stdlib/doc/src/ushell1.gif b/lib/stdlib/doc/src/ushell1.gif Binary files differdeleted file mode 100644 index 7c46464fd2..0000000000 --- a/lib/stdlib/doc/src/ushell1.gif +++ /dev/null diff --git a/lib/stdlib/doc/src/ushell1.ps b/lib/stdlib/doc/src/ushell1.ps deleted file mode 100644 index 95bfebf194..0000000000 --- a/lib/stdlib/doc/src/ushell1.ps +++ /dev/null @@ -1,1196 +0,0 @@ -%!PS-Adobe-3.0 -%%Creator: GIMP PostScript file plugin V 1.17 by Peter Kirchgessner -%%Title: ushell1.ps -%%CreationDate: Mon Mar 16 09:53:27 2009 -%%DocumentData: Clean7Bit -%%LanguageLevel: 2 -%%Pages: 1 -%%BoundingBox: 14 14 468 142 -%%EndComments -%%BeginProlog -% Use own dictionary to avoid conflicts -10 dict begin -%%EndProlog -%%Page: 1 1 -% Translate for offset -14.173228346456694 14.173228346456694 translate -% Translate to begin of first scanline -0 127.55905511811024 translate -453.54330708661422 -127.55905511811024 scale -% Image geometry -640 180 8 -% Transformation matrix -[ 640 0 0 180 0 0 ] -% Strings to hold RGB-samples per scanline -/rstr 640 string def -/gstr 640 string def -/bstr 640 string def -{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop} -true 3 -%%BeginData: 67571 ASCII Bytes -colorimage -JP1PeJP1PeJP1L~> -JP1PeJP1PeJP1L~> -JP1PeJP1PeJP1L~> -!!e'9JNA?CJNABDJ,~> -!!e'9JNA?CJNABDJ,~> -!!e'9JNA?CJNABDJ,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$2%<!Lr-2rr]6=Dt\b^JcC<$JcD\KJ,~> -!$2%<!Lr-2rr]6=Dt\b^JcC<$JcD\KJ,~> -!$2%<!Lr-2rr]6=Dt\b^JcC<$JcD\KJ,~> -#9EjEY>Q6)=6BMbc`J;XJcC<$JcC<$W;hA~> -#9EjEY>Q6)=6BMbc`J;XJcC<$JcC<$W;hA~> -#9EjEY>Q6)=6BMbc`J;XJcC<$JcC<$W;hA~> -#T`rUH-/@H`Dbmu!A3bks+13$s+13Ks*t~> -#T`rUH-/@H`Dbmu!A3bks+13$s+13Ks*t~> -#T`rUH-/@H`Dbmu!A3bks+13$s+13Ks*t~> -#T`rEG5Y4]rp0@Z!W@cP*=4P!s-oDP9H?1krVlmMmt(Lis+13$s/>sJ~> -#T`rEG5Y4]rp0@Z!W@cP*=4P!s-oDP9H?1krVlmMmt(Lis+13$s/>sJ~> -#T`rEG5Y4]rp0@Z!W@cP*=4P!s-oDP9H?1krVlmMmt(Lis+13$s/>sJ~> -#9EjER>jY@r:g3lQA5D=$0F2Hs)Ak%s4op<rrF_?JcC<$JcC<$W;hA~> -#9EjER>jY@r:g3lQA5D=$0F2Hs)Ak%s4op<rrF_?JcC<$JcC<$W;hA~> -#9EjER>jY@r:g3lQA5D=$0F2Hs)Ak%s4op<rrF_?JcC<$JcC<$W;hA~> -!$2(="-F9Q\G-"(Q=^'2$*?/ds)G.?s5e.srrF_?JcC<$JcC<$W;hA~> -!$2(="-F9Q\G-"(Q=^'2$*?/ds)G.?s5e.srrF_?JcC<$JcC<$W;hA~> -!$2(="-F9Q\G-"(Q=^'2$*?/ds)G.?s5e.srrF_?JcC<$JcC<$W;hA~> -!$2%<"&I0WZ2">"Q8&8B"FpIOENK!9!A3bks+13$s+13Ks*t~> -!$2%<"&I0WZ2">"Q8&8B"FpIOENK!9!A3bks+13$s+13Ks*t~> -!$2%<"&I0WZ2">"Q8&8B"FpIOENK!9!A3bks+13$s+13Ks*t~> -#T`rq^Ah!RpEo\8!LAK:rrI,@q>UIImt(Lis+13$s/>sJ~> -#T`rq^Ah!RpEo\8!LAK:rrI,@q>UIImt(Lis+13$s/>sJ~> -#T`rq^Ah!RpEo\8!LAK:rrI,@q>UIImt(Lis+13$s/>sJ~> -#T`s"JSH?.CSgh+!RuStC&iMXrrI,@qu6esC(k*/JcC<$JcC<$WW.J~> -#T`s"JSH?.CSgh+!RuStC&iMXrrI,@qu6esC(k*/JcC<$JcC<$WW.J~> -#T`s"JSH?.CSgh+!RuStC&iMXrrI,@qu6esC(k*/JcC<$JcC<$WW.J~> -#9EjEgp/o#[J'V#fD`&U])M^1eE6Z.!7LkP!5F*bJcC<$JcD_LJ,~> -#9EjEgp/o#[J'V#fD`&U])M^1eE6Z.!7LkP!5F*bJcC<$JcD_LJ,~> -#9EjEgp/o#[J'V#fD`&U])M^1eE6Z.!7LkP!5F*bJcC<$JcD_LJ,~> -!$2%<!T*O$s+13$s+13+s*t~> -!$2%<!T*O$s+13$s+13+s*t~> -!$2%<!T*O$s+13$s+13+s*t~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$.X1!VbZgrrMcToD\g]rn%5Fci4"4rn%5Kp&>-Vf\#!)rr_95f\lE%"5DJ\kK!T&r;P=KmIgJZ -p&<SDo\]ZKoD[ABp@\Fcm/GW;rJ1CTgXt0Ap&>$hrn%5=kPp&~> -!$.X1!VbZgrrMcToD\g]rn%5Fci4"4rn%5Kp&>-Vf\#!)rr_95f\lE%"5DJ\kK!T&r;P=KmIgJZ -p&<SDo\]ZKoD[ABp@\Fcm/GW;rJ1CTgXt0Ap&>$hrn%5=kPp&~> -!$.X1!VbZgrrMcToD\g]rn%5Fci4"4rn%5Kp&>-Vf\#!)rr_95f\lE%"5DJ\kK!T&r;P=KmIgJZ -p&<SDo\]ZKoD[ABp@\Fcm/GW;rJ1CTgXt0Ap&>$hrn%5=kPp&~> -!Zh=)r4;sJp\t?M]=[dnrrC^M])^+RrrBt8rrCOG])^O`rrN,;oD\mfUOUkp!5JHD!:K[_!<2l- -"nM]nb];P3rrf;C6q#_Srrf>i6k8JSrrZO_6rs9d"4Ffn`;BQ8r5SL2!SZc5rrL6To`##66p3I# -rrWs'7#p%%s5ESL#i+SBs6i?D-]@Zt"M5R?7*O]k#gN&Bs2.5m(WQ.J"Fqsi7/+e&!SceorrM<; -mf3=!!7LiG"&[email protected])jpjdIXT/=<lMlA~> -!Zh=)r4;sJp\t?M]=[dnrrC^M])^+RrrBt8rrCOG])^O`rrN,;oD\mfUOUkp!5JHD!:K[_!<2l- -"nM]nb];P3rrf;C6q#_Srrf>i6k8JSrrZO_6rs9d"4Ffn`;BQ8r5SL2!SZc5rrL6To`##66p3I# -rrWs'7#p%%s5ESL#i+SBs6i?D-]@Zt"M5R?7*O]k#gN&Bs2.5m(WQ.J"Fqsi7/+e&!SceorrM<; -mf3=!!7LiG"&[email protected])jpjdIXT/=<lMlA~> -!Zh=)r4;sJp\t?M]=[dnrrC^M])^+RrrBt8rrCOG])^O`rrN,;oD\mfUOUkp!5JHD!:K[_!<2l- -"nM]nb];P3rrf;C6q#_Srrf>i6k8JSrrZO_6rs9d"4Ffn`;BQ8r5SL2!SZc5rrL6To`##66p3I# -rrWs'7#p%%s5ESL#i+SBs6i?D-]@Zt"M5R?7*O]k#gN&Bs2.5m(WQ.J"Fqsi7/+e&!SceorrM<; -mf3=!!7LiG"&[email protected])jpjdIXT/=<lMlA~> -"!.E>/H&uWGk_4?XC2t&hZ!Um6iLKpEPqeU!Nm==rrAJc@0&Z7rr3%sBt![6!U2E,rrMo!m/I&" -r*]TWq>UHor*]UHci3qFSUgA4!NC.^rrFq?p&>%u_#438a`V$#Fo21>SLO=5!Hk=9rrIX^qu6]" -qYL3mju2l3!$0bm!oI+@o`#!G9q_Fa!Ki<5rrK`?iVrtr.,4\"!ER55rrIY@bPqUQnGW@fGGa6s -!Mju/rrf?1@<TfkrrKf@p&>'T2Z3RT*U<ZT~> -"!.E>/H&uWGk_4?XC2t&hZ!Um6iLKpEPqeU!Nm==rrAJc@0&Z7rr3%sBt![6!U2E,rrMo!m/I&" -r*]TWq>UHor*]UHci3qFSUgA4!NC.^rrFq?p&>%u_#438a`V$#Fo21>SLO=5!Hk=9rrIX^qu6]" -qYL3mju2l3!$0bm!oI+@o`#!G9q_Fa!Ki<5rrK`?iVrtr.,4\"!ER55rrIY@bPqUQnGW@fGGa6s -!Mju/rrf?1@<TfkrrKf@p&>'T2Z3RT*U<ZT~> -"!.E>/H&uWGk_4?XC2t&hZ!Um6iLKpEPqeU!Nm==rrAJc@0&Z7rr3%sBt![6!U2E,rrMo!m/I&" -r*]TWq>UHor*]UHci3qFSUgA4!NC.^rrFq?p&>%u_#438a`V$#Fo21>SLO=5!Hk=9rrIX^qu6]" -qYL3mju2l3!$0bm!oI+@o`#!G9q_Fa!Ki<5rrK`?iVrtr.,4\"!ER55rrIY@bPqUQnGW@fGGa6s -!Mju/rrf?1@<TfkrrKf@p&>'T2Z3RT*U<ZT~> -"!.E>Fn#D352P/r!Go">rs$)Ds8SmD(]"(;jFF`>s."Z-J$8PF!JQp-rrML@m/I&+mf*?sJ*6h2 -!mQ8Ap&>&lF34F_1[4T4!F<M>rrU8keG9+GR!:(SrrI8?nc&V>o(r@eju2l3!$0en"6=u%O7iMT -gjhP\r;Qe[MtR)N]4'_!"297-g\h'P<lXh4!J$`arrGO?rVlnBVV_=RUj2D/#+pAEs3lJ>rVlo3 [email protected]#ju3/;!$1A)J,~> -"!.E>Fn#D352P/r!Go">rs$)Ds8SmD(]"(;jFF`>s."Z-J$8PF!JQp-rrML@m/I&+mf*?sJ*6h2 -!mQ8Ap&>&lF34F_1[4T4!F<M>rrU8keG9+GR!:(SrrI8?nc&V>o(r@eju2l3!$0en"6=u%O7iMT -gjhP\r;Qe[MtR)N]4'_!"297-g\h'P<lXh4!J$`arrGO?rVlnBVV_=RUj2D/#+pAEs3lJ>rVlo3 [email protected]#ju3/;!$1A)J,~> -"!.E>Fn#D352P/r!Go">rs$)Ds8SmD(]"(;jFF`>s."Z-J$8PF!JQp-rrML@m/I&+mf*?sJ*6h2 -!mQ8Ap&>&lF34F_1[4T4!F<M>rrU8keG9+GR!:(SrrI8?nc&V>o(r@eju2l3!$0en"6=u%O7iMT -gjhP\r;Qe[MtR)N]4'_!"297-g\h'P<lXh4!J$`arrGO?rVlnBVV_=RUj2D/#+pAEs3lJ>rVlo3 [email protected]#ju3/;!$1A)J,~> -"!.E>Fo21B`NfH(A8hDG!C#B=rrB%tA-dJ\s0]Y*A7U/grro!)A9-Unq#:A2Yl=Y,]4(`5N:.e` -rsS:7]`8"tO7rV9LTgFO!$1t:!-S9O&?Pp3K(J/9A?l17I9dLsA@MR<!+u4@!5/(+!?L;$A,sX@ -rrHo@r;QeISb<!`WH8";!8%0["6KR_df!\YlMob-rr3C]n,M+2s.s7:o`+s/rF?$+s8Th3A,uW, -rrFq?p&>%u_#=96O,`u8"-Sf5.JNiGFf1%+A;L6b!gR;1rVm%pA.`0ZP5P=\ju2l3!$1t:!.4]U% -&a.*Lh(\`A?>h2KC/YK!3uM&!omgerVm#hE4h0krVlrPA?Pn2"QaaBIA$N<!Ki<5rrK`?rVm2rJ -cEJqAD7(`h>Updc%Y]errTT=df'1K_1:6*g\h'P<lXh4!J$a=rrBe3A-;i2s1/,>"DIi8`ioCA# -^67J_6fbDA97bM!36$/!-n8<'sV9g<Fg^]s*(>sA7UJqs/40BEb(..rrA/[A,qbkrr@uVA,qqpr -s4?iA;[1-s8RZLA,r>%rrVe&PlC[`45p2=!K3->rrKf@p&>'T2Z3RT*W?!?Us]5;rF?8Ss8R9Bc -X^S/s8RT~> -"!.E>Fo21B`NfH(A8hDG!C#B=rrB%tA-dJ\s0]Y*A7U/grro!)A9-Unq#:A2Yl=Y,]4(`5N:.e` -rsS:7]`8"tO7rV9LTgFO!$1t:!-S9O&?Pp3K(J/9A?l17I9dLsA@MR<!+u4@!5/(+!?L;$A,sX@ -rrHo@r;QeISb<!`WH8";!8%0["6KR_df!\YlMob-rr3C]n,M+2s.s7:o`+s/rF?$+s8Th3A,uW, -rrFq?p&>%u_#=96O,`u8"-Sf5.JNiGFf1%+A;L6b!gR;1rVm%pA.`0ZP5P=\ju2l3!$1t:!.4]U% -&a.*Lh(\`A?>h2KC/YK!3uM&!omgerVm#hE4h0krVlrPA?Pn2"QaaBIA$N<!Ki<5rrK`?rVm2rJ -cEJqAD7(`h>Updc%Y]errTT=df'1K_1:6*g\h'P<lXh4!J$a=rrBe3A-;i2s1/,>"DIi8`ioCA# -^67J_6fbDA97bM!36$/!-n8<'sV9g<Fg^]s*(>sA7UJqs/40BEb(..rrA/[A,qbkrr@uVA,qqpr -s4?iA;[1-s8RZLA,r>%rrVe&PlC[`45p2=!K3->rrKf@p&>'T2Z3RT*W?!?Us]5;rF?8Ss8R9Bc -X^S/s8RT~> -"!.E>Fo21B`NfH(A8hDG!C#B=rrB%tA-dJ\s0]Y*A7U/grro!)A9-Unq#:A2Yl=Y,]4(`5N:.e` -rsS:7]`8"tO7rV9LTgFO!$1t:!-S9O&?Pp3K(J/9A?l17I9dLsA@MR<!+u4@!5/(+!?L;$A,sX@ -rrHo@r;QeISb<!`WH8";!8%0["6KR_df!\YlMob-rr3C]n,M+2s.s7:o`+s/rF?$+s8Th3A,uW, -rrFq?p&>%u_#=96O,`u8"-Sf5.JNiGFf1%+A;L6b!gR;1rVm%pA.`0ZP5P=\ju2l3!$1t:!.4]U% -&a.*Lh(\`A?>h2KC/YK!3uM&!omgerVm#hE4h0krVlrPA?Pn2"QaaBIA$N<!Ki<5rrK`?rVm2rJ -cEJqAD7(`h>Updc%Y]errTT=df'1K_1:6*g\h'P<lXh4!J$a=rrBe3A-;i2s1/,>"DIi8`ioCA# -^67J_6fbDA97bM!36$/!-n8<'sV9g<Fg^]s*(>sA7UJqs/40BEb(..rrA/[A,qbkrr@uVA,qqpr -s4?iA;[1-s8RZLA,r>%rrVe&PlC[`45p2=!K3->rrKf@p&>'T2Z3RT*W?!?Us]5;rF?8Ss8R9Bc -X^S/s8RT~> -"!.E>+oY"=nGiOLJ4Q':Y\F(9!C#B=rrC[M[h5T^s+Mqh\$r2\s.PF]\#s&_q#:A21B'fKA^g\. -li/"%rrth%K`D'Nb5VDAU3cP5!$2";!HG1>[hOJ#s2@f;\$pmts21i\\$t2ns'E/5[fL`lq#:@. ->Q2)4ER=CY!i_%@r;QeISb<!`WH8%<!Tdkj[f]k-he;tt"'>BB;Z?\)39C2.O@Y5;[oi[TF8`QM ->5uWerO2c3r;QdRl1P&W?,6F="cnXr84[+erreOOo(+=)rsNs&T!u2#L]@D"[ho#C"k:"&Y.)XQ -rrM7?o`"n3r;QeBVuF.6Lo^P:UT.b3KX(P47fKjtJ@GOX!mgp]rVm"#a8_-[rVlr7!.+YE"nc'C -s*^R<rrJ7?p&>')@K$34qP-T)\$ol2lsTh%!c/D1rr3%Q!4Dh+"j;#Cs$>E;rrH0?p&>&CSc&Kf -iVg4f>lVWdrjN$<@K3$Jrr3BIs8SH4NjlL"D?$enrjMp-F85bQgUAo,\&JCIG;#Mr\"8?Sen[gZ -\!r?VOdQ-W"IG;Os2k8L%$R(YKV,`_S1]'dI\ZnO!K<iRrrVKd:]C@p45p2=!K3->rrKf@p&>'T -2Z3R\*WQ/Q8;ZX3=oGf:EmOcj88>WCDUnc;~> -"!.E>+oY"=nGiOLJ4Q':Y\F(9!C#B=rrC[M[h5T^s+Mqh\$r2\s.PF]\#s&_q#:A21B'fKA^g\. -li/"%rrth%K`D'Nb5VDAU3cP5!$2";!HG1>[hOJ#s2@f;\$pmts21i\\$t2ns'E/5[fL`lq#:@. ->Q2)4ER=CY!i_%@r;QeISb<!`WH8%<!Tdkj[f]k-he;tt"'>BB;Z?\)39C2.O@Y5;[oi[TF8`QM ->5uWerO2c3r;QdRl1P&W?,6F="cnXr84[+erreOOo(+=)rsNs&T!u2#L]@D"[ho#C"k:"&Y.)XQ -rrM7?o`"n3r;QeBVuF.6Lo^P:UT.b3KX(P47fKjtJ@GOX!mgp]rVm"#a8_-[rVlr7!.+YE"nc'C -s*^R<rrJ7?p&>')@K$34qP-T)\$ol2lsTh%!c/D1rr3%Q!4Dh+"j;#Cs$>E;rrH0?p&>&CSc&Kf -iVg4f>lVWdrjN$<@K3$Jrr3BIs8SH4NjlL"D?$enrjMp-F85bQgUAo,\&JCIG;#Mr\"8?Sen[gZ -\!r?VOdQ-W"IG;Os2k8L%$R(YKV,`_S1]'dI\ZnO!K<iRrrVKd:]C@p45p2=!K3->rrKf@p&>'T -2Z3R\*WQ/Q8;ZX3=oGf:EmOcj88>WCDUnc;~> -"!.E>+oY"=nGiOLJ4Q':Y\F(9!C#B=rrC[M[h5T^s+Mqh\$r2\s.PF]\#s&_q#:A21B'fKA^g\. -li/"%rrth%K`D'Nb5VDAU3cP5!$2";!HG1>[hOJ#s2@f;\$pmts21i\\$t2ns'E/5[fL`lq#:@. ->Q2)4ER=CY!i_%@r;QeISb<!`WH8%<!Tdkj[f]k-he;tt"'>BB;Z?\)39C2.O@Y5;[oi[TF8`QM ->5uWerO2c3r;QdRl1P&W?,6F="cnXr84[+erreOOo(+=)rsNs&T!u2#L]@D"[ho#C"k:"&Y.)XQ -rrM7?o`"n3r;QeBVuF.6Lo^P:UT.b3KX(P47fKjtJ@GOX!mgp]rVm"#a8_-[rVlr7!.+YE"nc'C -s*^R<rrJ7?p&>')@K$34qP-T)\$ol2lsTh%!c/D1rr3%Q!4Dh+"j;#Cs$>E;rrH0?p&>&CSc&Kf -iVg4f>lVWdrjN$<@K3$Jrr3BIs8SH4NjlL"D?$enrjMp-F85bQgUAo,\&JCIG;#Mr\"8?Sen[gZ -\!r?VOdQ-W"IG;Os2k8L%$R(YKV,`_S1]'dI\ZnO!K<iRrrVKd:]C@p45p2=!K3->rrKf@p&>'T -2Z3R\*WQ/Q8;ZX3=oGf:EmOcj88>WCDUnc;~> -"!.E>@K+aVr;Q`rJHPNDmOnJ<!C#B9rt+!Ns+Ppms8UV?s*^O>s62?6rrm5&e/6]nr;Qa;qYpcM -e=;Er;p,+>!MXo6rr=);rrG.?rr3F`PlLb'g].;(SH&WV0`:qO+T;<>"6]4S55Zo5_Z/6Crr3"* -^@hL,Lm7f:!Ip[5rrK*?rVloQ62gfch#5\orVlsEq8uV7rsUmKs8Sm*mJm2,s1)Y<rr^sSZ#'C= -!$2";!Aj!5rrHE@rVmFi(nB+*^0^i/Nq35A.KBF4r$r%hs)j7ms8U&>rVlj<qu6[Ho(r@eju2l3 -!$2";!CPT?rs\;\s#T/u49(2%s"_RmrrI\?rr3&oeE6c1"\.)Cs,E*<rrViBl2L\d6/2G>IA$N< -!Ki<5rrK`?r;R&L5j&+H4l>?\rr3&Z/Ed$4$gQ75s8UhXjo>?Hg\h'P<lXh4!J$a8rrX;AWH8(= -"TI-TTmQe=$m#BJQu;Bms6)??OGs2="8^pTS,=c:AcD]17+hJ<&U0-*s8U)>s8Prqqu=?:s(8b> -rrKN?qu7)+E;rqZs8VbKGlLIarr3#elMgebr7'^)rrG4?rr3"WP5YC]]jUO5!Tl<<rs0Y*.qmH" -s8O,<rsE/Hs6;)ns8Q3>s*t~> -"!.E>@K+aVr;Q`rJHPNDmOnJ<!C#B9rt+!Ns+Ppms8UV?s*^O>s62?6rrm5&e/6]nr;Qa;qYpcM -e=;Er;p,+>!MXo6rr=);rrG.?rr3F`PlLb'g].;(SH&WV0`:qO+T;<>"6]4S55Zo5_Z/6Crr3"* -^@hL,Lm7f:!Ip[5rrK*?rVloQ62gfch#5\orVlsEq8uV7rsUmKs8Sm*mJm2,s1)Y<rr^sSZ#'C= -!$2";!Aj!5rrHE@rVmFi(nB+*^0^i/Nq35A.KBF4r$r%hs)j7ms8U&>rVlj<qu6[Ho(r@eju2l3 -!$2";!CPT?rs\;\s#T/u49(2%s"_RmrrI\?rr3&oeE6c1"\.)Cs,E*<rrViBl2L\d6/2G>IA$N< -!Ki<5rrK`?r;R&L5j&+H4l>?\rr3&Z/Ed$4$gQ75s8UhXjo>?Hg\h'P<lXh4!J$a8rrX;AWH8(= -"TI-TTmQe=$m#BJQu;Bms6)??OGs2="8^pTS,=c:AcD]17+hJ<&U0-*s8U)>s8Prqqu=?:s(8b> -rrKN?qu7)+E;rqZs8VbKGlLIarr3#elMgebr7'^)rrG4?rr3"WP5YC]]jUO5!Tl<<rs0Y*.qmH" -s8O,<rsE/Hs6;)ns8Q3>s*t~> -"!.E>@K+aVr;Q`rJHPNDmOnJ<!C#B9rt+!Ns+Ppms8UV?s*^O>s62?6rrm5&e/6]nr;Qa;qYpcM -e=;Er;p,+>!MXo6rr=);rrG.?rr3F`PlLb'g].;(SH&WV0`:qO+T;<>"6]4S55Zo5_Z/6Crr3"* -^@hL,Lm7f:!Ip[5rrK*?rVloQ62gfch#5\orVlsEq8uV7rsUmKs8Sm*mJm2,s1)Y<rr^sSZ#'C= -!$2";!Aj!5rrHE@rVmFi(nB+*^0^i/Nq35A.KBF4r$r%hs)j7ms8U&>rVlj<qu6[Ho(r@eju2l3 -!$2";!CPT?rs\;\s#T/u49(2%s"_RmrrI\?rr3&oeE6c1"\.)Cs,E*<rrViBl2L\d6/2G>IA$N< -!Ki<5rrK`?r;R&L5j&+H4l>?\rr3&Z/Ed$4$gQ75s8UhXjo>?Hg\h'P<lXh4!J$a8rrX;AWH8(= -"TI-TTmQe=$m#BJQu;Bms6)??OGs2="8^pTS,=c:AcD]17+hJ<&U0-*s8U)>s8Prqqu=?:s(8b> -rrKN?qu7)+E;rqZs8VbKGlLIarr3#elMgebr7'^)rrG4?rr3"WP5YC]]jUO5!Tl<<rs0Y*.qmH" -s8O,<rsE/Hs6;)ns8Q3>s*t~> -"!.E>Fo)+AJXc]>rq-0h!C#B>rrMn@rZ)+Z5Q?G(rr38S8H4C6nF56up\tD5YlF#Po_e^h*VfX; -WcJ,<(]GEU&pj9O!$2";!BD(t*<[.Hs8O,=rrr2os8VI?r;Qfh+9!8_PktFNr:'daqu6\'^@qR. -Wd+@:rrIV?p&>&lF8c+>rO`"K"3gbn9)\bl,PfJ[rr3-]jo>@VGlI^FoDc@2qu6]%%/h1H.fB;I -1[4T4!F<M>rt(6GqZ$TP56$WZ*??+'>Q=KrnH8IaFf56=!QA.=rr=):rrFV?qYpTY2Y@"L*W5p< -ofW3o%!VLH3o]*[s,*$?1@+r>!J-a8rr=\N*WHZN[eTk&+9!8^%MHbZrrJ7?p&>')@Jp-+cqXN> -"82WS3;rjX2<Xf8!S.q`*<HH`q>L<o<lXh4!J$a>rrMt`r>btZs8%lW*<[SWs.Of=rraABs-SK= -rrhOCs,`3:rrDlmnGr7]rrGO?rVlnBVZ-T(``E->;9](?qtC&%(&f3V)0#WK;Z7[>'QF(PaSu2B -Uj2q47/e2-Dts,-!B]9>rrJ%@[email protected]#ju3/;!ua,Xm/?qa')`gR'Ysb61B.:TpAFr@~> -"!.E>Fo)+AJXc]>rq-0h!C#B>rrMn@rZ)+Z5Q?G(rr38S8H4C6nF56up\tD5YlF#Po_e^h*VfX; -WcJ,<(]GEU&pj9O!$2";!BD(t*<[.Hs8O,=rrr2os8VI?r;Qfh+9!8_PktFNr:'daqu6\'^@qR. -Wd+@:rrIV?p&>&lF8c+>rO`"K"3gbn9)\bl,PfJ[rr3-]jo>@VGlI^FoDc@2qu6]%%/h1H.fB;I -1[4T4!F<M>rt(6GqZ$TP56$WZ*??+'>Q=KrnH8IaFf56=!QA.=rr=):rrFV?qYpTY2Y@"L*W5p< -ofW3o%!VLH3o]*[s,*$?1@+r>!J-a8rr=\N*WHZN[eTk&+9!8^%MHbZrrJ7?p&>')@Jp-+cqXN> -"82WS3;rjX2<Xf8!S.q`*<HH`q>L<o<lXh4!J$a>rrMt`r>btZs8%lW*<[SWs.Of=rraABs-SK= -rrhOCs,`3:rrDlmnGr7]rrGO?rVlnBVZ-T(``E->;9](?qtC&%(&f3V)0#WK;Z7[>'QF(PaSu2B -Uj2q47/e2-Dts,-!B]9>rrJ%@[email protected]#ju3/;!ua,Xm/?qa')`gR'Ysb61B.:TpAFr@~> -"!.E>Fo)+AJXc]>rq-0h!C#B>rrMn@rZ)+Z5Q?G(rr38S8H4C6nF56up\tD5YlF#Po_e^h*VfX; -WcJ,<(]GEU&pj9O!$2";!BD(t*<[.Hs8O,=rrr2os8VI?r;Qfh+9!8_PktFNr:'daqu6\'^@qR. -Wd+@:rrIV?p&>&lF8c+>rO`"K"3gbn9)\bl,PfJ[rr3-]jo>@VGlI^FoDc@2qu6]%%/h1H.fB;I -1[4T4!F<M>rt(6GqZ$TP56$WZ*??+'>Q=KrnH8IaFf56=!QA.=rr=):rrFV?qYpTY2Y@"L*W5p< -ofW3o%!VLH3o]*[s,*$?1@+r>!J-a8rr=\N*WHZN[eTk&+9!8^%MHbZrrJ7?p&>')@Jp-+cqXN> -"82WS3;rjX2<Xf8!S.q`*<HH`q>L<o<lXh4!J$a>rrMt`r>btZs8%lW*<[SWs.Of=rraABs-SK= -rrhOCs,`3:rrDlmnGr7]rrGO?rVlnBVZ-T(``E->;9](?qtC&%(&f3V)0#WK;Z7[>'QF(PaSu2B -Uj2q47/e2-Dts,-!B]9>rrJ%@[email protected]#ju3/;!ua,Xm/?qa')`gR'Ysb61B.:TpAFr@~> -"!.E>Fo)+=JXcK8!C#B>s8S,ZrrrD25Q?G(rr35R8H7pi/1a!Yrs!;Ds8UtYjSf)Y*W?!=j7N?N -"KHMB;p,+>!MXo6rrGC?rVlmYj8/cU*W#d@m4eS?kjSQ)rVln:Xn_nrf_tgN?G?F=!qFb*rVlrl -R#h.E!q(Q@p&>&lF8c+>q;2)M""Woj9)\bl,PfJ[rr3-]jo>@VGl7RB\RYU<"5*XYD"mr11[4T4 -!F<M>rrJ1?rr3#U55bE]o-sG6#'Ggrs8U&>rVlj<qu6dKo)J:BrVlo\2Y@"L*W5p<htd9O%$HMJ -3o]*[s,*$?1;s1l!J-a>rrVrDjneuXNK=&<!qat*qYpSET`"fjOc/o4!P;e<rrLJ@r;QfZ3<&pZ -i@O0krrVK7o(r@e6/2>;!ER55rrIY@rVlo(C]=>:fH("]kPkJiq'?!6HiO-#)uor*K`:uSkV`C% -N;ihXqVLrG#=R5EpYc'qVZ-T!``E->;9\t<!G8h<rt/POs8VeRE;rqZs8V_IGlQ^rrr3"hJc>ZN -r6sX(rrucDs8VSDV>^Dp]jUO5!Tl<<rs0Y+/83N"s8O,9rrMC?qu;0~> -"!.E>Fo)+=JXcK8!C#B>s8S,ZrrrD25Q?G(rr35R8H7pi/1a!Yrs!;Ds8UtYjSf)Y*W?!=j7N?N -"KHMB;p,+>!MXo6rrGC?rVlmYj8/cU*W#d@m4eS?kjSQ)rVln:Xn_nrf_tgN?G?F=!qFb*rVlrl -R#h.E!q(Q@p&>&lF8c+>q;2)M""Woj9)\bl,PfJ[rr3-]jo>@VGl7RB\RYU<"5*XYD"mr11[4T4 -!F<M>rrJ1?rr3#U55bE]o-sG6#'Ggrs8U&>rVlj<qu6dKo)J:BrVlo\2Y@"L*W5p<htd9O%$HMJ -3o]*[s,*$?1;s1l!J-a>rrVrDjneuXNK=&<!qat*qYpSET`"fjOc/o4!P;e<rrLJ@r;QfZ3<&pZ -i@O0krrVK7o(r@e6/2>;!ER55rrIY@rVlo(C]=>:fH("]kPkJiq'?!6HiO-#)uor*K`:uSkV`C% -N;ihXqVLrG#=R5EpYc'qVZ-T!``E->;9\t<!G8h<rt/POs8VeRE;rqZs8V_IGlQ^rrr3"hJc>ZN -r6sX(rrucDs8VSDV>^Dp]jUO5!Tl<<rs0Y+/83N"s8O,9rrMC?qu;0~> -"!.E>Fo)+=JXcK8!C#B>s8S,ZrrrD25Q?G(rr35R8H7pi/1a!Yrs!;Ds8UtYjSf)Y*W?!=j7N?N -"KHMB;p,+>!MXo6rrGC?rVlmYj8/cU*W#d@m4eS?kjSQ)rVln:Xn_nrf_tgN?G?F=!qFb*rVlrl -R#h.E!q(Q@p&>&lF8c+>q;2)M""Woj9)\bl,PfJ[rr3-]jo>@VGl7RB\RYU<"5*XYD"mr11[4T4 -!F<M>rrJ1?rr3#U55bE]o-sG6#'Ggrs8U&>rVlj<qu6dKo)J:BrVlo\2Y@"L*W5p<htd9O%$HMJ -3o]*[s,*$?1;s1l!J-a>rrVrDjneuXNK=&<!qat*qYpSET`"fjOc/o4!P;e<rrLJ@r;QfZ3<&pZ -i@O0krrVK7o(r@e6/2>;!ER55rrIY@rVlo(C]=>:fH("]kPkJiq'?!6HiO-#)uor*K`:uSkV`C% -N;ihXqVLrG#=R5EpYc'qVZ-T!``E->;9\t<!G8h<rt/POs8VeRE;rqZs8V_IGlQ^rrr3"hJc>ZN -r6sX(rrucDs8VSDV>^Dp]jUO5!Tl<<rs0Y+/83N"s8O,9rrMC?qu;0~> -"!.E><;j6._>jOdS,<3sfsWK(^&S,8L:4Ot5<o1%Qi@!feO]_7NW-?d`V9B5Cp<p=%(fsJe$c\" -[^O]cMOa[S"Ho5R;p,+>!MXo6rrM@?rVln=WrBF,f`(mN*W#dAnm/]4I)#\h[JmT8GfBIX!*]?0 -!HHNdrrT>'N;`bW_HQg9!M+c5rrK*?rVlo[Ac9%>>P6lerO)f1qq5fb[KU*@s8Sm>r;QfBF8`NL ->Q;`frO2V'!Aj!5rrHE@rVlo%L&SL]W-/%<!V7c7rsa*)S[PttL]@D![M?6mrVm"5Z*os^rVlo\ -2Y@"L*W5p<Gc(JK%#:qa3o]FGs,*$?1.V>P!J%]ZrrUOIC&7i1NK=&<!l+e^qYpSET`"fjOc/o4 -!P;e<rrLJ@r;Qfa@K*\:C_,_.rrRiR[JKn(6/2>;!ER55rrIY@rVlo>GQ,#R<ZM.VHN(>]ZXWsI -I^Z[i'EA*"K`:uSkV`CEL&SL]WH@k6#C<5TTn35fVZ-T!``E->;9\t<!Ki`J[M6pbs,3AT[\#9n -s+R&Q[[]!qs*pdB[K2>`rr3&c!)NRn"CAOFOdu@L!PMn6rrM7?r;R$Cs8Tc(M<Y%DrO)jhs8V@> -qu;0~> -"!.E><;j6._>jOdS,<3sfsWK(^&S,8L:4Ot5<o1%Qi@!feO]_7NW-?d`V9B5Cp<p=%(fsJe$c\" -[^O]cMOa[S"Ho5R;p,+>!MXo6rrM@?rVln=WrBF,f`(mN*W#dAnm/]4I)#\h[JmT8GfBIX!*]?0 -!HHNdrrT>'N;`bW_HQg9!M+c5rrK*?rVlo[Ac9%>>P6lerO)f1qq5fb[KU*@s8Sm>r;QfBF8`NL ->Q;`frO2V'!Aj!5rrHE@rVlo%L&SL]W-/%<!V7c7rsa*)S[PttL]@D![M?6mrVm"5Z*os^rVlo\ -2Y@"L*W5p<Gc(JK%#:qa3o]FGs,*$?1.V>P!J%]ZrrUOIC&7i1NK=&<!l+e^qYpSET`"fjOc/o4 -!P;e<rrLJ@r;Qfa@K*\:C_,_.rrRiR[JKn(6/2>;!ER55rrIY@rVlo>GQ,#R<ZM.VHN(>]ZXWsI -I^Z[i'EA*"K`:uSkV`CEL&SL]WH@k6#C<5TTn35fVZ-T!``E->;9\t<!Ki`J[M6pbs,3AT[\#9n -s+R&Q[[]!qs*pdB[K2>`rr3&c!)NRn"CAOFOdu@L!PMn6rrM7?r;R$Cs8Tc(M<Y%DrO)jhs8V@> -qu;0~> -"!.E><;j6._>jOdS,<3sfsWK(^&S,8L:4Ot5<o1%Qi@!feO]_7NW-?d`V9B5Cp<p=%(fsJe$c\" -[^O]cMOa[S"Ho5R;p,+>!MXo6rrM@?rVln=WrBF,f`(mN*W#dAnm/]4I)#\h[JmT8GfBIX!*]?0 -!HHNdrrT>'N;`bW_HQg9!M+c5rrK*?rVlo[Ac9%>>P6lerO)f1qq5fb[KU*@s8Sm>r;QfBF8`NL ->Q;`frO2V'!Aj!5rrHE@rVlo%L&SL]W-/%<!V7c7rsa*)S[PttL]@D![M?6mrVm"5Z*os^rVlo\ -2Y@"L*W5p<Gc(JK%#:qa3o]FGs,*$?1.V>P!J%]ZrrUOIC&7i1NK=&<!l+e^qYpSET`"fjOc/o4 -!P;e<rrLJ@r;Qfa@K*\:C_,_.rrRiR[JKn(6/2>;!ER55rrIY@rVlo>GQ,#R<ZM.VHN(>]ZXWsI -I^Z[i'EA*"K`:uSkV`CEL&SL]WH@k6#C<5TTn35fVZ-T!``E->;9\t<!Ki`J[M6pbs,3AT[\#9n -s+R&Q[[]!qs*pdB[K2>`rr3&c!)NRn"CAOFOdu@L!PMn6rrM7?r;R$Cs8Tc(M<Y%DrO)jhs8V@> -qu;0~> -!Zh<`r+6(Zs8TIDqu6Z!ral.Qrr38'AnGcCN;p?%rr3,`OoNUMral1M\bQ1*VpPGC"4j.FUASU* -V>pRRral;$s8SGCrr3#>YP.ttno2/<rr@6AAcSt4rr@9=rr_7mB".d>!,):C!5/(+!;?A'!65!; -!mYDhrVloQSGW<fj+Xf2rs,MlR@3@?s4I9^"6KR_e,<k\lMpn0ral>jGQ7]bY5A5!_u40Lq>^K/ -rFQ<6s8UK7R31\drrZPJRA9c]!36$1!."[email protected];Qh8As<5o"1">P -2Y@"O(n$f.rr2tGral;2s8RrDrr3,2^AftMral/8rVlrZAu5A(!P?#CrrUkcYPS8)Z,ZhDp6h=M -MtR)U]/uFKj8]/>Pl(I\hYq*gc8FearrTT?e,'(MNTpKChjKlh`qB?:J6nY3qu?]2ral<"GQ7]S -ral.Frr383AnL-Fs8TpCrr3,kL&_1Rral.Uo`#,DAqU-`Xi^SB"l5XIs-DU?rrA2\AcS"nrt(-$ -AqnR0s8RjdArFa5s8R]MAcSS(rrVe(Q2L[^AcS:urr]!`EFAJ>#Nd.sRF;-8GQ%ODV:,D=rFQ2O -s8VYCqu;0~> -!Zh<`r+6(Zs8TIDqu6Z!ral.Qrr38'AnGcCN;p?%rr3,`OoNUMral1M\bQ1*VpPGC"4j.FUASU* -V>pRRral;$s8SGCrr3#>YP.ttno2/<rr@6AAcSt4rr@9=rr_7mB".d>!,):C!5/(+!;?A'!65!; -!mYDhrVloQSGW<fj+Xf2rs,MlR@3@?s4I9^"6KR_e,<k\lMpn0ral>jGQ7]bY5A5!_u40Lq>^K/ -rFQ<6s8UK7R31\drrZPJRA9c]!36$1!."[email protected];Qh8As<5o"1">P -2Y@"O(n$f.rr2tGral;2s8RrDrr3,2^AftMral/8rVlrZAu5A(!P?#CrrUkcYPS8)Z,ZhDp6h=M -MtR)U]/uFKj8]/>Pl(I\hYq*gc8FearrTT?e,'(MNTpKChjKlh`qB?:J6nY3qu?]2ral<"GQ7]S -ral.Frr383AnL-Fs8TpCrr3,kL&_1Rral.Uo`#,DAqU-`Xi^SB"l5XIs-DU?rrA2\AcS"nrt(-$ -AqnR0s8RjdArFa5s8R]MAcSS(rrVe(Q2L[^AcS:urr]!`EFAJ>#Nd.sRF;-8GQ%ODV:,D=rFQ2O -s8VYCqu;0~> -!Zh<`r+6(Zs8TIDqu6Z!ral.Qrr38'AnGcCN;p?%rr3,`OoNUMral1M\bQ1*VpPGC"4j.FUASU* -V>pRRral;$s8SGCrr3#>YP.ttno2/<rr@6AAcSt4rr@9=rr_7mB".d>!,):C!5/(+!;?A'!65!; -!mYDhrVloQSGW<fj+Xf2rs,MlR@3@?s4I9^"6KR_e,<k\lMpn0ral>jGQ7]bY5A5!_u40Lq>^K/ -rFQ<6s8UK7R31\drrZPJRA9c]!36$1!."[email protected];Qh8As<5o"1">P -2Y@"O(n$f.rr2tGral;2s8RrDrr3,2^AftMral/8rVlrZAu5A(!P?#CrrUkcYPS8)Z,ZhDp6h=M -MtR)U]/uFKj8]/>Pl(I\hYq*gc8FearrTT?e,'(MNTpKChjKlh`qB?:J6nY3qu?]2ral<"GQ7]S -ral.Frr383AnL-Fs8TpCrr3,kL&_1Rral.Uo`#,DAqU-`Xi^SB"l5XIs-DU?rrA2\AcS"nrt(-$ -AqnR0s8RjdArFa5s8R]MAcSS(rrVe(Q2L[^AcS:urr]!`EFAJ>#Nd.sRF;-8GQ%ODV:,D=rFQ2O -s8VYCqu;0~> -!$0_l!I^U>rrL>?c2Rh,WkJE5h6Z_Q!7h($!9VW-!65"j!;,sa"0dE1O3[b-Tn@ugo`##OK7gQ" -rrFn@g]%9Grdt3jp&>$Krdt4,o)A_JkOAKOfD^C&j7WEP_>]&eqXFLccb08W!$.[2"-%qcZ1\+s -oDX@BaQNSR~> -!$0_l!I^U>rrL>?c2Rh,WkJE5h6Z_Q!7h($!9VW-!65"j!;,sa"0dE1O3[b-Tn@ugo`##OK7gQ" -rrFn@g]%9Grdt3jp&>$Krdt4,o)A_JkOAKOfD^C&j7WEP_>]&eqXFLccb08W!$.[2"-%qcZ1\+s -oDX@BaQNSR~> -!$0_l!I^U>rrL>?c2Rh,WkJE5h6Z_Q!7h($!9VW-!65"j!;,sa"0dE1O3[b-Tn@ugo`##OK7gQ" -rrFn@g]%9Grdt3jp&>$Krdt4,o)A_JkOAKOfD^C&j7WEP_>]&eqXFLccb08W!$.[2"-%qcZ1\+s -oDX@BaQNSR~> -!$0_l!R>og?NFuWJcC<$dJj5&laHfo2<W*]!Si8*?N?dNs6ou<~> -!$0_l!R>og?NFuWJcC<$dJj5&laHfo2<W*]!Si8*?N?dNs6ou<~> -!$0_l!R>og?NFuWJcC<$dJj5&laHfo2<W*]!Si8*?N?dNs6ou<~> -!$0\k!7h(]!6TlmJcF*s!Qk2HrrL:<aSu7trk&7.JcG3=J,~> -!$0\k!7h(]!6TlmJcF*s!Qk2HrrL:<aSu7trk&7.JcG3=J,~> -!$0\k!7h(]!6TlmJcF*s!Qk2HrrL:<aSu7trk&7.JcG3=J,~> -!$..#"#_JQH%H!Hs+13$s7lVE~> -!$..#"#_JQH%H!Hs+13$s7lVE~> -!$..#"#_JQH%H!Hs+13$s7lVE~> -!$1P.",Is-hrXk>C)m`\rrQ[N'D)5,m2m?Wo_8@e73")Lrr`#hZsnUdJcC<$JcGNFJ,~> -!$1P.",Is-hrXk>C)m`\rrQ[N'D)5,m2m?Wo_8@e73")Lrr`#hZsnUdJcC<$JcGNFJ,~> -!$1P.",Is-hrXk>C)m`\rrQ[N'D)5,m2m?Wo_8@e73")Lrr`#hZsnUdJcC<$JcGNFJ,~> -!$1J,!C#B#rr=)9rr=)2rraJBs/L,5rrMX?lMgms@Y+Q1s+13$s7lVE~> -!$1J,!C#B#rr=)9rr=)2rraJBs/L,5rrMX?lMgms@Y+Q1s+13$s7lVE~> -!$1J,!C#B#rr=)9rr=)2rraJBs/L,5rrMX?lMgms@Y+Q1s+13$s7lVE~> -#9Ej*e^C_-[/U(*g&A5V[f$.+52Q#5"kqkTZ*D%BrrC@DYlMW<rr=)9rr=)9rrKOBr;R!Er;X^+ -s8Tq7YlN)JrrMX?r;Qc0rilIPrr2u0rilIRr;Qf0@Y+Q1s+13$s7lVE~> -#9Ej*e^C_-[/U(*g&A5V[f$.+52Q#5"kqkTZ*D%BrrC@DYlMW<rr=)9rr=)9rrKOBr;R!Er;X^+ -s8Tq7YlN)JrrMX?r;Qc0rilIPrr2u0rilIRr;Qf0@Y+Q1s+13$s7lVE~> -#9Ej*e^C_-[/U(*g&A5V[f$.+52Q#5"kqkTZ*D%BrrC@DYlMW<rr=)9rr=)9rrKOBr;R!Er;X^+ -s8Tq7YlN)JrrMX?r;Qc0rilIPrr2u0rilIRr;Qf0@Y+Q1s+13$s7lVE~> -$Q]8F7!rccCo%*_JGs<bDQ*O6!C#B6rs=B]GACu7ZN&$mrbDOU[f-4+*W#d9*W#d:$nqPY!?h=< -rr@rUCB8b%rr3#h/,fJKY]9YX"F\VrXDe)R!IiJqrrK`@JcC<$JcC<$q#>j~> -$Q]8F7!rccCo%*_JGs<bDQ*O6!C#B6rs=B]GACu7ZN&$mrbDOU[f-4+*W#d9*W#d:$nqPY!?h=< -rr@rUCB8b%rr3#h/,fJKY]9YX"F\VrXDe)R!IiJqrrK`@JcC<$JcC<$q#>j~> -$Q]8F7!rccCo%*_JGs<bDQ*O6!C#B6rs=B]GACu7ZN&$mrbDOU[f-4+*W#d9*W#d:$nqPY!?h=< -rr@rUCB8b%rr3#h/,fJKY]9YX"F\VrXDe)R!IiJqrrK`@JcC<$JcC<$q#>j~> -$Q]8F50X',pEop4IfB?JmOnJ<!C#B>rrBt7G70i=Ki$S)s4'[?I@pN=!R+C=rr=)9rr=)9rrJ+N -rr3,"G7Sk^q>UJiHN*pFnLOS<!CGQ?rrgQfs#K-=rrIq?rVlo1@Y+Q1s+13$s7lVE~> -$Q]8F50X',pEop4IfB?JmOnJ<!C#B>rrBt7G70i=Ki$S)s4'[?I@pN=!R+C=rr=)9rr=)9rrJ+N -rr3,"G7Sk^q>UJiHN*pFnLOS<!CGQ?rrgQfs#K-=rrIq?rVlo1@Y+Q1s+13$s7lVE~> -$Q]8F50X',pEop4IfB?JmOnJ<!C#B>rrBt7G70i=Ki$S)s4'[?I@pN=!R+C=rr=)9rr=)9rrJ+N -rr3,"G7Sk^q>UJiHN*pFnLOS<!CGQ?rrgQfs#K-=rrIq?rVlo1@Y+Q1s+13$s7lVE~> -"!.E>FoMCDpEop4/cJoS<%e.L!C#B>rrC[KV$"@0KpVf="P$'CI@pN=!R+C=rr=)9rr=)4rrg<; -'r/;;rr@KH=ogX0rr3#h/,fJK]Oh(G"JPkq3DocZ!AKc:rrK`@JcC<$JcC<$q#>j~> -"!.E>FoMCDpEop4/cJoS<%e.L!C#B>rrC[KV$"@0KpVf="P$'CI@pN=!R+C=rr=)9rr=)4rrg<; -'r/;;rr@KH=ogX0rr3#h/,fJK]Oh(G"JPkq3DocZ!AKc:rrK`@JcC<$JcC<$q#>j~> -"!.E>FoMCDpEop4/cJoS<%e.L!C#B>rrC[KV$"@0KpVf="P$'CI@pN=!R+C=rr=)9rr=)4rrg<; -'r/;;rr@KH=ogX0rr3#h/,fJK]Oh(G"JPkq3DocZ!AKc:rrK`@JcC<$JcC<$q#>j~> -"!.E>FoMCDpEop4?i@e@c2IYC52Q#5!JQm>rrgkCs*^O=rrL>?rVlj<qYpO9oD\h6r;HWrI&?nZ -!IK.lrrMX?r;Qc>rkS_oVuJcYrP8KqrVlo1@Y+Q1s+13$s7lVE~> -"!.E>FoMCDpEop4?i@e@c2IYC52Q#5!JQm>rrgkCs*^O=rrL>?rVlj<qYpO9oD\h6r;HWrI&?nZ -!IK.lrrMX?r;Qc>rkS_oVuJcYrP8KqrVlo1@Y+Q1s+13$s7lVE~> -"!.E>FoMCDpEop4?i@e@c2IYC52Q#5!JQm>rrgkCs*^O=rrL>?rVlj<qYpO9oD\h6r;HWrI&?nZ -!IK.lrrMX?r;Qc>rkS_oVuJcYrP8KqrVlo1@Y+Q1s+13$s7lVE~> -"!.E>FoMCDpEop4Ie`pD52Q#5$&'&,s8UV?s*^O=rrL>?rVlj<qYpO9qYpRH9)S\i+T23<##i\E -s2Pk#rr3#h/,fJK>2T>Z"HeWB3TKo7!P;fls+13$s+14Fs*t~> -"!.E>FoMCDpEop4Ie`pD52Q#5$&'&,s8UV?s*^O=rrL>?rVlj<qYpO9qYpRH9)S\i+T23<##i\E -s2Pk#rr3#h/,fJK>2T>Z"HeWB3TKo7!P;fls+13$s+14Fs*t~> -"!.E>FoMCDpEop4Ie`pD52Q#5$&'&,s8UV?s*^O=rrL>?rVlj<qYpO9qYpRH9)S\i+T23<##i\E -s2Pk#rr3#h/,fJK>2T>Z"HeWB3TKo7!P;fls+13$s+14Fs*t~> -"!.EGKDtlRpH/ESNrC%!/H5YPL`IBR1\^nUKp>sb*CB](rG_`V#YFsos(WPm*Duh9"CiGj*Ei=? -!@1&1rrG%UrVmK-9-#$QWJCNR73*9eGQ7^@4oQH)IJs3D2?"TrLAq2TkiSgQJcC<$JcC<$q#>j~> -"!.EGKDtlRpH/ESNrC%!/H5YPL`IBR1\^nUKp>sb*CB](rG_`V#YFsos(WPm*Duh9"CiGj*Ei=? -!@1&1rrG%UrVmK-9-#$QWJCNR73*9eGQ7^@4oQH)IJs3D2?"TrLAq2TkiSgQJcC<$JcC<$q#>j~> -"!.EGKDtlRpH/ESNrC%!/H5YPL`IBR1\^nUKp>sb*CB](rG_`V#YFsos(WPm*Duh9"CiGj*Ei=? -!@1&1rrG%UrVmK-9-#$QWJCNR73*9eGQ7^@4oQH)IJs3D2?"TrLAq2TkiSgQJcC<$JcC<$q#>j~> -!$1"t!JQlGrrY\J2M6S\JcC<$JcGNFJ,~> -!$1"t!JQlGrrY\J2M6S\JcC<$JcGNFJ,~> -!$1"t!JQlGrrY\J2M6S\JcC<$JcGNFJ,~> -!$1"t!JQkks+13$s+13Hs*t~> -!$1"t!JQkks+13$s+13Hs*t~> -!$1"t!JQkks+13$s+13Hs*t~> -!$1"t!P?=%s+13$s+13Hs*t~> -!$1"t!P?=%s+13$s+13Hs*t~> -!$1"t!P?=%s+13$s+13Hs*t~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$.@)!V"X)rrM*>JcC<$JcFU,J,~> -!$.@)!V"X)rrM*>JcC<$JcFU,J,~> -!$.@)!V"X)rrM*>JcC<$JcFU,J,~> -!Zh<ur1X1qq#:BJe+3M@aJ,F)rVlu=U8%SZrrL7$rr3)_`W*sUU&`:0rrBk4U&`O-rrVhUQM1=Z -Z2;uL!p66)pAY0d\+'Cuce\R"!9*mN!;6?k!jdU@JcC<$JcFX-J,~> -!Zh<ur1X1qq#:BJe+3M@aJ,F)rVlu=U8%SZrrL7$rr3)_`W*sUU&`:0rrBk4U&`O-rrVhUQM1=Z -Z2;uL!p66)pAY0d\+'Cuce\R"!9*mN!;6?k!jdU@JcC<$JcFX-J,~> -!Zh<ur1X1qq#:BJe+3M@aJ,F)rVlu=U8%SZrrL7$rr3)_`W*sUU&`:0rrBk4U&`O-rrVhUQM1=Z -Z2;uL!p66)pAY0d\+'Cuce\R"!9*mN!;6?k!jdU@JcC<$JcFX-J,~> -"!.E>3;n.'NqiVTQA4u1"0d(en,<7gXEkNRpAY/0Yl=Y*]4(_L=T*OGZ2">!V>Y]@9mZ7-!T6*5 -rrFP?lMgnMW9aHbgNpR2rrLi`rVloY3V!+Sc@:E's8V55rH\HtrVln5Z@W%,s+14-s*t~> -"!.E>3;n.'NqiVTQA4u1"0d(en,<7gXEkNRpAY/0Yl=Y*]4(_L=T*OGZ2">!V>Y]@9mZ7-!T6*5 -rrFP?lMgnMW9aHbgNpR2rrLi`rVloY3V!+Sc@:E's8V55rH\HtrVln5Z@W%,s+14-s*t~> -"!.E>3;n.'NqiVTQA4u1"0d(en,<7gXEkNRpAY/0Yl=Y*]4(_L=T*OGZ2">!V>Y]@9mZ7-!T6*5 -rrFP?lMgnMW9aHbgNpR2rrLi`rVloY3V!+Sc@:E's8V55rH\HtrVln5Z@W%,s+14-s*t~> -"!.E>Fn>V6QA4o/!A3d;rrFG?pAY/0Yl=Y*]4(_L\aTP"fm1^.rrW1;YP%nr..lg)!RjX#rrKB? -rVloY3V*1UdsK?QFR&nK;Z6UqU](2o_KOsjs+13$s5<p-~> -"!.E>Fn>V6QA4o/!A3d;rrFG?pAY/0Yl=Y*]4(_L\aTP"fm1^.rrW1;YP%nr..lg)!RjX#rrKB? -rVloY3V*1UdsK?QFR&nK;Z6UqU](2o_KOsjs+13$s5<p-~> -"!.E>Fn>V6QA4o/!A3d;rrFG?pAY/0Yl=Y*]4(_L\aTP"fm1^.rrW1;YP%nr..lg)!RjX#rrKB? -rVloY3V*1UdsK?QFR&nK;Z6UqU](2o_KOsjs+13$s5<p-~> -"!.E>Fo)+<VuB<p=9&;dL5\bu?2jj(S,Q%\A,Q?-/arT:!@@L6rsdIoh#HGJd/RUdBM2!LoD\j+ -?1.^nrZD%;!*T:o"Ju.u.-LS&!3?,!!)<Gc&@)98@/nYJ9=Os$<^%9l9>1-#!WHO+rs")2s5QaF -V>gK%nk1e^92!h8j#P!U9*!]@rs@Uudf9?f>5QH?p\t7gd"24Js+14.s*t~> -"!.E>Fo)+<VuB<p=9&;dL5\bu?2jj(S,Q%\A,Q?-/arT:!@@L6rsdIoh#HGJd/RUdBM2!LoD\j+ -?1.^nrZD%;!*T:o"Ju.u.-LS&!3?,!!)<Gc&@)98@/nYJ9=Os$<^%9l9>1-#!WHO+rs")2s5QaF -V>gK%nk1e^92!h8j#P!U9*!]@rs@Uudf9?f>5QH?p\t7gd"24Js+14.s*t~> -"!.E>Fo)+<VuB<p=9&;dL5\bu?2jj(S,Q%\A,Q?-/arT:!@@L6rsdIoh#HGJd/RUdBM2!LoD\j+ -?1.^nrZD%;!*T:o"Ju.u.-LS&!3?,!!)<Gc&@)98@/nYJ9=Os$<^%9l9>1-#!WHO+rs")2s5QaF -V>gK%nk1e^92!h8j#P!U9*!]@rs@Uudf9?f>5QH?p\t7gd"24Js+14.s*t~> -"!.E>(&ffgmf3<kIf@_'``2u((m`Rs^g$i5M>km']41a=!A3d;rrFG?p&>IscMuQcs8QRQ\'`Tr -L%YHIj&b4-rrN*@qu6ZGrm:k!Y5]n0rm:jp[JreDrm;:"]`6A3=4,E7_uJ2f4OMREq#:Bn+8u3D -:!`k7d=?cHrsJ\OO]TrXo`*qYAcC'X:[RuX!VbILrr^mPb#8!6!DUpls+13$s5<p-~> -"!.E>(&ffgmf3<kIf@_'``2u((m`Rs^g$i5M>km']41a=!A3d;rrFG?p&>IscMuQcs8QRQ\'`Tr -L%YHIj&b4-rrN*@qu6ZGrm:k!Y5]n0rm:jp[JreDrm;:"]`6A3=4,E7_uJ2f4OMREq#:Bn+8u3D -:!`k7d=?cHrsJ\OO]TrXo`*qYAcC'X:[RuX!VbILrr^mPb#8!6!DUpls+13$s5<p-~> -"!.E>(&ffgmf3<kIf@_'``2u((m`Rs^g$i5M>km']41a=!A3d;rrFG?p&>IscMuQcs8QRQ\'`Tr -L%YHIj&b4-rrN*@qu6ZGrm:k!Y5]n0rm:jp[JreDrm;:"]`6A3=4,E7_uJ2f4OMREq#:Bn+8u3D -:!`k7d=?cHrsJ\OO]TrXo`*qYAcC'X:[RuX!VbILrr^mPb#8!6!DUpls+13$s5<p-~> -"!.E>D>ro*rr<"nIfAsJoC;jHIJNpCju<=#M#R#Idm*g2!A3d;rrFG?p&>J%a8XI[s8V`Yr;Zf' -C%_K,e4B!,!WF2<rrD`koE9K1s!Zt-rrdSCruh:>rs`nKs7mi/s8QrHs8UP>p&>9q+9/Bms%Ui= -rrJ[@r;Qf&C]487j#-H-rrFt?nc&g9;ZHc1*>SMP!DUpls+13$s5<p-~> -"!.E>D>ro*rr<"nIfAsJoC;jHIJNpCju<=#M#R#Idm*g2!A3d;rrFG?p&>J%a8XI[s8V`Yr;Zf' -C%_K,e4B!,!WF2<rrD`koE9K1s!Zt-rrdSCruh:>rs`nKs7mi/s8QrHs8UP>p&>9q+9/Bms%Ui= -rrJ[@r;Qf&C]487j#-H-rrFt?nc&g9;ZHc1*>SMP!DUpls+13$s5<p-~> -"!.E>D>ro*rr<"nIfAsJoC;jHIJNpCju<=#M#R#Idm*g2!A3d;rrFG?p&>J%a8XI[s8V`Yr;Zf' -C%_K,e4B!,!WF2<rrD`koE9K1s!Zt-rrdSCruh:>rs`nKs7mi/s8QrHs8UP>p&>9q+9/Bms%Ui= -rrJ[@r;Qf&C]487j#-H-rrFt?nc&g9;ZHc1*>SMP!DUpls+13$s5<p-~> -"!.E>Fo)+<Q2W071uA7uLAq2Uju<=#(B#W]?2ad(/arT:!@@L4rrOq7-MdZBZYB.5!W"&-rrN*@ -r;QfS2?#!,'V,1Oo`"jnGbtE_rVlg"Dls'8,PqE@dn064#Q5bEV0Dr6ci3qFSUgY<!O6G=rrM.? -rVlmTkjeZRb#83<!$2";!DUpls+13$s5<p-~> -"!.E>Fo)+<Q2W071uA7uLAq2Uju<=#(B#W]?2ad(/arT:!@@L4rrOq7-MdZBZYB.5!W"&-rrN*@ -r;QfS2?#!,'V,1Oo`"jnGbtE_rVlg"Dls'8,PqE@dn064#Q5bEV0Dr6ci3qFSUgY<!O6G=rrM.? -rVlmTkjeZRb#83<!$2";!DUpls+13$s5<p-~> -"!.E>Fo)+<Q2W071uA7uLAq2Uju<=#(B#W]?2ad(/arT:!@@L4rrOq7-MdZBZYB.5!W"&-rrN*@ -r;QfS2?#!,'V,1Oo`"jnGbtE_rVlg"Dls'8,PqE@dn064#Q5bEV0Dr6ci3qFSUgY<!O6G=rrM.? -rVlmTkjeZRb#83<!$2";!DUpls+13$s5<p-~> -"!.E>Fo21>jkTk8"R[oBQA5D="QhZCNfNo7!A3d;rrFG?o`"tIi[4[)!S-Q9rrKH?rVlo1ao)/> -^0^1+!rc-@rVm0Ym/R+I?(CpC\,QC1GbtE_rVlg"Dls'8,PqEDdn0T>e*Zu2#Q5b5OaQ._ci3qF -SUgY<#I/(Es2t)r3W8sY2!FK0!Qn==rr=)<rrUech1>TWs+14.s*t~> -"!.E>Fo21>jkTk8"R[oBQA5D="QhZCNfNo7!A3d;rrFG?o`"tIi[4[)!S-Q9rrKH?rVlo1ao)/> -^0^1+!rc-@rVm0Ym/R+I?(CpC\,QC1GbtE_rVlg"Dls'8,PqEDdn0T>e*Zu2#Q5b5OaQ._ci3qF -SUgY<#I/(Es2t)r3W8sY2!FK0!Qn==rr=)<rrUech1>TWs+14.s*t~> -"!.E>Fo21>jkTk8"R[oBQA5D="QhZCNfNo7!A3d;rrFG?o`"tIi[4[)!S-Q9rrKH?rVlo1ao)/> -^0^1+!rc-@rVm0Ym/R+I?(CpC\,QC1GbtE_rVlg"Dls'8,PqEDdn0T>e*Zu2#Q5b5OaQ._ci3qF -SUgY<#I/(Es2t)r3W8sY2!FK0!Qn==rr=)<rrUech1>TWs+14.s*t~> -"!.E>8H#(]XT-4grga1[IfG^grr3,`2ugF@rga%hrr3,?SK*iqrr3,<SJRZup&>)DQ?rQ1!M>AN -SH4YDrVlm#2uN[U*U<Y*i&pu<$%SG7SVAkhs!V@USHOD^s)sq3SHO>bs7mo9rrqJ)SXk#Wq>Us( -Boe[jKQQ2Qn=<r_S`TkN#L@afST*rU3W8sY2!FK0!T%ttSH*F'rrHl?JcC<$JcFX-J,~> -"!.E>8H#(]XT-4grga1[IfG^grr3,`2ugF@rga%hrr3,?SK*iqrr3,<SJRZup&>)DQ?rQ1!M>AN -SH4YDrVlm#2uN[U*U<Y*i&pu<$%SG7SVAkhs!V@USHOD^s)sq3SHO>bs7mo9rrqJ)SXk#Wq>Us( -Boe[jKQQ2Qn=<r_S`TkN#L@afST*rU3W8sY2!FK0!T%ttSH*F'rrHl?JcC<$JcFX-J,~> -"!.E>8H#(]XT-4grga1[IfG^grr3,`2ugF@rga%hrr3,?SK*iqrr3,<SJRZup&>)DQ?rQ1!M>AN -SH4YDrVlm#2uN[U*U<Y*i&pu<$%SG7SVAkhs!V@USHOD^s)sq3SHO>bs7mo9rrqJ)SXk#Wq>Us( -Boe[jKQQ2Qn=<r_S`TkN#L@afST*rU3W8sY2!FK0!T%ttSH*F'rrHl?JcC<$JcFX-J,~> -!Zh<ir-ng3s8Tn6IftQ,s2r4Xrrhn]s8TS-IfPT0rrBD)IfP`4rrB8%IfPl.rrA)WrrAemIfQ>C -rrIY>r;QbWlMgqTJ$&\L#`4%\YeSH_POSR$!5ng9!.b&u"NUQBr/pgT"586Sd.dPGn"'LY[+PEY -lhu;5h#76Waa\g!s7)TWrrJP[nG`L>rI4h<rr3&;J(]DQJcC<$huA3~> -!Zh<ir-ng3s8Tn6IftQ,s2r4Xrrhn]s8TS-IfPT0rrBD)IfP`4rrB8%IfPl.rrA)WrrAemIfQ>C -rrIY>r;QbWlMgqTJ$&\L#`4%\YeSH_POSR$!5ng9!.b&u"NUQBr/pgT"586Sd.dPGn"'LY[+PEY -lhu;5h#76Waa\g!s7)TWrrJP[nG`L>rI4h<rr3&;J(]DQJcC<$huA3~> -!Zh<ir-ng3s8Tn6IftQ,s2r4Xrrhn]s8TS-IfPT0rrBD)IfP`4rrB8%IfPl.rrA)WrrAemIfQ>C -rrIY>r;QbWlMgqTJ$&\L#`4%\YeSH_POSR$!5ng9!.b&u"NUQBr/pgT"586Sd.dPGn"'LY[+PEY -lhu;5h#76Waa\g!s7)TWrrJP[nG`L>rI4h<rr3&;J(]DQJcC<$huA3~> -!$.@)!U.7_rrLKtJcC<$JcFU,J,~> -!$.@)!U.7_rrLKtJcC<$JcFU,J,~> -!$.@)!U.7_rrLKtJcC<$JcFU,J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$2%<!VH<grrMlioD\pdn*g5QrrVf\rndYUo_[nQ"9.cXpAP!ln+6&K!VZ?grrW(rKA$8/oDJXg -qX=1=rrMiio`#!en,'Hk!THTHrrG4Kqu6[UnU^^ks2P(h~> -!$2%<!VH<grrMlioD\pdn*g5QrrVf\rndYUo_[nQ"9.cXpAP!ln+6&K!VZ?grrW(rKA$8/oDJXg -qX=1=rrMiio`#!en,'Hk!THTHrrG4Kqu6[UnU^^ks2P(h~> -!$2%<!VH<grrMlioD\pdn*g5QrrVf\rndYUo_[nQ"9.cXpAP!ln+6&K!VZ?grrW(rKA$8/oDJXg -qX=1=rrMiio`#!en,'Hk!THTHrrG4Kqu6[UnU^^ks2P(h~> -!$2(=!p)_mrVlqdOn//E",\Z;k5>5\CG#,LrrLpOmJd2k_<Lt+`@WZ`lMpn`/RS#I!I1I?rrL/? -f`).EfDklc/Y1u&!pa0mo`"uX/]ZZH!TE_;rrLpOo)A\1qYpO9qu6]o,(]cFs2P(h~> -!$2(=!p)_mrVlqdOn//E",\Z;k5>5\CG#,LrrLpOmJd2k_<Lt+`@WZ`lMpn`/RS#I!I1I?rrL/? -f`).EfDklc/Y1u&!pa0mo`"uX/]ZZH!TE_;rrLpOo)A\1qYpO9qu6]o,(]cFs2P(h~> -!$2(=!p)_mrVlqdOn//E",\Z;k5>5\CG#,LrrLpOmJd2k_<Lt+`@WZ`lMpn`/RS#I!I1I?rrL/? -f`).EfDklc/Y1u&!pa0mo`"uX/]ZZH!TE_;rrLpOo)A\1qYpO9qu6]o,(]cFs2P(h~> -"s*aDh09a\r;Qh\Qh0hK!B0*,rrG1?mJd2k_<Lt&*WQ/%MsLBIGbtH?pRR)lrrGR?i;WoBL*<S@ -rrH*?li-uIiUd'M_-?d9!Ed>=rrW+#a+=8As2Y.i~> -"s*aDh09a\r;Qh\Qh0hK!B0*,rrG1?mJd2k_<Lt&*WQ/%MsLBIGbtH?pRR)lrrGR?i;WoBL*<S@ -rrH*?li-uIiUd'M_-?d9!Ed>=rrW+#a+=8As2Y.i~> -"s*aDh09a\r;Qh\Qh0hK!B0*,rrG1?mJd2k_<Lt&*WQ/%MsLBIGbtH?pRR)lrrGR?i;WoBL*<S@ -rrH*?li-uIiUd'M_-?d9!Ed>=rrW+#a+=8As2Y.i~> -"s*a!J\Fq)qu6_[OS\VL!B0*=rr^-*R/$X[!5JN##d+.,a-\'+R'?Si!4`#q!42V'!M-:jrrH?? -rr3,W`rH(Arg3u*s8TOps8V)prVm0Es8TdDs8Sj]Yl4S&XRlFY!6bBA$(RBM_g&$Xs5Z0;rrE#r -Qiu"?s.o&]Qil%As7ZDY"5NqVo)4pXi;`iGrg3rB7G%S*R$c5!rrVhrh#<ZClMpnGrg3cHWqlJj -"R:fBEiSg4"4rp-jSf)YeGYd4rrT]tpAY'lc2O(5o;_ijao7Y5UAt8AQnilQV>gJp_Z#o6WW3"A -fX$s4XT/=@Qm7?QYl=Y&\GhiqZi0n).e<H9!Tl<;rrLq?JcC<$a8^Y~> -"s*a!J\Fq)qu6_[OS\VL!B0*=rr^-*R/$X[!5JN##d+.,a-\'+R'?Si!4`#q!42V'!M-:jrrH?? -rr3,W`rH(Arg3u*s8TOps8V)prVm0Es8TdDs8Sj]Yl4S&XRlFY!6bBA$(RBM_g&$Xs5Z0;rrE#r -Qiu"?s.o&]Qil%As7ZDY"5NqVo)4pXi;`iGrg3rB7G%S*R$c5!rrVhrh#<ZClMpnGrg3cHWqlJj -"R:fBEiSg4"4rp-jSf)YeGYd4rrT]tpAY'lc2O(5o;_ijao7Y5UAt8AQnilQV>gJp_Z#o6WW3"A -fX$s4XT/=@Qm7?QYl=Y&\GhiqZi0n).e<H9!Tl<;rrLq?JcC<$a8^Y~> -"s*a!J\Fq)qu6_[OS\VL!B0*=rr^-*R/$X[!5JN##d+.,a-\'+R'?Si!4`#q!42V'!M-:jrrH?? -rr3,W`rH(Arg3u*s8TOps8V)prVm0Es8TdDs8Sj]Yl4S&XRlFY!6bBA$(RBM_g&$Xs5Z0;rrE#r -Qiu"?s.o&]Qil%As7ZDY"5NqVo)4pXi;`iGrg3rB7G%S*R$c5!rrVhrh#<ZClMpnGrg3cHWqlJj -"R:fBEiSg4"4rp-jSf)YeGYd4rrT]tpAY'lc2O(5o;_ijao7Y5UAt8AQnilQV>gJp_Z#o6WW3"A -fX$s4XT/=@Qm7?QYl=Y&\GhiqZi0n).e<H9!Tl<;rrLq?JcC<$a8^Y~> -"s*`o]`1dPqYpS`K_59F2Wju<"1Ek"n,E=f]Rg'8%"b>U]S%>KKVAGGZ[r+/!J7HPrrEN]rr3:/ -_>j(QI_5WWK)UE/J\(kRdf9?VB)V`0*W,j<O`W_arrFipre(B(bl<e(KEcrfV>pSV3;idVL])l/ -J(ai;IK"m%Ibk$RrIb9%ir4?(KEH\cqL&9q#C[iJs6,,1M#RDUiu<IHKEHYom!\kd!e,arq>UTZ -XT*=@p&>-?KJL1=rrL`$rIb0.rVm'u!9sO`c@>hH"FQ[<aajAD%"joHab23XKTuN:_1;N<'S<"X -IS>?HKS$$"]n@GLKV8AFZ[r+/!J7HQrrFV?qYpTY2Z*LTh*6JjJcEdjJ,~> -"s*`o]`1dPqYpS`K_59F2Wju<"1Ek"n,E=f]Rg'8%"b>U]S%>KKVAGGZ[r+/!J7HPrrEN]rr3:/ -_>j(QI_5WWK)UE/J\(kRdf9?VB)V`0*W,j<O`W_arrFipre(B(bl<e(KEcrfV>pSV3;idVL])l/ -J(ai;IK"m%Ibk$RrIb9%ir4?(KEH\cqL&9q#C[iJs6,,1M#RDUiu<IHKEHYom!\kd!e,arq>UTZ -XT*=@p&>-?KJL1=rrL`$rIb0.rVm'u!9sO`c@>hH"FQ[<aajAD%"joHab23XKTuN:_1;N<'S<"X -IS>?HKS$$"]n@GLKV8AFZ[r+/!J7HQrrFV?qYpTY2Z*LTh*6JjJcEdjJ,~> -"s*`o]`1dPqYpS`K_59F2Wju<"1Ek"n,E=f]Rg'8%"b>U]S%>KKVAGGZ[r+/!J7HPrrEN]rr3:/ -_>j(QI_5WWK)UE/J\(kRdf9?VB)V`0*W,j<O`W_arrFipre(B(bl<e(KEcrfV>pSV3;idVL])l/ -J(ai;IK"m%Ibk$RrIb9%ir4?(KEH\cqL&9q#C[iJs6,,1M#RDUiu<IHKEHYom!\kd!e,arq>UTZ -XT*=@p&>-?KJL1=rrL`$rIb0.rVm'u!9sO`c@>hH"FQ[<aajAD%"joHab23XKTuN:_1;N<'S<"X -IS>?HKS$$"]n@GLKV8AFZ[r+/!J7HQrrFV?qYpTY2Z*LTh*6JjJcEdjJ,~> -!$2%<!Dgu9rr=)3rrG%?qu6[Kn,E=fI@pN=!URQ"rrG1?rVln7Yl=Y'k/I<!!N!+$rs;oGF(M&\ -s8Q$?rr3M'HN-Ucs8TQ?s8Rm]$XStnrrM%?rVlmQR/[*pJ=QWfo`+sD9;V[gj>d);!$2%<"D>1C -(pX)?"(&_A*W?!?_!:k?rVlsoch&XbrrU.ifDbdNP_f>=!okQBrr3'WkhAE&rrG^@p&>&5Wq65k -@CuO=!O6J>rr=)<rs$32p](8dIK'6L*WQ/+L&V)Qk;N>>!E@/=rrIk?rr3;U8,n$Kk5YJ+:]C@p -45p/<!H#%>rrM9#rr3!Ko(r@eju3,:!Sotks+13js*t~> -!$2%<!Dgu9rr=)3rrG%?qu6[Kn,E=fI@pN=!URQ"rrG1?rVln7Yl=Y'k/I<!!N!+$rs;oGF(M&\ -s8Q$?rr3M'HN-Ucs8TQ?s8Rm]$XStnrrM%?rVlmQR/[*pJ=QWfo`+sD9;V[gj>d);!$2%<"D>1C -(pX)?"(&_A*W?!?_!:k?rVlsoch&XbrrU.ifDbdNP_f>=!okQBrr3'WkhAE&rrG^@p&>&5Wq65k -@CuO=!O6J>rr=)<rs$32p](8dIK'6L*WQ/+L&V)Qk;N>>!E@/=rrIk?rr3;U8,n$Kk5YJ+:]C@p -45p/<!H#%>rrM9#rr3!Ko(r@eju3,:!Sotks+13js*t~> -!$2%<!Dgu9rr=)3rrG%?qu6[Kn,E=fI@pN=!URQ"rrG1?rVln7Yl=Y'k/I<!!N!+$rs;oGF(M&\ -s8Q$?rr3M'HN-Ucs8TQ?s8Rm]$XStnrrM%?rVlmQR/[*pJ=QWfo`+sD9;V[gj>d);!$2%<"D>1C -(pX)?"(&_A*W?!?_!:k?rVlsoch&XbrrU.ifDbdNP_f>=!okQBrr3'WkhAE&rrG^@p&>&5Wq65k -@CuO=!O6J>rr=)<rs$32p](8dIK'6L*WQ/+L&V)Qk;N>>!E@/=rrIk?rr3;U8,n$Kk5YJ+:]C@p -45p/<!H#%>rrM9#rr3!Ko(r@eju3,:!Sotks+13js*t~> -!$2%<!Dgu:rrHN?o`"qMk55/Z/ar]=!T%ep5lbQrrrG1?rVloOB`:9tFSGe;>;EA"rVlms2#]cO -.=2"ddf9?VB)hnV^)"H2r;QfU4T59\1$no>"G!$B..mN="aHmDs5Z0;rr<r85m&%6ruM(<"(&_A -*W,j;r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkhAE&rrG^@p&>&5Wq65k@CuO=!O6J>rr=)7 -rrJd@rr3*As8SE0r]pQH2ZE^W<65%<!JZp>rrgnCs*gR=rrLA?rr3!\iVicWg1^IO!-A,=!@m[: -rrM7?qu6]Q5Crics2Y.i~> -!$2%<!Dgu:rrHN?o`"qMk55/Z/ar]=!T%ep5lbQrrrG1?rVloOB`:9tFSGe;>;EA"rVlms2#]cO -.=2"ddf9?VB)hnV^)"H2r;QfU4T59\1$no>"G!$B..mN="aHmDs5Z0;rr<r85m&%6ruM(<"(&_A -*W,j;r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkhAE&rrG^@p&>&5Wq65k@CuO=!O6J>rr=)7 -rrJd@rr3*As8SE0r]pQH2ZE^W<65%<!JZp>rrgnCs*gR=rrLA?rr3!\iVicWg1^IO!-A,=!@m[: -rrM7?qu6]Q5Crics2Y.i~> -!$2%<!Dgu:rrHN?o`"qMk55/Z/ar]=!T%ep5lbQrrrG1?rVloOB`:9tFSGe;>;EA"rVlms2#]cO -.=2"ddf9?VB)hnV^)"H2r;QfU4T59\1$no>"G!$B..mN="aHmDs5Z0;rr<r85m&%6ruM(<"(&_A -*W,j;r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkhAE&rrG^@p&>&5Wq65k@CuO=!O6J>rr=)7 -rrJd@rr3*As8SE0r]pQH2ZE^W<65%<!JZp>rrgnCs*gR=rrLA?rr3!\iVicWg1^IO!-A,=!@m[: -rrM7?qu6]Q5Crics2Y.i~> -!$2%<!Dgu;rrQWBrUg*j2Wjo:!A3d=rrD9^gB"`srr3![ir&fVk5O*9Wd+=="'_Wb48o0[;m$#Q -#OMI_df9?VB)V`0*W#d:i&pu<!AWs?rrdkBs![O=rrmYDs8V.>r;Qa:r7_;GruM(<"(&_A*W,j; -r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkk_C[,QXh=p&>&5Wq65k@CuO=!O6J>rr=)7rrMYA -rZhWqs8SF+rS%>3rr3!uaSl,>L6hi="P-*CI\-Q=!R4F>rrG4?r;[email protected]<H9!Tl<; -rrLq?JcC<$a8^Y~> -!$2%<!Dgu;rrQWBrUg*j2Wjo:!A3d=rrD9^gB"`srr3![ir&fVk5O*9Wd+=="'_Wb48o0[;m$#Q -#OMI_df9?VB)V`0*W#d:i&pu<!AWs?rrdkBs![O=rrmYDs8V.>r;Qa:r7_;GruM(<"(&_A*W,j; -r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkk_C[,QXh=p&>&5Wq65k@CuO=!O6J>rr=)7rrMYA -rZhWqs8SF+rS%>3rr3!uaSl,>L6hi="P-*CI\-Q=!R4F>rrG4?r;[email protected]<H9!Tl<; -rrLq?JcC<$a8^Y~> -!$2%<!Dgu;rrQWBrUg*j2Wjo:!A3d=rrD9^gB"`srr3![ir&fVk5O*9Wd+=="'_Wb48o0[;m$#Q -#OMI_df9?VB)V`0*W#d:i&pu<!AWs?rrdkBs![O=rrmYDs8V.>r;Qa:r7_;GruM(<"(&_A*W,j; -r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkk_C[,QXh=p&>&5Wq65k@CuO=!O6J>rr=)7rrMYA -rZhWqs8SF+rS%>3rr3!uaSl,>L6hi="P-*CI\-Q=!R4F>rrG4?r;[email protected]<H9!Tl<; -rrLq?JcC<$a8^Y~> -%NYTElKUsRlL"WOoPM5>p&>0dl7pcSrr38pl71QUs8S%KrosO!?iL'19%*"9C]Ag_rosNhDZ0S9 -'/fdh$!Y7@i>l@us'`V?l3Qq[s&m;*`t`4brr=):rs7u:2sU&8s"Wm>rsjRLs#T-(lBDdTs8W)9 ->5eI$0`C8=pAb.5rVlt+^Afhal2tM?r@e-7"'3nD2u`(NC^AYmqs&uclMCP@3W8s[2!Egmro*tA -qu-O&IgEmjs82*HBq4JRrVlq:#grYO#l!UeX6T]a[r;61!&4BO#$)#ps8T-Jro+(Hs8SaFrosLM -rVm+4]^!gBs,`ECl3YL3s*bTflKZ/krr33rdH\>Ys*h!Il2e2-rr3#p,l7NA,Ph9;!rFA@r;Qii -#YfmMJcF'rJ,~> -%NYTElKUsRlL"WOoPM5>p&>0dl7pcSrr38pl71QUs8S%KrosO!?iL'19%*"9C]Ag_rosNhDZ0S9 -'/fdh$!Y7@i>l@us'`V?l3Qq[s&m;*`t`4brr=):rs7u:2sU&8s"Wm>rsjRLs#T-(lBDdTs8W)9 ->5eI$0`C8=pAb.5rVlt+^Afhal2tM?r@e-7"'3nD2u`(NC^AYmqs&uclMCP@3W8s[2!Egmro*tA -qu-O&IgEmjs82*HBq4JRrVlq:#grYO#l!UeX6T]a[r;61!&4BO#$)#ps8T-Jro+(Hs8SaFrosLM -rVm+4]^!gBs,`ECl3YL3s*bTflKZ/krr33rdH\>Ys*h!Il2e2-rr3#p,l7NA,Ph9;!rFA@r;Qii -#YfmMJcF'rJ,~> -%NYTElKUsRlL"WOoPM5>p&>0dl7pcSrr38pl71QUs8S%KrosO!?iL'19%*"9C]Ag_rosNhDZ0S9 -'/fdh$!Y7@i>l@us'`V?l3Qq[s&m;*`t`4brr=):rs7u:2sU&8s"Wm>rsjRLs#T-(lBDdTs8W)9 ->5eI$0`C8=pAb.5rVlt+^Afhal2tM?r@e-7"'3nD2u`(NC^AYmqs&uclMCP@3W8s[2!Egmro*tA -qu-O&IgEmjs82*HBq4JRrVlq:#grYO#l!UeX6T]a[r;61!&4BO#$)#ps8T-Jro+(Hs8SaFrosLM -rVm+4]^!gBs,`ECl3YL3s*bTflKZ/krr33rdH\>Ys*h!Il2e2-rr3#p,l7NA,Ph9;!rFA@r;Qii -#YfmMJcF'rJ,~> -!Zh<Nr%\CPs8SOkoD\fMr\=IJrr2tQr\=IQrr2tJr\=IXr;Qgi11L7_!-.un!,;B2!BaK`rrIJk -rr3,5NrT,br\=J%rr3+%10(eqrVljiqu6Xfr\=V=s8Q6jrr3:mYlF_b1,=WJ\,QC/dUqP=rrDoo -1'=ZYs%<7i"+JGnmJSdB])VfmrA"Jks8V!U1'FgqhuD@-1'>l&l!XJi"'k6gV>W.MZMa_%"dK_8 -s8UXI1&s31rrRrdd/O%FYPg3Yp](8mrA"BPrVm(C1@>,Am",-k"?b98s-3L<!(Qnd!a[WVrr2tR -r\=aXs8RP>BJM>Hr;Qgj11C1^!-8&o!,2<1!CkZ<rrLS?qu6]C:&FqloeL<6s+13rs*t~> -!Zh<Nr%\CPs8SOkoD\fMr\=IJrr2tQr\=IQrr2tJr\=IXr;Qgi11L7_!-.un!,;B2!BaK`rrIJk -rr3,5NrT,br\=J%rr3+%10(eqrVljiqu6Xfr\=V=s8Q6jrr3:mYlF_b1,=WJ\,QC/dUqP=rrDoo -1'=ZYs%<7i"+JGnmJSdB])VfmrA"Jks8V!U1'FgqhuD@-1'>l&l!XJi"'k6gV>W.MZMa_%"dK_8 -s8UXI1&s31rrRrdd/O%FYPg3Yp](8mrA"BPrVm(C1@>,Am",-k"?b98s-3L<!(Qnd!a[WVrr2tR -r\=aXs8RP>BJM>Hr;Qgj11C1^!-8&o!,2<1!CkZ<rrLS?qu6]C:&FqloeL<6s+13rs*t~> -!Zh<Nr%\CPs8SOkoD\fMr\=IJrr2tQr\=IQrr2tJr\=IXr;Qgi11L7_!-.un!,;B2!BaK`rrIJk -rr3,5NrT,br\=J%rr3+%10(eqrVljiqu6Xfr\=V=s8Q6jrr3:mYlF_b1,=WJ\,QC/dUqP=rrDoo -1'=ZYs%<7i"+JGnmJSdB])VfmrA"Jks8V!U1'FgqhuD@-1'>l&l!XJi"'k6gV>W.MZMa_%"dK_8 -s8UXI1&s31rrRrdd/O%FYPg3Yp](8mrA"BPrVm(C1@>,Am",-k"?b98s-3L<!(Qnd!a[WVrr2tR -r\=aXs8RP>BJM>Hr;Qgj11C1^!-8&o!,2<1!CkZ<rrLS?qu6]C:&FqloeL<6s+13rs*t~> -!$/$<"ip05s0lUqrrL'0a8Z1p62gfa/+NT<!F<J3rrGj@kPkRUIK'6Imk*c'!IgX(rrMb1r;Qe0 -r;6Ko@tFZ2s2G"g~> -!$/$<"ip05s0lUqrrL'0a8Z1p62gfa/+NT<!F<J3rrGj@kPkRUIK'6Imk*c'!IgX(rrMb1r;Qe0 -r;6Ko@tFZ2s2G"g~> -!$/$<"ip05s0lUqrrL'0a8Z1p62gfa/+NT<!F<J3rrGj@kPkRUIK'6Imk*c'!IgX(rrMb1r;Qe0 -r;6Ko@tFZ2s2G"g~> -!$/$<"db13PDH-0rrMD.rK@8'p\t0oa*QJ3rrTu\jQHODaFF2Q!Ki-#rrIS?JcC<$SH"*~> -!$/$<"db13PDH-0rrMD.rK@8'p\t0oa*QJ3rrTu\jQHODaFF2Q!Ki-#rrIS?JcC<$SH"*~> -!$/$<"db13PDH-0rrMD.rK@8'p\t0oa*QJ3rrTu\jQHODaFF2Q!Ki-#rrIS?JcC<$SH"*~> -!$/!;".k@+Wh04jiVeT5li$ha_US2W!P7(JrrC%;M#`Y#rrL$cJcC<$SH"*~> -!$/!;".k@+Wh04jiVeT5li$ha_US2W!P7(JrrC%;M#`Y#rrL$cJcC<$SH"*~> -!$/!;".k@+Wh04jiVeT5li$ha_US2W!P7(JrrC%;M#`Y#rrL$cJcC<$SH"*~> -!$2";!d\!iJc>iPBN]t=s+13$s,R,0~> -!$2";!d\!iJc>iPBN]t=s+13$s,R,0~> -!$2";!d\!iJc>iPBN]t=s+13$s,R,0~> -!$2%<"*pi_bhN-ufQmJr!GFXFrrGGskl1[N](l:-aMX^)JcC<$JcCf2J,~> -!$2%<"*pi_bhN-ufQmJr!GFXFrrGGskl1[N](l:-aMX^)JcC<$JcCf2J,~> -!$2%<"*pi_bhN-ufQmJr!GFXFrrGGskl1[N](l:-aMX^)JcC<$JcCf2J,~> -!$2%<!DgtnrrL>?rr3#^i7%],i9'8'!I1I7rrKi?JcC<$JcCf2J,~> -!$2%<!DgtnrrL>?rr3#^i7%],i9'8'!I1I7rrKi?JcC<$JcCf2J,~> -!$2%<!DgtnrrL>?rr3#^i7%],i9'8'!I1I7rrKi?JcC<$JcCf2J,~> -!$2%<!b/kArr2uhroFFJs8V`^mcEQnrr2ufroF.Drr2ueroF.Err3>ojlP[L;#gR`jlQI@"n;9O -jlPk.rrqcNjluO/q#:fjq>^Kimf3=RqW?o$n,E=gkiM+-rrD9^jT+iMrrD6]jT+lNrs.]Jjm[Mk -s8W&Z!;-9j!rK6?JcC<$JcCf2J,~> -!$2%<!b/kArr2uhroFFJs8V`^mcEQnrr2ufroF.Drr2ueroF.Err3>ojlP[L;#gR`jlQI@"n;9O -jlPk.rrqcNjluO/q#:fjq>^Kimf3=RqW?o$n,E=gkiM+-rrD9^jT+iMrrD6]jT+lNrs.]Jjm[Mk -s8W&Z!;-9j!rK6?JcC<$JcCf2J,~> -!$2%<!b/kArr2uhroFFJs8V`^mcEQnrr2ufroF.Drr2ueroF.Err3>ojlP[L;#gR`jlQI@"n;9O -jlPk.rrqcNjluO/q#:fjq>^Kimf3=RqW?o$n,E=gkiM+-rrD9^jT+iMrrD6]jT+lNrs.]Jjm[Mk -s8W&Z!;-9j!rK6?JcC<$JcCf2J,~> -!$2(=#N*U%n,NFQJc7S:6.5e!BkoXd7b%J#GQ'N(9%Et'ErJ!-:Y5X,D)XC@TMY[gHoD</rsik& -;`?XAh>c;<2`FX*MYR2b;p,.?^Kpm>8u4d32f[pYs&:a0rVloJ8,bFMHKbCW6N/nPJ*R'\4Z><@ -?D[\J3W:rBM=(?Cj'9[bJcC<$JcCi3J,~> -!$2(=#N*U%n,NFQJc7S:6.5e!BkoXd7b%J#GQ'N(9%Et'ErJ!-:Y5X,D)XC@TMY[gHoD</rsik& -;`?XAh>c;<2`FX*MYR2b;p,.?^Kpm>8u4d32f[pYs&:a0rVloJ8,bFMHKbCW6N/nPJ*R'\4Z><@ -?D[\J3W:rBM=(?Cj'9[bJcC<$JcCi3J,~> -!$2(=#N*U%n,NFQJc7S:6.5e!BkoXd7b%J#GQ'N(9%Et'ErJ!-:Y5X,D)XC@TMY[gHoD</rsik& -;`?XAh>c;<2`FX*MYR2b;p,.?^Kpm>8u4d32f[pYs&:a0rVloJ8,bFMHKbCW6N/nPJ*R'\4Z><@ -?D[\J3W:rBM=(?Cj'9[bJcC<$JcCi3J,~> -!$2(=!c7q^rr3"kIfB?UmOnO*/AhGeju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<%;\)^ -s8T]>s'rV>s3OI6rshuMs8Ti>s8P\[p&G&[KDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ -ruh:>rrI&?rr3&6!.0:sJcC<$OT0h~> -!$2(=!c7q^rr3"kIfB?UmOnO*/AhGeju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<%;\)^ -s8T]>s'rV>s3OI6rshuMs8Ti>s8P\[p&G&[KDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ -ruh:>rrI&?rr3&6!.0:sJcC<$OT0h~> -!$2(=!c7q^rr3"kIfB?UmOnO*/AhGeju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<%;\)^ -s8T]>s'rV>s3OI6rshuMs8Ti>s8P\[p&G&[KDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ -ruh:>rrI&?rr3&6!.0:sJcC<$OT0h~> -!$2(=#Hi'&pAb/b7fJDNKeECkLAq2Uju<=#Nr/hWKpVf="P$'CI@pN=!R+C=rr=)<rrHr?rr389 -@K0iJs8UG>p\tOub5_LV?2spsdf07IR=kM=!U2E=rrG.?qu6[Om/I"fJ=QWfo`"jnGbtE_NW+qC -5-=kbmtYnkJcC<$JcCi3J,~> -!$2(=#Hi'&pAb/b7fJDNKeECkLAq2Uju<=#Nr/hWKpVf="P$'CI@pN=!R+C=rr=)<rrHr?rr389 -@K0iJs8UG>p\tOub5_LV?2spsdf07IR=kM=!U2E=rrG.?qu6[Om/I"fJ=QWfo`"jnGbtE_NW+qC -5-=kbmtYnkJcC<$JcCi3J,~> -!$2(=#Hi'&pAb/b7fJDNKeECkLAq2Uju<=#Nr/hWKpVf="P$'CI@pN=!R+C=rr=)<rrHr?rr389 -@K0iJs8UG>p\tOub5_LV?2spsdf07IR=kM=!U2E=rrG.?qu6[Om/I"fJ=QWfo`"jnGbtE_NW+qC -5-=kbmtYnkJcC<$JcCi3J,~> -!$2%<#+^SDs8S]\rJguSs-AE=rrhICs,N-:rrIh?rr3,O8H4+1rr3#C;#UCo*W?!=Cp<p=#eOOF -\p8:8G-L`@#uf"Hs1_k>s%:`=rrJO?rr3#`1]@=S3TL#:!AWs?rrdkBs![O=rrdSCrud="NWn2; -s6k`>JcC<$JcCf2J,~> -!$2%<#+^SDs8S]\rJguSs-AE=rrhICs,N-:rrIh?rr3,O8H4+1rr3#C;#UCo*W?!=Cp<p=#eOOF -\p8:8G-L`@#uf"Hs1_k>s%:`=rrJO?rr3#`1]@=S3TL#:!AWs?rrdkBs![O=rrdSCrud="NWn2; -s6k`>JcC<$JcCf2J,~> -!$2%<#+^SDs8S]\rJguSs-AE=rrhICs,N-:rrIh?rr3,O8H4+1rr3#C;#UCo*W?!=Cp<p=#eOOF -\p8:8G-L`@#uf"Hs1_k>s%:`=rrJO?rr3#`1]@=S3TL#:!AWs?rrdkBs![O=rrdSCrud="NWn2; -s6k`>JcC<$JcCf2J,~> -!$2%<!Dgu>rrJa@qu6\_LAq2Uju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<!Go">rs-/E -s-JqMX1J3.!EOLFrs;WHs8Ti>s8P^>rr3"eKDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ -ruh:9rrKi?JcC<$JcCf2J,~> -!$2%<!Dgu>rrJa@qu6\_LAq2Uju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<!Go">rs-/E -s-JqMX1J3.!EOLFrs;WHs8Ti>s8P^>rr3"eKDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ -ruh:9rrKi?JcC<$JcCf2J,~> -!$2%<!Dgu>rrJa@qu6\_LAq2Uju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<!Go">rs-/E -s-JqMX1J3.!EOLFrs;WHs8Ti>s8P^>rr3"eKDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ -ruh:9rrKi?JcC<$JcCf2J,~> -!$2%<!Dgu>rrMD9r\jsKs8SC>rr3,`2ui#ar\jsPec4`Or\kNefDjlJ2`F*s;#gQC2[23Qs8Qo> -rr3,[email protected]\jgur;Qd"2u`g`g/U'j>EbER9%*_=%%2bJs&:`]2i[k;e49Ks"FB;3dR*pl%"IRB -coj<HF'b^CcT1t`!3#kr!PVlks+13$s,[21~> -!$2%<!Dgu>rrMD9r\jsKs8SC>rr3,`2ui#ar\jsPec4`Or\kNefDjlJ2`F*s;#gQC2[23Qs8Qo> -rr3,[email protected]\jgur;Qd"2u`g`g/U'j>EbER9%*_=%%2bJs&:`]2i[k;e49Ks"FB;3dR*pl%"IRB -coj<HF'b^CcT1t`!3#kr!PVlks+13$s,[21~> -!$2%<!Dgu>rrMD9r\jsKs8SC>rr3,`2ui#ar\jsPec4`Or\kNefDjlJ2`F*s;#gQC2[23Qs8Qo> -rr3,[email protected]\jgur;Qd"2u`g`g/U'j>EbER9%*_=%%2bJs&:`]2i[k;e49Ks"FB;3dR*pl%"IRB -coj<HF'b^CcT1t`!3#kr!PVlks+13$s,[21~> -!$2%<#44f!56(ZRroF:Fs8V`^rr3-!lMpnRroF.Drr2ueroF.Err38mjlP\%mJm4SroF:Ks8VT_ -rr3,rn,IL3roF0ZH2[aDc8Y_%#jUO5lh0fJm/$_]"SD9bs60ID!:^!f!9jFD!:g'g!9a@C!:p-h -#Nk.0nF?5Ps8D$`o`+qH*Dc*Ss+13$s,[21~> -!$2%<#44f!56(ZRroF:Fs8V`^rr3-!lMpnRroF.Drr2ueroF.Err38mjlP\%mJm4SroF:Ks8VT_ -rr3,rn,IL3roF0ZH2[aDc8Y_%#jUO5lh0fJm/$_]"SD9bs60ID!:^!f!9jFD!:g'g!9a@C!:p-h -#Nk.0nF?5Ps8D$`o`+qH*Dc*Ss+13$s,[21~> -!$2%<#44f!56(ZRroF:Fs8V`^rr3-!lMpnRroF.Drr2ueroF.Err38mjlP\%mJm4SroF:Ks8VT_ -rr3,rn,IL3roF0ZH2[aDc8Y_%#jUO5lh0fJm/$_]"SD9bs60ID!:^!f!9jFD!:g'g!9a@C!:p-h -#Nk.0nF?5Ps8D$`o`+qH*Dc*Ss+13$s,[21~> -!$/lT!I(UDhuT^&rVloB:kAXts+13$s3Udr~> -!$/lT!I(UDhuT^&rVloB:kAXts+13$s3Udr~> -!$/lT!I(UDhuT^&rVloB:kAXts+13$s3Udr~> -!$/iS!-8'$!-\;?!)nIKJcC<$JcF'rJ,~> -!$/iS!-8'$!-\;?!)nIKJcC<$JcF'rJ,~> -!$/iS!-8'$!-\;?!)nIKJcC<$JcF'rJ,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -"!.FAao7+sNW/qY`R48V"0^kcRDo1.QN,gOJcC<$JcC<$iW"E~> -"!.FAao7+sNW/qY`R48V"0^kcRDo1.QN,gOJcC<$JcC<$iW"E~> -"!.FAao7+sNW/qY`R48V"0^kcRDo1.QN,gOJcC<$JcC<$iW"E~> -"!.EcIf>N8P(N`6b'V_2rrZ3AqBEoY"!.ENAqBu5s+13$s5F!.~> -"!.EcIf>N8P(N`6b'V_2rrZ3AqBEoY"!.ENAqBu5s+13$s5F!.~> -"!.EcIf>N8P(N`6b'V_2rrZ3AqBEoY"!.ENAqBu5s+13$s5F!.~> -"!.E>FoMCApEon>!mbW@o`#"land4#rrX;A[q:3kJcC<$JcF^/J,~> -"!.E>FoMCApEon>!mbW@o`#"land4#rrX;A[q:3kJcC<$JcF^/J,~> -"!.E>FoMCApEon>!mbW@o`#"land4#rrX;A[q:3kJcC<$JcF^/J,~> -"!.F0jSo/[8C[V<!o$B@p&>9Qo`!>>s.-4irrh.os-Btirrgnos,aejrs6qrs,"Pks57Vjrr?j6 -1&u7krrYqM13WZs!+#RZ!.FhG"4R;?iIV#[s+13$s5F!.~> -"!.F0jSo/[8C[V<!o$B@p&>9Qo`!>>s.-4irrh.os-Btirrgnos,aejrs6qrs,"Pks57Vjrr?j6 -1&u7krrYqM13WZs!+#RZ!.FhG"4R;?iIV#[s+13$s5F!.~> -"!.F0jSo/[8C[V<!o$B@p&>9Qo`!>>s.-4irrh.os-Btirrgnos,aejrs6qrs,"Pks57Vjrr?j6 -1&u7krrYqM13WZs!+#RZ!.FhG"4R;?iIV#[s+13$s5F!.~> -!$2%<!r>IAqu6]a0CSoAKpVf="P$'CI@pN='[0DRFK#6>hA08oCp<s>fgPl?E3K9+!NL>Brs'\0 -[oNJ.@D2[*!M"jps+13$s+14(s*t~> -!$2%<!r>IAqu6]a0CSoAKpVf="P$'CI@pN='[0DRFK#6>hA08oCp<s>fgPl?E3K9+!NL>Brs'\0 -[oNJ.@D2[*!M"jps+13$s+14(s*t~> -!$2%<!r>IAqu6]a0CSoAKpVf="P$'CI@pN='[0DRFK#6>hA08oCp<s>fgPl?E3K9+!NL>Brs'\0 -[oNJ.@D2[*!M"jps+13$s+14(s*t~> -!$2(=!pX(@qYpTT4R`:NKpVf="P$'CI>RsX'X(@5FK#5r5/^V(Cp<s>fgPl?A%DU=!OHM>rs'\E -aAr9?;m$&R!K28Ts+13$s+14(s*t~> -!$2(=!pX(@qYpTT4R`:NKpVf="P$'CI>RsX'X(@5FK#5r5/^V(Cp<s>fgPl?A%DU=!OHM>rs'\E -aAr9?;m$&R!K28Ts+13$s+14(s*t~> -!$2(=!pX(@qYpTT4R`:NKpVf="P$'CI>RsX'X(@5FK#5r5/^V(Cp<s>fgPl?A%DU=!OHM>rs'\E -aAr9?;m$&R!K28Ts+13$s+14(s*t~> -"WdXCiHE09rrUPJp@&"^KpVf="P$'CI4>.]'JrYdFK!D[s28(?Cp<s>fgPl?A%DU=%'s[Js34F> -aAr9?;`+G=!1\W?JcC<$JcFI(J,~> -"WdXCiHE09rrUPJp@&"^KpVf="P$'CI4>.]'JrYdFK!D[s28(?Cp<s>fgPl?A%DU=%'s[Js34F> -aAr9?;`+G=!1\W?JcC<$JcFI(J,~> -"WdXCiHE09rrUPJp@&"^KpVf="P$'CI4>.]'JrYdFK!D[s28(?Cp<s>fgPl?A%DU=%'s[Js34F> -aAr9?;`+G=!1\W?JcC<$JcFI(J,~> -"<IO"K"LmZ!lf6Amf*TIQiI*^L.M>mTDnj)bY\=]C2NA<`**+VYlFaV70!9s\c2U;ZYBI>52ZC] -<WE(tb45K5pS#?Qs+13$s+146s*t~> -"<IO"K"LmZ!lf6Amf*TIQiI*^L.M>mTDnj)bY\=]C2NA<`**+VYlFaV70!9s\c2U;ZYBI>52ZC] -<WE(tb45K5pS#?Qs+13$s+146s*t~> -"<IO"K"LmZ!lf6Amf*TIQiI*^L.M>mTDnj)bY\=]C2NA<`**+VYlFaV70!9s\c2U;ZYBI>52ZC] -<WE(tb45K5pS#?Qs+13$s+146s*t~> -"!.E>4oL$;QN.!%J(j;i$,D(EKS*o.s*^O=rtN[Rs)_,ss8U#?s(knmKQ&0Bs0%"QKFEF;s0*Ve -KP)jJs/(DIKE/:MrrVEb;1\aus+13$s60K5~> -"!.E>4oL$;QN.!%J(j;i$,D(EKS*o.s*^O=rtN[Rs)_,ss8U#?s(knmKQ&0Bs0%"QKFEF;s0*Ve -KP)jJs/(DIKE/:MrrVEb;1\aus+13$s60K5~> -"!.E>4oL$;QN.!%J(j;i$,D(EKS*o.s*^O=rtN[Rs)_,ss8U#?s(knmKQ&0Bs0%"QKFEF;s0*Ve -KP)jJs/(DIKE/:MrrVEb;1\aus+13$s60K5~> -!Zh<rr0RVbs8U<pli.7-R$aGp8H6ibrr3,d\GsV)rr3)`]`5qKQiqA:s8TJ*Qisnus''RDQiq#A -s8T8$QiOknrrVn]\Ujd3s+13$s60K5~> -!Zh<rr0RVbs8U<pli.7-R$aGp8H6ibrr3,d\GsV)rr3)`]`5qKQiqA:s8TJ*Qisnus''RDQiq#A -s8T8$QiOknrrVn]\Ujd3s+13$s60K5~> -!Zh<rr0RVbs8U<pli.7-R$aGp8H6ibrr3,d\GsV)rr3)`]`5qKQiqA:s8TJ*Qisnus''RDQiq#A -s8T8$QiOknrrVn]\Ujd3s+13$s60K5~> -!$1"t!NEL+rrLY@li.!t@JKj'cgC`3!Tqsas+13$s+13us*t~> -!$1"t!NEL+rrLY@li.!t@JKj'cgC`3!Tqsas+13$s+13us*t~> -!$1"t!NEL+rrLY@li.!t@JKj'cgC`3!Tqsas+13$s+13us*t~> -!$1"t!U4S&/HJH"li."Wj+75]s+13$s2+ed~> -!$1"t!U4S&/HJH"li."Wj+75]s+13$s2+ed~> -!$1"t!U4S&/HJH"li."Wj+75]s+13$s2+ed~> -!$2(="8MHXoB-&Qp&=C[nU^_$rr`#do(.G4JcDABJ,~> -!$2(="8MHXoB-&Qp&=C[nU^_$rr`#do(.G4JcDABJ,~> -!$2(="8MHXoB-&Qp&=C[nU^_$rr`#do(.G4JcDABJ,~> -!$2(=")T&-1&V%PiV<?N"6A%arr2otkl0-5g\h'Ph>HmE!8@>M!oDM`r;QiRf%p?*!9F(/!9j.V -!WCjOrrUg'j8ArWec=e0rr`5Nf&cQ(!VYROrr`)Jf'<87!<)lI!:]mc!U0(HrrMTRr;Qlif%0?h -rr_fBf(/e>!:^!=!;Q6e!U0(Ors%i@f(]4Eo_d8?g&D!Ol2K9+rrM*Rr;Qu_f%0j#s6]a8!oX+E -r72)2s8RBo.2IL7JcDABJ,~> -!$2(=")T&-1&V%PiV<?N"6A%arr2otkl0-5g\h'Ph>HmE!8@>M!oDM`r;QiRf%p?*!9F(/!9j.V -!WCjOrrUg'j8ArWec=e0rr`5Nf&cQ(!VYROrr`)Jf'<87!<)lI!:]mc!U0(HrrMTRr;Qlif%0?h -rr_fBf(/e>!:^!=!;Q6e!U0(Ors%i@f(]4Eo_d8?g&D!Ol2K9+rrM*Rr;Qu_f%0j#s6]a8!oX+E -r72)2s8RBo.2IL7JcDABJ,~> -!$2(=")T&-1&V%PiV<?N"6A%arr2otkl0-5g\h'Ph>HmE!8@>M!oDM`r;QiRf%p?*!9F(/!9j.V -!WCjOrrUg'j8ArWec=e0rr`5Nf&cQ(!VYROrr`)Jf'<87!<)lI!:]mc!U0(HrrMTRr;Qlif%0?h -rr_fBf(/e>!:^!=!;Q6e!U0(Ors%i@f(]4Eo_d8?g&D!Ol2K9+rrM*Rr;Qu_f%0j#s6]a8!oX+E -r72)2s8RBo.2IL7JcDABJ,~> -!$2(=!H#(<rrU\+ec#LRg2BGM^An5kH2^86<m(FA!mMnWo`"s6(&\(7c<Nh@bPqMIbZRD>cN!oK -2>ouERJ-X]ZRbtR"N*i$CY/Rc"MdJrDV>$h"M.&lEnpBg!r.jWrVm0%WCB@+j8].frC-gJmJ[%d -i)?WKrrVP,JGoK[ns2a7ORE/Kmug.0Pk"eRm>h08!9!SN!oR"WrVm,bK18>*qZ"e;7KaM,s5A>( -7KEG]rrU_-eG]CPg2KMN^&S,8rC-lP56%S[7KGS@r;Qa;JcC<$TDsE~> -!$2(=!H#(<rrU\+ec#LRg2BGM^An5kH2^86<m(FA!mMnWo`"s6(&\(7c<Nh@bPqMIbZRD>cN!oK -2>ouERJ-X]ZRbtR"N*i$CY/Rc"MdJrDV>$h"M.&lEnpBg!r.jWrVm0%WCB@+j8].frC-gJmJ[%d -i)?WKrrVP,JGoK[ns2a7ORE/Kmug.0Pk"eRm>h08!9!SN!oR"WrVm,bK18>*qZ"e;7KaM,s5A>( -7KEG]rrU_-eG]CPg2KMN^&S,8rC-lP56%S[7KGS@r;Qa;JcC<$TDsE~> -!$2(=!H#(<rrU\+ec#LRg2BGM^An5kH2^86<m(FA!mMnWo`"s6(&\(7c<Nh@bPqMIbZRD>cN!oK -2>ouERJ-X]ZRbtR"N*i$CY/Rc"MdJrDV>$h"M.&lEnpBg!r.jWrVm0%WCB@+j8].frC-gJmJ[%d -i)?WKrrVP,JGoK[ns2a7ORE/Kmug.0Pk"eRm>h08!9!SN!oR"WrVm,bK18>*qZ"e;7KaM,s5A>( -7KEG]rrU_-eG]CPg2KMN^&S,8rC-lP56%S[7KGS@r;Qa;JcC<$TDsE~> -!$2(=!H#(=rr^Pl*7b&g$.aRGs8Ql>s,N-=rs.@Es8U#Z'(>Mn!krU^rVmJLFlEAaEl.jnF6<M^ -FN+8a_=IU-X^NaYrt`"Xqu=rhdf6Ufs8U/hec2gjs8Tudg%YLJQVCKYrs5BFs8TH[ir:%trrHH? -rr3)XK.X(srr__K4b3P3')7TQs8SOJoCS*2s8S@Gp@4-5oD\pMK.O&%rs%YBXT/<NSGW<iX)\0u -KX^RQ"4)95eGfIPf7(a>s(nt<rrW1aM>[ATqNHs;rr='js+13Bs*t~> -!$2(=!H#(=rr^Pl*7b&g$.aRGs8Ql>s,N-=rs.@Es8U#Z'(>Mn!krU^rVmJLFlEAaEl.jnF6<M^ -FN+8a_=IU-X^NaYrt`"Xqu=rhdf6Ufs8U/hec2gjs8Tudg%YLJQVCKYrs5BFs8TH[ir:%trrHH? -rr3)XK.X(srr__K4b3P3')7TQs8SOJoCS*2s8S@Gp@4-5oD\pMK.O&%rs%YBXT/<NSGW<iX)\0u -KX^RQ"4)95eGfIPf7(a>s(nt<rrW1aM>[ATqNHs;rr='js+13Bs*t~> -!$2(=!H#(=rr^Pl*7b&g$.aRGs8Ql>s,N-=rs.@Es8U#Z'(>Mn!krU^rVmJLFlEAaEl.jnF6<M^ -FN+8a_=IU-X^NaYrt`"Xqu=rhdf6Ufs8U/hec2gjs8Tudg%YLJQVCKYrs5BFs8TH[ir:%trrHH? -rr3)XK.X(srr__K4b3P3')7TQs8SOJoCS*2s8S@Gp@4-5oD\pMK.O&%rs%YBXT/<NSGW<iX)\0u -KX^RQ"4)95eGfIPf7(a>s(nt<rrW1aM>[ATqNHs;rr='js+13Bs*t~> -!$2(=!H#(>rrgYtbt$=`rrJF?rr3,`2uenurr35Z5QATNg+Dderr]f^iZ8!t!Go">rt)eNs0@.> -s6"m>s'*=#It+Hlrr]'Il7;i5!CGQ?rsY$Js.+]?s3cD>s"Wm>rrIY?q#:GWNQ5&trr=)<rrZoB -ruM(<#[Y7Fs5ea>F/es7"T!VCSUg_>!UMN=rsLsInURY>s/:J?gHkH2"QY'BEiT-=!Q&(>rr=): -rra_Bs06D3rrgYtbt-@`rrJI?rr3#^2Z3RUq]Yk:!V7c;rr='js+13Bs*t~> -!$2(=!H#(>rrgYtbt$=`rrJF?rr3,`2uenurr35Z5QATNg+Dderr]f^iZ8!t!Go">rt)eNs0@.> -s6"m>s'*=#It+Hlrr]'Il7;i5!CGQ?rsY$Js.+]?s3cD>s"Wm>rrIY?q#:GWNQ5&trr=)<rrZoB -ruM(<#[Y7Fs5ea>F/es7"T!VCSUg_>!UMN=rsLsInURY>s/:J?gHkH2"QY'BEiT-=!Q&(>rr=): -rra_Bs06D3rrgYtbt-@`rrJI?rr3#^2Z3RUq]Yk:!V7c;rr='js+13Bs*t~> -!$2(=!H#(>rrgYtbt$=`rrJF?rr3,`2uenurr35Z5QATNg+Dderr]f^iZ8!t!Go">rt)eNs0@.> -s6"m>s'*=#It+Hlrr]'Il7;i5!CGQ?rsY$Js.+]?s3cD>s"Wm>rrIY?q#:GWNQ5&trr=)<rrZoB -ruM(<#[Y7Fs5ea>F/es7"T!VCSUg_>!UMN=rsLsInURY>s/:J?gHkH2"QY'BEiT-=!Q&(>rr=): -rra_Bs06D3rrgYtbt-@`rrJI?rr3#^2Z3RUq]Yk:!V7c;rr='js+13Bs*t~> -!$2(=!H#(>rrh'#s$tW=rrJF?rr3Mk2uenus8W!s5QB*^s"3^5rr^F#ruM(<!Go">rt2kOs8TTE -@Vr^Vs'&*]SXkV9q#:H,mdVh+rrGI@rr3ChMuWgT@UaQ)s8Oh?rr3"LSG<*dWW'q<rVlj<rVlt4 -[f7BIrs>hR^Am_@]`3&Pq#:KoXT+iArr3#c0`D"[4Q-8(M.0qOli5^*`Vs<HjS8`Xlb3==WrE#! -_H[!=!$2";#)nfDs06C@raGm,q#:KNc2U>arr3"cL&V)Qk;N8<!HY:<rrI&?qu6X:JcC<$TDsE~> -!$2(=!H#(>rrh'#s$tW=rrJF?rr3Mk2uenus8W!s5QB*^s"3^5rr^F#ruM(<!Go">rt2kOs8TTE -@Vr^Vs'&*]SXkV9q#:H,mdVh+rrGI@rr3ChMuWgT@UaQ)s8Oh?rr3"LSG<*dWW'q<rVlj<rVlt4 -[f7BIrs>hR^Am_@]`3&Pq#:KoXT+iArr3#c0`D"[4Q-8(M.0qOli5^*`Vs<HjS8`Xlb3==WrE#! -_H[!=!$2";#)nfDs06C@raGm,q#:KNc2U>arr3"cL&V)Qk;N8<!HY:<rrI&?qu6X:JcC<$TDsE~> -!$2(=!H#(>rrh'#s$tW=rrJF?rr3Mk2uenus8W!s5QB*^s"3^5rr^F#ruM(<!Go">rt2kOs8TTE -@Vr^Vs'&*]SXkV9q#:H,mdVh+rrGI@rr3ChMuWgT@UaQ)s8Oh?rr3"LSG<*dWW'q<rVlj<rVlt4 -[f7BIrs>hR^Am_@]`3&Pq#:KoXT+iArr3#c0`D"[4Q-8(M.0qOli5^*`Vs<HjS8`Xlb3==WrE#! -_H[!=!$2";#)nfDs06C@raGm,q#:KNc2U>arr3"cL&V)Qk;N8<!HY:<rrI&?qu6X:JcC<$TDsE~> -!$2(=!H#(;rrGX?rr3"bLAq2Zju<=uGst.lTg/MU!A3d3rr=)<rrHr?rr3SB@K6@PVn/[Ps8UQZ -pAb/mEq]M6nLOS<!CGQ?rsY$Js8Q96\riK^s"Wm>rrIY?pAY06<W2pt*W?!?B=@iLr^@-E7DAe$ -aT)9]WV-8lSUg_>!UMN=rsV$Js42%u\n]pugCeK)\cGt,pAY/6WrE#!_H[!=!$2";#"7,Ds03St -rji)5p&>%`eGfIKQ\GG=!TuB=rr=)9rr=)9rr='js+13Bs*t~> -!$2(=!H#(;rrGX?rr3"bLAq2Zju<=uGst.lTg/MU!A3d3rr=)<rrHr?rr3SB@K6@PVn/[Ps8UQZ -pAb/mEq]M6nLOS<!CGQ?rsY$Js8Q96\riK^s"Wm>rrIY?pAY06<W2pt*W?!?B=@iLr^@-E7DAe$ -aT)9]WV-8lSUg_>!UMN=rsV$Js42%u\n]pugCeK)\cGt,pAY/6WrE#!_H[!=!$2";#"7,Ds03St -rji)5p&>%`eGfIKQ\GG=!TuB=rr=)9rr=)9rr='js+13Bs*t~> -!$2(=!H#(;rrGX?rr3"bLAq2Zju<=uGst.lTg/MU!A3d3rr=)<rrHr?rr3SB@K6@PVn/[Ps8UQZ -pAb/mEq]M6nLOS<!CGQ?rsY$Js8Q96\riK^s"Wm>rrIY?pAY06<W2pt*W?!?B=@iLr^@-E7DAe$ -aT)9]WV-8lSUg_>!UMN=rsV$Js42%u\n]pugCeK)\cGt,pAY/6WrE#!_H[!=!$2";#"7,Ds03St -rji)5p&>%`eGfIKQ\GG=!TuB=rr=)9rr=)9rr='js+13Bs*t~> -!$2(=!H#(;rrGX?rr3"bLAq2Zju<>?l.=P>h*:l<!A3d3rr=)<rrHr?rr3;:@K1_Qq>^5bJc#HJ -WcIh5!V%]=rrGI@rr3ChMuQnts8VVDV>i::rr3"LSG)s`a&W*<!$2%<")#(BrVkCOls@F+$N;FT -&f]0jrrJ[@rr3#c0`D"[4Q,F!jT#8@9^LLmk5PA]/+N?5!HP4>rrKu@rVlj<rVm+KW;6JnZ!6So -rr=)4rrG[?rr3"cL&V)Qk;N;=!E[;<rrGp?qYpO9JcC<$TDsE~> -!$2(=!H#(;rrGX?rr3"bLAq2Zju<>?l.=P>h*:l<!A3d3rr=)<rrHr?rr3;:@K1_Qq>^5bJc#HJ -WcIh5!V%]=rrGI@rr3ChMuQnts8VVDV>i::rr3"LSG)s`a&W*<!$2%<")#(BrVkCOls@F+$N;FT -&f]0jrrJ[@rr3#c0`D"[4Q,F!jT#8@9^LLmk5PA]/+N?5!HP4>rrKu@rVlj<rVm+KW;6JnZ!6So -rr=)4rrG[?rr3"cL&V)Qk;N;=!E[;<rrGp?qYpO9JcC<$TDsE~> -!$2(=!H#(;rrGX?rr3"bLAq2Zju<>?l.=P>h*:l<!A3d3rr=)<rrHr?rr3;:@K1_Qq>^5bJc#HJ -WcIh5!V%]=rrGI@rr3ChMuQnts8VVDV>i::rr3"LSG)s`a&W*<!$2%<")#(BrVkCOls@F+$N;FT -&f]0jrrJ[@rr3#c0`D"[4Q,F!jT#8@9^LLmk5PA]/+N?5!HP4>rrKu@rVlj<rVm+KW;6JnZ!6So -rr=)4rrG[?rr3"cL&V)Qk;N;=!E[;<rrGp?qYpO9JcC<$TDsE~> -!$2(=!H#(;rrGX?rr31nL&(cKfL5W:!ROO=rrF_?qu6]dn,*+b*W?!MG,kK?r3L5>DR'->r2b#? -o_\Xf!NL2=rrMNXr;Qff/,fJ`:X9"?q2^a>8(IY>q1kI>5McA>pjf.=rrW,cp&+gja&W*<"t]9D -s7^9?r;QlmA&J?FoE&p)r;Q]tq<dtTrrJ[@rr3St47iLPoL@j%6hC?Xo02Ho62gfa/+NW=!qu$Y -r;Qe<WrE#&`aJN?s7%Z>rrVn+_#F?7Z#'C=!$2%<!qYgXr;QdgeGfIPSqQq>s4Kd=rrVdXkPY>] -o/c@:rr='js+13Bs*t~> -!$2(=!H#(;rrGX?rr31nL&(cKfL5W:!ROO=rrF_?qu6]dn,*+b*W?!MG,kK?r3L5>DR'->r2b#? -o_\Xf!NL2=rrMNXr;Qff/,fJ`:X9"?q2^a>8(IY>q1kI>5McA>pjf.=rrW,cp&+gja&W*<"t]9D -s7^9?r;QlmA&J?FoE&p)r;Q]tq<dtTrrJ[@rr3St47iLPoL@j%6hC?Xo02Ho62gfa/+NW=!qu$Y -r;Qe<WrE#&`aJN?s7%Z>rrVn+_#F?7Z#'C=!$2%<!qYgXr;QdgeGfIPSqQq>s4Kd=rrVdXkPY>] -o/c@:rr='js+13Bs*t~> -!$2(=!H#(;rrGX?rr31nL&(cKfL5W:!ROO=rrF_?qu6]dn,*+b*W?!MG,kK?r3L5>DR'->r2b#? -o_\Xf!NL2=rrMNXr;Qff/,fJ`:X9"?q2^a>8(IY>q1kI>5McA>pjf.=rrW,cp&+gja&W*<"t]9D -s7^9?r;QlmA&J?FoE&p)r;Q]tq<dtTrrJ[@rr3St47iLPoL@j%6hC?Xo02Ho62gfa/+NW=!qu$Y -r;Qe<WrE#&`aJN?s7%Z>rrVn+_#F?7Z#'C=!$2%<!qYgXr;QdgeGfIPSqQq>s4Kd=rrVdXkPY>] -o/c@:rr='js+13Bs*t~> -!$2(=!H#(>rseACd5nGXg&M)7QI4)Lrr3GhdF$63s8V?2d3Z]XhYmHT,o6L[%Hc'c(t$ais8R#B -dC<ffrs3;IdBd]is*:UCd/f_qrr3&r!'pP`%GoLO,LOp&s8Pd:d@H*mrrbg=d?p!orrbd<d?9go -rrULHCB"5Ef$U6^dF%I`qF/fZJueqOr;P(EIBrbM!HY7=ruS!:PlLd^dF!JXdF%ahf3m"RB?pPW -d:L_Q@b(M<=T-VJ9(W&^'70_os8Vc>d9V[XdJs6SHdU58nG`FgJ"HW=!P`^Sd/ZZ7rt8-.l2UeO -dEt%YdFnR"P*XMC7fE>f<64t:!DLl9rr='js+13Bs*t~> -!$2(=!H#(>rseACd5nGXg&M)7QI4)Lrr3GhdF$63s8V?2d3Z]XhYmHT,o6L[%Hc'c(t$ais8R#B -dC<ffrs3;IdBd]is*:UCd/f_qrr3&r!'pP`%GoLO,LOp&s8Pd:d@H*mrrbg=d?p!orrbd<d?9go -rrULHCB"5Ef$U6^dF%I`qF/fZJueqOr;P(EIBrbM!HY7=ruS!:PlLd^dF!JXdF%ahf3m"RB?pPW -d:L_Q@b(M<=T-VJ9(W&^'70_os8Vc>d9V[XdJs6SHdU58nG`FgJ"HW=!P`^Sd/ZZ7rt8-.l2UeO -dEt%YdFnR"P*XMC7fE>f<64t:!DLl9rr='js+13Bs*t~> -!$2(=!H#(>rseACd5nGXg&M)7QI4)Lrr3GhdF$63s8V?2d3Z]XhYmHT,o6L[%Hc'c(t$ais8R#B -dC<ffrs3;IdBd]is*:UCd/f_qrr3&r!'pP`%GoLO,LOp&s8Pd:d@H*mrrbg=d?p!orrbd<d?9go -rrULHCB"5Ef$U6^dF%I`qF/fZJueqOr;P(EIBrbM!HY7=ruS!:PlLd^dF!JXdF%ahf3m"RB?pPW -d:L_Q@b(M<=T-VJ9(W&^'70_os8Vc>d9V[XdJs6SHdU58nG`FgJ"HW=!P`^Sd/ZZ7rt8-.l2UeO -dEt%YdFnR"P*XMC7fE>f<64t:!DLl9rr='js+13Bs*t~> -!$2(=#&Sap[f?B>r(@$+rr3(Z92#3^rr\``92GQe!3,lh!.OnH!G*,IrrAVf9)skKrrR(2B)MZ1 -BM31?rr@9B9)sbGrrW'`5lUc`JG`%?W;ceu:eQK?rr`6n98`]J"8GM^OSo+[h)/sHrr?O+9*WB8 -s5lsIUAk/mp&7SaW;?MpSAY=*"i)ONs8Vlk9*XGVs2[i+_#=98_FmPArr2uGrC[.KrVm%q*5DOR -gAFSD:&b.pX%Q+BrVlnp]);R.Zi*Msrr)j#KFd>Is1J8&!,MT6"-#rHr;HWrLY2M(!J0,*rr^WT -Zl=SrJcDABJ,~> -!$2(=#&Sap[f?B>r(@$+rr3(Z92#3^rr\``92GQe!3,lh!.OnH!G*,IrrAVf9)skKrrR(2B)MZ1 -BM31?rr@9B9)sbGrrW'`5lUc`JG`%?W;ceu:eQK?rr`6n98`]J"8GM^OSo+[h)/sHrr?O+9*WB8 -s5lsIUAk/mp&7SaW;?MpSAY=*"i)ONs8Vlk9*XGVs2[i+_#=98_FmPArr2uGrC[.KrVm%q*5DOR -gAFSD:&b.pX%Q+BrVlnp]);R.Zi*Msrr)j#KFd>Is1J8&!,MT6"-#rHr;HWrLY2M(!J0,*rr^WT -Zl=SrJcDABJ,~> -!$2(=#&Sap[f?B>r(@$+rr3(Z92#3^rr\``92GQe!3,lh!.OnH!G*,IrrAVf9)skKrrR(2B)MZ1 -BM31?rr@9B9)sbGrrW'`5lUc`JG`%?W;ceu:eQK?rr`6n98`]J"8GM^OSo+[h)/sHrr?O+9*WB8 -s5lsIUAk/mp&7SaW;?MpSAY=*"i)ONs8Vlk9*XGVs2[i+_#=98_FmPArr2uGrC[.KrVm%q*5DOR -gAFSD:&b.pX%Q+BrVlnp]);R.Zi*Msrr)j#KFd>Is1J8&!,MT6"-#rHr;HWrLY2M(!J0,*rr^WT -Zl=SrJcDABJ,~> -!$2(="/omHD;5'j*S^Spgd'Wo!O?IprrIq?g]%:[^YAbh1[3cr"/0CAFFjICs.B=A~> -!$2(="/omHD;5'j*S^Spgd'Wo!O?IprrIq?g]%:[^YAbh1[3cr"/0CAFFjICs.B=A~> -!$2(="/omHD;5'j*S^Spgd'Wo!O?IprrIq?g]%:[^YAbh1[3cr"/0CAFFjICs.B=A~> -!$0Vi!KE/qrrW+FUtktOk'abprrUPsbhW4"\q/eprrT!Kon!-os+14Fs*t~> -!$0Vi!KE/qrrW+FUtktOk'abprrUPsbhW4"\q/eprrT!Kon!-os+14Fs*t~> -!$0Vi!KE/qrrW+FUtktOk'abprrUPsbhW4"\q/eprrT!Kon!-os+14Fs*t~> -!$0Vi!O2LWrrN)%g]%<B^>&YghTjmU!RKTVrrKn$JcC<$JcGKEJ,~> -!$0Vi!O2LWrrN)%g]%<B^>&YghTjmU!RKTVrrKn$JcC<$JcGKEJ,~> -!$0Vi!O2LWrrN)%g]%<B^>&YghTjmU!RKTVrrKn$JcC<$JcGKEJ,~> -!$.X1!SDh6rrC7A5QK%#rrJ>#p&>&uU\t,l_>Z\"ec#LKV6GONJcC<$hZ&*~> -!$.X1!SDh6rrC7A5QK%#rrJ>#p&>&uU\t,l_>Z\"ec#LKV6GONJcC<$hZ&*~> -!$.X1!SDh6rrC7A5QK%#rrJ>#p&>&uU\t,l_>Z\"ec#LKV6GONJcC<$hZ&*~> -!Zh<Ir$)>4s8S:^o)Ac,,O4Up!^%].ec,^;8E]sO"TCUbZAJS\"Z/[Js,sDOrs&<E>Q=_N_"[j3 -dV8`9[J'V'\lIP@p@\FeS;d+>rrC+<,66HsrrV_<Y5SA'o\A4na8Q#>mVojjs+13$s53j,~> -!Zh<Ir$)>4s8S:^o)Ac,,O4Up!^%].ec,^;8E]sO"TCUbZAJS\"Z/[Js,sDOrs&<E>Q=_N_"[j3 -dV8`9[J'V'\lIP@p@\FeS;d+>rrC+<,66HsrrV_<Y5SA'o\A4na8Q#>mVojjs+13$s53j,~> -!Zh<Ir$)>4s8S:^o)Ac,,O4Up!^%].ec,^;8E]sO"TCUbZAJS\"Z/[Js,sDOrs&<E>Q=_N_"[j3 -dV8`9[J'V'\lIP@p@\FeS;d+>rrC+<,66HsrrV_<Y5SA'o\A4na8Q#>mVojjs+13$s53j,~> -!Zh=>rV6]S3WK-WF0PQ6rrW)nrp9Xf5Ml4O?/GS],PqE@f1,]<&cNmPVK`(?2WiUik^]N0s6V]9 -rrs_D_.`%J]_DF/b>J:\WV6>m\mk:2!r%S@rr3#S7/co^0_,/B!f!3?qu6[sa8Gr=GH(Ijs+13$ -s5<p-~> -!Zh=>rV6]S3WK-WF0PQ6rrW)nrp9Xf5Ml4O?/GS],PqE@f1,]<&cNmPVK`(?2WiUik^]N0s6V]9 -rrs_D_.`%J]_DF/b>J:\WV6>m\mk:2!r%S@rr3#S7/co^0_,/B!f!3?qu6[sa8Gr=GH(Ijs+13$ -s5<p-~> -!Zh=>rV6]S3WK-WF0PQ6rrW)nrp9Xf5Ml4O?/GS],PqE@f1,]<&cNmPVK`(?2WiUik^]N0s6V]9 -rrs_D_.`%J]_DF/b>J:\WV6>m\mk:2!r%S@rr3#S7/co^0_,/B!f!3?qu6[sa8Gr=GH(Ijs+13$ -s5<p-~> -!$2";!S0d=rrR#CrV$6nilfOArVllVrlY;kr;QfHd/<nK3o^1dHN4$"rlYl+s8V$Is3AgBs8Us! -bPB?;rr2uLrlYT(s8Ufr(X("[rVlj<qu7B09`MVcs8Q9ls2[$bs8UT^-HjTqrr2uBrlY`7s8Pal -s0OVbs8UL?rlY<.rr3,OFoRN7pAY0)@f?<-qpkZF"k`YNs/C)<rrM+mrr3&R:%\Da!F<J;rrH-? -qu6[kd"24Js+14.s*t~> -!$2";!S0d=rrR#CrV$6nilfOArVllVrlY;kr;QfHd/<nK3o^1dHN4$"rlYl+s8V$Is3AgBs8Us! -bPB?;rr2uLrlYT(s8Ufr(X("[rVlj<qu7B09`MVcs8Q9ls2[$bs8UT^-HjTqrr2uBrlY`7s8Pal -s0OVbs8UL?rlY<.rr3,OFoRN7pAY0)@f?<-qpkZF"k`YNs/C)<rrM+mrr3&R:%\Da!F<J;rrH-? -qu6[kd"24Js+14.s*t~> -!$2";!S0d=rrR#CrV$6nilfOArVllVrlY;kr;QfHd/<nK3o^1dHN4$"rlYl+s8V$Is3AgBs8Us! -bPB?;rr2uLrlYT(s8Ufr(X("[rVlj<qu7B09`MVcs8Q9ls2[$bs8UT^-HjTqrr2uBrlY`7s8Pal -s0OVbs8UL?rlY<.rr3,OFoRN7pAY0)@f?<-qpkZF"k`YNs/C)<rrM+mrr3&R:%\Da!F<J;rrH-? -qu6[kd"24Js+14.s*t~> -!$2%<!luR\qu6\6YP.tuSPTp\rr3#PGQ(D>@)`*E!@8NZrrG1?rVloGD>m?GC!H[BQ*41aD:&;e -4]%qNE7=jeFoG2CF4L;C:^KrpQMpg`*UNe4CeG:8:p'ct\P`?&!J]_2rrF"/r_NWSlgOiS\mkX< -!UMN=rrkOCs8T'>q>UNBCZ53k!F<J;rrH-?qu6[kd"24Js+14.s*t~> -!$2%<!luR\qu6\6YP.tuSPTp\rr3#PGQ(D>@)`*E!@8NZrrG1?rVloGD>m?GC!H[BQ*41aD:&;e -4]%qNE7=jeFoG2CF4L;C:^KrpQMpg`*UNe4CeG:8:p'ct\P`?&!J]_2rrF"/r_NWSlgOiS\mkX< -!UMN=rrkOCs8T'>q>UNBCZ53k!F<J;rrH-?qu6[kd"24Js+14.s*t~> -!$2%<!luR\qu6\6YP.tuSPTp\rr3#PGQ(D>@)`*E!@8NZrrG1?rVloGD>m?GC!H[BQ*41aD:&;e -4]%qNE7=jeFoG2CF4L;C:^KrpQMpg`*UNe4CeG:8:p'ct\P`?&!J]_2rrF"/r_NWSlgOiS\mkX< -!UMN=rrkOCs8T'>q>UNBCZ53k!F<J;rrH-?qu6[kd"24Js+14.s*t~> -!$2%<".O.FUAOrj*VB@42Wk#=!JQm>rrLY@rVlnMHN*pI`0)9pTDnikCp<p=&\DKOs$g8gs0HG> ->J^=[s/L,:rrJj?rr2s=qYpO9li."Q0`:qO+T;<>!Nj`DrrEjhrr3"*^@2(&\mkX<$hSPIs8TTP -mJm3cF8Gn<b'_b:rrHE?qu6[sa8>l;:!eIkJcC<$i;\<~> -!$2%<".O.FUAOrj*VB@42Wk#=!JQm>rrLY@rVlnMHN*pI`0)9pTDnikCp<p=&\DKOs$g8gs0HG> ->J^=[s/L,:rrJj?rr2s=qYpO9li."Q0`:qO+T;<>!Nj`DrrEjhrr3"*^@2(&\mkX<$hSPIs8TTP -mJm3cF8Gn<b'_b:rrHE?qu6[sa8>l;:!eIkJcC<$i;\<~> -!$2%<".O.FUAOrj*VB@42Wk#=!JQm>rrLY@rVlnMHN*pI`0)9pTDnikCp<p=&\DKOs$g8gs0HG> ->J^=[s/L,:rrJj?rr2s=qYpO9li."Q0`:qO+T;<>!Nj`DrrEjhrr3"*^@2(&\mkX<$hSPIs8TTP -mJm3cF8Gn<b'_b:rrHE?qu6[sa8>l;:!eIkJcC<$i;\<~> -!$1t:!V[r=rrJ%?o`"qMk5PA]KpVf=!S'a9rrg)o++'CSrrHr?rr3\E@K6?sf)Pd*])Q!NruM-> -ErZ0&rcJ66HN*pE*W#d9*UE_+m4eJ<!1Ee.!5nR2!$2%<!FEM/rrK]?r;QrH:B1>t_uBZ:WH7t: -!mH/@qYpS%^\e$3<QG":!DUpls+13$s5<p-~> -!$1t:!V[r=rrJ%?o`"qMk5PA]KpVf=!S'a9rrg)o++'CSrrHr?rr3\E@K6?sf)Pd*])Q!NruM-> -ErZ0&rcJ66HN*pE*W#d9*UE_+m4eJ<!1Ee.!5nR2!$2%<!FEM/rrK]?r;QrH:B1>t_uBZ:WH7t: -!mH/@qYpS%^\e$3<QG":!DUpls+13$s5<p-~> -!$1t:!V[r=rrJ%?o`"qMk5PA]KpVf=!S'a9rrg)o++'CSrrHr?rr3\E@K6?sf)Pd*])Q!NruM-> -ErZ0&rcJ66HN*pE*W#d9*UE_+m4eJ<!1Ee.!5nR2!$2%<!FEM/rrK]?r;QrH:B1>t_uBZ:WH7t: -!mH/@qYpS%^\e$3<QG":!DUpls+13$s5<p-~> -!$1t:!V[r>rrRqDqt0mh2Wk#=!JQm>[email protected]/lXCp<p="hS4Cs$kT;rs;oGruM->ErV/d -rhfd3HN*pE*W#d9*UE_+m4eG;!3#mp!IDl_rr=)<rrHH?n,EF"@f660mZ3s:MsC<A!NC/<rrU\m -deWnD?,-::!EI2;rrGj@JcC<$JcF[.J,~> -!$1t:!V[r>rrRqDqt0mh2Wk#=!JQm>[email protected]/lXCp<p="hS4Cs$kT;rs;oGruM->ErV/d -rhfd3HN*pE*W#d9*UE_+m4eG;!3#mp!IDl_rr=)<rrHH?n,EF"@f660mZ3s:MsC<A!NC/<rrU\m -deWnD?,-::!EI2;rrGj@JcC<$JcF[.J,~> -!$1t:!V[r>rrRqDqt0mh2Wk#=!JQm>[email protected]/lXCp<p="hS4Cs$kT;rs;oGruM->ErV/d -rhfd3HN*pE*W#d9*UE_+m4eG;!3#mp!IDl_rr=)<rrHH?n,EF"@f660mZ3s:MsC<A!NC/<rrU\m -deWnD?,-::!EI2;rrGj@JcC<$JcF[.J,~> -"!.ESQ2^dapEon>!f3H@oD\hLk5PA]KpVf=!S'a>rrH1pr;QdYir/lXCp<p="hS4Cs$kT;rsi8L -ruM->ErT(\s8U@jHN*pI*WQ/ET`4rl\mt+,#jj>Gs-L=s;uQ^q!H5+8rr=)<rrHH?pAY3*40AJ] -!P2b;rr]NAGGY9<!pbT@rr3&[?J>5T!h#5@qu6[sa8Gr=N19J;rrRp:iIV#[s+146s*t~> -"!.ESQ2^dapEon>!f3H@oD\hLk5PA]KpVf=!S'a>rrH1pr;QdYir/lXCp<p="hS4Cs$kT;rsi8L -ruM->ErT(\s8U@jHN*pI*WQ/ET`4rl\mt+,#jj>Gs-L=s;uQ^q!H5+8rr=)<rrHH?pAY3*40AJ] -!P2b;rr]NAGGY9<!pbT@rr3&[?J>5T!h#5@qu6[sa8Gr=N19J;rrRp:iIV#[s+146s*t~> -"!.ESQ2^dapEon>!f3H@oD\hLk5PA]KpVf=!S'a>rrH1pr;QdYir/lXCp<p="hS4Cs$kT;rsi8L -ruM->ErT(\s8U@jHN*pI*WQ/ET`4rl\mt+,#jj>Gs-L=s;uQ^q!H5+8rr=)<rrHH?pAY3*40AJ] -!P2b;rr]NAGGY9<!pbT@rr3&[?J>5T!h#5@qu6[sa8Gr=N19J;rrRp:iIV#[s+146s*t~> -"!.F7L]/>!-0>1,OHfM3rs4/s"rf])s7("+*W\IhrVlmE(B"153o^,<!Ua.j*X,I.s8PR>r;R72 -_>iTrWcJ.*7ii^IO*^g*"P=b:D".H(!U2E,rs8S9*Zg(.s5bC&*W_5mq#:=7rVln)^@qR.V#ZbY -rrK]?qu6clO,!<)rrJ(@rr3#R#lG_DoD\ajH_UB:!EI2<rrHl?qu6_+!8)l&JcC<$kl6/~> -"!.F7L]/>!-0>1,OHfM3rs4/s"rf])s7("+*W\IhrVlmE(B"153o^,<!Ua.j*X,I.s8PR>r;R72 -_>iTrWcJ.*7ii^IO*^g*"P=b:D".H(!U2E,rs8S9*Zg(.s5bC&*W_5mq#:=7rVln)^@qR.V#ZbY -rrK]?qu6clO,!<)rrJ(@rr3#R#lG_DoD\ajH_UB:!EI2<rrHl?qu6_+!8)l&JcC<$kl6/~> -"!.F7L]/>!-0>1,OHfM3rs4/s"rf])s7("+*W\IhrVlmE(B"153o^,<!Ua.j*X,I.s8PR>r;R72 -_>iTrWcJ.*7ii^IO*^g*"P=b:D".H(!U2E,rs8S9*Zg(.s5bC&*W_5mq#:=7rVln)^@qR.V#ZbY -rrK]?qu6clO,!<)rrJ(@rr3#R#lG_DoD\ajH_UB:!EI2<rrHl?qu6_+!8)l&JcC<$kl6/~> -!Zh=@r;Q]q!<2Qhr;Q]qr;QWos8Mrr!<2lqr;Q]q!<2lq!<2or!ri6"rVcitrr)lrrqucsct2PC -rr2osr;QHj!<2rs!<2Wj!Jcp<rrg.X*?E#qs8W)qrrW1K\,QF)rr2ouUltZU"L]<k*6nH^!L9>S -s8W(Ls+13$s6'E4~> -!Zh=@r;Q]q!<2Qhr;Q]qr;QWos8Mrr!<2lqr;Q]q!<2lq!<2or!ri6"rVcitrr)lrrqucsct2PC -rr2osr;QHj!<2rs!<2Wj!Jcp<rrg.X*?E#qs8W)qrrW1K\,QF)rr2ouUltZU"L]<k*6nH^!L9>S -s8W(Ls+13$s6'E4~> -!Zh=@r;Q]q!<2Qhr;Q]qr;QWos8Mrr!<2lqr;Q]q!<2lq!<2or!ri6"rVcitrr)lrrqucsct2PC -rr2osr;QHj!<2rs!<2Wj!Jcp<rrg.X*?E#qs8W)qrrW1K\,QF)rr2ouUltZU"L]<k*6nH^!L9>S -s8W(Ls+13$s6'E4~> -!$-XjhZ!ZCKWKb)JcC<$W;hA~> -!$-XjhZ!ZCKWKb)JcC<$W;hA~> -!$-XjhZ!ZCKWKb)JcC<$W;hA~> -!$-XjhZ!VoV1JYts+13Js*t~> -!$-XjhZ!VoV1JYts+13Js*t~> -!$-XjhZ!VoV1JYts+13Js*t~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -"!.EGKDtlRpH/DRMZ3VYn3d5LP5bIij\#Q@RfEEb3<0$WGlA]a6N$i_#?;`a8,rVfC&T+R;>l(G -JcC<$JcE=]J,~> -"!.EGKDtlRpH/DRMZ3VYn3d5LP5bIij\#Q@RfEEb3<0$WGlA]a6N$i_#?;`a8,rVfC&T+R;>l(G -JcC<$JcE=]J,~> -"!.EGKDtlRpH/DRMZ3VYn3d5LP5bIij\#Q@RfEEb3<0$WGlA]a6N$i_#?;`a8,rVfC&T+R;>l(G -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4IfB?]mOnO*LB%;6+?0=\NrT.U,6.[sQi@!ceO]]>#=@//1]RJsW;cet`*%Zk -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4IfB?]mOnO*LB%;6+?0=\NrT.U,6.[sQi@!ceO]]>#=@//1]RJsW;cet`*%Zk -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4IfB?]mOnO*LB%;6+?0=\NrT.U,6.[sQi@!ceO]]>#=@//1]RJsW;cet`*%Zk -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4?iIkU[4_KFLB#Xsj#@!uNrT.U,6.[sQi@!ceO]]>#=@//1]RJsJ,[7bPZ`S; -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4?iIkU[4_KFLB#Xsj#@!uNrT.U,6.[sQi@!ceO]]>#=@//1]RJsJ,[7bPZ`S; -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4?iIkU[4_KFLB#Xsj#@!uNrT.U,6.[sQi@!ceO]]>#=@//1]RJsJ,[7bPZ`S; -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4/cJof<%e3:L;e#pju<=#NrT.U,6.[sQi@!neO]`?`bsu(1]RJs4T/F`P_&jc -s+13$s185\~> -"!.E>FoMCDpEop4/cJof<%e3:L;e#pju<=#NrT.U,6.[sQi@!neO]`?`bsu(1]RJs4T/F`P_&jc -s+13$s185\~> -"!.E>FoMCDpEop4/cJof<%e3:L;e#pju<=#NrT.U,6.[sQi@!neO]`?`bsu(1]RJs4T/F`P_&jc -s+13$s185\~> -"!.E>FoMCDN@+[tIfB?]mOnO*7t'7oju<=#NrT.U,6.[sQi@!neO]`?@(lU)1]RJsW.Fu"s+13$ -s0_lW~> -"!.E>FoMCDN@+[tIfB?]mOnO*7t'7oju<=#NrT.U,6.[sQi@!neO]`?@(lU)1]RJsW.Fu"s+13$ -s0_lW~> -"!.E>FoMCDN@+[tIfB?]mOnO*7t'7oju<=#NrT.U,6.[sQi@!neO]`?@(lU)1]RJsW.Fu"s+13$ -s0_lW~> -"!.EqJc9EfN$eRsIfB?]mOnO*:>,[Iju<=#4DS_H%ZgY6I/[miEjG_O-r4Vf'q,*@H2_RZNIh+\ -s+13$s1//[~> -"!.EqJc9EfN$eRsIfB?]mOnO*:>,[Iju<=#4DS_H%ZgY6I/[miEjG_O-r4Vf'q,*@H2_RZNIh+\ -s+13$s1//[~> -"!.EqJc9EfN$eRsIfB?]mOnO*:>,[Iju<=#4DS_H%ZgY6I/[miEjG_O-r4Vf'q,*@H2_RZNIh+\ -s+13$s1//[~> -"!.FAg]"G\n0\1pfDbdQqS3'fgA_*SpVQscrNQKo5QCc2rilU?s8RLcrilTd;#gR>rilI@JcC<$ -JcC<$\c70~> -"!.FAg]"G\n0\1pfDbdQqS3'fgA_*SpVQscrNQKo5QCc2rilU?s8RLcrilTd;#gR>rilI@JcC<$ -JcC<$\c70~> -"!.FAg]"G\n0\1pfDbdQqS3'fgA_*SpVQscrNQKo5QCc2rilU?s8RLcrilTd;#gR>rilI@JcC<$ -JcC<$\c70~> -"!.EOOT,7\pEo5+!Sp!8rrM9Lrr3#lh1>TWs+13$s0DZT~> -"!.EOOT,7\pEo5+!Sp!8rrM9Lrr3#lh1>TWs+13$s0DZT~> -"!.EOOT,7\pEo5+!Sp!8rrM9Lrr3#lh1>TWs+13$s0DZT~> -"!.F:N;agu,jt!u!W;G<s+13$s+13Es*t~> -"!.F:N;agu,jt!u!W;G<s+13$s+13Es*t~> -"!.F:N;agu,jt!u!W;G<s+13$s+13Es*t~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$1k7!Tr3hs+13$s+130s*t~> -!$1k7!Tr3hs+13$s+130s*t~> -!$1k7!Tr3hs+13$s+130s*t~> -!$1k7!M=gls+13$s+130s*t~> -!$1k7!M=gls+13$s+130s*t~> -!$1k7!M=gls+13$s+130s*t~> -!$1k7!M=gls+13$s+130s*t~> -!$1k7!M=gls+13$s+130s*t~> -!$1k7!M=gls+13$s+130s*t~> -"!.FA\c-1DC]FF,IfB?Jon%bpJcC<$JcCo5J,~> -"!.FA\c-1DC]FF,IfB?Jon%bpJcC<$JcCo5J,~> -"!.FA\c-1DC]FF,IfB?Jon%bpJcC<$JcCo5J,~> -"!.EWHiCG[ZXj*.IfKF\:r@kaJcC<$JcCo5J,~> -"!.EWHiCG[ZXj*.IfKF\:r@kaJcC<$JcCo5J,~> -"!.EWHiCG[ZXj*.IfKF\:r@kaJcC<$JcCo5J,~> -"!.E>FoMCHpEop4IX`oYeq*jPs+13$s,m>3~> -"!.E>FoMCHpEop4IX`oYeq*jPs+13$s,m>3~> -"!.E>FoMCHpEop4IX`oYeq*jPs+13$s,m>3~> -"!.E>FoMCGpEop4%[H&MJcC<$JcC<$OT0h~> -"!.E>FoMCGpEop4%[H&MJcC<$JcC<$OT0h~> -"!.E>FoMCGpEop4%[H&MJcC<$JcC<$OT0h~> -"!.E>FoMCHpEop4IXWfXf7EsQs+13$s,m>3~> -"!.E>FoMCHpEop4IXWfXf7EsQs+13$s,m>3~> -"!.E>FoMCHpEop4IXWfXf7EsQs+13$s,m>3~> -"!.EWHiCJ\Zt'-.IfKF[:W.haJcC<$JcCo5J,~> -"!.EWHiCJ\Zt'-.IfKF[:W.haJcC<$JcCo5J,~> -"!.EWHiCJ\Zt'-.IfKF[:W.haJcC<$JcCo5J,~> -"!.FA\Gg"ACB+=QZ2Xb(omhVnJcC<$JcCo5J,~> -"!.FA\Gg"ACB+=QZ2Xb(omhVnJcC<$JcCo5J,~> -"!.FA\Gg"ACB+=QZ2Xb(omhVnJcC<$JcCo5J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$1>(!5\M"!/Q4+JcC<$JcDMFJ,~> -!$1>(!5\M"!/Q4+JcC<$JcDMFJ,~> -!$1>(!5\M"!/Q4+JcC<$JcDMFJ,~> -!$1>(!NTW+fE%aPJcC<$JcC<$U]5i~> -!$1>(!NTW+fE%aPJcC<$JcC<$U]5i~> -!$1>(!NTW+fE%aPJcC<$JcC<$U]5i~> -!$2";!UqE7rrS:OqY'piX)\(;!Phrks+13$s+13Fs*t~> -!$2";!UqE7rrS:OqY'piX)\(;!Phrks+13$s+13Fs*t~> -!$2";!UqE7rrS:OqY'piX)\(;!Phrks+13$s+13Fs*t~> -!$2%<!o?]]rVlqMU\FN_!NU5<rrKo?JcC<$JcC<$U]5i~> -!$2%<!o?]]rVlqMU\FN_!NU5<rrKo?JcC<$JcC<$U]5i~> -!$2%<!o?]]rVlqMU\FN_!NU5<rrKo?JcC<$JcC<$U]5i~> -!$2(="4M)A*W5p=I\Q`7rrK0?r;Qf5>_2p+s+13$s.fUE~> -!$2(="4M)A*W5p=I\Q`7rrK0?r;Qf5>_2p+s+13$s.fUE~> -!$2(="4M)A*W5p=I\Q`7rrK0?r;Qf5>_2p+s+13$s.fUE~> -#9EjEc[=+>*W,j;JsuK7!NU5<rrKo?JcC<$JcC<$U]5i~> -#9EjEc[=+>*W,j;JsuK7!NU5<rrKo?JcC<$JcC<$U]5i~> -#9EjEc[=+>*W,j;JsuK7!NU5<rrKo?JcC<$JcC<$U]5i~> -#9EilJ'@rm*W#d9*VfX8X)\(;!Phrks+13$s+13Fs*t~> -#9EilJ'@rm*W#d9*VfX8X)\(;!Phrks+13$s+13Fs*t~> -#9EilJ'@rm*W#d9*VfX8X)\(;!Phrks+13$s+13Fs*t~> -"!.E>.fNZR$Zu=H!Ht@8rrK0?r;Qf5>_2p+s+13$s.fUE~> -"!.E>.fNZR$Zu=H!Ht@8rrK0?r;Qf5>_2p+s+13$s.fUE~> -"!.E>.fNZR$Zu=H!Ht@8rrK0?r;Qf5>_2p+s+13$s.fUE~> -!Zh=*rP/FLao25@FK>?7rrK0?r;Qf5>_2p+s+13$s.fUE~> -!Zh=*rP/FLao25@FK>?7rrK0?r;Qf5>_2p+s+13$s.fUE~> -!Zh=*rP/FLao25@FK>?7rrK0?r;Qf5>_2p+s+13$s.fUE~> -!$1t:!$2%<!dUd@p\t8pEW#h;^g)HjJcC<$JcDMFJ,~> -!$1t:!$2%<!dUd@p\t8pEW#h;^g)HjJcC<$JcDMFJ,~> -!$1t:!$2%<!dUd@p\t8pEW#h;^g)HjJcC<$JcDMFJ,~> -!$1t:!&FQR!g'2VpAY/oEW#h;^g)HjJcC<$JcDMFJ,~> -!$1t:!&FQR!g'2VpAY/oEW#h;^g)HjJcC<$JcDMFJ,~> -!$1t:!&FQR!g'2VpAY/oEW#h;^g)HjJcC<$JcDMFJ,~> -!$1>(!NU5<rrKo?JcC<$JcC<$U]5i~> -!$1>(!NU5<rrKo?JcC<$JcC<$U]5i~> -!$1>(!NU5<rrKo?JcC<$JcC<$U]5i~> -!$1>(!NSrmXT=#YJcC<$JcC<$U]5i~> -!$1>(!NSrmXT=#YJcC<$JcC<$U]5i~> -!$1>(!NSrmXT=#YJcC<$JcC<$U]5i~> -!$1>(!71L[!2G,FJcC<$JcDMFJ,~> -!$1>(!71L[!2G,FJcC<$JcDMFJ,~> -!$1>(!71L[!2G,FJcC<$JcDMFJ,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -%%EndData -showpage -%%Trailer -end -%%EOF diff --git a/lib/stdlib/doc/src/ushell2.gif b/lib/stdlib/doc/src/ushell2.gif Binary files differdeleted file mode 100644 index 273cf2078a..0000000000 --- a/lib/stdlib/doc/src/ushell2.gif +++ /dev/null diff --git a/lib/stdlib/doc/src/ushell2.ps b/lib/stdlib/doc/src/ushell2.ps deleted file mode 100644 index e6db3c2be2..0000000000 --- a/lib/stdlib/doc/src/ushell2.ps +++ /dev/null @@ -1,404 +0,0 @@ -%!PS-Adobe-3.0 -%%Creator: GIMP PostScript file plugin V 1.17 by Peter Kirchgessner -%%Title: ushell2.ps -%%CreationDate: Mon Mar 16 09:52:14 2009 -%%DocumentData: Clean7Bit -%%LanguageLevel: 2 -%%Pages: 1 -%%BoundingBox: 14 14 468 74 -%%EndComments -%%BeginProlog -% Use own dictionary to avoid conflicts -10 dict begin -%%EndProlog -%%Page: 1 1 -% Translate for offset -14.173228346456694 14.173228346456694 translate -% Translate to begin of first scanline -0 59.527559055118118 translate -453.54330708661422 -59.527559055118118 scale -% Image geometry -640 84 8 -% Transformation matrix -[ 640 0 0 84 0 0 ] -% Strings to hold RGB-samples per scanline -/rstr 640 string def -/gstr 640 string def -/bstr 640 string def -{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop} -true 3 -%%BeginData: 18704 ASCII Bytes -colorimage -J`MCCJ`MCCJ`M=~> -J`MCCJ`MCCJ`M=~> -J`MCCJ`MCCJ`M=~> -!)nG7JO+iQJO+lRJ,~> -!)nG7JO+iQJO+lRJ,~> -!)nG7JO+iQJO+lRJ,~> -!D=/Y=b0_,=b0_.=b$~> -!D=/Y=b0_,=b0_.=b$~> -!D=/Y=b0_,=b0_.=b$~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M>rrKOJQ2^i3JcC<$JcFU,J,~> -!D>M>rrKOJQ2^i3JcC<$JcFU,J,~> -!D>M>rrKOJQ2^i3JcC<$JcFU,J,~> -"%t`UXo(ooJ,'$ES](+!!dROKr;Qh<:2'>#!I!f.rr]@2s).a0!581-!+,Ru!6ss3!kNp@p\t9X -H-uWnOA=s3rrK.0nG`Jtr;QbGr_<HIrVlo/@tFZ2s+14-s*t~> -"%t`UXo(ooJ,'$ES](+!!dROKr;Qh<:2'>#!I!f.rr]@2s).a0!581-!+,Ru!6ss3!kNp@p\t9X -H-uWnOA=s3rrK.0nG`Jtr;QbGr_<HIrVlo/@tFZ2s+14-s*t~> -"%t`UXo(ooJ,'$ES](+!!dROKr;Qh<:2'>#!I!f.rr]@2s).a0!581-!+,Ru!6ss3!kNp@p\t9X -H-uWnOA=s3rrK.0nG`Jtr;QbGr_<HIrVlo/@tFZ2s+14-s*t~> -"A:iVM0T!F!8RAL!HP42rrUp#*W5p=fZ5E4rrG1@rr3+VQ2`JQrQbK2q#:?CrQbLB\*j7sS:L>5 -!T-**rrJOjli.%FchRG<!HlihrrIe?nc&Yn]MJP,!J%!FcN0Vnrr3#u++aHCs+14-s*t~> -"A:iVM0T!F!8RAL!HP42rrUp#*W5p=fZ5E4rrG1@rr3+VQ2`JQrQbK2q#:?CrQbLB\*j7sS:L>5 -!T-**rrJOjli.%FchRG<!HlihrrIe?nc&Yn]MJP,!J%!FcN0Vnrr3#u++aHCs+14-s*t~> -"A:iVM0T!F!8RAL!HP42rrUp#*W5p=fZ5E4rrG1@rr3+VQ2`JQrQbK2q#:?CrQbLB\*j7sS:L>5 -!T-**rrJOjli.%FchRG<!HlihrrIe?nc&Yn]MJP,!J%!FcN0Vnrr3#u++aHCs+14-s*t~> -"A:iVM3Rl:!;QQc#kn;uEiSm+o_8@b!;HKb!;HEk!$;%;!V[r6rs;$Hs8VtORf>#.rq$3eoD\lN -QMU(M!l&CArVllrrU^6ks5-$;rq$<ks8W#qoFLs$rV6Ego^r._r:GB$o^qnX!W2]krs&2ss8)B[ -rVlg+qXdY#o_/=bKUDN+o_/%Z$2J#IrQSC<s'rY>rrMrfrVloH97d+os+14.s*t~> -"A:iVM3Rl:!;QQc#kn;uEiSm+o_8@b!;HKb!;HEk!$;%;!V[r6rs;$Hs8VtORf>#.rq$3eoD\lN -QMU(M!l&CArVllrrU^6ks5-$;rq$<ks8W#qoFLs$rV6Ego^r._r:GB$o^qnX!W2]krs&2ss8)B[ -rVlg+qXdY#o_/=bKUDN+o_/%Z$2J#IrQSC<s'rY>rrMrfrVloH97d+os+14.s*t~> -"A:iVM3Rl:!;QQc#kn;uEiSm+o_8@b!;HKb!;HEk!$;%;!V[r6rs;$Hs8VtORf>#.rq$3eoD\lN -QMU(M!l&CArVllrrU^6ks5-$;rq$<ks8W#qoFLs$rV6Ego^r._r:GB$o^qnX!W2]krs&2ss8)B[ -rVlg+qXdY#o_/=bKUDN+o_/%Z$2J#IrQSC<s'rY>rrMrfrVloH97d+os+14.s*t~> -#>7/YM1+"-g&D!PlY?DI$r#a^Efa$E-rf=.k?@j5!F+acrr=,;rrMj@o`#1<s8Sp?s8O^er[7f! -nFce_n:I\.rrK]@r;QcHr?qfQqqj5tr[7lYrV35f-kO2nd:1^L-n*1:a!_W=-n+farrK!?rr35q -.KBF2-jn]-rsk_N%3R&fs8Rb&=stdhj7rWTKtI?a"NGcfA%MI8!T6+ls+13$s5<p-~> -#>7/YM1+"-g&D!PlY?DI$r#a^Efa$E-rf=.k?@j5!F+acrr=,;rrMj@o`#1<s8Sp?s8O^er[7f! -nFce_n:I\.rrK]@r;QcHr?qfQqqj5tr[7lYrV35f-kO2nd:1^L-n*1:a!_W=-n+farrK!?rr35q -.KBF2-jn]-rsk_N%3R&fs8Rb&=stdhj7rWTKtI?a"NGcfA%MI8!T6+ls+13$s5<p-~> -#>7/YM1+"-g&D!PlY?DI$r#a^Efa$E-rf=.k?@j5!F+acrr=,;rrMj@o`#1<s8Sp?s8O^er[7f! -nFce_n:I\.rrK]@r;QcHr?qfQqqj5tr[7lYrV35f-kO2nd:1^L-n*1:a!_W=-n+farrK!?rr35q -.KBF2-jn]-rsk_N%3R&fs8Rb&=stdhj7rWTKtI?a"NGcfA%MI8!T6+ls+13$s5<p-~> -#>7/YM'th_:B(7oI@pN=%,$43E\H&Es1hq?B=@g>!OQP=rr=,;rrMj@o`#1<s8Sp?s8Qb.rr3"H -U%SEdTmZ8-!P2e8rrb=Bh[t\DrrXkBdn0N<#U90GWZSDDruV1>!J6g6rs#?Ds3C3-.K08I6JDA; -!EI5>rs"-+b5_Li9CVrcA%M^:\[g>jrrM%@JcC<$JcF[.J,~> -#>7/YM'th_:B(7oI@pN=%,$43E\H&Es1hq?B=@g>!OQP=rr=,;rrMj@o`#1<s8Sp?s8Qb.rr3"H -U%SEdTmZ8-!P2e8rrb=Bh[t\DrrXkBdn0N<#U90GWZSDDruV1>!J6g6rs#?Ds3C3-.K08I6JDA; -!EI5>rs"-+b5_Li9CVrcA%M^:\[g>jrrM%@JcC<$JcF[.J,~> -#>7/YM'th_:B(7oI@pN=%,$43E\H&Es1hq?B=@g>!OQP=rr=,;rrMj@o`#1<s8Sp?s8Qb.rr3"H -U%SEdTmZ8-!P2e8rrb=Bh[t\DrrXkBdn0N<#U90GWZSDDruV1>!J6g6rs#?Ds3C3-.K08I6JDA; -!EI5>rs"-+b5_Li9CVrcA%M^:\[g>jrrM%@JcC<$JcF[.J,~> -"A:iVM3Ro;!O.@YS-6(rs)P.=rrg,Cs(?9LS,mM@rVlj=qu6]k-M7<@Ff=JZXSVqtI%g96!p59A -mJd3uA,Q?,li!=U,37WFrVlsOnC'u-rraPCs/L,=rr@3@rrI_@p&>5nG5k:_oI9b=!CGN<rrH-@ -rVlnOR/[*ddS'*0#%P7Eo4(4*rVloV4b<Was+14.s*t~> -"A:iVM3Ro;!O.@YS-6(rs)P.=rrg,Cs(?9LS,mM@rVlj=qu6]k-M7<@Ff=JZXSVqtI%g96!p59A -mJd3uA,Q?,li!=U,37WFrVlsOnC'u-rraPCs/L,=rr@3@rrI_@p&>5nG5k:_oI9b=!CGN<rrH-@ -rVlnOR/[*ddS'*0#%P7Eo4(4*rVloV4b<Was+14.s*t~> -"A:iVM3Ro;!O.@YS-6(rs)P.=rrg,Cs(?9LS,mM@rVlj=qu6]k-M7<@Ff=JZXSVqtI%g96!p59A -mJd3uA,Q?,li!=U,37WFrVlsOnC'u-rraPCs/L,=rr@3@rrI_@p&>5nG5k:_oI9b=!CGN<rrH-@ -rVlnOR/[*ddS'*0#%P7Eo4(4*rVloV4b<Was+14.s*t~> -"A:iVM3Rl:!3lHR"G!EMEiT-="Me=CB3bCd!4;_)!$;%;!V[r5rr`6B_HQd8!IUU7rrKi?m/I*t -A,ZE.oRHgj"!$CZ55kK`/b%4\rVm!Equ<[:qYpSJS+ZdcVKVu6ch&[brrGI?r;QdtaSl,>KUDc= -!RaX1rrHW@rr3#'D#OA7i&uYkJcC<$i;\<~> -"A:iVM3Rl:!3lHR"G!EMEiT-="Me=CB3bCd!4;_)!$;%;!V[r5rr`6B_HQd8!IUU7rrKi?m/I*t -A,ZE.oRHgj"!$CZ55kK`/b%4\rVm!Equ<[:qYpSJS+ZdcVKVu6ch&[brrGI?r;QdtaSl,>KUDc= -!RaX1rrHW@rr3#'D#OA7i&uYkJcC<$i;\<~> -"A:iVM3Rl:!3lHR"G!EMEiT-="Me=CB3bCd!4;_)!$;%;!V[r5rr`6B_HQd8!IUU7rrKi?m/I*t -A,ZE.oRHgj"!$CZ55kK`/b%4\rVm!Equ<[:qYpSJS+ZdcVKVu6ch&[brrGI?r;QdtaSl,>KUDc= -!RaX1rrHW@rr3#'D#OA7i&uYkJcC<$i;\<~> -"A:iVM3Ro;!O'H@rrgMCs)P.=rrg,Cs(Ae8rr=,;rrMj@o`#$m+2V4\rrITArr3"HU&P&nhI3>\ -rrVG(_X.:(jbC#[email protected]($+6gRf<<h/b%4\rVm!Equ<[:qYp\MS,`M0q#:PqG,("4kUHK1 -!CGN<rs)QFs6>F?KUDc=!RaX1rrHW@rr3#'D#XG9rb/]js+13$s5<p-~> -"A:iVM3Ro;!O'H@rrgMCs)P.=rrg,Cs(Ae8rr=,;rrMj@o`#$m+2V4\rrITArr3"HU&P&nhI3>\ -rrVG(_X.:(jbC#[email protected]($+6gRf<<h/b%4\rVm!Equ<[:qYp\MS,`M0q#:PqG,("4kUHK1 -!CGN<rs)QFs6>F?KUDc=!RaX1rrHW@rr3#'D#XG9rb/]js+13$s5<p-~> -"A:iVM3Ro;!O'H@rrgMCs)P.=rrg,Cs(Ae8rr=,;rrMj@o`#$m+2V4\rrITArr3"HU&P&nhI3>\ -rrVG(_X.:(jbC#[email protected]($+6gRf<<h/b%4\rVm!Equ<[:qYp\MS,`M0q#:PqG,("4kUHK1 -!CGN<rs)QFs6>F?KUDc=!RaX1rrHW@rr3#'D#XG9rb/]js+13$s5<p-~> -"A:iVM'r6h"*ae$E;i3->g`RlWrE#$^g6ulB)Y-tHN*pIEC1"!K)YcQBL)tmMtR)OX^KZXrrL26 -r^-^;k5PA^aT->ZrrLn@lMgmTJH#QLq5"!W"d:Fsh_(&j6Na7`oU5YF"K_V&WcIt9"Q"/d6bE-n% -HrJ%lcE#Es8S]Y&NNhhrs-tX6W;80KUDc=!RaX1rrL\]r^-^"f`(mOrZ?^kJcC<$huA3~> -"A:iVM'r6h"*ae$E;i3->g`RlWrE#$^g6ulB)Y-tHN*pIEC1"!K)YcQBL)tmMtR)OX^KZXrrL26 -r^-^;k5PA^aT->ZrrLn@lMgmTJH#QLq5"!W"d:Fsh_(&j6Na7`oU5YF"K_V&WcIt9"Q"/d6bE-n% -HrJ%lcE#Es8S]Y&NNhhrs-tX6W;80KUDc=!RaX1rrL\]r^-^"f`(mOrZ?^kJcC<$huA3~> -"A:iVM'r6h"*ae$E;i3->g`RlWrE#$^g6ulB)Y-tHN*pIEC1"!K)YcQBL)tmMtR)OX^KZXrrL26 -r^-^;k5PA^aT->ZrrLn@lMgmTJH#QLq5"!W"d:Fsh_(&j6Na7`oU5YF"K_V&WcIt9"Q"/d6bE-n% -HrJ%lcE#Es8S]Y&NNhhrs-tX6W;80KUDc=!RaX1rrL\]r^-^"f`(mOrZ?^kJcC<$huA3~> -"%t`Umek`?jT#8Drn.G5s8VBUrr3,pkPtSCrn.;4rr2uYrn.;6rr2uWrn.;8p&>'hir&fVh#>t, -mJ[%dpY"j1rrMuVlMgqTJ#rYL!;QQH"nL[MqW%/Gf`V$Ls7H9C"T&/uoBQ/O"6eFkrV-<pmdL2U -hZ!NTm/GZ<h>I9Win<2gs6]=TrrMoUn,ECKrn.;5rVlm_hLY]Xs+14-s*t~> -"%t`Umek`?jT#8Drn.G5s8VBUrr3,pkPtSCrn.;4rr2uYrn.;6rr2uWrn.;8p&>'hir&fVh#>t, -mJ[%dpY"j1rrMuVlMgqTJ#rYL!;QQH"nL[MqW%/Gf`V$Ls7H9C"T&/uoBQ/O"6eFkrV-<pmdL2U -hZ!NTm/GZ<h>I9Win<2gs6]=TrrMoUn,ECKrn.;5rVlm_hLY]Xs+14-s*t~> -"%t`Umek`?jT#8Drn.G5s8VBUrr3,pkPtSCrn.;4rr2uYrn.;6rr2uWrn.;8p&>'hir&fVh#>t, -mJ[%dpY"j1rrMuVlMgqTJ#rYL!;QQH"nL[MqW%/Gf`V$Ls7H9C"T&/uoBQ/O"6eFkrV-<pmdL2U -hZ!NTm/GZ<h>I9Win<2gs6]=TrrMoUn,ECKrn.;5rVlm_hLY]Xs+14-s*t~> -!D>M>rrN#pQ2^jZJcC<$JcFU,J,~> -!D>M>rrN#pQ2^jZJcC<$JcFU,J,~> -!D>M>rrN#pQ2^jZJcC<$JcFU,J,~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>NQrrK"krVlo0ipm$KT)JZh\GVHh_h%i=s+13$s/Z0M~> -!D>NQrrK"krVlo0ipm$KT)JZh\GVHh_h%i=s+13$s/Z0M~> -!D>NQrrK"krVlo0ipm$KT)JZh\GVHh_h%i=s+13$s/Z0M~> -!D>NRrrSc-mJ[%d[sIB4rsbK>%<J'>s0[duMN!M0JcC<$JcC<$X8d\~> -!D>NRrrSc-mJ[%d[sIB4rsbK>%<J'>s0[duMN!M0JcC<$JcC<$X8d\~> -!D>NRrrSc-mJ[%d[sIB4rsbK>%<J'>s0[duMN!M0JcC<$JcC<$X8d\~> -#>7/Ys.,5omJQtc^LI'5rsP9XW>)=oS,`M<TDjEAJcC<$JcD\KJ,~> -#>7/Ys.,5omJQtc^LI'5rsP9XW>)=oS,`M<TDjEAJcC<$JcD\KJ,~> -#>7/Ys.,5omJQtc^LI'5rsP9XW>)=oS,`M<TDjEAJcC<$JcD\KJ,~> -#>7/YUPnOomJHnacVF36$>6gI*rkZbs8P:?JcC<$JcC<$VuM8~> -#>7/YUPnOomJHnacVF36$>6gI*rkZbs8P:?JcC<$JcC<$VuM8~> -#>7/YUPnOomJHnacVF36$>6gI*rkZbs8P:?JcC<$JcC<$VuM8~> -#>7/YoCW&:mJ?h`dn0<6"RfFC*W>s:"6h]b:P&Oss+13$s/H$K~> -#>7/YoCW&:mJ?h`dn0<6"RfFC*W>s:"6h]b:P&Oss+13$s/H$K~> -#>7/YoCW&:mJ?h`dn0<6"RfFC*W>s:"6h]b:P&Oss+13$s/H$K~> -!D>NQrrFh@qYpTB:A+Vm7Kc'As8V@[j+75]s+13$s/5mI~> -!D>NQrrFh@qYpTB:A+Vm7Kc'As8V@[j+75]s+13$s/5mI~> -!D>NQrrFh@qYpTB:A+Vm7Kc'As8V@[j+75]s+13$s/5mI~> -!D>NQrrFh@qu6]>;Y0nm*o1A]s'3Bks+13$s+13Is*t~> -!D>NQrrFh@qu6]>;Y0nm*o1A]s'3Bks+13$s+13Is*t~> -!D>NQrrFh@qu6]>;Y0nm*o1A]s'3Bks+13$s+13Is*t~> -!D>NQrrFh@r;Qi1B);6$$-D</*riT\s'3D9rrW+o]Rg*6s+13$s0;TS~> -!D>NQrrFh@r;Qi1B);6$$-D</*riT\s'3D9rrW+o]Rg*6s+13$s0;TS~> -!D>NQrrFh@r;Qi1B);6$$-D</*riT\s'3D9rrW+o]Rg*6s+13$s0;TS~> -%SJn`a+f<dI>4^#s0R1?p&>?%JqahkK!>9SKDpT*YPnJ&pAdU4s+13$s+13Ts*t~> -%SJn`a+f<dI>4^#s0R1?p&>?%JqahkK!>9SKDpT*YPnJ&pAdU4s+13$s+13Ts*t~> -%SJn`a+f<dI>4^#s0R1?p&>?%JqahkK!>9SKDpT*YPnJ&pAdU4s+13$s+13Ts*t~> -"%t`UcMWt2ZiC'>jR`BS[$D;i[Jp1+Yl9phLTURU!rS@iJcC<$JcC<$Z2]=~> -"%t`UcMWt2ZiC'>jR`BS[$D;i[Jp1+Yl9phLTURU!rS@iJcC<$JcC<$Z2]=~> -"%t`UcMWt2ZiC'>jR`BS[$D;i[Jp1+Yl9phLTURU!rS@iJcC<$JcC<$Z2]=~> -!D>N9rrBb-rrK*@JcC<$JcC<$X8d\~> -!D>N9rrBb-rrK*@JcC<$JcC<$X8d\~> -!D>N9rrBb-rrK*@JcC<$JcC<$X8d\~> -!D>N1rr_L<A`eRDJcC<$JcDeNJ,~> -!D>N1rr_L<A`eRDJcC<$JcDeNJ,~> -!D>N1rr_L<A`eRDJcC<$JcDeNJ,~> -!D>N1rrW/foR[$ns+13$s/Q*L~> -!D>N1rrW/foR[$ns+13$s/Q*L~> -!D>N1rrW/foR[$ns+13$s/Q*L~> -"A:iVs5s=1"5EkUl29$2ir8rZh:1/0s+13$s+13<s*t~> -"A:iVs5s=1"5EkUl29$2ir8rZh:1/0s+13$s+13<s*t~> -"A:iVs5s=1"5EkUl29$2ir8rZh:1/0s+13$s+13<s*t~> -"A:iVh08ih"Bs"KI4bCg#D<'VcX9:FaasJCs+13$s-it<~> -"A:iVh08ih"Bs"KI4bCg#D<'VcX9:FaasJCs+13$s-it<~> -"A:iVh08ih"Bs"KI4bCg#D<'VcX9:FaasJCs+13$s-it<~> -"A:iVM3S#>"P-'BI@pE:#01rEs6Fa?JcC<$JcC<$S,\!~> -"A:iVM3S#>"P-'BI@pE:#01rEs6Fa?JcC<$JcC<$S,\!~> -"A:iVM3S#>"P-'BI@pE:#01rEs6Fa?JcC<$JcC<$S,\!~> -"A:iVM3S#>&_9GOI@m*XH^=^3]7/f?mX;?ks+13$s+13>s*t~> -"A:iVM3S#>&_9GOI@m*XH^=^3]7/f?mX;?ks+13$s+13>s*t~> -"A:iVM3S#>&_9GOI@m*XH^=^3]7/f?mX;?ks+13$s+13>s*t~> -(eZsjM3S&?peOBZI63OaTp(Z>s1iGWB=NNmJcC<$JcD2=J,~> -(eZsjM3S&?peOBZI63OaTp(Z>s1iGWB=NNmJcC<$JcD2=J,~> -(eZsjM3S&?peOBZI63OaTp(Z>s1iGWB=NNmJcC<$JcD2=J,~> -(eZsjiH7CN7\E6NhS8LYs3"=?s+$lZY&JslJcC<$JcD2=J,~> -(eZsjiH7CN7\E6NhS8LYs3"=?s+$lZY&JslJcC<$JcD2=J,~> -(eZsjiH7CN7\E6NhS8LYs3"=?s+$lZY&JslJcC<$JcD2=J,~> -#tmA[s6JIqhUY0nrs?kHs+R*>s8&m>JcC<$JcC<$S,\!~> -#tmA[s6JIqhUY0nrs?kHs+R*>s8&m>JcC<$JcC<$S,\!~> -#tmA[s6JIqhUY0nrs?kHs+R*>s8&m>JcC<$JcC<$S,\!~> -!D>NPrrrDA=TA!drr3;J;ZD6@qZ$K"Ck;V;s+13$s-s%=~> -!D>NPrrrDA=TA!drr3;J;ZD6@qZ$K"Ck;V;s+13$s-s%=~> -!D>NPrrrDA=TA!drr3;J;ZD6@qZ$K"Ck;V;s+13$s-s%=~> -$qi\^s5;#@_cHg;R?IQk#ci.Ks*1Qc[V16lJcC<$JcD2=J,~> -$qi\^s5;#@_cHg;R?IQk#ci.Ks*1Qc[V16lJcC<$JcD2=J,~> -$qi\^s5;#@_cHg;R?IQk#ci.Ks*1Qc[V16lJcC<$JcD2=J,~> -#>7/Ys-lqc>Q+R%Pl=S]G5_FBGZ/@.s+13$s+13<s*t~> -#>7/Ys-lqc>Q+R%Pl=S]G5_FBGZ/@.s+13$s+13<s*t~> -#>7/Ys-lqc>Q+R%Pl=S]G5_FBGZ/@.s+13$s+13<s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -"A:iVrHeGa#!r.ds)PpSp&>3n>ok%TqqqDSq^h^f!K[21s+13$s+13ns*t~> -"A:iVrHeGa#!r.ds)PpSp&>3n>ok%TqqqDSq^h^f!K[21s+13$s+13ns*t~> -"A:iVrHeGa#!r.ds)PpSp&>3n>ok%TqqqDSq^h^f!K[21s+13$s+13ns*t~> -"A:iVM3S#>!S0a>rrQHBrq??qB=@j?dS&Kt!DCl?rrJ._JcC<$JcC<$bQ!(~> -"A:iVM3S#>!S0a>rrQHBrq??qB=@j?dS&Kt!DCl?rrJ._JcC<$JcC<$bQ!(~> -"A:iVM3S#>!S0a>rrQHBrq??qB=@j?dS&Kt!DCl?rrJ._JcC<$JcC<$bQ!(~> -#tmA[Vm$.#r2Oo<rrHB@pAYG3[f?BU9E5%Cn,E=fl.l:<!64s:$./AAbQ$Y^s8U+<^B!*hrr<u: -^B!3krrBk6^B!;Fs+13$s+13us*t~> -#tmA[Vm$.#r2Oo<rrHB@pAYG3[f?BU9E5%Cn,E=fl.l:<!64s:$./AAbQ$Y^s8U+<^B!*hrr<u: -^B!3krrBk6^B!;Fs+13$s+13us*t~> -#tmA[Vm$.#r2Oo<rrHB@pAYG3[f?BU9E5%Cn,E=fl.l:<!64s:$./AAbQ$Y^s8U+<^B!*hrr<u: -^B!3krrBk6^B!;Fs+13$s+13us*t~> -!D>NPrrJs@qu6\1[.jS,B60c?KhMIG>f$F>!NC2?rr=,<rs;-HlS8F'MZ:+rra#_Uf`/6dra#_X -h#FNara#VXi.:oZs+13$s3q!u~> -!D>NPrrJs@qu6\1[.jS,B60c?KhMIG>f$F>!NC2?rr=,<rs;-HlS8F'MZ:+rra#_Uf`/6dra#_X -h#FNara#VXi.:oZs+13$s3q!u~> -!D>NPrrJs@qu6\1[.jS,B60c?KhMIG>f$F>!NC2?rr=,<rs;-HlS8F'MZ:+rra#_Uf`/6dra#_X -h#FNara#VXi.:oZs+13$s3q!u~> -!D>NQrrJU@qYpT[2#%"[B39M-AkW1(X+Kg?jFOf>rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr3+K -U&QA5rr3#-e:IXNs+13$s3q!u~> -!D>NQrrJU@qYpT[2#%"[B39M-AkW1(X+Kg?jFOf>rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr3+K -U&QA5rr3#-e:IXNs+13$s3q!u~> -!D>NQrrJU@qYpT[2#%"[B39M-AkW1(X+Kg?jFOf>rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr3+K -U&QA5rr3#-e:IXNs+13$s3q!u~> -!D>NRrrSFGq"k!kDm&j7"_Y:Ds3aR>rrbOCs2S1=rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr34N -U&V$'AnJ&os+13$s+13ts*t~> -!D>NRrrSFGq"k!kDm&j7"_Y:Ds3aR>rrbOCs2S1=rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr34N -U&V$'AnJ&os+13$s+13ts*t~> -!D>NRrrSFGq"k!kDm&j7"_Y:Ds3aR>rrbOCs2S1=rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr34N -U&V$'AnJ&os+13$s+13ts*t~> -#"q&Xs-8l>qYpS+](Z.-B=@j?dS'T>"NXX/@F+oP!$;(<#t<M41&mGps#T3>rre+Bs"<a=rs*qF -s'i@E\(?32JcC<$JcF-tJ,~> -#"q&Xs-8l>qYpS+](Z.-B=@j?dS'T>"NXX/@F+oP!$;(<#t<M41&mGps#T3>rre+Bs"<a=rs*qF -s'i@E\(?32JcC<$JcF-tJ,~> -#"q&Xs-8l>qYpS+](Z.-B=@j?dS'T>"NXX/@F+oP!$;(<#t<M41&mGps#T3>rre+Bs"<a=rs*qF -s'i@E\(?32JcC<$JcF-tJ,~> -"\UrWSW34:rrQZBrq??qB=@j?dS'Q=![-G]r;R9Ks2TNes$bT+1&mGps#T3>rre+Bs"<a=rrd_C -s!.@=rrItfrr3&A/!>J`JcC<$JcFF'J,~> -"\UrWSW34:rrQZBrq??qB=@j?dS'Q=![-G]r;R9Ks2TNes$bT+1&mGps#T3>rre+Bs"<a=rrd_C -s!.@=rrItfrr3&A/!>J`JcC<$JcFF'J,~> -"\UrWSW34:rrQZBrq??qB=@j?dS'Q=![-G]r;R9Ks2TNes$bT+1&mGps#T3>rre+Bs"<a=rrd_C -s!.@=rrItfrr3&A/!>J`JcC<$JcFF'J,~> -"A:iVO;Rp;"^M*-EkD;Crrr.#*Zd]BrVlrj2YI"J&*t?<p](9d4rX\IGkqC42?"X"Jbf?</H-[n -MYdAE,5rVaPl(I[!l>M%JcC<$JcC<$g&HR~> -"A:iVO;Rp;"^M*-EkD;Crrr.#*Zd]BrVlrj2YI"J&*t?<p](9d4rX\IGkqC42?"X"Jbf?</H-[n -MYdAE,5rVaPl(I[!l>M%JcC<$JcC<$g&HR~> -"A:iVO;Rp;"^M*-EkD;Crrr.#*Zd]BrVlrj2YI"J&*t?<p](9d4rX\IGkqC42?"X"Jbf?</H-[n -MYdAE,5rVaPl(I[!l>M%JcC<$JcC<$g&HR~> -!D>N"rrMF?JcC<$JcC<$\Gq'~> -!D>N"rrMF?JcC<$JcC<$\Gq'~> -!D>N"rrMF?JcC<$JcC<$\Gq'~> -!D>N"rrMF?JcC<$JcC<$\Gq'~> -!D>N"rrMF?JcC<$JcC<$\Gq'~> -!D>N"rrMF?JcC<$JcC<$\Gq'~> -!D>N"rrM_GJcC<$JcC<$\Gq'~> -!D>N"rrM_GJcC<$JcC<$\Gq'~> -!D>N"rrM_GJcC<$JcC<$\Gq'~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>NQrrIH;oD\f.li.!"FSu.?F^'4jrrR%AK'!"7oNPOlmf*=XI"D<Ks+13$s7QDB~> -!D>NQrrIH;oD\f.li.!"FSu.?F^'4jrrR%AK'!"7oNPOlmf*=XI"D<Ks+13$s7QDB~> -!D>NQrrIH;oD\f.li.!"FSu.?F^'4jrrR%AK'!"7oNPOlmf*=XI"D<Ks+13$s7QDB~> -#tmA[V69hPmGN'ZrrHa>li."*b5D8@bH`l#rrU9A4lu\Gqn\)'mf*=Q1YMd/Qb7nLJcC<$TDsE~> -#tmA[V69hPmGN'ZrrHa>li."*b5D8@bH`l#rrU9A4lu\Gqn\)'mf*=Q1YMd/Qb7nLJcC<$TDsE~> -#tmA[V69hPmGN'ZrrHa>li."*b5D8@bH`l#rrU9A4lu\Gqn\)'mf*=Q1YMd/Qb7nLJcC<$TDsE~> -#tmA[n8Punj$2c%rrQrD)WUhulS8;:!T6-$rrKH@mf*=Q1YMd/G,BijJcC<$TDsE~> -#tmA[n8Punj$2c%rrQrD)WUhulS8;:!T6-$rrKH@mf*=Q1YMd/G,BijJcC<$TDsE~> -#tmA[n8Punj$2c%rrQrD)WUhulS8;:!T6-$rrKH@mf*=Q1YMd/G,BijJcC<$TDsE~> -#YR8Zs6a"o,j+k$"+miX*r>m;&MNhW!_OI]qu6]_1&LtOi'%&=!U2-6*XdYos6(XJ*d)V6s8)[8 -!L<EYrrKH@pAY0m`Vgh\YP[T:VZ%V=\GZBJr>l?_rRb34I3'HPs8TJ)*WsmkpmV.&$f1io*Y&i) -*rl96M#JG%-KtK-K&fcM0C",gJcC<$ZN#F~> -#YR8Zs6a"o,j+k$"+miX*r>m;&MNhW!_OI]qu6]_1&LtOi'%&=!U2-6*XdYos6(XJ*d)V6s8)[8 -!L<EYrrKH@pAY0m`Vgh\YP[T:VZ%V=\GZBJr>l?_rRb34I3'HPs8TJ)*WsmkpmV.&$f1io*Y&i) -*rl96M#JG%-KtK-K&fcM0C",gJcC<$ZN#F~> -#YR8Zs6a"o,j+k$"+miX*r>m;&MNhW!_OI]qu6]_1&LtOi'%&=!U2-6*XdYos6(XJ*d)V6s8)[8 -!L<EYrrKH@pAY0m`Vgh\YP[T:VZ%V=\GZBJr>l?_rRb34I3'HPs8TJ)*WsmkpmV.&$f1io*Y&i) -*rl96M#JG%-KtK-K&fcM0C",gJcC<$ZN#F~> -#tmA[j`JBojA4A"rr=,:rrGJko`"sd-N!fDlS8;:!T6->rrFb?rr37OU&QA5s8S+>qYpS-\,QC. -ZYK46!VIi=rrd2tl3hh8rrG+?qu6sSnGfp5T`>$9qtpBq*rio]rVljnrr3"BVZ$MqVKVt=$1o\H -s*Z-8s6):js+13$s0DZT~> -#tmA[j`JBojA4A"rr=,:rrGJko`"sd-N!fDlS8;:!T6->rrFb?rr37OU&QA5s8S+>qYpS-\,QC. -ZYK46!VIi=rrd2tl3hh8rrG+?qu6sSnGfp5T`>$9qtpBq*rio]rVljnrr3"BVZ$MqVKVt=$1o\H -s*Z-8s6):js+13$s0DZT~> -#tmA[j`JBojA4A"rr=,:rrGJko`"sd-N!fDlS8;:!T6->rrFb?rr37OU&QA5s8S+>qYpS-\,QC. -ZYK46!VIi=rrd2tl3hh8rrG+?qu6sSnGfp5T`>$9qtpBq*rio]rVljnrr3"BVZ$MqVKVt=$1o\H -s*Z-8s6):js+13$s0DZT~> -#tmA[Z+0ffmGruorr=,+rrMj@qu6]_1&LtOi'%&=!A9uCW=)Uts!.@>s,N->s8Dnq!CO?qrrKH@ -pAY0d-i<oEl8/D="$#BAlM[[b*q93<BE%o5D#F=nrM]l7s1Mh9rrI;?rVlnq:B%4!U+--BI@pQ> -n9BNaJcC<$ZN#F~> -#tmA[Z+0ffmGruorr=,+rrMj@qu6]_1&LtOi'%&=!A9uCW=)Uts!.@>s,N->s8Dnq!CO?qrrKH@ -pAY0d-i<oEl8/D="$#BAlM[[b*q93<BE%o5D#F=nrM]l7s1Mh9rrI;?rVlnq:B%4!U+--BI@pQ> -n9BNaJcC<$ZN#F~> -#tmA[Z+0ffmGruorr=,+rrMj@qu6]_1&LtOi'%&=!A9uCW=)Uts!.@>s,N->s8Dnq!CO?qrrKH@ -pAY0d-i<oEl8/D="$#BAlM[[b*q93<BE%o5D#F=nrM]l7s1Mh9rrI;?rVlnq:B%4!U+--BI@pQ> -n9BNaJcC<$ZN#F~> -!D>NQrrJ/QoD\e3li."[-N!fDlS8;:!T6->rrF`grGr=hs+cMkN.JhFs-EV)!A:k\rrKH@pAY0d --i<oEl8/D="$#B2LAc/r'_).2BDhc1k(N\S!tkRH@/9g'G,G6<!N%dREs.C#s*^Mjs+13$s/uBP~> -!D>NQrrJ/QoD\e3li."[-N!fDlS8;:!T6->rrF`grGr=hs+cMkN.JhFs-EV)!A:k\rrKH@pAY0d --i<oEl8/D="$#B2LAc/r'_).2BDhc1k(N\S!tkRH@/9g'G,G6<!N%dREs.C#s*^Mjs+13$s/uBP~> -!D>NQrrJ/QoD\e3li."[-N!fDlS8;:!T6->rrF`grGr=hs+cMkN.JhFs-EV)!A:k\rrKH@pAY0d --i<oEl8/D="$#B2LAc/r'_).2BDhc1k(N\S!tkRH@/9g'G,G6<!N%dREs.C#s*^Mjs+13$s/uBP~> -!D>NArr=,:rrI;!o`"sd-N!fDlS8;:!T6->rrFb?qu6eNOHG[Brr2s>rr3&1/@YWY!OHP7rrMd? -rVltal0:)'rrY7Ah`h&>"dftms0cS<rrL2@rr3+[&c]OPrVlkIrr34HVZ6[Gs/'u9rrIP?JcC<$ -JcDnQJ,~> -!D>NArr=,:rrI;!o`"sd-N!fDlS8;:!T6->rrFb?qu6eNOHG[Brr2s>rr3&1/@YWY!OHP7rrMd? -rVltal0:)'rrY7Ah`h&>"dftms0cS<rrL2@rr3+[&c]OPrVlkIrr34HVZ6[Gs/'u9rrIP?JcC<$ -JcDnQJ,~> -!D>NArr=,:rrI;!o`"sd-N!fDlS8;:!T6->rrFb?qu6eNOHG[Brr2s>rr3&1/@YWY!OHP7rrMd? -rVltal0:)'rrY7Ah`h&>"dftms0cS<rrL2@rr3+[&c]OPrVlkIrr34HVZ6[Gs/'u9rrIP?JcC<$ -JcDnQJ,~> -!D>NDrs#)m;$g)sOT#1[%P@AR"ER<H;3V"Z"Dg[A;4IRb#\6F=;54*j]iY21"KMM%\l8T*"1%t, -[/No/IRUaGo2.Lk;8N&-!W8Vh;$3-Ul8/D="$#B7R/TqdP99;o[V,O<!Uc$J;$<@(s6>O@;#mj" -rs.%n;,Ok'j+I>.!*K7#!I^Sks+13$s/uBP~> -!D>NDrs#)m;$g)sOT#1[%P@AR"ER<H;3V"Z"Dg[A;4IRb#\6F=;54*j]iY21"KMM%\l8T*"1%t, -[/No/IRUaGo2.Lk;8N&-!W8Vh;$3-Ul8/D="$#B7R/TqdP99;o[V,O<!Uc$J;$<@(s6>O@;#mj" -rs.%n;,Ok'j+I>.!*K7#!I^Sks+13$s/uBP~> -!D>NDrs#)m;$g)sOT#1[%P@AR"ER<H;3V"Z"Dg[A;4IRb#\6F=;54*j]iY21"KMM%\l8T*"1%t, -[/No/IRUaGo2.Lk;8N&-!W8Vh;$3-Ul8/D="$#B7R/TqdP99;o[V,O<!Uc$J;$<@(s6>O@;#mj" -rs.%n;,Ok'j+I>.!*K7#!I^Sks+13$s/uBP~> -!D>NDrrD*WbQ-Q!rrLHrp&>$CrlbB"rr2uJrlbB%rr2uHrlbB'rr2uErlbN-s8Pm:rlbIg^&S*2 -bQR%cnc/OcbQ.&)rrDflbQID8qpt`G"5!DLoDZr;nC@I:nDX9E!:Kj1"SL4Cs6T^.!<)lr!oD/F -rr2u]rlbAfrr3#\m",1fs+13Qs*t~> -!D>NDrrD*WbQ-Q!rrLHrp&>$CrlbB"rr2uJrlbB%rr2uHrlbB'rr2uErlbN-s8Pm:rlbIg^&S*2 -bQR%cnc/OcbQ.&)rrDflbQID8qpt`G"5!DLoDZr;nC@I:nDX9E!:Kj1"SL4Cs6T^.!<)lr!oD/F -rr2u]rlbAfrr3#\m",1fs+13Qs*t~> -!D>NDrrD*WbQ-Q!rrLHrp&>$CrlbB"rr2uJrlbB%rr2uHrlbB'rr2uErlbN-s8Pm:rlbIg^&S*2 -bQR%cnc/OcbQ.&)rrDflbQID8qpt`G"5!DLoDZr;nC@I:nDX9E!:Kj1"SL4Cs6T^.!<)lr!oD/F -rr2u]rlbAfrr3#\m",1fs+13Qs*t~> -!D>MarrFV?rq?G6YCZ_)s+13$s3q!u~> -!D>MarrFV?rq?G6YCZ_)s+13$s3q!u~> -!D>MarrFV?rq?G6YCZ_)s+13$s3q!u~> -!D>M`rr=PJ-30Zhs+13$s+13us*t~> -!D>M`rr=PJ-30Zhs+13$s+13us*t~> -!D>M`rr=PJ-30Zhs+13$s+13us*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -"A:iVs2Y,1!3Z>$!Q4&Ns+13$s+132s*t~> -"A:iVs2Y,1!3Z>$!Q4&Ns+13$s+132s*t~> -"A:iVs2Y,1!3Z>$!Q4&Ns+13$s+132s*t~> -"A:iV`..c8#(NHKs0$t?JcC<$JcC<$OT0h~> -"A:iV`..c8#(NHKs0$t?JcC<$JcC<$OT0h~> -"A:iV`..c8#(NHKs0$t?JcC<$JcC<$OT0h~> -"A:iVM3S#>!S0a>rrTTDqgncus+13$s,m>3~> -"A:iVM3S#>!S0a>rrTTDqgncus+13$s,m>3~> -"A:iVM3S#>!S0a>rrTTDqgncus+13$s,m>3~> -#tmA[hr=\9pK5Z<rrL#?JcC<$JcC<$OoKq~> -#tmA[hr=\9pK5Z<rrL#?JcC<$JcC<$OoKq~> -#tmA[hr=\9pK5Z<rrL#?JcC<$JcC<$OoKq~> -!D>NPrrG@?qu6]<<.Y(#s+13$s-!D4~> -!D>NPrrG@?qu6]<<.Y(#s+13$s-!D4~> -!D>NPrrG@?qu6]<<.Y(#s+13$s-!D4~> -!D>NRrrVaZjneuXg-^GkJcC<$JcCo5J,~> -!D>NRrrVaZjneuXg-^GkJcC<$JcCo5J,~> -!D>NRrrVaZjneuXg-^GkJcC<$JcCo5J,~> -#"q&Xs5RM>qYpTI7tL\ks+13$s,m>3~> -#"q&Xs5RM>qYpTI7tL\ks+13$s,m>3~> -#"q&Xs5RM>qYpTI7tL\ks+13$s,m>3~> -"\UrWgN^j:rrU/EqLSZts+13$s,m>3~> -"\UrWgN^j:rrU/EqLSZts+13$s,m>3~> -"\UrWgN^j:rrU/EqLSZts+13$s,m>3~> -"A:iVM,sS>"gS+-]QWRks+13$s+133s*t~> -"A:iVM,sS>"gS+-]QWRks+13$s+133s*t~> -"A:iVM,sS>"gS+-]QWRks+13$s+133s*t~> -"%t`UaS^ktWrN+,i.:oZs+13$s,[21~> -"%t`UaS^ktWrN+,i.:oZs+13$s,[21~> -"%t`UaS^ktWrN+,i.:oZs+13$s,[21~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -%%EndData -showpage -%%Trailer -end -%%EOF diff --git a/lib/stdlib/doc/src/ushell3.gif b/lib/stdlib/doc/src/ushell3.gif Binary files differdeleted file mode 100644 index 2141268b91..0000000000 --- a/lib/stdlib/doc/src/ushell3.gif +++ /dev/null diff --git a/lib/stdlib/doc/src/ushell3.ps b/lib/stdlib/doc/src/ushell3.ps deleted file mode 100644 index dd64eeab5c..0000000000 --- a/lib/stdlib/doc/src/ushell3.ps +++ /dev/null @@ -1,662 +0,0 @@ -%!PS-Adobe-3.0 -%%Creator: GIMP PostScript file plugin V 1.17 by Peter Kirchgessner -%%Title: ushell3.ps -%%CreationDate: Mon Mar 16 12:15:17 2009 -%%DocumentData: Clean7Bit -%%LanguageLevel: 2 -%%Pages: 1 -%%BoundingBox: 14 14 468 78 -%%EndComments -%%BeginProlog -% Use own dictionary to avoid conflicts -10 dict begin -%%EndProlog -%%Page: 1 1 -% Translate for offset -14.173228346456694 14.173228346456694 translate -% Translate to begin of first scanline -0 63.070866141732289 translate -453.54330708661422 -63.070866141732289 scale -% Image geometry -640 89 8 -% Transformation matrix -[ 640 0 0 89 0 0 ] -% Strings to hold RGB-samples per scanline -/rstr 640 string def -/gstr 640 string def -/bstr 640 string def -{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop} -true 3 -%%BeginData: 37475 ASCII Bytes -colorimage -!1\W%J`VIEJ`VLFJ,~> -!1\W%J`VIEJ`VLFJ,~> -!1\W%J`VIEJ`VLFJ,~> -!T[+/6@hIS6@hIU6@]~> -!T[+/6@hIS6@hIU6@]~> -!T[+/6@hIS6@hIU6@]~> -!ouWfJQ.2"JQ.2"KN*I~> -!ouWfJQ.2"JQ.2"KN*I~> -!ouWfJQ.2"JQ.2"KN*I~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLQ2^m!`0L?'S`PG&JcC<$i;\<~> -!ouXLQ2^m!`0L?'S`PG&JcC<$i;\<~> -!ouXLQ2^m!`0L?'S`PG&JcC<$i;\<~> -"QVjNs.9-i!1*E[!L)+*rrR=EF8Z%>Dc_2VrrI?7rr3)%^&N-N<rg/5rr?^0<rgP6rrU,eg\:^K -o6/O]!ioDNpAY05U[\9aJa!(4!2BHo!,VW6!lAR@JcC<$JcF^/J,~> -"QVjNs.9-i!1*E[!L)+*rrR=EF8Z%>Dc_2VrrI?7rr3)%^&N-N<rg/5rr?^0<rgP6rrU,eg\:^K -o6/O]!ioDNpAY05U[\9aJa!(4!2BHo!,VW6!lAR@JcC<$JcF^/J,~> -"QVjNs.9-i!1*E[!L)+*rrR=EF8Z%>Dc_2VrrI?7rr3)%^&N-N<rg/5rr?^0<rgP6rrU,eg\:^K -o6/O]!ioDNpAY05U[\9aJa!(4!2BHo!,VW6!lAR@JcC<$JcF^/J,~> -"lqsOs)!Pi_uSNdrrHB@o)Ad;\gmXX!mS).p&>%Ili-neJXl`h]D]YJlM1AZb5K6Z:po`k!N()7 -rrML@l2LdgT^2UZil(<`rrJFsrVlntF7K88K;)-<s8T'RrkniDCAn/50C=>jJcC<$iW"E~> -"lqsOs)!Pi_uSNdrrHB@o)Ad;\gmXX!mS).p&>%Ili-neJXl`h]D]YJlM1AZb5K6Z:po`k!N()7 -rrML@l2LdgT^2UZil(<`rrJFsrVlntF7K88K;)-<s8T'RrkniDCAn/50C=>jJcC<$iW"E~> -"lqsOs)!Pi_uSNdrrHB@o)Ad;\gmXX!mS).p&>%Ili-neJXl`h]D]YJlM1AZb5K6Z:po`k!N()7 -rrML@l2LdgT^2UZil(<`rrJFsrVlntF7K88K;)-<s8T'RrkniDCAn/50C=>jJcC<$iW"E~> -"lqsOs)#"3rrHB@nc&X\.f95Hkqi#4!AWp>rrdnCs!dR.rrSCIp@&"_b$si5rrML@l2LdPK]E(5 -GGY9<!NC/2rs4ONpZ4C7s,`6>rrN#\rVlo\2M(mZs+14/s*t~> -"lqsOs)#"3rrHB@nc&X\.f95Hkqi#4!AWp>rrdnCs!dR.rrSCIp@&"_b$si5rrML@l2LdPK]E(5 -GGY9<!NC/2rs4ONpZ4C7s,`6>rrN#\rVlo\2M(mZs+14/s*t~> -"lqsOs)#"3rrHB@nc&X\.f95Hkqi#4!AWp>rrdnCs!dR.rrSCIp@&"_b$si5rrML@l2LdPK]E(5 -GGY9<!NC/2rs4ONpZ4C7s,`6>rrN#\rVlo\2M(mZs+14/s*t~> -#in9Rs)"@hjludE!Us(f*"5s=s'2Za*#r;Ts6`DV*!'XArr3#i.f95Hkqi#4$2ktGs.=c>s!c4l -*!(ffp&>*eKVIo7!Q/(<rrCjQ*!EJBm4518*!<VHr42bG&A7u":Z[6L*5hd.*"`Z#*8gPk!Q/1@ -rrsVGs/-hF]Dhg?\f;.l*$"qWWG$>m*$OSArs>H^rr<#^9E1*5p\t46JcC<$JcF^/J,~> -#in9Rs)"@hjludE!Us(f*"5s=s'2Za*#r;Ts6`DV*!'XArr3#i.f95Hkqi#4$2ktGs.=c>s!c4l -*!(ffp&>*eKVIo7!Q/(<rrCjQ*!EJBm4518*!<VHr42bG&A7u":Z[6L*5hd.*"`Z#*8gPk!Q/1@ -rrsVGs/-hF]Dhg?\f;.l*$"qWWG$>m*$OSArs>H^rr<#^9E1*5p\t46JcC<$JcF^/J,~> -#in9Rs)"@hjludE!Us(f*"5s=s'2Za*#r;Ts6`DV*!'XArr3#i.f95Hkqi#4$2ktGs.=c>s!c4l -*!(ffp&>*eKVIo7!Q/(<rrCjQ*!EJBm4518*!<VHr42bG&A7u":Z[6L*5hd.*"`Z#*8gPk!Q/1@ -rrsVGs/-hF]Dhg?\f;.l*$"qWWG$>m*$OSArs>H^rr<#^9E1*5p\t46JcC<$JcF^/J,~> -#in9Rs(s&?2H0VT!G&_>rsZf$s'-u6s8T->s&7&=rrJm@rr3#i.f95Hkqhu3#T`sFSpp_><^Zld -!I1F5rrJj?mJd4)=nhq!7G$o6ErQ(@4l><[rVm<akl8@2QiI(:nGiNVK_>?L_HQutn,FF,rrHT? -r;QeAV>^DuW>MT6s7dl/rs"REs8UEef)5OJ*J+6As+14/s*t~> -#in9Rs(s&?2H0VT!G&_>rsZf$s'-u6s8T->s&7&=rrJm@rr3#i.f95Hkqhu3#T`sFSpp_><^Zld -!I1F5rrJj?mJd4)=nhq!7G$o6ErQ(@4l><[rVm<akl8@2QiI(:nGiNVK_>?L_HQutn,FF,rrHT? -r;QeAV>^DuW>MT6s7dl/rs"REs8UEef)5OJ*J+6As+14/s*t~> -#in9Rs(s&?2H0VT!G&_>rsZf$s'-u6s8T->s&7&=rrJm@rr3#i.f95Hkqhu3#T`sFSpp_><^Zld -!I1F5rrJj?mJd4)=nhq!7G$o6ErQ(@4l><[rVm<akl8@2QiI(:nGiNVK_>?L_HQutn,FF,rrHT? -r;QeAV>^DuW>MT6s7dl/rs"REs8UEef)5OJ*J+6As+14/s*t~> -"lqsOs)#";rrJFMriH=Cs8Q??rr3,%EW8soriH3>HN*pFngaP:!U2E4rrciCl&)D8rrIA?pAY3[ -N2>qA!Q/(<rrDWgXTL6.m4eM="$PQ&3;rj[2<b(S?N0s.E:s82R=t85#/XRDCU3s\rVln-]);R/ -GGY9<!NC/>rrMm?nG`]SNW9#h7m6eM!$-XjJcC<$iW"E~> -"lqsOs)#";rrJFMriH=Cs8Q??rr3,%EW8soriH3>HN*pFngaP:!U2E4rrciCl&)D8rrIA?pAY3[ -N2>qA!Q/(<rrDWgXTL6.m4eM="$PQ&3;rj[2<b(S?N0s.E:s82R=t85#/XRDCU3s\rVln-]);R/ -GGY9<!NC/>rrMm?nG`]SNW9#h7m6eM!$-XjJcC<$iW"E~> -"lqsOs)#";rrJFMriH=Cs8Q??rr3,%EW8soriH3>HN*pFngaP:!U2E4rrciCl&)D8rrIA?pAY3[ -N2>qA!Q/(<rrDWgXTL6.m4eM="$PQ&3;rj[2<b(S?N0s.E:s82R=t85#/XRDCU3s\rVln-]);R/ -GGY9<!NC/>rrMm?nG`]SNW9#h7m6eM!$-XjJcC<$iW"E~> -"lqsOs)#":rrAAaD?P3us'3D>rrfBBs&2tsD?.$BrrM[?qu6]]1\C\Lp*RC[q>UJ?V"Xfh^Kp4+ -!Q/(=rrN"UrGD]YfBk9jrrYFAj>d,<"?#EC^0^[9!L\W6rs$>Ds(eq?*W?!=@_2L;!I(C=rrK*? -rr3#o,k1g7OH'8>!T-'<rr='js+13$s5F!.~> -"lqsOs)#":rrAAaD?P3us'3D>rrfBBs&2tsD?.$BrrM[?qu6]]1\C\Lp*RC[q>UJ?V"Xfh^Kp4+ -!Q/(=rrN"UrGD]YfBk9jrrYFAj>d,<"?#EC^0^[9!L\W6rs$>Ds(eq?*W?!=@_2L;!I(C=rrK*? -rr3#o,k1g7OH'8>!T-'<rr='js+13$s5F!.~> -"lqsOs)#":rrAAaD?P3us'3D>rrfBBs&2tsD?.$BrrM[?qu6]]1\C\Lp*RC[q>UJ?V"Xfh^Kp4+ -!Q/(=rrN"UrGD]YfBk9jrrYFAj>d,<"?#EC^0^[9!L\W6rs$>Ds(eq?*W?!=@_2L;!I(C=rrK*? -rr3#o,k1g7OH'8>!T-'<rr='js+13$s5F!.~> -"lqsOs)#";rrK1Rrr3,.C&_GSrr3,%EW8tZq>UKd.f95Hkqhu3"84(R@K-9-Mu!AP!I1F>rrV/% -Zi0n*m!?)+rrV55[Jp1,odBb="fictm1u>nrrYFAj>d,<"?#EC^0^[9"dt&Ds,$[Lrs$>D^O^an -*W?!=@_2L;#C!$Es+UKPF8l1?pa#A/!K`<?rrM"?rVlnZNIh+\s+14/s*t~> -"lqsOs)#";rrK1Rrr3,.C&_GSrr3,%EW8tZq>UKd.f95Hkqhu3"84(R@K-9-Mu!AP!I1F>rrV/% -Zi0n*m!?)+rrV55[Jp1,odBb="fictm1u>nrrYFAj>d,<"?#EC^0^[9"dt&Ds,$[Lrs$>D^O^an -*W?!=@_2L;#C!$Es+UKPF8l1?pa#A/!K`<?rrM"?rVlnZNIh+\s+14/s*t~> -"lqsOs)#";rrK1Rrr3,.C&_GSrr3,%EW8tZq>UKd.f95Hkqhu3"84(R@K-9-Mu!AP!I1F>rrV/% -Zi0n*m!?)+rrV55[Jp1,odBb="fictm1u>nrrYFAj>d,<"?#EC^0^[9"dt&Ds,$[Lrs$>D^O^an -*W?!=@_2L;#C!$Es+UKPF8l1?pa#A/!K`<?rrM"?rVlnZNIh+\s+14/s*t~> -"lqsOs(spt=Tb&ka`%/]"E;N`>f$F>"KQPB_eK*Q!1!Q`"F!iV=e#Ej"E@<P=ePKg"8nU*aSu2? -ZsEZ6!J95.rrU7AErH"=gd(0)!N()?rrN%dr`KD\/_BA3O8`8aPP"R6rE08hp]%s6qYp`RIT]gC -rV?I'lAL)E_.]YEs1_\\6!=!^rs-kl=]qs.WH8(=!Vdr0rrLc"r`K83]D_a10C=>jJcC<$iW"E~> -"lqsOs(spt=Tb&ka`%/]"E;N`>f$F>"KQPB_eK*Q!1!Q`"F!iV=e#Ej"E@<P=ePKg"8nU*aSu2? -ZsEZ6!J95.rrU7AErH"=gd(0)!N()?rrN%dr`KD\/_BA3O8`8aPP"R6rE08hp]%s6qYp`RIT]gC -rV?I'lAL)E_.]YEs1_\\6!=!^rs-kl=]qs.WH8(=!Vdr0rrLc"r`K83]D_a10C=>jJcC<$iW"E~> -"lqsOs(spt=Tb&ka`%/]"E;N`>f$F>"KQPB_eK*Q!1!Q`"F!iV=e#Ej"E@<P=ePKg"8nU*aSu2? -ZsEZ6!J95.rrU7AErH"=gd(0)!N()?rrN%dr`KD\/_BA3O8`8aPP"R6rE08hp]%s6qYp`RIT]gC -rV?I'lAL)E_.]YEs1_\\6!=!^rs-kl=]qs.WH8(=!Vdr0rrLc"r`K83]D_a10C=>jJcC<$iW"E~> -"QVjNs4mOh"53_Se,I2eeGoR$nG`FjleVU@ci1c]f`(mNc2PQ[gA_*PbPo?Yh"C[Jp<rm=!6+rS -!93tW!qO4arVlomdH^`5l@c>>rrDcl_?K,Np&!#$rk\d,s8VZg_?BH0s7"\:rr_/q_Y3a(#jL4G -s3:H@s6'?t!<)lr#1p`/s8VB?rr3#tb4#?1h>Y7kb5M>AGH(Ijs+13$s5F!.~> -"QVjNs4mOh"53_Se,I2eeGoR$nG`FjleVU@ci1c]f`(mNc2PQ[gA_*PbPo?Yh"C[Jp<rm=!6+rS -!93tW!qO4arVlomdH^`5l@c>>rrDcl_?K,Np&!#$rk\d,s8VZg_?BH0s7"\:rr_/q_Y3a(#jL4G -s3:H@s6'?t!<)lr#1p`/s8VB?rr3#tb4#?1h>Y7kb5M>AGH(Ijs+13$s5F!.~> -"QVjNs4mOh"53_Se,I2eeGoR$nG`FjleVU@ci1c]f`(mNc2PQ[gA_*PbPo?Yh"C[Jp<rm=!6+rS -!93tW!qO4arVlomdH^`5l@c>>rrDcl_?K,Np&!#$rk\d,s8VZg_?BH0s7"\:rr_/q_Y3a(#jL4G -s3:H@s6'?t!<)lr#1p`/s8VB?rr3#tb4#?1h>Y7kb5M>AGH(Ijs+13$s5F!.~> -!ouXLQ2^mSnWj+TkkTf0JcC<$i;\<~> -!ouXLQ2^mSnWj+TkkTf0JcC<$i;\<~> -!ouXLQ2^mSnWj+TkkTf0JcC<$i;\<~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcG]K!TNR]rr_0&bgEm!"4YQ=h#.0PdXhFLs/5mI~> -!ouXLJcG]K!TNR]rr_0&bgEm!"4YQ=h#.0PdXhFLs/5mI~> -!ouXLJcG]K!TNR]rr_0&bgEm!"4YQ=h#.0PdXhFLs/5mI~> -!ouXLrVll.r;Qf;o()e]\@V&,!kGPPr;QirYf6S@!PSC#rr_SkYi""2!o&"@rVm>df)Ne\s8V;e -g&M)arUBgji3;2?s1.k'"4H?3\b5t)>rthhrVluBmf'fsrr^ZQYd<0c!6"l@!6Y!7"1eHrci3qH -Bhpuir;Qe$_L_`<s/>sJ~> -!ouXLrVll.r;Qf;o()e]\@V&,!kGPPr;QirYf6S@!PSC#rr_SkYi""2!o&"@rVm>df)Ne\s8V;e -g&M)arUBgji3;2?s1.k'"4H?3\b5t)>rthhrVluBmf'fsrr^ZQYd<0c!6"l@!6Y!7"1eHrci3qH -Bhpuir;Qe$_L_`<s/>sJ~> -!ouXLrVll.r;Qf;o()e]\@V&,!kGPPr;QirYf6S@!PSC#rr_SkYi""2!o&"@rVm>df)Ne\s8V;e -g&M)arUBgji3;2?s1.k'"4H?3\b5t)>rthhrVluBmf'fsrr^ZQYd<0c!6"l@!6Y!7"1eHrci3qH -Bhpuir;Qe$_L_`<s/>sJ~> -!ouXLrr3"u(&\(5P`>P2rrICpm/I-7?T\2c!r?\qrVlmIoCDnbn;;!sKAbYe!T1]hrrJ(?r;RA& -HiH[ds6YVXJCjfHq>^KAch7;Bie2*#ir:%mrr[/AruLe4!F<J;rsMiIr#l%>c$X;BD5H.i!P<LR -CB8[tpAY48s0uV;rrLA?r;QfW3e@<^s/>sJ~> -!ouXLrr3"u(&\(5P`>P2rrICpm/I-7?T\2c!r?\qrVlmIoCDnbn;;!sKAbYe!T1]hrrJ(?r;RA& -HiH[ds6YVXJCjfHq>^KAch7;Bie2*#ir:%mrr[/AruLe4!F<J;rsMiIr#l%>c$X;BD5H.i!P<LR -CB8[tpAY48s0uV;rrLA?r;QfW3e@<^s/>sJ~> -!ouXLrr3"u(&\(5P`>P2rrICpm/I-7?T\2c!r?\qrVlmIoCDnbn;;!sKAbYe!T1]hrrJ(?r;RA& -HiH[ds6YVXJCjfHq>^KAch7;Bie2*#ir:%mrr[/AruLe4!F<J;rsMiIr#l%>c$X;BD5H.i!P<LR -CB8[tpAY48s0uV;rrLA?r;QfW3e@<^s/>sJ~> -#NS0Qs8SgH'Dqe1RtBDp!T?-5rrFS?o)AmK5l^js[GUubZ>0::!n)SAr;RA&HiH[ds3jR>_1$W_ -r;ZeYN:m2T\mk]II!C_Grr[/AruLe4!F<J;rsMiIr#l%>LR%o>oI8\t#.&^Es8U`apAY48s0uV; -rrLA?r;QigKVj>#JcD_LJ,~> -#NS0Qs8SgH'Dqe1RtBDp!T?-5rrFS?o)AmK5l^js[GUubZ>0::!n)SAr;RA&HiH[ds3jR>_1$W_ -r;ZeYN:m2T\mk]II!C_Grr[/AruLe4!F<J;rsMiIr#l%>LR%o>oI8\t#.&^Es8U`apAY48s0uV; -rrLA?r;QigKVj>#JcD_LJ,~> -#NS0Qs8SgH'Dqe1RtBDp!T?-5rrFS?o)AmK5l^js[GUubZ>0::!n)SAr;RA&HiH[ds3jR>_1$W_ -r;ZeYN:m2T\mk]II!C_Grr[/AruLe4!F<J;rsMiIr#l%>LR%o>oI8\t#.&^Es8U`apAY48s0uV; -rrLA?r;QigKVj>#JcD_LJ,~> -#NS0Qs.PG>*W#d:UNuP4!e+Bor;QbBr`fGnp&>'O48f*[Bj?GlrrFS4r`fH/rVlrS>c%E!#M_TE -s2a:$pAJ50_Z0XSs3"YPaT)6E>a;X#c2[h+rEKWWs79J\-<sg.rr3#!F8PtRh9c29n,Mjis8U'5 -o)IQO1br<@mf3=+rEKd&s5qB%s1G-(s3748r`fI&rr3)Dir?1SrrHE?qu72Dli+*fs+cm>s7@c? -s/j-:rrM&<rr3"Nm/?qjU<NXUs4*P;s,-e\!/pjV"@#[e>`o$c!.XuQ!1*T`"0V[da8Gr<btn6: -!QA,ks+13Ls*t~> -#NS0Qs.PG>*W#d:UNuP4!e+Bor;QbBr`fGnp&>'O48f*[Bj?GlrrFS4r`fH/rVlrS>c%E!#M_TE -s2a:$pAJ50_Z0XSs3"YPaT)6E>a;X#c2[h+rEKWWs79J\-<sg.rr3#!F8PtRh9c29n,Mjis8U'5 -o)IQO1br<@mf3=+rEKd&s5qB%s1G-(s3748r`fI&rr3)Dir?1SrrHE?qu72Dli+*fs+cm>s7@c? -s/j-:rrM&<rr3"Nm/?qjU<NXUs4*P;s,-e\!/pjV"@#[e>`o$c!.XuQ!1*T`"0V[da8Gr<btn6: -!QA,ks+13Ls*t~> -#NS0Qs.PG>*W#d:UNuP4!e+Bor;QbBr`fGnp&>'O48f*[Bj?GlrrFS4r`fH/rVlrS>c%E!#M_TE -s2a:$pAJ50_Z0XSs3"YPaT)6E>a;X#c2[h+rEKWWs79J\-<sg.rr3#!F8PtRh9c29n,Mjis8U'5 -o)IQO1br<@mf3=+rEKd&s5qB%s1G-(s3748r`fI&rr3)Dir?1SrrHE?qu72Dli+*fs+cm>s7@c? -s/j-:rrM&<rr3"Nm/?qjU<NXUs4*P;s,-e\!/pjV"@#[e>`o$c!.XuQ!1*T`"0V[da8Gr<btn6: -!QA,ks+13Ls*t~> -#NS0Qs4Zf=*W#d;ql"c5rrUHX,5qNBGcC\W!KEfHrrM(?r;Qi:U."t[!@^M;^B&_orr3&@!-8&< -!Sfs<rr>sq^CtP2s0b1+^V=R6rYN>5HbX4Is7Q?*#\)ch^P5S"^[V7&!NL5.rsA82Ch^Z!q#AcZ -rP&>0q#:E#)he4*!*/Ie!F<J5rs=H,5X7I]s8RS?rr3#E:B(7o4Q68=$#ZsH*WNf[s,<HH^BC!c -s+R-F^BBjes*pjD^B'Ldp&>';:](.m`E.WjJcD_LJ,~> -#NS0Qs4Zf=*W#d;ql"c5rrUHX,5qNBGcC\W!KEfHrrM(?r;Qi:U."t[!@^M;^B&_orr3&@!-8&< -!Sfs<rr>sq^CtP2s0b1+^V=R6rYN>5HbX4Is7Q?*#\)ch^P5S"^[V7&!NL5.rsA82Ch^Z!q#AcZ -rP&>0q#:E#)he4*!*/Ie!F<J5rs=H,5X7I]s8RS?rr3#E:B(7o4Q68=$#ZsH*WNf[s,<HH^BC!c -s+R-F^BBjes*pjD^B'Ldp&>';:](.m`E.WjJcD_LJ,~> -#NS0Qs4Zf=*W#d;ql"c5rrUHX,5qNBGcC\W!KEfHrrM(?r;Qi:U."t[!@^M;^B&_orr3&@!-8&< -!Sfs<rr>sq^CtP2s0b1+^V=R6rYN>5HbX4Is7Q?*#\)ch^P5S"^[V7&!NL5.rsA82Ch^Z!q#AcZ -rP&>0q#:E#)he4*!*/Ie!F<J5rs=H,5X7I]s8RS?rr3#E:B(7o4Q68=$#ZsH*WNf[s,<HH^BC!c -s+R-F^BBjes*pjD^B'Ldp&>';:](.m`E.WjJcD_LJ,~> -!ouXLrVlj<q>UKC9D/;cqBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@cOtrrIA?rr3&og["k="?7J0 -,`Vg'!$2%<&RW6Okq)#ts&R/=+9/3^s%^l:rrkjCs8TB>qu6\sF6ii,OGs/<!Q&%=rrCsOrrTH1 -mf*4d*U`q.?,-(4%YoShgX7PNs6XR>s6`D>rr3!]iVic_DQj'\s1Me>A[h[="LVqC?,6I>"KZSB -<65(=!V5UMrrLA?qu6]9=+UC&s/H$K~> -!ouXLrVlj<q>UKC9D/;cqBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@cOtrrIA?rr3&og["k="?7J0 -,`Vg'!$2%<&RW6Okq)#ts&R/=+9/3^s%^l:rrkjCs8TB>qu6\sF6ii,OGs/<!Q&%=rrCsOrrTH1 -mf*4d*U`q.?,-(4%YoShgX7PNs6XR>s6`D>rr3!]iVic_DQj'\s1Me>A[h[="LVqC?,6I>"KZSB -<65(=!V5UMrrLA?qu6]9=+UC&s/H$K~> -!ouXLrVlj<q>UKC9D/;cqBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@cOtrrIA?rr3&og["k="?7J0 -,`Vg'!$2%<&RW6Okq)#ts&R/=+9/3^s%^l:rrkjCs8TB>qu6\sF6ii,OGs/<!Q&%=rrCsOrrTH1 -mf*4d*U`q.?,-(4%YoShgX7PNs6XR>s6`D>rr3!]iVic_DQj'\s1Me>A[h[="LVqC?,6I>"KZSB -<65(=!V5UMrrLA?qu6]9=+UC&s/H$K~> -!ouXLrVlj<qYpWR7/HTV!W"#=rrGd@rr3"fK(f3FiB-r:!SKm>rrFS?rr3"DV"jrmpsqY'qu6Tq -*W?!@?bQL(1]IC^qYgC8s/(#?:<rj^r>YtHfDklVCA\#3WcRM+!K`9=rrN(ur>Ygmq#:B"C]485 -*U`q.?,-(4"c&BCs7@c>rrd5Er7aR:rrG7@rVm1AYQ#XC]jLeN\,QC1[:oSG_#F?=XDn4+7iWLI -oD\j9:](.m`E.WjJcD_LJ,~> -!ouXLrVlj<qYpWR7/HTV!W"#=rrGd@rr3"fK(f3FiB-r:!SKm>rrFS?rr3"DV"jrmpsqY'qu6Tq -*W?!@?bQL(1]IC^qYgC8s/(#?:<rj^r>YtHfDklVCA\#3WcRM+!K`9=rrN(ur>Ygmq#:B"C]485 -*U`q.?,-(4"c&BCs7@c>rrd5Er7aR:rrG7@rVm1AYQ#XC]jLeN\,QC1[:oSG_#F?=XDn4+7iWLI -oD\j9:](.m`E.WjJcD_LJ,~> -!ouXLrVlj<qYpWR7/HTV!W"#=rrGd@rr3"fK(f3FiB-r:!SKm>rrFS?rr3"DV"jrmpsqY'qu6Tq -*W?!@?bQL(1]IC^qYgC8s/(#?:<rj^r>YtHfDklVCA\#3WcRM+!K`9=rrN(ur>Ygmq#:B"C]485 -*U`q.?,-(4"c&BCs7@c>rrd5Er7aR:rrG7@rVm1AYQ#XC]jLeN\,QC1[:oSG_#F?=XDn4+7iWLI -oD\j9:](.m`E.WjJcD_LJ,~> -!ouXLrVlj<qu6]J7e?W\qBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@c+hrrIA?rr3&lcfG')!Sfs< -rr=)<rrc]Bs62?;rsAZHs/(#?:<rM[rr3Mf37n31Z>0F>gA1dK\p3N,rs+XEs8UUKoAKTI!$1k7 -!O6G=rr=)4rrV!%r;HWr?,-(4"c&BCs7@c=rrPU@*W5pJ4Q6.is8Qu?ruM-Q?iO]Hrr3,/B`DAR -rr3,&E;rnYrr3#ejn8WSbtn9;!rAp@r;Qincf'HTJcE"TJ,~> -!ouXLrVlj<qu6]J7e?W\qBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@c+hrrIA?rr3&lcfG')!Sfs< -rr=)<rrc]Bs62?;rsAZHs/(#?:<rM[rr3Mf37n31Z>0F>gA1dK\p3N,rs+XEs8UUKoAKTI!$1k7 -!O6G=rr=)4rrV!%r;HWr?,-(4"c&BCs7@c=rrPU@*W5pJ4Q6.is8Qu?ruM-Q?iO]Hrr3,/B`DAR -rr3,&E;rnYrr3#ejn8WSbtn9;!rAp@r;Qincf'HTJcE"TJ,~> -!ouXLrVlj<qu6]J7e?W\qBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@c+hrrIA?rr3&lcfG')!Sfs< -rr=)<rrc]Bs62?;rsAZHs/(#?:<rM[rr3Mf37n31Z>0F>gA1dK\p3N,rs+XEs8UUKoAKTI!$1k7 -!O6G=rr=)4rrV!%r;HWr?,-(4"c&BCs7@c=rrPU@*W5pJ4Q6.is8Qu?ruM-Q?iO]Hrr3,/B`DAR -rr3,&E;rnYrr3#ejn8WSbtn9;!rAp@r;Qincf'HTJcE"TJ,~> -&*-#Ys3n9Z'XG$js8U/Gp@S@j_ljo_cN!oqT`24tMQ$:L"M:6SZI]$T#e?<UZIo3W.8^#L!I_`^ -rrU7AErH"=gd(f;!+#Q/"EMoikqi8;$2u%H_4Ua&cM1ZDZ3k[gfDklmGI"Mcl2L\`N0*E+#I/ug -Z<@4XE;d'B>PS4!Z"s=<!$1b4!_EC^rVln(^\.U1X,+'eY%dk7!dpbLr;R:NVPeS:s-]%U'XF*e -s-&cJZN$0ks,EHGZ36;Zs+m3EZ2ouZp&>';:]14ni]?u:!nmW^JcC<$Z2]=~> -&*-#Ys3n9Z'XG$js8U/Gp@S@j_ljo_cN!oqT`24tMQ$:L"M:6SZI]$T#e?<UZIo3W.8^#L!I_`^ -rrU7AErH"=gd(f;!+#Q/"EMoikqi8;$2u%H_4Ua&cM1ZDZ3k[gfDklmGI"Mcl2L\`N0*E+#I/ug -Z<@4XE;d'B>PS4!Z"s=<!$1b4!_EC^rVln(^\.U1X,+'eY%dk7!dpbLr;R:NVPeS:s-]%U'XF*e -s-&cJZN$0ks,EHGZ36;Zs+m3EZ2ouZp&>';:]14ni]?u:!nmW^JcC<$Z2]=~> -&*-#Ys3n9Z'XG$js8U/Gp@S@j_ljo_cN!oqT`24tMQ$:L"M:6SZI]$T#e?<UZIo3W.8^#L!I_`^ -rrU7AErH"=gd(f;!+#Q/"EMoikqi8;$2u%H_4Ua&cM1ZDZ3k[gfDklmGI"Mcl2L\`N0*E+#I/ug -Z<@4XE;d'B>PS4!Z"s=<!$1b4!_EC^rVln(^\.U1X,+'eY%dk7!dpbLr;R:NVPeS:s-]%U'XF*e -s-&cJZN$0ks,EHGZ36;Zs+m3EZ2ouZp&>';:]14ni]?u:!nmW^JcC<$Z2]=~> -"QVjNs/Gp1"JPkqP32B9!/UVg!2'8j!/(8b!i5k1qj%?1s8RBDB`P=8rr@0?B`tdBs+0V;B`P[A -rrV%kXSr/"l]1oC!;HG+"NUQBnq[/C!WIHErr\VIs60Gr!l^>crr3)9Bkc?#rrTfah<b.GXD)D; -s8U7?B`Rf#rrLNGrVlkDp&>)W'(Pr#"'oo4T_ABfVeKj`qu6\Hq>:0mH?oJh"INm3Boi8n!0dCr -!0mK_!07%m!1Eid!/^\h!1roa"/_B0:]14nOc0,:!pOEmJcC<$Z2]=~> -"QVjNs/Gp1"JPkqP32B9!/UVg!2'8j!/(8b!i5k1qj%?1s8RBDB`P=8rr@0?B`tdBs+0V;B`P[A -rrV%kXSr/"l]1oC!;HG+"NUQBnq[/C!WIHErr\VIs60Gr!l^>crr3)9Bkc?#rrTfah<b.GXD)D; -s8U7?B`Rf#rrLNGrVlkDp&>)W'(Pr#"'oo4T_ABfVeKj`qu6\Hq>:0mH?oJh"INm3Boi8n!0dCr -!0mK_!07%m!1Eid!/^\h!1roa"/_B0:]14nOc0,:!pOEmJcC<$Z2]=~> -"QVjNs/Gp1"JPkqP32B9!/UVg!2'8j!/(8b!i5k1qj%?1s8RBDB`P=8rr@0?B`tdBs+0V;B`P[A -rrV%kXSr/"l]1oC!;HG+"NUQBnq[/C!WIHErr\VIs60Gr!l^>crr3)9Bkc?#rrTfah<b.GXD)D; -s8U7?B`Rf#rrLNGrVlkDp&>)W'(Pr#"'oo4T_ABfVeKj`qu6\Hq>:0mH?oJh"INm3Boi8n!0dCr -!0mK_!07%m!1Eid!/^\h!1roa"/_B0:]14nOc0,:!pOEmJcC<$Z2]=~> -!ouXLg&D&Xqh5$jXoAF5fYd^i/+NT<"0mQ6OPKj9*SgYsQ\C-or;Qb\JcC<$VuM8~> -!ouXLg&D&Xqh5$jXoAF5fYd^i/+NT<"0mQ6OPKj9*SgYsQ\C-or;Qb\JcC<$VuM8~> -!ouXLg&D&Xqh5$jXoAF5fYd^i/+NT<"0mQ6OPKj9*SgYsQ\C-or;Qb\JcC<$VuM8~> -!ouXLJcEIa!fWE@eGfLhJcC<$JcGNFJ,~> -!ouXLJcEIa!fWE@eGfLhJcC<$JcGNFJ,~> -!ouXLJcEIa!fWE@eGfLhJcC<$JcGNFJ,~> -!ouXLJcEIa!RUJfrrBugs+13$s7lVE~> -!ouXLJcEIa!RUJfrrBugs+13$s7lVE~> -!ouXLJcEIa!RUJfrrBugs+13$s7lVE~> -!ouXLrr3't-73*urrYk?->%i/VuHj>-71q,rrYb<->A&2JcDGDJ,~> -!ouXLrr3't-73*urrYk?->%i/VuHj>-71q,rrYb<->A&2JcDGDJ,~> -!ouXLrr3't-73*urrYk?->%i/VuHj>-71q,rrYb<->A&2JcDGDJ,~> -!ouXLrr3'UiUusDrs(%<p%\ReoM>H+"C_!'=r@2X",R!J62hi)Dt`u+!V%uF*!C]as#M+t*!3#; -5lDZ''<(^+!:'M&".8rb)Z1QV`W%.\*!$Vlq>UKpc2AUb[/Bt%Y5TCHaoDD<V]6\VqtU0lr3?2? -"47(m&H*RN*rl,4r>Ygpq#:BkQMhd'mem(cnKn27"SQdS*96en!V^p>*!%tfr;Qr]1&q:ID#PCZ -7.^HYq"=;#o`#1Qo$gS;s)5IHo)Ae=s1<%Frr_uf`D;'bJcDGDJ,~> -!ouXLrr3'UiUusDrs(%<p%\ReoM>H+"C_!'=r@2X",R!J62hi)Dt`u+!V%uF*!C]as#M+t*!3#; -5lDZ''<(^+!:'M&".8rb)Z1QV`W%.\*!$Vlq>UKpc2AUb[/Bt%Y5TCHaoDD<V]6\VqtU0lr3?2? -"47(m&H*RN*rl,4r>Ygpq#:BkQMhd'mem(cnKn27"SQdS*96en!V^p>*!%tfr;Qr]1&q:ID#PCZ -7.^HYq"=;#o`#1Qo$gS;s)5IHo)Ae=s1<%Frr_uf`D;'bJcDGDJ,~> -!ouXLrr3'UiUusDrs(%<p%\ReoM>H+"C_!'=r@2X",R!J62hi)Dt`u+!V%uF*!C]as#M+t*!3#; -5lDZ''<(^+!:'M&".8rb)Z1QV`W%.\*!$Vlq>UKpc2AUb[/Bt%Y5TCHaoDD<V]6\VqtU0lr3?2? -"47(m&H*RN*rl,4r>Ygpq#:BkQMhd'mem(cnKn27"SQdS*96en!V^p>*!%tfr;Qr]1&q:ID#PCZ -7.^HYq"=;#o`#1Qo$gS;s)5IHo)Ae=s1<%Frr_uf`D;'bJcDGDJ,~> -!ouXLrr3!SlMLS^.eEW=!F3J?rrfBBs&7&:rrGd@rr3"fK)#?H3o^/="Gr?B1$eT6!pk9@q#:=7 -rVlt4[Jq9@rrV=od/!\BodB_<"@^r?ZYfX?#PtQEs8R)Bqt^6mdn0N<"!m]c;>^@o_HQp<!$1k7 -!O6G=rr=)<rsRa\$ig7pJueqO6eV87!K`<?rrM"?rVm)U2_"e,J"HZ>!R=I<rr=)3rrXeBdRsN= -!C,E2rrX;A\7GO;!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEW=!F3J?rrfBBs&7&:rrGd@rr3"fK)#?H3o^/="Gr?B1$eT6!pk9@q#:=7 -rVlt4[Jq9@rrV=od/!\BodB_<"@^r?ZYfX?#PtQEs8R)Bqt^6mdn0N<"!m]c;>^@o_HQp<!$1k7 -!O6G=rr=)<rsRa\$ig7pJueqO6eV87!K`<?rrM"?rVm)U2_"e,J"HZ>!R=I<rr=)3rrXeBdRsN= -!C,E2rrX;A\7GO;!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEW=!F3J?rrfBBs&7&:rrGd@rr3"fK)#?H3o^/="Gr?B1$eT6!pk9@q#:=7 -rVlt4[Jq9@rrV=od/!\BodB_<"@^r?ZYfX?#PtQEs8R)Bqt^6mdn0N<"!m]c;>^@o_HQp<!$1k7 -!O6G=rr=)<rsRa\$ig7pJueqO6eV87!K`<?rrM"?rVm)U2_"e,J"HZ>!R=I<rr=)3rrXeBdRsN= -!C,E2rrX;A\7GO;!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEW=!F3J?rt2;Os&7%P[C+B]s*rr+s6>N>q#:@Uir8r\M3Irokl.sigACmN -QA>57!$2%<"D>.B*V99<!9="W!oJ@Aq#:Bf-iO&I7G$o\qu6of@a>#Rc!]u8rrLS?rVm!Gq9)Xm -rj;k&s2%t<rr=)7rrKB?rVlj<rr3DWCt]p0s1Eg>s8TE?q#:AVNW/qYh`^u=#KpH:.KBEtT)S`j -c;+<;!$1_3").AbJ,]HLGGkH=rrp[9]sY8prVlt,s36](rrLA?JcC<$U&TW~> -!ouXLrr3!SlMLS^.eEW=!F3J?rt2;Os&7%P[C+B]s*rr+s6>N>q#:@Uir8r\M3Irokl.sigACmN -QA>57!$2%<"D>.B*V99<!9="W!oJ@Aq#:Bf-iO&I7G$o\qu6of@a>#Rc!]u8rrLS?rVm!Gq9)Xm -rj;k&s2%t<rr=)7rrKB?rVlj<rr3DWCt]p0s1Eg>s8TE?q#:AVNW/qYh`^u=#KpH:.KBEtT)S`j -c;+<;!$1_3").AbJ,]HLGGkH=rrp[9]sY8prVlt,s36](rrLA?JcC<$U&TW~> -!ouXLrr3!SlMLS^.eEW=!F3J?rt2;Os&7%P[C+B]s*rr+s6>N>q#:@Uir8r\M3Irokl.sigACmN -QA>57!$2%<"D>.B*V99<!9="W!oJ@Aq#:Bf-iO&I7G$o\qu6of@a>#Rc!]u8rrLS?rVm!Gq9)Xm -rj;k&s2%t<rr=)7rrKB?rVlj<rr3DWCt]p0s1Eg>s8TE?q#:AVNW/qYh`^u=#KpH:.KBEtT)S`j -c;+<;!$1_3").AbJ,]HLGGkH=rrp[9]sY8prVlt,s36](rrLA?JcC<$U&TW~> -!ouXLrr3!SlMLS^.eEW=%U?jMs6409s&42YAnH;1rVlreMkg%H#s.)Gs4gM9s"TH2Ac[D5rVlrX -KX^^U!$2(="jT<>rtpjsAc[YJrr3)X=&duMrrMd?rr3/<.bF&Ai;T,MkPtS<GF=kBht[3Pdn0Q= -"g\s>b;"YKAcn:t_HQs=!N[(3rrKB?rr38#*WQ/eI*Va4rr3,/Kpe?Lp\u"jNW9%XL->S:_fb#3 -.KBEtT)\ibHqsV>!$1G+!ILR>rruGICM%1'an>Z7btiojJcDGDJ,~> -!ouXLrr3!SlMLS^.eEW=%U?jMs6409s&42YAnH;1rVlreMkg%H#s.)Gs4gM9s"TH2Ac[D5rVlrX -KX^^U!$2(="jT<>rtpjsAc[YJrr3)X=&duMrrMd?rr3/<.bF&Ai;T,MkPtS<GF=kBht[3Pdn0Q= -"g\s>b;"YKAcn:t_HQs=!N[(3rrKB?rr38#*WQ/eI*Va4rr3,/Kpe?Lp\u"jNW9%XL->S:_fb#3 -.KBEtT)\ibHqsV>!$1G+!ILR>rruGICM%1'an>Z7btiojJcDGDJ,~> -!ouXLrr3!SlMLS^.eEW=%U?jMs6409s&42YAnH;1rVlreMkg%H#s.)Gs4gM9s"TH2Ac[D5rVlrX -KX^^U!$2(="jT<>rtpjsAc[YJrr3)X=&duMrrMd?rr3/<.bF&Ai;T,MkPtS<GF=kBht[3Pdn0Q= -"g\s>b;"YKAcn:t_HQs=!N[(3rrKB?rr38#*WQ/eI*Va4rr3,/Kpe?Lp\u"jNW9%XL->S:_fb#3 -.KBEtT)\ibHqsV>!$1G+!ILR>rruGICM%1'an>Z7btiojJcDGDJ,~> -!ouXLrr3!SlMLS^.eEW=%^1>0I=>7;s/UbCs8Sj?rr3&pI\lc<$(cToI=F_Ms-e`BrrI\@rVlo) -C%hQ,NrFG1Il4Y6MO4>B!FNP>rrgr*Jq)eUrrMtIrd=s#,M2<!If=p)Idd<rItE9%jS8`Ul@Jq_ -"b@<^jat#ArrX;AiILoU!J(s[rrLf'rd>''*<4K;aoDA]rr3,:J:`B,p\t98J,Xj)E]sH>JD'tp -.KBF`J,XisBhnU+!$1D*!$2%<##o4*s8Sm?p&>';:P&Oss.TIC~> -!ouXLrr3!SlMLS^.eEW=%^1>0I=>7;s/UbCs8Sj?rr3&pI\lc<$(cToI=F_Ms-e`BrrI\@rVlo) -C%hQ,NrFG1Il4Y6MO4>B!FNP>rrgr*Jq)eUrrMtIrd=s#,M2<!If=p)Idd<rItE9%jS8`Ul@Jq_ -"b@<^jat#ArrX;AiILoU!J(s[rrLf'rd>''*<4K;aoDA]rr3,:J:`B,p\t98J,Xj)E]sH>JD'tp -.KBF`J,XisBhnU+!$1D*!$2%<##o4*s8Sm?p&>';:P&Oss.TIC~> -!ouXLrr3!SlMLS^.eEW=%^1>0I=>7;s/UbCs8Sj?rr3&pI\lc<$(cToI=F_Ms-e`BrrI\@rVlo) -C%hQ,NrFG1Il4Y6MO4>B!FNP>rrgr*Jq)eUrrMtIrd=s#,M2<!If=p)Idd<rItE9%jS8`Ul@Jq_ -"b@<^jat#ArrX;AiILoU!J(s[rrLf'rd>''*<4K;aoDA]rr3,:J:`B,p\t98J,Xj)E]sH>JD'tp -.KBF`J,XisBhnU+!$1D*!$2%<##o4*s8Sm?p&>';:P&Oss.TIC~> -!ouXLrr3!SlMLS^.eET<"h("oZ`A*CrrJm@rr3":Y4V_tWLf]sM3Ii:!J-d?rrV2$`qKE4rViAi -c"FH]rrHK?qu6[t`q]Q6nG]!_fgXN[*kVFO#sI26DSQ5O]l*B8rrD*YSc\%"q>:0o*WQ/qrgj/X -*V]R6f)D6Do,[k1/H0&a$[;WhJ(Xf$TR?M6(!<,>TAMg3LH/dO>n;nos21G"VTqs7rr=)*rrJR? -rr3!uaSu2?Uj;Y5!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eET<"h("oZ`A*CrrJm@rr3":Y4V_tWLf]sM3Ii:!J-d?rrV2$`qKE4rViAi -c"FH]rrHK?qu6[t`q]Q6nG]!_fgXN[*kVFO#sI26DSQ5O]l*B8rrD*YSc\%"q>:0o*WQ/qrgj/X -*V]R6f)D6Do,[k1/H0&a$[;WhJ(Xf$TR?M6(!<,>TAMg3LH/dO>n;nos21G"VTqs7rr=)*rrJR? -rr3!uaSu2?Uj;Y5!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eET<"h("oZ`A*CrrJm@rr3":Y4V_tWLf]sM3Ii:!J-d?rrV2$`qKE4rViAi -c"FH]rrHK?qu6[t`q]Q6nG]!_fgXN[*kVFO#sI26DSQ5O]l*B8rrD*YSc\%"q>:0o*WQ/qrgj/X -*V]R6f)D6Do,[k1/H0&a$[;WhJ(Xf$TR?M6(!<,>TAMg3LH/dO>n;nos21G"VTqs7rr=)*rrJR? -rr3!uaSu2?Uj;Y5!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEH8!NU5;rro0Ds8Q]>n,EEFPPb@\JXl`>!ROO0rrHc?qu6\(^&.g1<lXb2 -"%Ci/0`D"R4l><[rVlmUkk"fT,l.?;!$1q9!$1Y1!Zh=)rON+H`;cKXrr3#g/G&lDh`_"srji0: -*mOT^!R=I<rr=)*rs&7lJcGaLaSu2?Uj;Y5!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEH8!NU5;rro0Ds8Q]>n,EEFPPb@\JXl`>!ROO0rrHc?qu6\(^&.g1<lXb2 -"%Ci/0`D"R4l><[rVlmUkk"fT,l.?;!$1q9!$1Y1!Zh=)rON+H`;cKXrr3#g/G&lDh`_"srji0: -*mOT^!R=I<rr=)*rs&7lJcGaLaSu2?Uj;Y5!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEH8!NU5;rro0Ds8Q]>n,EEFPPb@\JXl`>!ROO0rrHc?qu6\(^&.g1<lXb2 -"%Ci/0`D"R4l><[rVlmUkk"fT,l.?;!$1q9!$1Y1!Zh=)rON+H`;cKXrr3#g/G&lDh`_"srji0: -*mOT^!R=I<rr=)*rs&7lJcGaLaSu2?Uj;Y5!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEK9"jpfCs)6?brro0Ds(Jn>qYpWa.;\k\"grXDs&/:ars"%Es8VR[iqr`W -cmaeIrre^NoD_,TrrZWAs$?V`!ER5>rrTPVV>C2pI&Hf-0`D"X4l?.@E<#rUZMXY$!gkFQqYp\* -^AcSurVlj<qu6[obPhGBHR[hJrrGL?qu6sCs8VS?WW2u_h>R?T?RbjKrrW-`i;ETUoI9\;!q&\@ -r;Qa;qu6[Y8Flcaa]&6><65(=!Mk#6rrLA?JcC<$U&TW~> -!ouXLrr3!SlMLS^.eEK9"jpfCs)6?brro0Ds(Jn>qYpWa.;\k\"grXDs&/:ars"%Es8VR[iqr`W -cmaeIrre^NoD_,TrrZWAs$?V`!ER5>rrTPVV>C2pI&Hf-0`D"X4l?.@E<#rUZMXY$!gkFQqYp\* -^AcSurVlj<qu6[obPhGBHR[hJrrGL?qu6sCs8VS?WW2u_h>R?T?RbjKrrW-`i;ETUoI9\;!q&\@ -r;Qa;qu6[Y8Flcaa]&6><65(=!Mk#6rrLA?JcC<$U&TW~> -!ouXLrr3!SlMLS^.eEK9"jpfCs)6?brro0Ds(Jn>qYpWa.;\k\"grXDs&/:ars"%Es8VR[iqr`W -cmaeIrre^NoD_,TrrZWAs$?V`!ER5>rrTPVV>C2pI&Hf-0`D"X4l?.@E<#rUZMXY$!gkFQqYp\* -^AcSurVlj<qu6[obPhGBHR[hJrrGL?qu6sCs8VS?WW2u_h>R?T?RbjKrrW-`i;ETUoI9\;!q&\@ -r;Qa;qu6[Y8Flcaa]&6><65(=!Mk#6rrLA?JcC<$U&TW~> -!ouXLrr3'Uh!jt7rrXb3nFZ_[#ZMc`9(r;bp.>6'"D[`3<Z:oV!0I3[!p^7krVm-\*#rScs8VaD -rYu)rqZ$T`48SsYb6i:hrrDop*!Cojs8V9\*!;u6s5<hq!NQ(srrT5,PlC[_h#88rTD\`gb5E:c -^]"31WuN+^r;HWsM[$WhrrBe4*!EqQs89%u*!>!rs0)DA!QtE?rrQs=fDbdMT`,o2f_YUJ/,oPO -q/RGpkPY>]9EP%hrrdon*$4bLrrMaLrr3+B*#pR'rr3)nnF.IqrrF>jo)AmkXapFYbPqMBWIagD -"7krt:P&Oss.TIC~> -!ouXLrr3'Uh!jt7rrXb3nFZ_[#ZMc`9(r;bp.>6'"D[`3<Z:oV!0I3[!p^7krVm-\*#rScs8VaD -rYu)rqZ$T`48SsYb6i:hrrDop*!Cojs8V9\*!;u6s5<hq!NQ(srrT5,PlC[_h#88rTD\`gb5E:c -^]"31WuN+^r;HWsM[$WhrrBe4*!EqQs89%u*!>!rs0)DA!QtE?rrQs=fDbdMT`,o2f_YUJ/,oPO -q/RGpkPY>]9EP%hrrdon*$4bLrrMaLrr3+B*#pR'rr3)nnF.IqrrF>jo)AmkXapFYbPqMBWIagD -"7krt:P&Oss.TIC~> -!ouXLrr3'Uh!jt7rrXb3nFZ_[#ZMc`9(r;bp.>6'"D[`3<Z:oV!0I3[!p^7krVm-\*#rScs8VaD -rYu)rqZ$T`48SsYb6i:hrrDop*!Cojs8V9\*!;u6s5<hq!NQ(srrT5,PlC[_h#88rTD\`gb5E:c -^]"31WuN+^r;HWsM[$WhrrBe4*!EqQs89%u*!>!rs0)DA!QtE?rrQs=fDbdMT`,o2f_YUJ/,oPO -q/RGpkPY>]9EP%hrrdon*$4bLrrMaLrr3+B*#pR'rr3)nnF.IqrrF>jo)AmkXapFYbPqMBWIagD -"7krt:P&Oss.TIC~> -!ouXLrr3(#.k>-.rrZ"H.r+/F!R+C#rrK-?j8T.9PiMcEB=?k#!CbW#rrFA?k5PO+.k=!arrMm@ -h>[RM.k>Ifs+13Ds*t~> -!ouXLrr3(#.k>-.rrZ"H.r+/F!R+C#rrK-?j8T.9PiMcEB=?k#!CbW#rrFA?k5PO+.k=!arrMm@ -h>[RM.k>Ifs+13Ds*t~> -!ouXLrr3(#.k>-.rrZ"H.r+/F!R+C#rrK-?j8T.9PiMcEB=?k#!CbW#rrFA?k5PO+.k=!arrMm@ -h>[RM.k>Ifs+13Ds*t~> -!ouXLf)GgITn2;)!q2;?jSo;DK<jS<!o8"AjSo;1B&htP!m4UAh#@Dd@Y+Q1s+14Gs*t~> -!ouXLf)GgITn2;)!q2;?jSo;DK<jS<!o8"AjSo;1B&htP!m4UAh#@Dd@Y+Q1s+14Gs*t~> -!ouXLf)GgITn2;)!q2;?jSo;DK<jS<!o8"AjSo;1B&htP!m4UAh#@Dd@Y+Q1s+14Gs*t~> -!ouXLf)GdAK&ck3h3[1%!QhG'rrKLBj8T.Pe`6Z1MsB's!-NkmJcC<$q#>j~> -!ouXLf)GdAK&ck3h3[1%!QhG'rrKLBj8T.Pe`6Z1MsB's!-NkmJcC<$q#>j~> -!ouXLf)GdAK&ck3h3[1%!QhG'rrKLBj8T.Pe`6Z1MsB's!-NkmJcC<$q#>j~> -!ouXLL&V1cdZa\2NkPGmp\oXB_RKOFg\u[&hYmHTaMn&V!Q<*2s-Nb9~> -!ouXLL&V1cdZa\2NkPGmp\oXB_RKOFg\u[&hYmHTaMn&V!Q<*2s-Nb9~> -!ouXLL&V1cdZa\2NkPGmp\oXB_RKOFg\u[&hYmHTaMn&V!Q<*2s-Nb9~> -"lqsOs8S>_@fVJerrJ2Co)A`3GfB[_Fl<9$^4G)<rrUAR]C#V#c!t8,r;Qi,A'k5T!S*h3rrSHq -B[m%_G,G3;",$1/N;ih\O(7qad\H:2"b:Cgs2))8rr[EDb,b79"R[8"SC%98#OmHI]"7qAr;L1+ -Xks'Xj8EHfg%PFR`if?(j8]/>S=DXRrVlrKIEMKf!muA@JcD,;J,~> -"lqsOs8S>_@fVJerrJ2Co)A`3GfB[_Fl<9$^4G)<rrUAR]C#V#c!t8,r;Qi,A'k5T!S*h3rrSHq -B[m%_G,G3;",$1/N;ih\O(7qad\H:2"b:Cgs2))8rr[EDb,b79"R[8"SC%98#OmHI]"7qAr;L1+ -Xks'Xj8EHfg%PFR`if?(j8]/>S=DXRrVlrKIEMKf!muA@JcD,;J,~> -"lqsOs8S>_@fVJerrJ2Co)A`3GfB[_Fl<9$^4G)<rrUAR]C#V#c!t8,r;Qi,A'k5T!S*h3rrSHq -B[m%_G,G3;",$1/N;ih\O(7qad\H:2"b:Cgs2))8rr[EDb,b79"R[8"SC%98#OmHI]"7qAr;L1+ -Xks'Xj8EHfg%PFR`if?(j8]/>S=DXRrVlrKIEMKf!muA@JcD,;J,~> -"lqsOs-ArL\-AC]s8U8Gp@J:b^VS_*!V0skrr=(trrJC?r;Qi]\DZcS"6d6nWVlbthRMhLrrK]? -nc&`YPdn,0hZ!V$j8/cV*rc*;$o@/%6N@(IR(-<![:oR>!FYftrs!qIZ=X'*JbK*G*WN'\pAY0] -0)PYRju37"NrT,`rjVu\[,Crbo3D7(!Ft<errf'Bs%:`;rrHK?r;Qe>W;HSqCpAQkR/_[~> -"lqsOs-ArL\-AC]s8U8Gp@J:b^VS_*!V0skrr=(trrJC?r;Qi]\DZcS"6d6nWVlbthRMhLrrK]? -nc&`YPdn,0hZ!V$j8/cV*rc*;$o@/%6N@(IR(-<![:oR>!FYftrs!qIZ=X'*JbK*G*WN'\pAY0] -0)PYRju37"NrT,`rjVu\[,Crbo3D7(!Ft<errf'Bs%:`;rrHK?r;Qe>W;HSqCpAQkR/_[~> -"lqsOs-ArL\-AC]s8U8Gp@J:b^VS_*!V0skrr=(trrJC?r;Qi]\DZcS"6d6nWVlbthRMhLrrK]? -nc&`YPdn,0hZ!V$j8/cV*rc*;$o@/%6N@(IR(-<![:oR>!FYftrs!qIZ=X'*JbK*G*WN'\pAY0] -0)PYRju37"NrT,`rjVu\[,Crbo3D7(!Ft<errf'Bs%:`;rrHK?r;Qe>W;HSqCpAQkR/_[~> -"lqsOs)#">rrKc?rr3#R6HoH1m4[i+!$1%u!l&a@j8T.$WV6>m\mk40"E(CBpa"_r!CYT;rrHE@ -qu7'\l.SK(s(/\>b:?iZrr3!CqXsjm9[NaJ.rFSFrrX;AW-.h6!U_T;rs.[Es,N->ruM+=!JZs! -rrW,+CB"56hYHpG"JU5B9%*Y;!FNP<rrV@pci!eEk$Q\js-`n;~> -"lqsOs)#">rrKc?rr3#R6HoH1m4[i+!$1%u!l&a@j8T.$WV6>m\mk40"E(CBpa"_r!CYT;rrHE@ -qu7'\l.SK(s(/\>b:?iZrr3!CqXsjm9[NaJ.rFSFrrX;AW-.h6!U_T;rs.[Es,N->ruM+=!JZs! -rrW,+CB"56hYHpG"JU5B9%*Y;!FNP<rrV@pci!eEk$Q\js-`n;~> -"lqsOs)#">rrKc?rr3#R6HoH1m4[i+!$1%u!l&a@j8T.$WV6>m\mk40"E(CBpa"_r!CYT;rrHE@ -qu7'\l.SK(s(/\>b:?iZrr3!CqXsjm9[NaJ.rFSFrrX;AW-.h6!U_T;rs.[Es,N->ruM+=!JZs! -rrW,+CB"56hYHpG"JU5B9%*Y;!FNP<rrV@pci!eEk$Q\js-`n;~> -$KOKTs7Z<hs3js?rVlrY6h^6R!_aRerVlol6N.r*EV]V6!p^A$rr3!gf_uj%J,91/m/R*]LB%9= -)?h*#PkY1Xn,44/S,<4+cMedcU\t/Xr>Z3Fr;JVFV]6\]rVt:D*!NkNs8TB>r;QlD*#=n?rrN)2 -r>Yj_rq??mF/f-<"0W[Z[f6:.\m:*j*!#='rrQ^Air/l^DQa$>o)JQDrYuV$o`(\EnkpG]p&C<n -*5aNjp&G&<rYu2-pA^*l#8eFOrVlm\iVWWkoD\^\s8Vfis8MN[s8R!W"<8Cns8VdbrYuMaq#C!a -s8DH\s8PmA,9.\Iq>UBrnc/I[pAY0]0)PYRrU^'aq#C@7rpg2AQiI(9rVm%8_#OG]>5\C+/,u]" -s'!eLdet-g\,H<I&cNaP^A\&Lr>Z*^rr<#op]'m`r;Qe)^&.g1=2t.;!D^pks-`n;~> -$KOKTs7Z<hs3js?rVlrY6h^6R!_aRerVlol6N.r*EV]V6!p^A$rr3!gf_uj%J,91/m/R*]LB%9= -)?h*#PkY1Xn,44/S,<4+cMedcU\t/Xr>Z3Fr;JVFV]6\]rVt:D*!NkNs8TB>r;QlD*#=n?rrN)2 -r>Yj_rq??mF/f-<"0W[Z[f6:.\m:*j*!#='rrQ^Air/l^DQa$>o)JQDrYuV$o`(\EnkpG]p&C<n -*5aNjp&G&<rYu2-pA^*l#8eFOrVlm\iVWWkoD\^\s8Vfis8MN[s8R!W"<8Cns8VdbrYuMaq#C!a -s8DH\s8PmA,9.\Iq>UBrnc/I[pAY0]0)PYRrU^'aq#C@7rpg2AQiI(9rVm%8_#OG]>5\C+/,u]" -s'!eLdet-g\,H<I&cNaP^A\&Lr>Z*^rr<#op]'m`r;Qe)^&.g1=2t.;!D^pks-`n;~> -$KOKTs7Z<hs3js?rVlrY6h^6R!_aRerVlol6N.r*EV]V6!p^A$rr3!gf_uj%J,91/m/R*]LB%9= -)?h*#PkY1Xn,44/S,<4+cMedcU\t/Xr>Z3Fr;JVFV]6\]rVt:D*!NkNs8TB>r;QlD*#=n?rrN)2 -r>Yj_rq??mF/f-<"0W[Z[f6:.\m:*j*!#='rrQ^Air/l^DQa$>o)JQDrYuV$o`(\EnkpG]p&C<n -*5aNjp&G&<rYu2-pA^*l#8eFOrVlm\iVWWkoD\^\s8Vfis8MN[s8R!W"<8Cns8VdbrYuMaq#C!a -s8DH\s8PmA,9.\Iq>UBrnc/I[pAY0]0)PYRrU^'aq#C@7rpg2AQiI(9rVm%8_#OG]>5\C+/,u]" -s'!eLdet-g\,H<I&cNaP^A\&Lr>Z*^rr<#op]'m`r;Qe)^&.g1=2t.;!D^pks-`n;~> -!ouXLrVloJ8c&Gfd7a04!W"#=rrGd@rr3"fK)YcOm3ulNrrG/,rr37\PQ*B's8S[>rr3#d0D,8E -*W?!@JAM6u:&b.n?bQ@:$Wb:Ii!OT+s%^l4-iO&KA)71bZ>079!K<->rrLn@rVlmLnFlk_F/f': -!Gf"?rrTbCPlC[_*W?!JF=H>Os7+ZGP1KO1s.Fc=rt4`Os8REaaoCN]s,`6?39B$\qu6]J7fNDg -7+hD:!Bf?,rrF>?rVlmtaSu2?ZB"_[#"'X1s8S:?n,EFV0(f/D)ZD/q'pnt#5McA>l>"B>s34C< -rs0\GVfi#8c1WL_rrYdBmP"P="$YT'2uWaW<pTGY!FNP;rrH3@qu6[lc@Q"`s*t~> -!ouXLrVloJ8c&Gfd7a04!W"#=rrGd@rr3"fK)YcOm3ulNrrG/,rr37\PQ*B's8S[>rr3#d0D,8E -*W?!@JAM6u:&b.n?bQ@:$Wb:Ii!OT+s%^l4-iO&KA)71bZ>079!K<->rrLn@rVlmLnFlk_F/f': -!Gf"?rrTbCPlC[_*W?!JF=H>Os7+ZGP1KO1s.Fc=rt4`Os8REaaoCN]s,`6?39B$\qu6]J7fNDg -7+hD:!Bf?,rrF>?rVlmtaSu2?ZB"_[#"'X1s8S:?n,EFV0(f/D)ZD/q'pnt#5McA>l>"B>s34C< -rs0\GVfi#8c1WL_rrYdBmP"P="$YT'2uWaW<pTGY!FNP;rrH3@qu6[lc@Q"`s*t~> -!ouXLrVloJ8c&Gfd7a04!W"#=rrGd@rr3"fK)YcOm3ulNrrG/,rr37\PQ*B's8S[>rr3#d0D,8E -*W?!@JAM6u:&b.n?bQ@:$Wb:Ii!OT+s%^l4-iO&KA)71bZ>079!K<->rrLn@rVlmLnFlk_F/f': -!Gf"?rrTbCPlC[_*W?!JF=H>Os7+ZGP1KO1s.Fc=rt4`Os8REaaoCN]s,`6?39B$\qu6]J7fNDg -7+hD:!Bf?,rrF>?rVlmtaSu2?ZB"_[#"'X1s8S:?n,EFV0(f/D)ZD/q'pnt#5McA>l>"B>s34C< -rs0\GVfi#8c1WL_rrYdBmP"P="$YT'2uWaW<pTGY!FNP;rrH3@qu6[lc@Q"`s*t~> -!ouXLrr3#?<Vl^sqPAT4rrMs?rVlmkdf07IRY(>7!BT6>rs4IFs"Wj>s.4]=rrML?o`"n3qYpO9 -rVlt,^&Rp,\cb7;s56$=rr[`)pfIF*!:9^b!O?J:rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1 -@f?<,*VfX@^dT:t7IU9VTR?b='_![Rs*LL?s6a\&OH'9"jki6$s4@7]!M)^ZrrGO?qu6[ZiTpLE -,5V9<!J7$E\cC4RrrGL?rr3"_MXUQGmOn/3!$2(=$&4fHs7.Z>s*^O=rrLA@r;R$Ds/1#>:X/S[ -rVlsif'Y3irrYIAlsB\&!9s+T!FNP;rrH3@qu6[lc@Q"`s*t~> -!ouXLrr3#?<Vl^sqPAT4rrMs?rVlmkdf07IRY(>7!BT6>rs4IFs"Wj>s.4]=rrML?o`"n3qYpO9 -rVlt,^&Rp,\cb7;s56$=rr[`)pfIF*!:9^b!O?J:rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1 -@f?<,*VfX@^dT:t7IU9VTR?b='_![Rs*LL?s6a\&OH'9"jki6$s4@7]!M)^ZrrGO?qu6[ZiTpLE -,5V9<!J7$E\cC4RrrGL?rr3"_MXUQGmOn/3!$2(=$&4fHs7.Z>s*^O=rrLA@r;R$Ds/1#>:X/S[ -rVlsif'Y3irrYIAlsB\&!9s+T!FNP;rrH3@qu6[lc@Q"`s*t~> -!ouXLrr3#?<Vl^sqPAT4rrMs?rVlmkdf07IRY(>7!BT6>rs4IFs"Wj>s.4]=rrML?o`"n3qYpO9 -rVlt,^&Rp,\cb7;s56$=rr[`)pfIF*!:9^b!O?J:rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1 -@f?<,*VfX@^dT:t7IU9VTR?b='_![Rs*LL?s6a\&OH'9"jki6$s4@7]!M)^ZrrGO?qu6[ZiTpLE -,5V9<!J7$E\cC4RrrGL?rr3"_MXUQGmOn/3!$2(=$&4fHs7.Z>s*^O=rrLA@r;R$Ds/1#>:X/S[ -rVlsif'Y3irrYIAlsB\&!9s+T!FNP;rrH3@qu6[lc@Q"`s*t~> -#NS0Qs8T`Ko(r@eT76G4!W"#=rrGd@rr3"fK)#?H3o^/=#`4cF1$el>Spp\=!UVQ4rr=)9rr=)< -rrZWAs-rsq"<kebi&po:!9a;h"c`$Os0?D9rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1@f?<, -*VTL6DQ`s<!MFi>rrqmCs8RG?r;R(bNW2!"h`_"eHN%=R;+CQb!CYT;rrG7@li-u0qY^?nKDo9[ -GKfj^!CPQ>rrJ=@n,EFV0(f/D*WH'FL6qr?pM7=mJ&M?d!R4I=rs0\GVfi#8c1WL_rrYdBmP"P= -"$YT7OoAbhbk(i8?bQ@:!E[;<rrGm?JcD/<J,~> -#NS0Qs8T`Ko(r@eT76G4!W"#=rrGd@rr3"fK)#?H3o^/=#`4cF1$el>Spp\=!UVQ4rr=)9rr=)< -rrZWAs-rsq"<kebi&po:!9a;h"c`$Os0?D9rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1@f?<, -*VTL6DQ`s<!MFi>rrqmCs8RG?r;R(bNW2!"h`_"eHN%=R;+CQb!CYT;rrG7@li-u0qY^?nKDo9[ -GKfj^!CPQ>rrJ=@n,EFV0(f/D*WH'FL6qr?pM7=mJ&M?d!R4I=rs0\GVfi#8c1WL_rrYdBmP"P= -"$YT7OoAbhbk(i8?bQ@:!E[;<rrGm?JcD/<J,~> -#NS0Qs8T`Ko(r@eT76G4!W"#=rrGd@rr3"fK)#?H3o^/=#`4cF1$el>Spp\=!UVQ4rr=)9rr=)< -rrZWAs-rsq"<kebi&po:!9a;h"c`$Os0?D9rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1@f?<, -*VTL6DQ`s<!MFi>rrqmCs8RG?r;R(bNW2!"h`_"eHN%=R;+CQb!CYT;rrG7@li-u0qY^?nKDo9[ -GKfj^!CPQ>rrJ=@n,EFV0(f/D*WH'FL6qr?pM7=mJ&M?d!R4I=rs0\GVfi#8c1WL_rrYdBmP"P= -"$YT7OoAbhbk(i8?bQ@:!E[;<rrGm?JcD/<J,~> -#38'Ps0mL>qYpS^L@bEGqBGn<!DCl?rrJR?rr3&lJuSbL!BP?%rs4IFs"Wj>s.4]=rrqdCs8Td\ -q#:=7rVlu-g]&;mrrZWAruM+="hQ>*s56$;rrMt]rVm'jfDkm'J&M6a!K<->rrLn@rVlmLnFlk_ -F/f':!Gf"?rrTbe_>aH7*W?!>Y(H0ArrI#?rVlnkI/a-Ln1=V>H_UH<%Zl\M39B$\s+cm>s7pM$ -rr3*hf`1-5rr3%hJc>$9#9s$Ef"(g]jSo/[Uj;b8!CPQ>rrJ=@pAY3]K!G:S!U_T4rr=)=rrIk@ -rr3)N8",&.rt!@Ns1?e\ruV3<FoP7^p*Tb<"%Ur10E(nQ52PB[rVlngoC`+a?bQC;!kWsAr;Qi- -J*-\/!jdLEJcDGDJ,~> -#38'Ps0mL>qYpS^L@bEGqBGn<!DCl?rrJR?rr3&lJuSbL!BP?%rs4IFs"Wj>s.4]=rrqdCs8Td\ -q#:=7rVlu-g]&;mrrZWAruM+="hQ>*s56$;rrMt]rVm'jfDkm'J&M6a!K<->rrLn@rVlmLnFlk_ -F/f':!Gf"?rrTbe_>aH7*W?!>Y(H0ArrI#?rVlnkI/a-Ln1=V>H_UH<%Zl\M39B$\s+cm>s7pM$ -rr3*hf`1-5rr3%hJc>$9#9s$Ef"(g]jSo/[Uj;b8!CPQ>rrJ=@pAY3]K!G:S!U_T4rr=)=rrIk@ -rr3)N8",&.rt!@Ns1?e\ruV3<FoP7^p*Tb<"%Ur10E(nQ52PB[rVlngoC`+a?bQC;!kWsAr;Qi- -J*-\/!jdLEJcDGDJ,~> -#38'Ps0mL>qYpS^L@bEGqBGn<!DCl?rrJR?rr3&lJuSbL!BP?%rs4IFs"Wj>s.4]=rrqdCs8Td\ -q#:=7rVlu-g]&;mrrZWAruM+="hQ>*s56$;rrMt]rVm'jfDkm'J&M6a!K<->rrLn@rVlmLnFlk_ -F/f':!Gf"?rrTbe_>aH7*W?!>Y(H0ArrI#?rVlnkI/a-Ln1=V>H_UH<%Zl\M39B$\s+cm>s7pM$ -rr3*hf`1-5rr3%hJc>$9#9s$Ef"(g]jSo/[Uj;b8!CPQ>rrJ=@pAY3]K!G:S!U_T4rr=)=rrIk@ -rr3)N8",&.rt!@Ns1?e\ruV3<FoP7^p*Tb<"%Ur10E(nQ52PB[rVlngoC`+a?bQC;!kWsAr;Qi- -J*-\/!jdLEJcDGDJ,~> -"lqsOs(t7(Ad+k-s,iH?o`#2OA.E4Bs8TQHral1Wc2R_Ekl=QZrrG0:ralakeGlXcAnH==_uKc3 -OCi*Ug\CdKUA\[+K]2qOrVlt,^&OGuAd*cC`rFsZr;QfpOo8kmMspZF!L8H=rs7Fm3'([/s7)'H -Ac\"hq#:rOAhHJ`n,NF0Ah-A`o`)KaJ,X$[TDeck=odLZrrI#?rVloOJGs-aBrh:6H_UH<%Zl\M -YOp^8s3H%(AnZ`brr3,.DJ!jtrVlm>rp9XiVe9Uc`rEYjral1UanYl:6eVJ=!L&E7rrUmS@/^*+ -mOn/3!2BI)!IiSurr_\IHHlEg&*o'aK&$D+P@d08Ar5jNOo8koMsgA%rFQ.ko(A%AAc[qbp&>&" -^&7m2FK#*:!Go%<rrQ[1eq*jps*t~> -"lqsOs(t7(Ad+k-s,iH?o`#2OA.E4Bs8TQHral1Wc2R_Ekl=QZrrG0:ralakeGlXcAnH==_uKc3 -OCi*Ug\CdKUA\[+K]2qOrVlt,^&OGuAd*cC`rFsZr;QfpOo8kmMspZF!L8H=rs7Fm3'([/s7)'H -Ac\"hq#:rOAhHJ`n,NF0Ah-A`o`)KaJ,X$[TDeck=odLZrrI#?rVloOJGs-aBrh:6H_UH<%Zl\M -YOp^8s3H%(AnZ`brr3,.DJ!jtrVlm>rp9XiVe9Uc`rEYjral1UanYl:6eVJ=!L&E7rrUmS@/^*+ -mOn/3!2BI)!IiSurr_\IHHlEg&*o'aK&$D+P@d08Ar5jNOo8koMsgA%rFQ.ko(A%AAc[qbp&>&" -^&7m2FK#*:!Go%<rrQ[1eq*jps*t~> -"lqsOs(t7(Ad+k-s,iH?o`#2OA.E4Bs8TQHral1Wc2R_Ekl=QZrrG0:ralakeGlXcAnH==_uKc3 -OCi*Ug\CdKUA\[+K]2qOrVlt,^&OGuAd*cC`rFsZr;QfpOo8kmMspZF!L8H=rs7Fm3'([/s7)'H -Ac\"hq#:rOAhHJ`n,NF0Ah-A`o`)KaJ,X$[TDeck=odLZrrI#?rVloOJGs-aBrh:6H_UH<%Zl\M -YOp^8s3H%(AnZ`brr3,.DJ!jtrVlm>rp9XiVe9Uc`rEYjral1UanYl:6eVJ=!L&E7rrUmS@/^*+ -mOn/3!2BI)!IiSurr_\IHHlEg&*o'aK&$D+P@d08Ar5jNOo8koMsgA%rFQ.ko(A%AAc[qbp&>&" -^&7m2FK#*:!Go%<rrQ[1eq*jps*t~> -"QVjNs4.%T"O[8Lb4G6)!6+rF!7:`F!5e`C!7LiG!r97Jrr3![ir6=cfDbdR]=#&og>`,3"nB". -h#DQp8cls2rVa,+iW&qlrVluIm/Qn\[0>C3mf3"#qu6ZgrNuXkrVlrWI)#[\!:Tlo"7Z?jm/=<m -o(DiO!rDr[rNub%s8V3Z[06@+ldFMd[/g.'rrUNSpAP!le`Zl1!8IL\!4i+/!SQQ3rrM$6rr3;u -a8c1h[C*O9ao25@^pV)XrrKi?li.$p[E\^N!6+rF!7:K?!Qk!5rrM'6pAY3dL1'u["RZ^k8u_Rb -!<2u*!8.5L!UA,1rrVDlh>[E[p9f'B[HRYjo_l0"li7"TrNuaps8VQd[/f[irrgP<8fYPCrrTHY -k5>5\X,-!:rrUWVo7?q8s*t~> -"QVjNs4.%T"O[8Lb4G6)!6+rF!7:`F!5e`C!7LiG!r97Jrr3![ir6=cfDbdR]=#&og>`,3"nB". -h#DQp8cls2rVa,+iW&qlrVluIm/Qn\[0>C3mf3"#qu6ZgrNuXkrVlrWI)#[\!:Tlo"7Z?jm/=<m -o(DiO!rDr[rNub%s8V3Z[06@+ldFMd[/g.'rrUNSpAP!le`Zl1!8IL\!4i+/!SQQ3rrM$6rr3;u -a8c1h[C*O9ao25@^pV)XrrKi?li.$p[E\^N!6+rF!7:K?!Qk!5rrM'6pAY3dL1'u["RZ^k8u_Rb -!<2u*!8.5L!UA,1rrVDlh>[E[p9f'B[HRYjo_l0"li7"TrNuaps8VQd[/f[irrgP<8fYPCrrTHY -k5>5\X,-!:rrUWVo7?q8s*t~> -"QVjNs4.%T"O[8Lb4G6)!6+rF!7:`F!5e`C!7LiG!r97Jrr3![ir6=cfDbdR]=#&og>`,3"nB". -h#DQp8cls2rVa,+iW&qlrVluIm/Qn\[0>C3mf3"#qu6ZgrNuXkrVlrWI)#[\!:Tlo"7Z?jm/=<m -o(DiO!rDr[rNub%s8V3Z[06@+ldFMd[/g.'rrUNSpAP!le`Zl1!8IL\!4i+/!SQQ3rrM$6rr3;u -a8c1h[C*O9ao25@^pV)XrrKi?li.$p[E\^N!6+rF!7:K?!Qk!5rrM'6pAY3dL1'u["RZ^k8u_Rb -!<2u*!8.5L!UA,1rrVDlh>[E[p9f'B[HRYjo_l0"li7"TrNuaps8VQd[/f[irrgP<8fYPCrrTHY -k5>5\X,-!:rrUWVo7?q8s*t~> -!ouXLd/O,-ip?[Fh##J!nC@O>kO7p?!;u]@!<0,#!7o'f!PDh=rrDurd/`FerrK$?h>[KGrm:`; -rVloalMLS^lKj*%QiDR~> -!ouXLd/O,-ip?[Fh##J!nC@O>kO7p?!;u]@!<0,#!7o'f!PDh=rrDurd/`FerrK$?h>[KGrm:`; -rVloalMLS^lKj*%QiDR~> -!ouXLd/O,-ip?[Fh##J!nC@O>kO7p?!;u]@!<0,#!7o'f!PDh=rrDurd/`FerrK$?h>[KGrm:`; -rVloalMLS^lKj*%QiDR~> -!ouXLd/O,5jFR>grrV%Uo[NmAWdB<sJcF[.J,~> -!ouXLd/O,5jFR>grrV%Uo[NmAWdB<sJcF[.J,~> -!ouXLd/O,5jFR>grrV%Uo[NmAWdB<sJcF[.J,~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -#in9Rs8UK[]uSt/!65#O!82r'JcC<$JcE(VJ,~> -#in9Rs8UK[]uSt/!65#O!82r'JcC<$JcE(VJ,~> -#in9Rs8UK[]uSt/!65#O!82r'JcC<$JcE(VJ,~> -$04BSs2f=g?>-n,rrKZDra#VPe:IXNs+13$s0VfV~> -$04BSs2f=g?>-n,rrKZDra#VPe:IXNs+13$s0VfV~> -$04BSs2f=g?>-n,rrKZDra#VPe:IXNs+13$s0VfV~> -$04BSs)#"?s4Kcsrs#$Hqu?]9_L_`<s+13$s0VfV~> -$04BSs)#"?s4Kcsrs#$Hqu?]9_L_`<s+13$s0VfV~> -$04BSs)#"?s4Kcsrs#$Hqu?]9_L_`<s+13$s0VfV~> -%HKfWs)#"?s4Kd>s.n3ErrLQFrr3"HrVca&Rc"$ks2_GEs+C;b!2TVo"=7VnBrV+3!."QX!3UnQ -JcC<$JcE@^J,~> -%HKfWs)#"?s4Kd>s.n3ErrLQFrr3"HrVca&Rc"$ks2_GEs+C;b!2TVo"=7VnBrV+3!."QX!3UnQ -JcC<$JcE@^J,~> -%HKfWs)#"?s4Kd>s.n3ErrLQFrr3"HrVca&Rc"$ks2_GEs+C;b!2TVo"=7VnBrV+3!."QX!3UnQ -JcC<$JcE@^J,~> -%HKfWs(sVe9L2&Gs(&Y=rrKK@rr3!Bqu-O$;p"k[s.ao?JY<"K"H'/XH_gYI"Ga/[Ff55F!J@_0 -s+13$s+13_s*t~> -%HKfWs(sVe9L2&Gs(&Y=rrKK@rr3!Bqu-O$;p"k[s.ao?JY<"K"H'/XH_gYI"Ga/[Ff55F!J@_0 -s+13$s+13_s*t~> -%HKfWs(sVe9L2&Gs(&Y=rrKK@rr3!Bqu-O$;p"k[s.ao?JY<"K"H'/XH_gYI"Ga/[Ff55F!J@_0 -s+13$s+13_s*t~> -'')>\s)!eBc`fe:s5I;>s4Ui>rr3!Bqu-O$;p"k[s.ao?9@Eh>"IarB6JDG="Hn]C3o^/=!U8m# -s+13$s+13_s*t~> -'')>\s)!eBc`fe:s5I;>s4Ui>rr3!Bqu-O$;p"k[s.ao?9@Eh>"IarB6JDG="Hn]C3o^/=!U8m# -s+13$s+13_s*t~> -'')>\s)!eBc`fe:s5I;>s4Ui>rr3!Bqu-O$;p"k[s.ao?9@Eh>"IarB6JDG="Hn]C3o^/=!U8m# -s+13$s+13_s*t~> -$04BSs)#"?s4Kd=rrc$Brle7<rrF;?rVm1&and4]UO)r5df07LRY(Q+gA_*WP)KA)1)q9LJcC<$ -JcC<$])R9~> -$04BSs)#"?s4Kd=rrc$Brle7<rrF;?rVm1&and4]UO)r5df07LRY(Q+gA_*WP)KA)1)q9LJcC<$ -JcC<$])R9~> -$04BSs)#"?s4Kd=rrc$Brle7<rrF;?rVm1&and4]UO)r5df07LRY(Q+gA_*WP)KA)1)q9LJcC<$ -JcC<$])R9~> -$04BSs)#"?s4Kd<rrO\00)Y_[+oD#ss8Q$>qBGs7HN-Xdrr3+iK)\0krr3+aMuPitrr3#fn:CUj -s+13$s1JA^~> -$04BSs)#"?s4Kd<rrO\00)Y_[+oD#ss8Q$>qBGs7HN-Xdrr3+iK)\0krr3+aMuPitrr3#fn:CUj -s+13$s1JA^~> -$04BSs)#"?s4Kd<rrO\00)Y_[+oD#ss8Q$>qBGs7HN-Xdrr3+iK)\0krr3+aMuPitrr3#fn:CUj -s+13$s1JA^~> -$04BSs,i\`^T;JSrrQQ4@f66:=hUV\s8RPD]G\JHS,[`4rkASqU&T,7rkASmW;gY<rkAJeY(?V( -s+13$s1JA^~> -$04BSs,i\`^T;JSrrQQ4@f66:=hUV\s8RPD]G\JHS,[`4rkASqU&T,7rkASmW;gY<rkAJeY(?V( -s+13$s1JA^~> -$04BSs,i\`^T;JSrrQQ4@f66:=hUV\s8RPD]G\JHS,[`4rkASqU&T,7rkASmW;gY<rkAJeY(?V( -s+13$s1JA^~> -#in9Rs8S-b>]flC!,q`6!FC9SrrddS$s].urr@?D>QC;nrr@0?>QCMtrr?s9>QC[Qs+13$s+13^ -s*t~> -#in9Rs8S-b>]flC!,q`6!FC9SrrddS$s].urr@?D>QC;nrr@0?>QCMtrr?s9>QC[Qs+13$s+13^ -s*t~> -#in9Rs8S-b>]flC!,q`6!FC9SrrddS$s].urr@?D>QC;nrr@0?>QCMtrr?s9>QC[Qs+13$s+13^ -s*t~> -!ouXLk5PJY,(]cFs+13$s.TIC~> -!ouXLk5PJY,(]cFs+13$s.TIC~> -!ouXLk5PJY,(]cFs+13$s.TIC~> -!ouXLk5PJY,(]cFs+13$s.TIC~> -!ouXLk5PJY,(]cFs+13$s.TIC~> -!ouXLk5PJY,(]cFs+13$s.TIC~> -!ouXLk5PJ]\q0m4s+13$s.TIC~> -!ouXLk5PJ]\q0m4s+13$s.TIC~> -!ouXLk5PJ]\q0m4s+13$s.TIC~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLq#:A8^4H<8s+13$s,[21~> -!ouXLq#:A8^4H<8s+13$s,[21~> -!ouXLq#:A8^4H<8s+13$s,[21~> -!ouXLq#:A*\:O[2s+13$s,[21~> -!ouXLq#:A*\:O[2s+13$s,[21~> -!ouXLq#:A*\:O[2s+13$s,[21~> -"lqsOs8UXK_?A<es(&Y=rrMNAJcC<$JcC<$PlH7~> -"lqsOs8UXK_?A<es(&Y=rrMNAJcC<$JcC<$PlH7~> -"lqsOs8UXK_?A<es(&Y=rrMNAJcC<$JcC<$PlH7~> -"lqsOs3,K^=UArcs(&Y>q504DJcC<$JcC<$PlH7~> -"lqsOs3,K^=UArcs(&Y>q504DJcC<$JcC<$PlH7~> -"lqsOs3,K^=UArcs(&Y>q504DJcC<$JcC<$PlH7~> -"lqsOs)#">rs68Fs(&X1Lio;?s+13$s+136s*t~> -"lqsOs)#">rs68Fs(&X1Lio;?s+13$s+136s*t~> -"lqsOs)#">rs68Fs(&X1Lio;?s+13$s+136s*t~> -"lqsOs)#">rs$,Ds("oaR=YBhs+13$s,m>3~> -"lqsOs)#">rs$,Ds("oaR=YBhs+13$s,m>3~> -"lqsOs)#">rs$,Ds("oaR=YBhs+13$s,m>3~> -"lqsOs)#">rs$,Ds($Od?%N$,s+13$s,m>3~> -"lqsOs)#">rs$,Ds($Od?%N$,s+13$s,m>3~> -"lqsOs)#">rs$,Ds($Od?%N$,s+13$s,m>3~> -"lqsOs)#">rs68Fs(&Xf^c$1`s+13$s+136s*t~> -"lqsOs)#">rs68Fs(&Xf^c$1`s+13$s+136s*t~> -"lqsOs)#">rs68Fs(&Xf^c$1`s+13$s+136s*t~> -"lqsOs6s7l*!oL2s(8hArT1&)JcC<$JcC<$PlH7~> -"lqsOs6s7l*!oL2s(8hArT1&)JcC<$JcC<$PlH7~> -"lqsOs6s7l*!oL2s(8hArT1&)JcC<$JcC<$PlH7~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -"QVjNs/l3<"K)5!R-9,$JcC<$JcCi3J,~> -"QVjNs/l3<"K)5!R-9,$JcC<$JcCi3J,~> -"QVjNs/l3<"K)5!R-9,$JcC<$JcCi3J,~> -"QVjNs3LYE#(Bt[s2&7>JcC<$JcC<$OoKq~> -"QVjNs3LYE#(Bt[s2&7>JcC<$JcC<$OoKq~> -"QVjNs3LYE#(Bt[s2&7>JcC<$JcC<$OoKq~> -!ouXLr;QiX?JPP[!ROMks+13$s+134s*t~> -!ouXLr;QiX?JPP[!ROMks+13$s+134s*t~> -!ouXLr;QiX?JPP[!ROMks+13$s+134s*t~> -!ouXLrVlrn3U6PB!S9bks+13$s+135s*t~> -!ouXLrVlrn3U6PB!S9bks+13$s+135s*t~> -!ouXLrVlrn3U6PB!S9bks+13$s+135s*t~> -%%EndData -showpage -%%Trailer -end -%%EOF diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml index cf0d581352..61f49f5940 100644 --- a/lib/stdlib/doc/src/zip.xml +++ b/lib/stdlib/doc/src/zip.xml @@ -217,7 +217,7 @@ <tag><c>{uncompress, <anno>What</anno>}</c></tag> <item> <p>Controls what types of files will be uncompressed. It is by - default set to <c>[".Z",".zip",".zoo",".arc",".lzh",".arj"]</c>. + default set to <c>[".Z", ".zip", ".zoo", ".arc", ".lzh", ".arj"]</c>. The following values of <c>What</c> are allowed:</p> <taglist> <tag><c>all</c></tag> @@ -355,7 +355,7 @@ {ok,{"dummy.zip", <<80,75,3,4,20,0,0,0,0,0,74,152,97,60,171,39,212,26,3,0, 0,0,3,0,0,...>>}} -> <input>catch zip:foldl(fun("foo", _, B, _) -> throw(B()); (_, _, _, Acc) -> Acc end, [], {Name, Bin}). </input> +> <input>catch zip:foldl(fun("foo", _, B, _) -> throw(B()); (_,_,_,Acc) -> Acc end, [], {Name, Bin}). </input> <<"FOO">> </pre> </desc> diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile index 14304824d3..575a5cbe4a 100644 --- a/lib/stdlib/src/Makefile +++ b/lib/stdlib/src/Makefile @@ -171,6 +171,7 @@ primary_bootstrap_compiler: \ $(BOOTSTRAP_COMPILER)/ebin/erl_scan.beam \ $(BOOTSTRAP_COMPILER)/ebin/erl_parse.beam \ $(BOOTSTRAP_COMPILER)/ebin/erl_lint.beam \ + $(BOOTSTRAP_COMPILER)/ebin/io.beam \ $(BOOTSTRAP_COMPILER)/ebin/otp_internal.beam $(BOOTSTRAP_COMPILER)/ebin/erl_parse.beam: erl_parse.yrl diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl index a920921a5e..4c1c0f904b 100644 --- a/lib/stdlib/src/c.erl +++ b/lib/stdlib/src/c.erl @@ -116,7 +116,7 @@ machine_load(Mod, File, Opts) -> File2 = filename:join(Dir, filename:basename(File, ".erl")), case compile:output_generated(Opts) of true -> - Base = packages:last(Mod), + Base = atom_to_list(Mod), case filename:basename(File, ".erl") of Base -> code:purge(Mod), diff --git a/lib/stdlib/src/dict.erl b/lib/stdlib/src/dict.erl index 2e9eba4bfa..4f8d45dc8d 100644 --- a/lib/stdlib/src/dict.erl +++ b/lib/stdlib/src/dict.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. 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 @@ -17,7 +18,7 @@ %% %CopyrightEnd% %% -%% We use the dynamic hashing techniques by Per-�ke Larsson as +%% We use the dynamic hashing techniques by Per-Åke Larsson as %% described in "The Design and Implementation of Dynamic Hashing for %% Sets and Tables in Icon" by Griswold and Townsend. Much of the %% terminology comes from that paper as well. diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl index 026bd9038f..1164ee49eb 100644 --- a/lib/stdlib/src/edlin.erl +++ b/lib/stdlib/src/edlin.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -317,9 +318,9 @@ over_non_word([], Stack, N) -> {[],Stack,N}. word_char(C) when C >= $A, C =< $Z -> true; -word_char(C) when C >= $�, C =< $�, C =/= $� -> true; +word_char(C) when C >= $À, C =< $Þ, C =/= $× -> true; word_char(C) when C >= $a, C =< $z -> true; -word_char(C) when C >= $�, C =< $�, C =/= $� -> true; +word_char(C) when C >= $ß, C =< $ÿ, C =/= $÷ -> true; word_char(C) when C >= $0, C =< $9 -> true; word_char(C) when C =:= $_ -> true; word_char(C) when C =:= $. -> true; % accept dot-separated names diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl index 2c8d84a9e1..a0f7660ecf 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.erl @@ -23,13 +23,18 @@ -export([open/2,open/3,open/5,close/1,format_error/1]). -export([scan_erl_form/1,parse_erl_form/1,macro_defs/1]). -export([parse_file/1, parse_file/3]). +-export([default_encoding/0, encoding_to_string/1, + read_encoding/1, read_encoding/2, set_encoding/1]). -export([interpret_file_attribute/1]). -export([normalize_typed_record_fields/1,restore_typed_record_fields/1]). %%------------------------------------------------------------------------ +-export_type([source_encoding/0]). + -type macros() :: [{atom(), term()}]. -type epp_handle() :: pid(). +-type source_encoding() :: latin1 | utf8. %% Epp state record. -record(epp, {file, %Current file @@ -213,6 +218,173 @@ parse_file(Epp) -> [{eof,Location}] end. +-define(DEFAULT_ENCODING, latin1). + +-spec default_encoding() -> source_encoding(). + +default_encoding() -> + ?DEFAULT_ENCODING. + +-spec encoding_to_string(Encoding) -> string() when + Encoding :: source_encoding(). + +encoding_to_string(latin1) -> "coding: latin-1"; +encoding_to_string(utf8) -> "coding: utf-8". + +-spec read_encoding(FileName) -> source_encoding() | none when + FileName :: file:name(). + +read_encoding(Name) -> + read_encoding(Name, []). + +-spec read_encoding(FileName, Options) -> source_encoding() | none when + FileName :: file:name(), + Options :: [Option], + Option :: {in_comment_only, boolean()}. + +read_encoding(Name, Options) -> + InComment = proplists:get_value(in_comment_only, Options, true), + case file:open(Name, [read]) of + {ok,File} -> + try read_encoding_from_file(File, InComment) + after ok = file:close(File) + end; + _Error -> + none + end. + +-spec set_encoding(File) -> source_encoding() | none when + File :: io:device(). % pid(); raw files don't work + +set_encoding(File) -> + Encoding = read_encoding_from_file(File, true), + Enc = case Encoding of + none -> default_encoding(); + Encoding -> Encoding + end, + ok = io:setopts(File, [{encoding, Enc}]), + Encoding. + +-spec read_encoding_from_file(File, InComment) -> source_encoding() | none when + File :: io:device(), + InComment :: boolean(). + +-define(ENC_CHUNK, 32). +-define(N_ENC_CHUNK, 16). % a total of 512 bytes + +read_encoding_from_file(File, InComment) -> + {ok, Pos0} = file:position(File, cur), + Opts = io:getopts(File), + Encoding0 = lists:keyfind(encoding, 1, Opts), + Binary0 = lists:keyfind(binary, 1, Opts), + ok = io:setopts(File, [binary, {encoding, latin1}]), + try + {B, Fun} = (reader(File, 0))(), + com_nl(B, Fun, 0, InComment) + catch + throw:no -> + none + after + {ok, Pos0} = file:position(File, Pos0), + ok = io:setopts(File, [Binary0, Encoding0]) + end. + +reader(Fd, N) -> + fun() when N =:= ?N_ENC_CHUNK -> + throw(no); + () -> + case file:read(Fd, ?ENC_CHUNK) of + eof -> + {<<>>, reader(Fd, N+1)}; + {ok, Bin} -> + {Bin, reader(Fd, N+1)}; + {error, _} -> + throw(no) % ignore errors + end + end. + +com_nl(_, _, 2, _) -> + throw(no); +com_nl(B, Fun, N, false=Com) -> + com_c(B, Fun, N, Com); +com_nl(B, Fun, N, true=Com) -> + com(B, Fun, N, Com). + +com(<<"\n",B/binary>>, Fun, N, Com) -> + com_nl(B, Fun, N+1, Com); +com(<<"%", B/binary>>, Fun, N, Com) -> + com_c(B, Fun, N, Com); +com(<<_:1/unit:8,B/binary>>, Fun, N, Com) -> + com(B, Fun, N, Com); +com(<<>>, Fun, N, Com) -> + {B, Fun1} = Fun(), + com(B, Fun1, N, Com). + +com_c(<<"c",B/binary>>, Fun, N, Com) -> + com_oding(B, Fun, N, Com); +com_c(<<"\n",B/binary>>, Fun, N, Com) -> + com_nl(B, Fun, N+1, Com); +com_c(<<_:1/unit:8,B/binary>>, Fun, N, Com) -> + com_c(B, Fun, N, Com); +com_c(<<>>, Fun, N, Com) -> + {B, Fun1} = Fun(), + com_c(B, Fun1, N, Com). + +com_oding(<<"oding",B/binary>>, Fun, N, Com) -> + com_sep(B, Fun, N, Com); +com_oding(B, Fun, N, Com) when byte_size(B) >= length("oding") -> + com_c(B, Fun, N, Com); +com_oding(B, Fun, N, Com) -> + {B1, Fun1} = Fun(), + com_oding(list_to_binary([B, B1]), Fun1, N, Com). + +com_sep(<<":",B/binary>>, Fun, N, Com) -> + com_space(B, Fun, N, Com); +com_sep(<<"=",B/binary>>, Fun, N, Com) -> + com_space(B, Fun, N, Com); +com_sep(<<"\s",B/binary>>, Fun, N, Com) -> + com_sep(B, Fun, N, Com); +com_sep(<<>>, Fun, N, Com) -> + {B, Fun1} = Fun(), + com_sep(B, Fun1, N, Com); +com_sep(B, Fun, N, Com) -> + com_c(B, Fun, N, Com). + +com_space(<<"\s",B/binary>>, Fun, N, Com) -> + com_space(B, Fun, N, Com); +com_space(<<>>, Fun, N, Com) -> + {B, Fun1} = Fun(), + com_space(B, Fun1, N, Com); +com_space(B, Fun, N, _Com) -> + com_enc(B, Fun, N, [], []). + +com_enc(<<C:1/unit:8,B/binary>>, Fun, N, L, Ps) when C >= $a, C =< $z; + C >= $A, C =< $Z; + C >= $0, C =< $9 -> + com_enc(B, Fun, N, [C | L], Ps); +com_enc(<<>>, Fun, N, L, Ps) -> + case Fun() of + {<<>>, _} -> + com_enc_end([L | Ps]); + {B, Fun1} -> + com_enc(B, Fun1, N, L, Ps) + end; +com_enc(<<"-",B/binary>>, Fun, N, L, Ps) -> + com_enc(B, Fun, N, [], [L | Ps]); +com_enc(_B, _Fun, _N, L, Ps) -> + com_enc_end([L | Ps]). + +com_enc_end(Ps0) -> + Ps = lists:reverse([lists:reverse(string:to_lower(P)) || P <- Ps0]), + com_encoding(Ps). + +com_encoding(["latin","1"|_]) -> + latin1; +com_encoding(["utf","8"|_]) -> + utf8; +com_encoding(_) -> + throw(no). % Don't try any further + normalize_typed_record_fields([]) -> {typed, []}; normalize_typed_record_fields(Fields) -> @@ -266,14 +438,17 @@ init_server(Pid, Name, File, AtLocation, Path, Pdm, Pre) -> Ms0 = predef_macros(Name), case user_predef(Pdm, Ms0) of {ok,Ms1} -> - epp_reply(Pid, {ok,self()}), - %% ensure directory of current source file is first in path + _ = set_encoding(File), + epp_reply(Pid, {ok,self()}), + %% ensure directory of current source file is + %% first in path Path1 = [filename:dirname(Name) | Path], - St = #epp{file=File, location=AtLocation, delta=0, name=Name, - name2=Name, path=Path1, macs=Ms1, pre_opened = Pre}, - From = wait_request(St), - enter_file_reply(From, Name, AtLocation, AtLocation), - wait_req_scan(St); + St = #epp{file=File, location=AtLocation, delta=0, + name=Name, name2=Name, path=Path1, macs=Ms1, + pre_opened = Pre}, + From = wait_request(St), + enter_file_reply(From, Name, AtLocation, AtLocation), + wait_req_scan(St); {error,E} -> epp_reply(Pid, {error,E}) end. @@ -385,19 +560,20 @@ enter_file(NewName, Inc, From, St) -> %% enter_file2(File, FullName, From, EppState, AtLocation) -> EppState. %% Set epp to use this file and "enter" it. -enter_file2(NewF, Pname, From, St, AtLocation) -> +enter_file2(NewF, Pname, From, St0, AtLocation) -> Loc = start_loc(AtLocation), enter_file_reply(From, Pname, Loc, AtLocation), - Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St#epp.macs), + Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St0#epp.macs), %% update the head of the include path to be the directory of the new %% source file, so that an included file can always include other files %% relative to its current location (this is also how C does it); note %% that the directory of the parent source file (the previous head of %% the path) must be dropped, otherwise the path used within the current %% file will depend on the order of file inclusions in the parent files - Path = [filename:dirname(Pname) | tl(St#epp.path)], + Path = [filename:dirname(Pname) | tl(St0#epp.path)], + _ = set_encoding(NewF), #epp{file=NewF,location=Loc,name=Pname,delta=0, - sstk=[St|St#epp.sstk],path=Path,macs=Ms}. + sstk=[St0|St0#epp.sstk],path=Path,macs=Ms}. enter_file_reply(From, Name, Location, AtLocation) -> Attr = loc_attr(AtLocation), @@ -456,7 +632,7 @@ leave_file(From, St) -> %% scan_toks(Tokens, From, EppState) scan_toks(From, St) -> - case io:scan_erl_form(St#epp.file, '', St#epp.location) of + case io:scan_erl_form(St#epp.file, '', St#epp.location, [unicode]) of {ok,Toks,Cl} -> scan_toks(Toks, From, St#epp{location=Cl}); {error,E,Cl} -> @@ -830,7 +1006,7 @@ new_location(Ln, {Le,_}, {Lf,_}) -> %% nested conditionals and repeated 'else's. skip_toks(From, St, [I|Sis]) -> - case io:scan_erl_form(St#epp.file, '', St#epp.location) of + case io:scan_erl_form(St#epp.file, '', St#epp.location, [unicode]) of {ok,[{'-',_Lh},{atom,_Li,ifdef}|_Toks],Cl} -> skip_toks(From, St#epp{location=Cl}, [ifdef,I|Sis]); {ok,[{'-',_Lh},{atom,_Li,ifndef}|_Toks],Cl} -> @@ -1094,6 +1270,7 @@ expand_arg([], Ts, L, Rest, Bs) -> %%% tokenized would yield the token list Ts. %% erl_scan:token_info(T, text) is not backward compatible with this. +%% Note that escaped characters will be replaced by themselves. token_src({dot, _}) -> "."; token_src({X, _}) when is_atom(X) -> @@ -1101,16 +1278,16 @@ token_src({X, _}) when is_atom(X) -> token_src({var, _, X}) -> atom_to_list(X); token_src({char,_,C}) -> - io_lib:write_char(C); + io_lib:write_unicode_char(C); token_src({string, _, X}) -> - lists:flatten(io_lib:format("~p", [X])); + io_lib:write_unicode_string(X); token_src({_, _, X}) -> - lists:flatten(io_lib:format("~w", [X])). + io_lib:format("~w", [X]). stringify1([]) -> []; stringify1([T | Tokens]) -> - [io_lib:format(" ~s", [token_src(T)]) | stringify1(Tokens)]. + [io_lib:format(" ~ts", [token_src(T)]) | stringify1(Tokens)]. stringify(Ts, L) -> [$\s | S] = lists:flatten(stringify1(Ts)), diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index 95ba6b1096..8471ae6b64 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -227,13 +227,6 @@ expr({bc,_,E,Qs}, Bs, Lf, Ef, RBs) -> expr({tuple,_,Es}, Bs0, Lf, Ef, RBs) -> {Vs,Bs} = expr_list(Es, Bs0, Lf, Ef), ret_expr(list_to_tuple(Vs), Bs, RBs); -expr({record_field,_,_,_}=Mod, Bs, _Lf, _Ef, RBs) -> - case expand_module_name(Mod, Bs) of - {atom,_,A} -> - ret_expr(A, Bs, RBs); %% This is the "x.y" syntax - _ -> - erlang:raise(error, {badexpr, '.'}, stacktrace()) - end; expr({record_field,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) -> erlang:raise(error, {undef_record,Name}, stacktrace()); expr({record_index,_,Name,_}, _Bs, _Lf, _Ef, _RBs) -> @@ -332,8 +325,7 @@ expr({call,L1,{remote,L2,{record_field,_,{atom,_,''},{atom,_,qlc}=Mod}, Bs, Lf, Ef, RBs) when length(As0) =< 1 -> expr({call,L1,{remote,L2,Mod,Func},As}, Bs, Lf, Ef, RBs); expr({call,_,{remote,_,Mod,Func},As0}, Bs0, Lf, Ef, RBs) -> - Mod1 = expand_module_name(Mod, Bs0), - {value,M,Bs1} = expr(Mod1, Bs0, Lf, Ef, none), + {value,M,Bs1} = expr(Mod, Bs0, Lf, Ef, none), {value,F,Bs2} = expr(Func, Bs0, Lf, Ef, none), {As,Bs3} = expr_list(As0, merge_bindings(Bs1, Bs2), Lf, Ef), %% M could be a parameterized module (not an atom). @@ -1210,41 +1202,6 @@ ret_expr(_Old, New) -> line(Expr) -> element(2, Expr). -%% In syntax trees, module/package names are atoms or lists of atoms. - -expand_module_name({atom,L,A} = M, Bs) -> - case binding({module,A}, Bs) of - {value, A1} -> - {atom,L,A1}; - unbound -> - case packages:is_segmented(A) of - true -> - M; - false -> -%%% P = case binding({module,'$package'}, Bs) of -%%% {value, P1} -> P1; -%%% unbound -> "" -%%% end, -%%% A1 = list_to_atom(packages:concat(P, A)), -%%% {atom,L,list_to_atom(A1)} - {atom,L,A} - end - end; -expand_module_name(M, _) -> - case erl_parse:package_segments(M) of - error -> - M; - M1 -> - L = element(2,M), - Mod = packages:concat(M1), - case packages:is_valid(Mod) of - true -> - {atom,L,list_to_atom(Mod)}; - false -> - erlang:raise(error, {bad_module_name, Mod}, stacktrace()) - end - end. - %% {?MODULE,expr,3} is still the stacktrace, despite the %% fact that expr() now takes two, three or four arguments... stacktrace() -> [{?MODULE,expr,3}]. diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl index 85defacc43..d05f630d8e 100644 --- a/lib/stdlib/src/erl_expand_records.erl +++ b/lib/stdlib/src/erl_expand_records.erl @@ -135,8 +135,6 @@ pattern({tuple,Line,Ps}, St0) -> %%pattern({struct,Line,Tag,Ps}, St0) -> %% {TPs,TPsvs,St1} = pattern_list(Ps, St0), %% {{struct,Line,Tag,TPs},TPsvs,St1}; -pattern({record_field,_,_,_}=M, St) -> - {M,St}; % must be a package name pattern({record_index,Line,Name,Field}, St) -> {index_expr(Line, Field, Name, record_fields(Name, St)),St}; pattern({record,Line,Name,Pfs}, St0) -> @@ -306,8 +304,6 @@ expr({tuple,Line,Es0}, St0) -> %%expr({struct,Line,Tag,Es0}, Vs, St0) -> %% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0), %% {{struct,Line,Tag,Es1},Esvs,Esus,St1}; -expr({record_field,_,_,_}=M, St) -> - {M,St}; % must be a package name expr({record_index,Line,Name,F}, St) -> I = index_expr(Line, F, Name, record_fields(Name, St)), expr(I, St); @@ -375,9 +371,6 @@ expr({call,Line,{atom,_La,N}=Atom,As0}, St0) -> end end end; -expr({call,Line,{record_field,_,_,_}=M,As0}, St0) -> - {As,St1} = expr_list(As0, St0), - {{call,Line,M,As},St1}; expr({call,Line,{remote,Lr,M,F},As0}, St0) -> {[M1,F1 | As1],St1} = expr_list([M,F | As0], St0), {{call,Line,{remote,Lr,M1,F1},As1},St1}; diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 1e5f962375..d24e2fff44 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -94,12 +94,10 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> %% the other function collections contain {Function, Arity}. -record(lint, {state=start :: 'start' | 'attribute' | 'function', module=[], %Module - package="", %Module package extends=[], %Extends behaviour=[], %Behaviour exports=gb_sets:empty() :: gb_set(), %Exports imports=[], %Imports - mod_imports=dict:new() :: dict(), %Module Imports compile=[], %Compile flags records=dict:new() :: dict(), %Record definitions locals=gb_sets:empty() :: gb_set(), %All defined functions (prescanned) @@ -539,7 +537,6 @@ start(File, Opts) -> end, #lint{state = start, exports = gb_sets:from_list([{module_info,0},{module_info,1}]), - mod_imports = dict:from_list([{erlang,erlang}]), compile = Opts, %% Internal pseudo-functions must appear as defined/reached. defined = gb_sets:from_list(pseudolocals()), @@ -681,8 +678,8 @@ form(Form, #lint{state=State}=St) -> %% start_state(Form, State) -> State' -start_state({attribute,L,module,{M,Ps}}, St) -> - St1 = set_module(M, L, St), +start_state({attribute,_,module,{M,Ps}}, St0) -> + St1 = St0#lint{module=M}, Arity = length(Ps), Ps1 = if is_atom(St1#lint.extends) -> ['BASE', 'THIS' | Ps]; @@ -693,23 +690,13 @@ start_state({attribute,L,module,{M,Ps}}, St) -> St2 = add_instance(Arity, St1), St3 = ensure_new(Arity, St2), St3#lint{state=attribute, extends=[], global_vt=Vt}; -start_state({attribute,L,module,M}, St) -> - St1 = set_module(M, L, St), +start_state({attribute,_,module,M}, St0) -> + St1 = St0#lint{module=M}, St1#lint{state=attribute, extends=[]}; start_state(Form, St) -> St1 = add_error(element(2, Form), undefined_module, St), attribute_state(Form, St1#lint{state=attribute, extends=[]}). -set_module(M, L, St) -> - M1 = package_to_string(M), - case packages:is_valid(M1) of - true -> - St#lint{module=list_to_atom(M1), - package=packages:strip_last(M1)}; - false -> - add_error(L, {bad_module_name, M1}, St) - end. - ensure_new(Arity, St) -> case St#lint.new of true -> @@ -1007,9 +994,9 @@ check_imports(Forms, St0) -> true -> Usage = St0#lint.usage, Unused = ordsets:subtract(St0#lint.imports, Usage#usage.imported), - Imports = [{{FA,list_to_atom(package_to_string(Mod))},L} - || {attribute,L,import,{Mod,Fs}} <- Forms, - FA <- lists:usort(Fs)], + Imports = [{{FA,Mod},L} || + {attribute,L,import,{Mod,Fs}} <- Forms, + FA <- lists:usort(Fs)], Bad = [{FM,L} || FM <- Unused, {FM2,L} <- Imports, FM =:= FM2], func_line_warning(unused_import, Bad, St0) end. @@ -1222,73 +1209,46 @@ export_type(Line, ETs, #lint{usage = Usage, exp_types = ETs0} = St0) -> -spec import(line(), import(), lint_state()) -> lint_state(). import(Line, {Mod,Fs}, St) -> - Mod1 = package_to_string(Mod), - case packages:is_valid(Mod1) of - true -> - Mfs = ordsets:from_list(Fs), - case check_imports(Line, Mfs, St#lint.imports) of - [] -> - St#lint{imports=add_imports(list_to_atom(Mod1), Mfs, - St#lint.imports)}; - Efs -> - {Err, St1} = - foldl(fun ({bif,{F,A},_}, {Err,St0}) -> - %% BifClash - import directive - Warn = is_warn_enabled(bif_clash, St0) - and (not bif_clash_specifically_disabled(St0,{F,A})), - AutoImpSup = is_autoimport_suppressed(St0#lint.no_auto,{F,A}), - OldBif = erl_internal:old_bif(F,A), - {Err,if - Warn and (not AutoImpSup) and OldBif -> - add_error - (Line, - {redefine_old_bif_import, {F,A}}, - St0); - Warn and (not AutoImpSup) -> - add_warning - (Line, - {redefine_bif_import, {F,A}}, - St0); - true -> - St0 - end}; - (Ef, {_Err,St0}) -> - {true,add_error(Line, - {redefine_import,Ef}, - St0)} - end, - {false,St}, Efs), - if - not Err -> - St1#lint{imports= - add_imports(list_to_atom(Mod1), Mfs, + Mfs = ordsets:from_list(Fs), + case check_imports(Line, Mfs, St#lint.imports) of + [] -> + St#lint{imports=add_imports(Mod, Mfs, + St#lint.imports)}; + Efs -> + {Err, St1} = + foldl(fun ({bif,{F,A},_}, {Err,St0}) -> + %% BifClash - import directive + Warn = is_warn_enabled(bif_clash, St0) andalso + (not bif_clash_specifically_disabled(St0,{F,A})), + AutoImpSup = is_autoimport_suppressed(St0#lint.no_auto,{F,A}), + OldBif = erl_internal:old_bif(F,A), + {Err,if + Warn and (not AutoImpSup) and OldBif -> + add_error + (Line, + {redefine_old_bif_import, {F,A}}, + St0); + Warn and (not AutoImpSup) -> + add_warning + (Line, + {redefine_bif_import, {F,A}}, + St0); + true -> + St0 + end}; + (Ef, {_Err,St0}) -> + {true,add_error(Line, + {redefine_import,Ef}, + St0)} + end, + {false,St}, Efs), + if + not Err -> + St1#lint{imports=add_imports(Mod, Mfs, St#lint.imports)}; - true -> - St1 - end - end; - false -> - add_error(Line, {bad_module_name, Mod1}, St) - end; -import(Line, Mod, St) -> - Mod1 = package_to_string(Mod), - case packages:is_valid(Mod1) of - true -> - Key = list_to_atom(packages:last(Mod1)), - Imps = St#lint.mod_imports, -%%% case dict:is_key(Key, Imps) of -%%% true -> -%%% M = packages:last(Mod1), -%%% P = packages:strip_last(Mod1), -%%% add_error(Line, {redefine_mod_import, M, P}, St); -%%% false -> -%%% St#lint{mod_imports = -%%% dict:store(Key, list_to_atom(Mod1), Imps)} -%%% end; - St#lint{mod_imports = dict:store(Key, list_to_atom(Mod1), - Imps)}; - false -> - add_error(Line, {bad_module_name, Mod1}, St) + true -> + St1 + end end. check_imports(_Line, Fs, Is) -> @@ -1463,13 +1423,6 @@ pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) -> pattern_field(Field, Name, Dfs, St1) end), {Vt1,[],St1}; -pattern({record_field,Line,_,_}=M, _Vt, _Old, _Bvt, St0) -> - case expand_package(M, St0) of - {error, St1} -> - {[],[],add_error(Line, illegal_expr, St1)}; - {_, St1} -> - {[],[],St1} - end; pattern({record,Line,Name,Pfs}, Vt, Old, Bvt, St) -> case dict:find(Name, St#lint.records) of {ok,{_Line,Fields}} -> @@ -1851,13 +1804,6 @@ gexpr({tuple,_Line,Es}, Vt, St) -> gexpr({record_index,Line,Name,Field}, _Vt, St) -> check_record(Line, Name, St, fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end ); -gexpr({record_field,Line,_,_}=M, _Vt, St0) -> - case expand_package(M, St0) of - {error, St1} -> - {[],add_error(Line, illegal_expr, St1)}; - {_, St1} -> - {[], St1} - end; gexpr({record_field,Line,Rec,Name,Field}, Vt, St0) -> {Rvt,St1} = gexpr(Rec, Vt, St0), {Fvt,St2} = check_record(Line, Name, St1, @@ -1996,8 +1942,6 @@ is_gexpr({tuple,_L,Es}, RDs) -> is_gexpr_list(Es, RDs); %% is_gexpr_list(Es, RDs); is_gexpr({record_index,_L,_Name,Field}, RDs) -> is_gexpr(Field, RDs); -is_gexpr({record_field,_L,_,_}=M, _RDs) -> - erl_parse:package_segments(M) =/= error; is_gexpr({record_field,_L,Rec,_Name,Field}, RDs) -> is_gexpr_list([Rec,Field], RDs); is_gexpr({record,L,Name,Inits}, RDs) -> @@ -2086,13 +2030,6 @@ expr({record,Line,Name,Inits}, Vt, St) -> fun (Dfs, St1) -> init_fields(Inits, Line, Name, Dfs, Vt, St1) end); -expr({record_field,Line,_,_}=M, _Vt, St0) -> - case expand_package(M, St0) of - {error, St1} -> - {[],add_error(Line, illegal_expr, St1)}; - {_, St1} -> - {[], St1} - end; expr({record_field,Line,Rec,Name,Field}, Vt, St0) -> {Rvt,St1} = record_expr(Line, Rec, Vt, St0), {Fvt,St2} = check_record(Line, Name, St1, @@ -2163,20 +2100,14 @@ expr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]}, expr({call,Line,{atom,Lf,is_record},[E,A]}, Vt, St0); expr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,is_record}]},As}, Vt, St) -> expr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,is_record}},As}, Vt, St); +expr({call,Line,{remote,_Lr,{atom,_Lm,M},{atom,Lf,F}},As}, Vt, St0) -> + St1 = keyword_warning(Lf, F, St0), + St2 = check_remote_function(Line, M, F, As, St1), + expr_list(As, Vt, St2); expr({call,Line,{remote,_Lr,M,F},As}, Vt, St0) -> - case expand_package(M, St0) of - {error, _} -> - expr_list([M,F|As], Vt, St0); - {{atom,_La,M1}, St1} -> - case F of - {atom,Lf,F1} -> - St2 = keyword_warning(Lf, F1, St1), - St3 = check_remote_function(Line, M1, F1, As, St2), - expr_list(As, Vt, St3); - _ -> - expr_list([F|As], Vt, St1) - end - end; + St1 = keyword_warning(Line, M, St0), + St2 = keyword_warning(Line, F, St1), + expr_list([M,F|As], Vt, St2); expr({call,Line,{atom,La,F},As}, Vt, St0) -> St1 = keyword_warning(La, F, St0), {Asvt,St2} = expr_list(As, Vt, St1), @@ -2232,13 +2163,6 @@ expr({call,Line,{atom,La,F},As}, Vt, St0) -> end end} end; -expr({call,Line,{record_field,_,_,_}=F,As}, Vt, St0) -> - case expand_package(F, St0) of - {error, _} -> - expr_list([F|As], Vt, St0); - {A, St1} -> - expr({call,Line,A,As}, Vt, St1) - end; expr({call,Line,F,As}, Vt, St0) -> St = warn_invalid_call(Line,F,St0), expr_list([F|As], Vt, St); %They see the same variables @@ -3618,6 +3542,10 @@ extract_sequence(4, [$t, $c | Fmt], Need) -> extract_sequence(5, [$c|Fmt], Need); extract_sequence(4, [$t, $s | Fmt], Need) -> extract_sequence(5, [$s|Fmt], Need); +extract_sequence(4, [$t, $p | Fmt], Need) -> + extract_sequence(5, [$p|Fmt], Need); +extract_sequence(4, [$t, $P | Fmt], Need) -> + extract_sequence(5, [$P|Fmt], Need); extract_sequence(4, [$t, C | _Fmt], _Need) -> {error,"invalid control ~t" ++ [C]}; extract_sequence(4, Fmt, Need) -> @@ -3654,49 +3582,6 @@ control_type($n, Need) -> Need; control_type($i, Need) -> [term|Need]; control_type(_C, _Need) -> error. -%% In syntax trees, module/package names are atoms or lists of atoms. - -package_to_string(A) when is_atom(A) -> atom_to_list(A); -package_to_string(L) when is_list(L) -> packages:concat(L). - -expand_package({atom,L,A} = M, St0) -> - St1 = keyword_warning(L, A, St0), - case dict:find(A, St1#lint.mod_imports) of - {ok, A1} -> - {{atom,L,A1}, St1}; - error -> - Name = atom_to_list(A), - case packages:is_valid(Name) of - true -> - case packages:is_segmented(Name) of - true -> - {M, St1}; - false -> - M1 = packages:concat(St1#lint.package, - Name), - {{atom,L,list_to_atom(M1)}, St1} - end; - false -> - St2 = add_error(L, {bad_module_name, Name}, St1), - {error, St2} - end - end; -expand_package(M, St0) -> - L = element(2, M), - case erl_parse:package_segments(M) of - error -> - {error, St0}; - M1 -> - Name = package_to_string(M1), - case packages:is_valid(Name) of - true -> - {{atom,L,list_to_atom(Name)}, St0}; - false -> - St1 = add_error(L, {bad_module_name, Name}, St0), - {error, St1} - end - end. - %% Prebuild set of local functions (to override auto-import) local_functions(Forms) -> diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 928c10f7f2..002abc11e8 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -26,7 +26,7 @@ attribute attr_val function function_clauses function_clause clause_args clause_guard clause_body expr expr_100 expr_150 expr_160 expr_200 expr_300 expr_400 expr_500 -expr_600 expr_700 expr_800 expr_900 +expr_600 expr_700 expr_800 expr_max list tail list_comprehension lc_expr lc_exprs @@ -253,15 +253,9 @@ expr_700 -> function_call : '$1'. expr_700 -> record_expr : '$1'. expr_700 -> expr_800 : '$1'. -expr_800 -> expr_900 ':' expr_max : +expr_800 -> expr_max ':' expr_max : {remote,?line('$2'),'$1','$3'}. -expr_800 -> expr_900 : '$1'. - -expr_900 -> '.' atom : - {record_field,?line('$1'),{atom,?line('$1'),''},'$2'}. -expr_900 -> expr_900 '.' atom : - {record_field,?line('$2'),'$1','$3'}. -expr_900 -> expr_max : '$1'. +expr_800 -> expr_max : '$1'. expr_max -> var : '$1'. expr_max -> atomic : '$1'. @@ -510,7 +504,7 @@ Erlang code. -export([parse_form/1,parse_exprs/1,parse_term/1]). -export([normalise/1,abstract/1,tokens/1,tokens/2]). --export([abstract/2, package_segments/1]). +-export([abstract/2]). -export([inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]). -export([set_line/2,get_attribute/2,get_attributes/1]). @@ -679,20 +673,6 @@ build_attribute({atom,La,module}, Val) -> {attribute,La,module,Module}; [{atom,_Lm,Module},ExpList] -> {attribute,La,module,{Module,var_list(ExpList)}}; - [Name] -> - case package_segments(Name) of - error -> - error_bad_decl(La, module); - Module -> - {attribute,La,module,Module} - end; - [Name,ExpList] -> - case package_segments(Name) of - error -> - error_bad_decl(La, module); - Module -> - {attribute,La,module,{Module,var_list(ExpList)}} - end; _Other -> error_bad_decl(La, module) end; @@ -704,22 +684,8 @@ build_attribute({atom,La,export}, Val) -> end; build_attribute({atom,La,import}, Val) -> case Val of - [Name] -> - case package_segments(Name) of - error -> - error_bad_decl(La, import); - Module -> - {attribute,La,import,Module} - end; [{atom,_Lm,Mod},ImpList] -> {attribute,La,import,{Mod,farity_list(ImpList)}}; - [Name, ImpList] -> - case package_segments(Name) of - error -> - error_bad_decl(La, import); - Module -> - {attribute,La,import,{Module,farity_list(ImpList)}} - end; _Other -> error_bad_decl(La, import) end; build_attribute({atom,La,record}, Val) -> @@ -820,18 +786,6 @@ term(Expr) -> catch _:_R -> ret_err(?line(Expr), "bad attribute") end. -package_segments(Name) -> - package_segments(Name, [], []). - -package_segments({record_field, _, F1, F2}, Fs, As) -> - package_segments(F1, [F2 | Fs], As); -package_segments({atom, _, A}, [F | Fs], As) -> - package_segments(F, Fs, [A | As]); -package_segments({atom, _, A}, [], As) -> - lists:reverse([A | As]); -package_segments(_, _, _) -> - error. - %% build_function([Clause]) -> {function,Line,Name,Arity,[Clause]} build_function(Cs) -> @@ -900,12 +854,6 @@ normalise({cons,_,Head,Tail}) -> [normalise(Head)|normalise(Tail)]; normalise({tuple,_,Args}) -> list_to_tuple(normalise_list(Args)); -%% Atom dot-notation, as in 'foo.bar.baz' -normalise({record_field,_,_,_}=A) -> - case package_segments(A) of - error -> erlang:error({badarg, A}); - As -> list_to_atom(packages:concat(As)) - end; %% Special case for unary +/-. normalise({op,_,'+',{char,_,I}}) -> I; normalise({op,_,'+',{integer,_,I}}) -> I; @@ -923,73 +871,77 @@ normalise_list([]) -> -spec abstract(Data) -> AbsTerm when Data :: term(), AbsTerm :: abstract_expr(). -abstract(T) when is_integer(T) -> {integer,0,T}; -abstract(T) when is_float(T) -> {float,0,T}; -abstract(T) when is_atom(T) -> {atom,0,T}; -abstract([]) -> {nil,0}; -abstract(B) when is_bitstring(B) -> - {bin, 0, [abstract_byte(Byte, 0) || Byte <- bitstring_to_list(B)]}; -abstract([C|T]) when is_integer(C), 0 =< C, C < 256 -> - abstract_string(T, [C]); -abstract([H|T]) -> - {cons,0,abstract(H),abstract(T)}; -abstract(Tuple) when is_tuple(Tuple) -> - {tuple,0,abstract_list(tuple_to_list(Tuple))}. - -abstract_string([C|T], String) when is_integer(C), 0 =< C, C < 256 -> - abstract_string(T, [C|String]); -abstract_string([], String) -> - {string, 0, lists:reverse(String)}; -abstract_string(T, String) -> - not_string(String, abstract(T)). - -not_string([C|T], Result) -> - not_string(T, {cons, 0, {integer, 0, C}, Result}); -not_string([], Result) -> +abstract(T) -> + abstract(T, 0, epp:default_encoding()). + +%%% abstract/2 takes line and encoding options +-spec abstract(Data, Options) -> AbsTerm when + Data :: term(), + Options :: Line | [Option], + Option :: {line, Line} | {encoding, Encoding}, + Encoding :: latin1 | unicode | utf8, + Line :: erl_scan:line(), + AbsTerm :: abstract_expr(). + +abstract(T, Line) when is_integer(Line) -> + abstract(T, Line, epp:default_encoding()); +abstract(T, Options) when is_list(Options) -> + Line = proplists:get_value(line, Options, 0), + Encoding = proplists:get_value(encoding, Options,epp:default_encoding()), + abstract(T, Line, Encoding). + +-define(UNICODE(C), + (C >= 0 andalso C < 16#D800 orelse + C > 16#DFFF andalso C < 16#FFFE orelse + C > 16#FFFF andalso C =< 16#10FFFF)). + +abstract(T, L, _E) when is_integer(T) -> {integer,L,T}; +abstract(T, L, _E) when is_float(T) -> {float,L,T}; +abstract(T, L, _E) when is_atom(T) -> {atom,L,T}; +abstract([], L, _E) -> {nil,L}; +abstract(B, L, _E) when is_bitstring(B) -> + {bin, L, [abstract_byte(Byte, L) || Byte <- bitstring_to_list(B)]}; +abstract([C|T], L, unicode=E) when ?UNICODE(C) -> + abstract_unicode_string(T, [C], L, E); +abstract([C|T], L, utf8=E) when ?UNICODE(C) -> + abstract_unicode_string(T, [C], L, E); +abstract([C|T], L, latin1=E) when is_integer(C), 0 =< C, C < 256 -> + abstract_string(T, [C], L, E); +abstract([H|T], L, E) -> + {cons,L,abstract(H, L, E),abstract(T, L, E)}; +abstract(Tuple, L, E) when is_tuple(Tuple) -> + {tuple,L,abstract_list(tuple_to_list(Tuple), L, E)}. + +abstract_string([C|T], String, L, E) when is_integer(C), 0 =< C, C < 256 -> + abstract_string(T, [C|String], L, E); +abstract_string([], String, L, _E) -> + {string, L, lists:reverse(String)}; +abstract_string(T, String, L, E) -> + not_string(String, abstract(T, L, E), L, E). + +abstract_unicode_string([C|T], String, L, E) when ?UNICODE(C) -> + abstract_unicode_string(T, [C|String], L, E); +abstract_unicode_string([], String, L, _E) -> + {string, L, lists:reverse(String)}; +abstract_unicode_string(T, String, L, E) -> + not_string(String, abstract(T, L, E), L, E). + +not_string([C|T], Result, L, E) -> + not_string(T, {cons, L, {integer, L, C}, Result}, L, E); +not_string([], Result, _L, _E) -> Result. -abstract_list([H|T]) -> - [abstract(H)|abstract_list(T)]; -abstract_list([]) -> +abstract_list([H|T], L, E) -> + [abstract(H, L, E)|abstract_list(T, L, E)]; +abstract_list([], _L, _E) -> []. -abstract_byte(Byte, Line) when is_integer(Byte) -> - {bin_element, Line, {integer, Line, Byte}, default, default}; -abstract_byte(Bits, Line) -> +abstract_byte(Byte, L) when is_integer(Byte) -> + {bin_element, L, {integer, L, Byte}, default, default}; +abstract_byte(Bits, L) -> Sz = bit_size(Bits), <<Val:Sz>> = Bits, - {bin_element, Line, {integer, Line, Val}, {integer, Line, Sz}, default}. - -%%% abstract/2 keeps the line number -abstract(T, Line) when is_integer(T) -> {integer,Line,T}; -abstract(T, Line) when is_float(T) -> {float,Line,T}; -abstract(T, Line) when is_atom(T) -> {atom,Line,T}; -abstract([], Line) -> {nil,Line}; -abstract(B, Line) when is_bitstring(B) -> - {bin, Line, [abstract_byte(Byte, Line) || Byte <- bitstring_to_list(B)]}; -abstract([C|T], Line) when is_integer(C), 0 =< C, C < 256 -> - abstract_string(T, [C], Line); -abstract([H|T], Line) -> - {cons,Line,abstract(H, Line),abstract(T, Line)}; -abstract(Tuple, Line) when is_tuple(Tuple) -> - {tuple,Line,abstract_list(tuple_to_list(Tuple), Line)}. - -abstract_string([C|T], String, Line) when is_integer(C), 0 =< C, C < 256 -> - abstract_string(T, [C|String], Line); -abstract_string([], String, Line) -> - {string, Line, lists:reverse(String)}; -abstract_string(T, String, Line) -> - not_string(String, abstract(T, Line), Line). - -not_string([C|T], Result, Line) -> - not_string(T, {cons, Line, {integer, Line, C}, Result}, Line); -not_string([], Result, _Line) -> - Result. - -abstract_list([H|T], Line) -> - [abstract(H, Line)|abstract_list(T, Line)]; -abstract_list([], _Line) -> - []. + {bin_element, L, {integer, L, Val}, {integer, L, Sz}, default}. %% Generate a list of tokens representing the abstract term. @@ -1079,9 +1031,9 @@ preop_prec('#') -> {700,800}. func_prec() -> {800,700}. --spec max_prec() -> 1000. +-spec max_prec() -> 900. -max_prec() -> 1000. +max_prec() -> 900. %%% [Experimental]. The parser just copies the attributes of the %%% scanner tokens to the abstract format. This design decision has diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 6b5aa951cf..0383ce6839 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -26,7 +26,7 @@ guard/1,guard/2,exprs/1,exprs/2,exprs/3,expr/1,expr/2,expr/3,expr/4]). -import(lists, [append/1,foldr/3,mapfoldl/3,reverse/1,reverse/2]). --import(io_lib, [write/1,format/2,write_char/1,write_string/1]). +-import(io_lib, [write/1,format/2]). -import(erl_parse, [inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]). -define(MAXLINE, 72). @@ -36,7 +36,15 @@ CurrentIndentation :: integer(), CurrentPrecedence :: non_neg_integer(), HookFunction :: hook_function()) -> - io_lib:chars())). + io_lib:chars())). + +-type(option() :: {hook, hook_function()} + | {encoding, latin1 | unicode | utf8}). +-type(options() :: hook_function() | [option()]). + +-record(pp, {string_fun, char_fun}). + +-record(options, {hook, encoding, opts}). %%% %%% Exported functions @@ -48,12 +56,12 @@ form(Thing) -> form(Thing, none). --spec(form(Form, HookFunction) -> io_lib:chars() when +-spec(form(Form, Options) -> io_lib:chars() when Form :: erl_parse:abstract_form(), - HookFunction :: hook_function()). + Options :: options()). -form(Thing, Hook) -> - frmt(lform(Thing, Hook)). +form(Thing, Options) -> + frmt(lform(Thing, options(Options)), state(Options)). -spec(attribute(Attribute) -> io_lib:chars() when Attribute :: erl_parse:abstract_form()). @@ -61,12 +69,12 @@ form(Thing, Hook) -> attribute(Thing) -> attribute(Thing, none). --spec(attribute(Attribute, HookFunction) -> io_lib:chars() when +-spec(attribute(Attribute, Options) -> io_lib:chars() when Attribute :: erl_parse:abstract_form(), - HookFunction :: hook_function()). + Options :: options()). -attribute(Thing, Hook) -> - frmt(lattribute(Thing, Hook)). +attribute(Thing, Options) -> + frmt(lattribute(Thing, options(Options)), state(Options)). -spec(function(Function) -> io_lib:chars() when Function :: erl_parse:abstract_form()). @@ -74,18 +82,18 @@ attribute(Thing, Hook) -> function(F) -> function(F, none). --spec(function(Function, HookFunction) -> io_lib:chars() when +-spec(function(Function, Options) -> io_lib:chars() when Function :: erl_parse:abstract_form(), - HookFunction :: hook_function()). + Options :: options()). -function(F, Hook) -> - frmt(lfunction(F, Hook)). +function(F, Options) -> + frmt(lfunction(F, options(Options)), state(Options)). rule(R) -> rule(R, none). -rule(R, Hook) -> - frmt(lrule(R, Hook)). +rule(R, Options) -> + frmt(lrule(R, options(Options)), state(Options)). -spec(guard(Guard) -> io_lib:chars() when Guard :: [erl_parse:abstract_expr()]). @@ -93,12 +101,12 @@ rule(R, Hook) -> guard(Gs) -> guard(Gs, none). --spec(guard(Guard, HookFunction) -> io_lib:chars() when +-spec(guard(Guard, Options) -> io_lib:chars() when Guard :: [erl_parse:abstract_expr()], - HookFunction :: hook_function()). + Options :: options()). -guard(Gs, Hook) -> - frmt(lguard(Gs, Hook)). +guard(Gs, Options) -> + frmt(lguard(Gs, options(Options)), state(Options)). -spec(exprs(Expressions) -> io_lib:chars() when Expressions :: [erl_parse:abstract_expr()]). @@ -106,99 +114,129 @@ guard(Gs, Hook) -> exprs(Es) -> exprs(Es, 0, none). --spec(exprs(Expressions, HookFunction) -> io_lib:chars() when +-spec(exprs(Expressions, Options) -> io_lib:chars() when Expressions :: [erl_parse:abstract_expr()], - HookFunction :: hook_function()). + Options :: options()). -exprs(Es, Hook) -> - exprs(Es, 0, Hook). +exprs(Es, Options) -> + exprs(Es, 0, Options). --spec(exprs(Expressions, Indent, HookFunction) -> io_lib:chars() when +-spec(exprs(Expressions, Indent, Options) -> io_lib:chars() when Expressions :: [erl_parse:abstract_expr()], Indent :: integer(), - HookFunction :: hook_function()). + Options :: options()). -exprs(Es, I, Hook) -> - frmt({seq,[],[],[$,],lexprs(Es, Hook)}, I). +exprs(Es, I, Options) -> + frmt({seq,[],[],[$,],lexprs(Es, options(Options))}, I, state(Options)). -spec(expr(Expression) -> io_lib:chars() when Expression :: erl_parse:abstract_expr()). expr(E) -> - frmt(lexpr(E, 0, none)). + frmt(lexpr(E, 0, options(none)), state(none)). --spec(expr(Expression, HookFunction) -> io_lib:chars() when +-spec(expr(Expression, Options) -> io_lib:chars() when Expression :: erl_parse:abstract_expr(), - HookFunction :: hook_function()). + Options :: options()). -expr(E, Hook) -> - frmt(lexpr(E, 0, Hook)). +expr(E, Options) -> + frmt(lexpr(E, 0, options(Options)), state(Options)). --spec(expr(Expression, Indent, HookFunction) -> io_lib:chars() when +-spec(expr(Expression, Indent, Options) -> io_lib:chars() when Expression :: erl_parse:abstract_expr(), Indent :: integer(), - HookFunction :: hook_function()). + Options :: options()). -expr(E, I, Hook) -> - frmt(lexpr(E, 0, Hook), I). +expr(E, I, Options) -> + frmt(lexpr(E, 0, options(Options)), I, state(Options)). --spec(expr(Expression, Indent, Precedence, HookFunction) -> io_lib:chars() when +-spec(expr(Expression, Indent, Precedence, Options) -> io_lib:chars() when Expression :: erl_parse:abstract_expr(), Indent :: integer(), Precedence :: non_neg_integer(), - HookFunction :: hook_function()). + Options :: options()). -expr(E, I, P, Hook) -> - frmt(lexpr(E, P, Hook), I). +expr(E, I, P, Options) -> + frmt(lexpr(E, P, options(Options)), I, state(Options)). %%% %%% Local functions %%% -lform({attribute,Line,Name,Arg}, Hook) -> - lattribute({attribute,Line,Name,Arg}, Hook); -lform({function,Line,Name,Arity,Clauses}, Hook) -> - lfunction({function,Line,Name,Arity,Clauses}, Hook); -lform({rule,Line,Name,Arity,Clauses}, Hook) -> - lrule({rule,Line,Name,Arity,Clauses}, Hook); +options(Options) when is_list(Options) -> + Hook = proplists:get_value(hook, Options, none), + Encoding = encoding(Options), + #options{hook = Hook, encoding = Encoding, opts = Options}; +options(Hook) -> + #options{hook = Hook, encoding = encoding([]), opts = Hook}. + +state(Options) when is_list(Options) -> + case encoding(Options) of + latin1 -> state(); + unicode -> unicode_state() + end; +state(_Hook) -> + state(). + +state() -> + #pp{string_fun = fun io_lib:write_unicode_string_as_latin1/1, + char_fun = fun io_lib:write_unicode_char_as_latin1/1}. + +unicode_state() -> + #pp{string_fun = fun io_lib:write_unicode_string/1, + char_fun = fun io_lib:write_unicode_char/1}. + +encoding(Options) -> + case proplists:get_value(encoding, Options, epp:default_encoding()) of + latin1 -> latin1; + utf8 -> unicode; + unicode -> unicode + end. + +lform({attribute,Line,Name,Arg}, Opts) -> + lattribute({attribute,Line,Name,Arg}, Opts); +lform({function,Line,Name,Arity,Clauses}, Opts) -> + lfunction({function,Line,Name,Arity,Clauses}, Opts); +lform({rule,Line,Name,Arity,Clauses}, Opts) -> + lrule({rule,Line,Name,Arity,Clauses}, Opts); %% These are specials to make it easier for the compiler. -lform({error,E}, _Hook) -> +lform({error,E}, _Opts) -> leaf(format("~p\n", [{error,E}])); -lform({warning,W}, _Hook) -> +lform({warning,W}, _Opts) -> leaf(format("~p\n", [{warning,W}])); -lform({eof,_Line}, _Hook) -> +lform({eof,_Line}, _Opts) -> $\n. -lattribute({attribute,_Line,type,Type}, Hook) -> - [typeattr(type, Type, Hook),leaf(".\n")]; -lattribute({attribute,_Line,opaque,Type}, Hook) -> - [typeattr(opaque, Type, Hook),leaf(".\n")]; -lattribute({attribute,_Line,spec,Arg}, _Hook) -> +lattribute({attribute,_Line,type,Type}, Opts) -> + [typeattr(type, Type, Opts),leaf(".\n")]; +lattribute({attribute,_Line,opaque,Type}, Opts) -> + [typeattr(opaque, Type, Opts),leaf(".\n")]; +lattribute({attribute,_Line,spec,Arg}, _Opts) -> [specattr(Arg),leaf(".\n")]; -lattribute({attribute,_Line,Name,Arg}, Hook) -> - [lattribute(Name, Arg, Hook),leaf(".\n")]. +lattribute({attribute,_Line,Name,Arg}, Opts) -> + [lattribute(Name, Arg, Opts),leaf(".\n")]. -lattribute(module, {M,Vs}, _Hook) -> +lattribute(module, {M,Vs}, _Opts) -> attr("module",[{var,0,pname(M)}, foldr(fun(V, C) -> {cons,0,{var,0,V},C} end, {nil,0}, Vs)]); -lattribute(module, M, _Hook) -> +lattribute(module, M, _Opts) -> attr("module", [{var,0,pname(M)}]); -lattribute(export, Falist, _Hook) -> +lattribute(export, Falist, _Opts) -> call({var,0,"-export"}, [falist(Falist)], 0, none); -lattribute(import, Name, _Hook) when is_list(Name) -> +lattribute(import, Name, _Opts) when is_list(Name) -> attr("import", [{var,0,pname(Name)}]); -lattribute(import, {From,Falist}, _Hook) -> +lattribute(import, {From,Falist}, _Opts) -> attr("import",[{var,0,pname(From)},falist(Falist)]); -lattribute(file, {Name,Line}, _Hook) -> +lattribute(file, {Name,Line}, _Opts) -> attr("file", [{var,0,format("~p", [Name])},{integer,0,Line}]); -lattribute(record, {Name,Is}, Hook) -> +lattribute(record, {Name,Is}, Opts) -> Nl = leaf(format("-record(~w,", [Name])), - [{first,Nl,record_fields(Is, Hook)},$)]; -lattribute(Name, Arg, _Hook) -> - attr(write(Name), [erl_parse:abstract(Arg)]). + [{first,Nl,record_fields(Is, Opts)},$)]; +lattribute(Name, Arg, #options{encoding = Encoding}) -> + attr(write(Name), [erl_parse:abstract(Arg, [{encoding,Encoding}])]). -typeattr(Tag, {TypeName,Type,Args}, _Hook) -> +typeattr(Tag, {TypeName,Type,Args}, _Opts) -> {first,leaf("-"++atom_to_list(Tag)++" "), typed(call({atom,0,TypeName}, Args, 0, none), Type)}. @@ -293,7 +331,7 @@ guard_type(Before, Gs) -> Gl = {list,[{step,'when',expr_list(Gs, [$,], fun constraint/2, none)}]}, {list,[{step,Before,Gl}]}. -constraint({type,_Line,constraint,[Tag,As]}, _Hook) -> +constraint({type,_Line,constraint,[Tag,As]}, _Opts) -> simple_type(Tag, As). fun_type(Before, {type,_,'fun',[FType,Ret]}) -> @@ -333,231 +371,232 @@ falist([]) -> falist([{Name,Arity}|Falist]) -> {cons,0,{var,0,format("~w/~w", [Name,Arity])},falist(Falist)}. -lfunction({function,_Line,Name,_Arity,Cs}, Hook) -> - Cll = nl_clauses(fun (C, H) -> func_clause(Name, C, H) end, $;, Hook, Cs), +lfunction({function,_Line,Name,_Arity,Cs}, Opts) -> + Cll = nl_clauses(fun (C, H) -> func_clause(Name, C, H) end, $;, Opts, Cs), [Cll,leaf(".\n")]. -func_clause(Name, {clause,Line,Head,Guard,Body}, Hook) -> - Hl = call({atom,Line,Name}, Head, 0, Hook), - Gl = guard_when(Hl, Guard, Hook), - Bl = body(Body, Hook), +func_clause(Name, {clause,Line,Head,Guard,Body}, Opts) -> + Hl = call({atom,Line,Name}, Head, 0, Opts), + Gl = guard_when(Hl, Guard, Opts), + Bl = body(Body, Opts), {step,Gl,Bl}. -lrule({rule,_Line,Name,_Arity,Cs}, Hook) -> - Cll = nl_clauses(fun (C, H) -> rule_clause(Name, C, H) end, $;, Hook, Cs), +lrule({rule,_Line,Name,_Arity,Cs}, Opts) -> + Cll = nl_clauses(fun (C, H) -> rule_clause(Name, C, H) end, $;, Opts, Cs), [Cll,leaf(".\n")]. -rule_clause(Name, {clause,Line,Head,Guard,Body}, Hook) -> - Hl = call({atom,Line,Name}, Head, 0, Hook), - Gl = guard_when(Hl, Guard, Hook, leaf(" :-")), - Bl = rule_body(Body, Hook), +rule_clause(Name, {clause,Line,Head,Guard,Body}, Opts) -> + Hl = call({atom,Line,Name}, Head, 0, Opts), + Gl = guard_when(Hl, Guard, Opts, leaf(" :-")), + Bl = rule_body(Body, Opts), {step,Gl,Bl}. -rule_body(Es, Hook) -> - lc_quals(Es, Hook). +rule_body(Es, Opts) -> + lc_quals(Es, Opts). -guard_when(Before, Guard, Hook) -> - guard_when(Before, Guard, Hook, ' ->'). +guard_when(Before, Guard, Opts) -> + guard_when(Before, Guard, Opts, ' ->'). -guard_when(Before, Guard, Hook, After) -> - Gl = lguard(Guard, Hook), +guard_when(Before, Guard, Opts, After) -> + Gl = lguard(Guard, Opts), [{list,[{step,Before,Gl}]},After]. -lguard([E|Es], Hook) when is_list(E) -> - {list,[{step,'when',expr_list([E|Es], [$;], fun guard0/2, Hook)}]}; -lguard([E|Es], Hook) -> % before R6 - lguard([[E|Es]], Hook); +lguard([E|Es], Opts) when is_list(E) -> + {list,[{step,'when',expr_list([E|Es], [$;], fun guard0/2, Opts)}]}; +lguard([E|Es], Opts) -> % before R6 + lguard([[E|Es]], Opts); lguard([], _) -> []. -guard0(Es, Hook) -> - expr_list(Es, [$,], fun lexpr/2, Hook). +guard0(Es, Opts) -> + expr_list(Es, [$,], fun lexpr/2, Opts). -%% body(Before, Es, Hook) -> [Char]. +%% body(Before, Es, Opts) -> [Char]. -body([E], Hook) -> - lexpr(E, Hook); -body(Es, Hook) -> - {prefer_nl,[$,],lexprs(Es, Hook)}. +body([E], Opts) -> + lexpr(E, Opts); +body(Es, Opts) -> + {prefer_nl,[$,],lexprs(Es, Opts)}. -lexpr(E, Hook) -> - lexpr(E, 0, Hook). +lexpr(E, Opts) -> + lexpr(E, 0, Opts). lexpr({var,_,V}, _, _) when is_integer(V) -> %Special hack for Robert leaf(format("_~w", [V])); lexpr({var,_,V}, _, _) -> leaf(format("~s", [V])); -lexpr({char,_,C}, _, _) -> leaf(write_char(C)); +lexpr({char,_,C}, _, _) -> {char,C}; lexpr({integer,_,N}, _, _) -> leaf(write(N)); lexpr({float,_,F}, _, _) -> leaf(write(F)); lexpr({atom,_,A}, _, _) -> leaf(write(A)); lexpr({string,_,S}, _, _) -> {string,S}; lexpr({nil,_}, _, _) -> '[]'; -lexpr({cons,_,H,T}, _, Hook) -> - list(T, [H], Hook); -lexpr({lc,_,E,Qs}, _Prec, Hook) -> - Lcl = {list,[{step,[lexpr(E, Hook),leaf(" ||")],lc_quals(Qs, Hook)}]}, +lexpr({cons,_,H,T}, _, Opts) -> + list(T, [H], Opts); +lexpr({lc,_,E,Qs}, _Prec, Opts) -> + Lcl = {list,[{step,[lexpr(E, Opts),leaf(" ||")],lc_quals(Qs, Opts)}]}, {list,[{seq,$[,[],[[]],[{force_nl,leaf(" "),[Lcl]}]},$]]}; %% {list,[{step,$[,Lcl},$]]}; -lexpr({bc,_,E,Qs}, _Prec, Hook) -> - Lcl = {list,[{step,[lexpr(E, Hook),leaf(" ||")],lc_quals(Qs, Hook)}]}, +lexpr({bc,_,E,Qs}, _Prec, Opts) -> + Lcl = {list,[{step,[lexpr(E, Opts),leaf(" ||")],lc_quals(Qs, Opts)}]}, {list,[{seq,'<<',[],[[]],[{force_nl,leaf(" "),[Lcl]}]},'>>']}; %% {list,[{step,'<<',Lcl},'>>']}; -lexpr({tuple,_,Elts}, _, Hook) -> - tuple(Elts, Hook); -%%lexpr({struct,_,Tag,Elts}, _, Hook) -> -%% {first,format("~w", [Tag]),tuple(Elts, Hook)}; -lexpr({record_index, _, Name, F}, Prec, Hook) -> +lexpr({tuple,_,Elts}, _, Opts) -> + tuple(Elts, Opts); +%%lexpr({struct,_,Tag,Elts}, _, Opts) -> +%% {first,format("~w", [Tag]),tuple(Elts, Opts)}; +lexpr({record_index, _, Name, F}, Prec, Opts) -> {P,R} = preop_prec('#'), Nl = record_name(Name), - El = [Nl,$.,lexpr(F, R, Hook)], + El = [Nl,$.,lexpr(F, R, Opts)], maybe_paren(P, Prec, El); -lexpr({record, _, Name, Fs}, Prec, Hook) -> +lexpr({record, _, Name, Fs}, Prec, Opts) -> {P,_R} = preop_prec('#'), Nl = record_name(Name), - El = {first,Nl,record_fields(Fs, Hook)}, + El = {first,Nl,record_fields(Fs, Opts)}, maybe_paren(P, Prec, El); -lexpr({record_field, _, Rec, Name, F}, Prec, Hook) -> +lexpr({record_field, _, Rec, Name, F}, Prec, Opts) -> {L,P,R} = inop_prec('#'), - Rl = lexpr(Rec, L, Hook), + Rl = lexpr(Rec, L, Opts), Nl = leaf(format("#~w.", [Name])), - El = [Rl,Nl,lexpr(F, R, Hook)], + El = [Rl,Nl,lexpr(F, R, Opts)], maybe_paren(P, Prec, El); -lexpr({record, _, Rec, Name, Fs}, Prec, Hook) -> +lexpr({record, _, Rec, Name, Fs}, Prec, Opts) -> {L,P,_R} = inop_prec('#'), - Rl = lexpr(Rec, L, Hook), + Rl = lexpr(Rec, L, Opts), Nl = record_name(Name), - El = {first,[Rl,Nl],record_fields(Fs, Hook)}, + El = {first,[Rl,Nl],record_fields(Fs, Opts)}, maybe_paren(P, Prec, El); -lexpr({record_field, _, {atom,_,''}, F}, Prec, Hook) -> +lexpr({record_field, _, {atom,_,''}, F}, Prec, Opts) -> {_L,P,R} = inop_prec('.'), - El = [$.,lexpr(F, R, Hook)], + El = [$.,lexpr(F, R, Opts)], maybe_paren(P, Prec, El); -lexpr({record_field, _, Rec, F}, Prec, Hook) -> +lexpr({record_field, _, Rec, F}, Prec, Opts) -> {L,P,R} = inop_prec('.'), - El = [lexpr(Rec, L, Hook),$.,lexpr(F, R, Hook)], + El = [lexpr(Rec, L, Opts),$.,lexpr(F, R, Opts)], maybe_paren(P, Prec, El); -lexpr({block,_,Es}, _, Hook) -> - {list,[{step,'begin',body(Es, Hook)},'end']}; -lexpr({'if',_,Cs}, _, Hook) -> - {list,[{step,'if',if_clauses(Cs, Hook)},'end']}; -lexpr({'case',_,Expr,Cs}, _, Hook) -> - {list,[{step,{list,[{step,'case',lexpr(Expr, Hook)},'of']}, - cr_clauses(Cs, Hook)}, +lexpr({block,_,Es}, _, Opts) -> + {list,[{step,'begin',body(Es, Opts)},'end']}; +lexpr({'if',_,Cs}, _, Opts) -> + {list,[{step,'if',if_clauses(Cs, Opts)},'end']}; +lexpr({'case',_,Expr,Cs}, _, Opts) -> + {list,[{step,{list,[{step,'case',lexpr(Expr, Opts)},'of']}, + cr_clauses(Cs, Opts)}, 'end']}; -lexpr({'cond',_,Cs}, _, Hook) -> - {list,[{step,leaf("cond"),cond_clauses(Cs, Hook)},'end']}; -lexpr({'receive',_,Cs}, _, Hook) -> - {list,[{step,'receive',cr_clauses(Cs, Hook)},'end']}; -lexpr({'receive',_,Cs,To,ToOpt}, _, Hook) -> - Al = {list,[{step,[lexpr(To, Hook),' ->'],body(ToOpt, Hook)}]}, - {list,[{step,'receive',cr_clauses(Cs, Hook)}, +lexpr({'cond',_,Cs}, _, Opts) -> + {list,[{step,leaf("cond"),cond_clauses(Cs, Opts)},'end']}; +lexpr({'receive',_,Cs}, _, Opts) -> + {list,[{step,'receive',cr_clauses(Cs, Opts)},'end']}; +lexpr({'receive',_,Cs,To,ToOpt}, _, Opts) -> + Al = {list,[{step,[lexpr(To, Opts),' ->'],body(ToOpt, Opts)}]}, + {list,[{step,'receive',cr_clauses(Cs, Opts)}, {step,'after',Al}, 'end']}; -lexpr({'fun',_,{function,F,A}}, _Prec, _Hook) -> +lexpr({'fun',_,{function,F,A}}, _Prec, _Opts) -> leaf(format("fun ~w/~w", [F,A])); -lexpr({'fun',_,{function,F,A},Extra}, _Prec, _Hook) -> +lexpr({'fun',_,{function,F,A},Extra}, _Prec, _Opts) -> {force_nl,fun_info(Extra),leaf(format("fun ~w/~w", [F,A]))}; -lexpr({'fun',_,{function,M,F,A}}, _Prec, _Hook) +lexpr({'fun',_,{function,M,F,A}}, _Prec, _Opts) when is_atom(M), is_atom(F), is_integer(A) -> %% For backward compatibility with pre-R15 abstract format. leaf(format("fun ~w:~w/~w", [M,F,A])); -lexpr({'fun',_,{function,M,F,A}}, _Prec, Hook) -> +lexpr({'fun',_,{function,M,F,A}}, _Prec, Opts) -> %% New format in R15. - NameItem = lexpr(M, Hook), - CallItem = lexpr(F, Hook), - ArityItem = lexpr(A, Hook), + NameItem = lexpr(M, Opts), + CallItem = lexpr(F, Opts), + ArityItem = lexpr(A, Opts), ["fun ",NameItem,$:,CallItem,$/,ArityItem]; -lexpr({'fun',_,{clauses,Cs}}, _Prec, Hook) -> - {list,[{first,'fun',fun_clauses(Cs, Hook)},'end']}; -lexpr({'fun',_,{clauses,Cs},Extra}, _Prec, Hook) -> +lexpr({'fun',_,{clauses,Cs}}, _Prec, Opts) -> + {list,[{first,'fun',fun_clauses(Cs, Opts)},'end']}; +lexpr({'fun',_,{clauses,Cs},Extra}, _Prec, Opts) -> {force_nl,fun_info(Extra), - {list,[{first,'fun',fun_clauses(Cs, Hook)},'end']}}; -lexpr({'query',_,Lc}, _Prec, Hook) -> - {list,[{step,leaf("query"),lexpr(Lc, 0, Hook)},'end']}; -lexpr({call,_,{remote,_,{atom,_,M},{atom,_,F}=N}=Name,Args}, Prec, Hook) -> + {list,[{first,'fun',fun_clauses(Cs, Opts)},'end']}}; +lexpr({'query',_,Lc}, _Prec, Opts) -> + {list,[{step,leaf("query"),lexpr(Lc, 0, Opts)},'end']}; +lexpr({call,_,{remote,_,{atom,_,M},{atom,_,F}=N}=Name,Args}, Prec, Opts) -> case erl_internal:bif(M, F, length(Args)) of true -> - call(N, Args, Prec, Hook); + call(N, Args, Prec, Opts); false -> - call(Name, Args, Prec, Hook) + call(Name, Args, Prec, Opts) end; -lexpr({call,_,Name,Args}, Prec, Hook) -> - call(Name, Args, Prec, Hook); -lexpr({'try',_,Es,Scs,Ccs,As}, _, Hook) -> +lexpr({call,_,Name,Args}, Prec, Opts) -> + call(Name, Args, Prec, Opts); +lexpr({'try',_,Es,Scs,Ccs,As}, _, Opts) -> {list,[if Scs =:= [] -> - {step,'try',body(Es, Hook)}; + {step,'try',body(Es, Opts)}; true -> - {step,{list,[{step,'try',body(Es, Hook)},'of']}, - cr_clauses(Scs, Hook)} + {step,{list,[{step,'try',body(Es, Opts)},'of']}, + cr_clauses(Scs, Opts)} end, if Ccs =:= [] -> []; true -> - {step,'catch',try_clauses(Ccs, Hook)} + {step,'catch',try_clauses(Ccs, Opts)} end, if As =:= [] -> []; true -> - {step,'after',body(As, Hook)} + {step,'after',body(As, Opts)} end, 'end']}; -lexpr({'catch',_,Expr}, Prec, Hook) -> +lexpr({'catch',_,Expr}, Prec, Opts) -> {P,R} = preop_prec('catch'), - El = {list,[{step,'catch',lexpr(Expr, R, Hook)}]}, + El = {list,[{step,'catch',lexpr(Expr, R, Opts)}]}, maybe_paren(P, Prec, El); -lexpr({match,_,Lhs,Rhs}, Prec, Hook) -> +lexpr({match,_,Lhs,Rhs}, Prec, Opts) -> {L,P,R} = inop_prec('='), - Pl = lexpr(Lhs, L, Hook), - Rl = lexpr(Rhs, R, Hook), + Pl = lexpr(Lhs, L, Opts), + Rl = lexpr(Rhs, R, Opts), El = {list,[{cstep,[Pl,' ='],Rl}]}, maybe_paren(P, Prec, El); -lexpr({op,_,Op,Arg}, Prec, Hook) -> +lexpr({op,_,Op,Arg}, Prec, Opts) -> {P,R} = preop_prec(Op), Ol = leaf(format("~s ", [Op])), - El = [Ol,lexpr(Arg, R, Hook)], + El = [Ol,lexpr(Arg, R, Opts)], maybe_paren(P, Prec, El); -lexpr({op,_,Op,Larg,Rarg}, Prec, Hook) when Op =:= 'orelse'; +lexpr({op,_,Op,Larg,Rarg}, Prec, Opts) when Op =:= 'orelse'; Op =:= 'andalso' -> %% Breaks lines since R12B. {L,P,R} = inop_prec(Op), - Ll = lexpr(Larg, L, Hook), + Ll = lexpr(Larg, L, Opts), Ol = leaf(format("~s", [Op])), - Lr = lexpr(Rarg, R, Hook), + Lr = lexpr(Rarg, R, Opts), El = {prefer_nl,[[]],[Ll,Ol,Lr]}, maybe_paren(P, Prec, El); -lexpr({op,_,Op,Larg,Rarg}, Prec, Hook) -> +lexpr({op,_,Op,Larg,Rarg}, Prec, Opts) -> {L,P,R} = inop_prec(Op), - Ll = lexpr(Larg, L, Hook), + Ll = lexpr(Larg, L, Opts), Ol = leaf(format("~s", [Op])), - Lr = lexpr(Rarg, R, Hook), + Lr = lexpr(Rarg, R, Opts), El = {list,[Ll,Ol,Lr]}, maybe_paren(P, Prec, El); %% Special expressions which are not really legal everywhere. -lexpr({remote,_,M,F}, Prec, Hook) -> +lexpr({remote,_,M,F}, Prec, Opts) -> {L,P,R} = inop_prec(':'), - NameItem = lexpr(M, L, Hook), - CallItem = lexpr(F, R, Hook), + NameItem = lexpr(M, L, Opts), + CallItem = lexpr(F, R, Opts), maybe_paren(P, Prec, [NameItem,$:,CallItem]); %% BIT SYNTAX: -lexpr({bin,_,Fs}, _, Hook) -> - bit_grp(Fs, Hook); +lexpr({bin,_,Fs}, _, Opts) -> + bit_grp(Fs, Opts); %% Special case for straight values. lexpr({value,_,Val}, _,_) -> leaf(write(Val)); %% Now do the hook. -lexpr(Other, _Precedence, none) -> +lexpr(Other, _Precedence, #options{hook = none}) -> leaf(format("INVALID-FORM:~w:",[Other])); -lexpr(HookExpr, Precedence, {Mod,Func,Eas}) when Mod =/= 'fun' -> +lexpr(HookExpr, Precedence, #options{hook = {Mod,Func,Eas}}) + when Mod =/= 'fun' -> {ehook,HookExpr,Precedence,{Mod,Func,Eas}}; -lexpr(HookExpr, Precedence, Func) -> - {hook,HookExpr,Precedence,Func}. +lexpr(HookExpr, Precedence, #options{hook = Func, opts = Options}) -> + {hook,HookExpr,Precedence,Func,Options}. -call(Name, Args, Prec, Hook) -> +call(Name, Args, Prec, Opts) -> {F,P} = func_prec(), - Item = {first,lexpr(Name, F, Hook),args(Args, Hook)}, + Item = {first,lexpr(Name, F, Opts),args(Args, Opts)}, maybe_paren(P, Prec, Item). fun_info(Extra) -> @@ -565,32 +604,18 @@ fun_info(Extra) -> %% BITS: -bit_grp(Fs, Hook) -> - append([['<<'], - [try - true = Fs =/= [], - S = bin_string(Fs), - true = io_lib:printable_list(S), - {string,S} - catch _:_ -> - bit_elems(Fs, Hook) - end], - ['>>']]). - -bin_string([]) -> - []; -bin_string([{bin_element,_,{char,_,C},_,_}|Bin]) -> - [C | bin_string(Bin)]. +bit_grp(Fs, Opts) -> + append([['<<'], [bit_elems(Fs, Opts)], ['>>']]). -bit_elems(Es, Hook) -> - expr_list(Es, $,, fun bit_elem/2, Hook). +bit_elems(Es, Opts) -> + expr_list(Es, $,, fun bit_elem/2, Opts). -bit_elem({bin_element,_,Expr,Sz,Types}, Hook) -> +bit_elem({bin_element,_,Expr,Sz,Types}, Opts) -> P = max_prec(), - VChars = lexpr(Expr, P, Hook), + VChars = lexpr(Expr, P, Opts), SChars = if Sz =/= default -> - [VChars,$:,lexpr(Sz, P, Hook)]; + [VChars,$:,lexpr(Sz, P, Opts)]; true -> VChars end, @@ -618,157 +643,157 @@ bit_elem_type(T) -> record_name(Name) -> leaf(format("#~w", [Name])). -record_fields(Fs, Hook) -> - tuple(Fs, fun record_field/2, Hook). +record_fields(Fs, Opts) -> + tuple(Fs, fun record_field/2, Opts). -record_field({record_field,_,F,Val}, Hook) -> +record_field({record_field,_,F,Val}, Opts) -> {L,_P,R} = inop_prec('='), - Fl = lexpr(F, L, Hook), - Vl = lexpr(Val, R, Hook), + Fl = lexpr(F, L, Opts), + Vl = lexpr(Val, R, Opts), {list,[{cstep,[Fl,' ='],Vl}]}; -record_field({typed_record_field,{record_field,_,F,Val},Type}, Hook) -> +record_field({typed_record_field,{record_field,_,F,Val},Type}, Opts) -> {L,_P,R} = inop_prec('='), - Fl = lexpr(F, L, Hook), - Vl = typed(lexpr(Val, R, Hook), Type), + Fl = lexpr(F, L, Opts), + Vl = typed(lexpr(Val, R, Opts), Type), {list,[{cstep,[Fl,' ='],Vl}]}; -record_field({typed_record_field,Field,Type}, Hook) -> - typed(record_field(Field, Hook), Type); -record_field({record_field,_,F}, Hook) -> - lexpr(F, 0, Hook). - -list({cons,_,H,T}, Es, Hook) -> - list(T, [H|Es], Hook); -list({nil,_}, Es, Hook) -> - proper_list(reverse(Es), Hook); -list(Other, Es, Hook) -> - improper_list(reverse(Es, [Other]), Hook). - -%% if_clauses(Clauses, Hook) -> [Char]. +record_field({typed_record_field,Field,Type}, Opts) -> + typed(record_field(Field, Opts), Type); +record_field({record_field,_,F}, Opts) -> + lexpr(F, 0, Opts). + +list({cons,_,H,T}, Es, Opts) -> + list(T, [H|Es], Opts); +list({nil,_}, Es, Opts) -> + proper_list(reverse(Es), Opts); +list(Other, Es, Opts) -> + improper_list(reverse(Es, [Other]), Opts). + +%% if_clauses(Clauses, Opts) -> [Char]. %% Print 'if' clauses. -if_clauses(Cs, Hook) -> - clauses(fun if_clause/2, Hook, Cs). +if_clauses(Cs, Opts) -> + clauses(fun if_clause/2, Opts, Cs). -if_clause({clause,_,[],G,B}, Hook) -> - Gl = [guard_no_when(G, Hook),' ->'], - {step,Gl,body(B, Hook)}. +if_clause({clause,_,[],G,B}, Opts) -> + Gl = [guard_no_when(G, Opts),' ->'], + {step,Gl,body(B, Opts)}. -guard_no_when([E|Es], Hook) when is_list(E) -> - expr_list([E|Es], $;, fun guard0/2, Hook); -guard_no_when([E|Es], Hook) -> % before R6 - guard_no_when([[E|Es]], Hook); +guard_no_when([E|Es], Opts) when is_list(E) -> + expr_list([E|Es], $;, fun guard0/2, Opts); +guard_no_when([E|Es], Opts) -> % before R6 + guard_no_when([[E|Es]], Opts); guard_no_when([], _) -> % cannot happen leaf("true"). -%% cr_clauses(Clauses, Hook) -> [Char]. +%% cr_clauses(Clauses, Opts) -> [Char]. %% Print 'case'/'receive' clauses. -cr_clauses(Cs, Hook) -> - clauses(fun cr_clause/2, Hook, Cs). +cr_clauses(Cs, Opts) -> + clauses(fun cr_clause/2, Opts, Cs). -cr_clause({clause,_,[T],G,B}, Hook) -> - El = lexpr(T, 0, Hook), - Gl = guard_when(El, G, Hook), - Bl = body(B, Hook), +cr_clause({clause,_,[T],G,B}, Opts) -> + El = lexpr(T, 0, Opts), + Gl = guard_when(El, G, Opts), + Bl = body(B, Opts), {step,Gl,Bl}. -%% try_clauses(Clauses, Hook) -> [Char]. +%% try_clauses(Clauses, Opts) -> [Char]. %% Print 'try' clauses. -try_clauses(Cs, Hook) -> - clauses(fun try_clause/2, Hook, Cs). +try_clauses(Cs, Opts) -> + clauses(fun try_clause/2, Opts, Cs). -try_clause({clause,_,[{tuple,_,[{atom,_,throw},V,S]}],G,B}, Hook) -> - El = lexpr(V, 0, Hook), - Sl = stack_backtrace(S, [El], Hook), - Gl = guard_when(Sl, G, Hook), - Bl = body(B, Hook), +try_clause({clause,_,[{tuple,_,[{atom,_,throw},V,S]}],G,B}, Opts) -> + El = lexpr(V, 0, Opts), + Sl = stack_backtrace(S, [El], Opts), + Gl = guard_when(Sl, G, Opts), + Bl = body(B, Opts), {step,Gl,Bl}; -try_clause({clause,_,[{tuple,_,[C,V,S]}],G,B}, Hook) -> - Cs = lexpr(C, 0, Hook), - El = lexpr(V, 0, Hook), +try_clause({clause,_,[{tuple,_,[C,V,S]}],G,B}, Opts) -> + Cs = lexpr(C, 0, Opts), + El = lexpr(V, 0, Opts), CsEl = [Cs,$:,El], - Sl = stack_backtrace(S, CsEl, Hook), - Gl = guard_when(Sl, G, Hook), - Bl = body(B, Hook), + Sl = stack_backtrace(S, CsEl, Opts), + Gl = guard_when(Sl, G, Opts), + Bl = body(B, Opts), {step,Gl,Bl}. -stack_backtrace({var,_,'_'}, El, _Hook) -> +stack_backtrace({var,_,'_'}, El, _Opts) -> El; -stack_backtrace(S, El, Hook) -> - El++[$:,lexpr(S, 0, Hook)]. +stack_backtrace(S, El, Opts) -> + El++[$:,lexpr(S, 0, Opts)]. -%% fun_clauses(Clauses, Hook) -> [Char]. +%% fun_clauses(Clauses, Opts) -> [Char]. %% Print 'fun' clauses. -fun_clauses(Cs, Hook) -> - nl_clauses(fun fun_clause/2, [$;], Hook, Cs). +fun_clauses(Cs, Opts) -> + nl_clauses(fun fun_clause/2, [$;], Opts, Cs). -fun_clause({clause,_,A,G,B}, Hook) -> - El = args(A, Hook), - Gl = guard_when(El, G, Hook), - Bl = body(B, Hook), +fun_clause({clause,_,A,G,B}, Opts) -> + El = args(A, Opts), + Gl = guard_when(El, G, Opts), + Bl = body(B, Opts), {step,Gl,Bl}. -%% cond_clauses(Clauses, Hook) -> [Char]. +%% cond_clauses(Clauses, Opts) -> [Char]. %% Print 'cond' clauses. -cond_clauses(Cs, Hook) -> - clauses(fun cond_clause/2, Hook, Cs). +cond_clauses(Cs, Opts) -> + clauses(fun cond_clause/2, Opts, Cs). -cond_clause({clause,_,[],[[E]],B}, Hook) -> - {step,[lexpr(E, Hook),' ->'],body(B, Hook)}. +cond_clause({clause,_,[],[[E]],B}, Opts) -> + {step,[lexpr(E, Opts),' ->'],body(B, Opts)}. -%% nl_clauses(Type, Hook, Clauses) -> [Char]. +%% nl_clauses(Type, Opts, Clauses) -> [Char]. %% Generic clause printing function (always breaks lines). -nl_clauses(Type, Sep, Hook, Cs) -> - {prefer_nl,Sep,lexprs(Cs, Type, Hook)}. +nl_clauses(Type, Sep, Opts, Cs) -> + {prefer_nl,Sep,lexprs(Cs, Type, Opts)}. -%% clauses(Type, Hook, Clauses) -> [Char]. +%% clauses(Type, Opts, Clauses) -> [Char]. %% Generic clause printing function (breaks lines since R12B). -clauses(Type, Hook, Cs) -> - {prefer_nl,[$;],lexprs(Cs, Type, Hook)}. +clauses(Type, Opts, Cs) -> + {prefer_nl,[$;],lexprs(Cs, Type, Opts)}. -%% lc_quals(Qualifiers, After, Hook) +%% lc_quals(Qualifiers, After, Opts) %% List comprehension qualifiers (breaks lines since R12B). -lc_quals(Qs, Hook) -> - {prefer_nl,[$,],lexprs(Qs, fun lc_qual/2, Hook)}. +lc_quals(Qs, Opts) -> + {prefer_nl,[$,],lexprs(Qs, fun lc_qual/2, Opts)}. -lc_qual({b_generate,_,Pat,E}, Hook) -> - Pl = lexpr(Pat, 0, Hook), - {list,[{step,[Pl,leaf(" <=")],lexpr(E, 0, Hook)}]}; -lc_qual({generate,_,Pat,E}, Hook) -> - Pl = lexpr(Pat, 0, Hook), - {list,[{step,[Pl,leaf(" <-")],lexpr(E, 0, Hook)}]}; -lc_qual(Q, Hook) -> - lexpr(Q, 0, Hook). +lc_qual({b_generate,_,Pat,E}, Opts) -> + Pl = lexpr(Pat, 0, Opts), + {list,[{step,[Pl,leaf(" <=")],lexpr(E, 0, Opts)}]}; +lc_qual({generate,_,Pat,E}, Opts) -> + Pl = lexpr(Pat, 0, Opts), + {list,[{step,[Pl,leaf(" <-")],lexpr(E, 0, Opts)}]}; +lc_qual(Q, Opts) -> + lexpr(Q, 0, Opts). -proper_list(Es, Hook) -> - {seq,$[,$],$,,lexprs(Es, Hook)}. +proper_list(Es, Opts) -> + {seq,$[,$],$,,lexprs(Es, Opts)}. -improper_list(Es, Hook) -> - {seq,$[,$],{$,,$|},lexprs(Es, Hook)}. +improper_list(Es, Opts) -> + {seq,$[,$],{$,,$|},lexprs(Es, Opts)}. -tuple(L, Hook) -> - tuple(L, fun lexpr/2, Hook). +tuple(L, Opts) -> + tuple(L, fun lexpr/2, Opts). -tuple(Es, F, Hook) -> - {seq,${,$},$,,lexprs(Es, F, Hook)}. +tuple(Es, F, Opts) -> + {seq,${,$},$,,lexprs(Es, F, Opts)}. -args(As, Hook) -> - {seq,$(,$),[$,],lexprs(As, Hook)}. +args(As, Opts) -> + {seq,$(,$),[$,],lexprs(As, Opts)}. -expr_list(Es, Sep, F, Hook) -> - {seq,[],[],Sep,lexprs(Es, F, Hook)}. +expr_list(Es, Sep, F, Opts) -> + {seq,[],[],Sep,lexprs(Es, F, Opts)}. -lexprs(Es, Hook) -> - lexprs(Es, fun lexpr/2, Hook). +lexprs(Es, Opts) -> + lexprs(Es, fun lexpr/2, Opts). -lexprs(Es, F, Hook) -> - [F(E, Hook) || E <- Es]. +lexprs(Es, F, Opts) -> + [F(E, Opts) || E <- Es]. maybe_paren(P, Prec, Expr) when P < Prec -> [$(,Expr,$)]; @@ -781,13 +806,13 @@ leaf(S) -> %%% Do the formatting. Currently nothing fancy. Could probably have %%% done it in one single pass. -frmt(Item) -> - frmt(Item, 0). +frmt(Item, PP) -> + frmt(Item, 0, PP). -frmt(Item, I) -> +frmt(Item, I, PP) -> ST = spacetab(), WT = wordtable(), - {Chars,_Length} = f(Item, I, ST, WT), + {Chars,_Length} = f(Item, I, ST, WT, PP), [Chars]. %%% What the tags mean: @@ -803,6 +828,7 @@ frmt(Item, I) -> %%% - {force_nl,ExtraInfo,I}: fun-info (a comment) forces linebreak before I. %%% - {prefer_nl,Sep,IPs}: forces linebreak between Is unlesss negative %%% indentation. +%%% - {char,C}: a character %%% - {string,S}: a string. %%% - {hook,...}, {ehook,...}: hook expressions. %%% @@ -812,22 +838,22 @@ frmt(Item, I) -> %%% cstep works similarly, but no linebreak if the width of I1 is less %%% than the indentation (this is for "A = <expression over several lines>). -f([]=Nil, _I0, _ST, _WT) -> +f([]=Nil, _I0, _ST, _WT, _PP) -> {Nil,0}; -f(C, _I0, _ST, _WT) when is_integer(C) -> +f(C, _I0, _ST, _WT, _PP) when is_integer(C) -> {C,1}; -f({leaf,Length,Chars}, _I0, _ST, _WT) -> +f({leaf,Length,Chars}, _I0, _ST, _WT, _PP) -> {Chars,Length}; -f([Item|Items], I0, ST, WT) -> - consecutive(Items, f(Item, I0, ST, WT), I0, ST, WT); -f({list,Items}, I0, ST, WT) -> - f({seq,[],[],[[]],Items}, I0, ST, WT); -f({first,E,Item}, I0, ST, WT) -> - f({seq,E,[],[[]],[Item]}, I0, ST, WT); -f({seq,Before,After,Sep,LItems}, I0, ST, WT) -> - BCharsSize = f(Before, I0, ST, WT), +f([Item|Items], I0, ST, WT, PP) -> + consecutive(Items, f(Item, I0, ST, WT, PP), I0, ST, WT, PP); +f({list,Items}, I0, ST, WT, PP) -> + f({seq,[],[],[[]],Items}, I0, ST, WT, PP); +f({first,E,Item}, I0, ST, WT, PP) -> + f({seq,E,[],[[]],[Item]}, I0, ST, WT, PP); +f({seq,Before,After,Sep,LItems}, I0, ST, WT, PP) -> + BCharsSize = f(Before, I0, ST, WT, PP), I = indent(BCharsSize, I0), - CharsSizeL = fl(LItems, Sep, I, After, ST, WT), + CharsSizeL = fl(LItems, Sep, I, After, ST, WT, PP), {CharsL,SizeL} = unz(CharsSizeL), {BCharsL,BSizeL} = unz1([BCharsSize]), Sizes = BSizeL ++ SizeL, @@ -848,15 +874,15 @@ f({seq,Before,After,Sep,LItems}, I0, ST, WT) -> {BCharsL++insert_newlines(CharsSizeL, I, ST), nsz(lists:last(Sizes), I0)} end; -f({force_nl,_ExtraInfoItem,Item}, I, ST, WT) when I < 0 -> +f({force_nl,_ExtraInfoItem,Item}, I, ST, WT, PP) when I < 0 -> %% Extra info is a comment; cannot have that on the same line - f(Item, I, ST, WT); -f({force_nl,ExtraInfoItem,Item}, I, ST, WT) -> - f({prefer_nl,[],[ExtraInfoItem,Item]}, I, ST, WT); -f({prefer_nl,Sep,LItems}, I, ST, WT) when I < 0 -> - f({seq,[],[],Sep,LItems}, I, ST, WT); -f({prefer_nl,Sep,LItems}, I0, ST, WT) -> - CharsSize2L = fl(LItems, Sep, I0, [], ST, WT), + f(Item, I, ST, WT, PP); +f({force_nl,ExtraInfoItem,Item}, I, ST, WT, PP) -> + f({prefer_nl,[],[ExtraInfoItem,Item]}, I, ST, WT, PP); +f({prefer_nl,Sep,LItems}, I, ST, WT, PP) when I < 0 -> + f({seq,[],[],Sep,LItems}, I, ST, WT, PP); +f({prefer_nl,Sep,LItems}, I0, ST, WT, PP) -> + CharsSize2L = fl(LItems, Sep, I0, [], ST, WT, PP), {_CharsL,Sizes} = unz(CharsSize2L), if Sizes =:= [] -> @@ -864,37 +890,40 @@ f({prefer_nl,Sep,LItems}, I0, ST, WT) -> true -> {insert_newlines(CharsSize2L, I0, ST),nsz(lists:last(Sizes), I0)} end; -f({string,S}, I, ST, WT) -> - f(write_a_string(S, I), I, ST, WT); -f({hook,HookExpr,Precedence,Func}, I, _ST, _WT) -> - Chars = Func(HookExpr, I, Precedence, Func), +f({char,C}, I, ST, WT, PP) -> + f(write_a_char(C, PP), I, ST, WT, PP); +f({string,S}, I, ST, WT, PP) -> + f(write_a_string(S, I, PP), I, ST, WT, PP); +f({hook,HookExpr,Precedence,Func,Options}, I, _ST, _WT, _PP) -> + Chars = Func(HookExpr, I, Precedence, Options), {Chars,indentation(Chars, I)}; -f({ehook,HookExpr,Precedence,{Mod,Func,Eas}=ModFuncEas}, I, _ST, _WT) -> +f({ehook,HookExpr,Precedence,{Mod,Func,Eas}=ModFuncEas}, I, _ST, _WT, _PP) -> Chars = apply(Mod, Func, [HookExpr,I,Precedence,ModFuncEas|Eas]), {Chars,indentation(Chars, I)}; -f(WordName, _I, _ST, WT) -> % when is_atom(WordName) +f(WordName, _I, _ST, WT, _PP) -> % when is_atom(WordName) word(WordName, WT). -define(IND, 4). %% fl(ListItems, I0, ST, WT) -> [[CharsSize1,CharsSize2]] %% ListItems = [{Item,Items}|Item] -fl([], _Sep, I0, After, ST, WT) -> - [[f(After, I0, ST, WT),{[],0}]]; -fl(CItems, Sep0, I0, After, ST, WT) -> +fl([], _Sep, I0, After, ST, WT, PP) -> + [[f(After, I0, ST, WT, PP),{[],0}]]; +fl(CItems, Sep0, I0, After, ST, WT, PP) -> F = fun({step,Item1,Item2}, S) -> - [f(Item1, I0, ST, WT),f([Item2,S], incr(I0, ?IND), ST, WT)]; + [f(Item1, I0, ST, WT, PP), + f([Item2,S], incr(I0, ?IND), ST, WT, PP)]; ({cstep,Item1,Item2}, S) -> - {_,Sz1} = CharSize1 = f(Item1, I0, ST, WT), + {_,Sz1} = CharSize1 = f(Item1, I0, ST, WT, PP), if is_integer(Sz1), Sz1 < ?IND -> Item2p = [leaf("\s"),Item2,S], - [consecutive(Item2p, CharSize1, I0, ST, WT),{[],0}]; + [consecutive(Item2p, CharSize1, I0, ST, WT, PP),{[],0}]; true -> - [CharSize1,f([Item2,S], incr(I0, ?IND), ST, WT)] + [CharSize1,f([Item2,S], incr(I0, ?IND), ST, WT, PP)] end; (Item, S) -> - [f([Item,S], I0, ST, WT),{[],0}] + [f([Item,S], I0, ST, WT, PP),{[],0}] end, {Sep,LastSep} = case Sep0 of {_,_} -> Sep0; _ -> {Sep0,Sep0} end, fl1(CItems, F, Sep, LastSep, After). @@ -906,10 +935,10 @@ fl1([CItem1,CItem2], F, _Sep, LastSep, After) -> fl1([CItem|CItems], F, Sep, LastSep, After) -> [F(CItem, Sep)|fl1(CItems, F, Sep, LastSep, After)]. -consecutive(Items, CharSize1, I0, ST, WT) -> +consecutive(Items, CharSize1, I0, ST, WT, PP) -> {CharsSizes,_Length} = mapfoldl(fun(Item, Len) -> - CharsSize = f(Item, Len, ST, WT), + CharsSize = f(Item, Len, ST, WT, PP), {CharsSize,indent(CharsSize, Len)} end, indent(CharSize1, I0), Items), {CharsL,SizeL} = unz1([CharSize1|CharsSizes]), @@ -999,26 +1028,40 @@ has_nl([C|Cs]) -> has_nl([]) -> false. +write_a_char(C, PP) -> + flat_leaf(write_char(C, PP)). + -define(MIN_SUBSTRING, 5). -write_a_string(S, I) when I < 0; S =:= [] -> - leaf(write_string(S)); -write_a_string(S, I) -> +write_a_string(S, I, PP) when I < 0; S =:= [] -> + flat_leaf(write_string(S, PP)); +write_a_string(S, I, PP) -> Len = erlang:max(?MAXLINE-I, ?MIN_SUBSTRING), - {list,write_a_string(S, Len, Len)}. + {list,write_a_string(S, Len, Len, PP)}. -write_a_string([], _N, _Len) -> +write_a_string([], _N, _Len, _PP) -> []; -write_a_string(S, N, Len) -> +write_a_string(S, N, Len, PP) -> SS = string:sub_string(S, 1, N), - Sl = write_string(SS), - case (iolist_size(Sl) > Len) and (N > ?MIN_SUBSTRING) of + Sl = write_string(SS, PP), + case (length(Sl) > Len) and (N > ?MIN_SUBSTRING) of true -> - write_a_string(S, N-1, Len); + write_a_string(S, N-1, Len, PP); false -> - [leaf(Sl)|write_a_string(lists:nthtail(length(SS), S), Len, Len)] + [flat_leaf(Sl) | + write_a_string(lists:nthtail(length(SS), S), Len, Len, PP)] end. +flat_leaf(S) -> + L = lists:flatten(S), + {leaf,length(L),L}. + +write_string(S, PP) -> + lists:flatten((PP#pp.string_fun)(S)). + +write_char(C, PP) -> + lists:flatten((PP#pp.char_fun)(C)). + %% %% Utilities %% diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl index 0c8735bb6d..e5bb287c45 100644 --- a/lib/stdlib/src/erl_scan.erl +++ b/lib/stdlib/src/erl_scan.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -32,19 +33,19 @@ %% 173 - 176 { - ~ punctuation %% 177 DEL control %% 200 - 237 control -%% 240 - 277 NBSP - � punctuation -%% 300 - 326 � - � uppercase -%% 327 � punctuation -%% 330 - 336 � - � uppercase -%% 337 - 366 � - � lowercase -%% 367 � punctuation -%% 370 - 377 � - � lowercase +%% 240 - 277 NBSP - ¿ punctuation +%% 300 - 326 À - Ö uppercase +%% 327 × punctuation +%% 330 - 336 Ø - Þ uppercase +%% 337 - 366 ß - ö lowercase +%% 367 ÷ punctuation +%% 370 - 377 ø - ÿ lowercase %% %% Many punctuation characters have special meaning: %% $\s, $_, $", $$, $%, $', $. %% DEL is a punctuation. %% -%% Must watch using � \327, very close to x \170. +%% Must watch using × \327, very close to x \170. -module(erl_scan). @@ -55,7 +56,15 @@ token_info/1,token_info/2, attributes_info/1,attributes_info/2,set_attribute/3]). --export_type([error_info/0, line/0, return_cont/0, tokens_result/0]). +%%% Private +-export([continuation_location/1]). + +-export_type([error_info/0, + line/0, + location/0, + options/0, + return_cont/0, + tokens_result/0]). %%% %%% Defines and type definitions @@ -74,7 +83,8 @@ -type location() :: line() | {line(),column()}. -type resword_fun() :: fun((atom()) -> boolean()). -type option() :: 'return' | 'return_white_spaces' | 'return_comments' - | 'text' | {'reserved_word_fun', resword_fun()}. + | 'text' | {'reserved_word_fun', resword_fun()} + | 'unicode'. -type options() :: option() | [option()]. -type symbol() :: atom() | float() | integer() | string(). -type info_line() :: integer() | term(). @@ -95,7 +105,8 @@ {resword_fun = fun reserved_word/1 :: resword_fun(), ws = false :: boolean(), comment = false :: boolean(), - text = false :: boolean()}). + text = false :: boolean(), + unicode = false :: boolean()}). %%---------------------------------------------------------------------------- @@ -183,6 +194,11 @@ tokens({erl_scan_continuation,Cs,Col,Toks,Line,St,Any,Fun}, CharSpec, _Loc, _Opts) -> tokens1(Cs++CharSpec, St, Line, Col, Toks, Fun, Any). +continuation_location({erl_scan_continuation,_,no_col,_,Line,_,_,_}) -> + Line; +continuation_location({erl_scan_continuation,_,Col,_,Line,_,_,_}) -> + {Line,Col}. + -type attribute_item() :: 'column' | 'length' | 'line' | 'location' | 'text'. -type info_location() :: location() | term(). @@ -201,15 +217,14 @@ token_info(Token) -> Items = [category,column,length,line,symbol,text], % undefined order token_info(Token, Items). --spec token_info(Token, TokenItem) -> TokenInfo | 'undefined' when - Token :: token(), - TokenItem :: token_item(), - TokenInfo :: TokenInfoTuple :: token_info(); - (Token, TokenItems) -> [TokenInfo] when - Token :: token(), - TokenItems :: [TokenItem], - TokenItem :: token_item(), - TokenInfo :: [TokenInfoTuple :: token_info()]. +-spec token_info(Token, TokenItem) -> TokenInfoTuple | 'undefined' when + Token :: token(), + TokenItem :: token_item(), + TokenInfoTuple :: token_info(); + (Token, TokenItems) -> TokenInfo when + Token :: token(), + TokenItems :: [TokenItem :: token_item()], + TokenInfo :: [TokenInfoTuple :: token_info()]. token_info(_Token, []) -> []; token_info(Token, [Item|Items]) when is_atom(Item) -> @@ -239,16 +254,15 @@ attributes_info(Attributes) -> Items = [column,length,line,text], % undefined order attributes_info(Attributes, Items). --spec attributes_info(Attributes, AttributeItem) -> - AttributeInfo | 'undefined' when - Attributes :: attributes(), - AttributeItem :: attribute_item(), - AttributeInfo :: AttributeInfoTuple :: attribute_info(); - (Attributes, AttributeItems) -> [AttributeInfo] when - Attributes :: attributes(), - AttributeItems :: [AttributeItem], - AttributeItem :: attribute_item(), - AttributeInfo :: [AttributeInfoTuple :: attribute_info()]. +-spec attributes_info + (Attributes, AttributeItem) -> AttributeInfoTuple | 'undefined' when + Attributes :: attributes(), + AttributeItem :: attribute_item(), + AttributeInfoTuple :: attribute_info(); + (Attributes, AttributeItems) -> AttributeInfo when + Attributes :: attributes(), + AttributeItems :: [AttributeItem :: attribute_item()], + AttributeInfo :: [AttributeInfoTuple :: attribute_info()]. attributes_info(_Attrs, []) -> []; attributes_info(Attrs, [A|As]) when is_atom(A) -> @@ -324,13 +338,20 @@ string_thing(_) -> "string". (C >= $\000 andalso C =< $\s orelse C >= $\200 andalso C =< $\240)). -define(DIGIT(C), C >= $0, C =< $9). -define(CHAR(C), is_integer(C), C >= 0). - -%% A workaround: Unicode strings are not returned as strings, but as -%% lists of integers. For instance, "b\x{aaa}c" => [98,2730,99]. This -%% is to protect the system from character codes greater than 255. To -%% be removed. Search for UNI to find workaround code. +-define(UNICODE(C), + (C >= 0 andalso C < 16#D800 orelse + C > 16#DFFF andalso C < 16#FFFE orelse + C > 16#FFFF andalso C =< 16#10FFFF)). + +%% When the option 'unicode' is false: return Unicode strings as lists +%% of integers and Unicode characters as integers. For instance, +%% erl_scan:string("\"b\x{aaa}c\".") is equivalent to +%% erl_scan:string("[98,2730,99]."). This is to protect the caller +%% from character codes greater than 255. Search for UNI to find code +%% implementing this "feature". The 'unicode' option is undocumented +%% and will probably be removed later. -define(NO_UNICODE, 0). --define(UNI255(C), (C) =< 16#ff). +-define(UNI255(C), (C =< 16#ff)). options(Opts0) when is_list(Opts0) -> Opts = lists:foldr(fun expand_opt/2, [], Opts0), @@ -344,10 +365,12 @@ options(Opts0) when is_list(Opts0) -> Comment = proplists:get_bool(return_comments, Opts), WS = proplists:get_bool(return_white_spaces, Opts), Txt = proplists:get_bool(text, Opts), + Unicode = proplists:get_bool(unicode, Opts), #erl_scan{resword_fun = RW_fun, comment = Comment, ws = WS, - text = Txt}; + text = Txt, + unicode = Unicode}; options(Opt) -> options([Opt]). @@ -513,9 +536,9 @@ scan1([$$|Cs], St, Line, Col, Toks) -> scan_char(Cs, St, Line, Col, Toks); scan1([$\r|Cs], St, Line, Col, Toks) when St#erl_scan.ws -> white_space_end(Cs, St, Line, Col, Toks, 1, "\r"); -scan1([C|Cs], St, Line, Col, Toks) when C >= $�, C =< $�, C =/= $� -> +scan1([C|Cs], St, Line, Col, Toks) when C >= $ß, C =< $ÿ, C =/= $÷ -> scan_atom(Cs, St, Line, Col, Toks, [C]); -scan1([C|Cs], St, Line, Col, Toks) when C >= $�, C =< $�, C /= $� -> +scan1([C|Cs], St, Line, Col, Toks) when C >= $À, C =< $Þ, C /= $× -> scan_variable(Cs, St, Line, Col, Toks, [C]); scan1([$\t|Cs], St, Line, Col, Toks) when St#erl_scan.ws -> scan_tabs(Cs, St, Line, Col, Toks, 1); @@ -628,15 +651,12 @@ scan1([$~|Cs], St, Line, Col, Toks) -> scan1([$&|Cs], St, Line, Col, Toks) -> tok2(Cs, St, Line, Col, Toks, "&", '&', 1); %% End of optimization. -scan1([C|Cs], St, Line, Col, Toks) when ?CHAR(C) -> +scan1([C|Cs], St, Line, Col, Toks) when ?CHAR(C), ?UNI255(C) -> Str = [C], - case catch list_to_atom(Str) of - Sym when is_atom(Sym) -> - tok2(Cs, St, Line, Col, Toks, Str, Sym, 1); - _ -> - Ncol = incr_column(Col, 1), - scan_error({illegal,character}, Line, Col, Line, Ncol, Cs) - end; + tok2(Cs, St, Line, Col, Toks, Str, list_to_atom(Str), 1); +scan1([C|Cs], _St, Line, Col, _Toks) when ?CHAR(C) -> + Ncol = incr_column(Col, 1), + scan_error({illegal,character}, Line, Col, Line, Ncol, Cs); scan1([]=Cs, _St, Line, Col, Toks) -> {more,{Cs,Col,Toks,Line,[],fun scan/6}}; scan1(eof=Cs, _St, Line, Col, Toks) -> @@ -685,9 +705,9 @@ scan_name([C|Cs], Ncs) when ?DIGIT(C) -> scan_name(Cs, [C|Ncs]); scan_name([$@=C|Cs], Ncs) -> scan_name(Cs, [C|Ncs]); -scan_name([C|Cs], Ncs) when C >= $�, C =< $�, C =/= $� -> +scan_name([C|Cs], Ncs) when C >= $ß, C =< $ÿ, C =/= $÷ -> scan_name(Cs, [C|Ncs]); -scan_name([C|Cs], Ncs) when C >= $�, C =< $�, C =/= $� -> +scan_name([C|Cs], Ncs) when C >= $À, C =< $Þ, C =/= $× -> scan_name(Cs, [C|Ncs]); scan_name([], Ncs) -> {more,Ncs}; @@ -834,32 +854,44 @@ scan_char([$\\|Cs]=Cs0, St, Line, Col, Toks) -> {eof,Ncol} -> scan_error(char, Line, Col, Line, Ncol, eof); {nl,Val,Str,Ncs,Ncol} -> - Attrs = attributes(Line, Col, St, "$\\"++Str), + Attrs = attributes(Line, Col, St, "$\\"++Str), %" Ntoks = [{char,Attrs,Val}|Toks], scan1(Ncs, St, Line+1, Ncol, Ntoks); {unicode,Val,Str,Ncs,Ncol} -> - Attrs = attributes(Line, Col, St, "$\\"++Str), - Ntoks = [{integer,Attrs,Val}|Toks], % UNI + Attrs = attributes(Line, Col, St, "$\\"++Str), %" + Tag = char_tag(Val, St), % UNI + Ntoks = [{Tag,Attrs,Val}|Toks], scan1(Ncs, St, Line, Ncol, Ntoks); {Val,Str,Ncs,Ncol} -> - Attrs = attributes(Line, Col, St, "$\\"++Str), + Attrs = attributes(Line, Col, St, "$\\"++Str), %" Ntoks = [{char,Attrs,Val}|Toks], scan1(Ncs, St, Line, Ncol, Ntoks) end; scan_char([$\n=C|Cs], St, Line, Col, Toks) -> Attrs = attributes(Line, Col, St, [$$,C]), scan1(Cs, St, Line+1, new_column(Col, 1), [{char,Attrs,C}|Toks]); -scan_char([C|Cs], St, Line, Col, Toks) when ?CHAR(C) -> - Tag = if ?UNI255(C) -> char; true -> integer end, % UNI +scan_char([C|Cs], St, Line, Col, Toks) when ?UNICODE(C) -> + Tag = char_tag(C, St), % UNI Attrs = attributes(Line, Col, St, [$$,C]), scan1(Cs, St, Line, incr_column(Col, 2), [{Tag,Attrs,C}|Toks]); +scan_char([C|_Cs], _St, Line, Col, _Toks) when ?CHAR(C) -> + scan_error({illegal,character}, Line, Col, Line, incr_column(Col, 1), eof); scan_char([], _St, Line, Col, Toks) -> {more,{[$$],Col,Toks,Line,[],fun scan/6}}; scan_char(eof, _St, Line, Col, _Toks) -> scan_error(char, Line, Col, Line, incr_column(Col, 1), eof). +-compile({inline,[char_tag/2]}). + +char_tag(C, _St) when ?UNI255(C) -> + char; +char_tag(_C, #erl_scan{unicode = true}) -> + char; +char_tag(_C, _St) -> + integer. + scan_string(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) -> - case scan_string0(Cs, St, Line, Col, $\", Str, Wcs, Uni0) of + case scan_string0(Cs, St, Line, Col, $\", true, Str, Wcs, Uni0) of %" {more,Ncs,Nline,Ncol,Nstr,Nwcs,Uni} -> State = {Nwcs,Nstr,Line0,Col0,Uni}, {more,{Ncs,Ncol,Toks,Nline,State,fun scan_string/6}}; @@ -867,8 +899,9 @@ scan_string(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) -> scan_error(Error, Nline, Ncol, Nline, EndCol, Ncs); {error,Nline,Ncol,Nwcs,Ncs} -> Estr = string:substr(Nwcs, 1, 16), % Expanded escape chars. - scan_error({string,$\",Estr}, Line0, Col0, Nline, Ncol, Ncs); - {Ncs,Nline,Ncol,Nstr,Nwcs,?NO_UNICODE} -> + scan_error({string,$\",Estr}, Line0, Col0, Nline, Ncol, Ncs); %" + {Ncs,Nline,Ncol,Nstr,Nwcs,Uni} when Uni =:= ?NO_UNICODE; + St#erl_scan.unicode -> Attrs = attributes(Line0, Col0, St, Nstr), scan1(Ncs, St, Nline, Ncol, [{string,Attrs,Nwcs}|Toks]); {Ncs,Nline,Ncol,Nstr,_Nwcs,_Uni} -> @@ -920,7 +953,8 @@ unicode_tokens(Line, Col, Str, Val, St, Toks, Cs, Cline, Ccol) -> [{',',attributes(Cline, Ccol, St, "")} || Cs =/= "\""] ++ [Token|Toks]. scan_qatom(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) -> - case scan_string0(Cs, St, Line, Col, $\', Str, Wcs, Uni0) of + AllowUni = St#erl_scan.unicode, + case scan_string0(Cs, St, Line, Col, $\', AllowUni, Str, Wcs, Uni0) of %' {more,Ncs,Nline,Ncol,Nstr,Nwcs,Uni} -> State = {Nwcs,Nstr,Line0,Col0,Uni}, {more,{Ncs,Ncol,Toks,Nline,State,fun scan_qatom/6}}; @@ -928,8 +962,9 @@ scan_qatom(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) -> scan_error(Error, Nline, Ncol, Nline, EndCol, Ncs); {error,Nline,Ncol,Nwcs,Ncs} -> Estr = string:substr(Nwcs, 1, 16), % Expanded escape chars. - scan_error({string,$\',Estr}, Line0, Col0, Nline, Ncol, Ncs); - {Ncs,Nline,Ncol,Nstr,Nwcs,?NO_UNICODE} -> + scan_error({string,$\',Estr}, Line0, Col0, Nline, Ncol, Ncs); %' + {Ncs,Nline,Ncol,Nstr,Nwcs,Uni} -> + true = Uni =:= ?NO_UNICODE orelse AllowUni, case catch list_to_atom(Nwcs) of A when is_atom(A) -> Attrs = attributes(Line0, Col0, St, Nstr), @@ -939,38 +974,40 @@ scan_qatom(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) -> end end. -scan_string0(Cs, #erl_scan{text=false}, Line, no_col=Col, Q, [], Wcs, Uni) -> - scan_string_no_col(Cs, Line, Col, Q, Wcs, Uni); -scan_string0(Cs, #erl_scan{text=true}, Line, no_col=Col, Q, Str, Wcs, Uni) -> - scan_string1(Cs, Line, Col, Q, Str, Wcs, Uni); -scan_string0(Cs, _St, Line, Col, Q, [], Wcs, Uni) -> - scan_string_col(Cs, Line, Col, Q, Wcs, Uni); -scan_string0(Cs, _St, Line, Col, Q, Str, Wcs, Uni) -> - scan_string1(Cs, Line, Col, Q, Str, Wcs, Uni). +scan_string0(Cs, #erl_scan{text=false}, Line, no_col=Col, Q, U, [], Wcs, Uni) -> + scan_string_no_col(Cs, Line, Col, Q, U, Wcs, Uni); +scan_string0(Cs, #erl_scan{text=true}, Line, no_col=Col, Q, U, Str, Wcs, Uni) -> + scan_string1(Cs, Line, Col, Q, U, Str, Wcs, Uni); +scan_string0(Cs, _St, Line, Col, Q, U, [], Wcs, Uni) -> + scan_string_col(Cs, Line, Col, Q, U, Wcs, Uni); +scan_string0(Cs, _St, Line, Col, Q, U, Str, Wcs, Uni) -> + scan_string1(Cs, Line, Col, Q, U, Str, Wcs, Uni). %% Optimization. Col =:= no_col. -scan_string_no_col([Q|Cs], Line, Col, Q, Wcs, Uni) -> +scan_string_no_col([Q|Cs], Line, Col, Q, _U, Wcs, Uni) -> {Cs,Line,Col,_DontCare=[],lists:reverse(Wcs),Uni}; -scan_string_no_col([$\n=C|Cs], Line, Col, Q, Wcs, Uni) -> - scan_string_no_col(Cs, Line+1, Col, Q, [C|Wcs], Uni); -scan_string_no_col([C|Cs], Line, Col, Q, Wcs, Uni) when C =/= $\\, - ?CHAR(C), ?UNI255(C) -> - scan_string_no_col(Cs, Line, Col, Q, [C|Wcs], Uni); -scan_string_no_col(Cs, Line, Col, Q, Wcs, Uni) -> - scan_string1(Cs, Line, Col, Q, Wcs, Wcs, Uni). +scan_string_no_col([$\n=C|Cs], Line, Col, Q, U, Wcs, Uni) -> + scan_string_no_col(Cs, Line+1, Col, Q, U, [C|Wcs], Uni); +scan_string_no_col([C|Cs], Line, Col, Q, U, Wcs, Uni) when C =/= $\\, + ?CHAR(C), + ?UNI255(C) -> + scan_string_no_col(Cs, Line, Col, Q, U, [C|Wcs], Uni); +scan_string_no_col(Cs, Line, Col, Q, U, Wcs, Uni) -> + scan_string1(Cs, Line, Col, Q, U, Wcs, Wcs, Uni). %% Optimization. Col =/= no_col. -scan_string_col([Q|Cs], Line, Col, Q, Wcs0, Uni) -> +scan_string_col([Q|Cs], Line, Col, Q, _U, Wcs0, Uni) -> Wcs = lists:reverse(Wcs0), Str = [Q|Wcs++[Q]], {Cs,Line,Col+1,Str,Wcs,Uni}; -scan_string_col([$\n=C|Cs], Line, _xCol, Q, Wcs, Uni) -> - scan_string_col(Cs, Line+1, 1, Q, [C|Wcs], Uni); -scan_string_col([C|Cs], Line, Col, Q, Wcs, Uni) when C =/= $\\, - ?CHAR(C), ?UNI255(C) -> - scan_string_col(Cs, Line, Col+1, Q, [C|Wcs], Uni); -scan_string_col(Cs, Line, Col, Q, Wcs, Uni) -> - scan_string1(Cs, Line, Col, Q, Wcs, Wcs, Uni). +scan_string_col([$\n=C|Cs], Line, _xCol, Q, U, Wcs, Uni) -> + scan_string_col(Cs, Line+1, 1, Q, U, [C|Wcs], Uni); +scan_string_col([C|Cs], Line, Col, Q, U, Wcs, Uni) when C =/= $\\, + ?CHAR(C), + ?UNI255(C) -> + scan_string_col(Cs, Line, Col+1, Q, U, [C|Wcs], Uni); +scan_string_col(Cs, Line, Col, Q, U, Wcs, Uni) -> + scan_string1(Cs, Line, Col, Q, U, Wcs, Wcs, Uni). %% UNI_STR is to be replaced by STR when the Unicode-string-to-list %% workaround is eventually removed. @@ -981,14 +1018,14 @@ scan_string_col(Cs, Line, Col, Q, Wcs, Uni) -> %% but then the end location of the error tuple would not correspond %% to the start location of the returned Rest string. (Maybe the end %% location could be modified, but that too is ugly.) -scan_string1([Q|Cs], Line, Col, Q, Str0, Wcs0, Uni) -> +scan_string1([Q|Cs], Line, Col, Q, _U, Str0, Wcs0, Uni) -> Wcs = lists:reverse(Wcs0), Str = ?UNI_STR(Col, [Q|lists:reverse(Str0, [Q])]), {Cs,Line,incr_column(Col, 1),Str,Wcs,Uni}; -scan_string1([$\n=C|Cs], Line, Col, Q, Str, Wcs, Uni) -> +scan_string1([$\n=C|Cs], Line, Col, Q, U, Str, Wcs, Uni) -> Ncol = new_column(Col, 1), - scan_string1(Cs, Line+1, Ncol, Q, ?UNI_STR(Col, [C|Str]), [C|Wcs], Uni); -scan_string1([$\\|Cs]=Cs0, Line, Col, Q, Str, Wcs, Uni) -> + scan_string1(Cs, Line+1, Ncol, Q, U, ?UNI_STR(Col, [C|Str]), [C|Wcs], Uni); +scan_string1([$\\|Cs]=Cs0, Line, Col, Q, U, Str, Wcs, Uni) -> case scan_escape(Cs, Col) of more -> {more,Cs0,Line,Col,Str,Wcs,Uni}; @@ -999,31 +1036,33 @@ scan_string1([$\\|Cs]=Cs0, Line, Col, Q, Str, Wcs, Uni) -> {nl,Val,ValStr,Ncs,Ncol} -> Nstr = ?UNI_STR(Ncol, lists:reverse(ValStr, [$\\|Str])), Nwcs = [Val|Wcs], - scan_string1(Ncs, Line+1, Ncol, Q, Nstr, Nwcs, Uni); - {unicode,_Val,_ValStr,Ncs,Ncol} when Q =:= $' -> %' Emacs + scan_string1(Ncs, Line+1, Ncol, Q, U, Nstr, Nwcs, Uni); + {unicode,_Val,_ValStr,Ncs,Ncol} when not U -> %' Emacs {char_error,Ncs,{illegal,character},Line,Col,incr_column(Ncol, 1)}; {unicode,Val,ValStr,Ncs,Ncol} -> % UNI. Uni is set to Val. Nstr = ?UNI_STR(Ncol, lists:reverse(ValStr, [$\\|Str])), Nwcs = [Val|Wcs], % not used - scan_string1(Ncs, Line, incr_column(Ncol, 1), Q, Nstr, Nwcs, Val); + scan_string1(Ncs, Line, incr_column(Ncol, 1), Q, U, Nstr, Nwcs, Val); {Val,ValStr,Ncs,Ncol} -> Nstr = ?UNI_STR(Ncol, lists:reverse(ValStr, [$\\|Str])), Nwcs = [Val|Wcs], - scan_string1(Ncs, Line, incr_column(Ncol, 1), Q, Nstr, Nwcs, Uni) + scan_string1(Ncs, Line, incr_column(Ncol, 1), Q, U, Nstr, Nwcs, Uni) end; -scan_string1([C|Cs], Line, no_col=Col, Q, Str, Wcs, Uni) when ?CHAR(C), - ?UNI255(C) -> - %% scan_string1(Cs, Line, Col, Q, Str, [C|Wcs], Uni); - scan_string1(Cs, Line, Col, Q, [C|Str], [C|Wcs], Uni); % UNI -scan_string1([C|Cs], Line, Col, Q, Str, Wcs, Uni) when ?CHAR(C), ?UNI255(C) -> - scan_string1(Cs, Line, Col+1, Q, [C|Str], [C|Wcs], Uni); -scan_string1([C|Cs], Line, Col, $', _Str, _Wcs, _Uni) when ?CHAR(C) -> %' UNI +scan_string1([C|Cs], Line, no_col=Col, Q, U, Str, Wcs, Uni) when ?CHAR(C), + ?UNI255(C) -> + %% scan_string1(Cs, Line, Col, Q, U, Str, [C|Wcs], Uni); + scan_string1(Cs, Line, Col, Q, U, [C|Str], [C|Wcs], Uni); % UNI +scan_string1([C|Cs], Line, Col, Q, U, Str, Wcs, Uni) when ?CHAR(C), ?UNI255(C) -> + scan_string1(Cs, Line, Col+1, Q, U, [C|Str], [C|Wcs], Uni); +scan_string1([C|Cs], Line, Col, _Q, false, _Str, _Wcs, _Uni) when ?CHAR(C) -> %' UNI + {char_error,Cs,{illegal,character},Line,Col,incr_column(Col, 1)}; +scan_string1([C|Cs], Line, Col, Q, U, Str, Wcs, _Uni) when ?UNICODE(C) -> + scan_string1(Cs, Line, incr_column(Col, 1), Q, U, [C|Str], [C|Wcs], C); +scan_string1([C|Cs], Line, Col, _Q, _U, _Str, _Wcs, _Uni) when ?CHAR(C) -> % UNI {char_error,Cs,{illegal,character},Line,Col,incr_column(Col, 1)}; -scan_string1([C|Cs], Line, Col, Q, Str, Wcs, _Uni) when ?CHAR(C) -> % UNI - scan_string1(Cs, Line, incr_column(Col, 1), Q, [C|Str], [C|Wcs], C); -scan_string1([]=Cs, Line, Col, _Q, Str, Wcs, Uni) -> +scan_string1([]=Cs, Line, Col, _Q, _U, Str, Wcs, Uni) -> {more,Cs,Line,Col,Str,Wcs,Uni}; -scan_string1(eof, Line, Col, _Q, _Str, Wcs, _Uni) -> +scan_string1(eof, Line, Col, _Q, _U, _Str, Wcs, _Uni) -> {error,Line,Col,lists:reverse(Wcs),eof}. -define(OCT(C), C >= $0, C =< $7). @@ -1074,8 +1113,10 @@ scan_escape([$\n=C|Cs], Col) -> scan_escape([C0|Cs], Col) when ?CHAR(C0), ?UNI255(C0) -> C = escape_char(C0), {C,?UNI_STR(Col, [C0]),Cs,incr_column(Col, 1)}; -scan_escape([C|Cs], Col) when ?CHAR(C) -> % UNI +scan_escape([C|Cs], Col) when ?UNICODE(C) -> {unicode,C,?UNI_STR(Col, [C]),Cs,incr_column(Col, 1)}; +scan_escape([C|Cs], Col) when ?CHAR(C) -> % UNI + {error,Cs,{illegal,character},incr_column(Col, 1)}; scan_escape([], _Col) -> more; scan_escape(eof, Col) -> @@ -1093,7 +1134,7 @@ scan_esc_end([$}|Cs], Col, Wcs0, B, Str0) -> case catch erlang:list_to_integer(Wcs, B) of Val when Val =< 16#FF -> {Val,?UNI_STR(Col, Str0++Wcs++[$}]),Cs,incr_column(Col, 1)}; - Val when Val =< 16#10FFFF -> + Val when ?UNICODE(Val) -> {unicode,Val,?UNI_STR(Col, Str0++Wcs++[$}]),Cs,incr_column(Col,1)}; _ -> {error,Cs,{illegal,character},incr_column(Col, 1)} @@ -1199,18 +1240,36 @@ float_end(Cs, St, Line, Col, Toks, Ncs0) -> scan_error({illegal,float}, Line, Col, Line, Ncol, Cs) end. -skip_comment([C|Cs], St, Line, Col, Toks, N) when C =/= $\n, ?CHAR(C) -> - skip_comment(Cs, St, Line, Col, Toks, N+1); -skip_comment([]=Cs, _St, Line, Col, Toks, N) -> - {more,{Cs,Col,Toks,Line,N,fun skip_comment/6}}; skip_comment(Cs, St, Line, Col, Toks, N) -> + skip_comment(Cs, St, Line, Col, Toks, N, St#erl_scan.unicode). + +skip_comment([C|Cs], St, Line, Col, Toks, N, U) when C =/= $\n, ?CHAR(C) -> + case ?UNI255(C) orelse U andalso ?UNICODE(C) of + true -> + skip_comment(Cs, St, Line, Col, Toks, N+1, U); + false -> + Ncol = incr_column(Col, N+1), + scan_error({illegal,character}, Line, Col, Line, Ncol, Cs) + end; +skip_comment([]=Cs, _St, Line, Col, Toks, N, _U) -> + {more,{Cs,Col,Toks,Line,N,fun skip_comment/6}}; +skip_comment(Cs, St, Line, Col, Toks, N, _U) -> scan1(Cs, St, Line, incr_column(Col, N), Toks). -scan_comment([C|Cs], St, Line, Col, Toks, Ncs) when C =/= $\n, ?CHAR(C) -> - scan_comment(Cs, St, Line, Col, Toks, [C|Ncs]); -scan_comment([]=Cs, _St, Line, Col, Toks, Ncs) -> +scan_comment(Cs, St, Line, Col, Toks, Ncs) -> + scan_comment(Cs, St, Line, Col, Toks, Ncs, St#erl_scan.unicode). + +scan_comment([C|Cs], St, Line, Col, Toks, Ncs, U) when C =/= $\n, ?CHAR(C) -> + case ?UNI255(C) orelse U andalso ?UNICODE(C) of + true -> + scan_comment(Cs, St, Line, Col, Toks, [C|Ncs], U); + false -> + Ncol = incr_column(Col, length(Ncs)+1), + scan_error({illegal,character}, Line, Col, Line, Ncol, Cs) + end; +scan_comment([]=Cs, _St, Line, Col, Toks, Ncs, _U) -> {more,{Cs,Col,Toks,Line,Ncs,fun scan_comment/6}}; -scan_comment(Cs, St, Line, Col, Toks, Ncs0) -> +scan_comment(Cs, St, Line, Col, Toks, Ncs0, _U) -> Ncs = lists:reverse(Ncs0), tok3(Cs, St, Line, Col, Toks, comment, Ncs, Ncs). diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index 498d850df3..99a9d138ac 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.erl @@ -710,7 +710,7 @@ epp_parse_file2(Epp, S, Forms, Parsed) -> epp_parse_file(Epp, S, [Form | Forms]) end; {error,{Ln,Mod,Args}} = Form -> - io:format("~s:~w: ~s\n", + io:format("~s:~w: ~ts\n", [S#state.file,Ln,Mod:format_error(Args)]), epp_parse_file(Epp, S#state{n_errors = S#state.n_errors + 1}, [Form | Forms]); {eof, _LastLine} = Eof -> @@ -780,10 +780,10 @@ report_errors(Errors) -> Errors). list_errors(F, [{Line,Mod,E}|Es]) -> - io:fwrite("~s:~w: ~s\n", [F,Line,Mod:format_error(E)]), + io:fwrite("~s:~w: ~ts\n", [F,Line,Mod:format_error(E)]), list_errors(F, Es); list_errors(F, [{Mod,E}|Es]) -> - io:fwrite("~s: ~s\n", [F,Mod:format_error(E)]), + io:fwrite("~s: ~ts\n", [F,Mod:format_error(E)]), list_errors(F, Es); list_errors(_F, []) -> ok. @@ -795,10 +795,10 @@ report_warnings(Ws0) -> lists:foreach(fun({_,Str}) -> io:put_chars(Str) end, Ws). format_message(F, [{Line,Mod,E}|Es]) -> - M = {{F,Line},io_lib:format("~s:~w: Warning: ~s\n", [F,Line,Mod:format_error(E)])}, + M = {{F,Line},io_lib:format("~s:~w: Warning: ~ts\n", [F,Line,Mod:format_error(E)])}, [M|format_message(F, Es)]; format_message(F, [{Mod,E}|Es]) -> - M = {none,io_lib:format("~s: Warning: ~s\n", [F,Mod:format_error(E)])}, + M = {none,io_lib:format("~s: Warning: ~ts\n", [F,Mod:format_error(E)])}, [M|format_message(F, Es)]; format_message(_, []) -> []. @@ -851,12 +851,27 @@ eval_exprs([E|Es], Bs0, Lf, Ef, RBs) -> eval_exprs(Es, Bs, Lf, Ef, RBs). format_exception(Class, Reason) -> + Enc = encoding(), + P = case Enc of + latin1 -> "P"; + _ -> "tP" + end, PF = fun(Term, I) -> - io_lib:format("~." ++ integer_to_list(I) ++ "P", [Term, 50]) + io_lib:format("~." ++ integer_to_list(I) ++ P, [Term, 50]) end, StackTrace = erlang:get_stacktrace(), StackFun = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end, - lib:format_exception(1, Class, Reason, StackTrace, StackFun, PF). + lib:format_exception(1, Class, Reason, StackTrace, StackFun, PF, Enc). + +encoding() -> + [{encoding, Encoding}] = enc(), + Encoding. + +enc() -> + case lists:keyfind(encoding, 1, io:getopts()) of + false -> [{encoding,latin1}]; % should never happen + Enc -> [Enc] + end. fatal(Str) -> throw(Str). diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl index 59d6de5d10..0c50eb34e6 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.erl @@ -878,7 +878,7 @@ filter_options(_Base, [], Result) -> %% Gets the source file given path of object code and module name. get_source_file(Obj, Mod, Rules) -> - source_by_rules(dirname(Obj), packages:last(Mod), Rules). + source_by_rules(dirname(Obj), atom_to_list(Mod), Rules). source_by_rules(Dir, Base, [{From, To}|Rest]) -> case try_rule(Dir, Base, From, To) of diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl index 391f1cff64..ba35a7170a 100644 --- a/lib/stdlib/src/gb_sets.erl +++ b/lib/stdlib/src/gb_sets.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -165,7 +166,7 @@ -export([new/0, is_element/2, add_element/2, del_element/2, subtract/2]). -%% GB-trees adapted from Sven-Olof Nystr�m's implementation for +%% GB-trees adapted from Sven-Olof Nyström's implementation for %% representation of sets. %% %% Data structures: diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl index 258713c90f..de0c239e26 100644 --- a/lib/stdlib/src/gb_trees.erl +++ b/lib/stdlib/src/gb_trees.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -19,7 +20,7 @@ %% ===================================================================== %% General Balanced Trees - highly efficient dictionaries. %% -%% Copyright (C) 1999-2001 Sven-Olof Nystr�m, Richard Carlsson +%% Copyright (C) 1999-2001 Sven-Olof Nyström, Richard Carlsson %% %% An efficient implementation of Prof. Arne Andersson's General %% Balanced Trees. These have no storage overhead compared to plain diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl index 9f65bbfa3a..ecf2aeb375 100644 --- a/lib/stdlib/src/io.erl +++ b/lib/stdlib/src/io.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -22,28 +22,28 @@ get_chars/2,get_chars/3,get_line/1,get_line/2, get_password/0, get_password/1, setopts/1, setopts/2, getopts/0, getopts/1]). --export([write/1,write/2,read/1,read/2,read/3]). +-export([write/1,write/2,read/1,read/2,read/3,read/4]). -export([columns/0,columns/1,rows/0,rows/1]). -export([fwrite/1,fwrite/2,fwrite/3,fread/2,fread/3, format/1,format/2,format/3]). --export([scan_erl_exprs/1,scan_erl_exprs/2,scan_erl_exprs/3, - scan_erl_form/1,scan_erl_form/2,scan_erl_form/3, +-export([scan_erl_exprs/1,scan_erl_exprs/2,scan_erl_exprs/3,scan_erl_exprs/4, + scan_erl_form/1,scan_erl_form/2,scan_erl_form/3,scan_erl_form/4, parse_erl_exprs/1,parse_erl_exprs/2,parse_erl_exprs/3, - parse_erl_form/1,parse_erl_form/2,parse_erl_form/3]). + parse_erl_exprs/4,parse_erl_form/1,parse_erl_form/2, + parse_erl_form/3,parse_erl_form/4]). -export([request/1,request/2,requests/1,requests/2]). --export_type([device/0, format/0]). +-export_type([device/0, format/0, server_no_data/0]). %%------------------------------------------------------------------------- -type device() :: atom() | pid(). -type prompt() :: atom() | string(). --type error_description() :: term(). % Whatever the io-server sends. --type request_error() :: {'error',error_description()}. +%% ErrorDescription is whatever the I/O-server sends. +-type server_no_data() :: {'error', ErrorDescription :: term()} | 'eof'. -%% XXX: Some uses of line() in this file may need to read erl_scan:location() --type line() :: pos_integer(). +-type location() :: erl_scan:location(). %%------------------------------------------------------------------------- @@ -73,9 +73,9 @@ o_request(Io, Request, Func) -> put_chars(Chars) -> put_chars(default_output(), Chars). --spec put_chars(IoDevice, IoData) -> 'ok' when +-spec put_chars(IoDevice, CharData) -> 'ok' when IoDevice :: device(), - IoData :: unicode:chardata(). + CharData :: unicode:chardata(). put_chars(Io, Chars) -> o_request(Io, {put_chars,unicode,Chars}, put_chars). @@ -124,7 +124,7 @@ rows(Io) -> {error,enotsup} end. --spec get_chars(Prompt, Count) -> Data | 'eof' when +-spec get_chars(Prompt, Count) -> Data | server_no_data() when Prompt :: prompt(), Count :: non_neg_integer(), Data :: [unicode:unicode_char()] | unicode:unicode_binary(). @@ -132,25 +132,23 @@ rows(Io) -> get_chars(Prompt, N) -> get_chars(default_input(), Prompt, N). --spec get_chars(IoDevice, Prompt, Count) -> Data | 'eof' | {error, Reason} when +-spec get_chars(IoDevice, Prompt, Count) -> Data | server_no_data() when IoDevice :: device(), Prompt :: prompt(), Count :: non_neg_integer(), - Reason :: term(), Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_chars(Io, Prompt, N) when is_integer(N), N >= 0 -> request(Io, {get_chars,unicode,Prompt,N}). --spec get_line(Prompt) -> Data | 'eof' | {'error', Reason} when +-spec get_line(Prompt) -> Data | server_no_data() when Prompt :: prompt(), - Reason :: term(), Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_line(Prompt) -> get_line(default_input(), Prompt). --spec get_line(IoDevice, Prompt) -> Data | 'eof' | {'error', term()} when +-spec get_line(IoDevice, Prompt) -> Data | server_no_data() when IoDevice :: device(), Prompt :: prompt(), Data :: [unicode:unicode_char()] | unicode:unicode_binary(). @@ -219,8 +217,9 @@ write(Io, Term) -> -spec read(Prompt) -> Result when Prompt :: prompt(), Result :: {'ok', Term :: term()} - | 'eof' - | {'error', ErrorInfo :: erl_scan:error_info()}. + | server_no_data() + | {'error', ErrorInfo}, + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(). % Read does not use get_until as erl_scan does not work with unicode % XXX:PaN fixme? @@ -231,8 +230,9 @@ read(Prompt) -> IoDevice :: device(), Prompt :: prompt(), Result :: {'ok', Term :: term()} - | 'eof' - | {'error', ErrorInfo :: erl_scan:error_info()}. + | server_no_data() + | {'error', ErrorInfo}, + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(). read(Io, Prompt) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[1]}) of @@ -248,24 +248,41 @@ read(Io, Prompt) -> Other end. --spec read(IoDevice, Prompt, StartLine) -> Result when +-spec read(IoDevice, Prompt, StartLocation) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLocation :: location(), + Result :: {'ok', Term :: term(), EndLocation :: location()} + | {'eof', EndLocation :: location()} + | server_no_data() + | {'error', ErrorInfo, ErrorLocation :: location()}, + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(). + +read(Io, Prompt, Pos0) -> + read(Io, Prompt, Pos0, []). + +-spec read(IoDevice, Prompt, StartLocation, Options) -> Result when IoDevice :: device(), Prompt :: prompt(), - StartLine :: line(), - Result :: {'ok', Term :: term(), EndLine :: line()} - | {'eof', EndLine :: line()} - | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()}. - -read(Io, Prompt, StartLine) when is_integer(StartLine) -> - case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[StartLine]}) of - {ok,Toks,EndLine} -> + StartLocation :: location(), + Options :: erl_scan:options(), + Result :: {'ok', Term :: term(), EndLocation :: location()} + | {'eof', EndLocation :: location()} + | server_no_data() + | {'error', ErrorInfo, ErrorLocation :: location()}, + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(). + +read(Io, Prompt, Pos0, Options) -> + Args = [Pos0,Options], + case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,Args}) of + {ok,Toks,EndLocation} -> case erl_parse:parse_term(Toks) of - {ok,Term} -> {ok,Term,EndLine}; - {error,ErrorInfo} -> {error,ErrorInfo,EndLine} + {ok,Term} -> {ok,Term,EndLocation}; + {error,ErrorInfo} -> {error,ErrorInfo,EndLocation} end; - {error,_E,_EndLine} = Error -> + {error,_E,_EndLocation} = Error -> Error; - {eof,_EndLine} = Eof -> + {eof,_EndLocation} = Eof -> Eof; Other -> Other @@ -313,7 +330,9 @@ fread(Prompt, Format) -> IoDevice :: device(), Prompt :: prompt(), Format :: format(), - Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}. + Result :: {'ok', Terms :: [term()]} + | {'error', FreadError :: io_lib:fread_error()} + | server_no_data(). fread(Io, Prompt, Format) -> case request(Io, {fread,Prompt,Format}) of @@ -348,7 +367,7 @@ format(Io, Format, Args) -> -spec scan_erl_exprs(Prompt) -> Result when Prompt :: prompt(), - Result :: erl_scan:tokens_result() | request_error(). + Result :: erl_scan:tokens_result() | server_no_data(). scan_erl_exprs(Prompt) -> scan_erl_exprs(default_input(), Prompt, 1). @@ -356,23 +375,33 @@ scan_erl_exprs(Prompt) -> -spec scan_erl_exprs(Device, Prompt) -> Result when Device :: device(), Prompt :: prompt(), - Result :: erl_scan:tokens_result() | request_error(). + Result :: erl_scan:tokens_result() | server_no_data(). scan_erl_exprs(Io, Prompt) -> scan_erl_exprs(Io, Prompt, 1). --spec scan_erl_exprs(Device, Prompt, StartLine) -> Result when +-spec scan_erl_exprs(Device, Prompt, StartLocation) -> Result when Device :: device(), Prompt :: prompt(), - StartLine :: line(), - Result :: erl_scan:tokens_result() | request_error(). + StartLocation :: location(), + Result :: erl_scan:tokens_result() | server_no_data(). scan_erl_exprs(Io, Prompt, Pos0) -> - request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). + scan_erl_exprs(Io, Prompt, Pos0, []). + +-spec scan_erl_exprs(Device, Prompt, StartLocation, Options) -> Result when + Device :: device(), + Prompt :: prompt(), + StartLocation :: location(), + Options :: erl_scan:options(), + Result :: erl_scan:tokens_result() | server_no_data(). + +scan_erl_exprs(Io, Prompt, Pos0, Options) -> + request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}). -spec scan_erl_form(Prompt) -> Result when Prompt :: prompt(), - Result :: erl_scan:tokens_result() | request_error(). + Result :: erl_scan:tokens_result() | server_no_data(). scan_erl_form(Prompt) -> scan_erl_form(default_input(), Prompt, 1). @@ -380,26 +409,41 @@ scan_erl_form(Prompt) -> -spec scan_erl_form(IoDevice, Prompt) -> Result when IoDevice :: device(), Prompt :: prompt(), - Result :: erl_scan:tokens_result() | request_error(). + Result :: erl_scan:tokens_result() | server_no_data(). scan_erl_form(Io, Prompt) -> scan_erl_form(Io, Prompt, 1). --spec scan_erl_form(IoDevice, Prompt, StartLine) -> Result when +-spec scan_erl_form(IoDevice, Prompt, StartLocation) -> Result when IoDevice :: device(), Prompt :: prompt(), - StartLine :: line(), - Result :: erl_scan:tokens_result() | request_error(). + StartLocation :: location(), + Result :: erl_scan:tokens_result() | server_no_data(). scan_erl_form(Io, Prompt, Pos0) -> - request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). + scan_erl_form(Io, Prompt, Pos0, []). + +-spec scan_erl_form(IoDevice, Prompt, StartLocation, Options) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLocation :: location(), + Options :: erl_scan:options(), + Result :: erl_scan:tokens_result() | server_no_data(). + +scan_erl_form(Io, Prompt, Pos0, Options) -> + request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}). %% Parsing Erlang code. --type parse_ret() :: {'ok', ExprList :: erl_parse:abstract_expr(), EndLine :: line()} - | {'eof', EndLine :: line()} - | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()} - | request_error(). +-type parse_ret() :: {'ok', + ExprList :: erl_parse:abstract_expr(), + EndLocation :: location()} + | {'eof', EndLocation :: location()} + | {'error', + ErrorInfo :: erl_scan:error_info() + | erl_parse:error_info(), + ErrorLocation :: location()} + | server_no_data(). -spec parse_erl_exprs(Prompt) -> Result when Prompt :: prompt(), @@ -416,14 +460,24 @@ parse_erl_exprs(Prompt) -> parse_erl_exprs(Io, Prompt) -> parse_erl_exprs(Io, Prompt, 1). --spec parse_erl_exprs(IoDevice, Prompt, StartLine) -> Result when +-spec parse_erl_exprs(IoDevice, Prompt, StartLocation) -> Result when IoDevice :: device(), Prompt :: prompt(), - StartLine :: line(), + StartLocation :: location(), Result :: parse_ret(). parse_erl_exprs(Io, Prompt, Pos0) -> - case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of + parse_erl_exprs(Io, Prompt, Pos0, []). + +-spec parse_erl_exprs(IoDevice, Prompt, StartLocation, Options) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLocation :: location(), + Options :: erl_scan:options(), + Result :: parse_ret(). + +parse_erl_exprs(Io, Prompt, Pos0, Options) -> + case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}) of {ok,Toks,EndPos} -> case erl_parse:parse_exprs(Toks) of {ok,Exprs} -> {ok,Exprs,EndPos}; @@ -433,10 +487,15 @@ parse_erl_exprs(Io, Prompt, Pos0) -> Other end. --type parse_form_ret() :: {'ok', AbsForm :: erl_parse:abstract_form(), EndLine :: line()} - | {'eof', EndLine :: line()} - | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()} - | request_error(). +-type parse_form_ret() :: {'ok', + AbsForm :: erl_parse:abstract_form(), + EndLocation :: location()} + | {'eof', EndLocation :: location()} + | {'error', + ErrorInfo :: erl_scan:error_info() + | erl_parse:error_info(), + ErrorLocation :: location()} + | server_no_data(). -spec parse_erl_form(Prompt) -> Result when Prompt :: prompt(), @@ -453,14 +512,25 @@ parse_erl_form(Prompt) -> parse_erl_form(Io, Prompt) -> parse_erl_form(Io, Prompt, 1). --spec parse_erl_form(IoDevice, Prompt, StartLine) -> Result when +-spec parse_erl_form(IoDevice, Prompt, StartLocation) -> Result when IoDevice :: device(), Prompt :: prompt(), - StartLine :: line(), + StartLocation :: location(), Result :: parse_form_ret(). parse_erl_form(Io, Prompt, Pos0) -> - case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of + parse_erl_form(Io, Prompt, Pos0, []). + +-spec parse_erl_form(IoDevice, Prompt, StartLocation, Options) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLocation :: location(), + Options :: erl_scan:options(), + Result :: parse_form_ret(). + +parse_erl_form(Io, Prompt, Pos0, Options) -> + Args = [Pos0, Options], + case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,Args}) of {ok,Toks,EndPos} -> case erl_parse:parse_form(Toks) of {ok,Exprs} -> {ok,Exprs,EndPos}; diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index 513d904c39..5ad505f683 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -46,25 +47,28 @@ %% 173 - 176 { - ~ punctuation %% 177 DEL control %% 200 - 237 control -%% 240 - 277 NBSP - � punctuation -%% 300 - 326 � - � uppercase -%% 327 � punctuation -%% 330 - 336 � - � uppercase -%% 337 - 366 � - � lowercase -%% 367 � punctuation -%% 370 - 377 � - � lowercase +%% 240 - 277 NBSP - ¿ punctuation +%% 300 - 326 À - Ö uppercase +%% 327 × punctuation +%% 330 - 336 Ø - Þ uppercase +%% 337 - 366 ß - ö lowercase +%% 367 ÷ punctuation +%% 370 - 377 ø - ÿ lowercase %% %% Many punctuation characters region have special meaning. Must -%% watch using � \327, very close to x \170 +%% watch using × \327, very close to x \170 -module(io_lib). -export([fwrite/2,fread/2,fread/3,format/2]). -export([print/1,print/4,indentation/2]). --export([write/1,write/2,write/3,nl/0,format_prompt/1]). +-export([write/1,write/2,write/3,nl/0,format_prompt/1,format_prompt/2]). -export([write_atom/1,write_string/1,write_string/2,write_unicode_string/1, - write_unicode_string/2, write_char/1, write_unicode_char/1]). + write_unicode_string/2, write_char/1, write_unicode_char/1]). + +-export([write_unicode_string_as_latin1/1, write_unicode_string_as_latin1/2, + write_unicode_char_as_latin1/1]). -export([quote_atom/2, char_list/1, unicode_char_list/1, deep_char_list/1, deep_unicode_char_list/1, @@ -75,11 +79,14 @@ collect_line/2, collect_line/3, collect_line/4, get_until/3, get_until/4]). --export_type([chars/0, continuation/0]). +-export_type([chars/0, unicode_chars/0, unicode_string/0, continuation/0, + fread_error/0]). %%---------------------------------------------------------------------- -type chars() :: [char() | chars()]. +-type unicode_chars() :: [unicode:unicode_char() | unicode_chars()]. +-type unicode_string() :: [unicode:unicode_char()]. -type depth() :: -1 | non_neg_integer(). -opaque continuation() :: {Format :: string(), @@ -87,6 +94,16 @@ Nchars :: non_neg_integer(), Results :: [term()]}. +-type fread_error() :: 'atom' + | 'based' + | 'character' + | 'float' + | 'format' + | 'input' + | 'integer' + | 'string' + | 'unsigned'. + %%---------------------------------------------------------------------- %% Interface calls to sub-modules. @@ -107,7 +124,7 @@ fwrite(Format, Args) -> | {'more', RestFormat :: string(), Nchars :: non_neg_integer(), InputStack :: chars()} - | {'error', What :: term()}. + | {'error', What :: fread_error()}. fread(Chars, Format) -> io_lib_fread:fread(Chars, Format). @@ -120,7 +137,7 @@ fread(Chars, Format) -> | {'done', Result, LeftOverChars :: string()}, Result :: {'ok', InputList :: [term()]} | 'eof' - | {'error', What :: term()}. + | {'error', What :: fread_error()}. fread(Cont, Chars, Format) -> io_lib_fread:fread(Cont, Chars, Format). @@ -162,27 +179,34 @@ indentation(Chars, Current) -> %% Format an IO-request prompt (handles formatting errors safely). -%% Atoms, binaries, and iolists can be used as-is, and will be -%% printed without any additional quotes. -%% Note that the output is a deep string, and not an iolist (i.e., -%% it may be deep, but never contains binaries, due to the "~s"). +%% Atoms, binaries, and iolists (or unicode:charlist()) can be used +%% as-is, and will be printed without any additional quotes. -spec format_prompt(term()) -> chars(). -format_prompt({format,Format,Args}) -> - format_prompt(Format,Args); -format_prompt(Prompt) - when is_list(Prompt); is_atom(Prompt); is_binary(Prompt) -> - format_prompt("~ts", [Prompt]); format_prompt(Prompt) -> - format_prompt("~tp", [Prompt]). + format_prompt(Prompt, latin1). + +-spec format_prompt(term(), atom()) -> chars(). + +format_prompt({format,Format,Args}, _Encoding) -> + do_format_prompt(Format, Args); +format_prompt(Prompt, Encoding) + when is_list(Prompt); is_atom(Prompt); is_binary(Prompt) -> + do_format_prompt(add_modifier(Encoding, "s"), [Prompt]); +format_prompt(Prompt, Encoding) -> + do_format_prompt(add_modifier(Encoding, "p"), [Prompt]). -format_prompt(Format, Args) -> +do_format_prompt(Format, Args) -> case catch io_lib:format(Format, Args) of {'EXIT',_} -> "???"; List -> List end. +add_modifier(latin1, C) -> + "~"++C; +add_modifier(_, C) -> + "~t"++C. %% write(Term) %% write(Term, Depth) @@ -294,7 +318,7 @@ quote_atom(Atom, Cs0) -> case Cs0 of [C|Cs] when C >= $a, C =< $z -> not name_chars(Cs); - [C|Cs] when C >= $�, C =< $�, C =/= $� -> + [C|Cs] when C >= $ß, C =< $ÿ, C =/= $÷ -> not name_chars(Cs); _ -> true end @@ -308,9 +332,9 @@ name_chars([C|Cs]) -> name_chars([]) -> true. name_char(C) when C >= $a, C =< $z -> true; -name_char(C) when C >= $�, C =< $�, C =/= $� -> true; +name_char(C) when C >= $ß, C =< $ÿ, C =/= $÷ -> true; name_char(C) when C >= $A, C =< $Z -> true; -name_char(C) when C >= $�, C =< $�, C =/= $� -> true; +name_char(C) when C >= $À, C =< $Þ, C =/= $× -> true; name_char(C) when C >= $0, C =< $9 -> true; name_char($_) -> true; name_char($@) -> true; @@ -330,11 +354,32 @@ write_string(S) -> write_string(S, Q) -> [Q|write_string1(latin1, S, Q)]. +%%% There are two functions to write Unicode strings: +%%% - they both escape control characters < 160; +%%% - write_unicode_string() never escapes characters >= 160; +%%% - write_unicode_string_as_latin1() also escapes characters >= 255. + +-spec write_unicode_string(UnicodeString) -> unicode_string() when + UnicodeString :: unicode_string(). + write_unicode_string(S) -> write_unicode_string(S, $"). %" +-spec write_unicode_string(unicode_string(), char()) -> unicode_string(). + write_unicode_string(S, Q) -> - [Q|write_string1(unicode, S, Q)]. + [Q|write_string1(unicode_as_unicode, S, Q)]. + +-spec write_unicode_string_as_latin1(UnicodeString) -> string() when + UnicodeString :: unicode_string(). + +write_unicode_string_as_latin1(S) -> + write_unicode_string_as_latin1(S, $"). %" + +-spec write_unicode_string_as_latin1(unicode_string(), char()) -> string(). + +write_unicode_string_as_latin1(S, Q) -> + [Q|write_string1(unicode_as_latin1, S, Q)]. write_string1(_,[], Q) -> [Q]; @@ -347,7 +392,11 @@ string_char(_,C, _, Tail) when C >= $\s, C =< $~ -> [C|Tail]; string_char(latin1,C, _, Tail) when C >= $\240, C =< $\377 -> [C|Tail]; -string_char(unicode,C, _, Tail) when C >= $\240 -> +string_char(unicode_as_unicode,C, _, Tail) when C >= $\240 -> + [C|Tail]; +string_char(unicode_as_latin1,C, _, Tail) when C >= $\240, C =< $\377 -> + [C|Tail]; +string_char(unicode_as_latin1,C, _, Tail) when C >= $\377 -> "\\x{"++erlang:integer_to_list(C, 16)++"}"++Tail; string_char(_,$\n, _, Tail) -> [$\\,$n|Tail]; %\n = LF string_char(_,$\r, _, Tail) -> [$\\,$r|Tail]; %\r = CR @@ -374,10 +423,22 @@ write_char($\s) -> "$\\s"; %Must special case this. write_char(C) when is_integer(C), C >= $\000, C =< $\377 -> [$$|string_char(latin1,C, -1, [])]. -write_unicode_char(Ch) when Ch =< 255 -> - write_char(Ch); -write_unicode_char(Uni) -> - [$$|string_char(unicode,Uni, -1, [])]. +%%% There are two functions to write a Unicode character: +%%% - they both escape control characters < 160; +%%% - write_unicode_char() never escapes characters >= 160; +%%% - write_unicode_char_as_latin1() also escapes characters >= 255. + +-spec write_unicode_char(UnicodeChar) -> unicode_string() when + UnicodeChar :: unicode:unicode_char(). + +write_unicode_char(Uni) when is_integer(Uni), Uni >= $\000 -> + [$$|string_char(unicode_as_unicode,Uni, -1, [])]. + +-spec write_unicode_char_as_latin1(UnicodeChar) -> string() when + UnicodeChar :: unicode:unicode_char(). + +write_unicode_char_as_latin1(Uni) when is_integer(Uni), Uni >= $\000 -> + [$$|string_char(unicode_as_latin1,Uni, -1, [])]. %% char_list(CharList) %% deep_char_list(CharList) @@ -392,7 +453,8 @@ char_list([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 -> char_list([]) -> true; char_list(_) -> false. %Everything else is false --spec unicode_char_list(term()) -> boolean(). +-spec unicode_char_list(Term) -> boolean() when + Term :: term(). unicode_char_list([C|Cs]) when is_integer(C), C >= 0, C < 16#D800; is_integer(C), C > 16#DFFF, C < 16#FFFE; @@ -417,7 +479,8 @@ deep_char_list([], []) -> true; deep_char_list(_, _More) -> %Everything else is false false. --spec deep_unicode_char_list(term()) -> boolean(). +-spec deep_unicode_char_list(Term) -> boolean() when + Term :: term(). deep_unicode_char_list(Cs) -> deep_unicode_char_list(Cs, []). @@ -462,7 +525,8 @@ printable_list(_) -> false. %Everything else is false %% Everything that is not a control character and not invalid unicode %% will be considered printable. --spec printable_unicode_list(term()) -> boolean(). +-spec printable_unicode_list(Term) -> boolean() when + Term :: term(). printable_unicode_list([C|Cs]) when is_integer(C), C >= $\040, C =< $\176 -> printable_unicode_list(Cs); diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl index 49a00a4ec7..5680f83ab6 100644 --- a/lib/stdlib/src/io_lib_format.erl +++ b/lib/stdlib/src/io_lib_format.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -22,7 +22,7 @@ -export([fwrite/2,fwrite_g/1,indentation/2]). -%% fwrite(Format, ArgList) -> [Char]. +%% fwrite(Format, ArgList) -> [unicode:unicode:char()]. %% Format the arguments in ArgList after string Format. Just generate %% an error if there is an error in the arguments. %% @@ -133,7 +133,7 @@ pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc}|Cs], Acc) -> pcount(Cs, Acc+1); pcount([_|Cs], Acc) -> pcount(Cs, Acc); pcount([], Acc) -> Acc. -%% build([Control], Pc, Indentation) -> [Char]. +%% build([Control], Pc, Indentation) -> [unicode:unicode_char()]. %% Interpret the control structures. Count the number of print %% remaining and only calculate indentation when necessary. Must also %% be smart when calculating indentation for characters in format. @@ -154,7 +154,7 @@ decr_pc($p, Pc) -> Pc - 1; decr_pc($P, Pc) -> Pc - 1; decr_pc(_, Pc) -> Pc. -%% indentation([Char], Indentation) -> Indentation. +%% indentation([unicode:unicode_char()], Indentation) -> Indentation. %% Calculate the indentation of the end of a string given its start %% indentation. We assume tabs at 8 cols. @@ -167,19 +167,19 @@ indentation([C|Cs], I) -> indentation([], I) -> I. %% control(FormatChar, [Argument], FieldWidth, Adjust, Precision, PadChar, -%% Indentation) -> -%% [Char] +%% Encoding, Indentation) -> +%% [unicode:unicode_char()] %% This is the main dispatch function for the various formatting commands. %% Field widths and precisions have already been calculated. control($w, [A], F, Adj, P, Pad, _Enc,_I) -> term(io_lib:write(A, -1), F, Adj, P, Pad); -control($p, [A], F, Adj, P, Pad, _Enc, I) -> - print(A, -1, F, Adj, P, Pad, I); +control($p, [A], F, Adj, P, Pad, Enc, I) -> + print(A, -1, F, Adj, P, Pad, Enc, I); control($W, [A,Depth], F, Adj, P, Pad, _Enc, _I) when is_integer(Depth) -> term(io_lib:write(A, Depth), F, Adj, P, Pad); -control($P, [A,Depth], F, Adj, P, Pad, _Enc, I) when is_integer(Depth) -> - print(A, Depth, F, Adj, P, Pad, I); +control($P, [A,Depth], F, Adj, P, Pad, Enc, I) when is_integer(Depth) -> + print(A, Depth, F, Adj, P, Pad, Enc, I); control($s, [A], F, Adj, P, Pad, _Enc, _I) when is_atom(A) -> string(atom_to_list(A), F, Adj, P, Pad); control($s, [L0], F, Adj, P, Pad, latin1, _I) -> @@ -187,6 +187,7 @@ control($s, [L0], F, Adj, P, Pad, latin1, _I) -> string(L, F, Adj, P, Pad); control($s, [L0], F, Adj, P, Pad, unicode, _I) -> L = unicode:characters_to_list(L0), + true = is_list(L), uniconv(string(L, F, Adj, P, Pad)); control($e, [A], F, Adj, P, Pad, _Enc, _I) when is_float(A) -> fwrite_e(A, F, Adj, P, Pad); @@ -256,13 +257,17 @@ term(T, F, Adj, P0, Pad) -> adjust(T, chars(Pad, F-L), Adj) end. -%% print(Term, Depth, Field, Adjust, Precision, PadChar, Indentation) +%% print(Term, Depth, Field, Adjust, Precision, PadChar, Encoding, +%% Indentation) %% Print a term. -print(T, D, none, Adj, P, Pad, I) -> print(T, D, 80, Adj, P, Pad, I); -print(T, D, F, Adj, none, Pad, I) -> print(T, D, F, Adj, I+1, Pad, I); -print(T, D, F, right, P, _Pad, _I) -> - io_lib_pretty:print(T, P, F, D). +print(T, D, none, Adj, P, Pad, E, I) -> print(T, D, 80, Adj, P, Pad, E, I); +print(T, D, F, Adj, none, Pad, E, I) -> print(T, D, F, Adj, I+1, Pad, E, I); +print(T, D, F, right, P, _Pad, latin1, _I) -> + io_lib_pretty:print(T, P, F, D); +print(T, D, F, right, P, _Pad, Enc, _I) -> + Options = [{column, P}, {line_length, F}, {depth, D}, {encoding, Enc}], + io_lib_pretty:print(T, Options). %% fwrite_e(Float, Field, Adjust, Precision, PadChar) @@ -608,7 +613,7 @@ prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase) term([Prefix|S], F, Adj, none, Pad) end. -%% char(Char, Field, Adjust, Precision, PadChar) -> [Char]. +%% char(Char, Field, Adjust, Precision, PadChar) -> [unicode:unicode_char()]. char(C, none, _Adj, none, _Pad) -> [C]; char(C, F, _Adj, none, _Pad) -> chars(C, F); diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl index ded1346097..84d4b8bba0 100644 --- a/lib/stdlib/src/io_lib_fread.erl +++ b/lib/stdlib/src/io_lib_fread.erl @@ -43,7 +43,7 @@ | {'done', Result, LeftOverChars :: string()}, Result :: {'ok', InputList :: io_lib:chars()} | 'eof' - | {'error', What :: term()}. + | {'error', What :: io_lib:fread_error()}. fread([], Chars, Format) -> %%io:format("FREAD: ~w `~s'~n", [Format,Chars]), diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl index 169410796b..99ad281a9b 100644 --- a/lib/stdlib/src/io_lib_pretty.erl +++ b/lib/stdlib/src/io_lib_pretty.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -33,43 +33,76 @@ %% print(Term, Column, LineLength, Depth) -> [Chars] %% Depth = -1 gives unlimited print depth. Use io_lib:write for atomic terms. +-spec print(term()) -> io_lib:chars(). + print(Term) -> print(Term, 1, 80, -1). %% print(Term, RecDefFun) -> [Chars] %% print(Term, Depth, RecDefFun) -> [Chars] %% RecDefFun = fun(Tag, NoFields) -> [FieldTag] | no -%% Used by the shell for printing records. +%% Used by the shell for printing records and for Unicode. + +-type rec_print_fun() :: fun((Tag :: atom(), NFields :: non_neg_integer()) -> + no | [FieldName :: atom()]). +-type column() :: integer(). +-type line_length() :: pos_integer(). +-type depth() :: integer(). +-type max_chars() :: integer(). + +-type chars() :: io_lib:chars(). +-type unicode_chars() :: io_lib:unicode_chars(). +-type option() :: {column, column()} + | {line_length, line_length()} + | {depth, depth()} + | {max_chars, max_chars()} + | {record_print_fun, rec_print_fun()} + | {encoding, latin1 | utf8 | unicode}. +-type options() :: [option()]. + +-spec print(term(), rec_print_fun()) -> chars() | unicode_chars(); + (term(), options()) -> chars() | unicode_chars(). + +print(Term, Options) when is_list(Options) -> + Col = proplists:get_value(column, Options, 1), + Ll = proplists:get_value(line_length, Options, 80), + D = proplists:get_value(depth, Options, -1), + M = proplists:get_value(max_chars, Options, -1), + RecDefFun = proplists:get_value(record_print_fun, Options, no_fun), + Encoding = proplists:get_value(encoding, Options, epp:default_encoding()), + print(Term, Col, Ll, D, M, RecDefFun, Encoding); print(Term, RecDefFun) -> print(Term, -1, RecDefFun). +-spec print(term(), depth(), rec_print_fun()) -> chars() | unicode_chars(). + print(Term, Depth, RecDefFun) -> print(Term, 1, 80, Depth, RecDefFun). +-spec print(term(), column(), line_length(), depth()) -> + chars() | unicode_chars(). + print(Term, Col, Ll, D) -> - print(Term, Col, Ll, D, _M=-1, no_fun). + print(Term, Col, Ll, D, _M=-1, no_fun, latin1). +-spec print(term(), column(), line_length(), depth(), rec_print_fun()) -> + chars() | unicode_chars(). print(Term, Col, Ll, D, RecDefFun) -> print(Term, Col, Ll, D, _M=-1, RecDefFun). -print(_, _, _, 0, _M, _RF) -> "..."; -print(Term, Col, Ll, D, M, RecDefFun) when Col =< 0 -> - print(Term, 1, Ll, D, M, RecDefFun); -print(Term, Col, Ll, D, M0, RecDefFun) when is_tuple(Term); - is_list(Term) -> - If = {_S, Len} = print_length(Term, D, RecDefFun), - M = max_cs(M0, Len), - if - Len < Ll - Col, Len =< M -> - write(If); - true -> - TInd = while_fail([-1, 4], - fun(I) -> cind(If, Col, Ll, M, I, 0, 0) end, - 1), - pp(If, Col, Ll, M, TInd, indent(Col), 0, 0) - end; -print(<<_/bitstring>>=Term, Col, Ll, D, M0, RecDefFun) -> - If = {_S, Len} = print_length(Term, D, RecDefFun), +-spec print(term(), column(), line_length(), depth(), max_chars(), + rec_print_fun()) -> chars() | unicode_chars(). + +print(Term, Col, Ll, D, M, RecDefFun) -> + print(Term, Col, Ll, D, M, RecDefFun, latin1). + +print(_, _, _, 0, _M, _RF, _Enc) -> "..."; +print(Term, Col, Ll, D, M, RecDefFun, Enc) when Col =< 0 -> + print(Term, 1, Ll, D, M, RecDefFun, Enc); +print(Term, Col, Ll, D, M0, RecDefFun, Enc) when is_tuple(Term); + is_list(Term); + is_bitstring(Term) -> + If = {_S, Len} = print_length(Term, D, RecDefFun, Enc), M = max_cs(M0, Len), if Len < Ll - Col, Len =< M -> @@ -80,7 +113,7 @@ print(<<_/bitstring>>=Term, Col, Ll, D, M0, RecDefFun) -> 1), pp(If, Col, Ll, M, TInd, indent(Col), 0, 0) end; -print(Term, _Col, _Ll, _D, _M, _RF) -> +print(Term, _Col, _Ll, _D, _M, _RF, _Enc) -> io_lib:write(Term). %%% @@ -294,50 +327,56 @@ write_tail(E, S) -> %% counted but need to be added later. %% D =/= 0 -print_length([], _D, _RF) -> +print_length([], _D, _RF, _Enc) -> {"[]", 2}; -print_length({}, _D, _RF) -> +print_length({}, _D, _RF, _Enc) -> {"{}", 2}; -print_length(List, D, RF) when is_list(List) -> - case printable_list(List, D) of +print_length(List, D, RF, Enc) when is_list(List) -> + case printable_list(List, D, Enc) of true -> - S = io_lib:write_string(List, $"), %" + S = write_string(List, Enc), {S, length(S)}; %% Truncated lists could break some existing code. % {true, Prefix} -> - % S = io_lib:write_string(Prefix, $"), %" + % S = write_string(Prefix, Enc), % {[S | "..."], 3 + length(S)}; false -> - print_length_list(List, D, RF) + print_length_list(List, D, RF, Enc) end; -print_length(Fun, _D, _RF) when is_function(Fun) -> +print_length(Fun, _D, _RF, _Enc) when is_function(Fun) -> S = io_lib:write(Fun), {S, iolist_size(S)}; -print_length(R, D, RF) when is_atom(element(1, R)), - is_function(RF) -> +print_length(R, D, RF, Enc) when is_atom(element(1, R)), + is_function(RF) -> case RF(element(1, R), tuple_size(R) - 1) of no -> - print_length_tuple(R, D, RF); + print_length_tuple(R, D, RF, Enc); RDefs -> - print_length_record(R, D, RF, RDefs) + print_length_record(R, D, RF, RDefs, Enc) end; -print_length(Tuple, D, RF) when is_tuple(Tuple) -> - print_length_tuple(Tuple, D, RF); -print_length(<<>>, _D, _RF) -> +print_length(Tuple, D, RF, Enc) when is_tuple(Tuple) -> + print_length_tuple(Tuple, D, RF, Enc); +print_length(<<>>, _D, _RF, _Enc) -> {"<<>>", 4}; -print_length(<<_/bitstring>>, 1, _RF) -> +print_length(<<_/bitstring>>, 1, _RF, _Enc) -> {"<<...>>", 7}; -print_length(<<_/bitstring>>=Bin, D, _RF) -> +print_length(<<_/bitstring>>=Bin, D, _RF, Enc) -> case bit_size(Bin) rem 8 of 0 -> D1 = D - 1, - case printable_bin(Bin, D1) of - List when is_list(List) -> - S = io_lib:write_string(List, $"), + case printable_bin(Bin, D1, Enc) of + {true, List} when is_list(List) -> + S = io_lib:write_string(List, $"), %" {[$<,$<,S,$>,$>], 4 + length(S)}; - {true, Prefix} -> - S = io_lib:write_string(Prefix, $"), - {[$<,$<, S | "...>>"], 4 + length(S)}; + {false, List} when is_list(List) -> + S = io_lib:write_unicode_string(List, $"), %" + {[$<,$<,S,"/utf8>>"], 9 + length(S)}; + {true, true, Prefix} -> + S = io_lib:write_string(Prefix, $"), %" + {[$<,$<, S | "...>>"], 7 + length(S)}; + {false, true, Prefix} -> + S = io_lib:write_unicode_string(Prefix, $"), %" + {[$<,$<, S | "/utf8...>>"], 12 + length(S)}; false -> S = io_lib:write(Bin, D), {{bin,S}, iolist_size(S)} @@ -346,51 +385,51 @@ print_length(<<_/bitstring>>=Bin, D, _RF) -> S = io_lib:write(Bin, D), {{bin,S}, iolist_size(S)} end; -print_length(Term, _D, _RF) -> +print_length(Term, _D, _RF, _Enc) -> S = io_lib:write(Term), {S, iolist_size(S)}. -print_length_tuple(_Tuple, 1, _RF) -> +print_length_tuple(_Tuple, 1, _RF, _Enc) -> {"{...}", 5}; -print_length_tuple(Tuple, D, RF) -> - L = print_length_list1(tuple_to_list(Tuple), D, RF), +print_length_tuple(Tuple, D, RF, Enc) -> + L = print_length_list1(tuple_to_list(Tuple), D, RF, Enc), IsTagged = is_atom(element(1, Tuple)) and (tuple_size(Tuple) > 1), {{tuple,IsTagged,L}, list_length(L, 2)}. -print_length_record(_Tuple, 1, _RF, _RDefs) -> +print_length_record(_Tuple, 1, _RF, _RDefs, _Enc) -> {"{...}", 5}; -print_length_record(Tuple, D, RF, RDefs) -> +print_length_record(Tuple, D, RF, RDefs, Enc) -> Name = [$# | io_lib:write_atom(element(1, Tuple))], NameL = length(Name), - L = print_length_fields(RDefs, D - 1, tl(tuple_to_list(Tuple)), RF), + L = print_length_fields(RDefs, D - 1, tl(tuple_to_list(Tuple)), RF, Enc), {{record, [{Name,NameL} | L]}, list_length(L, NameL + 2)}. -print_length_fields([], _D, [], _RF) -> +print_length_fields([], _D, [], _RF, _Enc) -> []; -print_length_fields(_, 1, _, _RF) -> +print_length_fields(_, 1, _, _RF, _Enc) -> {dots, 3}; -print_length_fields([Def | Defs], D, [E | Es], RF) -> - [print_length_field(Def, D - 1, E, RF) | - print_length_fields(Defs, D - 1, Es, RF)]. +print_length_fields([Def | Defs], D, [E | Es], RF, Enc) -> + [print_length_field(Def, D - 1, E, RF, Enc) | + print_length_fields(Defs, D - 1, Es, RF, Enc)]. -print_length_field(Def, D, E, RF) -> +print_length_field(Def, D, E, RF, Enc) -> Name = io_lib:write_atom(Def), - {S, L} = print_length(E, D, RF), + {S, L} = print_length(E, D, RF, Enc), NameL = length(Name) + 3, {{field, Name, NameL, {S, L}}, NameL + L}. -print_length_list(List, D, RF) -> - L = print_length_list1(List, D, RF), +print_length_list(List, D, RF, Enc) -> + L = print_length_list1(List, D, RF, Enc), {{list, L}, list_length(L, 2)}. -print_length_list1([], _D, _RF) -> +print_length_list1([], _D, _RF, _Enc) -> []; -print_length_list1(_, 1, _RF) -> +print_length_list1(_, 1, _RF, _Enc) -> {dots, 3}; -print_length_list1([E | Es], D, RF) -> - [print_length(E, D - 1, RF) | print_length_list1(Es, D - 1, RF)]; -print_length_list1(E, D, RF) -> - print_length(E, D - 1, RF). +print_length_list1([E | Es], D, RF, Enc) -> + [print_length(E, D - 1, RF, Enc) | print_length_list1(Es, D - 1, RF, Enc)]; +print_length_list1(E, D, RF, Enc) -> + print_length(E, D - 1, RF, Enc). list_length([], Acc) -> Acc; @@ -409,16 +448,16 @@ list_length_tail({_, Len}, Acc) -> %% ?CHARS printable characters has depth 1. -define(CHARS, 4). -printable_list(L, D) when D < 0 -> - io_lib:printable_list(L); -printable_list(_L, 1) -> +printable_list(_L, 1, _Enc) -> false; -printable_list(L, _D) -> - io_lib:printable_list(L). +printable_list(L, _D, latin1) -> + io_lib:printable_list(L); +printable_list(L, _D, _Uni) -> + io_lib:printable_unicode_list(L). %% Truncated lists could break some existing code. -% printable_list(L, D) -> +% printable_list(L, D, Enc) when D >= 0 -> % Len = ?CHARS * (D - 1), -% case printable_list1(L, Len) of +% case printable_list1(L, Len, Enc) of % all -> % true; % N when is_integer(N), Len - N >= D - 1 -> @@ -428,32 +467,41 @@ printable_list(L, _D) -> % false % end. -printable_bin(Bin, D) when D >= 0, ?CHARS * D =< byte_size(Bin) -> - printable_bin(Bin, erlang:min(?CHARS * D, byte_size(Bin)), D); -printable_bin(Bin, D) -> - printable_bin(Bin, byte_size(Bin), D). +printable_bin(Bin, D, Enc) when D >= 0, ?CHARS * D =< byte_size(Bin) -> + printable_bin(Bin, erlang:min(?CHARS * D, byte_size(Bin)), D, Enc); +printable_bin(Bin, D, Enc) -> + printable_bin(Bin, byte_size(Bin), D, Enc). -printable_bin(Bin, Len, D) -> +printable_bin(Bin, Len, D, latin1) -> N = erlang:min(20, Len), L = binary_to_list(Bin, 1, N), case printable_list1(L, N) of all when N =:= byte_size(Bin) -> - L; - all when N =:= Len -> % N < byte_size(Bin) {true, L}; + all when N =:= Len -> % N < byte_size(Bin) + {true, true, L}; all -> case printable_bin1(Bin, 1 + N, Len - N) of 0 when byte_size(Bin) =:= Len -> - binary_to_list(Bin); + {true, binary_to_list(Bin)}; NC when D > 0, Len - NC >= D -> - {true, binary_to_list(Bin, 1, Len - NC)}; + {true, true, binary_to_list(Bin, 1, Len - NC)}; NC when is_integer(NC) -> false end; NC when is_integer(NC), D > 0, N - NC >= D -> - {true, binary_to_list(Bin, 1, N - NC)}; + {true, true, binary_to_list(Bin, 1, N - NC)}; NC when is_integer(NC) -> false + end; +printable_bin(Bin, Len, D, _Uni) -> + case printable_unicode(Bin, Len, []) of + {_, <<>>, L} -> + {byte_size(Bin) =:= length(L), L}; + {NC, Bin1, L} when D > 0, Len - NC >= D -> + {byte_size(Bin)-byte_size(Bin1) =:= length(L), true, L}; + {_NC, _Bin, _L} -> + false end. printable_bin1(_Bin, _Start, 0) -> @@ -484,6 +532,16 @@ printable_list1([$\e | Cs], N) -> printable_list1(Cs, N - 1); printable_list1([], _) -> all; printable_list1(_, N) -> N. +printable_unicode(<<C/utf8, R/binary>>, I, L) when I > 0 -> + printable_unicode(R, I - 1, [C | L]); +printable_unicode(Bin, I, L) -> + {I, Bin, lists:reverse(L)}. + +write_string(S, latin1) -> + io_lib:write_string(S, $"); %" +write_string(S, _Uni) -> + io_lib:write_unicode_string(S, $"). %" + %% Throw 'no_good' if the indentation exceeds half the line length %% unless there is room for M characters on the line. diff --git a/lib/stdlib/src/lib.erl b/lib/stdlib/src/lib.erl index cf4b87d7eb..b2ce2a5a8f 100644 --- a/lib/stdlib/src/lib.erl +++ b/lib/stdlib/src/lib.erl @@ -21,8 +21,9 @@ -export([flush_receive/0, error_message/2, progname/0, nonl/1, send/2, sendw/2, eval_str/1]). --export([format_exception/6, format_stacktrace/4, - format_call/4, format_fun/1]). +-export([format_exception/6, format_exception/7, + format_stacktrace/4, format_stacktrace/5, + format_call/4, format_call/5, format_fun/1]). -spec flush_receive() -> 'ok'. @@ -128,32 +129,49 @@ all_white(_) -> false. %% as indentation whenever newline has been inserted); %% Class, Reason and StackTrace are the exception; %% FormatFun = fun(Term, I) -> iolist() formats terms; -%% StackFun = fun(Mod, Fun, Arity) -> bool() is used for trimming the +%% StackFun = fun(Mod, Fun, Arity) -> boolean() is used for trimming the %% end of the stack (typically calls to erl_eval are skipped). -format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun) +format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun) -> + format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun, + latin1). + +%% -> iolist() | unicode:charlist() (no \n at end) +%% FormatFun = fun(Term, I) -> iolist() | unicode:charlist(). +format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun, Encoding) when is_integer(I), I >= 1, is_function(StackFun, 3), is_function(FormatFun, 2) -> S = n_spaces(I-1), {Term,Trace1,Trace} = analyze_exception(Class, Reason, StackTrace), - Expl0 = explain_reason(Term, Class, Trace1, FormatFun, S), - Expl = io_lib:fwrite(<<"~s~s">>, [exited(Class), Expl0]), - case format_stacktrace1(S, Trace, FormatFun, StackFun) of + Expl0 = explain_reason(Term, Class, Trace1, FormatFun, S, Encoding), + FormatString = case Encoding of + latin1 -> "~s~s"; + _ -> "~s~ts" + end, + Expl = io_lib:fwrite(FormatString, [exited(Class), Expl0]), + case format_stacktrace1(S, Trace, FormatFun, StackFun, Encoding) of [] -> Expl; Stack -> [Expl, $\n, Stack] end. %% -> iolist() (no \n at end) -format_stacktrace(I, StackTrace, StackFun, FormatFun) +format_stacktrace(I, StackTrace, StackFun, FormatFun) -> + format_stacktrace(I, StackTrace, StackFun, FormatFun, latin1). + +%% -> iolist() | unicode:charlist() (no \n at end) +format_stacktrace(I, StackTrace, StackFun, FormatFun, Encoding) when is_integer(I), I >= 1, is_function(StackFun, 3), is_function(FormatFun, 2) -> S = n_spaces(I-1), - format_stacktrace1(S, StackTrace, FormatFun, StackFun). + format_stacktrace1(S, StackTrace, FormatFun, StackFun, Encoding). %% -> iolist() (no \n at end) -format_call(I, ForMForFun, As, FormatFun) when is_integer(I), I >= 1, - is_list(As), - is_function(FormatFun, 2) -> - format_call("", n_spaces(I-1), ForMForFun, As, FormatFun). +format_call(I, ForMForFun, As, FormatFun) -> + format_call(I, ForMForFun, As, FormatFun, latin1). + +%% -> iolist() | unicode:charlist() (no \n at end) +format_call(I, ForMForFun, As, FormatFun, Enc) + when is_integer(I), I >= 1, is_list(As), is_function(FormatFun, 2) -> + format_call("", n_spaces(I-1), ForMForFun, As, FormatFun, Enc). %% -> iolist() (no \n at end) format_fun(Fun) when is_function(Fun) -> @@ -204,79 +222,80 @@ is_stacktrace(_) -> false. %% ERTS exit codes (some of them are also returned by erl_eval): -explain_reason(badarg, error, [], _PF, _S) -> +explain_reason(badarg, error, [], _PF, _S, _Enc) -> <<"bad argument">>; -explain_reason({badarg,V}, error=Cl, [], PF, S) -> % orelse, andalso +explain_reason({badarg,V}, error=Cl, [], PF, S, _Enc) -> % orelse, andalso format_value(V, <<"bad argument: ">>, Cl, PF, S); -explain_reason(badarith, error, [], _PF, _S) -> +explain_reason(badarith, error, [], _PF, _S, _Enc) -> <<"an error occurred when evaluating an arithmetic expression">>; -explain_reason({badarity,{Fun,As}}, error, [], _PF, _S) +explain_reason({badarity,{Fun,As}}, error, [], _PF, _S, _Enc) when is_function(Fun) -> %% Only the arity is displayed, not the arguments As. io_lib:fwrite(<<"~s called with ~s">>, [format_fun(Fun), argss(length(As))]); -explain_reason({badfun,Term}, error=Cl, [], PF, S) -> +explain_reason({badfun,Term}, error=Cl, [], PF, S, _Enc) -> format_value(Term, <<"bad function ">>, Cl, PF, S); -explain_reason({badmatch,Term}, error=Cl, [], PF, S) -> - format_value(Term, <<"no match of right hand side value ">>, Cl, PF, S); -explain_reason({case_clause,V}, error=Cl, [], PF, S) -> +explain_reason({badmatch,Term}, error=Cl, [], PF, S, _Enc) -> + Str = <<"no match of right hand side value ">>, + format_value(Term, Str, Cl, PF, S); +explain_reason({case_clause,V}, error=Cl, [], PF, S, _Enc) -> %% "there is no case clause with a true guard sequence and a %% pattern matching..." format_value(V, <<"no case clause matching ">>, Cl, PF, S); -explain_reason(function_clause, error, [{F,A}], _PF, _S) -> +explain_reason(function_clause, error, [{F,A}], _PF, _S, _Enc) -> %% Shell commands FAs = io_lib:fwrite(<<"~w/~w">>, [F, A]), [<<"no function clause matching call to ">> | FAs]; -explain_reason(function_clause, error=Cl, [{M,F,As,Loc}], PF, S) -> +explain_reason(function_clause, error=Cl, [{M,F,As,Loc}], PF, S, Enc) -> Str = <<"no function clause matching ">>, - [format_errstr_call(Str, Cl, {M,F}, As, PF, S),$\s|location(Loc)]; -explain_reason(if_clause, error, [], _PF, _S) -> + [format_errstr_call(Str, Cl, {M,F}, As, PF, S, Enc),$\s|location(Loc)]; +explain_reason(if_clause, error, [], _PF, _S, _Enc) -> <<"no true branch found when evaluating an if expression">>; -explain_reason(noproc, error, [], _PF, _S) -> +explain_reason(noproc, error, [], _PF, _S, _Enc) -> <<"no such process or port">>; -explain_reason(notalive, error, [], _PF, _S) -> +explain_reason(notalive, error, [], _PF, _S, _Enc) -> <<"the node cannot be part of a distributed system">>; -explain_reason(system_limit, error, [], _PF, _S) -> +explain_reason(system_limit, error, [], _PF, _S, _Enc) -> <<"a system limit has been reached">>; -explain_reason(timeout_value, error, [], _PF, _S) -> +explain_reason(timeout_value, error, [], _PF, _S, _Enc) -> <<"bad receive timeout value">>; -explain_reason({try_clause,V}, error=Cl, [], PF, S) -> +explain_reason({try_clause,V}, error=Cl, [], PF, S, _Enc) -> %% "there is no try clause with a true guard sequence and a %% pattern matching..." format_value(V, <<"no try clause matching ">>, Cl, PF, S); -explain_reason(undef, error, [{M,F,A,_}], _PF, _S) -> +explain_reason(undef, error, [{M,F,A,_}], _PF, _S, _Enc) -> %% Only the arity is displayed, not the arguments, if there are any. io_lib:fwrite(<<"undefined function ~s">>, [mfa_to_string(M, F, n_args(A))]); -explain_reason({shell_undef,F,A,_}, error, [], _PF, _S) -> +explain_reason({shell_undef,F,A,_}, error, [], _PF, _S, _Enc) -> %% Give nicer reports for undefined shell functions %% (but not when the user actively calls shell_default:F(...)). io_lib:fwrite(<<"undefined shell command ~s/~w">>, [F, n_args(A)]); %% Exit codes returned by erl_eval only: -explain_reason({argument_limit,_Fun}, error, [], _PF, _S) -> +explain_reason({argument_limit,_Fun}, error, [], _PF, _S, _Enc) -> io_lib:fwrite(<<"limit of number of arguments to interpreted function" " exceeded">>, []); -explain_reason({bad_filter,V}, error=Cl, [], PF, S) -> +explain_reason({bad_filter,V}, error=Cl, [], PF, S, _Enc) -> format_value(V, <<"bad filter ">>, Cl, PF, S); -explain_reason({bad_generator,V}, error=Cl, [], PF, S) -> +explain_reason({bad_generator,V}, error=Cl, [], PF, S, _Enc) -> format_value(V, <<"bad generator ">>, Cl, PF, S); -explain_reason({unbound,V}, error, [], _PF, _S) -> +explain_reason({unbound,V}, error, [], _PF, _S, _Enc) -> io_lib:fwrite(<<"variable ~w is unbound">>, [V]); %% Exit codes local to the shell module (restricted shell): -explain_reason({restricted_shell_bad_return, V}, exit=Cl, [], PF, S) -> +explain_reason({restricted_shell_bad_return, V}, exit=Cl, [], PF, S, _Enc) -> Str = <<"restricted shell module returned bad value ">>, format_value(V, Str, Cl, PF, S); explain_reason({restricted_shell_disallowed,{ForMF,As}}, - exit=Cl, [], PF, S) -> + exit=Cl, [], PF, S, Enc) -> %% ForMF can be a fun, but not a shell fun. Str = <<"restricted shell does not allow ">>, - format_errstr_call(Str, Cl, ForMF, As, PF, S); -explain_reason(restricted_shell_started, exit, [], _PF, _S) -> + format_errstr_call(Str, Cl, ForMF, As, PF, S, Enc); +explain_reason(restricted_shell_started, exit, [], _PF, _S, _Enc) -> <<"restricted shell starts now">>; -explain_reason(restricted_shell_stopped, exit, [], _PF, _S) -> +explain_reason(restricted_shell_stopped, exit, [], _PF, _S, _Enc) -> <<"restricted shell stopped">>; %% Other exit code: -explain_reason(Reason, Class, [], PF, S) -> +explain_reason(Reason, Class, [], PF, S, _Enc) -> PF(Reason, (iolist_size(S)+1) + exited_size(Class)). n_args(A) when is_integer(A) -> @@ -293,28 +312,28 @@ argss(2) -> argss(I) -> io_lib:fwrite(<<"~w arguments">>, [I]). -format_stacktrace1(S0, Stack0, PF, SF) -> +format_stacktrace1(S0, Stack0, PF, SF, Enc) -> Stack1 = lists:dropwhile(fun({M,F,A,_}) -> SF(M, F, A) end, lists:reverse(Stack0)), S = [" " | S0], Stack = lists:reverse(Stack1), - format_stacktrace2(S, Stack, 1, PF). + format_stacktrace2(S, Stack, 1, PF, Enc). -format_stacktrace2(S, [{M,F,A,L}|Fs], N, PF) when is_integer(A) -> +format_stacktrace2(S, [{M,F,A,L}|Fs], N, PF, Enc) when is_integer(A) -> [io_lib:fwrite(<<"~s~s ~s ~s">>, [sep(N, S), origin(N, M, F, A), mfa_to_string(M, F, A), location(L)]) - | format_stacktrace2(S, Fs, N + 1, PF)]; -format_stacktrace2(S, [{M,F,As,_}|Fs], N, PF) when is_list(As) -> + | format_stacktrace2(S, Fs, N + 1, PF, Enc)]; +format_stacktrace2(S, [{M,F,As,_}|Fs], N, PF, Enc) when is_list(As) -> A = length(As), CalledAs = [S,<<" called as ">>], - C = format_call("", CalledAs, {M,F}, As, PF), - [io_lib:fwrite(<<"~s~s ~s\n~s~s">>, + C = format_call("", CalledAs, {M,F}, As, PF, Enc), + [io_lib:fwrite(<<"~s~s ~s\n~s~ts">>, [sep(N, S), origin(N, M, F, A), mfa_to_string(M, F, A), CalledAs, C]) - | format_stacktrace2(S, Fs, N + 1, PF)]; -format_stacktrace2(_S, [], _N, _PF) -> + | format_stacktrace2(S, Fs, N + 1, PF, Enc)]; +format_stacktrace2(_S, [], _N, _PF, _Enc) -> "". location(L) -> @@ -338,22 +357,22 @@ origin(1, M, F, A) -> origin(_N, _M, _F, _A) -> <<"in call from">>. -format_errstr_call(ErrStr, Class, ForMForFun, As, PF, Pre0) -> +format_errstr_call(ErrStr, Class, ForMForFun, As, PF, Pre0, Enc) -> Pre1 = [Pre0 | n_spaces(exited_size(Class))], - format_call(ErrStr, Pre1, ForMForFun, As, PF). + format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc). -format_call(ErrStr, Pre1, ForMForFun, As, PF) -> +format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc) -> Arity = length(As), [ErrStr | case is_op(ForMForFun, Arity) of {yes,Op} -> - format_op(ErrStr, Pre1, Op, As, PF); + format_op(ErrStr, Pre1, Op, As, PF, Enc); no -> MFs = mf_to_string(ForMForFun, Arity), I1 = iolist_size([Pre1,ErrStr|MFs]), - S1 = pp_arguments(PF, As, I1), - S2 = pp_arguments(PF, As, iolist_size([Pre1|MFs])), - Long = count_nl(pp_arguments(PF, [a2345,b2345], I1)) > 0, + S1 = pp_arguments(PF, As, I1, Enc), + S2 = pp_arguments(PF, As, iolist_size([Pre1|MFs]), Enc), + Long = count_nl(pp_arguments(PF, [a2345,b2345], I1, Enc)) > 0, case Long or (count_nl(S2) < count_nl(S1)) of true -> [$\n, Pre1, MFs, S2]; @@ -362,11 +381,11 @@ format_call(ErrStr, Pre1, ForMForFun, As, PF) -> end end]. -format_op(ErrStr, Pre, Op, [A1], PF) -> +format_op(ErrStr, Pre, Op, [A1], PF, _Enc) -> OpS = io_lib:fwrite(<<"~s ">>, [Op]), I1 = iolist_size([ErrStr,Pre,OpS]), [OpS | PF(A1, I1+1)]; -format_op(ErrStr, Pre, Op, [A1, A2], PF) -> +format_op(ErrStr, Pre, Op, [A1, A2], PF, Enc) -> I1 = iolist_size([ErrStr,Pre]), S1 = PF(A1, I1+1), S2 = PF(A2, I1+1), @@ -377,33 +396,40 @@ format_op(ErrStr, Pre, Op, [A1, A2], PF) -> [S1,Pre1,OpS,Pre1|S2]; false -> OpS2 = io_lib:fwrite(<<" ~s ">>, [Op]), - S2_2 = PF(A2, iolist_size([ErrStr,Pre,S1|OpS2])+1), + Size1 = iolist_size([ErrStr,Pre|OpS2]), + {Size2,S1_2} = size(Enc, S1), + S2_2 = PF(A2, Size1+Size2+1), case count_nl(S2) < count_nl(S2_2) of true -> - [S1,Pre1,OpS,Pre1|S2]; + [S1_2,Pre1,OpS,Pre1|S2]; false -> - [S1,OpS2|S2_2] + [S1_2,OpS2|S2_2] end end. -pp_arguments(PF, As, I) -> - case {As, io_lib:printable_list(As)} of +pp_arguments(PF, As, I, Enc) -> + case {As, printable_list(Enc, As)} of {[Int | T], true} -> L = integer_to_list(Int), Ll = length(L), A = list_to_atom(lists:duplicate(Ll, $a)), - S0 = binary_to_list(iolist_to_binary(PF([A | T], I+1))), - brackets_to_parens([$[,L,string:sub_string(S0, 2+Ll)]); + S0 = unicode:characters_to_list(PF([A | T], I+1), Enc), + brackets_to_parens([$[,L,string:sub_string(S0, 2+Ll)], Enc); _ -> - brackets_to_parens(PF(As, I+1)) + brackets_to_parens(PF(As, I+1), Enc) end. -brackets_to_parens(S) -> - B = iolist_to_binary(S), +brackets_to_parens(S, Enc) -> + B = unicode:characters_to_binary(S, Enc), Sz = byte_size(B) - 2, <<$[,R:Sz/binary,$]>> = B, [$(,R,$)]. +printable_list(latin1, As) -> + io_lib:printable_list(As); +printable_list(_, As) -> + io_lib:printable_unicode_list(As). + mfa_to_string(M, F, A) -> io_lib:fwrite(<<"~s/~w">>, [mf_to_string({M, F}, A), A]). @@ -472,3 +498,10 @@ exited(exit) -> <<"exception exit: ">>; exited(throw) -> <<"exception throw: ">>. + +size(latin1, S) -> + {iolist_size(S),S}; +size(_, S0) -> + S = unicode:characters_to_list(S0, unicode), + true = is_list(S), + {length(S),S}. diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index cddf345c76..400380a36d 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -78,89 +78,89 @@ obsolete_1(snmp, N, A) -> end; obsolete_1(snmpm, agent_info, 3) -> - {deprecated, {snmpm, agent_info, 2}, "R16B"}; + {removed, {snmpm, agent_info, 2}, "R16B"}; obsolete_1(snmpm, update_agent_info, 5) -> - {deprecated, {snmpm, update_agent_info, 4}, "R16B"}; + {removed, {snmpm, update_agent_info, 4}, "R16B"}; obsolete_1(snmpm, g, 3) -> - {deprecated, {snmpm, sync_get, 3}, "R16B"}; + {removed, {snmpm, sync_get, 3}, "R16B"}; obsolete_1(snmpm, g, 4) -> - {deprecated, {snmpm, sync_get, [3,4]}, "R16B"}; + {removed, {snmpm, sync_get, [3,4]}, "R16B"}; obsolete_1(snmpm, g, 5) -> - {deprecated, {snmpm, sync_get, [4,5]}, "R16B"}; + {removed, {snmpm, sync_get, [4,5]}, "R16B"}; obsolete_1(snmpm, g, 6) -> - {deprecated, {snmpm, sync_get, [5,6]}, "R16B"}; + {removed, {snmpm, sync_get, [5,6]}, "R16B"}; obsolete_1(snmpm, g, 7) -> - {deprecated, {snmpm, sync_get, 6}, "R16B"}; + {removed, {snmpm, sync_get, 6}, "R16B"}; obsolete_1(snmpm, ag, 3) -> - {deprecated, {snmpm, async_get, 3}, "R16B"}; + {removed, {snmpm, async_get, 3}, "R16B"}; obsolete_1(snmpm, ag, 4) -> - {deprecated, {snmpm, async_get, [3,4]}, "R16B"}; + {removed, {snmpm, async_get, [3,4]}, "R16B"}; obsolete_1(snmpm, ag, 5) -> - {deprecated, {snmpm, async_get, [4,5]}, "R16B"}; + {removed, {snmpm, async_get, [4,5]}, "R16B"}; obsolete_1(snmpm, ag, 6) -> - {deprecated, {snmpm, async_get, [5,6]}, "R16B"}; + {removed, {snmpm, async_get, [5,6]}, "R16B"}; obsolete_1(snmpm, ag, 7) -> - {deprecated, {snmpm, async_get, 6}, "R16B"}; + {removed, {snmpm, async_get, 6}, "R16B"}; obsolete_1(snmpm, gn, 3) -> - {deprecated, {snmpm, sync_get_next, 3}, "R16B"}; + {removed, {snmpm, sync_get_next, 3}, "R16B"}; obsolete_1(snmpm, gn, 4) -> - {deprecated, {snmpm, sync_get_next, [3,4]}, "R16B"}; + {removed, {snmpm, sync_get_next, [3,4]}, "R16B"}; obsolete_1(snmpm, gn, 5) -> - {deprecated, {snmpm, sync_get_next, [4,5]}, "R16B"}; + {removed, {snmpm, sync_get_next, [4,5]}, "R16B"}; obsolete_1(snmpm, gn, 6) -> - {deprecated, {snmpm, sync_get_next, [5,6]}, "R16B"}; + {removed, {snmpm, sync_get_next, [5,6]}, "R16B"}; obsolete_1(snmpm, gn, 7) -> - {deprecated, {snmpm, sync_get_next, 6}, "R16B"}; + {removed, {snmpm, sync_get_next, 6}, "R16B"}; obsolete_1(snmpm, agn, 3) -> - {deprecated, {snmpm, async_get_next, 3}, "R16B"}; + {removed, {snmpm, async_get_next, 3}, "R16B"}; obsolete_1(snmpm, agn, 4) -> - {deprecated, {snmpm, async_get_next, [3,4]}, "R16B"}; + {removed, {snmpm, async_get_next, [3,4]}, "R16B"}; obsolete_1(snmpm, agn, 5) -> - {deprecated, {snmpm, async_get_next, [4,5]}, "R16B"}; + {removed, {snmpm, async_get_next, [4,5]}, "R16B"}; obsolete_1(snmpm, agn, 6) -> - {deprecated, {snmpm, async_get_next, [5,6]}, "R16B"}; + {removed, {snmpm, async_get_next, [5,6]}, "R16B"}; obsolete_1(snmpm, agn, 7) -> - {deprecated, {snmpm, async_get_next, 6}, "R16B"}; + {removed, {snmpm, async_get_next, 6}, "R16B"}; obsolete_1(snmpm, s, 3) -> - {deprecated, {snmpm, sync_set, 3}, "R16B"}; + {removed, {snmpm, sync_set, 3}, "R16B"}; obsolete_1(snmpm, s, 4) -> - {deprecated, {snmpm, sync_set, [3,4]}, "R16B"}; + {removed, {snmpm, sync_set, [3,4]}, "R16B"}; obsolete_1(snmpm, s, 5) -> - {deprecated, {snmpm, sync_set, [4,5]}, "R16B"}; + {removed, {snmpm, sync_set, [4,5]}, "R16B"}; obsolete_1(snmpm, s, 6) -> - {deprecated, {snmpm, sync_set, [5,6]}, "R16B"}; + {removed, {snmpm, sync_set, [5,6]}, "R16B"}; obsolete_1(snmpm, s, 7) -> - {deprecated, {snmpm, sync_set, 6}, "R16B"}; + {removed, {snmpm, sync_set, 6}, "R16B"}; obsolete_1(snmpm, as, 3) -> - {deprecated, {snmpm, async_set, 3}, "R16B"}; + {removed, {snmpm, async_set, 3}, "R16B"}; obsolete_1(snmpm, as, 4) -> - {deprecated, {snmpm, async_set, [3,4]}, "R16B"}; + {removed, {snmpm, async_set, [3,4]}, "R16B"}; obsolete_1(snmpm, as, 5) -> - {deprecated, {snmpm, async_set, [4,5]}, "R16B"}; + {removed, {snmpm, async_set, [4,5]}, "R16B"}; obsolete_1(snmpm, as, 6) -> - {deprecated, {snmpm, async_set, [5,6]}, "R16B"}; + {removed, {snmpm, async_set, [5,6]}, "R16B"}; obsolete_1(snmpm, as, 7) -> - {deprecated, {snmpm, async_set, 6}, "R16B"}; + {removed, {snmpm, async_set, 6}, "R16B"}; obsolete_1(snmpm, gb, 5) -> - {deprecated, {snmpm, sync_get_bulk, 5}, "R16B"}; + {removed, {snmpm, sync_get_bulk, 5}, "R16B"}; obsolete_1(snmpm, gb, 6) -> - {deprecated, {snmpm, sync_get_bulk, [5,6]}, "R16B"}; + {removed, {snmpm, sync_get_bulk, [5,6]}, "R16B"}; obsolete_1(snmpm, gb, 7) -> - {deprecated, {snmpm, sync_get_bulk, [6,7]}, "R16B"}; + {removed, {snmpm, sync_get_bulk, [6,7]}, "R16B"}; obsolete_1(snmpm, gb, 8) -> - {deprecated, {snmpm, sync_get_bulk, [7,8]}, "R16B"}; + {removed, {snmpm, sync_get_bulk, [7,8]}, "R16B"}; obsolete_1(snmpm, gb, 9) -> - {deprecated, {snmpm, sync_get_bulk, 8}, "R16B"}; + {removed, {snmpm, sync_get_bulk, 8}, "R16B"}; obsolete_1(snmpm, agb, 5) -> - {deprecated, {snmpm, async_get_bulk, 5}, "R16B"}; + {removed, {snmpm, async_get_bulk, 5}, "R16B"}; obsolete_1(snmpm, agb, 6) -> - {deprecated, {snmpm, async_get_bulk, [5,6]}, "R16B"}; + {removed, {snmpm, async_get_bulk, [5,6]}, "R16B"}; obsolete_1(snmpm, agb, 7) -> - {deprecated, {snmpm, async_get_bulk, [6,7]}, "R16B"}; + {removed, {snmpm, async_get_bulk, [6,7]}, "R16B"}; obsolete_1(snmpm, agb, 8) -> - {deprecated, {snmpm, async_get_bulk, [7,8]}, "R16B"}; + {removed, {snmpm, async_get_bulk, [7,8]}, "R16B"}; obsolete_1(snmpm, agb, 9) -> - {deprecated, {snmpm, async_get_bulk, 8}, "R16B"}; + {removed, {snmpm, async_get_bulk, 8}, "R16B"}; %% *** MEGACO *** @@ -347,7 +347,7 @@ obsolete_1(docb_xml_check, _, _) -> obsolete_1(asn1rt, F, _) when F == load_driver; F == unload_driver -> {deprecated,"deprecated (will be removed in R16A); has no effect as drivers are no longer used."}; obsolete_1(ssl, pid, 1) -> - {deprecated,"deprecated (will be removed in R17); is no longer needed"}; + {removed,"was removed in R16; is no longer needed"}; obsolete_1(inviso, _, _) -> {removed,"the inviso application was removed in R16"}; diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl index e3eda5d932..204f8e128c 100644 --- a/lib/stdlib/src/proplists.erl +++ b/lib/stdlib/src/proplists.erl @@ -51,20 +51,21 @@ -export_type([property/0, proplist/0]). --type property() :: atom() | tuple(). +-type property() :: atom() | tuple(). -type proplist() :: [property()]. %% --------------------------------------------------------------------- %% @doc Creates a normal form (minimal) representation of a property. If -%% <code>P</code> is <code>{Key, true}</code> where <code>Key</code> is -%% an atom, this returns <code>Key</code>, otherwise the whole term -%% <code>P</code> is returned. +%% <code>PropertyIn</code> is <code>{Key, true}</code> where +%% <code>Key</code> is an atom, this returns <code>Key</code>, otherwise +%% the whole term <code>PropertyIn</code> is returned. %% %% @see property/2 --spec property(Property) -> Property when - Property :: property(). +-spec property(PropertyIn) -> PropertyOut when + PropertyIn :: property(), + PropertyOut :: property(). property({Key, true}) when is_atom(Key) -> Key; @@ -92,13 +93,14 @@ property(Key, Value) -> %% --------------------------------------------------------------------- -%% @doc Unfolds all occurences of atoms in <code>List</code> to tuples +%% @doc Unfolds all occurences of atoms in <code>ListIn</code> to tuples %% <code>{Atom, true}</code>. %% %% @see compact/1 --spec unfold(List) -> List when - List :: [term()]. +-spec unfold(ListIn) -> ListOut when + ListIn :: [term()], + ListOut :: [term()]. unfold([P | Ps]) -> if is_atom(P) -> @@ -110,16 +112,17 @@ unfold([]) -> []. %% @doc Minimizes the representation of all entries in the list. This is -%% equivalent to <code>[property(P) || P <- List]</code>. +%% equivalent to <code>[property(P) || P <- ListIn]</code>. %% %% @see unfold/1 %% @see property/1 --spec compact(List) -> List when - List :: [property()]. +-spec compact(ListIn) -> ListOut when + ListIn :: [property()], + ListOut :: [property()]. -compact(List) -> - [property(P) || P <- List]. +compact(ListIn) -> + [property(P) || P <- ListIn]. %% --------------------------------------------------------------------- @@ -199,7 +202,7 @@ is_defined(_Key, []) -> -spec get_value(Key, List) -> term() when Key :: term(), - List :: List::[term()]. + List :: [term()]. get_value(Key, List) -> get_value(Key, List, undefined). @@ -272,9 +275,10 @@ get_all_values(_Key, []) -> %% %% @see get_all_values/2 --spec append_values(Key, List) -> List when +-spec append_values(Key, ListIn) -> ListOut when Key :: term(), - List :: [term()]. + ListIn :: [term()], + ListOut :: [term()]. append_values(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -357,7 +361,7 @@ get_keys([], Keys) -> -spec delete(Key, List) -> List when Key :: term(), - List::[term()]. + List :: [term()]. delete(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -374,7 +378,7 @@ delete(_, []) -> %% --------------------------------------------------------------------- %% @doc Substitutes keys of properties. For each entry in -%% <code>List</code>, if it is associated with some key <code>K1</code> +%% <code>ListIn</code>, if it is associated with some key <code>K1</code> %% such that <code>{K1, K2}</code> occurs in <code>Aliases</code>, the %% key of the entry is changed to <code>Key2</code>. If the same %% <code>K1</code> occurs more than once in <code>Aliases</code>, only @@ -388,10 +392,11 @@ delete(_, []) -> %% @see substitute_negations/2 %% @see normalize/2 --spec substitute_aliases(Aliases, List) -> List when +-spec substitute_aliases(Aliases, ListIn) -> ListOut when Aliases :: [{Key, Key}], Key :: term(), - List::[term()]. + ListIn :: [term()], + ListOut :: [term()]. substitute_aliases(As, Props) -> [substitute_aliases_1(As, P) || P <- Props]. @@ -411,13 +416,13 @@ substitute_aliases_1([], P) -> %% --------------------------------------------------------------------- %% @doc Substitutes keys of boolean-valued properties and simultaneously -%% negates their values. For each entry in <code>List</code>, if it is +%% negates their values. For each entry in <code>ListIn</code>, if it is %% associated with some key <code>K1</code> such that <code>{K1, %% K2}</code> occurs in <code>Negations</code>, then if the entry was %% <code>{K1, true}</code> it will be replaced with <code>{K2, %% false}</code>, otherwise it will be replaced with <code>{K2, %% true}</code>, thus changing the name of the option and simultaneously -%% negating the value given by <code>get_bool(List)</code>. If the same +%% negating the value given by <code>get_bool(ListIn)</code>. If the same %% <code>K1</code> occurs more than once in <code>Negations</code>, only %% the first occurrence is used. %% @@ -431,10 +436,11 @@ substitute_aliases_1([], P) -> %% @see substitute_aliases/2 %% @see normalize/2 --spec substitute_negations(Negations, List) -> List when +-spec substitute_negations(Negations, ListIn) -> ListOut when Negations :: [{Key, Key}], Key :: term(), - List :: [term()]. + ListIn :: [term()], + ListOut :: [term()]. substitute_negations(As, Props) -> [substitute_negations_1(As, P) || P <- Props]. @@ -466,11 +472,11 @@ substitute_negations_1([], P) -> %% @doc Expands particular properties to corresponding sets of %% properties (or other terms). For each pair <code>{Property, %% Expansion}</code> in <code>Expansions</code>, if <code>E</code> is -%% the first entry in <code>List</code> with the same key as +%% the first entry in <code>ListIn</code> with the same key as %% <code>Property</code>, and <code>E</code> and <code>Property</code> %% have equivalent normal forms, then <code>E</code> is replaced with %% the terms in <code>Expansion</code>, and any following entries with -%% the same key are deleted from <code>List</code>. +%% the same key are deleted from <code>ListIn</code>. %% %% <p>For example, the following expressions all return <code>[fie, bar, %% baz, fum]</code>: @@ -497,9 +503,10 @@ substitute_negations_1([], P) -> %% %% @see normalize/2 --spec expand(Expansions, List) -> List when +-spec expand(Expansions, ListIn) -> ListOut when Expansions :: [{Property :: property(), Expansion :: [term()]}], - List :: [term()]. + ListIn :: [term()], + ListOut :: [term()]. expand(Es, Ps) when is_list(Ps) -> Es1 = [{property(P), V} || {P, V} <- Es], @@ -599,15 +606,16 @@ flatten([]) -> %% @see expand/2 %% @see compact/1 --spec normalize(List, Stages) -> List when - List :: [term()], +-spec normalize(ListIn, Stages) -> ListOut when + ListIn :: [term()], Stages :: [Operation], Operation :: {'aliases', Aliases} | {'negations', Negations} | {'expand', Expansions}, Aliases :: [{Key, Key}], Negations :: [{Key, Key}], - Expansions :: [{Property :: property(), Expansion :: [term()]}]. + Expansions :: [{Property :: property(), Expansion :: [term()]}], + ListOut :: [term()]. normalize(L, [{aliases, As} | Xs]) -> normalize(substitute_aliases(As, L), Xs); diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl index ad25fd559c..d441f38e44 100644 --- a/lib/stdlib/src/qlc_pt.erl +++ b/lib/stdlib/src/qlc_pt.erl @@ -31,9 +31,6 @@ %% Also in qlc.erl. -define(QLC_Q(L1, L2, L3, L4, LC, Os), {call,L1,{remote,L2,{atom,L3,?APIMOD},{atom,L4,?Q}},[LC | Os]}). --define(QLC_QQ(L1, L2, L3, L4, L5, L6, LC, Os), % packages... - {call,L1,{remote,L2,{record_field,L3,{atom,L4,''}, - {atom,L5,?APIMOD}},{atom,L6,?Q}},[LC | Os]}). -define(IMP_Q(L1, L2, LC, Os), {call,L,{atom,L2,?Q},[LC | Os]}). %% Also in qlc.erl. @@ -2475,13 +2472,6 @@ qlcmf(?QLC_Q(L1, L2, L3, L4, LC0, Os0), F, Imp, A0, No0) when length(Os0) < 2 -> NL = make_lcid(L1, No), {T, A} = F(NL, LC, A2), {?QLC_Q(L1, L2, L3, L4, T, Os), A, No + 1}; -qlcmf(?QLC_QQ(L1, L2, L3, L4, L5, L6, LC0, Os0), - F, Imp, A0, No0) when length(Os0) < 2 -> - {Os, A1, No1} = qlcmf(Os0, F, Imp, A0, No0), - {LC, A2, No} = qlcmf(LC0, F, Imp, A1, No1), % nested... - NL = make_lcid(L1, No), - {T, A} = F(NL, LC, A2), - {?QLC_QQ(L1, L2, L3, L4, L5, L6, T, Os), A, No + 1}; qlcmf(?IMP_Q(L1, L2, LC0, Os0), F, Imp=true, A0, No0) when length(Os0) < 2 -> {Os, A1, No1} = qlcmf(Os0, F, Imp, A0, No0), {LC, A2, No} = qlcmf(LC0, F, Imp, A1, No1), % nested... diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl index 3fd6c81e5f..e6f05b71d4 100644 --- a/lib/stdlib/src/sets.erl +++ b/lib/stdlib/src/sets.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. 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 @@ -17,7 +18,7 @@ %% %CopyrightEnd% %% -%% We use the dynamic hashing techniques by Per-�ke Larsson as +%% We use the dynamic hashing techniques by Per-Åke Larsson as %% described in "The Design and Implementation of Dynamic Hashing for %% Sets and Tables in Icon" by Griswold and Townsend. Much of the %% terminology comes from that paper as well. diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl index dc450f0ee6..5c929d2f51 100644 --- a/lib/stdlib/src/shell.erl +++ b/lib/stdlib/src/shell.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-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 @@ -128,7 +128,7 @@ start_restricted(RShMod) when is_atom(RShMod) -> error_logger:error_report( lists:flatten( io_lib:fwrite( - <<"Restricted shell module ~w not found: ~p\n">>, + "Restricted shell module ~w not found: ~"++cs_p() ++"\n", [RShMod,What]))), Error end. @@ -139,16 +139,6 @@ stop_restricted() -> application:unset_env(stdlib, restricted_shell), exit(restricted_shell_stopped). -default_packages() -> - []. -%%% ['erl','erl.lang']. - -default_modules() -> - []. -%%% [{pdict, 'erl.lang.proc.pdict'}, -%%% {keylist, 'erl.lang.list.keylist'}, -%%% {debug, 'erl.system.debug'}]. - -spec server(boolean(), boolean()) -> 'terminated'. server(NoCtrlG, StartSync) -> @@ -183,16 +173,7 @@ server(StartSync) -> end end, %% Our spawner has fixed the process groups. - Bs0 = erl_eval:new_bindings(), - Bs = lists:foldl(fun ({K, V}, D) -> - erl_eval:add_binding({module,K}, V, D) - end, - lists:foldl(fun (P, D) -> - import_all(P, D) - end, - Bs0, default_packages()), - default_modules()), - %% io:fwrite("Imported modules: ~p.\n", [erl_eval:bindings(Bs)]), + Bs = erl_eval:new_bindings(), %% Use an Ets table for record definitions. It takes too long to %% send a huge term to and from the evaluator. Ets makes it @@ -230,9 +211,10 @@ server(StartSync) -> ok; {RShMod2,What2} -> io:fwrite( - <<"Warning! Restricted shell module ~w not found: ~p.\n" - "Only the commands q() and init:stop() will be allowed!\n">>, - [RShMod2,What2]), + ("Warning! Restricted shell module ~w not found: ~" + ++cs_p()++".\n" + "Only the commands q() and init:stop() will be allowed!\n"), + [RShMod2,What2]), application:set_env(stdlib, restricted_shell, ?MODULE) end, @@ -244,7 +226,7 @@ server_loop(N0, Eval_0, Bs00, RT, Ds00, History0, Results0) -> {Eval_1,Bs0,Ds0,Prompt} = prompt(N, Eval_0, Bs00, RT, Ds00), {Res,Eval0} = get_command(Prompt, Eval_1, Bs0, RT, Ds0), case Res of - {ok,Es0,_EndLine} -> + {ok,Es0} -> case expand_hist(Es0, N) of {ok,Es} -> {V,Eval,Bs,Ds} = shell_cmd(Es, Eval0, Bs0, RT, Ds0, cmd), @@ -263,11 +245,11 @@ server_loop(N0, Eval_0, Bs00, RT, Ds00, History0, Results0) -> end, server_loop(N, Eval, Bs, RT, Ds, History, Results); {error,E} -> - fwrite_severity(benign, <<"~s">>, [E]), + fwrite_severity(benign, <<"~ts">>, [E]), server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0) end; - {error,{Line,Mod,What},_EndLine} -> - fwrite_severity(benign, <<"~w: ~s">>, + {error,{Line,Mod,What}} -> + fwrite_severity(benign, <<"~w: ~ts">>, [Line, Mod:format_error(What)]), server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0); {error,terminated} -> %Io process terminated @@ -277,20 +259,35 @@ server_loop(N0, Eval_0, Bs00, RT, Ds00, History0, Results0) -> exit(Eval0, kill), {_,Eval,_,_} = shell_rep(Eval0, Bs0, RT, Ds0), server_loop(N0, Eval, Bs0, RT, Ds0, History0, Results0); - {error,tokens} -> %Most probably unicode > 255 + {error,tokens} -> %Most probably character > 255 fwrite_severity(benign, <<"~w: Invalid tokens.">>, [N]), server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0); - {eof,_EndLine} -> - fwrite_severity(fatal, <<"Terminating erlang (~w)">>, [node()]), - halt(); eof -> fwrite_severity(fatal, <<"Terminating erlang (~w)">>, [node()]), halt() end. get_command(Prompt, Eval, Bs, RT, Ds) -> - Parse = fun() -> exit(io:parse_erl_exprs(Prompt)) end, + Parse = + fun() -> + exit( + case + io:scan_erl_exprs(group_leader(), Prompt, 1, [unicode]) + of + {ok,Toks,_EndPos} -> + erl_parse:parse_exprs(Toks); + {eof,_EndPos} -> + eof; + {error,ErrorInfo,_EndPos} -> + %% Skip the rest of the line: + _ = io:get_line(''), + {error,ErrorInfo}; + Else -> + Else + end + ) + end, Pid = spawn_link(Parse), get_command1(Pid, Eval, Bs, RT, Ds). @@ -337,7 +334,7 @@ get_prompt_func() -> end. bad_prompt_func(M) -> - fwrite_severity(benign, <<"Bad prompt function: ~p">>, [M]). + fwrite_severity(benign, "Bad prompt function: ~"++cs_p(), [M]). default_prompt(N) -> %% Don't bother flattening the list irrespective of what the @@ -453,7 +450,8 @@ expand_bin_elements([{bin_element,L,E,Sz,Ts}|Fs], C) -> no_command(N) -> throw({error, - io_lib:fwrite(<<"~s: command not found">>, [erl_pp:expr(N)])}). + io_lib:fwrite(<<"~ts: command not found">>, + [erl_pp:expr(N, enc())])}). %% add_cmd(Number, Expressions, Value) %% get_cmd(Number, CurrentCommand) @@ -518,7 +516,7 @@ shell_rep(Ev, Bs0, RT, Ds0) -> {shell_rep,Ev,{value,V,Bs,Ds}} -> {V,Ev,Bs,Ds}; {shell_rep,Ev,{command_error,{Line,M,Error}}} -> - fwrite_severity(benign, <<"~w: ~s">>, + fwrite_severity(benign, <<"~w: ~ts">>, [Line, M:format_error(Error)]), {{'EXIT',Error},Ev,Bs0,Ds0}; {shell_req,Ev,get_cmd} -> @@ -570,9 +568,10 @@ report_exception(Class, Severity, {Reason,Stacktrace}, RT) -> I = iolist_size(Tag) + 1, PF = fun(Term, I1) -> pp(Term, I1, RT) end, SF = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end, - io:requests([{put_chars, Tag}, - {put_chars, - lib:format_exception(I, Class, Reason, Stacktrace, SF, PF)}, + Enc = encoding(), + Str = lib:format_exception(I, Class, Reason, Stacktrace, SF, PF, Enc), + io:requests([{put_chars, latin1, Tag}, + {put_chars, unicode, Str}, nl]). start_eval(Bs, RT, Ds) -> @@ -671,7 +670,8 @@ exprs([E0|Es], Bs1, RT, Lf, Ef, Bs0, W) -> if Es =:= [] -> VS = pp(V0, 1, RT), - [io:requests([{put_chars, VS}, nl]) || W =:= cmd], + [io:requests([{put_chars, unicode, VS}, nl]) || + W =:= cmd], %% Don't send the result back if it will be %% discarded anyway. V = if @@ -753,7 +753,7 @@ used_records(E) -> {expr, E}. fwrite_severity(Severity, S, As) -> - io:fwrite(<<"~s\n">>, [format_severity(Severity, S, As)]). + io:fwrite(<<"~ts\n">>, [format_severity(Severity, S, As)]). format_severity(Severity, S, As) -> add_severity(Severity, io_lib:fwrite(S, As)). @@ -958,13 +958,13 @@ local_func(rd, [{atom,_,RecName},RecDef0], Bs, _Shell, RT, _Lf, _Ef) -> RecDef = expand_value(RecDef0), RDs = lists:flatten(erl_pp:expr(RecDef)), Attr = lists:concat(["-record('", RecName, "',", RDs, ")."]), - {ok, Tokens, _} = erl_scan:string(Attr), + {ok, Tokens, _} = erl_scan:string(Attr, 1, [unicode]), case erl_parse:parse_form(Tokens) of {ok,AttrForm} -> [RN] = add_records([AttrForm], Bs, RT), {value,RN,Bs}; {error,{_Line,M,ErrDesc}} -> - ErrStr = io_lib:fwrite(<<"~s">>, [M:format_error(ErrDesc)]), + ErrStr = io_lib:fwrite(<<"~ts">>, [M:format_error(ErrDesc)]), exit(lists:flatten(ErrStr)) end; local_func(rd, [_,_], _Bs, _Shell, _RT, _Lf, _Ef) -> @@ -988,11 +988,13 @@ local_func(rl, [A], Bs0, _Shell, RT, Lf, Ef) -> {value,list_records(record_defs(RT, listify(Recs))),Bs}; local_func(rp, [A], Bs0, _Shell, RT, Lf, Ef) -> {[V],Bs} = expr_list([A], Bs0, Lf, Ef), - W = columns(), - io:requests([{put_chars, - io_lib_pretty:print(V, 1, W, -1, ?CHAR_MAX, - record_print_fun(RT))}, - nl]), + Cs = io_lib_pretty:print(V, ([{column, 1}, + {line_length, columns()}, + {depth, -1}, + {max_chars, ?CHAR_MAX}, + {record_print_fun, record_print_fun(RT)}] + ++ enc())), + io:requests([{put_chars, unicode, Cs}, nl]), {value,ok,Bs}; local_func(rr, [A], Bs0, _Shell, RT, Lf, Ef) -> {[File],Bs} = expr_list([A], Bs0, Lf, Ef), @@ -1012,38 +1014,6 @@ local_func(which, [{atom,_,M}], Bs, _Shell, _RT, _Lf, _Ef) -> end; local_func(which, [_Other], _Bs, _Shell, _RT, _Lf, _Ef) -> erlang:raise(error, function_clause, [{shell,which,1}]); -local_func(import, [M], Bs, _Shell, _RT, _Lf, _Ef) -> - case erl_parse:package_segments(M) of - error -> erlang:raise(error, function_clause, [{shell,import,1}]); - M1 -> - Mod = packages:concat(M1), - case packages:is_valid(Mod) of - true -> - Key = list_to_atom(packages:last(Mod)), - Mod1 = list_to_atom(Mod), - {value,ok,erl_eval:add_binding({module,Key}, Mod1, Bs)}; - false -> - exit({{bad_module_name, Mod}, [{shell,import,1}]}) - end - end; -local_func(import_all, [P], Bs0, _Shell, _RT, _Lf, _Ef) -> - case erl_parse:package_segments(P) of - error -> erlang:raise(error, function_clause, [{shell,import_all,1}]); - P1 -> - Name = packages:concat(P1), - case packages:is_valid(Name) of - true -> - Bs1 = import_all(Name, Bs0), - {value,ok,Bs1}; - false -> - exit({{bad_package_name, Name}, - [{shell,import_all,1}]}) - end - end; -local_func(use, [M], Bs, Shell, RT, Lf, Ef) -> - local_func(import, [M], Bs, Shell, RT, Lf, Ef); -local_func(use_all, [M], Bs, Shell, RT, Lf, Ef) -> - local_func(import_all, [M], Bs, Shell, RT, Lf, Ef); local_func(history, [{integer,_,N}], Bs, _Shell, _RT, _Lf, _Ef) -> {value,history(N),Bs}; local_func(history, [_Other], _Bs, _Shell, _RT, _Lf, _Ef) -> @@ -1166,7 +1136,7 @@ add_records(RAs, Bs0, RT) -> case check_command([], Bs1) of {error,{_Line,M,ErrDesc}} -> %% A source file that has not been compiled. - ErrStr = io_lib:fwrite(<<"~s">>, [M:format_error(ErrDesc)]), + ErrStr = io_lib:fwrite(<<"~ts">>, [M:format_error(ErrDesc)]), exit(lists:flatten(ErrStr)); ok -> true = ets:insert(RT, Recs), @@ -1323,15 +1293,6 @@ record_attrs(Forms) -> %%% End of reading record information from file(s) -import_all(P, Bs0) -> - Ms = packages:find_modules(P), - lists:foldl(fun (M, Bs) -> - Key = list_to_atom(M), - M1 = list_to_atom(packages:concat(P, M)), - erl_eval:add_binding({module,Key}, M1, Bs) - end, - Bs0, Ms). - shell_req(Shell, Req) -> Shell ! {shell_req,self(),Req}, receive @@ -1343,25 +1304,25 @@ list_commands([{{N,command},Es0}, {{N,result}, V} |Ds], RT) -> VS = pp(V, 4, RT), Ns = io_lib:fwrite(<<"~w: ">>, [N]), I = iolist_size(Ns), - io:requests([{put_chars, Ns}, - {format,<<"~s\n">>,[erl_pp:exprs(Es, I, none)]}, + io:requests([{put_chars, latin1, Ns}, + {format,<<"~ts\n">>,[erl_pp:exprs(Es, I, enc())]}, {format,<<"-> ">>,[]}, - {put_chars, VS}, + {put_chars, unicode, VS}, nl]), list_commands(Ds, RT); list_commands([{{N,command},Es0} |Ds], RT) -> Es = prep_list_commands(Es0), Ns = io_lib:fwrite(<<"~w: ">>, [N]), I = iolist_size(Ns), - io:requests([{put_chars, Ns}, - {format,<<"~s\n">>,[erl_pp:exprs(Es, I, none)]}]), + io:requests([{put_chars, latin1, Ns}, + {format,<<"~ts\n">>,[erl_pp:exprs(Es, I, enc())]}]), list_commands(Ds, RT); list_commands([_D|Ds], RT) -> list_commands(Ds, RT); list_commands([], _RT) -> ok. list_bindings([{{module,M},Val}|Bs], RT) -> - io:fwrite(<<"~p is ~p\n">>, [M,Val]), + io:fwrite(<<"~w is ~w\n">>, [M,Val]), list_bindings(Bs, RT); list_bindings([{Name,Val}|Bs], RT) -> case erl_eval:fun_data(Val) of @@ -1369,13 +1330,13 @@ list_bindings([{Name,Val}|Bs], RT) -> FCs = expand_value(FCs0), % looks nicer F = {'fun',0,{clauses,FCs}}, M = {match,0,{var,0,Name},F}, - io:fwrite(<<"~s\n">>, [erl_pp:expr(M)]); + io:fwrite(<<"~ts\n">>, [erl_pp:expr(M, enc())]); false -> Namel = io_lib:fwrite(<<"~s = ">>, [Name]), Nl = iolist_size(Namel)+1, ValS = pp(Val, Nl, RT), - io:requests([{put_chars, Namel}, - {put_chars, ValS}, + io:requests([{put_chars, latin1, Namel}, + {put_chars, unicode, ValS}, nl]) end, list_bindings(Bs, RT); @@ -1384,7 +1345,7 @@ list_bindings([], _RT) -> list_records(Records) -> lists:foreach(fun({_Name,Attr}) -> - io:fwrite(<<"~s">>, [erl_pp:attribute(Attr)]) + io:fwrite(<<"~ts">>, [erl_pp:attribute(Attr, enc())]) end, Records). record_defs(RT, Names) -> @@ -1427,8 +1388,20 @@ get_history_and_results() -> {History, erlang:min(Results, History)}. pp(V, I, RT) -> - io_lib_pretty:print(V, I, columns(), ?LINEMAX, ?CHAR_MAX, - record_print_fun(RT)). + pp(V, I, RT, enc()). + +pp(V, I, RT, Enc) -> + io_lib_pretty:print(V, ([{column, I}, {line_length, columns()}, + {depth, ?LINEMAX}, {max_chars, ?CHAR_MAX}, + {record_print_fun, record_print_fun(RT)}] + ++ Enc)). + +%% Control sequence 'p' possibly with Unicode translation modifier +cs_p() -> + case encoding() of + latin1 -> "p"; + unicode -> "tp" + end. columns() -> case io:columns() of @@ -1436,9 +1409,20 @@ columns() -> _ -> 80 end. +encoding() -> + [{encoding, Encoding}] = enc(), + Encoding. + +enc() -> + case lists:keyfind(encoding, 1, io:getopts()) of + false -> [{encoding,latin1}]; % should never happen + Enc -> [Enc] + end. + garb(Shell) -> erlang:garbage_collect(Shell), catch erlang:garbage_collect(whereis(user)), + catch erlang:garbage_collect(whereis(group)), catch erlang:garbage_collect(group_leader()), erlang:garbage_collect(). @@ -1458,7 +1442,8 @@ check_env(V) -> ok; {ok, Val} -> Txt = io_lib:fwrite( - <<"Invalid value of STDLIB configuration parameter ~p: ~p\n">>, + ("Invalid value of STDLIB configuration parameter ~w: ~" + ++cs_p()++"\n"), [V, Val]), error_logger:info_report(lists:flatten(Txt)) end. diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl index fc029a582f..03f0a19f14 100644 --- a/lib/stdlib/src/string.erl +++ b/lib/stdlib/src/string.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -257,7 +258,7 @@ chars(C, N, Tail) when N > 0 -> chars(C, 0, Tail) when is_integer(C) -> Tail. -%% Torbj�rn's bit. +%% Torbjörn's bit. %%% COPIES %%% diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl index 4dd70ad425..2d6287814e 100644 --- a/lib/stdlib/src/sys.erl +++ b/lib/stdlib/src/sys.erl @@ -39,7 +39,18 @@ | {'in', Msg :: _, From :: _} | {'out', Msg :: _, To :: _} | term(). --opaque dbg_opt() :: list(). +-opaque dbg_opt() :: {'trace', 'true'} + | {'log', + {N :: non_neg_integer(), + [{Event :: system_event(), + FuncState :: _, + FormFunc :: dbg_fun()}]}} + | {'statistics', {file:date_time(), + {'reductions', non_neg_integer()}, + MessagesIn :: non_neg_integer(), + MessagesOut :: non_neg_integer()}} + | {'log_to_file', file:io_device()} + | {Func :: dbg_fun(), FuncState :: term()}. -type dbg_fun() :: fun((FuncState :: _, Event :: system_event(), ProcState :: _) -> 'done' | (NewFuncState :: _)). @@ -47,24 +58,22 @@ %%----------------------------------------------------------------- %% System messages %%----------------------------------------------------------------- --spec suspend(Name) -> Void when - Name :: name(), - Void :: term(). +-spec suspend(Name) -> 'ok' when + Name :: name(). suspend(Name) -> send_system_msg(Name, suspend). --spec suspend(Name, Timeout) -> Void when + +-spec suspend(Name, Timeout) -> 'ok' when Name :: name(), - Timeout :: timeout(), - Void :: term(). + Timeout :: timeout(). suspend(Name, Timeout) -> send_system_msg(Name, suspend, Timeout). --spec resume(Name) -> Void when - Name :: name(), - Void :: term(). +-spec resume(Name) -> 'ok' when + Name :: name(). resume(Name) -> send_system_msg(Name, resume). --spec resume(Name, Timeout) -> Void when + +-spec resume(Name, Timeout) -> 'ok' when Name :: name(), - Timeout :: timeout(), - Void :: term(). + Timeout :: timeout(). resume(Name, Timeout) -> send_system_msg(Name, resume, Timeout). -spec get_status(Name) -> Status when @@ -73,9 +82,10 @@ resume(Name, Timeout) -> send_system_msg(Name, resume, Timeout). SItem :: (PDict :: [{Key :: term(), Value :: term()}]) | (SysState :: 'running' | 'suspended') | (Parent :: pid()) - | (Dbg :: dbg_opt()) + | (Dbg :: [dbg_opt()]) | (Misc :: term()). get_status(Name) -> send_system_msg(Name, get_status). + -spec get_status(Name, Timeout) -> Status when Name :: name(), Timeout :: timeout(), @@ -83,7 +93,7 @@ get_status(Name) -> send_system_msg(Name, get_status). SItem :: (PDict :: [{Key :: term(), Value :: term()}]) | (SysState :: 'running' | 'suspended') | (Parent :: pid()) - | (Dbg :: dbg_opt()) + | (Dbg :: [dbg_opt()]) | (Misc :: term()). get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout). @@ -95,6 +105,7 @@ get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout). Reason :: term(). change_code(Name, Mod, Vsn, Extra) -> send_system_msg(Name, {change_code, Mod, Vsn, Extra}). + -spec change_code(Name, Module, OldVsn, Extra, Timeout) -> 'ok' | {error, Reason} when Name :: name(), @@ -191,35 +202,33 @@ no_debug(Name) -> send_system_msg(Name, {debug, no_debug}). Timeout :: timeout(). no_debug(Name, Timeout) -> send_system_msg(Name, {debug, no_debug}, Timeout). --spec install(Name, FuncSpec) -> Void when +-spec install(Name, FuncSpec) -> 'ok' when Name :: name(), FuncSpec :: {Func, FuncState}, Func :: dbg_fun(), - FuncState :: term(), - Void :: term(). + FuncState :: term(). install(Name, {Func, FuncState}) -> send_system_msg(Name, {debug, {install, {Func, FuncState}}}). --spec install(Name, FuncSpec, Timeout) -> Void when + +-spec install(Name, FuncSpec, Timeout) -> 'ok' when Name :: name(), FuncSpec :: {Func, FuncState}, Func :: dbg_fun(), FuncState :: term(), - Timeout :: timeout(), - Void :: term(). + Timeout :: timeout(). install(Name, {Func, FuncState}, Timeout) -> send_system_msg(Name, {debug, {install, {Func, FuncState}}}, Timeout). --spec remove(Name, Func) -> Void when +-spec remove(Name, Func) -> 'ok' when Name :: name(), - Func :: dbg_fun(), - Void :: term(). + Func :: dbg_fun(). remove(Name, Func) -> send_system_msg(Name, {debug, {remove, Func}}). --spec remove(Name, Func, Timeout) -> Void when + +-spec remove(Name, Func, Timeout) -> 'ok' when Name :: name(), Func :: dbg_fun(), - Timeout :: timeout(), - Void :: term(). + Timeout :: timeout(). remove(Name, Func, Timeout) -> send_system_msg(Name, {debug, {remove, Func}}, Timeout). @@ -245,18 +254,13 @@ mfa(Name, {change_code, Mod, Vsn, Extra}) -> {sys, change_code, [Name, Mod, Vsn, Extra]}; mfa(Name, Atom) -> {sys, Atom, [Name]}. + mfa(Name, Req, Timeout) -> {M, F, A} = mfa(Name, Req), {M, F, A ++ [Timeout]}. %%----------------------------------------------------------------- %% Func: handle_system_msg/6 -%% Args: Msg ::= term() -%% From ::= {pid(),Ref} but don't count on that -%% Parent ::= pid() -%% Module ::= atom() -%% Debug ::= [debug_opts()] -%% Misc ::= term() %% Purpose: Used by a process module that wishes to take care of %% system messages. The process receives a {system, From, %% Msg} message, and passes the Msg to this function. @@ -268,14 +272,14 @@ mfa(Name, Req, Timeout) -> %% The Module must export system_continue/3, system_terminate/4 %% and format_status/2 for status information. %%----------------------------------------------------------------- --spec handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> Void when +-spec handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> + no_return() when Msg :: term(), From :: {pid(), Tag :: _}, Parent :: pid(), Module :: module(), Debug :: [dbg_opt()], - Misc :: term(), - Void :: term(). + Misc :: term(). handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> handle_system_msg(running, Msg, From, Parent, Module, Debug, Misc, false). @@ -294,10 +298,6 @@ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib) -> %%----------------------------------------------------------------- %% Func: handle_debug/4 -%% Args: Debug ::= [debug_opts()] -%% Func ::= {M,F} | fun() arity 3 -%% State ::= term() -%% Event ::= {in, Msg} | {in, Msg, From} | {out, Msg, To} | term() %% Purpose: Called by a process that wishes to debug an event. %% Func is a formatting function, called as Func(Device, Event). %% Returns: [debug_opts()] @@ -453,6 +453,7 @@ print_event(Dev, {Event, State, FormFunc}) -> FormFunc(Dev, Event, State). init_stat() -> {erlang:localtime(), process_info(self(), reductions), 0, 0}. + get_stat({Time, {reductions, Reds}, In, Out}) -> {reductions, Reds2} = process_info(self(), reductions), [{start_time, Time}, {current_time, erlang:localtime()}, @@ -492,9 +493,8 @@ get_debug2(Item, Debug, Default) -> _ -> Default end. --spec print_log(Debug) -> Void when - Debug :: [dbg_opt()], - Void :: term(). +-spec print_log(Debug) -> 'ok' when + Debug :: [dbg_opt()]. print_log(Debug) -> {_N, Logs} = get_debug(log, Debug, {0, []}), lists:foreach(fun print_event/1, @@ -511,8 +511,6 @@ close_log_file(Debug) -> %%----------------------------------------------------------------- %% Func: debug_options/1 -%% Args: [trace|log|{log,N}|statistics|{log_to_file, FileName}| -%% {install, {Func, FuncState}}] %% Purpose: Initiate a debug structure. Called by a process that %% wishes to initiate the debug structure without the %% system messages. @@ -521,7 +519,11 @@ close_log_file(Debug) -> -spec debug_options(Options) -> [dbg_opt()] when Options :: [Opt], - Opt :: 'trace' | 'log' | 'statistics' | {'log_to_file', FileName} + Opt :: 'trace' + | 'log' + | {'log', pos_integer()} + | 'statistics' + | {'log_to_file', FileName} | {'install', FuncSpec}, FileName :: file:name(), FuncSpec :: {Func, FuncState}, @@ -529,6 +531,7 @@ close_log_file(Debug) -> FuncState :: term(). debug_options(Options) -> debug_options(Options, []). + debug_options([trace | T], Debug) -> debug_options(T, install_debug(trace, true, Debug)); debug_options([log | T], Debug) -> diff --git a/lib/stdlib/test/base64_SUITE.erl b/lib/stdlib/test/base64_SUITE.erl index 7b8650f224..b28df94221 100644 --- a/lib/stdlib/test/base64_SUITE.erl +++ b/lib/stdlib/test/base64_SUITE.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -179,7 +180,7 @@ mime_decode(Config) when is_list(Config) -> <<"o">> = base64:mime_decode(<<"b=w=====">>), %% Test misc white space and illegals with embedded padding <<"one">> = base64:mime_decode(<<" b~2=\r\n5()l===">>), - <<"on">> = base64:mime_decode(<<"\tb =2\"�4=�= ==">>), + <<"on">> = base64:mime_decode(<<"\tb =2\"¤4=¤= ==">>), <<"o">> = base64:mime_decode(<<"\nb=w=====">>), %% Two pads <<"Aladdin:open sesame">> = @@ -188,7 +189,7 @@ mime_decode(Config) when is_list(Config) -> <<"Hello World!!">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>), %% No pad <<"Aladdin:open sesam">> = - base64:mime_decode("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"), + base64:mime_decode("QWxhZGRpbjpvcG¤\")(VuIHNlc2Ft"), %% Encoded base 64 strings may be divided by non base 64 chars. %% In this cases whitespaces. <<"0123456789!@#0^&*();:<>,. []{}">> = @@ -222,7 +223,7 @@ mime_decode_to_string(Config) when is_list(Config) -> "o" = base64:mime_decode_to_string(<<"b=w=====">>), %% Test misc white space and illegals with embedded padding "one" = base64:mime_decode_to_string(<<" b~2=\r\n5()l===">>), - "on" = base64:mime_decode_to_string(<<"\tb =2\"�4=�= ==">>), + "on" = base64:mime_decode_to_string(<<"\tb =2\"¤4=¤= ==">>), "o" = base64:mime_decode_to_string(<<"\nb=w=====">>), %% Two pads "Aladdin:open sesame" = @@ -231,7 +232,7 @@ mime_decode_to_string(Config) when is_list(Config) -> "Hello World!!" = base64:mime_decode_to_string(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>), %% No pad "Aladdin:open sesam" = - base64:mime_decode_to_string("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"), + base64:mime_decode_to_string("QWxhZGRpbjpvcG¤\")(VuIHNlc2Ft"), %% Encoded base 64 strings may be divided by non base 64 chars. %% In this cases whitespaces. "0123456789!@#0^&*();:<>,. []{}" = diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl index bac59a3107..9b6f628aa9 100644 --- a/lib/stdlib/test/binary_module_SUITE.erl +++ b/lib/stdlib/test/binary_module_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 @@ -1331,9 +1332,9 @@ one_random_number(N) -> one_random(N) -> M = ((N - 1) rem 68) + 1, element(M,{$a,$b,$c,$d,$e,$f,$g,$h,$i,$j,$k,$l,$m,$n,$o,$p,$q,$r,$s,$t, - $u,$v,$w,$x,$y,$z,$�,$�,$�,$A,$B,$C,$D,$E,$F,$G,$H, - $I,$J,$K,$L,$M,$N,$O,$P,$Q,$R,$S,$T,$U,$V,$W,$X,$Y,$Z,$�, - $�,$�,$0,$1,$2,$3,$4,$5,$6,$7,$8,$9}). + $u,$v,$w,$x,$y,$z,$å,$ä,$ö,$A,$B,$C,$D,$E,$F,$G,$H, + $I,$J,$K,$L,$M,$N,$O,$P,$Q,$R,$S,$T,$U,$V,$W,$X,$Y,$Z,$Å, + $Ä,$Ö,$0,$1,$2,$3,$4,$5,$6,$7,$8,$9}). random_number({Min,Max}) -> % Min and Max are *length* of number in % decimal positions diff --git a/lib/stdlib/test/digraph_SUITE.erl b/lib/stdlib/test/digraph_SUITE.erl index 1d1326d60e..ed01b32a59 100644 --- a/lib/stdlib/test/digraph_SUITE.erl +++ b/lib/stdlib/test/digraph_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -400,7 +400,7 @@ sane1(G) -> lists:foreach( fun(V) -> InEs = digraph:in_edges(G, V), - %% Nu har man *alla* inkanter f�r V + %% *All* in-edoges of V lists:foreach( fun(E) -> case digraph:edge(G, E) of diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl index 77c615d6d9..606bbbcbb2 100644 --- a/lib/stdlib/test/epp_SUITE.erl +++ b/lib/stdlib/test/epp_SUITE.erl @@ -25,7 +25,7 @@ variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1, pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1, otp_8130/1, overload_mac/1, otp_8388/1, otp_8470/1, otp_8503/1, - otp_8562/1, otp_8665/1, otp_8911/1]). + otp_8562/1, otp_8665/1, otp_8911/1, otp_10302/1]). -export([epp_parse_erl_form/2]). @@ -67,7 +67,7 @@ all() -> {group, variable}, otp_4870, otp_4871, otp_5362, pmod, not_circular, skip_header, otp_6277, otp_7702, otp_8130, overload_mac, otp_8388, otp_8470, otp_8503, otp_8562, - otp_8665, otp_8911]. + otp_8665, otp_8911, otp_10302]. groups() -> [{upcase_mac, [], [upcase_mac_1, upcase_mac_2]}, @@ -582,12 +582,13 @@ otp_8130(suite) -> otp_8130(Config) when is_list(Config) -> true = os:putenv("epp_inc1", "stdlib"), Ts = [{otp_8130_1, - %% The scanner handles UNICODE in a special way. Hopefully - %% temporarily. <<"-define(M(A), ??A). " "t() -> " - " \"{ 34 , [ $1 , 2730 ] , \\\"34\\\" , X . a , 2730 }\" = " - " ?M({34,\"1\\x{aaa}\",\"34\",X.a,$\\x{aaa}}), ok. ">>, + " L = \"{ 34 , \\\"1\\\\x{AAA}\\\" , \\\"34\\\" , X . a , $\\\\x{AAA} }\", " + " R = ?M({34,\"1\\x{aaa}\",\"34\",X.a,$\\x{aaa}})," + " Lt = erl_scan:string(L, 1, [unicode])," + " Rt = erl_scan:string(R, 1, [unicode])," + " Lt = Rt, ok. ">>, ok}, {otp_8130_2, @@ -1284,6 +1285,75 @@ otp_8665(Config) when is_list(Config) -> ?line [] = compile(Config, Cs), ok. +otp_10302(doc) -> + "OTP-10302. Unicode characters scanner/parser."; +otp_10302(suite) -> + []; +otp_10302(Config) when is_list(Config) -> + %% Two messages (one too many). Keeps otp_4871 happy. + Cs = [{otp_8562, + <<"%% coding: utf-8\n \n \x{E4}">>, + {errors,[{3,epp,cannot_parse}, + {3,file_io_server,invalid_unicode}],[]}} + ], + [] = compile(Config, Cs), + Dir = ?config(priv_dir, Config), + File = filename:join(Dir, "otp_10302.erl"), + utf8 = encoding("coding: utf-8", File), + utf8 = encoding("coding: UTF-8", File), + latin1 = encoding("coding: Latin-1", File), + latin1 = encoding("coding: latin-1", File), + none = encoding_com("coding: utf-8", File), + none = encoding_com("\n\n%% coding: utf-8", File), + none = encoding_nocom("\n\n coding: utf-8", File), + utf8 = encoding_com("\n%% coding: utf-8", File), + utf8 = encoding_nocom("\n coding: utf-8", File), + none = encoding("coding: \nutf-8", File), + latin1 = encoding("Encoding : latin-1", File), + utf8 = encoding("ccoding: UTF-8", File), + utf8 = encoding("coding= utf-8", File), + utf8 = encoding_com(" %% coding= utf-8", File), + utf8 = encoding("coding = utf-8", File), + none = encoding("coding: utf-16 coding: utf-8", File), %first is bad + none = encoding("Coding: utf-8", File), %capital c + utf8 = encoding("-*- coding: utf-8 -*-", File), + utf8 = encoding("-*-coding= utf-8-*-", File), + utf8 = encoding("codingcoding= utf-8", File), + ok = prefix("coding: utf-8", File, utf8), + + "coding: latin-1" = epp:encoding_to_string(latin1), + "coding: utf-8" = epp:encoding_to_string(utf8), + true = lists:member(epp:default_encoding(), [latin1, utf8]), + + ok. + +prefix(S, File, Enc) -> + prefix(0, S, File, Enc). + +prefix(100, _S, _File, _) -> + ok; +prefix(N, S, File, Enc) -> + Enc = encoding(lists:duplicate(N, $\s) ++ S, File), + prefix(N+1, S, File, Enc). + +encoding(Enc, File) -> + E = encoding_com("%% " ++ Enc, File), + none = encoding_com(Enc, File), + E = encoding_nocom(Enc, File). + +encoding_com(Enc, File) -> + ok = file:write_file(File, Enc), + {ok, Fd} = file:open(File, [read]), + E = epp:set_encoding(Fd), + ok = file:close(Fd), + E = epp:read_encoding(File). + +encoding_nocom(Enc, File) -> + ok = file:write_file(File, Enc), + {ok, Fd} = file:open(File, [read]), + ok = file:close(Fd), + epp:read_encoding(File, [{in_comment_only, false}]). + check(Config, Tests) -> eval_tests(Config, fun check_test/2, Tests). diff --git a/lib/stdlib/test/erl_expand_records_SUITE.erl b/lib/stdlib/test/erl_expand_records_SUITE.erl index e248934e10..e51c05a22c 100644 --- a/lib/stdlib/test/erl_expand_records_SUITE.erl +++ b/lib/stdlib/test/erl_expand_records_SUITE.erl @@ -107,8 +107,7 @@ attributes(doc) -> attributes(suite) -> []; attributes(Config) when is_list(Config) -> Ts = [ - <<"-import(erl_expand_records_SUITE). - -import(lists, [append/2, reverse/1]). + <<"-import(lists, [append/2, reverse/1]). -record(r, {a,b}). @@ -158,12 +157,12 @@ expr(Config) when is_list(Config) -> 2 = fun(X) -> X end(One + One), 3 = fun exprec_test:f/1(3), 4 = exprec_test:f(4), - 5 = ''.f(5), + 5 = f(5), L = receive {a,message,L0} -> L0 end, - case catch a.b.c:foo(bar) of + case catch a:foo(bar) of {'EXIT', _} -> ok end, _ = receive %Suppress warning. @@ -263,8 +262,7 @@ pattern(doc) -> pattern(suite) -> []; pattern(Config) when is_list(Config) -> Ts = [ - <<"-import(erl_expand_records_SUITE). - -import(lists, [append/2, reverse/1]). + <<"-import(lists, [append/2, reverse/1]). -record(r, {a,b}). @@ -292,10 +290,10 @@ pattern(Config) when is_list(Config) -> 21 = t(#r{a = #r{}}), 22 = t(2), 23 = t(#r{a = #r{}, b = b}), - 24 = t(a.b.c), + 24 = t(abc), ok. - t(a.b.c) -> + t(abc) -> 24; t($a) -> 2; diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 90a37f6441..774229fca9 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1732,7 +1732,7 @@ otp_5362(Config) when is_list(Config) -> {otp_5362_2, <<"-export([inline/0]). - -import(lists.foo, [a/1,b/1]). % b/1 is not used + -import(lists, [a/1,b/1]). % b/1 is not used -compile([{inline,{inl,7}}]). % undefined -compile([{inline,[{inl,17}]}]). % undefined @@ -1764,7 +1764,7 @@ otp_5362(Config) when is_list(Config) -> {6,erl_lint,{bad_inline,{inl,17}}}, {11,erl_lint,{undefined_function,{fipp,0}}}, {22,erl_lint,{bad_nowarn_unused_function,{and_not_used,2}}}], - [{3,erl_lint,{unused_import,{{b,1},'lists.foo'}}}, + [{3,erl_lint,{unused_import,{{b,1},lists}}}, {9,erl_lint,{unused_function,{foop,0}}}, {19,erl_lint,{unused_function,{not_used,0}}}, {23,erl_lint,{unused_function,{and_not_used,1}}}]}}, diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index 64853ca078..db416b03b0 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 @@ -40,7 +40,7 @@ init_per_testcase/2, end_per_testcase/2]). -export([ func/1, call/1, recs/1, try_catch/1, if_then/1, - receive_after/1, bits/1, head_tail/1, package/1, + receive_after/1, bits/1, head_tail/1, cond1/1, block/1, case1/1, ops/1, messages/1, old_mnemosyne_syntax/1, import_export/1, misc_attrs/1, @@ -48,7 +48,8 @@ neg_indent/1, otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1, - otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1]). + otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1, + otp_10302/1]). %% Internal export. -export([ehook/6]). @@ -74,12 +75,12 @@ all() -> groups() -> [{expr, [], [func, call, recs, try_catch, if_then, receive_after, - bits, head_tail, package, cond1, block, case1, ops, + bits, head_tail, cond1, block, case1, ops, messages, old_mnemosyne_syntax]}, {attributes, [], [misc_attrs, import_export]}, {tickets, [], [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238, - otp_8473, otp_8522, otp_8567, otp_8664, otp_9147]}]. + otp_8473, otp_8522, otp_8567, otp_8664, otp_9147, otp_10302]}]. init_per_suite(Config) -> Config. @@ -438,9 +439,6 @@ bits(Config) when is_list(Config) -> ?line ok = pp_expr(<<"<<{a,b}/binary>>">>), ?line ok = pp_expr(<<"<<{foo:bar(),b}/binary>>">>), ?line ok = pp_expr(<<"<<(foo:bar()):(foo:bar())/binary>>">>), - ?line ok = pp_expr(<<"<<(foo.bar)/binary>>">>), - ?line ok = pp_expr(<<"<<(foo.bar):all/binary>>">>), - ?line ok = pp_expr(<<"<<(foo.bar):(foo.bar)/binary>>">>), ok. head_tail(suite) -> @@ -462,17 +460,6 @@ head_tail(Config) when is_list(Config) -> ?line compile(Config, Ts), ok. -package(suite) -> - []; -package(Config) when is_list(Config) -> - Ts = [{package_1, - <<"t() -> a.b:foo().">>}, - {package_2, - <<"t() -> .lists:sort([]).">>} - ], - ?line compile(Config, Ts), - ok. - cond1(suite) -> []; cond1(Config) when is_list(Config) -> @@ -614,13 +601,11 @@ misc_attrs(suite) -> []; misc_attrs(Config) when is_list(Config) -> ?line ok = pp_forms(<<"-module(m). ">>), - ?line ok = pp_forms(<<"-module(m.p, [A,B]). ">>), ?line ok = pp_forms(<<"-module(m, [Aafjlksfjdlsjflsdfjlsdjflkdsfjlk," "Blsjfdlslfjsdf]). ">>), ?line ok = pp_forms(<<"-export([]). ">>), ?line ok = pp_forms(<<"-export([foo/2, bar/0]). ">>), ?line ok = pp_forms(<<"-export([bar/0]). ">>), - ?line ok = pp_forms(<<"-import(.lists). ">>), ?line ok = pp_forms(<<"-import(lists, []). ">>), ?line ok = pp_forms(<<"-import(lists, [map/2]). ">>), ?line ok = pp_forms(<<"-import(lists, [map/2, foreach/2]). ">>), @@ -634,8 +619,12 @@ misc_attrs(Config) when is_list(Config) -> hook(suite) -> []; hook(Config) when is_list(Config) -> + F = fun(H) -> H end, + do_hook(F). + +do_hook(HookFun) -> Lc = parse_expr(binary_to_list(<<"[X || X <- [1,2,3]].">>)), - H = fun hook/4, + H = HookFun(fun hook/4), Expr = {call,0,{atom,0,fff},[{foo,Lc},{foo,Lc},{foo,Lc}]}, EChars = lists:flatten(erl_pp:expr(Expr, 0, H)), Call = {call,0,{atom,0,foo},[Lc]}, @@ -692,7 +681,7 @@ hook(Config) when is_list(Config) -> GChars2 = erl_pp:guard(G2), ?line true = GChars =:= lists:flatten(GChars2), - EH = {?MODULE, ehook, [foo,bar]}, + EH = HookFun({?MODULE, ehook, [foo,bar]}), XEChars = erl_pp:expr(Expr, -1, EH), ?line true = remove_indentation(EChars) =:= lists:flatten(XEChars), XEChars2 = erl_pp:expr(Expr, EH), @@ -1068,6 +1057,43 @@ otp_9147(Config) when is_list(Config) -> string:tokens(binary_to_list(Bin), "\n")), ok. +otp_10302(doc) -> + "OTP-10302. Unicode characters scanner/parser."; +otp_10302(suite) -> []; +otp_10302(Config) when is_list(Config) -> + Ts = [{uni_1, + <<"t() -> <<(<<\"abc\\x{aaa}\">>):3/binary>>.">>} + ], + compile(Config, Ts), + ok = pp_expr(<<"$\\x{aaa}">>), + ok = pp_expr(<<"\"1\\x{aaa}\"">>), + ok = pp_expr(<<"<<<<\"hej\">>/binary>>">>), + ok = pp_expr(<<"<< <<\"1\\x{aaa}\">>/binary>>">>), + + U = [{encoding,unicode}], + + do_hook(fun(H) -> [{hook,H}] end), + do_hook(fun(H) -> [{hook,H}]++U end), + + ok = pp_expr(<<"$\\x{aaa}">>, [{hook,fun hook/4}]), + + Opts = [{hook, fun unicode_hook/4},{encoding,unicode}], + Lc = parse_expr("[X || X <- [\"\x{400}\",\"\xFF\"]]."), + Expr = {call,0,{atom,0,fff},[{foo,{foo,Lc}},{foo,{foo,Lc}}]}, + EChars = lists:flatten(erl_pp:expr(Expr, 0, Opts)), + Call = {call,0,{atom,0,foo},[{call,0,{atom,0,foo},[Lc]}]}, + Expr2 = {call,0,{atom,0,fff},[Call,Call]}, + EChars2 = erl_pp:exprs([Expr2], U), + EChars = lists:flatten(EChars2), + [$\x{400},$\x{400}] = [C || C <- EChars, C > 255], + + ok = pp_forms(<<"function() -> {\"\x{400}\",$\x{400}}. "/utf8>>, U), + ok = pp_forms("function() -> {\"\x{400}\",$\x{400}}. ", []), + ok. + +unicode_hook({foo,E}, I, P, H) -> + erl_pp:expr({call,0,{atom,0,foo},[E]}, I, P, H). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% compile(Config, Tests) -> @@ -1137,9 +1163,11 @@ flat_expr(Expr) -> pp_forms(Bin) -> pp_forms(Bin, none). -pp_forms(Bin, Hook) -> - PP1 = (catch parse_and_pp_forms(binary_to_list(Bin), Hook)), - PP2 = (catch parse_and_pp_forms(PP1, Hook)), +pp_forms(Bin, Options) when is_binary(Bin) -> + pp_forms(to_list(Bin, Options), Options); +pp_forms(List, Options) when is_list(List) -> + PP1 = (catch parse_and_pp_forms(List, Options)), + PP2 = (catch parse_and_pp_forms(PP1, Options)), case PP1 =:= PP2 of % same line numbers true -> test_max_line(PP1); @@ -1147,8 +1175,8 @@ pp_forms(Bin, Hook) -> not_ok end. -parse_and_pp_forms(String, Hook) -> - lists:append(lists:map(fun(AF) -> erl_pp:form(AF, Hook) +parse_and_pp_forms(String, Options) -> + lists:append(lists:map(fun(AF) -> erl_pp:form(AF, Options) end, parse_forms(String))). parse_forms(Chars) -> @@ -1158,7 +1186,7 @@ parse_forms(Chars) -> parse_forms2([], _Cont, _Line, Forms) -> lists:reverse(Forms); parse_forms2(String, Cont0, Line, Forms) -> - case erl_scan:tokens(Cont0, String, Line) of + case erl_scan:tokens(Cont0, String, Line, [unicode]) of {done, {ok, Tokens, EndLine}, Chars} -> {ok, Form} = erl_parse:parse_form(Tokens), parse_forms2(Chars, [], EndLine, [Form | Forms]); @@ -1174,10 +1202,12 @@ pp_expr(Bin) -> pp_expr(Bin, none). %% Final dot is added. -pp_expr(Bin, Hook) -> - PP1 = (catch parse_and_pp_expr(binary_to_list(Bin), 0, Hook)), - PPneg = (catch parse_and_pp_expr(binary_to_list(Bin), -1, Hook)), - PP2 = (catch parse_and_pp_expr(PPneg, 0, Hook)), +pp_expr(Bin, Options) when is_binary(Bin) -> + pp_expr(to_list(Bin, Options), Options); +pp_expr(List, Options) when is_list(List) -> + PP1 = (catch parse_and_pp_expr(List, 0, Options)), + PPneg = (catch parse_and_pp_expr(List, -1, Options)), + PP2 = (catch parse_and_pp_expr(PPneg, 0, Options)), if PP1 =:= PP2 -> % same line numbers case @@ -1192,15 +1222,24 @@ pp_expr(Bin, Hook) -> not_ok end. -parse_and_pp_expr(String, Indent, Hook) -> +parse_and_pp_expr(String, Indent, Options) -> StringDot = lists:flatten(String) ++ ".", - erl_pp:expr(parse_expr(StringDot), Indent, Hook). + erl_pp:expr(parse_expr(StringDot), Indent, Options). parse_expr(Chars) -> - {ok, Tokens, _} = erl_scan:string(Chars), + {ok, Tokens, _} = erl_scan:string(Chars, 1, [unicode]), {ok, [Expr]} = erl_parse:parse_exprs(Tokens), Expr. +to_list(Bin, Options) when is_list(Options) -> + case proplists:get_value(encoding, Options) of + unicode -> unicode:characters_to_list(Bin); + encoding -> binary_to_list(Bin); + undefined -> binary_to_list(Bin) + end; +to_list(Bin, _Hook) -> + binary_to_list(Bin). + test_new_line(String) -> case string:chr(String, $\n) of 0 -> ok; diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl index 4298b2c701..34e1b99abe 100644 --- a/lib/stdlib/test/erl_scan_SUITE.erl +++ b/lib/stdlib/test/erl_scan_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 @@ -20,7 +21,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). --export([ error_1/1, error_2/1, iso88591/1, otp_7810/1]). +-export([ error_1/1, error_2/1, iso88591/1, otp_7810/1, otp_10302/1]). -import(lists, [nth/2,flatten/1]). -import(io_lib, [print/1]). @@ -59,7 +60,7 @@ end_per_testcase(_Case, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [{group, error}, iso88591, otp_7810]. + [{group, error}, iso88591, otp_7810, otp_10302]. groups() -> [{error, [], [error_1, error_2]}]. @@ -131,10 +132,10 @@ iso88591(Config) when is_list(Config) -> ?line ok = case catch begin %% Some atom and variable names - V1s = [$�,$�,$�,$�], - V2s = [$N,$�,$r], - A1s = [$h,$�,$r], - A2s = [$�,$r,$e], + V1s = [$Á,$á,$é,$ë], + V2s = [$N,$ä,$r], + A1s = [$h,$ä,$r], + A2s = [$ö,$r,$e], %% Test parsing atom and variable characters. {ok,Ts1,_} = erl_scan:string(V1s ++ " " ++ V2s ++ "\327" ++ @@ -214,8 +215,8 @@ atoms() -> ?line test_string("'a b'", [{atom,1,'a b'}]), ?line test_string("a", [{atom,1,a}]), ?line test_string("a@2", [{atom,1,a@2}]), - ?line test_string([39,65,200,39], [{atom,1,'A�'}]), - ?line test_string("�rlig �sten", [{atom,1,�rlig},{atom,1,�sten}]), + ?line test_string([39,65,200,39], [{atom,1,'AÈ'}]), + ?line test_string("ärlig östen", [{atom,1,ärlig},{atom,1,östen}]), ?line {ok,[{atom,_,'$a'}],{1,6}} = erl_scan:string("'$\\a'", {1,1}), ?line test("'$\\a'"), @@ -289,7 +290,7 @@ errors() -> ?line {error,{1,erl_scan,{string,$","str"}},1} = %" erl_scan:string("\"str"), %" ?line {error,{1,erl_scan,char},1} = erl_scan:string("$"), - ?line test_string([34,65,200,34], [{string,1,"A�"}]), + ?line test_string([34,65,200,34], [{string,1,"AÈ"}]), ?line test_string("\\", [{'\\',1}]), ?line {'EXIT',_} = (catch {foo, erl_scan:string('$\\a', {1,1})}), % type error @@ -354,7 +355,7 @@ dots() -> {".\n", {ok,[{dot,1}],2}}, {".%", {ok,[{dot,1}],1}}, {".\210",{ok,[{dot,1}],1}}, - {".% �h",{ok,[{dot,1}],1}}, + {".% öh",{ok,[{dot,1}],1}}, {".%\n", {ok,[{dot,1}],2}}, {".$", {error,{1,erl_scan,char},1}}, {".$\\", {error,{1,erl_scan,char},1}}, @@ -369,7 +370,7 @@ dots() -> ?line [{column,1},{length,1},{line,1},{text,"."}] = erl_scan:token_info(T2, [column, length, line, text]), ?line {ok,[{dot,_}=T3],{1,6}} = - erl_scan:string(".% �h", {1,1}, text), + erl_scan:string(".% öh", {1,1}, text), ?line [{column,1},{length,1},{line,1},{text,"."}] = erl_scan:token_info(T3, [column, length, line, text]), ?line {error,{{1,2},erl_scan,char},{1,3}} = @@ -472,11 +473,11 @@ chars() -> variables() -> - ?line test_string(" \237_Aou�eiy��", [{var,1,'_Aou�eiy��'}]), + ?line test_string(" \237_Aouåeiyäö", [{var,1,'_Aouåeiyäö'}]), ?line test_string("A_b_c@", [{var,1,'A_b_c@'}]), ?line test_string("V@2", [{var,1,'V@2'}]), - ?line test_string("ABD�", [{var,1,'ABD�'}]), - ?line test_string("�rlig �sten", [{var,1,'�rlig'},{var,1,'�sten'}]), + ?line test_string("ABDÀ", [{var,1,'ABDÀ'}]), + ?line test_string("Ärlig Östen", [{var,1,'Ärlig'},{var,1,'Östen'}]), ok. eof() -> @@ -823,7 +824,7 @@ unicode() -> ?line {ok,[{char,1,1}],1} = erl_scan:string([$$,$\\,$^,1089]), ?line {error,{1,erl_scan,Error},1} = erl_scan:string("\"qa\x{aaa}"), - ?line "unterminated string starting with \"qa\\x{AAA}\"" = + ?line "unterminated string starting with \"qa"++[2730]++"\"" = erl_scan:format_error(Error), ?line {error,{{1,1},erl_scan,_},{1,11}} = erl_scan:string("\"qa\\x{aaa}",{1,1}), @@ -887,9 +888,10 @@ unicode() -> {char,_,$d},{']',_}],{1,8}} = erl_scan:string(Str1, {1,1}), ?line test(Str1), Comment = "%% "++[1089], - ?line {ok,[{comment,1,[$%,$%,$\s,1089]}],1} = + %% Returned a comment In R15B03: + {error,{1,erl_scan,{illegal,character}},1} = erl_scan:string(Comment, 1, return), - ?line {ok,[{comment,_,[$%,$%,$\s,1089]}],{1,5}} = + {error,{{1,1},erl_scan,{illegal,character}},{1,5}} = erl_scan:string(Comment, {1,1}, return), ok. @@ -958,6 +960,182 @@ more_chars() -> erl_scan:string("$\\xg", {1,1}), ok. +otp_10302(doc) -> + "OTP-10302. Unicode characters scanner/parser."; +otp_10302(suite) -> + []; +otp_10302(Config) when is_list(Config) -> + %% From unicode(): + {error,{1,erl_scan,{illegal,atom}},1} = + erl_scan:string("'a"++[1089]++"b'", 1, unicode), + {error,{{1,1},erl_scan,{illegal,atom}},{1,12}} = + erl_scan:string("'qa\\x{aaa}'",{1,1},unicode), + + {ok,[{char,1,1089}],1} = erl_scan:string([$$,1089], 1, unicode), + {ok,[{char,1,1089}],1} = erl_scan:string([$$,$\\,1089],1,unicode), + + Qs = "$\\x{aaa}", + {ok,[{char,1,2730}],1} = erl_scan:string(Qs,1,unicode), + {ok,[Q2],{1,9}} = erl_scan:string(Qs,{1,1},[unicode,text]), + [{category,char},{column,1},{length,8}, + {line,1},{symbol,16#aaa},{text,Qs}] = + erl_scan:token_info(Q2), + + Tags = [category, column, length, line, symbol, text], + + U1 = "\"\\x{aaa}\"", + {ok,[T1],{1,10}} = erl_scan:string(U1, {1,1}, [unicode,text]), + [{category,string},{column,1},{length,9},{line,1}, + {symbol,[16#aaa]},{text,U1}] = erl_scan:token_info(T1, Tags), + + U2 = "\"\\x41\\x{fff}\\x42\"", + {ok,[{string,1,[65,4095,66]}],1} = erl_scan:string(U2, 1, unicode), + + U3 = "\"a\n\\x{fff}\n\"", + {ok,[{string,1,[97,10,4095,10]}],3} = erl_scan:string(U3, 1,unicode), + + U4 = "\"\\^\n\\x{aaa}\\^\n\"", + {ok,[{string,1,[10,2730,10]}],3} = erl_scan:string(U4, 1,[unicode]), + + Str1 = "\"ab" ++ [1089] ++ "cd\"", + {ok,[{string,1,[97,98,1089,99,100]}],1} = + erl_scan:string(Str1,1,unicode), + {ok,[{string,{1,1},[97,98,1089,99,100]}],{1,8}} = + erl_scan:string(Str1, {1,1},unicode), + + OK1 = 16#D800-1, + OK2 = 16#DFFF+1, + OK3 = 16#FFFE-1, + OK4 = 16#FFFF+1, + OKL = [OK1,OK2,OK3,OK4], + + Illegal1 = 16#D800, + Illegal2 = 16#DFFF, + Illegal3 = 16#FFFE, + Illegal4 = 16#FFFF, + IllegalL = [Illegal1,Illegal2,Illegal3,Illegal4], + + [{ok,[{comment,1,[$%,$%,$\s,OK]}],1} = + erl_scan:string("%% "++[OK], 1, [unicode,return]) || + OK <- OKL], + {ok,[{comment,_,[$%,$%,$\s,OK1]}],{1,5}} = + erl_scan:string("%% "++[OK1], {1,1}, [unicode,return]), + [{error,{1,erl_scan,{illegal,character}},1} = + erl_scan:string("%% "++[Illegal], 1, [unicode,return]) || + Illegal <- IllegalL], + {error,{{1,1},erl_scan,{illegal,character}},{1,5}} = + erl_scan:string("%% "++[Illegal1], {1,1}, [unicode,return]), + + [{ok,[],1} = erl_scan:string("%% "++[OK], 1, [unicode]) || + OK <- OKL], + {ok,[],{1,5}} = erl_scan:string("%% "++[OK1], {1,1}, [unicode]), + [{error,{1,erl_scan,{illegal,character}},1} = + erl_scan:string("%% "++[Illegal], 1, [unicode]) || + Illegal <- IllegalL], + {error,{{1,1},erl_scan,{illegal,character}},{1,5}} = + erl_scan:string("%% "++[Illegal1], {1,1}, [unicode]), + + [{ok,[{string,{1,1},[OK]}],{1,4}} = + erl_scan:string("\""++[OK]++"\"",{1,1},unicode) || + OK <- OKL], + [{error,{{1,2},erl_scan,{illegal,character}},{1,3}} = + erl_scan:string("\""++[OK]++"\"",{1,1},unicode) || + OK <- IllegalL], + + [{error,{{1,1},erl_scan,{illegal,character}},{1,2}} = + erl_scan:string([Illegal],{1,1},unicode) || + Illegal <- IllegalL], + + {ok,[{char,{1,1},OK1}],{1,3}} = + erl_scan:string([$$,OK1],{1,1},unicode), + {error,{{1,1},erl_scan,{illegal,character}},{1,2}} = + erl_scan:string([$$,Illegal1],{1,1},unicode), + + {ok,[{char,{1,1},OK1}],{1,4}} = + erl_scan:string([$$,$\\,OK1],{1,1},unicode), + {error,{{1,1},erl_scan,{illegal,character}},{1,4}} = + erl_scan:string([$$,$\\,Illegal1],{1,1},unicode), + + {ok,[{string,{1,1},[55295]}],{1,5}} = + erl_scan:string("\"\\"++[OK1]++"\"",{1,1},unicode), + {error,{{1,2},erl_scan,{illegal,character}},{1,4}} = + erl_scan:string("\"\\"++[Illegal1]++"\"",{1,1},unicode), + + {ok,[{char,{1,1},OK1}],{1,10}} = + erl_scan:string("$\\x{D7FF}",{1,1},unicode), + {error,{{1,1},erl_scan,{illegal,character}},{1,10}} = + erl_scan:string("$\\x{D800}",{1,1},unicode), + + %% Not erl_scan, but erl_parse. + {integer,0,1} = erl_parse:abstract(1), + Float = 3.14, {float,0,Float} = erl_parse:abstract(Float), + {nil,0} = erl_parse:abstract([]), + {bin,0, + [{bin_element,0,{integer,0,1},default,default}, + {bin_element,0,{integer,0,2},default,default}]} = + erl_parse:abstract(<<1,2>>), + {cons,0,{tuple,0,[{atom,0,a}]},{atom,0,b}} = + erl_parse:abstract([{a} | b]), + {string,0,"str"} = erl_parse:abstract("str"), + {cons,0, + {integer,0,$a}, + {cons,0,{integer,0,1024},{string,0,"c"}}} = + erl_parse:abstract("a"++[1024]++"c"), + + Line = 17, + {integer,Line,1} = erl_parse:abstract(1, Line), + Float = 3.14, {float,Line,Float} = erl_parse:abstract(Float, Line), + {nil,Line} = erl_parse:abstract([], Line), + {bin,Line, + [{bin_element,Line,{integer,Line,1},default,default}, + {bin_element,Line,{integer,Line,2},default,default}]} = + erl_parse:abstract(<<1,2>>, Line), + {cons,Line,{tuple,Line,[{atom,Line,a}]},{atom,Line,b}} = + erl_parse:abstract([{a} | b], Line), + {string,Line,"str"} = erl_parse:abstract("str", Line), + {cons,Line, + {integer,Line,$a}, + {cons,Line,{integer,Line,1024},{string,Line,"c"}}} = + erl_parse:abstract("a"++[1024]++"c", Line), + + Opts1 = [{line,17}], + {integer,Line,1} = erl_parse:abstract(1, Opts1), + Float = 3.14, {float,Line,Float} = erl_parse:abstract(Float, Opts1), + {nil,Line} = erl_parse:abstract([], Opts1), + {bin,Line, + [{bin_element,Line,{integer,Line,1},default,default}, + {bin_element,Line,{integer,Line,2},default,default}]} = + erl_parse:abstract(<<1,2>>, Opts1), + {cons,Line,{tuple,Line,[{atom,Line,a}]},{atom,Line,b}} = + erl_parse:abstract([{a} | b], Opts1), + {string,Line,"str"} = erl_parse:abstract("str", Opts1), + {cons,Line, + {integer,Line,$a}, + {cons,Line,{integer,Line,1024},{string,Line,"c"}}} = + erl_parse:abstract("a"++[1024]++"c", Opts1), + + [begin + {integer,Line,1} = erl_parse:abstract(1, Opts2), + Float = 3.14, {float,Line,Float} = erl_parse:abstract(Float, Opts2), + {nil,Line} = erl_parse:abstract([], Opts2), + {bin,Line, + [{bin_element,Line,{integer,Line,1},default,default}, + {bin_element,Line,{integer,Line,2},default,default}]} = + erl_parse:abstract(<<1,2>>, Opts2), + {cons,Line,{tuple,Line,[{atom,Line,a}]},{atom,Line,b}} = + erl_parse:abstract([{a} | b], Opts2), + {string,Line,"str"} = erl_parse:abstract("str", Opts2), + {string,Line,[97,1024,99]} = + erl_parse:abstract("a"++[1024]++"c", Opts2) + end || Opts2 <- [[{encoding,unicode},{line,Line}], + [{encoding,utf8},{line,Line}]]], + + {cons,0, + {integer,0,97}, + {cons,0,{integer,0,1024},{string,0,"c"}}} = + erl_parse:abstract("a"++[1024]++"c", [{encoding,latin1}]), + ok. + test_string(String, Expected) -> {ok, Expected, _End} = erl_scan:string(String), test(String). diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl index 5b592c65cc..7634c21a17 100644 --- a/lib/stdlib/test/escript_SUITE.erl +++ b/lib/stdlib/test/escript_SUITE.erl @@ -34,7 +34,8 @@ create_and_extract/1, foldl/1, overflow/1, - verify_sections/3 + verify_sections/3, + unicode/1 ]). -include_lib("test_server/include/test_server.hrl"). @@ -46,7 +47,7 @@ all() -> [basic, errors, strange_name, emulator_flags, module_script, beam_script, archive_script, epp, create_and_extract, foldl, overflow, - archive_script_file_access]. + archive_script_file_access, unicode]. groups() -> []. @@ -810,6 +811,8 @@ normalize_sections(Sections) -> end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + foldl(Config) when is_list(Config) -> {NewFile, _FileInfo, _EmuArg, _Source, @@ -887,6 +890,20 @@ emulate_escript_foldl(Fun, Acc, File) -> {error, Reason} end. +unicode(Config) when is_list(Config) -> + Data = ?config(data_dir, Config), + Dir = filename:absname(Data), %Get rid of trailing slash. + run(Dir, "unicode1", + [<<"escript: exception error: an error occurred when evaluating" + " an arithmetic expression\n in operator '/'/2\n " + "called as <<170>> / <<170>>\nExitCode:127">>]), + run(Dir, "unicode2", + [<<"escript: exception error: an error occurred when evaluating" + " an arithmetic expression\n in operator '/'/2\n " + "called as <<\"\xaa\">> / <<\"\xaa\">>\nExitCode:127">>]), + run(Dir, "unicode3", [<<"ExitCode:0">>]), + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% overflow(Config) when is_list(Config) -> diff --git a/lib/stdlib/test/escript_SUITE_data/unicode1 b/lib/stdlib/test/escript_SUITE_data/unicode1 new file mode 100755 index 0000000000..a77574625e --- /dev/null +++ b/lib/stdlib/test/escript_SUITE_data/unicode1 @@ -0,0 +1,14 @@ +#!/usr/bin/env escript +%% -*- erlang -*- + +-export([main/1]). + +main(_) -> + ok = io:setopts([{encoding,unicode}]), + _D = erlang:system_flag(backtrace_depth, 0), + A = <<"\x{aa}">>, + S = lists:flatten(io_lib:format("~p/~p.", [A, A])), + {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), + {ok, Es} = erl_parse:parse_exprs(Ts), + B = erl_eval:new_bindings(), + erl_eval:exprs(Es, B). diff --git a/lib/stdlib/test/escript_SUITE_data/unicode2 b/lib/stdlib/test/escript_SUITE_data/unicode2 new file mode 100755 index 0000000000..495188f6f0 --- /dev/null +++ b/lib/stdlib/test/escript_SUITE_data/unicode2 @@ -0,0 +1,14 @@ +#!/usr/bin/env escript +%% -*- erlang -*- + +-export([main/1]). + +main(_) -> + ok = io:setopts([{encoding,latin1}]), + _D = erlang:system_flag(backtrace_depth, 0), + A = <<"\x{aa}">>, + S = lists:flatten(io_lib:format("~p/~p.", [A, A])), + {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), + {ok, Es} = erl_parse:parse_exprs(Ts), + B = erl_eval:new_bindings(), + erl_eval:exprs(Es, B). diff --git a/lib/stdlib/test/escript_SUITE_data/unicode3 b/lib/stdlib/test/escript_SUITE_data/unicode3 new file mode 100755 index 0000000000..944487dcae --- /dev/null +++ b/lib/stdlib/test/escript_SUITE_data/unicode3 @@ -0,0 +1,13 @@ +#!/usr/bin/env escript +%% -*- erlang; coding: utf-8 -*- + +-export([main/1]). + +main(_) -> + ok = io:setopts([{encoding,unicode}]), + Bin1 = <<"örn_Ѐ שלום-שלום+של 日本語">>, + + L = [246,114,110,95,1024,32,1513,1500,1493,1501,45,1513,1500,1493, + 1501,43,1513,1500,32,26085,26412,35486], + L = unicode:characters_to_list(Bin1, utf8), + ok. diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl index 74fcdcc7d2..521d7255ea 100644 --- a/lib/stdlib/test/io_SUITE.erl +++ b/lib/stdlib/test/io_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-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 @@ -28,7 +29,7 @@ manpage/1, otp_6708/1, otp_7084/1, otp_7421/1, io_lib_collect_line_3_wb/1, cr_whitespace_in_string/1, io_fread_newlines/1, otp_8989/1, io_lib_fread_literal/1, - io_lib_print_binary_depth_one/1]). + io_lib_print_binary_depth_one/1, otp_10302/1]). %-define(debug, true). @@ -64,7 +65,7 @@ all() -> manpage, otp_6708, otp_7084, otp_7421, io_lib_collect_line_3_wb, cr_whitespace_in_string, io_fread_newlines, otp_8989, io_lib_fread_literal, - io_lib_print_binary_depth_one]. + io_lib_print_binary_depth_one, otp_10302]. groups() -> []. @@ -894,7 +895,7 @@ otp_6354(Config) when is_list(Config) -> ?line "\"\\b\\t\\n\\v\\f\\r\\e\250\"" = p([8,9,10,11,12,13,27,168], 1, 40, -1), % ?line "\"\\b\\t\\n\"\n \"\\v\\f\\r\"\n \"\\e\250\"" = - ?line "\"\\b\\t\\n\\v\\f\\r\\e�\"" = + ?line "\"\\b\\t\\n\\v\\f\\r\\e¨\"" = p([8,9,10,11,12,13,27,168], 1, 10, -1), ?line "\"\\b\\t\\n\\v\\f\\r\\e\250\"" = p([8,9,10,11,12,13,27,168], 1, 40, 100), @@ -2034,3 +2035,44 @@ io_lib_print_binary_depth_one(Suite) when is_list(Suite) -> ?line "<<...>>" = fmt("~W", [<<1:7>>, 1]), ?line "<<...>>" = fmt("~P", [<<1:7>>, 1]), ok. + +otp_10302(doc) -> + "OTP-10302. Unicode"; +otp_10302(Suite) when is_list(Suite) -> + "\"\x{400}\"" = pretty("\x{400}", -1), + "<<\"\x{400}\"/utf8>>" = pretty(<<"\x{400}"/utf8>>, -1), + + "<<\"\x{400}foo\"/utf8>>" = pretty(<<"\x{400}foo"/utf8>>, 2), + "<<\"äppl\"/utf8>>" = pretty(<<"äppl"/utf8>>, 2), + "<<\"äppl\"/utf8...>>" = pretty(<<"äpple"/utf8>>, 2), + "<<\"apel\">>" = pretty(<<"apel">>, 2), + "<<\"apel\"...>>" = pretty(<<"apelsin">>, 2), + "<<228,112,112,108>>" = fmt("~tp", [<<"äppl">>]), + "<<228,...>>" = fmt("~tP", [<<"äppl">>, 2]), + + Chars = lists:seq(0, 512), % just a few... + [] = [C || C <- Chars, S <- io_lib:write_unicode_char_as_latin1(C), + not is_latin1(S)], + L1 = [S || C <- Chars, S <- io_lib:write_unicode_char(C), + not is_latin1(S)], + L1 = lists:seq(256, 512), + + [] = [C || C <- Chars, S <- io_lib:write_unicode_string_as_latin1([C]), + not is_latin1(S)], + L2 = [S || C <- Chars, S <- io_lib:write_unicode_string([C]), + not is_latin1(S)], + L2 = lists:seq(256, 512), + + ok. + +pretty(Term, Depth) when is_integer(Depth) -> + Opts = [{column, 1}, {line_length, 20}, + {depth, Depth}, {max_chars, 60}, + {encoding, unicode}], + pretty(Term, Opts); +pretty(Term, Opts) when is_list(Opts) -> + R = io_lib_pretty:print(Term, Opts), + lists:flatten(io_lib:format("~ts", [R])). + +is_latin1(S) -> + S >= 0 andalso S =< 255. diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl index 17e69f7c1c..299daf0e42 100644 --- a/lib/stdlib/test/io_proto_SUITE.erl +++ b/lib/stdlib/test/io_proto_SUITE.erl @@ -736,7 +736,7 @@ binary_options(Config) when is_list(Config) -> {getline_re, ".*<<\"hej\\\\n\">>"}, {putline, "io:get_line('')."}, {putline, binary_to_list(<<"\345\344\366"/utf8>>)}, - {getline_re, ".*<<\""++binary_to_list(unicode:characters_to_binary(<<"\345\344\366"/utf8>>,latin1,utf8))++"\\\\n\">>"} + {getline_re, ".*<<\""++binary_to_list(<<"\345\344\366"/utf8>>)++"\\\\n\"/utf8>>"} ],[],[],"-oldshell"), ok. diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl index e3090e4a47..cac8309bd9 100644 --- a/lib/stdlib/test/qlc_SUITE.erl +++ b/lib/stdlib/test/qlc_SUITE.erl @@ -6062,21 +6062,6 @@ otp_6673(Config) when is_list(Config) -> ], ?line run(Config, Ts_RT), - %% Ulf Wiger provided a patch that makes QLC work with packages: - Dir = filename:join(?privdir, "p"), - ?line ok = filelib:ensure_dir(filename:join(Dir, ".")), - File = filename:join(Dir, "p.erl"), - ?line ok = file:write_file(File, - <<"-module(p.p).\n" - "-export([q/0]).\n" - "-include_lib(\"stdlib/include/qlc.hrl\").\n" - "q() ->\n" - " .qlc:q([X || X <- [1,2]]).">>), - ?line {ok, 'p.p'} = compile:file(File, [{outdir,Dir}]), - ?line code:purge('p.p'), - ?line {module, 'p.p'} = code:load_abs(filename:rootname(File), 'p.p'), - ?line [1,2] = qlc:e(p.p:q()), - ok. otp_6964(doc) -> diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl index 8ee0a13f4c..500f5fadb9 100644 --- a/lib/stdlib/test/re_SUITE.erl +++ b/lib/stdlib/test/re_SUITE.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -291,10 +292,10 @@ global_capture(Config) when is_list(Config) -> ?line match = re:run("ABCabcdABCabcdA","a(?<FOO>bcd)",[global,{capture,none,index}]), ?line match = re:run("ABCabcdABCabcdA","a(?<FOO>bcd)",[global,{capture,none,binary}]), ?line match = re:run("ABCabcdABCabcdA","a(?<FOO>bcd)",[global,{capture,none,list}]), - ?line {match,[[<<195,133,98,99,100>>,<<"bcd">>],[<<"abcd">>,<<"bcd">>]]} = re:run("ABC�bcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,binary},unicode]), - ?line {match,[["�bcd","bcd"],["abcd","bcd"]]} = re:run(<<"ABC",8#303,8#205,"bcdABCabcdA">>,".(?<FOO>bcd)",[global,{capture,all,list},unicode]), - ?line {match,[["�bcd","bcd"],["abcd","bcd"]]} = re:run("ABC�bcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,list},unicode]), - ?line {match,[[{3,5},{5,3}],[{11,4},{12,3}]]} = re:run("ABC�bcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,index},unicode]), + ?line {match,[[<<195,133,98,99,100>>,<<"bcd">>],[<<"abcd">>,<<"bcd">>]]} = re:run("ABCÅbcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,binary},unicode]), + ?line {match,[["Åbcd","bcd"],["abcd","bcd"]]} = re:run(<<"ABC",8#303,8#205,"bcdABCabcdA">>,".(?<FOO>bcd)",[global,{capture,all,list},unicode]), + ?line {match,[["Åbcd","bcd"],["abcd","bcd"]]} = re:run("ABCÅbcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,list},unicode]), + ?line {match,[[{3,5},{5,3}],[{11,4},{12,3}]]} = re:run("ABCÅbcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,index},unicode]), ?t:timetrap_cancel(Dog), ok. @@ -314,17 +315,17 @@ replace_return(Config) when is_list(Config) -> Dog = ?t:timetrap(?t:minutes(3)), ?line {'EXIT',{badarg,_}} = (catch re:replace("na","(a","")), ?line <<"nasse">> = re:replace(<<"nisse">>,"i","a",[{return,binary}]), - ?line <<"ABC�XABCXA">> = re:replace("ABC\305abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,binary}]), + ?line <<"ABCÅXABCXA">> = re:replace("ABC\305abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,binary}]), - ?line [<<"ABC�">>, + ?line [<<"ABCÅ">>, <<"X">>, <<"ABC">>, <<"X">> | <<"A">> ] = - re:replace("ABC�abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,iodata}]), - ?line "ABC�XABCXA" = re:replace("ABC�abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,list},unicode]), - ?line <<65,66,67,195,133,88,65,66,67,88,65>> = re:replace("ABC�abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,binary},unicode]), - ?line <<65,66,67,195,133,88,65,66,67,97,98,99,100,65>> = re:replace("ABC�abcdABCabcdA","a(?<FOO>bcd)","X",[{return,binary},unicode]), + re:replace("ABCÅabcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,iodata}]), + ?line "ABCÅXABCXA" = re:replace("ABCÅabcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,list},unicode]), + ?line <<65,66,67,195,133,88,65,66,67,88,65>> = re:replace("ABCÅabcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,binary},unicode]), + ?line <<65,66,67,195,133,88,65,66,67,97,98,99,100,65>> = re:replace("ABCÅabcdABCabcdA","a(?<FOO>bcd)","X",[{return,binary},unicode]), ?line <<"iXk">> = re:replace("abcdefghijk","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\9X",[{return,binary}]), ?line <<"jXk">> = re:replace("abcdefghijk","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\10X",[{return,binary}]), ?line <<"Xk">> = re:replace("abcdefghijk","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\11X",[{return,binary}]), diff --git a/lib/stdlib/test/re_testoutput1_replacement_test.erl b/lib/stdlib/test/re_testoutput1_replacement_test.erl index 69cb140e0d..8f8d8762ad 100644 --- a/lib/stdlib/test/re_testoutput1_replacement_test.erl +++ b/lib/stdlib/test/re_testoutput1_replacement_test.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -274,10 +275,10 @@ run() -> ?line <<"dthing">> = iolist_to_binary(re:replace("dthing","^[^]cde]","y\\1I&MoqRPG&GQa\\1l",[global])), ?line <<"ething">> = iolist_to_binary(re:replace("ething","^[^]cde]","AsxwUn\\1GqkWNdgRJk",[])), ?line <<"ething">> = iolist_to_binary(re:replace("ething","^[^]cde]","AsxwUn\\1GqkWNdgRJk",[global])), -?line <<"RornKmOnaFr�tWgtW">> = iolist_to_binary(re:replace("�","^\\�","R\\1o\\1r\\1nKmOnaFr&tWgtW",[])), -?line <<"RornKmOnaFr�tWgtW">> = iolist_to_binary(re:replace("�","^\\�","R\\1o\\1r\\1nKmOnaFr&tWgtW",[global])), -?line <<"ufbmbfOYuK�wf�E�dx">> = iolist_to_binary(re:replace("�","^�","ufbmbfOYuK&wf&E&\\1dx",[])), -?line <<"ufbmbfOYuK�wf�E�dx">> = iolist_to_binary(re:replace("�","^�","ufbmbfOYuK&wf&E&\\1dx",[global])), +?line <<"RornKmOnaFrtWgtW">> = iolist_to_binary(re:replace("","^\\","R\\1o\\1r\\1nKmOnaFr&tWgtW",[])), +?line <<"RornKmOnaFrtWgtW">> = iolist_to_binary(re:replace("","^\\","R\\1o\\1r\\1nKmOnaFr&tWgtW",[global])), +?line <<"ufbmbfOYuKÿwfÿEÿdx">> = iolist_to_binary(re:replace("ÿ","^ÿ","ufbmbfOYuK&wf&E&\\1dx",[])), +?line <<"ufbmbfOYuKÿwfÿEÿdx">> = iolist_to_binary(re:replace("ÿ","^ÿ","ufbmbfOYuK&wf&E&\\1dx",[global])), ?line <<"oAdJme0jw">> = iolist_to_binary(re:replace("0","^[0-9]+$","oAdJme\\1&jw",[])), ?line <<"oAdJme0jw">> = iolist_to_binary(re:replace("0","^[0-9]+$","oAdJme\\1&jw",[global])), ?line <<"1aoKN">> = iolist_to_binary(re:replace("1","^[0-9]+$","&aoKN",[])), @@ -14972,10 +14973,10 @@ def">> = iolist_to_binary(re:replace("abc def","abc$","M",[global])), ?line <<"abcWCabcSYXGPjRugTabcVGabcSX">> = iolist_to_binary(re:replace("abcS","(abc)\\123","\\1WC&YXGPjRugT\\1VG&X",[])), ?line <<"abcWCabcSYXGPjRugTabcVGabcSX">> = iolist_to_binary(re:replace("abcS","(abc)\\123","\\1WC&YXGPjRugT\\1VG&X",[global])), -?line <<"fabc�Uabc�UmiqabceCsabcabc�">> = iolist_to_binary(re:replace("abc�","(abc)\\223","f&U&Umiq\\1eCs\\1&",[])), -?line <<"fabc�Uabc�UmiqabceCsabcabc�">> = iolist_to_binary(re:replace("abc�","(abc)\\223","f&U&Umiq\\1eCs\\1&",[global])), -?line <<"JRFabcxnbabc�Vkabc�fWigQMuaY">> = iolist_to_binary(re:replace("abc�","(abc)\\323","JRF\\1xnb&Vk&fWigQMuaY",[])), -?line <<"JRFabcxnbabc�Vkabc�fWigQMuaY">> = iolist_to_binary(re:replace("abc�","(abc)\\323","JRF\\1xnb&Vk&fWigQMuaY",[global])), +?line <<"fabcUabcUmiqabceCsabcabc">> = iolist_to_binary(re:replace("abc","(abc)\\223","f&U&Umiq\\1eCs\\1&",[])), +?line <<"fabcUabcUmiqabceCsabcabc">> = iolist_to_binary(re:replace("abc","(abc)\\223","f&U&Umiq\\1eCs\\1&",[global])), +?line <<"JRFabcxnbabcÓVkabcÓfWigQMuaY">> = iolist_to_binary(re:replace("abcÓ","(abc)\\323","JRF\\1xnb&Vk&fWigQMuaY",[])), +?line <<"JRFabcxnbabcÓVkabcÓfWigQMuaY">> = iolist_to_binary(re:replace("abcÓ","(abc)\\323","JRF\\1xnb&Vk&fWigQMuaY",[global])), ?line <<"vgabc@QQ">> = iolist_to_binary(re:replace("abc@","(abc)\\100","vg&QQ",[])), ?line <<"vgabc@QQ">> = iolist_to_binary(re:replace("abc@","(abc)\\100","vg&QQ",[global])), ?line <<"abc@OkvNytabc@abcabc@a">> = iolist_to_binary(re:replace("abc@","(abc)\\100","&OkvNyt&\\1&a",[])), @@ -18343,24 +18344,24 @@ xb","(?!^)x","\\1tysI\\1v\\1BVwx\\1FOWG\\1&C",[multiline, ?line <<"cY">> = iolist_to_binary(re:replace("M","\\M","cY\\1",[global])), ?line <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(re:replace("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b","yWOTIFhIX\\1H",[])), ?line <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(re:replace("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b","yWOTIFhIX\\1H",[global])), -?line <<"NREGularsRREGularWEYrVRr">> = iolist_to_binary(re:replace("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)","N&\\1sR&WEYrVRr",[])), -?line <<"NREGularsRREGularWEYrVRr">> = iolist_to_binary(re:replace("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)","N&\\1sR&WEYrVRr",[global])), -?line <<"G">> = iolist_to_binary(re:replace("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)","G",[])), -?line <<"G">> = iolist_to_binary(re:replace("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)","G",[global])), -?line <<"PSsXtwlmy">> = iolist_to_binary(re:replace("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)","PSsXtwlmy",[])), -?line <<"PSsXtwlmy">> = iolist_to_binary(re:replace("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)","PSsXtwlmy",[global])), -?line <<"regul�rmiYTi">> = iolist_to_binary(re:replace("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)","&miYTi\\1\\1",[])), -?line <<"regul�rmiYTi">> = iolist_to_binary(re:replace("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)","&miYTi\\1\\1",[global])), -?line <<"W�����rxh�����yUoaLOIegmSA">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","W&rxh&yUoaL\\1OIegmS\\1A",[])), -?line <<"W�����rxh�����yUoaLOIegmSA">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","W&rxh&yUoaL\\1OIegmS\\1A",[global])), -?line <<"F�����gnWPyHeh�����tXTQ">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","F&gnWPyHe\\1h&tXTQ",[])), -?line <<"F�����gnWPyHeh�����tXTQ">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","F&gnWPyHe\\1h&tXTQ",[global])), -?line <<"sHerHnAhAdx">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","sHer\\1HnA\\1h\\1Adx",[])), -?line <<"sHerHnAhAdx">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","sHer\\1HnA\\1h\\1Adx",[global])), -?line <<"trobAQoU�����n">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","tr\\1obAQoU&n",[])), -?line <<"trobAQoU�����n">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","tr\\1obAQoU&n",[global])), -?line <<"�XAZSd">> = iolist_to_binary(re:replace("�XAZXB","(?<=Z)X.","Sd",[])), -?line <<"�XAZSd">> = iolist_to_binary(re:replace("�XAZXB","(?<=Z)X.","Sd",[global])), +?line <<"NREGularsRREGularWEYrVRr">> = iolist_to_binary(re:replace("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)","N&\\1sR&WEYrVRr",[])), +?line <<"NREGularsRREGularWEYrVRr">> = iolist_to_binary(re:replace("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)","N&\\1sR&WEYrVRr",[global])), +?line <<"G">> = iolist_to_binary(re:replace("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)","G",[])), +?line <<"G">> = iolist_to_binary(re:replace("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)","G",[global])), +?line <<"PSsXtwlmy">> = iolist_to_binary(re:replace("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)","PSsXtwlmy",[])), +?line <<"PSsXtwlmy">> = iolist_to_binary(re:replace("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)","PSsXtwlmy",[global])), +?line <<"regulärmiYTi">> = iolist_to_binary(re:replace("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)","&miYTi\\1\\1",[])), +?line <<"regulärmiYTi">> = iolist_to_binary(re:replace("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)","&miYTi\\1\\1",[global])), +?line <<"WÅæåäàrxhÅæåäàyUoaLOIegmSA">> = iolist_to_binary(re:replace("Åæåäà","Åæåä[à-ÿÀ-ß]+","W&rxh&yUoaL\\1OIegmS\\1A",[])), +?line <<"WÅæåäàrxhÅæåäàyUoaLOIegmSA">> = iolist_to_binary(re:replace("Åæåäà","Åæåä[à-ÿÀ-ß]+","W&rxh&yUoaL\\1OIegmS\\1A",[global])), +?line <<"FÅæåäÿgnWPyHehÅæåäÿtXTQ">> = iolist_to_binary(re:replace("Åæåäÿ","Åæåä[à-ÿÀ-ß]+","F&gnWPyHe\\1h&tXTQ",[])), +?line <<"FÅæåäÿgnWPyHehÅæåäÿtXTQ">> = iolist_to_binary(re:replace("Åæåäÿ","Åæåä[à-ÿÀ-ß]+","F&gnWPyHe\\1h&tXTQ",[global])), +?line <<"sHerHnAhAdx">> = iolist_to_binary(re:replace("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+","sHer\\1HnA\\1h\\1Adx",[])), +?line <<"sHerHnAhAdx">> = iolist_to_binary(re:replace("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+","sHer\\1HnA\\1h\\1Adx",[global])), +?line <<"trobAQoUÅæåäßn">> = iolist_to_binary(re:replace("Åæåäß","Åæåä[à-ÿÀ-ß]+","tr\\1obAQoU&n",[])), +?line <<"trobAQoUÅæåäßn">> = iolist_to_binary(re:replace("Åæåäß","Åæåä[à-ÿÀ-ß]+","tr\\1obAQoU&n",[global])), +?line <<"XAZSd">> = iolist_to_binary(re:replace("XAZXB","(?<=Z)X.","Sd",[])), +?line <<"XAZSd">> = iolist_to_binary(re:replace("XAZXB","(?<=Z)X.","Sd",[global])), ?line <<"A">> = iolist_to_binary(re:replace("ab cd defg","ab cd (?x) de fg","\\1A\\1",[])), ?line <<"A">> = iolist_to_binary(re:replace("ab cd defg","ab cd (?x) de fg","\\1A\\1",[global])), ?line <<"fab cddefgLdtKCtPab cddefgxvVUHDah">> = iolist_to_binary(re:replace("ab cddefg","ab cd(?x) de fg","f&LdtKC\\1\\1tP&xvVUHDah",[])), diff --git a/lib/stdlib/test/re_testoutput1_split_test.erl b/lib/stdlib/test/re_testoutput1_split_test.erl index e86a04b008..4fc85b95c0 100644 --- a/lib/stdlib/test/re_testoutput1_split_test.erl +++ b/lib/stdlib/test/re_testoutput1_split_test.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -524,14 +525,14 @@ run() -> ?line <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[{parts, 2}]))), ?line <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[]))), -?line <<"">> = iolist_to_binary(join(re:split("�","^\\�",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("�","^\\�",[{parts, +?line <<"">> = iolist_to_binary(join(re:split("","^\\",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("","^\\",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("�","^\\�",[]))), -?line <<"">> = iolist_to_binary(join(re:split("�","^�",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("�","^�",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("","^\\",[]))), +?line <<"">> = iolist_to_binary(join(re:split("ÿ","^ÿ",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("ÿ","^ÿ",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("�","^�",[]))), +?line <<":">> = iolist_to_binary(join(re:split("ÿ","^ÿ",[]))), ?line <<"">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[trim]))), ?line <<":">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[{parts, 2}]))), @@ -22879,14 +22880,14 @@ def","abc$",[]))), ?line <<":abc:">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[{parts, 2}]))), ?line <<":abc:">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[]))), -?line <<":abc">> = iolist_to_binary(join(re:split("abc�","(abc)\\223",[trim]))), -?line <<":abc:">> = iolist_to_binary(join(re:split("abc�","(abc)\\223",[{parts, +?line <<":abc">> = iolist_to_binary(join(re:split("abc","(abc)\\223",[trim]))), +?line <<":abc:">> = iolist_to_binary(join(re:split("abc","(abc)\\223",[{parts, 2}]))), -?line <<":abc:">> = iolist_to_binary(join(re:split("abc�","(abc)\\223",[]))), -?line <<":abc">> = iolist_to_binary(join(re:split("abc�","(abc)\\323",[trim]))), -?line <<":abc:">> = iolist_to_binary(join(re:split("abc�","(abc)\\323",[{parts, +?line <<":abc:">> = iolist_to_binary(join(re:split("abc","(abc)\\223",[]))), +?line <<":abc">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[trim]))), +?line <<":abc:">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[{parts, 2}]))), -?line <<":abc:">> = iolist_to_binary(join(re:split("abc�","(abc)\\323",[]))), +?line <<":abc:">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[]))), ?line <<":abc">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[trim]))), ?line <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[{parts, 2}]))), @@ -28929,42 +28930,42 @@ xb","(?!^)x",[multiline]))), ?line <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[{parts, 2}]))), ?line <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[]))), -?line <<"">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[{parts, +?line <<"">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[]))), -?line <<"">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[]))), +?line <<"">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[]))), -?line <<"">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[]))), +?line <<"">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[]))), -?line <<"">> = iolist_to_binary(join(re:split("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[]))), +?line <<"">> = iolist_to_binary(join(re:split("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[]))), -?line <<"">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[]))), +?line <<"">> = iolist_to_binary(join(re:split("Åæåäà","Åæåä[à-ÿÀ-ß]+",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("Åæåäà","Åæåä[à-ÿÀ-ß]+",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[]))), -?line <<"">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("Åæåäà","Åæåä[à-ÿÀ-ß]+",[]))), +?line <<"">> = iolist_to_binary(join(re:split("Åæåäÿ","Åæåä[à-ÿÀ-ß]+",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("Åæåäÿ","Åæåä[à-ÿÀ-ß]+",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[]))), -?line <<"">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("Åæåäÿ","Åæåä[à-ÿÀ-ß]+",[]))), +?line <<"">> = iolist_to_binary(join(re:split("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[]))), -?line <<"">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+",[]))), +?line <<"">> = iolist_to_binary(join(re:split("Åæåäß","Åæåä[à-ÿÀ-ß]+",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("Åæåäß","Åæåä[à-ÿÀ-ß]+",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[]))), -?line <<"�XAZ">> = iolist_to_binary(join(re:split("�XAZXB","(?<=Z)X.",[trim]))), -?line <<"�XAZ:">> = iolist_to_binary(join(re:split("�XAZXB","(?<=Z)X.",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("Åæåäß","Åæåä[à-ÿÀ-ß]+",[]))), +?line <<"XAZ">> = iolist_to_binary(join(re:split("XAZXB","(?<=Z)X.",[trim]))), +?line <<"XAZ:">> = iolist_to_binary(join(re:split("XAZXB","(?<=Z)X.",[{parts, 2}]))), -?line <<"�XAZ:">> = iolist_to_binary(join(re:split("�XAZXB","(?<=Z)X.",[]))), +?line <<"XAZ:">> = iolist_to_binary(join(re:split("XAZXB","(?<=Z)X.",[]))), ?line <<"">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[trim]))), ?line <<":">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[{parts, 2}]))), diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl index d49416c150..a32f846bd2 100644 --- a/lib/stdlib/test/shell_SUITE.erl +++ b/lib/stdlib/test/shell_SUITE.erl @@ -29,7 +29,7 @@ progex_bit_syntax/1, progex_records/1, progex_lc/1, progex_funs/1, otp_5990/1, otp_6166/1, otp_6554/1, otp_6785/1, - otp_7184/1, otp_7232/1, otp_8393/1]). + otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1]). -export([ start_restricted_from_shell/1, start_restricted_on_command_line/1,restricted_local/1]). @@ -93,7 +93,7 @@ groups() -> progex_funs]}, {tickets, [], [otp_5990, otp_6166, otp_6554, otp_6785, otp_7184, - otp_7232, otp_8393]}]. + otp_7232, otp_8393, otp_10302]}]. init_per_suite(Config) -> Config. @@ -108,7 +108,7 @@ end_per_group(_GroupName, Config) -> Config. --record(state, {bin, reply, leader}). +-record(state, {bin, reply, leader, unic = latin1}). start_restricted_from_shell(doc) -> @@ -374,15 +374,18 @@ records(Config) when is_list(Config) -> MS = ?MODULE_STRING, RR1 = "rr(" ++ MS ++ "). #state{}.", ?line "[state]\n" - "#state{bin = undefined,reply = undefined,leader = undefined}.\n" = + "#state{bin = undefined,reply = undefined,leader = undefined,\n" + " unic = latin1}.\n" = t(RR1), RR2 = "rr(" ++ MS ++ ",[state]). #state{}.", ?line "[state]\n" - "#state{bin = undefined,reply = undefined,leader = undefined}.\n" = + "#state{bin = undefined,reply = undefined,leader = undefined,\n" + " unic = latin1}.\n" = t(RR2), RR3 = "rr(" ++ MS ++ ",'_'). #state{}.", ?line "[state]\n" - "#state{bin = undefined,reply = undefined,leader = undefined}.\n" = + "#state{bin = undefined,reply = undefined,leader = undefined,\n" + " unic = latin1}.\n" = t(RR3), RR4 = "rr(" ++ MS ++ ", '_', {d,test1}).", ?line [[state]] = scan(RR4), @@ -2748,6 +2751,143 @@ prompt_err(B) -> S = string:strip(S2, both, $"), string:strip(S, right, $.). +otp_10302(doc) -> + "OTP-10302. Unicode."; +otp_10302(suite) -> []; +otp_10302(Config) when is_list(Config) -> + Test1 = + <<"begin + io:setopts([{encoding,utf8}]), + [1024] = \"\\x{400}\", + rd(rec, {a = \"\\x{400}\"}), + ok = rl(rec) + end.">>, + "-record(rec,{a = \"\x{400}\"}).\nok.\n" = t(Test1), + + Test3 = + <<"io:setopts([{encoding,utf8}]). + rd(rec, {a = \"\\x{400}\"}). + ok = rp(#rec{}).">>, + "ok.\nrec\n#rec{a = \"\x{400}\"}.\nok.\n" = t(Test3), + + Test4 = + <<"io:setopts([{encoding,utf8}]). + A = [1024] = \"\\x{400}\". + b(). + h().">>, + + "ok.\n\"\x{400}\"\nA = \"\x{400}\".\nok.\n" + "1: io:setopts([{encoding,utf8}])\n-> ok.\n" + "2: A = [1024] = \"\x{400}\"\n-> \"\x{400}\"\n" + "3: b()\n-> ok.\nok.\n" = t(Test4), + + Test5 = + <<"begin + io:setopts([{encoding,utf8}]), + results(0), + A = [1024] = \"\\x{400}\", + b(), + h() + end.">>, + "A = \"\x{400}\".\nok.\n" = t(Test5), + + %% One $" is "lost": + true = + "\x{400}\": command not found" =:= + prompt_err({<<"io:setopts([{encoding,utf8}]). v(\"\x{400}\")."/utf8>>, + unicode}), + + "ok.\ndefault\n* Bad prompt function: \"\x{400}\".\n" = + t({<<"io:setopts([{encoding,utf8}]). " + "shell:prompt_func(\"\x{400}\")."/utf8>>, + unicode}), + _ = shell:prompt_func(default), + + %% Test lib:format_exception() (cf. OTP-6554) + Test6 = + <<"begin + A = <<\"\\xaa\">>, + S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])), + {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), + {ok, Es} = erl_parse:parse_exprs(Ts), + B = erl_eval:new_bindings(), + erl_eval:exprs(Es, B) + end.">>, + + "** exception error: an error occurred when evaluating" + " an arithmetic expression\n in operator '/'/2\n" + " called as <<\"\xaa\">> / <<\"\xaa\">>.\n" = t(Test6), + Test7 = + <<"io:setopts([{encoding,utf8}]). + A = <<\"\\xaa\">>, + S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])), + {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), + {ok, Es} = erl_parse:parse_exprs(Ts), + B = erl_eval:new_bindings(), + erl_eval:exprs(Es, B).">>, + + "ok.\n** exception error: an error occurred when evaluating" + " an arithmetic expression\n in operator '/'/2\n" + " called as <<170>> / <<170>>.\n" = t(Test7), + Test8 = + <<"begin + A = [1089], + S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])), + {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), + {ok, Es} = erl_parse:parse_exprs(Ts), + B = erl_eval:new_bindings(), + erl_eval:exprs(Es, B) + end.">>, + "** exception error: an error occurred when evaluating" + " an arithmetic expression\n in operator '/'/2\n" + " called as [1089] / [1089].\n" = t(Test8), + Test9 = + <<"io:setopts([{encoding,utf8}]). + A = [1089], + S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])), + {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), + {ok, Es} = erl_parse:parse_exprs(Ts), + B = erl_eval:new_bindings(), + erl_eval:exprs(Es, B).">>, + + "ok.\n** exception error: an error occurred when evaluating" + " an arithmetic expression\n in operator '/'/2\n" + " called as \"\x{441}\" / \"\x{441}\".\n" = t(Test9), + Test10 = + <<"A = {\"1\\xaa\", + $\\xaa, + << <<\"hi\">>/binary >>, + <<\"1\xaa\">>}, + fun(a) -> true end(A).">>, + "** exception error: no function clause matching \n" + " erl_eval:'-inside-an-interpreted-fun-'" + "({\"1\xc2\xaa\",170,<<\"hi\">>,\n " + " <<\"1\xc2\xaa\">>}) .\n" = t(Test10), + Test11 = + <<"io:setopts([{encoding,utf8}]). + A = {\"1\\xaa\", + $\\xaa, + << <<\"hi\">>/binary >>, + <<\"1\xaa\">>}, + fun(a) -> true end(A).">>, + + "ok.\n** exception error: no function clause matching \n" + " erl_eval:'-inside-an-interpreted-fun-'" + "({\"1\xaa\",170,<<\"hi\">>,\n " + " <<\"1\xaa\"/utf8>>}) .\n" = t(Test11), + Test12 = <<"fun(a, b) -> false end(65, [1089]).">>, + "** exception error: no function clause matching \n" + " erl_eval:'-inside-an-interpreted-fun-'(65,[1089])" + " .\n" = t(Test12), + Test13 = + <<"io:setopts([{encoding,utf8}]). + fun(a, b) -> false end(65, [1089]).">>, + "ok.\n** exception error: no function clause matching \n" + " erl_eval:'-inside-an-interpreted-fun-'(65,\"\x{441}\")" + " .\n" = t(Test13), + + ok. + scan(B) -> F = fun(Ts) -> case erl_parse:parse_term(Ts) of @@ -2761,7 +2901,7 @@ scan(B) -> scan(t(B), F). scan(S0, F) -> - case erl_scan:tokens([], S0, 1) of + case erl_scan:tokens([], S0, 1, [unicode]) of {done,{ok,Ts,_},S} -> [F(Ts) | scan(S, F)]; _Else -> @@ -2769,29 +2909,36 @@ scan(S0, F) -> end. t({Node,Bin}) when is_atom(Node),is_binary(Bin) -> - t0(Bin, fun() -> start_new_shell(Node) end); + t0({Bin,latin1}, fun() -> start_new_shell(Node) end); t(Bin) when is_binary(Bin) -> - t0(Bin, fun() -> start_new_shell() end); + t0({Bin,latin1}, fun() -> start_new_shell() end); +t({Bin,Enc}) when is_binary(Bin), is_atom(Enc) -> + t0({Bin,Enc}, fun() -> start_new_shell() end); t(L) -> t(list_to_binary(L)). -t0(Bin, F) -> +t0({Bin,Enc}, F) -> %% Spawn a process so that io_request messages do not interfer. P = self(), - C = spawn(fun() -> t1(P, Bin, F) end), + C = spawn(fun() -> t1(P, {Bin, Enc}, F) end), receive {C, R} -> R end. -t1(Parent, Bin, F) -> - %% io:format("*** Testing ~s~n", [binary_to_list(Bin)]), - S = #state{bin = Bin, reply = [], leader = group_leader()}, +t1(Parent, {Bin,Enc}, F) -> + io:format("*** Testing ~s~n", [binary_to_list(Bin)]), + S = #state{bin = Bin, unic = Enc, reply = [], leader = group_leader()}, group_leader(self(), self()), _Shell = F(), try server_loop(S) catch exit:R -> Parent ! {self(), R}; - throw:{?MODULE,LoopReply} -> + throw:{?MODULE,LoopReply,latin1} -> L0 = binary_to_list(list_to_binary(LoopReply)), [$\n | L1] = lists:dropwhile(fun(X) -> X =/= $\n end, L0), + Parent ! {self(), dotify(L1)}; + throw:{?MODULE,LoopReply,_Uni} -> + Tmp = unicode:characters_to_binary(LoopReply), + L0 = unicode:characters_to_list(Tmp), + [$\n | L1] = lists:dropwhile(fun(X) -> X =/= $\n end, L0), Parent ! {self(), dotify(L1)} after group_leader(S#state.leader, self()) end. @@ -2835,7 +2982,7 @@ do_io_request(Req, From, S, ReplyAs) -> case io_requests([Req], [], S) of {_Status,{eof,_},S1} -> io_reply(From, ReplyAs, {error,terminated}), - throw({?MODULE,S1#state.reply}); + throw({?MODULE,S1#state.reply,S1#state.unic}); {_Status,Reply,S1} -> io_reply(From, ReplyAs, Reply), S1 @@ -2858,13 +3005,34 @@ io_requests([], [Rs|Cont], S) -> io_requests([], [], S) -> {ok,ok,S}. +io_request({setopts, Opts}, S) -> + #state{unic = OldEnc, bin = Bin} = S, + NewEnc = case proplists:get_value(encoding, Opts) of + undefined -> OldEnc; + utf8 -> unicode; + New -> New + end, + NewBin = case {OldEnc, NewEnc} of + {E, E} -> Bin; + {latin1, _} -> + unicode:characters_to_binary(Bin, latin1, unicode); + {_, latin1} -> + unicode:characters_to_binary(Bin, unicode, latin1); + {_, _} -> Bin + end, + {ok, ok, S#state{unic = NewEnc, bin = NewBin}}; +io_request(getopts, S) -> + {ok,[{encoding,S#state.unic}],S}; io_request({get_geometry,columns}, S) -> {ok,80,S}; io_request({get_geometry,rows}, S) -> {ok,24,S}; io_request({put_chars,Chars}, S) -> {ok,ok,S#state{reply = [S#state.reply | Chars]}}; -io_request({put_chars,_,Chars}, S) -> +io_request({put_chars,latin1,Chars}, S) -> + {ok,ok,S#state{reply = [S#state.reply | Chars]}}; +io_request({put_chars,unicode,Chars0}, S) -> + Chars = unicode:characters_to_list(Chars0), {ok,ok,S#state{reply = [S#state.reply | Chars]}}; io_request({put_chars,Mod,Func,Args}, S) -> case catch apply(Mod, Func, Args) of @@ -2890,9 +3058,12 @@ get_until_loop(M, F, As, S, {more,Cont}, Enc) -> 0 -> get_until_loop(M, F, As, S, catch apply(M, F, [Cont,eof|As]), Enc); + _ when S#state.unic =:= latin1 -> + get_until_loop(M, F, As, S#state{bin = <<>>}, + catch apply(M, F, [Cont,binary_to_list(Bin)|As]), Enc); _ -> get_until_loop(M, F, As, S#state{bin = <<>>}, - catch apply(M, F, [Cont,binary_to_list(Bin)|As]), Enc) + catch apply(M, F, [Cont,unicode:characters_to_list(Bin)|As]), Enc) end; get_until_loop(_M, _F, _As, S, {done,Res,Buf}, Enc) -> {ok,Res,S#state{bin = buf2bin(Buf, Enc)}}; @@ -2903,6 +3074,8 @@ buf2bin(eof,_) -> <<>>; buf2bin(Buf,latin1) -> list_to_binary(Buf); +buf2bin(Buf,utf8) -> + unicode:characters_to_binary(Buf,unicode,unicode); buf2bin(Buf,unicode) -> unicode:characters_to_binary(Buf,unicode,unicode). diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl index 6969c095a0..96e653985f 100644 --- a/lib/stdlib/test/string_SUITE.erl +++ b/lib/stdlib/test/string_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -473,8 +474,8 @@ to_upper_to_lower(suite) -> to_upper_to_lower(doc) -> []; to_upper_to_lower(Config) when is_list(Config) -> - ?line "1234ABCDEF���=" = string:to_upper("1234abcdef���="), - ?line "����������abc()" = string:to_lower("����������abc()"), + ?line "1234ABCDEFÅÄÖ=" = string:to_upper("1234abcdefåäö="), + ?line "éèíúùòóåäöabc()" = string:to_lower("ÉÈÍÚÙÒÓÅÄÖabc()"), ?line All = lists:seq(0, 255), ?line UC = string:to_upper(All), diff --git a/lib/syntax_tools/src/epp_dodger.erl b/lib/syntax_tools/src/epp_dodger.erl index b3ced34c14..70395848a1 100644 --- a/lib/syntax_tools/src/epp_dodger.erl +++ b/lib/syntax_tools/src/epp_dodger.erl @@ -186,6 +186,7 @@ quick_parse_file(File, Options) -> parse_file(File, Parser, Options) -> case file:open(File, [read]) of {ok, Dev} -> + _ = epp:set_encoding(Dev), try Parser(Dev, 1, Options) after ok = file:close(Dev) end; @@ -400,7 +401,7 @@ quick_parse_form(Dev, L0, Options) -> parse_form(Dev, L0, Parser, Options) -> NoFail = proplists:get_bool(no_fail, Options), Opt = #opt{clever = proplists:get_bool(clever, Options)}, - case io:scan_erl_form(Dev, "", L0) of + case io:scan_erl_form(Dev, "", L0, [unicode]) of {ok, Ts, L1} -> case catch {ok, Parser(Ts, Opt)} of {'EXIT', Term} -> @@ -419,6 +420,7 @@ parse_form(Dev, L0, Parser, Options) -> {ok, F, L1} end; {error, _IoErr, _L1} = Err -> Err; + {error, _Reason} -> {eof, L0}; % This is probably encoding problem {eof, _L1} = Eof -> Eof end. diff --git a/lib/syntax_tools/src/erl_comment_scan.erl b/lib/syntax_tools/src/erl_comment_scan.erl index b833e1c069..a70e7ba413 100644 --- a/lib/syntax_tools/src/erl_comment_scan.erl +++ b/lib/syntax_tools/src/erl_comment_scan.erl @@ -72,7 +72,17 @@ file(Name) -> {ok, V} -> case V of {ok, B} -> - string(binary_to_list(B)); + Enc = case epp:read_encoding(Name) of + none -> epp:default_encoding(); + Enc0 -> Enc0 + end, + case catch unicode:characters_to_list(B, Enc) of + String when is_list(String) -> + string(String); + R -> + error_read_file(Name1), + exit(R) + end; {error, E} -> error_read_file(Name1), exit({read, E}) diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl index f4bbf975c3..1a55a5e71c 100644 --- a/lib/syntax_tools/src/erl_prettypr.erl +++ b/lib/syntax_tools/src/erl_prettypr.erl @@ -60,7 +60,9 @@ hook = ?NOHOOK :: hook(), paper = ?PAPER :: integer(), ribbon = ?RIBBON :: integer(), - user = ?NOUSER :: term()}). + user = ?NOUSER :: term(), + encoding = epp:default_encoding() :: epp:source_encoding()}). + -type context() :: #ctxt{}. %% ===================================================================== @@ -231,6 +233,8 @@ format(Node) -> %% <dt>{user, term()}</dt> %% <dd>User-specific data for use in hook functions. The default %% value is `undefined'.</dd> +%% <dt>{encoding, epp:source_encoding()}</dt> +%% <dd>Specifies the encoding of the generated file.</dd> %% </dl> %% %% A hook function (cf. the {@link hook()} type) is passed the current @@ -342,7 +346,9 @@ layout(Node, Options) -> #ctxt{hook = proplists:get_value(hook, Options, ?NOHOOK), paper = proplists:get_value(paper, Options, ?PAPER), ribbon = proplists:get_value(ribbon, Options, ?RIBBON), - user = proplists:get_value(user, Options)}). + user = proplists:get_value(user, Options), + encoding = proplists:get_value(encoding, Options, + epp:default_encoding())}). lay(Node, Ctxt) -> case erl_syntax:get_ann(Node) of @@ -445,10 +451,10 @@ lay_2(Node, Ctxt) -> text(tidy_float(erl_syntax:float_literal(Node))); char -> - text(erl_syntax:char_literal(Node)); + text(erl_syntax:char_literal(Node, Ctxt#ctxt.encoding)); string -> - lay_string(erl_syntax:string_literal(Node), Ctxt); + lay_string(erl_syntax:string_literal(Node, Ctxt#ctxt.encoding), Ctxt); nil -> text("[]"); @@ -639,10 +645,6 @@ lay_2(Node, Ctxt) -> set_prec(Ctxt, PrecR)), beside(D1, beside(text(":"), D2)); - qualified_name -> - Ss = erl_syntax:qualified_name_segments(Node), - lay_qualified_name(Ss, Ctxt); - %% %% The rest is in alphabetical order %% @@ -966,26 +968,6 @@ maybe_parentheses(D, Prec, Ctxt) -> D end. -lay_qualified_name([S | Ss1] = Ss, Ctxt) -> - case erl_syntax:type(S) of - atom -> - case erl_syntax:atom_value(S) of - '' -> - beside(text("."), - lay_qualified_name_1(Ss1, Ctxt)); - _ -> - lay_qualified_name_1(Ss, Ctxt) - end; - _ -> - lay_qualified_name_1(Ss, Ctxt) - end. - -lay_qualified_name_1([S], Ctxt) -> - lay(S, Ctxt); -lay_qualified_name_1([S | Ss], Ctxt) -> - beside(lay(S, Ctxt), beside(text("."), - lay_qualified_name_1(Ss, Ctxt))). - lay_string(S, Ctxt) -> %% S includes leading/trailing double-quote characters. The segment %% width is 2/3 of the ribbon width - this seems to work well. diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index 151f04b03b..f7420030c3 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -161,6 +161,7 @@ is_char/2, char_value/1, char_literal/1, + char_literal/2, clause/2, clause/3, clause_body/1, @@ -234,8 +235,6 @@ prefix_expr/2, prefix_expr_argument/1, prefix_expr_operator/1, - qualified_name/1, - qualified_name_segments/1, query_expr/1, query_expr_body/1, receive_expr/1, @@ -271,6 +270,7 @@ is_string/2, string_value/1, string_literal/1, + string_literal/2, text/1, text_string/1, try_expr/2, @@ -449,7 +449,6 @@ %% <td>parentheses</td> %% <td>prefix_expr</td> %% </tr><tr> -%% <td>qualified_name</td> %% <td>query_expr</td> %% <td>receive_expr</td> %% <td>record_access</td> @@ -514,7 +513,6 @@ %% @see operator/1 %% @see parentheses/1 %% @see prefix_expr/2 -%% @see qualified_name/1 %% @see query_expr/1 %% @see receive_expr/3 %% @see record_access/3 @@ -584,11 +582,7 @@ type(Node) -> {record, _, _, _, _} -> record_expr; {record, _, _, _} -> record_expr; {record_field, _, _, _, _} -> record_access; - {record_field, _, _, _} -> - case is_qualified_name(Node) of - true -> qualified_name; - false -> record_access - end; + {record_field, _, _, _} -> record_access; {record_index, _, _, _} -> record_index_expr; {remote, _, _, _} -> module_qualifier; {rule, _, _, _, _} -> rule; @@ -1628,6 +1622,7 @@ float_literal(Node) -> %% %% @see char_value/1 %% @see char_literal/1 +%% @see char_literal/2 %% @see is_char/2 %% type(Node) = char @@ -1687,13 +1682,34 @@ char_value(Node) -> %% ===================================================================== %% @doc Returns the literal string represented by a `char' %% node. This includes the leading "`$'" character. +%% Characters beyond 255 will be escaped. %% %% @see char/1 -spec char_literal(syntaxTree()) -> nonempty_string(). char_literal(Node) -> - io_lib:write_char(char_value(Node)). + char_literal(Node, latin1). + + +%% ===================================================================== +%% @doc Returns the literal string represented by a `char' +%% node. This includes the leading "`$'" character. +%% Depending on the encoding a character beyond 255 will be escaped +%% ('latin1') or copied as is ('utf8'). +%% +%% @see char/1 + +-type encoding() :: 'utf8' | 'unicode' | 'latin1'. + +-spec char_literal(syntaxTree(), encoding()) -> nonempty_string(). + +char_literal(Node, unicode) -> + io_lib:write_unicode_char(char_value(Node)); +char_literal(Node, utf8) -> + io_lib:write_unicode_char(char_value(Node)); +char_literal(Node, latin1) -> + io_lib:write_unicode_char_as_latin1(char_value(Node)). %% ===================================================================== @@ -1708,6 +1724,7 @@ char_literal(Node) -> %% %% @see string_value/1 %% @see string_literal/1 +%% @see string_literal/2 %% @see is_string/2 %% @see char/1 @@ -1768,13 +1785,32 @@ string_value(Node) -> %% ===================================================================== %% @doc Returns the literal string represented by a `string' %% node. This includes surrounding double-quote characters. +%% Characters beyond 255 will be escaped. %% %% @see string/1 -spec string_literal(syntaxTree()) -> nonempty_string(). string_literal(Node) -> - io_lib:write_string(string_value(Node)). + string_literal(Node, latin1). + + +%% ===================================================================== +%% @doc Returns the literal string represented by a `string' +%% node. This includes surrounding double-quote characters. +%% Depending on the encoding characters beyond 255 will be escaped +%% ('latin1') or copied as is ('utf8'). +%% +%% @see string/1 + +-spec string_literal(syntaxTree(), encoding()) -> nonempty_string(). + +string_literal(Node, utf8) -> + io_lib:write_unicode_string(string_value(Node)); +string_literal(Node, unicode) -> + io_lib:write_unicode_string(string_value(Node)); +string_literal(Node, latin1) -> + io_lib:write_unicode_string_as_latin1(string_value(Node)). %% ===================================================================== @@ -3003,9 +3039,6 @@ revert_module_name(A) -> case type(A) of atom -> {ok, concrete(A)}; - qualified_name -> - Ss = qualified_name_segments(A), - {ok, [concrete(S) || S <- Ss]}; _ -> error end. @@ -3051,11 +3084,7 @@ attribute_arguments(Node) -> M0 -> {M0, none} end, - M2 = if is_list(M1) -> - qualified_name([atom(A) || A <- M1]); - true -> - atom(M1) - end, + M2 = atom(M1), M = set_pos(M2, Pos), if Vs == none -> [M]; true -> [M, set_pos(list(Vs), Pos)] @@ -3065,20 +3094,11 @@ attribute_arguments(Node) -> list(unfold_function_names(Data, Pos)), Pos)]; import -> - case Data of - {Module, Imports} -> - [if is_list(Module) -> - qualified_name([atom(A) - || A <- Module]); - true -> - set_pos(atom(Module), Pos) - end, - set_pos( - list(unfold_function_names(Imports, Pos)), - Pos)]; - _ -> - [qualified_name([atom(A) || A <- Data])] - end; + {Module, Imports} = Data, + [set_pos(atom(Module), Pos), + set_pos( + list(unfold_function_names(Imports, Pos)), + Pos)]; file -> {File, Line} = Data, [set_pos(string(File), Pos), @@ -3210,53 +3230,6 @@ module_qualifier_body(Node) -> %% ===================================================================== -%% @doc Creates an abstract qualified name. The result represents -%% "<code><em>S1</em>.<em>S2</em>. ... .<em>Sn</em></code>", if -%% `Segments' is `[S1, S2, ..., Sn]'. -%% -%% @see qualified_name_segments/1 - -%% type(Node) = qualified_name -%% data(Node) = [syntaxTree()] -%% -%% `erl_parse' representation: -%% -%% {record_field, Pos, Node, Node} -%% -%% Node = {atom, Pos, Value} | {record_field, Pos, Node, Node} -%% -%% Note that if not all leaf subnodes are (abstract) atoms, then Node -%% represents a Mnemosyne query record field access ('record_access'); -%% see type/1 for details. - --spec qualified_name([syntaxTree()]) -> syntaxTree(). - -qualified_name(Segments) -> - tree(qualified_name, Segments). - -revert_qualified_name(Node) -> - Pos = get_pos(Node), - fold_qualified_name(qualified_name_segments(Node), Pos). - - -%% ===================================================================== -%% @doc Returns the list of name segments of a -%% `qualified_name' node. -%% -%% @see qualified_name/1 - --spec qualified_name_segments(syntaxTree()) -> [syntaxTree()]. - -qualified_name_segments(Node) -> - case unwrap(Node) of - {record_field, _, _, _} = Node1 -> - unfold_qualified_name(Node1); - Node1 -> - data(Node1) - end. - - -%% ===================================================================== %% @doc Creates an abstract function definition. If `Clauses' %% is `[C1, ..., Cn]', the result represents %% "<code><em>Name</em> <em>C1</em>; ...; <em>Name</em> @@ -6068,8 +6041,6 @@ revert_root(Node) -> revert_parentheses(Node); prefix_expr -> revert_prefix_expr(Node); - qualified_name -> - revert_qualified_name(Node); query_expr -> revert_query_expr(Node); receive_expr -> @@ -6312,8 +6283,6 @@ subtrees(T) -> prefix_expr -> [[prefix_expr_operator(T)], [prefix_expr_argument(T)]]; - qualified_name -> - [qualified_name_segments(T)]; query_expr -> [[query_expr_body(T)]]; receive_expr -> @@ -6444,7 +6413,6 @@ make_tree(match_expr, [[P], [E]]) -> match_expr(P, E); make_tree(module_qualifier, [[M], [N]]) -> module_qualifier(M, N); make_tree(parentheses, [[E]]) -> parentheses(E); make_tree(prefix_expr, [[F], [A]]) -> prefix_expr(F, A); -make_tree(qualified_name, [S]) -> qualified_name(S); make_tree(query_expr, [[B]]) -> query_expr(B); make_tree(receive_expr, [C]) -> receive_expr(C); make_tree(receive_expr, [C, [E], A]) -> receive_expr(C, E, A); @@ -6788,32 +6756,6 @@ fold_variable_names(Vs) -> unfold_variable_names(Vs, Pos) -> [set_pos(variable(V), Pos) || V <- Vs]. -%% Support functions for qualified names ("foo.bar.baz", -%% "erl.lang.lists", etc.). The representation overlaps with the weird -%% "Mnesia query record access" operators. The '.' operator is left -%% associative, so folding should nest on the left. - -is_qualified_name({record_field, _, L, R}) -> - is_qualified_name(L) andalso is_qualified_name(R); -is_qualified_name({atom, _, _}) -> true; -is_qualified_name(_) -> false. - -unfold_qualified_name(Node) -> - lists:reverse(unfold_qualified_name(Node, [])). - -unfold_qualified_name({record_field, _, L, R}, Ss) -> - unfold_qualified_name(R, unfold_qualified_name(L, Ss)); -unfold_qualified_name(S, Ss) -> [S | Ss]. - -fold_qualified_name([S | Ss], Pos) -> - fold_qualified_name(Ss, Pos, {atom, Pos, atom_value(S)}). - -fold_qualified_name([S | Ss], Pos, Ack) -> - fold_qualified_name(Ss, Pos, {record_field, Pos, Ack, - {atom, Pos, atom_value(S)}}); -fold_qualified_name([], _Pos, Ack) -> - Ack. - %% Support functions for transforming lists of record field definitions. %% %% There is no unique representation for field definitions in the diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl index 36cd35f15d..2c94ac776d 100644 --- a/lib/syntax_tools/src/erl_syntax_lib.erl +++ b/lib/syntax_tools/src/erl_syntax_lib.erl @@ -2223,11 +2223,6 @@ module_name_to_atom(M) -> case erl_syntax:type(M) of atom -> erl_syntax:atom_value(M); - qualified_name -> - list_to_atom(packages:concat( - [erl_syntax:atom_value(A) - || A <- erl_syntax:qualified_name_segments(M)]) - ); _ -> throw(syntax_error) end. diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl index 59cf6c0a92..e9a88caff3 100644 --- a/lib/syntax_tools/src/erl_tidy.erl +++ b/lib/syntax_tools/src/erl_tidy.erl @@ -375,6 +375,8 @@ write_module(Tree, Name, Opts) -> end, filename(filename:join(Dir, Name1)) end, + Encoding = [{encoding,Enc} || Enc <- [epp:read_encoding(Name)], + Enc =/= none], case proplists:get_bool(backups, Opts) of true -> backup_file(File, Opts); @@ -382,9 +384,9 @@ write_module(Tree, Name, Opts) -> ok end, Printer = proplists:get_value(printer, Opts), - FD = open_output_file(File), + FD = open_output_file(File, Encoding), verbose("writing to file `~s'.", [File], Opts), - V = (catch {ok, output(FD, Printer, Tree, Opts)}), + V = (catch {ok, output(FD, Printer, Tree, Opts++Encoding)}), ok = file:close(FD), case V of {ok, _} -> @@ -432,8 +434,9 @@ file_type(Name, Links) -> throw(R) end. -open_output_file(FName) -> - case catch file:open(FName, [write]) of +open_output_file(FName, Options) -> +io:format("Options ~p~n", [Options]), + case catch file:open(FName, [write]++Options) of {ok, FD} -> FD; {error, R} -> diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl index 37e561cbbe..8abc3f41cb 100644 --- a/lib/syntax_tools/src/igor.erl +++ b/lib/syntax_tools/src/igor.erl @@ -341,10 +341,12 @@ merge(Name, Files) -> merge(Name, Files, Opts) -> Opts1 = Opts ++ ?DEFAULT_MERGE_OPTS, - {Tree, Stubs} = merge_files(Name, Files, Opts1), + {Sources, Enc} = merge_files1(Files, Opts1), + {Tree, Stubs} = merge_sources(Name, Sources, Opts1), Dir = proplists:get_value(dir, Opts1, ""), Filename = proplists:get_value(outfile, Opts1, Name), - File = write_module(Tree, Filename, Dir, Opts1), + Encoding = [{encoding, Enc} || Enc =/= none], + File = write_module(Tree, Filename, Dir, Encoding ++ Opts1), [File | maybe_create_stubs(Stubs, Opts1)]. @@ -459,16 +461,21 @@ merge_files(Name, Files, Options) -> -spec merge_files(atom(), erl_syntax:forms(), [file:filename()], [option()]) -> {erl_syntax:syntaxTree(), [stubDescriptor()]}. -merge_files(_, _Trees, [], _) -> +merge_files(Name, Trees, Files, Opts) -> + {Sources, _Encoding} = merge_files1(Files, Opts), + merge_sources(Name, Trees ++ Sources, Opts). + +merge_files1([], _) -> report_error("no files to merge."), exit(badarg); -merge_files(Name, Trees, Files, Opts) -> +merge_files1(Files, Opts) -> Opts1 = Opts ++ [{includes, ?DEFAULT_INCLUDES}, {macros, ?DEFAULT_MACROS}, {preprocess, false}, comments], - Sources = [read_module(F, Opts1) || F <- Files], - merge_sources(Name, Trees ++ Sources, Opts1). + SourceEncodings = [read_module(F, Opts1) || F <- Files], + {Sources, [Encoding | _]} = lists:unzip(SourceEncodings), + {Sources, Encoding}. %% ===================================================================== @@ -2512,7 +2519,11 @@ rename(Files, Renamings, Opts) -> lists:flatmap(fun (F) -> rename_file(F, Dict, Opts1) end, Files). rename_file(File, Dict, Opts) -> - S = read_module(File, Opts), + {S, Enc} = read_module(File, Opts), + %% Try to avoid *two* coding: comments: + Encoding = [{encoding, Enc} || + Enc =/= none, + not proplists:get_bool(comments, Opts)], M = get_module_info(S), Name = M#module.name, Name1 = case dict:find(Name, Dict) of @@ -2526,10 +2537,10 @@ rename_file(File, Dict, Opts) -> Opts1 = [no_headers, {export, [Name]}, {static, [Name]}, - {redirect, dict:to_list(Dict1)}] ++ Opts, + {redirect, dict:to_list(Dict1)}] ++ Encoding ++ Opts, {Tree, Stubs} = merge_sources(Name1, [S], Opts1), Dir = filename:dirname(filename(File)), - File1 = write_module(Tree, Name1, Dir, Opts), + File1 = write_module(Tree, Name1, Dir, Opts++Encoding), %% We create the stub file in the same directory as the source file %% and the target file. @@ -2648,7 +2659,7 @@ error_text(D, Name) -> {L, M, E} when is_integer(L), is_atom(M) -> case catch M:format_error(E) of S when is_list(S) -> - io_lib:fwrite("`~w', line ~w: ~s.", + io_lib:fwrite("`~w', line ~w: ~ts.", [Name, L, S]); _ -> error_text_1(D, Name) @@ -2706,7 +2717,17 @@ open_output_file(FName) -> exit(R) end. -%% read_module(Name, Options) -> syntaxTree() +output_encoding(FD, Opts) -> + case proplists:get_value(encoding, Opts) of + undefined -> + ok = io:setopts(FD, [{encoding, epp:default_encoding()}]); + Encoding -> + ok = io:setopts(FD, [{encoding, Encoding}]), + EncS = epp:encoding_to_string(Encoding), + ok = io:fwrite(FD, <<"%% ~s\n">>, [EncS]) + end. + +%% read_module(Name, Options) -> {syntaxTree(), epp:source_encoding()} %% %% This also tries to locate the real source file, if "Name" does not %% point directly to a particular file. @@ -2729,20 +2750,21 @@ read_module(Name, Options) -> read_module_1(Name, Options) -> verbose("reading module `~s'.", [filename(Name)], Options), - Forms = read_module_2(Name, Options), + {Forms, Enc} = read_module_2(Name, Options), case proplists:get_bool(comments, Options) of false -> - Forms; + {Forms, Enc}; true -> Comments = erl_comment_scan:file(Name), - erl_recomment:recomment_forms(Forms, Comments) + {erl_recomment:recomment_forms(Forms, Comments), Enc} end. read_module_2(Name, Options) -> case read_module_3(Name, Options) of {ok, Forms} -> check_forms(Forms, Name), - Forms; + Enc = epp:read_encoding(Name), + {Forms, Enc}; {error, _} = Error -> error_read_file(Name), exit(Error) @@ -2772,7 +2794,7 @@ check_forms([F | Fs], File) -> _ -> "unknown error" end, - report_error("in file `~s' at line ~w:\n ~s", + report_error("in file `~s' at line ~w:\n ~ts", [filename(File), erl_syntax:get_pos(F), S]), exit(error); _ -> @@ -2847,6 +2869,7 @@ write_module(Tree, Name, Dir, Opts) -> end, Printer = proplists:get_value(printer, Opts), FD = open_output_file(File), + ok = output_encoding(FD, Opts), verbose("writing to file `~s'.", [File], Opts), V = (catch {ok, output(FD, Printer, Tree, Opts)}), ok = file:close(FD), diff --git a/lib/test_server/doc/src/test_server_ctrl.xml b/lib/test_server/doc/src/test_server_ctrl.xml index 41bc0bcc75..af96f1fe7e 100644 --- a/lib/test_server/doc/src/test_server_ctrl.xml +++ b/lib/test_server/doc/src/test_server_ctrl.xml @@ -427,11 +427,21 @@ Optional, if not given the test server controller node <p>A <c>CoverFile</c> can have the following entries:</p> <code type="none"> {exclude, all | ExcludeModuleList}. -{include, IncludeModuleList}. </code> +{include, IncludeModuleList}. +{cross, CrossCoverInfo}.</code> <p>Note that each line must end with a full stop. <c>ExcludeModuleList</c> and <c>IncludeModuleList</c> are lists of atoms, where each atom is a module name. </p> + + <p><c>CrossCoverInfo</c> is used when collecting cover data + over multiple tests. Modules listed here are compiled, but + they will not be analysed when the test is finished. See + <seealso + marker="#cross_cover_analyse-2">cross_cover_analyse/2</seealso> + for more information about the cross cover mechanism and the + format of <c>CrossCoverInfo</c>. + </p> <p>If both an <c>Application</c> and a <c>CoverFile</c> is given, all modules in the application are cover compiled, except for the modules listed in <c>ExcludeModuleList</c>. The @@ -467,30 +477,71 @@ Optional, if not given the test server controller node </desc> </func> <func> - <name>cross_cover_analyse(Level) -> ok</name> - <fsummary>Analyse cover data collected from all tests</fsummary> + <name>cross_cover_analyse(Level, Tests) -> ok</name> + <fsummary>Analyse cover data collected from multiple tests</fsummary> <type> <v>Level = details | overview</v> + <v>Tests = [{Tag,LogDir}]</v> + <v>Tag = atom()</v> + <d>Test identifier.</d> + <v>LogDir = string()</v> + <d>Log directory for the test identified by <c>Tag</c>. This + can either be the <c>run.<timestamp></c> directory or + the parent directory of this (in which case the latest + <c>run.<timestamp></c> directory is chosen.</d> </type> <desc> - <p>Analyse cover data collected from all tests. The modules - analysed are the ones listed in the cross cover file - <c>cross.cover</c> in the current directory of the test - server.</p> - <p>The modules listed in the <c>cross.cover</c> file are - modules that are heavily used by other applications than the - one they belong to. This function should be run after all - tests are completed, and the result will be stored in a file - called cross_cover.html in the run.<timestamp> - directory of the application the modules belong to. - </p> - <p>The <c>cross.cover</c> file contains elements like this:</p> - <pre> -{App,Modules}. </pre> - <p>where <c>App</c> can be an application name or the atom - <c>all</c>. The application (or all applications) will cover - compile the listed <c>Modules</c>. - </p> + <p>Analyse cover data collected from multiple tests. The modules + analysed are the ones listed in <c>cross</c> statements in + the cover files. These are modules that are heavily used by + other tests than the one where they belong or are explicitly + tested. They should then be listed as cross modules in the + cover file for the test where they are used but do not + belong. Se example below.</p> + <p>This function should be run after all tests are completed, + and the result will be stored in a file called + <c>cross_cover.html</c> in the <c>run.<timestamp></c> + directory of the test the modules belong to.</p> + <p>Note that the function can be executed on any node, and it + does not require <c>test_server_ctrl</c> to be started first.</p> + <p>The <c>cross</c> statement in the cover file must be like this:</p> + <code type="none"> +{cross,[{Tag,Modules}]}.</code> + <p>where <c>Tag</c> is the same as <c>Tag</c> in the + <c>Tests</c> parameter to this function and <c>Modules</c> is a + list of module names (atoms).</p> + <p><em>Example:</em></p> + <p>If the module <c>m1</c> belongs to system <c>s1</c> but is + heavily used also in the tests for another system <c>s2</c>, + then the cover files for the two systems' tests could be like + this:</p> +<code type="none"> +s1.cover: + {include,[m1]}. + +s2.cover: + {include,[....]}. % modules belonging to system s2 + {cross,[{s1,[m1]}]}.</code> + <p>When the tests for both <c>s1</c> and <c>s2</c> are completed, run</p> +<code type="none"> +test_server_ctrl:cross_cover_analyse(Level,[{s1,S1LogDir},{s2,S2LogDir}]) +</code> + + <p>and the accumulated cover data for <c>m1</c> will be written to + <c>S1LogDir/[run.<timestamp>/]cross_cover.html</c>.</p> + <p>Note that the <c>m1</c> module will also be presented in the + normal coverage log for <c>s1</c> (due to the include statement in + <c>s1.cover</c>), but that only includes the coverage achieved by the + <c>s1</c> test itself.</p> + <p>The Tag in the <c>cross</c> statement in the cover file has + no other purpose than mapping the list of modules + (<c>[m1]</c> in the example above) to the correct log + directory where it should be included in the + <c>cross_cover.html</c> file (<c>S1LogDir</c> in the example + above). I.e. the value of <c>Tag</c> has no meaning, it + could be <c>foo</c> as well as <c>s1</c> above, as long as + the same <c>Tag</c> is used in the cover file and in the + call to this function.</p> </desc> </func> <func> diff --git a/lib/test_server/doc/src/ts.xml b/lib/test_server/doc/src/ts.xml index 4a2c536e96..82ba3a5017 100644 --- a/lib/test_server/doc/src/ts.xml +++ b/lib/test_server/doc/src/ts.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2007</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -450,7 +450,7 @@ This option is mandatory for remote targets <desc> <p>Analyse cover data collected from all tests. </p> - <p>See test_server_ctrl:cross_cover_analyse/1 + <p>See test_server_ctrl:cross_cover_analyse/2 </p> </desc> </func> diff --git a/lib/test_server/src/Makefile b/lib/test_server/src/Makefile index 20e7a5942c..3261936472 100644 --- a/lib/test_server/src/Makefile +++ b/lib/test_server/src/Makefile @@ -69,7 +69,6 @@ INTERNAL_HRL_FILES = test_server_internal.hrl TS_HRL_FILES= ts.hrl C_FILES = AUTOCONF_FILES = configure.in conf_vars.in -COVER_FILES = cross.cover PROGRAMS = configure config.sub config.guess install-sh CONFIG = ts.config ts.unix.config ts.win32.config @@ -137,7 +136,7 @@ release_tests_spec: opt $(INSTALL_DATA) $(ERL_FILES) $(TS_ERL_FILES) \ $(HRL_FILES) $(INTERNAL_HRL_FILES) $(TS_HRL_FILES) \ $(TS_TARGET_FILES) \ - $(AUTOCONF_FILES) $(C_FILES) $(COVER_FILES) $(CONFIG) \ + $(AUTOCONF_FILES) $(C_FILES) $(CONFIG) \ "$(RELEASE_PATH)/test_server" $(INSTALL_SCRIPT) $(PROGRAMS) "$(RELEASE_PATH)/test_server" diff --git a/lib/test_server/src/erl2html2.erl b/lib/test_server/src/erl2html2.erl index 9c459c05d4..1729257809 100644 --- a/lib/test_server/src/erl2html2.erl +++ b/lib/test_server/src/erl2html2.erl @@ -34,11 +34,17 @@ convert(File, Dest) -> %% %% FIXME: The colours should *really* be set with %% stylesheets... + Encoding = encoding(File), Header = ["<!DOCTYPE HTML PUBLIC " "\"-//W3C//DTD HTML 3.2 Final//EN\">\n" "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n" "<html>\n" - "<head><title>", File, "</title></head>\n\n" + "<head>\n" + "<meta http-equiv=\"Content-Type\" content=\"text/html;" + "charset=", + Encoding,"\"/>\n" + "<title>", File, "</title>\n" + "</head>\n\n" "<body bgcolor=\"white\" text=\"black\"" " link=\"blue\" vlink=\"purple\" alink=\"red\">\n"], convert(File, Dest, Header). @@ -55,12 +61,12 @@ convert(File, Dest, Header) -> case file:open(Dest,[write,raw]) of {ok,DFd} -> file:write(DFd,[Header,"<pre>\n"]), - Lines = build_html(SFd,DFd,Functions), + _Lines = build_html(SFd,DFd,Functions), file:write(DFd,["</pre>\n",footer(), "</body>\n</html>\n"]), %% {_, Time2} = statistics(runtime), %% io:format("Converted ~p lines in ~.2f Seconds.~n", - %% [Lines, Time2/1000]), + %% [_Lines, Time2/1000]), file:close(SFd), file:close(DFd), ok; @@ -180,3 +186,20 @@ possibly_enhance(Str,false) -> %%% End of the file footer() -> "". + +%%%----------------------------------------------------------------- +%%% Read encoding from source file +encoding(File) -> + Encoding = + case epp:read_encoding(File) of + none -> + epp:default_encoding(); + E -> + E + end, + html_encoding(Encoding). + +html_encoding(latin1) -> + "iso-8859-1"; +html_encoding(utf8) -> + "utf-8". diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl index 14cdfd391a..37cd8fac99 100644 --- a/lib/test_server/src/test_server.erl +++ b/lib/test_server/src/test_server.erl @@ -60,8 +60,6 @@ -include("test_server_internal.hrl"). -include_lib("kernel/include/file.hrl"). --define(pl2a(M), test_server_sup:package_atom(M)). - init_target_info() -> [$.|Emu] = code:objfile_extension(), {_, OTPRel} = init:script_id(), @@ -97,7 +95,8 @@ init_purify() -> %% is found, else {error,application_not_found}. cover_compile({none,_Exclude,Include,Cross}) -> - CompileMods = Include++Cross, + CrossMods = lists:flatmap(fun({_,M}) -> M end,Cross), + CompileMods = Include++CrossMods, case length(CompileMods) of 0 -> io:fwrite("WARNING: No modules to cover compile!\n\n",[]), @@ -111,7 +110,8 @@ cover_compile({none,_Exclude,Include,Cross}) -> {ok,Include} end; cover_compile({App,all,Include,Cross}) -> - CompileMods = Include++Cross, + CrossMods = lists:flatmap(fun({_,M}) -> M end,Cross), + CompileMods = Include++CrossMods, case length(CompileMods) of 0 -> io:fwrite("WARNING: No modules to cover compile!\n\n",[]), @@ -129,9 +129,10 @@ cover_compile({App,all,Include,Cross}) -> {ok,Include} end; cover_compile({App,Exclude,Include,Cross}) -> + CrossMods = lists:flatmap(fun({_,M}) -> M end,Cross), case code:lib_dir(App) of {error,bad_name} -> - case Include++Cross of + case Include++CrossMods of [] -> io:format("\nWARNING: Can't find lib_dir for \'~w\'\n" "Not cover compiling!\n\n",[App]), @@ -152,7 +153,7 @@ cover_compile({App,Exclude,Include,Cross}) -> WC = filename:join(EbinDir,"*.beam"), AllMods = module_names(filelib:wildcard(WC)), AnalyseMods = (AllMods ++ Include) -- Exclude, - CompileMods = AnalyseMods ++ Cross, + CompileMods = AnalyseMods ++ CrossMods, case length(CompileMods) of 0 -> io:fwrite("WARNING: No modules to cover compile!\n\n",[]), @@ -317,14 +318,21 @@ pmap(Fun,List) -> do_cover_for_node(Node,CoverFunc) -> + do_cover_for_node(Node,CoverFunc,true). +do_cover_for_node(Node,CoverFunc,StickUnstick) -> %% In case a slave node is starting another slave node! I.e. this %% function is executed on a slave node - then the cover function %% must be executed on the master node. This is for instance the %% case in test_server's own tests. MainCoverNode = cover:get_main_node(), - Sticky = unstick_all_sticky(MainCoverNode,Node), + Sticky = + if StickUnstick -> unstick_all_sticky(MainCoverNode,Node); + true -> ok + end, rpc:call(MainCoverNode,cover,CoverFunc,[Node]), - stick_all_sticky(Node,Sticky). + if StickUnstick -> stick_all_sticky(Node,Sticky); + true -> ok + end. unstick_all_sticky(Node) -> unstick_all_sticky(node(),Node). @@ -911,7 +919,7 @@ run_test_case_eval(Mod, Func, Args0, Name, Ref, RunInit, put(test_server_logopts, LogOpts), Where = [{Mod,Func}], put(test_server_loc, Where), - FWInitResult = test_server_sup:framework_call(init_tc,[?pl2a(Mod),Func,Args0], + FWInitResult = test_server_sup:framework_call(init_tc,[Mod,Func,Args0], {ok,Args0}), set_tc_state(running), {{Time,Value},Loc,Opts} = @@ -1045,7 +1053,7 @@ do_end_tc_call(Mod, Func, Res, Return) -> Ref = make_ref(), if FwMod == "ct_framework" ; FwMod == "undefined"; FwMod == false -> case test_server_sup:framework_call( - end_tc, [?pl2a(Mod),Func,Res, Return], ok) of + end_tc, [Mod,Func,Res, Return], ok) of {fail,FWReason} -> {failed,FWReason}; ok -> @@ -1060,7 +1068,7 @@ do_end_tc_call(Mod, Func, Res, Return) -> end; true -> case test_server_sup:framework_call(FwMod, end_tc, - [?pl2a(Mod),Func,Res], Ref) of + [Mod,Func,Res], Ref) of {fail,FWReason} -> {failed,FWReason}; _Else -> @@ -1294,11 +1302,11 @@ get_loc(Pid) -> fw_error_notify(Mod, Func, Args, Error) -> test_server_sup:framework_call(error_notification, - [?pl2a(Mod),Func,[Args], + [Mod,Func,[Args], {Error,unknown}]). fw_error_notify(Mod, Func, Args, Error, Loc) -> test_server_sup:framework_call(error_notification, - [?pl2a(Mod),Func,[Args], + [Mod,Func,[Args], {Error,Loc}]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2186,12 +2194,9 @@ start_node(Name, Type, Options) -> %% Cannot run cover on shielded node or on a node started %% by a shielded node. - Cover = case is_cover() of + Cover = case is_cover(Node) of true -> - not is_shielded(Name) - andalso same_version(Node) - andalso proplists:get_value(start_cover,Options, - true); + proplists:get_value(start_cover,Options,true); false -> false end, @@ -2230,15 +2235,8 @@ wait_for_node(Slave) -> Result = receive {sync_result,R} -> R end, case Result of ok -> - Cover = case is_cover() of - true -> - not is_shielded(Slave) andalso same_version(Slave); - false -> - false - end, - net_adm:ping(Slave), - case Cover of + case is_cover(Slave) of true -> do_cover_for_node(Slave,start); _ -> @@ -2256,12 +2254,9 @@ wait_for_node(Slave) -> %% Kills a (remote) node. %% Also inform test_server_ctrl so it can clean up! stop_node(Slave) -> - Nocover = is_shielded(Slave) orelse not same_version(Slave), - case is_cover() of - true when not Nocover -> - do_cover_for_node(Slave,flush); - _ -> - ok + Cover = is_cover(Slave), + if Cover -> do_cover_for_node(Slave,flush,false); + true -> ok end, group_leader() ! {sync_apply,self(),{test_server_ctrl,stop_node,[Slave]}}, Result = receive {sync_result,R} -> R end, @@ -2273,10 +2268,15 @@ stop_node(Slave) -> {nodedown, Slave} -> format(minor, "Stopped slave node: ~p", [Slave]), format(major, "=node_stop ~p", [Slave]), + if Cover -> do_cover_for_node(Slave,stop,false); + true -> ok + end, true after 30000 -> format("=== WARNING: Node ~p does not seem to terminate.", [Slave]), + erlang:monitor_node(Slave, false), + receive {nodedown, Slave} -> ok after 0 -> ok end, false end; {error, _Reason} -> @@ -2288,9 +2288,27 @@ stop_node(Slave) -> [Slave]), case net_adm:ping(Slave)of pong -> + erlang:monitor_node(Slave, true), slave:stop(Slave), - true; + receive + {nodedown, Slave} -> + format(minor, "Stopped slave node: ~p", [Slave]), + format(major, "=node_stop ~p", [Slave]), + if Cover -> do_cover_for_node(Slave,stop,false); + true -> ok + end, + true + after 30000 -> + format("=== WARNING: Node ~p does not seem to terminate.", + [Slave]), + erlang:monitor_node(Slave, false), + receive {nodedown, Slave} -> ok after 0 -> ok end, + false + end; pang -> + if Cover -> do_cover_for_node(Slave,stop,false); + true -> ok + end, false end end. @@ -2377,6 +2395,14 @@ same_version(Name) -> OtherVersion = rpc:call(Name, erlang, system_info, [version]), ThisVersion =:= OtherVersion. +is_cover(Name) -> + case is_cover() of + true -> + not is_shielded(Name) andalso same_version(Name); + false -> + false + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% temp_name(Stem) -> string() %% Stem = string() diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl index bc08c12089..c5c57426b4 100644 --- a/lib/test_server/src/test_server_ctrl.erl +++ b/lib/test_server/src/test_server_ctrl.erl @@ -53,8 +53,7 @@ -export([reject_io_reqs/1, get_levels/0, set_levels/3]). -export([multiply_timetraps/1, scale_timetraps/1, get_timetrap_parameters/0]). -export([create_priv_dir/1]). --export([cover/2, cover/3, cover/8, - cross_cover_analyse/2, cross_cover_analyse/3, trc/1, stop_trace/0]). +-export([cover/2, cover/3, cover/8, cross_cover_analyse/2, trc/1, stop_trace/0]). -export([testcase_callback/1]). -export([set_random_seed/1]). -export([kill_slavenodes/0]). @@ -88,17 +87,18 @@ -define(data_dir_suffix, "_data/"). -define(suitelog_name, "suite.log"). -define(coverlog_name, "cover.html"). +-define(raw_coverlog_name, "cover.log"). -define(cross_coverlog_name, "cross_cover.html"). +-define(raw_cross_coverlog_name, "cross_cover.log"). +-define(cross_cover_info, "cross_cover.info"). -define(cover_total, "total_cover.log"). -define(unexpected_io_log, "unexpected_io.log"). -define(last_file, "last_name"). -define(last_link, "last_link"). -define(last_test, "last_test"). -define(html_ext, ".html"). --define(cross_cover_file, "cross.cover"). -define(now, erlang:now()). --define(pl2a(M), test_server_sup:package_atom(M)). -define(void_fun, fun() -> ok end). -define(mod_result(X), if X == skip -> skipped; X == auto_skip -> skipped; @@ -409,7 +409,9 @@ cover(CoverFile, Analyse) -> cover(App, CoverFile, Analyse) -> controller_call({cover,{App,CoverFile},Analyse,true}). cover(App, CoverFile, Exclude, Include, Cross, Export, Analyse, Stop) -> - controller_call({cover,{App,{CoverFile,Exclude,Include,Cross,Export}},Analyse,Stop}). + controller_call({cover, + {App,{CoverFile,Exclude,Include,Cross,Export}}, + Analyse,Stop}). testcase_callback(ModFunc) -> controller_call({testcase_callback,ModFunc}). @@ -2277,7 +2279,7 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases], set_io_buffering(undefined), {Mod,Func} = skip_case(auto, Ref, 0, Case, Comment, false, SkipMode), test_server_sup:framework_call(report, [tc_auto_skip, - {?pl2a(Mod),Func,Comment}]), + {Mod,Func,Comment}]), run_test_cases_loop(Cases, Config, TimetrapData, ParentMode, delete_status(Ref, Status)); _ -> @@ -2286,7 +2288,7 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases], wait_for_cases(Ref), {Mod,Func} = skip_case(auto, Ref, 0, Case, Comment, true, SkipMode), test_server_sup:framework_call(report, [tc_auto_skip, - {?pl2a(Mod),Func,Comment}]), + {Mod,Func,Comment}]), case CurrIOHandler of {Ref,_} -> %% current_io_handler was set by start conf of this @@ -2303,7 +2305,7 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases], %% this is a skipped end conf for a non-parallel group that's not %% nested under a parallel group {Mod,Func} = skip_case(auto, Ref, 0, Case, Comment, false, SkipMode), - test_server_sup:framework_call(report, [tc_auto_skip,{?pl2a(Mod),Func,Comment}]), + test_server_sup:framework_call(report, [tc_auto_skip,{Mod,Func,Comment}]), %% Check if this group is auto skipped because of error in the init conf. %% If so, check if the parent group is a sequence, and if it is, skip @@ -2334,7 +2336,7 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases], %% this is a skipped end conf for a non-parallel group nested under %% a parallel group (io buffering is active) {Mod,Func} = skip_case(auto, Ref, 0, Case, Comment, true, SkipMode), - test_server_sup:framework_call(report, [tc_auto_skip,{?pl2a(Mod),Func,Comment}]), + test_server_sup:framework_call(report, [tc_auto_skip,{Mod,Func,Comment}]), case CurrIOHandler of {Ref,_} -> %% current_io_handler was set by start conf of this @@ -2350,7 +2352,7 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases], %% this is a skipped start conf for a group which is not nested %% under a parallel group {Mod,Func} = skip_case(auto, Ref, 0, Case, Comment, false, SkipMode), - test_server_sup:framework_call(report, [tc_auto_skip,{?pl2a(Mod),Func,Comment}]), + test_server_sup:framework_call(report, [tc_auto_skip,{Mod,Func,Comment}]), run_test_cases_loop(Cases, Config, TimetrapData, [conf(Ref,[])|Mode], Status); {_,Ref0} when is_reference(Ref0) -> %% this is a skipped start conf for a group nested under a parallel group @@ -2361,7 +2363,7 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases], ok end, {Mod,Func} = skip_case(auto, Ref, 0, Case, Comment, true, SkipMode), - test_server_sup:framework_call(report, [tc_auto_skip,{?pl2a(Mod),Func,Comment}]), + test_server_sup:framework_call(report, [tc_auto_skip,{Mod,Func,Comment}]), run_test_cases_loop(Cases, Config, TimetrapData, [conf(Ref,[])|Mode], Status) end; @@ -2369,7 +2371,7 @@ run_test_cases_loop([{auto_skip_case,{Case,Comment},SkipMode}|Cases], Config, TimetrapData, Mode, Status) -> {Mod,Func} = skip_case(auto, undefined, get(test_server_case_num)+1, Case, Comment, is_io_buffered(), SkipMode), - test_server_sup:framework_call(report, [tc_auto_skip,{?pl2a(Mod),Func,Comment}]), + test_server_sup:framework_call(report, [tc_auto_skip,{Mod,Func,Comment}]), run_test_cases_loop(Cases, Config, TimetrapData, Mode, update_status(skipped, Mod, Func, Status)); @@ -2385,7 +2387,7 @@ run_test_cases_loop([{skip_case,{conf,Ref,Case,Comment}}|Cases0], %% skipped start conf {skip_cases_upto(Ref, Cases0, Comment, conf, Mode),Config} end, - test_server_sup:framework_call(report, [tc_user_skip,{?pl2a(Mod),Func,Comment}]), + test_server_sup:framework_call(report, [tc_user_skip,{Mod,Func,Comment}]), run_test_cases_loop(Cases, Config1, TimetrapData, Mode, update_status(skipped, Mod, Func, Status)); @@ -2393,7 +2395,7 @@ run_test_cases_loop([{skip_case,{Case,Comment}}|Cases], Config, TimetrapData, Mode, Status) -> {Mod,Func} = skip_case(user, undefined, get(test_server_case_num)+1, Case, Comment, is_io_buffered()), - test_server_sup:framework_call(report, [tc_user_skip,{?pl2a(Mod),Func,Comment}]), + test_server_sup:framework_call(report, [tc_user_skip,{Mod,Func,Comment}]), run_test_cases_loop(Cases, Config, TimetrapData, Mode, update_status(skipped, Mod, Func, Status)); @@ -3571,7 +3573,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, end, test_server_sup:framework_call(report, - [tc_start,{{?pl2a(Mod),Func},MinorName}]), + [tc_start,{{Mod,Func},MinorName}]), print_props((RunInit==skip_init), get_props(Mode)), GroupName = case get_name(Mode) of @@ -3756,7 +3758,7 @@ progress(skip, CaseNum, Mod, Func, Loc, Reason, Time, print(major, "=result skipped", []), print(1, "*** SKIPPED *** ~s", [get_info_str(Func, CaseNum, get(test_server_cases))]), - test_server_sup:framework_call(report, [tc_done,{?pl2a(Mod),Func, + test_server_sup:framework_call(report, [tc_done,{Mod,Func, {skipped,Reason1}}]), ReasonStr = reason_to_string(Reason1), ReasonStr1 = lists:flatten([string:strip(S,left) || @@ -3787,7 +3789,7 @@ progress(failed, CaseNum, Mod, Func, Loc, timetrap_timeout, T, print(1, "*** FAILED *** ~s", [get_info_str(Func, CaseNum, get(test_server_cases))]), test_server_sup:framework_call(report, - [tc_done,{?pl2a(Mod),Func, + [tc_done,{Mod,Func, {failed,timetrap_timeout}}]), FormatLastLoc = test_server_sup:format_loc(get_last_loc(Loc)), ErrorReason = io_lib:format("{timetrap_timeout,~s}", [FormatLastLoc]), @@ -3813,7 +3815,7 @@ progress(failed, CaseNum, Mod, Func, Loc, {testcase_aborted,Reason}, _T, print(1, "*** FAILED *** ~s", [get_info_str(Func, CaseNum, get(test_server_cases))]), test_server_sup:framework_call(report, - [tc_done,{?pl2a(Mod),Func, + [tc_done,{Mod,Func, {failed,testcase_aborted}}]), FormatLastLoc = test_server_sup:format_loc(get_last_loc(Loc)), ErrorReason = io_lib:format("{testcase_aborted,~s}", [FormatLastLoc]), @@ -3838,7 +3840,7 @@ progress(failed, CaseNum, Mod, Func, unknown, Reason, Time, print(major, "=result failed: ~p, ~p", [Reason,unknown]), print(1, "*** FAILED *** ~s", [get_info_str(Func, CaseNum, get(test_server_cases))]), - test_server_sup:framework_call(report, [tc_done,{?pl2a(Mod),Func, + test_server_sup:framework_call(report, [tc_done,{Mod,Func, {failed,Reason}}]), TimeStr = io_lib:format(if is_float(Time) -> "~.3fs"; true -> "~w" @@ -3874,7 +3876,7 @@ progress(failed, CaseNum, Mod, Func, Loc, Reason, Time, print(major, "=result failed: ~p, ~p", [Reason,Loc]), print(1, "*** FAILED *** ~s", [get_info_str(Func, CaseNum, get(test_server_cases))]), - test_server_sup:framework_call(report, [tc_done,{?pl2a(Mod),Func, + test_server_sup:framework_call(report, [tc_done,{Mod,Func, {failed,Reason}}]), TimeStr = io_lib:format(if is_float(Time) -> "~.3fs"; true -> "~w" @@ -3899,7 +3901,7 @@ progress(failed, CaseNum, Mod, Func, Loc, Reason, Time, progress(ok, _CaseNum, Mod, Func, _Loc, RetVal, Time, Comment0, {St0,St1}) -> print(minor, "successfully completed test case", []), - test_server_sup:framework_call(report, [tc_done,{?pl2a(Mod),Func,ok}]), + test_server_sup:framework_call(report, [tc_done,{Mod,Func,ok}]), Comment = case RetVal of {comment,RetComment} -> @@ -4546,7 +4548,7 @@ collect_case_invoke(Mod, Case, MFA, St) -> end; _ -> Suite = test_server_sup:framework_call(get_suite, - [?pl2a(Mod),Case], + [Mod,Case], []), collect_subcases(Mod, Case, MFA, St, Suite) end. @@ -4898,33 +4900,52 @@ pinfo(P) -> %% - it does not belong to the application, but is listed in the %% {include,List} part of the App.cover file %% - it does not belong to the application, but is listed in the -%% cross.cover file (in the test_server application) under 'all' -%% or under the tested application. -%% -%% The modules listed in the cross.cover file are modules that are -%% hevily used by other applications than the one they belong -%% to. After all tests are completed, these modules can be analysed -%% with coverage data from all tests - see cross_cover_analyse/1. The -%% result is stored in a file called cross_cover.html in the -%% run.<timestamp> directory of the application the modules belong -%% to. -%% -%% For example, the lists module is listed in cross.cover to be -%% included in all tests. lists belongs to the stdlib -%% application. cross_cover_analyse/1 will create a file named -%% cross_cover.html under the newest stdlib.logs/run.xxx directory, -%% where the coverage result for the lists module from all tests is -%% presented. -%% -%% The lists module is also presented in the normal coverage log -%% for stdlib, but that only includes the coverage achieved by -%% the stdlib tests themselves. -%% -%% The Cross cover file cross.cover contains elements like this: -%% {App,Modules}. -%% where App can be an application name or the atom all. The -%% application (or all applications) shall cover compile the listed -%% Modules. +%% {cross,[{Tag,List}]} part of the App.cover file +%% +%% The modules listed in the 'cross' part of the cover file are +%% modules that are heavily used by other tests than the one where +%% they are explicitly tested. They should then be listed as 'cross' +%% in the cover file for the test where they are used but do not +%% belong. +%% +%% After all tests are completed, the these modules can be analysed +%% with coverage data from all tests where they are compiled - see +%% cross_cover_analyse/2. The result is stored in a file called +%% cross_cover.html in the run.<timestamp> directory of the +%% test the modules belong to. +%% +%% Example: +%% If the module m1 belongs to system s1 but is heavily used also in +%% the tests for another system s2, then the cover files for the two +%% systems could be like this: +%% +%% s1.cover: +%% {include,[m1]}. +%% +%% s2.cover: +%% {include,[....]}. % modules belonging to system s2 +%% {cross,[{s1,[m1]}]}. +%% +%% When the tests for both s1 and s2 are completed, run +%% cross_cover_analyse(Level,[{s1,S1LogDir},{s2,S2LogDir}]), and +%% the accumulated cover data for m1 will be written to +%% S1LogDir/[run.<timestamp>/]cross_cover.html +%% +%% S1LogDir and S2LogDir are either the run.<timestamp> directories +%% for the two tests, or the parent directory of these, in which case +%% the latest run.<timestamp> directory will be chosen. +%% +%% Note that the m1 module will also be presented in the normal +%% coverage log for s1 (due to the include statement in s1.cover), but +%% that only includes the coverage achieved by the s1 test itself. +%% +%% The Tag in the 'cross' statement in the cover file has no other +%% purpose than mapping the list of modules ([m1] in the example +%% above) to the correct log directory where it should be included in +%% the cross_cover.html file (S1LogDir in the example above). +%% I.e. the value of the Tag has no meaning, it could be foo as well +%% as s1 above, as long as the same Tag is used in the cover file and +%% in the call to cross_cover_analyse/2. %% Cover compilation @@ -4933,8 +4954,7 @@ cover_compile({App,{_File,Exclude,Include,Cross,_Export}}) -> cover_compile1({App,Exclude,Include,Cross}); cover_compile({App,CoverFile}) -> - Cross = get_cross_modules(App), - {Exclude,Include} = read_cover_file(CoverFile), + {Exclude,Include,Cross} = read_cover_file(CoverFile), cover_compile1({App,Exclude,Include,Cross}). cover_compile1(What) -> @@ -4945,41 +4965,57 @@ cover_compile1(What) -> %% (Exclude), and a list of modules that are not members of the %% application but shall be compiled (Include). read_cover_file(none) -> - {[],[]}; + {[],[],[]}; read_cover_file(CoverFile) -> case file:consult(CoverFile) of {ok,List} -> - case check_cover_file(List, [], []) of - {ok,Exclude,Include} -> {Exclude,Include}; + case check_cover_file(List, [], [], []) of + {ok,Exclude,Include,Cross} -> {Exclude,Include,Cross}; error -> io:fwrite("Faulty format of CoverFile ~p\n", [CoverFile]), - {[],[]} + {[],[],[]} end; {error,Reason} -> io:fwrite("Can't read CoverFile ~p\nReason: ~p\n", [CoverFile,Reason]), - {[],[]} + {[],[],[]} end. -check_cover_file([{exclude,all}|Rest], _, Include) -> - check_cover_file(Rest, all, Include); -check_cover_file([{exclude,Exclude}|Rest], _, Include) -> +check_cover_file([{exclude,all}|Rest], _, Include, Cross) -> + check_cover_file(Rest, all, Include, Cross); +check_cover_file([{exclude,Exclude}|Rest], _, Include, Cross) -> case lists:all(fun(M) -> is_atom(M) end, Exclude) of true -> - check_cover_file(Rest, Exclude, Include); + check_cover_file(Rest, Exclude, Include, Cross); false -> error end; -check_cover_file([{include,Include}|Rest], Exclude, _) -> +check_cover_file([{include,Include}|Rest], Exclude, _, Cross) -> case lists:all(fun(M) -> is_atom(M) end, Include) of true -> - check_cover_file(Rest, Exclude, Include); + check_cover_file(Rest, Exclude, Include, Cross); false -> error end; -check_cover_file([], Exclude, Include) -> - {ok,Exclude,Include}. +check_cover_file([{cross,Cross}|Rest], Exclude, Include, _) -> + case check_cross(Cross) of + true -> + check_cover_file(Rest, Exclude, Include, Cross); + false -> + error + end; +check_cover_file([], Exclude, Include, Cross) -> + {ok,Exclude,Include,Cross}. +check_cross([{Tag,Modules}|Rest]) -> + case lists:all(fun(M) -> is_atom(M) end, [Tag|Modules]) of + true -> + check_cross(Rest); + false -> + false + end; +check_cross([]) -> + true. %% Cover analysis, per application @@ -5000,16 +5036,17 @@ cover_analyse({App,CoverInfo}, Analyse, AnalyseMods, Stop, TestDir) -> "<p><a href=\"~s\">Coverdata collected over all tests</a></p>", [?cross_coverlog_name]), - {CoverFile,_Included,Excluded} = + {CoverFile,_Included,Excluded,Cross} = case CoverInfo of - {File,Excl,Incl,_Cross,Export} -> + {File,Excl,Incl,Cr,Export} -> cover:export(Export), - {File,Incl,Excl}; + {File,Incl,Excl,Cr}; File -> - {Excl,Incl} = read_cover_file(File), - {File,Incl,Excl} + {Excl,Incl,Cr} = read_cover_file(File), + {File,Incl,Excl,Cr} end, io:fwrite(CoverLog, "<p>CoverFile: <code>~p</code>\n", [CoverFile]), + write_cross_cover_info(TestDir,Cross), case length(cover:imported_modules()) of Imps when Imps > 0 -> @@ -5022,6 +5059,8 @@ cover_analyse({App,CoverInfo}, Analyse, AnalyseMods, Stop, TestDir) -> io:fwrite(CoverLog, "<p>Excluded module(s): <code>~p</code>\n", [Excluded]), Coverage = cover_analyse(Analyse, AnalyseMods, Stop), + file:write_file(filename:join(TestDir,?raw_coverlog_name), + term_to_binary(Coverage)), case lists:filter(fun({_M,{_,_,_}}) -> false; (_) -> true @@ -5043,20 +5082,20 @@ cover_analyse(Analyse, AnalyseMods, Stop) -> test_server:cover_analyse({Analyse,TestDir}, AnalyseMods, Stop). -%% Cover analysis, cross application +%% Cover analysis - accumulated over multiple tests %% This can be executed on any node after all tests are finished. -%% Apps = [{App,Dir}] -%% App = atom(), application name -%% Dir = string(), the log directory for App, normally where -%% run.<timestamp> is found. -%% Modules = [atom()], modules that have been cover compiled during tests -%% of other apps than the one they belong to. -cross_cover_analyse(Analyse, Apps) -> - cross_cover_analyse(Analyse, Apps, get_cross_modules()). -cross_cover_analyse(Analyse, Apps, Modules) -> - Apps1 = get_latest_run_dirs(Apps), - Apps2 = add_cross_modules(Modules,Apps1), - CoverdataFiles = get_coverdata_files(Apps2), +%% Analyse = overview | details +%% TagDirs = [{Tag,Dir}] +%% Tag = atom(), identifier +%% Dir = string(), the log directory for Tag, it can be a +%% run.<timestamp> directory or the parent directory of +%% such (in which case the latest run.<timestamp> directory +%% is used) +cross_cover_analyse(Analyse, TagDirs0) -> + TagDirs = get_latest_run_dirs(TagDirs0), + TagMods = get_all_cross_info(TagDirs,[]), + TagDirMods = add_cross_modules(TagMods,TagDirs), + CoverdataFiles = get_coverdata_files(TagDirMods), lists:foreach(fun(CDF) -> cover:import(CDF) end, CoverdataFiles), io:fwrite("Cover analysing...\n", []), DetailsFun = @@ -5066,39 +5105,52 @@ cross_cover_analyse(Analyse, Apps, Modules) -> OutFile = filename:join(Dir, atom_to_list(M) ++ ".CROSS_COVER.html"), - cover:analyse_to_file(M, OutFile, [html]), - {file,OutFile} + case cover:analyse_to_file(M, OutFile, [html]) of + {ok,_} -> + {file,OutFile}; + Error -> + Error + end end; _ -> fun(_,_) -> undefined end end, - Coverage = analyse_apps(Apps2, DetailsFun, []), + Coverage = analyse_tests(TagDirMods, DetailsFun, []), cover:stop(), - write_cross_cover_logs(Coverage,Apps2). + write_cross_cover_logs(Coverage,TagDirMods). -%% For each application from which there are cross cover analysed +write_cross_cover_info(_Dir,[]) -> + ok; +write_cross_cover_info(Dir,Cross) -> + {ok,Fd} = file:open(filename:join(Dir,?cross_cover_info),[write]), + lists:foreach(fun(C) -> io:format(Fd,"~p.~n",[C]) end, Cross), + file:close(Fd). + +%% For each test from which there are cross cover analysed %% modules, write a cross cover log (cross_cover.html). -write_cross_cover_logs([{App,Coverage}|T],Apps) -> - case lists:keyfind(App,1,Apps) of +write_cross_cover_logs([{Tag,Coverage}|T],TagDirMods) -> + case lists:keyfind(Tag,1,TagDirMods) of {_,Dir,Mods} when Mods=/=[] -> + file:write_file(filename:join(Dir,?raw_cross_coverlog_name), + term_to_binary(Coverage)), CoverLogName = filename:join(Dir,?cross_coverlog_name), {ok,CoverLog} = file:open(CoverLogName, [write]), write_coverlog_header(CoverLog), io:fwrite(CoverLog, "<h1>Coverage results for \'~w\' from all tests</h1>\n", - [App]), + [Tag]), write_cover_result_table(CoverLog, Coverage), io:fwrite("Written file ~p\n", [CoverLogName]); _ -> ok end, - write_cross_cover_logs(T,Apps); + write_cross_cover_logs(T,TagDirMods); write_cross_cover_logs([],_) -> io:fwrite("done\n", []). %% Get the latest run.<timestamp> directories -get_latest_run_dirs([{App,Dir}|Apps]) -> - [{App,get_latest_run_dir(Dir)} | get_latest_run_dirs(Apps)]; +get_latest_run_dirs([{Tag,Dir}|Rest]) -> + [{Tag,get_latest_run_dir(Dir)} | get_latest_run_dirs(Rest)]; get_latest_run_dirs([]) -> []. @@ -5117,44 +5169,47 @@ get_latest_dir([_|T],Latest) -> get_latest_dir([],Latest) -> Latest. -%% Associate the cross cover modules with their applications. -add_cross_modules(Mods,Apps)-> - do_add_cross_modules(Mods,[{App,Dir,[]} || {App,Dir} <- Apps]). -do_add_cross_modules([Mod|Mods],Apps)-> - App = get_app(Mod), - NewApps = - case lists:keytake(App,1,Apps) of - {value,{App,Dir,AppMods},Rest} -> - [{App,Dir,lists:umerge([Mod],AppMods)}|Rest]; +get_all_cross_info([{_Tag,Dir}|Rest],Acc) -> + case file:consult(filename:join(Dir,?cross_cover_info)) of + {ok,TagMods} -> + get_all_cross_info(Rest,TagMods++Acc); + _ -> + get_all_cross_info(Rest,Acc) + end; +get_all_cross_info([],Acc) -> + Acc. + +%% Associate the cross cover modules with their log directories +add_cross_modules(TagMods,TagDirs)-> + do_add_cross_modules(TagMods,[{Tag,Dir,[]} || {Tag,Dir} <- TagDirs]). +do_add_cross_modules([{Tag,Mods1}|TagMods],TagDirMods)-> + NewTagDirMods = + case lists:keytake(Tag,1,TagDirMods) of + {value,{Tag,Dir,Mods},Rest} -> + [{Tag,Dir,lists:umerge(lists:sort(Mods1),Mods)}|Rest]; false -> - Apps + TagDirMods end, - do_add_cross_modules(Mods,NewApps); -do_add_cross_modules([],Apps) -> - %% Just to get the modules in the same order as app-only cover log - [{App,Dir,lists:reverse(Mods)} || {App,Dir,Mods} <- Apps]. - -get_app(Module) -> - Beam = code:which(Module), - AppDir = filename:basename(filename:dirname(filename:dirname(Beam))), - [AppStr|_] = string:tokens(AppDir,"-"), - list_to_atom(AppStr). + do_add_cross_modules(TagMods,NewTagDirMods); +do_add_cross_modules([],TagDirMods) -> + %% Just to get the modules in the same order as in the normal cover log + [{Tag,Dir,lists:reverse(Mods)} || {Tag,Dir,Mods} <- TagDirMods]. %% Find all exported coverdata files. -get_coverdata_files(Apps) -> +get_coverdata_files(TagDirMods) -> lists:flatmap( - fun({_,LatestAppDir,_}) -> - filelib:wildcard(filename:join(LatestAppDir,"all.coverdata")) + fun({_,LatestDir,_}) -> + filelib:wildcard(filename:join(LatestDir,"all.coverdata")) end, - Apps). + TagDirMods). -%% For each application, analyse all modules +%% For each test, analyse all modules %% Used for cross cover analysis. -analyse_apps([{App,LastTest,Modules}|T], DetailsFun, Acc) -> +analyse_tests([{Tag,LastTest,Modules}|T], DetailsFun, Acc) -> Cov = analyse_modules(LastTest, Modules, DetailsFun, []), - analyse_apps(T, DetailsFun, [{App,Cov}|Acc]); -analyse_apps([], _DetailsFun, Acc) -> + analyse_tests(T, DetailsFun, [{Tag,Cov}|Acc]); +analyse_tests([], _DetailsFun, Acc) -> Acc. %% Analyse each module @@ -5167,27 +5222,6 @@ analyse_modules(_Dir, [], _DetailsFun, Acc) -> Acc. -%% Read the cross cover file (cross.cover) -get_cross_modules() -> - get_cross_modules(all). -get_cross_modules(App) -> - case file:consult(?cross_cover_file) of - {ok,List} -> - get_cross_modules(App, List, []); - _X -> - [] - end. - -get_cross_modules(App, [{_To,Modules}|T], Acc) when App==all-> - get_cross_modules(App, T, Acc ++ Modules); -get_cross_modules(App, [{To,Modules}|T], Acc) when To==App; To==all-> - get_cross_modules(App, T, Acc ++ Modules); -get_cross_modules(App, [_H|T], Acc) -> - get_cross_modules(App, T, Acc); -get_cross_modules(_App, [], Acc) -> - Acc. - - %% Support functions for writing the cover logs (both cross and normal) write_coverlog_header(CoverLog) -> case catch diff --git a/lib/test_server/src/test_server_sup.erl b/lib/test_server/src/test_server_sup.erl index c7553cccb5..a6d426887e 100644 --- a/lib/test_server/src/test_server_sup.erl +++ b/lib/test_server/src/test_server_sup.erl @@ -28,7 +28,7 @@ get_username/0, get_os_family/0, hostatom/0, hostatom/1, hoststr/0, hoststr/1, framework_call/2,framework_call/3,framework_call/4, - format_loc/1, package_str/1, package_atom/1, + format_loc/1, call_trace/1]). -include("test_server_internal.hrl"). -define(crash_dump_tar,"crash_dumps.tar.gz"). @@ -553,7 +553,7 @@ format_loc([{Mod,Func,Line}|Rest]) -> format_loc([{Mod,LineOrFunc}]) -> format_loc({Mod,LineOrFunc}); format_loc({Mod,Func}) when is_atom(Func) -> - io_lib:format("{~s,~w}",[package_str(Mod),Func]); + io_lib:format("{~w,~w}",[Mod,Func]); format_loc(Loc) -> io_lib:format("~p",[Loc]). @@ -562,15 +562,15 @@ format_loc1([{Mod,Func,Line}]) -> format_loc1([{Mod,Func,Line}|Rest]) -> [" ",format_loc1({Mod,Func,Line}),",\n"|format_loc1(Rest)]; format_loc1({Mod,Func,Line}) -> - ModStr = package_str(Mod), + ModStr = atom_to_list(Mod), case {lists:member(no_src, get(test_server_logopts)), lists:reverse(ModStr)} of {false,[$E,$T,$I,$U,$S,$_|_]} -> - io_lib:format("{~s,~w,<a href=\"~s~s#~w\">~w</a>}", - [ModStr,Func,downcase(ModStr),?src_listing_ext, + io_lib:format("{~w,~w,<a href=\"~s~s#~w\">~w</a>}", + [Mod,Func,downcase(ModStr),?src_listing_ext, Line,Line]); _ -> - io_lib:format("{~s,~w,~w}",[ModStr,Func,Line]) + io_lib:format("{~w,~w,~w}",[Mod,Func,Line]) end. downcase(S) -> downcase(S, []). @@ -581,22 +581,6 @@ downcase([C|Rest], Result) -> downcase([], Result) -> lists:reverse(Result). -package_str(Mod) when is_atom(Mod) -> - atom_to_list(Mod); -package_str(Mod) when is_list(Mod), is_atom(hd(Mod)) -> - %% convert [s1,s2] -> "s1.s2" - [_|M] = lists:flatten(["."++atom_to_list(S) || S <- Mod]), - M; -package_str(Mod) when is_list(Mod) -> - Mod. - -package_atom(Mod) when is_atom(Mod) -> - Mod; -package_atom(Mod) when is_list(Mod), is_atom(hd(Mod)) -> - list_to_atom(package_str(Mod)); -package_atom(Mod) when is_list(Mod) -> - list_to_atom(Mod). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% call_trace(TraceSpecFile) -> ok %% diff --git a/lib/test_server/src/ts.erl b/lib/test_server/src/ts.erl index 115e783070..cfd7161dbd 100644 --- a/lib/test_server/src/ts.erl +++ b/lib/test_server/src/ts.erl @@ -160,8 +160,8 @@ help(installed) -> " the given run options\n", " ts:cross_cover_analyse(Level)\n" " - Used after ts:run with option cover or \n" - " cover_details. Analyses modules specified in\n" - " cross.cover.\n" + " cover_details. Analyses modules specified with\n" + " a 'cross' statement in the cover spec file.\n" " Level can be 'overview' or 'details'.\n", " ts:compile_testcases()~n" " ts:compile_testcases(Apps)~n" @@ -528,8 +528,7 @@ cross_cover_analyse([Level]) -> cross_cover_analyse(Level); cross_cover_analyse(Level) -> Apps = get_last_app_tests(), - Modules = get_cross_modules(Apps,[]), - test_server_ctrl:cross_cover_analyse(Level,Apps,Modules). + test_server_ctrl:cross_cover_analyse(Level,Apps). get_last_app_tests() -> AllTests = filelib:wildcard(filename:join(["*","*_test.logs"])), @@ -558,30 +557,6 @@ get_last_app_tests([Dir|Dirs],RE,Acc) -> get_last_app_tests([],_,Acc) -> Acc. -get_cross_modules([{App,_}|Apps],Acc) -> - Mods = cross_modules(App), - get_cross_modules(Apps,lists:umerge(Mods,Acc)); -get_cross_modules([],Acc) -> - Acc. - -cross_modules(App) -> - case default_coverfile(App) of - none -> - []; - File -> - case catch file:consult(File) of - {ok,CoverSpec} -> - case lists:keyfind(cross_apps,1,CoverSpec) of - false -> - []; - {cross_apps,App,Modules} -> - lists:usort(Modules) - end; - _ -> - [] - end - end. - %%% Implementation. check_and_run(Fun) -> diff --git a/lib/test_server/test/test_server_SUITE.erl b/lib/test_server/test/test_server_SUITE.erl index 95a3423fef..fb82a87fd0 100644 --- a/lib/test_server/test/test_server_SUITE.erl +++ b/lib/test_server/test/test_server_SUITE.erl @@ -80,7 +80,7 @@ all() -> [test_server_SUITE, test_server_parallel01_SUITE, test_server_conf02_SUITE, test_server_conf01_SUITE, test_server_skip_SUITE, test_server_shuffle01_SUITE, - test_server_break_SUITE]. + test_server_break_SUITE, test_server_cover_SUITE]. %%-------------------------------------------------------------------- @@ -93,37 +93,95 @@ test_server_SUITE(Config) -> % rpc:call(Node,dbg, tracer,[]), % rpc:call(Node,dbg, p,[all,c]), % rpc:call(Node,dbg, tpl,[test_server_ctrl,x]), - run_test_server_tests("test_server_SUITE", 38, 1, 30, - 19, 9, 1, 11, 2, 25, Config). + run_test_server_tests("test_server_SUITE", + [{test_server_SUITE,skip_case7,"SKIPPED!"}], + 38, 1, 30, 19, 9, 1, 11, 2, 25, Config). test_server_parallel01_SUITE(Config) -> - run_test_server_tests("test_server_parallel01_SUITE", 37, 0, 19, - 19, 0, 0, 0, 0, 37, Config). + run_test_server_tests("test_server_parallel01_SUITE", [], + 37, 0, 19, 19, 0, 0, 0, 0, 37, Config). test_server_shuffle01_SUITE(Config) -> - run_test_server_tests("test_server_shuffle01_SUITE", 130, 0, 0, - 76, 0, 0, 0, 0, 130, Config). + run_test_server_tests("test_server_shuffle01_SUITE", [], + 130, 0, 0, 76, 0, 0, 0, 0, 130, Config). test_server_skip_SUITE(Config) -> - run_test_server_tests("test_server_skip_SUITE", 3, 0, 1, - 0, 0, 1, 3, 0, 0, Config). + run_test_server_tests("test_server_skip_SUITE", [], + 3, 0, 1, 0, 0, 1, 3, 0, 0, Config). test_server_conf01_SUITE(Config) -> - run_test_server_tests("test_server_conf01_SUITE", 24, 0, 12, - 12, 0, 0, 0, 0, 24, Config). + run_test_server_tests("test_server_conf01_SUITE", [], + 24, 0, 12, 12, 0, 0, 0, 0, 24, Config). test_server_conf02_SUITE(Config) -> - run_test_server_tests("test_server_conf02_SUITE", 26, 0, 12, - 12, 0, 0, 0, 0, 26, Config). + run_test_server_tests("test_server_conf02_SUITE", [], + 26, 0, 12, 12, 0, 0, 0, 0, 26, Config). test_server_break_SUITE(Config) -> - D = run_test_server_tests("test_server_break_SUITE", 8, 2, 6, - 4, 0, 0, 0, 2, 6, Config), - D. + run_test_server_tests("test_server_break_SUITE", [], + 8, 2, 6, 4, 0, 0, 0, 2, 6, Config). -run_test_server_tests(SuiteName, NCases, NFail, NExpected, NSucc, +test_server_cover_SUITE(Config) -> + case test_server:is_cover() of + true -> + {skip, "Cover already running"}; + false -> + PrivDir = ?config(priv_dir,Config), + + %% Test suite has two test cases + %% tc1 calls cover_helper:foo/0 + %% tc2 calls cover_helper:bar/0 + %% Each function in cover_helper is one line. + %% + %% First test run skips tc2, so only cover_helper:foo/0 is executed. + %% Cover file specifies to include cover_helper in this test run. + CoverFile1 = filename:join(PrivDir,"t1.cover"), + CoverSpec1 = {include,[cover_helper]}, + file:write_file(CoverFile1,io_lib:format("~p.~n",[CoverSpec1])), + run_test_server_tests("test_server_cover_SUITE", + [{test_server_cover_SUITE,tc2,"SKIPPED!"}], + 4, 0, 2, 1, 1, 0, 1, 0, 3, + CoverFile1, Config), + + %% Next test run skips tc1, so only cover_helper:bar/0 is executed. + %% Cover file specifies cross compilation of cover_helper + CoverFile2 = filename:join(PrivDir,"t2.cover"), + CoverSpec2 = {cross,[{t1,[cover_helper]}]}, + file:write_file(CoverFile2,io_lib:format("~p.~n",[CoverSpec2])), + run_test_server_tests("test_server_cover_SUITE", + [{test_server_cover_SUITE,tc1,"SKIPPED!"}], + 4, 0, 2, 1, 1, 0, 1, 0, 3, CoverFile2, Config), + + %% Cross cover analyse + WorkDir = ?config(work_dir,Config), + WC = filename:join([WorkDir,"test_server_cover_SUITE.logs","run.*"]), + [D2,D1|_] = lists:reverse(lists:sort(filelib:wildcard(WC))), + TagDirs = [{t1,D1},{t2,D2}], + test_server_ctrl:cross_cover_analyse(details,TagDirs), + + %% Check that cover log shows only what is really included + %% in the test and cross cover log show the accumulated + %% result. + {ok,Cover1} = file:read_file(filename:join(D1,"cover.log")), + [{cover_helper,{1,1,_}}] = binary_to_term(Cover1), + {ok,Cover2} = file:read_file(filename:join(D2,"cover.log")), + [] = binary_to_term(Cover2), + {ok,Cross} = file:read_file(filename:join(D1,"cross_cover.log")), + [{cover_helper,{2,0,_}}] = binary_to_term(Cross), + ok + end. + + +run_test_server_tests(SuiteName, Skip, NCases, NFail, NExpected, NSucc, NUsrSkip, NAutoSkip, NActualSkip, NActualFail, NActualSucc, Config) -> + run_test_server_tests(SuiteName, Skip, NCases, NFail, NExpected, NSucc, + NUsrSkip, NAutoSkip, + NActualSkip, NActualFail, NActualSucc, false, Config). + +run_test_server_tests(SuiteName, Skip, NCases, NFail, NExpected, NSucc, + NUsrSkip, NAutoSkip, + NActualSkip, NActualFail, NActualSucc, Cover, Config) -> WorkDir = proplists:get_value(work_dir, Config), ct:log("<a href=\"file://~s\">Test case log files</a>\n", @@ -131,11 +189,17 @@ run_test_server_tests(SuiteName, NCases, NFail, NExpected, NSucc, Node = proplists:get_value(node, Config), {ok,_Pid} = rpc:call(Node,test_server_ctrl, start, []), + case Cover of + false -> + ok; + _ -> + rpc:call(Node,test_server_ctrl,cover,[Cover,details]) + end, rpc:call(Node, test_server_ctrl,add_dir_with_skip, [SuiteName, [proplists:get_value(data_dir,Config)],SuiteName, - [{test_server_SUITE,skip_case7,"SKIPPED!"}]]), + Skip]), until(fun() -> rpc:call(Node,test_server_ctrl,jobs,[]) =:= [] diff --git a/lib/test_server/test/test_server_SUITE_data/Makefile.src b/lib/test_server/test/test_server_SUITE_data/Makefile.src index ec8ddd78b0..c770627f04 100644 --- a/lib/test_server/test/test_server_SUITE_data/Makefile.src +++ b/lib/test_server/test/test_server_SUITE_data/Makefile.src @@ -5,4 +5,6 @@ all: erlc test_server_shuffle01_SUITE.erl erlc test_server_conf02_SUITE.erl erlc test_server_skip_SUITE.erl - erlc test_server_break_SUITE.erl
\ No newline at end of file + erlc test_server_break_SUITE.erl + erlc test_server_cover_SUITE.erl + erlc +debug_info test_server_cover_SUITE_data/cover_helper.erl
\ No newline at end of file diff --git a/lib/test_server/test/test_server_SUITE_data/test_server_break_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_break_SUITE.erl index 70e30a3334..d9f009679a 100644 --- a/lib/test_server/test/test_server_SUITE_data/test_server_break_SUITE.erl +++ b/lib/test_server/test/test_server_SUITE_data/test_server_break_SUITE.erl @@ -41,7 +41,7 @@ init_per_suite(Config) -> spawn(fun break_and_continue_sup/0), Config. -end_per_suite(Config) -> +end_per_suite(_Config) -> ok. init_per_testcase(Case,Config) when Case==break_in_init_tc -> @@ -90,19 +90,19 @@ break_in_end_tc_after_fail(Config) when is_list(Config) -> break_in_end_tc_after_abort(Config) when is_list(Config) -> ?t:adjusted_sleep(2000). % will cause a timetrap timeout -%%%----------------------------------------------------------------- -%%% Internal functions - %% This test case checks that all breaks in previous test cases was %% also continued, and that the break lasted as long as expected. %% The reason for this is that some of the breaks above are in %% end_per_testcase, and failures there will only produce a warning, %% not an error - so this is to catch the error for real. -check_all_breaks(Config) -> +check_all_breaks(Config) when is_list(Config) -> break_and_continue_sup ! {done,self()}, receive {Breaks,Continued} -> check_all_breaks(Breaks,Continued) end. +%%%----------------------------------------------------------------- +%%% Internal functions + check_all_breaks([{From,Case,T,Start}|Breaks],[{From,End}|Continued]) -> Diff = timer:now_diff(End,Start), diff --git a/lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE.erl new file mode 100644 index 0000000000..b1ae70a302 --- /dev/null +++ b/lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE.erl @@ -0,0 +1,58 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. 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(test_server_cover_SUITE). + +-export([all/1, init_per_suite/1, end_per_suite/1]). +-export([init_per_testcase/2, end_per_testcase/2]). +-export([tc1/1, tc2/1]). + +-include_lib("test_server/include/test_server.hrl"). + +all(suite) -> + [tc1,tc2]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_testcase(_Case,Config) -> + Dog = ?t:timetrap({minutes,10}), + [{watchdog, Dog}|Config]. + +end_per_testcase(_Case,Config) -> + Dog=?config(watchdog, Config), + ?t:timetrap_cancel(Dog), + ok. + + +%%%----------------------------------------------------------------- +%%% Test cases +tc1(Config) when is_list(Config) -> + cover_helper:foo(), + ok. + +tc2(Config) when is_list(Config) -> + cover_helper:bar(), + ok. + +%%%----------------------------------------------------------------- +%%% Internal functions + diff --git a/lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE_data/cover_helper.erl b/lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE_data/cover_helper.erl new file mode 100644 index 0000000000..6c74eb4e8a --- /dev/null +++ b/lib/test_server/test/test_server_SUITE_data/test_server_cover_SUITE_data/cover_helper.erl @@ -0,0 +1,4 @@ +-module(cover_helper). +-compile(export_all). +foo() -> ok. +bar() -> ok. diff --git a/lib/test_server/test/test_server_test_lib.erl b/lib/test_server/test/test_server_test_lib.erl index 4e89abf308..d466aa0110 100644 --- a/lib/test_server/test/test_server_test_lib.erl +++ b/lib/test_server/test/test_server_test_lib.erl @@ -83,14 +83,21 @@ start_slave(Config,_Level) -> post_end_per_testcase(_TC, Config, Return, State) -> Node = proplists:get_value(node, Config), - case test_server:is_cover() of - true -> - cover:flush(Node); - false -> - ok + Cover = test_server:is_cover(), + if Cover-> cover:flush(Node); + true -> ok end, + erlang:monitor_node(Node, true), slave:stop(Node), - + receive + {nodedown, Node} -> + if Cover -> cover:stop(Node); + true -> ok + end + after 5000 -> + erlang:monitor_node(Node, false), + receive {nodedown, Node} -> ok after 0 -> ok end %flush + end, {Return, State}. %% Parse an .suite log file diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index e2bcd37def..2967acf310 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -997,6 +997,22 @@ behaviour.") 1 'font-lock-function-name-face t)) "Font lock keyword highlighting a function header.") +(defface erlang-font-lock-exported-function-name-face + '((default (:inherit font-lock-function-name-face))) + "Face used for highlighting exported functions.") + +(defvar erlang-font-lock-exported-function-name-face + 'erlang-font-lock-exported-function-name-face) + +(defvar erlang-inhibit-exported-function-name-face nil + "Inhibit separate face for exported functions") + +(defvar erlang-font-lock-keywords-exported-function-header + (list + (list #'erlang-match-next-exported-function + 1 'erlang-font-lock-exported-function-name-face t)) + "Font lock keyword highlighting an exported function header.") + (defvar erlang-font-lock-keywords-int-bifs (list (list (concat erlang-int-bif-regexp "\\s-*(") @@ -1132,7 +1148,8 @@ There exists three levels of Font Lock keywords for Erlang: `erlang-font-lock-keywords-1' - Function headers and reserved keywords. `erlang-font-lock-keywords-2' - Bifs, guards and `single quotes'. `erlang-font-lock-keywords-3' - Variables, macros and records. - `erlang-font-lock-keywords-4' - Function names, Funs, LCs (not Atoms) + `erlang-font-lock-keywords-4' - Exported functions, Function names, + Funs, LCs (not Atoms). To use a specific level, please set the variable `font-lock-maximum-decoration' to the appropriate level. Note that the @@ -1174,6 +1191,7 @@ Example: (defvar erlang-font-lock-keywords-4 (append erlang-font-lock-keywords-3 + erlang-font-lock-keywords-exported-function-header erlang-font-lock-keywords-int-function-calls erlang-font-lock-keywords-ext-function-calls erlang-font-lock-keywords-fun-n @@ -3662,6 +3680,38 @@ In the future the list may contain more elements." str) (store-match-data md)))) +(defun erlang-match-next-exported-function (max) + "Returns non-nil if there is an exported function in the current +buffer between point and MAX." + (block nil + (while (and (not erlang-inhibit-exported-function-name-face) + (erlang-match-next-function max)) + (when (erlang-last-match-exported-p) + (return (match-data)))))) + +(defun erlang-match-next-function (max) + "Searches forward in current buffer for the next erlang function, +bounded by position MAX." + (re-search-forward erlang-defun-prompt-regexp max 'move-point)) + +(defun erlang-last-match-exported-p () + "Returns non-nil if match-data describes the name and arity of an +exported function." + (save-excursion + (save-match-data + (goto-char (match-beginning 1)) + (erlang-function-exported-p + (erlang-remove-quotes (erlang-get-function-name)) + (erlang-get-function-arity))))) + +(defun erlang-function-exported-p (name arity) + "Returns non-nil if function of name and arity is exported in current buffer." + (save-excursion + (let* ((old-match-data (match-data)) + (exports (erlang-get-export))) + (store-match-data old-match-data) + (member (cons name arity) exports)))) + ;;; Check module name diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl index 10f14b0a49..ab29d156aa 100644 --- a/lib/tools/src/cover.erl +++ b/lib/tools/src/cover.erl @@ -705,7 +705,9 @@ main_process_loop(State) -> remote_collect('_',Nodes,true), reply(From, ok), Nodes1 = State#main_state.nodes--Nodes, - main_process_loop(State#main_state{nodes=Nodes1}); + LostNodes1 = State#main_state.lost_nodes--Nodes, + main_process_loop(State#main_state{nodes=Nodes1, + lost_nodes=LostNodes1}); {From, {flush,Nodes}} -> remote_collect('_',Nodes,false), @@ -792,8 +794,15 @@ main_process_loop(State) -> {'DOWN', _MRef, process, {?SERVER,Node}, _Info} -> %% A remote cover_server is down, mark as lost - Nodes = State#main_state.nodes--[Node], - Lost = [Node|State#main_state.lost_nodes], + {Nodes,Lost} = + case lists:member(Node,State#main_state.nodes) of + true -> + N = State#main_state.nodes--[Node], + L = [Node|State#main_state.lost_nodes], + {N,L}; + false -> % node stopped + {State#main_state.nodes,State#main_state.lost_nodes} + end, main_process_loop(State#main_state{nodes=Nodes,lost_nodes=Lost}); {nodeup,Node} -> @@ -2087,30 +2096,40 @@ do_analyse_to_file(Module, OutFile, ErlFile, HTML) -> case file:open(OutFile, [write]) of {ok, OutFd} -> if HTML -> - io:format(OutFd, - "<html>\n" - "<head><title>~s</title></head>" - "<body bgcolor=white text=black>\n" - "<pre>\n", - [OutFile]); + Encoding = encoding(ErlFile), + Header = + ["<!DOCTYPE HTML PUBLIC " + "\"-//W3C//DTD HTML 3.2 Final//EN\">\n" + "<html>\n" + "<head>\n" + "<meta http-equiv=\"Content-Type\"" + " content=\"text/html; charset=", + Encoding,"\"/>\n" + "<title>",OutFile,"</title>\n" + "</head>" + "<body style='background-color: white;" + " color: black'>\n" + "<pre>\n"], + file:write(OutFd,Header); true -> ok end, %% Write some initial information to the output file {{Y,Mo,D},{H,Mi,S}} = calendar:local_time(), - io:format(OutFd, "File generated from ~s by COVER " - "~p-~s-~s at ~s:~s:~s~n", - [ErlFile, - Y, - string:right(integer_to_list(Mo), 2, $0), - string:right(integer_to_list(D), 2, $0), - string:right(integer_to_list(H), 2, $0), - string:right(integer_to_list(Mi), 2, $0), - string:right(integer_to_list(S), 2, $0)]), - io:format(OutFd, "~n" - "**************************************" - "**************************************" - "~n~n", []), + Timestamp = + io_lib:format("~p-~s-~s at ~s:~s:~s", + [Y, + string:right(integer_to_list(Mo), 2, $0), + string:right(integer_to_list(D), 2, $0), + string:right(integer_to_list(H), 2, $0), + string:right(integer_to_list(Mi), 2, $0), + string:right(integer_to_list(S), 2, $0)]), + file:write(OutFd, + ["File generated from ",ErlFile," by COVER ", + Timestamp,"\n\n" + "**************************************" + "**************************************" + "\n\n"]), print_lines(Module, InFd, OutFd, 1, HTML), @@ -2405,3 +2424,20 @@ pmap(Fun, [], [], Limit, Cnt, Acc) -> {'DOWN', _Ref, process, X, _} when is_pid(X) -> pmap(Fun, [], [], Limit, Cnt - 1, Acc) end. + +%%%----------------------------------------------------------------- +%%% Read encoding from source file +encoding(File) -> + Encoding = + case epp:read_encoding(File) of + none -> + epp:default_encoding(); + E -> + E + end, + html_encoding(Encoding). + +html_encoding(latin1) -> + "iso-8859-1"; +html_encoding(utf8) -> + "utf-8". diff --git a/lib/tools/src/lcnt.erl b/lib/tools/src/lcnt.erl index 989a661b75..70d62307c8 100644 --- a/lib/tools/src/lcnt.erl +++ b/lib/tools/src/lcnt.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl index 3bf1b44af8..57260a3869 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -23,7 +23,9 @@ init_per_group/2,end_per_group/2]). -export([start/1, compile/1, analyse/1, misc/1, stop/1, - distribution/1, reconnect/1, die_and_reconnect/1, export_import/1, + distribution/1, reconnect/1, die_and_reconnect/1, + dont_reconnect_after_stop/1, stop_node_after_disconnect/1, + export_import/1, otp_5031/1, eif/1, otp_5305/1, otp_5418/1, otp_6115/1, otp_7095/1, otp_8188/1, otp_8270/1, otp_8273/1, otp_8340/1]). @@ -47,6 +49,7 @@ all() -> undefined -> [start, compile, analyse, misc, stop, distribution, reconnect, die_and_reconnect, + dont_reconnect_after_stop, stop_node_after_disconnect, export_import, otp_5031, eif, otp_5305, otp_5418, otp_6115, otp_7095, otp_8188, otp_8270, otp_8273, otp_8340]; @@ -86,8 +89,11 @@ init_per_testcase(TC, Config) when TC =:= misc; init_per_testcase(_TestCase, Config) -> Config. -end_per_testcase(_TestCase, _Config) -> - %cover:stop(), +end_per_testcase(TestCase, _Config) -> + case lists:member(TestCase,[start,compile,analyse,misc]) of + true -> ok; + false -> cover:stop() + end, ok. start(suite) -> []; @@ -447,8 +453,8 @@ reconnect(Config) -> %% Disconnect and check that node is removed from main cover node net_kernel:disconnect(N1), + timer:sleep(500), % allow some to detect disconnect and for f:f2() call [] = cover:which_nodes(), - timer:sleep(500), % allow some time for the f:f2() call %% Do some add one module (b) and remove one module (a) code:purge(a), @@ -520,6 +526,94 @@ die_and_reconnect(Config) -> ?t:stop_node(N1), ok. +%% Test that a stopped node is not marked as lost, i.e. that it is not +%% reconnected if it is restarted (OTP-10638) +dont_reconnect_after_stop(Config) -> + DataDir = ?config(data_dir, Config), + ok = file:set_cwd(DataDir), + + {ok,f} = compile:file(f), + + NodeName = cover_SUITE_dont_reconnect_after_stop, + {ok,N1} = ?t:start_node(NodeName,peer, + [{args," -pa " ++ DataDir},{start_cover,false}]), + {ok,f} = cover:compile(f), + {ok,[N1]} = cover:start(nodes()), + + %% A call to check later + rpc:call(N1,f,f1,[]), + + %% Stop cover on the node, then terminate the node + cover:stop(N1), + rpc:call(N1,erlang,halt,[]), + [] = cover:which_nodes(), + + check_f_calls(1,0), + + %% Restart the node and check that cover does not reconnect + {ok,N1} = ?t:start_node(NodeName,peer, + [{args," -pa " ++ DataDir},{start_cover,false}]), + timer:sleep(300), + [] = cover:which_nodes(), + Beam = rpc:call(N1,code,which,[f]), + false = (Beam==cover_compiled), + + %% One more call... + rpc:call(N1,f,f1,[]), + cover:flush(N1), + + %% Ensure that the last call is not counted + check_f_calls(1,0), + + cover:stop(), + ?t:stop_node(N1), + ok. + +%% Test that a node which is stopped while it is marked as lost is not +%% reconnected if it is restarted (OTP-10638) +stop_node_after_disconnect(Config) -> + DataDir = ?config(data_dir, Config), + ok = file:set_cwd(DataDir), + + {ok,f} = compile:file(f), + + NodeName = cover_SUITE_stop_node_after_disconnect, + {ok,N1} = ?t:start_node(NodeName,peer, + [{args," -pa " ++ DataDir},{start_cover,false}]), + {ok,f} = cover:compile(f), + {ok,[N1]} = cover:start(nodes()), + + %% A call to check later + rpc:call(N1,f,f1,[]), + + %% Flush the node, then terminate the node to make it marked as lost + cover:flush(N1), + rpc:call(N1,erlang,halt,[]), + + check_f_calls(1,0), + + %% Stop cover on node + cover:stop(N1), + + %% Restart the node and check that cover does not reconnect + {ok,N1} = ?t:start_node(NodeName,peer, + [{args," -pa " ++ DataDir},{start_cover,false}]), + timer:sleep(300), + [] = cover:which_nodes(), + Beam = rpc:call(N1,code,which,[f]), + false = (Beam==cover_compiled), + + %% One more call... + rpc:call(N1,f,f1,[]), + cover:flush(N1), + + %% Ensure that the last call is not counted + check_f_calls(1,0), + + cover:stop(), + ?t:stop_node(N1), + ok. + export_import(suite) -> []; export_import(Config) when is_list(Config) -> ?line DataDir = ?config(data_dir, Config), @@ -908,7 +1002,7 @@ otp_8270(Config) when is_list(Config) -> ok. otp_8273(doc) -> - ["OTP-8270. Bug."]; + ["OTP-8273. Bug."]; otp_8273(suite) -> []; otp_8273(Config) when is_list(Config) -> Test = <<"-module(t). diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl index fd3e111d8d..cf49526156 100644 --- a/lib/tools/test/xref_SUITE.erl +++ b/lib/tools/test/xref_SUITE.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -2521,7 +2522,7 @@ otp_10192(doc) -> otp_10192(Conf) when is_list(Conf) -> PrivDir = ?privdir, {ok, _Pid} = xref:start(s), - Dir = filename:join(PrivDir, "�"), + Dir = filename:join(PrivDir, "ä"), ok = file:make_dir(Dir), {ok, []} = xref:add_directory(s, Dir), xref:stop(s), diff --git a/lib/wx/test/wx_basic_SUITE.erl b/lib/wx/test/wx_basic_SUITE.erl index 20115a41fb..e240bbcfe8 100644 --- a/lib/wx/test/wx_basic_SUITE.erl +++ b/lib/wx/test/wx_basic_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -128,7 +129,7 @@ wx_api(Config) -> ?m(true, wx:is_null(Wx)), Null = ?mr(wx_ref, wx:null()), ?m(true, wx:is_null(Null)), - Frame = ?mt(wxFrame, wxFrame:new(Wx, 1, "WX API: " ++ unicode:characters_to_list("������"))), + Frame = ?mt(wxFrame, wxFrame:new(Wx, 1, "WX API: " ++ unicode:characters_to_list("åäöÅÄÖ"))), ?m(false, wx:is_null(Frame)), ?m(wxFrame, wx:getObjectType(Frame)), Env = ?mr(wx_env, wx:get_env()), diff --git a/lib/xmerl/doc/src/motorcycles_dtd.txt b/lib/xmerl/doc/src/motorcycles_dtd.txt index bab0d563f0..62ad4ac5fe 100644 --- a/lib/xmerl/doc/src/motorcycles_dtd.txt +++ b/lib/xmerl/doc/src/motorcycles_dtd.txt @@ -15,4 +15,5 @@ <!ELEMENT date (#PCDATA)> <!ATTLIST bike year NMTOKEN #REQUIRED color NMTOKENS #REQUIRED - condition (useless | bad | serviceable | moderate | good | excellent | new | outstanding) "excellent" > + condition (useless | bad | serviceable | moderate | good | + excellent | new | outstanding) "excellent" > diff --git a/lib/xmerl/src/xmerl_scan.erl b/lib/xmerl/src/xmerl_scan.erl index 05431a5fd2..883153628c 100644 --- a/lib/xmerl/src/xmerl_scan.erl +++ b/lib/xmerl/src/xmerl_scan.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 @@ -461,7 +462,7 @@ hook(X, State) -> event(_X, S) -> S. -%% The acc/3 function can return either {Acc�, S'} or {Acc', Pos', S'}, +%% The acc/3 function can return either {Acc´, S'} or {Acc', Pos', S'}, %% where Pos' can be derived from X#xmlElement.pos, X#xmlText.pos, or %% X#xmlAttribute.pos (whichever is the current object type.) %% The acc/3 function is not allowed to redefine the type of object diff --git a/lib/xmerl/src/xmerl_xsd.erl b/lib/xmerl/src/xmerl_xsd.erl index 78d84d23a4..ffe227c671 100644 --- a/lib/xmerl/src/xmerl_xsd.erl +++ b/lib/xmerl/src/xmerl_xsd.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -849,7 +850,7 @@ element_content({import,S=#xsd_state{schema_name=ThisSchema, SchemaLocation = get_attribute_value(schemaLocation,I,absent), %% If SchemaLocation is absent, the identification of that schema %% is leaved to the instance, application or user, via the - %% mechanisms described ��4.3 in XML Schema Part 1. + %% mechanisms described §4.3 in XML Schema Part 1. S2 = process_external_schema_once(SchemaLocation,Namespace,S), {{import,[]},S2#xsd_state{schema_name=ThisSchema, diff --git a/lib/xmerl/test/xmerl_SUITE.erl b/lib/xmerl/test/xmerl_SUITE.erl index 55b6d1844c..e21355f877 100644 --- a/lib/xmerl/test/xmerl_SUITE.erl +++ b/lib/xmerl/test/xmerl_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -531,8 +532,8 @@ ticket_7430(Config) -> {xmlElement,a,a,[], {xmlNamespace,[],[]}, [],1,[], - [{xmlText,[{a,1}],1,[],"�",text}, - {xmlText,[{a,1}],2,[],"\n�",text}], + [{xmlText,[{a,1}],1,[],"é",text}, + {xmlText,[{a,1}],2,[],"\né",text}], [],_,undeclared} -> ok; _ -> |