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