anchor.diff:  flush_buffer(_, []) -> ok;
   flush_buffer(File, Buf) ->
       ok = file:write(File, lists:reverse(Buf)).
  +
  +% Misc utilities
  +
  +-type process_status() :: active | dead | ready.
  +process_status(Pid) ->
  +    case is_process_alive(Pid) of
  +        true -> active;
  +        false -> dead
  +    end.
  +
  +json_list(List) -> json_list(List, []).
  +json_list([], _) -> [];
  +json_list([X], L) ->
  +    [<<"[">>, lists:reverse([X|L]), <<"]">>];
  +json_list([X|R], L) ->
  +    json_list(R, [<<X/binary, ",">>|L]).
  +
  +-spec unique_key(jobname(), dict()) -> invalid_prefix | {ok, jobname()}.
  +unique_key(Prefix, Dict) ->
  +    C = string:chr(Prefix, $/) + string:chr(Prefix, $.),
  +    if C > 0 ->
  +        invalid_prefix;
  +    true ->
  +        {MegaSecs, Secs, MicroSecs} = now(),
  +        Key = disco:format("~s@~.16b:~.16b:~.16b", [Prefix, MegaSecs, Secs, MicroSecs]),
  +        case dict:is_key(Key, Dict) of
  +            false ->
  +                {ok, Key};
  +            true ->
  +                unique_key(Prefix, Dict)
  +        end
  +    end.
  +
  +event_filter(Key, EventList) ->
  +    [V || {K, V} <- EventList, K == Key].
  +
  +task_format(Msg) when is_atom(Msg) or is_binary(Msg) or is_list(Msg) ->
  +    "~s";
  +task_format(_Msg) ->
  +    "~w".
  +
  +% Utilities to process job event file.
  +
  +event_log(JobName) ->
  +    filename:join(disco:jobhome(JobName), "events").
  +
  +tail_log(JobName, N) ->
  +    Tail = string:tokens(os:cmd(["tail -n ", integer_to_list(N), " ",
  +                                 event_log(JobName),
  +                                 " 2>/dev/null"]), "\n"),
  +    [list_to_binary(L) || L <- lists:reverse(Tail)].
  +
  +grep_log(JobName, Query, N) ->
  +    % We dont want execute stuff like "grep -i `rm -Rf *` ..." so
  +    % only whitelisted characters are allowed in the query
  +    CQ = re:replace(Query, "[^a-zA-Z0-9:-_!@]", "", [global, {return, list}]),
  +    Lines = string:tokens(os:cmd(["grep -i \"", CQ ,"\" ",
  +                                  event_log(JobName),
  +                                  " 2>/dev/null | head -n ", integer_to_list(N)]), "\n"),
  +    [list_to_binary(L) || L <- lists:reverse(Lines)].
  +
  +delete_jobdir(JobName) ->
  +    Safe = string:chr(JobName, $.) + string:chr(JobName, $/),
  +    if Safe =:= 0 ->
  +            _ = os:cmd("rm -Rf " ++ disco:jobhome(JobName)),
  +            ok;
  +       true ->
  +            ok
  +    end.
  
positive.code:     lager:warning("Unknown message received: ~p", [Msg]),
      {noreply, State}.
  
  event_log(JobName) ->
      filename:join(disco:jobhome(JobName), "events").
  
  tail_log(JobName, N) ->
      Tail = string:tokens(os:cmd(["tail -n ", integer_to_list(N), " ",
                                   event_log(JobName),
                                   " 2>/dev/null"]), "\n"),
      [list_to_binary(L) || L <- lists:reverse(Tail)].
  
  grep_log(JobName, Query, N) ->
      % We dont want execute stuff like "grep -i `rm -Rf *` ..." so
      % only whitelisted characters are allowed in the query
      CQ = re:replace(Query, "[^a-zA-Z0-9:-_!@]", "", [global, {return, list}]),
      Lines = string:tokens(os:cmd(["grep -i \"", CQ ,"\" ",
                                    event_log(JobName),
                                    " 2>/dev/null | head -n ", integer_to_list(N)]), "\n"),
      [list_to_binary(L) || L <- lists:reverse(Lines)].
  
  event_filter(Key, EventList) ->
      [V || {K, V} <- EventList, K == Key].
  
  add_event(Host0, JobName, Msg, Params, {Events, MsgBuf}) ->
      {ok, {NMsg, LstLen0, MsgLst0}} = dict:find(JobName, MsgBuf),
  
negative.code: -module(event_server).
  -behaviour(gen_server).
  
  % Job notification.
  -export([new_job/2, end_job/1, clean_job/1]).
  % Retrieval.
  -export([get_jobs/0, get_jobs/1, get_jobinfo/1, get_job_events/3,
           get_map_results/1, get_results/1]).
  % Event logging.
  -export([event/4, event/5, event/6,
           task_event/2, task_event/3, task_event/4, task_event/5]).
  % Server.
  -export([start_link/0, init/1, handle_call/3, handle_cast/2, handle_info/2,
           terminate/2, code_change/3]).
  
  