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 + -
显示快捷键?