{-# LANGUAGE CPP, ForeignFunctionInterface, PatternGuards #-} ----------------------------------------------------------------------------- -- | -- Module : Text.CSL.Input.Bibutils -- Copyright : (C) 2008 Andrea Rossato -- License : BSD3 -- -- Maintainer : andrea.rossato@unitn.it -- Stability : unstable -- Portability : unportable -- ----------------------------------------------------------------------------- module Text.CSL.Input.Bibutils ( readBiblioFile , readBiblioString , BibFormat (..) ) where import Data.ByteString.Lazy.UTF8 ( fromString ) import Data.Char import System.FilePath ( takeExtension ) import Text.CSL.Pickle import Text.CSL.Reference import Text.CSL.Input.Json import Text.CSL.Input.MODS import Text.JSON.Generic #ifdef USE_BIBUTILS import Control.Exception ( bracket, catch ) import Control.Monad.Trans ( liftIO ) import System.FilePath ( (), (<.>) ) import System.IO.Error ( isAlreadyExistsError ) import System.Directory import Text.Bibutils #endif -- | Read a file with a bibliographic database. The database format -- is recognized by the file extension. -- -- Supported formats are: @json@, @mods@, @bibtex@, @biblatex@, @ris@, -- @endnote@, @endnotexml@, @isi@, @medline@, and @copac@. readBiblioFile :: FilePath -> IO [Reference] #ifdef USE_BIBUTILS readBiblioFile f = case getExt f of ".mods" -> readBiblioFile' f mods_in ".bib" -> readBiblioFile' f biblatex_in ".bibtex" -> readBiblioFile' f bibtex_in ".ris" -> readBiblioFile' f ris_in ".enl" -> readBiblioFile' f endnote_in ".xml" -> readBiblioFile' f endnotexml_in ".wos" -> readBiblioFile' f isi_in ".medline" -> readBiblioFile' f medline_in ".copac" -> readBiblioFile' f copac_in ".json" -> readJsonInput f ".native" -> readFile f >>= return . decodeJSON _ -> error $ "citeproc: the format of the bibliographic database could not be recognized\n" ++ "using the file extension." #else readBiblioFile f | ".mods" <- getExt f = readModsCollectionFile f | ".json" <- getExt f = readJsonInput f | ".native" <- getExt f = readFile f >>= return . decodeJSON | otherwise = error $ "citeproc: Bibliography format not supported.\n" ++ "citeproc-hs was not compiled with bibutils support." #endif data BibFormat = Mods | Json | Native #ifdef USE_BIBUTILS | Bibtex | BibLatex | Ris | Endnote | EndnotXml | Isi | Medline | Copac #endif readBiblioString :: BibFormat -> String -> IO [Reference] readBiblioString b s | Mods <- b = return $ readXmlString xpModsCollection (fromString s) | Json <- b = return $ readJsonInputString s | Native <- b = return $ decodeJSON s #ifdef USE_BIBUTILS | Bibtex <- b = go bibtex_in | BibLatex <- b = go biblatex_in | Ris <- b = go ris_in | Endnote <- b = go endnote_in | EndnotXml <- b = go endnotexml_in | Isi <- b = go isi_in | Medline <- b = go medline_in | Copac <- b = go copac_in #endif | otherwise = error "in readBiblioString" #ifdef USE_BIBUTILS where go f = withTempDir "citeproc" $ \tdir -> do let tfile = tdir "bibutils-tmp.biblio" writeFile tfile s readBiblioFile' tfile f #endif #ifdef USE_BIBUTILS readBiblioFile' :: FilePath -> BiblioIn -> IO [Reference] readBiblioFile' fin bin | bin == mods_in = readModsCollectionFile fin | otherwise = withTempDir "citeproc" $ \tdir -> do let tfile = tdir "bibutils-tmp" param <- bibl_initparams bin mods_out "hs-bibutils" bibl <- bibl_init unsetBOM param setCharsetIn param bibl_charset_unicode setCharsetOut param bibl_charset_unicode _ <- bibl_read param bibl fin _ <- bibl_write param bibl tfile bibl_free bibl bibl_freeparams param refs <- readModsCollectionFile tfile return $! refs -- | Perform a function in a temporary directory and clean up. withTempDir :: FilePath -> (FilePath -> IO a) -> IO a withTempDir baseName = bracket (createTempDir 0 baseName) (removeDirectoryRecursive) -- | Create a temporary directory with a unique name. createTempDir :: Integer -> FilePath -> IO FilePath createTempDir num baseName = do sysTempDir <- getTemporaryDirectory let dirName = sysTempDir baseName <.> show num liftIO $ Control.Exception.catch (createDirectory dirName >> return dirName) $ \e -> if isAlreadyExistsError e then createTempDir (num + 1) baseName else ioError e #endif getExt :: String -> String getExt = takeExtension . map toLower