multi color filter tail

root / Tail.hs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
{-# LANGUAGE OverloadedStrings #-}
module Tail
  ( tailText
  ) where

import Data.Array (Array, array, (//), (!))
import qualified Data.ByteString.Char8 as BS
import Data.Foldable (foldlM)
import Data.Monoid ((<>))
import Data.Time.Clock (getCurrentTime)
import Data.Time.LocalTime (getTimeZone, utcToLocalTime)
import Data.Time.Format (formatTime, defaultTimeLocale)
import Text.Regex.Posix (matchM, matchTest)
import System.Exit (ExitCode)
import System.Process (system)

import Util
import TailTypes

type Substitutions = Array Char BS.ByteString
default_subst :: Substitutions
default_subst = array ('\0','\127') [('\\',BS.singleton '\\')]

timeFmt :: String -> IO String
timeFmt f = do
  t <- getCurrentTime
  z <- getTimeZone t
  return $ formatTime defaultTimeLocale f (utcToLocalTime z t)

shellEscape :: BS.ByteString -> BS.ByteString
-- shellEscape = BS.concatMap $ \c -> if isAlphaNum c then BS.singleton c else BS.pack ['\\',c]
shellEscape s
  | BS.null s = s
  | otherwise = '\\' `BS.cons` BS.intersperse '\\' s

substText :: Substitutions -> (BS.ByteString -> BS.ByteString) -> BS.ByteString -> BS.ByteString
substText sub f = BS.concat . go . BS.split '\\' where
  go (h:t) = h : rep t
  go l = l
  rep (p:l)
    | Just (c, r) <- BS.uncons p = f (sub!c) : r : rep l
    | otherwise = f (sub!'\\') : go l
  rep [] = []

matchText :: Substitutions -> TailMatch -> BS.ByteString -> Maybe Substitutions
matchText sub MatchAll t = Just (sub // [('_',t)])
matchText sub (MatchRegex m) t =
  (\(pre, mat, post, exps) ->
    (sub
      // [('_',t), ('`',pre), ('&',mat), ('\'',post)]
      // zip ['1'..'9'] exps))
    <$> matchM m t
matchText sub (MatchNotRegex m) t =
  not (matchTest m t) ?> (sub // [('_',t)])

execute :: TailRuntime -> Tail -> BS.ByteString -> IO ExitCode
execute tr th e = do
  tailErrMsg tr th ("execute: " <> e)
  system $ BS.unpack e

tailText :: TailRuntime -> Tail -> BS.ByteString -> IO ()
tailText tr t x = do
  now <- timeFmt $ tailTimeFmt t
  mapM_ proc $ foldlM mact (x, default_subst // [('0',tailName t),('@',BS.pack now)], mempty, []) (tailMatches t)
  where
  proc (out, _, color, exec) = do
    tailOutput tr color out
    mapM_ (execute tr t) exec
  mact r@(s, sub, cl, el) (m, a)
    | Just sub' <- matchText sub m s = case a of
        ActionNone -> return (s, sub', cl, el)
        ActionHide -> Nothing
        ActionColor c -> return (s, sub', c <> cl, el)
        ActionSubst s' -> return (substText sub' id s', sub', cl, el)
        ActionExecute e -> return (s, sub', cl, (substText sub' shellEscape e) : el)
    | otherwise = return r