Move sourcecode into 'senf/' directory
[senf.git] / senf / Utils / Termlib / Terminfo.cc
diff --git a/senf/Utils/Termlib/Terminfo.cc b/senf/Utils/Termlib/Terminfo.cc
new file mode 100644 (file)
index 0000000..44bdb6f
--- /dev/null
@@ -0,0 +1,621 @@
+// $Id$
+//
+// Copyright (C) 2009 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Terminfo non-inline non-template implementation */
+
+#include "Terminfo.hh"
+//#include "Terminfo.ih"
+
+// Custom includes
+#include <fstream>
+#include <iomanip>
+#include <boost/filesystem/operations.hpp>
+#include <senf/config.hh>
+#include <senf/Utils/hexdump.hh>
+#include <unistd.h>
+#include <string.h>
+
+//#include "Terminfo.mpp"
+#define prefix_
+///////////////////////////////cc.p////////////////////////////////////////
+
+char const * const senf::term::Terminfo::properties::BooleanNames [] = {
+    "AutoLeftMargin", "AutoRightMargin", "NoEscCtlc", "CeolStandoutGlitch", "EatNewlineGlitch",
+    "EraseOverstrike", "GenericType", "HardCopy", "HasMetaKey", "HasStatusLine", "InsertNullGlitch",
+    "MemoryAbove", "MemoryBelow", "MoveInsertMode", "MoveStandoutMode", "OverStrike",
+    "StatusLineEscOk", "DestTabsMagicSmso", "TildeGlitch", "TransparentUnderline", "XonXoff",
+    "NeedsXonXoff", "PrtrSilent", "HardCursor", "NonRevRmcup", "NoPadChar", "NonDestScrollRegion",
+    "CanChange", "BackColorErase", "HueLightnessSaturation", "ColAddrGlitch", "CrCancelsMicroMode",
+    "HasPrintWheel", "RowAddrGlitch", "SemiAutoRightMargin", "CpiChangesRes", "LpiChangesRes",
+    "BackspacesWithBs", "CrtNoScrolling", "NoCorrectlyWorkingCr", "GnuHasMetaKey",
+    "LinefeedIsNewline", "HasHardwareTabs", "ReturnDoesClrEol" };
+
+char const * const senf::term::Terminfo::properties::NumericNames[] = {
+    "Columns", "InitTabs", "Lines", "LinesOfMemory", "MagicCookieGlitch", "PaddingBaudRate",
+    "VirtualTerminal", "WidthStatusLine", "NumLabels", "LabelHeight", "LabelWidth", "MaxAttributes",
+    "MaximumWindows", "MaxColors", "MaxPairs", "NoColorVideo", "BufferCapacity", "DotVertSpacing",
+    "DotHorzSpacing", "MaxMicroAddress", "MaxMicroJump", "MicroColSize", "MicroLineSize",
+    "NumberOfPins", "OutputResChar", "OutputResLine", "OutputResHorzInch", "OutputResVertInch",
+    "PrintRate", "WideCharSize", "Buttons", "BitImageEntwining", "BitImageType",
+    "MagicCookieGlitchUl", "CarriageReturnDelay", "NewLineDelay", "BackspaceDelay",
+    "HorizontalTabDelay", "NumberOfFunctionKeys" };
+
+char const * const senf::term::Terminfo::properties::StringNames[] = {
+     "BackTab", "Bell", "CarriageReturn", "ChangeScrollRegion", "ClearAllTabs", "ClearScreen",
+     "ClrEol", "ClrEos", "ColumnAddress", "CommandCharacter", "CursorAddress", "CursorDown",
+     "CursorHome", "CursorInvisible", "CursorLeft", "CursorMemAddress", "CursorNormal",
+     "CursorRight", "CursorToLl", "CursorUp", "CursorVisible", "DeleteCharacter", "DeleteLine",
+     "DisStatusLine", "DownHalfLine", "EnterAltCharsetMode", "EnterBlinkMode", "EnterBoldMode",
+     "EnterCaMode", "EnterDeleteMode", "EnterDimMode", "EnterInsertMode", "EnterSecureMode",
+     "EnterProtectedMode", "EnterReverseMode", "EnterStandoutMode", "EnterUnderlineMode",
+     "EraseChars", "ExitAltCharsetMode", "ExitAttributeMode", "ExitCaMode", "ExitDeleteMode",
+     "ExitInsertMode", "ExitStandoutMode", "ExitUnderlineMode", "FlashScreen", "FormFeed",
+     "FromStatusLine", "Init1string", "Init2string", "Init3string", "InitFile", "InsertCharacter",
+     "InsertLine", "InsertPadding", "KeyBackspace", "KeyCatab", "KeyClear", "KeyCtab", "KeyDc",
+     "KeyDl", "KeyDown", "KeyEic", "KeyEol", "KeyEos", "KeyF0", "KeyF1", "KeyF10", "KeyF2", "KeyF3",
+     "KeyF4", "KeyF5", "KeyF6", "KeyF7", "KeyF8", "KeyF9", "KeyHome", "KeyIc", "KeyIl", "KeyLeft",
+     "KeyLl", "KeyNpage", "KeyPpage", "KeyRight", "KeySf", "KeySr", "KeyStab", "KeyUp",
+     "KeypadLocal", "KeypadXmit", "LabF0", "LabF1", "LabF10", "LabF2", "LabF3", "LabF4", "LabF5",
+     "LabF6", "LabF7", "LabF8", "LabF9", "MetaOff", "MetaOn", "Newline", "PadChar", "ParmDch",
+     "ParmDeleteLine", "ParmDownCursor", "ParmIch", "ParmIndex", "ParmInsertLine", "ParmLeftCursor",
+     "ParmRightCursor", "ParmRindex", "ParmUpCursor", "PkeyKey", "PkeyLocal", "PkeyXmit",
+     "PrintScreen", "PrtrOff", "PrtrOn", "RepeatChar", "Reset1string", "Reset2string",
+     "Reset3string", "ResetFile", "RestoreCursor", "RowAddress", "SaveCursor", "ScrollForward",
+     "ScrollReverse", "SetAttributes", "SetTab", "SetWindow", "Tab", "ToStatusLine", "UnderlineChar",
+     "UpHalfLine", "InitProg", "KeyA1", "KeyA3", "KeyB2", "KeyC1", "KeyC3", "PrtrNon", "CharPadding",
+     "AcsChars", "PlabNorm", "KeyBtab", "EnterXonMode", "ExitXonMode", "EnterAmMode", "ExitAmMode",
+     "XonCharacter", "XoffCharacter", "EnaAcs", "LabelOn", "LabelOff", "KeyBeg", "KeyCancel",
+     "KeyClose", "KeyCommand", "KeyCopy", "KeyCreate", "KeyEnd", "KeyEnter", "KeyExit", "KeyFind",
+     "KeyHelp", "KeyMark", "KeyMessage", "KeyMove", "KeyNext", "KeyOpen", "KeyOptions",
+     "KeyPrevious", "KeyPrint", "KeyRedo", "KeyReference", "KeyRefresh", "KeyReplace", "KeyRestart",
+     "KeyResume", "KeySave", "KeySuspend", "KeyUndo", "KeySbeg", "KeyScancel", "KeyScommand",
+     "KeyScopy", "KeyScreate", "KeySdc", "KeySdl", "KeySelect", "KeySend", "KeySeol", "KeySexit",
+     "KeySfind", "KeyShelp", "KeyShome", "KeySic", "KeySleft", "KeySmessage", "KeySmove", "KeySnext",
+     "KeySoptions", "KeySprevious", "KeySprint", "KeySredo", "KeySreplace", "KeySright", "KeySrsume",
+     "KeySsave", "KeySsuspend", "KeySundo", "ReqForInput", "KeyF11", "KeyF12", "KeyF13", "KeyF14",
+     "KeyF15", "KeyF16", "KeyF17", "KeyF18", "KeyF19", "KeyF20", "KeyF21", "KeyF22", "KeyF23",
+     "KeyF24", "KeyF25", "KeyF26", "KeyF27", "KeyF28", "KeyF29", "KeyF30", "KeyF31", "KeyF32",
+     "KeyF33", "KeyF34", "KeyF35", "KeyF36", "KeyF37", "KeyF38", "KeyF39", "KeyF40", "KeyF41",
+     "KeyF42", "KeyF43", "KeyF44", "KeyF45", "KeyF46", "KeyF47", "KeyF48", "KeyF49", "KeyF50",
+     "KeyF51", "KeyF52", "KeyF53", "KeyF54", "KeyF55", "KeyF56", "KeyF57", "KeyF58", "KeyF59",
+     "KeyF60", "KeyF61", "KeyF62", "KeyF63", "ClrBol", "ClearMargins", "SetLeftMargin",
+     "SetRightMargin", "LabelFormat", "SetClock", "DisplayClock", "RemoveClock", "CreateWindow",
+     "GotoWindow", "Hangup", "DialPhone", "QuickDial", "Tone", "Pulse", "FlashHook", "FixedPause",
+     "WaitTone", "User0", "User1", "User2", "User3", "User4", "User5", "User6", "User7", "User8",
+     "User9", "OrigPair", "OrigColors", "InitializeColor", "InitializePair", "SetColorPair",
+     "SetForeground", "SetBackground", "ChangeCharPitch", "ChangeLinePitch", "ChangeResHorz",
+     "ChangeResVert", "DefineChar", "EnterDoublewideMode", "EnterDraftQuality", "EnterItalicsMode",
+     "EnterLeftwardMode", "EnterMicroMode", "EnterNearLetterQuality", "EnterNormalQuality",
+     "EnterShadowMode", "EnterSubscriptMode", "EnterSuperscriptMode", "EnterUpwardMode",
+     "ExitDoublewideMode", "ExitItalicsMode", "ExitLeftwardMode", "ExitMicroMode", "ExitShadowMode",
+     "ExitSubscriptMode", "ExitSuperscriptMode", "ExitUpwardMode", "MicroColumnAddress", "MicroDown",
+     "MicroLeft", "MicroRight", "MicroRowAddress", "MicroUp", "OrderOfPins", "ParmDownMicro",
+     "ParmLeftMicro", "ParmRightMicro", "ParmUpMicro", "SelectCharSet", "SetBottomMargin",
+     "SetBottomMarginParm", "SetLeftMarginParm", "SetRightMarginParm", "SetTopMargin",
+     "SetTopMarginParm", "StartBitImage", "StartCharSetDef", "StopBitImage", "StopCharSetDef",
+     "SubscriptCharacters", "SuperscriptCharacters", "TheseCauseCr", "ZeroMotion", "CharSetNames",
+     "KeyMouse", "MouseInfo", "ReqMousePos", "GetMouse", "SetAForeground", "SetABackground",
+     "PkeyPlab", "DeviceType", "CodeSetInit", "Set0DesSeq", "Set1DesSeq", "Set2DesSeq", "Set3DesSeq",
+     "SetLrMargin", "SetTbMargin", "BitImageRepeat", "BitImageNewline", "BitImageCarriageReturn",
+     "ColorNames", "DefineBitImageRegion", "EndBitImageRegion", "SetColorBand", "SetPageLength",
+     "DisplayPcChar", "EnterPcCharsetMode", "ExitPcCharsetMode", "EnterScancodeMode",
+     "ExitScancodeMode", "PcTermOptions", "ScancodeEscape", "AltScancodeEsc",
+     "EnterHorizontalHlMode", "EnterLeftHlMode", "EnterLowHlMode", "EnterRightHlMode",
+     "EnterTopHlMode", "EnterVerticalHlMode", "SetAAttributes", "SetPglenInch", "TermcapInit2",
+     "TermcapReset", "LinefeedIfNotLf", "BackspaceIfNotBs", "OtherNonFunctionKeys", "ArrowKeyMap",
+     "AcsUlcorner", "AcsLlcorner", "AcsUrcorner", "AcsLrcorner", "AcsLtee", "AcsRtee", "AcsBtee",
+     "AcsTtee", "AcsHline", "AcsVline", "AcsPlus", "MemoryLock", "MemoryUnlock", "BoxChars1" };
+
+///////////////////////////////////////////////////////////////////////////
+// senf::term::Terminfo
+
+prefix_ senf::term::Terminfo::Terminfo()
+{}
+
+prefix_ senf::term::Terminfo::Terminfo(std::string const & term)
+{
+    load(term);
+}
+
+prefix_ void senf::term::Terminfo::load(std::string const & term)
+{
+    std::string filename (findTerminfo(term));
+    if (filename.empty()) throw InvalidTerminfoException();
+    std::ifstream is (filename.c_str());
+    if (!is) throw InvalidTerminfoException();
+    load(is);
+}
+
+prefix_ bool senf::term::Terminfo::getFlag(properties::Boolean p)
+    const
+{
+    if (BoolVec::size_type(p) >= booleans_.size())
+        return false;
+    return booleans_[p];
+}
+
+prefix_ senf::term::Terminfo::number_t senf::term::Terminfo::getNumber(properties::Numeric p)
+    const
+{
+    if (NumberVec::size_type(p) >= numbers_.size())
+        return NoValue;
+    return numbers_[p];
+}
+
+prefix_ senf::term::Terminfo::string_t senf::term::Terminfo::getString(properties::String p)
+    const
+{
+    if (StringVec::size_type(p) >= strings_.size())
+        return 0;
+    return strings_[p];
+}
+
+prefix_ bool senf::term::Terminfo::hasProperty(properties::Boolean p)
+    const
+{
+    return getFlag(p);
+}
+
+prefix_ bool senf::term::Terminfo::hasProperty(properties::Numeric p)
+    const
+{
+    return getNumber(p) != NoValue;
+}
+
+prefix_ bool senf::term::Terminfo::hasProperty(properties::String p)
+    const
+{
+    return getString(p) != 0;
+}
+
+namespace {
+
+    struct Stack
+    {
+        std::vector<senf::term::Terminfo::number_t> stack;
+
+        void push(senf::term::Terminfo::number_t v) 
+            {
+                stack.push_back(v);
+            }
+
+        senf::term::Terminfo::number_t pop() 
+            { 
+                if (stack.empty())
+                    return 0;
+                else {
+                    senf::term::Terminfo::number_t v (stack.back());
+                    stack.pop_back();
+                    return v;
+                }
+            }
+
+        senf::term::Terminfo::number_t popNonzero()
+            {
+                senf::term::Terminfo::number_t v (pop());
+                return v ? v : 1;
+            }
+    };
+
+}
+
+// The following code is taken directly from utio. As far as I understand it is buggy
+// and/or only partially implements the string format language. But seems to be enough for
+// all the common terminal types ...
+prefix_ std::string senf::term::Terminfo::formatString(properties::String p,
+                                                       number_t arg1, number_t arg2,
+                                                       number_t arg3, number_t arg4,
+                                                       number_t arg5, number_t arg6,
+                                                       number_t arg7, number_t arg8,
+                                                       number_t arg9)
+    const
+{
+    char const * fmt_p (getString(p));
+    if (! fmt_p)
+        return "";
+
+    std::string const prgstr (fmt_p);
+    Stack stack;
+    bool bCondValue (false);
+    std::string result;
+
+    for (std::string::const_iterator i (prgstr.begin()); i != prgstr.end(); ++i) {
+    if (*i != '%') {
+        result += *i;
+        continue;
+    }
+    int width = 0, base = 0;
+    switch (*++i) {
+        case '%': result += *i; break;
+        case 'i': ++arg1; ++arg2; break;
+        case 'c': result += char(stack.pop());  break;
+        case 'x': base = 16; continue;
+        case '0': if (!base) base = 8;
+        case '1': case '2': case '3': case '4':
+        case '5': case '6': case '7': case '8':
+        case '9': if (!base) base = 10;
+            width = width * base + (*i - '0');
+            continue;
+        case '\\': base = 0;
+        case '{': continue;
+        case '\'': if (*(i - 1) == '%') {
+            if (*(i + 1) != '\\')
+                width = *++i;
+            continue;
+        }
+        case '}': stack.push(width); break;
+        // Binary operands are in infix (reversed) order
+        case '+': stack.push(stack.pop() + stack.pop()); break;
+        case '-': stack.push(-stack.pop() + stack.pop()); break;
+        case '*': stack.push(stack.pop() * stack.pop()); break;
+        case '/': stack.push(stack.pop() / stack.popNonzero()); break;
+        case 'm': stack.push(stack.pop() % stack.popNonzero()); break;
+        case '|': stack.push(stack.pop() | stack.pop()); break;
+        case '&': stack.push(stack.pop() & stack.pop()); break;
+        case '^': stack.push(stack.pop() ^ stack.pop()); break;
+        case '>': stack.push(stack.pop() < stack.pop()); break;
+        case '<': stack.push(stack.pop() > stack.pop()); break;
+        case '=': stack.push(stack.pop() == stack.pop()); break;
+        case 'A': stack.push(stack.pop() && stack.pop()); break;
+        case 'O': stack.push(stack.pop() || stack.pop()); break;
+        case '!': stack.push(!stack.pop()); break;
+        case '~': stack.push(~stack.pop()); break;
+        case 't': bCondValue = stack.pop();
+        case 'e': if ((bCondValue = !bCondValue)) // this also supports elsif
+            --(i = prgstr.begin() + std::min (prgstr.find ("%e", i-prgstr.begin()), 
+                                              prgstr.find ("%;", i-prgstr.begin())));
+        case '?':
+        case ';': break;
+        case 'p': 
+            switch (*++i) {
+            case '1': stack.push(arg1); break;
+            case '2': stack.push(arg2); break;
+            case '3': stack.push(arg3); break;
+            case '4': stack.push(arg4); break;
+            case '5': stack.push(arg5); break;
+            case '6': stack.push(arg6); break;
+            case '7': stack.push(arg7); break;
+            case '8': stack.push(arg8); break;
+            case '9': stack.push(arg9); break;
+            }
+            break;
+        case 'd': {
+            number_t n = stack.pop();
+            const std::string::size_type iSize = result.size();
+            do {
+                result += std::string::value_type('0' + (n % 10));
+            } while ((n /= 10) || --width > 0);
+            reverse (result.begin() + iSize, result.end());
+            break; }
+        }
+    }
+
+    return result;
+}
+
+ prefix_ void senf::term::Terminfo::dump(std::ostream & os)
+     const
+ {
+     os << "Terminfo entry: " << name_ << "\n";
+     os << "Booleans: " << booleans_.size() << "\n";
+     os << "Numbers: " << numbers_.size() << "\n";
+     os << "Strings: " << strings_.size() << "\n";
+     os << "String pool size: " << stringPool_.size() << "\n";
+
+     {
+         os << "Flags:\n";
+         unsigned n (0);
+         BoolVec::const_iterator i (booleans_.begin());
+         BoolVec::const_iterator const i_end (booleans_.end());
+         for (; i != i_end; ++i, ++n)
+             if (*i && n < sizeof(properties::BooleanNames)/sizeof(properties::BooleanNames[0]))
+                 os << "    " << properties::BooleanNames[n] << "\n";
+     }
+
+     {
+         os << "Numbers:\n";
+         unsigned n (0);
+         NumberVec::const_iterator i (numbers_.begin());
+         NumberVec::const_iterator const i_end (numbers_.end());
+         for (; i != i_end; ++i, ++n)
+             if (*i != NoValue 
+                 && n < sizeof(properties::NumericNames)/sizeof(properties::NumericNames[0]))
+                 os << "    " << properties::NumericNames[n] << " = " << *i << "\n";
+     }
+
+     {
+         os << "Strings:\n";
+         unsigned n (0);
+         StringVec::const_iterator i (strings_.begin());
+         StringVec::const_iterator const i_end (strings_.end());
+         for (; i != i_end; ++i, ++n)
+             if (*i && n < sizeof(properties::StringNames)/sizeof(properties::StringNames[0])) {
+                 os << "    " << std::setw(32) << properties::StringNames[n] << " = ";
+                 hexdump(*i, *i + strlen(*i), os, 32);
+             }
+     }
+
+}
+
+prefix_ std::string senf::term::Terminfo::findTerminfo(std::string const & name)
+{
+    if (name.empty()) return "";
+    boost::filesystem::path subdir (name.substr(0,1)); subdir /= name;
+    boost::filesystem::path tientry;
+
+    {
+        char const * tivar (::getenv("TERMINFO"));
+        if (tivar) {
+            tientry = boost::filesystem::path(tivar) / subdir;
+            if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
+        }
+    }
+
+    tientry = boost::filesystem::path("/etc/terminfo") / subdir;
+    if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
+
+    tientry = boost::filesystem::path("/lib/terminfo")  / subdir;
+    if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
+
+    tientry = boost::filesystem::path("/usr/share/terminfo") / subdir;
+    if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
+
+    return "";
+}
+
+namespace {
+
+    boost::uint16_t const TerminfoMagic = 0x011A;
+
+    struct TerminfoHeader {
+        boost::uint16_t magic;
+        boost::uint16_t namesSz;
+        boost::uint16_t nBooleans;
+        boost::uint16_t nNumbers;
+        boost::uint16_t nStrings;
+        boost::uint16_t stringPoolSz;
+    };
+
+}
+
+prefix_ void senf::term::Terminfo::load(std::istream & is)
+{
+    TerminfoHeader h;
+    is.read(static_cast<char*>(static_cast<void*>(&h)), sizeof(h));
+    if (!is || h.magic != TerminfoMagic) throw InvalidTerminfoException();
+
+    name_.resize(h.namesSz);
+    is.read(&(name_[0]), name_.size());
+    if (!is) throw InvalidTerminfoException();
+    if (name_.size() & 1)
+        is.ignore(1u);
+    {
+        std::string::size_type n (name_.find('\0'));
+        if (n != std::string::npos)
+            name_.erase(n);
+    }
+
+    booleans_.resize(h.nBooleans);
+    for (BoolVec::iterator i (booleans_.begin()); i != booleans_.end(); ++i) {
+        char v;
+        is.read(&v, sizeof(v));
+        if (!is) throw InvalidTerminfoException();
+        *i = v;
+    }
+    if (booleans_.size() & 1)
+        is.ignore(1u);
+    
+    numbers_.resize(h.nNumbers);
+    for (NumberVec::iterator i (numbers_.begin()); i != numbers_.end(); ++i) {
+        number_t v;
+        is.read(static_cast<char*>(static_cast<void*>(&v)), sizeof(v));
+        if (!is) throw InvalidTerminfoException();
+        *i = v;
+    }
+
+    typedef std::vector<number_t> OffsetVec;
+    OffsetVec offsets;
+    offsets.resize (h.nStrings);
+    for (OffsetVec::iterator i (offsets.begin()); i != offsets.end(); ++i) {
+        number_t v;
+        is.read(static_cast<char*>(static_cast<void*>(&v)), sizeof(v));
+        if (!is) throw InvalidTerminfoException();
+        *i = v;
+    }
+    
+    stringPool_.resize(h.stringPoolSz);
+    is.read(&(stringPool_[0]), stringPool_.size());
+    if (!is) throw InvalidTerminfoException();
+
+    strings_.resize(offsets.size());
+    StringVec::iterator j (strings_.begin());
+    for (OffsetVec::iterator i (offsets.begin()); i != offsets.end(); ++i, ++j)
+        if (*i != NoValue && *i >= 0 && unsigned(*i) < stringPool_.size())
+            *j = &(stringPool_[0]) + *i;
+        else
+            *j = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::term::KeyParser
+
+char const * const senf::term::KeyParser::KeyNames[] = {
+    "Esc", "Backspace", "Backtab", "Begin", "CATab", "CTab", "Cancel", "Center", "Clear",
+    "ClearToEOL", "ClearToEOS", "Close", "Command", "Copy", "Create", "Delete", "DeleteLine",
+    "Down", "DownLeft", "DownRight", "End", "Enter", "Exit", "F0", "F1", "F2", "F3", "F4", "F5",
+    "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "F19",
+    "F20", "F21", "F22", "F23", "F24", "F25", "F26", "F27", "F28", "F29", "F30", "F31", "F32",
+    "F33", "F34", "F35", "F36", "F37", "F38", "F39", "F40", "F41", "F42", "F43", "F44", "F45",
+    "F46", "F47", "F48", "F49", "F50", "F51", "F52", "F53", "F54", "F55", "F56", "F57", "F58",
+    "F59", "F60", "F61", "F62", "F63", "Find", "Help", "Home", "Insert", "InsertLine", "Left",
+    "Mark", "Message", "Mouse", "Move", "Next", "Open", "Options", "PageDown", "PageUp", "Previous",
+    "Print", "Redo", "Reference", "Refresh", "Replace", "Restart", "Resume", "Right", "Save",
+    "Select", "ShiftBegin", "ShiftCancel", "ShiftCommand", "ShiftCopy", "ShiftCreate",
+    "ShiftDelete", "ShiftDeleteLine", "ShiftEnd", "ShiftClearToEOL", "ShiftExit", "ShiftFind",
+    "ShiftHelp", "ShiftHome", "ShiftInsert", "ShiftLeft", "ShiftMessage", "ShiftMove", "ShiftNext",
+    "ShiftOptions", "ShiftPrevious", "ShiftPrint", "ShiftRedo", "ShiftReplace", "ShiftResume",
+    "ShiftRight", "ShiftSave", "ShiftSuspend", "ShiftTab", "ShiftUndo", "Suspend", "Undo", "Up",
+    "UpLeft", "UpRight" };
+
+prefix_ senf::term::KeyParser::KeyParser()
+{}
+
+prefix_ senf::term::KeyParser::KeyParser(Terminfo const & ti)
+{
+    load(ti);
+}
+
+prefix_ void senf::term::KeyParser::load(Terminfo const & ti)
+{
+    static Terminfo::properties::String keyStrings [] = {
+        Terminfo::properties::KeyCommand, Terminfo::properties::KeyBackspace,
+        Terminfo::properties::KeyBtab, Terminfo::properties::KeyBeg, Terminfo::properties::KeyCatab,
+        Terminfo::properties::KeyCtab, Terminfo::properties::KeyCancel, Terminfo::properties::KeyB2,
+        Terminfo::properties::KeyClear, Terminfo::properties::KeyEol, Terminfo::properties::KeyEos,
+        Terminfo::properties::KeyClose, Terminfo::properties::KeyCommand,
+        Terminfo::properties::KeyCopy, Terminfo::properties::KeyCreate, Terminfo::properties::KeyDc,
+        Terminfo::properties::KeyDl, Terminfo::properties::KeyDown, Terminfo::properties::KeyC1,
+        Terminfo::properties::KeyC3, Terminfo::properties::KeyEnd, Terminfo::properties::KeyEnter,
+        Terminfo::properties::KeyExit, Terminfo::properties::KeyF0, Terminfo::properties::KeyF1,
+        Terminfo::properties::KeyF2, Terminfo::properties::KeyF3, Terminfo::properties::KeyF4,
+        Terminfo::properties::KeyF5, Terminfo::properties::KeyF6, Terminfo::properties::KeyF7,
+        Terminfo::properties::KeyF8, Terminfo::properties::KeyF9, Terminfo::properties::KeyF10,
+        Terminfo::properties::KeyF11, Terminfo::properties::KeyF12, Terminfo::properties::KeyF13,
+        Terminfo::properties::KeyF14, Terminfo::properties::KeyF15, Terminfo::properties::KeyF16,
+        Terminfo::properties::KeyF17, Terminfo::properties::KeyF18, Terminfo::properties::KeyF19,
+        Terminfo::properties::KeyF20, Terminfo::properties::KeyF21, Terminfo::properties::KeyF22,
+        Terminfo::properties::KeyF23, Terminfo::properties::KeyF24, Terminfo::properties::KeyF25,
+        Terminfo::properties::KeyF26, Terminfo::properties::KeyF27, Terminfo::properties::KeyF28,
+        Terminfo::properties::KeyF29, Terminfo::properties::KeyF30, Terminfo::properties::KeyF31,
+        Terminfo::properties::KeyF32, Terminfo::properties::KeyF33, Terminfo::properties::KeyF34,
+        Terminfo::properties::KeyF35, Terminfo::properties::KeyF36, Terminfo::properties::KeyF37,
+        Terminfo::properties::KeyF38, Terminfo::properties::KeyF39, Terminfo::properties::KeyF40,
+        Terminfo::properties::KeyF41, Terminfo::properties::KeyF42, Terminfo::properties::KeyF43,
+        Terminfo::properties::KeyF44, Terminfo::properties::KeyF45, Terminfo::properties::KeyF46,
+        Terminfo::properties::KeyF47, Terminfo::properties::KeyF48, Terminfo::properties::KeyF49,
+        Terminfo::properties::KeyF50, Terminfo::properties::KeyF51, Terminfo::properties::KeyF52,
+        Terminfo::properties::KeyF53, Terminfo::properties::KeyF54, Terminfo::properties::KeyF55,
+        Terminfo::properties::KeyF56, Terminfo::properties::KeyF57, Terminfo::properties::KeyF58,
+        Terminfo::properties::KeyF59, Terminfo::properties::KeyF60, Terminfo::properties::KeyF61,
+        Terminfo::properties::KeyF62, Terminfo::properties::KeyF63, Terminfo::properties::KeyFind,
+        Terminfo::properties::KeyHelp, Terminfo::properties::KeyHome, Terminfo::properties::KeyIc,
+        Terminfo::properties::KeyIl, Terminfo::properties::KeyLeft, Terminfo::properties::KeyMark,
+        Terminfo::properties::KeyMessage, Terminfo::properties::KeyMouse,
+        Terminfo::properties::KeyMove, Terminfo::properties::KeyNext, Terminfo::properties::KeyOpen,
+        Terminfo::properties::KeyOptions, Terminfo::properties::KeyNpage,
+        Terminfo::properties::KeyPpage, Terminfo::properties::KeyPrevious,
+        Terminfo::properties::KeyPrint, Terminfo::properties::KeyRedo,
+        Terminfo::properties::KeyReference, Terminfo::properties::KeyRefresh,
+        Terminfo::properties::KeyReplace, Terminfo::properties::KeyRestart,
+        Terminfo::properties::KeyResume, Terminfo::properties::KeyRight,
+        Terminfo::properties::KeySave, Terminfo::properties::KeySelect,
+        Terminfo::properties::KeySbeg, Terminfo::properties::KeyScancel,
+        Terminfo::properties::KeyScommand, Terminfo::properties::KeyScopy,
+        Terminfo::properties::KeyScreate, Terminfo::properties::KeySdc,
+        Terminfo::properties::KeySdl, Terminfo::properties::KeySend, Terminfo::properties::KeySeol,
+        Terminfo::properties::KeySexit, Terminfo::properties::KeySfind,
+        Terminfo::properties::KeyShelp, Terminfo::properties::KeyShome,
+        Terminfo::properties::KeySic, Terminfo::properties::KeySleft,
+        Terminfo::properties::KeySmessage, Terminfo::properties::KeySmove,
+        Terminfo::properties::KeySnext, Terminfo::properties::KeySoptions,
+        Terminfo::properties::KeySprevious, Terminfo::properties::KeySprint,
+        Terminfo::properties::KeySredo, Terminfo::properties::KeySreplace,
+        Terminfo::properties::KeySrsume, Terminfo::properties::KeySright,
+        Terminfo::properties::KeySsave, Terminfo::properties::KeySsuspend,
+        Terminfo::properties::KeyStab, Terminfo::properties::KeySundo,
+        Terminfo::properties::KeySuspend, Terminfo::properties::KeyUndo,
+        Terminfo::properties::KeyUp, Terminfo::properties::KeyA1, Terminfo::properties::KeyA3 };
+
+    table_.clear();
+    for (unsigned i (0); i < sizeof(keyStrings)/sizeof(keyStrings[0]); ++i) {
+        char const * key (ti.getString(keyStrings[i]));
+        if (key)
+            table_.insert(std::make_pair(key, KeyCode(i+First)));
+    }
+}
+
+prefix_ std::pair<senf::term::KeyParser::keycode_t, std::string::size_type>
+senf::term::KeyParser::lookup(std::string const & key)
+    const
+{
+    if (key.empty())
+        return std::make_pair(KeyCode(0), 0);
+
+    // There are several cases: 
+    // a) 'key' is an incomplete key sequence. In this case, 'key' will precede all completions in
+    //    the key table. The first possible completion is found by 'upper_bound'
+    // b) 'key' is a complete key sequence. This is the key sequence *preceding* the 'upper_bound'
+    // c) 'key' is a complete key sequence with additional trailing characters. In this case, 'key'
+    //    will follow the correct entry in the key table. Again, the correct key sequence is
+    //    the one preceding the 'upper_bound'
+
+    Keytable::const_iterator i (table_.upper_bound(key));
+    if (i != table_.end() && i->first.substr(0, key.size()) == key)
+        return std::make_pair(Incomplete, key.size());
+    if (i == table_.begin())
+        return std::make_pair(keycode_t(key[0]), 1);
+    --i;
+    if (key.substr(0, i->first.size()) == i->first)
+        return std::make_pair(i->second, i->first.size());
+    return std::make_pair(keycode_t(key[0]), 1);
+}
+
+prefix_ std::string senf::term::KeyParser::describe(keycode_t key)
+{
+    if (key < keycode_t(' '))
+        return "^" + std::string(1, '@' + key);
+    if (key < 256)
+        return std::string(1, char(key));
+    if (key >= keycode_t(First) && key < keycode_t(First + sizeof(KeyNames) / sizeof(KeyNames[0])))
+        return std::string(KeyNames[key-First]);
+    else
+        return "<" + boost::lexical_cast<std::string>(unsigned(key)) + ">";
+}
+
+prefix_ void senf::term::KeyParser::dump(std::ostream & os)
+    const
+{
+    os << "Keytable:\n";
+    for (Keytable::const_iterator i (table_.begin()); i != table_.end(); ++i) {
+        unsigned index (i->second - First);
+        if (index < sizeof(KeyNames)/sizeof(KeyNames[0])) {
+            std::cout << "    " << std::setw(32) << KeyNames[index] << ": ";
+            hexdump(i->first.begin(), i->first.end(), os);
+        }
+    }
+}
+
+///////////////////////////////cc.e////////////////////////////////////////
+#undef prefix_
+//#include "Terminfo.mpp"
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End: