@@ -36,7 +36,6 @@ module Text.Pandoc.Shared (
36 joinWithSep,
36 joinWithSep,
37 tabsToSpaces,
37 tabsToSpaces,
38 backslashEscape,
38 backslashEscape,
39− escapePreservingRegex,
41 stripTrailingNewlines,
40 stripTrailingNewlines,
42 removeLeadingTrailingSpace,
41 removeLeadingTrailingSpace,
@@ -74,12 +73,11 @@ module Text.Pandoc.Shared (
74 inTagsIndented
73 inTagsIndented
76import Text.Pandoc.Definition
75import Text.Pandoc.Definition
77−import Text.ParserCombinators.Parsec
76+import Text.ParserCombinators.Parsec as Parsec
78−import Text.Pandoc.Entities ( decodeEntities, encodeEntities, characterEntity )
77+import Text.Pandoc.Entities ( decodeEntities, charToEntity )
79−import Text.Regex ( matchRegexAll, mkRegex, subRegex, Regex )
80−import Text.PrettyPrint.HughesPJ as PP ( text, char, (<>), ($$), nest, Doc,
78+import Text.PrettyPrint.HughesPJ as PP ( text, char, (<>),
81− isEmpty )
79+ ($$), nest, Doc, isEmpty )
82−import Data.Char ( toLower )
80+import Data.Char ( toLower, ord )
83import Data.List ( find, groupBy, isPrefixOf )
81import Data.List ( find, groupBy, isPrefixOf )
85-- | Parse a string with a given parser and state.
83-- | Parse a string with a given parser and state.
@@ -228,17 +226,6 @@ backslashEscape special (x:xs) = if x `elem` special
228 then '\\':x:(backslashEscape special xs)
226 then '\\':x:(backslashEscape special xs)
229 else x:(backslashEscape special xs)
227 else x:(backslashEscape special xs)
231−-- | Escape string by applying a function, but don't touch anything that matches regex.
232−escapePreservingRegex :: (String -> String) -- ^ Escaping function
233− -> Regex -- ^ Regular expression
234− -> String -- ^ String to be escaped
236−escapePreservingRegex escapeFunction regex str =
237− case (matchRegexAll regex str) of
238− Nothing -> escapeFunction str
239− Just (before, matched, after, _) -> (escapeFunction before) ++
240− matched ++ (escapePreservingRegex escapeFunction regex after)
242-- | Returns @True@ if string ends with given character.
229-- | Returns @True@ if string ends with given character.
243endsWith :: Char -> [Char] -> Bool
230endsWith :: Char -> [Char] -> Bool
244endsWith char [] = False
231endsWith char [] = False
@@ -536,20 +523,56 @@ replaceRefLinksInline keytable (Quoted t lst) =
536 Quoted t (map (replaceRefLinksInline keytable) lst)
523 Quoted t (map (replaceRefLinksInline keytable) lst)
537replaceRefLinksInline keytable other = other
524replaceRefLinksInline keytable other = other
526+-- | Parse SGML character entity.
527+sgmlCharacterEntity :: GenParser Char st [Char]
528+sgmlCharacterEntity = sgmlNamedEntity <|> sgmlDecimalEntity <|>
529+ sgmlHexEntity <?> "SGML entity"
531+-- | Parse SGML character entity.
532+sgmlNamedEntity :: GenParser Char st [Char]
533+sgmlNamedEntity = try $ do
534+ st <- Parsec.char '&'
535+ body <- many1 alphaNum
536+ end <- Parsec.char ';'
537+ return $ (st:body) ++ [end]
539+-- | Parse SGML decimal entity.
540+sgmlDecimalEntity :: GenParser Char st [Char]
541+sgmlDecimalEntity = try $ do
543+ body <- many1 (oneOf "0123456789")
544+ end <- Parsec.char ';'
545+ return $ st ++ body ++ [end]
547+-- | Parse SGML hexadecimal entity.
548+sgmlHexEntity :: GenParser Char st [Char]
549+sgmlHexEntity = try $ do
552+ body <- many1 (oneOf "0123456789ABCDEFabcdef")
553+ end <- Parsec.char ';'
554+ return $ st ++ (hex:body) ++ [end]
556+-- | Escape special character to SGML entity.
557+specialCharToEntity :: Char -> [Char]
558+specialCharToEntity c = if (c `elem` "&<>\"") || (ord c > 127)
539-- | Escape string, preserving character entities.
562-- | Escape string, preserving character entities.
540stringToSGML :: String -> String
563stringToSGML :: String -> String
541−stringToSGML =
564+stringToSGML str =
542− encodeEntities . (escapePreservingRegex escapeSGML characterEntity)
565+ let segment = sgmlCharacterEntity <|>
567+ return $ specialCharToEntity c})
568+ sgmlString = (do{segs <- many segment; return $ concat segs}) in
569+ case parse sgmlString str str of
570+ Left err -> error $ "\nError:\n" ++ show err
571+ Right result -> result
544−-- | Escape string as needed for HTML. Entity references are not preserved.
573+-- | Escape string as needed for SGML. Entity references are not preserved.
545escapeSGML :: String -> String
574escapeSGML :: String -> String
546−escapeSGML [] = []
575+escapeSGML = concatMap specialCharToEntity
547−escapeSGML (x:xs) = case x of
548− '&' -> "&" ++ escapeSGML xs
549− '<' -> "<" ++ escapeSGML xs
550− '>' -> ">" ++ escapeSGML xs
551− '"' -> """ ++ escapeSGML xs
552− _ -> x:(escapeSGML xs)
554-- | Return a text object with a string of formatted SGML attributes.
577-- | Return a text object with a string of formatted SGML attributes.
555attributeList :: [(String, String)] -> Doc
578attributeList :: [(String, String)] -> Doc