{-# LANGUAGE OverloadedStrings #-} module Links(extractLinks, linkToText, Link(..), c_extractLinks) where import Text.XML import qualified Data.Map as M import Network.URI import Data.Text (Text, unpack, append, pack, replace, strip) import qualified Data.Text.Foreign as FTxt import Data.Maybe import Types import Foreign.StablePtr import Foreign.C.String import Foreign.Marshal.Array import Control.Monad (forM) import Control.Exception (catch) import System.Directory -- For locating links.xml import System.FilePath import System.IO (hPrint, stderr) -- For error reporting data Link = Link { label :: Text, title :: Text, href :: URI } linkToText :: Link -> Text linkToText (Link label' title' href') = rmWs label' +++ "\t" +++ rmWs title' +++ "\t" +++ pack (show href') rmWs text = strip $ replace "\t" " " $ replace "\n" " " text extractLinks :: Document -> [Link] extractLinks (Document prologue root misc) = extractMisc (prologueBefore prologue) ++ extractMisc (prologueAfter prologue) ++ extractEl root ++ extractMisc misc extractMisc :: [Miscellaneous] -> [Link] extractMisc (MiscInstruction (Instruction target dat):misc) | Just uri <- parseURIReference $ unpack target = Link dat "" uri : extractMisc misc extractMisc (_:misc) = extractMisc misc extractMisc [] = [] extractEl el@(Element _ _ children) = extractElAttr el "{http://www.w3.org/1999/xlink}href" ++ extractElAttr el "href" ++ extractElAttr el "longdesc" ++ extractElAttr el "src" ++ extractNodes children extractElAttr (Element _ attrs children) attr | Just val <- attr `M.lookup` attrs, Just uri <- parseURIReference $ unpack val = [Link label' title' uri] | otherwise = [] where label' = nodesText children title' = fromMaybe "" $ M.lookup "title" attrs extractNodes (NodeElement el:nodes) = extractEl el ++ extractNodes nodes extractNodes (NodeInstruction instruct:nodes) = extractMisc [MiscInstruction instruct] ++ extractNodes nodes extractNodes (_:nodes) = extractNodes nodes extractNodes [] = [] (+++) = append nodesText :: [Node] -> Text nodesText (NodeElement (Element _ attrs children):nodes) = nodesText children +++ nodesText nodes nodesText (NodeContent text:nodes) = text +++ nodesText nodes nodesText (_:nodes) = nodesText nodes nodesText [] = "" linksFromPage :: Page -> [Link] linksFromPage Page { url = url', pageTitle = title', html = html', apps = apps', backStack = back', forwardStack = forward' } = -- TODO internationalize! link' "reload" title' url' : link' "reload without cache" "Fetch again from server without checking for a local copy" url' { uriScheme = "nocache+" ++ uriScheme url' } : [link' "back" t u | (t, u) <- head' back'] ++ [link' "forward" t u | (t, u) <- head' forward' ] ++ [link' n desc $ URI "app:" Nothing id "" "" | Application n _ desc id <- apps'] ++ extractLinks html' head' (a:_) = [a] head' [] = [] link' l t h = Link (pack l) (pack t) h readBookmarks :: IO Document readBookmarks = do dir <- getXdgDirectory XdgData "rhapsode" let file = dir "links.xml" exists <- doesFileExist file if exists then Text.XML.readFile def file `catch` handleInvalid else nodoc where handleInvalid err@(InvalidXMLFile _ _) = hPrint stderr err >> nodoc nodoc = return $ Document (Prologue [] Nothing []) (Element "empty" M.empty []) [] -- C API foreign export ccall c_extractLinks :: StablePtr Page -> IO (CArray CString) c_extractLinks c_page = do page <- deRefStablePtr c_page bookmarks <- readBookmarks ret <- forM (linksFromPage page ++ extractLinks bookmarks) $ \link -> do c_label <- text2cstring $ strip $ label link c_title <- text2cstring $ strip $ title link c_href <- newCString $ uriToString id (href link) "" return [c_label, c_title, c_href] nil <- newCString " " newArray0 nil $ concat ret text2cstring txt = FTxt.withCStringLen txt $ \s -> (peekCStringLen s >>= newCString)