    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Network.HTTP.Utils
    -- Copyright   :  See LICENSE file
    -- License     :  BSD
    --
    -- Maintainer  :  Ganesh Sittampalam <http@projects.haskell.org>
    -- Stability   :  experimental
    -- Portability :  non-portable (not tested)
    --
    -- Set of utility functions and definitions used by package modules.
    --
    module Network.HTTP.Utils
           ( trim     -- :: String -> String
           , trimL    -- :: String -> String
           , trimR    -- :: String -> String

           , crlf     -- :: String
           , lf       -- :: String
           , sp       -- :: String

           , split    -- :: Eq a => a -> [a] -> Maybe ([a],[a])
           , splitBy  -- :: Eq a => a -> [a] -> [[a]]

           , readsOne -- :: Read a => (a -> b) -> b -> String -> b

           , dropWhileTail -- :: (a -> Bool) -> [a] -> [a]
           , chopAtDelim   -- :: Eq a => a -> [a] -> ([a],[a])

           ) where

    import Data.Char
    import Data.List ( elemIndex )
    import Data.Maybe ( fromMaybe )

    -- | @crlf@ is our beloved two-char line terminator.
    crlf :: String
    crlf = "\r\n"

    -- | @lf@ is a tolerated line terminator, per RFC 2616 section 19.3.
    lf :: String
    lf = "\n"

    -- | @sp@ lets you save typing one character.
    sp :: String
    sp   = " "

    -- | @split delim ls@ splits a list into two parts, the @delim@ occurring
    -- at the head of the second list.  If @delim@ isn't in @ls@, @Nothing@ is
    -- returned.
    split :: Eq a => a -> [a] -> Maybe ([a],[a])
    split delim list = case delim `elemIndex` list of
        Nothing -> Nothing
        Just x  -> Just $ splitAt x list

    -- | @trim str@ removes leading and trailing whitespace from @str@.
    trim :: String -> String
    trim xs = trimR (trimL xs)

    -- | @trimL str@ removes leading whitespace (as defined by 'Data.Char.isSpace')
    -- from @str@.
    trimL :: String -> String
    trimL xs = dropWhile isSpace xs

    -- | @trimL str@ removes trailing whitespace (as defined by 'Data.Char.isSpace')
    -- from @str@.
    trimR :: String -> String
    trimR str = fromMaybe "" $ foldr trimIt Nothing str
     where
      trimIt x (Just xs) = Just (x:xs)
      trimIt x Nothing
       | isSpace x = Nothing
       | otherwise = Just [x]

    -- | @splitMany delim ls@ removes the delimiter @delim@ from @ls@.
    splitBy :: Eq a => a -> [a] -> [[a]]
    splitBy _ [] = []
    splitBy c xs =
        case break (==c) xs of
          (_,[]) -> [xs]
          (as,_:bs) -> as : splitBy c bs

    -- | @readsOne f def str@ tries to 'read' @str@, taking
    -- the first result and passing it to @f@. If the 'read'
    -- doesn't succeed, return @def@.
    readsOne :: Read a => (a -> b) -> b -> String -> b
    readsOne f n str =
     case reads str of
       ((v,_):_) -> f v
       _ -> n

    -- | @dropWhileTail p ls@ chops off trailing elements from @ls@
    -- until @p@ returns @False@.
    dropWhileTail :: (a -> Bool) -> [a] -> [a]
    dropWhileTail f ls =
     case foldr chop Nothing ls of { Just xs -> xs; Nothing -> [] }
      where
        chop x (Just xs) = Just (x:xs)
        chop x _
         | f x       = Nothing
         | otherwise = Just [x]

    -- | @chopAtDelim elt ls@ breaks up @ls@ into two at first occurrence
    -- of @elt@; @elt@ is elided too. If @elt@ does not occur, the second
    -- list is empty and the first is equal to @ls@.
    chopAtDelim :: Eq a => a -> [a] -> ([a],[a])
    chopAtDelim elt xs =
      case break (==elt) xs of
        (_,[])    -> (xs,[])
        (as,_:bs) -> (as,bs)