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