-- ------------------------------------------------------------

{- |
   Module     : Text.XML.HXT.IO.GetFILE
   Copyright  : Copyright (C) 2008 Uwe Schmidt
   License    : MIT

   Maintainer : Uwe Schmidt (uwe@fh-wedel.de)
   Stability  : stable
   Portability: portable

   The GET method for file protocol

-}

-- ------------------------------------------------------------

module Text.XML.HXT.IO.GetFILE
    ( getStdinCont
    , getCont
    )

where

import           Control.Exception      ( try )

import qualified Data.ByteString.Lazy   as B

import           Network.URI            ( unEscapeString
                                        )
import           System.IO.Error        ( ioeGetErrorString
                                        )
import           System.Directory       ( doesFileExist
                                        -- , getPermissions
                                        -- , readable
                                        )
import           Text.XML.HXT.DOM.XmlKeywords

-- ------------------------------------------------------------

getStdinCont            :: Bool -> IO (Either ([(String, String)], String) B.ByteString)
getStdinCont strictInput
    = do
      c <- try ( do
                 cb <- B.getContents
                 if strictInput
                    then B.length cb `seq` return cb
                    else                   return cb
               )
      return (either readErr Right c)
    where
    readErr e
        = Left ( [ (transferStatus,  "999")
                 , (transferMessage, msg)
                 ]
               , msg
               )
          where
          msg = "stdin read error: " ++ es
          es  = ioeGetErrorString e

getCont         :: Bool -> String -> IO (Either ([(String, String)], String) B.ByteString)
getCont strictInput source
    = do                        -- preliminary
      source'' <- checkFile source'
      case source'' of
           Nothing -> return $ fileErr "file not found"
           Just fn -> do
                      -- perm <- getPermissions fn  -- getPermission may fail
                      -- if not (readable perm)
                      if False
                         then return $ fileErr "file not readable"
                         else do
                              c <- try $
                                   do
                                   cb <- B.readFile fn
                                   if strictInput
                                      then B.length `seq` return cb
                                      else                return cb
                              return (either readErr Right c)
    where
    source' = drivePath $ source
    readErr e
        = fileErr (ioeGetErrorString e)
    fileErr msg0
        = Left ( [ (transferStatus,  "999")
                 , (transferMessage, msg)
                 ]
               , msg
               )
          where
          msg = "file read error: " ++ show msg0 ++ " when accessing " ++ show source'

    -- remove leading / if file starts with windows drive letter, e.g. /c:/windows -> c:/windows
    drivePath ('/' : file@(d : ':' : _more))
        | d `elem` ['A'..'Z'] || d `elem` ['a'..'z']
            = file
    drivePath file
        = file

-- | check whether file exists, if not
-- try to unescape filename and check again
-- return the existing filename

checkFile       :: String -> IO (Maybe String)
checkFile fn
    = do
      exists <- doesFileExist fn
      if exists
         then return (Just fn)
         else do
              exists' <- doesFileExist fn'
              return ( if exists'
                       then Just fn'
                       else Nothing
                     )
    where
    fn' = unEscapeString fn

-- ------------------------------------------------------------