diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -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
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/exec/Exports.hs b/exec/Exports.hs
new file mode 100644
--- /dev/null
+++ b/exec/Exports.hs
@@ -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
diff --git a/exec/Main.hs b/exec/Main.hs
--- a/exec/Main.hs
+++ b/exec/Main.hs
@@ -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
diff --git a/exec/linux_exported_symbols.txt b/exec/linux_exported_symbols.txt
--- a/exec/linux_exported_symbols.txt
+++ b/exec/linux_exported_symbols.txt
@@ -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;
 };
diff --git a/exec/macos_exported_symbols.txt b/exec/macos_exported_symbols.txt
--- a/exec/macos_exported_symbols.txt
+++ b/exec/macos_exported_symbols.txt
@@ -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
diff --git a/glirc.cabal b/glirc.cabal
--- a/glirc.cabal
+++ b/glirc.cabal
@@ -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
diff --git a/include/glirc-api.h b/include/glirc-api.h
--- a/include/glirc-api.h
+++ b/include/glirc-api.h
@@ -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 **);
diff --git a/src/Client/Authentication/Ecdsa.hs b/src/Client/Authentication/Ecdsa.hs
--- a/src/Client/Authentication/Ecdsa.hs
+++ b/src/Client/Authentication/Ecdsa.hs
@@ -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"
diff --git a/src/Client/CApi.hs b/src/Client/CApi.hs
--- a/src/Client/CApi.hs
+++ b/src/Client/CApi.hs
@@ -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
 
 ------------------------------------------------------------------------
diff --git a/src/Client/CApi/Exports.hs b/src/Client/CApi/Exports.hs
--- a/src/Client/CApi/Exports.hs
+++ b/src/Client/CApi/Exports.hs
@@ -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
diff --git a/src/Client/CApi/Types.hsc b/src/Client/CApi/Types.hsc
--- a/src/Client/CApi/Types.hsc
+++ b/src/Client/CApi/Types.hsc
@@ -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)
diff --git a/src/Client/Commands.hs b/src/Client/Commands.hs
--- a/src/Client/Commands.hs
+++ b/src/Client/Commands.hs
@@ -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
diff --git a/src/Client/Commands/Arguments.hs b/src/Client/Commands/Arguments.hs
deleted file mode 100644
--- a/src/Client/Commands/Arguments.hs
+++ /dev/null
@@ -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 (==' ')
diff --git a/src/Client/Commands/Arguments/Parser.hs b/src/Client/Commands/Arguments/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/Client/Commands/Arguments/Parser.hs
@@ -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
diff --git a/src/Client/Commands/Arguments/Renderer.hs b/src/Client/Commands/Arguments/Renderer.hs
new file mode 100644
--- /dev/null
+++ b/src/Client/Commands/Arguments/Renderer.hs
@@ -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)
diff --git a/src/Client/Commands/Arguments/Spec.hs b/src/Client/Commands/Arguments/Spec.hs
new file mode 100644
--- /dev/null
+++ b/src/Client/Commands/Arguments/Spec.hs
@@ -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)
diff --git a/src/Client/Commands/Interpolation.hs b/src/Client/Commands/Interpolation.hs
--- a/src/Client/Commands/Interpolation.hs
+++ b/src/Client/Commands/Interpolation.hs
@@ -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 @$@.
diff --git a/src/Client/Configuration.hs b/src/Client/Configuration.hs
--- a/src/Client/Configuration.hs
+++ b/src/Client/Configuration.hs
@@ -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
diff --git a/src/Client/Configuration/ServerSettings.hs b/src/Client/Configuration/ServerSettings.hs
--- a/src/Client/Configuration/ServerSettings.hs
+++ b/src/Client/Configuration/ServerSettings.hs
@@ -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"
diff --git a/src/Client/EventLoop/Actions.hs b/src/Client/EventLoop/Actions.hs
--- a/src/Client/EventLoop/Actions.hs
+++ b/src/Client/EventLoop/Actions.hs
@@ -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
diff --git a/src/Client/Image/Arguments.hs b/src/Client/Image/Arguments.hs
deleted file mode 100644
--- a/src/Client/Image/Arguments.hs
+++ /dev/null
@@ -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 ' '
diff --git a/src/Client/Image/Message.hs b/src/Client/Image/Message.hs
--- a/src/Client/Image/Message.hs
+++ b/src/Client/Image/Message.hs
@@ -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
diff --git a/src/Client/Image/MircFormatting.hs b/src/Client/Image/MircFormatting.hs
--- a/src/Client/Image/MircFormatting.hs
+++ b/src/Client/Image/MircFormatting.hs
@@ -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'
diff --git a/src/Client/Image/StatusLine.hs b/src/Client/Image/StatusLine.hs
--- a/src/Client/Image/StatusLine.hs
+++ b/src/Client/Image/StatusLine.hs
@@ -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
diff --git a/src/Client/Image/Textbox.hs b/src/Client/Image/Textbox.hs
--- a/src/Client/Image/Textbox.hs
+++ b/src/Client/Image/Textbox.hs
@@ -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)
diff --git a/src/Client/Log.hs b/src/Client/Log.hs
--- a/src/Client/Log.hs
+++ b/src/Client/Log.hs
@@ -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)
 
 
diff --git a/src/Client/Network/Async.hs b/src/Client/Network/Async.hs
--- a/src/Client/Network/Async.hs
+++ b/src/Client/Network/Async.hs
@@ -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
diff --git a/src/Client/Network/Connect.hs b/src/Client/Network/Connect.hs
--- a/src/Client/Network/Connect.hs
+++ b/src/Client/Network/Connect.hs
@@ -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
diff --git a/src/Client/State.hs b/src/Client/State.hs
--- a/src/Client/State.hs
+++ b/src/Client/State.hs
@@ -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
diff --git a/src/Client/State/Network.hs b/src/Client/State/Network.hs
--- a/src/Client/State/Network.hs
+++ b/src/Client/State/Network.hs
@@ -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        = ""
diff --git a/src/Client/View.hs b/src/Client/View.hs
--- a/src/Client/View.hs
+++ b/src/Client/View.hs
@@ -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
diff --git a/src/Client/View/Digraphs.hs b/src/Client/View/Digraphs.hs
--- a/src/Client/View/Digraphs.hs
+++ b/src/Client/View/Digraphs.hs
@@ -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
diff --git a/src/Client/View/Help.hs b/src/Client/View/Help.hs
--- a/src/Client/View/Help.hs
+++ b/src/Client/View/Help.hs
@@ -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
diff --git a/src/Client/View/Mentions.hs b/src/Client/View/Mentions.hs
--- a/src/Client/View/Mentions.hs
+++ b/src/Client/View/Mentions.hs
@@ -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
diff --git a/src/Client/View/Messages.hs b/src/Client/View/Messages.hs
--- a/src/Client/View/Messages.hs
+++ b/src/Client/View/Messages.hs
@@ -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)
 
 ------------------------------------------------------------------------
 
diff --git a/src/Client/View/Palette.hs b/src/Client/View/Palette.hs
--- a/src/Client/View/Palette.hs
+++ b/src/Client/View/Palette.hs
@@ -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
   ]
diff --git a/src/DigraphQuote.hs b/src/DigraphQuote.hs
new file mode 100644
--- /dev/null
+++ b/src/DigraphQuote.hs
@@ -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
diff --git a/src/Digraphs.hs b/src/Digraphs.hs
--- a/src/Digraphs.hs
+++ b/src/Digraphs.hs
@@ -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
+  |]
diff --git a/src/RtsStats.hs b/src/RtsStats.hs
--- a/src/RtsStats.hs
+++ b/src/RtsStats.hs
@@ -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 =
