aboutsummaryrefslogtreecommitdiffstats
path: root/lib/sasl/src/rb.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/sasl/src/rb.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/sasl/src/rb.erl')
-rw-r--r--lib/sasl/src/rb.erl697
1 files changed, 697 insertions, 0 deletions
diff --git a/lib/sasl/src/rb.erl b/lib/sasl/src/rb.erl
new file mode 100644
index 0000000000..00d86285e5
--- /dev/null
+++ b/lib/sasl/src/rb.erl
@@ -0,0 +1,697 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% 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 online 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.
+%%
+%% %CopyrightEnd%
+%%
+-module(rb).
+
+-behaviour(gen_server).
+
+%% External exports
+-export([start/0, start/1, stop/0, rescan/0, rescan/1]).
+-export([list/0, list/1, show/0, show/1, grep/1, start_log/1, stop_log/0]).
+-export([h/0, help/0]).
+
+%% Internal exports
+-export([start_link/1]).
+
+%% gen_server callbacks
+-export([init/1, terminate/2, handle_call/3,
+ handle_cast/2, handle_info/2, code_change/3]).
+
+%%%-----------------------------------------------------------------
+%%% Report Browser Tool.
+%%% Formats Error reports written by log_mf_h
+%%%-----------------------------------------------------------------
+
+-record(state, {dir, data, device, max, type, abort, log}).
+
+%%-----------------------------------------------------------------
+%% Interface functions.
+%% For available options; see print_options().
+%%-----------------------------------------------------------------
+start() -> start([]).
+start(Options) ->
+ supervisor:start_child(sasl_sup,
+ {rb_server, {rb, start_link, [Options]},
+ temporary, brutal_kill, worker, [rb]}).
+
+start_link(Options) ->
+ gen_server:start_link({local, rb_server}, rb, Options, []).
+
+stop() ->
+ gen_server:call(rb_server, stop),
+ supervisor:delete_child(sasl_sup, rb_server).
+
+rescan() -> rescan([]).
+rescan(Options) ->
+ gen_server:call(rb_server, {rescan, Options}, infinity).
+
+list() -> list(all).
+list(Type) -> gen_server:call(rb_server, {list, Type}, infinity).
+
+show() ->
+ gen_server:call(rb_server, show, infinity).
+
+show(Number) when is_integer(Number) ->
+ gen_server:call(rb_server, {show_number, Number}, infinity);
+show(Type) when is_atom(Type) ->
+ gen_server:call(rb_server, {show_type, Type}, infinity).
+
+grep(RegExp) -> gen_server:call(rb_server, {grep, RegExp}, infinity).
+
+start_log(FileName) -> gen_server:call(rb_server, {start_log, FileName}).
+
+stop_log() -> gen_server:call(rb_server, stop_log).
+
+h() -> help().
+help() ->
+ io:format("~nReport Browser Tool - usage~n"),
+ io:format("===========================~n"),
+ io:format("rb:start() - start the rb_server with default options~n"),
+ io:format("rb:start(Options) - where Options is a list of:~n"),
+ print_options(),
+ io:format("rb:h() - print this help~n"),
+ io:format("rb:help() - print this help~n"),
+ io:format("rb:list() - list all reports~n"),
+ io:format("rb:list(Type) - list all reports of type Type~n"),
+ io:format(" currently supported types are:~n"),
+ print_types(),
+ io:format("rb:grep(RegExp) - print reports containing RegExp~n"),
+ io:format("rb:rescan() - rescans the report directory with same~n"),
+ io:format(" options.~n"),
+ io:format("rb:rescan(Options) - rescans the report directory with new~n"),
+ io:format(" options. Options is same as in start/1.~n"),
+ io:format("rb:show(Number) - print report no Number~n"),
+ io:format("rb:show(Type) - print all reports of type Type~n"),
+ io:format("rb:show() - print all reports~n"),
+ io:format("rb:start_log(File) - redirect all reports to file~n"),
+ io:format("rb:stop_log() - close the log file and redirect to~n"),
+ io:format(" standard_io~n"),
+ io:format("rb:stop - stop the rb_server~n").
+
+%%-----------------------------------------------------------------
+%% Internal functions.
+%%-----------------------------------------------------------------
+%%-----------------------------------------------------------------
+%% MAKE SURE THESE TWO FUNCTIONS ARE UPDATED!
+%%-----------------------------------------------------------------
+print_options() ->
+ io:format(" {start_log, FileName}~n"),
+ io:format(" - default: standard_io~n"),
+ io:format(" {max, MaxNoOfReports}~n"),
+ io:format(" - MaxNoOfReports should be an integer or 'all'~n"),
+ io:format(" - default: all~n"),
+ io:format(" {report_dir, DirString}~n"),
+ io:format(" - DirString should be a string without trailing~n"),
+ io:format(" - directory delimiter.~n"),
+ io:format(" - default: {sasl, error_logger_mf_dir}~n"),
+ io:format(" {type, ReportType}~n"),
+ io:format(" - ReportType should be a supported type, 'all'~n"),
+ io:format(" - or a list of supported types~n"),
+ io:format(" - default: all~n"),
+ io:format(" {abort_on_error, Bool}~n"),
+ io:format(" - Bool: true | false~n"),
+ io:format(" - default: false~n").
+
+print_types() ->
+ io:format(" - crash_report~n"),
+ io:format(" - supervisor_report~n"),
+ io:format(" - progress~n"),
+ io:format(" - error~n").
+
+
+init(Options) ->
+ process_flag(priority, low),
+ process_flag(trap_exit, true),
+ Log = get_option(Options, start_log, standard_io),
+ Device = open_log_file(Log),
+ Dir = get_report_dir(Options),
+ Max = get_option(Options, max, all),
+ Type = get_option(Options, type, all),
+ Abort = get_option(Options, abort_on_error, false),
+ Data = scan_files(Dir ++ "/", Max, Type),
+ {ok, #state{dir = Dir ++ "/", data = Data, device = Device,
+ max = Max, type = Type, abort = Abort, log = Log}}.
+
+handle_call({rescan, Options}, _From, State) ->
+ {Device,Log1} =
+ case get_option(Options, start_log, {undefined}) of
+ {undefined} ->
+ {State#state.device,State#state.log};
+ Log ->
+ close_device(State#state.device),
+ {open_log_file(Log),Log}
+ end,
+ Max = get_option(Options, max, State#state.max),
+ Type = get_option(Options, type, State#state.type),
+ Abort = get_option(Options, abort_on_error, false),
+ Data = scan_files(State#state.dir, Max, Type),
+ NewState = State#state{data = Data, max = Max, type = Type,
+ device = Device, abort = Abort, log = Log1},
+ {reply, ok, NewState};
+handle_call(stop, _From, State) ->
+ {stop, normal, stopped, State};
+handle_call(_, _From, #state{data = undefined}) ->
+ {reply, {error, no_data}, #state{}};
+handle_call({list, Type}, _From, State) ->
+ print_list(State#state.data, Type),
+ {reply, ok, State};
+handle_call({start_log, FileName}, _From, State) ->
+ NewDevice = open_log_file(FileName),
+ {reply, ok, State#state{device = NewDevice}};
+handle_call(stop_log, _From, State) ->
+ close_device(State#state.device),
+ {reply, ok, State#state{device = standard_io}};
+handle_call({show_number, Number}, _From, State) ->
+ #state{dir = Dir, data = Data, device = Device, abort = Abort, log = Log} = State,
+ NewDevice = print_report_by_num(Dir, Data, Number, Device, Abort, Log),
+ {reply, ok, State#state{device = NewDevice}};
+handle_call({show_type, Type}, _From, State) ->
+ #state{dir = Dir, data = Data, device = Device, abort = Abort, log = Log} = State,
+ NewDevice = print_typed_reports(Dir, Data, Type, Device, Abort, Log),
+ {reply, ok, State#state{device = NewDevice}};
+handle_call(show, _From, State) ->
+ #state{dir = Dir, data = Data, device = Device, abort = Abort, log = Log} = State,
+ NewDevice = print_all_reports(Dir, Data, Device, Abort, Log),
+ {reply, ok, State#state{device = NewDevice}};
+handle_call({grep, RegExp}, _From, State) ->
+ #state{dir = Dir, data = Data, device = Device, abort = Abort, log = Log} = State,
+ NewDevice = print_grep_reports(Dir, Data, RegExp, Device, Abort, Log),
+ {reply, ok, State#state{device = NewDevice}}.
+
+terminate(_Reason, #state{device = Device}) ->
+ close_device(Device).
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+handle_info(_Info, State) ->
+ {noreply, State}.
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%-----------------------------------------------------------------
+%% Func: open_log_file/1
+%% Args: FileName | standard_io
+%% Returns: A Device for later use in call to io:format
+%%-----------------------------------------------------------------
+open_log_file(standard_io) -> standard_io;
+open_log_file(FileName) ->
+ case file:open(FileName, [write,append]) of
+ {ok, Fd} -> Fd;
+ Error ->
+ io:format("rb: Cannot open file '~s' (~w).~n",
+ [FileName, Error]),
+ io:format("rb: Using standard_io~n"),
+ standard_io
+ end.
+
+close_device(Fd) when is_pid(Fd) ->
+ catch file:close(Fd);
+close_device(_) -> ok.
+
+get_option(Options, Key, Default) ->
+ case lists:keysearch(Key, 1, Options) of
+ {value, {_Key, Value}} -> Value;
+ _ -> Default
+ end.
+
+get_report_dir(Options) ->
+ case lists:keysearch(report_dir, 1, Options) of
+ {value, {_Key, RptDir}} -> RptDir;
+ _ ->
+ case catch application:get_env(sasl, error_logger_mf_dir) of
+ {ok, Dir} -> Dir;
+ _ ->
+ exit("cannot locate report directory")
+ end
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: scan_files(RptDir, Max, Type)
+%% Args: RptDir ::= string().
+%% Max ::= integer() | all, describing how many reports
+%5 to read.
+%% Type ::= atom(), describing which reports to read.
+%% Purpose: Scan all report files one time, and build a list of
+%% small elements
+%% Returns: Data, where Data is a list of
+%% {Number, Type, ShortDescr, Date, Fname, FilePosition}.
+%%-----------------------------------------------------------------
+scan_files(RptDir, Max, Type) ->
+ case file:open(RptDir ++ "/index", [raw, read]) of
+ {ok, Fd} ->
+ case catch file:read(Fd, 1) of
+ {ok, [LastWritten]} ->
+ Files = make_file_list(RptDir, LastWritten),
+ scan_files(RptDir, Files, Max, Type);
+ _ -> exit("cannot read the index file")
+ end;
+ _ -> exit("cannot read the index file")
+ end.
+
+make_file_list(Dir, FirstFileNo) ->
+ case file:list_dir(Dir) of
+ {ok, FileNames} ->
+ FileNumbers = lists:zf(fun(Name) ->
+ case catch list_to_integer(Name) of
+ Int when is_integer(Int) ->
+ {true, Int};
+ _ ->
+ false
+ end
+ end,
+ FileNames),
+ shift(lists:sort(FileNumbers), FirstFileNo);
+ _ -> exit({bad_directory, Dir})
+ end.
+
+shift(List, First) ->
+ shift(List, First, []).
+
+shift([H | T], H, Res) ->
+ [H | Res] ++ lists:reverse(T);
+shift([H | T], First, Res) ->
+ shift(T, First, [H | Res]);
+shift([], _, Res) ->
+ Res.
+
+%%-----------------------------------------------------------------
+%% Func: scan_files(Dir, Files, Max, Type)
+%% Args: Files is a list of FileName.
+%% Purpose: Scan the report files in the index variable.
+%% Returns: {Number, Type, ShortDescr, Date, FileName, FilePosition}
+%%-----------------------------------------------------------------
+scan_files(Dir, Files, Max, Type) ->
+ scan_files(Dir, 1, Files, [], Max, Type).
+scan_files(_Dir, _, [], Res, _Max, _Type) -> Res;
+scan_files(_Dir, _, _Files, Res, Max, _Type) when Max =< 0 -> Res;
+scan_files(Dir, No, [H|T], Res, Max, Type) ->
+ Data = get_report_data_from_file(Dir, No, H, Max, Type),
+ Len = length(Data),
+ NewMax = dec_max(Max, Len),
+ NewNo = No + Len,
+ NewData = Data ++ Res,
+ scan_files(Dir, NewNo, T, NewData, NewMax, Type).
+
+dec_max(all, _) -> all;
+dec_max(X,Y) -> X-Y.
+
+get_report_data_from_file(Dir, No, FileNr, Max, Type) ->
+ Fname = integer_to_list(FileNr),
+ FileName = lists:concat([Dir, Fname]),
+ case file:open(FileName, [read]) of
+ {ok, Fd} when is_pid(Fd) -> read_reports(No, Fd, Fname, Max, Type);
+ _ -> [{No, unknown, "Can't open file " ++ Fname, "???", Fname, 0}]
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: read_reports(No, Fd, Fname, Max, Type)
+%% Purpose: Read reports from one report file.
+%% Returns: A list of {No, Type, ShortDescr, Date, FileName, FilePosition}
+%% Note: We have to read all reports, and then check the max-
+%% variable, because the reports are reversed on the file, and
+%% we may need the last ones.
+%%-----------------------------------------------------------------
+read_reports(No, Fd, Fname, Max, Type) ->
+ io:format("rb: reading report..."),
+ case catch read_reports(Fd, [], Type) of
+ {ok, Res} ->
+ file:close(Fd),
+ io:format("done.~n"),
+ NewRes =
+ if
+ length(Res) > Max ->
+ lists:sublist(Res, 1, Max);
+ true ->
+ Res
+ end,
+ add_report_data(NewRes, No, Fname);
+ {error, [Problem | Res]} ->
+ file:close(Fd),
+ io:format("Error: ~p~n",[Problem]),
+ io:format("Salvaged ~p entries from corrupt report file ~s...~n",
+ [length(Res),Fname]),
+ NewRes =
+ if
+ length([Problem|Res]) > Max ->
+ lists:sublist([Problem|Res], 1, Max);
+ true ->
+ [Problem|Res]
+ end,
+ add_report_data(NewRes, No, Fname);
+ Else ->
+ io:format("err ~p~n", [Else]),
+ [{No, unknown, "Can't read reports from file " ++ Fname,
+ "???", Fname, 0}]
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: add_report_data(Res, No, FName)
+%% Args: Res is a list of {Type, ShortDescr, Date, FilePos}
+%% Purpose: Convert a list of {Type, ShortDescr, Date, FilePos} to
+%% a list of {No, Type, ShortDescr, Date, FileName, FilePos}
+%% Returns: A list of {No, Type, ShortDescr, Date, FileName, FilePos}
+%%-----------------------------------------------------------------
+add_report_data(Res, No, FName) ->
+ add_report_data(Res, No, FName, []).
+add_report_data([{Type, ShortDescr, Date, FilePos}|T], No, FName, Res) ->
+ add_report_data(T, No+1, FName,
+ [{No, Type, ShortDescr, Date, FName, FilePos}|Res]);
+add_report_data([], _No, _FName, Res) -> Res.
+
+read_reports(Fd, Res, Type) ->
+ {ok, FilePos} = file:position(Fd, cur),
+ case catch read_report(Fd) of
+ {ok, Report} ->
+ RealType = get_type(Report),
+ {ShortDescr, Date} = get_short_descr(Report),
+ Rep = {RealType, ShortDescr, Date, FilePos},
+ if
+ Type == all->
+ read_reports(Fd, [Rep | Res], Type);
+ RealType == Type ->
+ read_reports(Fd, [Rep | Res], Type);
+ is_list(Type) ->
+ case lists:member(RealType, Type) of
+ true ->
+ read_reports(Fd, [Rep | Res], Type);
+ _ ->
+ read_reports(Fd, Res, Type)
+ end;
+ true ->
+ read_reports(Fd, Res, Type)
+ end;
+ {error, Error} ->
+ {error, [{unknown, Error, [], FilePos} | Res]};
+ eof ->
+ {ok, Res};
+ {'EXIT', Reason} ->
+ [{unknown, Reason, [], FilePos} | Res]
+ end.
+
+read_report(Fd) ->
+ case io:get_chars(Fd,'',2) of
+ [Hi,Lo] ->
+ Size = get_int16(Hi,Lo),
+ case io:get_chars(Fd,'',Size) of
+ eof ->
+ {error,"Premature end of file"};
+ List ->
+ Bin = list_to_binary(List),
+ Ref = make_ref(),
+ case (catch {Ref,binary_to_term(Bin)}) of
+ {'EXIT',_} ->
+ {error, "Inclomplete erlang term in log"};
+ {Ref,Term} ->
+ {ok, Term}
+ end
+ end;
+ eof ->
+ eof
+ end.
+
+get_int16(Hi,Lo) ->
+ ((Hi bsl 8) band 16#ff00) bor (Lo band 16#ff).
+
+
+%%-----------------------------------------------------------------
+%% Update these functions with the reports that should be possible
+%% to browse with rb.
+%%-----------------------------------------------------------------
+get_type({_Time, {error_report, _Pid, {_, crash_report, _}}}) ->
+ crash_report;
+get_type({_Time, {error_report, _Pid, {_, supervisor_report, _}}}) ->
+ supervisor_report;
+get_type({_Time, {info_report, _Pid, {_, progress, _}}}) ->
+ progress;
+get_type({_Time, {Type, _, _}}) -> Type;
+get_type(_) -> unknown.
+
+get_short_descr({{Date, Time}, {error_report, Pid, {_, crash_report, Rep}}}) ->
+ [OwnRep | _] = Rep,
+ Name =
+ case lists:keysearch(registered_name, 1, OwnRep) of
+ {value, {_Key, []}} ->
+ case lists:keysearch(initial_call, 1, OwnRep) of
+ {value, {_K, {M,_F,_A}}} -> M;
+ _ -> Pid
+ end;
+ {value, {_Key, N}} -> N;
+ _ -> Pid
+ end,
+ NameStr = lists:flatten(io_lib:format("~w", [Name])),
+ {NameStr, date_str(Date, Time)};
+get_short_descr({{Date, Time}, {error_report, Pid, {_, supervisor_report,Rep}}}) ->
+ Name =
+ case lists:keysearch(supervisor, 1, Rep) of
+ {value, {_Key, N}} when is_atom(N) -> N;
+ _ -> Pid
+ end,
+ NameStr = lists:flatten(io_lib:format("~w", [Name])),
+ {NameStr, date_str(Date,Time)};
+get_short_descr({{Date, Time}, {_Type, Pid, _}}) ->
+ NameStr = lists:flatten(io_lib:format("~w", [Pid])),
+ {NameStr, date_str(Date,Time)};
+get_short_descr(_) ->
+ {"???", "???"}.
+
+date_str({Y,Mo,D}=Date,{H,Mi,S}=Time) ->
+ case application:get_env(sasl,utc_log) of
+ {ok,true} ->
+ {{YY,MoMo,DD},{HH,MiMi,SS}} =
+ local_time_to_universal_time({Date,Time}),
+ lists:flatten(io_lib:format("~w-~2.2.0w-~2.2.0w ~2.2.0w:"
+ "~2.2.0w:~2.2.0w UTC",
+ [YY,MoMo,DD,HH,MiMi,SS]));
+ _ ->
+ lists:flatten(io_lib:format("~w-~2.2.0w-~2.2.0w ~2.2.0w:"
+ "~2.2.0w:~2.2.0w",
+ [Y,Mo,D,H,Mi,S]))
+ end.
+
+local_time_to_universal_time({Date,Time}) ->
+ case calendar:local_time_to_universal_time_dst({Date,Time}) of
+ [UCT] ->
+ UCT;
+ [UCT1,_UCT2] ->
+ UCT1;
+ [] -> % should not happen
+ {Date,Time}
+ end.
+
+
+print_list(Data, Type) ->
+ Header = {"No", "Type", "Process", "Date Time"},
+ Width = find_width([Header | Data], 0)+1,
+ DateWidth = find_date_width([Header | Data], 0) +1,
+ Format = lists:concat(["~4s~20s ~", Width, "s~20s~n"]),
+ io:format(Format, tuple_to_list(Header)),
+ io:format(Format, ["==", "====", "=======", "==== ===="]),
+ print_list(Data, Type, Width, DateWidth).
+print_list([], _, _, _) -> true;
+print_list([H|T], Type, Width, DateWidth) ->
+ print_one_report(H, Type, Width, DateWidth),
+ print_list(T, Type, Width, DateWidth).
+
+find_width([], Width) -> Width;
+find_width([H|T], Width) ->
+ Try = length(element(3, H)),
+ if
+ Try > Width -> find_width(T, Try);
+ true -> find_width(T, Width)
+ end.
+find_date_width([], Width) -> Width;
+find_date_width([H|T], Width) ->
+ Try = length(element(4, H)),
+ if
+ Try > Width -> find_date_width(T, Try);
+ true -> find_date_width(T, Width)
+ end.
+
+print_one_report({No, RealType, ShortDescr, Date, _Fname, _FilePos},
+ WantedType,
+ Width, DateWidth) ->
+ if
+ WantedType == all ->
+ print_short_descr(No, RealType, ShortDescr, Date, Width,
+ DateWidth);
+ WantedType == RealType ->
+ print_short_descr(No, RealType, ShortDescr, Date, Width,
+ DateWidth);
+ true -> ok
+ end.
+
+print_short_descr(No, Type, ShortDescr, Date, Width, DateWidth) ->
+ Format = lists:concat(["~4w~20w ~", Width, "s~", DateWidth,"s~n"]),
+ io:format(Format, [No,
+ Type,
+ io_lib:format("~s", [ShortDescr]),
+ Date]).
+
+print_report_by_num(Dir, Data, Number, Device, Abort, Log) ->
+ {_,Device1} = print_report(Dir, Data, Number, Device, Abort, Log),
+ Device1.
+
+print_typed_reports(_Dir, [], _Type, Device, _Abort, _Log) ->
+ Device;
+print_typed_reports(Dir, Data, Type, Device, Abort, Log) ->
+ {Next,Device1} =
+ case element(2, hd(Data)) of
+ Type ->
+ print_report(Dir, Data, element(1, hd(Data)), Device, Abort, Log);
+ _ ->
+ {proceed,Device}
+ end,
+ if Next == abort ->
+ Device1;
+ true ->
+ print_typed_reports(Dir, tl(Data), Type, Device1, Abort, Log)
+ end.
+
+print_all_reports(_Dir, [], Device, _Abort, _Log) ->
+ Device;
+print_all_reports(Dir, Data, Device, Abort, Log) ->
+ {Next,Device1} = print_report(Dir, Data, element(1, hd(Data)),
+ Device, Abort, Log),
+ if Next == abort ->
+ Device1;
+ true ->
+ print_all_reports(Dir, tl(Data), Device1, Abort, Log)
+ end.
+
+print_report(Dir, Data, Number, Device, Abort, Log) ->
+ case find_report(Data, Number) of
+ {Fname, FilePosition} ->
+ FileName = lists:concat([Dir, Fname]),
+ case file:open(FileName, [read]) of
+ {ok, Fd} ->
+ read_rep(Fd, FilePosition, Device, Abort, Log);
+ _ ->
+ io:format("rb: can't open file ~p~n", [Fname]),
+ {proceed,Device}
+ end;
+ no_report ->
+ {proceed,Device}
+ end.
+
+find_report([{No, _Type, _Descr, _Date, Fname, FilePosition}|_T], No) ->
+ {Fname, FilePosition};
+find_report([_H|T], No) ->
+ find_report(T, No);
+find_report([], No) ->
+ io:format("There is no report with number ~p.~n", [No]),
+ no_report.
+
+print_grep_reports(_Dir, [], _RegExp, Device, _Abort, _Log) ->
+ Device;
+print_grep_reports(Dir, Data, RegExp, Device, Abort, Log) ->
+ {Next,Device1} = print_grep_report(Dir, Data, element(1, hd(Data)),
+ Device, RegExp, Abort, Log),
+ if Next == abort ->
+ Device1;
+ true ->
+ print_grep_reports(Dir, tl(Data), RegExp, Device1, Abort, Log)
+ end.
+
+print_grep_report(Dir, Data, Number, Device, RegExp, Abort, Log) ->
+ {Fname, FilePosition} = find_report(Data, Number),
+ FileName = lists:concat([Dir, Fname]),
+ case file:open(FileName, [read]) of
+ {ok, Fd} when is_pid(Fd) ->
+ check_rep(Fd, FilePosition, Device, RegExp, Number, Abort, Log);
+ _ ->
+ io:format("rb: can't open file ~p~n", [Fname]),
+ {proceed,Device}
+ end.
+
+check_rep(Fd, FilePosition, Device, RegExp, Number, Abort, Log) ->
+ case read_rep_msg(Fd, FilePosition) of
+ {Date, Msg} ->
+ MsgStr = lists:flatten(io_lib:format("~p",[Msg])),
+ case regexp:match(MsgStr, RegExp) of
+ {match, _, _} ->
+ io:format("Found match in report number ~w~n", [Number]),
+ case catch rb_format_supp:print(Date, Msg, Device) of
+ {'EXIT', _} ->
+ handle_bad_form(Date, Msg, Device, Abort, Log);
+ _ ->
+ {proceed,Device}
+ end;
+ _ ->
+ {proceed,Device}
+ end;
+ _ ->
+ io:format("rb: Cannot read from file~n"),
+ {proceed,Device}
+ end.
+
+read_rep(Fd, FilePosition, Device, Abort, Log) ->
+ case read_rep_msg(Fd, FilePosition) of
+ {Date, Msg} ->
+ case catch rb_format_supp:print(Date, Msg, Device) of
+ {'EXIT', _} ->
+ handle_bad_form(Date, Msg, Device, Abort, Log);
+ _ ->
+ {proceed,Device}
+ end;
+ _ ->
+ io:format("rb: Cannot read from file~n"),
+ {proceed,Device}
+ end.
+
+handle_bad_form(Date, Msg, Device, Abort, Log) ->
+ io:format("rb: ERROR! A report on bad form was encountered. " ++
+ "It can not be printed to the log.~n~n"),
+ io:format("Details:~n~p ~p~n~n", [Date,Msg]),
+ case {Abort,Device,open_log_file(Log)} of
+ {true,standard_io,standard_io} ->
+ io:format("rb: Logging aborted.~n"),
+ {abort,Device};
+ {false,standard_io,standard_io} ->
+ io:format("rb: Logging resumed...~n~n"),
+ {proceed,Device};
+ {_,_,standard_io} ->
+ io:format("rb: Can not reopen ~p. Logging aborted.~n", [Log]),
+ {abort,Device};
+ {true,_,NewDevice} ->
+ io:format(NewDevice,
+ "~n~n************************* RB ERROR ************************~n" ++
+ "A report on bad form was encountered here and the logging~n" ++
+ "process was aborted. Note that there may well be remaining~n" ++
+ "reports that haven't yet been logged. Please see the rb~n" ++
+ "manual for more info.~n" ++
+ "***********************************************************~n", []),
+ io:format("rb: Logging aborted.~n"),
+ {abort,NewDevice};
+ {false,_,NewDevice} ->
+ io:format(NewDevice,
+ "~n ********* RB: UNPRINTABLE REPORT ********~n~n", []),
+ io:format("rb: Logging resumed...~n~n"),
+ {proceed,NewDevice}
+ end.
+
+read_rep_msg(Fd, FilePosition) ->
+ file:position(Fd, {bof, FilePosition}),
+ Res =
+ case catch read_report(Fd) of
+ {ok, Report} ->
+ {_ShortDescr, Date} = get_short_descr(Report),
+ {Date, Report};
+ _ -> error
+ end,
+ file:close(Fd),
+ Res.