<commit_before>%%%----------------------------------------------------------------------
%%% Copyright (c) 2012, <NAME> <<EMAIL>>,
%%%                     <NAME> <<EMAIL>>
%%%                 and <NAME> <<EMAIL>>.
%%% All rights reserved.
%%%
%%% This file is distributed under the Simplified BSD License.
%%% Details can be found in the LICENSE file.
%%%----------------------------------------------------------------------
%%% Authors     : <NAME> <<EMAIL>>
%%% Description : Command Line Interface
%%%----------------------------------------------------------------------

-module(concuerror).

%% UI exports.
-export([gui/1, cli/0, analyze/1, export/2, stop/0]).
%% Log server callback exports.
-export([init/1, terminate/2, handle_event/2]).

-export_type([options/0]).

-include("gen.hrl").

%%%----------------------------------------------------------------------
%%% Debug
%%%----------------------------------------------------------------------

%%-define(TTY, true).
-ifdef(TTY).
-define(tty(), ok).
-else.
-define(tty(), error_logger:tty(false)).
-endif.

%%%----------------------------------------------------------------------
%%% Types
%%%----------------------------------------------------------------------

-type analysis_ret() ::
      sched:analysis_ret()
    | {'error', 'arguments', string()}.

%% Log event handler internal state.
%% The state (if we want to have progress bar) contains
%% the current preemption number,
%% the progress in per cent,
%% the number of interleaving contained in this preemption number,
%% the number of errors we have found so far.
-type progress() :: {non_neg_integer(), -1..100,
                     non_neg_integer(), non_neg_integer()}.

-type state() :: progress() | 'noprogress'.

%% Command line options
-type option() ::
      {'target',  sched:analysis_target()}
    | {'files',   [file:filename()]}
    | {'output',  file:filename()}
    | {'include', [file:name()]}
    | {'define',  epp:macros()}
    | {'noprogress'}
    | {'quiet'}
    | {'preb',    sched:bound()}
    | {'gui'}
    | {'help'}.

-type options() :: [option()].

%%%----------------------------------------------------------------------
%%% UI functions
%%%----------------------------------------------------------------------

%% @spec stop() -> ok
%% @doc: Stop the Concuerror analysis
-spec stop() -> ok.
stop() ->
    %% XXX: Erlang nodes is erroneous with Concuerror right
    %% now and there is times this approach may crash.
    %% Get the hostname
    Temp1 = atom_to_list(node()),
    Host = lists:dropwhile(fun(E) -> E /= $@ end, Temp1),
    %% Set Concuerror Node
    Node = list_to_atom("Concuerror" ++ Host),
    %% Connect to node
    case net_adm:ping(Node) of
        pong ->
            %% Stop analysis
            spawn(Node, fun() -> ?RP_SCHED ! stop_analysis end);
        _ ->
            %% Well some times we could not connect with the
            %% first try so for now just repeat
            stop()
    end,
    ok.

%% @spec gui(options()) -> 'true'
%% @doc: Start the CED GUI.
-spec gui(options()) -> 'true'.
gui(_Options) ->
    %% Disable error logging messages.
    ?tty(),
%%    gui:start().
    'true'.

%% @spec cli() -> 'true'
%% @doc: Parse the command line arguments and start Concuerror.
-spec cli() -> 'true'.
cli() ->
    %% Disable error logging messages.
    ?tty(),
    %% First get the command line options
    Args = init:get_arguments(),
    %% There should be no plain_arguments
    case init:get_plain_arguments() of
        [PlArg|_] ->
            io:format("~s: unrecognised argument: ~s", [?APP_STRING, PlArg]);
        [] ->
            case parse(Args, []) of
                {'error', 'arguments', Msg1} ->
                    io:format("~s: ~s\n", [?APP_STRING, Msg1]);
                Opts -> cliAux(Opts)
            end
    end.

cliAux(Options) ->
    case lists:keyfind('gui', 1, Options) of
        {'gui'} -> gui(Options);
        false ->
            %% Start the log manager and attach the event handler below.
            _ = log:start(),
            case lists:keyfind('quiet', 1, Options) of
                false -> _ = log:attach(?MODULE, Options);
                {'quiet'} -> continue
            end,
            %% Run analysis
            case analyzeAux(Options) of
                {'error', 'arguments', Msg1} ->
                    io:format("~s: ~s\n", [?APP_STRING, Msg1]);
                Result ->
                    %% Set output file
                    Output =
                        case lists:keyfind(output, 1, Options) of
                            {output, O} -> O;
                            false -> ?EXPORT_FILE
                        end,
                    case exportAux(Result, Output) of
                        {'error', Msg2} ->
                            io:format("~s: ~s\n",
                                [?APP_STRING, file:format_error(Msg2)]);
                        ok -> continue
                    end
            end,
            %% Stop event handler
            log:stop()
    end,
    'true'.

%% Parse command line arguments
parse([], Options) ->
    Options;
parse([{Opt, Param} | Args], Options) ->
    case atom_to_list(Opt) of
        T when T=:="t"; T=:="-target" ->
            case Param of
                [Module,Func|Pars] ->
                    AtomModule = erlang:list_to_atom(Module),
                    AtomFunc   = erlang:list_to_atom(Func),
                    case validateTerms(Pars, []) of
                        {'error',_,_}=Error -> Error;
                        AtomParams ->
                            Target = {AtomModule, AtomFunc, AtomParams},
                            NewOptions = lists:keystore(target, 1,
                                Options, {target, Target}),
                            parse(Args, NewOptions)
                    end;
                _Other -> wrongArgument('number', Opt)
            end;

        F when F=:="f"; F=:="-files" ->
            case Param of
                [] -> wrongArgument('number', Opt);
                Files ->
                    NewOptions = keyAppend(files, 1, Options, Files),
                    parse(Args, NewOptions)
            end;

        O when O=:="o"; O=:="-output" ->
            case Param of
                [File] ->
                    NewOptions = lists:keystore(output, 1,
                        Options, {output, File}),
                    parse(Args, NewOptions);
                _Other -> wrongArgument('number', Opt)
            end;

        "I" ->
            case Param of
                [Par] ->
                    NewOptions = keyAppend('include', 1,
                        Options, [Par]),
                    parse(Args, NewOptions);
                _Other -> wrongArgument('number', Opt)
            end;
        [$I | Include] ->
            case Param of
                [] -> parse([{'I', [Include]} | Args], Options);
                _Other -> wrongArgument('number', Opt)
            end;

        "D" ->
            case Param of
                [Par] ->
                    case string:tokens(Par, "=") of
                        [Name, Term] ->
                            AtomName = erlang:list_to_atom(Name),
                            case validateTerms([Term], []) of
                                {'error',_,_}=Error -> Error;
                                [AtomTerm] ->
                                    NewOptions = keyAppend('define', 1,
                                        Options, [{AtomName, AtomTerm}]),
                                    parse(Args, NewOptions)
                            end;
                        [Name] ->
                            AtomName = erlang:list_to_atom(Name),
                            case validateTerms(["true"], []) of
                                {'error',_,_}=Error -> Error;
                                [AtomTerm] ->
                                    NewOptions = keyAppend('define', 1,
                                        Options, [{AtomName, AtomTerm}]),
                                    parse(Args, NewOptions)
                            end;
                        _Other -> wrongArgument('type', Opt)
                    end;
                _Other -> wrongArgument('number', Opt)
            end;
        [$D | Define] ->
            case Param of
                [] -> parse([{'D', [Define]} | Args], Options);
                _Other -> wrongArgument('number', Opt)
            end;

        "-noprogress" ->
            case Param of
                [] ->
                    NewOptions = lists:keystore(noprogress, 1,
                        Options, {noprogress}),
                    parse(Args, NewOptions);
                _Other -> wrongArgument('number', Opt)
            end;

        "-quiet" ->
            case Param of
                [] ->
                    NewOptions = lists:keystore(quiet, 1,
                        Options, {quiet}),
                    parse(Args, NewOptions);
                _Other -> wrongArgument('number', Opt)
            end;

        P when P=:="p"; P=:="-preb" ->
            case Param of
                [Preb] ->
                    case string:to_integer(Preb) of
                        {I, []} when I>=0 ->
                            NewOptions = lists:keystore(preb, 1, Options,
                                {preb, I}),
                            parse(Args, NewOptions);<commit_msg>Add `-q' flag (same as `--quiet')<commit_after>%%%----------------------------------------------------------------------
%%% Copyright (c) 2012, <NAME> <<EMAIL>>,
%%%                     <NAME> <<EMAIL>>
%%%                 and <NAME> <<EMAIL>>.
%%% All rights reserved.
%%%
%%% This file is distributed under the Simplified BSD License.
%%% Details can be found in the LICENSE file.
%%%----------------------------------------------------------------------
%%% Authors     : <NAME> <<EMAIL>>
%%% Description : Command Line Interface
%%%----------------------------------------------------------------------

-module(concuerror).

%% UI exports.
-export([gui/1, cli/0, analyze/1, export/2, stop/0]).
%% Log server callback exports.
-export([init/1, terminate/2, handle_event/2]).

-export_type([options/0]).

-include("gen.hrl").

%%%----------------------------------------------------------------------
%%% Debug
%%%----------------------------------------------------------------------

%%-define(TTY, true).
-ifdef(TTY).
-define(tty(), ok).
-else.
-define(tty(), error_logger:tty(false)).
-endif.

%%%----------------------------------------------------------------------
%%% Types
%%%----------------------------------------------------------------------

-type analysis_ret() ::
      sched:analysis_ret()
    | {'error', 'arguments', string()}.

%% Log event handler internal state.
%% The state (if we want to have progress bar) contains
%% the current preemption number,
%% the progress in per cent,
%% the number of interleaving contained in this preemption number,
%% the number of errors we have found so far.
-type progress() :: {non_neg_integer(), -1..100,
                     non_neg_integer(), non_neg_integer()}.

-type state() :: progress() | 'noprogress'.

%% Command line options
-type option() ::
      {'target',  sched:analysis_target()}
    | {'files',   [file:filename()]}
    | {'output',  file:filename()}
    | {'include', [file:name()]}
    | {'define',  epp:macros()}
    | {'noprogress'}
    | {'quiet'}
    | {'preb',    sched:bound()}
    | {'gui'}
    | {'help'}.

-type options() :: [option()].

%%%----------------------------------------------------------------------
%%% UI functions
%%%----------------------------------------------------------------------

%% @spec stop() -> ok
%% @doc: Stop the Concuerror analysis
-spec stop() -> ok.
stop() ->
    %% XXX: Erlang nodes is erroneous with Concuerror right
    %% now and there is times this approach may crash.
    %% Get the hostname
    Temp1 = atom_to_list(node()),
    Host = lists:dropwhile(fun(E) -> E /= $@ end, Temp1),
    %% Set Concuerror Node
    Node = list_to_atom("Concuerror" ++ Host),
    %% Connect to node
    case net_adm:ping(Node) of
        pong ->
            %% Stop analysis
            spawn(Node, fun() -> ?RP_SCHED ! stop_analysis end);
        _ ->
            %% Well some times we could not connect with the
            %% first try so for now just repeat
            stop()
    end,
    ok.

%% @spec gui(options()) -> 'true'
%% @doc: Start the CED GUI.
-spec gui(options()) -> 'true'.
gui(_Options) ->
    %% Disable error logging messages.
    ?tty(),
%%    gui:start().
    'true'.

%% @spec cli() -> 'true'
%% @doc: Parse the command line arguments and start Concuerror.
-spec cli() -> 'true'.
cli() ->
    %% Disable error logging messages.
    ?tty(),
    %% First get the command line options
    Args = init:get_arguments(),
    %% There should be no plain_arguments
    case init:get_plain_arguments() of
        [PlArg|_] ->
            io:format("~s: unrecognised argument: ~s", [?APP_STRING, PlArg]);
        [] ->
            case parse(Args, []) of
                {'error', 'arguments', Msg1} ->
                    io:format("~s: ~s\n", [?APP_STRING, Msg1]);
                Opts -> cliAux(Opts)
            end
    end.

cliAux(Options) ->
    case lists:keyfind('gui', 1, Options) of
        {'gui'} -> gui(Options);
        false ->
            %% Start the log manager and attach the event handler below.
            _ = log:start(),
            case lists:keyfind('quiet', 1, Options) of
                false -> _ = log:attach(?MODULE, Options);
                {'quiet'} -> continue
            end,
            %% Run analysis
            case analyzeAux(Options) of
                {'error', 'arguments', Msg1} ->
                    io:format("~s: ~s\n", [?APP_STRING, Msg1]);
                Result ->
                    %% Set output file
                    Output =
                        case lists:keyfind(output, 1, Options) of
                            {output, O} -> O;
                            false -> ?EXPORT_FILE
                        end,
                    case exportAux(Result, Output) of
                        {'error', Msg2} ->
                            io:format("~s: ~s\n",
                                [?APP_STRING, file:format_error(Msg2)]);
                        ok -> continue
                    end
            end,
            %% Stop event handler
            log:stop()
    end,
    'true'.

%% Parse command line arguments
parse([], Options) ->
    Options;
parse([{Opt, Param} | Args], Options) ->
    case atom_to_list(Opt) of
        T when T=:="t"; T=:="-target" ->
            case Param of
                [Module,Func|Pars] ->
                    AtomModule = erlang:list_to_atom(Module),
                    AtomFunc   = erlang:list_to_atom(Func),
                    case validateTerms(Pars, []) of
                        {'error',_,_}=Error -> Error;
                        AtomParams ->
                            Target = {AtomModule, AtomFunc, AtomParams},
                            NewOptions = lists:keystore(target, 1,
                                Options, {target, Target}),
                            parse(Args, NewOptions)
                    end;
                _Other -> wrongArgument('number', Opt)
            end;

        F when F=:="f"; F=:="-files" ->
            case Param of
                [] -> wrongArgument('number', Opt);
                Files ->
                    NewOptions = keyAppend(files, 1, Options, Files),
                    parse(Args, NewOptions)
            end;

        O when O=:="o"; O=:="-output" ->
            case Param of
                [File] ->
                    NewOptions = lists:keystore(output, 1,
                        Options, {output, File}),
                    parse(Args, NewOptions);
                _Other -> wrongArgument('number', Opt)
            end;

        "I" ->
            case Param of
                [Par] ->
                    NewOptions = keyAppend('include', 1,
                        Options, [Par]),
                    parse(Args, NewOptions);
                _Other -> wrongArgument('number', Opt)
            end;
        [$I | Include] ->
            case Param of
                [] -> parse([{'I', [Include]} | Args], Options);
                _Other -> wrongArgument('number', Opt)
            end;

        "D" ->
            case Param of
                [Par] ->
                    case string:tokens(Par, "=") of
                        [Name, Term] ->
                            AtomName = erlang:list_to_atom(Name),
                            case validateTerms([Term], []) of
                                {'error',_,_}=Error -> Error;
                                [AtomTerm] ->
                                    NewOptions = keyAppend('define', 1,
                                        Options, [{AtomName, AtomTerm}]),
                                    parse(Args, NewOptions)
                            end;
                        [Name] ->
                            AtomName = erlang:list_to_atom(Name),
                            case validateTerms(["true"], []) of
                                {'error',_,_}=Error -> Error;
                                [AtomTerm] ->
                                    NewOptions = keyAppend('define', 1,
                                        Options, [{AtomName, AtomTerm}]),
                                    parse(Args, NewOptions)
                            end;
                        _Other -> wrongArgument('type', Opt)
                    end;
                _Other -> wrongArgument('number', Opt)
            end;
        [$D | Define] ->
            case Param of
                [] -> parse([{'D', [Define]} | Args], Options);
                _Other -> wrongArgument('number', Opt)
            end;

        "-noprogress" ->
            case Param of
                [] ->
                    NewOptions = lists:keystore(noprogress, 1,
                        Options, {noprogress}),
                    parse(Args, NewOptions);
                _Other -> wrongArgument('number', Opt)
            end;

        Q when Q=:="q"; Q=:="-quiet" ->
            case Param of
                [] ->
                    NewOptions = lists:keystore(quiet, 1,
                        Options, {quiet}),
                    parse(Args, NewOptions);
                _Other -> wrongArgument('number', Opt)
            end;

        P when P=:="p"; P=:="-preb" ->
            case Param of
                [Preb] ->
                    case string:to_integer(Preb) of
                        {I, []} when I>=0 ->
                            NewOptions = lists:keystore(preb, 1, Options,
                                {preb, I}),
                            parse(Args, NewOptions);
{'fraction_non_alphanumeric': 0.19834973024436686, 'fraction_numerical': 0.004019887866285835, 'mean_word_length': 1.1301261829652998, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 167, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5351834', 'n_tokens_mistral': 4985, 'n_tokens_neox': 4705, 'n_words': 1243}