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