Utils/Termlib: Implement terminfo format string interpretation
[senf.git] / Utils / Termlib / Terminfo.cc
1 // $Id$
2 //
3 // Copyright (C) 2009 
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief Terminfo non-inline non-template implementation */
25
26 #include "Terminfo.hh"
27 //#include "Terminfo.ih"
28
29 // Custom includes
30 #include <fstream>
31 #include <iomanip>
32 #include <boost/filesystem/operations.hpp>
33 #include <senf/config.hh>
34 #include <senf/Utils/hexdump.hh>
35 #include <unistd.h>
36 #include <string.h>
37
38 //#include "Terminfo.mpp"
39 #define prefix_
40 ///////////////////////////////cc.p////////////////////////////////////////
41
42 char const * const senf::term::Terminfo::properties::BooleanNames [] = {
43     "AutoLeftMargin", "AutoRightMargin", "NoEscCtlc", "CeolStandoutGlitch", "EatNewlineGlitch",
44     "EraseOverstrike", "GenericType", "HardCopy", "HasMetaKey", "HasStatusLine", "InsertNullGlitch",
45     "MemoryAbove", "MemoryBelow", "MoveInsertMode", "MoveStandoutMode", "OverStrike",
46     "StatusLineEscOk", "DestTabsMagicSmso", "TildeGlitch", "TransparentUnderline", "XonXoff",
47     "NeedsXonXoff", "PrtrSilent", "HardCursor", "NonRevRmcup", "NoPadChar", "NonDestScrollRegion",
48     "CanChange", "BackColorErase", "HueLightnessSaturation", "ColAddrGlitch", "CrCancelsMicroMode",
49     "HasPrintWheel", "RowAddrGlitch", "SemiAutoRightMargin", "CpiChangesRes", "LpiChangesRes",
50     "BackspacesWithBs", "CrtNoScrolling", "NoCorrectlyWorkingCr", "GnuHasMetaKey",
51     "LinefeedIsNewline", "HasHardwareTabs", "ReturnDoesClrEol" };
52
53 char const * const senf::term::Terminfo::properties::NumericNames[] = {
54     "Columns", "InitTabs", "Lines", "LinesOfMemory", "MagicCookieGlitch", "PaddingBaudRate",
55     "VirtualTerminal", "WidthStatusLine", "NumLabels", "LabelHeight", "LabelWidth", "MaxAttributes",
56     "MaximumWindows", "MaxColors", "MaxPairs", "NoColorVideo", "BufferCapacity", "DotVertSpacing",
57     "DotHorzSpacing", "MaxMicroAddress", "MaxMicroJump", "MicroColSize", "MicroLineSize",
58     "NumberOfPins", "OutputResChar", "OutputResLine", "OutputResHorzInch", "OutputResVertInch",
59     "PrintRate", "WideCharSize", "Buttons", "BitImageEntwining", "BitImageType",
60     "MagicCookieGlitchUl", "CarriageReturnDelay", "NewLineDelay", "BackspaceDelay",
61     "HorizontalTabDelay", "NumberOfFunctionKeys" };
62
63 char const * const senf::term::Terminfo::properties::StringNames[] = {
64      "BackTab", "Bell", "CarriageReturn", "ChangeScrollRegion", "ClearAllTabs", "ClearScreen",
65      "ClrEol", "ClrEos", "ColumnAddress", "CommandCharacter", "CursorAddress", "CursorDown",
66      "CursorHome", "CursorInvisible", "CursorLeft", "CursorMemAddress", "CursorNormal",
67      "CursorRight", "CursorToLl", "CursorUp", "CursorVisible", "DeleteCharacter", "DeleteLine",
68      "DisStatusLine", "DownHalfLine", "EnterAltCharsetMode", "EnterBlinkMode", "EnterBoldMode",
69      "EnterCaMode", "EnterDeleteMode", "EnterDimMode", "EnterInsertMode", "EnterSecureMode",
70      "EnterProtectedMode", "EnterReverseMode", "EnterStandoutMode", "EnterUnderlineMode",
71      "EraseChars", "ExitAltCharsetMode", "ExitAttributeMode", "ExitCaMode", "ExitDeleteMode",
72      "ExitInsertMode", "ExitStandoutMode", "ExitUnderlineMode", "FlashScreen", "FormFeed",
73      "FromStatusLine", "Init1string", "Init2string", "Init3string", "InitFile", "InsertCharacter",
74      "InsertLine", "InsertPadding", "KeyBackspace", "KeyCatab", "KeyClear", "KeyCtab", "KeyDc",
75      "KeyDl", "KeyDown", "KeyEic", "KeyEol", "KeyEos", "KeyF0", "KeyF1", "KeyF10", "KeyF2", "KeyF3",
76      "KeyF4", "KeyF5", "KeyF6", "KeyF7", "KeyF8", "KeyF9", "KeyHome", "KeyIc", "KeyIl", "KeyLeft",
77      "KeyLl", "KeyNpage", "KeyPpage", "KeyRight", "KeySf", "KeySr", "KeyStab", "KeyUp",
78      "KeypadLocal", "KeypadXmit", "LabF0", "LabF1", "LabF10", "LabF2", "LabF3", "LabF4", "LabF5",
79      "LabF6", "LabF7", "LabF8", "LabF9", "MetaOff", "MetaOn", "Newline", "PadChar", "ParmDch",
80      "ParmDeleteLine", "ParmDownCursor", "ParmIch", "ParmIndex", "ParmInsertLine", "ParmLeftCursor",
81      "ParmRightCursor", "ParmRindex", "ParmUpCursor", "PkeyKey", "PkeyLocal", "PkeyXmit",
82      "PrintScreen", "PrtrOff", "PrtrOn", "RepeatChar", "Reset1string", "Reset2string",
83      "Reset3string", "ResetFile", "RestoreCursor", "RowAddress", "SaveCursor", "ScrollForward",
84      "ScrollReverse", "SetAttributes", "SetTab", "SetWindow", "Tab", "ToStatusLine", "UnderlineChar",
85      "UpHalfLine", "InitProg", "KeyA1", "KeyA3", "KeyB2", "KeyC1", "KeyC3", "PrtrNon", "CharPadding",
86      "AcsChars", "PlabNorm", "KeyBtab", "EnterXonMode", "ExitXonMode", "EnterAmMode", "ExitAmMode",
87      "XonCharacter", "XoffCharacter", "EnaAcs", "LabelOn", "LabelOff", "KeyBeg", "KeyCancel",
88      "KeyClose", "KeyCommand", "KeyCopy", "KeyCreate", "KeyEnd", "KeyEnter", "KeyExit", "KeyFind",
89      "KeyHelp", "KeyMark", "KeyMessage", "KeyMove", "KeyNext", "KeyOpen", "KeyOptions",
90      "KeyPrevious", "KeyPrint", "KeyRedo", "KeyReference", "KeyRefresh", "KeyReplace", "KeyRestart",
91      "KeyResume", "KeySave", "KeySuspend", "KeyUndo", "KeySbeg", "KeyScancel", "KeyScommand",
92      "KeyScopy", "KeyScreate", "KeySdc", "KeySdl", "KeySelect", "KeySend", "KeySeol", "KeySexit",
93      "KeySfind", "KeyShelp", "KeyShome", "KeySic", "KeySleft", "KeySmessage", "KeySmove", "KeySnext",
94      "KeySoptions", "KeySprevious", "KeySprint", "KeySredo", "KeySreplace", "KeySright", "KeySrsume",
95      "KeySsave", "KeySsuspend", "KeySundo", "ReqForInput", "KeyF11", "KeyF12", "KeyF13", "KeyF14",
96      "KeyF15", "KeyF16", "KeyF17", "KeyF18", "KeyF19", "KeyF20", "KeyF21", "KeyF22", "KeyF23",
97      "KeyF24", "KeyF25", "KeyF26", "KeyF27", "KeyF28", "KeyF29", "KeyF30", "KeyF31", "KeyF32",
98      "KeyF33", "KeyF34", "KeyF35", "KeyF36", "KeyF37", "KeyF38", "KeyF39", "KeyF40", "KeyF41",
99      "KeyF42", "KeyF43", "KeyF44", "KeyF45", "KeyF46", "KeyF47", "KeyF48", "KeyF49", "KeyF50",
100      "KeyF51", "KeyF52", "KeyF53", "KeyF54", "KeyF55", "KeyF56", "KeyF57", "KeyF58", "KeyF59",
101      "KeyF60", "KeyF61", "KeyF62", "KeyF63", "ClrBol", "ClearMargins", "SetLeftMargin",
102      "SetRightMargin", "LabelFormat", "SetClock", "DisplayClock", "RemoveClock", "CreateWindow",
103      "GotoWindow", "Hangup", "DialPhone", "QuickDial", "Tone", "Pulse", "FlashHook", "FixedPause",
104      "WaitTone", "User0", "User1", "User2", "User3", "User4", "User5", "User6", "User7", "User8",
105      "User9", "OrigPair", "OrigColors", "InitializeColor", "InitializePair", "SetColorPair",
106      "SetForeground", "SetBackground", "ChangeCharPitch", "ChangeLinePitch", "ChangeResHorz",
107      "ChangeResVert", "DefineChar", "EnterDoublewideMode", "EnterDraftQuality", "EnterItalicsMode",
108      "EnterLeftwardMode", "EnterMicroMode", "EnterNearLetterQuality", "EnterNormalQuality",
109      "EnterShadowMode", "EnterSubscriptMode", "EnterSuperscriptMode", "EnterUpwardMode",
110      "ExitDoublewideMode", "ExitItalicsMode", "ExitLeftwardMode", "ExitMicroMode", "ExitShadowMode",
111      "ExitSubscriptMode", "ExitSuperscriptMode", "ExitUpwardMode", "MicroColumnAddress", "MicroDown",
112      "MicroLeft", "MicroRight", "MicroRowAddress", "MicroUp", "OrderOfPins", "ParmDownMicro",
113      "ParmLeftMicro", "ParmRightMicro", "ParmUpMicro", "SelectCharSet", "SetBottomMargin",
114      "SetBottomMarginParm", "SetLeftMarginParm", "SetRightMarginParm", "SetTopMargin",
115      "SetTopMarginParm", "StartBitImage", "StartCharSetDef", "StopBitImage", "StopCharSetDef",
116      "SubscriptCharacters", "SuperscriptCharacters", "TheseCauseCr", "ZeroMotion", "CharSetNames",
117      "KeyMouse", "MouseInfo", "ReqMousePos", "GetMouse", "SetAForeground", "SetABackground",
118      "PkeyPlab", "DeviceType", "CodeSetInit", "Set0DesSeq", "Set1DesSeq", "Set2DesSeq", "Set3DesSeq",
119      "SetLrMargin", "SetTbMargin", "BitImageRepeat", "BitImageNewline", "BitImageCarriageReturn",
120      "ColorNames", "DefineBitImageRegion", "EndBitImageRegion", "SetColorBand", "SetPageLength",
121      "DisplayPcChar", "EnterPcCharsetMode", "ExitPcCharsetMode", "EnterScancodeMode",
122      "ExitScancodeMode", "PcTermOptions", "ScancodeEscape", "AltScancodeEsc",
123      "EnterHorizontalHlMode", "EnterLeftHlMode", "EnterLowHlMode", "EnterRightHlMode",
124      "EnterTopHlMode", "EnterVerticalHlMode", "SetAAttributes", "SetPglenInch", "TermcapInit2",
125      "TermcapReset", "LinefeedIfNotLf", "BackspaceIfNotBs", "OtherNonFunctionKeys", "ArrowKeyMap",
126      "AcsUlcorner", "AcsLlcorner", "AcsUrcorner", "AcsLrcorner", "AcsLtee", "AcsRtee", "AcsBtee",
127      "AcsTtee", "AcsHline", "AcsVline", "AcsPlus", "MemoryLock", "MemoryUnlock", "BoxChars1" };
128
129 ///////////////////////////////////////////////////////////////////////////
130 // senf::term::Terminfo
131
132 prefix_ senf::term::Terminfo::Terminfo()
133 {}
134
135 prefix_ senf::term::Terminfo::Terminfo(std::string const & term)
136 {
137     load(term);
138 }
139
140 prefix_ void senf::term::Terminfo::load(std::string const & term)
141 {
142     std::string filename (findTerminfo(term));
143     std::ifstream is (filename.c_str());
144     if (!is)
145         throw InvalidTerminfoException();
146     load(is);
147 }
148
149 prefix_ bool senf::term::Terminfo::getFlag(properties::Boolean p)
150     const
151 {
152     if (BoolVec::size_type(p) >= booleans_.size())
153         return false;
154     return booleans_[p];
155 }
156
157 prefix_ senf::term::Terminfo::number_t senf::term::Terminfo::getNumber(properties::Numeric p)
158     const
159 {
160     if (NumberVec::size_type(p) >= numbers_.size())
161         return NoValue;
162     return numbers_[p];
163 }
164
165 prefix_ senf::term::Terminfo::string_t senf::term::Terminfo::getString(properties::String p)
166     const
167 {
168     if (StringVec::size_type(p) >= strings_.size())
169         return 0;
170     return strings_[p];
171 }
172
173 prefix_ bool senf::term::Terminfo::hasProperty(properties::Boolean p)
174     const
175 {
176     return getFlag(p);
177 }
178
179 prefix_ bool senf::term::Terminfo::hasProperty(properties::Numeric p)
180     const
181 {
182     return getNumber(p) != NoValue;
183 }
184
185 prefix_ bool senf::term::Terminfo::hasProperty(properties::String p)
186     const
187 {
188     return getString(p) != 0;
189 }
190
191 namespace {
192
193     struct Stack
194     {
195         std::vector<senf::term::Terminfo::number_t> stack;
196
197         void push(senf::term::Terminfo::number_t v) 
198             {
199                 stack.push_back(v);
200             }
201
202         senf::term::Terminfo::number_t pop() 
203             { 
204                 if (stack.empty())
205                     return 0;
206                 else {
207                     senf::term::Terminfo::number_t v (stack.back());
208                     stack.pop_back();
209                     return v;
210                 }
211             }
212
213         senf::term::Terminfo::number_t popNonzero()
214             {
215                 senf::term::Terminfo::number_t v (pop());
216                 return v ? v : 1;
217             }
218     };
219
220 }
221
222 // The following code is taken directly from utio. As far as I understand it is buggy
223 // and/or only partially implements the string format language. But seems to be enough for
224 // all the common terminal types ...
225 prefix_ std::string senf::term::Terminfo::formatString(properties::String p,
226                                                        number_t arg1, number_t arg2,
227                                                        number_t arg3, number_t arg4,
228                                                        number_t arg5, number_t arg6,
229                                                        number_t arg7, number_t arg8,
230                                                        number_t arg9)
231     const
232 {
233     char const * fmt_p (getString(p));
234     if (! fmt_p)
235         return "";
236
237     std::string const prgstr (fmt_p);
238     Stack stack;
239     bool bCondValue (false);
240     std::string result;
241
242     for (std::string::const_iterator i (prgstr.begin()); i != prgstr.end(); ++i) {
243         if (*i != '%') {
244             result += *i;
245             continue;
246         }
247         int width = 0, base = 0;
248         switch (*++i) {
249         case '%': result += *i; break;
250         case 'i': ++arg1; ++arg2; break;
251         case 'c': result += char(stack.pop());  break;
252         case 'x': base = 16; continue;
253         case '0': if (!base) base = 8;
254         case '1': case '2': case '3': case '4':
255         case '5': case '6': case '7': case '8':
256         case '9': if (!base) base = 10;
257             width = width * base + (*i - '0');
258             continue;
259         case '\\': base = 0;
260         case '{': continue;
261         case '\'': if (*(i - 1) == '%') {
262             if (*(i + 1) != '\\')
263                 width = *++i;
264             continue;
265         }
266         case '}': stack.push(width); break;
267             // Binary operands are in infix (reversed) order
268         case '+': stack.push(stack.pop() + stack.pop()); break;
269         case '-': stack.push(-stack.pop() + stack.pop()); break;
270         case '*': stack.push(stack.pop() * stack.pop()); break;
271         case '/': stack.push(stack.pop() / stack.popNonzero()); break;
272         case 'm': stack.push(stack.pop() % stack.popNonzero()); break;
273         case '|': stack.push(stack.pop() | stack.pop()); break;
274         case '&': stack.push(stack.pop() & stack.pop()); break;
275         case '^': stack.push(stack.pop() ^ stack.pop()); break;
276         case '>': stack.push(stack.pop() < stack.pop()); break;
277         case '<': stack.push(stack.pop() > stack.pop()); break;
278         case '=': stack.push(stack.pop() == stack.pop()); break;
279         case 'A': stack.push(stack.pop() && stack.pop()); break;
280         case 'O': stack.push(stack.pop() || stack.pop()); break;
281         case '!': stack.push(!stack.pop()); break;
282         case '~': stack.push(~stack.pop()); break;
283         case 't': bCondValue = stack.pop();
284         case 'e': if ((bCondValue = !bCondValue)) // this also supports elsif
285             --(i = prgstr.begin() + std::min (prgstr.find ("%e", i-prgstr.begin()), 
286                                               prgstr.find ("%;", i-prgstr.begin())));
287         case '?':
288         case ';': break;
289         case 'p': 
290             switch (*++i) {
291             case '1': stack.push(arg1); break;
292             case '2': stack.push(arg2); break;
293             case '3': stack.push(arg3); break;
294             case '4': stack.push(arg4); break;
295             case '5': stack.push(arg5); break;
296             case '6': stack.push(arg6); break;
297             case '7': stack.push(arg7); break;
298             case '8': stack.push(arg8); break;
299             case '9': stack.push(arg9); break;
300             }
301             break;
302         case 'd': {
303             number_t n = stack.pop();
304             const std::string::size_type iSize = result.size();
305             do {
306                 result += std::string::value_type('0' + (n % 10));
307             } while ((n /= 10) || --width > 0);
308             reverse (result.begin() + iSize, result.end());
309             break; }
310         }
311     }
312
313     return result;
314 }
315
316  prefix_ void senf::term::Terminfo::dump(std::ostream & os)
317      const
318  {
319      os << "Terminfo entry: " << name_ << "\n";
320      os << "Booleans: " << booleans_.size() << "\n";
321      os << "Numbers: " << numbers_.size() << "\n";
322      os << "Strings: " << strings_.size() << "\n";
323      os << "String pool size: " << stringPool_.size() << "\n";
324
325      {
326          os << "Flags:\n";
327          unsigned n (0);
328          BoolVec::const_iterator i (booleans_.begin());
329          BoolVec::const_iterator const i_end (booleans_.end());
330          for (; i != i_end; ++i, ++n)
331              if (*i && n < sizeof(properties::BooleanNames)/sizeof(properties::BooleanNames[0]))
332                  os << "    " << properties::BooleanNames[n] << "\n";
333      }
334
335      {
336          os << "Numbers:\n";
337          unsigned n (0);
338          NumberVec::const_iterator i (numbers_.begin());
339          NumberVec::const_iterator const i_end (numbers_.end());
340          for (; i != i_end; ++i, ++n)
341              if (*i != NoValue 
342                  && n < sizeof(properties::NumericNames)/sizeof(properties::NumericNames[0]))
343                  os << "    " << properties::NumericNames[n] << " = " << *i << "\n";
344      }
345
346      {
347          os << "Strings:\n";
348          unsigned n (0);
349          StringVec::const_iterator i (strings_.begin());
350          StringVec::const_iterator const i_end (strings_.end());
351          for (; i != i_end; ++i, ++n)
352              if (*i && n < sizeof(properties::StringNames)/sizeof(properties::StringNames[0])) {
353                  os << "    " << std::setw(32) << properties::StringNames[n] << " = ";
354                  hexdump(*i, *i + strlen(*i), os, 32);
355              }
356      }
357
358 }
359
360 prefix_ std::string senf::term::Terminfo::findTerminfo(std::string const & name)
361 {
362     boost::filesystem::path subdir (name.substr(0,1)); subdir /= name;
363     boost::filesystem::path tientry, tipath;
364
365     {
366         char const * tivar (::getenv("TERMINFO"));
367         if (tivar) {
368             tipath = tivar;
369             tientry = tipath / subdir;
370             if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
371         }
372     }
373
374     tipath = "/etc/terminfo";
375     tientry = tipath / subdir;
376     if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
377
378     tipath = "/lib/terminfo";
379     tientry = tipath / subdir;
380     if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
381
382     tipath = "/usr/share/terminfo";
383     tientry = tipath / subdir;
384     if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
385
386     return "";
387 }
388
389 namespace {
390
391     boost::uint16_t const TerminfoMagic = 0x011A;
392
393     struct TerminfoHeader {
394         boost::uint16_t magic;
395         boost::uint16_t namesSz;
396         boost::uint16_t nBooleans;
397         boost::uint16_t nNumbers;
398         boost::uint16_t nStrings;
399         boost::uint16_t stringPoolSz;
400     };
401
402 }
403
404 prefix_ void senf::term::Terminfo::load(std::istream & is)
405 {
406     TerminfoHeader h;
407     is.read(static_cast<char*>(static_cast<void*>(&h)), sizeof(h));
408     if (h.magic != TerminfoMagic)
409         throw InvalidTerminfoException();
410
411     name_.resize(h.namesSz);
412     is.read(&(name_[0]), name_.size());
413     {
414         std::string::size_type n (name_.find('\0'));
415         if (n != std::string::npos)
416             name_.erase(n);
417     }
418
419     booleans_.resize(h.nBooleans);
420     for (BoolVec::iterator i (booleans_.begin()); i != booleans_.end(); ++i) {
421         char v;
422         is.read(&v, sizeof(v));
423         *i = v;
424     }
425     if (booleans_.size() & 1)
426         is.ignore(1u);
427     
428     numbers_.resize(h.nNumbers);
429     for (NumberVec::iterator i (numbers_.begin()); i != numbers_.end(); ++i) {
430         number_t v;
431         is.read(static_cast<char*>(static_cast<void*>(&v)), sizeof(v));
432         *i = v;
433     }
434
435     typedef std::vector<number_t> OffsetVec;
436     OffsetVec offsets;
437     offsets.resize (h.nStrings);
438     for (OffsetVec::iterator i (offsets.begin()); i != offsets.end(); ++i) {
439         number_t v;
440         is.read(static_cast<char*>(static_cast<void*>(&v)), sizeof(v));
441         *i = v;
442     }
443     
444     stringPool_.resize(h.stringPoolSz);
445     is.read(&(stringPool_[0]), stringPool_.size());
446
447     strings_.resize(offsets.size());
448     StringVec::iterator j (strings_.begin());
449     for (OffsetVec::iterator i (offsets.begin()); i != offsets.end(); ++i, ++j)
450         if (*i != NoValue)
451             *j = &(stringPool_[0]) + *i;
452 }
453
454 ///////////////////////////////////////////////////////////////////////////
455 // senf::term::KeyParser
456
457 char const * const senf::term::KeyParser::KeyNames[] = {
458     "Esc", "Backspace", "Backtab", "Begin", "CATab", "CTab", "Cancel", "Center", "Clear",
459     "ClearToEOL", "ClearToEOS", "Close", "Command", "Copy", "Create", "Delete", "DeleteLine",
460     "Down", "DownLeft", "DownRight", "End", "Enter", "Exit", "F0", "F1", "F2", "F3", "F4", "F5",
461     "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "F19",
462     "F20", "F21", "F22", "F23", "F24", "F25", "F26", "F27", "F28", "F29", "F30", "F31", "F32",
463     "F33", "F34", "F35", "F36", "F37", "F38", "F39", "F40", "F41", "F42", "F43", "F44", "F45",
464     "F46", "F47", "F48", "F49", "F50", "F51", "F52", "F53", "F54", "F55", "F56", "F57", "F58",
465     "F59", "F60", "F61", "F62", "F63", "Find", "Help", "Home", "Insert", "InsertLine", "Left",
466     "Mark", "Message", "Mouse", "Move", "Next", "Open", "Options", "PageDown", "PageUp", "Previous",
467     "Print", "Redo", "Reference", "Refresh", "Replace", "Restart", "Resume", "Right", "Save",
468     "Select", "ShiftBegin", "ShiftCancel", "ShiftCommand", "ShiftCopy", "ShiftCreate",
469     "ShiftDelete", "ShiftDeleteLine", "ShiftEnd", "ShiftClearToEOL", "ShiftExit", "ShiftFind",
470     "ShiftHelp", "ShiftHome", "ShiftInsert", "ShiftLeft", "ShiftMessage", "ShiftMove", "ShiftNext",
471     "ShiftOptions", "ShiftPrevious", "ShiftPrint", "ShiftRedo", "ShiftReplace", "ShiftResume",
472     "ShiftRight", "ShiftSave", "ShiftSuspend", "ShiftTab", "ShiftUndo", "Suspend", "Undo", "Up",
473     "UpLeft", "UpRight" };
474
475 prefix_ senf::term::KeyParser::KeyParser()
476 {}
477
478 prefix_ senf::term::KeyParser::KeyParser(Terminfo const & ti)
479 {
480     load(ti);
481 }
482
483 prefix_ void senf::term::KeyParser::load(Terminfo const & ti)
484 {
485     static Terminfo::properties::String keyStrings [] = {
486         Terminfo::properties::KeyCommand, Terminfo::properties::KeyBackspace,
487         Terminfo::properties::KeyBtab, Terminfo::properties::KeyBeg, Terminfo::properties::KeyCatab,
488         Terminfo::properties::KeyCtab, Terminfo::properties::KeyCancel, Terminfo::properties::KeyB2,
489         Terminfo::properties::KeyClear, Terminfo::properties::KeyEol, Terminfo::properties::KeyEos,
490         Terminfo::properties::KeyClose, Terminfo::properties::KeyCommand,
491         Terminfo::properties::KeyCopy, Terminfo::properties::KeyCreate, Terminfo::properties::KeyDc,
492         Terminfo::properties::KeyDl, Terminfo::properties::KeyDown, Terminfo::properties::KeyC1,
493         Terminfo::properties::KeyC3, Terminfo::properties::KeyEnd, Terminfo::properties::KeyEnter,
494         Terminfo::properties::KeyExit, Terminfo::properties::KeyF0, Terminfo::properties::KeyF1,
495         Terminfo::properties::KeyF2, Terminfo::properties::KeyF3, Terminfo::properties::KeyF4,
496         Terminfo::properties::KeyF5, Terminfo::properties::KeyF6, Terminfo::properties::KeyF7,
497         Terminfo::properties::KeyF8, Terminfo::properties::KeyF9, Terminfo::properties::KeyF10,
498         Terminfo::properties::KeyF11, Terminfo::properties::KeyF12, Terminfo::properties::KeyF13,
499         Terminfo::properties::KeyF14, Terminfo::properties::KeyF15, Terminfo::properties::KeyF16,
500         Terminfo::properties::KeyF17, Terminfo::properties::KeyF18, Terminfo::properties::KeyF19,
501         Terminfo::properties::KeyF20, Terminfo::properties::KeyF21, Terminfo::properties::KeyF22,
502         Terminfo::properties::KeyF23, Terminfo::properties::KeyF24, Terminfo::properties::KeyF25,
503         Terminfo::properties::KeyF26, Terminfo::properties::KeyF27, Terminfo::properties::KeyF28,
504         Terminfo::properties::KeyF29, Terminfo::properties::KeyF30, Terminfo::properties::KeyF31,
505         Terminfo::properties::KeyF32, Terminfo::properties::KeyF33, Terminfo::properties::KeyF34,
506         Terminfo::properties::KeyF35, Terminfo::properties::KeyF36, Terminfo::properties::KeyF37,
507         Terminfo::properties::KeyF38, Terminfo::properties::KeyF39, Terminfo::properties::KeyF40,
508         Terminfo::properties::KeyF41, Terminfo::properties::KeyF42, Terminfo::properties::KeyF43,
509         Terminfo::properties::KeyF44, Terminfo::properties::KeyF45, Terminfo::properties::KeyF46,
510         Terminfo::properties::KeyF47, Terminfo::properties::KeyF48, Terminfo::properties::KeyF49,
511         Terminfo::properties::KeyF50, Terminfo::properties::KeyF51, Terminfo::properties::KeyF52,
512         Terminfo::properties::KeyF53, Terminfo::properties::KeyF54, Terminfo::properties::KeyF55,
513         Terminfo::properties::KeyF56, Terminfo::properties::KeyF57, Terminfo::properties::KeyF58,
514         Terminfo::properties::KeyF59, Terminfo::properties::KeyF60, Terminfo::properties::KeyF61,
515         Terminfo::properties::KeyF62, Terminfo::properties::KeyF63, Terminfo::properties::KeyFind,
516         Terminfo::properties::KeyHelp, Terminfo::properties::KeyHome, Terminfo::properties::KeyIc,
517         Terminfo::properties::KeyIl, Terminfo::properties::KeyLeft, Terminfo::properties::KeyMark,
518         Terminfo::properties::KeyMessage, Terminfo::properties::KeyMouse,
519         Terminfo::properties::KeyMove, Terminfo::properties::KeyNext, Terminfo::properties::KeyOpen,
520         Terminfo::properties::KeyOptions, Terminfo::properties::KeyNpage,
521         Terminfo::properties::KeyPpage, Terminfo::properties::KeyPrevious,
522         Terminfo::properties::KeyPrint, Terminfo::properties::KeyRedo,
523         Terminfo::properties::KeyReference, Terminfo::properties::KeyRefresh,
524         Terminfo::properties::KeyReplace, Terminfo::properties::KeyRestart,
525         Terminfo::properties::KeyResume, Terminfo::properties::KeyRight,
526         Terminfo::properties::KeySave, Terminfo::properties::KeySelect,
527         Terminfo::properties::KeySbeg, Terminfo::properties::KeyScancel,
528         Terminfo::properties::KeyScommand, Terminfo::properties::KeyScopy,
529         Terminfo::properties::KeyScreate, Terminfo::properties::KeySdc,
530         Terminfo::properties::KeySdl, Terminfo::properties::KeySend, Terminfo::properties::KeySeol,
531         Terminfo::properties::KeySexit, Terminfo::properties::KeySfind,
532         Terminfo::properties::KeyShelp, Terminfo::properties::KeyShome,
533         Terminfo::properties::KeySic, Terminfo::properties::KeySleft,
534         Terminfo::properties::KeySmessage, Terminfo::properties::KeySmove,
535         Terminfo::properties::KeySnext, Terminfo::properties::KeySoptions,
536         Terminfo::properties::KeySprevious, Terminfo::properties::KeySprint,
537         Terminfo::properties::KeySredo, Terminfo::properties::KeySreplace,
538         Terminfo::properties::KeySrsume, Terminfo::properties::KeySright,
539         Terminfo::properties::KeySsave, Terminfo::properties::KeySsuspend,
540         Terminfo::properties::KeyStab, Terminfo::properties::KeySundo,
541         Terminfo::properties::KeySuspend, Terminfo::properties::KeyUndo,
542         Terminfo::properties::KeyUp, Terminfo::properties::KeyA1, Terminfo::properties::KeyA3 };
543
544     table_.clear();
545     for (unsigned i (0); i < sizeof(keyStrings)/sizeof(keyStrings[0]); ++i) {
546         char const * key (ti.getString(keyStrings[i]));
547         if (key)
548             table_.insert(std::make_pair(key, KeyCode(i+First)));
549     }
550 }
551
552 prefix_ std::pair<senf::term::KeyParser::keycode_t, std::string::size_type>
553 senf::term::KeyParser::lookup(std::string const & key)
554     const
555 {
556     if (key.empty())
557         return std::make_pair(KeyCode(0), 0);
558
559     // There are several cases: 
560     // a) 'key' is an incomplete key sequence. In this case, 'key' will precede all completions in
561     //    the key table. The first possible completion is found by 'upper_bound'
562     // b) 'key' is a complete key sequence. This is the key sequence *preceding* the 'upper_bound'
563     // c) 'key' is a complete key sequence with additional trailing characters. In this case, 'key'
564     //    will follow the correct entry in the key table. Again, the correct key sequence is
565     //    the one preceding the 'upper_bound'
566
567     Keytable::const_iterator i (table_.upper_bound(key));
568     if (i != table_.end() && i->first.substr(0, key.size()) == key)
569         return std::make_pair(Incomplete, key.size());
570     if (i == table_.begin())
571         return std::make_pair(keycode_t(key[0]), 1);
572     --i;
573     if (key.substr(0, i->first.size()) == i->first)
574         return std::make_pair(i->second, i->first.size());
575     return std::make_pair(keycode_t(key[0]), 1);
576 }
577
578 prefix_ std::string senf::term::KeyParser::describe(keycode_t key)
579 {
580     if (key < keycode_t(' '))
581         return "^" + std::string(1, '@' + key);
582     if (key < 256)
583         return std::string(1, char(key));
584     if (key >= keycode_t(First) && key < keycode_t(First + sizeof(KeyNames) / sizeof(KeyNames[0])))
585         return std::string(KeyNames[key-First]);
586     else
587         return "<" + boost::lexical_cast<std::string>(unsigned(key)) + ">";
588 }
589
590 prefix_ void senf::term::KeyParser::dump(std::ostream & os)
591     const
592 {
593     os << "Keytable:\n";
594     for (Keytable::const_iterator i (table_.begin()); i != table_.end(); ++i) {
595         unsigned index (i->second - First);
596         if (index < sizeof(KeyNames)/sizeof(KeyNames[0])) {
597             std::cout << "    " << std::setw(32) << KeyNames[index] << ": ";
598             hexdump(i->first.begin(), i->first.end(), os);
599         }
600     }
601 }
602
603 ///////////////////////////////cc.e////////////////////////////////////////
604 #undef prefix_
605 //#include "Terminfo.mpp"
606
607 \f
608 // Local Variables:
609 // mode: c++
610 // fill-column: 100
611 // comment-column: 40
612 // c-file-style: "senf"
613 // indent-tabs-mode: nil
614 // ispell-local-dictionary: "american"
615 // compile-command: "scons -u test"
616 // End: