shell.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,268 行 · 第 1/3 页
ERL
1,268 行
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %% $Id $%%-module(shell).-export([start/0, start/1, start/2, server/1, server/2, history/1, results/1]).-export([whereis_evaluator/0, whereis_evaluator/1]).-export([start_restricted/1, stop_restricted/0]).-export([local_allowed/3, non_local_allowed/3]).-define(LINEMAX, 30).-define(DEF_HISTORY, 20).-define(DEF_RESULTS, 20).-define(RECORDS, shell_records).-define(MAXSIZE_HEAPBINARY, 64).% When used as the fallback restricted shell callback module...local_allowed(q,[],State) -> {true,State};local_allowed(_,_,State) -> {false,State}.non_local_allowed({init,stop},[],State) -> {true,State};non_local_allowed(_,_,State) -> {false,State}.start() -> start(false, false).start(init) -> start(false, true);start(NoCtrlG) -> start(NoCtrlG, false).start(NoCtrlG, StartSync) -> code:ensure_loaded(user_default), spawn(fun() -> server(NoCtrlG, StartSync) end).%% Find the pid of the current evaluator process.whereis_evaluator() -> %% locate top group leader, always registered as user %% can be implemented by group (normally) or user %% (if oldshell or noshell) case whereis(user) of undefined -> undefined; User -> %% get user_drv pid from group, or shell pid from user case group:interfaces(User) of [] -> % old- or noshell case user:interfaces(User) of [] -> undefined; [{shell,Shell}] -> whereis_evaluator(Shell) end; [{user_drv,UserDrv}] -> %% get current group pid from user_drv case user_drv:interfaces(UserDrv) of [] -> undefined; [{current_group,Group}] -> %% get shell pid from group GrIfs = group:interfaces(Group), case lists:keysearch(shell, 1, GrIfs) of {value,{shell,Shell}} -> whereis_evaluator(Shell); false -> undefined end end end end.whereis_evaluator(Shell) -> case process_info(Shell, dictionary) of {dictionary,Dict} -> case lists:keysearch(evaluator, 1, Dict) of {value,{_,Eval}} when is_pid(Eval) -> Eval; _ -> undefined end; _ -> undefined end. %% Call this function to start a user restricted shell %% from a normal shell session.start_restricted(RShMod) when is_atom(RShMod) -> case code:ensure_loaded(RShMod) of {module,RShMod} -> application:set_env(stdlib, restricted_shell, RShMod), exit('restricted shell starts now'); {error,What} -> error_logger:error_report( lists:flatten( io_lib:format( "Restricted shell module ~w not found: ~p~n", [RShMod,What]))), {error,What} end.stop_restricted() -> application:unset_env(stdlib, restricted_shell), exit('restricted shell stopped').default_packages() -> [].%%% ['erl','erl.lang'].default_modules() -> [].%%% [{pdict, 'erl.lang.proc.pdict'},%%% {keylist, 'erl.lang.list.keylist'},%%% {debug, 'erl.system.debug'}].server(NoCtrlG, StartSync) -> put(no_control_g, NoCtrlG), server(StartSync).%%% The shell should not start until the system is up and running.%%% We subscribe with init to get a notification of when.%%% In older releases we didn't syncronize the shell with init, but let it%%% start in parallell with other system processes. This was bad since %%% accessing the shell too early could interfere with the boot procedure.%%% Still, by means of a flag, we make it possible to start the shell the%%% old way (for backwards compatibility reasons). This should however not %%% be used unless for very special reasons necessary. server(StartSync) -> case init:get_argument(async_shell_start) of {ok,_} -> ok; % no sync with init _ when not StartSync -> ok; _ -> case init:notify_when_started(self()) of started -> ok; _ -> init:wait_until_started() end end, %% Our spawner has fixed the process groups. Bs0 = erl_eval:new_bindings(), Bs = lists:foldl(fun ({K, V}, D) -> erl_eval:add_binding({module,K}, V, D) end, lists:foldl(fun (P, D) -> import_all(P, D) end, Bs0, default_packages()), default_modules()), %% io:fwrite("Imported modules: ~p.\n", [erl_eval:bindings(Bs)]), %% Use an Ets table for record definitions. It takes too long to %% send a huge term to and from the evaluator. Ets makes it %% possible to have thousands of record definitions. RT = ets:new(?RECORDS, [public,ordered_set]), _ = initiate_records(Bs, RT), process_flag(trap_exit, true), %% Check if we're in user restricted mode. RShErr = case application:get_env(stdlib, restricted_shell) of {ok,RShMod} -> io:fwrite("Restricted ", []), case code:ensure_loaded(RShMod) of {module,RShMod} -> undefined; {error,What} -> {RShMod,What} end; undefined -> undefined end, case get(no_control_g) of true -> io:fwrite("Eshell V~s~n", [erlang:system_info(version)]); _undefined_or_false -> io:fwrite("Eshell V~s (abort with ^G)~n", [erlang:system_info(version)]) end, erase(no_control_g), case RShErr of undefined -> ok; {RShMod2,What2} -> io:fwrite("Warning! Restricted shell module ~w not found: ~p.~nOnly the commands q() and init:stop() will be allowed!~n", [RShMod2,What2]), application:set_env(stdlib, restricted_shell, ?MODULE) end, {History,Results} = check_and_get_history_and_results(), server_loop(0, start_eval(Bs, RT, []), Bs, RT, [], History, Results).server_loop(N0, Eval_0, Bs0, RT, Ds0, History0, Results0) -> N = N0 + 1, {Res, Eval0} = get_command(prompt(N), Eval_0, Bs0, RT, Ds0), case Res of {ok,Es0,_EndLine} -> case expand_hist(Es0, N) of {ok,Es} -> {V,Eval,Bs,Ds} = shell_cmd(Es, Eval0, Bs0, RT, Ds0), {History,Results} = check_and_get_history_and_results(), add_cmd(N, Es, V), HB1 = del_cmd(command, N - History, N - History0, false), HB = del_cmd(result, N - Results, N - Results0, HB1), %% The following test makes sure that large binaries %% (outside of the heap) are garbage collected as soon %% as possible. if HB -> erlang:garbage_collect(); true -> ok end, server_loop(N, Eval, Bs, RT, Ds, History, Results); {error,E} -> io:fwrite("** ~s **\n", [E]), server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0) end; {error,{Line,Mod,What},_EndLine} -> io:fwrite("** ~w: ~s **\n", [Line,Mod:format_error(What)]), server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0); {error,terminated} -> %Io process terminated exit(Eval0, kill), terminated; {error,interrupted} -> %Io process interrupted us exit(Eval0, kill), {_,Eval,_,_} = shell_rep(Eval0, Bs0, RT, Ds0), server_loop(N0, Eval, Bs0, RT, Ds0, History0, Results0); {eof,_EndLine} -> io:fwrite("** Terminating erlang (~w) **\n", [node()]), halt(); eof -> io:fwrite("** Terminating erlang (~w) **\n", [node()]), halt() end.get_command(Prompt, Eval, Bs, RT, Ds) -> Parse = fun() -> exit(io:parse_erl_exprs(Prompt)) end, Pid = spawn_link(Parse), get_command1(Pid, Eval, Bs, RT, Ds).get_command1(Pid, Eval, Bs, RT, Ds) -> receive {'EXIT', Pid, Res} -> {Res, Eval}; {'EXIT', Eval, Reason} -> io:fwrite("** exited: ~P **\n", [Reason, ?LINEMAX]), get_command1(Pid, start_eval(Bs, RT, Ds), Bs, RT, Ds) end.prompt(N) -> case is_alive() of true -> {format,"(~s)~w> ",[node(),N]}; false -> {format,"~w> ",[N]} end.%% expand_hist(Expressions, CommandNumber)%% Preprocess the expression list replacing all history list commands%% with their expansions.expand_hist(Es, C) -> catch {ok,expand_exprs(Es, C)}.expand_exprs([E|Es], C) -> [expand_expr(E, C)|expand_exprs(Es, C)];expand_exprs([], _C) -> [].expand_expr({cons,L,H,T}, C) -> {cons,L,expand_expr(H, C),expand_expr(T, C)};expand_expr({lc,L,E,Qs}, C) -> {lc,L,expand_expr(E, C),expand_quals(Qs, C)};expand_expr({tuple,L,Elts}, C) -> {tuple,L,expand_exprs(Elts, C)};expand_expr({record_index,L,Name,F}, C) -> {record_index,L,Name,expand_expr(F, C)};expand_expr({record,L,Name,Is}, C) -> {record,L,Name,expand_fields(Is, C)};expand_expr({record_field,L,R,Name,F}, C) -> {record_field,L,expand_expr(R, C),Name,expand_expr(F, C)};expand_expr({record,L,R,Name,Ups}, C) -> {record,L,expand_expr(R, C),Name,expand_fields(Ups, C)};expand_expr({record_field,L,R,F}, C) -> %This is really illegal! {record_field,L,expand_expr(R, C),expand_expr(F, C)};expand_expr({block,L,Es}, C) -> {block,L,expand_exprs(Es, C)};expand_expr({'if',L,Cs}, C) -> {'if',L,expand_cs(Cs, C)};expand_expr({'case',L,E,Cs}, C) -> {'case',L,expand_expr(E, C),expand_cs(Cs, C)};expand_expr({'try',L,Es,Scs,Ccs}, C) -> {'try',L,expand_exprs(Es, C),expand_cs(Scs, C),expand_cs(Ccs, C)};expand_expr({'receive',L,Cs}, C) -> {'receive',L,expand_cs(Cs, C)};expand_expr({'receive',L,Cs,To,ToEs}, C) -> {'receive',L,expand_cs(Cs, C), expand_expr(To, C), expand_exprs(ToEs, C)};expand_expr({call,L,{atom,_,e},[N]}, C) -> case get_cmd(N, C) of {undefined,_,_} -> no_command(N); {[Ce],_V,_CommandN} -> Ce; {Ces,_V,_CommandN} when is_list(Ces) -> {block,L,Ces} end;expand_expr({call,_L,{atom,_,v},[N]}, C) -> case get_cmd(N, C) of {_,undefined,_} -> no_command(N); {Ces,V,CommandN} when is_list(Ces) -> {value,CommandN,V} end;expand_expr({call,L,F,Args}, C) -> {call,L,expand_expr(F, C),expand_exprs(Args, C)};expand_expr({'catch',L,E}, C) -> {'catch',L,expand_expr(E, C)};expand_expr({match,L,Lhs,Rhs}, C) -> {match,L,Lhs,expand_expr(Rhs, C)};expand_expr({op,L,Op,Arg}, C) -> {op,L,Op,expand_expr(Arg, C)};expand_expr({op,L,Op,Larg,Rarg}, C) -> {op,L,Op,expand_expr(Larg, C),expand_expr(Rarg, C)};expand_expr({remote,L,M,F}, C) -> {remote,L,expand_expr(M, C),expand_expr(F, C)};expand_expr({'fun',L,{clauses,Cs}}, C) -> {'fun',L,{clauses,expand_exprs(Cs, C)}};expand_expr({clause,L,H,G,B}, C) -> %% Could expand H and G, but then erl_eval has to be changed as well. {clause,L,H, G, expand_exprs(B, C)};expand_expr(E, _C) -> % Constants, including binaries. E.expand_cs([{clause,L,P,G,B}|Cs], C) -> [{clause,L,P,G,expand_exprs(B, C)}|expand_cs(Cs, C)];expand_cs([], _C) -> [].expand_fields([{record_field,L,F,V}|Fs], C) -> [{record_field,L,expand_expr(F, C),expand_expr(V, C)}| expand_fields(Fs, C)];expand_fields([], _C) -> [].expand_quals([{generate,L,P,E}|Qs], C) -> [{generate,L,P,expand_expr(E, C)}|expand_quals(Qs, C)];expand_quals([E|Qs], C) -> [expand_expr(E, C)|expand_quals(Qs, C)];expand_quals([], _C) -> [].no_command(N) -> throw({error,io_lib:fwrite("~s: command not found", [erl_pp:expr(N)])}).%% add_cmd(Number, Expressions, Value)%% get_cmd(Number, CurrentCommand)%% del_cmd(Number, NewN, OldN, HasBin0) -> bool()add_cmd(N, Es, V) -> put({command,N}, Es), put({result,N}, V).getc(N) -> {get({command,N}), get({result,N}), N}.get_cmd(Num, C) -> case catch erl_eval:expr(Num, []) of {value,N,_} when N < 0 -> getc(C+N); {value,N,_} -> getc(N); _Other -> {undefined,undefined,undefined} end.del_cmd(_Type, N, N0, HasBin) when N < N0 -> HasBin;del_cmd(Type, N, N0, HasBin0) -> T = erase({Type,N}), HasBin = HasBin0 orelse has_binary(T), del_cmd(Type, N-1, N0, HasBin).has_binary(T) -> try has_bin(T), false catch true=Thrown -> Thrown end.has_bin(T) when is_tuple(T) -> has_bin(T, size(T));has_bin([E | Es]) -> has_bin(E), has_bin(Es);has_bin(B) when is_binary(B), size(B) > ?MAXSIZE_HEAPBINARY -> throw(true);has_bin(T) -> T.has_bin(T, 0) -> T;has_bin(T, I) -> has_bin(element(I, T)),
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?