Skip to content

Commit

Permalink
add new script runner borrowed from rebar
Browse files Browse the repository at this point in the history
  • Loading branch information
ericbmerritt committed May 12, 2012
1 parent 3198e8c commit 53fe047
Showing 1 changed file with 86 additions and 0 deletions.
86 changes: 86 additions & 0 deletions src/sin_sh.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
%% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*-
%%%---------------------------------------------------------------------------
%%% @author Dave Smith
%%% @author Eric Merritt
%%% @doc
%%% Run sh command with reasonable return values. This was originally taken
%%% from rebar and but has been much changed since then.
%%% @end
%%%---------------------------------------------------------------------------
-module(sin_sh).

%% API
-export([sh/3]).

%%====================================================================
%% API
%%====================================================================
%%
%% Env = [{string(), Val}]
%% Val = string() | false
%%
sh(Config, Command0, Options0) ->
Options = [expand_sh_flag(V)
|| V <- proplists:compact(Options0)],

Command = patch_on_windows(Command0, proplists:get_value(env, Options, [])),
PortSettings = proplists:get_all_values(port_settings, Options) ++
[exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide],
Port = open_port({spawn, Command}, PortSettings),
sh_loop(Port,
fun(Output, Acc) ->
sin_log:normal(Config, Output),
[Output | Acc]
end, []).

%%====================================================================
%% Internal Functions
%%====================================================================
sh_loop(Port, Fun, Acc) ->
receive
{Port, {data, {eol, Line}}} ->
sh_loop(Port, Fun, Fun(Line ++ "\n", Acc));
{Port, {data, {noeol, Line}}} ->
sh_loop(Port, Fun, Fun(Line, Acc));
{Port, {exit_status, 0}} ->
{ok, lists:flatten(lists:reverse(Acc))};
{Port, {exit_status, Rc}} ->
{error, {Rc, lists:flatten(lists:reverse(Acc))}}
end.

%% We do the shell variable substitution ourselves on Windows and hope that the
%% command doesn't use any other shell magic.
patch_on_windows(Cmd, Env) ->
case os:type() of
{win32,nt} ->
"cmd /q /c "
++ lists:foldl(fun({Key, Value}, Acc) ->
expand_env_variable(Acc, Key, Value)
end, Cmd, Env);
_ ->
Cmd
end.

%%
%% Given env. variable FOO we want to expand all references to
%% it in InStr. References can have two forms: $FOO and ${FOO}
%% The end of form $FOO is delimited with whitespace or eol
%%
expand_env_variable(InStr, VarName, RawVarValue) ->
case string:chr(InStr, $$) of
0 ->
%% No variables to expand
InStr;
_ ->
VarValue = re:replace(RawVarValue, "\\\\", "\\\\\\\\", [global]),
%% Use a regex to match/replace:
%% Given variable "FOO": match $FOO\s | $FOOeol | ${FOO}
RegEx = io_lib:format("\\\$(~s(\\s|$)|{~s})", [VarName, VarName]),
ReOpts = [global, {return, list}],
re:replace(InStr, RegEx, [VarValue, "\\2"], ReOpts)
end.

expand_sh_flag({cd, _CdArg} = Cd) ->
{port_settings, Cd};
expand_sh_flag({env, _EnvArg} = Env) ->
{port_settings, Env}.

0 comments on commit 53fe047

Please sign in to comment.