aboutsummaryrefslogtreecommitdiffstats
path: root/lib/asn1/src/asn1ct_gen.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/asn1/src/asn1ct_gen.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/asn1/src/asn1ct_gen.erl')
-rw-r--r--lib/asn1/src/asn1ct_gen.erl2066
1 files changed, 2066 insertions, 0 deletions
diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl
new file mode 100644
index 0000000000..fefb92bb34
--- /dev/null
+++ b/lib/asn1/src/asn1ct_gen.erl
@@ -0,0 +1,2066 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-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(asn1ct_gen).
+
+-include("asn1_records.hrl").
+%%-compile(export_all).
+-export([pgen_exports/3,
+ pgen_hrl/4,
+ gen_head/3,
+ demit/1,
+ emit/1,
+ get_inner/1,type/1,def_to_tag/1,prim_bif/1,
+ type_from_object/1,
+ get_typefromobject/1,get_fieldcategory/2,
+ get_classfieldcategory/2,
+ list2name/1,
+ list2rname/1,
+ constructed_suffix/2,
+ unify_if_string/1,
+ gen_check_call/7,
+ get_constraint/2,
+ insert_once/2,
+ rt2ct_suffix/1,
+ rt2ct_suffix/0,
+ index2suffix/1,
+ get_record_name_prefix/0]).
+-export([pgen/4,
+ pgen_module/5,
+ mk_var/1,
+ un_hyphen_var/1]).
+-export([gen_encode_constructed/4,
+ gen_decode_constructed/4]).
+
+%% pgen(Erules, Module, TypeOrVal)
+%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
+%% .hrl file is only generated if necessary
+%% Erules = per | ber | ber_bin | per_bin
+%% Module = atom()
+%% TypeOrVal = {TypeList,ValueList}
+%% TypeList = ValueList = [atom()]
+
+pgen(OutFile,Erules,Module,TypeOrVal) ->
+ pgen_module(OutFile,Erules,Module,TypeOrVal,true).
+
+
+pgen_module(OutFile,Erules,Module,
+ TypeOrVal = {Types,_Values,_Ptypes,_Classes,_Objects,_ObjectSets},
+ Indent) ->
+ N2nConvEnums = [CName|| {n2n,CName} <- get(encoding_options)],
+ case N2nConvEnums -- Types of
+ [] ->
+ ok;
+ UnmatchedTypes ->
+ exit({"Non existing ENUMERATION types used in n2n option",
+ UnmatchedTypes})
+ end,
+ put(outfile,OutFile),
+ HrlGenerated = pgen_hrl(Erules,Module,TypeOrVal,Indent),
+ asn1ct_name:start(),
+ ErlFile = lists:concat([OutFile,".erl"]),
+ Fid = fopen(ErlFile,[write]),
+ put(gen_file_out,Fid),
+ gen_head(Erules,Module,HrlGenerated),
+ pgen_exports(Erules,Module,TypeOrVal),
+ pgen_dispatcher(Erules,Module,TypeOrVal),
+ pgen_info(),
+ pgen_typeorval(wrap_ber(Erules),Module,N2nConvEnums,TypeOrVal),
+ pgen_partial_incomplete_decode(Erules),
+% gen_vars(asn1_db:mod_to_vars(Module)),
+% gen_tag_table(AllTypes),
+ file:close(Fid),
+ io:format("--~p--~n",[{generated,ErlFile}]).
+
+
+pgen_typeorval(Erules,Module,N2nConvEnums,{Types,Values,_Ptypes,_Classes,Objects,ObjectSets}) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ pgen_types(Rtmod,Erules,N2nConvEnums,Module,Types),
+ pgen_values(Erules,Module,Values),
+ pgen_objects(Rtmod,Erules,Module,Objects),
+ pgen_objectsets(Rtmod,Erules,Module,ObjectSets),
+ case catch lists:member(der,get(encoding_options)) of
+ true ->
+ pgen_check_defaultval(Erules,Module);
+ _ -> ok
+ end,
+ pgen_partial_decode(Rtmod,Erules,Module).
+
+pgen_values(_,_,[]) ->
+ true;
+pgen_values(Erules,Module,[H|T]) ->
+ Valuedef = asn1_db:dbget(Module,H),
+ gen_value(Valuedef),
+ pgen_values(Erules,Module,T).
+
+pgen_types(_,_,_,Module,[]) ->
+ gen_value_match(Module),
+ true;
+pgen_types(Rtmod,Erules,N2nConvEnums,Module,[H|T]) ->
+ asn1ct_name:clear(),
+ Typedef = asn1_db:dbget(Module,H),
+ Rtmod:gen_encode(Erules,Typedef),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,Typedef),
+ case lists:member(H,N2nConvEnums) of
+ true ->
+ pgen_n2nconversion(Erules,Typedef);
+ _ ->
+ true
+ end,
+ pgen_types(Rtmod,Erules,N2nConvEnums,Module,T).
+
+pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATED',{NN1,NN2}}}}) ->
+ NN = NN1 ++ NN2,
+ pgen_name2numfunc(TypeName,NN),
+ pgen_num2namefunc(TypeName,NN);
+pgen_n2nconversion(_Erules,_) ->
+ true.
+
+pgen_name2numfunc(_TypeName,[]) ->
+ true;
+pgen_name2numfunc(TypeName,[{Atom,Number}]) ->
+ emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,".",nl,nl]);
+pgen_name2numfunc(TypeName,[{Atom,Number}|NNRest]) ->
+ emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]),
+ pgen_name2numfunc(TypeName,NNRest).
+
+pgen_num2namefunc(_TypeName,[]) ->
+ true;
+pgen_num2namefunc(TypeName,[{Atom,Number}]) ->
+ emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},".",nl,nl]);
+pgen_num2namefunc(TypeName,[{Atom,Number}|NNRest]) ->
+ emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]),
+ pgen_num2namefunc(TypeName,NNRest).
+
+pgen_objects(_,_,_,[]) ->
+ true;
+pgen_objects(Rtmod,Erules,Module,[H|T]) ->
+ asn1ct_name:clear(),
+ Typedef = asn1_db:dbget(Module,H),
+ Rtmod:gen_obj_code(Erules,Module,Typedef),
+ pgen_objects(Rtmod,Erules,Module,T).
+
+pgen_objectsets(_,_,_,[]) ->
+ true;
+pgen_objectsets(Rtmod,Erules,Module,[H|T]) ->
+ asn1ct_name:clear(),
+ TypeDef = asn1_db:dbget(Module,H),
+ Rtmod:gen_objectset_code(Erules,TypeDef),
+ pgen_objectsets(Rtmod,Erules,Module,T).
+
+pgen_check_defaultval(Erules,Module) ->
+ CheckObjects = ets:tab2list(check_functions),
+ case get(asndebug) of
+ true ->
+ FileName = lists:concat([Module,".table"]),
+ {ok,IoDevice} = file:open(FileName,[write]),
+ Fun =
+ fun(X)->
+ io:format(IoDevice,"~n~n************~n~n~p~n~n*****"
+ "********~n~n",[X])
+ end,
+ lists:foreach(Fun,CheckObjects),
+ file:close(IoDevice);
+ _ -> ok
+ end,
+ gen_check_defaultval(Erules,Module,CheckObjects).
+
+pgen_partial_decode(Rtmod,Erule,Module) when Erule == ber_bin_v2 ->
+ pgen_partial_inc_dec(Rtmod,Erule,Module),
+ pgen_partial_dec(Rtmod,Erule,Module);
+pgen_partial_decode(_,_,_) ->
+ ok.
+
+pgen_partial_inc_dec(Rtmod,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)]),
+ ok;
+% [] ->
+% ok;
+ ConfList ->
+ PatternLists=lists:map(fun({_,P}) -> P end,ConfList),
+ pgen_partial_inc_dec1(Rtmod,Erules,Module,PatternLists),
+ gen_partial_inc_dec_refed_funcs(Rtmod,Erules)
+ end.
+
+%% pgen_partial_inc_dec1 generates a function of the toptype in each
+%% of the partial incomplete decoded types.
+pgen_partial_inc_dec1(Rtmod,Erules,Module,[P|Ps]) ->
+ TopTypeName = asn1ct:partial_inc_dec_toptype(P),
+ TypeDef=asn1_db:dbget(Module,TopTypeName),
+ asn1ct_name:clear(),
+ asn1ct:update_gen_state(namelist,P),
+ asn1ct:update_gen_state(active,true),
+ asn1ct:update_gen_state(prefix,"dec-inc-"),
+ case asn1ct:maybe_saved_sindex(TopTypeName,P) of
+ I when is_integer(I),I > 0 ->
+% io:format("Index:~p~n",[I]),
+ asn1ct:set_current_sindex(I);
+ _I ->
+ asn1ct:set_current_sindex(0),
+% io:format("Index=~p~n",[_I]),
+ ok
+ end,
+ Rtmod:gen_decode(Erules,TypeDef),
+ gen_dec_part_inner_constr(Rtmod,Erules,TypeDef,[TopTypeName]),
+ pgen_partial_inc_dec1(Rtmod,Erules,Module,Ps);
+pgen_partial_inc_dec1(_,_,_,[]) ->
+ ok.
+
+gen_partial_inc_dec_refed_funcs(Rtmod,Erule) when Erule == ber_bin_v2 ->
+ case asn1ct:next_refed_func() of
+ [] ->
+ ok;
+ {#'Externaltypereference'{module=M,type=Name},Sindex,Pattern} ->
+ TypeDef = asn1_db:dbget(M,Name),
+ asn1ct:update_gen_state(namelist,Pattern),
+ asn1ct:set_current_sindex(Sindex),
+ Rtmod:gen_inc_decode(Erule,TypeDef),
+ gen_dec_part_inner_constr(Rtmod,Erule,TypeDef,[Name]),
+ gen_partial_inc_dec_refed_funcs(Rtmod,Erule);
+ {Name,Sindex,Pattern,Type} ->
+ TypeDef=#typedef{name=asn1ct_gen:list2name(Name),typespec=Type},
+ asn1ct:update_gen_state(namelist,Pattern),
+ asn1ct:set_current_sindex(Sindex),
+ Rtmod:gen_inc_decode(Erule,TypeDef),
+ gen_dec_part_inner_constr(Rtmod,Erule,TypeDef,Name),
+ gen_partial_inc_dec_refed_funcs(Rtmod,Erule)
+ end;
+gen_partial_inc_dec_refed_funcs(_,_) ->
+ ok.
+
+pgen_partial_dec(_Rtmod,Erules,_Module) ->
+ Type_pattern = asn1ct:get_gen_state_field(type_pattern),
+% io:format("Type_pattern: ~w~n",[Type_pattern]),
+ %% Get the typedef of the top type and follow into the choosen components until the last type/component.
+ pgen_partial_types(Erules,Type_pattern),
+ ok.
+
+pgen_partial_types(Erules,Type_pattern) ->
+ % until this functionality works on all back-ends
+ Options = get(encoding_options),
+ case lists:member(asn1config,Options) of
+ true ->
+ pgen_partial_types1(Erules,Type_pattern);
+ _ -> ok
+ end.
+
+
+pgen_partial_types1(Erules,[{FuncName,[TopType|RestTypes]}|Rest]) ->
+% emit([FuncName,"(Bytes) ->",nl]),
+ CurrMod = get(currmod),
+ TypeDef = asn1_db:dbget(CurrMod,TopType),
+ traverse_type_structure(Erules,TypeDef,RestTypes,FuncName,
+ TypeDef#typedef.name),
+ pgen_partial_types1(Erules,Rest);
+pgen_partial_types1(_,[]) ->
+ ok;
+pgen_partial_types1(_,undefined) ->
+ ok.
+
+%% traverse_type_structure searches the structure of TypeDef for next
+%% type/component in TypeList until the last one. For the last type in
+%% TypeList a decode function will be generated.
+traverse_type_structure(Erules,Type,[],FuncName,TopTypeName) ->
+ %% this is the selected type
+ Ctmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ TypeDef =
+ case Type of
+ #type{} ->
+ #typedef{name=TopTypeName,typespec=Type};
+ #typedef{} -> Type
+ end,
+ Ctmod:gen_decode_selected(Erules,TypeDef,FuncName); % what if Type is #type{}
+traverse_type_structure(Erules,#type{def=Def},[[N]],FuncName,TopTypeName)
+ when is_integer(N) -> % this case a decode of one of the elements in
+ % the SEQUENCE OF is required.
+ InnerType = asn1ct_gen:get_inner(Def),
+ case InnerType of
+ 'SEQUENCE OF' ->
+ {_,Type} = Def,
+ traverse_type_structure(Erules,Type,[],FuncName,TopTypeName);
+ WrongType ->
+ exit({error,{configuration_file_error,[N],"only for SEQUENCE OF components",WrongType}})
+ end;
+traverse_type_structure(Erules,Type,[[N]|Ts],FuncName,TopTypeName)
+ when is_integer(N) ->
+ traverse_type_structure(Erules,Type,Ts,FuncName,TopTypeName);
+traverse_type_structure(Erules,#type{def=Def},[T|Ts],FuncName,TopTypeName) ->
+ InnerType = asn1ct_gen:get_inner(Def),
+ case InnerType of
+ 'SET' ->
+ #'SET'{components=Components} = Def,
+ C = get_component(T,Components),
+ traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
+ FuncName,[T|TopTypeName]);
+ 'SEQUENCE' ->
+ #'SEQUENCE'{components=Components} = Def,
+ C = get_component(T,Components),
+ traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
+ FuncName,[T|TopTypeName]);
+ 'CHOICE' ->
+ {_,Components} = Def,
+ C = get_component(T,Components),
+ traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
+ FuncName,[T|TopTypeName]);
+ 'SEQUENCE OF' ->
+ {_,Type} = Def,
+ traverse_SO_type_structure(Erules,Type,[T|Ts],FuncName,
+ TopTypeName);
+ 'SET OF' ->
+ {_,Type} = Def,
+ traverse_SO_type_structure(Erules,Type,[T|Ts],FuncName,
+ TopTypeName);
+ #'Externaltypereference'{module=M,type=TName} ->
+ TypeDef = asn1_db:dbget(M,TName),
+ traverse_type_structure(Erules,TypeDef,[T|Ts],FuncName,
+ [TypeDef#typedef.name]);
+ _ ->
+ traverse_type_structure(Erules,Def,Ts,FuncName,[T|TopTypeName])
+ end;
+traverse_type_structure(Erules,#typedef{typespec=Def},[T|Ts],FuncName,
+ TopTypeName) ->
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ case InnerType of
+ 'SET' ->
+ #'SET'{components=Components} = Def#type.def,
+ C = get_component(T,Components),
+ traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
+ FuncName,[T|TopTypeName]);
+ 'SEQUENCE' ->
+ #'SEQUENCE'{components=Components} = Def#type.def,
+ C = get_component(T,Components),
+ traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
+ FuncName,[T|TopTypeName]);
+ 'CHOICE' ->
+ {_,Components} = Def#type.def,
+ C = get_component(T,Components),
+ traverse_type_structure(Erules,C#'ComponentType'.typespec,Ts,
+ FuncName,[T|TopTypeName]);
+ 'SEQUENCE OF' ->
+ {_,Type} = Def#type.def,
+ traverse_SO_type_structure(Erules,Type,[T|Ts],FuncName,
+ TopTypeName);
+ 'SET OF' ->
+ {_,Type} = Def#type.def,
+ traverse_SO_type_structure(Erules,Type,[T|Ts],FuncName,
+ TopTypeName);
+ #'Externaltypereference'{module=M,type=TName} ->
+ TypeDef = asn1_db:dbget(M,TName),
+ traverse_type_structure(Erules,TypeDef,[T|Ts],FuncName,
+ [TypeDef#typedef.name]);
+ _ -> %this may be a referenced type that shall be traversed or
+ %the selected type
+ traverse_type_structure(Erules,Def,Ts,FuncName,[T|TopTypeName])
+ end.
+
+traverse_SO_type_structure(Erules,Type,[N|Rest],FuncName,TopTypeName)
+ when is_integer(N) ->
+ traverse_type_structure(Erules,Type,Rest,FuncName,TopTypeName);
+traverse_SO_type_structure(Erules,Type,TypeList,FuncName,TopTypeName) ->
+ traverse_type_structure(Erules,Type,TypeList,FuncName,TopTypeName).
+
+get_component(Name,{C1,C2}) when is_list(C1),is_list(C2) ->
+ get_component(Name,C1++C2);
+get_component(Name,[C=#'ComponentType'{name=Name}|_Cs]) ->
+ C;
+get_component(Name,[_C|Cs]) ->
+ get_component(Name,Cs);
+get_component(Name,_) ->
+ throw({error,{asn1,{internal_error,Name}}}).
+
+%% generate code for all inner types that are called from the top type
+%% of the partial incomplete decode and are defined within the top
+%% type.Constructed subtypes deeper in the structure will be generated
+%% in turn after all top types have been generated.
+gen_dec_part_inner_constr(Rtmod,Erules,TypeDef,TypeName) ->
+ Def = TypeDef#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ case InnerType of
+ 'SET' ->
+ #'SET'{components=Components} = Def#type.def,
+ gen_dec_part_inner_types(Rtmod,Erules,Components,TypeName);
+ %% Continue generate the inner of each component
+ 'SEQUENCE' ->
+ #'SEQUENCE'{components=Components} = Def#type.def,
+ gen_dec_part_inner_types(Rtmod,Erules,Components,TypeName);
+ 'CHOICE' ->
+ {_,Components} = Def#type.def,
+ gen_dec_part_inner_types(Rtmod,Erules,Components,TypeName);
+ 'SEQUENCE OF' ->
+ %% this and next case must be the last component in the
+ %% partial decode chain here. Not likely that this occur.
+ {_,Type} = Def#type.def,
+ NameSuffix = constructed_suffix(InnerType,Type#type.def),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,[NameSuffix|TypeName],Type);
+%% gen_types(Erules,[NameSuffix|Typename],Type);
+ 'SET OF' ->
+ {_,Type} = Def#type.def,
+ NameSuffix = constructed_suffix(InnerType,Type#type.def),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,[NameSuffix|TypeName],Type);
+ _ ->
+ ok
+ end.
+
+gen_dec_part_inner_types(Rtmod,Erules,[ComponentType|Rest],TypeName) ->
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,TypeName,ComponentType),
+ gen_dec_part_inner_types(Rtmod,Erules,Rest,TypeName);
+gen_dec_part_inner_types(Rtmod,Erules,{Comps1,Comps2},TypeName)
+ when is_list(Comps1),is_list(Comps2) ->
+ gen_dec_part_inner_types(Rtmod,Erules,Comps1 ++ Comps2,TypeName);
+gen_dec_part_inner_types(_,_,[],_) ->
+ ok.
+
+
+pgen_partial_incomplete_decode(Erule) ->
+ case asn1ct:get_gen_state_field(active) of
+ true ->
+ pgen_partial_incomplete_decode1(Erule),
+ asn1ct:reset_gen_state();
+ _ ->
+ ok
+ end.
+pgen_partial_incomplete_decode1(ber_bin_v2) ->
+ case asn1ct:read_config_data(partial_incomplete_decode) of
+ undefined ->
+ ok;
+ Data ->
+ lists:foreach(fun emit_partial_incomplete_decode/1,Data)
+ end,
+ GeneratedFs= asn1ct:get_gen_state_field(gen_refed_funcs),
+% io:format("GeneratedFs :~n~p~n",[GeneratedFs]),
+ gen_part_decode_funcs(GeneratedFs,0);
+pgen_partial_incomplete_decode1(_) -> ok.
+
+emit_partial_incomplete_decode({FuncName,TopType,Pattern}) ->
+ TypePattern = asn1ct:get_gen_state_field(inc_type_pattern),
+ TPattern =
+ case lists:keysearch(FuncName,1,TypePattern) of
+ {value,{_,TP}} -> TP;
+ _ -> exit({error,{asn1_internal_error,exclusive_decode}})
+ end,
+ TopTypeName =
+ case asn1ct:maybe_saved_sindex(TopType,TPattern) of
+ I when is_integer(I),I>0 ->
+ lists:concat([TopType,"_",I]);
+ _ ->
+ atom_to_list(TopType)
+ end,
+ emit([{asis,FuncName},"(Bytes) ->",nl,
+ " decode_partial_incomplete('",TopTypeName,"',Bytes,",{asis,Pattern},").",nl]);
+emit_partial_incomplete_decode(D) ->
+ throw({error,{asn1,{"bad data in asn1config file",D}}}).
+
+gen_part_decode_funcs([Data={Name,_,_,Type}|GeneratedFs],N) ->
+ InnerType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} ->
+ OCFTType;
+ _ ->
+ get_inner(Type#type.def)
+ end,
+ WhatKind = type(InnerType),
+ TypeName=list2name(Name),
+ if
+ N > 0 -> emit([";",nl]);
+ true -> ok
+ end,
+ emit(["decode_inc_disp('",TypeName,"',Data) ->",nl]),
+ gen_part_decode_funcs(WhatKind,TypeName,Data),
+ gen_part_decode_funcs(GeneratedFs,N+1);
+gen_part_decode_funcs([_H|T],N) ->
+ gen_part_decode_funcs(T,N);
+gen_part_decode_funcs([],N) ->
+ if
+ N > 0 ->
+ emit([".",nl]);
+ true ->
+ ok
+ end.
+
+gen_part_decode_funcs(#'Externaltypereference'{module=M,type=T},
+ _TypeName,Data) ->
+ #typedef{typespec=TS} = asn1_db:dbget(M,T),
+ InnerType =
+ case TS#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} ->
+ OCFTType;
+ _ ->
+ get_inner(TS#type.def)
+ end,
+ WhatKind = type(InnerType),
+ gen_part_decode_funcs(WhatKind,[T],Data);
+gen_part_decode_funcs({constructed,bif},TypeName,
+ {_Name,parts,Tag,_Type}) ->
+ emit([" case Data of",nl,
+ " L when is_list(L) ->",nl,
+ " 'dec_",TypeName,"'(lists:map(fun(X)->element(1,?RT_BER:decode(X)) end,L),",{asis,Tag},");",nl,
+ " _ ->",nl,
+ " [Res] = 'dec_",TypeName,"'([Data],",{asis,Tag},"),",nl,
+ " Res",nl,
+ " end"]);
+gen_part_decode_funcs(WhatKind,_TypeName,{_Name,parts,_Tag,_Type}) ->
+ throw({error,{asn1,{"only SEQUENCE OF/SET OF may have the partial incomplete directive 'parts'.",WhatKind}}});
+gen_part_decode_funcs({constructed,bif},TypeName,
+ {_Name,undecoded,Tag,_Type}) ->
+ emit([" 'dec_",TypeName,"'(Data,",{asis,Tag},")"]);
+gen_part_decode_funcs({primitive,bif},_TypeName,
+ {_Name,undecoded,Tag,Type}) ->
+ % Argument no 6 is 0, i.e. bit 6 for primitive encoding.
+ asn1ct_gen_ber_bin_v2:gen_dec_prim(ber_bin_v2,Type,"Data",Tag,[],0,", mandatory, ");
+gen_part_decode_funcs(WhatKind,_TypeName,{_,Directive,_,_}) ->
+ throw({error,{asn1,{"Not implemented yet",WhatKind," partial incomplete directive:",Directive}}}).
+
+gen_types(Erules,Tname,{RootL1,ExtList,RootL2})
+ when is_list(RootL1), is_list(RootL2) ->
+ gen_types(Erules,Tname,RootL1),
+ gen_types(Erules,Tname,ExtList),
+ gen_types(Erules,Tname,RootL2);
+gen_types(Erules,Tname,{RootList,ExtList}) when is_list(RootList) ->
+ gen_types(Erules,Tname,RootList),
+ gen_types(Erules,Tname,ExtList);
+gen_types(Erules,Tname,[{'EXTENSIONMARK',_,_}|Rest]) ->
+ gen_types(Erules,Tname,Rest);
+gen_types(Erules,Tname,[ComponentType|Rest]) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Rtmod:gen_encode(Erules,Tname,ComponentType),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,Tname,ComponentType),
+ gen_types(Erules,Tname,Rest);
+gen_types(_,_,[]) ->
+ true;
+gen_types(Erules,Tname,Type) when is_record(Type,type) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Rtmod:gen_encode(Erules,Tname,Type),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,Tname,Type).
+
+gen_value_match(Module) ->
+ case get(value_match) of
+ {true,Module} ->
+ emit(["value_match([{Index,Cname}|Rest],Value) ->",nl,
+ " Value2 =",nl,
+ " case element(Index,Value) of",nl,
+ " {Cname,Val2} -> Val2;",nl,
+ " X -> X",nl,
+ " end,",nl,
+ " value_match(Rest,Value2);",nl,
+ "value_match([],Value) ->",nl,
+ " Value.",nl]);
+ _ -> ok
+ end,
+ put(value_match,undefined).
+
+gen_check_defaultval(Erules,Module,[{Name,Type}|Rest]) ->
+ gen_check_func(Name,Type),
+ gen_check_defaultval(Erules,Module,Rest);
+gen_check_defaultval(_,_,[]) ->
+ ok.
+
+gen_check_func(Name,FType = #type{def=Def}) ->
+ EncName = ensure_atom(Name),
+ emit({{asis,EncName},"(_V,asn1_DEFAULT) ->",nl," true;",nl}),
+ emit({{asis,EncName},"(V,V) ->",nl," true;",nl}),
+ emit({{asis,EncName},"(V,{_,V}) ->",nl," true;",nl}),
+ case Def of
+ {'SEQUENCE OF',Type} ->
+ gen_check_sof(Name,'SEQOF',Type);
+ {'SET OF',Type} ->
+ gen_check_sof(Name,'SETOF',Type);
+ #'SEQUENCE'{components=Components} ->
+ gen_check_sequence(Name,Components);
+ #'SET'{components=Components} ->
+ gen_check_sequence(Name,Components);
+ {'CHOICE',Components} ->
+ gen_check_choice(Name,Components);
+ #'Externaltypereference'{type=T} ->
+ emit({{asis,EncName},"(DefaultValue,Value) ->",nl}),
+ emit({" '",list2name([T,check]),"'(DefaultValue,Value).",nl});
+ MaybePrim ->
+ InnerType = get_inner(MaybePrim),
+ case type(InnerType) of
+ {primitive,bif} ->
+ emit({{asis,EncName},"(DefaultValue,Value) ->",nl," "}),
+ gen_prim_check_call(get_inner(InnerType),"DefaultValue","Value",
+ FType),
+ emit({".",nl,nl});
+ _ ->
+ throw({asn1_error,{unknown,type,MaybePrim}})
+ end
+ end.
+
+gen_check_sof(Name,SOF,Type) ->
+ EncName = ensure_atom(Name),
+ NewName = ensure_atom(list2name([sorted,Name])),
+ emit({{asis,EncName},"(V1,V2) ->",nl}),
+ emit({" ",{asis,NewName},"(lists:sort(V1),lists:sort(V2)).",nl,nl}),
+ emit({{asis,NewName},"([],[]) ->",nl," true;",nl}),
+ emit({{asis,NewName},"([DV|DVs],[V|Vs]) ->",nl," "}),
+ InnerType = get_inner(Type#type.def),
+ case type(InnerType) of
+ {primitive,bif} ->
+ gen_prim_check_call(get_inner(InnerType),"DV","V",Type),
+ emit({",",nl});
+ {constructed,bif} ->
+ emit([{asis,ensure_atom(list2name([SOF,Name]))},"(DV, V),",nl]);
+ #'Externaltypereference'{type=T} ->
+ emit([{asis,ensure_atom(list2name([T,check]))},"(DV,V),",nl]);
+ 'ASN1_OPEN_TYPE' ->
+ emit(["DV = V,",nl]);
+ _ ->
+ emit(["DV = V,",nl])
+ end,
+ emit({" ",{asis,NewName},"(DVs,Vs).",nl,nl}).
+
+gen_check_sequence(Name,Components) ->
+ emit([{asis,ensure_atom(Name)},"(DefaultValue,Value) ->",nl]),
+ gen_check_sequence(Name,Components,1).
+gen_check_sequence(Name,[#'ComponentType'{name=N,typespec=Type}|Cs],Num) ->
+ InnerType = get_inner(Type#type.def),
+ NthDefV = ["element(",Num+1,",DefaultValue)"],
+ NthV = ["element(",Num+1,",Value)"],
+ gen_check_func_call(Name,Type,InnerType,NthDefV,NthV,N),
+ case Cs of
+ [] ->
+ emit({".",nl,nl});
+ _ ->
+ emit({",",nl}),
+ gen_check_sequence(Name,Cs,Num+1)
+ end;
+gen_check_sequence(_,[],_) ->
+ ok.
+
+gen_check_choice(Name,CList=[#'ComponentType'{}|_Cs]) ->
+ emit([{asis,ensure_atom(Name)},"({Id,DefaultValue},{Id,Value}) ->",nl]),
+ emit([" case Id of",nl]),
+ gen_check_choice_components(Name,CList,1).
+
+gen_check_choice_components(_,[],_)->
+ ok;
+gen_check_choice_components(Name,[#'ComponentType'{name=N,typespec=Type}|
+ Cs],Num) ->
+ Ind6 = " ",
+ InnerType = get_inner(Type#type.def),
+ emit({Ind6,"'",N,"' ->",nl,Ind6}),
+ gen_check_func_call(Name,Type,InnerType,{var,"defaultValue"},
+ {var,"value"},N),
+ case Cs of
+ [] ->
+ emit({nl," end.",nl,nl});
+ _ ->
+ emit({";",nl}),
+ gen_check_choice_components(Name,Cs,Num+1)
+ end.
+
+gen_check_func_call(Name,Type,InnerType,DefVal,Val,N) ->
+ case type(InnerType) of
+ {primitive,bif} ->
+ emit(" "),
+ gen_prim_check_call(get_inner(InnerType),DefVal,Val,Type);
+ #'Externaltypereference'{type=T} ->
+ emit({" ",{asis,ensure_atom(list2name([T,check]))},"(",DefVal,",",Val,")"});
+ 'ASN1_OPEN_TYPE' ->
+ emit([" if",nl,
+ " ",DefVal," == ",Val," -> true;",nl,
+ " true -> throw({error,{asn1_open_type}})",nl,
+ " end",nl]);
+ {constructed,bif} ->
+ emit([" ",{asis,ensure_atom(list2name([N,Name]))},"(",DefVal,",",Val,")"]);
+ _ ->
+ emit([" if",nl,
+ " ",DefVal," == ",Val," -> true;",nl,
+ " true -> throw({error,{asn1_open_type}})",nl,
+ " end",nl])
+ end.
+
+
+%% VARIOUS GENERATOR STUFF
+%% *************************************************
+%%**************************************************
+
+mk_var(X) when is_atom(X) ->
+ list_to_atom(mk_var(atom_to_list(X)));
+
+mk_var([H|T]) ->
+ [H-32|T].
+
+%% Since hyphens are allowed in ASN.1 names, it may occur in a
+%% variable to. Turn a hyphen into a under-score sign.
+un_hyphen_var(X) when is_atom(X) ->
+ list_to_atom(un_hyphen_var(atom_to_list(X)));
+un_hyphen_var([45|T]) ->
+ [95|un_hyphen_var(T)];
+un_hyphen_var([H|T]) ->
+ [H|un_hyphen_var(T)];
+un_hyphen_var([]) ->
+ [].
+
+%% Generate value functions ***************
+%% ****************************************
+%% Generates a function 'V'/0 for each Value V defined in the ASN.1 module
+%% the function returns the value in an Erlang representation which can be
+%% used as input to the runtime encode functions
+
+gen_value(Value) when is_record(Value,valuedef) ->
+%% io:format(" ~w ",[Value#valuedef.name]),
+ emit({"'",Value#valuedef.name,"'() ->",nl}),
+ V = Value#valuedef.value,
+ emit([{asis,V},".",nl,nl]).
+
+gen_encode_constructed(Erules,Typename,InnerType,D) when is_record(D,type) ->
+
+ Rtmod = list_to_atom(lists:concat(["asn1ct_constructed_",erule(Erules)])),
+ case InnerType of
+ 'SET' ->
+ Rtmod:gen_encode_set(Erules,Typename,D),
+ #'SET'{components=Components} = D#type.def,
+ gen_types(Erules,Typename,Components);
+ 'SEQUENCE' ->
+ Rtmod:gen_encode_sequence(Erules,Typename,D),
+ #'SEQUENCE'{components=Components} = D#type.def,
+ gen_types(Erules,Typename,Components);
+ 'CHOICE' ->
+ Rtmod:gen_encode_choice(Erules,Typename,D),
+ {_,Components} = D#type.def,
+ gen_types(Erules,Typename,Components);
+ 'SEQUENCE OF' ->
+ Rtmod:gen_encode_sof(Erules,Typename,InnerType,D),
+ {_,Type} = D#type.def,
+ NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
+ gen_types(Erules,[NameSuffix|Typename],Type);
+ 'SET OF' ->
+ Rtmod:gen_encode_sof(Erules,Typename,InnerType,D),
+ {_,Type} = D#type.def,
+ NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
+ gen_types(Erules,[NameSuffix|Typename],Type);
+ _ ->
+ exit({nyi,InnerType})
+ end;
+gen_encode_constructed(Erules,Typename,InnerType,D)
+ when is_record(D,typedef) ->
+ gen_encode_constructed(Erules,Typename,InnerType,D#typedef.typespec).
+
+gen_decode_constructed(Erules,Typename,InnerType,D) when is_record(D,type) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_constructed_",erule(Erules)])),
+ asn1ct:step_in_constructed(), %% updates namelist for exclusive decode
+ case InnerType of
+ 'SET' ->
+ Rtmod:gen_decode_set(Erules,Typename,D);
+ 'SEQUENCE' ->
+ Rtmod:gen_decode_sequence(Erules,Typename,D);
+ 'CHOICE' ->
+ Rtmod:gen_decode_choice(Erules,Typename,D);
+ 'SEQUENCE OF' ->
+ Rtmod:gen_decode_sof(Erules,Typename,InnerType,D);
+ 'SET OF' ->
+ Rtmod:gen_decode_sof(Erules,Typename,InnerType,D);
+ _ ->
+ exit({nyi,InnerType})
+ end;
+
+
+gen_decode_constructed(Erules,Typename,InnerType,D) when is_record(D,typedef) ->
+ gen_decode_constructed(Erules,Typename,InnerType,D#typedef.typespec).
+
+
+pgen_exports(Erules,_Module,{Types,Values,_,_,Objects,ObjectSets}) ->
+ emit({"-export([encoding_rule/0]).",nl}),
+ case Types of
+ [] -> ok;
+ _ ->
+ emit({"-export([",nl}),
+ case Erules of
+ ber ->
+ gen_exports1(Types,"enc_",2);
+ ber_bin ->
+ gen_exports1(Types,"enc_",2);
+ ber_bin_v2 ->
+ gen_exports1(Types,"enc_",2);
+ _ ->
+ gen_exports1(Types,"enc_",1)
+ end,
+ emit({"-export([",nl}),
+ gen_exports1(Types,"dec_",2),
+ case Erules of
+ ber ->
+ emit({"-export([",nl}),
+ gen_exports1(Types,"dec_",3);
+ ber_bin ->
+ emit({"-export([",nl}),
+ gen_exports1(Types,"dec_",3);
+% ber_bin_v2 ->
+% emit({"-export([",nl}),
+% gen_exports1(Types,"dec_",2);
+ _ -> ok
+ end
+ end,
+ case [X || {n2n,X} <- get(encoding_options)] of
+ [] -> ok;
+ A2nNames ->
+ emit({"-export([",nl}),
+ gen_exports1(A2nNames,"name2num_",1),
+ emit({"-export([",nl}),
+ gen_exports1(A2nNames,"num2name_",1)
+ end,
+ case Values of
+ [] -> ok;
+ _ ->
+ emit({"-export([",nl}),
+ gen_exports1(Values,"",0)
+ end,
+ case Objects of
+ [] -> ok;
+ _ ->
+ case erule(Erules) of
+ per ->
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"enc_",3),
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"dec_",4);
+ ber_bin_v2 ->
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"enc_",3),
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"dec_",3);
+ _ ->
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"enc_",4),
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"dec_",4)
+ end
+ end,
+ case ObjectSets of
+ [] -> ok;
+ _ ->
+ emit({"-export([",nl}),
+ gen_exports1(ObjectSets,"getenc_",2),
+ emit({"-export([",nl}),
+ gen_exports1(ObjectSets,"getdec_",2)
+ end,
+ emit({"-export([info/0]).",nl}),
+ gen_partial_inc_decode_exports(),
+ gen_selected_decode_exports(),
+ emit({nl,nl}).
+
+gen_exports1([F1,F2|T],Prefix,Arity) ->
+ emit({"'",Prefix,F1,"'/",Arity,com,nl}),
+ gen_exports1([F2|T],Prefix,Arity);
+gen_exports1([Flast|_T],Prefix,Arity) ->
+ emit({"'",Prefix,Flast,"'/",Arity,nl,"]).",nl,nl}).
+
+gen_partial_inc_decode_exports() ->
+ case {asn1ct:read_config_data(partial_incomplete_decode),
+ asn1ct:get_gen_state_field(inc_type_pattern)} of
+ {undefined,_} ->
+ ok;
+ {_,undefined} ->
+ ok;
+ {Data,_} ->
+ gen_partial_inc_decode_exports(Data),
+ emit(["-export([decode_part/2]).",nl])
+ end.
+gen_partial_inc_decode_exports([]) ->
+ ok;
+gen_partial_inc_decode_exports([{Name,_,_}|Rest]) ->
+ emit(["-export([",Name,"/1"]),
+ gen_partial_inc_decode_exports1(Rest);
+gen_partial_inc_decode_exports([_|Rest]) ->
+ gen_partial_inc_decode_exports(Rest).
+
+gen_partial_inc_decode_exports1([]) ->
+ emit(["]).",nl]);
+gen_partial_inc_decode_exports1([{Name,_,_}|Rest]) ->
+ emit([", ",Name,"/1"]),
+ gen_partial_inc_decode_exports1(Rest);
+gen_partial_inc_decode_exports1([_|Rest]) ->
+ gen_partial_inc_decode_exports1(Rest).
+
+gen_selected_decode_exports() ->
+ case asn1ct:get_gen_state_field(type_pattern) of
+ undefined ->
+ ok;
+ L ->
+ gen_selected_decode_exports(L)
+ end.
+
+gen_selected_decode_exports([]) ->
+ ok;
+gen_selected_decode_exports([{FuncName,_}|Rest]) ->
+ emit(["-export([",FuncName,"/1"]),
+ gen_selected_decode_exports1(Rest).
+gen_selected_decode_exports1([]) ->
+ emit(["]).",nl,nl]);
+gen_selected_decode_exports1([{FuncName,_}|Rest]) ->
+ emit([",",nl," ",FuncName,"/1"]),
+ gen_selected_decode_exports1(Rest).
+
+pgen_dispatcher(Erules,_Module,{[],_Values,_,_,_Objects,_ObjectSets}) ->
+ emit(["encoding_rule() ->",nl]),
+ emit([{asis,Erules},".",nl,nl]);
+pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
+ emit(["-export([encode/2,decode/2,encode_disp/2,decode_disp/2]).",nl,nl]),
+ emit(["encoding_rule() ->",nl]),
+ emit([" ",{asis,Erules},".",nl,nl]),
+ NoFinalPadding = lists:member(no_final_padding,get(encoding_options)),
+ Call = case Erules of
+ per -> "?RT_PER:complete(encode_disp(Type,Data))";
+ per_bin -> "?RT_PER:complete(encode_disp(Type,Data))";
+ ber -> "encode_disp(Type,Data)";
+ ber_bin -> "encode_disp(Type,Data)";
+ ber_bin_v2 -> "encode_disp(Type,Data)";
+ uper_bin when NoFinalPadding == true ->
+ "?RT_PER:complete_NFP(encode_disp(Type,Data))";
+ uper_bin -> "?RT_PER:complete(encode_disp(Type,Data))"
+ end,
+ EncWrap = case Erules of
+ ber -> "wrap_encode(Bytes)";
+ _ -> "Bytes"
+ end,
+ emit(["encode(Type,Data) ->",nl,
+ "case catch ",Call," of",nl,
+ " {'EXIT',{error,Reason}} ->",nl,
+ " {error,Reason};",nl,
+ " {'EXIT',Reason} ->",nl,
+ " {error,{asn1,Reason}};",nl,
+ " {Bytes,_Len} ->",nl,
+ " {ok,",EncWrap,"};",nl]),
+ case Erules of
+ per ->
+ emit([" Bytes when is_binary(Bytes) ->",nl,
+ " {ok,binary_to_list(Bytes)};",nl,
+ " Bytes ->",nl,
+ " {ok,binary_to_list(list_to_binary(Bytes))}",nl,
+ " end.",nl,nl]);
+ _ ->
+ emit([" Bytes ->",nl,
+ " {ok,",EncWrap,"}",nl,
+ "end.",nl,nl])
+ end,
+
+% case Erules of
+% ber_bin_v2 ->
+% emit(["decode(Type,Data0) ->",nl]),
+% emit(["{Data,_RestBin} = ?RT_BER:decode(Data0",driver_parameter(),"),",nl]);
+% _ ->
+% emit(["decode(Type,Data) ->",nl])
+% end,
+
+ Return_rest = lists:member(undec_rest,get(encoding_options)),
+ Data = case {Erules,Return_rest} of
+ {ber_bin_v2,true} -> "Data0";
+ _ -> "Data"
+ end,
+
+ emit(["decode(Type,",Data,") ->",nl]),
+ DecAnonymous =
+ case {Erules,Return_rest} of
+ {ber_bin_v2,false} ->
+ io_lib:format("~s~s~s~n",
+ ["element(1,?RT_BER:decode(Data",
+ driver_parameter(),"))"]);
+ {ber_bin_v2,true} ->
+ emit(["{Data,Rest} = ?RT_BER:decode(Data0",
+ driver_parameter(),"),",nl]),
+ "Data";
+ _ ->
+ "Data"
+ end,
+ DecWrap = case Erules of
+ ber -> "wrap_decode(Data)";
+ ber_bin_v2 ->
+ DecAnonymous;
+ per -> "list_to_binary(Data)";
+ _ -> "Data"
+ end,
+
+ emit(["case catch decode_disp(Type,",DecWrap,") of",nl,
+ " {'EXIT',{error,Reason}} ->",nl,
+ " {error,Reason};",nl,
+ " {'EXIT',Reason} ->",nl,
+ " {error,{asn1,Reason}};",nl]),
+ case {Erules,Return_rest} of
+ {ber_bin_v2,false} ->
+ emit([" Result ->",nl,
+ " {ok,Result}",nl]);
+ {ber_bin_v2,true} ->
+ emit([" Result ->",nl,
+ " {ok,Result,Rest}",nl]);
+ {per,false} ->
+ emit([" {X,_Rest} ->",nl,
+ " {ok,if_binary2list(X)};",nl,
+ " {X,_Rest,_Len} ->",nl,
+ " {ok,if_binary2list(X)}",nl]);
+ {_,false} ->
+ emit([" {X,_Rest} ->",nl,
+ " {ok,X};",nl,
+ " {X,_Rest,_Len} ->",nl,
+ " {ok,X}",nl]);
+ {per,true} ->
+ emit([" {X,{_,Rest}} ->",nl,
+ " {ok,if_binary2list(X),Rest};",nl,
+ " {X,{_,Rest},_Len} ->",nl,
+ " {ok,if_binary2list(X),Rest};",nl,
+ " {X,Rest} ->",nl,
+ " {ok,if_binary2list(X),Rest};",nl,
+ " {X,Rest,_Len} ->",nl,
+ " {ok,if_binary2list(X),Rest}",nl]);
+ {per_bin,true} ->
+ emit([" {X,{_,Rest}} ->",nl,
+ " {ok,X,Rest};",nl,
+ " {X,{_,Rest},_Len} ->",nl,
+ " {ok,X,Rest};",nl,
+ " {X,Rest} ->",nl,
+ " {ok,X,Rest};",nl,
+ " {X,Rest,_Len} ->",nl,
+ " {ok,X,Rest}",nl]);
+ {uper_bin,true} ->
+ emit([" {X,{_,Rest}} ->",nl,
+ " {ok,X,Rest};",nl,
+ " {X,{_,Rest},_Len} ->",nl,
+ " {ok,X,Rest};",nl,
+ " {X,Rest} ->",nl,
+ " {ok,X,Rest};",nl,
+ " {X,Rest,_Len} ->",nl,
+ " {ok,X,Rest}",nl]);
+ _ ->
+ emit([" {X,Rest} ->",nl,
+ " {ok,X,Rest};",nl,
+ " {X,Rest,_Len} ->",nl,
+ " {ok,X,Rest}",nl])
+ end,
+ emit(["end.",nl,nl]),
+
+ case Erules of
+ per ->
+ emit(["if_binary2list(B) when is_binary(B) ->",nl,
+ " binary_to_list(B);",nl,
+ "if_binary2list(L) -> L.",nl,nl]);
+ _ ->
+ ok
+ end,
+
+ gen_decode_partial_incomplete(Erules),
+
+ case Erules of
+ ber ->
+ gen_dispatcher(Types,"encode_disp","enc_",",[]"),
+ gen_dispatcher(Types,"decode_disp","dec_",",mandatory");
+ ber_bin ->
+ gen_dispatcher(Types,"encode_disp","enc_",",[]"),
+ gen_dispatcher(Types,"decode_disp","dec_",",mandatory");
+ ber_bin_v2 ->
+ gen_dispatcher(Types,"encode_disp","enc_",""),
+ gen_dispatcher(Types,"decode_disp","dec_",""),
+ gen_partial_inc_dispatcher();
+ _PerOrPer_bin ->
+ gen_dispatcher(Types,"encode_disp","enc_",""),
+ gen_dispatcher(Types,"decode_disp","dec_",",mandatory")
+ end,
+ emit([nl]),
+
+ case Erules of
+ ber ->
+ gen_wrapper();
+ _ -> ok
+ end,
+ emit({nl,nl}).
+
+
+gen_decode_partial_incomplete(Erule) when Erule == ber;Erule==ber_bin;
+ Erule==ber_bin_v2 ->
+ case {asn1ct:read_config_data(partial_incomplete_decode),
+ asn1ct:get_gen_state_field(inc_type_pattern)} of
+ {undefined,_} ->
+ ok;
+ {_,undefined} ->
+ ok;
+ _ ->
+ case Erule of
+ ber_bin_v2 ->
+ EmitCaseClauses =
+ fun() ->
+ emit([" {'EXIT',{error,Reason}} ->",nl,
+ " {error,Reason};",nl,
+ " {'EXIT',Reason} ->",nl,
+ " {error,{asn1,Reason}};",nl,
+ " Result ->",nl,
+ " {ok,Result}",nl,
+ " end.",nl,nl])
+ end,
+ emit(["decode_partial_incomplete(Type,Data0,",
+ "Pattern) ->",nl]),
+ emit([" {Data,_RestBin} =",nl,
+ " ?RT_BER:decode_primitive_",
+ "incomplete(Pattern,Data0),",nl,
+ " case catch decode_partial_inc_disp(Type,",
+ "Data) of",nl]),
+ EmitCaseClauses(),
+ emit(["decode_part(Type,Data0) ->",nl]),
+ Driver =
+ case lists:member(driver,get(encoding_options)) of
+ true ->
+ ",driver";
+ _ -> ""
+ end,
+ emit([" case catch decode_inc_disp(Type,element(1,?RT_BER:decode(Data0",Driver,"))) of",nl]),
+% " {Data,_RestBin} = ?RT_BER:decode(Data0),",nl,
+% " case catch decode_inc_disp(Type,Data) of",nl]),
+ EmitCaseClauses();
+ _ -> ok % add later
+ end
+ end;
+gen_decode_partial_incomplete(_Erule) ->
+ ok.
+
+gen_partial_inc_dispatcher() ->
+ case {asn1ct:read_config_data(partial_incomplete_decode),
+ asn1ct:get_gen_state_field(inc_type_pattern)} of
+ {undefined,_} ->
+ ok;
+ {_,undefined} ->
+ ok;
+ {Data1,Data2} ->
+% io:format("partial_incomplete_decode: ~p~ninc_type_pattern: ~p~n",[Data,Data2]),
+ gen_partial_inc_dispatcher(Data1,Data2)
+ end.
+gen_partial_inc_dispatcher([{FuncName,TopType,_Pattern}|Rest],TypePattern) ->
+ TPattern =
+ case lists:keysearch(FuncName,1,TypePattern) of
+ {value,{_,TP}} -> TP;
+ _ -> exit({error,{asn1_internal_error,exclusive_decode}})
+ end,
+ FuncName2=asn1ct:maybe_rename_function(inc_disp,TopType,TPattern),
+ TopTypeName =
+ case asn1ct:maybe_saved_sindex(TopType,TPattern) of
+ I when is_integer(I),I>0 ->
+ lists:concat([TopType,"_",I]);
+ _ ->
+ atom_to_list(TopType)
+ end,
+ emit(["decode_partial_inc_disp('",TopTypeName,"',Data) ->",nl,
+ " ",{asis,list_to_atom(lists:concat(["dec-inc-",FuncName2]))},
+ "(Data);",nl]),
+ gen_partial_inc_dispatcher(Rest,TypePattern);
+gen_partial_inc_dispatcher([],_) ->
+ emit(["decode_partial_inc_disp(Type,_Data) ->",nl,
+ " exit({error,{asn1,{undefined_type,Type}}}).",nl]).
+
+driver_parameter() ->
+ Options = get(encoding_options),
+ case lists:member(driver,Options) of
+ true ->
+ ",driver";
+ _ -> ""
+ end.
+
+gen_wrapper() ->
+ emit(["wrap_encode(Bytes) when is_list(Bytes) ->",nl,
+ " binary_to_list(list_to_binary(Bytes));",nl,
+ "wrap_encode(Bytes) when is_binary(Bytes) ->",nl,
+ " binary_to_list(Bytes);",nl,
+ "wrap_encode(Bytes) -> Bytes.",nl,nl]),
+ emit(["wrap_decode(Bytes) when is_list(Bytes) ->",nl,
+ " list_to_binary(Bytes);",nl,
+ "wrap_decode(Bytes) -> Bytes.",nl]).
+
+gen_dispatcher([F1,F2|T],FuncName,Prefix,ExtraArg) ->
+ emit([FuncName,"('",F1,"',Data) -> '",Prefix,F1,"'(Data",ExtraArg,")",";",nl]),
+ gen_dispatcher([F2|T],FuncName,Prefix,ExtraArg);
+gen_dispatcher([Flast|_T],FuncName,Prefix,ExtraArg) ->
+ emit([FuncName,"('",Flast,"',Data) -> '",Prefix,Flast,"'(Data",ExtraArg,")",";",nl]),
+ emit([FuncName,"(","Type",",_Data) -> exit({error,{asn1,{undefined_type,Type}}}).",nl,nl,nl]).
+
+pgen_info() ->
+ emit(["info() ->",nl,
+ " case ?MODULE:module_info() of",nl,
+ " MI when is_list(MI) ->",nl,
+ " case lists:keysearch(attributes,1,MI) of",nl,
+ " {value,{_,Attributes}} when is_list(Attributes) ->",nl,
+ " case lists:keysearch(asn1_info,1,Attributes) of",nl,
+ " {value,{_,Info}} when is_list(Info) ->",nl,
+ " Info;",nl,
+ " _ ->",nl,
+ " []",nl,
+ " end;",nl,
+ " _ ->",nl,
+ " []",nl,
+ " end",nl,
+ " end.",nl]).
+
+open_hrl(OutFile,Module) ->
+ File = lists:concat([OutFile,".hrl"]),
+ Fid = fopen(File,[write]),
+ put(gen_file_out,Fid),
+ gen_hrlhead(Module).
+
+%% EMIT functions ************************
+%% ***************************************
+
+ % debug generation
+demit(Term) ->
+ case get(asndebug) of
+ true -> emit(Term);
+ _ ->true
+ end.
+
+ % always generation
+
+emit({external,_M,T}) ->
+ emit(T);
+
+emit({prev,Variable}) when is_atom(Variable) ->
+ emit({var,asn1ct_name:prev(Variable)});
+
+emit({next,Variable}) when is_atom(Variable) ->
+ emit({var,asn1ct_name:next(Variable)});
+
+emit({curr,Variable}) when is_atom(Variable) ->
+ emit({var,asn1ct_name:curr(Variable)});
+
+emit({var,Variable}) when is_atom(Variable) ->
+ [Head|V] = atom_to_list(Variable),
+ emit([Head-32|V]);
+
+emit({var,Variable}) ->
+ [Head|V] = Variable,
+ emit([Head-32|V]);
+
+emit({asis,What}) ->
+ format(get(gen_file_out),"~w",[What]);
+
+emit(nl) ->
+ nl(get(gen_file_out));
+
+emit(com) ->
+ emit(",");
+
+emit(tab) ->
+ put_chars(get(gen_file_out)," ");
+
+emit(What) when is_integer(What) ->
+ put_chars(get(gen_file_out),integer_to_list(What));
+
+emit(What) when is_list(What), is_integer(hd(What)) ->
+ put_chars(get(gen_file_out),What);
+
+emit(What) when is_atom(What) ->
+ put_chars(get(gen_file_out),atom_to_list(What));
+
+emit(What) when is_tuple(What) ->
+ emit_parts(tuple_to_list(What));
+
+emit(What) when is_list(What) ->
+ emit_parts(What);
+
+emit(X) ->
+ exit({'cant emit ',X}).
+
+emit_parts([]) -> true;
+emit_parts([H|T]) ->
+ emit(H),
+ emit_parts(T).
+
+format(undefined,X,Y) ->
+ io:format(X,Y);
+format(X,Y,Z) ->
+ io:format(X,Y,Z).
+
+nl(undefined) -> io:nl();
+nl(X) -> io:nl(X).
+
+put_chars(undefined,X) ->
+ io:put_chars(X);
+put_chars(Y,X) ->
+ io:put_chars(Y,X).
+
+fopen(F, Mode) ->
+ case file:open(F, Mode) of
+ {ok, Fd} ->
+ Fd;
+ {error, Reason} ->
+ io:format("** Can't open file ~p ~n", [F]),
+ exit({error,Reason})
+ end.
+
+pgen_hrl(Erules,Module,TypeOrVal,_Indent) ->
+ put(currmod,Module),
+ {Types,Values,Ptypes,_,_,_} = TypeOrVal,
+ Ret =
+ case pgen_hrltypes(Erules,Module,Ptypes++Types,0) of
+ 0 ->
+ case Values of
+ [] ->
+ 0;
+ _ ->
+ open_hrl(get(outfile),get(currmod)),
+ pgen_macros(Erules,Module,Values),
+ 1
+ end;
+ X ->
+ pgen_macros(Erules,Module,Values),
+ X
+ end,
+ case Ret of
+ 0 ->
+ 0;
+ Y ->
+ Fid = get(gen_file_out),
+ file:close(Fid),
+ io:format("--~p--~n",
+ [{generated,lists:concat([get(outfile),".hrl"])}]),
+ Y
+ end.
+
+pgen_macros(_,_,[]) ->
+ true;
+pgen_macros(Erules,Module,[H|T]) ->
+ Valuedef = asn1_db:dbget(Module,H),
+ gen_macro(Valuedef),
+ pgen_macros(Erules,Module,T).
+
+pgen_hrltypes(_,_,[],NumRecords) ->
+ NumRecords;
+pgen_hrltypes(Erules,Module,[H|T],NumRecords) ->
+% io:format("records = ~p~n",NumRecords),
+ Typedef = asn1_db:dbget(Module,H),
+ AddNumRecords = gen_record(Typedef,NumRecords),
+ pgen_hrltypes(Erules,Module,T,NumRecords+AddNumRecords).
+
+
+%% Generates a macro for value Value defined in the ASN.1 module
+gen_macro(Value) when is_record(Value,valuedef) ->
+ emit({"-define('",Value#valuedef.name,"', ",
+ {asis,Value#valuedef.value},").",nl}).
+
+%% Generate record functions **************
+%% Generates an Erlang record for each named and unnamed SEQUENCE and SET in the ASN.1
+%% module. If no SEQUENCE or SET is found there is no .hrl file generated
+
+
+gen_record(Tdef,NumRecords) when is_record(Tdef,typedef) ->
+ Name = [Tdef#typedef.name],
+ Type = Tdef#typedef.typespec,
+ gen_record(type,Name,Type,NumRecords);
+
+gen_record(Tdef,NumRecords) when is_record(Tdef,ptypedef) ->
+ Name = [Tdef#ptypedef.name],
+ Type = Tdef#ptypedef.typespec,
+ gen_record(ptype,Name,Type,NumRecords).
+
+gen_record(TorPtype,Name,[#'ComponentType'{name=Cname,typespec=Type}|T],Num) ->
+ Num2 = gen_record(TorPtype,[Cname|Name],Type,Num),
+ gen_record(TorPtype,Name,T,Num2);
+gen_record(TorPtype,Name,{Clist1,Clist2},Num)
+ when is_list(Clist1), is_list(Clist2) ->
+ gen_record(TorPtype,Name,Clist1++Clist2,Num);
+gen_record(TorPtype,Name,{Clist1,EClist,Clist2},Num)
+ when is_list(Clist1), is_list(EClist), is_list(Clist2) ->
+ gen_record(TorPtype,Name,Clist1++EClist++Clist2,Num);
+gen_record(TorPtype,Name,[_|T],Num) -> % skip EXTENSIONMARK
+ gen_record(TorPtype,Name,T,Num);
+gen_record(_TorPtype,_Name,[],Num) ->
+ Num;
+
+gen_record(TorPtype,Name,Type,Num) when is_record(Type,type) ->
+ Def = Type#type.def,
+ Rec = case Def of
+ Seq when is_record(Seq,'SEQUENCE') ->
+ case Seq#'SEQUENCE'.pname of
+ false ->
+ {record,Seq#'SEQUENCE'.components};
+%% _Pname when TorPtype == type ->
+%% false;
+ _ ->
+ {record,Seq#'SEQUENCE'.components}
+ end;
+ Set when is_record(Set,'SET') ->
+ case Set#'SET'.pname of
+ false ->
+ {record,to_textual_order(Set#'SET'.components)};
+ _Pname when TorPtype == type ->
+ false;
+ _ ->
+ {record,to_textual_order(Set#'SET'.components)}
+ end;
+% {'SET',{_,_CompList}} ->
+% {record,_CompList};
+ {'CHOICE',_CompList} -> {inner,Def};
+ {'SEQUENCE OF',_CompList} -> {['SEQOF'|Name],Def};
+ {'SET OF',_CompList} -> {['SETOF'|Name],Def};
+ _ -> false
+ end,
+ case Rec of
+ false -> Num;
+ {record,CompList} ->
+ case Num of
+ 0 -> open_hrl(get(outfile),get(currmod));
+ _ -> true
+ end,
+ Prefix = get_record_name_prefix(),
+ emit({"-record('",Prefix,list2name(Name),"',{",nl}),
+ RootList = case CompList of
+ _ when is_list(CompList) ->
+ CompList;
+ {Rl,_} -> Rl;
+ {Rl1,_Ext,_Rl2} -> Rl1
+ end,
+ gen_record2(Name,'SEQUENCE',RootList),
+ NewCompList =
+ case CompList of
+ {CompList1,[]} ->
+ emit({"}). % with extension mark",nl,nl}),
+ CompList1;
+ {Tr,ExtensionList2} ->
+ case Tr of
+ [] -> true;
+ _ -> emit({",",nl})
+ end,
+ emit({"%% with extensions",nl}),
+ gen_record2(Name, 'SEQUENCE', ExtensionList2,
+ "", ext),
+ emit({"}).",nl,nl}),
+ Tr ++ ExtensionList2;
+ {Rootl1,Extl,Rootl2} ->
+ case Rootl1 of
+ [] -> true;
+ _ -> emit([",",nl])
+ end,
+ emit(["%% with extensions",nl]),
+ gen_record2(Name,'SEQUENCE',Extl,"",ext),
+ case Extl of
+ [_H|_] when Rootl2 /= [] -> emit([",",nl]);
+ _ -> ok
+ end,
+ emit(["%% end of extensions",nl]),
+ gen_record2(Name,'SEQUENCE',Rootl2,"",noext),
+ emit(["}).",nl,nl]),
+ Rootl1++Extl++Rootl2;
+ _ ->
+ emit({"}).",nl,nl}),
+ CompList
+ end,
+ gen_record(TorPtype,Name,NewCompList,Num+1);
+ {inner,{'CHOICE', CompList}} ->
+ gen_record(TorPtype,Name,CompList,Num);
+ {NewName,{_, CompList}} ->
+ gen_record(TorPtype,NewName,CompList,Num)
+ end;
+gen_record(_,_,_,NumRecords) -> % skip CLASS etc for now.
+ NumRecords.
+
+gen_head(Erules,Mod,Hrl) ->
+ Options = get(encoding_options),
+ {Rtmac,Rtmod} = case Erules of
+ per ->
+ emit({"%% Generated by the Erlang ASN.1 PER-"
+ "compiler version:",asn1ct:vsn(),nl}),
+ {"RT_PER",?RT_PER_BIN};
+ ber ->
+ emit({"%% Generated by the Erlang ASN.1 BER-"
+ "compiler version:",asn1ct:vsn(),nl}),
+ {"RT_BER",?RT_BER_BIN};
+ per_bin ->
+ emit({"%% Generated by the Erlang ASN.1 BER-"
+ "compiler version, utilizing bit-syntax:",
+ asn1ct:vsn(),nl}),
+ %% temporary code to enable rt2ct optimization
+ case lists:member(optimize,Options) of
+ true -> {"RT_PER","asn1rt_per_bin_rt2ct"};
+ _ -> {"RT_PER",?RT_PER_BIN}
+ end;
+ ber_bin ->
+ emit({"%% Generated by the Erlang ASN.1 BER-"
+ "compiler version, utilizing bit-syntax:",
+ asn1ct:vsn(),nl}),
+ {"RT_BER",?RT_BER_BIN};
+ ber_bin_v2 ->
+ emit({"%% Generated by the Erlang ASN.1 BER_V2-"
+ "compiler version, utilizing bit-syntax:",
+ asn1ct:vsn(),nl}),
+ {"RT_BER","asn1rt_ber_bin_v2"};
+ uper_bin ->
+ emit(["%% Generated by the Erlang ASN.1 UNALIGNED"
+ " PER-compiler version, utilizing"
+ " bit-syntax:",
+ asn1ct:vsn(),nl]),
+ {"RT_PER","asn1rt_uper_bin"}
+ end,
+ emit({"%% Purpose: encoder and decoder to the types in mod ",Mod,nl,nl}),
+ emit({"-module('",Mod,"').",nl}),
+ put(currmod,Mod),
+ %emit({"-compile(export_all).",nl}),
+ case Hrl of
+ 0 -> true;
+ _ ->
+ emit({"-include(\"",Mod,".hrl\").",nl})
+ end,
+ emit(["-define('",Rtmac,"',",Rtmod,").",nl]),
+ emit(["-asn1_info([{vsn,'",asn1ct:vsn(),"'},",nl,
+ " {module,'",Mod,"'},",nl,
+ " {options,",io_lib:format("~w",[Options]),"}]).",nl,nl]).
+
+
+gen_hrlhead(Mod) ->
+ emit({"%% Generated by the Erlang ASN.1 compiler version:",asn1ct:vsn(),nl}),
+ emit({"%% Purpose: Erlang record definitions for each named and unnamed",nl}),
+ emit({"%% SEQUENCE and SET, and macro definitions for each value",nl}),
+ emit({"%% definition,in module ",Mod,nl,nl}),
+ emit({nl,nl}).
+
+gen_record2(Name,SeqOrSet,Comps) ->
+ gen_record2(Name,SeqOrSet,Comps,"",noext).
+
+gen_record2(_Name,_SeqOrSet,[],_Com,_Extension) ->
+ true;
+gen_record2(Name,SeqOrSet,[{'EXTENSIONMARK',_,_}|T],Com,Extension) ->
+ gen_record2(Name,SeqOrSet,T,Com,Extension);
+gen_record2(_Name,_SeqOrSet,[H],Com,Extension) ->
+ #'ComponentType'{name=Cname} = H,
+ emit(Com),
+ emit({asis,Cname}),
+ gen_record_default(H, Extension);
+gen_record2(Name,SeqOrSet,[H|T],Com, Extension) ->
+ #'ComponentType'{name=Cname} = H,
+ emit(Com),
+ emit({asis,Cname}),
+ gen_record_default(H, Extension),
+ gen_record2(Name,SeqOrSet,T,", ", Extension).
+
+gen_record_default(#'ComponentType'{prop='OPTIONAL'}, _)->
+ emit(" = asn1_NOVALUE");
+gen_record_default(#'ComponentType'{prop={'DEFAULT',_}}, _)->
+ emit(" = asn1_DEFAULT");
+gen_record_default(_, _) ->
+ true.
+
+%% May only be a list or a two-tuple.
+to_textual_order({Root,Ext}) ->
+ {to_textual_order(Root),Ext};
+to_textual_order(Cs={_R1,_Ext,_R2}) ->
+ Cs;
+to_textual_order(Cs=[#'ComponentType'{textual_order=undefined}|_]) ->
+ Cs;
+to_textual_order(Cs) when is_list(Cs) ->
+ lists:keysort(#'ComponentType'.textual_order,Cs).
+
+
+gen_check_call(TopType,Cname,Type,InnerType,WhatKind,DefaultValue,Element) ->
+ case WhatKind of
+ {primitive,bif} ->
+ gen_prim_check_call(InnerType,DefaultValue,Element,Type);
+ #'Externaltypereference'{module=M,type=T} ->
+ %% generate function call
+ Name = list2name([T,check]),
+ emit({"'",Name,"'(",DefaultValue,", ",Element,")"}),
+ %% insert in ets table and do look ahead check
+ Typedef = asn1_db:dbget(M,T),
+ RefType = Typedef#typedef.typespec,
+ InType = asn1ct_gen:get_inner(RefType#type.def),
+ case insert_once(check_functions,{Name,RefType}) of
+ true ->
+ lookahead_innertype([T],InType,RefType);
+ _ ->
+ ok
+ end;
+ {constructed,bif} ->
+ NameList = [Cname|TopType],
+ Name = list2name(NameList ++ [check]),
+ emit({"'",Name,"'(",DefaultValue,", ",Element,")"}),
+ ets:insert(check_functions,{Name,Type}),
+ %% Must look for check functions in InnerType,
+ %% that may be referenced or internal defined
+ %% constructed types not used elsewhere.
+ lookahead_innertype(NameList,InnerType,Type);
+ _ ->
+ %% Generate Dummy function call i.e. anything is accepted
+ emit(["fun() -> true end ()"])
+ end.
+
+gen_prim_check_call(PrimType,DefaultValue,Element,Type) ->
+ case unify_if_string(PrimType) of
+ 'BOOLEAN' ->
+ emit({"asn1rt_check:check_bool(",DefaultValue,", ",
+ Element,")"});
+ 'INTEGER' ->
+ NNL =
+ case Type#type.def of
+ {_,NamedNumberList} -> NamedNumberList;
+ _ -> []
+ end,
+ emit({"asn1rt_check:check_int(",DefaultValue,", ",
+ Element,", ",{asis,NNL},")"});
+ 'BIT STRING' ->
+ {_,NBL} = Type#type.def,
+ emit({"asn1rt_check:check_bitstring(",DefaultValue,", ",
+ Element,", ",{asis,NBL},")"});
+ 'OCTET STRING' ->
+ emit({"asn1rt_check:check_octetstring(",DefaultValue,", ",
+ Element,")"});
+ 'NULL' ->
+ emit({"asn1rt_check:check_null(",DefaultValue,", ",
+ Element,")"});
+ 'OBJECT IDENTIFIER' ->
+ emit({"asn1rt_check:check_objectidentifier(",DefaultValue,
+ ", ",Element,")"});
+ 'RELATIVE-OID' ->
+ emit({"asn1rt_check:check_objectidentifier(",DefaultValue,
+ ", ",Element,")"});
+ 'ObjectDescriptor' ->
+ emit({"asn1rt_check:check_objectdescriptor(",DefaultValue,
+ ", ",Element,")"});
+ 'REAL' ->
+ emit({"asn1rt_check:check_real(",DefaultValue,
+ ", ",Element,")"});
+ 'ENUMERATED' ->
+ {_,Enumerations} = Type#type.def,
+ emit({"asn1rt_check:check_enum(",DefaultValue,
+ ", ",Element,", ",{asis,Enumerations},")"});
+ restrictedstring ->
+ emit({"asn1rt_check:check_restrictedstring(",DefaultValue,
+ ", ",Element,")"})
+ end.
+
+%% lokahead_innertype/3 traverses Type and checks if check functions
+%% have to be generated, i.e. for all constructed or referenced types.
+lookahead_innertype(Name,'SEQUENCE',Type) ->
+ Components = (Type#type.def)#'SEQUENCE'.components,
+ lookahead_components(Name,Components);
+lookahead_innertype(Name,'SET',Type) ->
+ Components = (Type#type.def)#'SET'.components,
+ lookahead_components(Name,Components);
+lookahead_innertype(Name,'CHOICE',Type) ->
+ {_,Components} = Type#type.def,
+ lookahead_components(Name,Components);
+lookahead_innertype(Name,'SEQUENCE OF',SeqOf) ->
+ lookahead_sof(Name,'SEQOF',SeqOf);
+lookahead_innertype(Name,'SET OF',SeqOf) ->
+ lookahead_sof(Name,'SETOF',SeqOf);
+lookahead_innertype(_Name,#'Externaltypereference'{module=M,type=T},_) ->
+ Typedef = asn1_db:dbget(M,T),
+ RefType = Typedef#typedef.typespec,
+ insert_once(check_functions,{list2name([T,check]),RefType}),
+ InType = asn1ct_gen:get_inner(RefType#type.def),
+ case type(InType) of
+ {constructed,bif} ->
+ lookahead_innertype([T],InType,RefType);
+ Ref = #'Externaltypereference'{} ->
+ lookahead_reference(Ref);
+ _ ->
+ ok
+ end;
+lookahead_innertype(_,_,_) ->
+ ok.
+
+lookahead_components(_,[]) -> ok;
+lookahead_components(Name,[C|Cs]) ->
+ #'ComponentType'{name=Cname,typespec=Type} = C,
+ InType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InType) of
+ {constructed,bif} ->
+ case insert_once(check_functions,
+ {list2name([Cname|Name] ++ [check]),Type}) of
+ true ->
+ lookahead_innertype([Cname|Name],InType,Type);
+ _ ->
+ ok
+ end;
+ #'Externaltypereference'{module=RefMod,type=RefName} ->
+ Typedef = asn1_db:dbget(RefMod,RefName),
+ RefType = Typedef#typedef.typespec,
+ case insert_once(check_functions,{list2name([RefName,check]),
+ RefType}) of
+ true ->
+ lookahead_innertype([RefName],InType,RefType);
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end,
+ lookahead_components(Name,Cs).
+
+lookahead_sof(Name,SOF,SOFType) ->
+ Type = case SOFType#type.def of
+ {_,_Type} -> _Type;
+ _Type -> _Type
+ end,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ %% this is if a constructed type is defined in
+ %% the SEQUENCE OF type
+ NameList = [SOF|Name],
+ insert_once(check_functions,
+ {list2name(NameList ++ [check]),Type}),
+ lookahead_innertype(NameList,InnerType,Type);
+ Ref = #'Externaltypereference'{} ->
+ lookahead_reference(Ref);
+ _ ->
+ ok
+ end.
+
+lookahead_reference(#'Externaltypereference'{module=M,type=T}) ->
+ Typedef = asn1_db:dbget(M,T),
+ RefType = Typedef#typedef.typespec,
+ InType = get_inner(RefType#type.def),
+ case insert_once(check_functions,
+ {list2name([T,check]),RefType}) of
+ true ->
+ lookahead_innertype([T],InType,RefType);
+ _ ->
+ ok
+ end.
+
+insert_once(Table,Object) ->
+ _Info = ets:info(Table),
+ case ets:lookup(Table,element(1,Object)) of
+ [] ->
+ ets:insert(Table,Object); %returns true
+ _ -> false
+ end.
+
+unify_if_string(PrimType) ->
+ case PrimType of
+ 'NumericString' ->
+ restrictedstring;
+ 'PrintableString' ->
+ restrictedstring;
+ 'TeletexString' ->
+ restrictedstring;
+ 'T61String' ->
+ restrictedstring;
+ 'VideotexString' ->
+ restrictedstring;
+ 'IA5String' ->
+ restrictedstring;
+ 'UTCTime' ->
+ restrictedstring;
+ 'GeneralizedTime' ->
+ restrictedstring;
+ 'GraphicString' ->
+ restrictedstring;
+ 'VisibleString' ->
+ restrictedstring;
+ 'GeneralString' ->
+ restrictedstring;
+ 'UniversalString' ->
+ restrictedstring;
+ 'BMPString' ->
+ restrictedstring;
+ 'UTF8String' ->
+ restrictedstring;
+ Other -> Other
+ end.
+
+
+
+
+
+get_inner(A) when is_atom(A) -> A;
+get_inner(Ext) when is_record(Ext,'Externaltypereference') -> Ext;
+get_inner(Tref) when is_record(Tref,typereference) -> Tref;
+get_inner({fixedtypevaluefield,_,Type}) ->
+ if
+ is_record(Type,type) ->
+ get_inner(Type#type.def);
+ true ->
+ get_inner(Type)
+ end;
+get_inner({typefield,TypeName}) ->
+ TypeName;
+get_inner(#'ObjectClassFieldType'{type=Type}) ->
+% get_inner(Type);
+ Type;
+get_inner(T) when is_tuple(T) ->
+ case element(1,T) of
+ Tuple when is_tuple(Tuple),element(1,Tuple) == objectclass ->
+ case catch(lists:last(element(2,T))) of
+ {valuefieldreference,FieldName} ->
+ get_fieldtype(element(2,Tuple),FieldName);
+ {typefieldreference,FieldName} ->
+ get_fieldtype(element(2,Tuple),FieldName);
+ {'EXIT',Reason} ->
+ throw({asn1,{'internal error in get_inner/1',Reason}})
+ end;
+ _ -> element(1,T)
+ end.
+
+
+
+
+
+type(X) when is_record(X,'Externaltypereference') ->
+ X;
+type(X) when is_record(X,typereference) ->
+ X;
+type('ASN1_OPEN_TYPE') ->
+ 'ASN1_OPEN_TYPE';
+type({fixedtypevaluefield,_Name,Type}) when is_record(Type,type) ->
+ type(get_inner(Type#type.def));
+type({typefield,_}) ->
+ 'ASN1_OPEN_TYPE';
+type(X) ->
+ %% io:format("asn1_types:type(~p)~n",[X]),
+ case catch type2(X) of
+ {'EXIT',_} ->
+ {notype,X};
+ Normal ->
+ Normal
+ end.
+
+type2(X) ->
+ case prim_bif(X) of
+ true ->
+ {primitive,bif};
+ false ->
+ case construct_bif(X) of
+ true ->
+ {constructed,bif};
+ false ->
+ {undefined,user}
+ end
+ end.
+
+prim_bif(X) ->
+ lists:member(X,['INTEGER' ,
+ 'ENUMERATED',
+ 'REAL',
+ 'OBJECT IDENTIFIER',
+ 'RELATIVE-OID',
+ 'ANY',
+ 'NULL',
+ 'BIT STRING' ,
+ 'OCTET STRING' ,
+ 'ObjectDescriptor',
+ 'NumericString',
+ 'TeletexString',
+ 'T61String',
+ 'VideotexString',
+ 'UTCTime',
+ 'GeneralizedTime',
+ 'GraphicString',
+ 'VisibleString',
+ 'GeneralString',
+ 'PrintableString',
+ 'IA5String',
+ 'UniversalString',
+ 'UTF8String',
+ 'BMPString',
+ 'ENUMERATED',
+ 'BOOLEAN']).
+
+construct_bif(T) ->
+ lists:member(T,['SEQUENCE' ,
+ 'SEQUENCE OF' ,
+ 'CHOICE' ,
+ 'SET' ,
+ 'SET OF']).
+
+def_to_tag(#tag{class=Class,number=Number}) ->
+ {Class,Number};
+def_to_tag(#'ObjectClassFieldType'{type=Type}) ->
+ case Type of
+ T when is_tuple(T),element(1,T)==fixedtypevaluefield ->
+ {'UNIVERSAL',get_inner(Type)};
+ _ ->
+ []
+ end;
+def_to_tag(Def) ->
+ {'UNIVERSAL',get_inner(Def)}.
+
+
+%% Information Object Class
+
+type_from_object(X) ->
+ case (catch lists:last(element(2,X))) of
+ {'EXIT',_} ->
+ {notype,X};
+ Normal ->
+ Normal
+ end.
+
+
+get_fieldtype([],_FieldName)->
+ {no_type,no_name};
+get_fieldtype([Field|Rest],FieldName) ->
+ case element(2,Field) of
+ FieldName ->
+ case element(1,Field) of
+ fixedtypevaluefield ->
+ {element(1,Field),FieldName,element(3,Field)};
+ _ ->
+ {element(1,Field),FieldName}
+ end;
+ _ ->
+ get_fieldtype(Rest,FieldName)
+ end.
+
+get_fieldcategory([],_FieldName) ->
+ no_cat;
+get_fieldcategory([Field|Rest],FieldName) ->
+ case element(2,Field) of
+ FieldName ->
+ element(1,Field);
+ _ ->
+ get_fieldcategory(Rest,FieldName)
+ end.
+
+get_typefromobject(Type) when is_record(Type,type) ->
+ case Type#type.def of
+ {{objectclass,_,_},TypeFrObj} when is_list(TypeFrObj) ->
+ {_,FieldName} = lists:last(TypeFrObj),
+ FieldName;
+ _ ->
+ {no_field}
+ end.
+
+get_classfieldcategory(Type,FieldName) ->
+ case (catch Type#type.def) of
+ {{obejctclass,Fields,_},_} ->
+ get_fieldcategory(Fields,FieldName);
+ {'EXIT',_} ->
+ no_cat;
+ _ ->
+ no_cat
+ end.
+%% Information Object Class
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Convert a list of name parts to something that can be output by emit
+%%
+%% used to output function names in generated code.
+
+list2name(L) ->
+ NewL = list2name1(L),
+ lists:concat(lists:reverse(NewL)).
+
+list2name1([{ptype,H1},H2|T]) ->
+ [H1,"_",list2name([H2|T])];
+list2name1([H1,H2|T]) ->
+ [H1,"_",list2name([H2|T])];
+list2name1([{ptype,H}|_T]) ->
+ [H];
+list2name1([H|_T]) ->
+ [H];
+list2name1([]) ->
+ [].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Convert a list of name parts to something that can be output by emit
+%% stops at {ptype,Pname} i.e Pname whill be the first part of the name
+%% used to output record names in generated code.
+
+list2rname(L) ->
+ NewL = list2rname1(L),
+ lists:concat(lists:reverse(NewL)).
+
+list2rname1([{ptype,H1},_H2|_T]) ->
+ [H1];
+list2rname1([H1,H2|T]) ->
+ [H1,"_",list2name([H2|T])];
+list2rname1([{ptype,H}|_T]) ->
+ [H];
+list2rname1([H|_T]) ->
+ [H];
+list2rname1([]) ->
+ [].
+
+
+
+constructed_suffix(_,#'SEQUENCE'{pname=Ptypename}) when Ptypename =/= false ->
+ {ptype, Ptypename};
+constructed_suffix(_,#'SET'{pname=Ptypename}) when Ptypename =/= false ->
+ {ptype,Ptypename};
+constructed_suffix('SEQUENCE OF',_) ->
+ 'SEQOF';
+constructed_suffix('SET OF',_) ->
+ 'SETOF'.
+
+erule(ber) ->
+ ber;
+erule(ber_bin) ->
+ ber;
+erule(ber_bin_v2) ->
+ ber_bin_v2;
+erule(per) ->
+ per;
+erule(per_bin) ->
+ per;
+erule(uper_bin) ->
+ per.
+
+wrap_ber(ber) ->
+ ber_bin;
+wrap_ber(Erule) ->
+ Erule.
+
+rt2ct_suffix() ->
+ Options = get(encoding_options),
+ case {lists:member(optimize,Options),lists:member(per_bin,Options)} of
+ {true,true} -> "_rt2ct";
+ _ -> ""
+ end.
+rt2ct_suffix(per_bin) ->
+ Options = get(encoding_options),
+ case lists:member(optimize,Options) of
+ true -> "_rt2ct";
+ _ -> ""
+ end;
+rt2ct_suffix(_) -> "".
+
+index2suffix(0) ->
+ "";
+index2suffix(N) ->
+ lists:concat(["_",N]).
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V;
+ {value,Cnstr} ->
+ Cnstr
+ end.
+
+ensure_atom(Atom) when is_atom(Atom) ->
+ Atom;
+ensure_atom(List) when is_list(List) ->
+ list_to_atom(List).
+
+get_record_name_prefix() ->
+ case lists:keysearch(record_name_prefix,1,get(encoding_options)) of
+ false ->
+ "";
+ {value,{_,Prefix}} ->
+ Prefix
+ end.