1structure tailbuffer :> tailbuffer =
2struct
3
4type t = {v : string Vector.vector, i: int, full : bool, s : string, max : int,
5          patterns : (string * bool) list}
6
7fun new {numlines,patterns} : t =
8  if numlines < 1 then raise Fail "tailbuffer.new: numlines <= 0"
9  else
10    let
11      val v = Vector.tabulate(numlines, (fn _ => ""))
12    in
13      {v = v, i = 0, full = false, s = "", max = numlines,
14       patterns = List.map (fn p => (p, false)) patterns}
15    end
16
17fun front_last l =
18  let
19    fun recurse acc l =
20      case l of
21          [] => NONE
22        | [h] => SOME (List.rev acc, h)
23        | h::t => recurse (h::acc) t
24  in
25    recurse [] l
26  end
27
28fun fill1 (news, {v,i,full,s,max,patterns} : t) : t =
29  let
30    val newline = s^news
31  in
32    {v = Vector.update(v,i,newline),
33     i = (i + 1) mod max,
34     full = full orelse i + 1 = max,
35     s = "",
36     max = max,
37     patterns =
38       List.map (fn pat as (_, true) => pat
39                  | (p, _) => (p, String.isSubstring p newline)) patterns}
40  end
41
42fun add_incomplete_line l ({v,i,full,s,max,patterns}:t) : t =
43  {v = v, i = i, full = full, max = max, patterns = patterns, s = s ^ l}
44
45fun append s' (tb:t) : t =
46  let
47    val lines = String.fields (fn c => c = #"\n") s'
48  in
49    case front_last lines of
50        NONE => raise Fail "tailbuffer.append: can't happen"
51      | SOME(lines, line) =>
52        let
53          val tb' = List.foldl fill1 tb lines
54        in
55          add_incomplete_line line tb'
56        end
57  end
58
59fun last_line ({v,i,full,max,...} : t) =
60  if not full andalso i = 0 then NONE
61  else SOME (Vector.sub(v,(i - 1) mod max))
62
63fun output ({v,i,full,s,max,patterns,...}:t) =
64  let
65    val patterns_seen = List.map #1 (List.filter #2 patterns)
66  in
67    if not full andalso i = 0 then
68      { fulllines = [], lastpartial = s, patterns_seen = patterns_seen }
69    else
70      let
71        val limit = if full then i else 0
72        fun recurse acc j =
73          if j = limit then
74            Vector.sub(v, j)::acc
75          else recurse (Vector.sub(v,j)::acc) ((j - 1) mod max)
76      in
77        { fulllines = recurse [] ((i - 1) mod max), lastpartial = s,
78          patterns_seen = patterns_seen }
79      end
80  end
81
82end
83