packages feed

glirc 2.22 → 2.23

raw patch · 40 files changed

+2797/−2130 lines, 40 filesdep +freedep +template-haskelldep ~vtyPVP ok

version bump matches the API change (PVP)

Dependencies added: free, template-haskell

Dependency ranges changed: vty

API changes (from Hackage documentation)

- Client.Commands.Arguments: [NoArg] :: ArgumentSpec ()
- Client.Commands.Arguments: [OptTokenArg] :: String -> ArgumentSpec rest -> ArgumentSpec (Maybe (String, rest))
- Client.Commands.Arguments: [RemainingArg] :: String -> ArgumentSpec String
- Client.Commands.Arguments: [ReqTokenArg] :: String -> ArgumentSpec rest -> ArgumentSpec (String, rest)
- Client.Commands.Arguments: data ArgumentSpec :: * -> *
- Client.Commands.Arguments: instance GHC.Show.Show (Client.Commands.Arguments.ArgumentSpec s)
- Client.Commands.Arguments: parseArguments :: ArgumentSpec a -> String -> Maybe a
- Client.Configuration: [_configConfigPath] :: Configuration -> Maybe FilePath
- Client.Configuration: configConfigPath :: Lens' Configuration (Maybe FilePath)
- Client.Configuration: resolveConfigurationPath :: FilePath -> IO FilePath
- Client.Image.Arguments: argumentsImage :: Palette -> ArgumentSpec a -> String -> Image'
- Client.Image.MircFormatting: parseIrcTextExplicit :: Text -> Image'
- Client.State: [_clientNextConnectionId] :: ClientState -> !NetworkId
- Client.State: clientNextConnectionId :: Lens' ClientState NetworkId
+ Client.CApi: chatExtension :: Ptr () -> Text -> Text -> Text -> [ActiveExtension] -> IO Bool
+ Client.CApi.Exports: glirc_clear_window :: Glirc_clear_window
+ Client.CApi.Exports: glirc_current_focus :: Glirc_current_focus
+ Client.CApi.Exports: glirc_free_string :: Glirc_free_string
+ Client.CApi.Exports: glirc_free_strings :: Glirc_free_strings
+ Client.CApi.Exports: glirc_identifier_cmp :: Glirc_identifier_cmp
+ Client.CApi.Exports: glirc_inject_chat :: Glirc_inject_chat
+ Client.CApi.Exports: glirc_is_channel :: Glirc_is_channel
+ Client.CApi.Exports: glirc_is_logged_on :: Glirc_is_channel
+ Client.CApi.Exports: glirc_list_channel_users :: Glirc_list_channel_users
+ Client.CApi.Exports: glirc_list_channels :: Glirc_list_channels
+ Client.CApi.Exports: glirc_list_networks :: Glirc_list_networks
+ Client.CApi.Exports: glirc_mark_seen :: Glirc_mark_seen
+ Client.CApi.Exports: glirc_my_nick :: Glirc_my_nick
+ Client.CApi.Exports: glirc_print :: Glirc_print
+ Client.CApi.Exports: glirc_send_message :: Glirc_send_message
+ Client.CApi.Exports: type Glirc_current_focus = Ptr () api token -> Ptr CString newly allocated network string -> Ptr CSize network length -> Ptr CString newly allocated target string -> Ptr CSize target length -> IO ()
+ Client.CApi.Exports: type Glirc_inject_chat = Ptr () api token -> CString network -> CSize network length -> CString source -> CSize source length -> CString target -> CSize target length -> CString message -> CSize message length -> IO CInt 0 on success
+ Client.CApi.Exports: type Glirc_is_channel = Ptr () api token -> CString network name -> CSize network length -> CString target name -> CSize target length -> IO CInt boolean
+ Client.CApi.Exports: type Glirc_is_logged_on = Ptr () api token -> CString network name -> CSize network length -> CString target name -> CSize target length -> IO CInt boolean
+ Client.CApi.Types: FgnChat :: FgnStringLen -> FgnStringLen -> FgnStringLen -> FgnChat
+ Client.CApi.Types: [fgnChat] :: FgnExtension -> FunPtr ProcessChat
+ Client.CApi.Types: [fhMessage] :: FgnChat -> FgnStringLen
+ Client.CApi.Types: [fhNetwork] :: FgnChat -> FgnStringLen
+ Client.CApi.Types: [fhTarget] :: FgnChat -> FgnStringLen
+ Client.CApi.Types: data FgnChat
+ Client.CApi.Types: exportText :: Ptr CString -> Ptr CSize -> Text -> IO ()
+ Client.CApi.Types: instance Foreign.Storable.Storable Client.CApi.Types.FgnChat
+ Client.CApi.Types: poke' :: Storable a => Ptr a -> a -> IO ()
+ Client.CApi.Types: runProcessChat :: Dynamic ProcessChat
+ Client.CApi.Types: withText0 :: Text -> (CStringLen -> IO a) -> IO a
+ Client.Commands.Arguments.Parser: parse :: r -> Args r a -> String -> Maybe a
+ Client.Commands.Arguments.Renderer: render :: Palette -> r -> Bool -> Args r a -> String -> Image'
+ Client.Commands.Arguments.Spec: RemainingArgument :: ArgumentShape
+ Client.Commands.Arguments.Spec: TokenArgument :: ArgumentShape
+ Client.Commands.Arguments.Spec: [Argument] :: ArgumentShape -> String -> (r -> String -> Maybe a) -> Arg r a
+ Client.Commands.Arguments.Spec: [Extension] :: String -> (r -> String -> Maybe (Args r a)) -> Arg r a
+ Client.Commands.Arguments.Spec: [Optional] :: Args r a -> Arg r (Maybe a)
+ Client.Commands.Arguments.Spec: data Arg :: * -> * -> *
+ Client.Commands.Arguments.Spec: data ArgumentShape
+ Client.Commands.Arguments.Spec: extensionArg :: String -> (r -> String -> Maybe (Args r a)) -> Args r a
+ Client.Commands.Arguments.Spec: numberArg :: Args r Int
+ Client.Commands.Arguments.Spec: optionalArg :: Args r a -> Args r (Maybe a)
+ Client.Commands.Arguments.Spec: remainingArg :: String -> Args r String
+ Client.Commands.Arguments.Spec: simpleToken :: String -> Args r String
+ Client.Commands.Arguments.Spec: tokenList :: [String] -> [String] -> Args r [String]
+ Client.Commands.Arguments.Spec: type Args r = Ap (Arg r)
+ Client.Image.Message: drawWindowLine :: Palette -> Int -> PaddingMode -> WindowLine -> [Image']
+ Client.Image.Message: parseIrcTextWithNicks :: Palette -> HashSet Identifier -> HashSet Identifier -> Bool -> Text -> Image'
+ Client.Image.MircFormatting: mircColor :: Int -> Maybe Color
+ Client.Image.MircFormatting: parseIrcText' :: Bool -> Text -> Image'
+ Client.State: [_clientConfigPath] :: ClientState -> !(Maybe FilePath)
+ Client.State: channelUserList :: Text -> Identifier -> ClientState -> [Identifier]
+ Client.State: clientConfigPath :: Lens' ClientState (Maybe FilePath)
+ Client.State: clientHighlightsNetwork :: Text -> ClientState -> HashSet Identifier
- Client.CApi.Types: FgnExtension :: FunPtr StartExtension -> FunPtr StopExtension -> FunPtr ProcessMessage -> FunPtr ProcessCommand -> CString -> CInt -> FgnExtension
+ Client.CApi.Types: FgnExtension :: FunPtr StartExtension -> FunPtr StopExtension -> FunPtr ProcessMessage -> FunPtr ProcessChat -> FunPtr ProcessCommand -> CString -> CInt -> FgnExtension
- Client.Commands: Command :: NonEmpty Text -> ArgumentSpec a -> Text -> CommandImpl a -> Command
+ Client.Commands: Command :: NonEmpty Text -> Args ClientState a -> Text -> CommandImpl a -> Command
- Client.Commands: [cmdArgumentSpec] :: Command -> ArgumentSpec a
+ Client.Commands: [cmdArgumentSpec] :: Command -> Args ClientState a
- Client.Commands.Interpolation: [MacroSpec] :: ArgumentSpec s -> MacroSpec
+ Client.Commands.Interpolation: [MacroSpec] :: (forall r. Args r [String]) -> MacroSpec
- Client.Configuration: Configuration :: ServerSettings -> (HashMap Text ServerSettings) -> Palette -> Text -> HashSet Identifier -> PaddingMode -> Maybe FilePath -> Recognizer Macro -> [FilePath] -> Maybe FilePath -> HashSet Identifier -> Bool -> Bool -> Bool -> KeyMap -> LayoutMode -> [Modifier] -> Configuration
+ Client.Configuration: Configuration :: ServerSettings -> (HashMap Text ServerSettings) -> Palette -> Text -> HashSet Identifier -> PaddingMode -> Recognizer Macro -> [FilePath] -> Maybe FilePath -> HashSet Identifier -> Bool -> Bool -> Bool -> KeyMap -> LayoutMode -> [Modifier] -> Configuration
- Client.Configuration: configurationSpec :: ValueSpecs (Maybe FilePath -> ServerSettings -> Configuration)
+ Client.Configuration: configurationSpec :: ValueSpecs (ServerSettings -> Configuration)
- Client.State: ClientState :: !(Map Focus Window) -> !Focus -> !Focus -> !Focus -> !Subfocus -> ![Focus] -> !(IntMap NetworkState) -> !NetworkId -> !(TQueue NetworkEvent) -> !(HashMap Text NetworkId) -> !Configuration -> !EditBox -> !Int -> !Int -> !Int -> !Int -> !Bool -> !Bool -> Maybe Regex -> !LayoutMode -> !Bool -> !(HashSet Identifier) -> !ExtensionState -> ![LogLine] -> Maybe Text -> Maybe Stats -> ClientState
+ Client.State: ClientState :: !(Map Focus Window) -> !Focus -> !Focus -> !Focus -> !Subfocus -> ![Focus] -> !(IntMap NetworkState) -> !(TQueue NetworkEvent) -> !(HashMap Text NetworkId) -> !Configuration -> !(Maybe FilePath) -> !EditBox -> !Int -> !Int -> !Int -> !Int -> !Bool -> !Bool -> Maybe Regex -> !LayoutMode -> !Bool -> !(HashSet Identifier) -> !ExtensionState -> ![LogLine] -> Maybe Text -> Maybe Stats -> ClientState
- Client.State: withClientState :: Configuration -> (ClientState -> IO a) -> IO a
+ Client.State: withClientState :: Maybe FilePath -> Configuration -> (ClientState -> IO a) -> IO a
- Client.State.EditBox: class HasLine c_aIxW where pos = (.) line pos text = (.) line text
+ Client.State.EditBox: class HasLine c_az5Q where pos = (.) line pos text = (.) line text
- Client.State.EditBox: line :: HasLine c_aIxW => Lens' c_aIxW Line
+ Client.State.EditBox: line :: HasLine c_az5Q => Lens' c_az5Q Line
- Client.State.EditBox: pos :: HasLine c_aIxW => Lens' c_aIxW Int
+ Client.State.EditBox: pos :: HasLine c_az5Q => Lens' c_az5Q Int
- Client.State.EditBox: text :: HasLine c_aIxW => Lens' c_aIxW String
+ Client.State.EditBox: text :: HasLine c_az5Q => Lens' c_az5Q String
- Client.State.EditBox.Content: class HasLine c_aIxW where pos = (.) line pos text = (.) line text
+ Client.State.EditBox.Content: class HasLine c_az5Q where pos = (.) line pos text = (.) line text
- Client.State.EditBox.Content: line :: HasLine c_aIxW => Lens' c_aIxW Line
+ Client.State.EditBox.Content: line :: HasLine c_az5Q => Lens' c_az5Q Line
- Client.State.EditBox.Content: pos :: HasLine c_aIxW => Lens' c_aIxW Int
+ Client.State.EditBox.Content: pos :: HasLine c_az5Q => Lens' c_az5Q Int
- Client.State.EditBox.Content: text :: HasLine c_aIxW => Lens' c_aIxW String
+ Client.State.EditBox.Content: text :: HasLine c_az5Q => Lens' c_az5Q String
- Client.View.Help: helpImageLines :: Maybe Text -> Palette -> [Image']
+ Client.View.Help: helpImageLines :: ClientState -> Maybe Text -> Palette -> [Image']

Files

ChangeLog.md view
@@ -1,5 +1,26 @@ # Revision history for glirc2 +## 2.23++* CONFIGURATION FILE CHANGE: Relative paths are now resolved+  from the directory of the configuration file. The "~" alias+  for the home directory works for resolving paths relative+  to $HOME. Absolute paths are unmodified.++* Support network:#channel in /query++* Add an optional OTR extension. See the otr-extension/ directory+  and the wiki for more information.++* Extended the C API with `glirc_inject_chat` and `chat_entrypoint`++* Smarter WYSIWYG highlighting in text edit box. Nicknames are+  highlighted and `/mode` command parameters get correct placeholders.++* Added more information to `/palette`++* Added "human readable" labels to `/rtsstats`+ ## 2.22  * Added dynanic indentation of wrapped lines. Lines now
README.md view
@@ -95,6 +95,11 @@ Learn more about this file format at [config-value](http://hackage.haskell.org/package/config-value)  ```+-- vim: filetype=config-value+-- Grab the Vim syntax highlighting file from the config-value package++-- Learn more about these settings with `glirc2 --config-format`+ -- Defaults used when not specified on command line defaults:   nick:            "yournick"@@ -143,7 +148,18 @@  extra-highlights: ["glirc", "lens"] +nick-padding:+   side: left -- try right if you don't like left padding+   width: 13++url-opener: "open" -- This works on macOS, "gnome-open" for GNOME++key-bindings:+  * bind: "C-M-b"+    command: "masks b"+ palette:+  line-marker: yellow   time:     fg: [10,10,10] -- RGB values for color for timestamps     bg: blue
+ exec/Exports.hs view
@@ -0,0 +1,30 @@+{-|+Module      : Exports+Description : C API function exports+Copyright   : (c) Eric Mertens, 2017+License     : ISC+Maintainer  : emertens@gmail.com++Entry point into glirc2 from the C API+-}+module Exports () where++import Client.CApi.Exports+import Client.CApi.Types+import Foreign.C++foreign export ccall glirc_send_message       :: Glirc_send_message+foreign export ccall glirc_print              :: Glirc_print+foreign export ccall glirc_inject_chat        :: Glirc_inject_chat+foreign export ccall glirc_list_networks      :: Glirc_list_networks+foreign export ccall glirc_identifier_cmp     :: Glirc_identifier_cmp+foreign export ccall glirc_is_channel         :: Glirc_is_channel+foreign export ccall glirc_is_logged_on       :: Glirc_is_channel+foreign export ccall glirc_list_channels      :: Glirc_list_channels+foreign export ccall glirc_list_channel_users :: Glirc_list_channel_users+foreign export ccall glirc_my_nick            :: Glirc_my_nick+foreign export ccall glirc_mark_seen          :: Glirc_mark_seen+foreign export ccall glirc_clear_window       :: Glirc_clear_window+foreign export ccall glirc_free_string        :: Glirc_free_string+foreign export ccall glirc_free_strings       :: Glirc_free_strings+foreign export ccall glirc_current_focus      :: Glirc_current_focus
exec/Main.hs view
@@ -1,5 +1,4 @@ {-|-Module      : Main Description : Entry-point of executable Copyright   : (c) Eric Mertens, 2016 License     : ISC@@ -7,10 +6,8 @@  Entry point into glirc2. This module sets up VTY and launches the client. -}- module Main where -import Control.Concurrent import Control.Exception import Control.Lens import Control.Monad@@ -26,14 +23,16 @@ import Client.State import Client.State.Focus +import Exports ()+ -- | Main action for IRC client main :: IO () main =   do opts <- getOptions-     cfg  <- loadConfiguration' (view optConfigFile opts)-     runInUnboundThread $-       withClientState cfg $ \st0 ->-       withVty             $ \vty ->+     let mbPath = view optConfigFile opts+     cfg  <- loadConfiguration' mbPath+     withClientState mbPath cfg $ \st0 ->+       withVty $ \vty ->          do st1 <- clientStartExtensions    st0             st2 <- initialNetworkLogic opts st1             st3 <- updateTerminalSize vty   st2@@ -54,10 +53,10 @@        Right cfg -> return cfg        Left (ConfigurationReadFailed e) ->          report "Failed to open configuration:" e-       Left (ConfigurationParseFailed path e) ->-         report ("Failed to parse configuration file: " ++ path) e-       Left (ConfigurationMalformed path e) ->-         report ("Malformed configuration file: " ++ path ++ "\n(try --config-format)") e+       Left (ConfigurationParseFailed p e) ->+         report ("Failed to parse configuration file: " ++ p) e+       Left (ConfigurationMalformed p e) ->+         report ("Malformed configuration file: " ++ p ++ "\n(try --config-format)") e   where     report problem msg =       do hPutStrLn stderr problem
exec/linux_exported_symbols.txt view
@@ -1,13 +1,17 @@ { glirc_send_message; glirc_print;+glirc_inject_chat; glirc_identifier_cmp; glirc_list_networks; glirc_list_channels; glirc_list_channel_users; glirc_my_nick; glirc_mark_seen;+glirc_is_channel;+glirc_is_logged_on; glirc_clear_window;+glirc_current_focus; glirc_free_string; glirc_free_strings; };
exec/macos_exported_symbols.txt view
@@ -1,11 +1,15 @@ _glirc_send_message _glirc_print+_glirc_inject_chat _glirc_identifier_cmp _glirc_list_networks _glirc_list_channels _glirc_list_channel_users _glirc_my_nick _glirc_mark_seen+_glirc_is_channel+_glirc_is_logged_on _glirc_clear_window+_glirc_current_focus _glirc_free_string _glirc_free_strings
glirc.cabal view
@@ -1,5 +1,5 @@ name:                glirc-version:             2.22+version:             2.23 synopsis:            Console IRC client description:         Console IRC client                      .@@ -25,34 +25,29 @@ custom-setup   setup-depends: base     >=4.9  && <4.11,                  filepath >=1.4  && <1.5,-                 Cabal    >=1.24 && <3+                 Cabal    >=1.24 && <2.1  source-repository head   type: git   location: git://github.com/glguy/irc-core.git   branch: v2 -flag ExportCApi-  description: Export C functions used by the extension API (incompatible with GHCi)-  default: True-  manual: True- executable glirc2   main-is:             Main.hs+  other-modules:       Exports   ghc-options:         -threaded -rtsopts    hs-source-dirs:      exec   default-language:    Haskell2010 -  if flag(ExportCApi)-    if os(Linux)-      ld-options: -Wl,--dynamic-list=exec/linux_exported_symbols.txt-    if os(Darwin)-      ld-options: -Wl,-exported_symbols_list,exec/macos_exported_symbols.txt-   -- Constraints can be found on the library itself   build-depends:       base, glirc, lens, text, vty +  if os(Linux)+      ld-options: -Wl,--dynamic-list=exec/linux_exported_symbols.txt+  if os(Darwin)+      ld-options: -Wl,-exported_symbols_list,exec/macos_exported_symbols.txt+ library   ghc-options:         -Wall -O2   hs-source-dirs:      src@@ -67,7 +62,9 @@                        Client.CApi.Exports                        Client.CApi.Types                        Client.Commands-                       Client.Commands.Arguments+                       Client.Commands.Arguments.Spec+                       Client.Commands.Arguments.Parser+                       Client.Commands.Arguments.Renderer                        Client.Commands.Exec                        Client.Commands.Interpolation                        Client.Commands.Recognizer@@ -83,7 +80,6 @@                        Client.Hook.Znc.Buffextras                        Client.Hooks                        Client.Image-                       Client.Image.Arguments                        Client.Image.Layout                        Client.Image.LineWrap                        Client.Image.Message@@ -121,26 +117,29 @@   other-modules:       LensUtils                        StrictUnit                        Digraphs+                       DigraphQuote                        RtsStats                        Paths_glirc                        Build_glirc    build-depends:       base                 >=4.9    && <4.11,+                       HsOpenSSL            >=0.11   && <0.12,                        async                >=2.1    && <2.2,                        attoparsec           >=0.13   && <0.14,+                       base64-bytestring    >=1.0.0.1&& <1.1,                        bytestring           >=0.10.8 && <0.11,-                       base64-bytestring    >=1.0.0.1 && <1.1,-                       config-value         >=0.6    && <0.7,                        config-schema        >=0.4    && <0.6,+                       config-value         >=0.6    && <0.7,                        containers           >=0.5.7  && <0.6,                        directory            >=1.2.6  && <1.4,                        filepath             >=1.4.1  && <1.5,+                       free                 >=4.12   && <4.13,                        gitrev               >=1.2    && <1.4,                        hashable             >=1.2.4  && <1.3,-                       HsOpenSSL            >=0.11   && <0.12,+                       hookup               >=0.1    && <0.2,                        irc-core             >=2.3    && <2.4,-                       lens                 >=4.14   && <4.16,                        kan-extensions       >=5.0    && <5.1,+                       lens                 >=4.14   && <4.16,                        network              >=2.6.2  && <2.7,                        process              >=1.4.2  && <1.7,                        regex-tdfa           >=1.2    && <1.3,@@ -148,17 +147,14 @@                        socks                >=0.5.5  && <0.6,                        split                >=0.2    && <0.3,                        stm                  >=2.4    && <2.5,+                       template-haskell     >=2.11   && <2.13,                        text                 >=1.2.2  && <1.3,                        time                 >=1.6    && <1.9,                        transformers         >=0.5.2  && <0.6,                        unix                 >=2.7    && <2.8,                        unordered-containers >=0.2.7  && <0.3,                        vector               >=0.11   && <0.13,-                       vty                  >=5.11.1 && <5.16,-                       hookup               >=0.1    && <0.2--  if flag(ExportCApi)-    cpp-options: -DEXPORT_GLIRC_CAPI+                       vty                  >=5.11.1 && <5.16  test-suite test   type:                exitcode-stdio-1.0
include/glirc-api.h view
@@ -33,6 +33,12 @@         size_t tags_n; }; +struct glirc_chat {+        struct glirc_string network;+        struct glirc_string target;+        struct glirc_string message;+};+ struct glirc_command {         const struct glirc_string *params;         size_t params_n;@@ -41,6 +47,7 @@ typedef void *start_type         (struct glirc *G, const char *path); typedef void stop_type           (struct glirc *G, void *S); typedef enum process_result process_message_type(struct glirc *G, void *S, const struct glirc_message *);+typedef enum process_result process_chat_type(struct glirc *G, void *S, const struct glirc_chat *); typedef void process_command_type(struct glirc *G, void *S, const struct glirc_command *);  struct glirc_extension {@@ -50,17 +57,28 @@         stop_type            *stop;         process_message_type *process_message;         process_command_type *process_command;+        process_chat_type    *process_chat; };  int glirc_send_message(struct glirc *G, const struct glirc_message *);-int glirc_print(struct glirc *G, enum message_code, struct glirc_string msg);+int glirc_print(struct glirc *G, enum message_code, const char *msg, size_t msglen);+int glirc_inject_chat(struct glirc *G,+                const char* net, size_t netLen,+                const char* src, size_t srcLen,+                const char* tgt, size_t tgtLen,+                const char* msg, size_t msgLen); char ** glirc_list_networks(struct glirc *G); char ** glirc_list_channels(struct glirc *G, struct glirc_string network); char ** glirc_list_channel_users(struct glirc *G, struct glirc_string network, struct glirc_string channel);-char * glirc_my_nick(struct glirc *G, struct glirc_string network);+void glirc_current_focus(struct glirc *G, char **net, size_t *netlen, char **tgt , size_t *tgtlen);+char * glirc_my_nick(struct glirc *G, const char *net, size_t netlen); void glirc_mark_seen(struct glirc *G, struct glirc_string network, struct glirc_string channel); void glirc_clear_window(struct glirc *G, struct glirc_string network, struct glirc_string channel); int glirc_identifier_cmp(struct glirc_string s, struct glirc_string t);+int glirc_is_channel(struct glirc *G, const char *net, size_t netlen,+                                      const char *tgt, size_t tgtlen);+int glirc_is_logged_on(struct glirc *G, const char *net, size_t netlen,+                                        const char *tgt, size_t tgtlen);  void glirc_free_string(char *); void glirc_free_strings(char **);
src/Client/Authentication/Ecdsa.hs view
@@ -20,7 +20,6 @@   , computeResponse   ) where -import           Client.Configuration (resolveConfigurationPath) import           Control.Exception (displayException, try) import           Data.ByteString.Base64 as Enc import           Data.Text (Text)@@ -52,11 +51,10 @@   Text                    {- ^ challenge string                 -} ->   IO (Either String Text) {- ^ error message or response string -} computeResponse privateKeyFile challenge =-  do path <- resolveConfigurationPath privateKeyFile-     res  <- try $ readProcess-                     "ecdsatool"-                     ["sign", path, Text.unpack challenge]-                     "" -- stdin+  do res <- try $ readProcess+                    "ecdsatool"+                    ["sign", privateKeyFile, Text.unpack challenge]+                    "" -- stdin      return $! case words <$> res of                  Right [resp] -> Right $! Text.pack resp                  Right _      -> Left "bad sasl ecdsa response message"
src/Client/CApi.hs view
@@ -21,6 +21,7 @@   , deactivateExtension   , notifyExtensions   , commandExtension+  , chatExtension   ) where  import           Client.CApi.Types@@ -29,7 +30,6 @@ import           Control.Monad.Codensity import           Data.Text (Text) import qualified Data.Text as Text-import qualified Data.Text.Foreign as Text import           Foreign.C import           Foreign.Marshal import           Foreign.Ptr@@ -100,16 +100,19 @@ -- | Call all of the process message callbacks in the list of extensions. -- This operation marshals the IRC message once and shares that across -- all of the callbacks.+--+-- Returns 'True' to pass message to client.  Returns 'False to drop message. notifyExtensions ::   Ptr ()            {- ^ clientstate stable pointer -} ->-  Text              {- ^ network              -} ->-  RawIrcMsg         {- ^ current message      -} ->-  [ActiveExtension] ->-  IO Bool {- ^ Return 'True' to pass message -}+  Text              {- ^ network                    -} ->+  RawIrcMsg         {- ^ current message            -} ->+  [ActiveExtension] {- ^ all active extensions      -} ->+  IO Bool           {- ^ should pass message        -} notifyExtensions stab network msg aes   | null aes' = return True   | otherwise = doNotifications   where+    -- only the extensions that have a incoming message callback     aes' = [ (f,s) | ae <- aes                   , let f = fgnMessage (aeFgn ae)                         s = aeSession ae@@ -127,6 +130,40 @@             then go rest msgPtr             else return False +-- | Call all of the process chat callbacks in the list of extensions.+-- This operation marshals the IRC message once and shares that across+-- all of the callbacks.+--+-- Returns 'True' to pass message to client.  Returns 'False to drop message.+chatExtension ::+  Ptr ()            {- ^ clientstate stable pointer -} ->+  Text              {- ^ network                    -} ->+  Text              {- ^ target (channel or user)   -} ->+  Text              {- ^ message body               -} ->+  [ActiveExtension] {- ^ all active extensions      -} ->+  IO Bool           {- ^ should pass message        -}+chatExtension stab net tgt msg aes+  | null aes' = return True+  | otherwise = doNotifications+  where+    -- only the extensions that have a chat callback+    aes' = [ (f, aeSession ae)+             | ae <- aes+             , let f = fgnChat (aeFgn ae)+             , f /= nullFunPtr ]++    doNotifications = evalNestedIO $+      do chat <- withChat net tgt msg+         liftIO (go aes' chat)++    -- run handlers until one of them drops the message+    go [] _ = return True+    go ((f,s):rest) ptr =+       do res <- runProcessChat f stab s ptr+          if res == passMessage+            then go rest ptr+            else return False+ -- | Notify an extension of a client command with the given parameters. commandExtension ::   Ptr ()          {- ^ client state stableptr -} ->@@ -160,6 +197,17 @@      nest1 $ with $ FgnMsg net pfxN pfxU pfxH cmd prmPtr (fromIntegral prmN)                                        keysPtr valsPtr (fromIntegral tagN) +withChat ::+  Text {- ^ network -} ->+  Text {- ^ target  -} ->+  Text {- ^ message -} ->+  NestedIO (Ptr FgnChat)+withChat net tgt msg =+  do net' <- withText net+     tgt' <- withText tgt+     msg' <- withText msg+     nest1 $ with $ FgnChat net' tgt' msg'+ withCommand ::   [Text] {- ^ parameters -} ->   NestedIO (Ptr FgnCmd)@@ -176,7 +224,7 @@  withText :: Text -> NestedIO FgnStringLen withText txt =-  do (ptr,len) <- nest1 $ Text.withCStringLen txt+  do (ptr,len) <- nest1 $ withText0 txt      return $ FgnStringLen ptr $ fromIntegral len  ------------------------------------------------------------------------
src/Client/CApi/Exports.hs view
@@ -1,4 +1,4 @@-{-# Language CPP, RecordWildCards #-}+{-# Language RecordWildCards #-} {-| Module      : Client.CApi.Exports Description : Foreign exports which expose functionality for extensions@@ -12,16 +12,49 @@ module Client.CApi.Exports  ( -- * Extension API types    Glirc_send_message+ , glirc_send_message+  , Glirc_print+ , glirc_print+  , Glirc_list_networks+ , glirc_list_networks+  , Glirc_list_channels+ , glirc_list_channels+  , Glirc_list_channel_users+ , glirc_list_channel_users+  , Glirc_my_nick+ , glirc_my_nick+  , Glirc_identifier_cmp+ , glirc_identifier_cmp++ , Glirc_is_channel+ , glirc_is_channel++ , Glirc_is_logged_on+ , glirc_is_logged_on+  , Glirc_mark_seen+ , glirc_mark_seen+  , Glirc_clear_window+ , glirc_clear_window++ , Glirc_current_focus+ , glirc_current_focus+  , Glirc_free_string+ , glirc_free_string+  , Glirc_free_strings+ , glirc_free_strings++ , Glirc_inject_chat+ , glirc_inject_chat  ) where  import           Client.CApi.Types@@ -49,6 +82,7 @@ import           Irc.Identifier import           Irc.RawIrcMsg import           Irc.UserInfo+import           Irc.Message import           LensUtils  ------------------------------------------------------------------------@@ -98,10 +132,6 @@   Ptr FgnMsg {- ^ pointer to message -} ->   IO CInt    {- ^ 0 on success       -} -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall "glirc_send_message" glirc_send_message :: Glirc_send_message-#endif- glirc_send_message :: Glirc_send_message glirc_send_message token msgPtr =   do mvar    <- derefToken token@@ -125,10 +155,6 @@   CSize   {- ^ message length    -} ->   IO CInt {- ^ 0 on success      -} -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_print :: Glirc_print-#endif- glirc_print :: Glirc_print glirc_print stab code msgPtr msgLen =   do mvar <- derefToken stab@@ -149,16 +175,46 @@  ------------------------------------------------------------------------ +-- | Print a message or error to the client window+type Glirc_inject_chat =+  Ptr ()  {- ^ api token         -} ->+  CString {- ^ network           -} ->+  CSize   {- ^ network length    -} ->+  CString {- ^ source            -} ->+  CSize   {- ^ source length     -} ->+  CString {- ^ target            -} ->+  CSize   {- ^ target length     -} ->+  CString {- ^ message           -} ->+  CSize   {- ^ message length    -} ->+  IO CInt {- ^ 0 on success      -}++glirc_inject_chat :: Glirc_inject_chat+glirc_inject_chat stab netPtr netLen srcPtr srcLen tgtPtr tgtLen msgPtr msgLen =+  do mvar <- derefToken stab+     net  <- peekFgnStringLen (FgnStringLen netPtr netLen)+     src  <- peekFgnStringLen (FgnStringLen srcPtr srcLen)+     tgt  <- mkId <$> peekFgnStringLen (FgnStringLen tgtPtr tgtLen)+     txt  <- peekFgnStringLen (FgnStringLen msgPtr msgLen)+     now  <- getZonedTime++     let msg = ClientMessage+                 { _msgBody    = IrcBody (Privmsg (parseUserInfo src) tgt txt)+                 , _msgTime    = now+                 , _msgNetwork = net+                 }+     modifyMVar_ mvar $ \st ->+       do return (recordChannelMessage net tgt msg st)+     return 0+  `catch` \SomeException{} -> return 1++------------------------------------------------------------------------+ -- | The resulting strings and array of strings are malloc'd and the -- caller must free them. NULL returned on failure. type Glirc_list_networks =   Ptr ()           {- ^ api token                                        -} ->   IO (Ptr CString) {- ^ null terminated array of null terminated strings -} -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_list_networks :: Glirc_list_networks-#endif- glirc_list_networks :: Glirc_list_networks glirc_list_networks stab =   do mvar <- derefToken stab@@ -180,10 +236,6 @@   CSize   {- ^ identifier 2 len -} ->   IO CInt -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_identifier_cmp :: Glirc_identifier_cmp-#endif- glirc_identifier_cmp :: Glirc_identifier_cmp glirc_identifier_cmp p1 n1 p2 n2 =   do txt1 <- peekFgnStringLen (FgnStringLen p1 n1)@@ -203,10 +255,6 @@   CSize   {- ^ network len -} ->   IO (Ptr CString) {- ^ null terminated array of null terminated strings -} -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_list_channels :: Glirc_list_channels-#endif- glirc_list_channels :: Glirc_list_channels glirc_list_channels stab networkPtr networkLen =   do mvar <- derefToken stab@@ -230,10 +278,6 @@   CSize   {- ^ channel len -} ->   IO (Ptr CString) {- ^ null terminated array of null terminated strings -} -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_list_channel_users :: Glirc_list_channel_users-#endif- glirc_list_channel_users :: Glirc_list_channel_users glirc_list_channel_users stab networkPtr networkLen channelPtr channelLen =   do mvar <- derefToken stab@@ -260,10 +304,6 @@   CSize   {- ^ network name length -} ->   IO CString -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_my_nick :: Glirc_my_nick-#endif- glirc_my_nick :: Glirc_my_nick glirc_my_nick stab networkPtr networkLen =   do mvar <- derefToken stab@@ -287,10 +327,6 @@   CSize   {- ^ channel name length -} ->   IO () -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_mark_seen :: Glirc_mark_seen-#endif- glirc_mark_seen :: Glirc_mark_seen glirc_mark_seen stab networkPtr networkLen channelPtr channelLen =   do network <- peekFgnStringLen (FgnStringLen networkPtr networkLen)@@ -318,10 +354,6 @@   CSize   {- ^ channel name length -} ->   IO () -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_clear_window :: Glirc_clear_window-#endif- glirc_clear_window :: Glirc_clear_window glirc_clear_window stab networkPtr networkLen channelPtr channelLen =   do network <- peekFgnStringLen (FgnStringLen networkPtr networkLen)@@ -344,10 +376,6 @@   CString {- ^ glirc allocated string -} ->   IO () -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_free_string :: Glirc_free_string-#endif- glirc_free_string :: Glirc_free_string glirc_free_string = free @@ -359,12 +387,91 @@   Ptr CString {- ^ glirc allocated strings, null-terminated -} ->   IO () -#ifdef EXPORT_GLIRC_CAPI-foreign export ccall glirc_free_strings :: Glirc_free_strings-#endif- glirc_free_strings :: Glirc_free_strings glirc_free_strings p =   unless (p == nullPtr) $     do traverse_ free =<< peekArray0 nullPtr p        free p++------------------------------------------------------------------------++-- | Free an array of heap allocated strings found as a return value+-- from the extension API. If argument is NULL, nothing happens.+--+-- Free the allocated strings with @glirc_free_string@+--+-- Strings set to NULL if there is no current network or no+-- current target.+type Glirc_current_focus =+  Ptr ()      {- ^ api token                      -} ->+  Ptr CString {- ^ newly allocated network string -} ->+  Ptr CSize   {- ^ network length                 -} ->+  Ptr CString {- ^ newly allocated target string  -} ->+  Ptr CSize   {- ^ target length                  -} ->+  IO ()++glirc_current_focus :: Glirc_current_focus+glirc_current_focus stab netP netL tgtP tgtL =+  do mvar <- derefToken stab+     st   <- readMVar mvar+     case view clientFocus st of+       Unfocused        -> do poke' netP nullPtr+                              poke' netL 0+                              poke' tgtP nullPtr+                              poke' tgtL 0++       NetworkFocus n   -> do exportText netP netL n+                              poke' tgtP nullPtr+                              poke' tgtL 0++       ChannelFocus n t -> do exportText netP netL n+                              exportText tgtP tgtL (idText t)++------------------------------------------------------------------------++-- | Returns @1@ when the given target on the given network is a channel+-- name, otherwise returns @0@.+--+-- If the given network is not currently active this returns @0@.+type Glirc_is_channel =+  Ptr ()  {- ^ api token       -} ->+  CString {- ^ network name    -} ->+  CSize   {- ^ network length  -} ->+  CString {- ^ target name     -} ->+  CSize   {- ^ target length   -} ->+  IO CInt {- ^ boolean         -}++glirc_is_channel :: Glirc_is_channel+glirc_is_channel stab net netL tgt tgtL =+  do mvar    <- derefToken stab+     st      <- readMVar mvar+     network <- peekFgnStringLen (FgnStringLen net netL)+     target  <- peekFgnStringLen (FgnStringLen tgt tgtL)++     case preview (clientConnection network) st of+       Just cs | isChannelIdentifier cs (mkId target) -> return 1+       _ -> return 0++------------------------------------------------------------------------++-- | Returns @1@ when the given target on the given network shares a+-- channel with the user, @0@ otherwise.+--+-- If the given network is not currently active this returns @0@.+type Glirc_is_logged_on =+  Ptr ()  {- ^ api token       -} ->+  CString {- ^ network name    -} ->+  CSize   {- ^ network length  -} ->+  CString {- ^ target name     -} ->+  CSize   {- ^ target length   -} ->+  IO CInt {- ^ boolean         -}++glirc_is_logged_on :: Glirc_is_channel+glirc_is_logged_on stab net netL tgt tgtL =+  do mvar    <- derefToken stab+     st      <- readMVar mvar+     network <- peekFgnStringLen (FgnStringLen net netL)+     target  <- peekFgnStringLen (FgnStringLen tgt tgtL)++     let online = has (clientConnection network . csUsers . ix (mkId target)) st+     return $! if online then 1 else 0
src/Client/CApi/Types.hsc view
@@ -1,4 +1,4 @@-{-# Language ForeignFunctionInterface, RecordWildCards #-}+{-# Language RecordWildCards #-}  {-| Module      : Client.CApi.Types@@ -30,23 +30,36 @@   -- * Commands   , FgnCmd(..) +  -- * Chat+  , FgnChat(..)+   -- * Function pointer calling   , Dynamic   , runStartExtension   , runStopExtension   , runProcessMessage   , runProcessCommand+  , runProcessChat    -- * report message codes   , MessageCode(..), normalMessage, errorMessage    -- * process message results   , MessageResult(..), passMessage, dropMessage++  -- * Marshaling helpers+  , withText0+  , exportText+  , poke'   ) where -import Foreign.C-import Foreign.Ptr-import Foreign.Storable+import           Control.Monad+import           Data.Text (Text)+import qualified Data.Text.Foreign as Text+import           Foreign.C+import           Foreign.Marshal.Array+import           Foreign.Ptr+import           Foreign.Storable  -- | Tag for describing the kind of message to display in the client -- as used in `glirc_print`.@@ -90,6 +103,13 @@   Ptr FgnCmd {- ^ command         -} ->   IO () +-- | @typedef void process_chat(void *glirc, void *S, const struct glirc_chat *);@+type ProcessChat =+  Ptr ()      {- ^ api token       -} ->+  Ptr ()      {- ^ extension state -} ->+  Ptr FgnChat {- ^ chat info       -} ->+  IO MessageResult+ -- | Type of dynamic function pointer wrappers. type Dynamic a = FunPtr a -> a @@ -97,6 +117,7 @@ foreign import ccall "dynamic" runStopExtension  :: Dynamic StopExtension foreign import ccall "dynamic" runProcessMessage :: Dynamic ProcessMessage foreign import ccall "dynamic" runProcessCommand :: Dynamic ProcessCommand+foreign import ccall "dynamic" runProcessChat    :: Dynamic ProcessChat  ------------------------------------------------------------------------ @@ -105,6 +126,7 @@   { fgnStart   :: FunPtr StartExtension -- ^ Optional startup callback   , fgnStop    :: FunPtr StopExtension  -- ^ Optional shutdown callback   , fgnMessage :: FunPtr ProcessMessage -- ^ Optional message received callback+  , fgnChat    :: FunPtr ProcessChat    -- ^ Optional message send callback   , fgnCommand :: FunPtr ProcessCommand -- ^ Optional client command callback   , fgnName    :: CString               -- ^ Null-terminated name   , fgnMajorVersion, fgnMinorVersion :: CInt -- ^ extension version@@ -117,6 +139,7 @@             <$> (#peek struct glirc_extension, start          ) p             <*> (#peek struct glirc_extension, stop           ) p             <*> (#peek struct glirc_extension, process_message) p+            <*> (#peek struct glirc_extension, process_chat   ) p             <*> (#peek struct glirc_extension, process_command) p             <*> (#peek struct glirc_extension, name           ) p             <*> (#peek struct glirc_extension, major_version  ) p@@ -125,6 +148,7 @@              do (#poke struct glirc_extension, start          ) p fgnStart                 (#poke struct glirc_extension, stop           ) p fgnStop                 (#poke struct glirc_extension, process_message) p fgnMessage+                (#poke struct glirc_extension, process_chat   ) p fgnChat                 (#poke struct glirc_extension, process_command) p fgnCommand                 (#poke struct glirc_extension, name           ) p fgnName                 (#poke struct glirc_extension, major_version  ) p fgnMajorVersion@@ -175,6 +199,28 @@  ------------------------------------------------------------------------ +-- | @struct glirc_message@+data FgnChat = FgnChat+  { fhNetwork    :: FgnStringLen+  , fhTarget     :: FgnStringLen+  , fhMessage    :: FgnStringLen+  }++instance Storable FgnChat where+  alignment _ = #alignment struct glirc_chat+  sizeOf    _ = #size      struct glirc_chat+  peek p      = FgnChat+            <$> (#peek struct glirc_chat, network) p+            <*> (#peek struct glirc_chat, target ) p+            <*> (#peek struct glirc_chat, message) p++  poke p FgnChat{..} =+             do (#poke struct glirc_chat, network) p fhNetwork+                (#poke struct glirc_chat, target ) p fhTarget+                (#poke struct glirc_chat, message) p fhMessage++------------------------------------------------------------------------+ -- | @struct glirc_command@ data FgnCmd = FgnCmd   { fcParams  :: Ptr FgnStringLen -- ^ array@@ -206,3 +252,30 @@   poke p (FgnStringLen x y) =              do (#poke struct glirc_string, str) p x                 (#poke struct glirc_string, len) p y++------------------------------------------------------------------------++-- | Like 'poke' except it doesn't write to NULL+poke' :: Storable a => Ptr a -> a -> IO ()+poke' ptr x = unless (nullPtr == ptr) (poke ptr x)++-- | Marshal a text as a malloced null-terminated CStringLen+exportText :: Ptr CString -> Ptr CSize -> Text -> IO ()+exportText dstP dstL txt =++  Text.withCStringLen txt $ \(srcP, srcL) ->+    do poke' dstL (fromIntegral srcL)+       unless (dstP == nullPtr) $+         do a <- mallocArray0 srcL+            copyArray a srcP srcL+            pokeElemOff a srcL 0+            poke dstP a++-- | Marshal a text as a temporary null-terminated CStringLen+withText0 :: Text -> (CStringLen -> IO a) -> IO a+withText0 txt k =+  Text.withCStringLen txt $ \(p,l) ->+  allocaArray0 l $ \p' ->+    do copyArray p' p l+       pokeElemOff p' l 0+       k (p', l)
src/Client/Commands.hs view
@@ -26,7 +26,8 @@   ) where  import           Client.CApi-import           Client.Commands.Arguments+import           Client.Commands.Arguments.Spec+import           Client.Commands.Arguments.Parser import           Client.Commands.Exec import           Client.Commands.Interpolation import           Client.Commands.Recognizer@@ -50,9 +51,10 @@ import           Data.List.Split import qualified Data.HashMap.Strict as HashMap import qualified Data.Map as Map+import           Data.Maybe (fromMaybe)+import           Data.Monoid ((<>)) import           Data.Text (Text) import qualified Data.Text as Text-import           Data.Monoid ((<>)) import           Data.Time import           Irc.Commands import           Irc.Identifier@@ -63,7 +65,6 @@ import           LensUtils import           RtsStats (getStats) import           System.Process-import           Text.Read import           Text.Regex.TDFA import           Text.Regex.TDFA.String (compile) @@ -108,7 +109,7 @@   { -- | Names of this command, first in the list is the "primary" name     cmdNames          :: NonEmpty Text   -- | Specification of the arguments of the command-  , cmdArgumentSpec   :: ArgumentSpec a+  , cmdArgumentSpec   :: Args ClientState a   -- | Multi-line IRC-formatted documentation text used for @/help@   , cmdDocumentation  :: Text   -- | Implementation of the command for both execution and tab completion@@ -150,23 +151,27 @@ -- | Execute command provided by user, resolve aliases if necessary. executeUserCommand :: Maybe Text -> String -> ClientState -> IO CommandResult executeUserCommand discoTime command st = do-  let key = Text.takeWhile (/=' ') tcmd-      rest = dropWhile (==' ') . dropWhile (/=' ') $ command+  let key = Text.takeWhile (/=' ') (Text.pack command)+      rest = dropWhile (==' ') (dropWhile (/=' ') command)    case views (clientConfig . configMacros) (recognize key) st of     Exact (Macro (MacroSpec spec) cmdExs) ->-      case parseArguments spec rest *> traverse resolveMacro cmdExs of+      case doExpansion spec cmdExs rest of         Nothing   -> commandFailureMsg "macro expansions failed" st         Just cmds -> process cmds st     _ -> executeCommand Nothing command st   where-    resolveMacro = resolveMacroExpansions (commandExpansion discoTime st) expandInt+    doExpansion spec cmdExs rest =+      do args <- parse st spec rest+         traverse (resolveMacro (map Text.pack args)) cmdExs -    tcmd = Text.pack command-    args = Text.words tcmd+    resolveMacro args = resolveMacroExpansions (commandExpansion discoTime st) (expandInt args) -    expandInt i = preview (ix (fromInteger i)) args+    expandInt :: [a] -> Integer -> Maybe a+    expandInt args i = preview (ix (fromInteger i)) args ++     process [] st0 = commandSuccess st0     process (c:cs) st0 =       do res <- executeCommand Nothing (Text.unpack c) st0@@ -209,15 +214,20 @@       | Just !cs <- preview (clientConnection network) st ->           do now <- getZonedTime              let msgTxt = Text.pack $ takeWhile (/='\n') msg-                 ircMsg = ircPrivmsg (idText channel) msgTxt-                 myNick = UserInfo (view csNick cs) "" ""+                 tgtTxt = idText channel++             (st1,allow) <- clientPark st $ \ptr ->+                do let aes = view (clientExtensions . esActive) st+                   chatExtension ptr network tgtTxt msgTxt aes++             when allow (sendMsg cs (ircPrivmsg tgtTxt msgTxt))++             let myNick = UserInfo (view csNick cs) "" ""                  entry = ClientMessage-                            { _msgTime    = now-                            , _msgNetwork = network-                            , _msgBody    = IrcBody (Privmsg myNick channel msgTxt)-                            }-             sendMsg cs ircMsg-             commandSuccess $! recordChannelMessage network channel entry st+                   { _msgTime    = now+                   , _msgNetwork = network+                   , _msgBody    = IrcBody (Privmsg myNick channel msgTxt) }+             commandSuccess $! recordChannelMessage network channel entry st1      _ -> commandFailureMsg "cannot send chat messages to this window" st @@ -238,7 +248,7 @@         case tabCompleteReversed of           Just isReversed -> tab isReversed st rest           Nothing ->-            case parseArguments spec rest of+            case parse st spec rest of               Nothing -> commandFailureMsg "bad command arguments" st               Just arg -> exec st arg   in@@ -295,13 +305,13 @@    [ Command       (pure "exit")-      NoArg+      (pure ())       "Exit the client immediately.\n"     $ ClientCommand cmdExit noClientTab    , Command       (pure "reload")-      (OptTokenArg "filename" NoArg)+      (optionalArg (simpleToken "filename"))       "Reload the client configuration file.\n\       \\n\       \If \^Bfilename\^B is provided it will be used to reload.\n\@@ -310,7 +320,7 @@    , Command       (pure "extension")-      (ReqTokenArg "extension" (RemainingArg "arguments"))+      (liftA2 (,) (simpleToken "extension") (remainingArg "arguments"))       "Calls the process_command callback of the given extension.\n\       \\n\       \\^Bextension\^B should be the name of the loaded extension.\n"@@ -318,21 +328,28 @@    , Command       (pure "palette")-      NoArg+      (pure ())       "Show the current palette settings and a color chart to help pick new colors.\n"     $ ClientCommand cmdPalette noClientTab    , Command       (pure "digraphs")-      NoArg-      "Show the table of digraphs.\n\+      (pure ())+      "\^BDescription:\^B\n\       \\n\-      \To enter a digraph type the two-character entry and press M-k.\n"+      \    Show the table of digraphs. A digraph is a pair of characters\n\+      \    can be used together to represent an uncommon character. Type\n\+      \    the two-character digraph corresponding to the desired output\n\+      \    character and then press M-k (default binding).\n\+      \\n\+      \    Note that the digraphs list is searchable with /grep.\n\+      \\n\+      \\^BSee also:\^B grep\n"     $ ClientCommand cmdDigraphs noClientTab    , Command       (pure "keymap")-      NoArg+      (pure ())       "Show the key binding map.\n\       \\n\       \Key bindings can be changed in configuration file. See `glirc2 --config-format`.\n"@@ -340,13 +357,13 @@    , Command       (pure "rtsstats")-      NoArg+      (pure ())       "Show the GHC RTS statistics.\n"     $ ClientCommand cmdRtsStats noClientTab    , Command       (pure "exec")-      (RemainingArg "arguments")+      (remainingArg "arguments")       "Execute a command synchnonously sending the to a configuration destination.\n\       \\n\       \\^Barguments\^B: [-n network] [-c channel] [-i input] command [command arguments...]\n\@@ -366,7 +383,7 @@    , Command       (pure "url")-      (OptTokenArg "number" NoArg)+      (optionalArg numberArg)       "Open a URL seen in chat.\n\       \\n\       \The URL is opened using the executable configured under \^Burl-opener\^B.\n\@@ -378,7 +395,7 @@    , Command       (pure "help")-      (OptTokenArg "command" NoArg)+      (optionalArg (simpleToken "command"))       "Show command documentation.\n\       \\n\       \When \^Bcommand\^B is omitted a list of all commands is displayed.\n\@@ -391,25 +408,25 @@    [ Command       (pure "toggle-detail")-      NoArg+      (pure ())       "Toggle detailed message view.\n"     $ ClientCommand cmdToggleDetail noClientTab    , Command       (pure "toggle-activity-bar")-      NoArg+      (pure ())       "Toggle detailed detailed activity information in status bar.\n"     $ ClientCommand cmdToggleActivityBar noClientTab    , Command       (pure "toggle-metadata")-      NoArg+      (pure ())       "Toggle visibility of metadata in chat windows.\n"     $ ClientCommand cmdToggleMetadata noClientTab    , Command       (pure "toggle-layout")-      NoArg+      (pure ())       "Toggle multi-window layout mode.\n"     $ ClientCommand cmdToggleLayout noClientTab @@ -419,7 +436,7 @@    [ Command       (pure "connect")-      (ReqTokenArg "network" NoArg)+      (simpleToken "network")       "Connect to \^Bnetwork\^B by name.\n\       \\n\       \If no name is configured the hostname is the 'name'.\n"@@ -427,13 +444,13 @@    , Command       (pure "reconnect")-      NoArg+      (pure ())       "Reconnect to the current network.\n"     $ ClientCommand cmdReconnect noClientTab    , Command       (pure "disconnect")-      NoArg+      (pure ())       "Immediately terminate the current network connection.\n\       \\n\       \See also: /quit /exit\n"@@ -441,7 +458,7 @@    , Command       (pure "quit")-      (RemainingArg "reason")+      (remainingArg "reason")       "Gracefully disconnect the current network connection.\n\       \\n\       \\^Breason\^B: optional quit reason\n\@@ -455,7 +472,7 @@    [ Command       (pure "focus")-      (ReqTokenArg "network" (OptTokenArg "target" NoArg))+      (liftA2 (,) (simpleToken "network") (optionalArg (simpleToken "target")))       "Change the focused window.\n\       \\n\       \When only \^Bnetwork\^B is specified this switches to the network status window.\n\@@ -467,18 +484,37 @@    , Command       ("query" :| ["c", "channel"])-      (ReqTokenArg "target" NoArg)-      "Change the focused window.\n\+      (simpleToken "focus")+      "\^BParameters:\^B\n\       \\n\-      \Changes the focus to the \^Btarget\^B chat window on the current network.\n\+      \    focuses: Focus name\n\       \\n\-      \Nicknames and channels can be specified in the \^Btarget\^B parameter.\n\-      \See also: /focus to switch to a target on a different network.\n"-    $ NetworkCommand cmdQuery tabQuery+      \\^BDescription:\^B\n\+      \\n\+      \    This command sets the current window focus. When\n\+      \    no network is specified, the current network will\n\+      \    be used.\n\+      \\n\+      \    Client:  *\n\+      \    Network: \^_network\^_:\n\+      \    Channel: \^_#channel\^_\n\+      \    Channel: \^_network\^_:\^_#channel\^_\n\+      \    User:    \^_nick\^_\n\+      \    User:    \^_network\^_:\^_nick\^_\n\+      \\n\+      \\^BExamples:\^B\n\+      \\n\+      \    /c fn:#haskell\n\+      \    /c #haskell\n\+      \    /c fn:\n\+      \    /c *:\n\+      \\n\+      \\^BSee also:\^B focus\n"+    $ ClientCommand cmdQuery tabQuery    , Command       (pure "clear")-      (OptTokenArg "network" (OptTokenArg "channel" NoArg))+      (optionalArg (liftA2 (,) (simpleToken "network") (optionalArg (simpleToken "channel"))))       "Clear a window.\n\       \\n\       \If no arguments are provided the current window is cleared.\n\@@ -491,7 +527,7 @@    , Command       (pure "windows")-      (OptTokenArg "kind" NoArg)+      (optionalArg (simpleToken "kind"))       "Show a list of all windows with an optional argument to limit the kinds of windows listed.\n\       \\n\       \\^Bkind\^O: one of \^Bnetworks\^O, \^Bchannels\^O, \^Busers\^O\n\@@ -500,7 +536,7 @@    , Command       (pure "splits")-      (RemainingArg "focuses")+      (remainingArg "focuses")       "\^BParameters:\^B\n\       \\n\       \    focuses: List of focus names\n\@@ -531,7 +567,7 @@    , Command       (pure "splits+")-      (RemainingArg "focuses")+      (remainingArg "focuses")       "Add windows to the splits list. Omit the list of focuses to add the\       \ current window.\n\       \\n\@@ -547,7 +583,7 @@    , Command       (pure "splits-")-      (RemainingArg "focuses")+      (remainingArg "focuses")       "Remove windows from the splits list. Omit the list of focuses to\       \ remove the current window.\n\       \\n\@@ -563,13 +599,13 @@    , Command       (pure "ignore")-      (RemainingArg "nicks")+      (remainingArg "nicks")       "Toggle the soft-ignore on each of the space-delimited given nicknames.\n"     $ ClientCommand cmdIgnore simpleClientTab    , Command       (pure "grep")-      (RemainingArg "regular-expression")+      (remainingArg "regular-expression")       "Set the persistent regular expression.\n\       \\n\       \Clear the regular expression by calling this without an argument.\n\@@ -580,7 +616,7 @@    , Command       (pure "grepi")-      (RemainingArg "regular-expression")+      (remainingArg "regular-expression")       "Set the persistent regular expression.\n\       \\n\       \Clear the regular expression by calling this without an argument.\n\@@ -591,7 +627,7 @@    , Command       (pure "mentions")-      NoArg+      (pure ())       "Show a list of all message that were highlighted as important.\n"     $ ClientCommand cmdMentions noClientTab @@ -601,7 +637,7 @@    [ Command       ("join" :| ["j"])-      (ReqTokenArg "channels" (OptTokenArg "keys" NoArg))+      (liftA2 (,) (simpleToken "channels") (optionalArg (simpleToken "keys")))       "\^BParameters:\^B\n\       \\n\       \    channels: Comma-separated list of channels\n\@@ -623,7 +659,7 @@    , Command       (pure "part")-      (RemainingArg "reason")+      (remainingArg "reason")       "\^BParameters:\^B\n\       \\n\       \    reason: Optional message sent to channel as part reason\@@ -642,7 +678,7 @@    , Command       (pure "msg")-      (ReqTokenArg "target" (RemainingArg "message"))+      (liftA2 (,) (simpleToken "target") (remainingArg "message"))       "\^BParameters:\^B\n\       \\n\       \    target:  Comma-separated list of nicknames and channels\n\@@ -667,7 +703,7 @@    , Command       (pure "me")-      (RemainingArg "message")+      (remainingArg "message")       "\^BParameters:\^B\n\       \\n\       \    message: Body of action message\n\@@ -687,7 +723,7 @@    , Command       (pure "say")-      (RemainingArg "message")+      (remainingArg "message")       "\^BParameters:\^B\n\       \\n\       \    message: Body of message\n\@@ -707,7 +743,7 @@    , Command       (pure "notice")-      (ReqTokenArg "target" (RemainingArg "message"))+      (liftA2 (,) (simpleToken "target") (remainingArg "message"))       "\^BParameters:\^B\n\       \\n\       \    target:  Comma-separated list of nicknames and channels\n\@@ -734,7 +770,7 @@    , Command       (pure "ctcp")-      (ReqTokenArg "target" (ReqTokenArg "command" (RemainingArg "arguments")))+      (liftA3 (,,) (simpleToken "target") (simpleToken "command") (remainingArg "arguments"))       "\^BParameters:\^B\n\       \\n\       \    target:    Comma-separated list of nicknames and channels\n\@@ -757,7 +793,7 @@    , Command       (pure "nick")-      (ReqTokenArg "nick" NoArg)+      (simpleToken "nick")       "\^BParameters:\^B\n\       \\n\       \    nick: New nickname\n\@@ -774,7 +810,7 @@    , Command       (pure "away")-      (RemainingArg "message")+      (remainingArg "message")       "\^BParameters:\^B\n\       \\n\       \    message: Optional away message\n\@@ -795,7 +831,7 @@    , Command       ("users" :| ["names"])-      NoArg+      (pure ())       "\^BDescription:\^B\n\       \\n\       \    Show the user list for the current channel.\n\@@ -808,7 +844,7 @@    , Command       (pure "channelinfo")-      NoArg+      (pure ())       "\^BDescription:\^B\n\       \\n\       \    Show information about the current channel.\n\@@ -819,9 +855,9 @@    , Command       (pure "quote")-      (RemainingArg "raw IRC command")+      (remainingArg "raw IRC command")       "Send a raw IRC command.\n"-    $ NetworkCommand cmdQuote  simpleNetworkTab+    $ NetworkCommand cmdQuote simpleNetworkTab    ------------------------------------------------------------------------   ] , CommandSection "IRC queries"@@ -829,79 +865,79 @@    [ Command       (pure "who")-      (RemainingArg "arguments")+      (remainingArg "arguments")       "Send WHO query to server with given arguments.\n"     $ NetworkCommand cmdWho simpleNetworkTab    , Command       (pure "whois")-      (RemainingArg "arguments")+      (remainingArg "arguments")       "Send WHOIS query to server with given arguments.\n"     $ NetworkCommand cmdWhois simpleNetworkTab    , Command       (pure "whowas")-      (RemainingArg "arguments")+      (remainingArg "arguments")       "Send WHOWAS query to server with given arguments.\n"     $ NetworkCommand cmdWhowas simpleNetworkTab    , Command       (pure "ison")-      (RemainingArg "arguments")+      (remainingArg "arguments")       "Send ISON query to server with given arguments.\n"     $ NetworkCommand cmdIson   simpleNetworkTab    , Command       (pure "userhost")-      (RemainingArg "arguments")+      (remainingArg "arguments")       "Send USERHOST query to server with given arguments.\n"     $ NetworkCommand cmdUserhost simpleNetworkTab    , Command       (pure "time")-      (OptTokenArg "servername" NoArg)+      (optionalArg (simpleToken "servername"))       "Send TIME query to server with given arguments.\n"     $ NetworkCommand cmdTime simpleNetworkTab    , Command       (pure "stats")-      (RemainingArg "arguments")+      (remainingArg "arguments")       "Send STATS query to server with given arguments.\n"     $ NetworkCommand cmdStats simpleNetworkTab    , Command       (pure "lusers")-      (OptTokenArg "mask" (OptTokenArg "servername" NoArg))+      (optionalArg (liftA2 (,) (simpleToken "mask") (optionalArg (simpleToken "servername"))))       "Send LUSERS query to server with given arguments.\n"     $ NetworkCommand cmdLusers simpleNetworkTab    , Command-      (pure "motd") (OptTokenArg "servername" NoArg)+      (pure "motd") (optionalArg (simpleToken "servername"))       "Send MOTD query to server.\n"     $ NetworkCommand cmdMotd simpleNetworkTab    , Command-      (pure "admin") (OptTokenArg "servername" NoArg)+      (pure "admin") (optionalArg (simpleToken "servername"))       "Send ADMIN query to server.\n"     $ NetworkCommand cmdAdmin simpleNetworkTab    , Command-      (pure "rules") (OptTokenArg "servername" NoArg)+      (pure "rules") (optionalArg (simpleToken "servername"))       "Send RULES query to server.\n"     $ NetworkCommand cmdRules simpleNetworkTab    , Command-      (pure "info") NoArg+      (pure "info") (pure ())       "Send INFO query to server.\n"     $ NetworkCommand cmdInfo noNetworkTab    , Command-      (pure "list") (RemainingArg "arguments")+      (pure "list") (remainingArg "arguments")       "Send LIST query to server.\n"     $ NetworkCommand cmdList simpleNetworkTab    , Command-      (pure "version") (OptTokenArg "servername" NoArg)+      (pure "version") (optionalArg (simpleToken "servername"))       "Send VERSION query to server.\n"     $ NetworkCommand cmdVersion simpleNetworkTab @@ -911,7 +947,7 @@    [ Command       (pure "mode")-      (RemainingArg "modes and parameters")+      (fromMaybe [] <$> optionalArg (extensionArg "modes" modeParamArgs))       "Sets IRC modes.\n\       \\n\       \Examples:\n\@@ -924,11 +960,11 @@       \When executed in a channel window, mode changes are applied to the channel.\n\       \\n\       \This command has parameter sensitive tab-completion.\n"-    $ NetworkCommand cmdMode   tabMode+    $ NetworkCommand cmdMode tabMode    , Command       (pure "masks")-      (ReqTokenArg "mode" NoArg)+      (simpleToken "mode")       "Show mask lists for current channel.\n\       \\n\       \Common \^Bmode\^B values:\n\@@ -942,13 +978,13 @@    , Command       (pure "invite")-      (ReqTokenArg "nick" NoArg)+      (simpleToken "nick")       "Invite a user to the current channel.\n"     $ ChannelCommand cmdInvite simpleChannelTab    , Command       (pure "topic")-      (RemainingArg "message")+      (remainingArg "message")       "Set the topic on the current channel.\n\       \\n\       \Tab-completion with no \^Bmessage\^B specified will load the current topic for editing.\n"@@ -956,15 +992,15 @@    , Command       (pure "kick")-      (ReqTokenArg "nick" (RemainingArg "reason"))+      (liftA2 (,) (simpleToken "nick") (remainingArg "reason"))       "Kick a user from the current channel.\n\       \\n\       \See also: /kickban /remove\n"-    $ ChannelCommand cmdKick   simpleChannelTab+    $ ChannelCommand cmdKick simpleChannelTab    , Command       (pure "kickban")-      (ReqTokenArg "nick" (RemainingArg "reason"))+      (liftA2 (,) (simpleToken "nick") (remainingArg "reason"))       "Ban and kick a user from the current channel.\n\       \\n\       \Users are banned by hostname match.\n\@@ -973,7 +1009,7 @@    , Command       (pure "remove")-      (ReqTokenArg "nick" (RemainingArg "reason"))+      (liftA2 (,) (simpleToken "nick") (remainingArg "reason"))       "Remove a user from the current channel.\n\       \\n\       \Remove works like /kick except it results in a PART.\n\@@ -982,7 +1018,7 @@    , Command       (pure "knock")-      (ReqTokenArg "channel" (RemainingArg "message"))+      (liftA2 (,) (simpleToken "channel") (remainingArg "message"))       "Request entry to an invite-only channel.\n"     $ NetworkCommand cmdKnock simpleNetworkTab @@ -992,7 +1028,7 @@    [ Command       (pure "znc")-      (RemainingArg "arguments")+      (remainingArg "arguments")       "Send command directly to ZNC.\n\       \\n\       \The advantage of this over /msg is that responses are not broadcast to call clients.\n"@@ -1000,7 +1036,7 @@    , Command       (pure "znc-playback")-      (OptTokenArg "time" (OptTokenArg "date" NoArg))+      (optionalArg (liftA2 (,) (simpleToken "time") (optionalArg (simpleToken "date"))))       "Request playback from the ZNC 'playback' module.\n\       \\n\       \\^Btime\^B determines the time to playback since.\n\@@ -1019,25 +1055,25 @@    [ Command       (pure "oper")-      (ReqTokenArg "user" (ReqTokenArg "password" NoArg))+      (liftA2 (,) (simpleToken "user") (simpleToken "password"))       "Authenticate as a server operator.\n"     $ NetworkCommand cmdOper noNetworkTab    , Command       (pure "kill")-      (ReqTokenArg "client" (RemainingArg "reason"))+      (liftA2 (,) (simpleToken "client") (remainingArg "reason"))       "Kill a client connection to the server.\n"     $ NetworkCommand cmdKill simpleNetworkTab    , Command       (pure "map")-      NoArg+      (pure ())       "Display network map.\n"     $ NetworkCommand cmdMap simpleNetworkTab    , Command       (pure "links")-      (RemainingArg "arguments")+      (remainingArg "arguments")       "Send LINKS query to server with given arguments.\n"     $ NetworkCommand cmdLinks simpleNetworkTab @@ -1094,14 +1130,14 @@ -- joined to this command will clear the messages but -- preserve the window. When used on a window that the -- user is not joined to this command will delete the window.-cmdClear :: ClientCommand (Maybe (String, Maybe (String, ())))+cmdClear :: ClientCommand (Maybe (String, Maybe String)) cmdClear st args =   case args of     Nothing                 -> clearFocus (view clientFocus st)     Just ("*", Nothing)     -> clearFocus Unfocused     Just (network, Nothing) -> clearFocus (NetworkFocus (Text.pack network))-    Just (network, Just ("*", _)) -> clearNetworkWindows network-    Just (network, Just (channel, _)) ->+    Just (network, Just "*") -> clearNetworkWindows network+    Just (network, Just channel) ->         clearFocus (ChannelFocus (Text.pack network) (mkId (Text.pack channel)))   where     clearNetworkWindows network@@ -1140,7 +1176,7 @@ -- sends to the current network. cmdQuote :: NetworkCommand String cmdQuote cs st rest =-  case parseRawIrcMsg (Text.pack rest) of+  case parseRawIrcMsg (Text.pack (dropWhile (' '==) rest)) of     Nothing  -> commandFailureMsg "failed to parse raw IRC command" st     Just raw ->       do sendMsg cs raw@@ -1163,8 +1199,8 @@        $! recordChannelMessage network channelId entry st  -- | Implementation of @/ctcp@-cmdCtcp :: NetworkCommand (String, (String, String))-cmdCtcp cs st (target, (cmd, args)) =+cmdCtcp :: NetworkCommand (String, String, String)+cmdCtcp cs st (target, cmd, args) =   do let cmdTxt = Text.toUpper (Text.pack cmd)          argTxt = Text.pack args          tgtTxt = Text.pack target@@ -1237,15 +1273,15 @@                       entries  -cmdConnect :: ClientCommand (String, ())-cmdConnect st (networkStr, _) =+cmdConnect :: ClientCommand String+cmdConnect st networkStr =   do -- abort any existing connection before connecting      let network = Text.pack networkStr      st' <- addConnection 0 Nothing network =<< abortNetwork network st      commandSuccess        $ changeFocus (NetworkFocus network) st' -cmdFocus :: ClientCommand (String, Maybe (String, ()))+cmdFocus :: ClientCommand (String, Maybe String) cmdFocus st (network, mbChannel)   | network == "*" = commandSuccess (changeFocus Unfocused st)   | otherwise =@@ -1253,7 +1289,7 @@        Nothing ->          let focus = NetworkFocus (Text.pack network) in          commandSuccess (changeFocus focus st)-       Just (channel,_) ->+       Just channel ->          let focus = ChannelFocus (Text.pack network) (mkId (Text.pack channel)) in          commandSuccess            $ changeFocus focus st@@ -1267,14 +1303,14 @@   -- | Implementation of @/windows@ command. Set subfocus to Windows.-cmdWindows :: ClientCommand (Maybe (String, ()))+cmdWindows :: ClientCommand (Maybe String) cmdWindows st arg =   case arg of-    Nothing             -> success AllWindows-    Just ("networks",_) -> success NetworkWindows-    Just ("channels",_) -> success ChannelWindows-    Just ("users"   ,_) -> success UserWindows-    _                   -> commandFailureMsg errmsg st+    Nothing         -> success AllWindows+    Just "networks" -> success NetworkWindows+    Just "channels" -> success ChannelWindows+    Just "users"    -> success UserWindows+    _               -> commandFailureMsg errmsg st   where     errmsg = "/windows expected networks, channels, or users"     success x =@@ -1307,10 +1343,10 @@                                  $ changeSubfocus FocusRtsStats st  -- | Implementation of @/help@ command. Set subfocus to Help.-cmdHelp :: ClientCommand (Maybe (String, ()))+cmdHelp :: ClientCommand (Maybe String) cmdHelp st mb = commandSuccess (changeSubfocus focus st)   where-    focus = FocusHelp (fmap (Text.pack . fst) mb)+    focus = FocusHelp (fmap Text.pack mb)  -- | Tab completion for @/splits[+]@. When given no arguments this -- populates the current list of splits, otherwise it tab completes@@ -1364,19 +1400,26 @@     Just args -> k args   where     mb = traverse-           (parseSplitFocus (views clientFocus focusNetwork st))+           (parseFocus (views clientFocus focusNetwork st))            (words str) --- | Parses a single entry in the format used by @/splits[+-]@ to specify--- windows.-parseSplitFocus :: Maybe Text -> String -> Maybe Focus-parseSplitFocus mbNet x =+-- | Parses a single focus name given a default network.+--+-- The default is parameterized over an arbitrary 'Applicative'+-- instance so that if you know the network you can use 'Identity'+-- and if you might not, you can use 'Maybe'+parseFocus ::+  Applicative f =>+  f Text {- ^ default network    -} ->+  String {- ^ @[network:]target@ -} ->+  f Focus+parseFocus mbNet x =   case break (==':') x of-    ("*","")     -> Just Unfocused-    (net,_:"")   -> Just (NetworkFocus (Text.pack net))-    (net,_:chan) -> Just (ChannelFocus (Text.pack net) (mkId (Text.pack chan)))-    (chan,"") -> do net <- mbNet-                    Just (ChannelFocus net (mkId (Text.pack chan)))+    ("*","")     -> pure Unfocused+    (net,_:"")   -> pure (NetworkFocus (Text.pack net))+    (net,_:chan) -> pure (ChannelFocus (Text.pack net) (mkId (Text.pack chan)))+    (chan,"")    -> mbNet <&> \net ->+                    ChannelFocus net (mkId (Text.pack chan))  -- | Render a entry from splits back to the textual format. renderSplitFocus :: Focus -> Text@@ -1492,35 +1535,35 @@   do sendMsg cs (ircStats (Text.pack <$> words rest))      commandSuccess st -cmdLusers :: NetworkCommand (Maybe (String, Maybe (String, ())))+cmdLusers :: NetworkCommand (Maybe (String, Maybe String)) cmdLusers cs st arg =   do sendMsg cs $ ircLusers $ fmap Text.pack $        case arg of-         Nothing                -> []-         Just (x, Nothing)      -> [x]-         Just (x, (Just (y,_))) -> [x,y]+         Nothing           -> []+         Just (x, Nothing) -> [x]+         Just (x, Just y)  -> [x,y]      commandSuccess st -cmdMotd :: NetworkCommand (Maybe (String, ()))+cmdMotd :: NetworkCommand (Maybe String) cmdMotd cs st mbservername =   do sendMsg cs $ ircMotd $ case mbservername of-                              Just (s,_) -> Text.pack s-                              Nothing    -> ""+                              Just s  -> Text.pack s+                              Nothing -> ""      commandSuccess st -cmdAdmin :: NetworkCommand (Maybe (String, ()))+cmdAdmin :: NetworkCommand (Maybe String) cmdAdmin cs st mbservername =   do sendMsg cs $ ircAdmin $ case mbservername of-                              Just (s,_) -> Text.pack s-                              Nothing    -> ""+                              Just s  -> Text.pack s+                              Nothing -> ""      commandSuccess st -cmdRules :: NetworkCommand (Maybe (String, ()))+cmdRules :: NetworkCommand (Maybe String) cmdRules cs st mbservername =   do sendMsg cs $ ircRules $        case mbservername of-         Just (s,_) -> Text.pack s-         Nothing    -> ""+         Just s  -> Text.pack s+         Nothing -> ""      commandSuccess st  cmdMap :: NetworkCommand ()@@ -1533,11 +1576,11 @@   do sendMsg cs ircInfo      commandSuccess st -cmdVersion :: NetworkCommand (Maybe (String, ()))+cmdVersion :: NetworkCommand (Maybe String) cmdVersion cs st mbservername =   do sendMsg cs $ ircVersion $ case mbservername of-                                Just (s,_) -> Text.pack s-                                Nothing    -> ""+                                Just s  -> Text.pack s+                                Nothing -> ""      commandSuccess st  cmdList :: NetworkCommand String@@ -1560,12 +1603,12 @@   do sendMsg cs (ircLinks (Text.pack <$> words rest))      commandSuccess st -cmdTime :: NetworkCommand (Maybe (String, ()))+cmdTime :: NetworkCommand (Maybe String) cmdTime cs st arg =   do sendMsg cs $ ircTime $        case arg of-         Nothing    -> ""-         Just (x,_) -> Text.pack x+         Nothing -> ""+         Just x  -> Text.pack x      commandSuccess st  cmdZnc :: NetworkCommand String@@ -1573,7 +1616,7 @@   do sendMsg cs (ircZnc (Text.words (Text.pack rest)))      commandSuccess st -cmdZncPlayback :: NetworkCommand (Maybe (String, Maybe (String, ())))+cmdZncPlayback :: NetworkCommand (Maybe (String, Maybe String)) cmdZncPlayback cs st args =   case args of @@ -1582,7 +1625,7 @@      -- current date explicit time     Just (timeStr, Nothing)-       | Just tod <- parse timeFormats timeStr ->+       | Just tod <- parseFormats timeFormats timeStr ->           do now <- getZonedTime              let (nowTod,t) = (zonedTimeLocalTime . localTimeTimeOfDay <<.~ tod) now                  yesterday = over (zonedTimeLocalTime . localTimeDay) (addDays (-1))@@ -1592,9 +1635,9 @@              successZoned (fixDay t)      -- explicit date and time-    Just (dateStr, Just (timeStr, _))-       | Just day  <- parse dateFormats dateStr-       , Just tod  <- parse timeFormats timeStr ->+    Just (dateStr, Just timeStr)+       | Just day  <- parseFormats dateFormats dateStr+       , Just tod  <- parseFormats timeFormats timeStr ->           do tz <- getCurrentTimeZone              successZoned ZonedTime                { zonedTimeZone = tz@@ -1608,7 +1651,7 @@     -- %k doesn't require a leading 0 for times before 10AM     timeFormats = ["%k:%M:%S","%k:%M"]     dateFormats = ["%F"]-    parse formats str =+    parseFormats formats str =       asum (map (parseTimeM False defaultTimeLocale ?? str) formats)      successZoned = success . formatTime defaultTimeLocale "%s"@@ -1617,11 +1660,11 @@       do sendMsg cs (ircZnc ["*playback", "play", "*", Text.pack start])          commandSuccess st -cmdMode :: NetworkCommand String-cmdMode cs st rest = modeCommand (Text.pack <$> words rest) cs st+cmdMode :: NetworkCommand [String]+cmdMode cs st xs = modeCommand (Text.pack <$> xs) cs st -cmdNick :: NetworkCommand (String, ())-cmdNick cs st (nick,_) =+cmdNick :: NetworkCommand String+cmdNick cs st nick =   do sendMsg cs (ircNick (Text.pack nick))      commandSuccess st @@ -1636,8 +1679,8 @@ cmdSay :: ChannelCommand String cmdSay _ _ st rest = executeChat rest st -cmdInvite :: ChannelCommand (String, ())-cmdInvite channelId cs st (nick,_) =+cmdInvite :: ChannelCommand String+cmdInvite channelId cs st nick =   do let freeTarget = has (csChannels . ix channelId . chanModes . ix 'g') cs          cmd = ircInvite (Text.pack nick) channelId      cs' <- if freeTarget@@ -1675,8 +1718,8 @@ cmdChannelInfo :: ChannelCommand () cmdChannelInfo _ _ st _ = commandSuccess (changeSubfocus FocusInfo st) -cmdMasks :: ChannelCommand (String,())-cmdMasks channel cs st (rest,_) =+cmdMasks :: ChannelCommand String+cmdMasks channel cs st rest =   case rest of     [mode] | mode `elem` view (csModeTypes . modesLists) cs -> @@ -1728,30 +1771,35 @@   do sendMsg cs (ircKnock (Text.pack chan) (Text.pack message))      commandSuccess st -cmdJoin :: NetworkCommand (String, Maybe (String, ()))+cmdJoin :: NetworkCommand (String, Maybe String) cmdJoin cs st (channels, mbKeys) =   do let network = view csNetwork cs      let channelId = mkId (Text.pack (takeWhile (/=',') channels))-     sendMsg cs (ircJoin (Text.pack channels) (Text.pack . fst <$> mbKeys))+     sendMsg cs (ircJoin (Text.pack channels) (Text.pack <$> mbKeys))      commandSuccess         $ changeFocus (ChannelFocus network channelId) st   -- | @/query@ command. Takes a channel or nickname and switches -- focus to that target on the current network.-cmdQuery :: NetworkCommand (String, ())-cmdQuery cs st (channel, _) =-  commandSuccess-    $ changeFocus (ChannelFocus (view csNetwork cs) (mkId (Text.pack channel))) st+cmdQuery :: ClientCommand String+cmdQuery st channel =+  case parseFocus (views clientFocus focusNetwork st) channel of+    Just focus -> commandSuccess (changeFocus focus st)+    Nothing    -> commandFailureMsg "No current network" st  -- | Tab completion for @/query@ tabQuery ::   Bool {- ^ reversed order -} ->-  NetworkCommand String-tabQuery isReversed cs st _ =+  ClientCommand String+tabQuery isReversed st _ =   simpleTabCompletion plainWordCompleteMode [] completions isReversed st   where-    completions = channelWindowsOnNetwork (view csNetwork cs) st+    completions = currentNet <> allWindows+    allWindows  = renderSplitFocus <$> views clientWindows Map.keys st+    currentNet  = case views clientFocus focusNetwork st of+                    Just net -> idText <$> channelWindowsOnNetwork net st+                    Nothing  -> []  -- | Return the list of identifiers for open channel windows on -- the given network name.@@ -1803,15 +1851,16 @@ -- | Implementation of @/reload@ -- -- Attempt to reload the configuration file-cmdReload :: ClientCommand (Maybe (String, ()))+cmdReload :: ClientCommand (Maybe String) cmdReload st mbPath =-  do let path = fst <$> mbPath-            <|> view (clientConfig . configConfigPath) st+  do let path   = mbPath <|> view clientConfigPath st      res <- loadConfiguration path      case res of-       Left e    -> commandFailureMsg (describeProblem e) st+       Left e -> commandFailureMsg (describeProblem e) st        Right cfg ->-         do st1 <- clientStartExtensions (set clientConfig cfg st)+         do st1 <- clientStartExtensions+                 $ set clientConfig cfg+                 $ set clientConfigPath mbPath st             commandSuccess st1    where@@ -1829,6 +1878,45 @@ tabReload :: Bool {- ^ reversed -} -> ClientCommand String tabReload _ st _ = commandFailure st ++modeParamArgs :: ClientState -> String -> Maybe (Args ClientState [String])+modeParamArgs st str =+  case view clientFocus st of+    Unfocused      -> Nothing+    NetworkFocus _ -> Just (pure [str])+    ChannelFocus net _ ->++         -- determine current mode types+      do cs <- preview (clientConnection net) st+         let types = view csModeTypes cs++         -- parse the list of modes being set+         flags <- splitModes types (Text.pack str) []++         -- generate the argument specification+         let (req,opt) = foldr (countFlags types) ([],[]) flags+         return ((str:) <$> tokenList req (map (++"?") opt))+++-- | This function computes the list of required and optional parameters+-- corresponding to the flags that have been entered.+countFlags ::+  ModeTypes           {- ^ network's mode behaviors              -} ->+  (Bool, Char, Text)  {- ^ polarity mode-letter unused-parameter -} ->+  ([String],[String]) {- ^ required-names optional-names         -} ->+  ([String],[String]) {- ^ required-names optional-names         -}+countFlags types (pol, flag, _)+  |        flag `elem` view modesLists       types = addOpt+  | pol && flag `elem` view modesSetArg      types = addReq+  |        flag `elem` view modesAlwaysArg   types = addReq+  | elemOf (modesPrefixModes . folded . _1) flag types = addReq+  | otherwise                                      = id+  where+    addReq (req,opt) = ((flag:" param"):req,opt)+    addOpt ([] ,opt) = ([], (flag:" param"):opt)+    addOpt (req,opt) = ((flag:" param"):req,opt)++ modeCommand ::   [Text] {- mode parameters -} ->   NetworkState                 ->@@ -2062,17 +2150,11 @@     } ste  -cmdUrl :: ClientCommand (Maybe (String, ()))-cmdUrl st mbArg =+cmdUrl :: ClientCommand (Maybe Int)+cmdUrl st arg =   case view (clientConfig . configUrlOpener) st of-    Nothing -> commandFailureMsg "url-opener not configured" st-    Just opener ->-      case mbArg of-        Nothing -> doUrlOpen opener 0-        Just (arg,_) ->-          case readMaybe arg of-            Just n | n > 0 -> doUrlOpen opener (n-1)-            _ -> commandFailureMsg "bad url number" st+    Nothing     -> commandFailureMsg "url-opener not configured" st+    Just opener -> doUrlOpen opener (maybe 0 (subtract 1) arg)   where     focus = view clientFocus st @@ -2102,7 +2184,7 @@         Left e -> commandFailureMsg (Text.pack e) st         Right r -> commandSuccess (set clientRegex (Just r) st) -cmdOper :: NetworkCommand (String, (String, ()))-cmdOper cs st (user, (pass, ())) =+cmdOper :: NetworkCommand (String, String)+cmdOper cs st (user, pass) =   do sendMsg cs (ircOper (Text.pack user) (Text.pack pass))      commandSuccess st
− src/Client/Commands/Arguments.hs
@@ -1,75 +0,0 @@-{-# Language GADTs, KindSignatures #-}--{-|-Module      : Client.Commands.Arguments-Description : Command argument description and parsing-Copyright   : (c) Eric Mertens, 2016-License     : ISC-Maintainer  : emertens@gmail.com--This module provides a description for the arguments expected-by command commands as well as a way to parse those arguments.--}--module Client.Commands.Arguments-  ( ArgumentSpec(..)-  , parseArguments-  ) where--import           Control.Monad---- | Description of a command's arguments indexed by the result of parsing--- those arguments. Arguments are annotated with a 'String' describing the--- argument.-data ArgumentSpec :: * -> * where--  -- | A required space-delimited token-  ReqTokenArg  :: String -> ArgumentSpec rest -> ArgumentSpec (String, rest)--  -- | An optional space-delimited token-  OptTokenArg  :: String -> ArgumentSpec rest -> ArgumentSpec (Maybe (String, rest))--  -- | Take all the remaining text in free-form-  RemainingArg :: String -> ArgumentSpec String--  -- | No arguments-  NoArg        :: ArgumentSpec ()--instance Show (ArgumentSpec s) where-  showsPrec p spec =-    case spec of-      ReqTokenArg s rest -> showParen (p >= 11)-                          $ showString "ReqTokenArg "-                          . showsPrec 11 s . showChar ' ' . showsPrec 11 rest-      OptTokenArg s rest -> showParen (p >= 11)-                          $ showString "OptTokenArg "-                          . showsPrec 11 s . showChar ' ' . showsPrec 11 rest-      RemainingArg s     -> showParen (p >= 11)-                          $ showString "RemainingArg " . showsPrec 11 s-      NoArg              -> showString "NoArg"---- | Parse the given input string using an argument specification.--- The arguments should start with a space but might have more.-parseArguments ::-  ArgumentSpec a {- ^ specification -} ->-  String         {- ^ input string  -} ->-  Maybe a        {- ^ parse results -}-parseArguments arg xs =-  case arg of-    NoArg          -> guard (all (==' ') xs)-    RemainingArg _ -> Just (drop 1 xs) -- drop the leading space-    OptTokenArg _ rest ->-      do let (tok, xs') = nextToken xs-         if null tok-           then Just Nothing-           else do rest' <- parseArguments rest xs'-                   return (Just (tok, rest'))-    ReqTokenArg _ rest ->-      do let (tok, xs') = nextToken xs-         guard (not (null tok))-         rest' <- parseArguments rest xs'-         return (tok, rest')---- | Return the next space delimited token. Leading space is dropped.-nextToken :: String -> (String, String)-nextToken = break (==' ') . dropWhile (==' ')
+ src/Client/Commands/Arguments/Parser.hs view
@@ -0,0 +1,65 @@+{-# Language GADTs #-}++{-|+Module      : Client.Commands.Arguments.Parser+Description : Interpret argument specifications as a parser+Copyright   : (c) Eric Mertens, 2017+License     : ISC+Maintainer  : emertens@gmail.com++-}++module Client.Commands.Arguments.Parser (parse) where++import Client.Commands.Arguments.Spec+import Control.Applicative+import Control.Monad+import Control.Monad.Trans.State+import Control.Monad.Trans.Class+import Control.Applicative.Free++------------------------------------------------------------------------+-- Parser++parse :: r -> Args r a -> String -> Maybe a+parse env spec str =+  do (a,rest) <- runStateT (parseArgs env spec) str+     guard (all (' '==) rest)+     return a++type Parser = StateT String Maybe++parseArgs :: r -> Args r a -> Parser a+parseArgs env spec = runAp (parseArg env) spec++parseArg :: r -> Arg r a -> Parser a+parseArg env spec =+  case spec of+    Argument shape _ f ->+      do t <- argumentString shape+         lift (f env t)+    Optional subspec -> optional (parseArgs env subspec)+    Extension _ parseFormat ->+      do t <- token+         subspec <- lift (parseFormat env t)+         parseArgs env subspec++argumentString :: ArgumentShape -> Parser String+argumentString TokenArgument     = token+argumentString RemainingArgument = remaining++remaining :: Parser String+remaining =+  do xs <- get+     put ""+     return $! case xs of+                 ' ':xs' -> xs'+                 _       -> xs++token :: Parser String+token =+  do xs <- get+     let (t, xs') = break (' '==) (dropWhile (' '==) xs)+     guard (not (null t))+     put (drop 1 xs')+     return t
+ src/Client/Commands/Arguments/Renderer.hs view
@@ -0,0 +1,106 @@+{-# Language OverloadedStrings, GADTs #-}++{-|+Module      : Client.Commands.Arguments.Renderer+Description : Interpretation of argument specification as a renderer+Copyright   : (c) Eric Mertens, 2017+License     : ISC+Maintainer  : emertens@gmail.com++-}++module Client.Commands.Arguments.Renderer (render) where++import           Client.Commands.Arguments.Spec+import           Client.Image.MircFormatting+import           Client.Image.PackedImage+import           Client.Image.Palette+import           Control.Applicative.Free+import           Control.Lens+import           Control.Monad.Trans.State+import           Data.Functor.Compose+import           Data.Semigroup ((<>))+import qualified Data.Text as Text+import           Graphics.Vty (wcswidth)+import           Graphics.Vty.Attributes++render ::+  Palette  {- ^ palette             -} ->+  r        {- ^ environment         -} ->+  Bool     {- ^ render placeholders -} ->+  Args r a {- ^ specification       -} ->+  String   {- ^ user input          -} ->+  Image'+render pal env placeholders spec str = extend (addExcess img)+  where+    (img, excess) = flip runState str . getState+                  $ renderArgs pal env placeholders spec++    addExcess+      | any (' '/=) excess = (<> string defAttr excess)+      | otherwise          = id++    extend i+      | imageWidth i < minLen = resizeImage minLen i+      | otherwise             = i+      where minLen = wcswidth str++++renderArgs :: Palette -> r -> Bool -> Args r a -> Renderer a+renderArgs pal r placeholders = runAp (renderArg pal r placeholders)++------------------------------------------------------------------------++type Renderer = Compose (State String) (Const Image')++getState :: Renderer a -> State String Image'+getState = fmap getConst . getCompose++putState :: State String Image' -> Renderer a+putState = Compose . fmap Const++------------------------------------------------------------------------++renderArg :: Palette -> r -> Bool -> Arg r a -> Renderer b+renderArg pal r placeholders spec = putState $++  let placeholder name+        | placeholders = return (" " <> string (view palCommandPlaceholder pal) name)+        | otherwise    = return mempty++      draw = parseIrcText' True . Text.pack+  in++  case spec of+    Optional subspec -> getState (renderArgs pal r placeholders subspec)++    Extension name ext ->+      do (lead,tok) <- state token+         if null tok then+           placeholder name+         else do+           rest <- case ext r tok of+                     Nothing      -> return mempty+                     Just subspec -> getState (renderArgs pal r placeholders subspec)+           return (draw (lead++tok) <> rest)++    Argument TokenArgument name _ ->+      do (lead,tok) <- state token+         if null tok then+           placeholder name+         else+           return (draw (lead++tok))++    Argument RemainingArgument name _ ->+      do rest <- state (\x -> (x,""))+         if all (' '==) rest then+           placeholder name+         else+           return (draw rest)++token :: String -> ((String, String), String)+token xs =+  let (lead, xs1) = span  (' '==) xs+      (tok , xs2) = break (' '==) xs1+  in ((lead, tok), xs2)
+ src/Client/Commands/Arguments/Spec.hs view
@@ -0,0 +1,64 @@+{-# Language KindSignatures, GADTs #-}++{-|+Module      : Client.Commands.Arguments.Spec+Description : Argument specifications used within client commands+Copyright   : (c) Eric Mertens, 2017+License     : ISC+Maintainer  : emertens@gmail.com++-}++module Client.Commands.Arguments.Spec+  ( Args+  , simpleToken+  , remainingArg+  , optionalArg+  , tokenList+  , numberArg+  , extensionArg++  , ArgumentShape(..)+  , Arg(..)+  ) where++import Control.Applicative+import Control.Applicative.Free+import Data.Maybe (fromMaybe)+import Text.Read (readMaybe)++type Args r = Ap (Arg r)++data ArgumentShape = TokenArgument | RemainingArgument++data Arg :: * -> * -> * where+  Argument  :: ArgumentShape -> String -> (r -> String -> Maybe a) -> Arg r a+  Optional  :: Args r a -> Arg r (Maybe a)+  Extension :: String -> (r -> String -> Maybe (Args r a)) -> Arg r a++tokenArg :: String -> (r -> String -> Maybe a) -> Args r a+tokenArg name parser = liftAp (Argument TokenArgument name parser)++remainingArg :: String -> Args r String+remainingArg name = liftAp (Argument RemainingArgument name (\_ -> Just))++optionalArg :: Args r a -> Args r (Maybe a)+optionalArg = liftAp . Optional++extensionArg :: String -> (r -> String -> Maybe (Args r a)) -> Args r a+extensionArg name parser = liftAp (Extension name parser)++simpleToken :: String -> Args r String+simpleToken name = tokenArg name (\_ -> Just)++numberArg :: Args r Int+numberArg = tokenArg "number" (\_ -> readMaybe)++tokenList ::+  [String] {- ^ required names -} ->+  [String] {- ^ optional names -} ->+  Args r [String]+tokenList req opt = foldr addReq (foldr addOpt (pure []) opt) req+  where+    addReq name      = liftA2 (:) (simpleToken name)+    addOpt name rest = fromMaybe [] <$> optionalArg (addReq name rest)
src/Client/Commands/Interpolation.hs view
@@ -1,4 +1,4 @@-{-# Language OverloadedStrings, GADTs #-}+{-# Language OverloadedStrings, GADTs, RankNTypes #-}  {-| Module      : Client.Commands.Interpolation@@ -24,10 +24,11 @@ import           Control.Applicative import           Data.Attoparsec.Text as P import           Data.Char+import           Data.Maybe import qualified Data.Text as Text import           Data.Text (Text) -import           Client.Commands.Arguments+import           Client.Commands.Arguments.Spec  -- | Parsed chunk of an expandable command data ExpansionChunk@@ -48,16 +49,14 @@   } deriving Show  data MacroSpec where-  MacroSpec :: ArgumentSpec s -> MacroSpec+  MacroSpec :: (forall r. Args r [String]) -> MacroSpec  instance Show MacroSpec where-  showsPrec p (MacroSpec as)-    = showParen (p >= 11)-    $ showString "MacroSpec " . showsPrec 11 as+  show MacroSpec{} = "MacroSpec"  -- | Specification used when unspecified, no arguments. noMacroArguments :: MacroSpec-noMacroArguments = MacroSpec NoArg+noMacroArguments = MacroSpec (pure [])  parseMacroSpecs :: Text -> Maybe MacroSpec parseMacroSpecs txt =@@ -71,10 +70,12 @@        <*> optional (char '?')        <*  P.skipSpace        <*> macroSpecs-    <|> pure (MacroSpec NoArg)- where- cons desc (Just _) (MacroSpec rest) = MacroSpec (OptTokenArg (Text.unpack desc) rest)- cons desc Nothing  (MacroSpec rest) = MacroSpec (ReqTokenArg (Text.unpack desc) rest)+    <|> pure (MacroSpec (pure []))+  where+    add1 desc = liftA2 (:) (simpleToken (Text.unpack desc))++    cons desc (Just _) (MacroSpec rest) = MacroSpec (fromMaybe [] <$> optionalArg (add1 desc rest))+    cons desc Nothing  (MacroSpec rest) = MacroSpec (add1 desc rest)  -- | Parse a 'Text' searching for the expansions as specified in -- 'ExpansionChunk'. @$$@ is used to escape a single @$@.
src/Client/Configuration.hs view
@@ -28,7 +28,6 @@   , configPalette   , configWindowNames   , configNickPadding-  , configConfigPath   , configMacros   , configExtensions   , configExtraHighlights@@ -45,7 +44,6 @@   , loadConfiguration    -- * Resolving paths-  , resolveConfigurationPath   , getNewConfigPath    -- * Specification@@ -92,8 +90,6 @@   , _configWindowNames     :: Text -- ^ Names of windows, used when alt-jumping)   , _configExtraHighlights :: HashSet Identifier -- ^ Extra highlight nicks/terms   , _configNickPadding     :: PaddingMode -- ^ Padding of nicks in messages-  , _configConfigPath      :: Maybe FilePath-        -- ^ manually specified configuration path, used for reloading   , _configMacros          :: Recognizer Macro -- ^ command macros   , _configExtensions      :: [FilePath] -- ^ paths to shared library   , _configUrlOpener       :: Maybe FilePath -- ^ paths to url opening executable@@ -214,7 +210,7 @@                 $ Text.unpack                 $ Text.unlines                 $ map explainLoadError (toList es)-       Right cfg -> return (cfg mbPath def)+       Right cfg -> resolvePaths path (cfg def)   explainLoadError :: LoadError Position -> Text@@ -237,7 +233,21 @@         SpecMismatch   t -> "expected "                  <> t  -configurationSpec :: ValueSpecs (Maybe FilePath -> ServerSettings -> Configuration)+-- | Resolve all the potentially relative file paths in the configuration file+resolvePaths :: FilePath -> Configuration -> IO Configuration+resolvePaths file cfg =+  do res <- resolveFilePath <$> newFilePathContext file+     let resolveServerFilePaths = over (ssTlsClientCert . mapped) res+                                . over (ssTlsClientKey  . mapped) res+                                . over (ssTlsServerCert . mapped) res+                                . over (ssSaslEcdsaFile . mapped) res+                                . over (ssLogDir        . mapped) res+     return $! over (configExtensions . mapped) res+             . over (configServers    . mapped) resolveServerFilePaths+             $ cfg++configurationSpec ::+  ValueSpecs (ServerSettings -> Configuration) configurationSpec = sectionsSpec "" $    do let sec' def name spec info = fromMaybe def <$> optSection' name spec info@@ -276,7 +286,7 @@                                "Extra key bindings"      _configLayout          <- sec' OneColumn "layout" layoutSpec                                "Initial setting for window layout"-     return (\_configConfigPath def ->+     return (\def ->              let _configDefaults = ssDefUpdate def                  _configServers  = buildServerMap _configDefaults ssUpdates                  _configKeyMap   = foldl (\acc f -> f acc) initialKeyMap bindings@@ -330,24 +340,24 @@  actBindingSpec :: ValueSpecs (KeyMap -> KeyMap) actBindingSpec = sectionsSpec "action-binding" $-  do (m,k) <- reqSection' "bind" keySpec-              "Key to be bound (e.g. a, C-b, M-c C-M-d)"-     a     <- reqSection "action"-              "Action name (see `/keymap`)"+  do ~(m,k) <- reqSection' "bind" keySpec+               "Key to be bound (e.g. a, C-b, M-c C-M-d)"+     a      <- reqSection "action"+               "Action name (see `/keymap`)"      return (addKeyBinding m k a)  cmdBindingSpec :: ValueSpecs (KeyMap -> KeyMap) cmdBindingSpec = sectionsSpec "command-binding" $-  do (m,k) <- reqSection' "bind" keySpec-              "Key to be bound (e.g. a, C-b, M-c C-M-d)"-     cmd   <- reqSection "command"-              "Client command to execute (exclude leading `/`)"+  do ~(m,k) <- reqSection' "bind" keySpec+               "Key to be bound (e.g. a, C-b, M-c C-M-d)"+     cmd    <- reqSection "command"+               "Client command to execute (exclude leading `/`)"      return (addKeyBinding m k (ActCommand cmd))  unbindingSpec :: ValueSpecs (KeyMap -> KeyMap) unbindingSpec = sectionsSpec "remove-binding" $-  do (m,k) <- reqSection' "unbind" keySpec-              "Key to be unbound (e.g. a, C-b, M-c C-M-d)"+  do ~(m,k) <- reqSection' "unbind" keySpec+               "Key to be unbound (e.g. a, C-b, M-c C-M-d)"      return (removeKeyBinding m k)  @@ -373,7 +383,10 @@            : [ optSection' lbl (set l <$> attrSpec) "" | (lbl, Lens l) <- paletteMap ]  -buildServerMap :: ServerSettings -> [ServerSettings -> ServerSettings] -> HashMap Text ServerSettings+buildServerMap ::+  ServerSettings {- ^ defaults -} ->+  [ServerSettings -> ServerSettings] ->+  HashMap Text ServerSettings buildServerMap def ups =   HashMap.fromList [ (serverSettingName ss, ss) | up <- ups, let ss = up def ]   where@@ -382,10 +395,15 @@                 (view ssName ss)  --- | Resolve relative paths starting at the home directory rather than--- the current directory of the client.-resolveConfigurationPath :: FilePath -> IO FilePath-resolveConfigurationPath path-  | isAbsolute path = return path-  | otherwise = do home <- getHomeDirectory-                   return (home </> path)+data FilePathContext = FilePathContext { fpBase, fpHome :: FilePath }++newFilePathContext ::+  FilePath {- ^ configuration file path -} ->+  IO FilePathContext+newFilePathContext base = FilePathContext (takeDirectory base) <$> getHomeDirectory++resolveFilePath :: FilePathContext -> FilePath -> FilePath+resolveFilePath fpc path+  | isAbsolute path                   = path+  | "~":rest <- splitDirectories path = joinPath (fpHome fpc : rest)+  | otherwise                         = fpBase fpc </> path
src/Client/Configuration/ServerSettings.hs view
@@ -154,16 +154,20 @@ serverSpec = sectionsSpec "server-settings" $   composeMaybe <$> sequenceA settings   where+     composeMaybe :: [Maybe (a -> a)] -> a -> a     composeMaybe = ala Endo (foldMap . foldMap) -    req name l s info = optSection' name ?? info-                      $ set l <$> s+    req name l s info+      = optSection' name ?? info+      $ set l <$> s -    opt name l s info = optSection' name ?? info-                      $ set l . Just <$> s-                    <!> set l Nothing <$ atomSpec "clear"+    opt name l s info+      = optSection' name ?? info+      $ set l . Just <$> s <!>+        set l Nothing <$ atomSpec "clear" +    settings :: [SectionSpecs (Maybe (ServerSettings -> ServerSettings))]     settings =       [ opt "name" ssName valuesSpec         "The name used to identify this server in the client"
src/Client/EventLoop/Actions.hs view
@@ -118,7 +118,7 @@    HashMap.fromList -  [("delete"            , (ActDelete           , [meta (KChar 'd'), norm KDel]))+  [("delete"            , (ActDelete           , [ctrl (KChar 'd'), norm KDel]))   ,("backspace"         , (ActBackspace        , [norm KBS]))   ,("home"              , (ActHome             , [norm KHome, ctrl (KChar 'a')]))   ,("end"               , (ActEnd              , [norm KEnd , ctrl (KChar 'e')]))@@ -244,6 +244,7 @@     liftMaybe mb = Compose ((,)[] <$> mb)     go str =       case str of+        "Space"     -> pure (KChar ' ')         "Tab"       -> pure (KChar '\t')         "BackTab"   -> pure KBackTab         "Enter"     -> pure KEnter@@ -276,6 +277,7 @@ prettyModifier MAlt   = showString "A-"  prettyKey :: Key -> String+prettyKey (KChar ' ') = "Space" prettyKey (KChar '\t') = "Tab" prettyKey (KChar c) = showLitChar c "" -- escapes anything non-ascii prettyKey (KFun n)  = 'F' : show n
− src/Client/Image/Arguments.hs
@@ -1,66 +0,0 @@-{-# Language GADTs #-}--{-|-Module      : Client.Image.Arguments-Description : Rendering logic for commanad arguments-Copyright   : (c) Eric Mertens, 2016-License     : ISC-Maintainer  : emertens@gmail.com--This module provides image rendering for the textbox in the-context of command argument processing.---}--module Client.Image.Arguments-  ( argumentsImage-  ) where--import           Client.Commands.Arguments-import           Client.Image.MircFormatting-import           Client.Image.PackedImage-import           Client.Image.Palette-import           Control.Lens-import           Data.Semigroup-import qualified Data.Text as Text-import           Graphics.Vty.Attributes---- | Parse command arguments against a given 'ArgumentSpec'.--- The given text will be rendered and then any missing arguments--- will be indicated by extra placeholder values appended onto the--- image. Parameters are rendered with 'plainText' except for--- the case of 'RemainingArg' which supports WYSIWYG.-argumentsImage :: Palette -> ArgumentSpec a -> String -> Image'-argumentsImage pal spec xs-  | all (==' ') xs = placeholders-                  <> string defAttr (drop (imageWidth placeholders) xs)-  | otherwise =-     case spec of-       NoArg           -> plainText xs-       ReqTokenArg _ a -> plainText token <> argumentsImage pal a xs'-       OptTokenArg _ a -> plainText token <> argumentsImage pal a xs'-       RemainingArg _  -> parseIrcTextExplicit (Text.pack xs)--  where-    token = token1 ++ token2-    (token1,(token2,xs')) =-         break (==' ') <$> span (==' ') xs--    placeholders = mkPlaceholders pal spec---- | Construct an 'Image' containing placeholders for each--- of the remaining arguments.-mkPlaceholders :: Palette -> ArgumentSpec a -> Image'-mkPlaceholders pal arg =-  case arg of-    NoArg           -> mempty-    ReqTokenArg n a -> leader-                    <> string (view palCommandPlaceholder pal) n-                    <> mkPlaceholders pal a-    OptTokenArg n a -> leader-                    <> string (view palCommandPlaceholder pal) (n ++ "?")-                    <> mkPlaceholders pal a-    RemainingArg n  -> leader-                    <> string (view palCommandPlaceholder pal) (n ++ "…")-  where-    leader = char defAttr ' '
src/Client/Image/Message.hs view
@@ -24,13 +24,17 @@   , cleanChar   , nickPad   , timeImage+  , drawWindowLine+  , parseIrcTextWithNicks   ) where  import           Client.Configuration (PaddingMode(..))+import           Client.Image.LineWrap import           Client.Image.MircFormatting import           Client.Image.PackedImage import           Client.Image.Palette import           Client.Message+import           Client.State.Window import           Control.Lens import           Data.Char import           Data.Hashable (hash)@@ -233,9 +237,8 @@       string (withForeColor defAttr blue) "* " <> who src     Ctcp {} -> mempty -    CtcpNotice src _dst "ACTION" _txt ->-      string (withForeColor defAttr red) "* " <> who src-    CtcpNotice {} -> mempty+    CtcpNotice src _dst _cmd _txt ->+      string (withForeColor defAttr red) "! " <> who src      Error {} -> string (view palError pal) "ERROR" <> ":" @@ -278,17 +281,17 @@     Authenticate{} -> "***"      Error                   txt -> parseIrcText txt-    Topic      _ _          txt -> parseIrcTextWithNicks pal myNicks nicks txt-    Kick       _ _ _        txt -> parseIrcTextWithNicks pal myNicks nicks txt-    Notice     _ _          txt -> parseIrcTextWithNicks pal myNicks nicks txt-    Privmsg    _ _          txt -> parseIrcTextWithNicks pal myNicks nicks txt-    Ctcp       _ _ "ACTION" txt -> parseIrcTextWithNicks pal myNicks nicks txt+    Topic      _ _          txt -> parseIrcTextWithNicks pal myNicks nicks False txt+    Kick       _ _ _        txt -> parseIrcTextWithNicks pal myNicks nicks False txt+    Notice     _ _          txt -> parseIrcTextWithNicks pal myNicks nicks False txt+    Privmsg    _ _          txt -> parseIrcTextWithNicks pal myNicks nicks False txt+    Ctcp       _ _ "ACTION" txt -> parseIrcTextWithNicks pal myNicks nicks False txt     Ctcp {}                     -> mempty-    CtcpNotice _ _ "ACTION" txt -> parseIrcTextWithNicks pal myNicks nicks txt-    CtcpNotice {}               -> mempty+    CtcpNotice _ _ cmd      txt -> parseIrcText cmd <> " " <>+                                   parseIrcTextWithNicks pal myNicks nicks False txt      Reply code params -> renderReplyCode NormalRender code params-    UnknownMsg irc    ->+    UnknownMsg irc ->       text' defAttr (view msgCommand irc) <>       char defAttr ' ' <>       separatedParams (view msgParams irc)@@ -354,40 +357,32 @@       string quietAttr "note " <>       who src <>       string (withForeColor defAttr red) ": " <>-      parseIrcTextWithNicks pal myNicks nicks txt+      parseIrcTextWithNicks pal myNicks nicks False txt      Privmsg src _dst txt ->       string quietAttr "chat " <>       who src <> ": " <>-      parseIrcTextWithNicks pal myNicks nicks txt+      parseIrcTextWithNicks pal myNicks nicks False txt      Ctcp src _dst "ACTION" txt ->       string quietAttr "actp " <>       string (withForeColor defAttr blue) "* " <>       who src <> " " <>-      parseIrcTextWithNicks pal myNicks nicks txt--    CtcpNotice src _dst "ACTION" txt ->-      string quietAttr "actn " <>-      string (withForeColor defAttr red) "* " <>-      who src <> " " <>-      parseIrcTextWithNicks pal myNicks nicks txt+      parseIrcTextWithNicks pal myNicks nicks False txt      Ctcp src _dst cmd txt ->       string quietAttr "ctcp " <>       string (withForeColor defAttr blue) "! " <>       who src <> " " <>       parseIrcText cmd <>-      separatorImage <>-      parseIrcText txt+      if Text.null txt then mempty else separatorImage <> parseIrcText txt      CtcpNotice src _dst cmd txt ->       string quietAttr "ctcp " <>       string (withForeColor defAttr red) "! " <>       who src <> " " <>       parseIrcText cmd <>-      separatorImage <>-      parseIrcText txt+      if Text.null txt then mempty else separatorImage <> parseIrcText txt      Ping params ->       "PING " <> separatedParams params@@ -578,13 +573,15 @@ -- the formatting codes. Otherwise the nicknames in the message are -- highlighted. parseIrcTextWithNicks ::-  Palette ->-  HashSet Identifier {- ^ my nicks    -} ->-  HashSet Identifier {- ^ other nicks -} ->-  Text -> Image'-parseIrcTextWithNicks palette myNicks nicks txt-  | Text.any isControl txt = parseIrcText txt-  | otherwise              = highlightNicks palette myNicks nicks txt+  Palette            {- ^ palette      -} ->+  HashSet Identifier {- ^ my nicks     -} ->+  HashSet Identifier {- ^ other nicks  -} ->+  Bool               {- ^ explicit controls rendering -} ->+  Text               {- ^ input text   -} ->+  Image'             {- ^ colored text -}+parseIrcTextWithNicks palette myNick nicks explicit txt+  | Text.any isControl txt = parseIrcText' explicit txt+  | otherwise              = highlightNicks palette myNick nicks txt  -- | Given a list of nicknames and a chat message, this will generate -- an image where all of the occurrences of those nicknames are colored.@@ -618,3 +615,19 @@ -- | Image used when treating ignored chat messages as metadata ignoreImage :: Image' ignoreImage = char (withForeColor defAttr yellow) 'I'+++-- | Render the normal view of a chat message line padded and wrapped.+drawWindowLine ::+  Palette     {- ^ palette       -} ->+  Int         {- ^ draw columns  -} ->+  PaddingMode {- ^ nick padding  -} ->+  WindowLine  {- ^ window line   -} ->+  [Image']    {- ^ wrapped lines -}+drawWindowLine palette w padAmt wl = wrap (drawPrefix wl) (view wlImage wl)+  where+    drawTime = timeImage palette . unpackTimeOfDay+    padNick = nickPad padAmt+    wrap pfx body = reverse (lineWrapPrefix w pfx body)+    drawPrefix = views wlTimestamp drawTime <>+                 views wlPrefix    padNick
src/Client/Image/MircFormatting.hs view
@@ -12,9 +12,10 @@ -} module Client.Image.MircFormatting   ( parseIrcText-  , parseIrcTextExplicit+  , parseIrcText'   , plainText   , controlImage+  , mircColor   ) where  import           Client.Image.PackedImage as I@@ -28,32 +29,11 @@ import           Data.Text (Text) import           Graphics.Vty.Attributes -data FormatState = FormatState-  { _fmtFore :: Maybe Color-  , _fmtBack :: Maybe Color-  , _fmtBold, _fmtItalic, _fmtUnderline, _fmtReverse :: !Bool-  }--makeLenses '' FormatState--formatAttr :: FormatState -> Attr-formatAttr fmt-  = doStyle (view fmtBold fmt) bold-  $ doStyle (view fmtUnderline fmt) underline-  $ doStyle (view fmtReverse fmt) reverseVideo-  $ doColor withForeColor (view fmtFore fmt)-  $ doColor withBackColor (view fmtBack fmt)-  $ defAttr--  where-    doStyle False _ attr = attr-    doStyle True  style attr = withStyle attr style--    doColor _ Nothing attr = attr-    doColor with (Just x) attr = with attr x--defaultFormatState :: FormatState-defaultFormatState = FormatState Nothing Nothing False False False False+makeLensesFor+  [ ("attrForeColor", "foreColorLens")+  , ("attrBackColor", "backColorLens")+  , ("attrStyle"    , "styleLens"    )]+  ''Attr  -- | Parse mIRC encoded format characters and hide the control characters. parseIrcText :: Text -> Image'@@ -62,12 +42,9 @@ -- | Parse mIRC encoded format characters and render the control characters -- explicitly. This view is useful when inputting control characters to make -- it clear where they are in the text.-parseIrcTextExplicit :: Text -> Image'-parseIrcTextExplicit = parseIrcText' True- parseIrcText' :: Bool -> Text -> Image' parseIrcText' explicit = either plainText id-                       . parseOnly (pIrcLine explicit defaultFormatState)+                       . parseOnly (pIrcLine explicit defAttr)  data Segment = TextSegment Text | ControlSegment Char @@ -75,14 +52,14 @@ pSegment = TextSegment    <$> takeWhile1 (not . isControl)        <|> ControlSegment <$> satisfy isControl -pIrcLine :: Bool -> FormatState -> Parser Image'+pIrcLine :: Bool -> Attr -> Parser Image' pIrcLine explicit fmt =   do seg <- option Nothing (Just <$> pSegment)      case seg of        Nothing -> return mempty        Just (TextSegment txt) ->            do rest <- pIrcLine explicit fmt-              return (text' (formatAttr fmt) txt <> rest)+              return (text' fmt txt <> rest)        Just (ControlSegment '\^C') ->            do (numberText, colorNumbers) <- match pColorNumbers               rest <- pIrcLine explicit (applyColors colorNumbers fmt)@@ -99,13 +76,17 @@           | otherwise -> next           where             mbFmt' = applyControlEffect c fmt-            next = pIrcLine explicit (fromMaybe fmt mbFmt')+            next   = pIrcLine explicit (fromMaybe fmt mbFmt') -pColorNumbers :: Parser (Maybe Int, Maybe Int)-pColorNumbers = option (Nothing,Nothing) $-  do n <- pNumber-     m <- optional (Parse.char ',' *> pNumber)-     return (Just n,m)+pColorNumbers :: Parser (Maybe (Color, Maybe Color))+pColorNumbers = option Nothing $+  do n       <- pNumber+     Just fc <- pure (mircColor n)+     bc      <- optional $+                  do m       <- Parse.char ',' *> pNumber+                     Just bc <- pure (mircColor m)+                     pure bc+     return (Just (fc,bc))    where     pNumber = do d1 <- digit@@ -115,11 +96,12 @@ optional :: Parser a -> Parser (Maybe a) optional p = option Nothing (Just <$> p) -applyColors :: (Maybe Int, Maybe Int) -> FormatState -> FormatState-applyColors (fore, back) = aux fmtFore fore . aux fmtBack back-  where-    aux _ Nothing  = id-    aux l (Just x) = set l (mircColor x)+applyColors :: (Maybe (Color, Maybe Color)) -> Attr -> Attr+applyColors Nothing = set foreColorLens Default+                    . set backColorLens Default+applyColors (Just (c1, Nothing)) = set foreColorLens (SetTo c1) -- preserve background+applyColors (Just (c1, Just c2)) = set foreColorLens (SetTo c1)+                                 . set backColorLens (SetTo c2)  mircColor :: Int -> Maybe Color mircColor  0 = Just (white                ) -- white@@ -143,13 +125,19 @@ rgbColor' :: Int -> Int -> Int -> Color rgbColor' = rgbColor -- fix the type to Int -applyControlEffect :: Char -> FormatState -> Maybe FormatState-applyControlEffect '\^B' = Just . over fmtBold not-applyControlEffect '\^O' = Just . const defaultFormatState-applyControlEffect '\^V' = Just . over fmtReverse not-applyControlEffect '\^]' = Just . over fmtItalic not-applyControlEffect '\^_' = Just . over fmtUnderline not-applyControlEffect _     = const Nothing+applyControlEffect :: Char -> Attr -> Maybe Attr+applyControlEffect '\^B' attr = Just $! toggleStyle bold attr+applyControlEffect '\^V' attr = Just $! toggleStyle reverseVideo attr+applyControlEffect '\^_' attr = Just $! toggleStyle underline attr+applyControlEffect '\^O' _    = Just defAttr+applyControlEffect '\^]' attr = Just attr -- italic not supported+applyControlEffect _     _    = Nothing++toggleStyle :: Style -> Attr -> Attr+toggleStyle s1 = over styleLens $ \old ->+  case old of+    SetTo s2 -> SetTo (xor s1 s2)+    _        -> SetTo s1  -- | Safely render a control character. controlImage :: Char -> Image'
src/Client/Image/StatusLine.hs view
@@ -35,9 +35,10 @@ import           Irc.Identifier (Identifier, idText) import           Numeric -bar :: Char-bar = '━'+bar :: Image'+bar = char (withStyle defAttr bold) '─' + -- | Renders the status line between messages and the textbox. statusLineImage ::   Int         {- ^ draw width   -} ->@@ -86,7 +87,7 @@   ClientState {- ^ client state -} ->   Image' minorStatusLineImage focus w showHideMeta st =-  content <> string defAttr (replicate fillSize bar)+  content <> mconcat (replicate fillSize bar)   where     content = focusImage focus st <>               if showHideMeta then nometaImage focus st else mempty@@ -146,8 +147,7 @@ -- | Wrap some text in parentheses to make it suitable for inclusion in the -- status line. infoBubble :: Image' -> Image'-infoBubble img =-  string defAttr (bar:"(") <> img <> ")"+infoBubble img = bar <> "(" <> img <> ")"   -- | Indicate that the client is in the /detailed/ view.@@ -177,7 +177,8 @@ activitySummary :: ClientState -> Vty.Image activitySummary st   | null indicators = Vty.emptyImage-  | otherwise       = Vty.string defAttr (bar:"[") Vty.<|>+  | otherwise       = unpackImage bar Vty.<|>+                      Vty.string defAttr "[" Vty.<|>                       Vty.horizCat indicators Vty.<|>                       Vty.string defAttr "]"   where@@ -209,13 +210,14 @@     baraux i (focus,w)       | n == 0 = Nothing -- todo: make configurable       | otherwise = Just-                  $ Vty.string defAttr (bar:"[") Vty.<|>+                  $ unpackImage bar Vty.<|>+                    Vty.char defAttr '[' Vty.<|>                     Vty.char (view palWindowName pal) i Vty.<|>                     Vty.char defAttr ':' Vty.<|>                     Vty.text' (view palLabel pal) focusText Vty.<|>                     Vty.char defAttr ':' Vty.<|>                     Vty.string attr (show n) Vty.<|>-                    Vty.string defAttr "]"+                    Vty.char defAttr ']'       where         n   = view winUnread w         pal = clientPalette st@@ -241,14 +243,16 @@ makeLines _ [] = Vty.emptyImage makeLines w (x:xs) = go x xs   where-     go acc (y:ys)       | let acc' = acc Vty.<|> y       , Vty.imageWidth acc' <= w       = go acc' ys      go acc ys = makeLines w ys-        Vty.<-> acc Vty.<|> Vty.charFill defAttr bar (max 0 (w - Vty.imageWidth acc)) 1+        Vty.<-> Vty.cropRight w acc+        Vty.<|> unpackImage (mconcat (replicate fillsize bar))+      where+        fillsize = max 0 (w - Vty.imageWidth acc)   myNickImage :: ClientState -> Vty.Image
src/Client/Image/Textbox.hs view
@@ -17,22 +17,27 @@  import           Client.Configuration import           Client.Commands-import           Client.Commands.Arguments+import           Client.Commands.Arguments.Renderer+import           Client.Commands.Arguments.Parser import           Client.Commands.Interpolation import           Client.Commands.Recognizer-import           Client.Image.Arguments+import           Client.Image.Message import           Client.Image.MircFormatting import           Client.Image.PackedImage import           Client.Image.Palette import           Client.State+import           Client.State.Focus import qualified Client.State.EditBox as Edit import           Control.Lens import           Data.Char+import qualified Data.HashSet as HashSet+import           Data.HashSet (HashSet) import           Data.List import           Data.Semigroup import qualified Data.Text as Text import           Graphics.Vty.Attributes import qualified Graphics.Vty.Image as Vty+import           Irc.Identifier  -- | Compute the UI image for the text input box. This computes -- the logical cursor position on the screen to compensate for@@ -43,7 +48,7 @@   where   macros = views (clientConfig . configMacros) (fmap macroSpec) st   (txt, content) =-     views (clientTextBox . Edit.content) (renderContent macros pal) st+     views (clientTextBox . Edit.content) (renderContent st myNick nicks macros pal) st    lineImage = unpackImage (beginning <> content <> ending) @@ -72,15 +77,28 @@   beginning = char attr '^'   ending    = char attr '$' ++  (myNick,nicks) =+    case view clientFocus st of+      ChannelFocus network channel ->+         (clientHighlightsNetwork network st,+          HashSet.fromList (channelUserList network channel st)+         )+      _ -> (HashSet.empty, HashSet.empty)++ -- | Renders the whole, uncropped text box as well as the 'String' -- corresponding to the rendered image which can be used for computing -- the logical cursor position of the cropped version of the text box. renderContent ::+  ClientState          {- ^ client state                          -} ->+  HashSet Identifier   {- ^ my nicknames                          -} ->+  HashSet Identifier   {- ^ other nicknames                       -} ->   Recognizer MacroSpec {- ^ macro completions                     -} ->   Palette              {- ^ palette                               -} ->   Edit.Content         {- ^ content                               -} ->   (String, Image')     {- ^ plain text rendering, image rendering -}-renderContent macros pal c = (txt, wholeImg)+renderContent st myNick nicks macros pal c = (txt, wholeImg)   where   as  = reverse (view Edit.above c)   bs  = view Edit.below c@@ -92,11 +110,13 @@   -- ["one","two"] "three" --> "two one three"   txt = foldl (\acc x -> x ++ ' ' : acc) leftCur as +  rndr = renderLine st pal myNick nicks macros+   wholeImg = mconcat            $ intersperse (plainText "\n")-           $ map renderOtherLine as-          ++ renderLine macros pal curTxt-           : map renderOtherLine bs+           $ map (rndr False) as+          ++ rndr True curTxt+           : map (rndr False) bs   -- | Version of 'wcwidth' that accounts for how control characters are@@ -112,18 +132,22 @@ myWcswidth = sum . map myWcwidth  --- | Render an unfocused line-renderOtherLine :: String -> Image'-renderOtherLine = parseIrcTextExplicit . Text.pack- -- | Render the active text box line using command highlighting and -- placeholders, and WYSIWYG mIRC formatting control characters.-renderLine :: Recognizer MacroSpec -> Palette -> String -> Image'-renderLine macros pal ('/':xs) =+renderLine ::+  ClientState ->+  Palette ->+  HashSet Identifier ->+  HashSet Identifier ->+  Recognizer MacroSpec {- ^ commands     -} ->+  Bool                 {- ^ focused      -} ->+  String               {- ^ input text   -} ->+  Image'               {- ^ output image -}+renderLine st pal myNick nicks macros focused ('/':xs) =   char defAttr '/' <> string attr cmd <> continue rest   where     specAttr spec =-      case parseArguments spec rest of+      case parse st spec rest of         Nothing -> view palCommand      pal         Just{}  -> view palCommandReady pal @@ -133,19 +157,19 @@       = case recognize (Text.pack cmd) allCommands of           Exact (Right Command{cmdArgumentSpec = spec}) ->             ( specAttr spec-            , argumentsImage pal spec+            , render pal st focused spec             )           Exact (Left (MacroSpec spec)) ->             ( specAttr spec-            , argumentsImage pal spec+            , render pal st focused spec             )           Prefix _ ->             ( view palCommandPrefix pal-            , renderOtherLine+            , parseIrcTextWithNicks pal myNick nicks focused . Text.pack             )           Invalid ->             ( view palCommandError pal-            , renderOtherLine+            , parseIrcTextWithNicks pal myNick nicks focused . Text.pack             ) -renderLine _ _ xs = parseIrcTextExplicit (Text.pack xs)+renderLine _ pal myNick nicks _ focused xs = parseIrcTextWithNicks pal myNick nicks focused (Text.pack xs)
src/Client/Log.hs view
@@ -11,7 +11,6 @@ -} module Client.Log where -import           Client.Configuration import           Client.Image.Message (cleanText) import           Client.Message import           Control.Exception@@ -43,12 +42,11 @@   LogLine  {- ^ log line -} ->   IO () writeLogLine ll = ignoreProblems $-  do dir' <- resolveConfigurationPath-             (logBaseDir ll </> Text.unpack (logTarget ll))-     let file = dir' </> formatTime defaultTimeLocale "%F" (logDay ll) <.> "log"+  do let dir = logBaseDir ll </> Text.unpack (logTarget ll)+     let file = dir </> formatTime defaultTimeLocale "%F" (logDay ll) <.> "log"       let recursiveFlag = True-     createDirectoryIfMissing recursiveFlag dir'+     createDirectoryIfMissing recursiveFlag dir      L.appendFile file (logLine ll)  
src/Client/Network/Async.hs view
@@ -43,6 +43,7 @@ import           Control.Lens import           Control.Monad import           Data.ByteString (ByteString)+import qualified Data.ByteString as B import           Data.Foldable import           Data.Time import           Irc.RateLimit@@ -176,7 +177,8 @@ receiveLoop network h inQueue =   do mb <- recvLine h (2*ircMaxMessageLength)      for_ mb $ \msg ->-       do now <- getZonedTime-          atomically $ writeTQueue inQueue-                     $ NetworkLine network now msg+       do unless (B.null msg) $ -- RFC says to ignore empty messages+            do now <- getZonedTime+               atomically $ writeTQueue inQueue+                          $ NetworkLine network now msg           receiveLoop network h inQueue
src/Client/Network/Connect.hs view
@@ -18,7 +18,6 @@   ( withConnection   ) where -import           Client.Configuration import           Client.Configuration.ServerSettings import           Control.Applicative import           Control.Exception  (bracket)@@ -26,34 +25,31 @@ import           Network.Socket     (PortNumber) import           Hookup -buildConnectionParams :: ServerSettings -> IO ConnectionParams+buildConnectionParams :: ServerSettings -> ConnectionParams buildConnectionParams args =-  do let resPath = traverse resolveConfigurationPath--     tlsParams <- TlsParams-        <$> resPath (view ssTlsClientCert args)-        <*> resPath (view ssTlsClientKey  args <|>-                     view ssTlsClientCert args)-        <*> resPath (view ssTlsServerCert args)-        <*> pure    (view ssTlsCiphers    args)+  let tlsParams = TlsParams+                    (view ssTlsClientCert args)+                    (view ssTlsClientKey  args <|> view ssTlsClientCert args)+                    (view ssTlsServerCert args)+                    (view ssTlsCiphers    args) -     let useSecure =-           case view ssTls args of-             UseInsecure    -> Nothing-             UseInsecureTls -> Just (tlsParams True)-             UseTls         -> Just (tlsParams False)+      useSecure =+        case view ssTls args of+          UseInsecure    -> Nothing+          UseInsecureTls -> Just (tlsParams True)+          UseTls         -> Just (tlsParams False) -         proxySettings = view ssSocksHost args <&> \host ->-                           SocksParams-                             host-                             (view ssSocksPort args)+      proxySettings = view ssSocksHost args <&> \host ->+                        SocksParams+                          host+                          (view ssSocksPort args) -     return ConnectionParams-       { cpHost  = view ssHostName args-       , cpPort  = ircPort args-       , cpTls   = useSecure-       , cpSocks = proxySettings-       }+  in ConnectionParams+    { cpHost  = view ssHostName args+    , cpPort  = ircPort args+    , cpTls   = useSecure+    , cpSocks = proxySettings+    }   ircPort :: ServerSettings -> PortNumber@@ -70,5 +66,4 @@ -- finishes. withConnection :: ServerSettings -> (Connection -> IO a) -> IO a withConnection settings k =-  do params <- buildConnectionParams settings-     bracket (connect params) close k+  bracket (connect (buildConnectionParams settings)) close k
src/Client/State.hs view
@@ -31,7 +31,6 @@   , clientDetailView   , clientActivityBar   , clientSubfocus-  , clientNextConnectionId   , clientNetworkMap   , clientIgnores   , clientConnection@@ -43,6 +42,7 @@   , clientErrorMsg   , clientLayout   , clientRtsStats+  , clientConfigPath    -- * Client operations   , withClientState@@ -53,6 +53,8 @@   , clientMatcher'   , clientActiveRegex   , clientToggleHideMeta+  , clientHighlightsNetwork+  , channelUserList    , consumeInput   , currentCompletionList@@ -129,6 +131,7 @@ import           Data.HashSet (HashSet) import qualified Data.HashSet as HashSet import           Data.IntMap (IntMap)+import qualified Data.IntMap as IntMap import           Data.List import           Data.Maybe import           Data.Map (Map)@@ -159,11 +162,11 @@   , _clientExtraFocus        :: ![Focus]            -- ^ extra messages windows to view    , _clientConnections       :: !(IntMap NetworkState) -- ^ state of active connections-  , _clientNextConnectionId  :: !NetworkId              -- ^ next available 'NetworkId'   , _clientEvents            :: !(TQueue NetworkEvent)    -- ^ incoming network event queue   , _clientNetworkMap        :: !(HashMap Text NetworkId) -- ^ network name to connection ID    , _clientConfig            :: !Configuration            -- ^ client configuration+  , _clientConfigPath        :: !(Maybe FilePath)         -- ^ client configuration file path    , _clientTextBox           :: !Edit.EditBox             -- ^ primary text box   , _clientTextBoxOffset     :: !Int                      -- ^ size to crop from left of text box@@ -232,8 +235,8 @@ clientLine = views (clientTextBox . Edit.line) (\(Edit.Line n t) -> (n, t))  -- | Construct an initial 'ClientState' using default values.-withClientState :: Configuration -> (ClientState -> IO a) -> IO a-withClientState cfg k =+withClientState :: Maybe FilePath -> Configuration -> (ClientState -> IO a) -> IO a+withClientState cfgPath cfg k =    withExtensionState $ \exts -> @@ -254,12 +257,12 @@         , _clientSubfocus          = FocusMessages         , _clientExtraFocus        = []         , _clientConfig            = cfg+        , _clientConfigPath        = cfgPath         , _clientScroll            = 0         , _clientDetailView        = False         , _clientRegex             = Nothing         , _clientLayout            = view configLayout cfg         , _clientActivityBar       = view configActivityBar cfg-        , _clientNextConnectionId  = 0         , _clientBell              = False         , _clientExtensions        = exts         , _clientLogQueue          = []@@ -285,9 +288,12 @@ abortNetwork network st =   case preview (clientConnection network) st of     Nothing -> return st-    Just cs -> do abortConnection ForcedDisconnect (view csSocket cs)+    Just cs -> do -- cancel the network thread+                  abortConnection ForcedDisconnect (view csSocket cs)+                  -- unassociate this network name from this network id                   return $! over clientNetworkMap (sans network) st + -- | Add a message to the window associated with a given channel recordChannelMessage ::   Text       {- ^ network -} ->@@ -418,7 +424,7 @@   ClientMessage ->   ClientState -> ClientState recordIrcMessage network target msg st =-  updateTransientError msg $+  updateTransientError (NetworkFocus network) msg $   case target of     TargetHidden      -> st     TargetNetwork     -> recordNetworkMessage msg st@@ -463,11 +469,15 @@   -- | Detect /error/ messages and add the message text to the transient--- error display.-updateTransientError :: ClientMessage -> ClientState -> ClientState-updateTransientError msg =+-- error display. The transient message will not be generated if the+-- user is focused on the window where the message is going to be+-- rendered, anyway.+updateTransientError :: Focus -> ClientMessage -> ClientState -> ClientState+updateTransientError destination msg st+  | view clientFocus st == destination = st+  | otherwise = -  let err = set clientErrorMsg . Just in+  let err e = set clientErrorMsg (Just e) st in    case view msgBody msg of     ErrorBody txt       -> err txt@@ -476,12 +486,12 @@       | let info = replyCodeInfo code       , ErrorReply <- replyCodeType info ->           err (Text.intercalate " " (replyCodeText info : drop 1 args))-    _ -> id+    _ -> st   -- | Record a message on a network window recordNetworkMessage :: ClientMessage -> ClientState -> ClientState-recordNetworkMessage msg st = updateTransientError msg+recordNetworkMessage msg st = updateTransientError focus msg                             $ recordWindowLine focus wl st   where     network    = view msgNetwork msg@@ -696,21 +706,31 @@          settings = fromMaybe defSettings                   $ preview (clientConfig . configServers . ix network) st -     let (i,st') = st & clientNextConnectionId <+~ 1+         i = nextAvailableKey (view clientConnections st)+          -- don't bother delaying on the first reconnect          delay = 15 * max 0 (attempts - 1)+      c <- createConnection             delay             i             settings-            (view clientEvents st')+            (view clientEvents st)       let cs = newNetworkState i network settings c (PingConnecting attempts lastTime)      traverse_ (sendMsg cs) (initialMessages cs)       return $ set (clientNetworkMap . at network) (Just i)-            $ set (clientConnections . at i) (Just cs) st'+            $ set (clientConnections . at i) (Just cs) st +-- | Find the first unused key in the intmap starting at 0.+nextAvailableKey :: IntMap a -> Int+nextAvailableKey m = foldr aux id (IntMap.keys m) 0+  where+    aux k next i+      | k == i    = next (i+1)+      | otherwise = i+ applyMessageToClientState ::   ZonedTime                  {- ^ timestamp                -} ->   IrcMsg                     {- ^ message received         -} ->@@ -776,7 +796,7 @@   do let cfg = view clientConfig st      st1        <- clientStopExtensions st      (st2, res) <- clientPark st1 $ \ptr ->-            traverse (try . activateExtension ptr <=< resolveConfigurationPath)+            traverse (try . activateExtension ptr)                      (view configExtensions cfg)       let (errors, exts) = partitionEithers res
src/Client/State/Network.hs view
@@ -161,6 +161,9 @@ makePrisms ''Transaction makePrisms ''PingStatus +defaultChannelTypes :: String+defaultChannelTypes = "#&"+ csNick :: Lens' NetworkState Identifier csNick = csUserInfo . uiNick @@ -223,7 +226,7 @@   , _csUserInfo     = UserInfo "*" "" ""   , _csChannels     = HashMap.empty   , _csSocket       = sock-  , _csChannelTypes = "#&"+  , _csChannelTypes = defaultChannelTypes   , _csModeTypes    = defaultModeTypes   , _csTransaction  = NoTransaction   , _csModes        = ""
src/Client/View.hs view
@@ -47,7 +47,7 @@     (_, FocusPalette) -> paletteViewLines pal     (_, FocusDigraphs) -> digraphLines w st     (_, FocusKeyMap) -> keyMapLines st-    (_, FocusHelp mb) -> helpImageLines mb pal+    (_, FocusHelp mb) -> helpImageLines st mb pal     (_, FocusRtsStats) -> rtsStatsLines (view clientRtsStats st) pal     _ -> chatMessageImages focus w st   where
src/Client/View/Digraphs.hs view
@@ -17,6 +17,7 @@ import           Data.List import           Data.List.Split import qualified Data.Text as Text+import           Data.Text (Text) import           Digraphs import           Graphics.Vty.Attributes import           Graphics.Vty.Image (wcwidth, wcswidth)@@ -32,7 +33,7 @@   $ map (text' defAttr)   $ matcher   $ map (Text.pack . drawEntry)-  $ digraphListToList digraphs+  $ Text.chunksOf 3 digraphs   where     matcher        = maybe id filter (clientMatcher' st)     entriesPerLine = max 1 -- just in case?@@ -47,9 +48,10 @@ sep :: Image' sep = text' defAttr "   " -drawEntry :: (Char,Char,Char) -> String-drawEntry (x,y,z) = output ++ replicate (entryWidth - wcswidth output) ' '+drawEntry :: Text {- ^ 3-character entry -} -> String+drawEntry entry = output ++ replicate (entryWidth - wcswidth output) ' '   where+    [x,y,z] = Text.unpack entry     output = x:y:z2     dottedCircle = '\x25cc'     z1 = cleanChar z
src/Client/View/Help.hs view
@@ -14,13 +14,14 @@   ( helpImageLines   ) where +import           Client.State (ClientState) import           Client.Commands-import           Client.Commands.Arguments-import           Client.Image.Arguments+import           Client.Commands.Arguments.Spec+import           Client.Commands.Arguments.Renderer+import           Client.Commands.Recognizer import           Client.Image.MircFormatting import           Client.Image.PackedImage import           Client.Image.Palette-import           Client.Commands.Recognizer import           Control.Lens import           Data.Foldable (toList) import           Data.List (delete, intercalate)@@ -34,20 +35,22 @@ -- or when given a command name generate the detailed help text -- for that command. helpImageLines ::-  Maybe Text {- ^ optional command name -} ->-  Palette    {- ^ palette               -} ->-  [Image']   {- ^ help lines            -}-helpImageLines mbCmd pal =+  ClientState {- ^ client state          -} ->+  Maybe Text  {- ^ optional command name -} ->+  Palette     {- ^ palette               -} ->+  [Image']    {- ^ help lines            -}+helpImageLines st mbCmd pal =   case mbCmd of-    Nothing  -> listAllCommands pal-    Just cmd -> commandHelpLines cmd pal+    Nothing  -> listAllCommands st pal+    Just cmd -> commandHelpLines st cmd pal  -- | Generate detailed help lines for the command with the given name. commandHelpLines ::-  Text     {- ^ command name -} ->-  Palette  {- ^ palette      -} ->-  [Image'] {- ^ lines        -}-commandHelpLines cmdName pal =+  ClientState {- ^ client state -} ->+  Text        {- ^ command name -} ->+  Palette     {- ^ palette      -} ->+  [Image']    {- ^ lines        -}+commandHelpLines st cmdName pal =   case recognize cmdName commands of     Invalid -> [string (view palError pal) "Unknown command, try /help"]     Prefix sfxs ->@@ -56,7 +59,7 @@       suggestions = Text.unpack $ Text.intercalate " " ((cmdName <>) <$> sfxs)     Exact Command{cmdNames = names, cmdImplementation = impl,                   cmdArgumentSpec = spec, cmdDocumentation = doc} ->-      reverse $ heading "Syntax: " <> commandSummary pal (pure cmdName) spec+      reverse $ heading "Syntax: " <> commandSummary st pal (pure cmdName) spec               : emptyLine               : aliasLines              ++ explainContext impl@@ -89,20 +92,22 @@  -- | Generate the lines for the help window showing all commands. listAllCommands ::-  Palette  {- ^ palette    -} ->-  [Image'] {- ^ help lines -}-listAllCommands pal+  ClientState {- ^ client state    -} ->+  Palette     {- ^ palette         -} ->+  [Image']    {- ^ help lines      -}+listAllCommands st pal   = intercalate [emptyLine]   $ map reverse-  $ listCommandSection pal <$> commandsList+  $ listCommandSection st pal <$> commandsList  listCommandSection ::+  ClientState    {- ^ client state    -} ->   Palette        {- ^ palette         -} ->   CommandSection {- ^ command section -} ->   [Image']       {- ^ help lines      -}-listCommandSection pal sec+listCommandSection st pal sec   = text' (withStyle defAttr bold) (cmdSectionName sec)-  : [ commandSummary pal names spec+  : [ commandSummary st pal names spec     | -- pattern needed due to existential quantification       Command { cmdNames        = names               , cmdArgumentSpec = spec@@ -112,14 +117,15 @@ -- | Generate the help line for the given command and its -- specification for use in the list of commands. commandSummary ::-  Palette        {- ^ palette                  -} ->-  NonEmpty Text  {- ^ command name and aliases -} ->-  ArgumentSpec a {- ^ argument specification   -} ->-  Image'         {- ^ summary help line        -}-commandSummary pal (cmd :| _) args  =+  r                {- ^ client state             -} ->+  Palette          {- ^ palette                  -} ->+  NonEmpty Text    {- ^ command name and aliases -} ->+  Args r a         {- ^ argument specification   -} ->+  Image'           {- ^ summary help line        -}+commandSummary st pal (cmd :| _) args  =   char defAttr '/' <>   text' (view palCommandReady pal) cmd <>-  argumentsImage pal' args ""+  render pal' st True args ""    where     pal' = set palCommandPlaceholder defAttr pal
src/Client/View/Mentions.hs view
@@ -14,14 +14,16 @@   ( mentionsViewLines   ) where +import           Client.Configuration (PaddingMode, configNickPadding)+import           Client.Image.Message import           Client.Image.PackedImage+import           Client.Image.Palette (Palette) import           Client.Image.StatusLine import           Client.State import           Client.State.Focus import           Client.State.Window-import qualified Data.Map as Map import           Control.Lens-import           Data.Semigroup+import qualified Data.Map as Map import           Data.Time (UTCTime)  -- | Generate the list of message lines marked important ordered by@@ -35,18 +37,22 @@      detail = view clientDetailView st +    padAmt = view (clientConfig . configNickPadding) st+    palette = clientPalette st+     entries = merge-              [windowEntries detail n focus v+              [windowEntries palette w padAmt detail n focus v               | (n,(focus, v))                 <- names `zip` Map.toList (view clientWindows st) ]  data MentionLine = MentionLine-  { mlTimestamp  :: UTCTime-  , mlWindowName :: Char-  , mlFocus      :: Focus-  , mlImage      :: Image'+  { mlTimestamp  :: UTCTime  -- ^ message timestamp for sorting+  , mlWindowName :: Char     -- ^ window names shortcut+  , mlFocus      :: Focus    -- ^ associated window+  , mlImage      :: [Image'] -- ^ wrapped rendered lines   } +-- | Insert channel name markers between messages from different channels addMarkers ::   Int           {- ^ draw width                        -} ->   ClientState   {- ^ client state                      -} ->@@ -54,7 +60,7 @@   [Image']      {- ^ mention images and channel labels -} addMarkers _ _ [] = [] addMarkers w !st (!ml : xs)-  = map mlImage (ml:same)+  = concatMap mlImage (ml:same)  ++ minorStatusLineImage (mlFocus ml) w False st   : addMarkers w st rest   where@@ -63,24 +69,25 @@     (same,rest) = span isSame xs  windowEntries ::-  Bool   {- ^ detailed view -} ->-  Char   {- ^ window name   -} ->-  Focus  {- ^ window focus  -} ->-  Window {- ^ window        -} ->+  Palette     {- ^ palette       -} ->+  Int         {- ^ draw columns  -} ->+  PaddingMode {- ^ nick padding  -} ->+  Bool        {- ^ detailed view -} ->+  Char        {- ^ window name   -} ->+  Focus       {- ^ window focus  -} ->+  Window      {- ^ window        -} ->   [MentionLine]-windowEntries !detailed name focus w =+windowEntries palette w padAmt detailed name focus win =   [ MentionLine       { mlTimestamp  = views wlTimestamp unpackUTCTime l       , mlWindowName = name       , mlFocus      = focus       , mlImage      = if detailed-                        then view wlFullImage l-                        else view wlPrefix l <>-                             char mempty ' ' <>-                             view wlImage l+                        then [view wlFullImage l]+                        else drawWindowLine palette w padAmt l       }   | let p x = WLImportant == view wlImportance x-  , l <- toListOf (winMessages . each . filtered p) w+  , l <- toListOf (winMessages . each . filtered p) win   ]  -- | Merge a list of sorted lists of mention lines into a single sorted list
src/Client/View/Messages.hs view
@@ -87,7 +87,7 @@ windowLinesToImages st w hideMeta wwls =   case gatherMetadataLines st wwls of     ([], [])   -> []-    ([], wl:wls) -> drawLine wl+    ([], wl:wls) -> drawWindowLine palette w padAmt wl                 ++ windowLinesToImages st w hideMeta wls     ((img,who,mbnext):mds, wls) @@ -106,16 +106,10 @@     config  = view clientConfig st     padAmt  = view configNickPadding config -    drawTime = timeImage palette . unpackTimeOfDay     padNick  = nickPad padAmt     metaPad  = "      " <> padNick "" -    drawPrefix = views wlTimestamp drawTime <>-                 views wlPrefix    padNick-     wrap pfx body = reverse (lineWrapPrefix w pfx body)--    drawLine wl = wrap (drawPrefix wl) (view wlImage wl)  ------------------------------------------------------------------------ 
src/Client/View/Palette.hs view
@@ -1,3 +1,4 @@+{-# Language OverloadedStrings #-} {-| Module      : Client.View.Palette Description : View current palette and to see all terminal colors@@ -15,6 +16,7 @@   ) where  import           Client.Image.Palette+import           Client.Image.MircFormatting import           Client.Image.PackedImage import           Control.Lens import           Data.List@@ -26,6 +28,11 @@ digitImage :: Char -> Image' digitImage d = string defAttr [' ',d,' '] +decimalImage :: Int -> Image'+decimalImage n+  | n < 10    = string defAttr (' ':'0':show n)+  | otherwise = string defAttr (    ' ':show n)+ columns :: [Image'] -> Image' columns = mconcat . intersperse (char defAttr ' ') @@ -33,30 +40,64 @@ -- all the colors used in the current palette as well as -- the colors available for use in palettes. paletteViewLines :: Palette -> [Image']-paletteViewLines pal =+paletteViewLines pal = reverse $++  [ "Current client palette"+  , ""+  , columns (paletteEntries pal)+  , ""++  , "Current client palette nick highlight colors"+  , ""+  , columns (nickHighlights pal)+  , ""++  , "Chat formatting colors: ^C[foreground[,background]]"+  , ""+  , columns ("   " : map decimalImage [0..15])+  , columns mircColors+  , ""++  , "Available palette colors: 0x<row><col>"+  , ""+  , columns (map digitImage (' ':digits))+  , columns isoColors ]++  +++   [ columns   $ digitImage digit   : [ string (withBackColor defAttr c) "   "     | col <- [0 .. 15]     , let c = Color240 (row * 16 + col)     ]-  | (digit,row) <- reverse $ take 15 $ zip (drop 1 digits) [0 ..]-  ] ++-  [ columns-  $ digitImage '0'+  | (digit,row) <- zip (drop 1 digits) [0 ..]+  ]+++isoColors :: [Image']+isoColors =+  digitImage '0'   : [ string (withBackColor defAttr (ISOColor c)) "   "     | c <- [0..15]     ] -  , columns (map digitImage (' ':digits))-  , mempty-  , columns-    [ text' (view l pal) name-    | (name, Lens l) <- paletteMap-    ]-  , mempty-  , columns-    [ string attr "nicks"-    | attr <- toListOf (palNicks . folded) pal+mircColors :: [Image']+mircColors =+  "   "+  : [ string (withBackColor defAttr c) "   "+    | i <- [0..15]+    , let Just c = mircColor i     ]++paletteEntries :: Palette -> [Image']+paletteEntries pal =+  [ text' (view l pal) name+  | (name, Lens l) <- paletteMap+  ]++nickHighlights :: Palette -> [Image']+nickHighlights pal =+  [ string attr "nicks"+  | attr <- toListOf (palNicks . folded) pal   ]
+ src/DigraphQuote.hs view
@@ -0,0 +1,53 @@+{-|+Module      : DigraphQuote+Description : Template Haskell quasi-quoter for digraph table+Copyright   : (c) Eric Mertens, 2017+License     : ISC+Maintainer  : emertens@gmail.com++-}+module DigraphQuote (digraphTable) where++import Data.Char+import Control.Monad+import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Numeric (readHex)++digraphTable :: QuasiQuoter+digraphTable = QuasiQuoter+  { quoteExp  = digraphTableExp+  , quotePat  = const (fail "Digraph table must be an expression")+  , quoteType = const (fail "Digraph table must be an expression")+  , quoteDec  = const (fail "Digraph table must be an expression")+  }++digraphTableExp :: String -> ExpQ+digraphTableExp = stringE . concat <=< traverse parseEntry . lines++-- Parse entries, empty lines are ignored, -- comments are allowed+-- Entries are a two-character digraph followed by a hexadecimal+-- representation of the replacement character's unicode value.+--+-- Examples+--+-- > "'   14+-- > AB 0123+-- > CD 0FDE -- with a comment+-- >+-- > -- with a comment+parseEntry :: String -> Q String+parseEntry line =+  case words line of+    [x,y] : ('U':'+':hex) : rest+       | [(n,"")] <- readHex hex+       , isAllowedTerminator rest   -> pure [x,y,chr n]++    rest | isAllowedTerminator rest -> pure "" -- skip empty lines+         | otherwise                -> fail ("Bad digraph entry: " ++ line)++-- Optionally tolerate a comment+isAllowedTerminator :: [String] -> Bool+isAllowedTerminator (('-':'-':_):_) = True+isAllowedTerminator []              = True+isAllowedTerminator _               = False
src/Digraphs.hs view
@@ -1,1359 +1,1356 @@-{-# Language OverloadedStrings #-}-{-|-Module      : Digraphs-Description : Character mnemonics-Copyright   : (c) Eric Mertens, 2016-License     : ISC-Maintainer  : emertens@gmail.com--This module provides an implementation of /digraphs/ as implemented-in Vim and as specified in RFC 1345 (2-character only).--<https://tools.ietf.org/html/rfc1345>-<http://vimdoc.sourceforge.net/htmldoc/digraph.html>---}-module Digraphs-  ( lookupDigraph-  , digraphs-  , digraphListToList-  ) where--import           Data.IntMap (IntMap)-import qualified Data.IntMap as IntMap-import           Data.Char----- | Look up 2-character digraph.-lookupDigraph :: Char -> Char -> Maybe Char-lookupDigraph x y-  | isAscii x, isAscii y = digraphToInt x y `IntMap.lookup` digraphMap-  | otherwise            = Nothing---digraphMap :: IntMap Char-digraphMap = IntMap.fromList-               [ (digraphToInt x y, z) | (x,y,z) <- digraphListToList digraphs ]----- | Injective mapping from 2-character ASCII digraph to 'Int'-digraphToInt :: Char -> Char -> Int-digraphToInt x y = ord x * 127 + ord y----------------------------------------------------------------------------digraphListToList :: DigraphList -> [(Char,Char,Char)]-digraphListToList (Entry x y z xs) = (x,y,z) : digraphListToList xs-digraphListToList Nil              = []----- | Packed list of digraph entries-data DigraphList-  = Entry {-# UNPACK #-}!Char {-# UNPACK #-}!Char {-# UNPACK #-}!Char DigraphList-  | Nil---digraphs :: DigraphList-digraphs-  = Entry 'N' 'U' '\x00'      -- NULL (NUL)-  $ Entry 'S' 'H' '\x01'      -- START OF HEADING (SOH)-  $ Entry 'S' 'X' '\x02'      -- START OF TEXT (STX)-  $ Entry 'E' 'X' '\x03'      -- END OF TEXT (ETX)-  $ Entry 'E' 'T' '\x04'      -- END OF TRANSMISSION (EOT)-  $ Entry 'E' 'Q' '\x05'      -- ENQUIRY (ENQ)-  $ Entry 'A' 'K' '\x06'      -- ACKNOWLEDGE (ACK)-  $ Entry 'B' 'L' '\x07'      -- BELL (BEL)-  $ Entry 'B' 'S' '\x08'      -- BACKSPACE (BS)-  $ Entry 'H' 'T' '\x09'      -- CHARACTER TABULATION (HT)---  $ Entry 'L' 'F' '\x0a'    -- LINE FEED (LF)-  $ Entry 'V' 'T' '\x0b'      -- LINE TABULATION (VT)-  $ Entry 'F' 'F' '\x0c'      -- FORM FEED (FF)---  $ Entry 'C' 'R' '\x0d'    -- CARRIAGE RETURN (CR)-  $ Entry 'S' 'O' '\x0e'      -- SHIFT OUT (SO)-  $ Entry 'S' 'I' '\x0f'      -- SHIFT IN (SI)-  $ Entry 'D' 'L' '\x10'      -- DATALINK ESCAPE (DLE)-  $ Entry 'D' '1' '\x11'      -- DEVICE CONTROL ONE (DC1)-  $ Entry 'D' '2' '\x12'      -- DEVICE CONTROL TWO (DC2)-  $ Entry 'D' '3' '\x13'      -- DEVICE CONTROL THREE (DC3)-  $ Entry 'D' '4' '\x14'      -- DEVICE CONTROL FOUR (DC4)-  $ Entry 'N' 'K' '\x15'      -- NEGATIVE ACKNOWLEDGE (NAK)-  $ Entry 'S' 'Y' '\x16'      -- SYNCHRONOUS IDLE (SYN)-  $ Entry 'E' 'B' '\x17'      -- END OF TRANSMISSION BLOCK (ETB)-  $ Entry 'C' 'N' '\x18'      -- CANCEL (CAN)-  $ Entry 'E' 'M' '\x19'      -- END OF MEDIUM (EM)-  $ Entry 'S' 'B' '\x1a'      -- SUBSTITUTE (SUB)-  $ Entry 'E' 'C' '\x1b'      -- ESCAPE (ESC)-  $ Entry 'F' 'S' '\x1c'      -- FILE SEPARATOR (IS4)-  $ Entry 'G' 'S' '\x1d'      -- GROUP SEPARATOR (IS3)-  $ Entry 'R' 'S' '\x1e'      -- RECORD SEPARATOR (IS2)-  $ Entry 'U' 'S' '\x1f'      -- UNIT SEPARATOR (IS1)-  $ Entry 'S' 'P' '\x20'      -- SPACE-  $ Entry 'N' 'b' '\x23'      -- NUMBER SIGN-  $ Entry 'D' 'O' '\x24'      -- DOLLAR SIGN-  $ Entry 'A' 't' '\x40'      -- COMMERCIAL AT-  $ Entry '<' '(' '\x5b'      -- LEFT SQUARE BRACKET-  $ Entry '/' '/' '\x5c'      -- REVERSE SOLIDUS-  $ Entry ')' '>' '\x5d'      -- RIGHT SQUARE BRACKET-  $ Entry '\'' '>' '\x5e'      -- CIRCUMFLEX ACCENT-  $ Entry '\'' '!' '\x60'      -- GRAVE ACCENT-  $ Entry '(' '!' '\x7b'      -- LEFT CURLY BRACKET-  $ Entry '!' '!' '\x7c'      -- VERTICAL LINE-  $ Entry '!' ')' '\x7d'      -- RIGHT CURLY BRACKET-  $ Entry '\'' '?' '\x7e'      -- TILDE-  $ Entry 'D' 'T' '\x7f'      -- DELETE (DEL)-  $ Entry 'P' 'A' '\x80'      -- PADDING CHARACTER (PAD)-  $ Entry 'H' 'O' '\x81'      -- HIGH OCTET PRESET (HOP)-  $ Entry 'B' 'H' '\x82'      -- BREAK PERMITTED HERE (BPH)-  $ Entry 'N' 'H' '\x83'      -- NO BREAK HERE (NBH)-  $ Entry 'I' 'N' '\x84'      -- INDEX (IND)-  $ Entry 'N' 'L' '\x85'      -- NEXT LINE (NEL)-  $ Entry 'S' 'A' '\x86'      -- START OF SELECTED AREA (SSA)-  $ Entry 'E' 'S' '\x87'      -- END OF SELECTED AREA (ESA)-  $ Entry 'H' 'S' '\x88'      -- CHARACTER TABULATION SET (HTS)-  $ Entry 'H' 'J' '\x89'      -- CHARACTER TABULATION WITH JUSTIFICATION (HTJ)-  $ Entry 'V' 'S' '\x8a'      -- LINE TABULATION SET (VTS)-  $ Entry 'P' 'D' '\x8b'      -- PARTIAL LINE FORWARD (PLD)-  $ Entry 'P' 'U' '\x8c'      -- PARTIAL LINE BACKWARD (PLU)-  $ Entry 'R' 'I' '\x8d'      -- REVERSE LINE FEED (RI)-  $ Entry 'S' '2' '\x8e'      -- SINGLE-SHIFT TWO (SS2)-  $ Entry 'S' '3' '\x8f'      -- SINGLE-SHIFT THREE (SS3)-  $ Entry 'D' 'C' '\x90'      -- DEVICE CONTROL STRING (DCS)-  $ Entry 'P' '1' '\x91'      -- PRIVATE USE ONE (PU1)-  $ Entry 'P' '2' '\x92'      -- PRIVATE USE TWO (PU2)-  $ Entry 'T' 'S' '\x93'      -- SET TRANSMIT STATE (STS)-  $ Entry 'C' 'C' '\x94'      -- CANCEL CHARACTER (CCH)-  $ Entry 'M' 'W' '\x95'      -- MESSAGE WAITING (MW)-  $ Entry 'S' 'G' '\x96'      -- START OF GUARDED AREA (SPA)-  $ Entry 'E' 'G' '\x97'      -- END OF GUARDED AREA (EPA)-  $ Entry 'S' 'S' '\x98'      -- START OF STRING (SOS)-  $ Entry 'G' 'C' '\x99'      -- SINGLE GRAPHIC CHARACTER INTRODUCER (SGCI)-  $ Entry 'S' 'C' '\x9a'      -- SINGLE CHARACTER INTRODUCER (SCI)-  $ Entry 'C' 'I' '\x9b'      -- CONTROL SEQUENCE INTRODUCER (CSI)-  $ Entry 'S' 'T' '\x9c'      -- STRING TERMINATOR (ST)-  $ Entry 'O' 'C' '\x9d'      -- OPERATING SYSTEM COMMAND (OSC)-  $ Entry 'P' 'M' '\x9e'      -- PRIVACY MESSAGE (PM)-  $ Entry 'A' 'C' '\x9f'      -- APPLICATION PROGRAM COMMAND (APC)-  $ Entry 'N' 'S' '\xa0'      -- NO-BREAK SPACE-  $ Entry '!' 'I' '\xa1'      -- INVERTED EXCLAMATION MARK-  $ Entry 'C' 't' '\xa2'      -- CENT SIGN-  $ Entry 'P' 'd' '\xa3'      -- POUND SIGN-  $ Entry 'C' 'u' '\xa4'      -- CURRENCY SIGN-  $ Entry 'Y' 'e' '\xa5'      -- YEN SIGN-  $ Entry 'B' 'B' '\xa6'      -- BROKEN BAR-  $ Entry 'S' 'E' '\xa7'      -- SECTION SIGN-  $ Entry '\'' ':' '\xa8'      -- DIAERESIS-  $ Entry 'C' 'o' '\xa9'      -- COPYRIGHT SIGN-  $ Entry '-' 'a' '\xaa'      -- FEMININE ORDINAL INDICATOR-  $ Entry '<' '<' '\xab'      -- LEFT-POINTING DOUBLE ANGLE QUOTATION MARK-  $ Entry 'N' 'O' '\xac'      -- NOT SIGN-  $ Entry '-' '-' '\xad'      -- SOFT HYPHEN-  $ Entry 'R' 'g' '\xae'      -- REGISTERED SIGN-  $ Entry '\'' 'm' '\xaf'      -- MACRON-  $ Entry 'D' 'G' '\xb0'      -- DEGREE SIGN-  $ Entry '+' '-' '\xb1'      -- PLUS-MINUS SIGN-  $ Entry '2' 'S' '\xb2'      -- SUPERSCRIPT TWO-  $ Entry '3' 'S' '\xb3'      -- SUPERSCRIPT THREE-  $ Entry '\'' '\'' '\xb4'      -- ACUTE ACCENT-  $ Entry 'M' 'y' '\xb5'      -- MICRO SIGN-  $ Entry 'P' 'I' '\xb6'      -- PILCROW SIGN-  $ Entry '.' 'M' '\xb7'      -- MIDDLE DOT-  $ Entry '\'' ',' '\xb8'      -- CEDILLA-  $ Entry '1' 'S' '\xb9'      -- SUPERSCRIPT ONE-  $ Entry '-' 'o' '\xba'      -- MASCULINE ORDINAL INDICATOR-  $ Entry '>' '>' '\xbb'      -- RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK-  $ Entry '1' '4' '\xbc'      -- VULGAR FRACTION ONE QUARTER-  $ Entry '1' '2' '\xbd'      -- VULGAR FRACTION ONE HALF-  $ Entry '3' '4' '\xbe'      -- VULGAR FRACTION THREE QUARTERS-  $ Entry '?' 'I' '\xbf'      -- INVERTED QUESTION MARK-  $ Entry 'A' '!' '\xc0'      -- LATIN CAPITAL LETTER A WITH GRAVE-  $ Entry 'A' '\'' '\xc1'      -- LATIN CAPITAL LETTER A WITH ACUTE-  $ Entry 'A' '>' '\xc2'      -- LATIN CAPITAL LETTER A WITH CIRCUMFLEX-  $ Entry 'A' '?' '\xc3'      -- LATIN CAPITAL LETTER A WITH TILDE-  $ Entry 'A' ':' '\xc4'      -- LATIN CAPITAL LETTER A WITH DIAERESIS-  $ Entry 'A' 'A' '\xc5'      -- LATIN CAPITAL LETTER A WITH RING ABOVE-  $ Entry 'A' 'E' '\xc6'      -- LATIN CAPITAL LETTER AE-  $ Entry 'C' ',' '\xc7'      -- LATIN CAPITAL LETTER C WITH CEDILLA-  $ Entry 'E' '!' '\xc8'      -- LATIN CAPITAL LETTER E WITH GRAVE-  $ Entry 'E' '\'' '\xc9'      -- LATIN CAPITAL LETTER E WITH ACUTE-  $ Entry 'E' '>' '\xca'      -- LATIN CAPITAL LETTER E WITH CIRCUMFLEX-  $ Entry 'E' ':' '\xcb'      -- LATIN CAPITAL LETTER E WITH DIAERESIS-  $ Entry 'I' '!' '\xcc'      -- LATIN CAPITAL LETTER I WITH GRAVE-  $ Entry 'I' '\'' '\xcd'      -- LATIN CAPITAL LETTER I WITH ACUTE-  $ Entry 'I' '>' '\xce'      -- LATIN CAPITAL LETTER I WITH CIRCUMFLEX-  $ Entry 'I' ':' '\xcf'      -- LATIN CAPITAL LETTER I WITH DIAERESIS-  $ Entry 'D' '-' '\xd0'      -- LATIN CAPITAL LETTER ETH (Icelandic)-  $ Entry 'N' '?' '\xd1'      -- LATIN CAPITAL LETTER N WITH TILDE-  $ Entry 'O' '!' '\xd2'      -- LATIN CAPITAL LETTER O WITH GRAVE-  $ Entry 'O' '\'' '\xd3'      -- LATIN CAPITAL LETTER O WITH ACUTE-  $ Entry 'O' '>' '\xd4'      -- LATIN CAPITAL LETTER O WITH CIRCUMFLEX-  $ Entry 'O' '?' '\xd5'      -- LATIN CAPITAL LETTER O WITH TILDE-  $ Entry 'O' ':' '\xd6'      -- LATIN CAPITAL LETTER O WITH DIAERESIS-  $ Entry '*' 'X' '\xd7'      -- MULTIPLICATION SIGN-  $ Entry 'O' '/' '\xd8'      -- LATIN CAPITAL LETTER O WITH STROKE-  $ Entry 'U' '!' '\xd9'      -- LATIN CAPITAL LETTER U WITH GRAVE-  $ Entry 'U' '\'' '\xda'      -- LATIN CAPITAL LETTER U WITH ACUTE-  $ Entry 'U' '>' '\xdb'      -- LATIN CAPITAL LETTER U WITH CIRCUMFLEX-  $ Entry 'U' ':' '\xdc'      -- LATIN CAPITAL LETTER U WITH DIAERESIS-  $ Entry 'Y' '\'' '\xdd'      -- LATIN CAPITAL LETTER Y WITH ACUTE-  $ Entry 'T' 'H' '\xde'      -- LATIN CAPITAL LETTER THORN (Icelandic)-  $ Entry 's' 's' '\xdf'      -- LATIN SMALL LETTER SHARP S (German)-  $ Entry 'a' '!' '\xe0'      -- LATIN SMALL LETTER A WITH GRAVE-  $ Entry 'a' '\'' '\xe1'      -- LATIN SMALL LETTER A WITH ACUTE-  $ Entry 'a' '>' '\xe2'      -- LATIN SMALL LETTER A WITH CIRCUMFLEX-  $ Entry 'a' '?' '\xe3'      -- LATIN SMALL LETTER A WITH TILDE-  $ Entry 'a' ':' '\xe4'      -- LATIN SMALL LETTER A WITH DIAERESIS-  $ Entry 'a' 'a' '\xe5'      -- LATIN SMALL LETTER A WITH RING ABOVE-  $ Entry 'a' 'e' '\xe6'      -- LATIN SMALL LETTER AE-  $ Entry 'c' ',' '\xe7'      -- LATIN SMALL LETTER C WITH CEDILLA-  $ Entry 'e' '!' '\xe8'      -- LATIN SMALL LETTER E WITH GRAVE-  $ Entry 'e' '\'' '\xe9'      -- LATIN SMALL LETTER E WITH ACUTE-  $ Entry 'e' '>' '\xea'      -- LATIN SMALL LETTER E WITH CIRCUMFLEX-  $ Entry 'e' ':' '\xeb'      -- LATIN SMALL LETTER E WITH DIAERESIS-  $ Entry 'i' '!' '\xec'      -- LATIN SMALL LETTER I WITH GRAVE-  $ Entry 'i' '\'' '\xed'      -- LATIN SMALL LETTER I WITH ACUTE-  $ Entry 'i' '>' '\xee'      -- LATIN SMALL LETTER I WITH CIRCUMFLEX-  $ Entry 'i' ':' '\xef'      -- LATIN SMALL LETTER I WITH DIAERESIS-  $ Entry 'd' '-' '\xf0'      -- LATIN SMALL LETTER ETH (Icelandic)-  $ Entry 'n' '?' '\xf1'      -- LATIN SMALL LETTER N WITH TILDE-  $ Entry 'o' '!' '\xf2'      -- LATIN SMALL LETTER O WITH GRAVE-  $ Entry 'o' '\'' '\xf3'      -- LATIN SMALL LETTER O WITH ACUTE-  $ Entry 'o' '>' '\xf4'      -- LATIN SMALL LETTER O WITH CIRCUMFLEX-  $ Entry 'o' '?' '\xf5'      -- LATIN SMALL LETTER O WITH TILDE-  $ Entry 'o' ':' '\xf6'      -- LATIN SMALL LETTER O WITH DIAERESIS-  $ Entry '-' ':' '\xf7'      -- DIVISION SIGN-  $ Entry 'o' '/' '\xf8'      -- LATIN SMALL LETTER O WITH STROKE-  $ Entry 'u' '!' '\xf9'      -- LATIN SMALL LETTER U WITH GRAVE-  $ Entry 'u' '\'' '\xfa'      -- LATIN SMALL LETTER U WITH ACUTE-  $ Entry 'u' '>' '\xfb'      -- LATIN SMALL LETTER U WITH CIRCUMFLEX-  $ Entry 'u' ':' '\xfc'      -- LATIN SMALL LETTER U WITH DIAERESIS-  $ Entry 'y' '\'' '\xfd'      -- LATIN SMALL LETTER Y WITH ACUTE-  $ Entry 't' 'h' '\xfe'      -- LATIN SMALL LETTER THORN (Icelandic)-  $ Entry 'y' ':' '\xff'      -- LATIN SMALL LETTER Y WITH DIAERESIS-  $ Entry 'A' '-' '\x0100'    -- LATIN CAPITAL LETTER A WITH MACRON-  $ Entry 'a' '-' '\x0101'    -- LATIN SMALL LETTER A WITH MACRON-  $ Entry 'A' '(' '\x0102'    -- LATIN CAPITAL LETTER A WITH BREVE-  $ Entry 'a' '(' '\x0103'    -- LATIN SMALL LETTER A WITH BREVE-  $ Entry 'A' ';' '\x0104'    -- LATIN CAPITAL LETTER A WITH OGONEK-  $ Entry 'a' ';' '\x0105'    -- LATIN SMALL LETTER A WITH OGONEK-  $ Entry 'C' '\'' '\x0106'    -- LATIN CAPITAL LETTER C WITH ACUTE-  $ Entry 'c' '\'' '\x0107'    -- LATIN SMALL LETTER C WITH ACUTE-  $ Entry 'C' '>' '\x0108'    -- LATIN CAPITAL LETTER C WITH CIRCUMFLEX-  $ Entry 'c' '>' '\x0109'    -- LATIN SMALL LETTER C WITH CIRCUMFLEX-  $ Entry 'C' '.' '\x010A'    -- LATIN CAPITAL LETTER C WITH DOT ABOVE-  $ Entry 'c' '.' '\x010B'    -- LATIN SMALL LETTER C WITH DOT ABOVE-  $ Entry 'C' '<' '\x010C'    -- LATIN CAPITAL LETTER C WITH CARON-  $ Entry 'c' '<' '\x010D'    -- LATIN SMALL LETTER C WITH CARON-  $ Entry 'D' '<' '\x010E'    -- LATIN CAPITAL LETTER D WITH CARON-  $ Entry 'd' '<' '\x010F'    -- LATIN SMALL LETTER D WITH CARON-  $ Entry 'D' '/' '\x0110'    -- LATIN CAPITAL LETTER D WITH STROKE-  $ Entry 'd' '/' '\x0111'    -- LATIN SMALL LETTER D WITH STROKE-  $ Entry 'E' '-' '\x0112'    -- LATIN CAPITAL LETTER E WITH MACRON-  $ Entry 'e' '-' '\x0113'    -- LATIN SMALL LETTER E WITH MACRON-  $ Entry 'E' '(' '\x0114'    -- LATIN CAPITAL LETTER E WITH BREVE-  $ Entry 'e' '(' '\x0115'    -- LATIN SMALL LETTER E WITH BREVE-  $ Entry 'E' '.' '\x0116'    -- LATIN CAPITAL LETTER E WITH DOT ABOVE-  $ Entry 'e' '.' '\x0117'    -- LATIN SMALL LETTER E WITH DOT ABOVE-  $ Entry 'E' ';' '\x0118'    -- LATIN CAPITAL LETTER E WITH OGONEK-  $ Entry 'e' ';' '\x0119'    -- LATIN SMALL LETTER E WITH OGONEK-  $ Entry 'E' '<' '\x011A'    -- LATIN CAPITAL LETTER E WITH CARON-  $ Entry 'e' '<' '\x011B'    -- LATIN SMALL LETTER E WITH CARON-  $ Entry 'G' '>' '\x011C'    -- LATIN CAPITAL LETTER G WITH CIRCUMFLEX-  $ Entry 'g' '>' '\x011D'    -- LATIN SMALL LETTER G WITH CIRCUMFLEX-  $ Entry 'G' '(' '\x011E'    -- LATIN CAPITAL LETTER G WITH BREVE-  $ Entry 'g' '(' '\x011F'    -- LATIN SMALL LETTER G WITH BREVE-  $ Entry 'G' '.' '\x0120'    -- LATIN CAPITAL LETTER G WITH DOT ABOVE-  $ Entry 'g' '.' '\x0121'    -- LATIN SMALL LETTER G WITH DOT ABOVE-  $ Entry 'G' ',' '\x0122'    -- LATIN CAPITAL LETTER G WITH CEDILLA-  $ Entry 'g' ',' '\x0123'    -- LATIN SMALL LETTER G WITH CEDILLA-  $ Entry 'H' '>' '\x0124'    -- LATIN CAPITAL LETTER H WITH CIRCUMFLEX-  $ Entry 'h' '>' '\x0125'    -- LATIN SMALL LETTER H WITH CIRCUMFLEX-  $ Entry 'H' '/' '\x0126'    -- LATIN CAPITAL LETTER H WITH STROKE-  $ Entry 'h' '/' '\x0127'    -- LATIN SMALL LETTER H WITH STROKE-  $ Entry 'I' '?' '\x0128'    -- LATIN CAPITAL LETTER I WITH TILDE-  $ Entry 'i' '?' '\x0129'    -- LATIN SMALL LETTER I WITH TILDE-  $ Entry 'I' '-' '\x012A'    -- LATIN CAPITAL LETTER I WITH MACRON-  $ Entry 'i' '-' '\x012B'    -- LATIN SMALL LETTER I WITH MACRON-  $ Entry 'I' '(' '\x012C'    -- LATIN CAPITAL LETTER I WITH BREVE-  $ Entry 'i' '(' '\x012D'    -- LATIN SMALL LETTER I WITH BREVE-  $ Entry 'I' ';' '\x012E'    -- LATIN CAPITAL LETTER I WITH OGONEK-  $ Entry 'i' ';' '\x012F'    -- LATIN SMALL LETTER I WITH OGONEK-  $ Entry 'I' '.' '\x0130'    -- LATIN CAPITAL LETTER I WITH DOT ABOVE-  $ Entry 'i' '.' '\x0131'    -- LATIN SMALL LETTER DOTLESS I-  $ Entry 'I' 'J' '\x0132'    -- LATIN CAPITAL LIGATURE IJ-  $ Entry 'i' 'j' '\x0133'    -- LATIN SMALL LIGATURE IJ-  $ Entry 'J' '>' '\x0134'    -- LATIN CAPITAL LETTER J WITH CIRCUMFLEX-  $ Entry 'j' '>' '\x0135'    -- LATIN SMALL LETTER J WITH CIRCUMFLEX-  $ Entry 'K' ',' '\x0136'    -- LATIN CAPITAL LETTER K WITH CEDILLA-  $ Entry 'k' ',' '\x0137'    -- LATIN SMALL LETTER K WITH CEDILLA-  $ Entry 'k' 'k' '\x0138'    -- LATIN SMALL LETTER KRA-  $ Entry 'L' '\'' '\x0139'    -- LATIN CAPITAL LETTER L WITH ACUTE-  $ Entry 'l' '\'' '\x013A'    -- LATIN SMALL LETTER L WITH ACUTE-  $ Entry 'L' ',' '\x013B'    -- LATIN CAPITAL LETTER L WITH CEDILLA-  $ Entry 'l' ',' '\x013C'    -- LATIN SMALL LETTER L WITH CEDILLA-  $ Entry 'L' '<' '\x013D'    -- LATIN CAPITAL LETTER L WITH CARON-  $ Entry 'l' '<' '\x013E'    -- LATIN SMALL LETTER L WITH CARON-  $ Entry 'L' '.' '\x013F'    -- LATIN CAPITAL LETTER L WITH MIDDLE DOT-  $ Entry 'l' '.' '\x0140'    -- LATIN SMALL LETTER L WITH MIDDLE DOT-  $ Entry 'L' '/' '\x0141'    -- LATIN CAPITAL LETTER L WITH STROKE-  $ Entry 'l' '/' '\x0142'    -- LATIN SMALL LETTER L WITH STROKE-  $ Entry 'N' '\'' '\x0143'    -- LATIN CAPITAL LETTER N WITH ACUTE `-  $ Entry 'n' '\'' '\x0144'    -- LATIN SMALL LETTER N WITH ACUTE `-  $ Entry 'N' ',' '\x0145'    -- LATIN CAPITAL LETTER N WITH CEDILLA `-  $ Entry 'n' ',' '\x0146'    -- LATIN SMALL LETTER N WITH CEDILLA `-  $ Entry 'N' '<' '\x0147'    -- LATIN CAPITAL LETTER N WITH CARON `-  $ Entry 'n' '<' '\x0148'    -- LATIN SMALL LETTER N WITH CARON `-  $ Entry '\'' 'n' '\x0149'    -- LATIN SMALL LETTER N PRECEDED BY APOSTROPHE `-  $ Entry 'N' 'G' '\x014A'    -- LATIN CAPITAL LETTER ENG-  $ Entry 'n' 'g' '\x014B'    -- LATIN SMALL LETTER ENG-  $ Entry 'O' '-' '\x014C'    -- LATIN CAPITAL LETTER O WITH MACRON-  $ Entry 'o' '-' '\x014D'    -- LATIN SMALL LETTER O WITH MACRON-  $ Entry 'O' '(' '\x014E'    -- LATIN CAPITAL LETTER O WITH BREVE-  $ Entry 'o' '(' '\x014F'    -- LATIN SMALL LETTER O WITH BREVE-  $ Entry 'O' '"' '\x0150'   -- LATIN CAPITAL LETTER O WITH DOUBLE ACUTE-  $ Entry 'o' '"' '\x0151'   -- LATIN SMALL LETTER O WITH DOUBLE ACUTE-  $ Entry 'O' 'E' '\x0152'    -- LATIN CAPITAL LIGATURE OE-  $ Entry 'o' 'e' '\x0153'    -- LATIN SMALL LIGATURE OE-  $ Entry 'R' '\'' '\x0154'    -- LATIN CAPITAL LETTER R WITH ACUTE-  $ Entry 'r' '\'' '\x0155'    -- LATIN SMALL LETTER R WITH ACUTE-  $ Entry 'R' ',' '\x0156'    -- LATIN CAPITAL LETTER R WITH CEDILLA-  $ Entry 'r' ',' '\x0157'    -- LATIN SMALL LETTER R WITH CEDILLA-  $ Entry 'R' '<' '\x0158'    -- LATIN CAPITAL LETTER R WITH CARON-  $ Entry 'r' '<' '\x0159'    -- LATIN SMALL LETTER R WITH CARON-  $ Entry 'S' '\'' '\x015A'    -- LATIN CAPITAL LETTER S WITH ACUTE-  $ Entry 's' '\'' '\x015B'    -- LATIN SMALL LETTER S WITH ACUTE-  $ Entry 'S' '>' '\x015C'    -- LATIN CAPITAL LETTER S WITH CIRCUMFLEX-  $ Entry 's' '>' '\x015D'    -- LATIN SMALL LETTER S WITH CIRCUMFLEX-  $ Entry 'S' ',' '\x015E'    -- LATIN CAPITAL LETTER S WITH CEDILLA-  $ Entry 's' ',' '\x015F'    -- LATIN SMALL LETTER S WITH CEDILLA-  $ Entry 'S' '<' '\x0160'    -- LATIN CAPITAL LETTER S WITH CARON-  $ Entry 's' '<' '\x0161'    -- LATIN SMALL LETTER S WITH CARON-  $ Entry 'T' ',' '\x0162'    -- LATIN CAPITAL LETTER T WITH CEDILLA-  $ Entry 't' ',' '\x0163'    -- LATIN SMALL LETTER T WITH CEDILLA-  $ Entry 'T' '<' '\x0164'    -- LATIN CAPITAL LETTER T WITH CARON-  $ Entry 't' '<' '\x0165'    -- LATIN SMALL LETTER T WITH CARON-  $ Entry 'T' '/' '\x0166'    -- LATIN CAPITAL LETTER T WITH STROKE-  $ Entry 't' '/' '\x0167'    -- LATIN SMALL LETTER T WITH STROKE-  $ Entry 'U' '?' '\x0168'    -- LATIN CAPITAL LETTER U WITH TILDE-  $ Entry 'u' '?' '\x0169'    -- LATIN SMALL LETTER U WITH TILDE-  $ Entry 'U' '-' '\x016A'    -- LATIN CAPITAL LETTER U WITH MACRON-  $ Entry 'u' '-' '\x016B'    -- LATIN SMALL LETTER U WITH MACRON-  $ Entry 'U' '(' '\x016C'    -- LATIN CAPITAL LETTER U WITH BREVE-  $ Entry 'u' '(' '\x016D'    -- LATIN SMALL LETTER U WITH BREVE-  $ Entry 'U' '0' '\x016E'    -- LATIN CAPITAL LETTER U WITH RING ABOVE-  $ Entry 'u' '0' '\x016F'    -- LATIN SMALL LETTER U WITH RING ABOVE-  $ Entry 'U' '"' '\x0170'   -- LATIN CAPITAL LETTER U WITH DOUBLE ACUTE-  $ Entry 'u' '"' '\x0171'   -- LATIN SMALL LETTER U WITH DOUBLE ACUTE-  $ Entry 'U' ';' '\x0172'    -- LATIN CAPITAL LETTER U WITH OGONEK-  $ Entry 'u' ';' '\x0173'    -- LATIN SMALL LETTER U WITH OGONEK-  $ Entry 'W' '>' '\x0174'    -- LATIN CAPITAL LETTER W WITH CIRCUMFLEX-  $ Entry 'w' '>' '\x0175'    -- LATIN SMALL LETTER W WITH CIRCUMFLEX-  $ Entry 'Y' '>' '\x0176'    -- LATIN CAPITAL LETTER Y WITH CIRCUMFLEX-  $ Entry 'y' '>' '\x0177'    -- LATIN SMALL LETTER Y WITH CIRCUMFLEX-  $ Entry 'Y' ':' '\x0178'    -- LATIN CAPITAL LETTER Y WITH DIAERESIS-  $ Entry 'Z' '\'' '\x0179'    -- LATIN CAPITAL LETTER Z WITH ACUTE-  $ Entry 'z' '\'' '\x017A'    -- LATIN SMALL LETTER Z WITH ACUTE-  $ Entry 'Z' '.' '\x017B'    -- LATIN CAPITAL LETTER Z WITH DOT ABOVE-  $ Entry 'z' '.' '\x017C'    -- LATIN SMALL LETTER Z WITH DOT ABOVE-  $ Entry 'Z' '<' '\x017D'    -- LATIN CAPITAL LETTER Z WITH CARON-  $ Entry 'z' '<' '\x017E'    -- LATIN SMALL LETTER Z WITH CARON-  $ Entry 'O' '9' '\x01A0'    -- LATIN CAPITAL LETTER O WITH HORN-  $ Entry 'o' '9' '\x01A1'    -- LATIN SMALL LETTER O WITH HORN-  $ Entry 'O' 'I' '\x01A2'    -- LATIN CAPITAL LETTER OI-  $ Entry 'o' 'i' '\x01A3'    -- LATIN SMALL LETTER OI-  $ Entry 'y' 'r' '\x01A6'    -- LATIN LETTER YR-  $ Entry 'U' '9' '\x01AF'    -- LATIN CAPITAL LETTER U WITH HORN-  $ Entry 'u' '9' '\x01B0'    -- LATIN SMALL LETTER U WITH HORN-  $ Entry 'Z' '/' '\x01B5'    -- LATIN CAPITAL LETTER Z WITH STROKE-  $ Entry 'z' '/' '\x01B6'    -- LATIN SMALL LETTER Z WITH STROKE-  $ Entry 'E' 'D' '\x01B7'    -- LATIN CAPITAL LETTER EZH-  $ Entry 'A' '<' '\x01CD'    -- LATIN CAPITAL LETTER A WITH CARON-  $ Entry 'a' '<' '\x01CE'    -- LATIN SMALL LETTER A WITH CARON-  $ Entry 'I' '<' '\x01CF'    -- LATIN CAPITAL LETTER I WITH CARON-  $ Entry 'i' '<' '\x01D0'    -- LATIN SMALL LETTER I WITH CARON-  $ Entry 'O' '<' '\x01D1'    -- LATIN CAPITAL LETTER O WITH CARON-  $ Entry 'o' '<' '\x01D2'    -- LATIN SMALL LETTER O WITH CARON-  $ Entry 'U' '<' '\x01D3'    -- LATIN CAPITAL LETTER U WITH CARON-  $ Entry 'u' '<' '\x01D4'    -- LATIN SMALL LETTER U WITH CARON-  $ Entry 'A' '1' '\x01DE'    -- LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON-  $ Entry 'a' '1' '\x01DF'    -- LATIN SMALL LETTER A WITH DIAERESIS AND MACRON-  $ Entry 'A' '7' '\x01E0'    -- LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON-  $ Entry 'a' '7' '\x01E1'    -- LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON-  $ Entry 'A' '3' '\x01E2'    -- LATIN CAPITAL LETTER AE WITH MACRON-  $ Entry 'a' '3' '\x01E3'    -- LATIN SMALL LETTER AE WITH MACRON-  $ Entry 'G' '/' '\x01E4'    -- LATIN CAPITAL LETTER G WITH STROKE-  $ Entry 'g' '/' '\x01E5'    -- LATIN SMALL LETTER G WITH STROKE-  $ Entry 'G' '<' '\x01E6'    -- LATIN CAPITAL LETTER G WITH CARON-  $ Entry 'g' '<' '\x01E7'    -- LATIN SMALL LETTER G WITH CARON-  $ Entry 'K' '<' '\x01E8'    -- LATIN CAPITAL LETTER K WITH CARON-  $ Entry 'k' '<' '\x01E9'    -- LATIN SMALL LETTER K WITH CARON-  $ Entry 'O' ';' '\x01EA'    -- LATIN CAPITAL LETTER O WITH OGONEK-  $ Entry 'o' ';' '\x01EB'    -- LATIN SMALL LETTER O WITH OGONEK-  $ Entry 'O' '1' '\x01EC'    -- LATIN CAPITAL LETTER O WITH OGONEK AND MACRON-  $ Entry 'o' '1' '\x01ED'    -- LATIN SMALL LETTER O WITH OGONEK AND MACRON-  $ Entry 'E' 'Z' '\x01EE'    -- LATIN CAPITAL LETTER EZH WITH CARON-  $ Entry 'e' 'z' '\x01EF'    -- LATIN SMALL LETTER EZH WITH CARON-  $ Entry 'j' '<' '\x01F0'    -- LATIN SMALL LETTER J WITH CARON-  $ Entry 'G' '\'' '\x01F4'    -- LATIN CAPITAL LETTER G WITH ACUTE-  $ Entry 'g' '\'' '\x01F5'    -- LATIN SMALL LETTER G WITH ACUTE-  $ Entry ';' 'S' '\x02BF'    -- MODIFIER LETTER LEFT HALF RING-  $ Entry '\'' '<' '\x02C7'    -- CARON-  $ Entry '\'' '(' '\x02D8'    -- BREVE-  $ Entry '\'' '.' '\x02D9'    -- DOT ABOVE-  $ Entry '\'' '0' '\x02DA'    -- RING ABOVE-  $ Entry '\'' ';' '\x02DB'    -- OGONEK-  $ Entry '\'' '"' '\x02DD'    -- DOUBLE ACUTE ACCENT-  $ Entry 'A' '%' '\x0386'    -- GREEK CAPITAL LETTER ALPHA WITH TONOS-  $ Entry 'E' '%' '\x0388'    -- GREEK CAPITAL LETTER EPSILON WITH TONOS-  $ Entry 'Y' '%' '\x0389'    -- GREEK CAPITAL LETTER ETA WITH TONOS-  $ Entry 'I' '%' '\x038A'    -- GREEK CAPITAL LETTER IOTA WITH TONOS-  $ Entry 'O' '%' '\x038C'    -- GREEK CAPITAL LETTER OMICRON WITH TONOS-  $ Entry 'U' '%' '\x038E'    -- GREEK CAPITAL LETTER UPSILON WITH TONOS-  $ Entry 'W' '%' '\x038F'    -- GREEK CAPITAL LETTER OMEGA WITH TONOS-  $ Entry 'i' '3' '\x0390'    -- GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS-  $ Entry 'A' '*' '\x0391'    -- GREEK CAPITAL LETTER ALPHA-  $ Entry 'B' '*' '\x0392'    -- GREEK CAPITAL LETTER BETA-  $ Entry 'G' '*' '\x0393'    -- GREEK CAPITAL LETTER GAMMA-  $ Entry 'D' '*' '\x0394'    -- GREEK CAPITAL LETTER DELTA-  $ Entry 'E' '*' '\x0395'    -- GREEK CAPITAL LETTER EPSILON-  $ Entry 'Z' '*' '\x0396'    -- GREEK CAPITAL LETTER ZETA-  $ Entry 'Y' '*' '\x0397'    -- GREEK CAPITAL LETTER ETA-  $ Entry 'H' '*' '\x0398'    -- GREEK CAPITAL LETTER THETA-  $ Entry 'I' '*' '\x0399'    -- GREEK CAPITAL LETTER IOTA-  $ Entry 'K' '*' '\x039A'    -- GREEK CAPITAL LETTER KAPPA-  $ Entry 'L' '*' '\x039B'    -- GREEK CAPITAL LETTER LAMDA-  $ Entry 'M' '*' '\x039C'    -- GREEK CAPITAL LETTER MU-  $ Entry 'N' '*' '\x039D'    -- GREEK CAPITAL LETTER NU-  $ Entry 'C' '*' '\x039E'    -- GREEK CAPITAL LETTER XI-  $ Entry 'O' '*' '\x039F'    -- GREEK CAPITAL LETTER OMICRON-  $ Entry 'P' '*' '\x03A0'    -- GREEK CAPITAL LETTER PI-  $ Entry 'R' '*' '\x03A1'    -- GREEK CAPITAL LETTER RHO-  $ Entry 'S' '*' '\x03A3'    -- GREEK CAPITAL LETTER SIGMA-  $ Entry 'T' '*' '\x03A4'    -- GREEK CAPITAL LETTER TAU-  $ Entry 'U' '*' '\x03A5'    -- GREEK CAPITAL LETTER UPSILON-  $ Entry 'F' '*' '\x03A6'    -- GREEK CAPITAL LETTER PHI-  $ Entry 'X' '*' '\x03A7'    -- GREEK CAPITAL LETTER CHI-  $ Entry 'Q' '*' '\x03A8'    -- GREEK CAPITAL LETTER PSI-  $ Entry 'W' '*' '\x03A9'    -- GREEK CAPITAL LETTER OMEGA-  $ Entry 'J' '*' '\x03AA'    -- GREEK CAPITAL LETTER IOTA WITH DIALYTIKA-  $ Entry 'V' '*' '\x03AB'    -- GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA-  $ Entry 'a' '%' '\x03AC'    -- GREEK SMALL LETTER ALPHA WITH TONOS-  $ Entry 'e' '%' '\x03AD'    -- GREEK SMALL LETTER EPSILON WITH TONOS-  $ Entry 'y' '%' '\x03AE'    -- GREEK SMALL LETTER ETA WITH TONOS-  $ Entry 'i' '%' '\x03AF'    -- GREEK SMALL LETTER IOTA WITH TONOS-  $ Entry 'u' '3' '\x03B0'    -- GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS-  $ Entry 'a' '*' '\x03B1'    -- GREEK SMALL LETTER ALPHA-  $ Entry 'b' '*' '\x03B2'    -- GREEK SMALL LETTER BETA-  $ Entry 'g' '*' '\x03B3'    -- GREEK SMALL LETTER GAMMA-  $ Entry 'd' '*' '\x03B4'    -- GREEK SMALL LETTER DELTA-  $ Entry 'e' '*' '\x03B5'    -- GREEK SMALL LETTER EPSILON-  $ Entry 'z' '*' '\x03B6'    -- GREEK SMALL LETTER ZETA-  $ Entry 'y' '*' '\x03B7'    -- GREEK SMALL LETTER ETA-  $ Entry 'h' '*' '\x03B8'    -- GREEK SMALL LETTER THETA-  $ Entry 'i' '*' '\x03B9'    -- GREEK SMALL LETTER IOTA-  $ Entry 'k' '*' '\x03BA'    -- GREEK SMALL LETTER KAPPA-  $ Entry 'l' '*' '\x03BB'    -- GREEK SMALL LETTER LAMDA-  $ Entry 'm' '*' '\x03BC'    -- GREEK SMALL LETTER MU-  $ Entry 'n' '*' '\x03BD'    -- GREEK SMALL LETTER NU-  $ Entry 'c' '*' '\x03BE'    -- GREEK SMALL LETTER XI-  $ Entry 'o' '*' '\x03BF'    -- GREEK SMALL LETTER OMICRON-  $ Entry 'p' '*' '\x03C0'    -- GREEK SMALL LETTER PI-  $ Entry 'r' '*' '\x03C1'    -- GREEK SMALL LETTER RHO-  $ Entry '*' 's' '\x03C2'    -- GREEK SMALL LETTER FINAL SIGMA-  $ Entry 's' '*' '\x03C3'    -- GREEK SMALL LETTER SIGMA-  $ Entry 't' '*' '\x03C4'    -- GREEK SMALL LETTER TAU-  $ Entry 'u' '*' '\x03C5'    -- GREEK SMALL LETTER UPSILON-  $ Entry 'f' '*' '\x03C6'    -- GREEK SMALL LETTER PHI-  $ Entry 'x' '*' '\x03C7'    -- GREEK SMALL LETTER CHI-  $ Entry 'q' '*' '\x03C8'    -- GREEK SMALL LETTER PSI-  $ Entry 'w' '*' '\x03C9'    -- GREEK SMALL LETTER OMEGA-  $ Entry 'j' '*' '\x03CA'    -- GREEK SMALL LETTER IOTA WITH DIALYTIKA-  $ Entry 'v' '*' '\x03CB'    -- GREEK SMALL LETTER UPSILON WITH DIALYTIKA-  $ Entry 'o' '%' '\x03CC'    -- GREEK SMALL LETTER OMICRON WITH TONOS-  $ Entry 'u' '%' '\x03CD'    -- GREEK SMALL LETTER UPSILON WITH TONOS-  $ Entry 'w' '%' '\x03CE'    -- GREEK SMALL LETTER OMEGA WITH TONOS-  $ Entry '\'' 'G' '\x03D8'    -- GREEK LETTER ARCHAIC KOPPA-  $ Entry ',' 'G' '\x03D9'    -- GREEK SMALL LETTER ARCHAIC KOPPA-  $ Entry 'T' '3' '\x03DA'    -- GREEK LETTER STIGMA-  $ Entry 't' '3' '\x03DB'    -- GREEK SMALL LETTER STIGMA-  $ Entry 'M' '3' '\x03DC'    -- GREEK LETTER DIGAMMA-  $ Entry 'm' '3' '\x03DD'    -- GREEK SMALL LETTER DIGAMMA-  $ Entry 'K' '3' '\x03DE'    -- GREEK LETTER KOPPA-  $ Entry 'k' '3' '\x03DF'    -- GREEK SMALL LETTER KOPPA-  $ Entry 'P' '3' '\x03E0'    -- GREEK LETTER SAMPI-  $ Entry 'p' '3' '\x03E1'    -- GREEK SMALL LETTER SAMPI-  $ Entry '\'' '%' '\x03F4'    -- GREEK CAPITAL THETA SYMBOL-  $ Entry 'j' '3' '\x03F5'    -- GREEK LUNATE EPSILON SYMBOL-  $ Entry 'I' 'O' '\x0401'    -- CYRILLIC CAPITAL LETTER IO-  $ Entry 'D' '%' '\x0402'    -- CYRILLIC CAPITAL LETTER DJE-  $ Entry 'G' '%' '\x0403'    -- CYRILLIC CAPITAL LETTER GJE-  $ Entry 'I' 'E' '\x0404'    -- CYRILLIC CAPITAL LETTER UKRAINIAN IE-  $ Entry 'D' 'S' '\x0405'    -- CYRILLIC CAPITAL LETTER DZE-  $ Entry 'I' 'I' '\x0406'    -- CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I-  $ Entry 'Y' 'I' '\x0407'    -- CYRILLIC CAPITAL LETTER YI-  $ Entry 'J' '%' '\x0408'    -- CYRILLIC CAPITAL LETTER JE-  $ Entry 'L' 'J' '\x0409'    -- CYRILLIC CAPITAL LETTER LJE-  $ Entry 'N' 'J' '\x040A'    -- CYRILLIC CAPITAL LETTER NJE-  $ Entry 'T' 's' '\x040B'    -- CYRILLIC CAPITAL LETTER TSHE-  $ Entry 'K' 'J' '\x040C'    -- CYRILLIC CAPITAL LETTER KJE-  $ Entry 'V' '%' '\x040E'    -- CYRILLIC CAPITAL LETTER SHORT U-  $ Entry 'D' 'Z' '\x040F'    -- CYRILLIC CAPITAL LETTER DZHE-  $ Entry 'A' '=' '\x0410'    -- CYRILLIC CAPITAL LETTER A-  $ Entry 'B' '=' '\x0411'    -- CYRILLIC CAPITAL LETTER BE-  $ Entry 'V' '=' '\x0412'    -- CYRILLIC CAPITAL LETTER VE-  $ Entry 'G' '=' '\x0413'    -- CYRILLIC CAPITAL LETTER GHE-  $ Entry 'D' '=' '\x0414'    -- CYRILLIC CAPITAL LETTER DE-  $ Entry 'E' '=' '\x0415'    -- CYRILLIC CAPITAL LETTER IE-  $ Entry 'Z' '%' '\x0416'    -- CYRILLIC CAPITAL LETTER ZHE-  $ Entry 'Z' '=' '\x0417'    -- CYRILLIC CAPITAL LETTER ZE-  $ Entry 'I' '=' '\x0418'    -- CYRILLIC CAPITAL LETTER I-  $ Entry 'J' '=' '\x0419'    -- CYRILLIC CAPITAL LETTER SHORT I-  $ Entry 'K' '=' '\x041A'    -- CYRILLIC CAPITAL LETTER KA-  $ Entry 'L' '=' '\x041B'    -- CYRILLIC CAPITAL LETTER EL-  $ Entry 'M' '=' '\x041C'    -- CYRILLIC CAPITAL LETTER EM-  $ Entry 'N' '=' '\x041D'    -- CYRILLIC CAPITAL LETTER EN-  $ Entry 'O' '=' '\x041E'    -- CYRILLIC CAPITAL LETTER O-  $ Entry 'P' '=' '\x041F'    -- CYRILLIC CAPITAL LETTER PE-  $ Entry 'R' '=' '\x0420'    -- CYRILLIC CAPITAL LETTER ER-  $ Entry 'S' '=' '\x0421'    -- CYRILLIC CAPITAL LETTER ES-  $ Entry 'T' '=' '\x0422'    -- CYRILLIC CAPITAL LETTER TE-  $ Entry 'U' '=' '\x0423'    -- CYRILLIC CAPITAL LETTER U-  $ Entry 'F' '=' '\x0424'    -- CYRILLIC CAPITAL LETTER EF-  $ Entry 'H' '=' '\x0425'    -- CYRILLIC CAPITAL LETTER HA-  $ Entry 'C' '=' '\x0426'    -- CYRILLIC CAPITAL LETTER TSE-  $ Entry 'C' '%' '\x0427'    -- CYRILLIC CAPITAL LETTER CHE-  $ Entry 'S' '%' '\x0428'    -- CYRILLIC CAPITAL LETTER SHA-  $ Entry 'S' 'c' '\x0429'    -- CYRILLIC CAPITAL LETTER SHCHA-  $ Entry '=' '\'' '\x042A'   -- CYRILLIC CAPITAL LETTER HARD SIGN-  $ Entry 'Y' '=' '\x042B'    -- CYRILLIC CAPITAL LETTER YERU-  $ Entry '%' '\'' '\x042C'   -- CYRILLIC CAPITAL LETTER SOFT SIGN-  $ Entry 'J' 'E' '\x042D'    -- CYRILLIC CAPITAL LETTER E-  $ Entry 'J' 'U' '\x042E'    -- CYRILLIC CAPITAL LETTER YU-  $ Entry 'J' 'A' '\x042F'    -- CYRILLIC CAPITAL LETTER YA-  $ Entry 'a' '=' '\x0430'    -- CYRILLIC SMALL LETTER A-  $ Entry 'b' '=' '\x0431'    -- CYRILLIC SMALL LETTER BE-  $ Entry 'v' '=' '\x0432'    -- CYRILLIC SMALL LETTER VE-  $ Entry 'g' '=' '\x0433'    -- CYRILLIC SMALL LETTER GHE-  $ Entry 'd' '=' '\x0434'    -- CYRILLIC SMALL LETTER DE-  $ Entry 'e' '=' '\x0435'    -- CYRILLIC SMALL LETTER IE-  $ Entry 'z' '%' '\x0436'    -- CYRILLIC SMALL LETTER ZHE-  $ Entry 'z' '=' '\x0437'    -- CYRILLIC SMALL LETTER ZE-  $ Entry 'i' '=' '\x0438'    -- CYRILLIC SMALL LETTER I-  $ Entry 'j' '=' '\x0439'    -- CYRILLIC SMALL LETTER SHORT I-  $ Entry 'k' '=' '\x043A'    -- CYRILLIC SMALL LETTER KA-  $ Entry 'l' '=' '\x043B'    -- CYRILLIC SMALL LETTER EL-  $ Entry 'm' '=' '\x043C'    -- CYRILLIC SMALL LETTER EM-  $ Entry 'n' '=' '\x043D'    -- CYRILLIC SMALL LETTER EN-  $ Entry 'o' '=' '\x043E'    -- CYRILLIC SMALL LETTER O-  $ Entry 'p' '=' '\x043F'    -- CYRILLIC SMALL LETTER PE-  $ Entry 'r' '=' '\x0440'    -- CYRILLIC SMALL LETTER ER-  $ Entry 's' '=' '\x0441'    -- CYRILLIC SMALL LETTER ES-  $ Entry 't' '=' '\x0442'    -- CYRILLIC SMALL LETTER TE-  $ Entry 'u' '=' '\x0443'    -- CYRILLIC SMALL LETTER U-  $ Entry 'f' '=' '\x0444'    -- CYRILLIC SMALL LETTER EF-  $ Entry 'h' '=' '\x0445'    -- CYRILLIC SMALL LETTER HA-  $ Entry 'c' '=' '\x0446'    -- CYRILLIC SMALL LETTER TSE-  $ Entry 'c' '%' '\x0447'    -- CYRILLIC SMALL LETTER CHE-  $ Entry 's' '%' '\x0448'    -- CYRILLIC SMALL LETTER SHA-  $ Entry 's' 'c' '\x0449'    -- CYRILLIC SMALL LETTER SHCHA-  $ Entry '=' '\'' '\x044A'    -- CYRILLIC SMALL LETTER HARD SIGN-  $ Entry 'y' '=' '\x044B'    -- CYRILLIC SMALL LETTER YERU-  $ Entry '%' '\'' '\x044C'    -- CYRILLIC SMALL LETTER SOFT SIGN-  $ Entry 'j' 'e' '\x044D'    -- CYRILLIC SMALL LETTER E-  $ Entry 'j' 'u' '\x044E'    -- CYRILLIC SMALL LETTER YU-  $ Entry 'j' 'a' '\x044F'    -- CYRILLIC SMALL LETTER YA-  $ Entry 'i' 'o' '\x0451'    -- CYRILLIC SMALL LETTER IO-  $ Entry 'd' '%' '\x0452'    -- CYRILLIC SMALL LETTER DJE-  $ Entry 'g' '%' '\x0453'    -- CYRILLIC SMALL LETTER GJE-  $ Entry 'i' 'e' '\x0454'    -- CYRILLIC SMALL LETTER UKRAINIAN IE-  $ Entry 'd' 's' '\x0455'    -- CYRILLIC SMALL LETTER DZE-  $ Entry 'i' 'i' '\x0456'    -- CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I-  $ Entry 'y' 'i' '\x0457'    -- CYRILLIC SMALL LETTER YI-  $ Entry 'j' '%' '\x0458'    -- CYRILLIC SMALL LETTER JE-  $ Entry 'l' 'j' '\x0459'    -- CYRILLIC SMALL LETTER LJE-  $ Entry 'n' 'j' '\x045A'    -- CYRILLIC SMALL LETTER NJE-  $ Entry 't' 's' '\x045B'    -- CYRILLIC SMALL LETTER TSHE-  $ Entry 'k' 'j' '\x045C'    -- CYRILLIC SMALL LETTER KJE-  $ Entry 'v' '%' '\x045E'    -- CYRILLIC SMALL LETTER SHORT U-  $ Entry 'd' 'z' '\x045F'    -- CYRILLIC SMALL LETTER DZHE-  $ Entry 'Y' '3' '\x0462'    -- CYRILLIC CAPITAL LETTER YAT-  $ Entry 'y' '3' '\x0463'    -- CYRILLIC SMALL LETTER YAT-  $ Entry 'O' '3' '\x046A'    -- CYRILLIC CAPITAL LETTER BIG YUS-  $ Entry 'o' '3' '\x046B'    -- CYRILLIC SMALL LETTER BIG YUS-  $ Entry 'F' '3' '\x0472'    -- CYRILLIC CAPITAL LETTER FITA-  $ Entry 'f' '3' '\x0473'    -- CYRILLIC SMALL LETTER FITA-  $ Entry 'V' '3' '\x0474'    -- CYRILLIC CAPITAL LETTER IZHITSA-  $ Entry 'v' '3' '\x0475'    -- CYRILLIC SMALL LETTER IZHITSA-  $ Entry 'C' '3' '\x0480'    -- CYRILLIC CAPITAL LETTER KOPPA-  $ Entry 'c' '3' '\x0481'    -- CYRILLIC SMALL LETTER KOPPA-  $ Entry 'G' '3' '\x0490'    -- CYRILLIC CAPITAL LETTER GHE WITH UPTURN-  $ Entry 'g' '3' '\x0491'    -- CYRILLIC SMALL LETTER GHE WITH UPTURN-  $ Entry 'A' '+' '\x05D0'    -- HEBREW LETTER ALEF-  $ Entry 'B' '+' '\x05D1'    -- HEBREW LETTER BET-  $ Entry 'G' '+' '\x05D2'    -- HEBREW LETTER GIMEL-  $ Entry 'D' '+' '\x05D3'    -- HEBREW LETTER DALET-  $ Entry 'H' '+' '\x05D4'    -- HEBREW LETTER HE-  $ Entry 'W' '+' '\x05D5'    -- HEBREW LETTER VAV-  $ Entry 'Z' '+' '\x05D6'    -- HEBREW LETTER ZAYIN-  $ Entry 'X' '+' '\x05D7'    -- HEBREW LETTER HET-  $ Entry 'T' 'j' '\x05D8'    -- HEBREW LETTER TET-  $ Entry 'J' '+' '\x05D9'    -- HEBREW LETTER YOD-  $ Entry 'K' '%' '\x05DA'    -- HEBREW LETTER FINAL KAF-  $ Entry 'K' '+' '\x05DB'    -- HEBREW LETTER KAF-  $ Entry 'L' '+' '\x05DC'    -- HEBREW LETTER LAMED-  $ Entry 'M' '%' '\x05DD'    -- HEBREW LETTER FINAL MEM-  $ Entry 'M' '+' '\x05DE'    -- HEBREW LETTER MEM-  $ Entry 'N' '%' '\x05DF'    -- HEBREW LETTER FINAL NUN `-  $ Entry 'N' '+' '\x05E0'    -- HEBREW LETTER NUN `-  $ Entry 'S' '+' '\x05E1'    -- HEBREW LETTER SAMEKH-  $ Entry 'E' '+' '\x05E2'    -- HEBREW LETTER AYIN-  $ Entry 'P' '%' '\x05E3'    -- HEBREW LETTER FINAL PE-  $ Entry 'P' '+' '\x05E4'    -- HEBREW LETTER PE-  $ Entry 'Z' 'j' '\x05E5'    -- HEBREW LETTER FINAL TSADI-  $ Entry 'Z' 'J' '\x05E6'    -- HEBREW LETTER TSADI-  $ Entry 'Q' '+' '\x05E7'    -- HEBREW LETTER QOF-  $ Entry 'R' '+' '\x05E8'    -- HEBREW LETTER RESH-  $ Entry 'S' 'h' '\x05E9'    -- HEBREW LETTER SHIN-  $ Entry 'T' '+' '\x05EA'    -- HEBREW LETTER TAV-  $ Entry ',' '+' '\x060C'    -- ARABIC COMMA-  $ Entry ';' '+' '\x061B'    -- ARABIC SEMICOLON-  $ Entry '?' '+' '\x061F'    -- ARABIC QUESTION MARK-  $ Entry 'H' '\'' '\x0621'    -- ARABIC LETTER HAMZA-  $ Entry 'a' 'M' '\x0622'    -- ARABIC LETTER ALEF WITH MADDA ABOVE-  $ Entry 'a' 'H' '\x0623'    -- ARABIC LETTER ALEF WITH HAMZA ABOVE-  $ Entry 'w' 'H' '\x0624'    -- ARABIC LETTER WAW WITH HAMZA ABOVE-  $ Entry 'a' 'h' '\x0625'    -- ARABIC LETTER ALEF WITH HAMZA BELOW-  $ Entry 'y' 'H' '\x0626'    -- ARABIC LETTER YEH WITH HAMZA ABOVE-  $ Entry 'a' '+' '\x0627'    -- ARABIC LETTER ALEF-  $ Entry 'b' '+' '\x0628'    -- ARABIC LETTER BEH-  $ Entry 't' 'm' '\x0629'    -- ARABIC LETTER TEH MARBUTA-  $ Entry 't' '+' '\x062A'    -- ARABIC LETTER TEH-  $ Entry 't' 'k' '\x062B'    -- ARABIC LETTER THEH-  $ Entry 'g' '+' '\x062C'    -- ARABIC LETTER JEEM-  $ Entry 'h' 'k' '\x062D'    -- ARABIC LETTER HAH-  $ Entry 'x' '+' '\x062E'    -- ARABIC LETTER KHAH-  $ Entry 'd' '+' '\x062F'    -- ARABIC LETTER DAL-  $ Entry 'd' 'k' '\x0630'    -- ARABIC LETTER THAL-  $ Entry 'r' '+' '\x0631'    -- ARABIC LETTER REH-  $ Entry 'z' '+' '\x0632'    -- ARABIC LETTER ZAIN-  $ Entry 's' '+' '\x0633'    -- ARABIC LETTER SEEN-  $ Entry 's' 'n' '\x0634'    -- ARABIC LETTER SHEEN-  $ Entry 'c' '+' '\x0635'    -- ARABIC LETTER SAD-  $ Entry 'd' 'd' '\x0636'    -- ARABIC LETTER DAD-  $ Entry 't' 'j' '\x0637'    -- ARABIC LETTER TAH-  $ Entry 'z' 'H' '\x0638'    -- ARABIC LETTER ZAH-  $ Entry 'e' '+' '\x0639'    -- ARABIC LETTER AIN-  $ Entry 'i' '+' '\x063A'    -- ARABIC LETTER GHAIN-  $ Entry '+' '+' '\x0640'    -- ARABIC TATWEEL-  $ Entry 'f' '+' '\x0641'    -- ARABIC LETTER FEH-  $ Entry 'q' '+' '\x0642'    -- ARABIC LETTER QAF-  $ Entry 'k' '+' '\x0643'    -- ARABIC LETTER KAF-  $ Entry 'l' '+' '\x0644'    -- ARABIC LETTER LAM-  $ Entry 'm' '+' '\x0645'    -- ARABIC LETTER MEEM-  $ Entry 'n' '+' '\x0646'    -- ARABIC LETTER NOON-  $ Entry 'h' '+' '\x0647'    -- ARABIC LETTER HEH-  $ Entry 'w' '+' '\x0648'    -- ARABIC LETTER WAW-  $ Entry 'j' '+' '\x0649'    -- ARABIC LETTER ALEF MAKSURA-  $ Entry 'y' '+' '\x064A'    -- ARABIC LETTER YEH-  $ Entry ':' '+' '\x064B'    -- ARABIC FATHATAN-  $ Entry '"' '+' '\x064C'   -- ARABIC DAMMATAN-  $ Entry '=' '+' '\x064D'    -- ARABIC KASRATAN-  $ Entry '/' '+' '\x064E'    -- ARABIC FATHA-  $ Entry '\'' '+' '\x064F'    -- ARABIC DAMMA-  $ Entry '1' '+' '\x0650'    -- ARABIC KASRA-  $ Entry '3' '+' '\x0651'    -- ARABIC SHADDA-  $ Entry '0' '+' '\x0652'    -- ARABIC SUKUN-  $ Entry 'a' 'S' '\x0670'    -- ARABIC LETTER SUPERSCRIPT ALEF-  $ Entry 'p' '+' '\x067E'    -- ARABIC LETTER PEH-  $ Entry 'v' '+' '\x06A4'    -- ARABIC LETTER VEH-  $ Entry 'g' 'f' '\x06AF'    -- ARABIC LETTER GAF-  $ Entry '0' 'a' '\x06F0'    -- EXTENDED ARABIC-INDIC DIGIT ZERO-  $ Entry '1' 'a' '\x06F1'    -- EXTENDED ARABIC-INDIC DIGIT ONE-  $ Entry '2' 'a' '\x06F2'    -- EXTENDED ARABIC-INDIC DIGIT TWO-  $ Entry '3' 'a' '\x06F3'    -- EXTENDED ARABIC-INDIC DIGIT THREE-  $ Entry '4' 'a' '\x06F4'    -- EXTENDED ARABIC-INDIC DIGIT FOUR-  $ Entry '5' 'a' '\x06F5'    -- EXTENDED ARABIC-INDIC DIGIT FIVE-  $ Entry '6' 'a' '\x06F6'    -- EXTENDED ARABIC-INDIC DIGIT SIX-  $ Entry '7' 'a' '\x06F7'    -- EXTENDED ARABIC-INDIC DIGIT SEVEN-  $ Entry '8' 'a' '\x06F8'    -- EXTENDED ARABIC-INDIC DIGIT EIGHT-  $ Entry '9' 'a' '\x06F9'    -- EXTENDED ARABIC-INDIC DIGIT NINE-  $ Entry 'B' '.' '\x1E02'    -- LATIN CAPITAL LETTER B WITH DOT ABOVE-  $ Entry 'b' '.' '\x1E03'    -- LATIN SMALL LETTER B WITH DOT ABOVE-  $ Entry 'B' '_' '\x1E06'    -- LATIN CAPITAL LETTER B WITH LINE BELOW-  $ Entry 'b' '_' '\x1E07'    -- LATIN SMALL LETTER B WITH LINE BELOW-  $ Entry 'D' '.' '\x1E0A'    -- LATIN CAPITAL LETTER D WITH DOT ABOVE-  $ Entry 'd' '.' '\x1E0B'    -- LATIN SMALL LETTER D WITH DOT ABOVE-  $ Entry 'D' '_' '\x1E0E'    -- LATIN CAPITAL LETTER D WITH LINE BELOW-  $ Entry 'd' '_' '\x1E0F'    -- LATIN SMALL LETTER D WITH LINE BELOW-  $ Entry 'D' ',' '\x1E10'    -- LATIN CAPITAL LETTER D WITH CEDILLA-  $ Entry 'd' ',' '\x1E11'    -- LATIN SMALL LETTER D WITH CEDILLA-  $ Entry 'F' '.' '\x1E1E'    -- LATIN CAPITAL LETTER F WITH DOT ABOVE-  $ Entry 'f' '.' '\x1E1F'    -- LATIN SMALL LETTER F WITH DOT ABOVE-  $ Entry 'G' '-' '\x1E20'    -- LATIN CAPITAL LETTER G WITH MACRON-  $ Entry 'g' '-' '\x1E21'    -- LATIN SMALL LETTER G WITH MACRON-  $ Entry 'H' '.' '\x1E22'    -- LATIN CAPITAL LETTER H WITH DOT ABOVE-  $ Entry 'h' '.' '\x1E23'    -- LATIN SMALL LETTER H WITH DOT ABOVE-  $ Entry 'H' ':' '\x1E26'    -- LATIN CAPITAL LETTER H WITH DIAERESIS-  $ Entry 'h' ':' '\x1E27'    -- LATIN SMALL LETTER H WITH DIAERESIS-  $ Entry 'H' ',' '\x1E28'    -- LATIN CAPITAL LETTER H WITH CEDILLA-  $ Entry 'h' ',' '\x1E29'    -- LATIN SMALL LETTER H WITH CEDILLA-  $ Entry 'K' '\'' '\x1E30'    -- LATIN CAPITAL LETTER K WITH ACUTE-  $ Entry 'k' '\'' '\x1E31'    -- LATIN SMALL LETTER K WITH ACUTE-  $ Entry 'K' '_' '\x1E34'    -- LATIN CAPITAL LETTER K WITH LINE BELOW-  $ Entry 'k' '_' '\x1E35'    -- LATIN SMALL LETTER K WITH LINE BELOW-  $ Entry 'L' '_' '\x1E3A'    -- LATIN CAPITAL LETTER L WITH LINE BELOW-  $ Entry 'l' '_' '\x1E3B'    -- LATIN SMALL LETTER L WITH LINE BELOW-  $ Entry 'M' '\'' '\x1E3E'    -- LATIN CAPITAL LETTER M WITH ACUTE-  $ Entry 'm' '\'' '\x1E3F'    -- LATIN SMALL LETTER M WITH ACUTE-  $ Entry 'M' '.' '\x1E40'    -- LATIN CAPITAL LETTER M WITH DOT ABOVE-  $ Entry 'm' '.' '\x1E41'    -- LATIN SMALL LETTER M WITH DOT ABOVE-  $ Entry 'N' '.' '\x1E44'    -- LATIN CAPITAL LETTER N WITH DOT ABOVE `-  $ Entry 'n' '.' '\x1E45'    -- LATIN SMALL LETTER N WITH DOT ABOVE `-  $ Entry 'N' '_' '\x1E48'    -- LATIN CAPITAL LETTER N WITH LINE BELOW `-  $ Entry 'n' '_' '\x1E49'    -- LATIN SMALL LETTER N WITH LINE BELOW `-  $ Entry 'P' '\'' '\x1E54'    -- LATIN CAPITAL LETTER P WITH ACUTE-  $ Entry 'p' '\'' '\x1E55'    -- LATIN SMALL LETTER P WITH ACUTE-  $ Entry 'P' '.' '\x1E56'    -- LATIN CAPITAL LETTER P WITH DOT ABOVE-  $ Entry 'p' '.' '\x1E57'    -- LATIN SMALL LETTER P WITH DOT ABOVE-  $ Entry 'R' '.' '\x1E58'    -- LATIN CAPITAL LETTER R WITH DOT ABOVE-  $ Entry 'r' '.' '\x1E59'    -- LATIN SMALL LETTER R WITH DOT ABOVE-  $ Entry 'R' '_' '\x1E5E'    -- LATIN CAPITAL LETTER R WITH LINE BELOW-  $ Entry 'r' '_' '\x1E5F'    -- LATIN SMALL LETTER R WITH LINE BELOW-  $ Entry 'S' '.' '\x1E60'    -- LATIN CAPITAL LETTER S WITH DOT ABOVE-  $ Entry 's' '.' '\x1E61'    -- LATIN SMALL LETTER S WITH DOT ABOVE-  $ Entry 'T' '.' '\x1E6A'    -- LATIN CAPITAL LETTER T WITH DOT ABOVE-  $ Entry 't' '.' '\x1E6B'    -- LATIN SMALL LETTER T WITH DOT ABOVE-  $ Entry 'T' '_' '\x1E6E'    -- LATIN CAPITAL LETTER T WITH LINE BELOW-  $ Entry 't' '_' '\x1E6F'    -- LATIN SMALL LETTER T WITH LINE BELOW-  $ Entry 'V' '?' '\x1E7C'    -- LATIN CAPITAL LETTER V WITH TILDE-  $ Entry 'v' '?' '\x1E7D'    -- LATIN SMALL LETTER V WITH TILDE-  $ Entry 'W' '!' '\x1E80'    -- LATIN CAPITAL LETTER W WITH GRAVE-  $ Entry 'w' '!' '\x1E81'    -- LATIN SMALL LETTER W WITH GRAVE-  $ Entry 'W' '\'' '\x1E82'    -- LATIN CAPITAL LETTER W WITH ACUTE-  $ Entry 'w' '\'' '\x1E83'    -- LATIN SMALL LETTER W WITH ACUTE-  $ Entry 'W' ':' '\x1E84'    -- LATIN CAPITAL LETTER W WITH DIAERESIS-  $ Entry 'w' ':' '\x1E85'    -- LATIN SMALL LETTER W WITH DIAERESIS-  $ Entry 'W' '.' '\x1E86'    -- LATIN CAPITAL LETTER W WITH DOT ABOVE-  $ Entry 'w' '.' '\x1E87'    -- LATIN SMALL LETTER W WITH DOT ABOVE-  $ Entry 'X' '.' '\x1E8A'    -- LATIN CAPITAL LETTER X WITH DOT ABOVE-  $ Entry 'x' '.' '\x1E8B'    -- LATIN SMALL LETTER X WITH DOT ABOVE-  $ Entry 'X' ':' '\x1E8C'    -- LATIN CAPITAL LETTER X WITH DIAERESIS-  $ Entry 'x' ':' '\x1E8D'    -- LATIN SMALL LETTER X WITH DIAERESIS-  $ Entry 'Y' '.' '\x1E8E'    -- LATIN CAPITAL LETTER Y WITH DOT ABOVE-  $ Entry 'y' '.' '\x1E8F'    -- LATIN SMALL LETTER Y WITH DOT ABOVE-  $ Entry 'Z' '>' '\x1E90'    -- LATIN CAPITAL LETTER Z WITH CIRCUMFLEX-  $ Entry 'z' '>' '\x1E91'    -- LATIN SMALL LETTER Z WITH CIRCUMFLEX-  $ Entry 'Z' '_' '\x1E94'    -- LATIN CAPITAL LETTER Z WITH LINE BELOW-  $ Entry 'z' '_' '\x1E95'    -- LATIN SMALL LETTER Z WITH LINE BELOW-  $ Entry 'h' '_' '\x1E96'    -- LATIN SMALL LETTER H WITH LINE BELOW-  $ Entry 't' ':' '\x1E97'    -- LATIN SMALL LETTER T WITH DIAERESIS-  $ Entry 'w' '0' '\x1E98'    -- LATIN SMALL LETTER W WITH RING ABOVE-  $ Entry 'y' '0' '\x1E99'    -- LATIN SMALL LETTER Y WITH RING ABOVE-  $ Entry 'A' '2' '\x1EA2'    -- LATIN CAPITAL LETTER A WITH HOOK ABOVE-  $ Entry 'a' '2' '\x1EA3'    -- LATIN SMALL LETTER A WITH HOOK ABOVE-  $ Entry 'E' '2' '\x1EBA'    -- LATIN CAPITAL LETTER E WITH HOOK ABOVE-  $ Entry 'e' '2' '\x1EBB'    -- LATIN SMALL LETTER E WITH HOOK ABOVE-  $ Entry 'E' '?' '\x1EBC'    -- LATIN CAPITAL LETTER E WITH TILDE-  $ Entry 'e' '?' '\x1EBD'    -- LATIN SMALL LETTER E WITH TILDE-  $ Entry 'I' '2' '\x1EC8'    -- LATIN CAPITAL LETTER I WITH HOOK ABOVE-  $ Entry 'i' '2' '\x1EC9'    -- LATIN SMALL LETTER I WITH HOOK ABOVE-  $ Entry 'O' '2' '\x1ECE'    -- LATIN CAPITAL LETTER O WITH HOOK ABOVE-  $ Entry 'o' '2' '\x1ECF'    -- LATIN SMALL LETTER O WITH HOOK ABOVE-  $ Entry 'U' '2' '\x1EE6'    -- LATIN CAPITAL LETTER U WITH HOOK ABOVE-  $ Entry 'u' '2' '\x1EE7'    -- LATIN SMALL LETTER U WITH HOOK ABOVE-  $ Entry 'Y' '!' '\x1EF2'    -- LATIN CAPITAL LETTER Y WITH GRAVE-  $ Entry 'y' '!' '\x1EF3'    -- LATIN SMALL LETTER Y WITH GRAVE-  $ Entry 'Y' '2' '\x1EF6'    -- LATIN CAPITAL LETTER Y WITH HOOK ABOVE-  $ Entry 'y' '2' '\x1EF7'    -- LATIN SMALL LETTER Y WITH HOOK ABOVE-  $ Entry 'Y' '?' '\x1EF8'    -- LATIN CAPITAL LETTER Y WITH TILDE-  $ Entry 'y' '?' '\x1EF9'    -- LATIN SMALL LETTER Y WITH TILDE-  $ Entry ';' '\'' '\x1F00'    -- GREEK SMALL LETTER ALPHA WITH PSILI-  $ Entry ',' '\'' '\x1F01'    -- GREEK SMALL LETTER ALPHA WITH DASIA-  $ Entry ';' '!' '\x1F02'    -- GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA-  $ Entry ',' '!' '\x1F03'    -- GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA-  $ Entry '?' ';' '\x1F04'    -- GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA-  $ Entry '?' ',' '\x1F05'    -- GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA-  $ Entry '!' ':' '\x1F06'    -- GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI-  $ Entry '?' ':' '\x1F07'    -- GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI-  $ Entry '1' 'N' '\x2002'    -- EN SPACE-  $ Entry '1' 'M' '\x2003'    -- EM SPACE-  $ Entry '3' 'M' '\x2004'    -- THREE-PER-EM SPACE-  $ Entry '4' 'M' '\x2005'    -- FOUR-PER-EM SPACE-  $ Entry '6' 'M' '\x2006'    -- SIX-PER-EM SPACE-  $ Entry '1' 'T' '\x2009'    -- THIN SPACE-  $ Entry '1' 'H' '\x200A'    -- HAIR SPACE-  $ Entry '-' '1' '\x2010'    -- HYPHEN-  $ Entry '-' 'N' '\x2013'    -- EN DASH `-  $ Entry '-' 'M' '\x2014'    -- EM DASH-  $ Entry '-' '3' '\x2015'    -- HORIZONTAL BAR-  $ Entry '!' '2' '\x2016'    -- DOUBLE VERTICAL LINE-  $ Entry '=' '2' '\x2017'    -- DOUBLE LOW LINE-  $ Entry '\'' '6' '\x2018'    -- LEFT SINGLE QUOTATION MARK-  $ Entry '\'' '9' '\x2019'    -- RIGHT SINGLE QUOTATION MARK-  $ Entry '.' '9' '\x201A'    -- SINGLE LOW-9 QUOTATION MARK-  $ Entry '9' '\'' '\x201B'    -- SINGLE HIGH-REVERSED-9 QUOTATION MARK-  $ Entry '"' '6' '\x201C'   -- LEFT DOUBLE QUOTATION MARK-  $ Entry '"' '9' '\x201D'   -- RIGHT DOUBLE QUOTATION MARK-  $ Entry ':' '9' '\x201E'    -- DOUBLE LOW-9 QUOTATION MARK-  $ Entry '9' '"' '\x201F'   -- DOUBLE HIGH-REVERSED-9 QUOTATION MARK-  $ Entry '/' '-' '\x2020'    -- DAGGER-  $ Entry '/' '=' '\x2021'    -- DOUBLE DAGGER-  $ Entry '.' '.' '\x2025'    -- TWO DOT LEADER-  $ Entry '%' '0' '\x2030'    -- PER MILLE SIGN-  $ Entry '1' '\'' '\x2032'    -- PRIME-  $ Entry '2' '\'' '\x2033'    -- DOUBLE PRIME-  $ Entry '3' '\'' '\x2034'    -- TRIPLE PRIME-  $ Entry '1' '"' '\x2035'   -- REVERSED PRIME-  $ Entry '2' '"' '\x2036'   -- REVERSED DOUBLE PRIME-  $ Entry '3' '"' '\x2037'   -- REVERSED TRIPLE PRIME-  $ Entry 'C' 'a' '\x2038'    -- CARET-  $ Entry '<' '1' '\x2039'    -- SINGLE LEFT-POINTING ANGLE QUOTATION MARK-  $ Entry '>' '1' '\x203A'    -- SINGLE RIGHT-POINTING ANGLE QUOTATION MARK-  $ Entry ':' 'X' '\x203B'    -- REFERENCE MARK-  $ Entry '\'' '-' '\x203E'    -- OVERLINE-  $ Entry '/' 'f' '\x2044'    -- FRACTION SLASH-  $ Entry '0' 'S' '\x2070'    -- SUPERSCRIPT ZERO-  $ Entry '4' 'S' '\x2074'    -- SUPERSCRIPT FOUR-  $ Entry '5' 'S' '\x2075'    -- SUPERSCRIPT FIVE-  $ Entry '6' 'S' '\x2076'    -- SUPERSCRIPT SIX-  $ Entry '7' 'S' '\x2077'    -- SUPERSCRIPT SEVEN-  $ Entry '8' 'S' '\x2078'    -- SUPERSCRIPT EIGHT-  $ Entry '9' 'S' '\x2079'    -- SUPERSCRIPT NINE-  $ Entry '+' 'S' '\x207A'    -- SUPERSCRIPT PLUS SIGN-  $ Entry '-' 'S' '\x207B'    -- SUPERSCRIPT MINUS-  $ Entry '=' 'S' '\x207C'    -- SUPERSCRIPT EQUALS SIGN-  $ Entry '(' 'S' '\x207D'    -- SUPERSCRIPT LEFT PARENTHESIS-  $ Entry ')' 'S' '\x207E'    -- SUPERSCRIPT RIGHT PARENTHESIS-  $ Entry 'n' 'S' '\x207F'    -- SUPERSCRIPT LATIN SMALL LETTER N `-  $ Entry '0' 's' '\x2080'    -- SUBSCRIPT ZERO-  $ Entry '1' 's' '\x2081'    -- SUBSCRIPT ONE-  $ Entry '2' 's' '\x2082'    -- SUBSCRIPT TWO-  $ Entry '3' 's' '\x2083'    -- SUBSCRIPT THREE-  $ Entry '4' 's' '\x2084'    -- SUBSCRIPT FOUR-  $ Entry '5' 's' '\x2085'    -- SUBSCRIPT FIVE-  $ Entry '6' 's' '\x2086'    -- SUBSCRIPT SIX-  $ Entry '7' 's' '\x2087'    -- SUBSCRIPT SEVEN-  $ Entry '8' 's' '\x2088'    -- SUBSCRIPT EIGHT-  $ Entry '9' 's' '\x2089'    -- SUBSCRIPT NINE-  $ Entry '+' 's' '\x208A'    -- SUBSCRIPT PLUS SIGN-  $ Entry '-' 's' '\x208B'    -- SUBSCRIPT MINUS-  $ Entry '=' 's' '\x208C'    -- SUBSCRIPT EQUALS SIGN-  $ Entry '(' 's' '\x208D'    -- SUBSCRIPT LEFT PARENTHESIS-  $ Entry ')' 's' '\x208E'    -- SUBSCRIPT RIGHT PARENTHESIS-  $ Entry 'L' 'i' '\x20A4'    -- LIRA SIGN-  $ Entry 'P' 't' '\x20A7'    -- PESETA SIGN-  $ Entry 'W' '=' '\x20A9'    -- WON SIGN-  $ Entry 'E' 'u' '\x20AC'    -- EURO SIGN-  $ Entry 'o' 'C' '\x2103'    -- DEGREE CELSIUS-  $ Entry 'c' 'o' '\x2105'    -- CARE OF-  $ Entry 'o' 'F' '\x2109'    -- DEGREE FAHRENHEIT-  $ Entry 'N' '0' '\x2116'    -- NUMERO SIGN-  $ Entry 'P' 'O' '\x2117'    -- SOUND RECORDING COPYRIGHT-  $ Entry 'R' 'x' '\x211E'    -- PRESCRIPTION TAKE-  $ Entry 'S' 'M' '\x2120'    -- SERVICE MARK-  $ Entry 'T' 'M' '\x2122'    -- TRADE MARK SIGN-  $ Entry 'O' 'm' '\x2126'    -- OHM SIGN-  $ Entry 'A' 'O' '\x212B'    -- ANGSTROM SIGN-  $ Entry '1' '3' '\x2153'    -- VULGAR FRACTION ONE THIRD-  $ Entry '2' '3' '\x2154'    -- VULGAR FRACTION TWO THIRDS-  $ Entry '1' '5' '\x2155'    -- VULGAR FRACTION ONE FIFTH-  $ Entry '2' '5' '\x2156'    -- VULGAR FRACTION TWO FIFTHS-  $ Entry '3' '5' '\x2157'    -- VULGAR FRACTION THREE FIFTHS-  $ Entry '4' '5' '\x2158'    -- VULGAR FRACTION FOUR FIFTHS-  $ Entry '1' '6' '\x2159'    -- VULGAR FRACTION ONE SIXTH-  $ Entry '5' '6' '\x215A'    -- VULGAR FRACTION FIVE SIXTHS-  $ Entry '1' '8' '\x215B'    -- VULGAR FRACTION ONE EIGHTH-  $ Entry '3' '8' '\x215C'    -- VULGAR FRACTION THREE EIGHTHS-  $ Entry '5' '8' '\x215D'    -- VULGAR FRACTION FIVE EIGHTHS-  $ Entry '7' '8' '\x215E'    -- VULGAR FRACTION SEVEN EIGHTHS-  $ Entry '1' 'R' '\x2160'    -- ROMAN NUMERAL ONE-  $ Entry '2' 'R' '\x2161'    -- ROMAN NUMERAL TWO-  $ Entry '3' 'R' '\x2162'    -- ROMAN NUMERAL THREE-  $ Entry '4' 'R' '\x2163'    -- ROMAN NUMERAL FOUR-  $ Entry '5' 'R' '\x2164'    -- ROMAN NUMERAL FIVE-  $ Entry '6' 'R' '\x2165'    -- ROMAN NUMERAL SIX-  $ Entry '7' 'R' '\x2166'    -- ROMAN NUMERAL SEVEN-  $ Entry '8' 'R' '\x2167'    -- ROMAN NUMERAL EIGHT-  $ Entry '9' 'R' '\x2168'    -- ROMAN NUMERAL NINE-  $ Entry 'a' 'R' '\x2169'    -- ROMAN NUMERAL TEN-  $ Entry 'b' 'R' '\x216A'    -- ROMAN NUMERAL ELEVEN-  $ Entry 'c' 'R' '\x216B'    -- ROMAN NUMERAL TWELVE-  $ Entry '1' 'r' '\x2170'    -- SMALL ROMAN NUMERAL ONE-  $ Entry '2' 'r' '\x2171'    -- SMALL ROMAN NUMERAL TWO-  $ Entry '3' 'r' '\x2172'    -- SMALL ROMAN NUMERAL THREE-  $ Entry '4' 'r' '\x2173'    -- SMALL ROMAN NUMERAL FOUR-  $ Entry '5' 'r' '\x2174'    -- SMALL ROMAN NUMERAL FIVE-  $ Entry '6' 'r' '\x2175'    -- SMALL ROMAN NUMERAL SIX-  $ Entry '7' 'r' '\x2176'    -- SMALL ROMAN NUMERAL SEVEN-  $ Entry '8' 'r' '\x2177'    -- SMALL ROMAN NUMERAL EIGHT-  $ Entry '9' 'r' '\x2178'    -- SMALL ROMAN NUMERAL NINE-  $ Entry 'a' 'r' '\x2179'    -- SMALL ROMAN NUMERAL TEN-  $ Entry 'b' 'r' '\x217A'    -- SMALL ROMAN NUMERAL ELEVEN-  $ Entry 'c' 'r' '\x217B'    -- SMALL ROMAN NUMERAL TWELVE-  $ Entry '<' '-' '\x2190'    -- LEFTWARDS ARROW-  $ Entry '-' '!' '\x2191'    -- UPWARDS ARROW-  $ Entry '-' '>' '\x2192'    -- RIGHTWARDS ARROW-  $ Entry '-' 'v' '\x2193'    -- DOWNWARDS ARROW-  $ Entry '<' '>' '\x2194'    -- LEFT RIGHT ARROW-  $ Entry 'U' 'D' '\x2195'    -- UP DOWN ARROW-  $ Entry '<' '=' '\x21D0'    -- LEFTWARDS DOUBLE ARROW-  $ Entry '=' '>' '\x21D2'    -- RIGHTWARDS DOUBLE ARROW-  $ Entry '=' '=' '\x21D4'    -- LEFT RIGHT DOUBLE ARROW-  $ Entry 'F' 'A' '\x2200'    -- FOR ALL-  $ Entry 'd' 'P' '\x2202'    -- PARTIAL DIFFERENTIAL-  $ Entry 'T' 'E' '\x2203'    -- THERE EXISTS-  $ Entry '/' '0' '\x2205'    -- EMPTY SET-  $ Entry 'D' 'E' '\x2206'    -- INCREMENT-  $ Entry 'N' 'B' '\x2207'    -- NABLA-  $ Entry '(' '-' '\x2208'    -- ELEMENT OF-  $ Entry '-' ')' '\x220B'    -- CONTAINS AS MEMBER-  $ Entry '*' 'P' '\x220F'    -- N-ARY PRODUCT `-  $ Entry '+' 'Z' '\x2211'    -- N-ARY SUMMATION `-  $ Entry '-' '2' '\x2212'    -- MINUS SIGN-  $ Entry '-' '+' '\x2213'    -- MINUS-OR-PLUS SIGN-  $ Entry '*' '-' '\x2217'    -- ASTERISK OPERATOR-  $ Entry 'O' 'b' '\x2218'    -- RING OPERATOR-  $ Entry 'S' 'b' '\x2219'    -- BULLET OPERATOR-  $ Entry 'R' 'T' '\x221A'    -- SQUARE ROOT-  $ Entry '0' '(' '\x221D'    -- PROPORTIONAL TO-  $ Entry '0' '0' '\x221E'    -- INFINITY-  $ Entry '-' 'L' '\x221F'    -- RIGHT ANGLE-  $ Entry '-' 'V' '\x2220'    -- ANGLE-  $ Entry 'P' 'P' '\x2225'    -- PARALLEL TO-  $ Entry 'A' 'N' '\x2227'    -- LOGICAL AND-  $ Entry 'O' 'R' '\x2228'    -- LOGICAL OR-  $ Entry '(' 'U' '\x2229'    -- INTERSECTION-  $ Entry ')' 'U' '\x222A'    -- UNION-  $ Entry 'I' 'n' '\x222B'    -- INTEGRAL-  $ Entry 'D' 'I' '\x222C'    -- DOUBLE INTEGRAL-  $ Entry 'I' 'o' '\x222E'    -- CONTOUR INTEGRAL-  $ Entry '.' ':' '\x2234'    -- THEREFORE-  $ Entry ':' '.' '\x2235'    -- BECAUSE-  $ Entry ':' 'R' '\x2236'    -- RATIO-  $ Entry ':' ':' '\x2237'    -- PROPORTION-  $ Entry '?' '1' '\x223C'    -- TILDE OPERATOR-  $ Entry 'C' 'G' '\x223E'    -- INVERTED LAZY S-  $ Entry '?' '-' '\x2243'    -- ASYMPTOTICALLY EQUAL TO-  $ Entry '?' '=' '\x2245'    -- APPROXIMATELY EQUAL TO-  $ Entry '?' '2' '\x2248'    -- ALMOST EQUAL TO-  $ Entry '=' '?' '\x224C'    -- ALL EQUAL TO-  $ Entry 'H' 'I' '\x2253'    -- IMAGE OF OR APPROXIMATELY EQUAL TO-  $ Entry '!' '=' '\x2260'    -- NOT EQUAL TO-  $ Entry '=' '3' '\x2261'    -- IDENTICAL TO-  $ Entry '=' '<' '\x2264'    -- LESS-THAN OR EQUAL TO-  $ Entry '>' '=' '\x2265'    -- GREATER-THAN OR EQUAL TO-  $ Entry '<' '*' '\x226A'    -- MUCH LESS-THAN-  $ Entry '*' '>' '\x226B'    -- MUCH GREATER-THAN-  $ Entry '!' '<' '\x226E'    -- NOT LESS-THAN-  $ Entry '!' '>' '\x226F'    -- NOT GREATER-THAN-  $ Entry '(' 'C' '\x2282'    -- SUBSET OF-  $ Entry ')' 'C' '\x2283'    -- SUPERSET OF-  $ Entry '(' '_' '\x2286'    -- SUBSET OF OR EQUAL TO-  $ Entry ')' '_' '\x2287'    -- SUPERSET OF OR EQUAL TO-  $ Entry '0' '.' '\x2299'    -- CIRCLED DOT OPERATOR-  $ Entry '0' '2' '\x229A'    -- CIRCLED RING OPERATOR-  $ Entry '-' 'T' '\x22A5'    -- UP TACK-  $ Entry '.' 'P' '\x22C5'    -- DOT OPERATOR-  $ Entry ':' '3' '\x22EE'    -- VERTICAL ELLIPSIS-  $ Entry '.' '3' '\x22EF'    -- MIDLINE HORIZONTAL ELLIPSIS-  $ Entry 'E' 'h' '\x2302'    -- HOUSE-  $ Entry '<' '7' '\x2308'    -- LEFT CEILING-  $ Entry '>' '7' '\x2309'    -- RIGHT CEILING-  $ Entry '7' '<' '\x230A'    -- LEFT FLOOR-  $ Entry '7' '>' '\x230B'    -- RIGHT FLOOR-  $ Entry 'N' 'I' '\x2310'    -- REVERSED NOT SIGN-  $ Entry '(' 'A' '\x2312'    -- ARC-  $ Entry 'T' 'R' '\x2315'    -- TELEPHONE RECORDER-  $ Entry 'I' 'u' '\x2320'    -- TOP HALF INTEGRAL-  $ Entry 'I' 'l' '\x2321'    -- BOTTOM HALF INTEGRAL-  $ Entry '<' '/' '\x2329'    -- LEFT-POINTING ANGLE BRACKET-  $ Entry '/' '>' '\x232A'    -- RIGHT-POINTING ANGLE BRACKET-  $ Entry 'V' 's' '\x2423'    -- OPEN BOX-  $ Entry '1' 'h' '\x2440'    -- OCR HOOK-  $ Entry '3' 'h' '\x2441'    -- OCR CHAIR-  $ Entry '2' 'h' '\x2442'    -- OCR FORK-  $ Entry '4' 'h' '\x2443'    -- OCR INVERTED FORK-  $ Entry '1' 'j' '\x2446'    -- OCR BRANCH BANK IDENTIFICATION-  $ Entry '2' 'j' '\x2447'    -- OCR AMOUNT OF CHECK-  $ Entry '3' 'j' '\x2448'    -- OCR DASH-  $ Entry '4' 'j' '\x2449'    -- OCR CUSTOMER ACCOUNT NUMBER-  $ Entry '1' '.' '\x2488'    -- DIGIT ONE FULL STOP-  $ Entry '2' '.' '\x2489'    -- DIGIT TWO FULL STOP-  $ Entry '3' '.' '\x248A'    -- DIGIT THREE FULL STOP-  $ Entry '4' '.' '\x248B'    -- DIGIT FOUR FULL STOP-  $ Entry '5' '.' '\x248C'    -- DIGIT FIVE FULL STOP-  $ Entry '6' '.' '\x248D'    -- DIGIT SIX FULL STOP-  $ Entry '7' '.' '\x248E'    -- DIGIT SEVEN FULL STOP-  $ Entry '8' '.' '\x248F'    -- DIGIT EIGHT FULL STOP-  $ Entry '9' '.' '\x2490'    -- DIGIT NINE FULL STOP-  $ Entry 'h' 'h' '\x2500'    -- BOX DRAWINGS LIGHT HORIZONTAL-  $ Entry 'H' 'H' '\x2501'    -- BOX DRAWINGS HEAVY HORIZONTAL-  $ Entry 'v' 'v' '\x2502'    -- BOX DRAWINGS LIGHT VERTICAL-  $ Entry 'V' 'V' '\x2503'    -- BOX DRAWINGS HEAVY VERTICAL-  $ Entry '3' '-' '\x2504'    -- BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL-  $ Entry '3' '_' '\x2505'    -- BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL-  $ Entry '3' '!' '\x2506'    -- BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL-  $ Entry '3' '/' '\x2507'    -- BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL-  $ Entry '4' '-' '\x2508'    -- BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL-  $ Entry '4' '_' '\x2509'    -- BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL-  $ Entry '4' '!' '\x250A'    -- BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL-  $ Entry '4' '/' '\x250B'    -- BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL-  $ Entry 'd' 'r' '\x250C'    -- BOX DRAWINGS LIGHT DOWN AND RIGHT-  $ Entry 'd' 'R' '\x250D'    -- BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY-  $ Entry 'D' 'r' '\x250E'    -- BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT-  $ Entry 'D' 'R' '\x250F'    -- BOX DRAWINGS HEAVY DOWN AND RIGHT-  $ Entry 'd' 'l' '\x2510'    -- BOX DRAWINGS LIGHT DOWN AND LEFT-  $ Entry 'd' 'L' '\x2511'    -- BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY-  $ Entry 'D' 'l' '\x2512'    -- BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT-  $ Entry 'L' 'D' '\x2513'    -- BOX DRAWINGS HEAVY DOWN AND LEFT-  $ Entry 'u' 'r' '\x2514'    -- BOX DRAWINGS LIGHT UP AND RIGHT-  $ Entry 'u' 'R' '\x2515'    -- BOX DRAWINGS UP LIGHT AND RIGHT HEAVY-  $ Entry 'U' 'r' '\x2516'    -- BOX DRAWINGS UP HEAVY AND RIGHT LIGHT-  $ Entry 'U' 'R' '\x2517'    -- BOX DRAWINGS HEAVY UP AND RIGHT-  $ Entry 'u' 'l' '\x2518'    -- BOX VOICED SOUND MARKDRAWINGS LIGHT UP AND LEFT-  $ Entry 'u' 'L' '\x2519'    -- BOX DRAWINGS UP LIGHT AND LEFT HEAVY-  $ Entry 'U' 'l' '\x251A'    -- BOX DRAWINGS UP HEAVY AND LEFT LIGHT-  $ Entry 'U' 'L' '\x251B'    -- BOX DRAWINGS HEAVY UP AND LEFT-  $ Entry 'v' 'r' '\x251C'    -- BOX DRAWINGS LIGHT VERTICAL AND RIGHT-  $ Entry 'v' 'R' '\x251D'    -- BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY-  $ Entry 'V' 'r' '\x2520'    -- BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT-  $ Entry 'V' 'R' '\x2523'    -- BOX DRAWINGS HEAVY VERTICAL AND RIGHT-  $ Entry 'v' 'l' '\x2524'    -- BOX DRAWINGS LIGHT VERTICAL AND LEFT-  $ Entry 'v' 'L' '\x2525'    -- BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY-  $ Entry 'V' 'l' '\x2528'    -- BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT-  $ Entry 'V' 'L' '\x252B'    -- BOX DRAWINGS HEAVY VERTICAL AND LEFT-  $ Entry 'd' 'h' '\x252C'    -- BOX DRAWINGS LIGHT DOWN AND HORIZONTAL-  $ Entry 'd' 'H' '\x252F'    -- BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY-  $ Entry 'D' 'h' '\x2530'    -- BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT-  $ Entry 'D' 'H' '\x2533'    -- BOX DRAWINGS HEAVY DOWN AND HORIZONTAL-  $ Entry 'u' 'h' '\x2534'    -- BOX DRAWINGS LIGHT UP AND HORIZONTAL-  $ Entry 'u' 'H' '\x2537'    -- BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY-  $ Entry 'U' 'h' '\x2538'    -- BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT-  $ Entry 'U' 'H' '\x253B'    -- BOX DRAWINGS HEAVY UP AND HORIZONTAL-  $ Entry 'v' 'h' '\x253C'    -- BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL-  $ Entry 'v' 'H' '\x253F'    -- BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY-  $ Entry 'V' 'h' '\x2542'    -- BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT-  $ Entry 'V' 'H' '\x254B'    -- BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL-  $ Entry 'F' 'D' '\x2571'    -- BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT-  $ Entry 'B' 'D' '\x2572'    -- BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT-  $ Entry 'T' 'B' '\x2580'    -- UPPER HALF BLOCK-  $ Entry 'L' 'B' '\x2584'    -- LOWER HALF BLOCK-  $ Entry 'F' 'B' '\x2588'    -- FULL BLOCK-  $ Entry 'l' 'B' '\x258C'    -- LEFT HALF BLOCK-  $ Entry 'R' 'B' '\x2590'    -- RIGHT HALF BLOCK-  $ Entry '.' 'S' '\x2591'    -- LIGHT SHADE-  $ Entry ':' 'S' '\x2592'    -- MEDIUM SHADE-  $ Entry '?' 'S' '\x2593'    -- DARK SHADE-  $ Entry 'f' 'S' '\x25A0'    -- BLACK SQUARE-  $ Entry 'O' 'S' '\x25A1'    -- WHITE SQUARE-  $ Entry 'R' 'O' '\x25A2'    -- WHITE SQUARE WITH ROUNDED CORNERS-  $ Entry 'R' 'r' '\x25A3'    -- WHITE SQUARE CONTAINING BLACK SMALL SQUARE-  $ Entry 'R' 'F' '\x25A4'    -- SQUARE WITH HORIZONTAL FILL-  $ Entry 'R' 'Y' '\x25A5'    -- SQUARE WITH VERTICAL FILL-  $ Entry 'R' 'H' '\x25A6'    -- SQUARE WITH ORTHOGONAL CROSSHATCH FILL-  $ Entry 'R' 'Z' '\x25A7'    -- SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL-  $ Entry 'R' 'K' '\x25A8'    -- SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL-  $ Entry 'R' 'X' '\x25A9'    -- SQUARE WITH DIAGONAL CROSSHATCH FILL-  $ Entry 's' 'B' '\x25AA'    -- BLACK SMALL SQUARE-  $ Entry 'S' 'R' '\x25AC'    -- BLACK RECTANGLE-  $ Entry 'O' 'r' '\x25AD'    -- WHITE RECTANGLE-  $ Entry 'U' 'T' '\x25B2'    -- BLACK UP-POINTING TRIANGLE-  $ Entry 'u' 'T' '\x25B3'    -- WHITE UP-POINTING TRIANGLE-  $ Entry 'P' 'R' '\x25B6'    -- BLACK RIGHT-POINTING TRIANGLE-  $ Entry 'T' 'r' '\x25B7'    -- WHITE RIGHT-POINTING TRIANGLE-  $ Entry 'D' 't' '\x25BC'    -- BLACK DOWN-POINTING TRIANGLE-  $ Entry 'd' 'T' '\x25BD'    -- WHITE DOWN-POINTING TRIANGLE-  $ Entry 'P' 'L' '\x25C0'    -- BLACK LEFT-POINTING TRIANGLE-  $ Entry 'T' 'l' '\x25C1'    -- WHITE LEFT-POINTING TRIANGLE-  $ Entry 'D' 'b' '\x25C6'    -- BLACK DIAMOND-  $ Entry 'D' 'w' '\x25C7'    -- WHITE DIAMOND-  $ Entry 'L' 'Z' '\x25CA'    -- LOZENGE-  $ Entry '0' 'm' '\x25CB'    -- WHITE CIRCLE-  $ Entry '0' 'o' '\x25CE'    -- BULLSEYE-  $ Entry '0' 'M' '\x25CF'    -- BLACK CIRCLE-  $ Entry '0' 'L' '\x25D0'    -- CIRCLE WITH LEFT HALF BLACK-  $ Entry '0' 'R' '\x25D1'    -- CIRCLE WITH RIGHT HALF BLACK-  $ Entry 'S' 'n' '\x25D8'    -- INVERSE BULLET-  $ Entry 'I' 'c' '\x25D9'    -- INVERSE WHITE CIRCLE-  $ Entry 'F' 'd' '\x25E2'    -- BLACK LOWER RIGHT TRIANGLE-  $ Entry 'B' 'd' '\x25E3'    -- BLACK LOWER LEFT TRIANGLE-  $ Entry '*' '2' '\x2605'    -- BLACK STAR-  $ Entry '*' '1' '\x2606'    -- WHITE STAR-  $ Entry '<' 'H' '\x261C'    -- WHITE LEFT POINTING INDEX-  $ Entry '>' 'H' '\x261E'    -- WHITE RIGHT POINTING INDEX-  $ Entry '0' 'u' '\x263A'    -- WHITE SMILING FACE-  $ Entry '0' 'U' '\x263B'    -- BLACK SMILING FACE-  $ Entry 'S' 'U' '\x263C'    -- WHITE SUN WITH RAYS-  $ Entry 'F' 'm' '\x2640'    -- FEMALE SIGN-  $ Entry 'M' 'l' '\x2642'    -- MALE SIGN-  $ Entry 'c' 'S' '\x2660'    -- BLACK SPADE SUIT-  $ Entry 'c' 'H' '\x2661'    -- WHITE HEART SUIT-  $ Entry 'c' 'D' '\x2662'    -- WHITE DIAMOND SUIT-  $ Entry 'c' 'C' '\x2663'    -- BLACK CLUB SUIT-  $ Entry 'M' 'd' '\x2669'    -- QUARTER NOTE `-  $ Entry 'M' '8' '\x266A'    -- EIGHTH NOTE `-  $ Entry 'M' '2' '\x266B'    -- BEAMED EIGHTH NOTES-  $ Entry 'M' 'b' '\x266D'    -- MUSIC FLAT SIGN-  $ Entry 'M' 'x' '\x266E'    -- MUSIC NATURAL SIGN-  $ Entry 'M' 'X' '\x266F'    -- MUSIC SHARP SIGN-  $ Entry 'O' 'K' '\x2713'    -- CHECK MARK-  $ Entry 'X' 'X' '\x2717'    -- BALLOT X-  $ Entry '-' 'X' '\x2720'    -- MALTESE CROSS-  $ Entry 'I' 'S' '\x3000'    -- IDEOGRAPHIC SPACE-  $ Entry ',' '_' '\x3001'    -- IDEOGRAPHIC COMMA-  $ Entry '.' '_' '\x3002'    -- IDEOGRAPHIC FULL STOP-  $ Entry '+' '"' '\x3003'   -- DITTO MARK-  $ Entry '+' '_' '\x3004'    -- JAPANESE INDUSTRIAL STANDARD SYMBOL-  $ Entry '*' '_' '\x3005'    -- IDEOGRAPHIC ITERATION MARK-  $ Entry ';' '_' '\x3006'    -- IDEOGRAPHIC CLOSING MARK-  $ Entry '0' '_' '\x3007'    -- IDEOGRAPHIC NUMBER ZERO-  $ Entry '<' '+' '\x300A'    -- LEFT DOUBLE ANGLE BRACKET-  $ Entry '>' '+' '\x300B'    -- RIGHT DOUBLE ANGLE BRACKET-  $ Entry '<' '\'' '\x300C'    -- LEFT CORNER BRACKET-  $ Entry '>' '\'' '\x300D'    -- RIGHT CORNER BRACKET-  $ Entry '<' '"' '\x300E'   -- LEFT WHITE CORNER BRACKET-  $ Entry '>' '"' '\x300F'   -- RIGHT WHITE CORNER BRACKET-  $ Entry '(' '"' '\x3010'   -- LEFT BLACK LENTICULAR BRACKET-  $ Entry ')' '"' '\x3011'   -- RIGHT BLACK LENTICULAR BRACKET-  $ Entry '=' 'T' '\x3012'    -- POSTAL MARK-  $ Entry '=' '_' '\x3013'    -- GETA MARK-  $ Entry '(' '\'' '\x3014'    -- LEFT TORTOISE SHELL BRACKET-  $ Entry ')' '\'' '\x3015'    -- RIGHT TORTOISE SHELL BRACKET-  $ Entry '(' 'I' '\x3016'    -- LEFT WHITE LENTICULAR BRACKET-  $ Entry ')' 'I' '\x3017'    -- RIGHT WHITE LENTICULAR BRACKET-  $ Entry '-' '?' '\x301C'    -- WAVE DASH-  $ Entry 'A' '5' '\x3041'    -- HIRAGANA LETTER SMALL A-  $ Entry 'a' '5' '\x3042'    -- HIRAGANA LETTER A-  $ Entry 'I' '5' '\x3043'    -- HIRAGANA LETTER SMALL I-  $ Entry 'i' '5' '\x3044'    -- HIRAGANA LETTER I-  $ Entry 'U' '5' '\x3045'    -- HIRAGANA LETTER SMALL U-  $ Entry 'u' '5' '\x3046'    -- HIRAGANA LETTER U-  $ Entry 'E' '5' '\x3047'    -- HIRAGANA LETTER SMALL E-  $ Entry 'e' '5' '\x3048'    -- HIRAGANA LETTER E-  $ Entry 'O' '5' '\x3049'    -- HIRAGANA LETTER SMALL O-  $ Entry 'o' '5' '\x304A'    -- HIRAGANA LETTER O-  $ Entry 'k' 'a' '\x304B'    -- HIRAGANA LETTER KA-  $ Entry 'g' 'a' '\x304C'    -- HIRAGANA LETTER GA-  $ Entry 'k' 'i' '\x304D'    -- HIRAGANA LETTER KI-  $ Entry 'g' 'i' '\x304E'    -- HIRAGANA LETTER GI-  $ Entry 'k' 'u' '\x304F'    -- HIRAGANA LETTER KU-  $ Entry 'g' 'u' '\x3050'    -- HIRAGANA LETTER GU-  $ Entry 'k' 'e' '\x3051'    -- HIRAGANA LETTER KE-  $ Entry 'g' 'e' '\x3052'    -- HIRAGANA LETTER GE-  $ Entry 'k' 'o' '\x3053'    -- HIRAGANA LETTER KO-  $ Entry 'g' 'o' '\x3054'    -- HIRAGANA LETTER GO-  $ Entry 's' 'a' '\x3055'    -- HIRAGANA LETTER SA-  $ Entry 'z' 'a' '\x3056'    -- HIRAGANA LETTER ZA-  $ Entry 's' 'i' '\x3057'    -- HIRAGANA LETTER SI-  $ Entry 'z' 'i' '\x3058'    -- HIRAGANA LETTER ZI-  $ Entry 's' 'u' '\x3059'    -- HIRAGANA LETTER SU-  $ Entry 'z' 'u' '\x305A'    -- HIRAGANA LETTER ZU-  $ Entry 's' 'e' '\x305B'    -- HIRAGANA LETTER SE-  $ Entry 'z' 'e' '\x305C'    -- HIRAGANA LETTER ZE-  $ Entry 's' 'o' '\x305D'    -- HIRAGANA LETTER SO-  $ Entry 'z' 'o' '\x305E'    -- HIRAGANA LETTER ZO-  $ Entry 't' 'a' '\x305F'    -- HIRAGANA LETTER TA-  $ Entry 'd' 'a' '\x3060'    -- HIRAGANA LETTER DA-  $ Entry 't' 'i' '\x3061'    -- HIRAGANA LETTER TI-  $ Entry 'd' 'i' '\x3062'    -- HIRAGANA LETTER DI-  $ Entry 't' 'U' '\x3063'    -- HIRAGANA LETTER SMALL TU-  $ Entry 't' 'u' '\x3064'    -- HIRAGANA LETTER TU-  $ Entry 'd' 'u' '\x3065'    -- HIRAGANA LETTER DU-  $ Entry 't' 'e' '\x3066'    -- HIRAGANA LETTER TE-  $ Entry 'd' 'e' '\x3067'    -- HIRAGANA LETTER DE-  $ Entry 't' 'o' '\x3068'    -- HIRAGANA LETTER TO-  $ Entry 'd' 'o' '\x3069'    -- HIRAGANA LETTER DO-  $ Entry 'n' 'a' '\x306A'    -- HIRAGANA LETTER NA-  $ Entry 'n' 'i' '\x306B'    -- HIRAGANA LETTER NI-  $ Entry 'n' 'u' '\x306C'    -- HIRAGANA LETTER NU-  $ Entry 'n' 'e' '\x306D'    -- HIRAGANA LETTER NE-  $ Entry 'n' 'o' '\x306E'    -- HIRAGANA LETTER NO-  $ Entry 'h' 'a' '\x306F'    -- HIRAGANA LETTER HA-  $ Entry 'b' 'a' '\x3070'    -- HIRAGANA LETTER BA-  $ Entry 'p' 'a' '\x3071'    -- HIRAGANA LETTER PA-  $ Entry 'h' 'i' '\x3072'    -- HIRAGANA LETTER HI-  $ Entry 'b' 'i' '\x3073'    -- HIRAGANA LETTER BI-  $ Entry 'p' 'i' '\x3074'    -- HIRAGANA LETTER PI-  $ Entry 'h' 'u' '\x3075'    -- HIRAGANA LETTER HU-  $ Entry 'b' 'u' '\x3076'    -- HIRAGANA LETTER BU-  $ Entry 'p' 'u' '\x3077'    -- HIRAGANA LETTER PU-  $ Entry 'h' 'e' '\x3078'    -- HIRAGANA LETTER HE-  $ Entry 'b' 'e' '\x3079'    -- HIRAGANA LETTER BE-  $ Entry 'p' 'e' '\x307A'    -- HIRAGANA LETTER PE-  $ Entry 'h' 'o' '\x307B'    -- HIRAGANA LETTER HO-  $ Entry 'b' 'o' '\x307C'    -- HIRAGANA LETTER BO-  $ Entry 'p' 'o' '\x307D'    -- HIRAGANA LETTER PO-  $ Entry 'm' 'a' '\x307E'    -- HIRAGANA LETTER MA-  $ Entry 'm' 'i' '\x307F'    -- HIRAGANA LETTER MI-  $ Entry 'm' 'u' '\x3080'    -- HIRAGANA LETTER MU-  $ Entry 'm' 'e' '\x3081'    -- HIRAGANA LETTER ME-  $ Entry 'm' 'o' '\x3082'    -- HIRAGANA LETTER MO-  $ Entry 'y' 'A' '\x3083'    -- HIRAGANA LETTER SMALL YA-  $ Entry 'y' 'a' '\x3084'    -- HIRAGANA LETTER YA-  $ Entry 'y' 'U' '\x3085'    -- HIRAGANA LETTER SMALL YU-  $ Entry 'y' 'u' '\x3086'    -- HIRAGANA LETTER YU-  $ Entry 'y' 'O' '\x3087'    -- HIRAGANA LETTER SMALL YO-  $ Entry 'y' 'o' '\x3088'    -- HIRAGANA LETTER YO-  $ Entry 'r' 'a' '\x3089'    -- HIRAGANA LETTER RA-  $ Entry 'r' 'i' '\x308A'    -- HIRAGANA LETTER RI-  $ Entry 'r' 'u' '\x308B'    -- HIRAGANA LETTER RU-  $ Entry 'r' 'e' '\x308C'    -- HIRAGANA LETTER RE-  $ Entry 'r' 'o' '\x308D'    -- HIRAGANA LETTER RO-  $ Entry 'w' 'A' '\x308E'    -- HIRAGANA LETTER SMALL WA-  $ Entry 'w' 'a' '\x308F'    -- HIRAGANA LETTER WA-  $ Entry 'w' 'i' '\x3090'    -- HIRAGANA LETTER WI-  $ Entry 'w' 'e' '\x3091'    -- HIRAGANA LETTER WE-  $ Entry 'w' 'o' '\x3092'    -- HIRAGANA LETTER WO-  $ Entry 'n' '5' '\x3093'    -- HIRAGANA LETTER N `-  $ Entry 'v' 'u' '\x3094'    -- HIRAGANA LETTER VU-  $ Entry '"' '5' '\x309B'   -- KATAKANA-HIRAGANA VOICED SOUND MARK-  $ Entry '0' '5' '\x309C'    -- KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK-  $ Entry '*' '5' '\x309D'    -- HIRAGANA ITERATION MARK-  $ Entry '+' '5' '\x309E'    -- HIRAGANA VOICED ITERATION MARK-  $ Entry 'a' '6' '\x30A1'    -- KATAKANA LETTER SMALL A-  $ Entry 'A' '6' '\x30A2'    -- KATAKANA LETTER A-  $ Entry 'i' '6' '\x30A3'    -- KATAKANA LETTER SMALL I-  $ Entry 'I' '6' '\x30A4'    -- KATAKANA LETTER I-  $ Entry 'u' '6' '\x30A5'    -- KATAKANA LETTER SMALL U-  $ Entry 'U' '6' '\x30A6'    -- KATAKANA LETTER U-  $ Entry 'e' '6' '\x30A7'    -- KATAKANA LETTER SMALL E-  $ Entry 'E' '6' '\x30A8'    -- KATAKANA LETTER E-  $ Entry 'o' '6' '\x30A9'    -- KATAKANA LETTER SMALL O-  $ Entry 'O' '6' '\x30AA'    -- KATAKANA LETTER O-  $ Entry 'K' 'a' '\x30AB'    -- KATAKANA LETTER KA-  $ Entry 'G' 'a' '\x30AC'    -- KATAKANA LETTER GA-  $ Entry 'K' 'i' '\x30AD'    -- KATAKANA LETTER KI-  $ Entry 'G' 'i' '\x30AE'    -- KATAKANA LETTER GI-  $ Entry 'K' 'u' '\x30AF'    -- KATAKANA LETTER KU-  $ Entry 'G' 'u' '\x30B0'    -- KATAKANA LETTER GU-  $ Entry 'K' 'e' '\x30B1'    -- KATAKANA LETTER KE-  $ Entry 'G' 'e' '\x30B2'    -- KATAKANA LETTER GE-  $ Entry 'K' 'o' '\x30B3'    -- KATAKANA LETTER KO-  $ Entry 'G' 'o' '\x30B4'    -- KATAKANA LETTER GO-  $ Entry 'S' 'a' '\x30B5'    -- KATAKANA LETTER SA-  $ Entry 'Z' 'a' '\x30B6'    -- KATAKANA LETTER ZA-  $ Entry 'S' 'i' '\x30B7'    -- KATAKANA LETTER SI-  $ Entry 'Z' 'i' '\x30B8'    -- KATAKANA LETTER ZI-  $ Entry 'S' 'u' '\x30B9'    -- KATAKANA LETTER SU-  $ Entry 'Z' 'u' '\x30BA'    -- KATAKANA LETTER ZU-  $ Entry 'S' 'e' '\x30BB'    -- KATAKANA LETTER SE-  $ Entry 'Z' 'e' '\x30BC'    -- KATAKANA LETTER ZE-  $ Entry 'S' 'o' '\x30BD'    -- KATAKANA LETTER SO-  $ Entry 'Z' 'o' '\x30BE'    -- KATAKANA LETTER ZO-  $ Entry 'T' 'a' '\x30BF'    -- KATAKANA LETTER TA-  $ Entry 'D' 'a' '\x30C0'    -- KATAKANA LETTER DA-  $ Entry 'T' 'i' '\x30C1'    -- KATAKANA LETTER TI-  $ Entry 'D' 'i' '\x30C2'    -- KATAKANA LETTER DI-  $ Entry 'T' 'U' '\x30C3'    -- KATAKANA LETTER SMALL TU-  $ Entry 'T' 'u' '\x30C4'    -- KATAKANA LETTER TU-  $ Entry 'D' 'u' '\x30C5'    -- KATAKANA LETTER DU-  $ Entry 'T' 'e' '\x30C6'    -- KATAKANA LETTER TE-  $ Entry 'D' 'e' '\x30C7'    -- KATAKANA LETTER DE-  $ Entry 'T' 'o' '\x30C8'    -- KATAKANA LETTER TO-  $ Entry 'D' 'o' '\x30C9'    -- KATAKANA LETTER DO-  $ Entry 'N' 'a' '\x30CA'    -- KATAKANA LETTER NA-  $ Entry 'N' 'i' '\x30CB'    -- KATAKANA LETTER NI-  $ Entry 'N' 'u' '\x30CC'    -- KATAKANA LETTER NU-  $ Entry 'N' 'e' '\x30CD'    -- KATAKANA LETTER NE-  $ Entry 'N' 'o' '\x30CE'    -- KATAKANA LETTER NO-  $ Entry 'H' 'a' '\x30CF'    -- KATAKANA LETTER HA-  $ Entry 'B' 'a' '\x30D0'    -- KATAKANA LETTER BA-  $ Entry 'P' 'a' '\x30D1'    -- KATAKANA LETTER PA-  $ Entry 'H' 'i' '\x30D2'    -- KATAKANA LETTER HI-  $ Entry 'B' 'i' '\x30D3'    -- KATAKANA LETTER BI-  $ Entry 'P' 'i' '\x30D4'    -- KATAKANA LETTER PI-  $ Entry 'H' 'u' '\x30D5'    -- KATAKANA LETTER HU-  $ Entry 'B' 'u' '\x30D6'    -- KATAKANA LETTER BU-  $ Entry 'P' 'u' '\x30D7'    -- KATAKANA LETTER PU-  $ Entry 'H' 'e' '\x30D8'    -- KATAKANA LETTER HE-  $ Entry 'B' 'e' '\x30D9'    -- KATAKANA LETTER BE-  $ Entry 'P' 'e' '\x30DA'    -- KATAKANA LETTER PE-  $ Entry 'H' 'o' '\x30DB'    -- KATAKANA LETTER HO-  $ Entry 'B' 'o' '\x30DC'    -- KATAKANA LETTER BO-  $ Entry 'P' 'o' '\x30DD'    -- KATAKANA LETTER PO-  $ Entry 'M' 'a' '\x30DE'    -- KATAKANA LETTER MA-  $ Entry 'M' 'i' '\x30DF'    -- KATAKANA LETTER MI-  $ Entry 'M' 'u' '\x30E0'    -- KATAKANA LETTER MU-  $ Entry 'M' 'e' '\x30E1'    -- KATAKANA LETTER ME-  $ Entry 'M' 'o' '\x30E2'    -- KATAKANA LETTER MO-  $ Entry 'Y' 'A' '\x30E3'    -- KATAKANA LETTER SMALL YA-  $ Entry 'Y' 'a' '\x30E4'    -- KATAKANA LETTER YA-  $ Entry 'Y' 'U' '\x30E5'    -- KATAKANA LETTER SMALL YU-  $ Entry 'Y' 'u' '\x30E6'    -- KATAKANA LETTER YU-  $ Entry 'Y' 'O' '\x30E7'    -- KATAKANA LETTER SMALL YO-  $ Entry 'Y' 'o' '\x30E8'    -- KATAKANA LETTER YO-  $ Entry 'R' 'a' '\x30E9'    -- KATAKANA LETTER RA-  $ Entry 'R' 'i' '\x30EA'    -- KATAKANA LETTER RI-  $ Entry 'R' 'u' '\x30EB'    -- KATAKANA LETTER RU-  $ Entry 'R' 'e' '\x30EC'    -- KATAKANA LETTER RE-  $ Entry 'R' 'o' '\x30ED'    -- KATAKANA LETTER RO-  $ Entry 'W' 'A' '\x30EE'    -- KATAKANA LETTER SMALL WA-  $ Entry 'W' 'a' '\x30EF'    -- KATAKANA LETTER WA-  $ Entry 'W' 'i' '\x30F0'    -- KATAKANA LETTER WI-  $ Entry 'W' 'e' '\x30F1'    -- KATAKANA LETTER WE-  $ Entry 'W' 'o' '\x30F2'    -- KATAKANA LETTER WO-  $ Entry 'N' '6' '\x30F3'    -- KATAKANA LETTER N `-  $ Entry 'V' 'u' '\x30F4'    -- KATAKANA LETTER VU-  $ Entry 'K' 'A' '\x30F5'    -- KATAKANA LETTER SMALL KA-  $ Entry 'K' 'E' '\x30F6'    -- KATAKANA LETTER SMALL KE-  $ Entry 'V' 'a' '\x30F7'    -- KATAKANA LETTER VA-  $ Entry 'V' 'i' '\x30F8'    -- KATAKANA LETTER VI-  $ Entry 'V' 'e' '\x30F9'    -- KATAKANA LETTER VE-  $ Entry 'V' 'o' '\x30FA'    -- KATAKANA LETTER VO-  $ Entry '.' '6' '\x30FB'    -- KATAKANA MIDDLE DOT-  $ Entry '-' '6' '\x30FC'    -- KATAKANA-HIRAGANA PROLONGED SOUND MARK-  $ Entry '*' '6' '\x30FD'    -- KATAKANA ITERATION MARK-  $ Entry '+' '6' '\x30FE'    -- KATAKANA VOICED ITERATION MARK-  $ Entry 'b' '4' '\x3105'    -- BOPOMOFO LETTER B-  $ Entry 'p' '4' '\x3106'    -- BOPOMOFO LETTER P-  $ Entry 'm' '4' '\x3107'    -- BOPOMOFO LETTER M-  $ Entry 'f' '4' '\x3108'    -- BOPOMOFO LETTER F-  $ Entry 'd' '4' '\x3109'    -- BOPOMOFO LETTER D-  $ Entry 't' '4' '\x310A'    -- BOPOMOFO LETTER T-  $ Entry 'n' '4' '\x310B'    -- BOPOMOFO LETTER N `-  $ Entry 'l' '4' '\x310C'    -- BOPOMOFO LETTER L-  $ Entry 'g' '4' '\x310D'    -- BOPOMOFO LETTER G-  $ Entry 'k' '4' '\x310E'    -- BOPOMOFO LETTER K-  $ Entry 'h' '4' '\x310F'    -- BOPOMOFO LETTER H-  $ Entry 'j' '4' '\x3110'    -- BOPOMOFO LETTER J-  $ Entry 'q' '4' '\x3111'    -- BOPOMOFO LETTER Q-  $ Entry 'x' '4' '\x3112'    -- BOPOMOFO LETTER X-  $ Entry 'z' 'h' '\x3113'    -- BOPOMOFO LETTER ZH-  $ Entry 'c' 'h' '\x3114'    -- BOPOMOFO LETTER CH-  $ Entry 's' 'h' '\x3115'    -- BOPOMOFO LETTER SH-  $ Entry 'r' '4' '\x3116'    -- BOPOMOFO LETTER R-  $ Entry 'z' '4' '\x3117'    -- BOPOMOFO LETTER Z-  $ Entry 'c' '4' '\x3118'    -- BOPOMOFO LETTER C-  $ Entry 's' '4' '\x3119'    -- BOPOMOFO LETTER S-  $ Entry 'a' '4' '\x311A'    -- BOPOMOFO LETTER A-  $ Entry 'o' '4' '\x311B'    -- BOPOMOFO LETTER O-  $ Entry 'e' '4' '\x311C'    -- BOPOMOFO LETTER E-  $ Entry 'a' 'i' '\x311E'    -- BOPOMOFO LETTER AI-  $ Entry 'e' 'i' '\x311F'    -- BOPOMOFO LETTER EI-  $ Entry 'a' 'u' '\x3120'    -- BOPOMOFO LETTER AU-  $ Entry 'o' 'u' '\x3121'    -- BOPOMOFO LETTER OU-  $ Entry 'a' 'n' '\x3122'    -- BOPOMOFO LETTER AN-  $ Entry 'e' 'n' '\x3123'    -- BOPOMOFO LETTER EN-  $ Entry 'a' 'N' '\x3124'    -- BOPOMOFO LETTER ANG-  $ Entry 'e' 'N' '\x3125'    -- BOPOMOFO LETTER ENG-  $ Entry 'e' 'r' '\x3126'    -- BOPOMOFO LETTER ER-  $ Entry 'i' '4' '\x3127'    -- BOPOMOFO LETTER I-  $ Entry 'u' '4' '\x3128'    -- BOPOMOFO LETTER U-  $ Entry 'i' 'u' '\x3129'    -- BOPOMOFO LETTER IU-  $ Entry 'v' '4' '\x312A'    -- BOPOMOFO LETTER V-  $ Entry 'n' 'G' '\x312B'    -- BOPOMOFO LETTER NG-  $ Entry 'g' 'n' '\x312C'    -- BOPOMOFO LETTER GN-  $ Entry '1' 'c' '\x3220'    -- PARENTHESIZED IDEOGRAPH ONE-  $ Entry '2' 'c' '\x3221'    -- PARENTHESIZED IDEOGRAPH TWO-  $ Entry '3' 'c' '\x3222'    -- PARENTHESIZED IDEOGRAPH THREE-  $ Entry '4' 'c' '\x3223'    -- PARENTHESIZED IDEOGRAPH FOUR-  $ Entry '5' 'c' '\x3224'    -- PARENTHESIZED IDEOGRAPH FIVE-  $ Entry '6' 'c' '\x3225'    -- PARENTHESIZED IDEOGRAPH SIX-  $ Entry '7' 'c' '\x3226'    -- PARENTHESIZED IDEOGRAPH SEVEN-  $ Entry '8' 'c' '\x3227'    -- PARENTHESIZED IDEOGRAPH EIGHT-  $ Entry '9' 'c' '\x3228'    -- PARENTHESIZED IDEOGRAPH NINE-  $ Entry 'f' 'f' '\xFB00'    -- LATIN SMALL LIGATURE FF-  $ Entry 'f' 'i' '\xFB01'    -- LATIN SMALL LIGATURE FI-  $ Entry 'f' 'l' '\xFB02'    -- LATIN SMALL LIGATURE FL-  $ Entry 'f' 't' '\xFB05'    -- LATIN SMALL LIGATURE LONG S T-  $ Entry 's' 't' '\xFB06'    -- LATIN SMALL LIGATURE ST-  $ Nil+{-# Language BangPatterns, QuasiQuotes, OverloadedStrings #-}+{-|+Module      : Digraphs+Description : Character mnemonics+Copyright   : (c) Eric Mertens, 2016+License     : ISC+Maintainer  : emertens@gmail.com++This module provides an implementation of /digraphs/ as implemented+in Vim and as specified in RFC 1345 (2-character only).++<https://tools.ietf.org/html/rfc1345>+<http://vimdoc.sourceforge.net/htmldoc/digraph.html>++-}+module Digraphs+  ( lookupDigraph+  , digraphs+  ) where++import           DigraphQuote+import           Data.Text (Text)+import qualified Data.Text as Text++-- | States for the digraph lookup state machine+data St+  = Ready -- ^ ready to match two-character name+  | Match -- ^ first character matched, ready for second+  | Found -- ^ both characters matched, return the next+  | Skip2 -- ^ skip the next two characters+  | Skip1 -- ^ skip the next character++-- | Find the entry in the digraph table give a two-character+-- key and return the matched value.+lookupDigraph :: Char -> Char -> Maybe Char+lookupDigraph !x !y = Text.foldr step finish digraphs Ready+  where+    finish _st = Nothing+    step z k st =+      case st of+        Ready | x == z    -> k Match+              | otherwise -> k Skip2+        Match | y == z    -> k Found+              | otherwise -> k Skip1+        Skip1             -> k Ready+        Skip2             -> k Skip1+        Found             -> Just z++------------------------------------------------------------------------++-- These are explicitly prohibited by the IRC RFC+-- NU U+0000 -- NULL (NUL)+-- LF U+000a -- LINE FEED (LF)+-- CR U+000d -- CARRIAGE RETURN (CR)++digraphs :: Text+digraphs = [digraphTable|+  SH U+0001 -- START OF HEADING (SOH)+  SX U+0002 -- START OF TEXT (STX)+  EX U+0003 -- END OF TEXT (ETX)+  ET U+0004 -- END OF TRANSMISSION (EOT)+  EQ U+0005 -- ENQUIRY (ENQ)+  AK U+0006 -- ACKNOWLEDGE (ACK)+  BL U+0007 -- BELL (BEL)+  BS U+0008 -- BACKSPACE (BS)+  HT U+0009 -- CHARACTER TABULATION (HT)+  VT U+000B -- LINE TABULATION (VT)+  FF U+000C -- FORM FEED (FF)+  SO U+000E -- SHIFT OUT (SO)+  SI U+000F -- SHIFT IN (SI)+  DL U+0010 -- DATALINK ESCAPE (DLE)+  D1 U+0011 -- DEVICE CONTROL ONE (DC1)+  D2 U+0012 -- DEVICE CONTROL TWO (DC2)+  D3 U+0013 -- DEVICE CONTROL THREE (DC3)+  D4 U+0014 -- DEVICE CONTROL FOUR (DC4)+  NK U+0015 -- NEGATIVE ACKNOWLEDGE (NAK)+  SY U+0016 -- SYNCHRONOUS IDLE (SYN)+  EB U+0017 -- END OF TRANSMISSION BLOCK (ETB)+  CN U+0018 -- CANCEL (CAN)+  EM U+0019 -- END OF MEDIUM (EM)+  SB U+001A -- SUBSTITUTE (SUB)+  EC U+001B -- ESCAPE (ESC)+  FS U+001C -- FILE SEPARATOR (IS4)+  GS U+001D -- GROUP SEPARATOR (IS3)+  RS U+001E -- RECORD SEPARATOR (IS2)+  US U+001F -- UNIT SEPARATOR (IS1)+  SP U+0020 -- SPACE+  Nb U+0023 -- NUMBER SIGN+  DO U+0024 -- DOLLAR SIGN+  At U+0040 -- COMMERCIAL AT+  <( U+005B -- LEFT SQUARE BRACKET+  // U+005C -- REVERSE SOLIDUS+  )> U+005D -- RIGHT SQUARE BRACKET+  '> U+005E -- CIRCUMFLEX ACCENT+  '! U+0060 -- GRAVE ACCENT+  (! U+007B -- LEFT CURLY BRACKET+  !! U+007C -- VERTICAL LINE+  !) U+007D -- RIGHT CURLY BRACKET+  '? U+007E -- TILDE+  DT U+007F -- DELETE (DEL)+  PA U+0080 -- PADDING CHARACTER (PAD)+  HO U+0081 -- HIGH OCTET PRESET (HOP)+  BH U+0082 -- BREAK PERMITTED HERE (BPH)+  NH U+0083 -- NO BREAK HERE (NBH)+  IN U+0084 -- INDEX (IND)+  NL U+0085 -- NEXT LINE (NEL)+  SA U+0086 -- START OF SELECTED AREA (SSA)+  ES U+0087 -- END OF SELECTED AREA (ESA)+  HS U+0088 -- CHARACTER TABULATION SET (HTS)+  HJ U+0089 -- CHARACTER TABULATION WITH JUSTIFICATION (HTJ)+  VS U+008A -- LINE TABULATION SET (VTS)+  PD U+008B -- PARTIAL LINE FORWARD (PLD)+  PU U+008C -- PARTIAL LINE BACKWARD (PLU)+  RI U+008D -- REVERSE LINE FEED (RI)+  S2 U+008E -- SINGLE-SHIFT TWO (SS2)+  S3 U+008F -- SINGLE-SHIFT THREE (SS3)+  DC U+0090 -- DEVICE CONTROL STRING (DCS)+  P1 U+0091 -- PRIVATE USE ONE (PU1)+  P2 U+0092 -- PRIVATE USE TWO (PU2)+  TS U+0093 -- SET TRANSMIT STATE (STS)+  CC U+0094 -- CANCEL CHARACTER (CCH)+  MW U+0095 -- MESSAGE WAITING (MW)+  SG U+0096 -- START OF GUARDED AREA (SPA)+  EG U+0097 -- END OF GUARDED AREA (EPA)+  SS U+0098 -- START OF STRING (SOS)+  GC U+0099 -- SINGLE GRAPHIC CHARACTER INTRODUCER (SGCI)+  SC U+009A -- SINGLE CHARACTER INTRODUCER (SCI)+  CI U+009B -- CONTROL SEQUENCE INTRODUCER (CSI)+  ST U+009C -- STRING TERMINATOR (ST)+  OC U+009D -- OPERATING SYSTEM COMMAND (OSC)+  PM U+009E -- PRIVACY MESSAGE (PM)+  AC U+009F -- APPLICATION PROGRAM COMMAND (APC)+  NS U+00A0 -- NO-BREAK SPACE+  !I U+00A1 -- INVERTED EXCLAMATION MARK+  Ct U+00A2 -- CENT SIGN+  Pd U+00A3 -- POUND SIGN+  Cu U+00A4 -- CURRENCY SIGN+  Ye U+00A5 -- YEN SIGN+  BB U+00A6 -- BROKEN BAR+  SE U+00A7 -- SECTION SIGN+  ': U+00A8 -- DIAERESIS+  Co U+00A9 -- COPYRIGHT SIGN+  -a U+00AA -- FEMININE ORDINAL INDICATOR+  << U+00AB -- LEFT-POINTING DOUBLE ANGLE QUOTATION MARK+  NO U+00AC -- NOT SIGN+  -- U+00AD -- SOFT HYPHEN+  Rg U+00AE -- REGISTERED SIGN+  'm U+00AF -- MACRON+  DG U+00B0 -- DEGREE SIGN+  +- U+00B1 -- PLUS-MINUS SIGN+  2S U+00B2 -- SUPERSCRIPT TWO+  3S U+00B3 -- SUPERSCRIPT THREE+  '' U+00B4 -- ACUTE ACCENT+  My U+00B5 -- MICRO SIGN+  PI U+00B6 -- PILCROW SIGN+  .M U+00B7 -- MIDDLE DOT+  ', U+00B8 -- CEDILLA+  1S U+00B9 -- SUPERSCRIPT ONE+  -o U+00BA -- MASCULINE ORDINAL INDICATOR+  >> U+00BB -- RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK+  14 U+00BC -- VULGAR FRACTION ONE QUARTER+  12 U+00BD -- VULGAR FRACTION ONE HALF+  34 U+00BE -- VULGAR FRACTION THREE QUARTERS+  ?I U+00BF -- INVERTED QUESTION MARK+  A! U+00C0 -- LATIN CAPITAL LETTER A WITH GRAVE+  A' U+00C1 -- LATIN CAPITAL LETTER A WITH ACUTE+  A> U+00C2 -- LATIN CAPITAL LETTER A WITH CIRCUMFLEX+  A? U+00C3 -- LATIN CAPITAL LETTER A WITH TILDE+  A: U+00C4 -- LATIN CAPITAL LETTER A WITH DIAERESIS+  AA U+00C5 -- LATIN CAPITAL LETTER A WITH RING ABOVE+  AE U+00C6 -- LATIN CAPITAL LETTER AE+  C, U+00C7 -- LATIN CAPITAL LETTER C WITH CEDILLA+  E! U+00C8 -- LATIN CAPITAL LETTER E WITH GRAVE+  E' U+00C9 -- LATIN CAPITAL LETTER E WITH ACUTE+  E> U+00CA -- LATIN CAPITAL LETTER E WITH CIRCUMFLEX+  E: U+00CB -- LATIN CAPITAL LETTER E WITH DIAERESIS+  I! U+00CC -- LATIN CAPITAL LETTER I WITH GRAVE+  I' U+00CD -- LATIN CAPITAL LETTER I WITH ACUTE+  I> U+00CE -- LATIN CAPITAL LETTER I WITH CIRCUMFLEX+  I: U+00CF -- LATIN CAPITAL LETTER I WITH DIAERESIS+  D- U+00D0 -- LATIN CAPITAL LETTER ETH (Icelandic)+  N? U+00D1 -- LATIN CAPITAL LETTER N WITH TILDE+  O! U+00D2 -- LATIN CAPITAL LETTER O WITH GRAVE+  O' U+00D3 -- LATIN CAPITAL LETTER O WITH ACUTE+  O> U+00D4 -- LATIN CAPITAL LETTER O WITH CIRCUMFLEX+  O? U+00D5 -- LATIN CAPITAL LETTER O WITH TILDE+  O: U+00D6 -- LATIN CAPITAL LETTER O WITH DIAERESIS+  *X U+00D7 -- MULTIPLICATION SIGN+  O/ U+00D8 -- LATIN CAPITAL LETTER O WITH STROKE+  U! U+00D9 -- LATIN CAPITAL LETTER U WITH GRAVE+  U' U+00DA -- LATIN CAPITAL LETTER U WITH ACUTE+  U> U+00DB -- LATIN CAPITAL LETTER U WITH CIRCUMFLEX+  U: U+00DC -- LATIN CAPITAL LETTER U WITH DIAERESIS+  Y' U+00DD -- LATIN CAPITAL LETTER Y WITH ACUTE+  TH U+00DE -- LATIN CAPITAL LETTER THORN (Icelandic)+  ss U+00DF -- LATIN SMALL LETTER SHARP S (German)+  a! U+00E0 -- LATIN SMALL LETTER A WITH GRAVE+  a' U+00E1 -- LATIN SMALL LETTER A WITH ACUTE+  a> U+00E2 -- LATIN SMALL LETTER A WITH CIRCUMFLEX+  a? U+00E3 -- LATIN SMALL LETTER A WITH TILDE+  a: U+00E4 -- LATIN SMALL LETTER A WITH DIAERESIS+  aa U+00E5 -- LATIN SMALL LETTER A WITH RING ABOVE+  ae U+00E6 -- LATIN SMALL LETTER AE+  c, U+00E7 -- LATIN SMALL LETTER C WITH CEDILLA+  e! U+00E8 -- LATIN SMALL LETTER E WITH GRAVE+  e' U+00E9 -- LATIN SMALL LETTER E WITH ACUTE+  e> U+00EA -- LATIN SMALL LETTER E WITH CIRCUMFLEX+  e: U+00EB -- LATIN SMALL LETTER E WITH DIAERESIS+  i! U+00EC -- LATIN SMALL LETTER I WITH GRAVE+  i' U+00ED -- LATIN SMALL LETTER I WITH ACUTE+  i> U+00EE -- LATIN SMALL LETTER I WITH CIRCUMFLEX+  i: U+00EF -- LATIN SMALL LETTER I WITH DIAERESIS+  d- U+00F0 -- LATIN SMALL LETTER ETH (Icelandic)+  n? U+00F1 -- LATIN SMALL LETTER N WITH TILDE+  o! U+00F2 -- LATIN SMALL LETTER O WITH GRAVE+  o' U+00F3 -- LATIN SMALL LETTER O WITH ACUTE+  o> U+00F4 -- LATIN SMALL LETTER O WITH CIRCUMFLEX+  o? U+00F5 -- LATIN SMALL LETTER O WITH TILDE+  o: U+00F6 -- LATIN SMALL LETTER O WITH DIAERESIS+  -: U+00F7 -- DIVISION SIGN+  o/ U+00F8 -- LATIN SMALL LETTER O WITH STROKE+  u! U+00F9 -- LATIN SMALL LETTER U WITH GRAVE+  u' U+00FA -- LATIN SMALL LETTER U WITH ACUTE+  u> U+00FB -- LATIN SMALL LETTER U WITH CIRCUMFLEX+  u: U+00FC -- LATIN SMALL LETTER U WITH DIAERESIS+  y' U+00FD -- LATIN SMALL LETTER Y WITH ACUTE+  th U+00FE -- LATIN SMALL LETTER THORN (Icelandic)+  y: U+00FF -- LATIN SMALL LETTER Y WITH DIAERESIS+  A- U+0100 -- LATIN CAPITAL LETTER A WITH MACRON+  a- U+0101 -- LATIN SMALL LETTER A WITH MACRON+  A( U+0102 -- LATIN CAPITAL LETTER A WITH BREVE+  a( U+0103 -- LATIN SMALL LETTER A WITH BREVE+  A; U+0104 -- LATIN CAPITAL LETTER A WITH OGONEK+  a; U+0105 -- LATIN SMALL LETTER A WITH OGONEK+  C' U+0106 -- LATIN CAPITAL LETTER C WITH ACUTE+  c' U+0107 -- LATIN SMALL LETTER C WITH ACUTE+  C> U+0108 -- LATIN CAPITAL LETTER C WITH CIRCUMFLEX+  c> U+0109 -- LATIN SMALL LETTER C WITH CIRCUMFLEX+  C. U+010A -- LATIN CAPITAL LETTER C WITH DOT ABOVE+  c. U+010B -- LATIN SMALL LETTER C WITH DOT ABOVE+  C< U+010C -- LATIN CAPITAL LETTER C WITH CARON+  c< U+010D -- LATIN SMALL LETTER C WITH CARON+  D< U+010E -- LATIN CAPITAL LETTER D WITH CARON+  d< U+010F -- LATIN SMALL LETTER D WITH CARON+  D/ U+0110 -- LATIN CAPITAL LETTER D WITH STROKE+  d/ U+0111 -- LATIN SMALL LETTER D WITH STROKE+  E- U+0112 -- LATIN CAPITAL LETTER E WITH MACRON+  e- U+0113 -- LATIN SMALL LETTER E WITH MACRON+  E( U+0114 -- LATIN CAPITAL LETTER E WITH BREVE+  e( U+0115 -- LATIN SMALL LETTER E WITH BREVE+  E. U+0116 -- LATIN CAPITAL LETTER E WITH DOT ABOVE+  e. U+0117 -- LATIN SMALL LETTER E WITH DOT ABOVE+  E; U+0118 -- LATIN CAPITAL LETTER E WITH OGONEK+  e; U+0119 -- LATIN SMALL LETTER E WITH OGONEK+  E< U+011A -- LATIN CAPITAL LETTER E WITH CARON+  e< U+011B -- LATIN SMALL LETTER E WITH CARON+  G> U+011C -- LATIN CAPITAL LETTER G WITH CIRCUMFLEX+  g> U+011D -- LATIN SMALL LETTER G WITH CIRCUMFLEX+  G( U+011E -- LATIN CAPITAL LETTER G WITH BREVE+  g( U+011F -- LATIN SMALL LETTER G WITH BREVE+  G. U+0120 -- LATIN CAPITAL LETTER G WITH DOT ABOVE+  g. U+0121 -- LATIN SMALL LETTER G WITH DOT ABOVE+  G, U+0122 -- LATIN CAPITAL LETTER G WITH CEDILLA+  g, U+0123 -- LATIN SMALL LETTER G WITH CEDILLA+  H> U+0124 -- LATIN CAPITAL LETTER H WITH CIRCUMFLEX+  h> U+0125 -- LATIN SMALL LETTER H WITH CIRCUMFLEX+  H/ U+0126 -- LATIN CAPITAL LETTER H WITH STROKE+  h/ U+0127 -- LATIN SMALL LETTER H WITH STROKE+  I? U+0128 -- LATIN CAPITAL LETTER I WITH TILDE+  i? U+0129 -- LATIN SMALL LETTER I WITH TILDE+  I- U+012A -- LATIN CAPITAL LETTER I WITH MACRON+  i- U+012B -- LATIN SMALL LETTER I WITH MACRON+  I( U+012C -- LATIN CAPITAL LETTER I WITH BREVE+  i( U+012D -- LATIN SMALL LETTER I WITH BREVE+  I; U+012E -- LATIN CAPITAL LETTER I WITH OGONEK+  i; U+012F -- LATIN SMALL LETTER I WITH OGONEK+  I. U+0130 -- LATIN CAPITAL LETTER I WITH DOT ABOVE+  i. U+0131 -- LATIN SMALL LETTER DOTLESS I+  IJ U+0132 -- LATIN CAPITAL LIGATURE IJ+  ij U+0133 -- LATIN SMALL LIGATURE IJ+  J> U+0134 -- LATIN CAPITAL LETTER J WITH CIRCUMFLEX+  j> U+0135 -- LATIN SMALL LETTER J WITH CIRCUMFLEX+  K, U+0136 -- LATIN CAPITAL LETTER K WITH CEDILLA+  k, U+0137 -- LATIN SMALL LETTER K WITH CEDILLA+  kk U+0138 -- LATIN SMALL LETTER KRA+  L' U+0139 -- LATIN CAPITAL LETTER L WITH ACUTE+  l' U+013A -- LATIN SMALL LETTER L WITH ACUTE+  L, U+013B -- LATIN CAPITAL LETTER L WITH CEDILLA+  l, U+013C -- LATIN SMALL LETTER L WITH CEDILLA+  L< U+013D -- LATIN CAPITAL LETTER L WITH CARON+  l< U+013E -- LATIN SMALL LETTER L WITH CARON+  L. U+013F -- LATIN CAPITAL LETTER L WITH MIDDLE DOT+  l. U+0140 -- LATIN SMALL LETTER L WITH MIDDLE DOT+  L/ U+0141 -- LATIN CAPITAL LETTER L WITH STROKE+  l/ U+0142 -- LATIN SMALL LETTER L WITH STROKE+  N' U+0143 -- LATIN CAPITAL LETTER N WITH ACUTE `+  n' U+0144 -- LATIN SMALL LETTER N WITH ACUTE `+  N, U+0145 -- LATIN CAPITAL LETTER N WITH CEDILLA `+  n, U+0146 -- LATIN SMALL LETTER N WITH CEDILLA `+  N< U+0147 -- LATIN CAPITAL LETTER N WITH CARON `+  n< U+0148 -- LATIN SMALL LETTER N WITH CARON `+  'n U+0149 -- LATIN SMALL LETTER N PRECEDED BY APOSTROPHE `+  NG U+014A -- LATIN CAPITAL LETTER ENG+  ng U+014B -- LATIN SMALL LETTER ENG+  O- U+014C -- LATIN CAPITAL LETTER O WITH MACRON+  o- U+014D -- LATIN SMALL LETTER O WITH MACRON+  O( U+014E -- LATIN CAPITAL LETTER O WITH BREVE+  o( U+014F -- LATIN SMALL LETTER O WITH BREVE+  O" U+0150 -- LATIN CAPITAL LETTER O WITH DOUBLE ACUTE+  o" U+0151 -- LATIN SMALL LETTER O WITH DOUBLE ACUTE+  OE U+0152 -- LATIN CAPITAL LIGATURE OE+  oe U+0153 -- LATIN SMALL LIGATURE OE+  R' U+0154 -- LATIN CAPITAL LETTER R WITH ACUTE+  r' U+0155 -- LATIN SMALL LETTER R WITH ACUTE+  R, U+0156 -- LATIN CAPITAL LETTER R WITH CEDILLA+  r, U+0157 -- LATIN SMALL LETTER R WITH CEDILLA+  R< U+0158 -- LATIN CAPITAL LETTER R WITH CARON+  r< U+0159 -- LATIN SMALL LETTER R WITH CARON+  S' U+015A -- LATIN CAPITAL LETTER S WITH ACUTE+  s' U+015B -- LATIN SMALL LETTER S WITH ACUTE+  S> U+015C -- LATIN CAPITAL LETTER S WITH CIRCUMFLEX+  s> U+015D -- LATIN SMALL LETTER S WITH CIRCUMFLEX+  S, U+015E -- LATIN CAPITAL LETTER S WITH CEDILLA+  s, U+015F -- LATIN SMALL LETTER S WITH CEDILLA+  S< U+0160 -- LATIN CAPITAL LETTER S WITH CARON+  s< U+0161 -- LATIN SMALL LETTER S WITH CARON+  T, U+0162 -- LATIN CAPITAL LETTER T WITH CEDILLA+  t, U+0163 -- LATIN SMALL LETTER T WITH CEDILLA+  T< U+0164 -- LATIN CAPITAL LETTER T WITH CARON+  t< U+0165 -- LATIN SMALL LETTER T WITH CARON+  T/ U+0166 -- LATIN CAPITAL LETTER T WITH STROKE+  t/ U+0167 -- LATIN SMALL LETTER T WITH STROKE+  U? U+0168 -- LATIN CAPITAL LETTER U WITH TILDE+  u? U+0169 -- LATIN SMALL LETTER U WITH TILDE+  U- U+016A -- LATIN CAPITAL LETTER U WITH MACRON+  u- U+016B -- LATIN SMALL LETTER U WITH MACRON+  U( U+016C -- LATIN CAPITAL LETTER U WITH BREVE+  u( U+016D -- LATIN SMALL LETTER U WITH BREVE+  U0 U+016E -- LATIN CAPITAL LETTER U WITH RING ABOVE+  u0 U+016F -- LATIN SMALL LETTER U WITH RING ABOVE+  U" U+0170 -- LATIN CAPITAL LETTER U WITH DOUBLE ACUTE+  u" U+0171 -- LATIN SMALL LETTER U WITH DOUBLE ACUTE+  U; U+0172 -- LATIN CAPITAL LETTER U WITH OGONEK+  u; U+0173 -- LATIN SMALL LETTER U WITH OGONEK+  W> U+0174 -- LATIN CAPITAL LETTER W WITH CIRCUMFLEX+  w> U+0175 -- LATIN SMALL LETTER W WITH CIRCUMFLEX+  Y> U+0176 -- LATIN CAPITAL LETTER Y WITH CIRCUMFLEX+  y> U+0177 -- LATIN SMALL LETTER Y WITH CIRCUMFLEX+  Y: U+0178 -- LATIN CAPITAL LETTER Y WITH DIAERESIS+  Z' U+0179 -- LATIN CAPITAL LETTER Z WITH ACUTE+  z' U+017A -- LATIN SMALL LETTER Z WITH ACUTE+  Z. U+017B -- LATIN CAPITAL LETTER Z WITH DOT ABOVE+  z. U+017C -- LATIN SMALL LETTER Z WITH DOT ABOVE+  Z< U+017D -- LATIN CAPITAL LETTER Z WITH CARON+  z< U+017E -- LATIN SMALL LETTER Z WITH CARON+  O9 U+01A0 -- LATIN CAPITAL LETTER O WITH HORN+  o9 U+01A1 -- LATIN SMALL LETTER O WITH HORN+  OI U+01A2 -- LATIN CAPITAL LETTER OI+  oi U+01A3 -- LATIN SMALL LETTER OI+  yr U+01A6 -- LATIN LETTER YR+  U9 U+01AF -- LATIN CAPITAL LETTER U WITH HORN+  u9 U+01B0 -- LATIN SMALL LETTER U WITH HORN+  Z/ U+01B5 -- LATIN CAPITAL LETTER Z WITH STROKE+  z/ U+01B6 -- LATIN SMALL LETTER Z WITH STROKE+  ED U+01B7 -- LATIN CAPITAL LETTER EZH+  A< U+01CD -- LATIN CAPITAL LETTER A WITH CARON+  a< U+01CE -- LATIN SMALL LETTER A WITH CARON+  I< U+01CF -- LATIN CAPITAL LETTER I WITH CARON+  i< U+01D0 -- LATIN SMALL LETTER I WITH CARON+  O< U+01D1 -- LATIN CAPITAL LETTER O WITH CARON+  o< U+01D2 -- LATIN SMALL LETTER O WITH CARON+  U< U+01D3 -- LATIN CAPITAL LETTER U WITH CARON+  u< U+01D4 -- LATIN SMALL LETTER U WITH CARON+  A1 U+01DE -- LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON+  a1 U+01DF -- LATIN SMALL LETTER A WITH DIAERESIS AND MACRON+  A7 U+01E0 -- LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON+  a7 U+01E1 -- LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON+  A3 U+01E2 -- LATIN CAPITAL LETTER AE WITH MACRON+  a3 U+01E3 -- LATIN SMALL LETTER AE WITH MACRON+  G/ U+01E4 -- LATIN CAPITAL LETTER G WITH STROKE+  g/ U+01E5 -- LATIN SMALL LETTER G WITH STROKE+  G< U+01E6 -- LATIN CAPITAL LETTER G WITH CARON+  g< U+01E7 -- LATIN SMALL LETTER G WITH CARON+  K< U+01E8 -- LATIN CAPITAL LETTER K WITH CARON+  k< U+01E9 -- LATIN SMALL LETTER K WITH CARON+  O; U+01EA -- LATIN CAPITAL LETTER O WITH OGONEK+  o; U+01EB -- LATIN SMALL LETTER O WITH OGONEK+  O1 U+01EC -- LATIN CAPITAL LETTER O WITH OGONEK AND MACRON+  o1 U+01ED -- LATIN SMALL LETTER O WITH OGONEK AND MACRON+  EZ U+01EE -- LATIN CAPITAL LETTER EZH WITH CARON+  ez U+01EF -- LATIN SMALL LETTER EZH WITH CARON+  j< U+01F0 -- LATIN SMALL LETTER J WITH CARON+  G' U+01F4 -- LATIN CAPITAL LETTER G WITH ACUTE+  g' U+01F5 -- LATIN SMALL LETTER G WITH ACUTE+  ;S U+02BF -- MODIFIER LETTER LEFT HALF RING+  '< U+02C7 -- CARON+  '( U+02D8 -- BREVE+  '. U+02D9 -- DOT ABOVE+  '0 U+02DA -- RING ABOVE+  '; U+02DB -- OGONEK+  '" U+02DD -- DOUBLE ACUTE ACCENT+  A% U+0386 -- GREEK CAPITAL LETTER ALPHA WITH TONOS+  E% U+0388 -- GREEK CAPITAL LETTER EPSILON WITH TONOS+  Y% U+0389 -- GREEK CAPITAL LETTER ETA WITH TONOS+  I% U+038A -- GREEK CAPITAL LETTER IOTA WITH TONOS+  O% U+038C -- GREEK CAPITAL LETTER OMICRON WITH TONOS+  U% U+038E -- GREEK CAPITAL LETTER UPSILON WITH TONOS+  W% U+038F -- GREEK CAPITAL LETTER OMEGA WITH TONOS+  i3 U+0390 -- GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS+  A* U+0391 -- GREEK CAPITAL LETTER ALPHA+  B* U+0392 -- GREEK CAPITAL LETTER BETA+  G* U+0393 -- GREEK CAPITAL LETTER GAMMA+  D* U+0394 -- GREEK CAPITAL LETTER DELTA+  E* U+0395 -- GREEK CAPITAL LETTER EPSILON+  Z* U+0396 -- GREEK CAPITAL LETTER ZETA+  Y* U+0397 -- GREEK CAPITAL LETTER ETA+  H* U+0398 -- GREEK CAPITAL LETTER THETA+  I* U+0399 -- GREEK CAPITAL LETTER IOTA+  K* U+039A -- GREEK CAPITAL LETTER KAPPA+  L* U+039B -- GREEK CAPITAL LETTER LAMDA+  M* U+039C -- GREEK CAPITAL LETTER MU+  N* U+039D -- GREEK CAPITAL LETTER NU+  C* U+039E -- GREEK CAPITAL LETTER XI+  O* U+039F -- GREEK CAPITAL LETTER OMICRON+  P* U+03A0 -- GREEK CAPITAL LETTER PI+  R* U+03A1 -- GREEK CAPITAL LETTER RHO+  S* U+03A3 -- GREEK CAPITAL LETTER SIGMA+  T* U+03A4 -- GREEK CAPITAL LETTER TAU+  U* U+03A5 -- GREEK CAPITAL LETTER UPSILON+  F* U+03A6 -- GREEK CAPITAL LETTER PHI+  X* U+03A7 -- GREEK CAPITAL LETTER CHI+  Q* U+03A8 -- GREEK CAPITAL LETTER PSI+  W* U+03A9 -- GREEK CAPITAL LETTER OMEGA+  J* U+03AA -- GREEK CAPITAL LETTER IOTA WITH DIALYTIKA+  V* U+03AB -- GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA+  a% U+03AC -- GREEK SMALL LETTER ALPHA WITH TONOS+  e% U+03AD -- GREEK SMALL LETTER EPSILON WITH TONOS+  y% U+03AE -- GREEK SMALL LETTER ETA WITH TONOS+  i% U+03AF -- GREEK SMALL LETTER IOTA WITH TONOS+  u3 U+03B0 -- GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS+  a* U+03B1 -- GREEK SMALL LETTER ALPHA+  b* U+03B2 -- GREEK SMALL LETTER BETA+  g* U+03B3 -- GREEK SMALL LETTER GAMMA+  d* U+03B4 -- GREEK SMALL LETTER DELTA+  e* U+03B5 -- GREEK SMALL LETTER EPSILON+  z* U+03B6 -- GREEK SMALL LETTER ZETA+  y* U+03B7 -- GREEK SMALL LETTER ETA+  h* U+03B8 -- GREEK SMALL LETTER THETA+  i* U+03B9 -- GREEK SMALL LETTER IOTA+  k* U+03BA -- GREEK SMALL LETTER KAPPA+  l* U+03BB -- GREEK SMALL LETTER LAMDA+  m* U+03BC -- GREEK SMALL LETTER MU+  n* U+03BD -- GREEK SMALL LETTER NU+  c* U+03BE -- GREEK SMALL LETTER XI+  o* U+03BF -- GREEK SMALL LETTER OMICRON+  p* U+03C0 -- GREEK SMALL LETTER PI+  r* U+03C1 -- GREEK SMALL LETTER RHO+  *s U+03C2 -- GREEK SMALL LETTER FINAL SIGMA+  s* U+03C3 -- GREEK SMALL LETTER SIGMA+  t* U+03C4 -- GREEK SMALL LETTER TAU+  u* U+03C5 -- GREEK SMALL LETTER UPSILON+  f* U+03C6 -- GREEK SMALL LETTER PHI+  x* U+03C7 -- GREEK SMALL LETTER CHI+  q* U+03C8 -- GREEK SMALL LETTER PSI+  w* U+03C9 -- GREEK SMALL LETTER OMEGA+  j* U+03CA -- GREEK SMALL LETTER IOTA WITH DIALYTIKA+  v* U+03CB -- GREEK SMALL LETTER UPSILON WITH DIALYTIKA+  o% U+03CC -- GREEK SMALL LETTER OMICRON WITH TONOS+  u% U+03CD -- GREEK SMALL LETTER UPSILON WITH TONOS+  w% U+03CE -- GREEK SMALL LETTER OMEGA WITH TONOS+  'G U+03D8 -- GREEK LETTER ARCHAIC KOPPA+  ,G U+03D9 -- GREEK SMALL LETTER ARCHAIC KOPPA+  T3 U+03DA -- GREEK LETTER STIGMA+  t3 U+03DB -- GREEK SMALL LETTER STIGMA+  M3 U+03DC -- GREEK LETTER DIGAMMA+  m3 U+03DD -- GREEK SMALL LETTER DIGAMMA+  K3 U+03DE -- GREEK LETTER KOPPA+  k3 U+03DF -- GREEK SMALL LETTER KOPPA+  P3 U+03E0 -- GREEK LETTER SAMPI+  p3 U+03E1 -- GREEK SMALL LETTER SAMPI+  '% U+03F4 -- GREEK CAPITAL THETA SYMBOL+  j3 U+03F5 -- GREEK LUNATE EPSILON SYMBOL+  IO U+0401 -- CYRILLIC CAPITAL LETTER IO+  D% U+0402 -- CYRILLIC CAPITAL LETTER DJE+  G% U+0403 -- CYRILLIC CAPITAL LETTER GJE+  IE U+0404 -- CYRILLIC CAPITAL LETTER UKRAINIAN IE+  DS U+0405 -- CYRILLIC CAPITAL LETTER DZE+  II U+0406 -- CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I+  YI U+0407 -- CYRILLIC CAPITAL LETTER YI+  J% U+0408 -- CYRILLIC CAPITAL LETTER JE+  LJ U+0409 -- CYRILLIC CAPITAL LETTER LJE+  NJ U+040A -- CYRILLIC CAPITAL LETTER NJE+  Ts U+040B -- CYRILLIC CAPITAL LETTER TSHE+  KJ U+040C -- CYRILLIC CAPITAL LETTER KJE+  V% U+040E -- CYRILLIC CAPITAL LETTER SHORT U+  DZ U+040F -- CYRILLIC CAPITAL LETTER DZHE+  A= U+0410 -- CYRILLIC CAPITAL LETTER A+  B= U+0411 -- CYRILLIC CAPITAL LETTER BE+  V= U+0412 -- CYRILLIC CAPITAL LETTER VE+  G= U+0413 -- CYRILLIC CAPITAL LETTER GHE+  D= U+0414 -- CYRILLIC CAPITAL LETTER DE+  E= U+0415 -- CYRILLIC CAPITAL LETTER IE+  Z% U+0416 -- CYRILLIC CAPITAL LETTER ZHE+  Z= U+0417 -- CYRILLIC CAPITAL LETTER ZE+  I= U+0418 -- CYRILLIC CAPITAL LETTER I+  J= U+0419 -- CYRILLIC CAPITAL LETTER SHORT I+  K= U+041A -- CYRILLIC CAPITAL LETTER KA+  L= U+041B -- CYRILLIC CAPITAL LETTER EL+  M= U+041C -- CYRILLIC CAPITAL LETTER EM+  N= U+041D -- CYRILLIC CAPITAL LETTER EN+  O= U+041E -- CYRILLIC CAPITAL LETTER O+  P= U+041F -- CYRILLIC CAPITAL LETTER PE+  R= U+0420 -- CYRILLIC CAPITAL LETTER ER+  S= U+0421 -- CYRILLIC CAPITAL LETTER ES+  T= U+0422 -- CYRILLIC CAPITAL LETTER TE+  U= U+0423 -- CYRILLIC CAPITAL LETTER U+  F= U+0424 -- CYRILLIC CAPITAL LETTER EF+  H= U+0425 -- CYRILLIC CAPITAL LETTER HA+  C= U+0426 -- CYRILLIC CAPITAL LETTER TSE+  C% U+0427 -- CYRILLIC CAPITAL LETTER CHE+  S% U+0428 -- CYRILLIC CAPITAL LETTER SHA+  Sc U+0429 -- CYRILLIC CAPITAL LETTER SHCHA+  =' U+042A -- CYRILLIC CAPITAL LETTER HARD SIGN+  Y= U+042B -- CYRILLIC CAPITAL LETTER YERU+  %' U+042C -- CYRILLIC CAPITAL LETTER SOFT SIGN+  JE U+042D -- CYRILLIC CAPITAL LETTER E+  JU U+042E -- CYRILLIC CAPITAL LETTER YU+  JA U+042F -- CYRILLIC CAPITAL LETTER YA+  a= U+0430 -- CYRILLIC SMALL LETTER A+  b= U+0431 -- CYRILLIC SMALL LETTER BE+  v= U+0432 -- CYRILLIC SMALL LETTER VE+  g= U+0433 -- CYRILLIC SMALL LETTER GHE+  d= U+0434 -- CYRILLIC SMALL LETTER DE+  e= U+0435 -- CYRILLIC SMALL LETTER IE+  z% U+0436 -- CYRILLIC SMALL LETTER ZHE+  z= U+0437 -- CYRILLIC SMALL LETTER ZE+  i= U+0438 -- CYRILLIC SMALL LETTER I+  j= U+0439 -- CYRILLIC SMALL LETTER SHORT I+  k= U+043A -- CYRILLIC SMALL LETTER KA+  l= U+043B -- CYRILLIC SMALL LETTER EL+  m= U+043C -- CYRILLIC SMALL LETTER EM+  n= U+043D -- CYRILLIC SMALL LETTER EN+  o= U+043E -- CYRILLIC SMALL LETTER O+  p= U+043F -- CYRILLIC SMALL LETTER PE+  r= U+0440 -- CYRILLIC SMALL LETTER ER+  s= U+0441 -- CYRILLIC SMALL LETTER ES+  t= U+0442 -- CYRILLIC SMALL LETTER TE+  u= U+0443 -- CYRILLIC SMALL LETTER U+  f= U+0444 -- CYRILLIC SMALL LETTER EF+  h= U+0445 -- CYRILLIC SMALL LETTER HA+  c= U+0446 -- CYRILLIC SMALL LETTER TSE+  c% U+0447 -- CYRILLIC SMALL LETTER CHE+  s% U+0448 -- CYRILLIC SMALL LETTER SHA+  sc U+0449 -- CYRILLIC SMALL LETTER SHCHA+  =' U+044A -- CYRILLIC SMALL LETTER HARD SIGN+  y= U+044B -- CYRILLIC SMALL LETTER YERU+  %' U+044C -- CYRILLIC SMALL LETTER SOFT SIGN+  je U+044D -- CYRILLIC SMALL LETTER E+  ju U+044E -- CYRILLIC SMALL LETTER YU+  ja U+044F -- CYRILLIC SMALL LETTER YA+  io U+0451 -- CYRILLIC SMALL LETTER IO+  d% U+0452 -- CYRILLIC SMALL LETTER DJE+  g% U+0453 -- CYRILLIC SMALL LETTER GJE+  ie U+0454 -- CYRILLIC SMALL LETTER UKRAINIAN IE+  ds U+0455 -- CYRILLIC SMALL LETTER DZE+  ii U+0456 -- CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I+  yi U+0457 -- CYRILLIC SMALL LETTER YI+  j% U+0458 -- CYRILLIC SMALL LETTER JE+  lj U+0459 -- CYRILLIC SMALL LETTER LJE+  nj U+045A -- CYRILLIC SMALL LETTER NJE+  ts U+045B -- CYRILLIC SMALL LETTER TSHE+  kj U+045C -- CYRILLIC SMALL LETTER KJE+  v% U+045E -- CYRILLIC SMALL LETTER SHORT U+  dz U+045F -- CYRILLIC SMALL LETTER DZHE+  Y3 U+0462 -- CYRILLIC CAPITAL LETTER YAT+  y3 U+0463 -- CYRILLIC SMALL LETTER YAT+  O3 U+046A -- CYRILLIC CAPITAL LETTER BIG YUS+  o3 U+046B -- CYRILLIC SMALL LETTER BIG YUS+  F3 U+0472 -- CYRILLIC CAPITAL LETTER FITA+  f3 U+0473 -- CYRILLIC SMALL LETTER FITA+  V3 U+0474 -- CYRILLIC CAPITAL LETTER IZHITSA+  v3 U+0475 -- CYRILLIC SMALL LETTER IZHITSA+  C3 U+0480 -- CYRILLIC CAPITAL LETTER KOPPA+  c3 U+0481 -- CYRILLIC SMALL LETTER KOPPA+  G3 U+0490 -- CYRILLIC CAPITAL LETTER GHE WITH UPTURN+  g3 U+0491 -- CYRILLIC SMALL LETTER GHE WITH UPTURN+  A+ U+05D0 -- HEBREW LETTER ALEF+  B+ U+05D1 -- HEBREW LETTER BET+  G+ U+05D2 -- HEBREW LETTER GIMEL+  D+ U+05D3 -- HEBREW LETTER DALET+  H+ U+05D4 -- HEBREW LETTER HE+  W+ U+05D5 -- HEBREW LETTER VAV+  Z+ U+05D6 -- HEBREW LETTER ZAYIN+  X+ U+05D7 -- HEBREW LETTER HET+  Tj U+05D8 -- HEBREW LETTER TET+  J+ U+05D9 -- HEBREW LETTER YOD+  K% U+05DA -- HEBREW LETTER FINAL KAF+  K+ U+05DB -- HEBREW LETTER KAF+  L+ U+05DC -- HEBREW LETTER LAMED+  M% U+05DD -- HEBREW LETTER FINAL MEM+  M+ U+05DE -- HEBREW LETTER MEM+  N% U+05DF -- HEBREW LETTER FINAL NUN `+  N+ U+05E0 -- HEBREW LETTER NUN `+  S+ U+05E1 -- HEBREW LETTER SAMEKH+  E+ U+05E2 -- HEBREW LETTER AYIN+  P% U+05E3 -- HEBREW LETTER FINAL PE+  P+ U+05E4 -- HEBREW LETTER PE+  Zj U+05E5 -- HEBREW LETTER FINAL TSADI+  ZJ U+05E6 -- HEBREW LETTER TSADI+  Q+ U+05E7 -- HEBREW LETTER QOF+  R+ U+05E8 -- HEBREW LETTER RESH+  Sh U+05E9 -- HEBREW LETTER SHIN+  T+ U+05EA -- HEBREW LETTER TAV+  ,+ U+060C -- ARABIC COMMA+  ;+ U+061B -- ARABIC SEMICOLON+  ?+ U+061F -- ARABIC QUESTION MARK+  H' U+0621 -- ARABIC LETTER HAMZA+  aM U+0622 -- ARABIC LETTER ALEF WITH MADDA ABOVE+  aH U+0623 -- ARABIC LETTER ALEF WITH HAMZA ABOVE+  wH U+0624 -- ARABIC LETTER WAW WITH HAMZA ABOVE+  ah U+0625 -- ARABIC LETTER ALEF WITH HAMZA BELOW+  yH U+0626 -- ARABIC LETTER YEH WITH HAMZA ABOVE+  a+ U+0627 -- ARABIC LETTER ALEF+  b+ U+0628 -- ARABIC LETTER BEH+  tm U+0629 -- ARABIC LETTER TEH MARBUTA+  t+ U+062A -- ARABIC LETTER TEH+  tk U+062B -- ARABIC LETTER THEH+  g+ U+062C -- ARABIC LETTER JEEM+  hk U+062D -- ARABIC LETTER HAH+  x+ U+062E -- ARABIC LETTER KHAH+  d+ U+062F -- ARABIC LETTER DAL+  dk U+0630 -- ARABIC LETTER THAL+  r+ U+0631 -- ARABIC LETTER REH+  z+ U+0632 -- ARABIC LETTER ZAIN+  s+ U+0633 -- ARABIC LETTER SEEN+  sn U+0634 -- ARABIC LETTER SHEEN+  c+ U+0635 -- ARABIC LETTER SAD+  dd U+0636 -- ARABIC LETTER DAD+  tj U+0637 -- ARABIC LETTER TAH+  zH U+0638 -- ARABIC LETTER ZAH+  e+ U+0639 -- ARABIC LETTER AIN+  i+ U+063A -- ARABIC LETTER GHAIN+  ++ U+0640 -- ARABIC TATWEEL+  f+ U+0641 -- ARABIC LETTER FEH+  q+ U+0642 -- ARABIC LETTER QAF+  k+ U+0643 -- ARABIC LETTER KAF+  l+ U+0644 -- ARABIC LETTER LAM+  m+ U+0645 -- ARABIC LETTER MEEM+  n+ U+0646 -- ARABIC LETTER NOON+  h+ U+0647 -- ARABIC LETTER HEH+  w+ U+0648 -- ARABIC LETTER WAW+  j+ U+0649 -- ARABIC LETTER ALEF MAKSURA+  y+ U+064A -- ARABIC LETTER YEH+  :+ U+064B -- ARABIC FATHATAN+  "+ U+064C -- ARABIC DAMMATAN+  =+ U+064D -- ARABIC KASRATAN+  /+ U+064E -- ARABIC FATHA+  '+ U+064F -- ARABIC DAMMA+  1+ U+0650 -- ARABIC KASRA+  3+ U+0651 -- ARABIC SHADDA+  0+ U+0652 -- ARABIC SUKUN+  aS U+0670 -- ARABIC LETTER SUPERSCRIPT ALEF+  p+ U+067E -- ARABIC LETTER PEH+  v+ U+06A4 -- ARABIC LETTER VEH+  gf U+06AF -- ARABIC LETTER GAF+  0a U+06F0 -- EXTENDED ARABIC-INDIC DIGIT ZERO+  1a U+06F1 -- EXTENDED ARABIC-INDIC DIGIT ONE+  2a U+06F2 -- EXTENDED ARABIC-INDIC DIGIT TWO+  3a U+06F3 -- EXTENDED ARABIC-INDIC DIGIT THREE+  4a U+06F4 -- EXTENDED ARABIC-INDIC DIGIT FOUR+  5a U+06F5 -- EXTENDED ARABIC-INDIC DIGIT FIVE+  6a U+06F6 -- EXTENDED ARABIC-INDIC DIGIT SIX+  7a U+06F7 -- EXTENDED ARABIC-INDIC DIGIT SEVEN+  8a U+06F8 -- EXTENDED ARABIC-INDIC DIGIT EIGHT+  9a U+06F9 -- EXTENDED ARABIC-INDIC DIGIT NINE+  B. U+1E02 -- LATIN CAPITAL LETTER B WITH DOT ABOVE+  b. U+1E03 -- LATIN SMALL LETTER B WITH DOT ABOVE+  B_ U+1E06 -- LATIN CAPITAL LETTER B WITH LINE BELOW+  b_ U+1E07 -- LATIN SMALL LETTER B WITH LINE BELOW+  D. U+1E0A -- LATIN CAPITAL LETTER D WITH DOT ABOVE+  d. U+1E0B -- LATIN SMALL LETTER D WITH DOT ABOVE+  D_ U+1E0E -- LATIN CAPITAL LETTER D WITH LINE BELOW+  d_ U+1E0F -- LATIN SMALL LETTER D WITH LINE BELOW+  D, U+1E10 -- LATIN CAPITAL LETTER D WITH CEDILLA+  d, U+1E11 -- LATIN SMALL LETTER D WITH CEDILLA+  F. U+1E1E -- LATIN CAPITAL LETTER F WITH DOT ABOVE+  f. U+1E1F -- LATIN SMALL LETTER F WITH DOT ABOVE+  G- U+1E20 -- LATIN CAPITAL LETTER G WITH MACRON+  g- U+1E21 -- LATIN SMALL LETTER G WITH MACRON+  H. U+1E22 -- LATIN CAPITAL LETTER H WITH DOT ABOVE+  h. U+1E23 -- LATIN SMALL LETTER H WITH DOT ABOVE+  H: U+1E26 -- LATIN CAPITAL LETTER H WITH DIAERESIS+  h: U+1E27 -- LATIN SMALL LETTER H WITH DIAERESIS+  H, U+1E28 -- LATIN CAPITAL LETTER H WITH CEDILLA+  h, U+1E29 -- LATIN SMALL LETTER H WITH CEDILLA+  K' U+1E30 -- LATIN CAPITAL LETTER K WITH ACUTE+  k' U+1E31 -- LATIN SMALL LETTER K WITH ACUTE+  K_ U+1E34 -- LATIN CAPITAL LETTER K WITH LINE BELOW+  k_ U+1E35 -- LATIN SMALL LETTER K WITH LINE BELOW+  L_ U+1E3A -- LATIN CAPITAL LETTER L WITH LINE BELOW+  l_ U+1E3B -- LATIN SMALL LETTER L WITH LINE BELOW+  M' U+1E3E -- LATIN CAPITAL LETTER M WITH ACUTE+  m' U+1E3F -- LATIN SMALL LETTER M WITH ACUTE+  M. U+1E40 -- LATIN CAPITAL LETTER M WITH DOT ABOVE+  m. U+1E41 -- LATIN SMALL LETTER M WITH DOT ABOVE+  N. U+1E44 -- LATIN CAPITAL LETTER N WITH DOT ABOVE `+  n. U+1E45 -- LATIN SMALL LETTER N WITH DOT ABOVE `+  N_ U+1E48 -- LATIN CAPITAL LETTER N WITH LINE BELOW `+  n_ U+1E49 -- LATIN SMALL LETTER N WITH LINE BELOW `+  P' U+1E54 -- LATIN CAPITAL LETTER P WITH ACUTE+  p' U+1E55 -- LATIN SMALL LETTER P WITH ACUTE+  P. U+1E56 -- LATIN CAPITAL LETTER P WITH DOT ABOVE+  p. U+1E57 -- LATIN SMALL LETTER P WITH DOT ABOVE+  R. U+1E58 -- LATIN CAPITAL LETTER R WITH DOT ABOVE+  r. U+1E59 -- LATIN SMALL LETTER R WITH DOT ABOVE+  R_ U+1E5E -- LATIN CAPITAL LETTER R WITH LINE BELOW+  r_ U+1E5F -- LATIN SMALL LETTER R WITH LINE BELOW+  S. U+1E60 -- LATIN CAPITAL LETTER S WITH DOT ABOVE+  s. U+1E61 -- LATIN SMALL LETTER S WITH DOT ABOVE+  T. U+1E6A -- LATIN CAPITAL LETTER T WITH DOT ABOVE+  t. U+1E6B -- LATIN SMALL LETTER T WITH DOT ABOVE+  T_ U+1E6E -- LATIN CAPITAL LETTER T WITH LINE BELOW+  t_ U+1E6F -- LATIN SMALL LETTER T WITH LINE BELOW+  V? U+1E7C -- LATIN CAPITAL LETTER V WITH TILDE+  v? U+1E7D -- LATIN SMALL LETTER V WITH TILDE+  W! U+1E80 -- LATIN CAPITAL LETTER W WITH GRAVE+  w! U+1E81 -- LATIN SMALL LETTER W WITH GRAVE+  W' U+1E82 -- LATIN CAPITAL LETTER W WITH ACUTE+  w' U+1E83 -- LATIN SMALL LETTER W WITH ACUTE+  W: U+1E84 -- LATIN CAPITAL LETTER W WITH DIAERESIS+  w: U+1E85 -- LATIN SMALL LETTER W WITH DIAERESIS+  W. U+1E86 -- LATIN CAPITAL LETTER W WITH DOT ABOVE+  w. U+1E87 -- LATIN SMALL LETTER W WITH DOT ABOVE+  X. U+1E8A -- LATIN CAPITAL LETTER X WITH DOT ABOVE+  x. U+1E8B -- LATIN SMALL LETTER X WITH DOT ABOVE+  X: U+1E8C -- LATIN CAPITAL LETTER X WITH DIAERESIS+  x: U+1E8D -- LATIN SMALL LETTER X WITH DIAERESIS+  Y. U+1E8E -- LATIN CAPITAL LETTER Y WITH DOT ABOVE+  y. U+1E8F -- LATIN SMALL LETTER Y WITH DOT ABOVE+  Z> U+1E90 -- LATIN CAPITAL LETTER Z WITH CIRCUMFLEX+  z> U+1E91 -- LATIN SMALL LETTER Z WITH CIRCUMFLEX+  Z_ U+1E94 -- LATIN CAPITAL LETTER Z WITH LINE BELOW+  z_ U+1E95 -- LATIN SMALL LETTER Z WITH LINE BELOW+  h_ U+1E96 -- LATIN SMALL LETTER H WITH LINE BELOW+  t: U+1E97 -- LATIN SMALL LETTER T WITH DIAERESIS+  w0 U+1E98 -- LATIN SMALL LETTER W WITH RING ABOVE+  y0 U+1E99 -- LATIN SMALL LETTER Y WITH RING ABOVE+  A2 U+1EA2 -- LATIN CAPITAL LETTER A WITH HOOK ABOVE+  a2 U+1EA3 -- LATIN SMALL LETTER A WITH HOOK ABOVE+  E2 U+1EBA -- LATIN CAPITAL LETTER E WITH HOOK ABOVE+  e2 U+1EBB -- LATIN SMALL LETTER E WITH HOOK ABOVE+  E? U+1EBC -- LATIN CAPITAL LETTER E WITH TILDE+  e? U+1EBD -- LATIN SMALL LETTER E WITH TILDE+  I2 U+1EC8 -- LATIN CAPITAL LETTER I WITH HOOK ABOVE+  i2 U+1EC9 -- LATIN SMALL LETTER I WITH HOOK ABOVE+  O2 U+1ECE -- LATIN CAPITAL LETTER O WITH HOOK ABOVE+  o2 U+1ECF -- LATIN SMALL LETTER O WITH HOOK ABOVE+  U2 U+1EE6 -- LATIN CAPITAL LETTER U WITH HOOK ABOVE+  u2 U+1EE7 -- LATIN SMALL LETTER U WITH HOOK ABOVE+  Y! U+1EF2 -- LATIN CAPITAL LETTER Y WITH GRAVE+  y! U+1EF3 -- LATIN SMALL LETTER Y WITH GRAVE+  Y2 U+1EF6 -- LATIN CAPITAL LETTER Y WITH HOOK ABOVE+  y2 U+1EF7 -- LATIN SMALL LETTER Y WITH HOOK ABOVE+  Y? U+1EF8 -- LATIN CAPITAL LETTER Y WITH TILDE+  y? U+1EF9 -- LATIN SMALL LETTER Y WITH TILDE+  ;' U+1F00 -- GREEK SMALL LETTER ALPHA WITH PSILI+  ,' U+1F01 -- GREEK SMALL LETTER ALPHA WITH DASIA+  ;! U+1F02 -- GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA+  ,! U+1F03 -- GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA+  ?; U+1F04 -- GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA+  ?, U+1F05 -- GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA+  !: U+1F06 -- GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI+  ?: U+1F07 -- GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI+  1N U+2002 -- EN SPACE+  1M U+2003 -- EM SPACE+  3M U+2004 -- THREE-PER-EM SPACE+  4M U+2005 -- FOUR-PER-EM SPACE+  6M U+2006 -- SIX-PER-EM SPACE+  1T U+2009 -- THIN SPACE+  1H U+200A -- HAIR SPACE+  -1 U+2010 -- HYPHEN+  -N U+2013 -- EN DASH `+  -M U+2014 -- EM DASH+  -3 U+2015 -- HORIZONTAL BAR+  !2 U+2016 -- DOUBLE VERTICAL LINE+  =2 U+2017 -- DOUBLE LOW LINE+  '6 U+2018 -- LEFT SINGLE QUOTATION MARK+  '9 U+2019 -- RIGHT SINGLE QUOTATION MARK+  .9 U+201A -- SINGLE LOW-9 QUOTATION MARK+  9' U+201B -- SINGLE HIGH-REVERSED-9 QUOTATION MARK+  "6 U+201C -- LEFT DOUBLE QUOTATION MARK+  "9 U+201D -- RIGHT DOUBLE QUOTATION MARK+  :9 U+201E -- DOUBLE LOW-9 QUOTATION MARK+  9" U+201F -- DOUBLE HIGH-REVERSED-9 QUOTATION MARK+  /- U+2020 -- DAGGER+  /= U+2021 -- DOUBLE DAGGER+  .. U+2025 -- TWO DOT LEADER+  %0 U+2030 -- PER MILLE SIGN+  1' U+2032 -- PRIME+  2' U+2033 -- DOUBLE PRIME+  3' U+2034 -- TRIPLE PRIME+  1" U+2035 -- REVERSED PRIME+  2" U+2036 -- REVERSED DOUBLE PRIME+  3" U+2037 -- REVERSED TRIPLE PRIME+  Ca U+2038 -- CARET+  <1 U+2039 -- SINGLE LEFT-POINTING ANGLE QUOTATION MARK+  >1 U+203A -- SINGLE RIGHT-POINTING ANGLE QUOTATION MARK+  :X U+203B -- REFERENCE MARK+  '- U+203E -- OVERLINE+  /f U+2044 -- FRACTION SLASH+  0S U+2070 -- SUPERSCRIPT ZERO+  4S U+2074 -- SUPERSCRIPT FOUR+  5S U+2075 -- SUPERSCRIPT FIVE+  6S U+2076 -- SUPERSCRIPT SIX+  7S U+2077 -- SUPERSCRIPT SEVEN+  8S U+2078 -- SUPERSCRIPT EIGHT+  9S U+2079 -- SUPERSCRIPT NINE+  +S U+207A -- SUPERSCRIPT PLUS SIGN+  -S U+207B -- SUPERSCRIPT MINUS+  =S U+207C -- SUPERSCRIPT EQUALS SIGN+  (S U+207D -- SUPERSCRIPT LEFT PARENTHESIS+  )S U+207E -- SUPERSCRIPT RIGHT PARENTHESIS+  nS U+207F -- SUPERSCRIPT LATIN SMALL LETTER N `+  0s U+2080 -- SUBSCRIPT ZERO+  1s U+2081 -- SUBSCRIPT ONE+  2s U+2082 -- SUBSCRIPT TWO+  3s U+2083 -- SUBSCRIPT THREE+  4s U+2084 -- SUBSCRIPT FOUR+  5s U+2085 -- SUBSCRIPT FIVE+  6s U+2086 -- SUBSCRIPT SIX+  7s U+2087 -- SUBSCRIPT SEVEN+  8s U+2088 -- SUBSCRIPT EIGHT+  9s U+2089 -- SUBSCRIPT NINE+  +s U+208A -- SUBSCRIPT PLUS SIGN+  -s U+208B -- SUBSCRIPT MINUS+  =s U+208C -- SUBSCRIPT EQUALS SIGN+  (s U+208D -- SUBSCRIPT LEFT PARENTHESIS+  )s U+208E -- SUBSCRIPT RIGHT PARENTHESIS+  Li U+20A4 -- LIRA SIGN+  Pt U+20A7 -- PESETA SIGN+  W= U+20A9 -- WON SIGN+  Eu U+20AC -- EURO SIGN+  oC U+2103 -- DEGREE CELSIUS+  co U+2105 -- CARE OF+  oF U+2109 -- DEGREE FAHRENHEIT+  N0 U+2116 -- NUMERO SIGN+  PO U+2117 -- SOUND RECORDING COPYRIGHT+  Rx U+211E -- PRESCRIPTION TAKE+  SM U+2120 -- SERVICE MARK+  TM U+2122 -- TRADE MARK SIGN+  Om U+2126 -- OHM SIGN+  AO U+212B -- ANGSTROM SIGN+  13 U+2153 -- VULGAR FRACTION ONE THIRD+  23 U+2154 -- VULGAR FRACTION TWO THIRDS+  15 U+2155 -- VULGAR FRACTION ONE FIFTH+  25 U+2156 -- VULGAR FRACTION TWO FIFTHS+  35 U+2157 -- VULGAR FRACTION THREE FIFTHS+  45 U+2158 -- VULGAR FRACTION FOUR FIFTHS+  16 U+2159 -- VULGAR FRACTION ONE SIXTH+  56 U+215A -- VULGAR FRACTION FIVE SIXTHS+  18 U+215B -- VULGAR FRACTION ONE EIGHTH+  38 U+215C -- VULGAR FRACTION THREE EIGHTHS+  58 U+215D -- VULGAR FRACTION FIVE EIGHTHS+  78 U+215E -- VULGAR FRACTION SEVEN EIGHTHS+  1R U+2160 -- ROMAN NUMERAL ONE+  2R U+2161 -- ROMAN NUMERAL TWO+  3R U+2162 -- ROMAN NUMERAL THREE+  4R U+2163 -- ROMAN NUMERAL FOUR+  5R U+2164 -- ROMAN NUMERAL FIVE+  6R U+2165 -- ROMAN NUMERAL SIX+  7R U+2166 -- ROMAN NUMERAL SEVEN+  8R U+2167 -- ROMAN NUMERAL EIGHT+  9R U+2168 -- ROMAN NUMERAL NINE+  aR U+2169 -- ROMAN NUMERAL TEN+  bR U+216A -- ROMAN NUMERAL ELEVEN+  cR U+216B -- ROMAN NUMERAL TWELVE+  1r U+2170 -- SMALL ROMAN NUMERAL ONE+  2r U+2171 -- SMALL ROMAN NUMERAL TWO+  3r U+2172 -- SMALL ROMAN NUMERAL THREE+  4r U+2173 -- SMALL ROMAN NUMERAL FOUR+  5r U+2174 -- SMALL ROMAN NUMERAL FIVE+  6r U+2175 -- SMALL ROMAN NUMERAL SIX+  7r U+2176 -- SMALL ROMAN NUMERAL SEVEN+  8r U+2177 -- SMALL ROMAN NUMERAL EIGHT+  9r U+2178 -- SMALL ROMAN NUMERAL NINE+  ar U+2179 -- SMALL ROMAN NUMERAL TEN+  br U+217A -- SMALL ROMAN NUMERAL ELEVEN+  cr U+217B -- SMALL ROMAN NUMERAL TWELVE+  <- U+2190 -- LEFTWARDS ARROW+  -! U+2191 -- UPWARDS ARROW+  -> U+2192 -- RIGHTWARDS ARROW+  -v U+2193 -- DOWNWARDS ARROW+  <> U+2194 -- LEFT RIGHT ARROW+  UD U+2195 -- UP DOWN ARROW+  <= U+21D0 -- LEFTWARDS DOUBLE ARROW+  => U+21D2 -- RIGHTWARDS DOUBLE ARROW+  == U+21D4 -- LEFT RIGHT DOUBLE ARROW+  FA U+2200 -- FOR ALL+  dP U+2202 -- PARTIAL DIFFERENTIAL+  TE U+2203 -- THERE EXISTS+  /0 U+2205 -- EMPTY SET+  DE U+2206 -- INCREMENT+  NB U+2207 -- NABLA+  (- U+2208 -- ELEMENT OF+  -) U+220B -- CONTAINS AS MEMBER+  *P U+220F -- N-ARY PRODUCT `+  +Z U+2211 -- N-ARY SUMMATION `+  -2 U+2212 -- MINUS SIGN+  -+ U+2213 -- MINUS-OR-PLUS SIGN+  *- U+2217 -- ASTERISK OPERATOR+  Ob U+2218 -- RING OPERATOR+  Sb U+2219 -- BULLET OPERATOR+  RT U+221A -- SQUARE ROOT+  0( U+221D -- PROPORTIONAL TO+  00 U+221E -- INFINITY+  -L U+221F -- RIGHT ANGLE+  -V U+2220 -- ANGLE+  PP U+2225 -- PARALLEL TO+  AN U+2227 -- LOGICAL AND+  OR U+2228 -- LOGICAL OR+  (U U+2229 -- INTERSECTION+  )U U+222A -- UNION+  In U+222B -- INTEGRAL+  DI U+222C -- DOUBLE INTEGRAL+  Io U+222E -- CONTOUR INTEGRAL+  .: U+2234 -- THEREFORE+  :. U+2235 -- BECAUSE+  :R U+2236 -- RATIO+  :: U+2237 -- PROPORTION+  ?1 U+223C -- TILDE OPERATOR+  CG U+223E -- INVERTED LAZY S+  ?- U+2243 -- ASYMPTOTICALLY EQUAL TO+  ?= U+2245 -- APPROXIMATELY EQUAL TO+  ?2 U+2248 -- ALMOST EQUAL TO+  =? U+224C -- ALL EQUAL TO+  HI U+2253 -- IMAGE OF OR APPROXIMATELY EQUAL TO+  != U+2260 -- NOT EQUAL TO+  =3 U+2261 -- IDENTICAL TO+  =< U+2264 -- LESS-THAN OR EQUAL TO+  >= U+2265 -- GREATER-THAN OR EQUAL TO+  <* U+226A -- MUCH LESS-THAN+  *> U+226B -- MUCH GREATER-THAN+  !< U+226E -- NOT LESS-THAN+  !> U+226F -- NOT GREATER-THAN+  (C U+2282 -- SUBSET OF+  )C U+2283 -- SUPERSET OF+  (_ U+2286 -- SUBSET OF OR EQUAL TO+  )_ U+2287 -- SUPERSET OF OR EQUAL TO+  0. U+2299 -- CIRCLED DOT OPERATOR+  02 U+229A -- CIRCLED RING OPERATOR+  -T U+22A5 -- UP TACK+  .P U+22C5 -- DOT OPERATOR+  :3 U+22EE -- VERTICAL ELLIPSIS+  .3 U+22EF -- MIDLINE HORIZONTAL ELLIPSIS+  Eh U+2302 -- HOUSE+  <7 U+2308 -- LEFT CEILING+  >7 U+2309 -- RIGHT CEILING+  7< U+230A -- LEFT FLOOR+  7> U+230B -- RIGHT FLOOR+  NI U+2310 -- REVERSED NOT SIGN+  (A U+2312 -- ARC+  TR U+2315 -- TELEPHONE RECORDER+  Iu U+2320 -- TOP HALF INTEGRAL+  Il U+2321 -- BOTTOM HALF INTEGRAL+  </ U+2329 -- LEFT-POINTING ANGLE BRACKET+  /> U+232A -- RIGHT-POINTING ANGLE BRACKET+  Vs U+2423 -- OPEN BOX+  1h U+2440 -- OCR HOOK+  3h U+2441 -- OCR CHAIR+  2h U+2442 -- OCR FORK+  4h U+2443 -- OCR INVERTED FORK+  1j U+2446 -- OCR BRANCH BANK IDENTIFICATION+  2j U+2447 -- OCR AMOUNT OF CHECK+  3j U+2448 -- OCR DASH+  4j U+2449 -- OCR CUSTOMER ACCOUNT NUMBER+  1. U+2488 -- DIGIT ONE FULL STOP+  2. U+2489 -- DIGIT TWO FULL STOP+  3. U+248A -- DIGIT THREE FULL STOP+  4. U+248B -- DIGIT FOUR FULL STOP+  5. U+248C -- DIGIT FIVE FULL STOP+  6. U+248D -- DIGIT SIX FULL STOP+  7. U+248E -- DIGIT SEVEN FULL STOP+  8. U+248F -- DIGIT EIGHT FULL STOP+  9. U+2490 -- DIGIT NINE FULL STOP+  hh U+2500 -- BOX DRAWINGS LIGHT HORIZONTAL+  HH U+2501 -- BOX DRAWINGS HEAVY HORIZONTAL+  vv U+2502 -- BOX DRAWINGS LIGHT VERTICAL+  VV U+2503 -- BOX DRAWINGS HEAVY VERTICAL+  3- U+2504 -- BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL+  3_ U+2505 -- BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL+  3! U+2506 -- BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL+  3/ U+2507 -- BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL+  4- U+2508 -- BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL+  4_ U+2509 -- BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL+  4! U+250A -- BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL+  4/ U+250B -- BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL+  dr U+250C -- BOX DRAWINGS LIGHT DOWN AND RIGHT+  dR U+250D -- BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY+  Dr U+250E -- BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT+  DR U+250F -- BOX DRAWINGS HEAVY DOWN AND RIGHT+  dl U+2510 -- BOX DRAWINGS LIGHT DOWN AND LEFT+  dL U+2511 -- BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY+  Dl U+2512 -- BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT+  LD U+2513 -- BOX DRAWINGS HEAVY DOWN AND LEFT+  ur U+2514 -- BOX DRAWINGS LIGHT UP AND RIGHT+  uR U+2515 -- BOX DRAWINGS UP LIGHT AND RIGHT HEAVY+  Ur U+2516 -- BOX DRAWINGS UP HEAVY AND RIGHT LIGHT+  UR U+2517 -- BOX DRAWINGS HEAVY UP AND RIGHT+  ul U+2518 -- BOX VOICED SOUND MARKDRAWINGS LIGHT UP AND LEFT+  uL U+2519 -- BOX DRAWINGS UP LIGHT AND LEFT HEAVY+  Ul U+251A -- BOX DRAWINGS UP HEAVY AND LEFT LIGHT+  UL U+251B -- BOX DRAWINGS HEAVY UP AND LEFT+  vr U+251C -- BOX DRAWINGS LIGHT VERTICAL AND RIGHT+  vR U+251D -- BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY+  Vr U+2520 -- BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT+  VR U+2523 -- BOX DRAWINGS HEAVY VERTICAL AND RIGHT+  vl U+2524 -- BOX DRAWINGS LIGHT VERTICAL AND LEFT+  vL U+2525 -- BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY+  Vl U+2528 -- BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT+  VL U+252B -- BOX DRAWINGS HEAVY VERTICAL AND LEFT+  dh U+252C -- BOX DRAWINGS LIGHT DOWN AND HORIZONTAL+  dH U+252F -- BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY+  Dh U+2530 -- BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT+  DH U+2533 -- BOX DRAWINGS HEAVY DOWN AND HORIZONTAL+  uh U+2534 -- BOX DRAWINGS LIGHT UP AND HORIZONTAL+  uH U+2537 -- BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY+  Uh U+2538 -- BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT+  UH U+253B -- BOX DRAWINGS HEAVY UP AND HORIZONTAL+  vh U+253C -- BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL+  vH U+253F -- BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY+  Vh U+2542 -- BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT+  VH U+254B -- BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL+  FD U+2571 -- BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT+  BD U+2572 -- BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT+  TB U+2580 -- UPPER HALF BLOCK+  LB U+2584 -- LOWER HALF BLOCK+  FB U+2588 -- FULL BLOCK+  lB U+258C -- LEFT HALF BLOCK+  RB U+2590 -- RIGHT HALF BLOCK+  .S U+2591 -- LIGHT SHADE+  :S U+2592 -- MEDIUM SHADE+  ?S U+2593 -- DARK SHADE+  fS U+25A0 -- BLACK SQUARE+  OS U+25A1 -- WHITE SQUARE+  RO U+25A2 -- WHITE SQUARE WITH ROUNDED CORNERS+  Rr U+25A3 -- WHITE SQUARE CONTAINING BLACK SMALL SQUARE+  RF U+25A4 -- SQUARE WITH HORIZONTAL FILL+  RY U+25A5 -- SQUARE WITH VERTICAL FILL+  RH U+25A6 -- SQUARE WITH ORTHOGONAL CROSSHATCH FILL+  RZ U+25A7 -- SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL+  RK U+25A8 -- SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL+  RX U+25A9 -- SQUARE WITH DIAGONAL CROSSHATCH FILL+  sB U+25AA -- BLACK SMALL SQUARE+  SR U+25AC -- BLACK RECTANGLE+  Or U+25AD -- WHITE RECTANGLE+  UT U+25B2 -- BLACK UP-POINTING TRIANGLE+  uT U+25B3 -- WHITE UP-POINTING TRIANGLE+  PR U+25B6 -- BLACK RIGHT-POINTING TRIANGLE+  Tr U+25B7 -- WHITE RIGHT-POINTING TRIANGLE+  Dt U+25BC -- BLACK DOWN-POINTING TRIANGLE+  dT U+25BD -- WHITE DOWN-POINTING TRIANGLE+  PL U+25C0 -- BLACK LEFT-POINTING TRIANGLE+  Tl U+25C1 -- WHITE LEFT-POINTING TRIANGLE+  Db U+25C6 -- BLACK DIAMOND+  Dw U+25C7 -- WHITE DIAMOND+  LZ U+25CA -- LOZENGE+  0m U+25CB -- WHITE CIRCLE+  0o U+25CE -- BULLSEYE+  0M U+25CF -- BLACK CIRCLE+  0L U+25D0 -- CIRCLE WITH LEFT HALF BLACK+  0R U+25D1 -- CIRCLE WITH RIGHT HALF BLACK+  Sn U+25D8 -- INVERSE BULLET+  Ic U+25D9 -- INVERSE WHITE CIRCLE+  Fd U+25E2 -- BLACK LOWER RIGHT TRIANGLE+  Bd U+25E3 -- BLACK LOWER LEFT TRIANGLE+  *2 U+2605 -- BLACK STAR+  *1 U+2606 -- WHITE STAR+  <H U+261C -- WHITE LEFT POINTING INDEX+  >H U+261E -- WHITE RIGHT POINTING INDEX+  0u U+263A -- WHITE SMILING FACE+  0U U+263B -- BLACK SMILING FACE+  SU U+263C -- WHITE SUN WITH RAYS+  Fm U+2640 -- FEMALE SIGN+  Ml U+2642 -- MALE SIGN+  cS U+2660 -- BLACK SPADE SUIT+  cH U+2661 -- WHITE HEART SUIT+  cD U+2662 -- WHITE DIAMOND SUIT+  cC U+2663 -- BLACK CLUB SUIT+  Md U+2669 -- QUARTER NOTE `+  M8 U+266A -- EIGHTH NOTE `+  M2 U+266B -- BEAMED EIGHTH NOTES+  Mb U+266D -- MUSIC FLAT SIGN+  Mx U+266E -- MUSIC NATURAL SIGN+  MX U+266F -- MUSIC SHARP SIGN+  OK U+2713 -- CHECK MARK+  XX U+2717 -- BALLOT X+  -X U+2720 -- MALTESE CROSS+  IS U+3000 -- IDEOGRAPHIC SPACE+  ,_ U+3001 -- IDEOGRAPHIC COMMA+  ._ U+3002 -- IDEOGRAPHIC FULL STOP+  +" U+3003 -- DITTO MARK+  +_ U+3004 -- JAPANESE INDUSTRIAL STANDARD SYMBOL+  *_ U+3005 -- IDEOGRAPHIC ITERATION MARK+  ;_ U+3006 -- IDEOGRAPHIC CLOSING MARK+  0_ U+3007 -- IDEOGRAPHIC NUMBER ZERO+  <+ U+300A -- LEFT DOUBLE ANGLE BRACKET+  >+ U+300B -- RIGHT DOUBLE ANGLE BRACKET+  <' U+300C -- LEFT CORNER BRACKET+  >' U+300D -- RIGHT CORNER BRACKET+  <" U+300E -- LEFT WHITE CORNER BRACKET+  >" U+300F -- RIGHT WHITE CORNER BRACKET+  (" U+3010 -- LEFT BLACK LENTICULAR BRACKET+  )" U+3011 -- RIGHT BLACK LENTICULAR BRACKET+  =T U+3012 -- POSTAL MARK+  =_ U+3013 -- GETA MARK+  (' U+3014 -- LEFT TORTOISE SHELL BRACKET+  )' U+3015 -- RIGHT TORTOISE SHELL BRACKET+  (I U+3016 -- LEFT WHITE LENTICULAR BRACKET+  )I U+3017 -- RIGHT WHITE LENTICULAR BRACKET+  -? U+301C -- WAVE DASH+  A5 U+3041 -- HIRAGANA LETTER SMALL A+  a5 U+3042 -- HIRAGANA LETTER A+  I5 U+3043 -- HIRAGANA LETTER SMALL I+  i5 U+3044 -- HIRAGANA LETTER I+  U5 U+3045 -- HIRAGANA LETTER SMALL U+  u5 U+3046 -- HIRAGANA LETTER U+  E5 U+3047 -- HIRAGANA LETTER SMALL E+  e5 U+3048 -- HIRAGANA LETTER E+  O5 U+3049 -- HIRAGANA LETTER SMALL O+  o5 U+304A -- HIRAGANA LETTER O+  ka U+304B -- HIRAGANA LETTER KA+  ga U+304C -- HIRAGANA LETTER GA+  ki U+304D -- HIRAGANA LETTER KI+  gi U+304E -- HIRAGANA LETTER GI+  ku U+304F -- HIRAGANA LETTER KU+  gu U+3050 -- HIRAGANA LETTER GU+  ke U+3051 -- HIRAGANA LETTER KE+  ge U+3052 -- HIRAGANA LETTER GE+  ko U+3053 -- HIRAGANA LETTER KO+  go U+3054 -- HIRAGANA LETTER GO+  sa U+3055 -- HIRAGANA LETTER SA+  za U+3056 -- HIRAGANA LETTER ZA+  si U+3057 -- HIRAGANA LETTER SI+  zi U+3058 -- HIRAGANA LETTER ZI+  su U+3059 -- HIRAGANA LETTER SU+  zu U+305A -- HIRAGANA LETTER ZU+  se U+305B -- HIRAGANA LETTER SE+  ze U+305C -- HIRAGANA LETTER ZE+  so U+305D -- HIRAGANA LETTER SO+  zo U+305E -- HIRAGANA LETTER ZO+  ta U+305F -- HIRAGANA LETTER TA+  da U+3060 -- HIRAGANA LETTER DA+  ti U+3061 -- HIRAGANA LETTER TI+  di U+3062 -- HIRAGANA LETTER DI+  tU U+3063 -- HIRAGANA LETTER SMALL TU+  tu U+3064 -- HIRAGANA LETTER TU+  du U+3065 -- HIRAGANA LETTER DU+  te U+3066 -- HIRAGANA LETTER TE+  de U+3067 -- HIRAGANA LETTER DE+  to U+3068 -- HIRAGANA LETTER TO+  do U+3069 -- HIRAGANA LETTER DO+  na U+306A -- HIRAGANA LETTER NA+  ni U+306B -- HIRAGANA LETTER NI+  nu U+306C -- HIRAGANA LETTER NU+  ne U+306D -- HIRAGANA LETTER NE+  no U+306E -- HIRAGANA LETTER NO+  ha U+306F -- HIRAGANA LETTER HA+  ba U+3070 -- HIRAGANA LETTER BA+  pa U+3071 -- HIRAGANA LETTER PA+  hi U+3072 -- HIRAGANA LETTER HI+  bi U+3073 -- HIRAGANA LETTER BI+  pi U+3074 -- HIRAGANA LETTER PI+  hu U+3075 -- HIRAGANA LETTER HU+  bu U+3076 -- HIRAGANA LETTER BU+  pu U+3077 -- HIRAGANA LETTER PU+  he U+3078 -- HIRAGANA LETTER HE+  be U+3079 -- HIRAGANA LETTER BE+  pe U+307A -- HIRAGANA LETTER PE+  ho U+307B -- HIRAGANA LETTER HO+  bo U+307C -- HIRAGANA LETTER BO+  po U+307D -- HIRAGANA LETTER PO+  ma U+307E -- HIRAGANA LETTER MA+  mi U+307F -- HIRAGANA LETTER MI+  mu U+3080 -- HIRAGANA LETTER MU+  me U+3081 -- HIRAGANA LETTER ME+  mo U+3082 -- HIRAGANA LETTER MO+  yA U+3083 -- HIRAGANA LETTER SMALL YA+  ya U+3084 -- HIRAGANA LETTER YA+  yU U+3085 -- HIRAGANA LETTER SMALL YU+  yu U+3086 -- HIRAGANA LETTER YU+  yO U+3087 -- HIRAGANA LETTER SMALL YO+  yo U+3088 -- HIRAGANA LETTER YO+  ra U+3089 -- HIRAGANA LETTER RA+  ri U+308A -- HIRAGANA LETTER RI+  ru U+308B -- HIRAGANA LETTER RU+  re U+308C -- HIRAGANA LETTER RE+  ro U+308D -- HIRAGANA LETTER RO+  wA U+308E -- HIRAGANA LETTER SMALL WA+  wa U+308F -- HIRAGANA LETTER WA+  wi U+3090 -- HIRAGANA LETTER WI+  we U+3091 -- HIRAGANA LETTER WE+  wo U+3092 -- HIRAGANA LETTER WO+  n5 U+3093 -- HIRAGANA LETTER N `+  vu U+3094 -- HIRAGANA LETTER VU+  "5 U+309B -- KATAKANA-HIRAGANA VOICED SOUND MARK+  05 U+309C -- KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK+  *5 U+309D -- HIRAGANA ITERATION MARK+  +5 U+309E -- HIRAGANA VOICED ITERATION MARK+  a6 U+30A1 -- KATAKANA LETTER SMALL A+  A6 U+30A2 -- KATAKANA LETTER A+  i6 U+30A3 -- KATAKANA LETTER SMALL I+  I6 U+30A4 -- KATAKANA LETTER I+  u6 U+30A5 -- KATAKANA LETTER SMALL U+  U6 U+30A6 -- KATAKANA LETTER U+  e6 U+30A7 -- KATAKANA LETTER SMALL E+  E6 U+30A8 -- KATAKANA LETTER E+  o6 U+30A9 -- KATAKANA LETTER SMALL O+  O6 U+30AA -- KATAKANA LETTER O+  Ka U+30AB -- KATAKANA LETTER KA+  Ga U+30AC -- KATAKANA LETTER GA+  Ki U+30AD -- KATAKANA LETTER KI+  Gi U+30AE -- KATAKANA LETTER GI+  Ku U+30AF -- KATAKANA LETTER KU+  Gu U+30B0 -- KATAKANA LETTER GU+  Ke U+30B1 -- KATAKANA LETTER KE+  Ge U+30B2 -- KATAKANA LETTER GE+  Ko U+30B3 -- KATAKANA LETTER KO+  Go U+30B4 -- KATAKANA LETTER GO+  Sa U+30B5 -- KATAKANA LETTER SA+  Za U+30B6 -- KATAKANA LETTER ZA+  Si U+30B7 -- KATAKANA LETTER SI+  Zi U+30B8 -- KATAKANA LETTER ZI+  Su U+30B9 -- KATAKANA LETTER SU+  Zu U+30BA -- KATAKANA LETTER ZU+  Se U+30BB -- KATAKANA LETTER SE+  Ze U+30BC -- KATAKANA LETTER ZE+  So U+30BD -- KATAKANA LETTER SO+  Zo U+30BE -- KATAKANA LETTER ZO+  Ta U+30BF -- KATAKANA LETTER TA+  Da U+30C0 -- KATAKANA LETTER DA+  Ti U+30C1 -- KATAKANA LETTER TI+  Di U+30C2 -- KATAKANA LETTER DI+  TU U+30C3 -- KATAKANA LETTER SMALL TU+  Tu U+30C4 -- KATAKANA LETTER TU+  Du U+30C5 -- KATAKANA LETTER DU+  Te U+30C6 -- KATAKANA LETTER TE+  De U+30C7 -- KATAKANA LETTER DE+  To U+30C8 -- KATAKANA LETTER TO+  Do U+30C9 -- KATAKANA LETTER DO+  Na U+30CA -- KATAKANA LETTER NA+  Ni U+30CB -- KATAKANA LETTER NI+  Nu U+30CC -- KATAKANA LETTER NU+  Ne U+30CD -- KATAKANA LETTER NE+  No U+30CE -- KATAKANA LETTER NO+  Ha U+30CF -- KATAKANA LETTER HA+  Ba U+30D0 -- KATAKANA LETTER BA+  Pa U+30D1 -- KATAKANA LETTER PA+  Hi U+30D2 -- KATAKANA LETTER HI+  Bi U+30D3 -- KATAKANA LETTER BI+  Pi U+30D4 -- KATAKANA LETTER PI+  Hu U+30D5 -- KATAKANA LETTER HU+  Bu U+30D6 -- KATAKANA LETTER BU+  Pu U+30D7 -- KATAKANA LETTER PU+  He U+30D8 -- KATAKANA LETTER HE+  Be U+30D9 -- KATAKANA LETTER BE+  Pe U+30DA -- KATAKANA LETTER PE+  Ho U+30DB -- KATAKANA LETTER HO+  Bo U+30DC -- KATAKANA LETTER BO+  Po U+30DD -- KATAKANA LETTER PO+  Ma U+30DE -- KATAKANA LETTER MA+  Mi U+30DF -- KATAKANA LETTER MI+  Mu U+30E0 -- KATAKANA LETTER MU+  Me U+30E1 -- KATAKANA LETTER ME+  Mo U+30E2 -- KATAKANA LETTER MO+  YA U+30E3 -- KATAKANA LETTER SMALL YA+  Ya U+30E4 -- KATAKANA LETTER YA+  YU U+30E5 -- KATAKANA LETTER SMALL YU+  Yu U+30E6 -- KATAKANA LETTER YU+  YO U+30E7 -- KATAKANA LETTER SMALL YO+  Yo U+30E8 -- KATAKANA LETTER YO+  Ra U+30E9 -- KATAKANA LETTER RA+  Ri U+30EA -- KATAKANA LETTER RI+  Ru U+30EB -- KATAKANA LETTER RU+  Re U+30EC -- KATAKANA LETTER RE+  Ro U+30ED -- KATAKANA LETTER RO+  WA U+30EE -- KATAKANA LETTER SMALL WA+  Wa U+30EF -- KATAKANA LETTER WA+  Wi U+30F0 -- KATAKANA LETTER WI+  We U+30F1 -- KATAKANA LETTER WE+  Wo U+30F2 -- KATAKANA LETTER WO+  N6 U+30F3 -- KATAKANA LETTER N `+  Vu U+30F4 -- KATAKANA LETTER VU+  KA U+30F5 -- KATAKANA LETTER SMALL KA+  KE U+30F6 -- KATAKANA LETTER SMALL KE+  Va U+30F7 -- KATAKANA LETTER VA+  Vi U+30F8 -- KATAKANA LETTER VI+  Ve U+30F9 -- KATAKANA LETTER VE+  Vo U+30FA -- KATAKANA LETTER VO+  .6 U+30FB -- KATAKANA MIDDLE DOT+  -6 U+30FC -- KATAKANA-HIRAGANA PROLONGED SOUND MARK+  *6 U+30FD -- KATAKANA ITERATION MARK+  +6 U+30FE -- KATAKANA VOICED ITERATION MARK+  b4 U+3105 -- BOPOMOFO LETTER B+  p4 U+3106 -- BOPOMOFO LETTER P+  m4 U+3107 -- BOPOMOFO LETTER M+  f4 U+3108 -- BOPOMOFO LETTER F+  d4 U+3109 -- BOPOMOFO LETTER D+  t4 U+310A -- BOPOMOFO LETTER T+  n4 U+310B -- BOPOMOFO LETTER N `+  l4 U+310C -- BOPOMOFO LETTER L+  g4 U+310D -- BOPOMOFO LETTER G+  k4 U+310E -- BOPOMOFO LETTER K+  h4 U+310F -- BOPOMOFO LETTER H+  j4 U+3110 -- BOPOMOFO LETTER J+  q4 U+3111 -- BOPOMOFO LETTER Q+  x4 U+3112 -- BOPOMOFO LETTER X+  zh U+3113 -- BOPOMOFO LETTER ZH+  ch U+3114 -- BOPOMOFO LETTER CH+  sh U+3115 -- BOPOMOFO LETTER SH+  r4 U+3116 -- BOPOMOFO LETTER R+  z4 U+3117 -- BOPOMOFO LETTER Z+  c4 U+3118 -- BOPOMOFO LETTER C+  s4 U+3119 -- BOPOMOFO LETTER S+  a4 U+311A -- BOPOMOFO LETTER A+  o4 U+311B -- BOPOMOFO LETTER O+  e4 U+311C -- BOPOMOFO LETTER E+  ai U+311E -- BOPOMOFO LETTER AI+  ei U+311F -- BOPOMOFO LETTER EI+  au U+3120 -- BOPOMOFO LETTER AU+  ou U+3121 -- BOPOMOFO LETTER OU+  an U+3122 -- BOPOMOFO LETTER AN+  en U+3123 -- BOPOMOFO LETTER EN+  aN U+3124 -- BOPOMOFO LETTER ANG+  eN U+3125 -- BOPOMOFO LETTER ENG+  er U+3126 -- BOPOMOFO LETTER ER+  i4 U+3127 -- BOPOMOFO LETTER I+  u4 U+3128 -- BOPOMOFO LETTER U+  iu U+3129 -- BOPOMOFO LETTER IU+  v4 U+312A -- BOPOMOFO LETTER V+  nG U+312B -- BOPOMOFO LETTER NG+  gn U+312C -- BOPOMOFO LETTER GN+  1c U+3220 -- PARENTHESIZED IDEOGRAPH ONE+  2c U+3221 -- PARENTHESIZED IDEOGRAPH TWO+  3c U+3222 -- PARENTHESIZED IDEOGRAPH THREE+  4c U+3223 -- PARENTHESIZED IDEOGRAPH FOUR+  5c U+3224 -- PARENTHESIZED IDEOGRAPH FIVE+  6c U+3225 -- PARENTHESIZED IDEOGRAPH SIX+  7c U+3226 -- PARENTHESIZED IDEOGRAPH SEVEN+  8c U+3227 -- PARENTHESIZED IDEOGRAPH EIGHT+  9c U+3228 -- PARENTHESIZED IDEOGRAPH NINE+  ff U+FB00 -- LATIN SMALL LIGATURE FF+  fi U+FB01 -- LATIN SMALL LIGATURE FI+  fl U+FB02 -- LATIN SMALL LIGATURE FL+  ft U+FB05 -- LATIN SMALL LIGATURE LONG S T+  st U+FB06 -- LATIN SMALL LIGATURE ST+  |]
src/RtsStats.hs view
@@ -1,5 +1,4 @@-{-# LANGUAGE OverloadedStrings, FlexibleContexts, FlexibleInstances, DeriveGeneric, TypeOperators, StandaloneDeriving, CPP, EmptyCase #-}-{-# OPTIONS_GHC -Wno-orphans -funfolding-use-threshold=2000 -funfolding-creation-threshold=1000 #-}+{-# Language OverloadedStrings, CPP #-}  {-| Module      : RtsStats@@ -22,94 +21,100 @@ import           Data.Text (Text) import qualified Data.Text as Text import           Data.Word-import           GHC.Generics import           GHC.Stats -type KVs = [(Text,Text)] -> [(Text,Text)]--cons :: (Text, Text) -> KVs-cons = (:)--class    Fields a      where fields :: a -> Text -> KVs-instance Fields Int64  where fields = commaFields-instance Fields Word32 where fields = commaFields-instance Fields Word64 where fields = commaFields-instance Fields Double where fields = showFields--showFields :: Show a => a -> Text -> KVs-showFields x n = cons (n, Text.pack (show x))-{-# NOINLINE showFields #-}+class    Render a      where render :: a -> Text+instance Render Word32 where render = prettyNum+instance Render Word64 where render = prettyNum+instance Render Int64  where render = prettyNum+instance Render Double where render = Text.pack . show -commaFields :: Show a => a -> Text -> KVs-commaFields x n = cons (n, Text.pack (addCommas (show x)))-{-# NOINLINE commaFields #-}+prettyNum :: Show a => Num a => Ord a => a -> Text+prettyNum n = Text.pack (addSign (addCommas (show (abs n))))+  where+    addSign = if n < 0 then (:) '-' else id  addCommas :: String -> String addCommas = reverse . intercalate "," . chunksOf 3 . reverse -genericFields :: (Generic a, GFields (Rep a)) => a -> Text -> KVs-genericFields = gfields . from--class GFields f where-  gfields :: f p -> Text -> KVs--instance GFields f => GFields (D1 c f) where-  gfields (M1 x) = gfields x-  {-# INLINE gfields #-}--instance GFields f => GFields (C1 c f) where-  gfields (M1 x) = gfields x-  {-# INLINE gfields #-}--instance (Selector s, GFields f) => GFields (S1 s f) where-  gfields s@(M1 x) _ = gfields x (Text.pack (selName s))-  {-# INLINE gfields #-}--instance (GFields f, GFields g) => GFields (f :*: g) where-  gfields (x :*: y) n = gfields x n . gfields y n-  {-# INLINE gfields #-}--instance (GFields f, GFields g) => GFields (f :+: g) where-  gfields (L1 x) = gfields x-  gfields (R1 x) = gfields x-  {-# INLINE gfields #-}--instance GFields U1 where-  gfields _ _ = id-  {-# INLINE gfields #-}--instance GFields V1 where-  gfields v _ = case v of {}-  {-# INLINE gfields #-}--instance Fields a => GFields (K1 i a) where-  gfields (K1 x) = fields x-  {-# INLINE gfields #-}--statsToEntries :: Stats -> [(Text, Text)]-statsToEntries (Stats rts) = fields rts "stats" []- #if MIN_VERSION_base(4,10,0)  newtype Stats = Stats RTSStats -deriving instance Generic RTSStats-deriving instance Generic GCDetails-instance Fields RTSStats  where fields = genericFields-instance Fields GCDetails where fields = genericFields- getStats :: IO (Maybe Stats) getStats =   do enabled <- getRTSStatsEnabled      if enabled then Just . Stats <$> getRTSStats                 else pure Nothing +statsToEntries :: Stats -> [(Text, Text)]+statsToEntries (Stats rts) = seq rgc+  [ ("GCs"                                 , render $ gcs                             rts)+  , ("Major GCs"                           , render $ major_gcs                       rts)+  , ("Allocated bytes"                     , render $ allocated_bytes                 rts)+  , ("Max live bytes"                      , render $ max_live_bytes                  rts)+  , ("Max large objects bytes"             , render $ max_large_objects_bytes         rts)+  , ("Max compact bytes"                   , render $ max_compact_bytes               rts)+  , ("Max slop bytes"                      , render $ max_slop_bytes                  rts)+  , ("Max memory in use bytes"             , render $ max_mem_in_use_bytes            rts)+  , ("Cumulative live bytes"               , render $ cumulative_live_bytes           rts)+  , ("Copied bytes"                        , render $ copied_bytes                    rts)+  , ("Parallel copied bytes"               , render $ par_copied_bytes                rts)+  , ("Cumulative parallel max copied bytes", render $ cumulative_par_max_copied_bytes rts)+  , ("Mutator CPU ns"                      , render $ mutator_cpu_ns                  rts)+  , ("Mutator elapsed ns"                  , render $ mutator_elapsed_ns              rts)+  , ("GC CPU ns"                           , render $ gc_cpu_ns                       rts)+  , ("GC elapsed ns"                       , render $ gc_elapsed_ns                   rts)+  , ("CPU ns"                              , render $ cpu_ns                          rts)+  , ("Elapsed ns"                          , render $ elapsed_ns                      rts)+  , ("==[Totals]=="                        , ""                                          )+  , (""                                    , ""                                          )+  , ("Generation"                          , render $ gcdetails_gen                   rgc)+  , ("Threads"                             , render $ gcdetails_threads               rgc)+  , ("Allocated bytes"                     , render $ gcdetails_allocated_bytes       rgc)+  , ("Live bytes"                          , render $ gcdetails_live_bytes            rgc)+  , ("Large objects bytes"                 , render $ gcdetails_large_objects_bytes   rgc)+  , ("Compact bytes"                       , render $ gcdetails_compact_bytes         rgc)+  , ("Slop bytes"                          , render $ gcdetails_slop_bytes            rgc)+  , ("Memory in use bytes"                 , render $ gcdetails_mem_in_use_bytes      rgc)+  , ("Copied bytes"                        , render $ gcdetails_copied_bytes          rgc)+  , ("Parallel max copied bytes"           , render $ gcdetails_par_max_copied_bytes  rgc)+  , ("Sync elapsed ns"                     , render $ gcdetails_sync_elapsed_ns       rgc)+  , ("CPU ns"                              , render $ gcdetails_cpu_ns                rgc)+  , ("Elapsed ns"                          , render $ gcdetails_elapsed_ns            rgc)+  , ("==[Last GC]=="                       , ""                                          )+  ]+  where+    rgc = gc rts+ #else -newtype Stats = Stats GCStats+statsToEntries :: Stats -> [(Text, Text)]+statsToEntries (Stats rts) = seq rts+  [ ("Allocated bytes"            , render $ bytesAllocated         rts)+  , ("GCs"                        , render $ numGcs                 rts)+  , ("Major GCs"                  , render $ numByteUsageSamples    rts)+  , ("Cumulative allocated bytes" , render $ maxBytesUsed           rts)+  , ("Cumulative live bytes"      , render $ cumulativeBytesUsed    rts)+  , ("Cumulative copied  bytes"   , render $ bytesCopied            rts)+  , ("Max slop bytes"             , render $ maxBytesSlop           rts)+  , ("Maximum allocated megabytes", render $ peakMegabytesAllocated rts)+  , ("Mutator CPU seconds"        , render $ mutatorCpuSeconds      rts)+  , ("Mutator elapsed seconds"    , render $ mutatorWallSeconds     rts)+  , ("GC CPU seconds"             , render $ gcCpuSeconds           rts)+  , ("GC elapsed seconds"         , render $ gcWallSeconds          rts)+  , ("CPU seconds"                , render $ cpuSeconds             rts)+  , ("Elapsed seconds"            , render $ wallSeconds            rts)+  , ("Total bytes copied"         , render $ parTotBytesCopied      rts)+  , ("Max parallel bytes copied"  , render $ parMaxBytesCopied      rts)+  , ("==[Totals]=="               , ""                                 )+  , (""                           , ""                                 )+  , ("(last) Live bytes"          , render $ currentBytesUsed       rts)+  , ("(last) Slop bytes"          , render $ currentBytesSlop       rts)+  , ("==[Last GC]=="              , ""                                 )+  ] -deriving instance Generic GCStats-instance Fields GCStats where fields = genericFields+newtype Stats = Stats GCStats  getStats :: IO (Maybe Stats) getStats =