diff options
Diffstat (limited to 'lib/kernel/src/os.erl')
-rw-r--r-- | lib/kernel/src/os.erl | 96 |
1 files changed, 52 insertions, 44 deletions
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index c2efab1ef5..b5f19d4b99 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -21,10 +21,12 @@ %% Provides a common operating system interface. --export([type/0, version/0, cmd/1, cmd/2, find_executable/1, find_executable/2]). +-export([type/0, version/0, cmd/1, find_executable/1, find_executable/2]). -include("file.hrl"). +-export_type([env_var_name/0, env_var_value/0, env_var_name_value/0, command_input/0]). + %%% BIFs -export([getenv/0, getenv/1, getenv/2, getpid/0, @@ -32,26 +34,29 @@ putenv/2, set_signal/2, system_time/0, system_time/1, timestamp/0, unsetenv/1]). --type os_command() :: atom() | io_lib:chars(). --type os_command_opts() :: #{ max_size => non_neg_integer() | infinity }. +-type env_var_name() :: nonempty_string(). + +-type env_var_value() :: string(). + +-type env_var_name_value() :: nonempty_string(). --export_type([os_command/0, os_command_opts/0]). +-type command_input() :: atom() | io_lib:chars(). --spec getenv() -> [string()]. +-spec getenv() -> [env_var_name_value()]. getenv() -> erlang:nif_error(undef). -spec getenv(VarName) -> Value | false when - VarName :: string(), - Value :: string(). + VarName :: env_var_name(), + Value :: env_var_value(). getenv(_) -> erlang:nif_error(undef). -spec getenv(VarName, DefaultValue) -> Value when - VarName :: string(), - DefaultValue :: string(), - Value :: string(). + VarName :: env_var_name(), + DefaultValue :: env_var_value(), + Value :: env_var_value(). getenv(VarName, DefaultValue) -> case os:getenv(VarName) of @@ -80,8 +85,8 @@ perf_counter(Unit) -> erlang:convert_time_unit(os:perf_counter(), perf_counter, Unit). -spec putenv(VarName, Value) -> true when - VarName :: string(), - Value :: string(). + VarName :: env_var_name(), + Value :: env_var_value(). putenv(_, _) -> erlang:nif_error(undef). @@ -104,7 +109,7 @@ timestamp() -> erlang:nif_error(undef). -spec unsetenv(VarName) -> true when - VarName :: string(). + VarName :: env_var_name(). unsetenv(_) -> erlang:nif_error(undef). @@ -183,7 +188,7 @@ verify_executable(Name0, [Ext|Rest], OrigExtensions) -> end; verify_executable(Name, [], OrigExtensions) when OrigExtensions =/= [""] -> %% Windows %% Will only happen on windows, hence case insensitivity - case can_be_full_name(string:to_lower(Name),OrigExtensions) of + case can_be_full_name(string:lowercase(Name),OrigExtensions) of true -> verify_executable(Name,[""],[""]); _ -> @@ -237,21 +242,14 @@ extensions() -> %% Executes the given command in the default shell for the operating system. -spec cmd(Command) -> string() when - Command :: os_command(). + Command :: os:command_input(). cmd(Cmd) -> - cmd(Cmd, #{ }). - --spec cmd(Command, Options) -> string() when - Command :: os_command(), - Options :: os_command_opts(). -cmd(Cmd, Opts) -> - validate(Cmd), - {SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), Cmd), + {SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), validate(Cmd)), Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout, stream, in, hide | SpawnOpts]), MonRef = erlang:monitor(port, Port), true = port_command(Port, SpawnInput), - Bytes = get_data(Port, MonRef, Eot, [], 0, maps:get(max_size, Opts, infinity)), + Bytes = get_data(Port, MonRef, Eot, []), demonitor(MonRef, [flush]), String = unicode:characters_to_list(Bytes), if %% Convert to unicode list if possible otherwise return bytes @@ -266,8 +264,6 @@ mk_cmd({win32,Wtype}, Cmd) -> {Cspec,_} -> lists:concat([Cspec," /c",Cmd]) end, {Command, [], [], <<>>}; -mk_cmd(OsType,Cmd) when is_atom(Cmd) -> - mk_cmd(OsType, atom_to_list(Cmd)); mk_cmd(_,Cmd) -> %% Have to send command in like this in order to make sh commands like %% cd and ulimit available @@ -290,25 +286,40 @@ mk_cmd(_,Cmd) -> <<$\^D>>}. validate(Atom) when is_atom(Atom) -> - ok; + validate(atom_to_list(Atom)); validate(List) when is_list(List) -> - validate1(List). + case validate1(List) of + false -> + List; + true -> + %% Had zeros at end; remove them... + string:trim(List, trailing, [0]) + end. -validate1([C|Rest]) when is_integer(C) -> +validate1([0|Rest]) -> + validate2(Rest); +validate1([C|Rest]) when is_integer(C), C > 0 -> validate1(Rest); validate1([List|Rest]) when is_list(List) -> - validate1(List), - validate1(Rest); + validate1(List) or validate1(Rest); validate1([]) -> - ok. + false. + +%% Ensure that the rest is zero only... +validate2([]) -> + true; +validate2([0|Rest]) -> + validate2(Rest); +validate2([List|Rest]) when is_list(List) -> + validate2(List), + validate2(Rest). -get_data(Port, MonRef, Eot, Sofar, Size, Max) -> +get_data(Port, MonRef, Eot, Sofar) -> receive {Port, {data, Bytes}} -> - case eot(Bytes, Eot, Size, Max) of + case eot(Bytes, Eot) of more -> - get_data(Port, MonRef, Eot, [Sofar, Bytes], - Size + size(Bytes), Max); + get_data(Port, MonRef, Eot, [Sofar,Bytes]); Last -> catch port_close(Port), flush_until_down(Port, MonRef), @@ -319,16 +330,13 @@ get_data(Port, MonRef, Eot, Sofar, Size, Max) -> iolist_to_binary(Sofar) end. -eot(_Bs, <<>>, _Size, _Max) -> +eot(_Bs, <<>>) -> more; -eot(Bs, Eot, Size, Max) -> +eot(Bs, Eot) -> case binary:match(Bs, Eot) of - nomatch when Size + size(Bs) < Max -> - more; - {Pos, _} when Size + Pos < Max -> - binary:part(Bs,{0, Pos}); - _ -> - binary:part(Bs,{0, Max - Size}) + nomatch -> more; + {Pos, _} -> + binary:part(Bs,{0, Pos}) end. %% When port_close returns we know that all the |