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