1%% vim: tabstop=8:shiftwidth=4
2%%
3%% %CopyrightBegin%
4%%
5%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
6%%
7%% Licensed under the Apache License, Version 2.0 (the "License");
8%% you may not use this file except in compliance with the License.
9%% You may obtain a copy of the License at
10%%
11%%     http://www.apache.org/licenses/LICENSE-2.0
12%%
13%% Unless required by applicable law or agreed to in writing, software
14%% distributed under the License is distributed on an "AS IS" BASIS,
15%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16%% See the License for the specific language governing permissions and
17%% limitations under the License.
18%%
19%% %CopyrightEnd%
20%%
21%%
22-module(asn1ct).
23
24%% Compile Time functions for ASN.1 (e.g ASN.1 compiler).
25
26%% Public exports
27-export([compile/1, compile/2]).
28-export([test/1, test/2, test/3, value/2, value/3]).
29
30%% Application internal exports
31-export([compile_asn/3,compile_asn1/3,compile_py/3,compile/3,
32	 vsn/0,
33	 get_name_of_def/1,get_pos_of_def/1,
34	 unset_pos_mod/1]).
35-export([read_config_data/1,get_gen_state_field/1,
36	 partial_inc_dec_toptype/1,update_gen_state/2,
37	 get_tobe_refed_func/1,reset_gen_state/0,is_function_generated/1,
38	 generated_refed_func/1,next_refed_func/0,
39	 update_namelist/1,step_in_constructed/0,
40	 add_tobe_refed_func/1,add_generated_refed_func/1,
41	 maybe_rename_function/3,current_sindex/0,
42	 set_current_sindex/1,maybe_saved_sindex/2,
43	 parse_and_save/2,verbose/3,warning/3,warning/4,error/3,format_error/1]).
44-export([get_bit_string_format/0,use_legacy_types/0]).
45
46-include("asn1_records.hrl").
47-include_lib("stdlib/include/erl_compile.hrl").
48-include_lib("kernel/include/file.hrl").
49
50-import(asn1ct_gen_ber_bin_v2,[encode_tag_val/3,decode_class/1]).
51
52-ifndef(vsn).
53-define(vsn,"0.0.1").
54-endif.
55
56-define(unique_names,0).
57-define(dupl_uniquedefs,1).
58-define(dupl_equaldefs,2).
59-define(dupl_eqdefs_uniquedefs,?dupl_equaldefs bor ?dupl_uniquedefs).
60
61-define(CONSTRUCTED, 2#00100000).
62
63%% macros used for partial decode commands
64-define(CHOOSEN,choosen).
65-define(SKIP,skip).
66-define(SKIP_OPTIONAL,skip_optional).
67
68%% macros used for partial incomplete decode commands
69-define(MANDATORY,mandatory).
70-define(DEFAULT,default).
71-define(OPTIONAL,opt).
72-define(OPTIONAL_UNDECODED,opt_undec).
73-define(PARTS,parts).
74-define(UNDECODED,undec).
75-define(ALTERNATIVE,alt).
76-define(ALTERNATIVE_UNDECODED,alt_undec).
77-define(ALTERNATIVE_PARTS,alt_parts).
78
79%% Removed functions
80
81-removed({decode,'_',"use Mod:decode/2 instead"}).
82-removed({encode,'_',"use Mod:encode/2 instead"}).
83
84%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85%% This is the interface to the compiler
86
87compile(File) ->
88    compile(File,[]).
89
90compile(File, Options0) when is_list(Options0) ->
91    try translate_options(Options0) of
92	Options1 ->
93	    Options2 = includes(File,Options1),
94	    Includes = strip_includes(Options2),
95	    in_process(fun() -> compile_proc(File, Includes, Options2) end)
96    catch throw:Error ->
97	    Error
98    end.
99
100-record(st,
101	{file=[],
102	 files=[],
103	 inputmodules=[],
104	 code,
105	 opts=[],
106	 outfile,
107	 dbfile,
108	 includes=[],
109	 erule,
110	 error=none,
111	 run
112	}).
113
114compile_proc(File, Includes, Options) ->
115    Erule = get_rule(Options),
116    St = #st{opts=Options,includes=Includes,erule=Erule},
117    case input_file_type(File, Includes) of
118        {single_file, SuffixedFile} -> %% "e.g. "/tmp/File.asn"
119            compile1(SuffixedFile, St);
120        {multiple_files_file, SetBase, FileName} ->
121            case get_file_list(FileName, Includes) of
122                FileList when is_list(FileList) ->
123                    compile_set(SetBase, FileList, St);
124                Err ->
125                    Err
126            end;
127        Err = {input_file_error, _Reason} ->
128            {error, Err}
129    end.
130
131set_passes() ->
132    [{pass,scan_parse,fun set_scan_parse_pass/1},
133     {pass,merge,fun merge_pass/1}|common_passes()].
134
135single_passes() ->
136    [{pass,scan,fun scan_pass/1},
137     {pass,parse,fun parse_pass/1}|common_passes()].
138
139parse_and_save_passes() ->
140    [{pass,scan,fun scan_pass/1},
141     {pass,parse,fun parse_pass/1},
142     {pass,save,fun save_pass/1}].
143
144common_passes() ->
145    [{iff,parse,{pass,parse_listing,fun parse_listing/1}},
146     {pass,check,fun check_pass/1},
147     {iff,abs,{pass,abs_listing,fun abs_listing/1}},
148     {pass,generate,fun generate_pass/1},
149     {unless,noobj,{pass,compile,fun compile_pass/1}}].
150
151scan_pass(#st{file=File}=St) ->
152    case asn1ct_tok:file(File) of
153	{error,Reason} ->
154	    {error,St#st{error=Reason}};
155	Tokens when is_list(Tokens) ->
156	    {ok,St#st{code=Tokens}}
157    end.
158
159set_scan_parse_pass(#st{files=Files}=St) ->
160    try
161	L = set_scan_parse_pass_1(Files, St),
162	{ok,St#st{code=L}}
163    catch
164	throw:Error ->
165	    {error,St#st{error=Error}}
166    end.
167
168set_scan_parse_pass_1([F|Fs], #st{file=File}=St) ->
169    case asn1ct_tok:file(F) of
170	{error,Error} ->
171	    throw(Error);
172	Tokens when is_list(Tokens) ->
173	    case asn1ct_parser2:parse(File, Tokens) of
174		{ok,M} ->
175		    [M|set_scan_parse_pass_1(Fs, St)];
176		{error,Errors} ->
177		    throw(Errors)
178	    end
179    end;
180set_scan_parse_pass_1([], _) -> [].
181
182parse_pass(#st{file=File,code=Tokens}=St) ->
183    case asn1ct_parser2:parse(File, Tokens) of
184	{ok,M} ->
185	    {ok,St#st{code=M}};
186	{error,Errors} ->
187	    {error,St#st{error=Errors}}
188    end.
189
190merge_pass(#st{file=Base,code=Code}=St) ->
191    M = merge_modules(Code, Base),
192    {ok,St#st{code=M}}.
193
194check_pass(#st{code=M,file=File,includes=Includes,
195	       erule=Erule,dbfile=DbFile,opts=Opts,
196	       inputmodules=InputModules}=St) ->
197    start(Includes),
198    case asn1ct_check:storeindb(#state{erule=Erule,options=Opts}, M) of
199	ok ->
200	    Module = asn1_db:dbget(M#module.name, 'MODULE'),
201	    State = #state{mname=Module#module.name,
202			   module=Module#module{typeorval=[]},
203			   erule=Erule,
204			   inputmodules=InputModules,
205			   options=Opts,
206			   sourcedir=filename:dirname(File)},
207	    case asn1ct_check:check(State, Module#module.typeorval) of
208		{error,Reason} ->
209		    {error,St#st{error=Reason}};
210		{ok,NewTypeOrVal,GenTypeOrVal} ->
211		    NewM = Module#module{typeorval=NewTypeOrVal},
212		    asn1_db:dbput(NewM#module.name, 'MODULE', NewM),
213		    asn1_db:dbsave(DbFile, M#module.name),
214		    verbose("--~p--~n", [{generated,DbFile}], Opts),
215		    {ok,St#st{code={M,GenTypeOrVal}}}
216	    end;
217	{error,Reason} ->
218	    {error,St#st{error=Reason}}
219    end.
220
221save_pass(#st{code=M,erule=Erule,opts=Opts}=St) ->
222    ok = asn1ct_check:storeindb(#state{erule=Erule,options=Opts}, M),
223    {ok,St}.
224
225parse_listing(#st{code=Code,outfile=OutFile0}=St) ->
226    OutFile = OutFile0 ++ ".parse",
227    case file:write_file(OutFile, io_lib:format("~p\n", [Code])) of
228	ok ->
229	    done;
230	{error,Reason} ->
231	    Error = {write_error,OutFile,Reason},
232	    {error,St#st{error=[{structured_error,{OutFile0,none},?MODULE,Error}]}}
233    end.
234
235abs_listing(#st{code={M,_},outfile=OutFile}) ->
236    pretty2(M#module.name, OutFile++".abs"),
237    done.
238
239generate_pass(#st{code=Code,outfile=OutFile,erule=Erule,opts=Opts}=St0) ->
240    St = St0#st{code=undefined},		%Reclaim heap space
241    generate(Code, OutFile, Erule, Opts),
242    {ok,St}.
243
244compile_pass(#st{outfile=OutFile,opts=Opts0}=St) ->
245    asn1_db:dbstop(),				%Reclaim memory.
246    asn1ct_table:delete([renamed_defs,original_imports,automatic_tags]),
247    Opts = remove_asn_flags(Opts0),
248    case c:c(OutFile, Opts) of
249	{ok,_Module} ->
250	    {ok,St};
251	_ ->
252	    {error,St}
253    end.
254
255run_passes(Passes, #st{opts=Opts}=St) ->
256    Run = case lists:member(time, Opts) of
257	      false ->
258		  fun(_, Pass, S) -> Pass(S) end;
259	      true ->
260		  fun run_tc/3
261	  end,
262    run_passes_1(Passes, St#st{run=Run}).
263
264run_tc(Name, Fun, St) ->
265    Before0 = statistics(runtime),
266    Val = (catch Fun(St)),
267    After0 = statistics(runtime),
268    {Before_c, _} = Before0,
269    {After_c, _} = After0,
270    io:format("~-31s: ~10.2f s\n",
271	      [Name,(After_c-Before_c) / 1000]),
272    Val.
273
274run_passes_1([{unless,Opt,Pass}|Passes], #st{opts=Opts}=St) ->
275    case proplists:get_bool(Opt, Opts) of
276	false ->
277	    run_passes_1([Pass|Passes], St);
278	true ->
279	    run_passes_1(Passes, St)
280    end;
281run_passes_1([{iff,Opt,Pass}|Passes], #st{opts=Opts}=St) ->
282    case proplists:get_bool(Opt, Opts) of
283	true ->
284	    run_passes_1([Pass|Passes], St);
285	false ->
286	    run_passes_1(Passes, St)
287    end;
288run_passes_1([{pass,Name,Pass}|Passes], #st{run=Run}=St0)
289  when is_function(Pass, 1) ->
290    try Run(Name, Pass, St0) of
291	{ok,St} ->
292	    run_passes_1(Passes, St);
293	{error,#st{error=Errors}} ->
294	    {Structured,AllErrors} = clean_errors(Errors),
295	    print_structured_errors(Structured),
296	    {error,AllErrors};
297	done ->
298	    ok
299    catch
300	Class:Error:Stk ->
301	    io:format("Internal error: ~p:~p\n~p\n",
302		      [Class,Error,Stk]),
303	    {error,{internal_error,{Class,Error}}}
304    end;
305run_passes_1([], _St) ->
306    ok.
307
308clean_errors(Errors) when is_list(Errors) ->
309    F = fun({structured_error,_,_,_}) -> true;
310	   (_) -> false
311	end,
312    {Structured0,AdHoc} = lists:partition(F, Errors),
313    Structured = lists:sort(Structured0),
314    {Structured,Structured ++ AdHoc};
315clean_errors(AdHoc) -> {[],AdHoc}.
316
317print_structured_errors([_|_]=Errors) ->
318    _ = [io:format("~ts:~w: ~ts\n", [F,L,M:format_error(E)]) ||
319	    {structured_error,{F,L},M,E} <- Errors],
320    ok;
321print_structured_errors(_) -> ok.
322
323compile1(File, #st{opts=Opts}=St0) ->
324    compiler_verbose(File, Opts),
325    Passes = single_passes(),
326    Base = filename:rootname(filename:basename(File)),
327    OutFile = outfile(Base, "", Opts),
328    DbFile = outfile(Base, "asn1db", Opts),
329    St1 = St0#st{file=File,outfile=OutFile,dbfile=DbFile},
330    run_passes(Passes, St1).
331
332%%****************************************************************************%%
333%% functions dealing with compiling of several input files to one output file %%
334%%****************************************************************************%%
335
336%% compile_set/3 merges and compiles a number of asn1 modules
337%% specified in a .set.asn file to one .erl file.
338compile_set(SetBase, Files, #st{opts=Opts}=St0) ->
339    compiler_verbose(Files, Opts),
340    OutFile = outfile(SetBase, "", Opts),
341    DbFile = outfile(SetBase, "asn1db", Opts),
342    InputModules = [begin
343			F1 = filename:basename(F0),
344			F = filename:rootname(F1),
345			list_to_atom(F)
346		    end || F0 <- Files],
347    St = St0#st{file=SetBase,files=Files,outfile=OutFile,
348		dbfile=DbFile,inputmodules=InputModules},
349    Passes = set_passes(),
350    run_passes(Passes, St).
351
352compiler_verbose(What, Opts) ->
353    verbose("Erlang ASN.1 compiler ~s\n", [?vsn], Opts),
354    verbose("Compiling: ~p\n", [What], Opts),
355    verbose("Options: ~p\n", [Opts], Opts).
356
357%% merge_modules/2 -> returns a module record where the typeorval lists are merged,
358%% the exports lists are merged, the imports lists are merged when the
359%% elements come from other modules than the merge set, the tagdefault
360%% field gets the shared value if all modules have same tagging scheme,
361%% otherwise a tagging_error exception is thrown,
362%% the extensiondefault ...(not handled yet).
363merge_modules(ModuleList, CommonName) ->
364    NewModuleList = remove_name_collisions(ModuleList),
365    case asn1ct_table:size(renamed_defs) of
366        0 -> asn1ct_table:delete(renamed_defs);
367        _ -> ok
368    end,
369    save_imports(NewModuleList),
370    TypeOrVal = lists:append(lists:map(fun(X)->X#module.typeorval end,
371				       NewModuleList)),
372    InputMNameList = lists:map(fun(X)->X#module.name end,
373			       NewModuleList),
374    CExports = common_exports(NewModuleList),
375
376    ImportsModuleNameList = lists:map(fun(X)->
377					      {X#module.imports,
378					       X#module.name} end,
379				      NewModuleList),
380    %% ImportsModuleNameList: [{Imports,ModuleName},...]
381    %% Imports is a tuple {imports,[#'SymbolsFromModule'{},...]}
382    CImports = common_imports(ImportsModuleNameList,InputMNameList),
383    TagDefault = check_tagdefault(NewModuleList),
384    #module{name=CommonName,tagdefault=TagDefault,exports=CExports,
385	    imports=CImports,typeorval=TypeOrVal}.
386
387%% causes an exit if duplicate definition names exist in a module
388remove_name_collisions(Modules) ->
389    asn1ct_table:new(renamed_defs),
390    %% Name duplicates in the same module is not allowed.
391    lists:foreach(fun exit_if_nameduplicate/1,Modules),
392    %% Then remove duplicates in different modules and return the
393    %% new list of modules.
394    remove_name_collisions2(Modules,[]).
395
396%% For each definition in the first module in module list, find
397%% all definitons with same name and rename both definitions in
398%% the first module and in rest of modules
399remove_name_collisions2([M|Ms],Acc) ->
400    TypeOrVal = M#module.typeorval,
401    MName = M#module.name,
402    %% Test each name in TypeOrVal on all modules in Ms
403    {NewM,NewMs} = remove_name_collisions2(MName,TypeOrVal,Ms,[]),
404    remove_name_collisions2(NewMs,[M#module{typeorval=NewM}|Acc]);
405remove_name_collisions2([],Acc) ->
406    finished_warn_prints(),
407    Acc.
408
409%% For each definition in list of defs find definitions in (rest of)
410%% modules that have same name. If duplicate was found rename def.
411%% Test each name in [T|Ts] on all modules in Ms
412remove_name_collisions2(ModName,[T|Ts],Ms,Acc) ->
413    Name = get_name_of_def(T),
414    case discover_dupl_in_mods(Name,T,Ms,[],?unique_names) of
415	{_,?unique_names} -> % there was no name collision
416	    remove_name_collisions2(ModName,Ts,Ms,[T|Acc]);
417	{NewMs,?dupl_uniquedefs} -> % renamed defs in NewMs
418	    %% rename T
419	    NewT = set_name_of_def(ModName,Name,T), %rename def
420	    warn_renamed_def(ModName,get_name_of_def(NewT),Name),
421	    asn1ct_table:insert(renamed_defs,
422	                        {get_name_of_def(NewT), Name, ModName}),
423	    remove_name_collisions2(ModName,Ts,NewMs,[NewT|Acc]);
424	{NewMs,?dupl_equaldefs} -> % name duplicates, but identical defs
425	    %% keep name of T
426	    warn_kept_def(ModName,Name),
427	    remove_name_collisions2(ModName,Ts,NewMs,[T|Acc]);
428	{NewMs,?dupl_eqdefs_uniquedefs} ->
429	    %% keep name of T, renamed defs in NewMs
430	    warn_kept_def(ModName,Name),
431	    remove_name_collisions2(ModName,Ts,NewMs,[T|Acc])
432    end;
433remove_name_collisions2(_,[],Ms,Acc) ->
434    {Acc,Ms}.
435
436%% Name is the name of a definition. If a definition with the same name
437%% is found in the modules Ms the definition will be renamed and returned.
438discover_dupl_in_mods(Name,Def,[M=#module{name=N,typeorval=TorV}|Ms],
439			      Acc,AnyRenamed) ->
440    Fun = fun(T,RenamedOrDupl)->
441		  case {get_name_of_def(T),compare_defs(Def,T)} of
442		      {Name,not_equal} ->
443			  %% rename def
444			  NewT=set_name_of_def(N,Name,T),
445			  warn_renamed_def(N,get_name_of_def(NewT),Name),
446			  asn1ct_table:insert(renamed_defs,
447						          {get_name_of_def(NewT), Name, N}),
448			  {NewT,?dupl_uniquedefs bor RenamedOrDupl};
449		      {Name,equal} ->
450			  %% delete def
451			  warn_deleted_def(N,Name),
452			  {[],?dupl_equaldefs bor RenamedOrDupl};
453		      _ ->
454			  {T,RenamedOrDupl}
455		  end
456	  end,
457    {NewTorV,NewAnyRenamed} = lists:mapfoldl(Fun,AnyRenamed,TorV),
458    %% have to flatten the NewTorV to remove any empty list elements
459    discover_dupl_in_mods(Name,Def,Ms,
460			  [M#module{typeorval=lists:flatten(NewTorV)}|Acc],
461			  NewAnyRenamed);
462discover_dupl_in_mods(_,_,[],Acc,AnyRenamed) ->
463    {Acc,AnyRenamed}.
464
465warn_renamed_def(ModName,NewName,OldName) ->
466    maybe_first_warn_print(),
467    io:format("NOTICE: The ASN.1 definition in module ~p with name ~p has been renamed in generated module. New name is ~p.~n",[ModName,OldName,NewName]).
468
469warn_deleted_def(ModName,DefName) ->
470    maybe_first_warn_print(),
471    io:format("NOTICE: The ASN.1 definition in module ~p with name ~p has been deleted in generated module.~n",[ModName,DefName]).
472
473warn_kept_def(ModName,DefName) ->
474    maybe_first_warn_print(),
475    io:format("NOTICE: The ASN.1 definition in module ~p with name ~p has kept its name due to equal definition as duplicate.~n",[ModName,DefName]).
476
477maybe_first_warn_print() ->
478    case get(warn_duplicate_defs) of
479	undefined ->
480	    put(warn_duplicate_defs,true),
481	    io:format("~nDue to multiple occurrences of a definition name in "
482		      "multi-file compiled files:~n");
483	_ ->
484	    ok
485    end.
486finished_warn_prints() ->
487    put(warn_duplicate_defs,undefined).
488
489
490exit_if_nameduplicate(#module{typeorval=TorV}) ->
491    exit_if_nameduplicate(TorV);
492exit_if_nameduplicate([]) ->
493    ok;
494exit_if_nameduplicate([Def|Rest]) ->
495    Name=get_name_of_def(Def),
496    exit_if_nameduplicate2(Name,Rest),
497    exit_if_nameduplicate(Rest).
498
499exit_if_nameduplicate2(Name,Rest) ->
500    Pred=fun(Def)->
501		 case get_name_of_def(Def) of
502		     Name -> true;
503		     _ -> false
504		 end
505	 end,
506        case lists:any(Pred,Rest) of
507	true ->
508	    throw({error,{"more than one definition with same name",Name}});
509	_ ->
510	    ok
511    end.
512
513compare_defs(D1,D2) ->
514    compare_defs2(unset_pos_mod(D1),unset_pos_mod(D2)).
515compare_defs2(D,D) ->
516    equal;
517compare_defs2(_,_) ->
518    not_equal.
519
520unset_pos_mod(Def) when is_record(Def,typedef) ->
521    Def#typedef{pos=undefined};
522unset_pos_mod(Def) when is_record(Def,classdef) ->
523    Def#classdef{pos=undefined};
524unset_pos_mod(Def) when is_record(Def,valuedef) ->
525    Def#valuedef{pos=undefined,module=undefined};
526unset_pos_mod(Def) when is_record(Def,ptypedef) ->
527    Def#ptypedef{pos=undefined};
528unset_pos_mod(Def) when is_record(Def,pvaluedef) ->
529    Def#pvaluedef{pos=undefined};
530unset_pos_mod(Def) when is_record(Def,pvaluesetdef) ->
531    Def#pvaluesetdef{pos=undefined};
532unset_pos_mod(Def) when is_record(Def,pobjectdef) ->
533    Def#pobjectdef{pos=undefined};
534unset_pos_mod(Def) when is_record(Def,pobjectsetdef) ->
535    Def#pobjectsetdef{pos=undefined};
536unset_pos_mod(#'ComponentType'{} = Def) ->
537    Def#'ComponentType'{pos=undefined};
538unset_pos_mod(Def) -> Def.
539
540get_pos_of_def(#typedef{pos=Pos}) ->
541    Pos;
542get_pos_of_def(#classdef{pos=Pos}) ->
543    Pos;
544get_pos_of_def(#valuedef{pos=Pos}) ->
545    Pos;
546get_pos_of_def(#ptypedef{pos=Pos}) ->
547    Pos;
548get_pos_of_def(#pvaluedef{pos=Pos}) ->
549    Pos;
550get_pos_of_def(#pvaluesetdef{pos=Pos}) ->
551    Pos;
552get_pos_of_def(#pobjectdef{pos=Pos}) ->
553    Pos;
554get_pos_of_def(#pobjectsetdef{pos=Pos}) ->
555    Pos;
556get_pos_of_def(#'Externaltypereference'{pos=Pos}) ->
557    Pos;
558get_pos_of_def(#'Externalvaluereference'{pos=Pos}) ->
559    Pos;
560get_pos_of_def(_) ->
561    undefined.
562
563
564get_name_of_def(#typedef{name=Name}) ->
565    Name;
566get_name_of_def(#classdef{name=Name}) ->
567    Name;
568get_name_of_def(#valuedef{name=Name}) ->
569    Name;
570get_name_of_def(#ptypedef{name=Name}) ->
571    Name;
572get_name_of_def(#pvaluedef{name=Name}) ->
573    Name;
574get_name_of_def(#pvaluesetdef{name=Name}) ->
575    Name;
576get_name_of_def(#pobjectdef{name=Name}) ->
577    Name;
578get_name_of_def(#pobjectsetdef{name=Name}) ->
579    Name;
580get_name_of_def(_) ->
581    undefined.
582
583set_name_of_def(ModName,Name,OldDef) ->
584    NewName = list_to_atom(lists:concat([Name,ModName])),
585    case OldDef of
586	#typedef{} -> OldDef#typedef{name=NewName};
587	#classdef{} -> OldDef#classdef{name=NewName};
588	#valuedef{} -> OldDef#valuedef{name=NewName};
589	#ptypedef{} -> OldDef#ptypedef{name=NewName};
590	#pvaluedef{} -> OldDef#pvaluedef{name=NewName};
591	#pvaluesetdef{} -> OldDef#pvaluesetdef{name=NewName};
592	#pobjectdef{} -> OldDef#pobjectdef{name=NewName};
593	#pobjectsetdef{} -> OldDef#pobjectsetdef{name=NewName}
594    end.
595
596save_imports(ModuleList)->
597    Fun = fun(M) ->
598		  case M#module.imports of
599		      {_,[]} -> [];
600		      {_,I} ->
601			  {M#module.name,I}
602		  end
603	  end,
604    ImportsList = lists:map(Fun,ModuleList),
605    case lists:flatten(ImportsList) of
606	[] ->
607	    ok;
608	ImportsList2 ->
609	    asn1ct_table:new(original_imports),
610	    lists:foreach(fun(X) -> asn1ct_table:insert(original_imports, X) end,
611                      ImportsList2)
612    end.
613
614
615common_exports(ModuleList) ->
616    %% if all modules exports 'all' then export 'all',
617    %% otherwise export each typeorval name
618    case lists:filter(fun(X)->
619			      element(2,X#module.exports) /= all
620		      end,
621		      ModuleList) of
622	[]->
623	    {exports,all};
624	ModsWithExpList ->
625	    CExports1 =
626		lists:append(lists:map(fun(X)->element(2,X#module.exports) end,
627				       ModsWithExpList)),
628	    CExports2 = export_all(lists:subtract(ModuleList,ModsWithExpList)),
629	    {exports,CExports1++CExports2}
630    end.
631
632export_all([])->[];
633export_all(ModuleList) ->
634    ExpList =
635	lists:map(
636	  fun(M)->
637		  TorVL=M#module.typeorval,
638		  MName = M#module.name,
639		  lists:map(
640		    fun(Def)->
641			    case Def of
642				T when is_record(T,typedef)->
643				    #'Externaltypereference'{pos=0,
644							     module=MName,
645							     type=T#typedef.name};
646				V when is_record(V,valuedef) ->
647				    #'Externalvaluereference'{pos=0,
648							      module=MName,
649							      value=V#valuedef.name};
650				C when is_record(C,classdef) ->
651				    #'Externaltypereference'{pos=0,
652							     module=MName,
653							     type=C#classdef.name};
654				P when is_record(P,ptypedef) ->
655				    #'Externaltypereference'{pos=0,
656							     module=MName,
657							     type=P#ptypedef.name};
658				PV when is_record(PV,pvaluesetdef) ->
659				    #'Externaltypereference'{pos=0,
660							     module=MName,
661							     type=PV#pvaluesetdef.name};
662				PO when is_record(PO,pobjectdef) ->
663				    #'Externalvaluereference'{pos=0,
664							      module=MName,
665							      value=PO#pobjectdef.name}
666			    end
667		    end,
668		    TorVL)
669	  end,
670	  ModuleList),
671    lists:append(ExpList).
672
673%% common_imports/2
674%% IList is a list of tuples, {Imports,MName}, where Imports is the imports of
675%% the module with name MName.
676%% InputMNameL holds the names of all merged modules.
677%% Returns an import tuple with a list of imports that are external the merged
678%% set of modules.
679common_imports(IList,InputMNameL) ->
680    SetExternalImportsList = remove_in_set_imports(IList,InputMNameL,[]),
681    {imports,remove_import_doubles(SetExternalImportsList)}.
682
683check_tagdefault(ModList) ->
684    case have_same_tagdefault(ModList) of
685	{true,TagDefault}  -> TagDefault;
686	{false,TagDefault} ->
687        asn1ct_table:new(automatic_tags),
688	    save_automatic_tagged_types(ModList),
689	    TagDefault
690    end.
691
692have_same_tagdefault([#module{tagdefault=T}|Ms]) ->
693    have_same_tagdefault(Ms,{true,T}).
694
695have_same_tagdefault([],TagDefault) ->
696    TagDefault;
697have_same_tagdefault([#module{tagdefault=T}|Ms],TDefault={_,T}) ->
698    have_same_tagdefault(Ms,TDefault);
699have_same_tagdefault([#module{tagdefault=T1}|Ms],{_,T2}) ->
700    have_same_tagdefault(Ms,{false,rank_tagdef([T1,T2])}).
701
702rank_tagdef(L) ->
703    case lists:member('EXPLICIT',L) of
704	true -> 'EXPLICIT';
705	_ -> 'IMPLICIT'
706    end.
707
708save_automatic_tagged_types([])->
709    done;
710save_automatic_tagged_types([#module{tagdefault='AUTOMATIC',
711				     typeorval=TorV}|Ms]) ->
712    Fun =
713	fun(T) ->
714		asn1ct_table:insert(automatic_tags, {get_name_of_def(T)})
715	end,
716    lists:foreach(Fun,TorV),
717    save_automatic_tagged_types(Ms);
718save_automatic_tagged_types([_M|Ms]) ->
719    save_automatic_tagged_types(Ms).
720
721%% remove_in_set_imports/3 :
722%% input: list with tuples of each module's imports and module name
723%% respectively.
724%% output: one list with same format but each occured import from a
725%% module in the input set (IMNameL) is removed.
726remove_in_set_imports([{{imports,ImpL},_ModName}|Rest],InputMNameL,Acc) ->
727    NewImpL = remove_in_set_imports1(ImpL,InputMNameL,[]),
728    remove_in_set_imports(Rest,InputMNameL,NewImpL++Acc);
729remove_in_set_imports([],_,Acc) ->
730    lists:reverse(Acc).
731
732remove_in_set_imports1([I|Is],InputMNameL,Acc) ->
733    case I#'SymbolsFromModule'.module of
734	#'Externaltypereference'{type=MName} ->
735	    case lists:member(MName,InputMNameL) of
736		true ->
737		    remove_in_set_imports1(Is,InputMNameL,Acc);
738		false ->
739		    remove_in_set_imports1(Is,InputMNameL,[I|Acc])
740	    end;
741	_ ->
742	    remove_in_set_imports1(Is,InputMNameL,[I|Acc])
743    end;
744remove_in_set_imports1([],_,Acc) ->
745    lists:reverse(Acc).
746
747remove_import_doubles([]) ->
748    [];
749%% If several modules in the merge set imports symbols from
750%% the same external module it might be doubled.
751%% ImportList has #'SymbolsFromModule' elements
752remove_import_doubles(ImportList) ->
753    MergedImportList =
754	merge_symbols_from_module(ImportList,[]),
755    delete_double_of_symbol(MergedImportList,[]).
756
757merge_symbols_from_module([Imp|Imps],Acc) ->
758    #'Externaltypereference'{type=ModName} = Imp#'SymbolsFromModule'.module,
759    IfromModName =
760	lists:filter(
761	  fun(I)->
762		  case I#'SymbolsFromModule'.module of
763		      #'Externaltypereference'{type=ModName} ->
764			  true;
765		      #'Externalvaluereference'{value=ModName} ->
766			  true;
767		      _ -> false
768		  end
769	  end,
770	  Imps),
771    NewImps = lists:subtract(Imps,IfromModName),
772    NewImp =
773	Imp#'SymbolsFromModule'{
774	  symbols = lists:append(
775		      lists:map(fun(SL)->
776					SL#'SymbolsFromModule'.symbols
777				end,[Imp|IfromModName]))},
778    merge_symbols_from_module(NewImps,[NewImp|Acc]);
779merge_symbols_from_module([],Acc) ->
780    lists:reverse(Acc).
781
782delete_double_of_symbol([I|Is],Acc) ->
783    SymL=I#'SymbolsFromModule'.symbols,
784    NewSymL = delete_double_of_symbol1(SymL,[]),
785    delete_double_of_symbol(Is,[I#'SymbolsFromModule'{symbols=NewSymL}|Acc]);
786delete_double_of_symbol([],Acc) ->
787    Acc.
788
789delete_double_of_symbol1([TRef=#'Externaltypereference'{type=TrefName}|Rest],Acc)->
790    NewRest =
791	lists:filter(fun(S)->
792			     case S of
793				 #'Externaltypereference'{type=TrefName}->
794				     false;
795				 _ -> true
796			     end
797		     end,
798		     Rest),
799    delete_double_of_symbol1(NewRest,[TRef|Acc]);
800delete_double_of_symbol1([VRef=#'Externalvaluereference'{value=VName}|Rest],Acc) ->
801    NewRest =
802	lists:filter(fun(S)->
803			     case S of
804				 #'Externalvaluereference'{value=VName}->
805				     false;
806				 _ -> true
807			     end
808		     end,
809		     Rest),
810    delete_double_of_symbol1(NewRest,[VRef|Acc]);
811delete_double_of_symbol1([TRef={#'Externaltypereference'{type=MRef},
812				#'Externaltypereference'{type=TRef}}|Rest],
813			 Acc)->
814    NewRest =
815	lists:filter(
816	  fun(S)->
817		  case S of
818		      {#'Externaltypereference'{type=MRef},
819		       #'Externaltypereference'{type=TRef}}->
820			  false;
821		      _ -> true
822		  end
823	  end,
824	  Rest),
825    delete_double_of_symbol1(NewRest,[TRef|Acc]);
826delete_double_of_symbol1([],Acc) ->
827    Acc.
828
829
830%%***********************************
831
832generate({M,CodeTuple}, OutFile, EncodingRule, Options) ->
833    {Types,Values,Ptypes,Classes,Objects,ObjectSets} = CodeTuple,
834    Code = #abst{name=M#module.name,
835                 types=Types,values=Values,ptypes=Ptypes,
836                 classes=Classes,objects=Objects,objsets=ObjectSets},
837    setup_bit_string_format(Options),
838    setup_legacy_erlang_types(Options),
839    asn1ct_table:new(check_functions),
840
841    Gen = init_gen_record(EncodingRule, Options),
842
843    check_maps_option(Gen),
844
845    %% create decoding function names and taglists for partial decode
846    try
847        specialized_decode_prepare(Gen, M)
848    catch
849        throw:{error, Reason} ->
850            warning("Error in configuration file: ~n~p~n",
851                    [Reason], Options,
852                    "Error in configuration file")
853    end,
854
855    asn1ct_gen:pgen(OutFile, Gen, Code),
856    cleanup_bit_string_format(),
857    erase(tlv_format), % used in ber
858    erase(class_default_type),% used in ber
859    asn1ct_table:delete(check_functions),
860    ok.
861
862init_gen_record(EncodingRule, Options) ->
863    Erule = case EncodingRule of
864                uper -> per;
865                _ -> EncodingRule
866            end,
867    Der = proplists:get_bool(der, Options),
868    Jer = proplists:get_bool(jer, Options) andalso (EncodingRule =/= jer),
869    Aligned = EncodingRule =:= per,
870    RecPrefix = proplists:get_value(record_name_prefix, Options, ""),
871    MacroPrefix = proplists:get_value(macro_name_prefix, Options, ""),
872    Pack = case proplists:get_value(maps, Options, false) of
873               true -> map;
874               false -> record
875           end,
876    #gen{erule=Erule,der=Der,jer=Jer,aligned=Aligned,
877         rec_prefix=RecPrefix,macro_prefix=MacroPrefix,
878         pack=Pack,options=Options}.
879
880
881setup_legacy_erlang_types(Opts) ->
882    F = case lists:member(legacy_erlang_types, Opts) of
883	    false ->
884		case get_bit_string_format() of
885		    bitstring ->
886			false;
887		    compact ->
888			legacy_forced_info(compact_bit_string),
889			true;
890		    legacy ->
891			legacy_forced_info(legacy_bit_string),
892			true
893		end;
894	    true ->
895		true
896	end,
897    put(use_legacy_erlang_types, F).
898
899legacy_forced_info(Opt) ->
900    io:format("Info: The option 'legacy_erlang_types' "
901	      "is implied by the '~s' option.\n", [Opt]).
902
903use_legacy_types() ->
904    get(use_legacy_erlang_types).
905
906setup_bit_string_format(Opts) ->
907    Format = case {lists:member(compact_bit_string, Opts),
908		   lists:member(legacy_bit_string, Opts)} of
909		 {false,false} -> bitstring;
910		 {true,false} -> compact;
911		 {false,true} -> legacy;
912		 {true,true} ->
913		     Message = "Contradicting options given: "
914			 "compact_bit_string and legacy_bit_string",
915		     exit({error,{asn1,Message}})
916	     end,
917    put(bit_string_format, Format).
918
919cleanup_bit_string_format() ->
920    erase(bit_string_format).
921
922get_bit_string_format() ->
923    get(bit_string_format).
924
925check_maps_option(#gen{pack=map}) ->
926    case get_bit_string_format() of
927        bitstring ->
928            ok;
929        _ ->
930            Message1 = "The 'maps' option must not be combined with "
931                "'compact_bit_string' or 'legacy_bit_string'",
932            exit({error,{asn1,Message1}})
933    end,
934    case use_legacy_types() of
935        false ->
936            ok;
937        true ->
938            Message2 = "The 'maps' option must not be combined with "
939                "'legacy_erlang_types'",
940            exit({error,{asn1,Message2}})
941    end;
942check_maps_option(#gen{}) ->
943    ok.
944
945
946%% parse_and_save parses an asn1 spec and saves the unchecked parse
947%% tree in a data base file.
948%% Does not support multifile compilation files
949parse_and_save(Module,S) ->
950    Options = S#state.options,
951    SourceDir = S#state.sourcedir,
952    Includes = [I || {i,I} <- Options],
953    Erule = S#state.erule,
954    Maps = lists:member(maps, Options),
955    case get_input_file(Module, [SourceDir|Includes]) of
956	%% search for asn1 source
957	{file,SuffixedASN1source} ->
958	    Mtime = filelib:last_modified(SuffixedASN1source),
959	    case asn1_db:dbload(Module, Erule, Maps, Mtime) of
960		ok -> ok;
961		error -> parse_and_save1(S, SuffixedASN1source, Options)
962	    end;
963	Err when not Maps ->
964	    case asn1_db:dbload(Module) of
965		ok ->
966                    %% FIXME: This should be an error.
967		    warning("could not do a consistency check of the ~p file: no asn1 source file was found.~n",
968			    [lists:concat([Module,".asn1db"])],Options);
969		error ->
970		    ok
971	    end,
972	    {error,{asn1,input_file_error,Err}};
973        Err ->
974            %% Always fail directly when the 'maps' option is used.
975	    {error,{asn1,input_file_error,Err}}
976    end.
977
978parse_and_save1(#state{erule=Erule}, File, Options) ->
979    Ext = filename:extension(File),
980    Base = filename:basename(File, Ext),
981    DbFile = outfile(Base, "asn1db", Options),
982    St = #st{file=File,dbfile=DbFile,erule=Erule},
983    Passes = parse_and_save_passes(),
984    run_passes(Passes, St).
985
986get_input_file(Module,[]) ->
987    Module;
988get_input_file(Module,[I|Includes]) ->
989    case (catch input_file_type(filename:join([I,Module]))) of
990	{single_file,FileName} ->
991		    {file,FileName};
992	_ ->
993	    get_input_file(Module,Includes)
994    end.
995
996input_file_type(Name,I) ->
997   case input_file_type(Name) of
998       {error,_} -> input_file_type2(filename:basename(Name),I);
999       Err={input_file_error,_} -> Err;
1000       Res -> Res
1001   end.
1002input_file_type2(Name,[I|Is]) ->
1003    case input_file_type(filename:join([I,Name])) of
1004	{error,_} -> input_file_type2(Name,Is);
1005	Err={input_file_error,_} -> Err;
1006	Res -> Res
1007    end;
1008input_file_type2(Name,[]) ->
1009    input_file_type(Name).
1010
1011input_file_type([]) ->
1012    {empty_name,[]};
1013input_file_type(File) ->
1014    case filename:extension(File) of
1015	[] ->
1016	    case file:read_file_info(lists:concat([File,".asn1"])) of
1017		{ok,_FileInfo} ->
1018		    {single_file, lists:concat([File,".asn1"])};
1019		_ ->
1020		    case file:read_file_info(lists:concat([File,".asn"])) of
1021			{ok,_FileInfo} ->
1022			    {single_file, lists:concat([File,".asn"])};
1023			_ ->
1024			    case file:read_file_info(lists:concat([File,".py"])) of
1025				{ok,_FileInfo} ->
1026				    {single_file, lists:concat([File,".py"])};
1027				Error ->
1028				    Error
1029			    end
1030		    end
1031	    end;
1032	".asn1config" ->
1033	    case read_config_file_info(File, asn1_module) of
1034		{ok,Asn1Module} ->
1035		    input_file_type(Asn1Module);
1036		Error ->
1037		    Error
1038	    end;
1039	Asn1SFix ->
1040	    Base = filename:basename(File,Asn1SFix),
1041	    Ret =
1042		case filename:extension(Base) of
1043		    [] ->
1044			{single_file,File};
1045		    SetSFix when (SetSFix == ".set") ->
1046			{multiple_files_file,
1047			 list_to_atom(filename:basename(Base,SetSFix)),
1048			 File};
1049		    _Error ->
1050			throw({input_file_error,{'Bad input file',File}})
1051		end,
1052	    %% check that the file exists
1053	    case file:read_file_info(File) of
1054		{ok,_} -> Ret;
1055		Err -> Err
1056	    end
1057    end.
1058
1059get_file_list(File,Includes) ->
1060    case file:open(File,[read]) of
1061	{error,Reason} ->
1062	    {error,{File,file:format_error(Reason)}};
1063	{ok,Stream} ->
1064	    get_file_list1(Stream,filename:dirname(File),Includes,[])
1065    end.
1066
1067get_file_list1(Stream,Dir,Includes,Acc) ->
1068    Ret = io:get_line(Stream,''),
1069    case Ret of
1070	eof ->
1071	    ok = file:close(Stream),
1072	    lists:reverse(Acc);
1073	FileName ->
1074	    SuffixedNameList =
1075		case (catch input_file_type(filename:join([Dir,lists:delete($\n,FileName)]),Includes)) of
1076		    {empty_name,[]} -> [];
1077		    {single_file,Name} -> [Name];
1078		    {multiple_files_file,_,Name} ->
1079			get_file_list(Name,Includes);
1080		    _Err ->
1081			[]
1082		end,
1083	    get_file_list1(Stream,Dir,Includes,SuffixedNameList++Acc)
1084    end.
1085
1086get_rule(Options) ->
1087    case [Rule || Rule <- [ber,per,uper,jer],
1088		  Opt <- Options,
1089		  Rule =:= Opt] of
1090	[Rule] ->
1091	    Rule;
1092	[Rule|_] ->
1093	    Rule;
1094	[] ->
1095	    ber
1096    end.
1097
1098%% translate_options(NewOptions) -> OldOptions
1099%%  Translate the new option names to the old option name.
1100
1101translate_options([ber_bin|T]) ->
1102    io:format("Warning: The option 'ber_bin' is now called 'ber'.\n"),
1103    [ber|translate_options(T)];
1104translate_options([per_bin|T]) ->
1105    io:format("Warning: The option 'per_bin' is now called 'per'.\n"),
1106    [per|translate_options(T)];
1107translate_options([uper_bin|T]) ->
1108    io:format("Warning: The option 'uper_bin' is now called 'uper'.\n"),
1109    translate_options([uper|T]);
1110translate_options([nif|T]) ->
1111    io:format("Warning: The option 'nif' is no longer needed.\n"),
1112    translate_options(T);
1113translate_options([optimize|T]) ->
1114    io:format("Warning: The option 'optimize' is no longer needed.\n"),
1115    translate_options(T);
1116translate_options([inline|T]) ->
1117    io:format("Warning: The option 'inline' is no longer needed.\n"),
1118    translate_options(T);
1119translate_options([{inline,_}|_]) ->
1120    io:format("ERROR: The option {inline,OutputFilename} is no longer supported.\n"),
1121    throw({error,{unsupported_option,inline}});
1122translate_options([H|T]) ->
1123    [H|translate_options(T)];
1124translate_options([]) -> [].
1125
1126remove_asn_flags(Options) ->
1127    [X || X <- Options, not is_asn1_flag(X)].
1128
1129is_asn1_flag(asn1config) -> true;
1130is_asn1_flag(ber) -> true;
1131is_asn1_flag(compact_bit_string) -> true;
1132is_asn1_flag(debug) -> true;
1133is_asn1_flag(der) -> true;
1134is_asn1_flag(legacy_bit_string) -> true;
1135is_asn1_flag({macro_name_prefix,_}) -> true;
1136is_asn1_flag({n2n,_}) -> true;
1137is_asn1_flag(noobj) -> true;
1138is_asn1_flag(no_ok_wrapper) -> true;
1139is_asn1_flag(optimize) -> true;
1140is_asn1_flag(per) -> true;
1141is_asn1_flag({record_name_prefix,_}) -> true;
1142is_asn1_flag(undec_rec) -> true;
1143is_asn1_flag(uper) -> true;
1144is_asn1_flag(verbose) -> true;
1145%% 'warnings_as_errors' is intentionally passed through to the compiler.
1146is_asn1_flag(_) -> false.
1147
1148
1149outfile(Base, Ext, Opts) ->
1150    Obase = case lists:keysearch(outdir, 1, Opts) of
1151		{value, {outdir, Odir}} -> filename:join(Odir, Base);
1152		_NotFound -> Base % Not found or bad format
1153	    end,
1154    case Ext of
1155	[] ->
1156	    Obase;
1157	_ ->
1158	    lists:concat([Obase,".",Ext])
1159    end.
1160
1161includes(File,Options) ->
1162    Options2 = include_append(".", Options),
1163    Options3 = include_append(filename:dirname(File), Options2),
1164    case proplists:get_value(outdir, Options) of
1165        undefined -> Options3;
1166        OutDir    -> include_prepend(OutDir, Options3)
1167    end.
1168
1169include_append(Dir, Options) ->
1170    option_add({i, Dir}, Options, fun(Opts) -> Opts ++ [{i, Dir}] end).
1171
1172include_prepend(Dir, Options) ->
1173    option_add({i, Dir}, Options, fun(Opts) -> [{i, Dir}|Opts] end).
1174
1175option_add(Option, Options, Fun) ->
1176    case lists:member(Option, Options) of
1177        true  -> Options;
1178        false -> Fun(Options)
1179    end.
1180
1181strip_includes(Includes) ->
1182    [I || {i, I} <- Includes].
1183
1184
1185%% compile(AbsFileName, Options)
1186%%   Compile entry point for erl_compile.
1187
1188compile_asn(File,OutFile,Options) ->
1189    compile(lists:concat([File,".asn"]),OutFile,Options).
1190
1191compile_asn1(File,OutFile,Options) ->
1192    compile(lists:concat([File,".asn1"]),OutFile,Options).
1193
1194compile_py(File,OutFile,Options) ->
1195    compile(lists:concat([File,".py"]),OutFile,Options).
1196
1197compile(File, _OutFile, Options) ->
1198    case compile(File, make_erl_options(Options)) of
1199	{error,_Reason} ->
1200	    error;
1201	ok ->
1202	    ok;
1203	ParseRes when is_tuple(ParseRes) ->
1204	    io:format("~p~n",[ParseRes]),
1205	    ok;
1206	ScanRes when is_list(ScanRes) ->
1207	    io:format("~p~n",[ScanRes]),
1208	    ok
1209    end.
1210
1211%% Converts generic compiler options to specific options.
1212
1213make_erl_options(Opts) ->
1214
1215    %% This way of extracting will work even if the record passed
1216    %% has more fields than known during compilation.
1217
1218    Includes = Opts#options.includes,
1219    Defines = Opts#options.defines,
1220    Outdir = Opts#options.outdir,
1221    Warning = Opts#options.warning,
1222    Verbose = Opts#options.verbose,
1223    Specific = Opts#options.specific,
1224    Optimize = Opts#options.optimize,
1225    OutputType = Opts#options.output_type,
1226    Cwd = Opts#options.cwd,
1227
1228    Options =
1229	case Verbose of
1230	    true ->  [verbose];
1231	    false -> []
1232	end ++
1233	case Warning of
1234	    0 -> [];
1235	    _ -> [warnings]
1236	end ++
1237	[] ++
1238	case Optimize of
1239	    1 -> [optimize];
1240	    999 -> [];
1241	    _ -> [{optimize,Optimize}]
1242	end ++
1243	lists:map(
1244	  fun ({Name, Value}) ->
1245		  {d, Name, Value};
1246	      (Name) ->
1247		  {d, Name}
1248	  end,
1249	  Defines) ++
1250	case OutputType of
1251	    undefined -> [ber]; % temporary default (ber when it's ready)
1252	    _ -> [OutputType]	% pass through
1253	end,
1254
1255    Options++[errors, {cwd, Cwd}, {outdir, Outdir}|
1256	      lists:map(fun(Dir) -> {i, Dir} end, Includes)]++Specific.
1257
1258pretty2(Module,AbsFile) ->
1259    {ok,F} = file:open(AbsFile,[write]),
1260    M = asn1_db:dbget(Module,'MODULE'),
1261    io:format(F,"%%%%%%%%%%%%%%%%%%%   ~p  %%%%%%%%%%%%%%%%%%%~n",[Module]),
1262    io:format(F,"~s.\n",[asn1ct_pretty_format:term(M#module.defid)]),
1263    io:format(F,"~s.\n",[asn1ct_pretty_format:term(M#module.tagdefault)]),
1264    io:format(F,"~s.\n",[asn1ct_pretty_format:term(M#module.exports)]),
1265    io:format(F,"~s.\n",[asn1ct_pretty_format:term(M#module.imports)]),
1266    io:format(F,"~s.\n\n",[asn1ct_pretty_format:term(M#module.extensiondefault)]),
1267
1268    {Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets} = M#module.typeorval,
1269    io:format(F,"%%%%%%%%%%%%%%%%%%% TYPES in ~p  %%%%%%%%%%%%%%%%%%%~n",[Module]),
1270    lists:foreach(fun(T)-> io:format(F,"~s.\n",
1271				     [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
1272		  end,Types),
1273    io:format(F,"%%%%%%%%%%%%%%%%%%% VALUES in ~p  %%%%%%%%%%%%%%%%%%%~n",[Module]),
1274    lists:foreach(fun(T)-> io:format(F,"~s.\n",
1275				     [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
1276		  end,Values),
1277    io:format(F,"%%%%%%%%%%%%%%%%%%% Parameterized Types in ~p  %%%%%%%%%%%%%%%%%%%~n",[Module]),
1278    lists:foreach(fun(T)-> io:format(F,"~s.\n",
1279				     [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
1280		  end,ParameterizedTypes),
1281    io:format(F,"%%%%%%%%%%%%%%%%%%% Classes in ~p  %%%%%%%%%%%%%%%%%%%~n",[Module]),
1282    lists:foreach(fun(T)-> io:format(F,"~s.\n",
1283				     [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
1284		  end,Classes),
1285    io:format(F,"%%%%%%%%%%%%%%%%%%% Objects in ~p  %%%%%%%%%%%%%%%%%%%~n",[Module]),
1286    lists:foreach(fun(T)-> io:format(F,"~s.\n",
1287				     [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
1288		  end,Objects),
1289    io:format(F,"%%%%%%%%%%%%%%%%%%% Object Sets in ~p  %%%%%%%%%%%%%%%%%%%~n",[Module]),
1290    lists:foreach(fun(T)-> io:format(F,"~s.\n",
1291				     [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
1292		  end,ObjectSets).
1293
1294start(Includes) when is_list(Includes) ->
1295    asn1_db:dbstart(Includes).
1296
1297test(Module)                             -> test_module(Module, []).
1298
1299test(Module, [] = Options)               -> test_module(Module, Options);
1300test(Module, [{i, _}|_] = Options)       -> test_module(Module, Options);
1301test(Module, Type)                       -> test_type(Module, Type, []).
1302
1303test(Module, Type, [] = Options)         -> test_type(Module, Type, Options);
1304test(Module, Type, [{i, _}|_] = Options) -> test_type(Module, Type, Options);
1305test(Module, Type, Value)                -> test_value(Module, Type, Value).
1306
1307test_module(Module, Includes) ->
1308    in_process(fun() ->
1309                   start(strip_includes(Includes)),
1310                   case check(Module, Includes) of
1311                       {ok, NewTypes} -> test_each(Module, NewTypes);
1312                       Error          -> Error
1313                   end
1314               end).
1315
1316test_each(Module, [Type|Rest]) ->
1317    case test_type(Module, Type) of
1318        {ok, _Result} -> test_each(Module, Rest);
1319        Error         -> Error
1320    end;
1321test_each(_,[]) ->
1322    ok.
1323
1324test_type(Module, Type, Includes) ->
1325    in_process(fun() ->
1326                   start(strip_includes(Includes)),
1327                   case check(Module, Includes) of
1328                       {ok, _NewTypes} -> test_type(Module, Type);
1329                       Error           -> Error
1330                   end
1331               end).
1332
1333test_type(Module, Type) ->
1334    case get_value(Module, Type) of
1335        {ok, Val}       -> test_value(Module, Type, Val);
1336        {error, Reason} -> {error, {asn1, {value, Reason}}}
1337    end.
1338
1339test_value(Module, Type, Value) ->
1340    in_process(fun() ->
1341                   case catch Module:encode(Type, Value) of
1342                       {ok, Bytes} ->
1343                           test_value_decode(Module, Type, Value, Bytes);
1344                       Bytes when is_binary(Bytes) ->
1345                           test_value_decode(Module, Type, Value, Bytes);
1346                       Error ->
1347                           {error, {asn1,
1348                                    {encode, {{Module, Type, Value}, Error}}}}
1349                   end
1350               end).
1351
1352
1353test_value_decode(Module, Type, Value, Bytes) ->
1354    NewBytes = prepare_bytes(Bytes),
1355    case Module:decode(Type, NewBytes) of
1356        {ok,Value}      -> {ok, {Module,Type,Value}};
1357        {ok,Value,<<>>} -> {ok, {Module,Type,Value}};
1358        Value           -> {ok, {Module,Type,Value}};
1359        {Value,<<>>}    -> {ok, {Module,Type,Value}};
1360
1361        %% Errors:
1362        {ok, Res}   ->
1363            {error, {asn1,
1364                     {encode_decode_mismatch,
1365                      {{Module, Type, Value}, Res}}}};
1366        {ok, Res, Rest} ->
1367            {error, {asn1,
1368                     {encode_decode_mismatch,
1369                      {{Module, Type, Value}, {Res,Rest}}}}};
1370        Error       ->
1371            {error, {asn1,
1372                     {{decode,
1373                       {Module, Type, Value}, Error}}}}
1374    end.
1375
1376value(Module, Type) -> value(Module, Type, []).
1377
1378value(Module, Type, Includes) ->
1379    in_process(fun() ->
1380                   start(strip_includes(Includes)),
1381                   case check(Module, Includes) of
1382                       {ok, _NewTypes} -> get_value(Module, Type);
1383                       Error           -> Error
1384                   end
1385               end).
1386
1387get_value(Module, Type) ->
1388    case asn1ct_value:from_type(Module, Type) of
1389        {error, Reason} -> {error, Reason};
1390        Result          -> {ok, Result}
1391    end.
1392
1393check(Module, Includes) ->
1394    case asn1_db:dbload(Module) of
1395	error ->
1396            {error,asn1db_missing_or_out_of_date};
1397	ok ->
1398	    M = asn1_db:dbget(Module, 'MODULE'),
1399            TypeOrVal =  M#module.typeorval,
1400            State = #state{mname = M#module.name,
1401                           module = M#module{typeorval=[]},
1402                           options = Includes},
1403            case asn1ct_check:check(State, TypeOrVal) of
1404                {ok, {NewTypes, _, _, _, _, _}, _} -> {ok, NewTypes};
1405                {error, Reason}                    -> {error, Reason}
1406            end
1407    end.
1408
1409prepare_bytes(Bytes) when is_binary(Bytes) -> Bytes;
1410prepare_bytes(Bytes) -> list_to_binary(Bytes).
1411
1412vsn() ->
1413    ?vsn.
1414
1415specialized_decode_prepare(#gen{erule=ber,options=Options}=Gen, M) ->
1416    case lists:member(asn1config, Options) of
1417	true ->
1418	    special_decode_prepare_1(Gen, M);
1419	false ->
1420	    ok
1421    end;
1422specialized_decode_prepare(_, _) ->
1423    ok.
1424
1425%% Reads the configuration file if it exists and stores information
1426%% about partial decode and incomplete decode
1427special_decode_prepare_1(#gen{options=Options}=Gen, M) ->
1428    %% read configure file
1429    ModName = case lists:keyfind(asn1config, 1, Options) of
1430                  {_,MName} -> MName;
1431                  false -> M#module.name
1432              end,
1433%%    io:format("ModName: ~p~nM#module.name: ~p~n~n",[ModName,M#module.name]),
1434    case read_config_file(Gen, ModName) of
1435        no_config_file ->
1436            ok;
1437        CfgList ->
1438            SelectedDecode = get_config_info(CfgList,selective_decode),
1439            ExclusiveDecode = get_config_info(CfgList,exclusive_decode),
1440            CommandList = create_partial_decode_gen_info(M#module.name,
1441                                                         SelectedDecode),
1442            %% To convert CommandList to a proper list for the driver change
1443            %% the list:[[choosen,Tag1],skip,[skip_optional,Tag2]] to L =
1444            %% [5,2,Tag1,0,1,Tag2] where 5 is the length, and call
1445            %% port_control(asn1_driver_port,3,[L| Bin])
1446            save_config(partial_decode,CommandList),
1447            save_gen_state(selective_decode,SelectedDecode),
1448            CommandList2 = create_partial_inc_decode_gen_info(M#module.name,
1449                                                              ExclusiveDecode),
1450            Part_inc_tlv_tags = tlv_tags(CommandList2),
1451            save_config(partial_incomplete_decode,Part_inc_tlv_tags),
1452            save_gen_state(exclusive_decode,ExclusiveDecode,Part_inc_tlv_tags)
1453    end.
1454
1455%% create_partial_inc_decode_gen_info/2
1456%%
1457%% Creats a list of tags out of the information in TypeNameList that
1458%% tells which value will be incomplete decoded, i.e. each end
1459%% component/type in TypeNameList. The significant types/components in
1460%% the path from the toptype must be specified in the
1461%% TypeNameList. Significant elements are all constructed types that
1462%% branches the path to the leaf and the leaf it selfs.
1463%%
1464%% Returns a list of elements, where an element may be one of
1465%% mandatory|[opt,Tag]|[bin,Tag]. mandatory correspond to a mandatory
1466%% element that shall be decoded as usual. [opt,Tag] matches an
1467%% OPTIONAL or DEFAULT element that shall be decoded as
1468%% usual. [bin,Tag] corresponds to an element, mandatory, OPTIONAL or
1469%% DEFAULT, that shall be left encoded (incomplete decoded).
1470create_partial_inc_decode_gen_info(ModName,{Mod,[{Name,L}|Ls]}) when is_list(L) ->
1471    TopTypeName = partial_inc_dec_toptype(L),
1472    [{Name,TopTypeName,
1473      create_partial_inc_decode_gen_info1(ModName,TopTypeName,{Mod,L})}|
1474     create_partial_inc_decode_gen_info(ModName,{Mod,Ls})];
1475create_partial_inc_decode_gen_info(_,{_,[]}) ->
1476    [];
1477create_partial_inc_decode_gen_info(_,[]) ->
1478    [].
1479
1480create_partial_inc_decode_gen_info1(ModName,TopTypeName,{ModName,
1481					    [_TopType|Rest]}) ->
1482    case asn1_db:dbget(ModName,TopTypeName) of
1483	#typedef{typespec=TS} ->
1484	    TagCommand = get_tag_command(TS,?MANDATORY,mandatory),
1485	    create_pdec_inc_command(ModName,get_components(TS#type.def),
1486				    Rest,[TagCommand]);
1487	_ ->
1488	    throw({error,{"wrong type list in asn1 config file",
1489			  TopTypeName}})
1490    end;
1491create_partial_inc_decode_gen_info1(M1,_,{M2,_}) when M1 /= M2 ->
1492    throw({error,{"wrong module name in asn1 config file",
1493		  M2}});
1494create_partial_inc_decode_gen_info1(_,_,TNL) ->
1495    throw({error,{"wrong type list in asn1 config file",
1496		  TNL}}).
1497
1498%%
1499%% Only when there is a 'ComponentType' the config data C1 may be a
1500%% list, where the incomplete decode is branched. So, C1 may be a
1501%% list, a "binary tuple", a "parts tuple" or an atom. The second
1502%% element of a binary tuple and a parts tuple is an atom.
1503create_pdec_inc_command(_ModName,_,[],Acc) ->
1504    lists:reverse(Acc);
1505create_pdec_inc_command(ModName,{Comps1,Comps2},TNL,Acc)
1506  when is_list(Comps1),is_list(Comps2) ->
1507    create_pdec_inc_command(ModName,Comps1 ++ Comps2,TNL,Acc);
1508%% The following two clauses match on the type after the top
1509%% type. This one if the top type had no tag, i.e. a CHOICE.
1510create_pdec_inc_command(ModN,Clist,[CL|_Rest],[[]]) when is_list(CL) ->
1511    create_pdec_inc_command(ModN,Clist,CL,[]);
1512create_pdec_inc_command(ModN,Clist,[CL|_Rest],Acc) when is_list(CL) ->
1513    InnerDirectives=create_pdec_inc_command(ModN,Clist,CL,[]),
1514    lists:reverse([InnerDirectives|Acc]);
1515create_pdec_inc_command(ModName,
1516			CList=[#'ComponentType'{name=Name,typespec=TS,
1517						prop=Prop}|Comps],
1518			TNL=[C1|Cs],Acc)  ->
1519    case C1 of
1520	{Name,undecoded} ->
1521	    TagCommand = get_tag_command(TS,?UNDECODED,Prop),
1522	    create_pdec_inc_command(ModName,Comps,Cs,concat_sequential(TagCommand,Acc));
1523	{Name,parts} ->
1524	    TagCommand = get_tag_command(TS,?PARTS,Prop),
1525	    create_pdec_inc_command(ModName,Comps,Cs,concat_sequential(TagCommand,Acc));
1526	L when is_list(L) ->
1527            %% I guess this never happens due to previous clause.
1528	    %% This case is only possible as the first element after
1529	    %% the top type element, when top type is SEGUENCE or SET.
1530	    %% Follow each element in L. Must note every tag on the
1531	    %% way until the last command is reached, but it ought to
1532	    %% be enough to have a "complete" or "complete optional"
1533	    %% command for each component that is not specified in the
1534	    %% config file. Then in the TLV decode the components with
1535	    %% a "complete" command will be decoded by an ordinary TLV
1536	    %% decode.
1537	    create_pdec_inc_command(ModName,CList,L,Acc);
1538	{Name,RestPartsList} when is_list(RestPartsList) ->
1539	    %% Same as previous, but this may occur at any place in
1540	    %% the structure. The previous is only possible as the
1541	    %% second element.
1542	    case get_tag_command(TS,?MANDATORY,Prop) of
1543		?MANDATORY ->
1544		    InnerDirectives=
1545			create_pdec_inc_command(ModName,TS#type.def,
1546						RestPartsList,[]),
1547		    create_pdec_inc_command(ModName,Comps,Cs,
1548					    [[?MANDATORY,InnerDirectives]|Acc]);
1549		[Opt,EncTag] ->
1550		    InnerDirectives =
1551			create_pdec_inc_command(ModName,TS#type.def,
1552						RestPartsList,[]),
1553		    create_pdec_inc_command(ModName,Comps,Cs,
1554					    [[Opt,EncTag,InnerDirectives]|Acc])
1555	    end;
1556	_ ->
1557            %% this component may not be in the config list
1558	    TagCommand = get_tag_command(TS,?MANDATORY,Prop),
1559	    create_pdec_inc_command(ModName,Comps,TNL,concat_sequential(TagCommand,Acc))
1560    end;
1561create_pdec_inc_command(ModName,
1562			{'CHOICE',[#'ComponentType'{name=C1,
1563						    typespec=TS,
1564						    prop=Prop}|Comps]},
1565			[{C1,Directive}|Rest],Acc) ->
1566    case Directive of
1567	List when is_list(List) ->
1568	    TagCommand = get_tag_command(TS,?ALTERNATIVE,Prop),
1569	    CompAcc =
1570		create_pdec_inc_command(ModName,
1571					get_components(TS#type.def),List,[]),
1572	    NewAcc = case TagCommand of
1573			 [Command,Tag] when is_atom(Command) ->
1574			     [[Command,Tag,CompAcc]|Acc];
1575			 [L1,_L2|Rest] when is_list(L1) ->
1576			     case lists:reverse(TagCommand) of
1577				 [Atom|Comms] when is_atom(Atom) ->
1578				     [concat_sequential(lists:reverse(Comms),
1579							[Atom,CompAcc])|Acc];
1580				 [[Command2,Tag2]|Comms] ->
1581				     [concat_sequential(lists:reverse(Comms),
1582							[[Command2,Tag2,CompAcc]])|Acc]
1583			     end
1584		     end,
1585	    create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest,
1586				    NewAcc);
1587	undecoded ->
1588	    TagCommand = get_tag_command(TS,?ALTERNATIVE_UNDECODED,Prop),
1589	    create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest,
1590				    concat_sequential(TagCommand,Acc));
1591	parts ->
1592	    TagCommand = get_tag_command(TS,?ALTERNATIVE_PARTS,Prop),
1593	    create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest,
1594				    concat_sequential(TagCommand,Acc))
1595    end;
1596create_pdec_inc_command(ModName,
1597			{'CHOICE',[#'ComponentType'{typespec=TS,
1598						    prop=Prop}|Comps]},
1599			TNL,Acc) ->
1600    TagCommand = get_tag_command(TS,?ALTERNATIVE,Prop),
1601    create_pdec_inc_command(ModName,{'CHOICE',Comps},TNL,
1602			    concat_sequential(TagCommand,Acc));
1603create_pdec_inc_command(M,{'CHOICE',{Cs1,Cs2}},TNL,Acc)
1604  when is_list(Cs1),is_list(Cs2) ->
1605    create_pdec_inc_command(M,{'CHOICE',Cs1 ++ Cs2},TNL,Acc);
1606create_pdec_inc_command(ModName,#'Externaltypereference'{module=M,type=Name},
1607			TNL,Acc) ->
1608    #type{def=Def} = get_referenced_type(M,Name),
1609    create_pdec_inc_command(ModName,get_components(Def),TNL,Acc);
1610create_pdec_inc_command(_,_,TNL,_) ->
1611    throw({error,{"unexpected error when creating partial "
1612		  "decode command",TNL}}).
1613
1614partial_inc_dec_toptype([T|_]) when is_atom(T) ->
1615    T;
1616partial_inc_dec_toptype([{T,_}|_]) when is_atom(T) ->
1617    T;
1618partial_inc_dec_toptype([L|_]) when is_list(L) ->
1619    partial_inc_dec_toptype(L);
1620partial_inc_dec_toptype(_) ->
1621    throw({error,{"no top type found for partial incomplete decode"}}).
1622
1623
1624%% Creats a list of tags out of the information in TypeList and Types
1625%% that tells which value will be decoded.  Each constructed type that
1626%% is in the TypeList will get a "choosen" command. Only the last
1627%% type/component in the TypeList may be a primitive type. Components
1628%% "on the way" to the final element may get the "skip" or the
1629%% "skip_optional" command.
1630%% CommandList = [Elements]
1631%% Elements = {choosen,Tag}|{skip_optional,Tag}|skip
1632%% Tag is a binary with the tag BER encoded.
1633create_partial_decode_gen_info(ModName,{ModName,TypeLists}) ->
1634    [create_partial_decode_gen_info1(ModName,TL) || TL <- TypeLists];
1635create_partial_decode_gen_info(_,[]) ->
1636    [];
1637create_partial_decode_gen_info(_M1,{M2,_}) ->
1638    throw({error,{"wrong module name in asn1 config file",
1639		  M2}}).
1640
1641create_partial_decode_gen_info1(ModName,{FuncName,TypeList}) ->
1642    case TypeList of
1643	[TopType|Rest] ->
1644	    case asn1_db:dbget(ModName,TopType) of
1645		#typedef{typespec=TS} ->
1646		    TagCommand = get_tag_command(TS,?CHOOSEN),
1647		    Ret=create_pdec_command(ModName,
1648					    get_components(TS#type.def),
1649					    Rest,concat_tags(TagCommand,[])),
1650		    {FuncName,Ret};
1651		_ ->
1652		    throw({error,{"wrong type list in asn1 config file",
1653				  TypeList}})
1654	    end;
1655	_ ->
1656	    []
1657    end;
1658create_partial_decode_gen_info1(_,_) ->
1659    ok.
1660
1661%% create_pdec_command/4 for each name (type or component) in the
1662%% third argument, TypeNameList, a command is created. The command has
1663%% information whether the component/type shall be skipped, looked
1664%% into or returned. The list of commands is returned.
1665create_pdec_command(_ModName,_,[],Acc) ->
1666    Remove_empty_lists =
1667	fun([[]|L],Res,Fun) ->
1668		Fun(L,Res,Fun);
1669	   ([],Res,_) ->
1670		Res;
1671	   ([H|L],Res,Fun) ->
1672		Fun(L,[H|Res],Fun)
1673	end,
1674    Remove_empty_lists(Acc,[],Remove_empty_lists);
1675create_pdec_command(ModName,[#'ComponentType'{name=C1,typespec=TS}|_Comps],
1676		    [C1|Cs],Acc) ->
1677    %% this component is a constructed type or the last in the
1678    %% TypeNameList otherwise the config spec is wrong
1679    TagCommand = get_tag_command(TS,?CHOOSEN),
1680    create_pdec_command(ModName,get_components(TS#type.def),
1681			Cs,concat_tags(TagCommand,Acc));
1682create_pdec_command(ModName,[#'ComponentType'{typespec=TS,
1683					      prop=Prop}|Comps],
1684		    [C2|Cs],Acc) ->
1685    TagCommand =
1686	case Prop of
1687	    mandatory ->
1688		get_tag_command(TS,?SKIP);
1689	    _ ->
1690		get_tag_command(TS,?SKIP_OPTIONAL)
1691	end,
1692    create_pdec_command(ModName,Comps,[C2|Cs],concat_tags(TagCommand,Acc));
1693create_pdec_command(ModName,{'CHOICE',[Comp=#'ComponentType'{name=C1}|_]},TNL=[C1|_Cs],Acc) ->
1694    create_pdec_command(ModName,[Comp],TNL,Acc);
1695create_pdec_command(ModName,{'CHOICE',[#'ComponentType'{}|Comps]},TNL,Acc) ->
1696    create_pdec_command(ModName,{'CHOICE',Comps},TNL,Acc);
1697create_pdec_command(ModName,{'CHOICE',{Cs1,Cs2}},TNL,Acc)
1698  when is_list(Cs1),is_list(Cs2) ->
1699    create_pdec_command(ModName,{'CHOICE',Cs1 ++ Cs2},TNL,Acc);
1700create_pdec_command(ModName,#'Externaltypereference'{module=M,type=C1},
1701		    TypeNameList,Acc) ->
1702     #type{def=Def} = get_referenced_type(M,C1),
1703    create_pdec_command(ModName,get_components(Def),TypeNameList,
1704			Acc);
1705create_pdec_command(ModName,TS=#type{def=Def},[C1|Cs],Acc) ->
1706    %% This case when we got the "components" of a SEQUENCE/SET OF
1707    case C1 of
1708	[1] ->
1709	    %% A list with an integer is the only valid option in a 'S
1710	    %% OF', the other valid option would be an empty
1711	    %% TypeNameList saying that the entire 'S OF' will be
1712	    %% decoded.
1713	    TagCommand = get_tag_command(TS,?CHOOSEN),
1714	    create_pdec_command(ModName,Def,Cs,concat_tags(TagCommand,Acc));
1715	[N] when is_integer(N) ->
1716	    TagCommand = get_tag_command(TS,?SKIP),
1717	    create_pdec_command(ModName,Def,[[N-1]|Cs],
1718				concat_tags(TagCommand,Acc));
1719	Err ->
1720	    throw({error,{"unexpected error when creating partial "
1721			  "decode command",Err}})
1722    end;
1723create_pdec_command(_,_,TNL,_) ->
1724    throw({error,{"unexpected error when creating partial "
1725		  "decode command",TNL}}).
1726
1727get_components(#'SEQUENCE'{components={C1,C2}}) when is_list(C1),is_list(C2) ->
1728    C1++C2;
1729get_components(#'SEQUENCE'{components=Components}) ->
1730    Components;
1731get_components(#'SET'{components={C1,C2}}) when is_list(C1),is_list(C2) ->
1732    C1++C2;
1733get_components(#'SET'{components=Components}) ->
1734    Components;
1735get_components({'SEQUENCE OF',Components}) ->
1736    Components;
1737get_components({'SET OF',Components}) ->
1738    Components;
1739get_components(Def) ->
1740    Def.
1741
1742concat_sequential(L=[A,B],Acc) when is_atom(A),is_binary(B) ->
1743    [L|Acc];
1744concat_sequential(L,Acc) when is_list(L) ->
1745    concat_sequential1(lists:reverse(L),Acc);
1746concat_sequential(A,Acc)  ->
1747    [A|Acc].
1748concat_sequential1([],Acc) ->
1749    Acc;
1750concat_sequential1([[]],Acc) ->
1751    Acc;
1752concat_sequential1([El|RestEl],Acc) when is_list(El) ->
1753    concat_sequential1(RestEl,[El|Acc]);
1754concat_sequential1([mandatory|RestEl],Acc) ->
1755    concat_sequential1(RestEl,[mandatory|Acc]);
1756concat_sequential1(L,Acc) ->
1757    [L|Acc].
1758
1759
1760many_tags([?SKIP])->
1761    false;
1762many_tags([?SKIP_OPTIONAL,_]) ->
1763    false;
1764many_tags([?CHOOSEN,_]) ->
1765    false;
1766many_tags(_) ->
1767    true.
1768
1769concat_tags(Ts,Acc) ->
1770    case many_tags(Ts) of
1771	true when is_list(Ts) ->
1772	    lists:reverse(Ts)++Acc;
1773	true ->
1774	    [Ts|Acc];
1775	false ->
1776	    [Ts|Acc]
1777    end.
1778%% get_tag_command(Type,Command)
1779
1780%% Type is the type that has information about the tag Command tells
1781%% what to do with the encoded value with the tag of Type when
1782%% decoding.
1783get_tag_command(#type{tag=[]},_) ->
1784    [];
1785%% SKIP and SKIP_OPTIONAL shall return only one tag command regardless
1786get_tag_command(#type{},?SKIP) ->
1787    ?SKIP;
1788get_tag_command(#type{tag=Tags},?SKIP_OPTIONAL) ->
1789    Tag=hd(Tags),
1790    [?SKIP_OPTIONAL,encode_tag_val(decode_class(Tag#tag.class),
1791				   Tag#tag.form,Tag#tag.number)];
1792get_tag_command(#type{tag=[Tag]},Command) ->
1793    %% encode the tag according to BER
1794    [Command,encode_tag_val(decode_class(Tag#tag.class),Tag#tag.form,
1795			    Tag#tag.number)];
1796get_tag_command(T=#type{tag=[Tag|Tags]},Command) ->
1797    TC = get_tag_command(T#type{tag=[Tag]},Command),
1798    TCs = get_tag_command(T#type{tag=Tags},Command),
1799    case many_tags(TCs) of
1800	true when is_list(TCs) ->
1801	    [TC|TCs];
1802	_ -> [TC|[TCs]]
1803    end.
1804
1805%% get_tag_command/3 used by create_pdec_inc_command
1806get_tag_command(#type{tag=[]},_,_) ->
1807    [];
1808get_tag_command(#type{tag=[Tag]},?MANDATORY,Prop) ->
1809    case Prop of
1810	mandatory ->
1811	    ?MANDATORY;
1812	{'DEFAULT',_} ->
1813	    [?DEFAULT,encode_tag_val(decode_class(Tag#tag.class),
1814				     Tag#tag.form,Tag#tag.number)];
1815	_ -> [?OPTIONAL,encode_tag_val(decode_class(Tag#tag.class),
1816				       Tag#tag.form,Tag#tag.number)]
1817    end;
1818get_tag_command(#type{tag=[Tag]},Command,Prop) ->
1819    [anonymous_dec_command(Command,Prop),encode_tag_val(decode_class(Tag#tag.class),Tag#tag.form, Tag#tag.number)];
1820get_tag_command(#type{tag=Tag},Command,Prop) when is_record(Tag,tag) ->
1821    get_tag_command(#type{tag=[Tag]},Command,Prop);
1822get_tag_command(T=#type{tag=[Tag|Tags]},Command,Prop) ->
1823    [get_tag_command(T#type{tag=[Tag]},Command,Prop)|[
1824     get_tag_command(T#type{tag=Tags},Command,Prop)]].
1825
1826anonymous_dec_command(?UNDECODED,'OPTIONAL') ->
1827    ?OPTIONAL_UNDECODED;
1828anonymous_dec_command(Command,_) ->
1829    Command.
1830
1831get_referenced_type(M,Name) ->
1832    case asn1_db:dbget(M,Name) of
1833	#typedef{typespec=TS} ->
1834	    case TS of
1835		#type{def=#'Externaltypereference'{module=M2,type=Name2}} ->
1836		    %% The tags have already been taken care of in the
1837		    %% first reference where they were gathered in a
1838		    %% list of tags.
1839		    get_referenced_type(M2,Name2);
1840		#type{} -> TS;
1841		_  ->
1842		    throw({error,{"unexpected element when"
1843				  " fetching referenced type",TS}})
1844	    end;
1845	T ->
1846	    throw({error,{"unexpected element when fetching "
1847			  "referenced type",T}})
1848    end.
1849
1850
1851tlv_tags([]) ->
1852    [];
1853tlv_tags([mandatory|Rest]) ->
1854    [mandatory|tlv_tags(Rest)];
1855tlv_tags([[Command,Tag]|Rest]) when is_atom(Command),is_binary(Tag) ->
1856    [[Command,tlv_tag(Tag)]|tlv_tags(Rest)];
1857tlv_tags([[Command,Directives]|Rest]) when is_atom(Command),is_list(Directives) ->
1858    [[Command,tlv_tags(Directives)]|tlv_tags(Rest)];
1859%% remove all empty lists
1860tlv_tags([[]|Rest]) ->
1861    tlv_tags(Rest);
1862tlv_tags([{Name,TopType,L1}|Rest]) when is_list(L1),is_atom(TopType) ->
1863    [{Name,TopType,tlv_tags(L1)}|tlv_tags(Rest)];
1864tlv_tags([[Command,Tag,L1]|Rest]) when is_list(L1),is_binary(Tag) ->
1865    [[Command,tlv_tag(Tag),tlv_tags(L1)]|tlv_tags(Rest)];
1866tlv_tags([[mandatory|Rest]]) ->
1867    [[mandatory|tlv_tags(Rest)]];
1868tlv_tags([L=[L1|_]|Rest]) when is_list(L1) ->
1869    [tlv_tags(L)|tlv_tags(Rest)].
1870
1871tlv_tag(<<Cl:2,_:1,TagNo:5>>) when TagNo < 31 ->
1872    (Cl bsl 16) + TagNo;
1873tlv_tag(<<Cl:2,_:1,31:5,0:1,TagNo:7>>) ->
1874    (Cl bsl 16) + TagNo;
1875tlv_tag(<<Cl:2,_:1,31:5,Buffer/binary>>) ->
1876    TagNo = tlv_tag1(Buffer,0),
1877    (Cl bsl 16) + TagNo.
1878tlv_tag1(<<0:1,PartialTag:7>>,Acc) ->
1879    (Acc bsl 7) bor PartialTag;
1880tlv_tag1(<<1:1,PartialTag:7,Buffer/binary>>,Acc) ->
1881    tlv_tag1(Buffer,(Acc bsl 7) bor PartialTag).
1882
1883%% Reads the content from the configuration file and returns the
1884%% selected part chosen by InfoType. Assumes that the config file
1885%% content is an Erlang term.
1886read_config_file_info(ModuleName, InfoType) when is_atom(InfoType) ->
1887    Name = ensure_ext(ModuleName, ".asn1config"),
1888    CfgList = read_config_file0(Name, []),
1889    get_config_info(CfgList, InfoType).
1890
1891read_config_file(#gen{options=Options}, ModuleName) ->
1892    Name = ensure_ext(ModuleName, ".asn1config"),
1893    Includes = [I || {i,I} <- Options],
1894    read_config_file0(Name, ["."|Includes]).
1895
1896read_config_file0(Name, [D|Dirs]) ->
1897    case file:consult(filename:join(D, Name)) of
1898	{ok,CfgList} ->
1899	    CfgList;
1900	{error,enoent} ->
1901            read_config_file0(Name, Dirs);
1902	{error,Reason} ->
1903	    Error = "error reading asn1 config file: " ++
1904		file:format_error(Reason),
1905	    throw({error,Error})
1906    end;
1907read_config_file0(_, []) ->
1908    no_config_file.
1909
1910ensure_ext(ModuleName, Ext) ->
1911    Name = filename:join([ModuleName]),
1912    case filename:extension(Name) of
1913        Ext -> Name;
1914        _ -> Name ++ Ext
1915    end.
1916
1917get_config_info(CfgList,InfoType) ->
1918    case lists:keysearch(InfoType,1,CfgList) of
1919	{value,{InfoType,Value}} ->
1920	    Value;
1921	false ->
1922	    []
1923    end.
1924
1925%% save_config/2 saves the Info with the key Key
1926%% Before saving anything check if a table exists
1927%% The record gen_state is saved with the key {asn1_config,gen_state}
1928save_config(Key,Info) ->
1929    asn1ct_table:new_reuse(asn1_general),
1930    asn1ct_table:insert(asn1_general, {{asn1_config, Key}, Info}).
1931
1932read_config_data(Key) ->
1933    case asn1ct_table:exists(asn1_general) of
1934	false -> undefined;
1935	true ->
1936	    case asn1ct_table:lookup(asn1_general,{asn1_config,Key}) of
1937		[{_,Data}] -> Data;
1938		Err ->
1939                    %% Err is [] when nothing was saved in the ets table
1940		    Err
1941	    end
1942    end.
1943
1944
1945%%
1946%% Functions to manipulate the gen_state record saved in the
1947%% asn1_general ets table.
1948%%
1949
1950%% saves input data in a new gen_state record
1951save_gen_state(exclusive_decode,{_,ConfList},PartIncTlvTagList) ->
1952    State =
1953	case get_gen_state() of
1954	    S when is_record(S,gen_state) -> S;
1955	    _ -> #gen_state{}
1956	end,
1957    StateRec = State#gen_state{inc_tag_pattern=PartIncTlvTagList,
1958			       inc_type_pattern=ConfList},
1959    save_config(gen_state,StateRec);
1960save_gen_state(_,_,_) ->
1961    case get_gen_state() of
1962	S when is_record(S,gen_state) -> ok;
1963	_ -> save_config(gen_state,#gen_state{})
1964    end.
1965
1966save_gen_state(selective_decode,{_,Type_component_name_list}) ->
1967    State =
1968	case get_gen_state() of
1969	    S when is_record(S,gen_state) -> S;
1970	    _ -> #gen_state{}
1971	end,
1972    StateRec = State#gen_state{type_pattern=Type_component_name_list},
1973    save_config(gen_state,StateRec);
1974save_gen_state(selective_decode,_) ->
1975    ok.
1976
1977save_gen_state(GenState) when is_record(GenState,gen_state) ->
1978    save_config(gen_state,GenState).
1979
1980
1981%% get_gen_state_field returns undefined if no gen_state exists or if
1982%% Field is undefined or the data at the field.
1983get_gen_state_field(Field) ->
1984    case read_config_data(gen_state) of
1985	undefined ->
1986	    undefined;
1987	GenState when is_record(GenState,gen_state) ->
1988	    get_gen_state_field(GenState,Field);
1989	Err ->
1990	    exit({error,{asn1,{"false configuration file info",Err}}})
1991    end.
1992get_gen_state_field(#gen_state{active=Active},active) ->
1993    Active;
1994get_gen_state_field(_,active) ->
1995    false;
1996get_gen_state_field(GS,prefix) ->
1997    GS#gen_state.prefix;
1998get_gen_state_field(GS,inc_tag_pattern) ->
1999    GS#gen_state.inc_tag_pattern;
2000get_gen_state_field(GS,tag_pattern) ->
2001    GS#gen_state.tag_pattern;
2002get_gen_state_field(GS,inc_type_pattern) ->
2003    GS#gen_state.inc_type_pattern;
2004get_gen_state_field(GS,type_pattern) ->
2005    GS#gen_state.type_pattern;
2006get_gen_state_field(GS,func_name) ->
2007    GS#gen_state.func_name;
2008get_gen_state_field(GS,namelist) ->
2009    GS#gen_state.namelist;
2010get_gen_state_field(GS,tobe_refed_funcs) ->
2011    GS#gen_state.tobe_refed_funcs;
2012get_gen_state_field(GS,gen_refed_funcs) ->
2013    GS#gen_state.gen_refed_funcs;
2014get_gen_state_field(GS,generated_functions) ->
2015    GS#gen_state.generated_functions;
2016get_gen_state_field(GS,suffix_index) ->
2017    GS#gen_state.suffix_index;
2018get_gen_state_field(GS,current_suffix_index) ->
2019    GS#gen_state.current_suffix_index.
2020
2021get_gen_state() ->
2022    read_config_data(gen_state).
2023
2024
2025update_gen_state(Field,Data) ->
2026    case get_gen_state() of
2027	State when is_record(State,gen_state) ->
2028	    update_gen_state(Field,State,Data);
2029	_ ->
2030	    exit({error,{asn1,{internal,
2031			       "tried to update nonexistent gen_state",Field,Data}}})
2032    end.
2033update_gen_state(active,State,Data) ->
2034    save_gen_state(State#gen_state{active=Data});
2035update_gen_state(prefix,State,Data) ->
2036    save_gen_state(State#gen_state{prefix=Data});
2037update_gen_state(inc_tag_pattern,State,Data) ->
2038    save_gen_state(State#gen_state{inc_tag_pattern=Data});
2039update_gen_state(tag_pattern,State,Data) ->
2040    save_gen_state(State#gen_state{tag_pattern=Data});
2041update_gen_state(inc_type_pattern,State,Data) ->
2042    save_gen_state(State#gen_state{inc_type_pattern=Data});
2043update_gen_state(type_pattern,State,Data) ->
2044    save_gen_state(State#gen_state{type_pattern=Data});
2045update_gen_state(func_name,State,Data) ->
2046    save_gen_state(State#gen_state{func_name=Data});
2047update_gen_state(namelist,State,Data) ->
2048    save_gen_state(State#gen_state{namelist=Data});
2049update_gen_state(tobe_refed_funcs,State,Data) ->
2050    save_gen_state(State#gen_state{tobe_refed_funcs=Data});
2051update_gen_state(gen_refed_funcs,State,Data) ->
2052    save_gen_state(State#gen_state{gen_refed_funcs=Data});
2053update_gen_state(generated_functions,State,Data) ->
2054    save_gen_state(State#gen_state{generated_functions=Data});
2055update_gen_state(suffix_index,State,Data) ->
2056    save_gen_state(State#gen_state{suffix_index=Data});
2057update_gen_state(current_suffix_index,State,Data) ->
2058    save_gen_state(State#gen_state{current_suffix_index=Data}).
2059
2060update_namelist(Name) ->
2061    case get_gen_state_field(namelist) of
2062	[Name,Rest] -> update_gen_state(namelist,Rest);
2063	[Name|Rest] -> update_gen_state(namelist,Rest);
2064	[{Name,List}] when is_list(List) -> update_gen_state(namelist,List);
2065	[{Name,Atom}|Rest] when is_atom(Atom) -> update_gen_state(namelist,Rest);
2066	Other -> Other
2067    end.
2068
2069%% removes a bracket from the namelist
2070step_in_constructed() ->
2071    case get_gen_state_field(namelist) of
2072	[L] when is_list(L) ->
2073	    update_gen_state(namelist,L);
2074	_ -> ok
2075    end.
2076
2077is_function_generated(Name) ->
2078    case get_gen_state_field(gen_refed_funcs) of
2079	L when is_list(L) ->
2080	    lists:member(Name,L);
2081	_ ->
2082	    false
2083    end.
2084
2085get_tobe_refed_func(Name) ->
2086    case get_gen_state_field(tobe_refed_funcs) of
2087	L when is_list(L) ->
2088	    case lists:keysearch(Name,1,L) of
2089		{_,Element} ->
2090		    Element;
2091		_ ->
2092		    undefined
2093	    end;
2094	_ ->
2095	    undefined
2096    end.
2097
2098%% add_tobe_refed_func saves Data that is a three or four element
2099%% tuple.  Do not save if it exists in generated_functions, because
2100%% then it will be or already is generated.
2101add_tobe_refed_func(Data) ->
2102    {Name,SI,Pattern} =
2103	fun({N,Si,P,_}) -> {N,Si,P};
2104	    (D) -> D end (Data),
2105    NewData =
2106	case SI of
2107	    I when is_integer(I) ->
2108		fun(D) -> D end(Data);
2109	    _ ->
2110		fun({N,_,P}) -> {N,0,P};
2111		   ({N,_,P,T}) -> {N,0,P,T} end (Data)
2112	end,
2113
2114    L = get_gen_state_field(generated_functions),
2115    case generated_functions_member(get(currmod),Name,L,Pattern) of
2116	true ->
2117            %% it exists in generated_functions, it has already
2118            %% been generated or saved in tobe_refed_func
2119	    ok;
2120	_ ->
2121	    add_once_tobe_refed_func(NewData),
2122	    %% only to get it saved in generated_functions
2123	    maybe_rename_function(tobe_refed,Name,Pattern)
2124    end.
2125
2126
2127
2128%% Adds only one element with same Name and Index where Data =
2129%% {Name,Index,Pattern}.
2130add_once_tobe_refed_func(Data) ->
2131    TRFL = get_gen_state_field(tobe_refed_funcs),
2132    {Name,Index} = {element(1,Data),element(2,Data)},
2133    case lists:filter(fun({N,I,_}) when N==Name,I==Index ->true;
2134			 ({N,I,_,_}) when N==Name,I==Index -> true;
2135			 (_) -> false end,TRFL) of
2136	[] ->
2137	    update_gen_state(tobe_refed_funcs,[Data|TRFL]);
2138	_ ->
2139	    ok
2140    end.
2141
2142
2143%% Moves Name from the to be list to the generated list.
2144generated_refed_func(Name) ->
2145    L = get_gen_state_field(tobe_refed_funcs),
2146    NewL = lists:keydelete(Name,1,L),
2147    update_gen_state(tobe_refed_funcs,NewL),
2148    L2 = get_gen_state_field(gen_refed_funcs),
2149    update_gen_state(gen_refed_funcs,[Name|L2]).
2150
2151%% Adds Data to gen_refed_funcs field in gen_state.
2152add_generated_refed_func(Data) ->
2153    case is_function_generated(Data) of
2154	true ->
2155	    ok;
2156	_ ->
2157	    L = get_gen_state_field(gen_refed_funcs),
2158	    update_gen_state(gen_refed_funcs,[Data|L])
2159    end.
2160
2161next_refed_func() ->
2162    case get_gen_state_field(tobe_refed_funcs) of
2163	[] ->
2164	    [];
2165	[H|T] ->
2166	    update_gen_state(tobe_refed_funcs,T),
2167	    H
2168    end.
2169
2170reset_gen_state() ->
2171    save_gen_state(#gen_state{}).
2172
2173%% Adds Data to generated_functions field in gen_state.
2174add_generated_function(Data) ->
2175    L = get_gen_state_field(generated_functions),
2176    update_gen_state(generated_functions,[Data|L]).
2177
2178
2179%% Each type has its own index starting from 0. If index is 0 there is
2180%% no renaming.
2181maybe_rename_function(Mode,Name,Pattern) ->
2182    case get_gen_state_field(generated_functions) of
2183	[] when Mode==inc_disp -> add_generated_function({Name,0,Pattern}),
2184	      Name;
2185	[] ->
2186	    exit({error,{asn1,internal_error_exclusive_decode}});
2187	L ->
2188	    case {Mode,generated_functions_member(get(currmod),Name,L)} of
2189		{_,true} ->
2190		    L2 = generated_functions_filter(get(currmod),Name,L),
2191		    case lists:keysearch(Pattern,3,L2) of
2192			false ->
2193                            %% name existed, but not pattern
2194			    NextIndex = length(L2),
2195			    %% rename function
2196			    Suffix = lists:concat(["_",NextIndex]),
2197			    NewName =
2198				maybe_rename_function2(type_check(Name),Name,
2199						       Suffix),
2200			    add_generated_function({Name,NextIndex,Pattern}),
2201			    NewName;
2202			Value ->
2203                            %% name and pattern existed
2204			    %% do not save any new index
2205			    Suffix = make_suffix(Value),
2206			    Name2 =
2207				case Name of
2208				    #'Externaltypereference'{type=T} -> T;
2209				    _ -> Name
2210				end,
2211			    lists:concat([Name2,Suffix])
2212		    end;
2213		{inc_disp,_} ->
2214                    %% this is when decode_partial_inc_disp/2 is
2215                    %% generated
2216		    add_generated_function({Name,0,Pattern}),
2217		    Name;
2218		_ -> % this if call from add_tobe_refed_func
2219		    add_generated_function({Name,0,Pattern}),
2220		    Name
2221	    end
2222    end.
2223
2224
2225maybe_rename_function2(record,#'Externaltypereference'{type=Name},Suffix) ->
2226    lists:concat([Name,Suffix]);
2227maybe_rename_function2(list,List,Suffix) ->
2228    lists:concat([asn1ct_gen:list2name(List),Suffix]);
2229maybe_rename_function2(Thing,Name,Suffix)
2230  when Thing==atom;Thing==integer;Thing==string ->
2231    lists:concat([Name,Suffix]).
2232
2233%% generated_functions_member/4 checks on both Name and Pattern if
2234%%  the element exists in L
2235generated_functions_member(M,Name,L,Pattern) ->
2236    case generated_functions_member(M,Name,L) of
2237	true ->
2238	    L2 = generated_functions_filter(M,Name,L),
2239	    case lists:keysearch(Pattern,3,L2) of
2240		{value,_} ->
2241		    true;
2242		_ -> false
2243	    end;
2244	_ -> false
2245    end.
2246
2247generated_functions_member(_M,Name,[{Name,_,_}|_]) ->
2248    true;
2249generated_functions_member(M,#'Externaltypereference'{module=M,type=T},
2250			   [{#'Externaltypereference'{module=M,type=T}
2251			     ,_,_}|_]) ->
2252    true;
2253generated_functions_member(M,#'Externaltypereference'{module=M,type=Name},
2254			  [{Name,_,_}|_]) ->
2255    true;
2256generated_functions_member(M,Name,[_|T]) ->
2257    generated_functions_member(M,Name,T);
2258generated_functions_member(_,_,[]) ->
2259    false.
2260
2261generated_functions_filter(_,Name,L) when is_atom(Name);is_list(Name) ->
2262    lists:filter(fun({N,_,_}) when N==Name -> true;
2263		    (_) -> false
2264		 end, L);
2265generated_functions_filter(M,#'Externaltypereference'{module=M,type=Name},L)->
2266    %% remove top typename from patterns
2267    RemoveTType =
2268	fun({N,I,[N,P]}) when N == Name ->
2269		{N,I,P};
2270	   ({#'Externaltypereference'{module=M1,type=N},I,P}) when M1==M ->
2271		{N,I,P};
2272	   (P) -> P
2273	end,
2274    L2 = lists:map(RemoveTType,L),
2275    generated_functions_filter(M,Name,L2).
2276
2277
2278maybe_saved_sindex(Name,Pattern) ->
2279    case get_gen_state_field(generated_functions) of
2280	[] -> false;
2281	L ->
2282	    case generated_functions_member(get(currmod),Name,L) of
2283		true ->
2284		    L2 = generated_functions_filter(get(currmod),Name,L),
2285		    case lists:keysearch(Pattern,3,L2) of
2286			{value,{_,I,_}} ->
2287			    I;
2288			_ -> length(L2) % this should be length(L2)!
2289		    end;
2290		_ -> false
2291	    end
2292    end.
2293
2294current_sindex() ->
2295    get_gen_state_field(current_suffix_index).
2296
2297set_current_sindex(Index) ->
2298    update_gen_state(current_suffix_index,Index).
2299
2300
2301type_check(A) when is_atom(A) ->
2302    atom;
2303type_check(L) when is_list(L) ->
2304    Pred = fun(X) when X=<255 ->
2305		   false;
2306	      (_) -> true
2307	   end,
2308    case lists:filter(Pred,L) of
2309	[] ->
2310	    string;
2311	_ ->
2312	    list
2313    end;
2314type_check(#'Externaltypereference'{}) ->
2315    record.
2316
2317 make_suffix({_,{_,0,_}}) ->
2318     "";
2319 make_suffix({_,{_,I,_}}) ->
2320     lists:concat(["_",I]);
2321 make_suffix(_) ->
2322     "".
2323
2324%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2325%% Report functions.
2326%%
2327%% Error messages are controlled with the 'errors' compiler option
2328%% Warning messages are controlled with the 'warnings' compiler option
2329%% Verbose messages are controlled with the 'verbose' compiler option
2330
2331error(Format, Args, S) ->
2332    case is_error(S) of
2333	true ->
2334	    io:format(Format, Args);
2335	false ->
2336	    ok
2337    end.
2338
2339warning(Format, Args, S) ->
2340    case is_warning(S) of
2341	true ->
2342	    io:format("Warning: " ++ Format, Args);
2343	false ->
2344	    ok
2345    end.
2346
2347warning(Format, Args, S, Reason) ->
2348    case {is_werr(S), is_error(S), is_warning(S)} of
2349	{true, true, _} ->
2350	    io:format(Format, Args),
2351	    throw({error, Reason});
2352	{false, _, true} ->
2353	    io:format(Format, Args);
2354	_ ->
2355	    ok
2356    end.
2357
2358verbose(Format, Args, S) ->
2359    case is_verbose(S) of
2360	true ->
2361	    io:format(Format, Args);
2362	false ->
2363	    ok
2364    end.
2365
2366format_error({write_error,File,Reason}) ->
2367    io_lib:format("writing output file ~s failed: ~s",
2368		  [File,file:format_error(Reason)]).
2369
2370is_error(#state{options=Opts}) ->
2371    is_error(Opts);
2372is_error(#gen{options=Opts}) ->
2373    is_error(Opts);
2374is_error(O) ->
2375    lists:member(errors, O) orelse is_verbose(O).
2376
2377is_warning(S) when is_record(S, state) ->
2378    is_warning(S#state.options);
2379is_warning(O) ->
2380    lists:member(warnings, O) orelse is_verbose(O).
2381
2382is_verbose(#state{options=Opts}) ->
2383    is_verbose(Opts);
2384is_verbose(#gen{options=Opts}) ->
2385    is_verbose(Opts);
2386is_verbose(O) ->
2387    lists:member(verbose, O).
2388
2389is_werr(S) when is_record(S, state) ->
2390    is_werr(S#state.options);
2391is_werr(O) ->
2392    lists:member(warnings_as_errors, O).
2393
2394
2395in_process(Fun) ->
2396    Parent = self(),
2397    Pid = spawn_link(fun() -> process(Parent, Fun) end),
2398    receive
2399        {Pid, Result}               -> Result;
2400        {Pid, Class, Reason, Stack} ->
2401            ST = try throw(x) catch throw:x:Stk -> Stk end,
2402            erlang:raise(Class, Reason, Stack ++ ST)
2403    end.
2404
2405process(Parent, Fun) ->
2406    try
2407        Parent ! {self(), Fun()}
2408    catch Class:Reason:Stack ->
2409        Parent ! {self(), Class, Reason, Stack}
2410    end.
2411