1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2009-2017. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19 20-module(reltool_utils). 21 22%% Public 23-export([root_dir/0, erl_libs/0, lib_dirs/1, 24 split_app_name/1, prim_consult/1, 25 default_rels/0, choose_default/3, 26 normalize_dir/1, 27 28 assign_image_list/1, get_latest_resize/1, wait_for_stop_motion/2, 29 mod_conds/0, list_to_mod_cond/1, mod_cond_to_index/1, 30 incl_conds/0, list_to_incl_cond/1, incl_cond_to_index/1, elem_to_index/2, 31 app_dir_test/2, split_app_dir/1, 32 get_item/1, get_items/1, get_selected_items/3, 33 select_items/3, select_item/2, 34 get_column_width/1, 35 36 safe_keysearch/5, print/4, add_warning/3, 37 38 create_dir/1, list_dir/1, read_file_info/1, 39 write_file_info/2, read_file/1, write_file/2, 40 recursive_delete/1, delete/2, recursive_copy_file/2, copy_file/2, 41 42 throw_error/2, 43 44 decode_regexps/3, 45 default_val/2, 46 escript_foldl/3, 47 48 call/2, cast/2, reply/3]). 49 50%% For testing 51-export([erl_libs/2]). 52 53-include_lib("kernel/include/file.hrl"). 54-include_lib("wx/include/wx.hrl"). 55-include("reltool.hrl"). 56 57root_dir() -> 58 code:root_dir(). 59 60erl_libs() -> 61 erl_libs(os:getenv("ERL_LIBS", ""), os:type()). 62 63erl_libs(ErlLibs, OsType) when is_list(ErlLibs) -> 64 Sep = 65 case OsType of 66 {win32, _} -> ";"; 67 _ -> ":" 68 end, 69 string:lexemes(ErlLibs, Sep). 70 71lib_dirs(Dir) -> 72 case erl_prim_loader:list_dir(Dir) of 73 {ok, Files} -> 74 [F || F <- Files, 75 filelib:is_dir(filename:join([Dir, F]), 76 erl_prim_loader)]; 77 error -> 78 [] 79 end. 80 81%% "asn1-1.6.2" -> {"asn1", "1.6.2"}; "asn1" -> {"asn1", ""} 82split_app_name(Name) -> 83 Pred = 84 fun(Elem) -> 85 if 86 Elem =:= $\. -> true; 87 Elem >= $0, Elem =< $9 -> true; 88 true -> false 89 end 90 end, 91 case lists:splitwith(Pred, lists:reverse(Name)) of 92 {Vsn, [$- | App]} -> 93 {list_to_atom(lists:reverse(App)), lists:reverse(Vsn)}; 94 _ -> 95 {list_to_atom(Name), ""} 96 end. 97 98 99normalize_dir(RelDir) -> 100 Tokens = filename:split(filename:absname(RelDir)), 101 filename:join(lists:reverse(normalize_dir(Tokens, []))). 102 103normalize_dir([".."|Dirs], [_Dir|Path]) -> 104 normalize_dir(Dirs, Path); 105normalize_dir(["."|Dirs], Path) -> 106 normalize_dir(Dirs, Path); 107normalize_dir([Dir|Dirs], Path) -> 108 normalize_dir(Dirs, [Dir|Path]); 109normalize_dir([], Path) -> 110 Path. 111 112 113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 114 115prim_consult(Bin) when is_binary(Bin) -> 116 case erl_scan:string(unicode:characters_to_list(Bin,encoding(Bin))) of 117 {ok, Tokens, _EndLine} -> 118 prim_parse(Tokens, []); 119 {error, {_ErrorLine, Module, Reason}, _EndLine} -> 120 {error, Module:format_error(Reason)} 121 end; 122prim_consult(FullName) when is_list(FullName) -> 123 case erl_prim_loader:get_file(FullName) of 124 {ok, Bin, _} -> 125 prim_consult(Bin); 126 error -> 127 {error, file:format_error(enoent)} 128 end. 129 130encoding(Bin) when is_binary(Bin) -> 131 case epp:read_encoding_from_binary(Bin) of 132 none -> 133 epp:default_encoding(); 134 E -> 135 E 136 end. 137 138prim_parse(Tokens, Acc) -> 139 case lists:splitwith(fun(T) -> element(1,T) =/= dot end, Tokens) of 140 {[], []} -> 141 {ok, lists:reverse(Acc)}; 142 {Tokens2, [{dot,_} = Dot | Rest]} -> 143 case erl_parse:parse_term(Tokens2 ++ [Dot]) of 144 {ok, Term} -> 145 prim_parse(Rest, [Term | Acc]); 146 {error, {_ErrorLine, Module, Reason}} -> 147 {error, Module:format_error(Reason)} 148 end; 149 {Tokens2, []} -> 150 case erl_parse:parse_term(Tokens2) of 151 {ok, Term} -> 152 {ok, lists:reverse([Term | Acc])}; 153 {error, {_ErrorLine, Module, Reason}} -> 154 {error, Module:format_error(Reason)} 155 end 156 end. 157 158%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 159 160default_rels() -> 161 %% kernel and stdlib are added automatically in every release 162 [ 163 #rel{name = ?DEFAULT_REL_NAME, 164 vsn = "1.0", 165 rel_apps = []}, 166 #rel{name = "start_sasl", 167 vsn = "1.0", 168 rel_apps = [#rel_app{name = sasl}]}, 169 #rel{name = "no_dot_erlang", %% Needed by escript and erlc 170 vsn = "1.0", 171 rel_apps = [], 172 load_dot_erlang = false 173 } 174 ]. 175 176choose_default(Tag, Profile, InclDefs) 177 when Profile =:= ?DEFAULT_PROFILE; InclDefs -> 178 case Tag of 179 incl_sys_filters -> ?DEFAULT_INCL_SYS_FILTERS; 180 excl_sys_filters -> ?DEFAULT_EXCL_SYS_FILTERS; 181 incl_app_filters -> ?DEFAULT_INCL_APP_FILTERS; 182 excl_app_filters -> ?DEFAULT_EXCL_APP_FILTERS; 183 embedded_app_type -> ?DEFAULT_EMBEDDED_APP_TYPE 184 end; 185choose_default(Tag, standalone, _InclDefs) -> 186 case Tag of 187 incl_sys_filters -> ?STANDALONE_INCL_SYS_FILTERS; 188 excl_sys_filters -> ?STANDALONE_EXCL_SYS_FILTERS; 189 incl_app_filters -> ?STANDALONE_INCL_APP_FILTERS; 190 excl_app_filters -> ?STANDALONE_EXCL_APP_FILTERS; 191 embedded_app_type -> ?DEFAULT_EMBEDDED_APP_TYPE 192 end; 193choose_default(Tag, embedded, _InclDefs) -> 194 case Tag of 195 incl_sys_filters -> ?EMBEDDED_INCL_SYS_FILTERS; 196 excl_sys_filters -> ?EMBEDDED_EXCL_SYS_FILTERS; 197 incl_app_filters -> ?EMBEDDED_INCL_APP_FILTERS; 198 excl_app_filters -> ?EMBEDDED_EXCL_APP_FILTERS; 199 embedded_app_type -> ?EMBEDDED_APP_TYPE 200 end. 201 202%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 203 204assign_image_list(ListCtrl) -> 205 Art = wxImageList:new(16,16), 206 [wxImageList:add(Art, wxArtProvider:getBitmap(Image, [{size, {16,16}}])) 207 || Image <- ["wxART_ERROR", 208 "wxART_WARNING", 209 "wxART_QUESTION", 210 "wxART_TICK_MARK", 211 "wxART_CROSS_MARK", 212 "wxART_GO_HOME"]], 213 wxListCtrl:assignImageList(ListCtrl, Art, ?wxIMAGE_LIST_SMALL). 214 215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 216 217get_latest_resize(#wx{obj = ObjRef, event = #wxSize{}} = Wx) -> 218 receive 219 #wx{obj = ObjRef, event = #wxSize{}} = Wx2 -> 220 get_latest_resize(Wx2) 221 after 10 -> 222 Wx 223 end. 224 225%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 226 227wait_for_stop_motion(ObjRef, {_,_}=Pos) -> 228 receive 229 #wx{obj = ObjRef, event = #wxMouse{type = motion, x=X, y=Y}} -> 230 wait_for_stop_motion(ObjRef, {X,Y}) 231 after 100 -> 232 Pos 233 end. 234 235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 236 237mod_conds() -> 238 ["all (ebin + app file)", "ebin + derived", "app file + derived", "derived", "none"]. 239 240list_to_mod_cond(List) -> 241 case List of 242 "all" ++ _ -> all; 243 "ebin" ++ _ -> ebin; 244 "app" ++ _ -> app; 245 "derived" -> derived; 246 "none" -> none 247 end. 248 249mod_cond_to_index(ModCond) -> 250 case ModCond of 251 all -> 0; 252 ebin -> 1; 253 app -> 2; 254 derived -> 3; 255 undefined -> 3; 256 none -> 4 257 end. 258 259incl_conds() -> 260 ["include", "exclude", "derived"]. 261 262list_to_incl_cond(List) -> 263 case List of 264 "include" -> include; 265 "exclude" -> exclude; 266 "derived" -> derived 267 end. 268 269incl_cond_to_index(ModCond) -> 270 case ModCond of 271 include -> 0; 272 exclude -> 1; 273 derived -> 2 274 end. 275 276elem_to_index(Elem, List) -> 277 elem_to_index(Elem, List, 1). 278 279elem_to_index(Elem, [H | T], Index) -> 280 case Elem =:= H of 281 true -> Index; 282 false -> elem_to_index(Elem, T, Index + 1) 283 end; 284elem_to_index(Elem, [], _) -> 285 erlang:error({not_found, Elem}). 286 287app_dir_test(Dir1, Dir2) -> 288 {Name1, Vsn1, Parent1} = split_app_dir(Dir1), 289 {Name2, Vsn2, Parent2} = split_app_dir(Dir2), 290 if 291 Name1 < Name2 -> true; 292 Name1 > Name2 -> false; 293 Vsn1 < Vsn2 -> false; 294 Vsn1 > Vsn2 -> true; 295 Parent1 =< Parent2 -> true; 296 true -> false 297 end. 298 299split_app_dir(Dir) -> 300 ParentDir = filename:dirname(Dir), 301 Base = filename:basename(Dir), 302 {Name, Vsn} = split_app_name(Base), 303 Vsn2 = 304 try 305 [list_to_integer(N) || N <- string:lexemes(Vsn, ".")] 306 catch 307 _:_ -> 308 Vsn 309 end, 310 {Name, Vsn2, ParentDir}. 311 312get_item(ListCtrl) -> 313 case wxListCtrl:getItemCount(ListCtrl) of 314 0 -> 315 undefined; 316 _ -> 317 case wxListCtrl:getNextItem(ListCtrl, 318 -1, 319 [{geometry, ?wxLIST_NEXT_ALL}, 320 {state, ?wxLIST_STATE_SELECTED}]) of 321 -1 -> 322 ItemNo = wxListCtrl:getTopItem(ListCtrl), 323 case wxListCtrl:getItemText(ListCtrl, ItemNo) of 324 "" -> 325 undefined; 326 Text -> 327 {ItemNo, Text} 328 end; 329 ItemNo -> 330 Text = wxListCtrl:getItemText(ListCtrl, ItemNo), 331 {ItemNo, Text} 332 end 333 end. 334 335get_items(ListCtrl) -> 336 case wxListCtrl:getItemCount(ListCtrl) of 337 0 -> 338 []; 339 Count -> 340 case get_selected_items(ListCtrl, -1, []) of 341 [] -> 342 ItemNo = wxListCtrl:getTopItem(ListCtrl), 343 case wxListCtrl:getItemText(ListCtrl, ItemNo) of 344 "" -> 345 []; 346 Text when Text =/= ?MISSING_APP_TEXT -> 347 [{ItemNo, Text}]; 348 _MissingText when Count > 1 -> 349 case wxListCtrl:getItemText(ListCtrl, ItemNo + 1) of 350 "" -> 351 []; 352 Text -> 353 [{ItemNo, Text}] 354 end; 355 _MissingText -> 356 [] 357 end; 358 Items -> 359 Items 360 end 361 end. 362 363get_selected_items(ListCtrl, PrevItem, Acc) -> 364 case wxListCtrl:getNextItem(ListCtrl, 365 PrevItem, 366 [{geometry, ?wxLIST_NEXT_ALL}, 367 {state, ?wxLIST_STATE_SELECTED}]) of 368 -1 -> 369 Acc; 370 ItemNo -> 371 case wxListCtrl:getItemText(ListCtrl, ItemNo) of 372 Text when Text =/= ?MISSING_APP_TEXT -> 373 get_selected_items(ListCtrl, 374 ItemNo, 375 [{ItemNo, Text} | Acc]); 376 _Text -> 377 get_selected_items(ListCtrl, ItemNo, Acc) 378 end 379 end. 380 381select_items(_ListCtrl, _OldItems, []) -> 382 %% No new items. Nothing to select. 383 false; 384select_items(ListCtrl, [], Items) -> 385 %% No old selection. Select first. 386 select_item(ListCtrl, Items); 387select_items(ListCtrl, _OldItems, [Item]) -> 388 %% Only one new item. Select it. 389 select_item(ListCtrl, [Item]); 390select_items(ListCtrl, OldItems, NewItems) -> 391 %% Try to propagate old selection to new items. 392 Filter = 393 fun({_OldItemNo, Text}) -> 394 case lists:keysearch(Text, 2, NewItems) of 395 {value, Item} -> {true, Item}; 396 false -> false 397 end 398 end, 399 case lists:zf(Filter, OldItems) of 400 [] -> 401 %% None of the old selections are valid. Select the first. 402 select_item(ListCtrl, NewItems); 403 ValidItems -> 404 %% Some old selections are still valid. Select them again. 405 lists:foreach(fun(Item) -> select_item(ListCtrl, [Item]) end, 406 ValidItems) 407 end. 408 409select_item(ListCtrl, [{ItemNo, Text} | Items]) -> 410 case Text =:= ?MISSING_APP_TEXT of 411 true -> 412 select_item(ListCtrl, Items); 413 false -> 414 StateMask = ?wxLIST_STATE_SELECTED, 415 State = wxListCtrl:getItemState(ListCtrl, ItemNo, StateMask), 416 State2 = State bor ?wxLIST_STATE_SELECTED, 417 wxListCtrl:setItemState(ListCtrl, ItemNo, State2, StateMask), 418 wxListCtrl:refreshItem(ListCtrl, ItemNo) 419 end; 420select_item(_ListCtrl, []) -> 421 ok. 422 423get_column_width(ListCtrl) -> 424 wx:batch(fun() -> 425 {Total, _} = wxWindow:getClientSize(ListCtrl), 426 Total - scroll_size(ListCtrl) 427 end). 428 429scroll_size(ObjRef) -> 430 case os:type() of 431 {win32, nt} -> 0; 432 {unix, darwin} -> 433 %% I can't figure out is there is a visible scrollbar 434 %% Always make room for it 435 wxSystemSettings:getMetric(?wxSYS_VSCROLL_X); 436 _ -> 437 case wxWindow:hasScrollbar(ObjRef, ?wxVERTICAL) of 438 true -> wxSystemSettings:getMetric(?wxSYS_VSCROLL_X); 439 false -> 0 440 end 441 end. 442 443safe_keysearch(Key, Pos, List, Mod, Line) -> 444 case lists:keysearch(Key, Pos, List) of 445 false -> 446 io:format("~w(~w): lists:keysearch(~tp, ~w, ~tp) -> false\n", 447 [Mod, Line, Key, Pos, List]), 448 erlang:error({Mod, Line, lists, keysearch, [Key, Pos, List]}); 449 {value, Val} -> 450 Val 451 end. 452 453print(X, X, Format, Args) -> 454 io:format(Format, Args); 455print(_, _, _, _) -> 456 ok. 457 458add_warning(Format, Args, {ok,Warnings}) -> 459 Warning = lists:flatten(io_lib:format(Format,Args)), 460 case lists:member(Warning,Warnings) of 461 true -> 462 {ok,Warnings}; 463 false -> 464 {ok,[Warning|Warnings]} 465 end. 466 467%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 468 469create_dir(Dir) -> 470 filelib:ensure_dir(Dir), 471 case file:make_dir(Dir) of 472 ok -> 473 ok; 474 {error, eexist} -> 475 ok; 476 {error, Reason} -> 477 Text = file:format_error(Reason), 478 throw_error("create dir ~ts: ~ts", [Dir, Text]) 479 end. 480 481list_dir(Dir) -> 482 case erl_prim_loader:list_dir(Dir) of 483 {ok, Files} -> 484 Files; 485 error -> 486 Text = file:format_error(enoent), 487 throw_error("list dir ~ts: ~ts", [Dir, Text]) 488 end. 489 490read_file_info(File) -> 491 case file:read_file_info(File) of 492 {ok, Info} -> 493 Info; 494 {error, Reason} -> 495 Text = file:format_error(Reason), 496 throw_error("read file info ~ts: ~ts", [File, Text]) 497 end. 498 499write_file_info(File, Info) -> 500 case file:write_file_info(File, Info) of 501 ok -> 502 ok; 503 {error, Reason} -> 504 Text = file:format_error(Reason), 505 throw_error("write file info ~ts: ~ts", [File, Text]) 506 end. 507 508read_file(File) -> 509 case file:read_file(File) of 510 {ok, Bin} -> 511 Bin; 512 {error, Reason} -> 513 Text = file:format_error(Reason), 514 throw_error("read file ~ts: ~ts", [File, Text]) 515 end. 516 517write_file(File, Bin) -> 518 case file:write_file(File, Bin) of 519 ok -> 520 ok; 521 {error, Reason} -> 522 Text = file:format_error(Reason), 523 throw_error("write file ~ts: ~ts", [File, Text]) 524 end. 525 526recursive_delete(Dir) -> 527 case filelib:is_dir(Dir) of 528 true -> 529 case file:list_dir(Dir) of 530 {ok, Files} -> 531 Fun = 532 fun(F) -> recursive_delete(filename:join([Dir, F])) end, 533 lists:foreach(Fun, Files), 534 delete(Dir, directory); 535 {error, enoent} -> 536 ok; 537 {error, Reason} -> 538 Text = file:format_error(Reason), 539 throw_error("delete file ~ts: ~ts\n", [Dir, Text]) 540 end; 541 false -> 542 delete(Dir, regular) 543 end. 544 545delete(File, Type) -> 546 case do_delete(File, Type) of 547 ok -> 548 ok; 549 {error, enoent} -> 550 ok; 551 {error, Reason} -> 552 Text = file:format_error(Reason), 553 throw_error("delete file ~ts: ~ts\n", [File, Text]) 554 end. 555 556do_delete(File, regular) -> 557 file:delete(File); 558do_delete(Dir, directory) -> 559 file:del_dir(Dir). 560 561recursive_copy_file(From, To) -> 562 case erl_prim_loader:list_dir(From) of 563 {ok, Files} -> 564 %% Copy all files in the directory 565 create_dir(To), 566 Copy = 567 fun(F) -> 568 recursive_copy_file(filename:join([From, F]), 569 filename:join([To, F])) 570 end, 571 lists:foreach(Copy, Files); 572 error -> 573 %% Copy single file 574 copy_file(From, To) 575 end. 576 577copy_file(From, To) -> 578 case erl_prim_loader:get_file(From) of 579 {ok, Bin, _} -> 580 case file:write_file(To, Bin) of 581 ok -> 582 FromInfo = read_file_info(From), 583 ToInfo = read_file_info(To), 584 FromMode = FromInfo#file_info.mode, 585 ToMode = ToInfo#file_info.mode, 586 ToMode2 = FromMode bor ToMode, 587 FileInfo = ToInfo#file_info{mode = ToMode2}, 588 write_file_info(To, FileInfo), 589 ok; 590 {error, Reason} -> 591 Text = file:format_error(Reason), 592 throw_error("copy file ~ts -> ~ts: ~ts\n", [From, To, Text]) 593 end; 594 error -> 595 Text = file:format_error(enoent), 596 throw_error("copy file ~ts -> ~ts: ~ts\n", [From, To, Text]) 597 end. 598 599throw_error(Format, Args) -> 600 throw({error, lists:flatten(io_lib:format(Format, Args))}). 601 602%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 603 604decode_regexps(Key, Regexps, undefined) -> 605 decode_regexps(Key, Regexps, []); 606decode_regexps(Key, {add, Regexps}, Old) when is_list(Regexps) -> 607 do_decode_regexps(Key, Regexps, Old); 608decode_regexps(_Key, {del, Regexps}, Old) when is_list(Regexps) -> 609 [Re || Re <- Old, not lists:member(Re#regexp.source, Regexps)]; 610decode_regexps(Key, Regexps, _Old) when is_list(Regexps) -> 611 do_decode_regexps(Key, Regexps, []). 612 613do_decode_regexps(Key, [Regexp | Regexps], Acc) -> 614 case catch re:compile(Regexp, [unicode]) of 615 {ok, MP} -> 616 do_decode_regexps(Key, 617 Regexps, 618 [#regexp{source = Regexp, compiled = MP} | Acc]); 619 _ -> 620 Text = lists:flatten(io_lib:format("~tp", [{Key, Regexp}])), 621 throw({error, "Illegal option: " ++ Text}) 622 end; 623do_decode_regexps(_Key, [], Acc) -> 624 lists:sort(Acc). 625 626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 627 628default_val(Val, Default) -> 629 case Val of 630 undefined -> Default; 631 _ -> Val 632 end. 633 634%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 635 636escript_foldl(Fun, Acc, File) -> 637 case escript:extract(File, [compile_source]) of 638 {ok, [_Shebang, _Comment, _EmuArgs, Body]} -> 639 case Body of 640 {source, BeamCode} -> 641 GetInfo = fun() -> file:read_file_info(File) end, 642 GetBin = fun() -> BeamCode end, 643 {ok, Fun(".", GetInfo, GetBin, Acc)}; 644 {beam, BeamCode} -> 645 GetInfo = fun() -> file:read_file_info(File) end, 646 GetBin = fun() -> BeamCode end, 647 {ok, Fun(".", GetInfo, GetBin, Acc)}; 648 {archive, ArchiveBin} -> 649 zip:foldl(Fun, Acc, {File, ArchiveBin}) 650 end; 651 {error, Reason} -> 652 {error, Reason} 653 end. 654 655%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 656 657call(Name, Msg) when is_atom(Name) -> 658 case whereis(Name) of 659 undefined -> 660 {error, {noproc, Name}}; 661 Pid -> 662 call(Pid, Msg) 663 end; 664call(Pid, Msg) when is_pid(Pid) -> 665 Ref = erlang:monitor(process, Pid), 666 Pid ! {call, self(), Ref, Msg}, 667 receive 668 {Ref, Reply} -> 669 Reply; 670 {'EXIT', Pid, Reason} -> 671 erlang:demonitor(Ref, [flush]), 672 {error, Reason}; 673 {'DOWN', Ref, _, _, Reason} -> 674 {error, Reason} 675 end. 676 677cast(Pid, Msg) -> 678 Pid ! {cast, self(), Msg}, 679 ok. 680 681reply(Pid, Ref, Msg) -> 682 Pid ! {Ref, Msg}. 683