    {-# LANGUAGE CPP #-}
    {-# LANGUAGE ForeignFunctionInterface #-}

    -- |
    -- Module: Options.Util
    -- License: MIT
    module Options.Util where

    import           Data.Char (isAlphaNum, isLetter, isUpper)
    import qualified Data.Set as Set

    #if defined(OPTIONS_ENCODING_UTF8)
    import           Data.Char (chr)
    import           Data.ByteString.Unsafe (unsafeUseAsCStringLen)
    import qualified Data.ByteString.Char8 as Char8
    import           Foreign
    import           Foreign.C
    #endif

    stringToGhc704 :: String -> String
    #if defined(OPTIONS_ENCODING_UTF8)
    stringToGhc704 = decodeUtf8 . Char8.pack

    decodeUtf8 :: Char8.ByteString -> String
    decodeUtf8 bytes = map (chr . fromIntegral) word32s where
    	word32s = unsafePerformIO (unsafeUseAsCStringLen bytes io)
    	io (bytesPtr, len) = allocaArray len $ \wordsPtr -> do
    		nWords <- c_decodeString (castPtr bytesPtr) wordsPtr (fromIntegral len)
    		peekArray (fromIntegral nWords) wordsPtr

    foreign import ccall unsafe "hsoptions_decode_string"
    	c_decodeString :: Ptr Word8 -> Ptr Word32 -> CInt -> IO CInt
    #else
    stringToGhc704 = id
    #endif

    validFieldName :: String -> Bool
    validFieldName = valid where
    	valid s = case s of
    		[] -> False
    		c : cs -> validFirst c && all validGeneral cs
    	validFirst c = c == '_' || (isLetter c && not (isUpper c))
    	validGeneral c = isAlphaNum c || c == '_' || c == '\''

    validShortFlag :: Char -> Bool
    validShortFlag = isAlphaNum

    validLongFlag :: String -> Bool
    validLongFlag = valid where
    	valid s = case s of
    		[] -> False
    		c : cs -> validFirst c && all validGeneral cs
    	validFirst = isAlphaNum
    	validGeneral c = isAlphaNum c || c == '-' || c == '_'

    hasDuplicates :: Ord a => [a] -> Bool
    hasDuplicates xs = Set.size (Set.fromList xs) /= length xs