16fbc62e69db385d9253218d7221e9430f1f3b4d
[senf.git] / senf / 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 //-/////////////////////////////////////////////////////////////////////////////////////////////////
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     if (filename.empty()) throw InvalidTerminfoException();
144     std::ifstream is (filename.c_str());
145     if (!is) 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     if (name.empty()) return "";
363     boost::filesystem::path subdir (name.substr(0,1)); subdir /= name;
364     boost::filesystem::path tientry;
365
366     {
367         char const * tivar (::getenv("TERMINFO"));
368         if (tivar) {
369             tientry = boost::filesystem::path(tivar) / subdir;
370             if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
371         }
372     }
373
374     tientry = boost::filesystem::path("/etc/terminfo") / subdir;
375     if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
376
377     tientry = boost::filesystem::path("/lib/terminfo")  / subdir;
378     if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
379
380     tientry = boost::filesystem::path("/usr/share/terminfo") / subdir;
381     if (boost::filesystem::exists(tientry)) return tientry.native_file_string();
382
383     return "";
384 }
385
386 namespace {
387
388     boost::uint16_t const TerminfoMagic = 0x011A;
389
390     struct TerminfoHeader {
391         boost::uint16_t magic;
392         boost::uint16_t namesSz;
393         boost::uint16_t nBooleans;
394         boost::uint16_t nNumbers;
395         boost::uint16_t nStrings;
396         boost::uint16_t stringPoolSz;
397     };
398
399 }
400
401 prefix_ void senf::term::Terminfo::load(std::istream & is)
402 {
403     TerminfoHeader h;
404     is.read(static_cast<char*>(static_cast<void*>(&h)), sizeof(h));
405     if (!is || h.magic != TerminfoMagic) throw InvalidTerminfoException();
406
407     name_.resize(h.namesSz);
408     is.read(&(name_[0]), name_.size());
409     if (!is) throw InvalidTerminfoException();
410     if (name_.size() & 1)
411         is.ignore(1u);
412     {
413         std::string::size_type n (name_.find('\0'));
414         if (n != std::string::npos)
415             name_.erase(n);
416     }
417
418     booleans_.resize(h.nBooleans);
419     for (BoolVec::iterator i (booleans_.begin()); i != booleans_.end(); ++i) {
420         char v;
421         is.read(&v, sizeof(v));
422         if (!is) throw InvalidTerminfoException();
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         if (!is) throw InvalidTerminfoException();
433         *i = v;
434     }
435
436     typedef std::vector<number_t> OffsetVec;
437     OffsetVec offsets;
438     offsets.resize (h.nStrings);
439     for (OffsetVec::iterator i (offsets.begin()); i != offsets.end(); ++i) {
440         number_t v;
441         is.read(static_cast<char*>(static_cast<void*>(&v)), sizeof(v));
442         if (!is) throw InvalidTerminfoException();
443         *i = v;
444     }
445
446     stringPool_.resize(h.stringPoolSz);
447     is.read(&(stringPool_[0]), stringPool_.size());
448     if (!is) throw InvalidTerminfoException();
449
450     strings_.resize(offsets.size());
451     StringVec::iterator j (strings_.begin());
452     for (OffsetVec::iterator i (offsets.begin()); i != offsets.end(); ++i, ++j)
453         if (*i != NoValue && *i >= 0 && unsigned(*i) < stringPool_.size())
454             *j = &(stringPool_[0]) + *i;
455         else
456             *j = 0;
457 }
458
459 //-/////////////////////////////////////////////////////////////////////////////////////////////////
460 // senf::term::KeyParser
461
462 char const * const senf::term::KeyParser::KeyNames[] = {
463     "Esc", "Backspace", "Backtab", "Begin", "CATab", "CTab", "Cancel", "Center", "Clear",
464     "ClearToEOL", "ClearToEOS", "Close", "Command", "Copy", "Create", "Delete", "DeleteLine",
465     "Down", "DownLeft", "DownRight", "End", "Enter", "Exit", "F0", "F1", "F2", "F3", "F4", "F5",
466     "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "F19",
467     "F20", "F21", "F22", "F23", "F24", "F25", "F26", "F27", "F28", "F29", "F30", "F31", "F32",
468     "F33", "F34", "F35", "F36", "F37", "F38", "F39", "F40", "F41", "F42", "F43", "F44", "F45",
469     "F46", "F47", "F48", "F49", "F50", "F51", "F52", "F53", "F54", "F55", "F56", "F57", "F58",
470     "F59", "F60", "F61", "F62", "F63", "Find", "Help", "Home", "Insert", "InsertLine", "Left",
471     "Mark", "Message", "Mouse", "Move", "Next", "Open", "Options", "PageDown", "PageUp", "Previous",
472     "Print", "Redo", "Reference", "Refresh", "Replace", "Restart", "Resume", "Right", "Save",
473     "Select", "ShiftBegin", "ShiftCancel", "ShiftCommand", "ShiftCopy", "ShiftCreate",
474     "ShiftDelete", "ShiftDeleteLine", "ShiftEnd", "ShiftClearToEOL", "ShiftExit", "ShiftFind",
475     "ShiftHelp", "ShiftHome", "ShiftInsert", "ShiftLeft", "ShiftMessage", "ShiftMove", "ShiftNext",
476     "ShiftOptions", "ShiftPrevious", "ShiftPrint", "ShiftRedo", "ShiftReplace", "ShiftResume",
477     "ShiftRight", "ShiftSave", "ShiftSuspend", "ShiftTab", "ShiftUndo", "Suspend", "Undo", "Up",
478     "UpLeft", "UpRight" };
479
480 prefix_ senf::term::KeyParser::KeyParser()
481 {}
482
483 prefix_ senf::term::KeyParser::KeyParser(Terminfo const & ti)
484 {
485     load(ti);
486 }
487
488 prefix_ void senf::term::KeyParser::load(Terminfo const & ti)
489 {
490     static Terminfo::properties::String keyStrings [] = {
491         Terminfo::properties::KeyCommand, Terminfo::properties::KeyBackspace,
492         Terminfo::properties::KeyBtab, Terminfo::properties::KeyBeg, Terminfo::properties::KeyCatab,
493         Terminfo::properties::KeyCtab, Terminfo::properties::KeyCancel, Terminfo::properties::KeyB2,
494         Terminfo::properties::KeyClear, Terminfo::properties::KeyEol, Terminfo::properties::KeyEos,
495         Terminfo::properties::KeyClose, Terminfo::properties::KeyCommand,
496         Terminfo::properties::KeyCopy, Terminfo::properties::KeyCreate, Terminfo::properties::KeyDc,
497         Terminfo::properties::KeyDl, Terminfo::properties::KeyDown, Terminfo::properties::KeyC1,
498         Terminfo::properties::KeyC3, Terminfo::properties::KeyEnd, Terminfo::properties::KeyEnter,
499         Terminfo::properties::KeyExit, Terminfo::properties::KeyF0, Terminfo::properties::KeyF1,
500         Terminfo::properties::KeyF2, Terminfo::properties::KeyF3, Terminfo::properties::KeyF4,
501         Terminfo::properties::KeyF5, Terminfo::properties::KeyF6, Terminfo::properties::KeyF7,
502         Terminfo::properties::KeyF8, Terminfo::properties::KeyF9, Terminfo::properties::KeyF10,
503         Terminfo::properties::KeyF11, Terminfo::properties::KeyF12, Terminfo::properties::KeyF13,
504         Terminfo::properties::KeyF14, Terminfo::properties::KeyF15, Terminfo::properties::KeyF16,
505         Terminfo::properties::KeyF17, Terminfo::properties::KeyF18, Terminfo::properties::KeyF19,
506         Terminfo::properties::KeyF20, Terminfo::properties::KeyF21, Terminfo::properties::KeyF22,
507         Terminfo::properties::KeyF23, Terminfo::properties::KeyF24, Terminfo::properties::KeyF25,
508         Terminfo::properties::KeyF26, Terminfo::properties::KeyF27, Terminfo::properties::KeyF28,
509         Terminfo::properties::KeyF29, Terminfo::properties::KeyF30, Terminfo::properties::KeyF31,
510         Terminfo::properties::KeyF32, Terminfo::properties::KeyF33, Terminfo::properties::KeyF34,
511         Terminfo::properties::KeyF35, Terminfo::properties::KeyF36, Terminfo::properties::KeyF37,
512         Terminfo::properties::KeyF38, Terminfo::properties::KeyF39, Terminfo::properties::KeyF40,
513         Terminfo::properties::KeyF41, Terminfo::properties::KeyF42, Terminfo::properties::KeyF43,
514         Terminfo::properties::KeyF44, Terminfo::properties::KeyF45, Terminfo::properties::KeyF46,
515         Terminfo::properties::KeyF47, Terminfo::properties::KeyF48, Terminfo::properties::KeyF49,
516         Terminfo::properties::KeyF50, Terminfo::properties::KeyF51, Terminfo::properties::KeyF52,
517         Terminfo::properties::KeyF53, Terminfo::properties::KeyF54, Terminfo::properties::KeyF55,
518         Terminfo::properties::KeyF56, Terminfo::properties::KeyF57, Terminfo::properties::KeyF58,
519         Terminfo::properties::KeyF59, Terminfo::properties::KeyF60, Terminfo::properties::KeyF61,
520         Terminfo::properties::KeyF62, Terminfo::properties::KeyF63, Terminfo::properties::KeyFind,
521         Terminfo::properties::KeyHelp, Terminfo::properties::KeyHome, Terminfo::properties::KeyIc,
522         Terminfo::properties::KeyIl, Terminfo::properties::KeyLeft, Terminfo::properties::KeyMark,
523         Terminfo::properties::KeyMessage, Terminfo::properties::KeyMouse,
524         Terminfo::properties::KeyMove, Terminfo::properties::KeyNext, Terminfo::properties::KeyOpen,
525         Terminfo::properties::KeyOptions, Terminfo::properties::KeyNpage,
526         Terminfo::properties::KeyPpage, Terminfo::properties::KeyPrevious,
527         Terminfo::properties::KeyPrint, Terminfo::properties::KeyRedo,
528         Terminfo::properties::KeyReference, Terminfo::properties::KeyRefresh,
529         Terminfo::properties::KeyReplace, Terminfo::properties::KeyRestart,
530         Terminfo::properties::KeyResume, Terminfo::properties::KeyRight,
531         Terminfo::properties::KeySave, Terminfo::properties::KeySelect,
532         Terminfo::properties::KeySbeg, Terminfo::properties::KeyScancel,
533         Terminfo::properties::KeyScommand, Terminfo::properties::KeyScopy,
534         Terminfo::properties::KeyScreate, Terminfo::properties::KeySdc,
535         Terminfo::properties::KeySdl, Terminfo::properties::KeySend, Terminfo::properties::KeySeol,
536         Terminfo::properties::KeySexit, Terminfo::properties::KeySfind,
537         Terminfo::properties::KeyShelp, Terminfo::properties::KeyShome,
538         Terminfo::properties::KeySic, Terminfo::properties::KeySleft,
539         Terminfo::properties::KeySmessage, Terminfo::properties::KeySmove,
540         Terminfo::properties::KeySnext, Terminfo::properties::KeySoptions,
541         Terminfo::properties::KeySprevious, Terminfo::properties::KeySprint,
542         Terminfo::properties::KeySredo, Terminfo::properties::KeySreplace,
543         Terminfo::properties::KeySrsume, Terminfo::properties::KeySright,
544         Terminfo::properties::KeySsave, Terminfo::properties::KeySsuspend,
545         Terminfo::properties::KeyStab, Terminfo::properties::KeySundo,
546         Terminfo::properties::KeySuspend, Terminfo::properties::KeyUndo,
547         Terminfo::properties::KeyUp, Terminfo::properties::KeyA1, Terminfo::properties::KeyA3 };
548
549     table_.clear();
550     for (unsigned i (0); i < sizeof(keyStrings)/sizeof(keyStrings[0]); ++i) {
551         char const * key (ti.getString(keyStrings[i]));
552         if (key)
553             table_.insert(std::make_pair(key, KeyCode(i+First)));
554     }
555 }
556
557 prefix_ std::pair<senf::term::KeyParser::keycode_t, std::string::size_type>
558 senf::term::KeyParser::lookup(std::string const & key)
559     const
560 {
561     if (key.empty())
562         return std::make_pair(KeyCode(0), 0);
563
564     // There are several cases:
565     // a) 'key' is an incomplete key sequence. In this case, 'key' will precede all completions in
566     //    the key table. The first possible completion is found by 'upper_bound'
567     // b) 'key' is a complete key sequence. This is the key sequence *preceding* the 'upper_bound'
568     // c) 'key' is a complete key sequence with additional trailing characters. In this case, 'key'
569     //    will follow the correct entry in the key table. Again, the correct key sequence is
570     //    the one preceding the 'upper_bound'
571
572     Keytable::const_iterator i (table_.upper_bound(key));
573     if (i != table_.end() && i->first.substr(0, key.size()) == key)
574         return std::make_pair(Incomplete, key.size());
575     if (i == table_.begin())
576         return std::make_pair(keycode_t(key[0]), 1);
577     --i;
578     if (key.substr(0, i->first.size()) == i->first)
579         return std::make_pair(i->second, i->first.size());
580     return std::make_pair(keycode_t(key[0]), 1);
581 }
582
583 prefix_ std::string senf::term::KeyParser::describe(keycode_t key)
584 {
585     if (key < keycode_t(' '))
586         return "^" + std::string(1, '@' + key);
587     if (key < 256)
588         return std::string(1, char(key));
589     if (key >= keycode_t(First) && key < keycode_t(First + sizeof(KeyNames) / sizeof(KeyNames[0])))
590         return std::string(KeyNames[key-First]);
591     else
592         return "<" + boost::lexical_cast<std::string>(unsigned(key)) + ">";
593 }
594
595 prefix_ void senf::term::KeyParser::dump(std::ostream & os)
596     const
597 {
598     os << "Keytable:\n";
599     for (Keytable::const_iterator i (table_.begin()); i != table_.end(); ++i) {
600         unsigned index (i->second - First);
601         if (index < sizeof(KeyNames)/sizeof(KeyNames[0])) {
602             std::cout << "    " << std::setw(32) << KeyNames[index] << ": ";
603             hexdump(i->first.begin(), i->first.end(), os);
604         }
605     }
606 }
607
608 //-/////////////////////////////////////////////////////////////////////////////////////////////////
609 #undef prefix_
610 //#include "Terminfo.mpp"
611
612 \f
613 // Local Variables:
614 // mode: c++
615 // fill-column: 100
616 // comment-column: 40
617 // c-file-style: "senf"
618 // indent-tabs-mode: nil
619 // ispell-local-dictionary: "american"
620 // compile-command: "scons -u test"
621 // End: