<commit_before>%% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*-
%%%---------------------------------------------------------------------------
%%% @author <NAME>
%%% @doc
%%%  A group of utility functions for project automation.
%%% @end
%%% @copyright 2006 - 2011 Erlware
%%%---------------------------------------------------------------------------
-module(sin_utils).

-include_lib("kernel/include/file.hrl").
-include_lib("eunit/include/eunit.hrl").
-include("internal.hrl").

%% API
-export([copy_dir/3,
         copy_dir/4,
         copy_dir/5,
         delete_dir/1,
         file_exists/2,
         get_application_env/2,
         get_ignore_dirs/1,
         is_dir_ignorable/2,
         remove_code_paths/1,
         term_to_list/1,
         to_bool/1,
         format_exception/1,
         basename/1]).

%%====================================================================
%% API
%%====================================================================

%% @doc Trivially convert a value to a boolean
-spec to_bool(any()) -> boolean().
to_bool(true) ->
    true;
to_bool("True") ->
    true;
to_bool("true") ->
    true;
to_bool(_) ->
    false.

%% @doc Delete the directory and all of its sub directories.
%% @private
-spec delete_dir(Dir::string()) -> ok.
delete_dir(Dir) ->
    case file:list_dir(Dir) of
        {ok, Files} ->
            lists:foldl(fun (File, _Acc) ->
                                NFile = filename:join([Dir, File]),
                                case filelib:is_dir(NFile) of
                                    true ->
                                        case is_symlink(NFile) of
                                            true -> file:delete(NFile);
                                            false -> delete_dir(NFile)
                                        end;
                                    false -> file:delete(NFile)
                                end
                        end,
                        [], Files),
            file:del_dir(Dir);
        _ ->
            ok
    end.

%% @doc Get the ignore dirs
-spec get_ignore_dirs(sin_state:state()) ->
                             IgnoreDirs::[string()].
get_ignore_dirs(State) ->
    sin_state:get_value(ignore_dirs, [], State).

%% @doc Check to see if a file exists.
-spec file_exists(sin_state:state(), FileName::string()) -> boolean().
file_exists(State, FileName) ->
    case file:read_file_info(FileName) of
        {error, enoent} ->
            false;
        Error = {error, _} ->
            ?SIN_RAISE(State, {file_access, FileName}, Error);
        _ ->
            true
    end.

%% @doc Copies the specified directory down to the build dir on a file by file
%% basis. It only copies if the file has .
-spec copy_dir(sin_state:state(), BuilderDir::string(),
               TargetDir::string()) -> ok.
copy_dir(State, BuildDir, TargetDir) ->
    copy_dir(State, BuildDir, TargetDir, [], []).

-spec copy_dir(sin_state:state(), BuilderDir::string(),
               TargetDir::string(),
               Subdirs::string()) -> ok.
copy_dir(State, BuildDir, TargetDir, Sub) ->
    copy_dir(State, BuildDir, TargetDir, Sub, []).

-spec copy_dir(sin_state:state(),
               BuilderDir::string(),
               TargetDir::string(),
               Subdirs::string(),
               Ignorables::string()) -> sin_state:state().
copy_dir(State0, BuildDir, TargetDir, SubDir, Ignorables) ->
    check_not_circular(State0, BuildDir, TargetDir, SubDir),
    case are_dirs_ignorable(SubDir, Ignorables) of
        true ->
            State0;
        false ->
            Target = filename:join([BuildDir | SubDir]),
            filelib:ensure_dir(filename:join([Target, "tmp"])),
            CpyTarget = filename:join([TargetDir | SubDir]),
            {ok, Files} = file:list_dir(CpyTarget),
            lists:foldl(fun (IFile, State1) ->
                                File = filename:join([CpyTarget, IFile]),
                                case {is_dir_ignorable(IFile, Ignorables),
                                      filelib:is_dir(File)}
                                    of
                                    {true, _} ->
                                        State1;
                                    {_, true} ->
                                        copy_dir(State1,
                                                 BuildDir, TargetDir,
                                                 SubDir ++ [IFile], Ignorables);
                                    {_, false} ->
                                        copy_file(State1, Target, IFile, File)
                                end
                        end,
                        State0, Files)
    end.

%% @doc Remove the specified code paths from the system code paths.
-spec remove_code_paths(Paths::[string()]) -> ok.
remove_code_paths([Path | T]) ->
    code:del_path(Path), remove_code_paths(T);
remove_code_paths([]) ->
    ok.

%% @doc Return wether the directory is in the list of ignorables. If it is then
%%  return true, otherwise return false.
-spec is_dir_ignorable(Directory::string(), ListOfIgnores::[string()]) ->
    boolean().
is_dir_ignorable(Sub, [Ignore | Rest]) ->
    case ignore_dir(Sub, Ignore) of
        true ->
            true;
        false ->
            is_dir_ignorable(Sub, Rest)
    end;
is_dir_ignorable(_Sub, []) ->
    false.

%% Strip the extension off of a base.
-spec basename(string()) ->
    string().
basename(File) ->
    basename1(lists:reverse(File), []).

%% @doc Format an exception thrown by this module
-spec format_exception(sin_exceptions:exception()) ->
    string().
format_exception(Exception) ->
    sin_exceptions:format_exception(Exception).

%%====================================================================
%% Internal functions
%%====================================================================

%% @doc Copies the file specified by file to the target specified by ifile.
-spec copy_file(sin_state:state(),
                Target::string(), IFile::string(), File::string()) -> sin_state:state().
copy_file(_, _Target, [$. | _], _File) ->
             ok;
copy_file(State, Target, IFile, File) ->
    NFile = filename:join([Target, IFile]),
    case sin_sig:changed(copy, File, State) of
        file_not_found ->
            ewl_talk:say("File ~s is not does not exist in the "
                       "file System. This shouldn't happen.",
                         [File]),
            ?SIN_RAISE(State, {file_not_on_disc, File});
        unable_to_access ->
            ewl_talk:say("File ~s exists but is inaccessable.",
                         [File]),
            ?SIN_RAISE(State, {file_inaccessable, File});
        true ->
            {ok, FileInfo} = file:read_file_info(File),
            file:copy(File, NFile),
            file:write_file_info(NFile, FileInfo),
            sin_sig:update(copy, File, State);
        _ -><commit_msg>fix bug in copy_file return values<commit_after>%% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*-
%%%---------------------------------------------------------------------------
%%% @author <NAME>
%%% @doc
%%%  A group of utility functions for project automation.
%%% @end
%%% @copyright 2006 - 2011 Erlware
%%%---------------------------------------------------------------------------
-module(sin_utils).

-include_lib("kernel/include/file.hrl").
-include_lib("eunit/include/eunit.hrl").
-include("internal.hrl").

%% API
-export([copy_dir/3,
         copy_dir/4,
         copy_dir/5,
         delete_dir/1,
         file_exists/2,
         get_application_env/2,
         get_ignore_dirs/1,
         is_dir_ignorable/2,
         remove_code_paths/1,
         term_to_list/1,
         to_bool/1,
         format_exception/1,
         basename/1]).

%%====================================================================
%% API
%%====================================================================

%% @doc Trivially convert a value to a boolean
-spec to_bool(any()) -> boolean().
to_bool(true) ->
    true;
to_bool("True") ->
    true;
to_bool("true") ->
    true;
to_bool(_) ->
    false.

%% @doc Delete the directory and all of its sub directories.
%% @private
-spec delete_dir(Dir::string()) -> ok.
delete_dir(Dir) ->
    case file:list_dir(Dir) of
        {ok, Files} ->
            lists:foldl(fun (File, _Acc) ->
                                NFile = filename:join([Dir, File]),
                                case filelib:is_dir(NFile) of
                                    true ->
                                        case is_symlink(NFile) of
                                            true -> file:delete(NFile);
                                            false -> delete_dir(NFile)
                                        end;
                                    false -> file:delete(NFile)
                                end
                        end,
                        [], Files),
            file:del_dir(Dir);
        _ ->
            ok
    end.

%% @doc Get the ignore dirs
-spec get_ignore_dirs(sin_state:state()) ->
                             IgnoreDirs::[string()].
get_ignore_dirs(State) ->
    sin_state:get_value(ignore_dirs, [], State).

%% @doc Check to see if a file exists.
-spec file_exists(sin_state:state(), FileName::string()) -> boolean().
file_exists(State, FileName) ->
    case file:read_file_info(FileName) of
        {error, enoent} ->
            false;
        Error = {error, _} ->
            ?SIN_RAISE(State, {file_access, FileName}, Error);
        _ ->
            true
    end.

%% @doc Copies the specified directory down to the build dir on a file by file
%% basis. It only copies if the file has .
-spec copy_dir(sin_state:state(), BuilderDir::string(),
               TargetDir::string()) -> ok.
copy_dir(State, BuildDir, TargetDir) ->
    copy_dir(State, BuildDir, TargetDir, [], []).

-spec copy_dir(sin_state:state(), BuilderDir::string(),
               TargetDir::string(),
               Subdirs::string()) -> ok.
copy_dir(State, BuildDir, TargetDir, Sub) ->
    copy_dir(State, BuildDir, TargetDir, Sub, []).

-spec copy_dir(sin_state:state(),
               BuilderDir::string(),
               TargetDir::string(),
               Subdirs::string(),
               Ignorables::string()) -> sin_state:state().
copy_dir(State0, BuildDir, TargetDir, SubDir, Ignorables) ->
    check_not_circular(State0, BuildDir, TargetDir, SubDir),
    case are_dirs_ignorable(SubDir, Ignorables) of
        true ->
            State0;
        false ->
            Target = filename:join([BuildDir | SubDir]),
            filelib:ensure_dir(filename:join([Target, "tmp"])),
            CpyTarget = filename:join([TargetDir | SubDir]),
            {ok, Files} = file:list_dir(CpyTarget),
            lists:foldl(fun (IFile, State1) ->
                                File = filename:join([CpyTarget, IFile]),
                                case {is_dir_ignorable(IFile, Ignorables),
                                      filelib:is_dir(File)}
                                    of
                                    {true, _} ->
                                        State1;
                                    {_, true} ->
                                        copy_dir(State1,
                                                 BuildDir, TargetDir,
                                                 SubDir ++ [IFile], Ignorables);
                                    {_, false} ->
                                        copy_file(State1, Target, IFile, File)
                                end
                        end,
                        State0, Files)
    end.

%% @doc Remove the specified code paths from the system code paths.
-spec remove_code_paths(Paths::[string()]) -> ok.
remove_code_paths([Path | T]) ->
    code:del_path(Path), remove_code_paths(T);
remove_code_paths([]) ->
    ok.

%% @doc Return wether the directory is in the list of ignorables. If it is then
%%  return true, otherwise return false.
-spec is_dir_ignorable(Directory::string(), ListOfIgnores::[string()]) ->
    boolean().
is_dir_ignorable(Sub, [Ignore | Rest]) ->
    case ignore_dir(Sub, Ignore) of
        true ->
            true;
        false ->
            is_dir_ignorable(Sub, Rest)
    end;
is_dir_ignorable(_Sub, []) ->
    false.

%% Strip the extension off of a base.
-spec basename(string()) ->
    string().
basename(File) ->
    basename1(lists:reverse(File), []).

%% @doc Format an exception thrown by this module
-spec format_exception(sin_exceptions:exception()) ->
    string().
format_exception(Exception) ->
    sin_exceptions:format_exception(Exception).

%%====================================================================
%% Internal functions
%%====================================================================

%% @doc Copies the file specified by file to the target specified by ifile.
-spec copy_file(sin_state:state(),
                Target::string(), IFile::string(), File::string()) ->
                       sin_state:state().
copy_file(State, _Target, [$. | _], _File) ->
            State;
copy_file(State, Target, IFile, File) ->
    NFile = filename:join([Target, IFile]),
    case sin_sig:changed(copy, File, State) of
        file_not_found ->
            ewl_talk:say("File ~s is not does not exist in the "
                       "file System. This shouldn't happen.",
                         [File]),
            ?SIN_RAISE(State, {file_not_on_disc, File});
        unable_to_access ->
            ewl_talk:say("File ~s exists but is inaccessable.",
                         [File]),
            ?SIN_RAISE(State, {file_inaccessable, File});
        true ->
            {ok, FileInfo} = file:read_file_info(File),
            file:copy(File, NFile),
            file:write_file_info(NFile, FileInfo),
            sin_sig:update(copy, File, State);
        _ ->
{'fraction_non_alphanumeric': 0.18019830435407386, 'fraction_numerical': 0.00488575944819658, 'mean_word_length': 1.6798228725452444, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 109, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 42, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3780617', 'n_tokens_mistral': 3820, 'n_tokens_neox': 3706, 'n_words': 845}