diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,13 @@
 # Revision history for glirc2
 
+## 2.18
+
+* Add digraph support under `M-k` and `/digraphs`
+* Add ECDSA-NIST256P-CHALLENGE support for Freenode via Tor
+* Load mask list on `/masks`
+* Add `C-x` to change to next network window
+* Allow `/clear NETWORK *` to clear all windows for the given network
+
 ## 2.17
 
 * Add `reconnect-attempts` setting
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -194,7 +194,8 @@
 | `realname`            | text                 | real name / GECOS                                              |
 | `password`            | text                 | server password                                                |
 | `sasl-username`       | text                 | SASL username                                                  |
-| `sasl-password`       | text                 | SASL password                                                  |
+| `sasl-password`       | text                 | SASL password (PLAIN mode)                                     |
+| `sasl-ecdsa-key`      | text                 | Path ecdsa private key file (ECDSA-NIST256P-CHALLENGE mode)    |
 | `tls`                 | yes/yes-insecure/no  | use TLS to connect (insecure mode disables certificate checks) |
 | `tls-client-cert`     | text                 | path to TLS client certificate                                 |
 | `tls-client-key`      | text                 | path to TLS client key                                         |
@@ -272,6 +273,7 @@
 * `/reload [path]` - Load a new configuration file (optional path)
 * `/windows [filter]` - List all open windows (filters: networks, channels, users)
 * `/palette` - Show the client palette
+* `/digraphs` - Show the table of digraphs
 * `/mentions` - Show all the highlighted lines across all windows
 * `/extension <extension name> <params...>` - Send the given params to the named extension
 * `/exec [-n network] [-c channel] <command> <arguments...>` - Execute a command, If no network or channel are provided send output to client window, if network and channel are provided send output as messages, if network is provided send output as raw IRC messages.
@@ -355,8 +357,9 @@
 
 Window navigation
 
-* `C-n` next channel
-* `C-p` previous channel
+* `C-n` next window
+* `C-p` previous window
+* `C-x` next network
 * `M-#` jump to window - `1234567890qwertyuiop!@#$%^&*()QWERTYUIOP`
 * `M-a` jump to activity
 * `M-s` jump to previous window
@@ -379,6 +382,7 @@
 * `M-Backspace` delete word backwards
 * `M-d` delete word forwards
 * `M-Enter` insert newline
+* `M-k` insert digraph (2-characters before cursor)
 
 * `Tab` nickname completion
 
diff --git a/exec/Main.hs b/exec/Main.hs
--- a/exec/Main.hs
+++ b/exec/Main.hs
@@ -36,11 +36,11 @@
        eventLoop
 
 initialNetworkLogic :: Options -> ClientState -> IO ClientState
-initialNetworkLogic opts st
-  | view optNoConnect opts = return st
-  | otherwise              = addInitialNetworks networks st
+initialNetworkLogic opts st = addInitialNetworks (nub networks) st
   where
-    networks = nub (clientAutoconnects st ++ view optInitialNetworks opts)
+    networks
+      | view optNoConnect opts = view optInitialNetworks opts
+      | otherwise              = view optInitialNetworks opts ++ clientAutoconnects st
 
 -- | Load configuration and handle errors along the way.
 loadConfiguration' :: Maybe FilePath -> IO Configuration
diff --git a/glirc.cabal b/glirc.cabal
--- a/glirc.cabal
+++ b/glirc.cabal
@@ -1,5 +1,5 @@
 name:                glirc
-version:             2.17
+version:             2.18
 synopsis:            Console IRC client
 description:         Console IRC client
                      .
@@ -54,7 +54,8 @@
   default-language:    Haskell2010
   build-tools:         hsc2hs
 
-  exposed-modules:     Client.CApi
+  exposed-modules:     Client.Authentication.Ecdsa
+                       Client.CApi
                        Client.CApi.Exports
                        Client.CApi.Types
                        Client.Commands
@@ -75,9 +76,11 @@
                        Client.Image.Arguments
                        Client.Image.Message
                        Client.Image.MircFormatting
+                       Client.Image.PackedImage
                        Client.Image.Palette
                        Client.Image.StatusLine
                        Client.Image.Textbox
+                       Client.Log
                        Client.Message
                        Client.Network.Async
                        Client.Network.Connect
@@ -91,6 +94,7 @@
                        Client.State.Window
                        Client.View
                        Client.View.ChannelInfo
+                       Client.View.Digraphs
                        Client.View.Help
                        Client.View.MaskList
                        Client.View.Mentions
@@ -103,6 +107,7 @@
 
   other-modules:       LensUtils
                        StrictUnit
+                       Digraphs
                        Paths_glirc
 
   build-depends:       base                 >=4.9    && <4.10,
@@ -118,8 +123,9 @@
                        filepath             >=1.4.1  && <1.5,
                        gitrev               >=1.2    && <1.3,
                        hashable             >=1.2.4  && <1.3,
-                       irc-core             >=2.1.1  && <2.2,
+                       irc-core             >=2.2    && <2.3,
                        lens                 >=4.14   && <4.15,
+                       memory               >=0.13   && <0.14,
                        network              >=2.6.2  && <2.7,
                        process              >=1.4.2  && <1.5,
                        regex-tdfa           >=1.2    && <1.3,
diff --git a/src/Client/Authentication/Ecdsa.hs b/src/Client/Authentication/Ecdsa.hs
new file mode 100644
--- /dev/null
+++ b/src/Client/Authentication/Ecdsa.hs
@@ -0,0 +1,45 @@
+{-# Language OverloadedStrings #-}
+{-|
+Module      : Client.Authentication.Ecdsa
+Description : Binding to ecdsatool
+Copyright   : (c) Eric Mertens, 2016
+License     : ISC
+Maintainer  : emertens@gmail.com
+
+Implementation of ECDSA-NIST256P-CHALLENGE SASL authentication mode
+as implemented at <https://github.com/kaniini/ecdsatool> and used
+on Freenode.
+
+Using this mode requires that the @ecdsa@ utility program is available
+in your search path.
+
+-}
+module Client.Authentication.Ecdsa
+  ( authenticationMode
+  , encodeUsername
+  , computeResponse
+  ) where
+
+import           Client.Configuration (resolveConfigurationPath)
+import           Control.Exception (displayException, try)
+import           Data.ByteArray.Encoding
+import           Data.Text (Text)
+import qualified Data.Text as Text
+import qualified Data.Text.Encoding as Text
+import           System.IO.Error (IOError)
+import           System.Process (readProcess)
+
+authenticationMode :: Text
+authenticationMode = "ECDSA-NIST256P-CHALLENGE"
+
+encodeUsername :: Text -> Text
+encodeUsername = Text.decodeUtf8 . convertToBase Base64 . Text.encodeUtf8
+
+computeResponse :: FilePath -> Text -> IO (Either String Text)
+computeResponse privateKeyFile challenge =
+  do path <- resolveConfigurationPath privateKeyFile
+     res  <- try (readProcess "ecdsatool" ["sign", path, Text.unpack challenge] "")
+     return $! case words <$> res of
+                 Right [resp] -> Right $! Text.pack resp
+                 Right _      -> Left "bad sasl ecdsa response message"
+                 Left e       -> Left (displayException (e :: IOError))
diff --git a/src/Client/Commands.hs b/src/Client/Commands.hs
--- a/src/Client/Commands.hs
+++ b/src/Client/Commands.hs
@@ -103,8 +103,8 @@
 -- and documentation. The arguments and implementation must match so that
 -- the parsed arguments will match what the implementation expects.
 data Command = forall a. Command
-  -- | Names of this command, first in the list is the "primary" name
-  { cmdNames          :: NonEmpty Text
+  { -- | 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
   -- | Multi-line IRC-formatted documentation text used for @/help@
@@ -201,7 +201,7 @@
       | Just !cs <- preview (clientConnection network) st ->
           do now <- getZonedTime
              let msgTxt = Text.pack $ takeWhile (/='\n') msg
-                 ircMsg = ircPrivmsg channel msgTxt
+                 ircMsg = ircPrivmsg (idText channel) msgTxt
                  myNick = UserInfo (view csNick cs) "" ""
                  entry = ClientMessage
                             { _msgTime    = now
@@ -209,7 +209,7 @@
                             , _msgBody    = IrcBody (Privmsg myNick channel msgTxt)
                             }
              sendMsg cs ircMsg
-             commandSuccess $ recordChannelMessage network channel entry st
+             commandSuccess $! recordChannelMessage network channel entry st
 
     _ -> commandFailureMsg "This command requires an active channel" st
 
@@ -315,6 +315,7 @@
       \If no arguments are provided the current window is cleared.\n\
       \If \^Bnetwork\^B is provided the that network window is cleared.\n\
       \If \^Bnetwork\^B and \^Bchannel\^B are provided that chat window is cleared.\n\
+      \If \^Bnetwork\^B is provided and \^Bchannel\^B is \^B*\^O all windows for that network are cleared.\n\
       \\n\
       \If a window is cleared and no longer active that window will be removed from the client.\n"
     $ ClientCommand cmdClear tabFocus
@@ -370,6 +371,14 @@
     $ ClientCommand cmdPalette noClientTab
 
   , Command
+      (pure "digraphs")
+      NoArg
+      "Show the table of digraphs.\n\
+      \\n\
+      \To enter a digraph type the two-character entry and press M-k.\n"
+    $ ClientCommand cmdDigraphs noClientTab
+
+  , Command
       (pure "exec")
       (RemainingArg "arguments")
       "Execute a command synchnonously sending the to a configuration destination.\n\
@@ -761,17 +770,26 @@
     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, _)) ->
         clearFocus (ChannelFocus (Text.pack network) (mkId (Text.pack channel)))
   where
-    clearFocus focus = commandSuccess (focusEffect (windowEffect st))
+    clearNetworkWindows network
+      = commandSuccess
+      $ foldl' (flip clearFocus1) st
+      $ filter (\x -> focusNetwork x == Just (Text.pack network))
+      $ views clientWindows Map.keys st
+
+    clearFocus focus = commandSuccess (clearFocus1 focus st)
+
+    clearFocus1 focus st' = focusEffect (windowEffect st')
       where
         windowEffect
           | isActive  = setWindow (Just emptyWindow)
           | otherwise = setWindow Nothing
 
         focusEffect
-          | not isActive && view clientFocus st == focus = advanceFocus
+          | not isActive && view clientFocus st' == focus = advanceFocus
           | otherwise                                    = id
 
         setWindow = set (clientWindows . at focus)
@@ -779,9 +797,9 @@
         isActive =
           case focus of
             Unfocused                    -> False
-            NetworkFocus network         -> has (clientConnection network) st
+            NetworkFocus network         -> has (clientConnection network) st'
             ChannelFocus network channel -> has (clientConnection network
-                                                .csChannels . ix channel) st
+                                                .csChannels . ix channel) st'
 
 -- | Implementation of @/quote@. Parses arguments as a raw IRC command and
 -- sends to the current network.
@@ -805,9 +823,9 @@
                     , _msgNetwork = network
                     , _msgBody = IrcBody (Ctcp myNick channelId "ACTION" (Text.pack rest))
                     }
-     sendMsg cs (ircPrivmsg channelId actionTxt)
+     sendMsg cs (ircPrivmsg (idText channelId) actionTxt)
      commandSuccess
-       $ recordChannelMessage network channelId entry st
+       $! recordChannelMessage network channelId entry st
 
 -- | Implementation of @/ctcp@
 cmdCtcp :: NetworkCommand (String, (String, String))
@@ -816,7 +834,7 @@
          argTxt = Text.pack args
          tgtTxt = Text.pack target
 
-     sendMsg cs (ircPrivmsg (mkId tgtTxt) ("\^A" <> cmdTxt <> " " <> argTxt <> "\^A"))
+     sendMsg cs (ircPrivmsg tgtTxt ("\^A" <> cmdTxt <> " " <> argTxt <> "\^A"))
      chatCommand
         (\src tgt -> Ctcp src tgt cmdTxt argTxt)
         tgtTxt cs st
@@ -829,7 +847,7 @@
       do let restTxt = Text.pack rest
              tgtTxt = Text.pack target
 
-         sendMsg cs (ircNotice (mkId tgtTxt) restTxt)
+         sendMsg cs (ircNotice tgtTxt restTxt)
          chatCommand
             (\src tgt -> Notice src tgt restTxt)
             tgtTxt cs st
@@ -842,7 +860,7 @@
       do let restTxt = Text.pack rest
              tgtTxt = Text.pack target
 
-         sendMsg cs (ircPrivmsg (mkId tgtTxt) restTxt)
+         sendMsg cs (ircPrivmsg tgtTxt restTxt)
          chatCommand
             (\src tgt -> Privmsg src tgt restTxt)
             tgtTxt cs st
@@ -879,7 +897,7 @@
                        | targetId <- targetIds ]
 
      return $! foldl' (\acc (targetId, entry) ->
-                         recordChannelMessage network targetId entry acc)
+                        recordChannelMessage network targetId entry acc)
                       st
                       entries
 
@@ -927,15 +945,19 @@
     success x =
       commandSuccess (changeSubfocus (FocusWindows x) st)
 
--- | Implementation of @/mentions@ command. Set subfocus to Windows.
+-- | Implementation of @/mentions@ command. Set subfocus to Mentions.
 cmdMentions :: ClientCommand ()
 cmdMentions st _ = commandSuccess (changeSubfocus FocusMentions st)
 
--- | Implementation of @/palette@ command. Set subfocus to Windows.
+-- | Implementation of @/palette@ command. Set subfocus to Palette.
 cmdPalette :: ClientCommand ()
 cmdPalette st _ = commandSuccess (changeSubfocus FocusPalette st)
 
--- | Implementation of @/help@ command. Set subfocus to Windows.
+-- | Implementation of @/digraphs@ command. Set subfocus to Digraphs.
+cmdDigraphs :: ClientCommand ()
+cmdDigraphs st _ = commandSuccess (changeSubfocus FocusDigraphs st)
+
+-- | Implementation of @/help@ command. Set subfocus to Help.
 cmdHelp :: ClientCommand (Maybe (String, ()))
 cmdHelp st mb = commandSuccess (changeSubfocus focus st)
   where
@@ -1036,7 +1058,7 @@
 
 cmdIson :: NetworkCommand String
 cmdIson cs st rest =
-  do sendMsg cs (ircIson [Text.pack (unwords (words rest))])
+  do sendMsg cs (ircIson (Text.pack <$> words rest))
      commandSuccess st
 
 cmdUserhost :: NetworkCommand String
@@ -1118,7 +1140,7 @@
 
 cmdNick :: NetworkCommand (String, ())
 cmdNick cs st (nick,_) =
-  do sendMsg cs (ircNick (mkId (Text.pack nick)))
+  do sendMsg cs (ircNick (Text.pack nick))
      commandSuccess st
 
 cmdPart :: ChannelCommand String
@@ -1172,10 +1194,17 @@
 cmdChannelInfo _ _ st _ = commandSuccess (changeSubfocus FocusInfo st)
 
 cmdMasks :: ChannelCommand (String,())
-cmdMasks _ cs st (rest,_) =
+cmdMasks channel cs st (rest,_) =
   case rest of
     [mode] | mode `elem` view (csModeTypes . modesLists) cs ->
-        commandSuccess (changeSubfocus (FocusMasks mode) st)
+
+        do let connecting = has (csPingStatus . _PingConnecting) cs
+               listLoaded = has (csChannels . ix channel . chanLists . ix mode) cs
+           unless (connecting || listLoaded)
+             (sendMsg cs (ircMode channel [Text.singleton mode]))
+
+           commandSuccess (changeSubfocus (FocusMasks mode) st)
+
     _ -> commandFailureMsg "Unknown mask mode" st
 
 cmdKick :: ChannelCommand (String, String)
@@ -1378,9 +1407,8 @@
     focus@(ChannelFocus network channel) ->
       toListOf
         ( clientWindows    . ix focus
-        . winMessages      . folded
-        . wlBody           . _IrcBody
-        . folding msgActor . to userNick
+        . winMessages      . each
+        . wlSummary        . folding summaryActor
         . filtered isActive
         . filtered isNotSelf ) st
       where
@@ -1395,6 +1423,15 @@
 
     _ -> []
 
+  where
+    -- Returns the 'Identifier' of the nickname responsible for
+    -- the window line when that action was significant enough to
+    -- be considered a hint for tab completion.
+    summaryActor :: IrcSummary -> Maybe Identifier
+    summaryActor (ChatSummary who) = Just who
+    summaryActor (JoinSummary who) = Just who
+    summaryActor _                 = Nothing
+
 -- | Use the *!*@host masks of users for channel lists when setting list modes
 --
 -- Use the channel's mask list for removing modes
@@ -1509,7 +1546,7 @@
     sendToChannel cs channel msgs =
       commandSuccess =<<
       foldM (\st1 msg ->
-        do sendMsg cs (ircPrivmsg (mkId channel) msg)
+        do sendMsg cs (ircPrivmsg channel msg)
            chatCommand'
               (\src tgt -> Privmsg src tgt msg)
               channel
@@ -1553,10 +1590,7 @@
   where
     focus = view clientFocus st
 
-    urlMatches :: Text -> [Text]
-    urlMatches = getAllTextMatches . match urlPattern
-
-    urls = toListOf ( clientWindows . ix focus . winMessages . folded . wlText
+    urls = toListOf ( clientWindows . ix focus . winMessages . each . wlText
                     . folding urlMatches) st
 
     doUrlOpen opener n =
diff --git a/src/Client/Configuration.hs b/src/Client/Configuration.hs
--- a/src/Client/Configuration.hs
+++ b/src/Client/Configuration.hs
@@ -260,6 +260,7 @@
     "password"            -> setFieldMb     ssPassword
     "sasl-username"       -> setFieldMb     ssSaslUsername
     "sasl-password"       -> setFieldMb     ssSaslPassword
+    "sasl-ecdsa-key"      -> setFieldWithMb ssSaslEcdsaFile parseString
     "hostname"            -> setFieldWith   ssHostName      parseString
     "port"                -> setFieldWithMb ssPort          parseNum
     "tls"                 -> setFieldWith   ssTls           parseUseTls
@@ -277,6 +278,7 @@
     "reconnect-attempts"  -> setField       ssReconnectAttempts
     "autoconnect"         -> setField       ssAutoconnect
     "nick-completion"     -> setFieldWith   ssNickCompletion parseNickCompletion
+    "log-dir"             -> setFieldWithMb ssLogDir parseString
     _                     -> failure "Unknown setting"
   where
     setField   l = setFieldWith   l parseConfig
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
@@ -25,6 +25,7 @@
   , ssPassword
   , ssSaslUsername
   , ssSaslPassword
+  , ssSaslEcdsaFile
   , ssHostName
   , ssPort
   , ssTls
@@ -42,6 +43,7 @@
   , ssReconnectAttempts
   , ssAutoconnect
   , ssNickCompletion
+  , ssLogDir
 
   -- * Load function
   , loadDefaultServerSettings
@@ -71,7 +73,8 @@
   , _ssUserInfo         :: !Text -- ^ CTCP userinfo
   , _ssPassword         :: !(Maybe Text) -- ^ server password
   , _ssSaslUsername     :: !(Maybe Text) -- ^ SASL username
-  , _ssSaslPassword     :: !(Maybe Text) -- ^ SASL password
+  , _ssSaslPassword     :: !(Maybe Text) -- ^ SASL plain password
+  , _ssSaslEcdsaFile    :: !(Maybe FilePath) -- ^ SASL ecdsa private key
   , _ssHostName         :: !HostName -- ^ server hostname
   , _ssPort             :: !(Maybe PortNumber) -- ^ server port
   , _ssTls              :: !UseTls -- ^ use TLS to connect
@@ -89,6 +92,7 @@
   , _ssReconnectAttempts:: !Int -- ^ The number of reconnect attempts to make on error
   , _ssAutoconnect      :: Bool -- ^ Connect to this network on server startup
   , _ssNickCompletion   :: WordCompletionMode -- ^ Nick completion mode for this server
+  , _ssLogDir           :: Maybe FilePath -- ^ Directory to save logs of chat
   }
   deriving Show
 
@@ -116,6 +120,7 @@
        , _ssPassword      = Text.pack <$> lookup "IRCPASSWORD" env
        , _ssSaslUsername  = Nothing
        , _ssSaslPassword  = Text.pack <$> lookup "SASLPASSWORD" env
+       , _ssSaslEcdsaFile = Nothing
        , _ssHostName      = ""
        , _ssPort          = Nothing
        , _ssTls           = UseInsecure
@@ -133,4 +138,5 @@
        , _ssReconnectAttempts= 6 -- six feels great
        , _ssAutoconnect      = False
        , _ssNickCompletion   = defaultNickWordCompleteMode
+       , _ssLogDir           = Nothing
        }
diff --git a/src/Client/EventLoop.hs b/src/Client/EventLoop.hs
--- a/src/Client/EventLoop.hs
+++ b/src/Client/EventLoop.hs
@@ -15,6 +15,7 @@
   ( eventLoop
   ) where
 
+import qualified Client.Authentication.Ecdsa as Ecdsa
 import           Client.CApi
 import           Client.Commands
 import           Client.Commands.Interpolation
@@ -23,6 +24,7 @@
 import           Client.Hook
 import           Client.Hooks
 import           Client.Image
+import           Client.Log
 import           Client.Message
 import           Client.Network.Async
 import           Client.State
@@ -47,6 +49,7 @@
 import           GHC.IO.Exception (IOErrorType(..), ioe_type)
 import           Graphics.Vty
 import           Irc.Codes
+import           Irc.Commands
 import           Irc.Message
 import           Irc.RawIrcMsg
 import           LensUtils
@@ -96,6 +99,7 @@
 eventLoop st =
   do let vty = view clientVty st
      when (view clientBell st) (beep vty)
+     processLogEntries st
 
      let (pic, st') = clientPicture (clientTick st)
      update vty pic
@@ -117,6 +121,10 @@
 beep :: Vty -> IO ()
 beep = ringTerminalBell . outputIface
 
+processLogEntries :: ClientState -> IO ()
+processLogEntries =
+  traverse_ writeLogLine . reverse . view clientLogQueue
+
 -- | Respond to a network connection successfully connecting.
 doNetworkOpen ::
   NetworkId   {- ^ network id   -} ->
@@ -237,41 +245,75 @@
 
              case stateHook (cookIrcMsg raw) of
                Nothing  -> return st1 -- Message ignored
-               Just irc -> do traverse_ (sendMsg cs) replies
-                              clientResponse time' irc cs st2
-                 where
-                   -- state with message recorded
-                   recSt = case viewHook irc of
-                             Nothing   -> st1 -- Message hidden
-                             Just irc' -> recordIrcMessage network target msg st1
-                               where
-                                 myNick = view csNick cs
-                                 target = msgTarget myNick irc
-                                 msg = ClientMessage
-                                         { _msgTime    = time'
-                                         , _msgNetwork = network
-                                         , _msgBody    = IrcBody irc'
-                                         }
+               Just irc ->
+                 do -- state with message recorded
+                    -- record messages *before* applying state changes
+                    let st2 =
+                          case viewHook irc of
+                            Nothing   -> st1 -- Message hidden
+                            Just irc' -> recordIrcMessage network target msg st1
+                              where
+                                myNick = view csNick cs
+                                target = msgTarget myNick irc
+                                msg = ClientMessage
+                                        { _msgTime    = time'
+                                        , _msgNetwork = network
+                                        , _msgBody    = IrcBody irc'
+                                        }
 
-                   -- record messages *before* applying the changes
-                   --
-                   -- Note: it's important to do this before 'clientResponse'
-                   -- as $nick won't be set until 'doWelcome' happens.
-                   (replies, st2) = applyMessageToClientState time irc networkId cs recSt
+                    let (replies, st3) = applyMessageToClientState time irc networkId cs st2
 
+                    traverse_ (sendMsg cs) replies
+                    clientResponse time' irc cs st3
+
+
 -- | Client-level responses to specific IRC messages.
 -- This is in contrast to the connection state tracking logic in
--- "Client.NetworkState   "
+-- "Client.NetworkState"
 clientResponse :: ZonedTime -> IrcMsg -> NetworkState -> ClientState -> IO ClientState
 clientResponse now irc cs st =
   case irc of
     Reply RPL_WELCOME _ ->
-      foldM
-        (processConnectCmd now cs)
-        st
-        (view (csSettings . ssConnectCmds) cs)
+      -- run connection commands with the network focused and restore it afterward
+      do let focus = NetworkFocus (view csNetwork cs)
+         st' <- foldM (processConnectCmd now cs)
+                      (set clientFocus focus st)
+                      (view (csSettings . ssConnectCmds) cs)
+         return $! set clientFocus (view clientFocus st) st'
+
+    Authenticate challenge
+      | AS_EcdsaWaitChallenge <- view csAuthenticationState cs ->
+         processSaslEcdsa now challenge cs st
+
     _ -> return st
 
+
+processSaslEcdsa ::
+  ZonedTime    {- ^ message time  -} ->
+  Text         {- ^ challenge     -} ->
+  NetworkState {- ^ network state -} ->
+  ClientState  {- ^ client state  -} ->
+  IO ClientState
+processSaslEcdsa now challenge cs st =
+  case view ssSaslEcdsaFile ss of
+    Nothing ->
+      do sendMsg cs ircCapEnd
+         return $! recordError now cs "panic: ecdsatool malformed output" st
+
+    Just path ->
+      do res <- Ecdsa.computeResponse path challenge
+         case res of
+           Left e ->
+             do sendMsg cs ircCapEnd
+                return $! recordError now cs (Text.pack e) st
+           Right resp ->
+             do sendMsg cs (ircAuthenticate resp)
+                return $! set asLens AS_None st
+  where
+    ss = view csSettings cs
+    asLens = clientConnections . ix (view csNetworkId cs) . csAuthenticationState
+
+
 processConnectCmd ::
   ZonedTime       {- ^ now             -} ->
   NetworkState    {- ^ current network -} ->
@@ -341,7 +383,6 @@
     EvKey k modifier -> doKey k modifier st
     EvResize{} -> -- ignore event parameters due to raw TChan use
       do let vty = view clientVty st
-         refresh vty
          (w,h) <- displayBounds (outputIface vty)
          return $! Just $! set clientWidth  w
                          $ set clientHeight h st
@@ -363,6 +404,11 @@
       changeContent f = changeEditor
                       $ over Edit.content f
                       . set  Edit.lastOperation Edit.OtherOperation
+
+      mbChangeEditor f =
+        case clientTextBox f st of
+          Nothing -> continue $! set clientBell True st
+          Just st' -> continue st'
   in
   case modifier of
     [MCtrl] ->
@@ -383,12 +429,16 @@
         KChar 'v' -> changeEditor (Edit.insert '\^V')
         KChar 'p' -> continue (retreatFocus st)
         KChar 'n' -> continue (advanceFocus st)
+        KChar 'x' -> continue (advanceNetworkFocus st)
         KChar 'l' -> do refresh (view clientVty st)
                         continue st
         _         -> continue st
 
     [MMeta] ->
       case key of
+        KChar c   | let names = clientWindowNames st
+                  , Just i <- elemIndex c names ->
+                            continue (jumpFocus i st)
         KEnter    -> changeEditor (Edit.insert '\^J')
         KBS       -> changeEditor (Edit.killWordBackward True)
         KChar 'd' -> changeEditor (Edit.killWordForward True)
@@ -398,9 +448,7 @@
         KRight    -> changeContent Edit.rightWord
         KChar 'a' -> continue (jumpToActivity st)
         KChar 's' -> continue (returnFocus st)
-        KChar c   | let names = clientWindowNames st
-                  , Just i <- elemIndex c names ->
-                            continue (jumpFocus i st)
+        KChar 'k' -> mbChangeEditor Edit.insertDigraph
         _ -> continue st
 
     [] -> -- no modifier
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
@@ -21,6 +21,7 @@
   , coloredUserInfo
   , coloredIdentifier
   , cleanText
+  , cleanChar
   ) where
 
 import           Client.Image.MircFormatting
@@ -72,7 +73,6 @@
   MessageRendererParams -> MessageBody -> Image
 msgImage rm when params body = horizCat
   [ renderTime rm (rendPalette params) when
-  , char defAttr ' '
   , statusMsgImage (rendStatusMsg params)
   , bodyImage rm params body
   ]
@@ -140,7 +140,7 @@
 timeImage :: Palette -> ZonedTime -> Image
 timeImage palette
   = string (view palTime palette)
-  . formatTime defaultTimeLocale "%R"
+  . formatTime defaultTimeLocale "%R "
 
 -- | Render a 'ZonedTime' as full date and time user quiet attributes
 --
@@ -150,7 +150,7 @@
 datetimeImage :: Palette -> ZonedTime -> Image
 datetimeImage palette
   = string (view palTime palette)
-  . formatTime defaultTimeLocale "%F %T"
+  . formatTime defaultTimeLocale "%F %T "
 
 -- | Level of detail to use when rendering
 data RenderMode
@@ -161,9 +161,8 @@
 -- the minimum width.
 rightPad :: RenderMode -> Maybe Integer -> Image -> Image
 rightPad NormalRender (Just minWidth) i =
-  let h = 1
-      w = max 0 (fromIntegral minWidth - imageWidth i)
-  in i <|> backgroundFill w h
+  let w = max 0 (fromIntegral minWidth - imageWidth i)
+  in i <|> string defAttr (replicate w ' ')
 rightPad _ _ i = i
 
 -- | Render a chat message given a rendering mode, the sigils of the user
@@ -407,7 +406,7 @@
     quietAttr = view palMeta palette
     aux x xs
       | Text.null xs = emptyImage
-      | otherwise    = char defAttr x <|> text' quietAttr xs
+      | otherwise    = char quietAttr x <|> text' quietAttr xs
 
 -- | Render an identifier without using colors. This is useful for metadata.
 quietIdentifier :: Palette -> Identifier -> Image
@@ -446,16 +445,15 @@
 
 -- | Returns image and identifier to be used when collapsing metadata
 -- messages.
-metadataImg :: IrcMsg -> Maybe (Image, Identifier, Maybe Identifier)
+metadataImg :: IrcSummary -> Maybe (Image, Identifier, Maybe Identifier)
 metadataImg msg =
   case msg of
-    Quit who _   -> Just (char (withForeColor defAttr red  ) 'x', userNick who, Nothing)
-    Part who _ _ -> Just (char (withForeColor defAttr red  ) '-', userNick who, Nothing)
-    Join who _   -> Just (char (withForeColor defAttr green) '+', userNick who, Nothing)
-    Ctcp who _ cmd _ | cmd /= "ACTION"  ->
-                    Just (char (withForeColor defAttr white) 'C', userNick who, Nothing)
-    Nick old new -> Just (char (withForeColor defAttr yellow) '>', userNick old, Just new)
-    _            -> Nothing
+    QuitSummary who     -> Just (char (withForeColor defAttr red   ) 'x', who, Nothing)
+    PartSummary who     -> Just (char (withForeColor defAttr red   ) '-', who, Nothing)
+    JoinSummary who     -> Just (char (withForeColor defAttr green ) '+', who, Nothing)
+    CtcpSummary who     -> Just (char (withForeColor defAttr white ) 'C', who, Nothing)
+    NickSummary old new -> Just (char (withForeColor defAttr yellow) '>', old, Just new)
+    _                   -> Nothing
 
 -- | Image used when treating ignored chat messages as metadata
 ignoreImage :: Image
diff --git a/src/Client/Image/PackedImage.hs b/src/Client/Image/PackedImage.hs
new file mode 100644
--- /dev/null
+++ b/src/Client/Image/PackedImage.hs
@@ -0,0 +1,121 @@
+{-# Language TypeOperators, MultiParamTypeClasses, StandaloneDeriving, DeriveGeneric #-}
+{-# OPTIONS_GHC -Wno-orphans -funfolding-creation-threshold=1500 -funfolding-use-threshold=5000 #-}
+{-|
+Module      : Client.Image.PackedImage
+Description : Packed vty Image type
+Copyright   : (c) Eric Mertens, 2016
+License     : ISC
+Maintainer  : emertens@gmail.com
+
+This module provides a more memory efficient way to store images.
+
+-}
+module Client.Image.PackedImage
+  ( Image'
+  , _Image'
+  ) where
+
+import           Control.Lens (Iso', iso)
+import qualified Data.Text as S
+import qualified Data.Text.Lazy as L
+import           Data.List
+import           GHC.Generics
+import           Graphics.Vty.Image
+import           Graphics.Vty.Image.Internal
+
+deriving instance Generic Image
+
+
+-- | Isomorphism between packed images and normal images.
+_Image' :: Iso' Image' Image
+_Image' = iso mirror (mirror . compress)
+{-# INLINE _Image' #-}
+
+
+-- | Attempts to locate adjacent text sections with equal attributes
+-- so that they can be merged.
+compress :: Image -> Image
+compress = horizCat . map horizCat . groupBy textsWithEqAttr . flip horizList []
+
+
+textsWithEqAttr :: Image -> Image -> Bool
+textsWithEqAttr (HorizText a _ _ _) (HorizText b _ _ _) = a == b
+textsWithEqAttr _                 _                     = False
+
+
+horizList :: Image -> [Image] -> [Image]
+horizList (HorizJoin x y _ _) = horizList x . horizList y
+horizList x = (x:)
+
+
+-- | Packed, strict version of 'Image' used for long-term storage of images.
+data Image'
+  = HorizText'
+      Attr -- don't unpack, these get reused from the palette
+      {-# UNPACK #-} !S.Text
+      {-# UNPACK #-} !Int
+      {-# UNPACK #-} !Int
+  | HorizJoin'
+      !Image'
+      !Image'
+      {-# UNPACK #-} !Int
+      {-# UNPACK #-} !Int
+  | VertJoin'
+      !Image'
+      !Image'
+      {-# UNPACK #-} !Int
+      {-# UNPACK #-} !Int
+  | BGFill'
+      {-# UNPACK #-} !Int
+      {-# UNPACK #-} !Int
+  | CropRight'
+      !Image'
+      {-# UNPACK #-} !Int
+      {-# UNPACK #-} !Int
+  | CropLeft'
+      !Image'
+      {-# UNPACK #-} !Int
+      {-# UNPACK #-} !Int
+      {-# UNPACK #-} !Int
+  | CropBottom'
+      !Image'
+      {-# UNPACK #-} !Int
+      {-# UNPACK #-} !Int
+  | CropTop'
+      !Image'
+      {-# UNPACK #-} !Int
+      {-# UNPACK #-} !Int
+      {-# UNPACK #-} !Int
+  | EmptyImage'
+  deriving (Show, Generic)
+
+------------------------------------------------------------------------
+
+class    Mirror a      b      where mirror :: a -> b
+instance Mirror Attr   Attr   where mirror = id
+instance Mirror Int    Int    where mirror = id
+instance Mirror L.Text S.Text where mirror = L.toStrict
+instance Mirror S.Text L.Text where mirror = L.fromStrict
+instance Mirror Image  Image' where mirror = to . gmirror . from
+instance Mirror Image' Image  where mirror = to . gmirror . from
+
+------------------------------------------------------------------------
+
+class GMirror f g where
+  gmirror :: f p -> g q
+
+instance GMirror f g => GMirror (M1 i c f) (M1 j d g) where
+  gmirror (M1 x) = M1 (gmirror x)
+
+instance (GMirror f1 g1, GMirror f2 g2) => GMirror (f1 :*: f2) (g1 :*: g2) where
+  gmirror (x :*: y) = gmirror x :*: gmirror y
+
+instance (GMirror f1 g1, GMirror f2 g2) => GMirror (f1 :+: f2) (g1 :+: g2) where
+  gmirror (L1 x) = L1 (gmirror x)
+  gmirror (R1 x) = R1 (gmirror x)
+
+instance GMirror U1 U1 where
+  gmirror _ = U1
+
+instance Mirror a b => GMirror (K1 i a) (K1 j b) where
+  gmirror (K1 x) = K1 (mirror x)
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
@@ -275,6 +275,7 @@
     FocusUsers    -> Just $ string (view palLabel pal) "users"
     FocusMentions -> Just $ string (view palLabel pal) "mentions"
     FocusPalette  -> Just $ string (view palLabel pal) "palette"
+    FocusDigraphs -> Just $ string (view palLabel pal) "digraphs"
     FocusHelp mb  -> Just $ string (view palLabel pal) "help" <|>
                             opt mb
     FocusMasks m  -> Just $ horizCat
diff --git a/src/Client/Log.hs b/src/Client/Log.hs
new file mode 100644
--- /dev/null
+++ b/src/Client/Log.hs
@@ -0,0 +1,90 @@
+{-# Language OverloadedStrings, BangPatterns #-}
+{-|
+Module      : Client.Log
+Description : Support for logging IRC traffic
+Copyright   : (c) Eric Mertens, 2016
+License     : ISC
+Maintainer  : emertens@gmail.com
+
+This module provides provides logging functionality for IRC traffic.
+
+-}
+module Client.Log where
+
+import           Client.Configuration
+import           Client.Image.Message (cleanText)
+import           Client.Message
+import           Control.Exception
+import           Control.Lens hiding ((<.>))
+import qualified Data.ByteString as B
+import           Data.Monoid
+import           Data.Time
+import           Data.Text (Text)
+import qualified Data.Text as Text
+import qualified Data.Text.Encoding as Text
+import qualified Data.Text.Lazy as L
+import qualified Data.Text.Lazy.IO as L
+import qualified Data.Vector.Primitive as PV
+import           Irc.Identifier
+import           Irc.Message
+import           Irc.UserInfo
+import           System.Directory
+import           System.FilePath
+
+data LogLine = LogLine
+  { logBaseDir :: FilePath
+  , logDay     :: Day
+  , logTarget  :: Text
+  , logLine    :: L.Text
+  }
+
+writeLogLine ::
+  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"
+
+     let recursiveFlag = True
+     createDirectoryIfMissing recursiveFlag dir'
+     L.appendFile file (logLine ll)
+
+ignoreProblems :: IO () -> IO ()
+ignoreProblems m = () <$ (try m :: IO (Either IOError ()))
+
+renderLogLine ::
+  ClientMessage {- ^ message       -} ->
+  FilePath      {- ^ log directory -} ->
+  Identifier    {- ^ target        -} ->
+  Maybe LogLine
+renderLogLine !msg dir target =
+  case view msgBody msg of
+    NormalBody{} -> Nothing
+    ErrorBody {} -> Nothing
+    IrcBody irc ->
+      case irc of
+        Privmsg who _ txt ->
+           success (L.fromChunks ["<", idText (userNick who), "> ", cleanText txt])
+        Notice who _ txt ->
+           success (L.fromChunks ["-", idText (userNick who), "- ", cleanText txt])
+        Ctcp who _ "ACTION" txt ->
+           success (L.fromChunks ["* ", idText (userNick who), " ", cleanText txt])
+        _          -> Nothing
+
+  where
+    localtime = zonedTimeToLocalTime (view msgTime msg)
+    day       = localDay localtime
+    tod       = localTimeOfDay localtime
+    todStr    = formatTime defaultTimeLocale "%T" tod
+
+    success txt = Just LogLine
+      { logBaseDir = dir
+      , logDay     = day
+      , logTarget  = Text.toLower
+                   $ Text.decodeUtf8
+                   $ B.pack
+                   $ PV.toList
+                   $ idDenote target
+      , logLine    = L.fromChunks ["[", Text.pack todStr, "] "] <> txt <> "\n"
+      }
diff --git a/src/Client/Message.hs b/src/Client/Message.hs
--- a/src/Client/Message.hs
+++ b/src/Client/Message.hs
@@ -1,4 +1,4 @@
-{-# Language TemplateHaskell #-}
+{-# Language OverloadedStrings, TemplateHaskell #-}
 {-|
 Module      : Client.Message
 Description : Messages to be added to buffers
@@ -25,6 +25,10 @@
   , _NormalBody
 
   -- * Client message operations
+  , IrcSummary(..)
+  , msgSummary
+
+  -- * Client message operations
   , msgText
   ) where
 
@@ -32,6 +36,9 @@
 import           Data.Text (Text)
 import           Data.Time (ZonedTime)
 import           Irc.Message
+import           Irc.Identifier
+import           Irc.UserInfo
+import           Irc.Codes
 
 data MessageBody
   = IrcBody    !IrcMsg
@@ -48,8 +55,42 @@
 
 makeLenses ''ClientMessage
 
+data IrcSummary
+  = JoinSummary {-# UNPACK #-} !Identifier
+  | QuitSummary {-# UNPACK #-} !Identifier
+  | PartSummary {-# UNPACK #-} !Identifier
+  | NickSummary {-# UNPACK #-} !Identifier {-# UNPACK #-} !Identifier
+  | ReplySummary {-# UNPACK #-} !ReplyCode
+  | ChatSummary {-# UNPACK #-} !Identifier
+  | CtcpSummary {-# UNPACK #-} !Identifier
+  | NoSummary
+  deriving (Eq, Show)
+
+
 -- | Compute a searchable text representation of the message
 msgText :: MessageBody -> Text
 msgText (IrcBody    irc) = ircMsgText irc
 msgText (ErrorBody  txt) = txt
 msgText (NormalBody txt) = txt
+
+
+msgSummary :: MessageBody -> IrcSummary
+msgSummary (IrcBody    irc) = ircSummary irc
+msgSummary (ErrorBody  _  ) = NoSummary
+msgSummary (NormalBody _  ) = NoSummary
+
+
+ircSummary :: IrcMsg -> IrcSummary
+ircSummary msg =
+  case msg of
+    Join who _      -> JoinSummary (userNick who)
+    Part who _ _    -> PartSummary (userNick who)
+    Quit who _      -> QuitSummary (userNick who)
+    Nick who who'   -> NickSummary (userNick who) who'
+    Privmsg who _ _ -> ChatSummary (userNick who)
+    Notice who _ _  -> ChatSummary (userNick who)
+    Ctcp who _ "ACTION" _ -> ChatSummary (userNick who)
+    Ctcp who _ _ _ -> CtcpSummary (userNick who)
+    CtcpNotice who _ _ _ -> ChatSummary (userNick who)
+    Reply code _    -> ReplySummary code
+    _               -> NoSummary
diff --git a/src/Client/State.hs b/src/Client/State.hs
--- a/src/Client/State.hs
+++ b/src/Client/State.hs
@@ -39,6 +39,7 @@
   , clientBell
   , clientExtensions
   , clientRegex
+  , clientLogQueue
 
   -- * Client operations
   , withClientState
@@ -50,7 +51,7 @@
 
   , consumeInput
   , currentCompletionList
-  , ircIgnorable
+  , identIgnored
   , clientFirstLine
   , clientLine
   , abortNetwork
@@ -77,6 +78,7 @@
   , changeSubfocus
   , returnFocus
   , advanceFocus
+  , advanceNetworkFocus
   , retreatFocus
   , jumpToActivity
   , jumpFocus
@@ -90,6 +92,7 @@
 
   -- * URL view
   , urlPattern
+  , urlMatches
 
   ) where
 
@@ -98,7 +101,9 @@
 import           Client.Configuration
 import           Client.Configuration.ServerSettings
 import           Client.Image.Message
+import           Client.Image.PackedImage
 import           Client.Image.Palette
+import           Client.Log
 import           Client.Message
 import           Client.Network.Async
 import           Client.State.Channel
@@ -106,6 +111,7 @@
 import           Client.State.Focus
 import           Client.State.Network
 import           Client.State.Window
+import           Control.Applicative
 import           Control.Concurrent.MVar
 import           Control.Concurrent.STM
 import           Control.DeepSeq
@@ -129,7 +135,7 @@
 import           Data.Time
 import           Foreign.Ptr
 import           Foreign.StablePtr
-import           Graphics.Vty
+import           Graphics.Vty hiding ((<|>))
 import           Irc.Codes
 import           Irc.Identifier
 import           Irc.Message
@@ -172,7 +178,8 @@
 
   , _clientIgnores           :: !(HashSet Identifier)     -- ^ ignored nicknames
 
-  , _clientExtensions        :: !ExtensionState
+  , _clientExtensions        :: !ExtensionState           -- ^ state of loaded extensions
+  , _clientLogQueue          :: ![LogLine]                -- ^ log lines ready to write
   }
 
 data ExtensionState = ExtensionState
@@ -246,6 +253,7 @@
         , _clientNextConnectionId  = 0
         , _clientBell              = False
         , _clientExtensions        = exts
+        , _clientLogQueue          = []
         }
 
 -- | Initialize a 'Vty' value and run a continuation. Shutdown the 'Vty'
@@ -272,16 +280,18 @@
   case preview (clientConnection network) st of
     Nothing -> return st
     Just cs -> do abortConnection ForcedDisconnect (view csSocket cs)
-                  return $ set (clientNetworkMap . at network) Nothing st
+                  return $! over clientNetworkMap (sans network) st
 
 -- | Add a message to the window associated with a given channel
 recordChannelMessage ::
   Text       {- ^ network -} ->
   Identifier {- ^ channel -} ->
   ClientMessage ->
-  ClientState -> ClientState
-recordChannelMessage network channel msg st =
-  recordWindowLine focus wl st
+  ClientState ->
+  ClientState
+recordChannelMessage network channel msg st
+  = recordLogLine msg channel
+  $ recordWindowLine focus wl st
   where
     focus      = ChannelFocus network channel'
     wl         = toWindowLine rendParams importance msg
@@ -302,6 +312,20 @@
     highlights              = clientHighlightsNetwork network st
 
 
+recordLogLine ::
+  ClientMessage {- ^ message      -} ->
+  Identifier    {- ^ target       -} ->
+  ClientState   {- ^ client state -} ->
+  ClientState
+recordLogLine msg target st =
+  case view (clientConnection (view msgNetwork msg) . csSettings . ssLogDir) st of
+    Nothing -> st
+    Just dir ->
+      case renderLogLine msg dir target of
+        Nothing  -> st
+        Just ll  -> over clientLogQueue (cons ll) st
+
+
 -- | Extract the status mode sigils from a message target.
 splitStatusMsgModes ::
   [Char]               {- ^ possible modes              -} ->
@@ -358,7 +382,7 @@
 -- | Predicate for messages that should be ignored based on the
 -- configurable ignore list
 ircIgnorable :: IrcMsg -> ClientState -> Maybe Identifier
-ircIgnorable msg st =
+ircIgnorable msg !st =
   case msg of
     Privmsg who _ _ -> checkUser who
     Notice  who _ _ -> checkUser who
@@ -366,14 +390,16 @@
     Ctcp who _ "ACTION" _ -> checkUser who
     -- notice ctcp responses are not already metadata
     CtcpNotice who _ _ _ -> checkUser who
-    _               -> Nothing
+    _                    -> Nothing
   where
-    ignores = view clientIgnores st
     checkUser !who
-      | HashSet.member (userNick who) ignores = Just (userNick who)
-      | otherwise                             = Nothing
+      | identIgnored (userNick who) st = Just (userNick who)
+      | otherwise                      = Nothing
 
+identIgnored :: Identifier -> ClientState -> Bool
+identIgnored who st = HashSet.member who (view clientIgnores st)
 
+
 -- | Record a message in the windows corresponding to the given target
 recordIrcMessage ::
   Text {- ^ network -} ->
@@ -439,17 +465,18 @@
 recordWindowLine ::
   Focus ->
   WindowLine ->
-  ClientState -> ClientState
+  ClientState ->
+  ClientState
 recordWindowLine focus wl =
   over (clientWindows . at focus)
        (\w -> Just $! addToWindow wl (fromMaybe emptyWindow w))
 
 toWindowLine :: MessageRendererParams -> WindowLineImportance -> ClientMessage -> WindowLine
 toWindowLine params importance msg = WindowLine
-  { _wlBody       = view msgBody msg
+  { _wlSummary    = msgSummary (view msgBody msg)
   , _wlText       = msgText (view msgBody msg)
-  , _wlImage      = mkImage NormalRender
-  , _wlFullImage  = mkImage DetailedRender
+  , _wlImage'     = _Image' # mkImage NormalRender
+  , _wlFullImage' = _Image' # mkImage DetailedRender
   , _wlImportance = importance
   , _wlTimestamp  = zonedTimeToUTC (view msgTime msg)
   }
@@ -469,7 +496,9 @@
 
 -- | Function applied to the client state every redraw.
 clientTick :: ClientState -> ClientState
-clientTick = set clientBell False . markSeen
+clientTick = set clientBell False
+           . markSeen
+           . set clientLogQueue []
 
 
 -- | Mark the messages on the current window (and any splits) as seen.
@@ -560,8 +589,17 @@
   compile
     defaultCompOpt
     defaultExecOpt{captureGroups=False}
-    "https?://([[:alnum:]-]+\\.)*([[:alnum:]-]+)(:[[:digit:]]+)?(/[^[:cntrl:][:space:]]*)"
+    "https?://([[:alnum:]-]+\\.)*([[:alnum:]-]+)(:[[:digit:]]+)?(/[^[:cntrl:][:space:]]*)|\
+    \<https?://[^>]*>"
 
+urlMatches :: Text -> [Text]
+urlMatches = map removeBrackets . getAllTextMatches . match urlPattern
+  where
+    removeBrackets t =
+      case Text.uncons t of
+       Just ('<',t') | not (Text.null t') -> Text.init t'
+       _                                  -> t
+
 -- | Remove a network connection and unlink it from the network map.
 -- This operation assumes that the networkconnection exists and should
 -- only be applied once per connection.
@@ -771,29 +809,44 @@
 -- | Step focus to the next window when on message view. Otherwise
 -- switch to message view.
 advanceFocus :: ClientState -> ClientState
-advanceFocus = stepFocus False
+advanceFocus = stepFocus $ \l r ->
+  fst . fst <$> Map.minViewWithKey r <|>
+  fst . fst <$> Map.minViewWithKey l
 
 -- | Step focus to the previous window when on message view. Otherwise
 -- switch to message view.
 retreatFocus :: ClientState -> ClientState
-retreatFocus = stepFocus True
+retreatFocus = stepFocus $ \l r ->
+  fst . fst <$> Map.maxViewWithKey l <|>
+  fst . fst <$> Map.maxViewWithKey r
 
 -- | Step focus to the next window when on message view. Otherwise
--- switch to message view. Reverse the step order when argument is 'True'.
-stepFocus :: Bool {- ^ reversed -} -> ClientState -> ClientState
-stepFocus isReversed st
-  | view clientSubfocus st /= FocusMessages = changeSubfocus FocusMessages st
-
-  | isReversed, Just ((k,_),_) <- Map.maxViewWithKey l = changeFocus k st
-  | isReversed, Just ((k,_),_) <- Map.maxViewWithKey r = changeFocus k st
+-- switch to message view.
+advanceNetworkFocus :: ClientState -> ClientState
+advanceNetworkFocus = stepFocus $ \l r ->
+  fst . fst <$> Map.minViewWithKey (Map.filterWithKey isNetwork r) <|>
+  fst . fst <$> Map.minViewWithKey (Map.filterWithKey isNetwork l)
+  where
+    isNetwork k _ = has _NetworkFocus k
 
-  | isForward , Just ((k,_),_) <- Map.minViewWithKey r = changeFocus k st
-  | isForward , Just ((k,_),_) <- Map.minViewWithKey l = changeFocus k st
+-- | Selection function used in 'stepFocus'
+type FocusSelector =
+  Map Focus Window {- ^ windows before current window -} ->
+  Map Focus Window {- ^ windows after current window  -} ->
+  Maybe Focus      {- ^ window to focus               -}
 
-  | otherwise                                          = st
+-- | Step focus to the next window when on message view. Otherwise
+-- switch to message view. Reverse the step order when argument is 'True'.
+stepFocus ::
+  FocusSelector {- ^ selection function -} ->
+  ClientState   {- ^ client state       -} ->
+  ClientState
+stepFocus selector st =
+  case selector l r of
+    Just k  -> changeFocus k st
+    Nothing -> st
   where
-    isForward = not isReversed
-    (l,r)     = Map.split (view clientFocus st) (view clientWindows st)
+    (l,r) = Map.split (view clientFocus st) (view clientWindows st)
 
 -- | Compute the set of extra identifiers that should be highlighted given
 -- a particular network state.
diff --git a/src/Client/State/EditBox.hs b/src/Client/State/EditBox.hs
--- a/src/Client/State/EditBox.hs
+++ b/src/Client/State/EditBox.hs
@@ -54,6 +54,7 @@
   , earlier
   , later
   , success
+  , insertDigraph
 
   -- * Last operation
   , LastOperation(..)
@@ -241,7 +242,14 @@
   = set lastOperation OtherOperation
   . over content (insertChar c)
 
+
 insertPaste :: String -> EditBox -> EditBox
 insertPaste paste
   = over content (insertPastedString paste)
+  . set lastOperation OtherOperation
+
+
+insertDigraph :: EditBox -> Maybe EditBox
+insertDigraph
+  = content digraph
   . set lastOperation OtherOperation
diff --git a/src/Client/State/EditBox/Content.hs b/src/Client/State/EditBox/Content.hs
--- a/src/Client/State/EditBox/Content.hs
+++ b/src/Client/State/EditBox/Content.hs
@@ -43,11 +43,14 @@
   , insertString
   , insertChar
   , toggle
+  , digraph
   ) where
 
 import           Control.Lens hiding (below)
+import           Control.Monad (guard)
 import           Data.Char (isAlphaNum)
 import           Data.List (find)
+import           Digraphs (lookupDigraph)
 
 data Line = Line
   { _pos  :: !Int
@@ -269,3 +272,15 @@
     swapAt 0 (x:y:z) = y:x:z
     swapAt i (x:xs)  = x:swapAt (i-1) xs
     swapAt _ _       = error "toggle: PANIC! Invalid argument"
+
+
+-- | Use the two characters preceeding the cursor as a digraph and replace
+-- them with the corresponding character.
+digraph :: Content -> Maybe Content
+digraph !c =
+  do let Line n txt = view line c
+     guard (2 <= n)
+     let (pfx,x:y:sfx) = splitAt (n - 2) txt
+     d <- lookupDigraph x y
+     let line' = Line (n-1) (pfx++d:sfx)
+     Just $! set line line' c
diff --git a/src/Client/State/Focus.hs b/src/Client/State/Focus.hs
--- a/src/Client/State/Focus.hs
+++ b/src/Client/State/Focus.hs
@@ -50,6 +50,7 @@
   | FocusWindows WindowsFilter -- ^ Show client windows
   | FocusPalette     -- ^ Show current palette
   | FocusMentions    -- ^ Show all mentions
+  | FocusDigraphs    -- ^ Show all digraphs
   | FocusHelp (Maybe Text) -- ^ Show help window with optional command
   deriving (Eq,Show)
 
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
@@ -20,6 +20,7 @@
   (
   -- * Connection state
     NetworkState(..)
+  , AuthenticateState(..)
   , newNetworkState
 
   -- * Lenses
@@ -42,6 +43,7 @@
   , csPingStatus
   , csLastReceived
   , csMessageHooks
+  , csAuthenticationState
 
   -- * User information
   , UserAndHost(..)
@@ -61,6 +63,7 @@
 
   -- * Timer information
   , PingStatus(..)
+  , _PingConnecting
   , TimedAction(..)
   , nextTimedAction
   , applyTimedAction
@@ -71,6 +74,7 @@
   , sendTopic
   ) where
 
+import qualified Client.Authentication.Ecdsa as Ecdsa
 import           Client.Configuration.ServerSettings
 import           Client.Network.Async
 import           Client.State.Channel
@@ -119,9 +123,18 @@
   , _csPingStatus   :: !PingStatus -- ^ state of ping timer
   , _csLastReceived :: !(Maybe UTCTime) -- ^ time of last message received
   , _csMessageHooks :: ![Text] -- ^ names of message hooks to apply to this connection
+  , _csAuthenticationState :: !AuthenticateState
   }
   deriving Show
 
+-- | State of the authentication transaction
+data AuthenticateState
+  = AS_None -- ^ no active transaction
+  | AS_PlainStarted -- ^ PLAIN mode initiated
+  | AS_EcdsaStarted -- ^ ECDSA-NIST mode initiated
+  | AS_EcdsaWaitChallenge -- ^ ECDSA_NIST user sent waiting for challenge
+  deriving Show
+
 -- | Pair of username and hostname. Empty strings represent missing information.
 data UserAndHost =
   UserAndHost {-# UNPACK #-} !Text {-# UNPACK #-} !Text
@@ -136,6 +149,7 @@
   | PingConnecting !Int !(Maybe UTCTime) -- ^ number of attempts, last known connection time
   deriving Show
 
+
 data Transaction
   = NoTransaction
   | NamesTransaction [Text]
@@ -145,6 +159,7 @@
 
 makeLenses ''NetworkState
 makePrisms ''Transaction
+makePrisms ''PingStatus
 
 csNick :: Lens' NetworkState Identifier
 csNick = csUserInfo . uiNick
@@ -221,6 +236,7 @@
   , _csNextPingTime = Nothing
   , _csLastReceived = Nothing
   , _csMessageHooks = view ssMessageHooks settings
+  , _csAuthenticationState = AS_None
   }
 
 
@@ -261,7 +277,7 @@
            noReply
          $ forgetUser' (userNick user) -- possibly forget
          $ if userNick user == view csNick cs
-             then set (csChannels . at chan) Nothing cs
+             then over csChannels (sans chan) cs
              else overChannel chan (partChannel (userNick user)) cs
 
     Nick oldNick newNick ->
@@ -307,7 +323,7 @@
   ([RawIrcMsg], NetworkState) {- ^ replies, updated state -}
 doBadNick badNick cs =
   case NonEmpty.dropWhile (badNick/=) (view (csSettings . ssNicks) cs) of
-    _:next:_ -> ([ircNick (mkId next)], cs)
+    _:next:_ -> ([ircNick next], cs)
     _        -> ([], cs)
 
 doTopic :: ZonedTime -> UserInfo -> Identifier -> Text -> NetworkState -> NetworkState
@@ -577,7 +593,7 @@
         in setStrict (chanLists . ix mode . at arg) entry c
 
       | polarity  = set (chanModes . at mode) (Just arg) c
-      | otherwise = set (chanModes . at mode) Nothing    c
+      | otherwise = over chanModes (sans mode) c
 
     setPrefixMode polarity sigil sigils
       | not polarity        = delete sigil sigils
@@ -602,17 +618,32 @@
   where
     ss = view csSettings cs
     sasl = ["sasl" | isJust (view ssSaslUsername ss)
-                   , isJust (view ssSaslPassword ss) ]
+                   , isJust (view ssSaslPassword ss) ||
+                     isJust (view ssSaslEcdsaFile ss) ]
 
 doAuthenticate :: Text -> NetworkState -> ([RawIrcMsg], NetworkState)
-doAuthenticate "+" cs
-  | Just user <- view ssSaslUsername ss
-  , Just pass <- view ssSaslPassword ss
-  = ([ircAuthenticate (encodePlainAuthentication user pass)], cs)
+doAuthenticate param cs =
+  case view csAuthenticationState cs of
+    AS_PlainStarted
+      | "+" <- param
+      , Just user <- view ssSaslUsername ss
+      , Just pass <- view ssSaslPassword ss
+      -> ([ircAuthenticate (encodePlainAuthentication user pass)],
+          set csAuthenticationState AS_None cs)
+
+    AS_EcdsaStarted
+      | "+" <- param
+      , Just user <- view ssSaslUsername ss
+      -> ([ircAuthenticate (Ecdsa.encodeUsername user)],
+          set csAuthenticationState AS_EcdsaWaitChallenge cs)
+
+    AS_EcdsaWaitChallenge -> ([], cs) -- handled in Client.EventLoop!
+
+    _ -> ([ircCapEnd], cs) -- really shouldn't happen
+
   where
     ss = view csSettings cs
 
-doAuthenticate _ cs = ([ircCapEnd], cs)
 
 doCap :: CapCmd -> [Text] -> NetworkState -> ([RawIrcMsg], NetworkState)
 doCap cmd args cs =
@@ -626,8 +657,13 @@
 
     (CapAck,[capsTxt])
       | "sasl" `elem` caps ->
-          ([ircAuthenticate plainAuthenticationMode], cs)
+          if isJust (view ssSaslEcdsaFile ss)
+            then ([ircAuthenticate Ecdsa.authenticationMode], set csAuthenticationState AS_EcdsaStarted cs)
+          else if isJust (view ssSaslUsername ss)
+            then ([ircAuthenticate plainAuthenticationMode], set csAuthenticationState AS_PlainStarted cs)
+          else ([ircCapEnd], cs)
       where
+        ss   = view csSettings cs
         caps = Text.words capsTxt
 
     _ -> ([ircCapEnd], cs)
@@ -637,7 +673,7 @@
 initialMessages cs
    = [ ircCapLs ]
   ++ [ ircPass pass | Just pass <- [view ssPassword ss]]
-  ++ [ ircNick (mkId (views ssNicks NonEmpty.head ss))
+  ++ [ ircNick (views ssNicks NonEmpty.head ss)
      , ircUser (view ssUser ss) False True (view ssReal ss)
      ]
   where
@@ -751,7 +787,7 @@
                     (Just (UserAndHost user host))
 
 forgetUser :: Identifier -> NetworkState -> NetworkState
-forgetUser nick = set (csUsers . at nick) Nothing
+forgetUser = over csUsers . sans
 
 renameUser :: Identifier -> Identifier -> NetworkState -> NetworkState
 renameUser old new cs = set (csUsers . at new) entry cs'
diff --git a/src/Client/State/Window.hs b/src/Client/State/Window.hs
--- a/src/Client/State/Window.hs
+++ b/src/Client/State/Window.hs
@@ -1,4 +1,4 @@
-{-# Language BangPatterns, TemplateHaskell #-}
+{-# Language MultiParamTypeClasses, BangPatterns, TemplateHaskell #-}
 
 {-|
 Module      : Client.State.Window
@@ -22,7 +22,7 @@
 
   -- * Window lines
   , WindowLine(..)
-  , wlBody
+  , wlSummary
   , wlText
   , wlImage
   , wlFullImage
@@ -38,6 +38,7 @@
   , windowSeen
   ) where
 
+import           Client.Image.PackedImage
 import           Client.Message
 import           Control.Lens
 import           Data.Text (Text)
@@ -46,18 +47,22 @@
 
 -- | A single message to be displayed in a window
 data WindowLine = WindowLine
-  { _wlBody       :: !MessageBody -- ^ Original Haskell value
+  { _wlSummary    :: !IrcSummary  -- ^ Summary value
   , _wlText       :: {-# UNPACK #-} !Text -- ^ Searchable text form
-  , _wlImage      :: !Image       -- ^ Normal rendered image
-  , _wlFullImage  :: !Image       -- ^ Detailed rendered image
+  , _wlImage'     :: !Image'      -- ^ Normal rendered image
+  , _wlFullImage' :: !Image'      -- ^ Detailed rendered image
   , _wlImportance :: !WindowLineImportance -- ^ Importance of message
   , _wlTimestamp  :: {-# UNPACK #-} !UTCTime
   }
 
+data WindowLines
+  = {-# UNPACK #-} !WindowLine :- WindowLines
+  | Nil
+
 -- | A 'Window' tracks all of the messages and metadata for a particular
 -- message buffer.
 data Window = Window
-  { _winMessages :: ![WindowLine] -- ^ Messages to display, newest first
+  { _winMessages :: !WindowLines  -- ^ Messages to display, newest first
   , _winUnread   :: !Int          -- ^ Messages added since buffer was visible
   , _winTotal    :: !Int          -- ^ Messages in buffer
   , _winMention  :: !Bool         -- ^ Indicates an important event is unread
@@ -73,10 +78,22 @@
 makeLenses ''Window
 makeLenses ''WindowLine
 
+
+-- | Lens for the '_wlImage' field viewed in unpacked form.
+wlImage :: Lens' WindowLine Image
+wlImage = wlImage' . _Image'
+{-# INLINE wlImage #-}
+
+-- | Lens for the '_wlFullImage' field viewed in unpacked form.
+wlFullImage :: Lens' WindowLine Image
+wlFullImage = wlFullImage' . _Image'
+{-# INLINE wlFullImage #-}
+
+
 -- | A window with no messages
 emptyWindow :: Window
 emptyWindow = Window
-  { _winMessages = []
+  { _winMessages = Nil
   , _winUnread   = 0
   , _winTotal    = 0
   , _winMention  = False
@@ -86,7 +103,7 @@
 -- unread count will be updated according to the given importance.
 addToWindow :: WindowLine -> Window -> Window
 addToWindow !msg !win = Window
-    { _winMessages = msg : view winMessages win
+    { _winMessages = msg :- view winMessages win
     , _winTotal    = view winTotal win + 1
     , _winUnread   = view winUnread win
                    + (if view wlImportance msg == WLBoring then 0 else 1)
@@ -98,3 +115,8 @@
 windowSeen :: Window -> Window
 windowSeen = set winUnread 0
            . set winMention False
+
+
+instance Each WindowLines WindowLines WindowLine WindowLine where
+  each _ Nil = pure Nil
+  each f (x :- xs) = (:-) <$> f x <*> each f xs
diff --git a/src/Client/View.hs b/src/Client/View.hs
--- a/src/Client/View.hs
+++ b/src/Client/View.hs
@@ -16,6 +16,7 @@
 import           Client.State
 import           Client.State.Focus
 import           Client.View.ChannelInfo
+import           Client.View.Digraphs
 import           Client.View.Help
 import           Client.View.MaskList
 import           Client.View.Mentions
@@ -30,7 +31,8 @@
 viewLines :: Focus -> Subfocus -> ClientState -> [Image]
 viewLines focus subfocus !st =
   case (focus, subfocus) of
-    _ | Just ("url",_) <- clientActiveCommand st -> urlSelectionView focus st
+    _ | Just ("url",arg) <- clientActiveCommand st ->
+      urlSelectionView focus arg st
     (ChannelFocus network channel, FocusInfo) ->
       channelInfoImages network channel st
     (ChannelFocus network channel, FocusUsers)
@@ -41,6 +43,7 @@
     (_, FocusWindows filt) -> windowsImages filt st
     (_, FocusMentions) -> mentionsViewLines st
     (_, FocusPalette) -> paletteViewLines pal
+    (_, FocusDigraphs) -> digraphLines st
     (_, FocusHelp mb) -> helpImageLines mb pal
     _ -> chatMessageImages focus st
   where
diff --git a/src/Client/View/Digraphs.hs b/src/Client/View/Digraphs.hs
new file mode 100644
--- /dev/null
+++ b/src/Client/View/Digraphs.hs
@@ -0,0 +1,56 @@
+{-# Language OverloadedStrings #-}
+{-|
+Module      : Client.View.Digraphs
+Description : Character mnemonics
+Copyright   : (c) Eric Mertens, 2016
+License     : ISC
+Maintainer  : emertens@gmail.com
+
+This module provides an view of the built-in digraph list.
+
+-}
+module Client.View.Digraphs (digraphLines) where
+
+import           Client.Image.Message (cleanChar)
+import           Client.State
+import           Control.Lens
+import           Data.List
+import           Data.List.Split
+import qualified Data.Text as Text
+import           Digraphs
+import           Graphics.Vty.Image
+
+-- | Render the lines of a table showing all of the available digraph entries
+digraphLines ::
+  ClientState {- ^ client state -} ->
+  [Image]     {- ^ output lines -}
+digraphLines st
+  = map (horizCat . intersperse sep)
+  $ chunksOf entriesPerLine
+  $ map (text' defAttr)
+  $ filter matcher
+  $ map (Text.pack . drawEntry)
+  $ digraphListToList digraphs
+  where
+    w              = view clientWidth st
+    matcher        = clientMatcher st
+    entriesPerLine = max 1 -- just in case?
+                   $ (w + sepWidth) `quot` (entryWidth + sepWidth)
+
+entryWidth :: Int
+entryWidth = 5 -- "Ka カ"
+
+sepWidth :: Int
+sepWidth = imageWidth sep
+
+sep :: Image
+sep = text' defAttr "   "
+
+drawEntry :: (Char,Char,Char) -> String
+drawEntry (x,y,z) = output ++ replicate (entryWidth - wcswidth output) ' '
+  where
+    output = x:y:z2
+    dottedCircle = '\x25cc'
+    z1 = cleanChar z
+    z2 | wcwidth z1 == 0 = [' ', dottedCircle, z1]
+       | otherwise       = [' ', z1]
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
@@ -73,8 +73,8 @@
       , mlFocus      = focus
       , mlImage      = if detailed then view wlFullImage l else view wlImage l
       }
-  | l <- view winMessages w
-  , WLImportant == view wlImportance l
+  | let p x = WLImportant == view wlImportance x
+  , l <- toListOf (winMessages . each . filtered p) w
   ]
 
 -- | 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
@@ -22,7 +22,9 @@
 import           Client.State.Window
 import           Client.Message
 import           Control.Lens
+import           Control.Monad
 import           Irc.Identifier
+import           Irc.Message
 import           Graphics.Vty.Image
 
 chatMessageImages :: Focus -> ClientState -> [Image]
@@ -31,8 +33,9 @@
     matcher = clientMatcher st
 
     focusedMessages
-        = filter (views wlText matcher)
-        $ view (clientWindows . ix focus . winMessages) st
+        = toListOf ( clientWindows . ix focus
+                   . winMessages . each
+                   . filtered (views wlText matcher)) st
 
     windowLineProcessor
 
@@ -44,9 +47,9 @@
       | otherwise = windowLinesToImages st . filter (not . isNoisy)
 
     isNoisy msg =
-      case view wlBody msg of
-        IrcBody irc -> squelchIrcMsg irc
-        _           -> False
+      case view wlSummary msg of
+        ReplySummary code -> squelchIrcMsg (Reply code [])
+        _                 -> False
 
 detailedImagesWithoutMetadata :: ClientState -> [WindowLine] -> [Image]
 detailedImagesWithoutMetadata st wwls =
@@ -127,9 +130,6 @@
   Maybe (Image, Identifier, Maybe Identifier)
         {- ^ Image, incoming identifier, outgoing identifier if changed -}
 metadataWindowLine st wl =
-  case view wlBody wl of
-    IrcBody irc
-      | Just who <- ircIgnorable irc st -> Just (ignoreImage, who, Nothing)
-      | otherwise                       -> metadataImg irc
-    _                                   -> Nothing
-
+  case view wlSummary wl of
+    ChatSummary who -> (ignoreImage, who, Nothing) <$ guard (identIgnored who st)
+    summary         -> metadataImg summary
diff --git a/src/Client/View/UrlSelection.hs b/src/Client/View/UrlSelection.hs
--- a/src/Client/View/UrlSelection.hs
+++ b/src/Client/View/UrlSelection.hs
@@ -1,4 +1,3 @@
-{-# Language BangPatterns #-}
 {-|
 Module      : Client.View.UrlSelection
 Description : URL selection module
@@ -16,22 +15,41 @@
 
 import           Client.State
 import           Client.State.Window
-import           Client.State.Focus
 import           Client.Image.Message
+import           Client.State.Focus
 import           Control.Lens
-import           Graphics.Vty.Image
-import           Text.Regex.TDFA
 import           Data.Text (Text)
+import           Graphics.Vty.Image
+import           Text.Read (readMaybe)
 
 
-urlSelectionView :: Focus -> ClientState -> [Image]
-urlSelectionView focus st =
-    zipWith draw [1..]
-         $ toListOf (clientWindows . ix focus . winMessages . folded . wlText . folding textUrls) st
+-- | Generate the lines used for the view when typing @/url@
+urlSelectionView ::
+  Focus       {- ^ window to search    -} ->
+  String      {- ^ argument to command -} ->
+  ClientState {- ^ client state        -} ->
+  [Image]     {- ^ image lines         -}
+urlSelectionView focus arg st =
+  zipWith (draw selected) [1..] (toListOf urled st)
+  where
+    urled = clientWindows . ix focus
+          . winMessages   . each
+          . wlText        . folding urlMatches
 
-textUrls :: Text -> [Text]
-textUrls = getAllTextMatches . match urlPattern
+    selected
+      | all (==' ') arg         = 1
+      | Just i <- readMaybe arg = i
+      | otherwise               = 0 -- won't match
 
-draw :: Int -> Text -> Image
-draw i url = string defAttr (shows i ". ")
-         <|> text' defAttr (cleanText url)
+
+-- | Render one line of the url list
+draw ::
+  Int   {- ^ selected index -} ->
+  Int   {- ^ url index      -} ->
+  Text  {- ^ url text       -} ->
+  Image {- ^ rendered line  -}
+draw selected i url = string attr (shows i ". ") <|>
+                      text' attr (cleanText url)
+  where
+    attr | selected == i = withStyle defAttr reverseVideo
+         | otherwise     = defAttr
diff --git a/src/Digraphs.hs b/src/Digraphs.hs
new file mode 100644
--- /dev/null
+++ b/src/Digraphs.hs
@@ -0,0 +1,1359 @@
+{-# 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
